From a25491362443824a854be7bb2c79e64d8f2459a3 Mon Sep 17 00:00:00 2001 From: Andreas Weizel Date: Fri, 1 Jun 2018 13:39:34 +0200 Subject: [PATCH 001/221] Work for throw expressions. --- .../CSharp/ExpressionBuilder.cs | 11 ++- .../CSharp/TranslatedExpression.cs | 3 + ICSharpCode.Decompiler/IL/Instructions.cs | 2 +- ICSharpCode.Decompiler/IL/Instructions.tt | 2 +- .../IL/Instructions/TryInstruction.cs | 5 ++ .../IL/Transforms/NullCoalescingTransform.cs | 69 ++++++++++++++++++- 6 files changed, 86 insertions(+), 6 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 93c66b85d..ce304a7a3 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -746,6 +746,13 @@ namespace ICSharpCode.Decompiler.CSharp return HandleThreeValuedLogic(inst, BinaryOperatorType.BitwiseOr, ExpressionType.Or); } + protected internal override TranslatedExpression VisitThrow(Throw inst, TranslationContext context) + { + return new ThrowExpression(Translate(inst.Argument)) + .WithILInstruction(inst) + .WithRR(new ResolveResult(SpecialType.NoType)); + } + TranslatedExpression HandleThreeValuedLogic(BinaryInstruction inst, BinaryOperatorType op, ExpressionType eop) { var left = Translate(inst.Left); @@ -2181,7 +2188,9 @@ namespace ICSharpCode.Decompiler.CSharp var rr = resolver.ResolveBinaryOperator(BinaryOperatorType.NullCoalescing, value.ResolveResult, fallback.ResolveResult); if (rr.IsError) { IType targetType; - if (!value.Type.Equals(SpecialType.NullType) && !fallback.Type.Equals(SpecialType.NullType) && !value.Type.Equals(fallback.Type)) { + if (fallback.Expression is ThrowExpression && fallback.Type.Equals(SpecialType.NoType)) { + targetType = value.Type; + } else if (!value.Type.Equals(SpecialType.NullType) && !fallback.Type.Equals(SpecialType.NullType) && !value.Type.Equals(fallback.Type)) { targetType = compilation.FindType(inst.UnderlyingResultType.ToKnownTypeCode()); } else { targetType = value.Type.Equals(SpecialType.NullType) ? fallback.Type : value.Type; diff --git a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs index 5ebd40771..0346e2502 100644 --- a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs @@ -203,6 +203,9 @@ namespace ICSharpCode.Decompiler.CSharp if (targetType.Kind == TypeKind.Unknown || targetType.Kind == TypeKind.Void || targetType.Kind == TypeKind.None) { return this; // don't attempt to insert cast to '?' or 'void' as these are not valid. } + if (Expression is ThrowExpression) { + return this; // Throw expressions have no type and are implicitly convertible to any type + } if (Expression is TupleExpression tupleExpr && targetType is TupleType targetTupleType && tupleExpr.Elements.Count == targetTupleType.ElementTypes.Length) { diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index 97e585d0d..76e4c76d6 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -4225,7 +4225,7 @@ namespace ICSharpCode.Decompiler.IL public Throw(ILInstruction argument) : base(OpCode.Throw, argument) { } - public override StackType ResultType { get { return StackType.Void; } } + public override StackType ResultType { get { return this.resultType; } } protected override InstructionFlags ComputeFlags() { return base.ComputeFlags() | InstructionFlags.MayThrow | InstructionFlags.EndPointUnreachable; diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index 95347ede4..4b81890a7 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -250,7 +250,7 @@ new OpCode("default.value", "Returns the default value for a type.", NoArguments, HasTypeOperand, ResultType("type.GetStackType()")), new OpCode("throw", "Throws an exception.", - Unary, MayThrow, UnconditionalBranch), + Unary, MayThrow, HasFlag("InstructionFlags.EndPointUnreachable"), ResultType("this.resultType")), new OpCode("rethrow", "Rethrows the current exception.", NoArguments, MayThrow, UnconditionalBranch), new OpCode("sizeof", "Gets the size of a type in bytes.", diff --git a/ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs index b53611c42..6f7f6daa5 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/TryInstruction.cs @@ -366,4 +366,9 @@ namespace ICSharpCode.Decompiler.IL } } } + + public partial class Throw + { + internal StackType resultType = StackType.Void; + } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs index 0eab50bd0..49aa44fe0 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs @@ -46,6 +46,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// } /// => /// stloc s(if.notnull(valueInst, fallbackInst)) + /// + /// ------------------- + /// + /// stloc obj(valueInst) + /// if (comp(ldloc obj == ldnull)) { + /// throw(...) + /// } + /// => + /// stloc obj(if.notnull(valueInst, throw(...))) /// bool TransformRefTypes(Block block, int pos, StatementTransformContext context) { @@ -55,15 +64,69 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!block.Instructions[pos + 1].MatchIfInstruction(out var condition, out var trueInst)) return false; + if (!(condition.MatchCompEquals(out var left, out var right) && left.MatchLdLoc(stloc.Variable) && right.MatchLdNull())) + return false; trueInst = Block.Unwrap(trueInst); - if (condition.MatchCompEquals(out var left, out var right) && left.MatchLdLoc(stloc.Variable) && right.MatchLdNull() - && trueInst.MatchStLoc(stloc.Variable, out var fallbackValue) - ) { + if (trueInst.MatchStLoc(stloc.Variable, out var fallbackValue)) { context.Step("NullCoalescingTransform (reference types)", stloc); stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, fallbackValue); block.Instructions.RemoveAt(pos + 1); // remove if instruction ILInlining.InlineOneIfPossible(block, pos, false, context); return true; + } else if (trueInst is Throw throwInst) { + context.Step("NullCoalescingTransform (throw expression)", stloc); + throwInst.resultType = StackType.O; + stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, throwInst); + block.Instructions.RemoveAt(pos + 1); // remove if instruction + ILInlining.InlineOneIfPossible(block, pos, false, context); + return true; + } + return false; + } + + /// + /// Handles NullCoalescingInstruction case 1: reference types. + /// + /// stloc s(valueInst) + /// if (comp(ldloc s == ldnull)) { + /// stloc s(fallbackInst) + /// } + /// => + /// stloc s(if.notnull(valueInst, fallbackInst)) + /// + /// ------------------- + /// + /// stloc obj(valueInst) + /// if (comp(ldloc obj == ldnull)) { + /// throw(...) + /// } + /// => + /// stloc obj(if.notnull(valueInst, throw(...))) + /// + bool TransformRefTypesA(Block block, int pos, StatementTransformContext context) + { + if (!(block.Instructions[pos] is StLoc stloc)) + return false; + if (stloc.Variable.Kind != VariableKind.StackSlot) + return false; + if (!block.Instructions[pos + 1].MatchIfInstruction(out var condition, out var trueInst)) + return false; + if (!(condition.MatchCompEquals(out var left, out var right) && left.MatchLdLoc(stloc.Variable) && right.MatchLdNull())) + return false; + trueInst = Block.Unwrap(trueInst); + if (trueInst.MatchStLoc(stloc.Variable, out var fallbackValue)) { + context.Step("NullCoalescingTransform (reference types)", stloc); + stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, fallbackValue); + block.Instructions.RemoveAt(pos + 1); // remove if instruction + ILInlining.InlineOneIfPossible(block, pos, false, context); + return true; + } else if (trueInst is Throw throwInst) { + context.Step("NullCoalescingTransform (throw expression)", stloc); + throwInst.resultType = StackType.O; + stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, throwInst); + block.Instructions.RemoveAt(pos + 1); // remove if instruction + ILInlining.InlineOneIfPossible(block, pos, false, context); + return true; } return false; } From 58b0ddda9e62a9c3f84ce307dd8e2ce873caf81a Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 2 Jun 2018 09:16:02 +0200 Subject: [PATCH 002/221] Add ThrowExpressionConversion. --- .../CSharp/ExpressionBuilder.cs | 16 +++---- .../CSharp/Resolver/CSharpConversions.cs | 3 ++ .../CSharp/TranslatedExpression.cs | 2 +- .../ICSharpCode.Decompiler.csproj | 1 + .../IL/Transforms/NullCoalescingTransform.cs | 47 ------------------- .../Semantics/Conversion.cs | 19 +++++++- .../Semantics/ThrowResolveResult.cs | 29 ++++++++++++ 7 files changed, 59 insertions(+), 58 deletions(-) create mode 100644 ICSharpCode.Decompiler/Semantics/ThrowResolveResult.cs diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index ce304a7a3..f207ff408 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -746,13 +746,6 @@ namespace ICSharpCode.Decompiler.CSharp return HandleThreeValuedLogic(inst, BinaryOperatorType.BitwiseOr, ExpressionType.Or); } - protected internal override TranslatedExpression VisitThrow(Throw inst, TranslationContext context) - { - return new ThrowExpression(Translate(inst.Argument)) - .WithILInstruction(inst) - .WithRR(new ResolveResult(SpecialType.NoType)); - } - TranslatedExpression HandleThreeValuedLogic(BinaryInstruction inst, BinaryOperatorType op, ExpressionType eop) { var left = Translate(inst.Left); @@ -775,6 +768,13 @@ namespace ICSharpCode.Decompiler.CSharp .WithILInstruction(inst); } + protected internal override TranslatedExpression VisitThrow(Throw inst, TranslationContext context) + { + return new ThrowExpression(Translate(inst.Argument)) + .WithILInstruction(inst) + .WithRR(new ThrowResolveResult()); + } + ExpressionWithResolveResult Assignment(TranslatedExpression left, TranslatedExpression right) { right = right.ConvertTo(left.Type, this, allowImplicitConversion: true); @@ -2189,7 +2189,7 @@ namespace ICSharpCode.Decompiler.CSharp if (rr.IsError) { IType targetType; if (fallback.Expression is ThrowExpression && fallback.Type.Equals(SpecialType.NoType)) { - targetType = value.Type; + targetType = NullableType.GetUnderlyingType(value.Type); } else if (!value.Type.Equals(SpecialType.NullType) && !fallback.Type.Equals(SpecialType.NullType) && !value.Type.Equals(fallback.Type)) { targetType = compilation.FindType(inst.UnderlyingResultType.ToKnownTypeCode()); } else { diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs index 5d8860d36..2d42e143b 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs @@ -115,6 +115,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver if (c != Conversion.None) return c; } + if (resolveResult is ThrowResolveResult) { + return Conversion.ThrowExpressionConversion; + } if (allowUserDefined && allowTuple) { // if allowUserDefined and allowTuple are true, we might as well use the cache c = ImplicitConversion(resolveResult.Type, toType); diff --git a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs index 0346e2502..6217398aa 100644 --- a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs @@ -203,7 +203,7 @@ namespace ICSharpCode.Decompiler.CSharp if (targetType.Kind == TypeKind.Unknown || targetType.Kind == TypeKind.Void || targetType.Kind == TypeKind.None) { return this; // don't attempt to insert cast to '?' or 'void' as these are not valid. } - if (Expression is ThrowExpression) { + if (Expression is ThrowExpression && allowImplicitConversion) { return this; // Throw expressions have no type and are implicitly convertible to any type } if (Expression is TupleExpression tupleExpr && targetType is TupleType targetTupleType diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index cacda3343..64608367e 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -326,6 +326,7 @@ + diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs index 49aa44fe0..031c0e00b 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs @@ -83,52 +83,5 @@ namespace ICSharpCode.Decompiler.IL.Transforms } return false; } - - /// - /// Handles NullCoalescingInstruction case 1: reference types. - /// - /// stloc s(valueInst) - /// if (comp(ldloc s == ldnull)) { - /// stloc s(fallbackInst) - /// } - /// => - /// stloc s(if.notnull(valueInst, fallbackInst)) - /// - /// ------------------- - /// - /// stloc obj(valueInst) - /// if (comp(ldloc obj == ldnull)) { - /// throw(...) - /// } - /// => - /// stloc obj(if.notnull(valueInst, throw(...))) - /// - bool TransformRefTypesA(Block block, int pos, StatementTransformContext context) - { - if (!(block.Instructions[pos] is StLoc stloc)) - return false; - if (stloc.Variable.Kind != VariableKind.StackSlot) - return false; - if (!block.Instructions[pos + 1].MatchIfInstruction(out var condition, out var trueInst)) - return false; - if (!(condition.MatchCompEquals(out var left, out var right) && left.MatchLdLoc(stloc.Variable) && right.MatchLdNull())) - return false; - trueInst = Block.Unwrap(trueInst); - if (trueInst.MatchStLoc(stloc.Variable, out var fallbackValue)) { - context.Step("NullCoalescingTransform (reference types)", stloc); - stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, fallbackValue); - block.Instructions.RemoveAt(pos + 1); // remove if instruction - ILInlining.InlineOneIfPossible(block, pos, false, context); - return true; - } else if (trueInst is Throw throwInst) { - context.Step("NullCoalescingTransform (throw expression)", stloc); - throwInst.resultType = StackType.O; - stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, throwInst); - block.Instructions.RemoveAt(pos + 1); // remove if instruction - ILInlining.InlineOneIfPossible(block, pos, false, context); - return true; - } - return false; - } } } diff --git a/ICSharpCode.Decompiler/Semantics/Conversion.cs b/ICSharpCode.Decompiler/Semantics/Conversion.cs index 49af18b3e..0e3ba13f5 100644 --- a/ICSharpCode.Decompiler/Semantics/Conversion.cs +++ b/ICSharpCode.Decompiler/Semantics/Conversion.cs @@ -75,7 +75,12 @@ namespace ICSharpCode.Decompiler.Semantics /// C# 'as' cast. /// public static readonly Conversion TryCast = new BuiltinConversion(false, 9); - + + /// + /// C# 7 throw expression being converted to an arbitrary type. + /// + public static readonly Conversion ThrowExpressionConversion = new BuiltinConversion(true, 10); + public static Conversion UserDefinedConversion(IMethod operatorMethod, bool isImplicit, Conversion conversionBeforeUserDefinedOperator, Conversion conversionAfterUserDefinedOperator, bool isLifted = false, bool isAmbiguous = false) { if (operatorMethod == null) @@ -227,6 +232,10 @@ namespace ICSharpCode.Decompiler.Semantics public override bool IsTryCast { get { return type == 9; } } + + public override bool IsThrowExpressionConversion { + get { return type == 10; } + } public override string ToString() { @@ -257,6 +266,8 @@ namespace ICSharpCode.Decompiler.Semantics return "unboxing conversion"; case 9: return "try cast"; + case 10: + return "throw-expression conversion"; } return (isImplicit ? "implicit " : "explicit ") + name + " conversion"; } @@ -443,7 +454,11 @@ namespace ICSharpCode.Decompiler.Semantics public virtual bool IsTryCast { get { return false; } } - + + public virtual bool IsThrowExpressionConversion { + get { return false; } + } + public virtual bool IsIdentityConversion { get { return false; } } diff --git a/ICSharpCode.Decompiler/Semantics/ThrowResolveResult.cs b/ICSharpCode.Decompiler/Semantics/ThrowResolveResult.cs new file mode 100644 index 000000000..518e38e4f --- /dev/null +++ b/ICSharpCode.Decompiler/Semantics/ThrowResolveResult.cs @@ -0,0 +1,29 @@ +// Copyright (c) 2018 Daniel Grunwald +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using ICSharpCode.Decompiler.TypeSystem; + +namespace ICSharpCode.Decompiler.Semantics +{ + class ThrowResolveResult : ResolveResult + { + public ThrowResolveResult() : base(SpecialType.NoType) + { + } + } +} From 62c4635cb8f1e44bb0c176c3b7a2a9663eb45f04 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 10 May 2019 08:59:11 +0200 Subject: [PATCH 003/221] Add DecompilerSettings.ThrowExpressions --- ICSharpCode.Decompiler/DecompilerSettings.cs | 20 +++++++++++++++++++- ILSpy/Properties/Resources.Designer.cs | 9 +++++++++ ILSpy/Properties/Resources.resx | 3 +++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index 163d58346..ae72802db 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -88,6 +88,7 @@ namespace ICSharpCode.Decompiler } if (languageVersion < CSharp.LanguageVersion.CSharp7) { outVariables = false; + throwExpressions = false; tupleTypes = false; tupleConversions = false; discards = false; @@ -117,7 +118,7 @@ namespace ICSharpCode.Decompiler if (introduceRefModifiersOnStructs || introduceReadonlyAndInModifiers || nonTrailingNamedArguments) return CSharp.LanguageVersion.CSharp7_2; // C# 7.1 missing - if (outVariables || tupleTypes || tupleConversions || discards || localFunctions) + if (outVariables || throwExpressions || tupleTypes || tupleConversions || discards || localFunctions) return CSharp.LanguageVersion.CSharp7; if (awaitInCatchFinally || useExpressionBodyForCalculatedGetterOnlyProperties || nullPropagation || stringInterpolation || dictionaryInitializers || extensionMethodsInCollectionInitializers) @@ -856,6 +857,23 @@ namespace ICSharpCode.Decompiler } } + bool throwExpressions = true; + + /// + /// Gets/Sets whether throw expressions should be used. + /// + [Category("C# 7.0 / VS 2017")] + [Description("DecompilerSettings.UseThrowExpressions")] + public bool ThrowExpressions { + get { return throwExpressions; } + set { + if (throwExpressions != value) { + throwExpressions = value; + OnPropertyChanged(); + } + } + } + bool tupleConversions = true; /// diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index 5c86e06e2..59d99b15d 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -908,6 +908,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Use throw expressions. + /// + public static string DecompilerSettings_UseThrowExpressions { + get { + return ResourceManager.GetString("DecompilerSettings.UseThrowExpressions", resourceCulture); + } + } + /// /// Looks up a localized string similar to Use tuple type syntax. /// diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index 0128389d9..f3a237aec 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -729,4 +729,7 @@ Entity could not be resolved. Cannot analyze entities from missing assembly references. Add the missing reference and try again. + + Use throw expressions + \ No newline at end of file From c6f4f77b57776b928d69bb68f8597bd55f514846 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 10 May 2019 08:59:55 +0200 Subject: [PATCH 004/221] Reorder code in NullCoalescingTransform.cs --- .../IL/Transforms/NullCoalescingTransform.cs | 43 +++++++++---------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs index 2a0ce4db8..2f534ef03 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs @@ -39,22 +39,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// Handles NullCoalescingInstruction case 1: reference types. - /// - /// stloc s(valueInst) - /// if (comp(ldloc s == ldnull)) { - /// stloc s(fallbackInst) - /// } - /// => - /// stloc s(if.notnull(valueInst, fallbackInst)) - /// - /// ------------------- - /// - /// stloc obj(valueInst) - /// if (comp(ldloc obj == ldnull)) { - /// throw(...) - /// } - /// => - /// stloc obj(if.notnull(valueInst, throw(...))) /// bool TransformRefTypes(Block block, int pos, StatementTransformContext context) { @@ -67,19 +51,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!(condition.MatchCompEquals(out var left, out var right) && left.MatchLdLoc(stloc.Variable) && right.MatchLdNull())) return false; trueInst = Block.Unwrap(trueInst); + // stloc s(valueInst) + // if (comp(ldloc s == ldnull)) { + // stloc s(fallbackInst) + // } + // => + // stloc s(if.notnull(valueInst, fallbackInst)) if (trueInst.MatchStLoc(stloc.Variable, out var fallbackValue)) { context.Step("NullCoalescingTransform: simple (reference types)", stloc); stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, fallbackValue); block.Instructions.RemoveAt(pos + 1); // remove if instruction ILInlining.InlineOneIfPossible(block, pos, InliningOptions.None, context); return true; - } else if (trueInst is Throw throwInst) { - context.Step("NullCoalescingTransform (throw expression)", stloc); - throwInst.resultType = StackType.O; - stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, throwInst); - block.Instructions.RemoveAt(pos + 1); // remove if instruction - ILInlining.InlineOneIfPossible(block, pos, InliningOptions.None, context); - return true; } // sometimes the compiler generates: // stloc s(valueInst) @@ -99,6 +82,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms ILInlining.InlineOneIfPossible(block, pos, InliningOptions.None, context); return true; } + // stloc obj(valueInst) + // if (comp(ldloc obj == ldnull)) { + // throw(...) + // } + // => + // stloc obj(if.notnull(valueInst, throw(...))) + if (context.Settings.ThrowExpressions && trueInst is Throw throwInst) { + context.Step("NullCoalescingTransform (throw expression)", stloc); + throwInst.resultType = StackType.O; + stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, throwInst); + block.Instructions.RemoveAt(pos + 1); // remove if instruction + ILInlining.InlineOneIfPossible(block, pos, InliningOptions.None, context); + return true; + } return false; } } From 49586c77cbb6a751c8703e892c33f97206ab270f Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 13 Jun 2019 20:06:38 +0200 Subject: [PATCH 005/221] Fix #1546: Fix PresentationOptionsAttributeHandler --- ILSpy.BamlDecompiler.Tests/BamlTestRunner.cs | 6 ++++ .../Cases/Issue1546.xaml | 35 +++++++++++++++++++ .../ILSpy.BamlDecompiler.Tests.csproj | 3 ++ .../Handlers/Blocks/KeyElementStartHandler.cs | 2 +- .../ConstructorParameterTypeHandler.cs | 2 +- .../Handlers/Records/DefAttributeHandler.cs | 2 +- .../Records/DefAttributeKeyStringHandler.cs | 2 +- .../Records/DefAttributeKeyTypeHandler.cs | 4 +-- .../Handlers/Records/LiteralContentHandler.cs | 2 +- .../Records/OptimizedStaticResourceHandler.cs | 4 +-- .../PresentationOptionsAttributeHandler.cs | 2 +- .../Handlers/Records/PropertyHandler.cs | 2 +- .../Records/PropertyTypeReferenceHandler.cs | 2 +- .../Rewrite/AttributeRewritePass.cs | 2 +- .../Rewrite/ConnectionIdRewritePass.cs | 2 +- .../Rewrite/MarkupExtensionRewritePass.cs | 2 +- .../Rewrite/XClassRewritePass.cs | 4 +-- ILSpy.BamlDecompiler/XamlContext.cs | 14 +++++--- 18 files changed, 70 insertions(+), 22 deletions(-) create mode 100644 ILSpy.BamlDecompiler.Tests/Cases/Issue1546.xaml diff --git a/ILSpy.BamlDecompiler.Tests/BamlTestRunner.cs b/ILSpy.BamlDecompiler.Tests/BamlTestRunner.cs index be9039601..b5cb07263 100644 --- a/ILSpy.BamlDecompiler.Tests/BamlTestRunner.cs +++ b/ILSpy.BamlDecompiler.Tests/BamlTestRunner.cs @@ -107,6 +107,12 @@ namespace ILSpy.BamlDecompiler.Tests RunTest("cases/issue1435"); } + [Test] + public void Issue1546() + { + RunTest("cases/issue1546"); + } + #region RunTest void RunTest(string name) { diff --git a/ILSpy.BamlDecompiler.Tests/Cases/Issue1546.xaml b/ILSpy.BamlDecompiler.Tests/Cases/Issue1546.xaml new file mode 100644 index 000000000..c09ce5b77 --- /dev/null +++ b/ILSpy.BamlDecompiler.Tests/Cases/Issue1546.xaml @@ -0,0 +1,35 @@ + + #f1f1f1 + #2d2d30 + #3f3f41 + #007acc + #333337 + #3f3f3f + #999999 + #686868 + #9e9e9e + #f1f1f1 + #2d2d30 + #3f3f41 + #b20000 + #990000 + #009700 + #007400 + #1c97ea + #007acc + #333337 + #3f3f3f + #999999 + #686868 + #9e9e9e + + + + + + #FFD1D1D1 + + #FFA3A3A3 + + #FF747474 + \ No newline at end of file diff --git a/ILSpy.BamlDecompiler.Tests/ILSpy.BamlDecompiler.Tests.csproj b/ILSpy.BamlDecompiler.Tests/ILSpy.BamlDecompiler.Tests.csproj index 198bbe621..5971fd32c 100644 --- a/ILSpy.BamlDecompiler.Tests/ILSpy.BamlDecompiler.Tests.csproj +++ b/ILSpy.BamlDecompiler.Tests/ILSpy.BamlDecompiler.Tests.csproj @@ -74,6 +74,9 @@ MSBuild:Compile + + MSBuild:Compile + Designer diff --git a/ILSpy.BamlDecompiler/Handlers/Blocks/KeyElementStartHandler.cs b/ILSpy.BamlDecompiler/Handlers/Blocks/KeyElementStartHandler.cs index 12d0c22fe..f5844bd41 100644 --- a/ILSpy.BamlDecompiler/Handlers/Blocks/KeyElementStartHandler.cs +++ b/ILSpy.BamlDecompiler/Handlers/Blocks/KeyElementStartHandler.cs @@ -38,7 +38,7 @@ namespace ILSpy.BamlDecompiler.Handlers { var key = (XamlResourceKey)node.Annotation; var bamlElem = new BamlElement(node); - bamlElem.Xaml = new XElement(ctx.GetXamlNsName("Key", parent.Xaml)); + bamlElem.Xaml = new XElement(ctx.GetKnownNamespace("Key", XamlContext.KnownNamespace_Xaml, parent.Xaml)); parent.Xaml.Element.Add(bamlElem.Xaml.Element); key.KeyElement = bamlElem; base.Translate(ctx, node, bamlElem); diff --git a/ILSpy.BamlDecompiler/Handlers/Records/ConstructorParameterTypeHandler.cs b/ILSpy.BamlDecompiler/Handlers/Records/ConstructorParameterTypeHandler.cs index 7bc103727..278586687 100644 --- a/ILSpy.BamlDecompiler/Handlers/Records/ConstructorParameterTypeHandler.cs +++ b/ILSpy.BamlDecompiler/Handlers/Records/ConstructorParameterTypeHandler.cs @@ -31,7 +31,7 @@ namespace ILSpy.BamlDecompiler.Handlers { public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (ConstructorParameterTypeRecord)((BamlRecordNode)node).Record; - var elem = new XElement(ctx.GetXamlNsName("TypeExtension", parent.Xaml)); + var elem = new XElement(ctx.GetKnownNamespace("TypeExtension", XamlContext.KnownNamespace_Xaml, parent.Xaml)); elem.AddAnnotation(ctx.ResolveType(0xfd4d)); // Known type - TypeExtension var bamlElem = new BamlElement(node); diff --git a/ILSpy.BamlDecompiler/Handlers/Records/DefAttributeHandler.cs b/ILSpy.BamlDecompiler/Handlers/Records/DefAttributeHandler.cs index 221c29aa3..8b141d002 100644 --- a/ILSpy.BamlDecompiler/Handlers/Records/DefAttributeHandler.cs +++ b/ILSpy.BamlDecompiler/Handlers/Records/DefAttributeHandler.cs @@ -31,7 +31,7 @@ namespace ILSpy.BamlDecompiler.Handlers { var record = (DefAttributeRecord)((BamlRecordNode)node).Record; var attrName = ctx.ResolveString(record.NameId); - parent.Xaml.Element.Add(new XAttribute(ctx.GetXamlNsName(attrName), record.Value)); + parent.Xaml.Element.Add(new XAttribute(ctx.GetKnownNamespace(attrName, XamlContext.KnownNamespace_Xaml), record.Value)); return null; } diff --git a/ILSpy.BamlDecompiler/Handlers/Records/DefAttributeKeyStringHandler.cs b/ILSpy.BamlDecompiler/Handlers/Records/DefAttributeKeyStringHandler.cs index b92a5588a..0068ba4ac 100644 --- a/ILSpy.BamlDecompiler/Handlers/Records/DefAttributeKeyStringHandler.cs +++ b/ILSpy.BamlDecompiler/Handlers/Records/DefAttributeKeyStringHandler.cs @@ -38,7 +38,7 @@ namespace ILSpy.BamlDecompiler.Handlers { var key = (XamlResourceKey)node.Annotation; var bamlElem = new BamlElement(node); - bamlElem.Xaml = new XElement(ctx.GetXamlNsName("Key", parent.Xaml)); + bamlElem.Xaml = new XElement(ctx.GetKnownNamespace("Key", XamlContext.KnownNamespace_Xaml, parent.Xaml)); parent.Xaml.Element.Add(bamlElem.Xaml.Element); bamlElem.Xaml.Element.Value = ctx.ResolveString(record.ValueId); key.KeyElement = bamlElem; diff --git a/ILSpy.BamlDecompiler/Handlers/Records/DefAttributeKeyTypeHandler.cs b/ILSpy.BamlDecompiler/Handlers/Records/DefAttributeKeyTypeHandler.cs index 0b45d8583..8b6080248 100644 --- a/ILSpy.BamlDecompiler/Handlers/Records/DefAttributeKeyTypeHandler.cs +++ b/ILSpy.BamlDecompiler/Handlers/Records/DefAttributeKeyTypeHandler.cs @@ -40,10 +40,10 @@ namespace ILSpy.BamlDecompiler.Handlers { var key = (XamlResourceKey)node.Annotation; var bamlElem = new BamlElement(node); - bamlElem.Xaml = new XElement(ctx.GetXamlNsName("Key", parent.Xaml)); + bamlElem.Xaml = new XElement(ctx.GetKnownNamespace("Key", XamlContext.KnownNamespace_Xaml, parent.Xaml)); parent.Xaml.Element.Add(bamlElem.Xaml.Element); - var typeElem = new XElement(ctx.GetXamlNsName("TypeExtension", parent.Xaml)); + var typeElem = new XElement(ctx.GetKnownNamespace("TypeExtension", XamlContext.KnownNamespace_Xaml, parent.Xaml)); typeElem.AddAnnotation(ctx.ResolveType(0xfd4d)); // Known type - TypeExtension typeElem.Add(new XElement(ctx.GetPseudoName("Ctor"), typeName)); bamlElem.Xaml.Element.Add(typeElem); diff --git a/ILSpy.BamlDecompiler/Handlers/Records/LiteralContentHandler.cs b/ILSpy.BamlDecompiler/Handlers/Records/LiteralContentHandler.cs index d5b0e73aa..ab36554b4 100644 --- a/ILSpy.BamlDecompiler/Handlers/Records/LiteralContentHandler.cs +++ b/ILSpy.BamlDecompiler/Handlers/Records/LiteralContentHandler.cs @@ -30,7 +30,7 @@ namespace ILSpy.BamlDecompiler.Handlers { public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (LiteralContentRecord)((BamlRecordNode)node).Record; - var elem = new XElement(ctx.GetXamlNsName("XData", parent.Xaml)); + var elem = new XElement(ctx.GetKnownNamespace("XData", XamlContext.KnownNamespace_Xaml, parent.Xaml)); var content = XElement.Parse(record.Value); elem.Add(content); diff --git a/ILSpy.BamlDecompiler/Handlers/Records/OptimizedStaticResourceHandler.cs b/ILSpy.BamlDecompiler/Handlers/Records/OptimizedStaticResourceHandler.cs index 64a54695b..463acedc2 100644 --- a/ILSpy.BamlDecompiler/Handlers/Records/OptimizedStaticResourceHandler.cs +++ b/ILSpy.BamlDecompiler/Handlers/Records/OptimizedStaticResourceHandler.cs @@ -43,7 +43,7 @@ namespace ILSpy.BamlDecompiler.Handlers { if (record.IsType) { var value = ctx.ResolveType(record.ValueId); - var typeElem = new XElement(ctx.GetXamlNsName("TypeExtension", parent.Xaml)); + var typeElem = new XElement(ctx.GetKnownNamespace("TypeExtension", XamlContext.KnownNamespace_Xaml, parent.Xaml)); typeElem.AddAnnotation(ctx.ResolveType(0xfd4d)); // Known type - TypeExtension typeElem.Add(new XElement(ctx.GetPseudoName("Ctor"), ctx.ToString(parent.Xaml, value))); key = typeElem; @@ -82,7 +82,7 @@ namespace ILSpy.BamlDecompiler.Handlers { attrName = ctx.ToString(parent.Xaml, xName); } - var staticElem = new XElement(ctx.GetXamlNsName("StaticExtension", parent.Xaml)); + var staticElem = new XElement(ctx.GetKnownNamespace("StaticExtension", XamlContext.KnownNamespace_Xaml, parent.Xaml)); staticElem.AddAnnotation(ctx.ResolveType(0xfda6)); // Known type - StaticExtension staticElem.Add(new XElement(ctx.GetPseudoName("Ctor"), attrName)); key = staticElem; diff --git a/ILSpy.BamlDecompiler/Handlers/Records/PresentationOptionsAttributeHandler.cs b/ILSpy.BamlDecompiler/Handlers/Records/PresentationOptionsAttributeHandler.cs index 01d648ef7..e0f3f728a 100644 --- a/ILSpy.BamlDecompiler/Handlers/Records/PresentationOptionsAttributeHandler.cs +++ b/ILSpy.BamlDecompiler/Handlers/Records/PresentationOptionsAttributeHandler.cs @@ -31,7 +31,7 @@ namespace ILSpy.BamlDecompiler.Handlers { var record = (PresentationOptionsAttributeRecord)((BamlRecordNode)node).Record; var attrName = ctx.ResolveString(record.NameId); - var attr = new XAttribute(ctx.GetXamlNsName(attrName, parent.Xaml), record.Value); + var attr = new XAttribute(ctx.GetKnownNamespace(attrName, XamlContext.KnownNamespace_PresentationOptions, parent.Xaml), record.Value); parent.Xaml.Element.Add(attr); return null; diff --git a/ILSpy.BamlDecompiler/Handlers/Records/PropertyHandler.cs b/ILSpy.BamlDecompiler/Handlers/Records/PropertyHandler.cs index c084b76e6..822fc0627 100644 --- a/ILSpy.BamlDecompiler/Handlers/Records/PropertyHandler.cs +++ b/ILSpy.BamlDecompiler/Handlers/Records/PropertyHandler.cs @@ -45,7 +45,7 @@ namespace ILSpy.BamlDecompiler.Handlers { return new XAttribute(xamlProp.ToXName(ctx, parent.Xaml, true), value); if (xamlProp.PropertyName == "Name" && elemType.ResolvedType.GetDefinition()?.ParentModule.IsMainModule == true) - return new XAttribute(ctx.GetXamlNsName("Name"), value); + return new XAttribute(ctx.GetKnownNamespace("Name", XamlContext.KnownNamespace_Xaml), value); return new XAttribute(xamlProp.ToXName(ctx, parent.Xaml, false), value); } diff --git a/ILSpy.BamlDecompiler/Handlers/Records/PropertyTypeReferenceHandler.cs b/ILSpy.BamlDecompiler/Handlers/Records/PropertyTypeReferenceHandler.cs index 45a94f91e..aa29642af 100644 --- a/ILSpy.BamlDecompiler/Handlers/Records/PropertyTypeReferenceHandler.cs +++ b/ILSpy.BamlDecompiler/Handlers/Records/PropertyTypeReferenceHandler.cs @@ -42,7 +42,7 @@ namespace ILSpy.BamlDecompiler.Handlers { elem.Xaml.Element.AddAnnotation(elemAttr); parent.Xaml.Element.Add(elem.Xaml.Element); - var typeElem = new XElement(ctx.GetXamlNsName("TypeExtension", parent.Xaml)); + var typeElem = new XElement(ctx.GetKnownNamespace("TypeExtension", XamlContext.KnownNamespace_Xaml, parent.Xaml)); typeElem.AddAnnotation(ctx.ResolveType(0xfd4d)); // Known type - TypeExtension typeElem.Add(new XElement(ctx.GetPseudoName("Ctor"), typeName)); elem.Xaml.Element.Add(typeElem); diff --git a/ILSpy.BamlDecompiler/Rewrite/AttributeRewritePass.cs b/ILSpy.BamlDecompiler/Rewrite/AttributeRewritePass.cs index b8e902119..a8c5ac22b 100644 --- a/ILSpy.BamlDecompiler/Rewrite/AttributeRewritePass.cs +++ b/ILSpy.BamlDecompiler/Rewrite/AttributeRewritePass.cs @@ -29,7 +29,7 @@ namespace ILSpy.BamlDecompiler.Rewrite { XName key; public void Run(XamlContext ctx, XDocument document) { - key = ctx.GetXamlNsName("Key"); + key = ctx.GetKnownNamespace("Key", XamlContext.KnownNamespace_Xaml); bool doWork; do { diff --git a/ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs b/ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs index fe2912ff9..82ab77302 100644 --- a/ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs +++ b/ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs @@ -59,7 +59,7 @@ namespace ILSpy.BamlDecompiler.Rewrite { var result = new List<(LongSet, EventRegistration[])>(); - var xClass = document.Root.Elements().First().Attribute(ctx.GetXamlNsName("Class")); + var xClass = document.Root.Elements().First().Attribute(ctx.GetKnownNamespace("Class", XamlContext.KnownNamespace_Xaml)); if (xClass == null) return result; diff --git a/ILSpy.BamlDecompiler/Rewrite/MarkupExtensionRewritePass.cs b/ILSpy.BamlDecompiler/Rewrite/MarkupExtensionRewritePass.cs index e05eee62e..e2411672f 100644 --- a/ILSpy.BamlDecompiler/Rewrite/MarkupExtensionRewritePass.cs +++ b/ILSpy.BamlDecompiler/Rewrite/MarkupExtensionRewritePass.cs @@ -31,7 +31,7 @@ namespace ILSpy.BamlDecompiler.Rewrite { XName ctor; public void Run(XamlContext ctx, XDocument document) { - key = ctx.GetXamlNsName("Key"); + key = ctx.GetKnownNamespace("Key", XamlContext.KnownNamespace_Xaml); ctor = ctx.GetPseudoName("Ctor"); bool doWork; diff --git a/ILSpy.BamlDecompiler/Rewrite/XClassRewritePass.cs b/ILSpy.BamlDecompiler/Rewrite/XClassRewritePass.cs index 332454d17..2e2d25e59 100644 --- a/ILSpy.BamlDecompiler/Rewrite/XClassRewritePass.cs +++ b/ILSpy.BamlDecompiler/Rewrite/XClassRewritePass.cs @@ -48,11 +48,11 @@ namespace ILSpy.BamlDecompiler.Rewrite { elem.Name = xamlType.ToXName(ctx); - var attrName = ctx.GetXamlNsName("Class", elem); + var attrName = ctx.GetKnownNamespace("Class", XamlContext.KnownNamespace_Xaml, elem); var attrs = elem.Attributes().ToList(); if (typeDef.Accessibility != ICSharpCode.Decompiler.TypeSystem.Accessibility.Public) { - var classModifierName = ctx.GetXamlNsName("ClassModifier", elem); + var classModifierName = ctx.GetKnownNamespace("ClassModifier", XamlContext.KnownNamespace_Xaml, elem); attrs.Insert(0, new XAttribute(classModifierName, "internal")); } attrs.Insert(0, new XAttribute(attrName, type.ResolvedType.FullName)); diff --git a/ILSpy.BamlDecompiler/XamlContext.cs b/ILSpy.BamlDecompiler/XamlContext.cs index 6afe81eee..50063cf6e 100644 --- a/ILSpy.BamlDecompiler/XamlContext.cs +++ b/ILSpy.BamlDecompiler/XamlContext.cs @@ -164,6 +164,10 @@ namespace ILSpy.BamlDecompiler { return ns; } + public const string KnownNamespace_Xaml = "http://schemas.microsoft.com/winfx/2006/xaml"; + public const string KnownNamespace_Presentation = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"; + public const string KnownNamespace_PresentationOptions = "http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"; + public string TryGetXmlNamespace(IModule assembly, string typeNamespace) { if (assembly == null) return null; @@ -184,16 +188,16 @@ namespace ILSpy.BamlDecompiler { possibleXmlNs.Add(xmlNs); } - if (possibleXmlNs.Contains("http://schemas.microsoft.com/winfx/2006/xaml/presentation")) - return "http://schemas.microsoft.com/winfx/2006/xaml/presentation"; + if (possibleXmlNs.Contains(KnownNamespace_Presentation)) + return KnownNamespace_Presentation; return possibleXmlNs.FirstOrDefault(); } - public XName GetXamlNsName(string name, XElement elem = null) { - var xNs = GetXmlNamespace("http://schemas.microsoft.com/winfx/2006/xaml"); + public XName GetKnownNamespace(string name, string xmlNamespace, XElement context = null) { + var xNs = GetXmlNamespace(xmlNamespace); XName xName; - if (elem != null && xNs == elem.GetDefaultNamespace()) + if (context != null && xNs == context.GetDefaultNamespace()) xName = name; else xName = xNs + name; From d965e6aa9e47c88ef5d2fc67b54873226845fb73 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 14 Jun 2019 09:17:13 +0200 Subject: [PATCH 006/221] Fix #1547: Fully qualify attached properties in styles. --- .../TypeSystem/TypeSystemExtensions.cs | 4 +- ILSpy.BamlDecompiler.Tests/BamlTestRunner.cs | 6 + .../Cases/AvalonDockCommon.xaml | 4 +- .../Cases/Issue1547.xaml | 24 + .../Cases/Issue1547.xaml.cs | 27 + .../ILSpy.BamlDecompiler.Tests.csproj | 4 + ILSpy.BamlDecompiler/Baml/test.cs | 2325 ----------------- .../Handlers/Records/PropertyCustomHandler.cs | 14 +- .../Records/PropertyTypeReferenceHandler.cs | 15 + 9 files changed, 93 insertions(+), 2330 deletions(-) create mode 100644 ILSpy.BamlDecompiler.Tests/Cases/Issue1547.xaml create mode 100644 ILSpy.BamlDecompiler.Tests/Cases/Issue1547.xaml.cs delete mode 100644 ILSpy.BamlDecompiler/Baml/test.cs diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs index bbba0455b..34631f0a1 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs @@ -521,9 +521,9 @@ namespace ICSharpCode.Decompiler.TypeSystem return SpecialType.UnknownType; } - public static bool FullNameIs(this IMethod method, string type, string name) + public static bool FullNameIs(this IMember member, string type, string name) { - return method.Name == name && method.DeclaringType?.FullName == type; + return member.Name == name && member.DeclaringType?.FullName == type; } public static KnownAttribute IsBuiltinAttribute(this ITypeDefinition type) diff --git a/ILSpy.BamlDecompiler.Tests/BamlTestRunner.cs b/ILSpy.BamlDecompiler.Tests/BamlTestRunner.cs index b5cb07263..682fc679e 100644 --- a/ILSpy.BamlDecompiler.Tests/BamlTestRunner.cs +++ b/ILSpy.BamlDecompiler.Tests/BamlTestRunner.cs @@ -113,6 +113,12 @@ namespace ILSpy.BamlDecompiler.Tests RunTest("cases/issue1546"); } + [Test] + public void Issue1547() + { + RunTest("cases/issue1547"); + } + #region RunTest void RunTest(string name) { diff --git a/ILSpy.BamlDecompiler.Tests/Cases/AvalonDockCommon.xaml b/ILSpy.BamlDecompiler.Tests/Cases/AvalonDockCommon.xaml index bcb18aed2..31d456472 100644 --- a/ILSpy.BamlDecompiler.Tests/Cases/AvalonDockCommon.xaml +++ b/ILSpy.BamlDecompiler.Tests/Cases/AvalonDockCommon.xaml @@ -35,8 +35,8 @@ - - + + diff --git a/ILSpy.BamlDecompiler.Tests/Cases/Issue1547.xaml b/ILSpy.BamlDecompiler.Tests/Cases/Issue1547.xaml new file mode 100644 index 000000000..8943fcfa3 --- /dev/null +++ b/ILSpy.BamlDecompiler.Tests/Cases/Issue1547.xaml @@ -0,0 +1,24 @@ + + + + + + + + + + diff --git a/ILSpy.BamlDecompiler.Tests/Cases/Issue1547.xaml.cs b/ILSpy.BamlDecompiler.Tests/Cases/Issue1547.xaml.cs new file mode 100644 index 000000000..f9be09f20 --- /dev/null +++ b/ILSpy.BamlDecompiler.Tests/Cases/Issue1547.xaml.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Shapes; + +namespace ILSpy.BamlDecompiler.Tests.Cases +{ + /// + /// Interaction logic for Issue1547.xaml + /// + public partial class Issue1547 : Window + { + public Issue1547() + { + InitializeComponent(); + } + } +} diff --git a/ILSpy.BamlDecompiler.Tests/ILSpy.BamlDecompiler.Tests.csproj b/ILSpy.BamlDecompiler.Tests/ILSpy.BamlDecompiler.Tests.csproj index 5971fd32c..0f4829272 100644 --- a/ILSpy.BamlDecompiler.Tests/ILSpy.BamlDecompiler.Tests.csproj +++ b/ILSpy.BamlDecompiler.Tests/ILSpy.BamlDecompiler.Tests.csproj @@ -47,6 +47,7 @@ AttachedEvent.xaml + MyControl.xaml @@ -77,6 +78,9 @@ MSBuild:Compile + + MSBuild:Compile + Designer diff --git a/ILSpy.BamlDecompiler/Baml/test.cs b/ILSpy.BamlDecompiler/Baml/test.cs deleted file mode 100644 index c2727645f..000000000 --- a/ILSpy.BamlDecompiler/Baml/test.cs +++ /dev/null @@ -1,2325 +0,0 @@ - internal enum KnownTypes : short { - Unknown = 0, - AccessText = 1, - AdornedElementPlaceholder = 2, - Adorner = 3, - AdornerDecorator = 4, - AdornerLayer = 5, - AffineTransform3D = 6, - AmbientLight = 7, - AnchoredBlock = 8, - Animatable = 9, - AnimationClock = 10, - AnimationTimeline = 11, - Application = 12, - ArcSegment = 13, - ArrayExtension = 14, - AxisAngleRotation3D = 15, - BaseIListConverter = 16, - BeginStoryboard = 17, - BevelBitmapEffect = 18, - BezierSegment = 19, - Binding = 20, - BindingBase = 21, - BindingExpression = 22, - BindingExpressionBase = 23, - BindingListCollectionView = 24, - BitmapDecoder = 25, - BitmapEffect = 26, - BitmapEffectCollection = 27, - BitmapEffectGroup = 28, - BitmapEffectInput = 29, - BitmapEncoder = 30, - BitmapFrame = 31, - BitmapImage = 32, - BitmapMetadata = 33, - BitmapPalette = 34, - BitmapSource = 35, - Block = 36, - BlockUIContainer = 37, - BlurBitmapEffect = 38, - BmpBitmapDecoder = 39, - BmpBitmapEncoder = 40, - Bold = 41, - BoolIListConverter = 42, - Boolean = 43, - BooleanAnimationBase = 44, - BooleanAnimationUsingKeyFrames = 45, - BooleanConverter = 46, - BooleanKeyFrame = 47, - BooleanKeyFrameCollection = 48, - BooleanToVisibilityConverter = 49, - Border = 50, - BorderGapMaskConverter = 51, - Brush = 52, - BrushConverter = 53, - BulletDecorator = 54, - Button = 55, - ButtonBase = 56, - Byte = 57, - ByteAnimation = 58, - ByteAnimationBase = 59, - ByteAnimationUsingKeyFrames = 60, - ByteConverter = 61, - ByteKeyFrame = 62, - ByteKeyFrameCollection = 63, - CachedBitmap = 64, - Camera = 65, - Canvas = 66, - Char = 67, - CharAnimationBase = 68, - CharAnimationUsingKeyFrames = 69, - CharConverter = 70, - CharIListConverter = 71, - CharKeyFrame = 72, - CharKeyFrameCollection = 73, - CheckBox = 74, - Clock = 75, - ClockController = 76, - ClockGroup = 77, - CollectionContainer = 78, - CollectionView = 79, - CollectionViewSource = 80, - Color = 81, - ColorAnimation = 82, - ColorAnimationBase = 83, - ColorAnimationUsingKeyFrames = 84, - ColorConvertedBitmap = 85, - ColorConvertedBitmapExtension = 86, - ColorConverter = 87, - ColorKeyFrame = 88, - ColorKeyFrameCollection = 89, - ColumnDefinition = 90, - CombinedGeometry = 91, - ComboBox = 92, - ComboBoxItem = 93, - CommandConverter = 94, - ComponentResourceKey = 95, - ComponentResourceKeyConverter = 96, - CompositionTarget = 97, - Condition = 98, - ContainerVisual = 99, - ContentControl = 100, - ContentElement = 101, - ContentPresenter = 102, - ContentPropertyAttribute = 103, - ContentWrapperAttribute = 104, - ContextMenu = 105, - ContextMenuService = 106, - Control = 107, - ControlTemplate = 108, - ControllableStoryboardAction = 109, - CornerRadius = 110, - CornerRadiusConverter = 111, - CroppedBitmap = 112, - CultureInfo = 113, - CultureInfoConverter = 114, - CultureInfoIetfLanguageTagConverter = 115, - Cursor = 116, - CursorConverter = 117, - DashStyle = 118, - DataChangedEventManager = 119, - DataTemplate = 120, - DataTemplateKey = 121, - DataTrigger = 122, - DateTime = 123, - DateTimeConverter = 124, - DateTimeConverter2 = 125, - Decimal = 126, - DecimalAnimation = 127, - DecimalAnimationBase = 128, - DecimalAnimationUsingKeyFrames = 129, - DecimalConverter = 130, - DecimalKeyFrame = 131, - DecimalKeyFrameCollection = 132, - Decorator = 133, - DefinitionBase = 134, - DependencyObject = 135, - DependencyProperty = 136, - DependencyPropertyConverter = 137, - DialogResultConverter = 138, - DiffuseMaterial = 139, - DirectionalLight = 140, - DiscreteBooleanKeyFrame = 141, - DiscreteByteKeyFrame = 142, - DiscreteCharKeyFrame = 143, - DiscreteColorKeyFrame = 144, - DiscreteDecimalKeyFrame = 145, - DiscreteDoubleKeyFrame = 146, - DiscreteInt16KeyFrame = 147, - DiscreteInt32KeyFrame = 148, - DiscreteInt64KeyFrame = 149, - DiscreteMatrixKeyFrame = 150, - DiscreteObjectKeyFrame = 151, - DiscretePoint3DKeyFrame = 152, - DiscretePointKeyFrame = 153, - DiscreteQuaternionKeyFrame = 154, - DiscreteRectKeyFrame = 155, - DiscreteRotation3DKeyFrame = 156, - DiscreteSingleKeyFrame = 157, - DiscreteSizeKeyFrame = 158, - DiscreteStringKeyFrame = 159, - DiscreteThicknessKeyFrame = 160, - DiscreteVector3DKeyFrame = 161, - DiscreteVectorKeyFrame = 162, - DockPanel = 163, - DocumentPageView = 164, - DocumentReference = 165, - DocumentViewer = 166, - DocumentViewerBase = 167, - Double = 168, - DoubleAnimation = 169, - DoubleAnimationBase = 170, - DoubleAnimationUsingKeyFrames = 171, - DoubleAnimationUsingPath = 172, - DoubleCollection = 173, - DoubleCollectionConverter = 174, - DoubleConverter = 175, - DoubleIListConverter = 176, - DoubleKeyFrame = 177, - DoubleKeyFrameCollection = 178, - Drawing = 179, - DrawingBrush = 180, - DrawingCollection = 181, - DrawingContext = 182, - DrawingGroup = 183, - DrawingImage = 184, - DrawingVisual = 185, - DropShadowBitmapEffect = 186, - Duration = 187, - DurationConverter = 188, - DynamicResourceExtension = 189, - DynamicResourceExtensionConverter = 190, - Ellipse = 191, - EllipseGeometry = 192, - EmbossBitmapEffect = 193, - EmissiveMaterial = 194, - EnumConverter = 195, - EventManager = 196, - EventSetter = 197, - EventTrigger = 198, - Expander = 199, - Expression = 200, - ExpressionConverter = 201, - Figure = 202, - FigureLength = 203, - FigureLengthConverter = 204, - FixedDocument = 205, - FixedDocumentSequence = 206, - FixedPage = 207, - Floater = 208, - FlowDocument = 209, - FlowDocumentPageViewer = 210, - FlowDocumentReader = 211, - FlowDocumentScrollViewer = 212, - FocusManager = 213, - FontFamily = 214, - FontFamilyConverter = 215, - FontSizeConverter = 216, - FontStretch = 217, - FontStretchConverter = 218, - FontStyle = 219, - FontStyleConverter = 220, - FontWeight = 221, - FontWeightConverter = 222, - FormatConvertedBitmap = 223, - Frame = 224, - FrameworkContentElement = 225, - FrameworkElement = 226, - FrameworkElementFactory = 227, - FrameworkPropertyMetadata = 228, - FrameworkPropertyMetadataOptions = 229, - FrameworkRichTextComposition = 230, - FrameworkTemplate = 231, - FrameworkTextComposition = 232, - Freezable = 233, - GeneralTransform = 234, - GeneralTransformCollection = 235, - GeneralTransformGroup = 236, - Geometry = 237, - Geometry3D = 238, - GeometryCollection = 239, - GeometryConverter = 240, - GeometryDrawing = 241, - GeometryGroup = 242, - GeometryModel3D = 243, - GestureRecognizer = 244, - GifBitmapDecoder = 245, - GifBitmapEncoder = 246, - GlyphRun = 247, - GlyphRunDrawing = 248, - GlyphTypeface = 249, - Glyphs = 250, - GradientBrush = 251, - GradientStop = 252, - GradientStopCollection = 253, - Grid = 254, - GridLength = 255, - GridLengthConverter = 256, - GridSplitter = 257, - GridView = 258, - GridViewColumn = 259, - GridViewColumnHeader = 260, - GridViewHeaderRowPresenter = 261, - GridViewRowPresenter = 262, - GridViewRowPresenterBase = 263, - GroupBox = 264, - GroupItem = 265, - Guid = 266, - GuidConverter = 267, - GuidelineSet = 268, - HeaderedContentControl = 269, - HeaderedItemsControl = 270, - HierarchicalDataTemplate = 271, - HostVisual = 272, - Hyperlink = 273, - IAddChild = 274, - IAddChildInternal = 275, - ICommand = 276, - IComponentConnector = 277, - INameScope = 278, - IStyleConnector = 279, - IconBitmapDecoder = 280, - Image = 281, - ImageBrush = 282, - ImageDrawing = 283, - ImageMetadata = 284, - ImageSource = 285, - ImageSourceConverter = 286, - InPlaceBitmapMetadataWriter = 287, - InkCanvas = 288, - InkPresenter = 289, - Inline = 290, - InlineCollection = 291, - InlineUIContainer = 292, - InputBinding = 293, - InputDevice = 294, - InputLanguageManager = 295, - InputManager = 296, - InputMethod = 297, - InputScope = 298, - InputScopeConverter = 299, - InputScopeName = 300, - InputScopeNameConverter = 301, - Int16 = 302, - Int16Animation = 303, - Int16AnimationBase = 304, - Int16AnimationUsingKeyFrames = 305, - Int16Converter = 306, - Int16KeyFrame = 307, - Int16KeyFrameCollection = 308, - Int32 = 309, - Int32Animation = 310, - Int32AnimationBase = 311, - Int32AnimationUsingKeyFrames = 312, - Int32Collection = 313, - Int32CollectionConverter = 314, - Int32Converter = 315, - Int32KeyFrame = 316, - Int32KeyFrameCollection = 317, - Int32Rect = 318, - Int32RectConverter = 319, - Int64 = 320, - Int64Animation = 321, - Int64AnimationBase = 322, - Int64AnimationUsingKeyFrames = 323, - Int64Converter = 324, - Int64KeyFrame = 325, - Int64KeyFrameCollection = 326, - Italic = 327, - ItemCollection = 328, - ItemsControl = 329, - ItemsPanelTemplate = 330, - ItemsPresenter = 331, - JournalEntry = 332, - JournalEntryListConverter = 333, - JournalEntryUnifiedViewConverter = 334, - JpegBitmapDecoder = 335, - JpegBitmapEncoder = 336, - KeyBinding = 337, - KeyConverter = 338, - KeyGesture = 339, - KeyGestureConverter = 340, - KeySpline = 341, - KeySplineConverter = 342, - KeyTime = 343, - KeyTimeConverter = 344, - KeyboardDevice = 345, - Label = 346, - LateBoundBitmapDecoder = 347, - LengthConverter = 348, - Light = 349, - Line = 350, - LineBreak = 351, - LineGeometry = 352, - LineSegment = 353, - LinearByteKeyFrame = 354, - LinearColorKeyFrame = 355, - LinearDecimalKeyFrame = 356, - LinearDoubleKeyFrame = 357, - LinearGradientBrush = 358, - LinearInt16KeyFrame = 359, - LinearInt32KeyFrame = 360, - LinearInt64KeyFrame = 361, - LinearPoint3DKeyFrame = 362, - LinearPointKeyFrame = 363, - LinearQuaternionKeyFrame = 364, - LinearRectKeyFrame = 365, - LinearRotation3DKeyFrame = 366, - LinearSingleKeyFrame = 367, - LinearSizeKeyFrame = 368, - LinearThicknessKeyFrame = 369, - LinearVector3DKeyFrame = 370, - LinearVectorKeyFrame = 371, - List = 372, - ListBox = 373, - ListBoxItem = 374, - ListCollectionView = 375, - ListItem = 376, - ListView = 377, - ListViewItem = 378, - Localization = 379, - LostFocusEventManager = 380, - MarkupExtension = 381, - Material = 382, - MaterialCollection = 383, - MaterialGroup = 384, - Matrix = 385, - Matrix3D = 386, - Matrix3DConverter = 387, - MatrixAnimationBase = 388, - MatrixAnimationUsingKeyFrames = 389, - MatrixAnimationUsingPath = 390, - MatrixCamera = 391, - MatrixConverter = 392, - MatrixKeyFrame = 393, - MatrixKeyFrameCollection = 394, - MatrixTransform = 395, - MatrixTransform3D = 396, - MediaClock = 397, - MediaElement = 398, - MediaPlayer = 399, - MediaTimeline = 400, - Menu = 401, - MenuBase = 402, - MenuItem = 403, - MenuScrollingVisibilityConverter = 404, - MeshGeometry3D = 405, - Model3D = 406, - Model3DCollection = 407, - Model3DGroup = 408, - ModelVisual3D = 409, - ModifierKeysConverter = 410, - MouseActionConverter = 411, - MouseBinding = 412, - MouseDevice = 413, - MouseGesture = 414, - MouseGestureConverter = 415, - MultiBinding = 416, - MultiBindingExpression = 417, - MultiDataTrigger = 418, - MultiTrigger = 419, - NameScope = 420, - NavigationWindow = 421, - NullExtension = 422, - NullableBoolConverter = 423, - NullableConverter = 424, - NumberSubstitution = 425, - Object = 426, - ObjectAnimationBase = 427, - ObjectAnimationUsingKeyFrames = 428, - ObjectDataProvider = 429, - ObjectKeyFrame = 430, - ObjectKeyFrameCollection = 431, - OrthographicCamera = 432, - OuterGlowBitmapEffect = 433, - Page = 434, - PageContent = 435, - PageFunctionBase = 436, - Panel = 437, - Paragraph = 438, - ParallelTimeline = 439, - ParserContext = 440, - PasswordBox = 441, - Path = 442, - PathFigure = 443, - PathFigureCollection = 444, - PathFigureCollectionConverter = 445, - PathGeometry = 446, - PathSegment = 447, - PathSegmentCollection = 448, - PauseStoryboard = 449, - Pen = 450, - PerspectiveCamera = 451, - PixelFormat = 452, - PixelFormatConverter = 453, - PngBitmapDecoder = 454, - PngBitmapEncoder = 455, - Point = 456, - Point3D = 457, - Point3DAnimation = 458, - Point3DAnimationBase = 459, - Point3DAnimationUsingKeyFrames = 460, - Point3DCollection = 461, - Point3DCollectionConverter = 462, - Point3DConverter = 463, - Point3DKeyFrame = 464, - Point3DKeyFrameCollection = 465, - Point4D = 466, - Point4DConverter = 467, - PointAnimation = 468, - PointAnimationBase = 469, - PointAnimationUsingKeyFrames = 470, - PointAnimationUsingPath = 471, - PointCollection = 472, - PointCollectionConverter = 473, - PointConverter = 474, - PointIListConverter = 475, - PointKeyFrame = 476, - PointKeyFrameCollection = 477, - PointLight = 478, - PointLightBase = 479, - PolyBezierSegment = 480, - PolyLineSegment = 481, - PolyQuadraticBezierSegment = 482, - Polygon = 483, - Polyline = 484, - Popup = 485, - PresentationSource = 486, - PriorityBinding = 487, - PriorityBindingExpression = 488, - ProgressBar = 489, - ProjectionCamera = 490, - PropertyPath = 491, - PropertyPathConverter = 492, - QuadraticBezierSegment = 493, - Quaternion = 494, - QuaternionAnimation = 495, - QuaternionAnimationBase = 496, - QuaternionAnimationUsingKeyFrames = 497, - QuaternionConverter = 498, - QuaternionKeyFrame = 499, - QuaternionKeyFrameCollection = 500, - QuaternionRotation3D = 501, - RadialGradientBrush = 502, - RadioButton = 503, - RangeBase = 504, - Rect = 505, - Rect3D = 506, - Rect3DConverter = 507, - RectAnimation = 508, - RectAnimationBase = 509, - RectAnimationUsingKeyFrames = 510, - RectConverter = 511, - RectKeyFrame = 512, - RectKeyFrameCollection = 513, - Rectangle = 514, - RectangleGeometry = 515, - RelativeSource = 516, - RemoveStoryboard = 517, - RenderOptions = 518, - RenderTargetBitmap = 519, - RepeatBehavior = 520, - RepeatBehaviorConverter = 521, - RepeatButton = 522, - ResizeGrip = 523, - ResourceDictionary = 524, - ResourceKey = 525, - ResumeStoryboard = 526, - RichTextBox = 527, - RotateTransform = 528, - RotateTransform3D = 529, - Rotation3D = 530, - Rotation3DAnimation = 531, - Rotation3DAnimationBase = 532, - Rotation3DAnimationUsingKeyFrames = 533, - Rotation3DKeyFrame = 534, - Rotation3DKeyFrameCollection = 535, - RoutedCommand = 536, - RoutedEvent = 537, - RoutedEventConverter = 538, - RoutedUICommand = 539, - RoutingStrategy = 540, - RowDefinition = 541, - Run = 542, - RuntimeNamePropertyAttribute = 543, - SByte = 544, - SByteConverter = 545, - ScaleTransform = 546, - ScaleTransform3D = 547, - ScrollBar = 548, - ScrollContentPresenter = 549, - ScrollViewer = 550, - Section = 551, - SeekStoryboard = 552, - Selector = 553, - Separator = 554, - SetStoryboardSpeedRatio = 555, - Setter = 556, - SetterBase = 557, - Shape = 558, - Single = 559, - SingleAnimation = 560, - SingleAnimationBase = 561, - SingleAnimationUsingKeyFrames = 562, - SingleConverter = 563, - SingleKeyFrame = 564, - SingleKeyFrameCollection = 565, - Size = 566, - Size3D = 567, - Size3DConverter = 568, - SizeAnimation = 569, - SizeAnimationBase = 570, - SizeAnimationUsingKeyFrames = 571, - SizeConverter = 572, - SizeKeyFrame = 573, - SizeKeyFrameCollection = 574, - SkewTransform = 575, - SkipStoryboardToFill = 576, - Slider = 577, - SolidColorBrush = 578, - SoundPlayerAction = 579, - Span = 580, - SpecularMaterial = 581, - SpellCheck = 582, - SplineByteKeyFrame = 583, - SplineColorKeyFrame = 584, - SplineDecimalKeyFrame = 585, - SplineDoubleKeyFrame = 586, - SplineInt16KeyFrame = 587, - SplineInt32KeyFrame = 588, - SplineInt64KeyFrame = 589, - SplinePoint3DKeyFrame = 590, - SplinePointKeyFrame = 591, - SplineQuaternionKeyFrame = 592, - SplineRectKeyFrame = 593, - SplineRotation3DKeyFrame = 594, - SplineSingleKeyFrame = 595, - SplineSizeKeyFrame = 596, - SplineThicknessKeyFrame = 597, - SplineVector3DKeyFrame = 598, - SplineVectorKeyFrame = 599, - SpotLight = 600, - StackPanel = 601, - StaticExtension = 602, - StaticResourceExtension = 603, - StatusBar = 604, - StatusBarItem = 605, - StickyNoteControl = 606, - StopStoryboard = 607, - Storyboard = 608, - StreamGeometry = 609, - StreamGeometryContext = 610, - StreamResourceInfo = 611, - String = 612, - StringAnimationBase = 613, - StringAnimationUsingKeyFrames = 614, - StringConverter = 615, - StringKeyFrame = 616, - StringKeyFrameCollection = 617, - StrokeCollection = 618, - StrokeCollectionConverter = 619, - Style = 620, - Stylus = 621, - StylusDevice = 622, - TabControl = 623, - TabItem = 624, - TabPanel = 625, - Table = 626, - TableCell = 627, - TableColumn = 628, - TableRow = 629, - TableRowGroup = 630, - TabletDevice = 631, - TemplateBindingExpression = 632, - TemplateBindingExpressionConverter = 633, - TemplateBindingExtension = 634, - TemplateBindingExtensionConverter = 635, - TemplateKey = 636, - TemplateKeyConverter = 637, - TextBlock = 638, - TextBox = 639, - TextBoxBase = 640, - TextComposition = 641, - TextCompositionManager = 642, - TextDecoration = 643, - TextDecorationCollection = 644, - TextDecorationCollectionConverter = 645, - TextEffect = 646, - TextEffectCollection = 647, - TextElement = 648, - TextSearch = 649, - ThemeDictionaryExtension = 650, - Thickness = 651, - ThicknessAnimation = 652, - ThicknessAnimationBase = 653, - ThicknessAnimationUsingKeyFrames = 654, - ThicknessConverter = 655, - ThicknessKeyFrame = 656, - ThicknessKeyFrameCollection = 657, - Thumb = 658, - TickBar = 659, - TiffBitmapDecoder = 660, - TiffBitmapEncoder = 661, - TileBrush = 662, - TimeSpan = 663, - TimeSpanConverter = 664, - Timeline = 665, - TimelineCollection = 666, - TimelineGroup = 667, - ToggleButton = 668, - ToolBar = 669, - ToolBarOverflowPanel = 670, - ToolBarPanel = 671, - ToolBarTray = 672, - ToolTip = 673, - ToolTipService = 674, - Track = 675, - Transform = 676, - Transform3D = 677, - Transform3DCollection = 678, - Transform3DGroup = 679, - TransformCollection = 680, - TransformConverter = 681, - TransformGroup = 682, - TransformedBitmap = 683, - TranslateTransform = 684, - TranslateTransform3D = 685, - TreeView = 686, - TreeViewItem = 687, - Trigger = 688, - TriggerAction = 689, - TriggerBase = 690, - TypeExtension = 691, - TypeTypeConverter = 692, - Typography = 693, - UIElement = 694, - UInt16 = 695, - UInt16Converter = 696, - UInt32 = 697, - UInt32Converter = 698, - UInt64 = 699, - UInt64Converter = 700, - UShortIListConverter = 701, - Underline = 702, - UniformGrid = 703, - Uri = 704, - UriTypeConverter = 705, - UserControl = 706, - Validation = 707, - Vector = 708, - Vector3D = 709, - Vector3DAnimation = 710, - Vector3DAnimationBase = 711, - Vector3DAnimationUsingKeyFrames = 712, - Vector3DCollection = 713, - Vector3DCollectionConverter = 714, - Vector3DConverter = 715, - Vector3DKeyFrame = 716, - Vector3DKeyFrameCollection = 717, - VectorAnimation = 718, - VectorAnimationBase = 719, - VectorAnimationUsingKeyFrames = 720, - VectorCollection = 721, - VectorCollectionConverter = 722, - VectorConverter = 723, - VectorKeyFrame = 724, - VectorKeyFrameCollection = 725, - VideoDrawing = 726, - ViewBase = 727, - Viewbox = 728, - Viewport3D = 729, - Viewport3DVisual = 730, - VirtualizingPanel = 731, - VirtualizingStackPanel = 732, - Visual = 733, - Visual3D = 734, - VisualBrush = 735, - VisualTarget = 736, - WeakEventManager = 737, - WhitespaceSignificantCollectionAttribute = 738, - Window = 739, - WmpBitmapDecoder = 740, - WmpBitmapEncoder = 741, - WrapPanel = 742, - WriteableBitmap = 743, - XamlBrushSerializer = 744, - XamlInt32CollectionSerializer = 745, - XamlPathDataSerializer = 746, - XamlPoint3DCollectionSerializer = 747, - XamlPointCollectionSerializer = 748, - XamlReader = 749, - XamlStyleSerializer = 750, - XamlTemplateSerializer = 751, - XamlVector3DCollectionSerializer = 752, - XamlWriter = 753, - XmlDataProvider = 754, - XmlLangPropertyAttribute = 755, - XmlLanguage = 756, - XmlLanguageConverter = 757, - XmlNamespaceMapping = 758, - ZoomPercentageConverter = 759, - } - - internal enum KnownMembers : short { - Unknown = 0, - AccessText_Text = 1, - BeginStoryboard_Storyboard = 2, - BitmapEffectGroup_Children = 3, - Border_Background = 4, - Border_BorderBrush = 5, - Border_BorderThickness = 6, - ButtonBase_Command = 7, - ButtonBase_CommandParameter = 8, - ButtonBase_CommandTarget = 9, - ButtonBase_IsPressed = 10, - ColumnDefinition_MaxWidth = 11, - ColumnDefinition_MinWidth = 12, - ColumnDefinition_Width = 13, - ContentControl_Content = 14, - ContentControl_ContentTemplate = 15, - ContentControl_ContentTemplateSelector = 16, - ContentControl_HasContent = 17, - ContentElement_Focusable = 18, - ContentPresenter_Content = 19, - ContentPresenter_ContentSource = 20, - ContentPresenter_ContentTemplate = 21, - ContentPresenter_ContentTemplateSelector = 22, - ContentPresenter_RecognizesAccessKey = 23, - Control_Background = 24, - Control_BorderBrush = 25, - Control_BorderThickness = 26, - Control_FontFamily = 27, - Control_FontSize = 28, - Control_FontStretch = 29, - Control_FontStyle = 30, - Control_FontWeight = 31, - Control_Foreground = 32, - Control_HorizontalContentAlignment = 33, - Control_IsTabStop = 34, - Control_Padding = 35, - Control_TabIndex = 36, - Control_Template = 37, - Control_VerticalContentAlignment = 38, - DockPanel_Dock = 39, - DockPanel_LastChildFill = 40, - DocumentViewerBase_Document = 41, - DrawingGroup_Children = 42, - FlowDocumentReader_Document = 43, - FlowDocumentScrollViewer_Document = 44, - FrameworkContentElement_Style = 45, - FrameworkElement_FlowDirection = 46, - FrameworkElement_Height = 47, - FrameworkElement_HorizontalAlignment = 48, - FrameworkElement_Margin = 49, - FrameworkElement_MaxHeight = 50, - FrameworkElement_MaxWidth = 51, - FrameworkElement_MinHeight = 52, - FrameworkElement_MinWidth = 53, - FrameworkElement_Name = 54, - FrameworkElement_Style = 55, - FrameworkElement_VerticalAlignment = 56, - FrameworkElement_Width = 57, - GeneralTransformGroup_Children = 58, - GeometryGroup_Children = 59, - GradientBrush_GradientStops = 60, - Grid_Column = 61, - Grid_ColumnSpan = 62, - Grid_Row = 63, - Grid_RowSpan = 64, - GridViewColumn_Header = 65, - HeaderedContentControl_HasHeader = 66, - HeaderedContentControl_Header = 67, - HeaderedContentControl_HeaderTemplate = 68, - HeaderedContentControl_HeaderTemplateSelector = 69, - HeaderedItemsControl_HasHeader = 70, - HeaderedItemsControl_Header = 71, - HeaderedItemsControl_HeaderTemplate = 72, - HeaderedItemsControl_HeaderTemplateSelector = 73, - Hyperlink_NavigateUri = 74, - Image_Source = 75, - Image_Stretch = 76, - ItemsControl_ItemContainerStyle = 77, - ItemsControl_ItemContainerStyleSelector = 78, - ItemsControl_ItemTemplate = 79, - ItemsControl_ItemTemplateSelector = 80, - ItemsControl_ItemsPanel = 81, - ItemsControl_ItemsSource = 82, - MaterialGroup_Children = 83, - Model3DGroup_Children = 84, - Page_Content = 85, - Panel_Background = 86, - Path_Data = 87, - PathFigure_Segments = 88, - PathGeometry_Figures = 89, - Popup_Child = 90, - Popup_IsOpen = 91, - Popup_Placement = 92, - Popup_PopupAnimation = 93, - RowDefinition_Height = 94, - RowDefinition_MaxHeight = 95, - RowDefinition_MinHeight = 96, - ScrollViewer_CanContentScroll = 97, - ScrollViewer_HorizontalScrollBarVisibility = 98, - ScrollViewer_VerticalScrollBarVisibility = 99, - Shape_Fill = 100, - Shape_Stroke = 101, - Shape_StrokeThickness = 102, - TextBlock_Background = 103, - TextBlock_FontFamily = 104, - TextBlock_FontSize = 105, - TextBlock_FontStretch = 106, - TextBlock_FontStyle = 107, - TextBlock_FontWeight = 108, - TextBlock_Foreground = 109, - TextBlock_Text = 110, - TextBlock_TextDecorations = 111, - TextBlock_TextTrimming = 112, - TextBlock_TextWrapping = 113, - TextBox_Text = 114, - TextElement_Background = 115, - TextElement_FontFamily = 116, - TextElement_FontSize = 117, - TextElement_FontStretch = 118, - TextElement_FontStyle = 119, - TextElement_FontWeight = 120, - TextElement_Foreground = 121, - TimelineGroup_Children = 122, - Track_IsDirectionReversed = 123, - Track_Maximum = 124, - Track_Minimum = 125, - Track_Orientation = 126, - Track_Value = 127, - Track_ViewportSize = 128, - Transform3DGroup_Children = 129, - TransformGroup_Children = 130, - UIElement_ClipToBounds = 131, - UIElement_Focusable = 132, - UIElement_IsEnabled = 133, - UIElement_RenderTransform = 134, - UIElement_Visibility = 135, - Viewport3D_Children = 136, - - AdornedElementPlaceholder_Child = 138, - AdornerDecorator_Child = 139, - AnchoredBlock_Blocks = 140, - ArrayExtension_Items = 141, - BlockUIContainer_Child = 142, - Bold_Inlines = 143, - BooleanAnimationUsingKeyFrames_KeyFrames = 144, - Border_Child = 145, - BulletDecorator_Child = 146, - Button_Content = 147, - ButtonBase_Content = 148, - ByteAnimationUsingKeyFrames_KeyFrames = 149, - Canvas_Children = 150, - CharAnimationUsingKeyFrames_KeyFrames = 151, - CheckBox_Content = 152, - ColorAnimationUsingKeyFrames_KeyFrames = 153, - ComboBox_Items = 154, - ComboBoxItem_Content = 155, - ContextMenu_Items = 156, - ControlTemplate_VisualTree = 157, - DataTemplate_VisualTree = 158, - DataTrigger_Setters = 159, - DecimalAnimationUsingKeyFrames_KeyFrames = 160, - Decorator_Child = 161, - DockPanel_Children = 162, - DocumentViewer_Document = 163, - DoubleAnimationUsingKeyFrames_KeyFrames = 164, - EventTrigger_Actions = 165, - Expander_Content = 166, - Figure_Blocks = 167, - FixedDocument_Pages = 168, - FixedDocumentSequence_References = 169, - FixedPage_Children = 170, - Floater_Blocks = 171, - FlowDocument_Blocks = 172, - FlowDocumentPageViewer_Document = 173, - FrameworkTemplate_VisualTree = 174, - Grid_Children = 175, - GridView_Columns = 176, - GridViewColumnHeader_Content = 177, - GroupBox_Content = 178, - GroupItem_Content = 179, - HeaderedContentControl_Content = 180, - HeaderedItemsControl_Items = 181, - HierarchicalDataTemplate_VisualTree = 182, - Hyperlink_Inlines = 183, - InkCanvas_Children = 184, - InkPresenter_Child = 185, - InlineUIContainer_Child = 186, - InputScopeName_NameValue = 187, - Int16AnimationUsingKeyFrames_KeyFrames = 188, - Int32AnimationUsingKeyFrames_KeyFrames = 189, - Int64AnimationUsingKeyFrames_KeyFrames = 190, - Italic_Inlines = 191, - ItemsControl_Items = 192, - ItemsPanelTemplate_VisualTree = 193, - Label_Content = 194, - LinearGradientBrush_GradientStops = 195, - List_ListItems = 196, - ListBox_Items = 197, - ListBoxItem_Content = 198, - ListItem_Blocks = 199, - ListView_Items = 200, - ListViewItem_Content = 201, - MatrixAnimationUsingKeyFrames_KeyFrames = 202, - Menu_Items = 203, - MenuBase_Items = 204, - MenuItem_Items = 205, - ModelVisual3D_Children = 206, - MultiBinding_Bindings = 207, - MultiDataTrigger_Setters = 208, - MultiTrigger_Setters = 209, - ObjectAnimationUsingKeyFrames_KeyFrames = 210, - PageContent_Child = 211, - PageFunctionBase_Content = 212, - Panel_Children = 213, - Paragraph_Inlines = 214, - ParallelTimeline_Children = 215, - Point3DAnimationUsingKeyFrames_KeyFrames = 216, - PointAnimationUsingKeyFrames_KeyFrames = 217, - PriorityBinding_Bindings = 218, - QuaternionAnimationUsingKeyFrames_KeyFrames = 219, - RadialGradientBrush_GradientStops = 220, - RadioButton_Content = 221, - RectAnimationUsingKeyFrames_KeyFrames = 222, - RepeatButton_Content = 223, - RichTextBox_Document = 224, - Rotation3DAnimationUsingKeyFrames_KeyFrames = 225, - Run_Text = 226, - ScrollViewer_Content = 227, - Section_Blocks = 228, - Selector_Items = 229, - SingleAnimationUsingKeyFrames_KeyFrames = 230, - SizeAnimationUsingKeyFrames_KeyFrames = 231, - Span_Inlines = 232, - StackPanel_Children = 233, - StatusBar_Items = 234, - StatusBarItem_Content = 235, - Storyboard_Children = 236, - StringAnimationUsingKeyFrames_KeyFrames = 237, - Style_Setters = 238, - TabControl_Items = 239, - TabItem_Content = 240, - TabPanel_Children = 241, - Table_RowGroups = 242, - TableCell_Blocks = 243, - TableRow_Cells = 244, - TableRowGroup_Rows = 245, - TextBlock_Inlines = 246, - ThicknessAnimationUsingKeyFrames_KeyFrames = 247, - ToggleButton_Content = 248, - ToolBar_Items = 249, - ToolBarOverflowPanel_Children = 250, - ToolBarPanel_Children = 251, - ToolBarTray_ToolBars = 252, - ToolTip_Content = 253, - TreeView_Items = 254, - TreeViewItem_Items = 255, - Trigger_Setters = 256, - Underline_Inlines = 257, - UniformGrid_Children = 258, - UserControl_Content = 259, - Vector3DAnimationUsingKeyFrames_KeyFrames = 260, - VectorAnimationUsingKeyFrames_KeyFrames = 261, - Viewbox_Child = 262, - Viewport3DVisual_Children = 263, - VirtualizingPanel_Children = 264, - VirtualizingStackPanel_Children = 265, - Window_Content = 266, - WrapPanel_Children = 267, - XmlDataProvider_XmlSerializer = 268, - } - - void InitAssemblies() { - assemblies[0] = ResolveAssembly("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); - assemblies[1] = ResolveAssembly("System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); - assemblies[2] = ResolveAssembly("WindowsBase, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); - assemblies[3] = ResolveAssembly("PresentationCore, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); - assemblies[4] = ResolveAssembly("PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); - assemblies[5] = ResolveAssembly("System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); - } - - void InitTypes() { - - types[KnownTypes.AccessText] = InitType(assemblies[4], "System.Windows.Controls", "AccessText"); - types[KnownTypes.AdornedElementPlaceholder] = InitType(assemblies[4], "System.Windows.Controls", "AdornedElementPlaceholder"); - types[KnownTypes.Adorner] = InitType(assemblies[4], "System.Windows.Documents", "Adorner"); - types[KnownTypes.AdornerDecorator] = InitType(assemblies[4], "System.Windows.Documents", "AdornerDecorator"); - types[KnownTypes.AdornerLayer] = InitType(assemblies[4], "System.Windows.Documents", "AdornerLayer"); - types[KnownTypes.AffineTransform3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "AffineTransform3D"); - types[KnownTypes.AmbientLight] = InitType(assemblies[3], "System.Windows.Media.Media3D", "AmbientLight"); - types[KnownTypes.AnchoredBlock] = InitType(assemblies[4], "System.Windows.Documents", "AnchoredBlock"); - types[KnownTypes.Animatable] = InitType(assemblies[3], "System.Windows.Media.Animation", "Animatable"); - types[KnownTypes.AnimationClock] = InitType(assemblies[3], "System.Windows.Media.Animation", "AnimationClock"); - types[KnownTypes.AnimationTimeline] = InitType(assemblies[3], "System.Windows.Media.Animation", "AnimationTimeline"); - types[KnownTypes.Application] = InitType(assemblies[4], "System.Windows", "Application"); - types[KnownTypes.ArcSegment] = InitType(assemblies[3], "System.Windows.Media", "ArcSegment"); - types[KnownTypes.ArrayExtension] = InitType(assemblies[4], "System.Windows.Markup", "ArrayExtension"); - types[KnownTypes.AxisAngleRotation3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "AxisAngleRotation3D"); - types[KnownTypes.BaseIListConverter] = InitType(assemblies[3], "System.Windows.Media.Converters", "BaseIListConverter"); - types[KnownTypes.BeginStoryboard] = InitType(assemblies[4], "System.Windows.Media.Animation", "BeginStoryboard"); - types[KnownTypes.BevelBitmapEffect] = InitType(assemblies[3], "System.Windows.Media.Effects", "BevelBitmapEffect"); - types[KnownTypes.BezierSegment] = InitType(assemblies[3], "System.Windows.Media", "BezierSegment"); - types[KnownTypes.Binding] = InitType(assemblies[4], "System.Windows.Data", "Binding"); - types[KnownTypes.BindingBase] = InitType(assemblies[4], "System.Windows.Data", "BindingBase"); - types[KnownTypes.BindingExpression] = InitType(assemblies[4], "System.Windows.Data", "BindingExpression"); - types[KnownTypes.BindingExpressionBase] = InitType(assemblies[4], "System.Windows.Data", "BindingExpressionBase"); - types[KnownTypes.BindingListCollectionView] = InitType(assemblies[4], "System.Windows.Data", "BindingListCollectionView"); - types[KnownTypes.BitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BitmapDecoder"); - types[KnownTypes.BitmapEffect] = InitType(assemblies[3], "System.Windows.Media.Effects", "BitmapEffect"); - types[KnownTypes.BitmapEffectCollection] = InitType(assemblies[3], "System.Windows.Media.Effects", "BitmapEffectCollection"); - types[KnownTypes.BitmapEffectGroup] = InitType(assemblies[3], "System.Windows.Media.Effects", "BitmapEffectGroup"); - types[KnownTypes.BitmapEffectInput] = InitType(assemblies[3], "System.Windows.Media.Effects", "BitmapEffectInput"); - types[KnownTypes.BitmapEncoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BitmapEncoder"); - types[KnownTypes.BitmapFrame] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BitmapFrame"); - types[KnownTypes.BitmapImage] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BitmapImage"); - types[KnownTypes.BitmapMetadata] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BitmapMetadata"); - types[KnownTypes.BitmapPalette] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BitmapPalette"); - types[KnownTypes.BitmapSource] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BitmapSource"); - types[KnownTypes.Block] = InitType(assemblies[4], "System.Windows.Documents", "Block"); - types[KnownTypes.BlockUIContainer] = InitType(assemblies[4], "System.Windows.Documents", "BlockUIContainer"); - types[KnownTypes.BlurBitmapEffect] = InitType(assemblies[3], "System.Windows.Media.Effects", "BlurBitmapEffect"); - types[KnownTypes.BmpBitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BmpBitmapDecoder"); - types[KnownTypes.BmpBitmapEncoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "BmpBitmapEncoder"); - types[KnownTypes.Bold] = InitType(assemblies[4], "System.Windows.Documents", "Bold"); - types[KnownTypes.BoolIListConverter] = InitType(assemblies[3], "System.Windows.Media.Converters", "BoolIListConverter"); - types[KnownTypes.Boolean] = InitType(assemblies[0], "System", "Boolean"); - types[KnownTypes.BooleanAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "BooleanAnimationBase"); - types[KnownTypes.BooleanAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "BooleanAnimationUsingKeyFrames"); - types[KnownTypes.BooleanConverter] = InitType(assemblies[1], "System.ComponentModel", "BooleanConverter"); - types[KnownTypes.BooleanKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "BooleanKeyFrame"); - types[KnownTypes.BooleanKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "BooleanKeyFrameCollection"); - types[KnownTypes.BooleanToVisibilityConverter] = InitType(assemblies[4], "System.Windows.Controls", "BooleanToVisibilityConverter"); - types[KnownTypes.Border] = InitType(assemblies[4], "System.Windows.Controls", "Border"); - types[KnownTypes.BorderGapMaskConverter] = InitType(assemblies[4], "System.Windows.Controls", "BorderGapMaskConverter"); - types[KnownTypes.Brush] = InitType(assemblies[3], "System.Windows.Media", "Brush"); - types[KnownTypes.BrushConverter] = InitType(assemblies[3], "System.Windows.Media", "BrushConverter"); - types[KnownTypes.BulletDecorator] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "BulletDecorator"); - types[KnownTypes.Button] = InitType(assemblies[4], "System.Windows.Controls", "Button"); - types[KnownTypes.ButtonBase] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "ButtonBase"); - types[KnownTypes.Byte] = InitType(assemblies[0], "System", "Byte"); - types[KnownTypes.ByteAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "ByteAnimation"); - types[KnownTypes.ByteAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "ByteAnimationBase"); - types[KnownTypes.ByteAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "ByteAnimationUsingKeyFrames"); - types[KnownTypes.ByteConverter] = InitType(assemblies[1], "System.ComponentModel", "ByteConverter"); - types[KnownTypes.ByteKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "ByteKeyFrame"); - types[KnownTypes.ByteKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "ByteKeyFrameCollection"); - types[KnownTypes.CachedBitmap] = InitType(assemblies[3], "System.Windows.Media.Imaging", "CachedBitmap"); - types[KnownTypes.Camera] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Camera"); - types[KnownTypes.Canvas] = InitType(assemblies[4], "System.Windows.Controls", "Canvas"); - types[KnownTypes.Char] = InitType(assemblies[0], "System", "Char"); - types[KnownTypes.CharAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "CharAnimationBase"); - types[KnownTypes.CharAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "CharAnimationUsingKeyFrames"); - types[KnownTypes.CharConverter] = InitType(assemblies[1], "System.ComponentModel", "CharConverter"); - types[KnownTypes.CharIListConverter] = InitType(assemblies[3], "System.Windows.Media.Converters", "CharIListConverter"); - types[KnownTypes.CharKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "CharKeyFrame"); - types[KnownTypes.CharKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "CharKeyFrameCollection"); - types[KnownTypes.CheckBox] = InitType(assemblies[4], "System.Windows.Controls", "CheckBox"); - types[KnownTypes.Clock] = InitType(assemblies[3], "System.Windows.Media.Animation", "Clock"); - types[KnownTypes.ClockController] = InitType(assemblies[3], "System.Windows.Media.Animation", "ClockController"); - types[KnownTypes.ClockGroup] = InitType(assemblies[3], "System.Windows.Media.Animation", "ClockGroup"); - types[KnownTypes.CollectionContainer] = InitType(assemblies[4], "System.Windows.Data", "CollectionContainer"); - types[KnownTypes.CollectionView] = InitType(assemblies[4], "System.Windows.Data", "CollectionView"); - types[KnownTypes.CollectionViewSource] = InitType(assemblies[4], "System.Windows.Data", "CollectionViewSource"); - types[KnownTypes.Color] = InitType(assemblies[3], "System.Windows.Media", "Color"); - types[KnownTypes.ColorAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "ColorAnimation"); - types[KnownTypes.ColorAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "ColorAnimationBase"); - types[KnownTypes.ColorAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "ColorAnimationUsingKeyFrames"); - types[KnownTypes.ColorConvertedBitmap] = InitType(assemblies[3], "System.Windows.Media.Imaging", "ColorConvertedBitmap"); - types[KnownTypes.ColorConvertedBitmapExtension] = InitType(assemblies[4], "System.Windows", "ColorConvertedBitmapExtension"); - types[KnownTypes.ColorConverter] = InitType(assemblies[3], "System.Windows.Media", "ColorConverter"); - types[KnownTypes.ColorKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "ColorKeyFrame"); - types[KnownTypes.ColorKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "ColorKeyFrameCollection"); - types[KnownTypes.ColumnDefinition] = InitType(assemblies[4], "System.Windows.Controls", "ColumnDefinition"); - types[KnownTypes.CombinedGeometry] = InitType(assemblies[3], "System.Windows.Media", "CombinedGeometry"); - types[KnownTypes.ComboBox] = InitType(assemblies[4], "System.Windows.Controls", "ComboBox"); - types[KnownTypes.ComboBoxItem] = InitType(assemblies[4], "System.Windows.Controls", "ComboBoxItem"); - types[KnownTypes.CommandConverter] = InitType(assemblies[4], "System.Windows.Input", "CommandConverter"); - types[KnownTypes.ComponentResourceKey] = InitType(assemblies[4], "System.Windows", "ComponentResourceKey"); - types[KnownTypes.ComponentResourceKeyConverter] = InitType(assemblies[4], "System.Windows.Markup", "ComponentResourceKeyConverter"); - types[KnownTypes.CompositionTarget] = InitType(assemblies[3], "System.Windows.Media", "CompositionTarget"); - types[KnownTypes.Condition] = InitType(assemblies[4], "System.Windows", "Condition"); - types[KnownTypes.ContainerVisual] = InitType(assemblies[3], "System.Windows.Media", "ContainerVisual"); - types[KnownTypes.ContentControl] = InitType(assemblies[4], "System.Windows.Controls", "ContentControl"); - types[KnownTypes.ContentElement] = InitType(assemblies[3], "System.Windows", "ContentElement"); - types[KnownTypes.ContentPresenter] = InitType(assemblies[4], "System.Windows.Controls", "ContentPresenter"); - types[KnownTypes.ContentPropertyAttribute] = InitType(assemblies[2], "System.Windows.Markup", "ContentPropertyAttribute"); - types[KnownTypes.ContentWrapperAttribute] = InitType(assemblies[2], "System.Windows.Markup", "ContentWrapperAttribute"); - types[KnownTypes.ContextMenu] = InitType(assemblies[4], "System.Windows.Controls", "ContextMenu"); - types[KnownTypes.ContextMenuService] = InitType(assemblies[4], "System.Windows.Controls", "ContextMenuService"); - types[KnownTypes.Control] = InitType(assemblies[4], "System.Windows.Controls", "Control"); - types[KnownTypes.ControlTemplate] = InitType(assemblies[4], "System.Windows.Controls", "ControlTemplate"); - types[KnownTypes.ControllableStoryboardAction] = InitType(assemblies[4], "System.Windows.Media.Animation", "ControllableStoryboardAction"); - types[KnownTypes.CornerRadius] = InitType(assemblies[4], "System.Windows", "CornerRadius"); - types[KnownTypes.CornerRadiusConverter] = InitType(assemblies[4], "System.Windows", "CornerRadiusConverter"); - types[KnownTypes.CroppedBitmap] = InitType(assemblies[3], "System.Windows.Media.Imaging", "CroppedBitmap"); - types[KnownTypes.CultureInfo] = InitType(assemblies[0], "System.Globalization", "CultureInfo"); - types[KnownTypes.CultureInfoConverter] = InitType(assemblies[1], "System.ComponentModel", "CultureInfoConverter"); - types[KnownTypes.CultureInfoIetfLanguageTagConverter] = InitType(assemblies[3], "System.Windows", "CultureInfoIetfLanguageTagConverter"); - types[KnownTypes.Cursor] = InitType(assemblies[3], "System.Windows.Input", "Cursor"); - types[KnownTypes.CursorConverter] = InitType(assemblies[3], "System.Windows.Input", "CursorConverter"); - types[KnownTypes.DashStyle] = InitType(assemblies[3], "System.Windows.Media", "DashStyle"); - types[KnownTypes.DataChangedEventManager] = InitType(assemblies[4], "System.Windows.Data", "DataChangedEventManager"); - types[KnownTypes.DataTemplate] = InitType(assemblies[4], "System.Windows", "DataTemplate"); - types[KnownTypes.DataTemplateKey] = InitType(assemblies[4], "System.Windows", "DataTemplateKey"); - types[KnownTypes.DataTrigger] = InitType(assemblies[4], "System.Windows", "DataTrigger"); - types[KnownTypes.DateTime] = InitType(assemblies[0], "System", "DateTime"); - types[KnownTypes.DateTimeConverter] = InitType(assemblies[1], "System.ComponentModel", "DateTimeConverter"); - types[KnownTypes.DateTimeConverter2] = InitType(assemblies[2], "System.Windows.Markup", "DateTimeConverter2"); - types[KnownTypes.Decimal] = InitType(assemblies[0], "System", "Decimal"); - types[KnownTypes.DecimalAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "DecimalAnimation"); - types[KnownTypes.DecimalAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "DecimalAnimationBase"); - types[KnownTypes.DecimalAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "DecimalAnimationUsingKeyFrames"); - types[KnownTypes.DecimalConverter] = InitType(assemblies[1], "System.ComponentModel", "DecimalConverter"); - types[KnownTypes.DecimalKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DecimalKeyFrame"); - types[KnownTypes.DecimalKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "DecimalKeyFrameCollection"); - types[KnownTypes.Decorator] = InitType(assemblies[4], "System.Windows.Controls", "Decorator"); - types[KnownTypes.DefinitionBase] = InitType(assemblies[4], "System.Windows.Controls", "DefinitionBase"); - types[KnownTypes.DependencyObject] = InitType(assemblies[2], "System.Windows", "DependencyObject"); - types[KnownTypes.DependencyProperty] = InitType(assemblies[2], "System.Windows", "DependencyProperty"); - types[KnownTypes.DependencyPropertyConverter] = InitType(assemblies[4], "System.Windows.Markup", "DependencyPropertyConverter"); - types[KnownTypes.DialogResultConverter] = InitType(assemblies[4], "System.Windows", "DialogResultConverter"); - types[KnownTypes.DiffuseMaterial] = InitType(assemblies[3], "System.Windows.Media.Media3D", "DiffuseMaterial"); - types[KnownTypes.DirectionalLight] = InitType(assemblies[3], "System.Windows.Media.Media3D", "DirectionalLight"); - types[KnownTypes.DiscreteBooleanKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteBooleanKeyFrame"); - types[KnownTypes.DiscreteByteKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteByteKeyFrame"); - types[KnownTypes.DiscreteCharKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteCharKeyFrame"); - types[KnownTypes.DiscreteColorKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteColorKeyFrame"); - types[KnownTypes.DiscreteDecimalKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteDecimalKeyFrame"); - types[KnownTypes.DiscreteDoubleKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteDoubleKeyFrame"); - types[KnownTypes.DiscreteInt16KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteInt16KeyFrame"); - types[KnownTypes.DiscreteInt32KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteInt32KeyFrame"); - types[KnownTypes.DiscreteInt64KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteInt64KeyFrame"); - types[KnownTypes.DiscreteMatrixKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteMatrixKeyFrame"); - types[KnownTypes.DiscreteObjectKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteObjectKeyFrame"); - types[KnownTypes.DiscretePoint3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscretePoint3DKeyFrame"); - types[KnownTypes.DiscretePointKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscretePointKeyFrame"); - types[KnownTypes.DiscreteQuaternionKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteQuaternionKeyFrame"); - types[KnownTypes.DiscreteRectKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteRectKeyFrame"); - types[KnownTypes.DiscreteRotation3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteRotation3DKeyFrame"); - types[KnownTypes.DiscreteSingleKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteSingleKeyFrame"); - types[KnownTypes.DiscreteSizeKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteSizeKeyFrame"); - types[KnownTypes.DiscreteStringKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteStringKeyFrame"); - types[KnownTypes.DiscreteThicknessKeyFrame] = InitType(assemblies[4], "System.Windows.Media.Animation", "DiscreteThicknessKeyFrame"); - types[KnownTypes.DiscreteVector3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteVector3DKeyFrame"); - types[KnownTypes.DiscreteVectorKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DiscreteVectorKeyFrame"); - types[KnownTypes.DockPanel] = InitType(assemblies[4], "System.Windows.Controls", "DockPanel"); - types[KnownTypes.DocumentPageView] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "DocumentPageView"); - types[KnownTypes.DocumentReference] = InitType(assemblies[4], "System.Windows.Documents", "DocumentReference"); - types[KnownTypes.DocumentViewer] = InitType(assemblies[4], "System.Windows.Controls", "DocumentViewer"); - types[KnownTypes.DocumentViewerBase] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "DocumentViewerBase"); - types[KnownTypes.Double] = InitType(assemblies[0], "System", "Double"); - types[KnownTypes.DoubleAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "DoubleAnimation"); - types[KnownTypes.DoubleAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "DoubleAnimationBase"); - types[KnownTypes.DoubleAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "DoubleAnimationUsingKeyFrames"); - types[KnownTypes.DoubleAnimationUsingPath] = InitType(assemblies[3], "System.Windows.Media.Animation", "DoubleAnimationUsingPath"); - types[KnownTypes.DoubleCollection] = InitType(assemblies[3], "System.Windows.Media", "DoubleCollection"); - types[KnownTypes.DoubleCollectionConverter] = InitType(assemblies[3], "System.Windows.Media", "DoubleCollectionConverter"); - types[KnownTypes.DoubleConverter] = InitType(assemblies[1], "System.ComponentModel", "DoubleConverter"); - types[KnownTypes.DoubleIListConverter] = InitType(assemblies[3], "System.Windows.Media.Converters", "DoubleIListConverter"); - types[KnownTypes.DoubleKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "DoubleKeyFrame"); - types[KnownTypes.DoubleKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "DoubleKeyFrameCollection"); - types[KnownTypes.Drawing] = InitType(assemblies[3], "System.Windows.Media", "Drawing"); - types[KnownTypes.DrawingBrush] = InitType(assemblies[3], "System.Windows.Media", "DrawingBrush"); - types[KnownTypes.DrawingCollection] = InitType(assemblies[3], "System.Windows.Media", "DrawingCollection"); - types[KnownTypes.DrawingContext] = InitType(assemblies[3], "System.Windows.Media", "DrawingContext"); - types[KnownTypes.DrawingGroup] = InitType(assemblies[3], "System.Windows.Media", "DrawingGroup"); - types[KnownTypes.DrawingImage] = InitType(assemblies[3], "System.Windows.Media", "DrawingImage"); - types[KnownTypes.DrawingVisual] = InitType(assemblies[3], "System.Windows.Media", "DrawingVisual"); - types[KnownTypes.DropShadowBitmapEffect] = InitType(assemblies[3], "System.Windows.Media.Effects", "DropShadowBitmapEffect"); - types[KnownTypes.Duration] = InitType(assemblies[3], "System.Windows", "Duration"); - types[KnownTypes.DurationConverter] = InitType(assemblies[3], "System.Windows", "DurationConverter"); - types[KnownTypes.DynamicResourceExtension] = InitType(assemblies[4], "System.Windows", "DynamicResourceExtension"); - types[KnownTypes.DynamicResourceExtensionConverter] = InitType(assemblies[4], "System.Windows", "DynamicResourceExtensionConverter"); - types[KnownTypes.Ellipse] = InitType(assemblies[4], "System.Windows.Shapes", "Ellipse"); - types[KnownTypes.EllipseGeometry] = InitType(assemblies[3], "System.Windows.Media", "EllipseGeometry"); - types[KnownTypes.EmbossBitmapEffect] = InitType(assemblies[3], "System.Windows.Media.Effects", "EmbossBitmapEffect"); - types[KnownTypes.EmissiveMaterial] = InitType(assemblies[3], "System.Windows.Media.Media3D", "EmissiveMaterial"); - types[KnownTypes.EnumConverter] = InitType(assemblies[1], "System.ComponentModel", "EnumConverter"); - types[KnownTypes.EventManager] = InitType(assemblies[3], "System.Windows", "EventManager"); - types[KnownTypes.EventSetter] = InitType(assemblies[4], "System.Windows", "EventSetter"); - types[KnownTypes.EventTrigger] = InitType(assemblies[4], "System.Windows", "EventTrigger"); - types[KnownTypes.Expander] = InitType(assemblies[4], "System.Windows.Controls", "Expander"); - types[KnownTypes.Expression] = InitType(assemblies[2], "System.Windows", "Expression"); - types[KnownTypes.ExpressionConverter] = InitType(assemblies[2], "System.Windows", "ExpressionConverter"); - types[KnownTypes.Figure] = InitType(assemblies[4], "System.Windows.Documents", "Figure"); - types[KnownTypes.FigureLength] = InitType(assemblies[4], "System.Windows", "FigureLength"); - types[KnownTypes.FigureLengthConverter] = InitType(assemblies[4], "System.Windows", "FigureLengthConverter"); - types[KnownTypes.FixedDocument] = InitType(assemblies[4], "System.Windows.Documents", "FixedDocument"); - types[KnownTypes.FixedDocumentSequence] = InitType(assemblies[4], "System.Windows.Documents", "FixedDocumentSequence"); - types[KnownTypes.FixedPage] = InitType(assemblies[4], "System.Windows.Documents", "FixedPage"); - types[KnownTypes.Floater] = InitType(assemblies[4], "System.Windows.Documents", "Floater"); - types[KnownTypes.FlowDocument] = InitType(assemblies[4], "System.Windows.Documents", "FlowDocument"); - types[KnownTypes.FlowDocumentPageViewer] = InitType(assemblies[4], "System.Windows.Controls", "FlowDocumentPageViewer"); - types[KnownTypes.FlowDocumentReader] = InitType(assemblies[4], "System.Windows.Controls", "FlowDocumentReader"); - types[KnownTypes.FlowDocumentScrollViewer] = InitType(assemblies[4], "System.Windows.Controls", "FlowDocumentScrollViewer"); - types[KnownTypes.FocusManager] = InitType(assemblies[3], "System.Windows.Input", "FocusManager"); - types[KnownTypes.FontFamily] = InitType(assemblies[3], "System.Windows.Media", "FontFamily"); - types[KnownTypes.FontFamilyConverter] = InitType(assemblies[3], "System.Windows.Media", "FontFamilyConverter"); - types[KnownTypes.FontSizeConverter] = InitType(assemblies[4], "System.Windows", "FontSizeConverter"); - types[KnownTypes.FontStretch] = InitType(assemblies[3], "System.Windows", "FontStretch"); - types[KnownTypes.FontStretchConverter] = InitType(assemblies[3], "System.Windows", "FontStretchConverter"); - types[KnownTypes.FontStyle] = InitType(assemblies[3], "System.Windows", "FontStyle"); - types[KnownTypes.FontStyleConverter] = InitType(assemblies[3], "System.Windows", "FontStyleConverter"); - types[KnownTypes.FontWeight] = InitType(assemblies[3], "System.Windows", "FontWeight"); - types[KnownTypes.FontWeightConverter] = InitType(assemblies[3], "System.Windows", "FontWeightConverter"); - types[KnownTypes.FormatConvertedBitmap] = InitType(assemblies[3], "System.Windows.Media.Imaging", "FormatConvertedBitmap"); - types[KnownTypes.Frame] = InitType(assemblies[4], "System.Windows.Controls", "Frame"); - types[KnownTypes.FrameworkContentElement] = InitType(assemblies[4], "System.Windows", "FrameworkContentElement"); - types[KnownTypes.FrameworkElement] = InitType(assemblies[4], "System.Windows", "FrameworkElement"); - types[KnownTypes.FrameworkElementFactory] = InitType(assemblies[4], "System.Windows", "FrameworkElementFactory"); - types[KnownTypes.FrameworkPropertyMetadata] = InitType(assemblies[4], "System.Windows", "FrameworkPropertyMetadata"); - types[KnownTypes.FrameworkPropertyMetadataOptions] = InitType(assemblies[4], "System.Windows", "FrameworkPropertyMetadataOptions"); - types[KnownTypes.FrameworkRichTextComposition] = InitType(assemblies[4], "System.Windows.Documents", "FrameworkRichTextComposition"); - types[KnownTypes.FrameworkTemplate] = InitType(assemblies[4], "System.Windows", "FrameworkTemplate"); - types[KnownTypes.FrameworkTextComposition] = InitType(assemblies[4], "System.Windows.Documents", "FrameworkTextComposition"); - types[KnownTypes.Freezable] = InitType(assemblies[2], "System.Windows", "Freezable"); - types[KnownTypes.GeneralTransform] = InitType(assemblies[3], "System.Windows.Media", "GeneralTransform"); - types[KnownTypes.GeneralTransformCollection] = InitType(assemblies[3], "System.Windows.Media", "GeneralTransformCollection"); - types[KnownTypes.GeneralTransformGroup] = InitType(assemblies[3], "System.Windows.Media", "GeneralTransformGroup"); - types[KnownTypes.Geometry] = InitType(assemblies[3], "System.Windows.Media", "Geometry"); - types[KnownTypes.Geometry3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Geometry3D"); - types[KnownTypes.GeometryCollection] = InitType(assemblies[3], "System.Windows.Media", "GeometryCollection"); - types[KnownTypes.GeometryConverter] = InitType(assemblies[3], "System.Windows.Media", "GeometryConverter"); - types[KnownTypes.GeometryDrawing] = InitType(assemblies[3], "System.Windows.Media", "GeometryDrawing"); - types[KnownTypes.GeometryGroup] = InitType(assemblies[3], "System.Windows.Media", "GeometryGroup"); - types[KnownTypes.GeometryModel3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "GeometryModel3D"); - types[KnownTypes.GestureRecognizer] = InitType(assemblies[3], "System.Windows.Ink", "GestureRecognizer"); - types[KnownTypes.GifBitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "GifBitmapDecoder"); - types[KnownTypes.GifBitmapEncoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "GifBitmapEncoder"); - types[KnownTypes.GlyphRun] = InitType(assemblies[3], "System.Windows.Media", "GlyphRun"); - types[KnownTypes.GlyphRunDrawing] = InitType(assemblies[3], "System.Windows.Media", "GlyphRunDrawing"); - types[KnownTypes.GlyphTypeface] = InitType(assemblies[3], "System.Windows.Media", "GlyphTypeface"); - types[KnownTypes.Glyphs] = InitType(assemblies[4], "System.Windows.Documents", "Glyphs"); - types[KnownTypes.GradientBrush] = InitType(assemblies[3], "System.Windows.Media", "GradientBrush"); - types[KnownTypes.GradientStop] = InitType(assemblies[3], "System.Windows.Media", "GradientStop"); - types[KnownTypes.GradientStopCollection] = InitType(assemblies[3], "System.Windows.Media", "GradientStopCollection"); - types[KnownTypes.Grid] = InitType(assemblies[4], "System.Windows.Controls", "Grid"); - types[KnownTypes.GridLength] = InitType(assemblies[4], "System.Windows", "GridLength"); - types[KnownTypes.GridLengthConverter] = InitType(assemblies[4], "System.Windows", "GridLengthConverter"); - types[KnownTypes.GridSplitter] = InitType(assemblies[4], "System.Windows.Controls", "GridSplitter"); - types[KnownTypes.GridView] = InitType(assemblies[4], "System.Windows.Controls", "GridView"); - types[KnownTypes.GridViewColumn] = InitType(assemblies[4], "System.Windows.Controls", "GridViewColumn"); - types[KnownTypes.GridViewColumnHeader] = InitType(assemblies[4], "System.Windows.Controls", "GridViewColumnHeader"); - types[KnownTypes.GridViewHeaderRowPresenter] = InitType(assemblies[4], "System.Windows.Controls", "GridViewHeaderRowPresenter"); - types[KnownTypes.GridViewRowPresenter] = InitType(assemblies[4], "System.Windows.Controls", "GridViewRowPresenter"); - types[KnownTypes.GridViewRowPresenterBase] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "GridViewRowPresenterBase"); - types[KnownTypes.GroupBox] = InitType(assemblies[4], "System.Windows.Controls", "GroupBox"); - types[KnownTypes.GroupItem] = InitType(assemblies[4], "System.Windows.Controls", "GroupItem"); - types[KnownTypes.Guid] = InitType(assemblies[0], "System", "Guid"); - types[KnownTypes.GuidConverter] = InitType(assemblies[1], "System.ComponentModel", "GuidConverter"); - types[KnownTypes.GuidelineSet] = InitType(assemblies[3], "System.Windows.Media", "GuidelineSet"); - types[KnownTypes.HeaderedContentControl] = InitType(assemblies[4], "System.Windows.Controls", "HeaderedContentControl"); - types[KnownTypes.HeaderedItemsControl] = InitType(assemblies[4], "System.Windows.Controls", "HeaderedItemsControl"); - types[KnownTypes.HierarchicalDataTemplate] = InitType(assemblies[4], "System.Windows", "HierarchicalDataTemplate"); - types[KnownTypes.HostVisual] = InitType(assemblies[3], "System.Windows.Media", "HostVisual"); - types[KnownTypes.Hyperlink] = InitType(assemblies[4], "System.Windows.Documents", "Hyperlink"); - types[KnownTypes.IAddChild] = InitType(assemblies[3], "System.Windows.Markup", "IAddChild"); - types[KnownTypes.IAddChildInternal] = InitType(assemblies[3], "System.Windows.Markup", "IAddChildInternal"); - types[KnownTypes.ICommand] = InitType(assemblies[3], "System.Windows.Input", "ICommand"); - types[KnownTypes.IComponentConnector] = InitType(assemblies[2], "System.Windows.Markup", "IComponentConnector"); - types[KnownTypes.INameScope] = InitType(assemblies[2], "System.Windows.Markup", "INameScope"); - types[KnownTypes.IStyleConnector] = InitType(assemblies[4], "System.Windows.Markup", "IStyleConnector"); - types[KnownTypes.IconBitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "IconBitmapDecoder"); - types[KnownTypes.Image] = InitType(assemblies[4], "System.Windows.Controls", "Image"); - types[KnownTypes.ImageBrush] = InitType(assemblies[3], "System.Windows.Media", "ImageBrush"); - types[KnownTypes.ImageDrawing] = InitType(assemblies[3], "System.Windows.Media", "ImageDrawing"); - types[KnownTypes.ImageMetadata] = InitType(assemblies[3], "System.Windows.Media", "ImageMetadata"); - types[KnownTypes.ImageSource] = InitType(assemblies[3], "System.Windows.Media", "ImageSource"); - types[KnownTypes.ImageSourceConverter] = InitType(assemblies[3], "System.Windows.Media", "ImageSourceConverter"); - types[KnownTypes.InPlaceBitmapMetadataWriter] = InitType(assemblies[3], "System.Windows.Media.Imaging", "InPlaceBitmapMetadataWriter"); - types[KnownTypes.InkCanvas] = InitType(assemblies[4], "System.Windows.Controls", "InkCanvas"); - types[KnownTypes.InkPresenter] = InitType(assemblies[4], "System.Windows.Controls", "InkPresenter"); - types[KnownTypes.Inline] = InitType(assemblies[4], "System.Windows.Documents", "Inline"); - types[KnownTypes.InlineCollection] = InitType(assemblies[4], "System.Windows.Documents", "InlineCollection"); - types[KnownTypes.InlineUIContainer] = InitType(assemblies[4], "System.Windows.Documents", "InlineUIContainer"); - types[KnownTypes.InputBinding] = InitType(assemblies[3], "System.Windows.Input", "InputBinding"); - types[KnownTypes.InputDevice] = InitType(assemblies[3], "System.Windows.Input", "InputDevice"); - types[KnownTypes.InputLanguageManager] = InitType(assemblies[3], "System.Windows.Input", "InputLanguageManager"); - types[KnownTypes.InputManager] = InitType(assemblies[3], "System.Windows.Input", "InputManager"); - types[KnownTypes.InputMethod] = InitType(assemblies[3], "System.Windows.Input", "InputMethod"); - types[KnownTypes.InputScope] = InitType(assemblies[3], "System.Windows.Input", "InputScope"); - types[KnownTypes.InputScopeConverter] = InitType(assemblies[3], "System.Windows.Input", "InputScopeConverter"); - types[KnownTypes.InputScopeName] = InitType(assemblies[3], "System.Windows.Input", "InputScopeName"); - types[KnownTypes.InputScopeNameConverter] = InitType(assemblies[3], "System.Windows.Input", "InputScopeNameConverter"); - types[KnownTypes.Int16] = InitType(assemblies[0], "System", "Int16"); - types[KnownTypes.Int16Animation] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int16Animation"); - types[KnownTypes.Int16AnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int16AnimationBase"); - types[KnownTypes.Int16AnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int16AnimationUsingKeyFrames"); - types[KnownTypes.Int16Converter] = InitType(assemblies[1], "System.ComponentModel", "Int16Converter"); - types[KnownTypes.Int16KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int16KeyFrame"); - types[KnownTypes.Int16KeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int16KeyFrameCollection"); - types[KnownTypes.Int32] = InitType(assemblies[0], "System", "Int32"); - types[KnownTypes.Int32Animation] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int32Animation"); - types[KnownTypes.Int32AnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int32AnimationBase"); - types[KnownTypes.Int32AnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int32AnimationUsingKeyFrames"); - types[KnownTypes.Int32Collection] = InitType(assemblies[3], "System.Windows.Media", "Int32Collection"); - types[KnownTypes.Int32CollectionConverter] = InitType(assemblies[3], "System.Windows.Media", "Int32CollectionConverter"); - types[KnownTypes.Int32Converter] = InitType(assemblies[1], "System.ComponentModel", "Int32Converter"); - types[KnownTypes.Int32KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int32KeyFrame"); - types[KnownTypes.Int32KeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int32KeyFrameCollection"); - types[KnownTypes.Int32Rect] = InitType(assemblies[2], "System.Windows", "Int32Rect"); - types[KnownTypes.Int32RectConverter] = InitType(assemblies[2], "System.Windows", "Int32RectConverter"); - types[KnownTypes.Int64] = InitType(assemblies[0], "System", "Int64"); - types[KnownTypes.Int64Animation] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int64Animation"); - types[KnownTypes.Int64AnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int64AnimationBase"); - types[KnownTypes.Int64AnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int64AnimationUsingKeyFrames"); - types[KnownTypes.Int64Converter] = InitType(assemblies[1], "System.ComponentModel", "Int64Converter"); - types[KnownTypes.Int64KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int64KeyFrame"); - types[KnownTypes.Int64KeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "Int64KeyFrameCollection"); - types[KnownTypes.Italic] = InitType(assemblies[4], "System.Windows.Documents", "Italic"); - types[KnownTypes.ItemCollection] = InitType(assemblies[4], "System.Windows.Controls", "ItemCollection"); - types[KnownTypes.ItemsControl] = InitType(assemblies[4], "System.Windows.Controls", "ItemsControl"); - types[KnownTypes.ItemsPanelTemplate] = InitType(assemblies[4], "System.Windows.Controls", "ItemsPanelTemplate"); - types[KnownTypes.ItemsPresenter] = InitType(assemblies[4], "System.Windows.Controls", "ItemsPresenter"); - types[KnownTypes.JournalEntry] = InitType(assemblies[4], "System.Windows.Navigation", "JournalEntry"); - types[KnownTypes.JournalEntryListConverter] = InitType(assemblies[4], "System.Windows.Navigation", "JournalEntryListConverter"); - types[KnownTypes.JournalEntryUnifiedViewConverter] = InitType(assemblies[4], "System.Windows.Navigation", "JournalEntryUnifiedViewConverter"); - types[KnownTypes.JpegBitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "JpegBitmapDecoder"); - types[KnownTypes.JpegBitmapEncoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "JpegBitmapEncoder"); - types[KnownTypes.KeyBinding] = InitType(assemblies[3], "System.Windows.Input", "KeyBinding"); - types[KnownTypes.KeyConverter] = InitType(assemblies[2], "System.Windows.Input", "KeyConverter"); - types[KnownTypes.KeyGesture] = InitType(assemblies[3], "System.Windows.Input", "KeyGesture"); - types[KnownTypes.KeyGestureConverter] = InitType(assemblies[3], "System.Windows.Input", "KeyGestureConverter"); - types[KnownTypes.KeySpline] = InitType(assemblies[3], "System.Windows.Media.Animation", "KeySpline"); - types[KnownTypes.KeySplineConverter] = InitType(assemblies[3], "System.Windows", "KeySplineConverter"); - types[KnownTypes.KeyTime] = InitType(assemblies[3], "System.Windows.Media.Animation", "KeyTime"); - types[KnownTypes.KeyTimeConverter] = InitType(assemblies[3], "System.Windows", "KeyTimeConverter"); - types[KnownTypes.KeyboardDevice] = InitType(assemblies[3], "System.Windows.Input", "KeyboardDevice"); - types[KnownTypes.Label] = InitType(assemblies[4], "System.Windows.Controls", "Label"); - types[KnownTypes.LateBoundBitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "LateBoundBitmapDecoder"); - types[KnownTypes.LengthConverter] = InitType(assemblies[4], "System.Windows", "LengthConverter"); - types[KnownTypes.Light] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Light"); - types[KnownTypes.Line] = InitType(assemblies[4], "System.Windows.Shapes", "Line"); - types[KnownTypes.LineBreak] = InitType(assemblies[4], "System.Windows.Documents", "LineBreak"); - types[KnownTypes.LineGeometry] = InitType(assemblies[3], "System.Windows.Media", "LineGeometry"); - types[KnownTypes.LineSegment] = InitType(assemblies[3], "System.Windows.Media", "LineSegment"); - types[KnownTypes.LinearByteKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearByteKeyFrame"); - types[KnownTypes.LinearColorKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearColorKeyFrame"); - types[KnownTypes.LinearDecimalKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearDecimalKeyFrame"); - types[KnownTypes.LinearDoubleKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearDoubleKeyFrame"); - types[KnownTypes.LinearGradientBrush] = InitType(assemblies[3], "System.Windows.Media", "LinearGradientBrush"); - types[KnownTypes.LinearInt16KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearInt16KeyFrame"); - types[KnownTypes.LinearInt32KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearInt32KeyFrame"); - types[KnownTypes.LinearInt64KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearInt64KeyFrame"); - types[KnownTypes.LinearPoint3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearPoint3DKeyFrame"); - types[KnownTypes.LinearPointKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearPointKeyFrame"); - types[KnownTypes.LinearQuaternionKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearQuaternionKeyFrame"); - types[KnownTypes.LinearRectKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearRectKeyFrame"); - types[KnownTypes.LinearRotation3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearRotation3DKeyFrame"); - types[KnownTypes.LinearSingleKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearSingleKeyFrame"); - types[KnownTypes.LinearSizeKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearSizeKeyFrame"); - types[KnownTypes.LinearThicknessKeyFrame] = InitType(assemblies[4], "System.Windows.Media.Animation", "LinearThicknessKeyFrame"); - types[KnownTypes.LinearVector3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearVector3DKeyFrame"); - types[KnownTypes.LinearVectorKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "LinearVectorKeyFrame"); - types[KnownTypes.List] = InitType(assemblies[4], "System.Windows.Documents", "List"); - types[KnownTypes.ListBox] = InitType(assemblies[4], "System.Windows.Controls", "ListBox"); - types[KnownTypes.ListBoxItem] = InitType(assemblies[4], "System.Windows.Controls", "ListBoxItem"); - types[KnownTypes.ListCollectionView] = InitType(assemblies[4], "System.Windows.Data", "ListCollectionView"); - types[KnownTypes.ListItem] = InitType(assemblies[4], "System.Windows.Documents", "ListItem"); - types[KnownTypes.ListView] = InitType(assemblies[4], "System.Windows.Controls", "ListView"); - types[KnownTypes.ListViewItem] = InitType(assemblies[4], "System.Windows.Controls", "ListViewItem"); - types[KnownTypes.Localization] = InitType(assemblies[4], "System.Windows", "Localization"); - types[KnownTypes.LostFocusEventManager] = InitType(assemblies[4], "System.Windows", "LostFocusEventManager"); - types[KnownTypes.MarkupExtension] = InitType(assemblies[2], "System.Windows.Markup", "MarkupExtension"); - types[KnownTypes.Material] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Material"); - types[KnownTypes.MaterialCollection] = InitType(assemblies[3], "System.Windows.Media.Media3D", "MaterialCollection"); - types[KnownTypes.MaterialGroup] = InitType(assemblies[3], "System.Windows.Media.Media3D", "MaterialGroup"); - types[KnownTypes.Matrix] = InitType(assemblies[2], "System.Windows.Media", "Matrix"); - types[KnownTypes.Matrix3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Matrix3D"); - types[KnownTypes.Matrix3DConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Matrix3DConverter"); - types[KnownTypes.MatrixAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "MatrixAnimationBase"); - types[KnownTypes.MatrixAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "MatrixAnimationUsingKeyFrames"); - types[KnownTypes.MatrixAnimationUsingPath] = InitType(assemblies[3], "System.Windows.Media.Animation", "MatrixAnimationUsingPath"); - types[KnownTypes.MatrixCamera] = InitType(assemblies[3], "System.Windows.Media.Media3D", "MatrixCamera"); - types[KnownTypes.MatrixConverter] = InitType(assemblies[2], "System.Windows.Media", "MatrixConverter"); - types[KnownTypes.MatrixKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "MatrixKeyFrame"); - types[KnownTypes.MatrixKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "MatrixKeyFrameCollection"); - types[KnownTypes.MatrixTransform] = InitType(assemblies[3], "System.Windows.Media", "MatrixTransform"); - types[KnownTypes.MatrixTransform3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "MatrixTransform3D"); - types[KnownTypes.MediaClock] = InitType(assemblies[3], "System.Windows.Media", "MediaClock"); - types[KnownTypes.MediaElement] = InitType(assemblies[4], "System.Windows.Controls", "MediaElement"); - types[KnownTypes.MediaPlayer] = InitType(assemblies[3], "System.Windows.Media", "MediaPlayer"); - types[KnownTypes.MediaTimeline] = InitType(assemblies[3], "System.Windows.Media", "MediaTimeline"); - types[KnownTypes.Menu] = InitType(assemblies[4], "System.Windows.Controls", "Menu"); - types[KnownTypes.MenuBase] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "MenuBase"); - types[KnownTypes.MenuItem] = InitType(assemblies[4], "System.Windows.Controls", "MenuItem"); - types[KnownTypes.MenuScrollingVisibilityConverter] = InitType(assemblies[4], "System.Windows.Controls", "MenuScrollingVisibilityConverter"); - types[KnownTypes.MeshGeometry3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "MeshGeometry3D"); - types[KnownTypes.Model3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Model3D"); - types[KnownTypes.Model3DCollection] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Model3DCollection"); - types[KnownTypes.Model3DGroup] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Model3DGroup"); - types[KnownTypes.ModelVisual3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "ModelVisual3D"); - types[KnownTypes.ModifierKeysConverter] = InitType(assemblies[2], "System.Windows.Input", "ModifierKeysConverter"); - types[KnownTypes.MouseActionConverter] = InitType(assemblies[3], "System.Windows.Input", "MouseActionConverter"); - types[KnownTypes.MouseBinding] = InitType(assemblies[3], "System.Windows.Input", "MouseBinding"); - types[KnownTypes.MouseDevice] = InitType(assemblies[3], "System.Windows.Input", "MouseDevice"); - types[KnownTypes.MouseGesture] = InitType(assemblies[3], "System.Windows.Input", "MouseGesture"); - types[KnownTypes.MouseGestureConverter] = InitType(assemblies[3], "System.Windows.Input", "MouseGestureConverter"); - types[KnownTypes.MultiBinding] = InitType(assemblies[4], "System.Windows.Data", "MultiBinding"); - types[KnownTypes.MultiBindingExpression] = InitType(assemblies[4], "System.Windows.Data", "MultiBindingExpression"); - types[KnownTypes.MultiDataTrigger] = InitType(assemblies[4], "System.Windows", "MultiDataTrigger"); - types[KnownTypes.MultiTrigger] = InitType(assemblies[4], "System.Windows", "MultiTrigger"); - types[KnownTypes.NameScope] = InitType(assemblies[4], "System.Windows", "NameScope"); - types[KnownTypes.NavigationWindow] = InitType(assemblies[4], "System.Windows.Navigation", "NavigationWindow"); - types[KnownTypes.NullExtension] = InitType(assemblies[4], "System.Windows.Markup", "NullExtension"); - types[KnownTypes.NullableBoolConverter] = InitType(assemblies[4], "System.Windows", "NullableBoolConverter"); - types[KnownTypes.NullableConverter] = InitType(assemblies[1], "System.ComponentModel", "NullableConverter"); - types[KnownTypes.NumberSubstitution] = InitType(assemblies[3], "System.Windows.Media", "NumberSubstitution"); - types[KnownTypes.Object] = InitType(assemblies[0], "System", "Object"); - types[KnownTypes.ObjectAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "ObjectAnimationBase"); - types[KnownTypes.ObjectAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "ObjectAnimationUsingKeyFrames"); - types[KnownTypes.ObjectDataProvider] = InitType(assemblies[4], "System.Windows.Data", "ObjectDataProvider"); - types[KnownTypes.ObjectKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "ObjectKeyFrame"); - types[KnownTypes.ObjectKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "ObjectKeyFrameCollection"); - types[KnownTypes.OrthographicCamera] = InitType(assemblies[3], "System.Windows.Media.Media3D", "OrthographicCamera"); - types[KnownTypes.OuterGlowBitmapEffect] = InitType(assemblies[3], "System.Windows.Media.Effects", "OuterGlowBitmapEffect"); - types[KnownTypes.Page] = InitType(assemblies[4], "System.Windows.Controls", "Page"); - types[KnownTypes.PageContent] = InitType(assemblies[4], "System.Windows.Documents", "PageContent"); - types[KnownTypes.PageFunctionBase] = InitType(assemblies[4], "System.Windows.Navigation", "PageFunctionBase"); - types[KnownTypes.Panel] = InitType(assemblies[4], "System.Windows.Controls", "Panel"); - types[KnownTypes.Paragraph] = InitType(assemblies[4], "System.Windows.Documents", "Paragraph"); - types[KnownTypes.ParallelTimeline] = InitType(assemblies[3], "System.Windows.Media.Animation", "ParallelTimeline"); - types[KnownTypes.ParserContext] = InitType(assemblies[4], "System.Windows.Markup", "ParserContext"); - types[KnownTypes.PasswordBox] = InitType(assemblies[4], "System.Windows.Controls", "PasswordBox"); - types[KnownTypes.Path] = InitType(assemblies[4], "System.Windows.Shapes", "Path"); - types[KnownTypes.PathFigure] = InitType(assemblies[3], "System.Windows.Media", "PathFigure"); - types[KnownTypes.PathFigureCollection] = InitType(assemblies[3], "System.Windows.Media", "PathFigureCollection"); - types[KnownTypes.PathFigureCollectionConverter] = InitType(assemblies[3], "System.Windows.Media", "PathFigureCollectionConverter"); - types[KnownTypes.PathGeometry] = InitType(assemblies[3], "System.Windows.Media", "PathGeometry"); - types[KnownTypes.PathSegment] = InitType(assemblies[3], "System.Windows.Media", "PathSegment"); - types[KnownTypes.PathSegmentCollection] = InitType(assemblies[3], "System.Windows.Media", "PathSegmentCollection"); - types[KnownTypes.PauseStoryboard] = InitType(assemblies[4], "System.Windows.Media.Animation", "PauseStoryboard"); - types[KnownTypes.Pen] = InitType(assemblies[3], "System.Windows.Media", "Pen"); - types[KnownTypes.PerspectiveCamera] = InitType(assemblies[3], "System.Windows.Media.Media3D", "PerspectiveCamera"); - types[KnownTypes.PixelFormat] = InitType(assemblies[3], "System.Windows.Media", "PixelFormat"); - types[KnownTypes.PixelFormatConverter] = InitType(assemblies[3], "System.Windows.Media", "PixelFormatConverter"); - types[KnownTypes.PngBitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "PngBitmapDecoder"); - types[KnownTypes.PngBitmapEncoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "PngBitmapEncoder"); - types[KnownTypes.Point] = InitType(assemblies[2], "System.Windows", "Point"); - types[KnownTypes.Point3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Point3D"); - types[KnownTypes.Point3DAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "Point3DAnimation"); - types[KnownTypes.Point3DAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "Point3DAnimationBase"); - types[KnownTypes.Point3DAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "Point3DAnimationUsingKeyFrames"); - types[KnownTypes.Point3DCollection] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Point3DCollection"); - types[KnownTypes.Point3DCollectionConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Point3DCollectionConverter"); - types[KnownTypes.Point3DConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Point3DConverter"); - types[KnownTypes.Point3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "Point3DKeyFrame"); - types[KnownTypes.Point3DKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "Point3DKeyFrameCollection"); - types[KnownTypes.Point4D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Point4D"); - types[KnownTypes.Point4DConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Point4DConverter"); - types[KnownTypes.PointAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "PointAnimation"); - types[KnownTypes.PointAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "PointAnimationBase"); - types[KnownTypes.PointAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "PointAnimationUsingKeyFrames"); - types[KnownTypes.PointAnimationUsingPath] = InitType(assemblies[3], "System.Windows.Media.Animation", "PointAnimationUsingPath"); - types[KnownTypes.PointCollection] = InitType(assemblies[3], "System.Windows.Media", "PointCollection"); - types[KnownTypes.PointCollectionConverter] = InitType(assemblies[3], "System.Windows.Media", "PointCollectionConverter"); - types[KnownTypes.PointConverter] = InitType(assemblies[2], "System.Windows", "PointConverter"); - types[KnownTypes.PointIListConverter] = InitType(assemblies[3], "System.Windows.Media.Converters", "PointIListConverter"); - types[KnownTypes.PointKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "PointKeyFrame"); - types[KnownTypes.PointKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "PointKeyFrameCollection"); - types[KnownTypes.PointLight] = InitType(assemblies[3], "System.Windows.Media.Media3D", "PointLight"); - types[KnownTypes.PointLightBase] = InitType(assemblies[3], "System.Windows.Media.Media3D", "PointLightBase"); - types[KnownTypes.PolyBezierSegment] = InitType(assemblies[3], "System.Windows.Media", "PolyBezierSegment"); - types[KnownTypes.PolyLineSegment] = InitType(assemblies[3], "System.Windows.Media", "PolyLineSegment"); - types[KnownTypes.PolyQuadraticBezierSegment] = InitType(assemblies[3], "System.Windows.Media", "PolyQuadraticBezierSegment"); - types[KnownTypes.Polygon] = InitType(assemblies[4], "System.Windows.Shapes", "Polygon"); - types[KnownTypes.Polyline] = InitType(assemblies[4], "System.Windows.Shapes", "Polyline"); - types[KnownTypes.Popup] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "Popup"); - types[KnownTypes.PresentationSource] = InitType(assemblies[3], "System.Windows", "PresentationSource"); - types[KnownTypes.PriorityBinding] = InitType(assemblies[4], "System.Windows.Data", "PriorityBinding"); - types[KnownTypes.PriorityBindingExpression] = InitType(assemblies[4], "System.Windows.Data", "PriorityBindingExpression"); - types[KnownTypes.ProgressBar] = InitType(assemblies[4], "System.Windows.Controls", "ProgressBar"); - types[KnownTypes.ProjectionCamera] = InitType(assemblies[3], "System.Windows.Media.Media3D", "ProjectionCamera"); - types[KnownTypes.PropertyPath] = InitType(assemblies[4], "System.Windows", "PropertyPath"); - types[KnownTypes.PropertyPathConverter] = InitType(assemblies[4], "System.Windows", "PropertyPathConverter"); - types[KnownTypes.QuadraticBezierSegment] = InitType(assemblies[3], "System.Windows.Media", "QuadraticBezierSegment"); - types[KnownTypes.Quaternion] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Quaternion"); - types[KnownTypes.QuaternionAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "QuaternionAnimation"); - types[KnownTypes.QuaternionAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "QuaternionAnimationBase"); - types[KnownTypes.QuaternionAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "QuaternionAnimationUsingKeyFrames"); - types[KnownTypes.QuaternionConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "QuaternionConverter"); - types[KnownTypes.QuaternionKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "QuaternionKeyFrame"); - types[KnownTypes.QuaternionKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "QuaternionKeyFrameCollection"); - types[KnownTypes.QuaternionRotation3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "QuaternionRotation3D"); - types[KnownTypes.RadialGradientBrush] = InitType(assemblies[3], "System.Windows.Media", "RadialGradientBrush"); - types[KnownTypes.RadioButton] = InitType(assemblies[4], "System.Windows.Controls", "RadioButton"); - types[KnownTypes.RangeBase] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "RangeBase"); - types[KnownTypes.Rect] = InitType(assemblies[2], "System.Windows", "Rect"); - types[KnownTypes.Rect3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Rect3D"); - types[KnownTypes.Rect3DConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Rect3DConverter"); - types[KnownTypes.RectAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "RectAnimation"); - types[KnownTypes.RectAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "RectAnimationBase"); - types[KnownTypes.RectAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "RectAnimationUsingKeyFrames"); - types[KnownTypes.RectConverter] = InitType(assemblies[2], "System.Windows", "RectConverter"); - types[KnownTypes.RectKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "RectKeyFrame"); - types[KnownTypes.RectKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "RectKeyFrameCollection"); - types[KnownTypes.Rectangle] = InitType(assemblies[4], "System.Windows.Shapes", "Rectangle"); - types[KnownTypes.RectangleGeometry] = InitType(assemblies[3], "System.Windows.Media", "RectangleGeometry"); - types[KnownTypes.RelativeSource] = InitType(assemblies[4], "System.Windows.Data", "RelativeSource"); - types[KnownTypes.RemoveStoryboard] = InitType(assemblies[4], "System.Windows.Media.Animation", "RemoveStoryboard"); - types[KnownTypes.RenderOptions] = InitType(assemblies[3], "System.Windows.Media", "RenderOptions"); - types[KnownTypes.RenderTargetBitmap] = InitType(assemblies[3], "System.Windows.Media.Imaging", "RenderTargetBitmap"); - types[KnownTypes.RepeatBehavior] = InitType(assemblies[3], "System.Windows.Media.Animation", "RepeatBehavior"); - types[KnownTypes.RepeatBehaviorConverter] = InitType(assemblies[3], "System.Windows.Media.Animation", "RepeatBehaviorConverter"); - types[KnownTypes.RepeatButton] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "RepeatButton"); - types[KnownTypes.ResizeGrip] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "ResizeGrip"); - types[KnownTypes.ResourceDictionary] = InitType(assemblies[4], "System.Windows", "ResourceDictionary"); - types[KnownTypes.ResourceKey] = InitType(assemblies[4], "System.Windows", "ResourceKey"); - types[KnownTypes.ResumeStoryboard] = InitType(assemblies[4], "System.Windows.Media.Animation", "ResumeStoryboard"); - types[KnownTypes.RichTextBox] = InitType(assemblies[4], "System.Windows.Controls", "RichTextBox"); - types[KnownTypes.RotateTransform] = InitType(assemblies[3], "System.Windows.Media", "RotateTransform"); - types[KnownTypes.RotateTransform3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "RotateTransform3D"); - types[KnownTypes.Rotation3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Rotation3D"); - types[KnownTypes.Rotation3DAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "Rotation3DAnimation"); - types[KnownTypes.Rotation3DAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "Rotation3DAnimationBase"); - types[KnownTypes.Rotation3DAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "Rotation3DAnimationUsingKeyFrames"); - types[KnownTypes.Rotation3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "Rotation3DKeyFrame"); - types[KnownTypes.Rotation3DKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "Rotation3DKeyFrameCollection"); - types[KnownTypes.RoutedCommand] = InitType(assemblies[3], "System.Windows.Input", "RoutedCommand"); - types[KnownTypes.RoutedEvent] = InitType(assemblies[3], "System.Windows", "RoutedEvent"); - types[KnownTypes.RoutedEventConverter] = InitType(assemblies[4], "System.Windows.Markup", "RoutedEventConverter"); - types[KnownTypes.RoutedUICommand] = InitType(assemblies[3], "System.Windows.Input", "RoutedUICommand"); - types[KnownTypes.RoutingStrategy] = InitType(assemblies[3], "System.Windows", "RoutingStrategy"); - types[KnownTypes.RowDefinition] = InitType(assemblies[4], "System.Windows.Controls", "RowDefinition"); - types[KnownTypes.Run] = InitType(assemblies[4], "System.Windows.Documents", "Run"); - types[KnownTypes.RuntimeNamePropertyAttribute] = InitType(assemblies[2], "System.Windows.Markup", "RuntimeNamePropertyAttribute"); - types[KnownTypes.SByte] = InitType(assemblies[0], "System", "SByte"); - types[KnownTypes.SByteConverter] = InitType(assemblies[1], "System.ComponentModel", "SByteConverter"); - types[KnownTypes.ScaleTransform] = InitType(assemblies[3], "System.Windows.Media", "ScaleTransform"); - types[KnownTypes.ScaleTransform3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "ScaleTransform3D"); - types[KnownTypes.ScrollBar] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "ScrollBar"); - types[KnownTypes.ScrollContentPresenter] = InitType(assemblies[4], "System.Windows.Controls", "ScrollContentPresenter"); - types[KnownTypes.ScrollViewer] = InitType(assemblies[4], "System.Windows.Controls", "ScrollViewer"); - types[KnownTypes.Section] = InitType(assemblies[4], "System.Windows.Documents", "Section"); - types[KnownTypes.SeekStoryboard] = InitType(assemblies[4], "System.Windows.Media.Animation", "SeekStoryboard"); - types[KnownTypes.Selector] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "Selector"); - types[KnownTypes.Separator] = InitType(assemblies[4], "System.Windows.Controls", "Separator"); - types[KnownTypes.SetStoryboardSpeedRatio] = InitType(assemblies[4], "System.Windows.Media.Animation", "SetStoryboardSpeedRatio"); - types[KnownTypes.Setter] = InitType(assemblies[4], "System.Windows", "Setter"); - types[KnownTypes.SetterBase] = InitType(assemblies[4], "System.Windows", "SetterBase"); - types[KnownTypes.Shape] = InitType(assemblies[4], "System.Windows.Shapes", "Shape"); - types[KnownTypes.Single] = InitType(assemblies[0], "System", "Single"); - types[KnownTypes.SingleAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "SingleAnimation"); - types[KnownTypes.SingleAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "SingleAnimationBase"); - types[KnownTypes.SingleAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "SingleAnimationUsingKeyFrames"); - types[KnownTypes.SingleConverter] = InitType(assemblies[1], "System.ComponentModel", "SingleConverter"); - types[KnownTypes.SingleKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SingleKeyFrame"); - types[KnownTypes.SingleKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "SingleKeyFrameCollection"); - types[KnownTypes.Size] = InitType(assemblies[2], "System.Windows", "Size"); - types[KnownTypes.Size3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Size3D"); - types[KnownTypes.Size3DConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Size3DConverter"); - types[KnownTypes.SizeAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "SizeAnimation"); - types[KnownTypes.SizeAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "SizeAnimationBase"); - types[KnownTypes.SizeAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "SizeAnimationUsingKeyFrames"); - types[KnownTypes.SizeConverter] = InitType(assemblies[2], "System.Windows", "SizeConverter"); - types[KnownTypes.SizeKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SizeKeyFrame"); - types[KnownTypes.SizeKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "SizeKeyFrameCollection"); - types[KnownTypes.SkewTransform] = InitType(assemblies[3], "System.Windows.Media", "SkewTransform"); - types[KnownTypes.SkipStoryboardToFill] = InitType(assemblies[4], "System.Windows.Media.Animation", "SkipStoryboardToFill"); - types[KnownTypes.Slider] = InitType(assemblies[4], "System.Windows.Controls", "Slider"); - types[KnownTypes.SolidColorBrush] = InitType(assemblies[3], "System.Windows.Media", "SolidColorBrush"); - types[KnownTypes.SoundPlayerAction] = InitType(assemblies[4], "System.Windows.Controls", "SoundPlayerAction"); - types[KnownTypes.Span] = InitType(assemblies[4], "System.Windows.Documents", "Span"); - types[KnownTypes.SpecularMaterial] = InitType(assemblies[3], "System.Windows.Media.Media3D", "SpecularMaterial"); - types[KnownTypes.SpellCheck] = InitType(assemblies[4], "System.Windows.Controls", "SpellCheck"); - types[KnownTypes.SplineByteKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineByteKeyFrame"); - types[KnownTypes.SplineColorKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineColorKeyFrame"); - types[KnownTypes.SplineDecimalKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineDecimalKeyFrame"); - types[KnownTypes.SplineDoubleKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineDoubleKeyFrame"); - types[KnownTypes.SplineInt16KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineInt16KeyFrame"); - types[KnownTypes.SplineInt32KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineInt32KeyFrame"); - types[KnownTypes.SplineInt64KeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineInt64KeyFrame"); - types[KnownTypes.SplinePoint3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplinePoint3DKeyFrame"); - types[KnownTypes.SplinePointKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplinePointKeyFrame"); - types[KnownTypes.SplineQuaternionKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineQuaternionKeyFrame"); - types[KnownTypes.SplineRectKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineRectKeyFrame"); - types[KnownTypes.SplineRotation3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineRotation3DKeyFrame"); - types[KnownTypes.SplineSingleKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineSingleKeyFrame"); - types[KnownTypes.SplineSizeKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineSizeKeyFrame"); - types[KnownTypes.SplineThicknessKeyFrame] = InitType(assemblies[4], "System.Windows.Media.Animation", "SplineThicknessKeyFrame"); - types[KnownTypes.SplineVector3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineVector3DKeyFrame"); - types[KnownTypes.SplineVectorKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "SplineVectorKeyFrame"); - types[KnownTypes.SpotLight] = InitType(assemblies[3], "System.Windows.Media.Media3D", "SpotLight"); - types[KnownTypes.StackPanel] = InitType(assemblies[4], "System.Windows.Controls", "StackPanel"); - types[KnownTypes.StaticExtension] = InitType(assemblies[4], "System.Windows.Markup", "StaticExtension"); - types[KnownTypes.StaticResourceExtension] = InitType(assemblies[4], "System.Windows", "StaticResourceExtension"); - types[KnownTypes.StatusBar] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "StatusBar"); - types[KnownTypes.StatusBarItem] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "StatusBarItem"); - types[KnownTypes.StickyNoteControl] = InitType(assemblies[4], "System.Windows.Controls", "StickyNoteControl"); - types[KnownTypes.StopStoryboard] = InitType(assemblies[4], "System.Windows.Media.Animation", "StopStoryboard"); - types[KnownTypes.Storyboard] = InitType(assemblies[4], "System.Windows.Media.Animation", "Storyboard"); - types[KnownTypes.StreamGeometry] = InitType(assemblies[3], "System.Windows.Media", "StreamGeometry"); - types[KnownTypes.StreamGeometryContext] = InitType(assemblies[3], "System.Windows.Media", "StreamGeometryContext"); - types[KnownTypes.StreamResourceInfo] = InitType(assemblies[4], "System.Windows.Resources", "StreamResourceInfo"); - types[KnownTypes.String] = InitType(assemblies[0], "System", "String"); - types[KnownTypes.StringAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "StringAnimationBase"); - types[KnownTypes.StringAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "StringAnimationUsingKeyFrames"); - types[KnownTypes.StringConverter] = InitType(assemblies[1], "System.ComponentModel", "StringConverter"); - types[KnownTypes.StringKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "StringKeyFrame"); - types[KnownTypes.StringKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "StringKeyFrameCollection"); - types[KnownTypes.StrokeCollection] = InitType(assemblies[3], "System.Windows.Ink", "StrokeCollection"); - types[KnownTypes.StrokeCollectionConverter] = InitType(assemblies[3], "System.Windows", "StrokeCollectionConverter"); - types[KnownTypes.Style] = InitType(assemblies[4], "System.Windows", "Style"); - types[KnownTypes.Stylus] = InitType(assemblies[3], "System.Windows.Input", "Stylus"); - types[KnownTypes.StylusDevice] = InitType(assemblies[3], "System.Windows.Input", "StylusDevice"); - types[KnownTypes.TabControl] = InitType(assemblies[4], "System.Windows.Controls", "TabControl"); - types[KnownTypes.TabItem] = InitType(assemblies[4], "System.Windows.Controls", "TabItem"); - types[KnownTypes.TabPanel] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "TabPanel"); - types[KnownTypes.Table] = InitType(assemblies[4], "System.Windows.Documents", "Table"); - types[KnownTypes.TableCell] = InitType(assemblies[4], "System.Windows.Documents", "TableCell"); - types[KnownTypes.TableColumn] = InitType(assemblies[4], "System.Windows.Documents", "TableColumn"); - types[KnownTypes.TableRow] = InitType(assemblies[4], "System.Windows.Documents", "TableRow"); - types[KnownTypes.TableRowGroup] = InitType(assemblies[4], "System.Windows.Documents", "TableRowGroup"); - types[KnownTypes.TabletDevice] = InitType(assemblies[3], "System.Windows.Input", "TabletDevice"); - types[KnownTypes.TemplateBindingExpression] = InitType(assemblies[4], "System.Windows", "TemplateBindingExpression"); - types[KnownTypes.TemplateBindingExpressionConverter] = InitType(assemblies[4], "System.Windows", "TemplateBindingExpressionConverter"); - types[KnownTypes.TemplateBindingExtension] = InitType(assemblies[4], "System.Windows", "TemplateBindingExtension"); - types[KnownTypes.TemplateBindingExtensionConverter] = InitType(assemblies[4], "System.Windows", "TemplateBindingExtensionConverter"); - types[KnownTypes.TemplateKey] = InitType(assemblies[4], "System.Windows", "TemplateKey"); - types[KnownTypes.TemplateKeyConverter] = InitType(assemblies[4], "System.Windows.Markup", "TemplateKeyConverter"); - types[KnownTypes.TextBlock] = InitType(assemblies[4], "System.Windows.Controls", "TextBlock"); - types[KnownTypes.TextBox] = InitType(assemblies[4], "System.Windows.Controls", "TextBox"); - types[KnownTypes.TextBoxBase] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "TextBoxBase"); - types[KnownTypes.TextComposition] = InitType(assemblies[3], "System.Windows.Input", "TextComposition"); - types[KnownTypes.TextCompositionManager] = InitType(assemblies[3], "System.Windows.Input", "TextCompositionManager"); - types[KnownTypes.TextDecoration] = InitType(assemblies[3], "System.Windows", "TextDecoration"); - types[KnownTypes.TextDecorationCollection] = InitType(assemblies[3], "System.Windows", "TextDecorationCollection"); - types[KnownTypes.TextDecorationCollectionConverter] = InitType(assemblies[3], "System.Windows", "TextDecorationCollectionConverter"); - types[KnownTypes.TextEffect] = InitType(assemblies[3], "System.Windows.Media", "TextEffect"); - types[KnownTypes.TextEffectCollection] = InitType(assemblies[3], "System.Windows.Media", "TextEffectCollection"); - types[KnownTypes.TextElement] = InitType(assemblies[4], "System.Windows.Documents", "TextElement"); - types[KnownTypes.TextSearch] = InitType(assemblies[4], "System.Windows.Controls", "TextSearch"); - types[KnownTypes.ThemeDictionaryExtension] = InitType(assemblies[4], "System.Windows", "ThemeDictionaryExtension"); - types[KnownTypes.Thickness] = InitType(assemblies[4], "System.Windows", "Thickness"); - types[KnownTypes.ThicknessAnimation] = InitType(assemblies[4], "System.Windows.Media.Animation", "ThicknessAnimation"); - types[KnownTypes.ThicknessAnimationBase] = InitType(assemblies[4], "System.Windows.Media.Animation", "ThicknessAnimationBase"); - types[KnownTypes.ThicknessAnimationUsingKeyFrames] = InitType(assemblies[4], "System.Windows.Media.Animation", "ThicknessAnimationUsingKeyFrames"); - types[KnownTypes.ThicknessConverter] = InitType(assemblies[4], "System.Windows", "ThicknessConverter"); - types[KnownTypes.ThicknessKeyFrame] = InitType(assemblies[4], "System.Windows.Media.Animation", "ThicknessKeyFrame"); - types[KnownTypes.ThicknessKeyFrameCollection] = InitType(assemblies[4], "System.Windows.Media.Animation", "ThicknessKeyFrameCollection"); - types[KnownTypes.Thumb] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "Thumb"); - types[KnownTypes.TickBar] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "TickBar"); - types[KnownTypes.TiffBitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "TiffBitmapDecoder"); - types[KnownTypes.TiffBitmapEncoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "TiffBitmapEncoder"); - types[KnownTypes.TileBrush] = InitType(assemblies[3], "System.Windows.Media", "TileBrush"); - types[KnownTypes.TimeSpan] = InitType(assemblies[0], "System", "TimeSpan"); - types[KnownTypes.TimeSpanConverter] = InitType(assemblies[1], "System.ComponentModel", "TimeSpanConverter"); - types[KnownTypes.Timeline] = InitType(assemblies[3], "System.Windows.Media.Animation", "Timeline"); - types[KnownTypes.TimelineCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "TimelineCollection"); - types[KnownTypes.TimelineGroup] = InitType(assemblies[3], "System.Windows.Media.Animation", "TimelineGroup"); - types[KnownTypes.ToggleButton] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "ToggleButton"); - types[KnownTypes.ToolBar] = InitType(assemblies[4], "System.Windows.Controls", "ToolBar"); - types[KnownTypes.ToolBarOverflowPanel] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "ToolBarOverflowPanel"); - types[KnownTypes.ToolBarPanel] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "ToolBarPanel"); - types[KnownTypes.ToolBarTray] = InitType(assemblies[4], "System.Windows.Controls", "ToolBarTray"); - types[KnownTypes.ToolTip] = InitType(assemblies[4], "System.Windows.Controls", "ToolTip"); - types[KnownTypes.ToolTipService] = InitType(assemblies[4], "System.Windows.Controls", "ToolTipService"); - types[KnownTypes.Track] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "Track"); - types[KnownTypes.Transform] = InitType(assemblies[3], "System.Windows.Media", "Transform"); - types[KnownTypes.Transform3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Transform3D"); - types[KnownTypes.Transform3DCollection] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Transform3DCollection"); - types[KnownTypes.Transform3DGroup] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Transform3DGroup"); - types[KnownTypes.TransformCollection] = InitType(assemblies[3], "System.Windows.Media", "TransformCollection"); - types[KnownTypes.TransformConverter] = InitType(assemblies[3], "System.Windows.Media", "TransformConverter"); - types[KnownTypes.TransformGroup] = InitType(assemblies[3], "System.Windows.Media", "TransformGroup"); - types[KnownTypes.TransformedBitmap] = InitType(assemblies[3], "System.Windows.Media.Imaging", "TransformedBitmap"); - types[KnownTypes.TranslateTransform] = InitType(assemblies[3], "System.Windows.Media", "TranslateTransform"); - types[KnownTypes.TranslateTransform3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "TranslateTransform3D"); - types[KnownTypes.TreeView] = InitType(assemblies[4], "System.Windows.Controls", "TreeView"); - types[KnownTypes.TreeViewItem] = InitType(assemblies[4], "System.Windows.Controls", "TreeViewItem"); - types[KnownTypes.Trigger] = InitType(assemblies[4], "System.Windows", "Trigger"); - types[KnownTypes.TriggerAction] = InitType(assemblies[4], "System.Windows", "TriggerAction"); - types[KnownTypes.TriggerBase] = InitType(assemblies[4], "System.Windows", "TriggerBase"); - types[KnownTypes.TypeExtension] = InitType(assemblies[4], "System.Windows.Markup", "TypeExtension"); - types[KnownTypes.TypeTypeConverter] = InitType(assemblies[2], "System.Windows.Markup", "TypeTypeConverter"); - types[KnownTypes.Typography] = InitType(assemblies[4], "System.Windows.Documents", "Typography"); - types[KnownTypes.UIElement] = InitType(assemblies[3], "System.Windows", "UIElement"); - types[KnownTypes.UInt16] = InitType(assemblies[0], "System", "UInt16"); - types[KnownTypes.UInt16Converter] = InitType(assemblies[1], "System.ComponentModel", "UInt16Converter"); - types[KnownTypes.UInt32] = InitType(assemblies[0], "System", "UInt32"); - types[KnownTypes.UInt32Converter] = InitType(assemblies[1], "System.ComponentModel", "UInt32Converter"); - types[KnownTypes.UInt64] = InitType(assemblies[0], "System", "UInt64"); - types[KnownTypes.UInt64Converter] = InitType(assemblies[1], "System.ComponentModel", "UInt64Converter"); - types[KnownTypes.UShortIListConverter] = InitType(assemblies[3], "System.Windows.Media.Converters", "UShortIListConverter"); - types[KnownTypes.Underline] = InitType(assemblies[4], "System.Windows.Documents", "Underline"); - types[KnownTypes.UniformGrid] = InitType(assemblies[4], "System.Windows.Controls.Primitives", "UniformGrid"); - types[KnownTypes.Uri] = InitType(assemblies[1], "System", "Uri"); - types[KnownTypes.UriTypeConverter] = InitType(assemblies[1], "System", "UriTypeConverter"); - types[KnownTypes.UserControl] = InitType(assemblies[4], "System.Windows.Controls", "UserControl"); - types[KnownTypes.Validation] = InitType(assemblies[4], "System.Windows.Controls", "Validation"); - types[KnownTypes.Vector] = InitType(assemblies[2], "System.Windows", "Vector"); - types[KnownTypes.Vector3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Vector3D"); - types[KnownTypes.Vector3DAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "Vector3DAnimation"); - types[KnownTypes.Vector3DAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "Vector3DAnimationBase"); - types[KnownTypes.Vector3DAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "Vector3DAnimationUsingKeyFrames"); - types[KnownTypes.Vector3DCollection] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Vector3DCollection"); - types[KnownTypes.Vector3DCollectionConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Vector3DCollectionConverter"); - types[KnownTypes.Vector3DConverter] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Vector3DConverter"); - types[KnownTypes.Vector3DKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "Vector3DKeyFrame"); - types[KnownTypes.Vector3DKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "Vector3DKeyFrameCollection"); - types[KnownTypes.VectorAnimation] = InitType(assemblies[3], "System.Windows.Media.Animation", "VectorAnimation"); - types[KnownTypes.VectorAnimationBase] = InitType(assemblies[3], "System.Windows.Media.Animation", "VectorAnimationBase"); - types[KnownTypes.VectorAnimationUsingKeyFrames] = InitType(assemblies[3], "System.Windows.Media.Animation", "VectorAnimationUsingKeyFrames"); - types[KnownTypes.VectorCollection] = InitType(assemblies[3], "System.Windows.Media", "VectorCollection"); - types[KnownTypes.VectorCollectionConverter] = InitType(assemblies[3], "System.Windows.Media", "VectorCollectionConverter"); - types[KnownTypes.VectorConverter] = InitType(assemblies[2], "System.Windows", "VectorConverter"); - types[KnownTypes.VectorKeyFrame] = InitType(assemblies[3], "System.Windows.Media.Animation", "VectorKeyFrame"); - types[KnownTypes.VectorKeyFrameCollection] = InitType(assemblies[3], "System.Windows.Media.Animation", "VectorKeyFrameCollection"); - types[KnownTypes.VideoDrawing] = InitType(assemblies[3], "System.Windows.Media", "VideoDrawing"); - types[KnownTypes.ViewBase] = InitType(assemblies[4], "System.Windows.Controls", "ViewBase"); - types[KnownTypes.Viewbox] = InitType(assemblies[4], "System.Windows.Controls", "Viewbox"); - types[KnownTypes.Viewport3D] = InitType(assemblies[4], "System.Windows.Controls", "Viewport3D"); - types[KnownTypes.Viewport3DVisual] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Viewport3DVisual"); - types[KnownTypes.VirtualizingPanel] = InitType(assemblies[4], "System.Windows.Controls", "VirtualizingPanel"); - types[KnownTypes.VirtualizingStackPanel] = InitType(assemblies[4], "System.Windows.Controls", "VirtualizingStackPanel"); - types[KnownTypes.Visual] = InitType(assemblies[3], "System.Windows.Media", "Visual"); - types[KnownTypes.Visual3D] = InitType(assemblies[3], "System.Windows.Media.Media3D", "Visual3D"); - types[KnownTypes.VisualBrush] = InitType(assemblies[3], "System.Windows.Media", "VisualBrush"); - types[KnownTypes.VisualTarget] = InitType(assemblies[3], "System.Windows.Media", "VisualTarget"); - types[KnownTypes.WeakEventManager] = InitType(assemblies[2], "System.Windows", "WeakEventManager"); - types[KnownTypes.WhitespaceSignificantCollectionAttribute] = InitType(assemblies[2], "System.Windows.Markup", "WhitespaceSignificantCollectionAttribute"); - types[KnownTypes.Window] = InitType(assemblies[4], "System.Windows", "Window"); - types[KnownTypes.WmpBitmapDecoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "WmpBitmapDecoder"); - types[KnownTypes.WmpBitmapEncoder] = InitType(assemblies[3], "System.Windows.Media.Imaging", "WmpBitmapEncoder"); - types[KnownTypes.WrapPanel] = InitType(assemblies[4], "System.Windows.Controls", "WrapPanel"); - types[KnownTypes.WriteableBitmap] = InitType(assemblies[3], "System.Windows.Media.Imaging", "WriteableBitmap"); - types[KnownTypes.XamlBrushSerializer] = InitType(assemblies[4], "System.Windows.Markup", "XamlBrushSerializer"); - types[KnownTypes.XamlInt32CollectionSerializer] = InitType(assemblies[4], "System.Windows.Markup", "XamlInt32CollectionSerializer"); - types[KnownTypes.XamlPathDataSerializer] = InitType(assemblies[4], "System.Windows.Markup", "XamlPathDataSerializer"); - types[KnownTypes.XamlPoint3DCollectionSerializer] = InitType(assemblies[4], "System.Windows.Markup", "XamlPoint3DCollectionSerializer"); - types[KnownTypes.XamlPointCollectionSerializer] = InitType(assemblies[4], "System.Windows.Markup", "XamlPointCollectionSerializer"); - types[KnownTypes.XamlReader] = InitType(assemblies[4], "System.Windows.Markup", "XamlReader"); - types[KnownTypes.XamlStyleSerializer] = InitType(assemblies[4], "System.Windows.Markup", "XamlStyleSerializer"); - types[KnownTypes.XamlTemplateSerializer] = InitType(assemblies[4], "System.Windows.Markup", "XamlTemplateSerializer"); - types[KnownTypes.XamlVector3DCollectionSerializer] = InitType(assemblies[4], "System.Windows.Markup", "XamlVector3DCollectionSerializer"); - types[KnownTypes.XamlWriter] = InitType(assemblies[4], "System.Windows.Markup", "XamlWriter"); - types[KnownTypes.XmlDataProvider] = InitType(assemblies[4], "System.Windows.Data", "XmlDataProvider"); - types[KnownTypes.XmlLangPropertyAttribute] = InitType(assemblies[2], "System.Windows.Markup", "XmlLangPropertyAttribute"); - types[KnownTypes.XmlLanguage] = InitType(assemblies[3], "System.Windows.Markup", "XmlLanguage"); - types[KnownTypes.XmlLanguageConverter] = InitType(assemblies[3], "System.Windows.Markup", "XmlLanguageConverter"); - types[KnownTypes.XmlNamespaceMapping] = InitType(assemblies[4], "System.Windows.Data", "XmlNamespaceMapping"); - types[KnownTypes.ZoomPercentageConverter] = InitType(assemblies[4], "System.Windows.Documents", "ZoomPercentageConverter"); - } - - void InitMembers() { - - members[KnownMembers.AccessText_Text] = InitMember(KnownTypes.AccessText, "Text", InitType(assemblies[0], "System", "String")); - members[KnownMembers.BeginStoryboard_Storyboard] = InitMember(KnownTypes.BeginStoryboard, "Storyboard", InitType(assemblies[4], "System.Windows.Media.Animation", "Storyboard")); - members[KnownMembers.BitmapEffectGroup_Children] = InitMember(KnownTypes.BitmapEffectGroup, "Children", InitType(assemblies[3], "System.Windows.Media.Effects", "BitmapEffectCollection")); - members[KnownMembers.Border_Background] = InitMember(KnownTypes.Border, "Background", InitType(assemblies[3], "System.Windows.Media", "Brush")); - members[KnownMembers.Border_BorderBrush] = InitMember(KnownTypes.Border, "BorderBrush", InitType(assemblies[3], "System.Windows.Media", "Brush")); - members[KnownMembers.Border_BorderThickness] = InitMember(KnownTypes.Border, "BorderThickness", InitType(assemblies[4], "System.Windows", "Thickness")); - members[KnownMembers.ButtonBase_Command] = InitMember(KnownTypes.ButtonBase, "Command", InitType(assemblies[3], "System.Windows.Input", "ICommand")); - members[KnownMembers.ButtonBase_CommandParameter] = InitMember(KnownTypes.ButtonBase, "CommandParameter", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.ButtonBase_CommandTarget] = InitMember(KnownTypes.ButtonBase, "CommandTarget", InitType(assemblies[3], "System.Windows", "IInputElement")); - members[KnownMembers.ButtonBase_IsPressed] = InitMember(KnownTypes.ButtonBase, "IsPressed", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.ColumnDefinition_MaxWidth] = InitMember(KnownTypes.ColumnDefinition, "MaxWidth", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.ColumnDefinition_MinWidth] = InitMember(KnownTypes.ColumnDefinition, "MinWidth", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.ColumnDefinition_Width] = InitMember(KnownTypes.ColumnDefinition, "Width", InitType(assemblies[4], "System.Windows", "GridLength")); - members[KnownMembers.ContentControl_Content] = InitMember(KnownTypes.ContentControl, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.ContentControl_ContentTemplate] = InitMember(KnownTypes.ContentControl, "ContentTemplate", InitType(assemblies[4], "System.Windows", "DataTemplate")); - members[KnownMembers.ContentControl_ContentTemplateSelector] = InitMember(KnownTypes.ContentControl, "ContentTemplateSelector", InitType(assemblies[4], "System.Windows.Controls", "DataTemplateSelector")); - members[KnownMembers.ContentControl_HasContent] = InitMember(KnownTypes.ContentControl, "HasContent", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.ContentElement_Focusable] = InitMember(KnownTypes.ContentElement, "Focusable", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.ContentPresenter_Content] = InitMember(KnownTypes.ContentPresenter, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.ContentPresenter_ContentSource] = InitMember(KnownTypes.ContentPresenter, "ContentSource", InitType(assemblies[0], "System", "String")); - members[KnownMembers.ContentPresenter_ContentTemplate] = InitMember(KnownTypes.ContentPresenter, "ContentTemplate", InitType(assemblies[4], "System.Windows", "DataTemplate")); - members[KnownMembers.ContentPresenter_ContentTemplateSelector] = InitMember(KnownTypes.ContentPresenter, "ContentTemplateSelector", InitType(assemblies[4], "System.Windows.Controls", "DataTemplateSelector")); - members[KnownMembers.ContentPresenter_RecognizesAccessKey] = InitMember(KnownTypes.ContentPresenter, "RecognizesAccessKey", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.Control_Background] = InitMember(KnownTypes.Control, "Background", InitType(assemblies[3], "System.Windows.Media", "Brush")); - members[KnownMembers.Control_BorderBrush] = InitMember(KnownTypes.Control, "BorderBrush", InitType(assemblies[3], "System.Windows.Media", "Brush")); - members[KnownMembers.Control_BorderThickness] = InitMember(KnownTypes.Control, "BorderThickness", InitType(assemblies[4], "System.Windows", "Thickness")); - members[KnownMembers.Control_FontFamily] = InitMember(KnownTypes.Control, "FontFamily", InitType(assemblies[3], "System.Windows.Media", "FontFamily")); - members[KnownMembers.Control_FontSize] = InitMember(KnownTypes.Control, "FontSize", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.Control_FontStretch] = InitMember(KnownTypes.Control, "FontStretch", InitType(assemblies[3], "System.Windows", "FontStretch")); - members[KnownMembers.Control_FontStyle] = InitMember(KnownTypes.Control, "FontStyle", InitType(assemblies[3], "System.Windows", "FontStyle")); - members[KnownMembers.Control_FontWeight] = InitMember(KnownTypes.Control, "FontWeight", InitType(assemblies[3], "System.Windows", "FontWeight")); - members[KnownMembers.Control_Foreground] = InitMember(KnownTypes.Control, "Foreground", InitType(assemblies[3], "System.Windows.Media", "Brush")); - members[KnownMembers.Control_HorizontalContentAlignment] = InitMember(KnownTypes.Control, "HorizontalContentAlignment", InitType(assemblies[4], "System.Windows", "HorizontalAlignment")); - members[KnownMembers.Control_IsTabStop] = InitMember(KnownTypes.Control, "IsTabStop", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.Control_Padding] = InitMember(KnownTypes.Control, "Padding", InitType(assemblies[4], "System.Windows", "Thickness")); - members[KnownMembers.Control_TabIndex] = InitMember(KnownTypes.Control, "TabIndex", InitType(assemblies[0], "System", "Int32")); - members[KnownMembers.Control_Template] = InitMember(KnownTypes.Control, "Template", InitType(assemblies[4], "System.Windows.Controls", "ControlTemplate")); - members[KnownMembers.Control_VerticalContentAlignment] = InitMember(KnownTypes.Control, "VerticalContentAlignment", InitType(assemblies[4], "System.Windows", "VerticalAlignment")); - members[KnownMembers.DockPanel_Dock] = InitMember(KnownTypes.DockPanel, "Dock", InitType(assemblies[4], "System.Windows.Controls", "Dock")); - members[KnownMembers.DockPanel_LastChildFill] = InitMember(KnownTypes.DockPanel, "LastChildFill", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.DocumentViewerBase_Document] = InitMember(KnownTypes.DocumentViewerBase, "Document", InitType(assemblies[3], "System.Windows.Documents", "IDocumentPaginatorSource")); - members[KnownMembers.DrawingGroup_Children] = InitMember(KnownTypes.DrawingGroup, "Children", InitType(assemblies[3], "System.Windows.Media", "DrawingCollection")); - members[KnownMembers.FlowDocumentReader_Document] = InitMember(KnownTypes.FlowDocumentReader, "Document", InitType(assemblies[4], "System.Windows.Documents", "FlowDocument")); - members[KnownMembers.FlowDocumentScrollViewer_Document] = InitMember(KnownTypes.FlowDocumentScrollViewer, "Document", InitType(assemblies[4], "System.Windows.Documents", "FlowDocument")); - members[KnownMembers.FrameworkContentElement_Style] = InitMember(KnownTypes.FrameworkContentElement, "Style", InitType(assemblies[4], "System.Windows", "Style")); - members[KnownMembers.FrameworkElement_FlowDirection] = InitMember(KnownTypes.FrameworkElement, "FlowDirection", InitType(assemblies[3], "System.Windows", "FlowDirection")); - members[KnownMembers.FrameworkElement_Height] = InitMember(KnownTypes.FrameworkElement, "Height", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.FrameworkElement_HorizontalAlignment] = InitMember(KnownTypes.FrameworkElement, "HorizontalAlignment", InitType(assemblies[4], "System.Windows", "HorizontalAlignment")); - members[KnownMembers.FrameworkElement_Margin] = InitMember(KnownTypes.FrameworkElement, "Margin", InitType(assemblies[4], "System.Windows", "Thickness")); - members[KnownMembers.FrameworkElement_MaxHeight] = InitMember(KnownTypes.FrameworkElement, "MaxHeight", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.FrameworkElement_MaxWidth] = InitMember(KnownTypes.FrameworkElement, "MaxWidth", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.FrameworkElement_MinHeight] = InitMember(KnownTypes.FrameworkElement, "MinHeight", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.FrameworkElement_MinWidth] = InitMember(KnownTypes.FrameworkElement, "MinWidth", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.FrameworkElement_Name] = InitMember(KnownTypes.FrameworkElement, "Name", InitType(assemblies[0], "System", "String")); - members[KnownMembers.FrameworkElement_Style] = InitMember(KnownTypes.FrameworkElement, "Style", InitType(assemblies[4], "System.Windows", "Style")); - members[KnownMembers.FrameworkElement_VerticalAlignment] = InitMember(KnownTypes.FrameworkElement, "VerticalAlignment", InitType(assemblies[4], "System.Windows", "VerticalAlignment")); - members[KnownMembers.FrameworkElement_Width] = InitMember(KnownTypes.FrameworkElement, "Width", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.GeneralTransformGroup_Children] = InitMember(KnownTypes.GeneralTransformGroup, "Children", InitType(assemblies[3], "System.Windows.Media", "GeneralTransformCollection")); - members[KnownMembers.GeometryGroup_Children] = InitMember(KnownTypes.GeometryGroup, "Children", InitType(assemblies[3], "System.Windows.Media", "GeometryCollection")); - members[KnownMembers.GradientBrush_GradientStops] = InitMember(KnownTypes.GradientBrush, "GradientStops", InitType(assemblies[3], "System.Windows.Media", "GradientStopCollection")); - members[KnownMembers.Grid_Column] = InitMember(KnownTypes.Grid, "Column", InitType(assemblies[0], "System", "Int32")); - members[KnownMembers.Grid_ColumnSpan] = InitMember(KnownTypes.Grid, "ColumnSpan", InitType(assemblies[0], "System", "Int32")); - members[KnownMembers.Grid_Row] = InitMember(KnownTypes.Grid, "Row", InitType(assemblies[0], "System", "Int32")); - members[KnownMembers.Grid_RowSpan] = InitMember(KnownTypes.Grid, "RowSpan", InitType(assemblies[0], "System", "Int32")); - members[KnownMembers.GridViewColumn_Header] = InitMember(KnownTypes.GridViewColumn, "Header", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.HeaderedContentControl_HasHeader] = InitMember(KnownTypes.HeaderedContentControl, "HasHeader", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.HeaderedContentControl_Header] = InitMember(KnownTypes.HeaderedContentControl, "Header", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.HeaderedContentControl_HeaderTemplate] = InitMember(KnownTypes.HeaderedContentControl, "HeaderTemplate", InitType(assemblies[4], "System.Windows", "DataTemplate")); - members[KnownMembers.HeaderedContentControl_HeaderTemplateSelector] = InitMember(KnownTypes.HeaderedContentControl, "HeaderTemplateSelector", InitType(assemblies[4], "System.Windows.Controls", "DataTemplateSelector")); - members[KnownMembers.HeaderedItemsControl_HasHeader] = InitMember(KnownTypes.HeaderedItemsControl, "HasHeader", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.HeaderedItemsControl_Header] = InitMember(KnownTypes.HeaderedItemsControl, "Header", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.HeaderedItemsControl_HeaderTemplate] = InitMember(KnownTypes.HeaderedItemsControl, "HeaderTemplate", InitType(assemblies[4], "System.Windows", "DataTemplate")); - members[KnownMembers.HeaderedItemsControl_HeaderTemplateSelector] = InitMember(KnownTypes.HeaderedItemsControl, "HeaderTemplateSelector", InitType(assemblies[4], "System.Windows.Controls", "DataTemplateSelector")); - members[KnownMembers.Hyperlink_NavigateUri] = InitMember(KnownTypes.Hyperlink, "NavigateUri", InitType(assemblies[1], "System", "Uri")); - members[KnownMembers.Image_Source] = InitMember(KnownTypes.Image, "Source", InitType(assemblies[3], "System.Windows.Media", "ImageSource")); - members[KnownMembers.Image_Stretch] = InitMember(KnownTypes.Image, "Stretch", InitType(assemblies[3], "System.Windows.Media", "Stretch")); - members[KnownMembers.ItemsControl_ItemContainerStyle] = InitMember(KnownTypes.ItemsControl, "ItemContainerStyle", InitType(assemblies[4], "System.Windows", "Style")); - members[KnownMembers.ItemsControl_ItemContainerStyleSelector] = InitMember(KnownTypes.ItemsControl, "ItemContainerStyleSelector", InitType(assemblies[4], "System.Windows.Controls", "StyleSelector")); - members[KnownMembers.ItemsControl_ItemTemplate] = InitMember(KnownTypes.ItemsControl, "ItemTemplate", InitType(assemblies[4], "System.Windows", "DataTemplate")); - members[KnownMembers.ItemsControl_ItemTemplateSelector] = InitMember(KnownTypes.ItemsControl, "ItemTemplateSelector", InitType(assemblies[4], "System.Windows.Controls", "DataTemplateSelector")); - members[KnownMembers.ItemsControl_ItemsPanel] = InitMember(KnownTypes.ItemsControl, "ItemsPanel", InitType(assemblies[4], "System.Windows.Controls", "ItemsPanelTemplate")); - members[KnownMembers.ItemsControl_ItemsSource] = InitMember(KnownTypes.ItemsControl, "ItemsSource", InitType(assemblies[0], "System.Collections", "IEnumerable")); - members[KnownMembers.MaterialGroup_Children] = InitMember(KnownTypes.MaterialGroup, "Children", InitType(assemblies[3], "System.Windows.Media.Media3D", "MaterialCollection")); - members[KnownMembers.Model3DGroup_Children] = InitMember(KnownTypes.Model3DGroup, "Children", InitType(assemblies[3], "System.Windows.Media.Media3D", "Model3DCollection")); - members[KnownMembers.Page_Content] = InitMember(KnownTypes.Page, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.Panel_Background] = InitMember(KnownTypes.Panel, "Background", InitType(assemblies[3], "System.Windows.Media", "Brush")); - members[KnownMembers.Path_Data] = InitMember(KnownTypes.Path, "Data", InitType(assemblies[3], "System.Windows.Media", "Geometry")); - members[KnownMembers.PathFigure_Segments] = InitMember(KnownTypes.PathFigure, "Segments", InitType(assemblies[3], "System.Windows.Media", "PathSegmentCollection")); - members[KnownMembers.PathGeometry_Figures] = InitMember(KnownTypes.PathGeometry, "Figures", InitType(assemblies[3], "System.Windows.Media", "PathFigureCollection")); - members[KnownMembers.Popup_Child] = InitMember(KnownTypes.Popup, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); - members[KnownMembers.Popup_IsOpen] = InitMember(KnownTypes.Popup, "IsOpen", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.Popup_Placement] = InitMember(KnownTypes.Popup, "Placement", InitType(assemblies[4], "System.Windows.Controls.Primitives", "PlacementMode")); - members[KnownMembers.Popup_PopupAnimation] = InitMember(KnownTypes.Popup, "PopupAnimation", InitType(assemblies[4], "System.Windows.Controls.Primitives", "PopupAnimation")); - members[KnownMembers.RowDefinition_Height] = InitMember(KnownTypes.RowDefinition, "Height", InitType(assemblies[4], "System.Windows", "GridLength")); - members[KnownMembers.RowDefinition_MaxHeight] = InitMember(KnownTypes.RowDefinition, "MaxHeight", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.RowDefinition_MinHeight] = InitMember(KnownTypes.RowDefinition, "MinHeight", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.ScrollViewer_CanContentScroll] = InitMember(KnownTypes.ScrollViewer, "CanContentScroll", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.ScrollViewer_HorizontalScrollBarVisibility] = InitMember(KnownTypes.ScrollViewer, "HorizontalScrollBarVisibility", InitType(assemblies[4], "System.Windows.Controls", "ScrollBarVisibility")); - members[KnownMembers.ScrollViewer_VerticalScrollBarVisibility] = InitMember(KnownTypes.ScrollViewer, "VerticalScrollBarVisibility", InitType(assemblies[4], "System.Windows.Controls", "ScrollBarVisibility")); - members[KnownMembers.Shape_Fill] = InitMember(KnownTypes.Shape, "Fill", InitType(assemblies[3], "System.Windows.Media", "Brush")); - members[KnownMembers.Shape_Stroke] = InitMember(KnownTypes.Shape, "Stroke", InitType(assemblies[3], "System.Windows.Media", "Brush")); - members[KnownMembers.Shape_StrokeThickness] = InitMember(KnownTypes.Shape, "StrokeThickness", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.TextBlock_Background] = InitMember(KnownTypes.TextBlock, "Background", InitType(assemblies[3], "System.Windows.Media", "Brush")); - members[KnownMembers.TextBlock_FontFamily] = InitMember(KnownTypes.TextBlock, "FontFamily", InitType(assemblies[3], "System.Windows.Media", "FontFamily")); - members[KnownMembers.TextBlock_FontSize] = InitMember(KnownTypes.TextBlock, "FontSize", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.TextBlock_FontStretch] = InitMember(KnownTypes.TextBlock, "FontStretch", InitType(assemblies[3], "System.Windows", "FontStretch")); - members[KnownMembers.TextBlock_FontStyle] = InitMember(KnownTypes.TextBlock, "FontStyle", InitType(assemblies[3], "System.Windows", "FontStyle")); - members[KnownMembers.TextBlock_FontWeight] = InitMember(KnownTypes.TextBlock, "FontWeight", InitType(assemblies[3], "System.Windows", "FontWeight")); - members[KnownMembers.TextBlock_Foreground] = InitMember(KnownTypes.TextBlock, "Foreground", InitType(assemblies[3], "System.Windows.Media", "Brush")); - members[KnownMembers.TextBlock_Text] = InitMember(KnownTypes.TextBlock, "Text", InitType(assemblies[0], "System", "String")); - members[KnownMembers.TextBlock_TextDecorations] = InitMember(KnownTypes.TextBlock, "TextDecorations", InitType(assemblies[3], "System.Windows", "TextDecorationCollection")); - members[KnownMembers.TextBlock_TextTrimming] = InitMember(KnownTypes.TextBlock, "TextTrimming", InitType(assemblies[3], "System.Windows", "TextTrimming")); - members[KnownMembers.TextBlock_TextWrapping] = InitMember(KnownTypes.TextBlock, "TextWrapping", InitType(assemblies[3], "System.Windows", "TextWrapping")); - members[KnownMembers.TextBox_Text] = InitMember(KnownTypes.TextBox, "Text", InitType(assemblies[0], "System", "String")); - members[KnownMembers.TextElement_Background] = InitMember(KnownTypes.TextElement, "Background", InitType(assemblies[3], "System.Windows.Media", "Brush")); - members[KnownMembers.TextElement_FontFamily] = InitMember(KnownTypes.TextElement, "FontFamily", InitType(assemblies[3], "System.Windows.Media", "FontFamily")); - members[KnownMembers.TextElement_FontSize] = InitMember(KnownTypes.TextElement, "FontSize", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.TextElement_FontStretch] = InitMember(KnownTypes.TextElement, "FontStretch", InitType(assemblies[3], "System.Windows", "FontStretch")); - members[KnownMembers.TextElement_FontStyle] = InitMember(KnownTypes.TextElement, "FontStyle", InitType(assemblies[3], "System.Windows", "FontStyle")); - members[KnownMembers.TextElement_FontWeight] = InitMember(KnownTypes.TextElement, "FontWeight", InitType(assemblies[3], "System.Windows", "FontWeight")); - members[KnownMembers.TextElement_Foreground] = InitMember(KnownTypes.TextElement, "Foreground", InitType(assemblies[3], "System.Windows.Media", "Brush")); - members[KnownMembers.TimelineGroup_Children] = InitMember(KnownTypes.TimelineGroup, "Children", InitType(assemblies[3], "System.Windows.Media.Animation", "TimelineCollection")); - members[KnownMembers.Track_IsDirectionReversed] = InitMember(KnownTypes.Track, "IsDirectionReversed", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.Track_Maximum] = InitMember(KnownTypes.Track, "Maximum", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.Track_Minimum] = InitMember(KnownTypes.Track, "Minimum", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.Track_Orientation] = InitMember(KnownTypes.Track, "Orientation", InitType(assemblies[4], "System.Windows.Controls", "Orientation")); - members[KnownMembers.Track_Value] = InitMember(KnownTypes.Track, "Value", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.Track_ViewportSize] = InitMember(KnownTypes.Track, "ViewportSize", InitType(assemblies[0], "System", "Double")); - members[KnownMembers.Transform3DGroup_Children] = InitMember(KnownTypes.Transform3DGroup, "Children", InitType(assemblies[3], "System.Windows.Media.Media3D", "Transform3DCollection")); - members[KnownMembers.TransformGroup_Children] = InitMember(KnownTypes.TransformGroup, "Children", InitType(assemblies[3], "System.Windows.Media", "TransformCollection")); - members[KnownMembers.UIElement_ClipToBounds] = InitMember(KnownTypes.UIElement, "ClipToBounds", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.UIElement_Focusable] = InitMember(KnownTypes.UIElement, "Focusable", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.UIElement_IsEnabled] = InitMember(KnownTypes.UIElement, "IsEnabled", InitType(assemblies[0], "System", "Boolean")); - members[KnownMembers.UIElement_RenderTransform] = InitMember(KnownTypes.UIElement, "RenderTransform", InitType(assemblies[3], "System.Windows.Media", "Transform")); - members[KnownMembers.UIElement_Visibility] = InitMember(KnownTypes.UIElement, "Visibility", InitType(assemblies[3], "System.Windows", "Visibility")); - members[KnownMembers.Viewport3D_Children] = InitMember(KnownTypes.Viewport3D, "Children", InitType(assemblies[3], "System.Windows.Media.Media3D", "Visual3DCollection")); - - members[KnownMembers.AdornedElementPlaceholder_Child] = InitMember(KnownTypes.AdornedElementPlaceholder, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); - members[KnownMembers.AdornerDecorator_Child] = InitMember(KnownTypes.AdornerDecorator, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); - members[KnownMembers.AnchoredBlock_Blocks] = InitMember(KnownTypes.AnchoredBlock, "Blocks", InitType(assemblies[4], "System.Windows.Documents", "BlockCollection")); - members[KnownMembers.ArrayExtension_Items] = InitMember(KnownTypes.ArrayExtension, "Items", InitType(assemblies[0], "System.Collections", "IList")); - members[KnownMembers.BlockUIContainer_Child] = InitMember(KnownTypes.BlockUIContainer, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); - members[KnownMembers.Bold_Inlines] = InitMember(KnownTypes.Bold, "Inlines", InitType(assemblies[4], "System.Windows.Documents", "InlineCollection")); - members[KnownMembers.BooleanAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.BooleanAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "BooleanKeyFrameCollection")); - members[KnownMembers.Border_Child] = InitMember(KnownTypes.Border, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); - members[KnownMembers.BulletDecorator_Child] = InitMember(KnownTypes.BulletDecorator, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); - members[KnownMembers.Button_Content] = InitMember(KnownTypes.Button, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.ButtonBase_Content] = InitMember(KnownTypes.ButtonBase, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.ByteAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.ByteAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "ByteKeyFrameCollection")); - members[KnownMembers.Canvas_Children] = InitMember(KnownTypes.Canvas, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.CharAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.CharAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "CharKeyFrameCollection")); - members[KnownMembers.CheckBox_Content] = InitMember(KnownTypes.CheckBox, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.ColorAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.ColorAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "ColorKeyFrameCollection")); - members[KnownMembers.ComboBox_Items] = InitMember(KnownTypes.ComboBox, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.ComboBoxItem_Content] = InitMember(KnownTypes.ComboBoxItem, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.ContextMenu_Items] = InitMember(KnownTypes.ContextMenu, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.ControlTemplate_VisualTree] = InitMember(KnownTypes.ControlTemplate, "VisualTree", InitType(assemblies[4], "System.Windows", "FrameworkElementFactory")); - members[KnownMembers.DataTemplate_VisualTree] = InitMember(KnownTypes.DataTemplate, "VisualTree", InitType(assemblies[4], "System.Windows", "FrameworkElementFactory")); - members[KnownMembers.DataTrigger_Setters] = InitMember(KnownTypes.DataTrigger, "Setters", InitType(assemblies[4], "System.Windows", "SetterBaseCollection")); - members[KnownMembers.DecimalAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.DecimalAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "DecimalKeyFrameCollection")); - members[KnownMembers.Decorator_Child] = InitMember(KnownTypes.Decorator, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); - members[KnownMembers.DockPanel_Children] = InitMember(KnownTypes.DockPanel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.DocumentViewer_Document] = InitMember(KnownTypes.DocumentViewer, "Document", InitType(assemblies[3], "System.Windows.Documents", "IDocumentPaginatorSource")); - members[KnownMembers.DoubleAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.DoubleAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "DoubleKeyFrameCollection")); - members[KnownMembers.EventTrigger_Actions] = InitMember(KnownTypes.EventTrigger, "Actions", InitType(assemblies[4], "System.Windows", "TriggerActionCollection")); - members[KnownMembers.Expander_Content] = InitMember(KnownTypes.Expander, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.Figure_Blocks] = InitMember(KnownTypes.Figure, "Blocks", InitType(assemblies[4], "System.Windows.Documents", "BlockCollection")); - members[KnownMembers.FixedDocument_Pages] = InitMember(KnownTypes.FixedDocument, "Pages", InitType(assemblies[4], "System.Windows.Documents", "PageContentCollection")); - members[KnownMembers.FixedDocumentSequence_References] = InitMember(KnownTypes.FixedDocumentSequence, "References", InitType(assemblies[4], "System.Windows.Documents", "DocumentReferenceCollection")); - members[KnownMembers.FixedPage_Children] = InitMember(KnownTypes.FixedPage, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.Floater_Blocks] = InitMember(KnownTypes.Floater, "Blocks", InitType(assemblies[4], "System.Windows.Documents", "BlockCollection")); - members[KnownMembers.FlowDocument_Blocks] = InitMember(KnownTypes.FlowDocument, "Blocks", InitType(assemblies[4], "System.Windows.Documents", "BlockCollection")); - members[KnownMembers.FlowDocumentPageViewer_Document] = InitMember(KnownTypes.FlowDocumentPageViewer, "Document", InitType(assemblies[3], "System.Windows.Documents", "IDocumentPaginatorSource")); - members[KnownMembers.FrameworkTemplate_VisualTree] = InitMember(KnownTypes.FrameworkTemplate, "VisualTree", InitType(assemblies[4], "System.Windows", "FrameworkElementFactory")); - members[KnownMembers.Grid_Children] = InitMember(KnownTypes.Grid, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.GridView_Columns] = InitMember(KnownTypes.GridView, "Columns", InitType(assemblies[4], "System.Windows.Controls", "GridViewColumnCollection")); - members[KnownMembers.GridViewColumnHeader_Content] = InitMember(KnownTypes.GridViewColumnHeader, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.GroupBox_Content] = InitMember(KnownTypes.GroupBox, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.GroupItem_Content] = InitMember(KnownTypes.GroupItem, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.HeaderedContentControl_Content] = InitMember(KnownTypes.HeaderedContentControl, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.HeaderedItemsControl_Items] = InitMember(KnownTypes.HeaderedItemsControl, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.HierarchicalDataTemplate_VisualTree] = InitMember(KnownTypes.HierarchicalDataTemplate, "VisualTree", InitType(assemblies[4], "System.Windows", "FrameworkElementFactory")); - members[KnownMembers.Hyperlink_Inlines] = InitMember(KnownTypes.Hyperlink, "Inlines", InitType(assemblies[4], "System.Windows.Documents", "InlineCollection")); - members[KnownMembers.InkCanvas_Children] = InitMember(KnownTypes.InkCanvas, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.InkPresenter_Child] = InitMember(KnownTypes.InkPresenter, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); - members[KnownMembers.InlineUIContainer_Child] = InitMember(KnownTypes.InlineUIContainer, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); - members[KnownMembers.InputScopeName_NameValue] = InitMember(KnownTypes.InputScopeName, "NameValue", InitType(assemblies[3], "System.Windows.Input", "InputScopeNameValue")); - members[KnownMembers.Int16AnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.Int16AnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "Int16KeyFrameCollection")); - members[KnownMembers.Int32AnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.Int32AnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "Int32KeyFrameCollection")); - members[KnownMembers.Int64AnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.Int64AnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "Int64KeyFrameCollection")); - members[KnownMembers.Italic_Inlines] = InitMember(KnownTypes.Italic, "Inlines", InitType(assemblies[4], "System.Windows.Documents", "InlineCollection")); - members[KnownMembers.ItemsControl_Items] = InitMember(KnownTypes.ItemsControl, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.ItemsPanelTemplate_VisualTree] = InitMember(KnownTypes.ItemsPanelTemplate, "VisualTree", InitType(assemblies[4], "System.Windows", "FrameworkElementFactory")); - members[KnownMembers.Label_Content] = InitMember(KnownTypes.Label, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.LinearGradientBrush_GradientStops] = InitMember(KnownTypes.LinearGradientBrush, "GradientStops", InitType(assemblies[3], "System.Windows.Media", "GradientStopCollection")); - members[KnownMembers.List_ListItems] = InitMember(KnownTypes.List, "ListItems", InitType(assemblies[4], "System.Windows.Documents", "ListItemCollection")); - members[KnownMembers.ListBox_Items] = InitMember(KnownTypes.ListBox, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.ListBoxItem_Content] = InitMember(KnownTypes.ListBoxItem, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.ListItem_Blocks] = InitMember(KnownTypes.ListItem, "Blocks", InitType(assemblies[4], "System.Windows.Documents", "BlockCollection")); - members[KnownMembers.ListView_Items] = InitMember(KnownTypes.ListView, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.ListViewItem_Content] = InitMember(KnownTypes.ListViewItem, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.MatrixAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.MatrixAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "MatrixKeyFrameCollection")); - members[KnownMembers.Menu_Items] = InitMember(KnownTypes.Menu, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.MenuBase_Items] = InitMember(KnownTypes.MenuBase, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.MenuItem_Items] = InitMember(KnownTypes.MenuItem, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.ModelVisual3D_Children] = InitMember(KnownTypes.ModelVisual3D, "Children", InitType(assemblies[3], "System.Windows.Media.Media3D", "Visual3DCollection")); - members[KnownMembers.MultiBinding_Bindings] = InitMember(KnownTypes.MultiBinding, "Bindings", InitType(assemblies[0], "System.Collections.ObjectModel", "Collection`1")); - members[KnownMembers.MultiDataTrigger_Setters] = InitMember(KnownTypes.MultiDataTrigger, "Setters", InitType(assemblies[4], "System.Windows", "SetterBaseCollection")); - members[KnownMembers.MultiTrigger_Setters] = InitMember(KnownTypes.MultiTrigger, "Setters", InitType(assemblies[4], "System.Windows", "SetterBaseCollection")); - members[KnownMembers.ObjectAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.ObjectAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "ObjectKeyFrameCollection")); - members[KnownMembers.PageContent_Child] = InitMember(KnownTypes.PageContent, "Child", InitType(assemblies[4], "System.Windows.Documents", "FixedPage")); - members[KnownMembers.PageFunctionBase_Content] = InitMember(KnownTypes.PageFunctionBase, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.Panel_Children] = InitMember(KnownTypes.Panel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.Paragraph_Inlines] = InitMember(KnownTypes.Paragraph, "Inlines", InitType(assemblies[4], "System.Windows.Documents", "InlineCollection")); - members[KnownMembers.ParallelTimeline_Children] = InitMember(KnownTypes.ParallelTimeline, "Children", InitType(assemblies[3], "System.Windows.Media.Animation", "TimelineCollection")); - members[KnownMembers.Point3DAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.Point3DAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "Point3DKeyFrameCollection")); - members[KnownMembers.PointAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.PointAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "PointKeyFrameCollection")); - members[KnownMembers.PriorityBinding_Bindings] = InitMember(KnownTypes.PriorityBinding, "Bindings", InitType(assemblies[0], "System.Collections.ObjectModel", "Collection`1")); - members[KnownMembers.QuaternionAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.QuaternionAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "QuaternionKeyFrameCollection")); - members[KnownMembers.RadialGradientBrush_GradientStops] = InitMember(KnownTypes.RadialGradientBrush, "GradientStops", InitType(assemblies[3], "System.Windows.Media", "GradientStopCollection")); - members[KnownMembers.RadioButton_Content] = InitMember(KnownTypes.RadioButton, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.RectAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.RectAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "RectKeyFrameCollection")); - members[KnownMembers.RepeatButton_Content] = InitMember(KnownTypes.RepeatButton, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.RichTextBox_Document] = InitMember(KnownTypes.RichTextBox, "Document", InitType(assemblies[4], "System.Windows.Documents", "FlowDocument")); - members[KnownMembers.Rotation3DAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.Rotation3DAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "Rotation3DKeyFrameCollection")); - members[KnownMembers.Run_Text] = InitMember(KnownTypes.Run, "Text", InitType(assemblies[0], "System", "String")); - members[KnownMembers.ScrollViewer_Content] = InitMember(KnownTypes.ScrollViewer, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.Section_Blocks] = InitMember(KnownTypes.Section, "Blocks", InitType(assemblies[4], "System.Windows.Documents", "BlockCollection")); - members[KnownMembers.Selector_Items] = InitMember(KnownTypes.Selector, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.SingleAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.SingleAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "SingleKeyFrameCollection")); - members[KnownMembers.SizeAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.SizeAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "SizeKeyFrameCollection")); - members[KnownMembers.Span_Inlines] = InitMember(KnownTypes.Span, "Inlines", InitType(assemblies[4], "System.Windows.Documents", "InlineCollection")); - members[KnownMembers.StackPanel_Children] = InitMember(KnownTypes.StackPanel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.StatusBar_Items] = InitMember(KnownTypes.StatusBar, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.StatusBarItem_Content] = InitMember(KnownTypes.StatusBarItem, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.Storyboard_Children] = InitMember(KnownTypes.Storyboard, "Children", InitType(assemblies[3], "System.Windows.Media.Animation", "TimelineCollection")); - members[KnownMembers.StringAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.StringAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "StringKeyFrameCollection")); - members[KnownMembers.Style_Setters] = InitMember(KnownTypes.Style, "Setters", InitType(assemblies[4], "System.Windows", "SetterBaseCollection")); - members[KnownMembers.TabControl_Items] = InitMember(KnownTypes.TabControl, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.TabItem_Content] = InitMember(KnownTypes.TabItem, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.TabPanel_Children] = InitMember(KnownTypes.TabPanel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.Table_RowGroups] = InitMember(KnownTypes.Table, "RowGroups", InitType(assemblies[4], "System.Windows.Documents", "TableRowGroupCollection")); - members[KnownMembers.TableCell_Blocks] = InitMember(KnownTypes.TableCell, "Blocks", InitType(assemblies[4], "System.Windows.Documents", "BlockCollection")); - members[KnownMembers.TableRow_Cells] = InitMember(KnownTypes.TableRow, "Cells", InitType(assemblies[4], "System.Windows.Documents", "TableCellCollection")); - members[KnownMembers.TableRowGroup_Rows] = InitMember(KnownTypes.TableRowGroup, "Rows", InitType(assemblies[4], "System.Windows.Documents", "TableRowCollection")); - members[KnownMembers.TextBlock_Inlines] = InitMember(KnownTypes.TextBlock, "Inlines", InitType(assemblies[4], "System.Windows.Documents", "InlineCollection")); - members[KnownMembers.ThicknessAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.ThicknessAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[4], "System.Windows.Media.Animation", "ThicknessKeyFrameCollection")); - members[KnownMembers.ToggleButton_Content] = InitMember(KnownTypes.ToggleButton, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.ToolBar_Items] = InitMember(KnownTypes.ToolBar, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.ToolBarOverflowPanel_Children] = InitMember(KnownTypes.ToolBarOverflowPanel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.ToolBarPanel_Children] = InitMember(KnownTypes.ToolBarPanel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.ToolBarTray_ToolBars] = InitMember(KnownTypes.ToolBarTray, "ToolBars", InitType(assemblies[0], "System.Collections.ObjectModel", "Collection`1")); - members[KnownMembers.ToolTip_Content] = InitMember(KnownTypes.ToolTip, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.TreeView_Items] = InitMember(KnownTypes.TreeView, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.TreeViewItem_Items] = InitMember(KnownTypes.TreeViewItem, "Items", InitType(assemblies[4], "System.Windows.Controls", "ItemCollection")); - members[KnownMembers.Trigger_Setters] = InitMember(KnownTypes.Trigger, "Setters", InitType(assemblies[4], "System.Windows", "SetterBaseCollection")); - members[KnownMembers.Underline_Inlines] = InitMember(KnownTypes.Underline, "Inlines", InitType(assemblies[4], "System.Windows.Documents", "InlineCollection")); - members[KnownMembers.UniformGrid_Children] = InitMember(KnownTypes.UniformGrid, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.UserControl_Content] = InitMember(KnownTypes.UserControl, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.Vector3DAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.Vector3DAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "Vector3DKeyFrameCollection")); - members[KnownMembers.VectorAnimationUsingKeyFrames_KeyFrames] = InitMember(KnownTypes.VectorAnimationUsingKeyFrames, "KeyFrames", InitType(assemblies[3], "System.Windows.Media.Animation", "VectorKeyFrameCollection")); - members[KnownMembers.Viewbox_Child] = InitMember(KnownTypes.Viewbox, "Child", InitType(assemblies[3], "System.Windows", "UIElement")); - members[KnownMembers.Viewport3DVisual_Children] = InitMember(KnownTypes.Viewport3DVisual, "Children", InitType(assemblies[3], "System.Windows.Media.Media3D", "Visual3DCollection")); - members[KnownMembers.VirtualizingPanel_Children] = InitMember(KnownTypes.VirtualizingPanel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.VirtualizingStackPanel_Children] = InitMember(KnownTypes.VirtualizingStackPanel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.Window_Content] = InitMember(KnownTypes.Window, "Content", InitType(assemblies[0], "System", "Object")); - members[KnownMembers.WrapPanel_Children] = InitMember(KnownTypes.WrapPanel, "Children", InitType(assemblies[4], "System.Windows.Controls", "UIElementCollection")); - members[KnownMembers.XmlDataProvider_XmlSerializer] = InitMember(KnownTypes.XmlDataProvider, "XmlSerializer", InitType(assemblies[5], "System.Xml.Serialization", "IXmlSerializable")); - } - - void InitStrings() { - strings[1] = "Name"; - strings[2] = "Uid"; - } - - void InitResources() { - - resources[1] = ("SystemColors", "ActiveBorderBrushKey", "ActiveBorderBrush"); - resources[2] = ("SystemColors", "ActiveCaptionBrushKey", "ActiveCaptionBrush"); - resources[3] = ("SystemColors", "ActiveCaptionTextBrushKey", "ActiveCaptionTextBrush"); - resources[4] = ("SystemColors", "AppWorkspaceBrushKey", "AppWorkspaceBrush"); - resources[5] = ("SystemColors", "ControlBrushKey", "ControlBrush"); - resources[6] = ("SystemColors", "ControlDarkBrushKey", "ControlDarkBrush"); - resources[7] = ("SystemColors", "ControlDarkDarkBrushKey", "ControlDarkDarkBrush"); - resources[8] = ("SystemColors", "ControlLightBrushKey", "ControlLightBrush"); - resources[9] = ("SystemColors", "ControlLightLightBrushKey", "ControlLightLightBrush"); - resources[10] = ("SystemColors", "ControlTextBrushKey", "ControlTextBrush"); - resources[11] = ("SystemColors", "DesktopBrushKey", "DesktopBrush"); - resources[12] = ("SystemColors", "GradientActiveCaptionBrushKey", "GradientActiveCaptionBrush"); - resources[13] = ("SystemColors", "GradientInactiveCaptionBrushKey", "GradientInactiveCaptionBrush"); - resources[14] = ("SystemColors", "GrayTextBrushKey", "GrayTextBrush"); - resources[15] = ("SystemColors", "HighlightBrushKey", "HighlightBrush"); - resources[16] = ("SystemColors", "HighlightTextBrushKey", "HighlightTextBrush"); - resources[17] = ("SystemColors", "HotTrackBrushKey", "HotTrackBrush"); - resources[18] = ("SystemColors", "InactiveBorderBrushKey", "InactiveBorderBrush"); - resources[19] = ("SystemColors", "InactiveCaptionBrushKey", "InactiveCaptionBrush"); - resources[20] = ("SystemColors", "InactiveCaptionTextBrushKey", "InactiveCaptionTextBrush"); - resources[21] = ("SystemColors", "InfoBrushKey", "InfoBrush"); - resources[22] = ("SystemColors", "InfoTextBrushKey", "InfoTextBrush"); - resources[23] = ("SystemColors", "MenuBrushKey", "MenuBrush"); - resources[24] = ("SystemColors", "MenuBarBrushKey", "MenuBarBrush"); - resources[25] = ("SystemColors", "MenuHighlightBrushKey", "MenuHighlightBrush"); - resources[26] = ("SystemColors", "MenuTextBrushKey", "MenuTextBrush"); - resources[27] = ("SystemColors", "ScrollBarBrushKey", "ScrollBarBrush"); - resources[28] = ("SystemColors", "WindowBrushKey", "WindowBrush"); - resources[29] = ("SystemColors", "WindowFrameBrushKey", "WindowFrameBrush"); - resources[30] = ("SystemColors", "WindowTextBrushKey", "WindowTextBrush"); - resources[31] = ("SystemColors", "ActiveBorderColorKey", "ActiveBorderColor"); - resources[32] = ("SystemColors", "ActiveCaptionColorKey", "ActiveCaptionColor"); - resources[33] = ("SystemColors", "ActiveCaptionTextColorKey", "ActiveCaptionTextColor"); - resources[34] = ("SystemColors", "AppWorkspaceColorKey", "AppWorkspaceColor"); - resources[35] = ("SystemColors", "ControlColorKey", "ControlColor"); - resources[36] = ("SystemColors", "ControlDarkColorKey", "ControlDarkColor"); - resources[37] = ("SystemColors", "ControlDarkDarkColorKey", "ControlDarkDarkColor"); - resources[38] = ("SystemColors", "ControlLightColorKey", "ControlLightColor"); - resources[39] = ("SystemColors", "ControlLightLightColorKey", "ControlLightLightColor"); - resources[40] = ("SystemColors", "ControlTextColorKey", "ControlTextColor"); - resources[41] = ("SystemColors", "DesktopColorKey", "DesktopColor"); - resources[42] = ("SystemColors", "GradientActiveCaptionColorKey", "GradientActiveCaptionColor"); - resources[43] = ("SystemColors", "GradientInactiveCaptionColorKey", "GradientInactiveCaptionColor"); - resources[44] = ("SystemColors", "GrayTextColorKey", "GrayTextColor"); - resources[45] = ("SystemColors", "HighlightColorKey", "HighlightColor"); - resources[46] = ("SystemColors", "HighlightTextColorKey", "HighlightTextColor"); - resources[47] = ("SystemColors", "HotTrackColorKey", "HotTrackColor"); - resources[48] = ("SystemColors", "InactiveBorderColorKey", "InactiveBorderColor"); - resources[49] = ("SystemColors", "InactiveCaptionColorKey", "InactiveCaptionColor"); - resources[50] = ("SystemColors", "InactiveCaptionTextColorKey", "InactiveCaptionTextColor"); - resources[51] = ("SystemColors", "InfoColorKey", "InfoColor"); - resources[52] = ("SystemColors", "InfoTextColorKey", "InfoTextColor"); - resources[53] = ("SystemColors", "MenuColorKey", "MenuColor"); - resources[54] = ("SystemColors", "MenuBarColorKey", "MenuBarColor"); - resources[55] = ("SystemColors", "MenuHighlightColorKey", "MenuHighlightColor"); - resources[56] = ("SystemColors", "MenuTextColorKey", "MenuTextColor"); - resources[57] = ("SystemColors", "ScrollBarColorKey", "ScrollBarColor"); - resources[58] = ("SystemColors", "WindowColorKey", "WindowColor"); - resources[59] = ("SystemColors", "WindowFrameColorKey", "WindowFrameColor"); - resources[60] = ("SystemColors", "WindowTextColorKey", "WindowTextColor"); - - - resources[63] = ("SystemFonts", "CaptionFontSizeKey", "CaptionFontSize"); - resources[64] = ("SystemFonts", "CaptionFontFamilyKey", "CaptionFontFamily"); - resources[65] = ("SystemFonts", "CaptionFontStyleKey", "CaptionFontStyle"); - resources[66] = ("SystemFonts", "CaptionFontWeightKey", "CaptionFontWeight"); - resources[67] = ("SystemFonts", "CaptionFontTextDecorationsKey", "CaptionFontTextDecorations"); - resources[68] = ("SystemFonts", "SmallCaptionFontSizeKey", "SmallCaptionFontSize"); - resources[69] = ("SystemFonts", "SmallCaptionFontFamilyKey", "SmallCaptionFontFamily"); - resources[70] = ("SystemFonts", "SmallCaptionFontStyleKey", "SmallCaptionFontStyle"); - resources[71] = ("SystemFonts", "SmallCaptionFontWeightKey", "SmallCaptionFontWeight"); - resources[72] = ("SystemFonts", "SmallCaptionFontTextDecorationsKey", "SmallCaptionFontTextDecorations"); - resources[73] = ("SystemFonts", "MenuFontSizeKey", "MenuFontSize"); - resources[74] = ("SystemFonts", "MenuFontFamilyKey", "MenuFontFamily"); - resources[75] = ("SystemFonts", "MenuFontStyleKey", "MenuFontStyle"); - resources[76] = ("SystemFonts", "MenuFontWeightKey", "MenuFontWeight"); - resources[77] = ("SystemFonts", "MenuFontTextDecorationsKey", "MenuFontTextDecorations"); - resources[78] = ("SystemFonts", "StatusFontSizeKey", "StatusFontSize"); - resources[79] = ("SystemFonts", "StatusFontFamilyKey", "StatusFontFamily"); - resources[80] = ("SystemFonts", "StatusFontStyleKey", "StatusFontStyle"); - resources[81] = ("SystemFonts", "StatusFontWeightKey", "StatusFontWeight"); - resources[82] = ("SystemFonts", "StatusFontTextDecorationsKey", "StatusFontTextDecorations"); - resources[83] = ("SystemFonts", "MessageFontSizeKey", "MessageFontSize"); - resources[84] = ("SystemFonts", "MessageFontFamilyKey", "MessageFontFamily"); - resources[85] = ("SystemFonts", "MessageFontStyleKey", "MessageFontStyle"); - resources[86] = ("SystemFonts", "MessageFontWeightKey", "MessageFontWeight"); - resources[87] = ("SystemFonts", "MessageFontTextDecorationsKey", "MessageFontTextDecorations"); - resources[88] = ("SystemFonts", "IconFontSizeKey", "IconFontSize"); - resources[89] = ("SystemFonts", "IconFontFamilyKey", "IconFontFamily"); - resources[90] = ("SystemFonts", "IconFontStyleKey", "IconFontStyle"); - resources[91] = ("SystemFonts", "IconFontWeightKey", "IconFontWeight"); - resources[92] = ("SystemFonts", "IconFontTextDecorationsKey", "IconFontTextDecorations"); - - - resources[95] = ("SystemParameters", "ThinHorizontalBorderHeightKey", "ThinHorizontalBorderHeight"); - resources[96] = ("SystemParameters", "ThinVerticalBorderWidthKey", "ThinVerticalBorderWidth"); - resources[97] = ("SystemParameters", "CursorWidthKey", "CursorWidth"); - resources[98] = ("SystemParameters", "CursorHeightKey", "CursorHeight"); - resources[99] = ("SystemParameters", "ThickHorizontalBorderHeightKey", "ThickHorizontalBorderHeight"); - resources[100] = ("SystemParameters", "ThickVerticalBorderWidthKey", "ThickVerticalBorderWidth"); - resources[101] = ("SystemParameters", "FixedFrameHorizontalBorderHeightKey", "FixedFrameHorizontalBorderHeight"); - resources[102] = ("SystemParameters", "FixedFrameVerticalBorderWidthKey", "FixedFrameVerticalBorderWidth"); - resources[103] = ("SystemParameters", "FocusHorizontalBorderHeightKey", "FocusHorizontalBorderHeight"); - resources[104] = ("SystemParameters", "FocusVerticalBorderWidthKey", "FocusVerticalBorderWidth"); - resources[105] = ("SystemParameters", "FullPrimaryScreenWidthKey", "FullPrimaryScreenWidth"); - resources[106] = ("SystemParameters", "FullPrimaryScreenHeightKey", "FullPrimaryScreenHeight"); - resources[107] = ("SystemParameters", "HorizontalScrollBarButtonWidthKey", "HorizontalScrollBarButtonWidth"); - resources[108] = ("SystemParameters", "HorizontalScrollBarHeightKey", "HorizontalScrollBarHeight"); - resources[109] = ("SystemParameters", "HorizontalScrollBarThumbWidthKey", "HorizontalScrollBarThumbWidth"); - resources[110] = ("SystemParameters", "IconWidthKey", "IconWidth"); - resources[111] = ("SystemParameters", "IconHeightKey", "IconHeight"); - resources[112] = ("SystemParameters", "IconGridWidthKey", "IconGridWidth"); - resources[113] = ("SystemParameters", "IconGridHeightKey", "IconGridHeight"); - resources[114] = ("SystemParameters", "MaximizedPrimaryScreenWidthKey", "MaximizedPrimaryScreenWidth"); - resources[115] = ("SystemParameters", "MaximizedPrimaryScreenHeightKey", "MaximizedPrimaryScreenHeight"); - resources[116] = ("SystemParameters", "MaximumWindowTrackWidthKey", "MaximumWindowTrackWidth"); - resources[117] = ("SystemParameters", "MaximumWindowTrackHeightKey", "MaximumWindowTrackHeight"); - resources[118] = ("SystemParameters", "MenuCheckmarkWidthKey", "MenuCheckmarkWidth"); - resources[119] = ("SystemParameters", "MenuCheckmarkHeightKey", "MenuCheckmarkHeight"); - resources[120] = ("SystemParameters", "MenuButtonWidthKey", "MenuButtonWidth"); - resources[121] = ("SystemParameters", "MenuButtonHeightKey", "MenuButtonHeight"); - resources[122] = ("SystemParameters", "MinimumWindowWidthKey", "MinimumWindowWidth"); - resources[123] = ("SystemParameters", "MinimumWindowHeightKey", "MinimumWindowHeight"); - resources[124] = ("SystemParameters", "MinimizedWindowWidthKey", "MinimizedWindowWidth"); - resources[125] = ("SystemParameters", "MinimizedWindowHeightKey", "MinimizedWindowHeight"); - resources[126] = ("SystemParameters", "MinimizedGridWidthKey", "MinimizedGridWidth"); - resources[127] = ("SystemParameters", "MinimizedGridHeightKey", "MinimizedGridHeight"); - resources[128] = ("SystemParameters", "MinimumWindowTrackWidthKey", "MinimumWindowTrackWidth"); - resources[129] = ("SystemParameters", "MinimumWindowTrackHeightKey", "MinimumWindowTrackHeight"); - resources[130] = ("SystemParameters", "PrimaryScreenWidthKey", "PrimaryScreenWidth"); - resources[131] = ("SystemParameters", "PrimaryScreenHeightKey", "PrimaryScreenHeight"); - resources[132] = ("SystemParameters", "WindowCaptionButtonWidthKey", "WindowCaptionButtonWidth"); - resources[133] = ("SystemParameters", "WindowCaptionButtonHeightKey", "WindowCaptionButtonHeight"); - resources[134] = ("SystemParameters", "ResizeFrameHorizontalBorderHeightKey", "ResizeFrameHorizontalBorderHeight"); - resources[135] = ("SystemParameters", "ResizeFrameVerticalBorderWidthKey", "ResizeFrameVerticalBorderWidth"); - resources[136] = ("SystemParameters", "SmallIconWidthKey", "SmallIconWidth"); - resources[137] = ("SystemParameters", "SmallIconHeightKey", "SmallIconHeight"); - resources[138] = ("SystemParameters", "SmallWindowCaptionButtonWidthKey", "SmallWindowCaptionButtonWidth"); - resources[139] = ("SystemParameters", "SmallWindowCaptionButtonHeightKey", "SmallWindowCaptionButtonHeight"); - resources[140] = ("SystemParameters", "VirtualScreenWidthKey", "VirtualScreenWidth"); - resources[141] = ("SystemParameters", "VirtualScreenHeightKey", "VirtualScreenHeight"); - resources[142] = ("SystemParameters", "VerticalScrollBarWidthKey", "VerticalScrollBarWidth"); - resources[143] = ("SystemParameters", "VerticalScrollBarButtonHeightKey", "VerticalScrollBarButtonHeight"); - resources[144] = ("SystemParameters", "WindowCaptionHeightKey", "WindowCaptionHeight"); - resources[145] = ("SystemParameters", "KanjiWindowHeightKey", "KanjiWindowHeight"); - resources[146] = ("SystemParameters", "MenuBarHeightKey", "MenuBarHeight"); - resources[147] = ("SystemParameters", "SmallCaptionHeightKey", "SmallCaptionHeight"); - resources[148] = ("SystemParameters", "VerticalScrollBarThumbHeightKey", "VerticalScrollBarThumbHeight"); - resources[149] = ("SystemParameters", "IsImmEnabledKey", "IsImmEnabled"); - resources[150] = ("SystemParameters", "IsMediaCenterKey", "IsMediaCenter"); - resources[151] = ("SystemParameters", "IsMenuDropRightAlignedKey", "IsMenuDropRightAligned"); - resources[152] = ("SystemParameters", "IsMiddleEastEnabledKey", "IsMiddleEastEnabled"); - resources[153] = ("SystemParameters", "IsMousePresentKey", "IsMousePresent"); - resources[154] = ("SystemParameters", "IsMouseWheelPresentKey", "IsMouseWheelPresent"); - resources[155] = ("SystemParameters", "IsPenWindowsKey", "IsPenWindows"); - resources[156] = ("SystemParameters", "IsRemotelyControlledKey", "IsRemotelyControlled"); - resources[157] = ("SystemParameters", "IsRemoteSessionKey", "IsRemoteSession"); - resources[158] = ("SystemParameters", "ShowSoundsKey", "ShowSounds"); - resources[159] = ("SystemParameters", "IsSlowMachineKey", "IsSlowMachine"); - resources[160] = ("SystemParameters", "SwapButtonsKey", "SwapButtons"); - resources[161] = ("SystemParameters", "IsTabletPCKey", "IsTabletPC"); - resources[162] = ("SystemParameters", "VirtualScreenLeftKey", "VirtualScreenLeft"); - resources[163] = ("SystemParameters", "VirtualScreenTopKey", "VirtualScreenTop"); - resources[164] = ("SystemParameters", "FocusBorderWidthKey", "FocusBorderWidth"); - resources[165] = ("SystemParameters", "FocusBorderHeightKey", "FocusBorderHeight"); - resources[166] = ("SystemParameters", "HighContrastKey", "HighContrast"); - resources[167] = ("SystemParameters", "DropShadowKey", "DropShadow"); - resources[168] = ("SystemParameters", "FlatMenuKey", "FlatMenu"); - resources[169] = ("SystemParameters", "WorkAreaKey", "WorkArea"); - resources[170] = ("SystemParameters", "IconHorizontalSpacingKey", "IconHorizontalSpacing"); - resources[171] = ("SystemParameters", "IconVerticalSpacingKey", "IconVerticalSpacing"); - resources[172] = ("SystemParameters", "IconTitleWrapKey", "IconTitleWrap"); - resources[173] = ("SystemParameters", "KeyboardCuesKey", "KeyboardCues"); - resources[174] = ("SystemParameters", "KeyboardDelayKey", "KeyboardDelay"); - resources[175] = ("SystemParameters", "KeyboardPreferenceKey", "KeyboardPreference"); - resources[176] = ("SystemParameters", "KeyboardSpeedKey", "KeyboardSpeed"); - resources[177] = ("SystemParameters", "SnapToDefaultButtonKey", "SnapToDefaultButton"); - resources[178] = ("SystemParameters", "WheelScrollLinesKey", "WheelScrollLines"); - resources[179] = ("SystemParameters", "MouseHoverTimeKey", "MouseHoverTime"); - resources[180] = ("SystemParameters", "MouseHoverHeightKey", "MouseHoverHeight"); - resources[181] = ("SystemParameters", "MouseHoverWidthKey", "MouseHoverWidth"); - resources[182] = ("SystemParameters", "MenuDropAlignmentKey", "MenuDropAlignment"); - resources[183] = ("SystemParameters", "MenuFadeKey", "MenuFade"); - resources[184] = ("SystemParameters", "MenuShowDelayKey", "MenuShowDelay"); - resources[185] = ("SystemParameters", "ComboBoxAnimationKey", "ComboBoxAnimation"); - resources[186] = ("SystemParameters", "ClientAreaAnimationKey", "ClientAreaAnimation"); - resources[187] = ("SystemParameters", "CursorShadowKey", "CursorShadow"); - resources[188] = ("SystemParameters", "GradientCaptionsKey", "GradientCaptions"); - resources[189] = ("SystemParameters", "HotTrackingKey", "HotTracking"); - resources[190] = ("SystemParameters", "ListBoxSmoothScrollingKey", "ListBoxSmoothScrolling"); - resources[191] = ("SystemParameters", "MenuAnimationKey", "MenuAnimation"); - resources[192] = ("SystemParameters", "SelectionFadeKey", "SelectionFade"); - resources[193] = ("SystemParameters", "StylusHotTrackingKey", "StylusHotTracking"); - resources[194] = ("SystemParameters", "ToolTipAnimationKey", "ToolTipAnimation"); - resources[195] = ("SystemParameters", "ToolTipFadeKey", "ToolTipFade"); - resources[196] = ("SystemParameters", "UIEffectsKey", "UIEffects"); - resources[197] = ("SystemParameters", "MinimizeAnimationKey", "MinimizeAnimation"); - resources[198] = ("SystemParameters", "BorderKey", "Border"); - resources[199] = ("SystemParameters", "CaretWidthKey", "CaretWidth"); - resources[200] = ("SystemParameters", "ForegroundFlashCountKey", "ForegroundFlashCount"); - resources[201] = ("SystemParameters", "DragFullWindowsKey", "DragFullWindows"); - resources[202] = ("SystemParameters", "BorderWidthKey", "BorderWidth"); - resources[203] = ("SystemParameters", "ScrollWidthKey", "ScrollWidth"); - resources[204] = ("SystemParameters", "ScrollHeightKey", "ScrollHeight"); - resources[205] = ("SystemParameters", "CaptionWidthKey", "CaptionWidth"); - resources[206] = ("SystemParameters", "CaptionHeightKey", "CaptionHeight"); - resources[207] = ("SystemParameters", "SmallCaptionWidthKey", "SmallCaptionWidth"); - resources[208] = ("SystemParameters", "MenuWidthKey", "MenuWidth"); - resources[209] = ("SystemParameters", "MenuHeightKey", "MenuHeight"); - resources[210] = ("SystemParameters", "ComboBoxPopupAnimationKey", "ComboBoxPopupAnimation"); - resources[211] = ("SystemParameters", "MenuPopupAnimationKey", "MenuPopupAnimation"); - resources[212] = ("SystemParameters", "ToolTipPopupAnimationKey", "ToolTipPopupAnimation"); - resources[213] = ("SystemParameters", "PowerLineStatusKey", "PowerLineStatus"); - - resources[215] = ("SystemParameters", "FocusVisualStyleKey", "FocusVisualStyle"); - resources[216] = ("SystemParameters", "NavigationChromeDownLevelStyleKey", "NavigationChromeDownLevelStyle"); - resources[217] = ("SystemParameters", "NavigationChromeStyleKey", "NavigationChromeStyle"); - - resources[219] = ("MenuItem", "SeparatorStyleKey", "MenuItemSeparatorStyle"); - resources[220] = ("GridView", "GridViewScrollViewerStyleKey", "GridViewScrollViewerStyle"); - resources[221] = ("GridView", "GridViewStyleKey", "GridViewStyle"); - resources[222] = ("GridView", "GridViewItemContainerStyleKey", "GridViewItemContainerStyle"); - resources[223] = ("StatusBar", "SeparatorStyleKey", "StatusBarSeparatorStyle"); - resources[224] = ("ToolBar", "ButtonStyleKey", "ToolBarButtonStyle"); - resources[225] = ("ToolBar", "ToggleButtonStyleKey", "ToolBarToggleButtonStyle"); - resources[226] = ("ToolBar", "SeparatorStyleKey", "ToolBarSeparatorStyle"); - resources[227] = ("ToolBar", "CheckBoxStyleKey", "ToolBarCheckBoxStyle"); - resources[228] = ("ToolBar", "RadioButtonStyleKey", "ToolBarRadioButtonStyle"); - resources[229] = ("ToolBar", "ComboBoxStyleKey", "ToolBarComboBoxStyle"); - resources[230] = ("ToolBar", "TextBoxStyleKey", "ToolBarTextBoxStyle"); - resources[231] = ("ToolBar", "MenuStyleKey", "ToolBarMenuStyle"); - - - resources[234] = ("SystemColors", "InactiveSelectionHighlightBrushKey", "InactiveSelectionHighlightBrush"); - resources[235] = ("SystemColors", "InactiveSelectionHighlightTextBrushKey", "InactiveSelectionHighlightTextBrush"); - - } - - diff --git a/ILSpy.BamlDecompiler/Handlers/Records/PropertyCustomHandler.cs b/ILSpy.BamlDecompiler/Handlers/Records/PropertyCustomHandler.cs index 2ae98ebfe..7c0d92d8c 100644 --- a/ILSpy.BamlDecompiler/Handlers/Records/PropertyCustomHandler.cs +++ b/ILSpy.BamlDecompiler/Handlers/Records/PropertyCustomHandler.cs @@ -47,7 +47,7 @@ namespace ILSpy.BamlDecompiler.Handlers { case KnownTypes.DependencyPropertyConverter: { if (value.Length == 2) { var property = ctx.ResolveProperty(reader.ReadUInt16()); - return ctx.ToString(elem, property.ToXName(ctx, elem, false)); + return ctx.ToString(elem, property.ToXName(ctx, elem, NeedsFullName(property, ctx, elem))); } else { var type = ctx.ResolveType(reader.ReadUInt16()); @@ -142,6 +142,18 @@ namespace ILSpy.BamlDecompiler.Handlers { throw new NotSupportedException(ser.ToString()); } + private bool NeedsFullName(XamlProperty property, XamlContext ctx, XElement elem) + { + XElement p = elem.Parent; + while (p != null && p.Annotation()?.ResolvedType.FullName != "System.Windows.Style") { + p = p.Parent; + } + var type = p?.Annotation()?.Type; + if (type == null) + return true; + return property.IsAttachedTo(type); + } + public BamlElement Translate(XamlContext ctx, BamlNode node, BamlElement parent) { var record = (PropertyCustomRecord)((BamlRecordNode)node).Record; var serTypeId = ((short)record.SerializerTypeId & 0xfff); diff --git a/ILSpy.BamlDecompiler/Handlers/Records/PropertyTypeReferenceHandler.cs b/ILSpy.BamlDecompiler/Handlers/Records/PropertyTypeReferenceHandler.cs index aa29642af..c9212db25 100644 --- a/ILSpy.BamlDecompiler/Handlers/Records/PropertyTypeReferenceHandler.cs +++ b/ILSpy.BamlDecompiler/Handlers/Records/PropertyTypeReferenceHandler.cs @@ -23,6 +23,7 @@ using System.Xml.Linq; using ILSpy.BamlDecompiler.Baml; using ILSpy.BamlDecompiler.Xaml; +using ICSharpCode.Decompiler.TypeSystem; namespace ILSpy.BamlDecompiler.Handlers { internal class PropertyTypeReferenceHandler : IHandler { @@ -39,6 +40,10 @@ namespace ILSpy.BamlDecompiler.Handlers { var elemAttr = ctx.ResolveProperty(record.AttributeId); elem.Xaml = new XElement(elemAttr.ToXName(ctx, null)); + if (attr.ResolvedMember.FullNameIs("System.Windows.Style", "TargetType")) { + parent.Xaml.Element.AddAnnotation(new TargetTypeAnnotation(type)); + } + elem.Xaml.Element.AddAnnotation(elemAttr); parent.Xaml.Element.Add(elem.Xaml.Element); @@ -53,4 +58,14 @@ namespace ILSpy.BamlDecompiler.Handlers { return elem; } } + + internal class TargetTypeAnnotation + { + public XamlType Type { get; } + + public TargetTypeAnnotation(XamlType type) + { + this.Type = type; + } + } } \ No newline at end of file From 34098b16e86624e3712507e47a0664f1527bf840 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 19 Jun 2019 13:46:21 +0200 Subject: [PATCH 007/221] Fix #1548: Detection of lambdas. --- ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs index dfdda704c..14048219e 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs @@ -110,9 +110,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms static bool IsAnonymousMethod(ITypeDefinition decompiledTypeDefinition, IMethod method) { - if (method == null || !(method.HasGeneratedName() || method.Name.Contains("$") || ContainsAnonymousType(method))) + if (method == null || !(method.HasGeneratedName() || method.Name.Contains("$") || method.IsCompilerGeneratedOrIsInCompilerGeneratedClass() || ContainsAnonymousType(method))) return false; - if (!(method.IsCompilerGeneratedOrIsInCompilerGeneratedClass() || IsPotentialClosure(decompiledTypeDefinition, method.DeclaringTypeDefinition))) + if (!IsPotentialClosure(decompiledTypeDefinition, method.DeclaringTypeDefinition)) return false; return true; } From cf7deca7e17ca9c6437c06d92c99fb1d7429616c Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 19 Jun 2019 17:46:08 +0200 Subject: [PATCH 008/221] Fix build. --- .../IL/Transforms/DelegateConstruction.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs index 14048219e..aa4148c33 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs @@ -110,9 +110,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms static bool IsAnonymousMethod(ITypeDefinition decompiledTypeDefinition, IMethod method) { - if (method == null || !(method.HasGeneratedName() || method.Name.Contains("$") || method.IsCompilerGeneratedOrIsInCompilerGeneratedClass() || ContainsAnonymousType(method))) + if (method == null) return false; - if (!IsPotentialClosure(decompiledTypeDefinition, method.DeclaringTypeDefinition)) + if (!(method.HasGeneratedName() + || method.Name.Contains("$") + || method.IsCompilerGeneratedOrIsInCompilerGeneratedClass() + || IsPotentialClosure(decompiledTypeDefinition, method.DeclaringTypeDefinition) + || ContainsAnonymousType(method))) return false; return true; } From b85d14ac5576f1166cb3e4677d3612d8a8db9dd7 Mon Sep 17 00:00:00 2001 From: dymanoid <9433345+dymanoid@users.noreply.github.com> Date: Fri, 21 Jun 2019 14:40:08 +0200 Subject: [PATCH 009/221] Add extension method for DependencyObject --- ILSpy/Controls/ExtensionMethods.cs | 34 ++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/ILSpy/Controls/ExtensionMethods.cs b/ILSpy/Controls/ExtensionMethods.cs index 095ddc41e..1c5e9f794 100644 --- a/ILSpy/Controls/ExtensionMethods.cs +++ b/ILSpy/Controls/ExtensionMethods.cs @@ -19,6 +19,7 @@ using System; using System.Windows; using System.Windows.Markup; +using System.Windows.Media; namespace ICSharpCode.ILSpy.Controls { @@ -27,6 +28,39 @@ namespace ICSharpCode.ILSpy.Controls /// public static class ExtensionMethods { + /// + /// Checks if the current is contained in the visual tree of the + /// object. + /// + /// The object to check, may be null. + /// The object whose visual tree will be inspected. + /// + /// true if this object is contained in the visual tree of the ; + /// otherwise, false. + /// + /// Thrown when is null. + public static bool IsInVisualTreeOf(this DependencyObject thisObject, DependencyObject dependencyObject) + { + if (dependencyObject == null) { + throw new ArgumentNullException(nameof(dependencyObject)); + } + + if (thisObject is null) { + return false; + } + + var parent = VisualTreeHelper.GetParent(thisObject); + while (parent != null) { + if (parent == dependencyObject) { + return true; + } + + parent = VisualTreeHelper.GetParent(parent); + } + + return false; + } + /// /// Sets the value of a dependency property on using a markup extension. /// From 864672c07cc9d709d7840ec72950bb4000c761e0 Mon Sep 17 00:00:00 2001 From: dymanoid <9433345+dymanoid@users.noreply.github.com> Date: Fri, 21 Jun 2019 14:41:18 +0200 Subject: [PATCH 010/221] Enable the save code command only when appropriate --- ILSpy/MainWindow.xaml | 1 + ILSpy/MainWindow.xaml.cs | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml index a168d2e66..166c8ac80 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -28,6 +28,7 @@ Executed="RefreshCommandExecuted" /> n is AssemblyTreeNode); + } + void SaveCommandExecuted(object sender, ExecutedRoutedEventArgs e) { if (this.SelectedNodes.Count() == 1) { From 46dfa7295442e3f828d98582a99613c67ec847ce Mon Sep 17 00:00:00 2001 From: dymanoid <9433345+dymanoid@users.noreply.github.com> Date: Fri, 21 Jun 2019 15:16:36 +0200 Subject: [PATCH 011/221] Add 'Save Code' context menu item --- ILSpy/Commands/SaveCodeContextMenuEntry.cs | 40 ++++++++++++++++++++++ ILSpy/ILSpy.csproj | 1 + 2 files changed, 41 insertions(+) create mode 100644 ILSpy/Commands/SaveCodeContextMenuEntry.cs diff --git a/ILSpy/Commands/SaveCodeContextMenuEntry.cs b/ILSpy/Commands/SaveCodeContextMenuEntry.cs new file mode 100644 index 000000000..85e5e301f --- /dev/null +++ b/ILSpy/Commands/SaveCodeContextMenuEntry.cs @@ -0,0 +1,40 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System.Windows.Input; +using ICSharpCode.ILSpy.Properties; + +namespace ICSharpCode.ILSpy.TextView +{ + [ExportContextMenuEntry(Header = nameof(Resources._SaveCode), Category = nameof(Resources.Save), Icon = "Images/SaveFile.png")] + sealed class SaveCodeContextMenuEntry : IContextMenuEntry + { + private readonly ICommand saveCommand; + + public SaveCodeContextMenuEntry() + { + saveCommand = ApplicationCommands.Save; + } + + public void Execute(TextViewContext context) => saveCommand.Execute(parameter: null); + + public bool IsEnabled(TextViewContext context) => true; + + public bool IsVisible(TextViewContext context) => context.TreeView != null && saveCommand.CanExecute(parameter: null); + } +} diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index c7f0658d6..e994dddff 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -207,6 +207,7 @@ + From 4ceb8f62d061ff9d3f1f82111dcb6afa9aeb4a15 Mon Sep 17 00:00:00 2001 From: dymanoid <9433345+dymanoid@users.noreply.github.com> Date: Fri, 21 Jun 2019 19:00:25 +0200 Subject: [PATCH 012/221] Implement decompilation of multiple selected assemblies icsharpcode#972 --- ILSpy/ILSpy.csproj | 1 + ILSpy/MainWindow.xaml.cs | 29 +++- ILSpy/SolutionWriter.cs | 198 ++++++++++++++++++++++++++++ ILSpy/TreeNodes/AssemblyTreeNode.cs | 53 +++++--- ILSpy/TreeNodes/ILSpyTreeNode.cs | 9 ++ 5 files changed, 263 insertions(+), 27 deletions(-) create mode 100644 ILSpy/SolutionWriter.cs diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index e994dddff..24f0d0b59 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -204,6 +204,7 @@ + diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 6e6ea8607..238c74307 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -907,19 +907,34 @@ namespace ICSharpCode.ILSpy e.CanExecute = true; return; } - var selectedNodes = SelectedNodes.ToArray(); - e.CanExecute = selectedNodes.Length == 1 || Array.TrueForAll(selectedNodes, n => n is AssemblyTreeNode); + var selectedNodes = SelectedNodes.ToList(); + e.CanExecute = selectedNodes.Count == 1 || selectedNodes.TrueForAll(n => n is AssemblyTreeNode); } void SaveCommandExecuted(object sender, ExecutedRoutedEventArgs e) { - if (this.SelectedNodes.Count() == 1) { - if (this.SelectedNodes.Single().Save(this.TextView)) + var selectedNodes = SelectedNodes.ToList(); + if (selectedNodes.Count > 1) { + var assemblyNodes = selectedNodes + .OfType() + .Where(n => n.Language is CSharpLanguage) + .ToList(); + + if (assemblyNodes.Count == selectedNodes.Count) { + var initialPath = Path.GetDirectoryName(assemblyNodes[0].LoadedAssembly.FileName); + var selectedPath = SolutionWriter.SelectSolutionFile(initialPath); + + if (!string.IsNullOrEmpty(selectedPath)) { + SolutionWriter.CreateSolution(TextView, selectedPath, assemblyNodes); + } return; + } + } + + if (selectedNodes.Count != 1 || !selectedNodes[0].Save(TextView)) { + var options = new DecompilationOptions() { FullDecompilation = true }; + TextView.SaveToDisk(CurrentLanguage, selectedNodes, options); } - this.TextView.SaveToDisk(this.CurrentLanguage, - this.SelectedNodes, - new DecompilationOptions() { FullDecompilation = true }); } public void RefreshDecompiledView() diff --git a/ILSpy/SolutionWriter.cs b/ILSpy/SolutionWriter.cs new file mode 100644 index 000000000..a4ebf5779 --- /dev/null +++ b/ILSpy/SolutionWriter.cs @@ -0,0 +1,198 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Security; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using ICSharpCode.Decompiler; +using ICSharpCode.ILSpy.TextView; +using ICSharpCode.ILSpy.TreeNodes; +using Microsoft.Win32; + +namespace ICSharpCode.ILSpy +{ + /// + /// An utility class that creates a Visual Studio solution containing projects for the + /// decompiled assemblies. + /// + internal static class SolutionWriter + { + private const string SolutionExtension = ".sln"; + private const string DefaultSolutionName = "Solution"; + + /// + /// Shows a File Selection dialog where the user can select the target file for the solution. + /// + /// The initial path to show in the dialog. If not specified, the 'Documents' directory + /// will be used. + /// + /// The full path of the selected target file, or null if the user canceled. + public static string SelectSolutionFile(string path) + { + if (string.IsNullOrWhiteSpace(path)) { + path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); + } + + SaveFileDialog dlg = new SaveFileDialog(); + dlg.InitialDirectory = path; + dlg.FileName = Path.Combine(path, DefaultSolutionName + SolutionExtension); + dlg.Filter = "Visual Studio Solution file|*" + SolutionExtension; + + bool targetInvalid; + do { + if (dlg.ShowDialog() != true) { + return null; + } + + string selectedPath = Path.GetDirectoryName(dlg.FileName); + try { + targetInvalid = Directory.EnumerateFileSystemEntries(selectedPath).Any(); + } catch (Exception e) when (e is IOException || e is UnauthorizedAccessException || e is SecurityException) { + MessageBox.Show( + "The directory cannot be accessed. Please ensure it exists and you have sufficient rights to access it.", + "Solution directory not accessible", + MessageBoxButton.OK, MessageBoxImage.Error); + targetInvalid = true; + continue; + } + + if (targetInvalid) { + MessageBox.Show( + "The directory is not empty. Please select an empty directory.", + "Solution directory not empty", + MessageBoxButton.OK, MessageBoxImage.Warning); + } + } while (targetInvalid); + + return dlg.FileName; + } + + /// + /// Creates a Visual Studio solution that contains projects with decompiled code + /// of the specified . The solution file will be saved + /// to the . The directory of this file must either + /// be empty or not exist. + /// + /// A reference to the instance. + /// The target file path of the solution file. + /// The assembly nodes to decompile. + /// + /// Thrown when is null, + /// an empty or a whitespace string. + /// Thrown when > or + /// is null. + public static void CreateSolution(DecompilerTextView textView, string solutionFilePath, IEnumerable assemblyNodes) + { + if (textView == null) { + throw new ArgumentNullException(nameof(textView)); + } + + if (string.IsNullOrWhiteSpace(solutionFilePath)) { + throw new ArgumentException("The solution file path cannot be null or empty.", nameof(solutionFilePath)); + } + + if (assemblyNodes == null) { + throw new ArgumentNullException(nameof(assemblyNodes)); + } + + textView + .RunWithCancellation(ct => CreateSolution(solutionFilePath, assemblyNodes, ct)) + .Then(output => textView.ShowText(output)) + .HandleExceptions(); + } + + private static async Task CreateSolution( + string solutionFilePath, + IEnumerable assemblyNodes, + CancellationToken ct) + { + var solutionDirectory = Path.GetDirectoryName(solutionFilePath); + var statusOutput = new ConcurrentBag(); + var result = new AvalonEditTextOutput(); + + var duplicates = new HashSet(); + if (assemblyNodes.Any(n => !duplicates.Add(n.LoadedAssembly.ShortName))) { + result.WriteLine("Duplicate assembly names selected, cannot generate a solution."); + return result; + } + + Stopwatch stopwatch = Stopwatch.StartNew(); + + await Task.Run(() => Parallel.ForEach(assemblyNodes, n => WriteProject(n, solutionDirectory, statusOutput, ct))) + .ConfigureAwait(false); + + + foreach (var item in statusOutput) { + result.WriteLine(item); + } + + if (statusOutput.Count == 0) { + result.WriteLine("Successfully decompiled the following assemblies to a Visual Studio Solution:"); + foreach (var item in assemblyNodes.Select(n => n.Text.ToString())) { + result.WriteLine(item); + } + + result.WriteLine(); + result.WriteLine("Elapsed time: " + stopwatch.Elapsed.TotalSeconds.ToString("F1") + " seconds."); + result.WriteLine(); + result.AddButton(null, "Open Explorer", delegate { Process.Start("explorer", "/select,\"" + solutionFilePath + "\""); }); + } + + return result; + } + + private static void WriteProject(AssemblyTreeNode assemblyNode, string targetDirectory, ConcurrentBag statusOutput, CancellationToken ct) + { + var loadedAssembly = assemblyNode.LoadedAssembly; + + targetDirectory = Path.Combine(targetDirectory, loadedAssembly.ShortName); + string projectFileName = Path.Combine(targetDirectory, loadedAssembly.ShortName + assemblyNode.Language.ProjectFileExtension); + + if (!Directory.Exists(targetDirectory)) { + try { + Directory.CreateDirectory(targetDirectory); + } catch (Exception e) { + statusOutput.Add($"Failed to create a directory '{targetDirectory}':{Environment.NewLine}{e}"); + return; + } + } + + try { + using (var projectFileWriter = new StreamWriter(projectFileName)) { + var projectFileOutput = new PlainTextOutput(projectFileWriter); + var options = new DecompilationOptions() { + FullDecompilation = true, + CancellationToken = ct, + SaveAsProjectDirectory = targetDirectory }; + + assemblyNode.Decompile(assemblyNode.Language, projectFileOutput, options); + } + } catch (Exception e) { + statusOutput.Add($"Failed to decompile the assembly '{loadedAssembly.FileName}':{Environment.NewLine}{e}"); + return; + } + } + } +} diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index 51aa65bdf..60f34e6b2 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -279,31 +279,44 @@ namespace ICSharpCode.ILSpy.TreeNodes public override bool Save(DecompilerTextView textView) { Language language = this.Language; - if (string.IsNullOrEmpty(language.ProjectFileExtension)) + if (string.IsNullOrEmpty(language.ProjectFileExtension)) { return false; + } + SaveFileDialog dlg = new SaveFileDialog(); dlg.FileName = DecompilerTextView.CleanUpName(LoadedAssembly.ShortName) + language.ProjectFileExtension; - dlg.Filter = language.Name + " project|*" + language.ProjectFileExtension + "|" + language.Name + " single file|*" + language.FileExtension + "|All files|*.*"; - if (dlg.ShowDialog() == true) { - DecompilationOptions options = new DecompilationOptions(); - options.FullDecompilation = true; - if (dlg.FilterIndex == 1) { - options.SaveAsProjectDirectory = Path.GetDirectoryName(dlg.FileName); - foreach (string entry in Directory.GetFileSystemEntries(options.SaveAsProjectDirectory)) { - if (!string.Equals(entry, dlg.FileName, StringComparison.OrdinalIgnoreCase)) { - var result = MessageBox.Show( - "The directory is not empty. File will be overwritten." + Environment.NewLine + - "Are you sure you want to continue?", - "Project Directory not empty", - MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No); - if (result == MessageBoxResult.No) - return true; // don't save, but mark the Save operation as handled - break; - } - } + dlg.Filter = language.Name + " project|*" + language.ProjectFileExtension; + if (dlg.ShowDialog() != true) { + return true; + } + + var targetDirectory = Path.GetDirectoryName(dlg.FileName); + var existingFiles = Directory.GetFileSystemEntries(targetDirectory); + + if (existingFiles.Any(e => !string.Equals(e, dlg.FileName, StringComparison.OrdinalIgnoreCase))) { + var result = MessageBox.Show( + "The directory is not empty. File will be overwritten." + Environment.NewLine + + "Are you sure you want to continue?", + "Project Directory not empty", + MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No); + if (result == MessageBoxResult.No) { + return true; // don't save, but mark the Save operation as handled } - textView.SaveToDisk(language, new[] { this }, options, dlg.FileName); } + + Save(textView, dlg.FileName); + return true; + } + + public override bool Save(DecompilerTextView textView, string fileName) + { + var targetDirectory = Path.GetDirectoryName(fileName); + DecompilationOptions options = new DecompilationOptions { + FullDecompilation = true, + SaveAsProjectDirectory = targetDirectory + }; + + textView.SaveToDisk(Language, new[] { this }, options, fileName); return true; } diff --git a/ILSpy/TreeNodes/ILSpyTreeNode.cs b/ILSpy/TreeNodes/ILSpyTreeNode.cs index fa1e663e4..11fe3b278 100644 --- a/ILSpy/TreeNodes/ILSpyTreeNode.cs +++ b/ILSpy/TreeNodes/ILSpyTreeNode.cs @@ -79,6 +79,15 @@ namespace ICSharpCode.ILSpy.TreeNodes return false; } + /// + /// Saves the content this node represents to the specified . + /// The file will be silently overwritten. + /// + /// A reference to a instance. + /// The target full path to save the content to. + /// true on success; otherwise, false. + public virtual bool Save(TextView.DecompilerTextView textView, string fileName) => Save(textView); + protected override void OnChildrenChanged(NotifyCollectionChangedEventArgs e) { if (e.NewItems != null) { From 5e6a261b8607c12af71d48e03754cbba90daaba0 Mon Sep 17 00:00:00 2001 From: dymanoid <9433345+dymanoid@users.noreply.github.com> Date: Fri, 21 Jun 2019 23:17:42 +0200 Subject: [PATCH 013/221] Implement Visual Studio solution generation for icsharpcode#972 --- .../CSharp/SolutionCreator.cs | 275 ++++++++++++++++++ .../CSharp/WholeProjectDecompiler.cs | 9 +- .../ICSharpCode.Decompiler.csproj | 1 + ILSpy/Languages/CSharpLanguage.cs | 6 +- ILSpy/Languages/ILLanguage.cs | 4 +- ILSpy/Languages/Language.cs | 6 +- ILSpy/SolutionWriter.cs | 54 +++- ILSpy/TreeNodes/AssemblyListTreeNode.cs | 4 +- ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs | 4 +- ILSpy/TreeNodes/AssemblyTreeNode.cs | 12 +- ILSpy/TreeNodes/BaseTypesEntryNode.cs | 3 +- ILSpy/TreeNodes/BaseTypesTreeNode.cs | 4 +- ILSpy/TreeNodes/DerivedTypesEntryNode.cs | 3 +- ILSpy/TreeNodes/DerivedTypesTreeNode.cs | 3 +- ILSpy/TreeNodes/EventTreeNode.cs | 3 +- ILSpy/TreeNodes/FieldTreeNode.cs | 3 +- ILSpy/TreeNodes/ILSpyTreeNode.cs | 2 +- ILSpy/TreeNodes/MethodTreeNode.cs | 3 +- ILSpy/TreeNodes/ModuleReferenceTreeNode.cs | 3 +- ILSpy/TreeNodes/NamespaceTreeNode.cs | 3 +- ILSpy/TreeNodes/PropertyTreeNode.cs | 3 +- ILSpy/TreeNodes/ReferenceFolderTreeNode.cs | 4 +- ILSpy/TreeNodes/ResourceListTreeNode.cs | 4 +- .../ImageListResourceEntryNode.cs | 3 +- .../ResourceNodes/ResourceEntryNode.cs | 3 +- .../ResourceNodes/ResourceTreeNode.cs | 4 +- .../ResourceNodes/ResourcesFileTreeNode.cs | 4 +- ILSpy/TreeNodes/ThreadingSupport.cs | 6 +- ILSpy/TreeNodes/TypeTreeNode.cs | 3 +- 29 files changed, 394 insertions(+), 45 deletions(-) create mode 100644 ICSharpCode.Decompiler/CSharp/SolutionCreator.cs diff --git a/ICSharpCode.Decompiler/CSharp/SolutionCreator.cs b/ICSharpCode.Decompiler/CSharp/SolutionCreator.cs new file mode 100644 index 000000000..7c3f26e0e --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/SolutionCreator.cs @@ -0,0 +1,275 @@ +// Copyright (c) 2016 Daniel Grunwald +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml.Linq; + +namespace ICSharpCode.Decompiler.CSharp +{ + /// + /// A container class that holds information about a Visual Studio project. + /// + public sealed class ProjectItem : ProjectId + { + /// + /// Initializes a new instance of the class. + /// + /// The full path of the project file. + /// The project platform. + /// The project GUID. + /// + /// Thrown when + /// or is null or empty. + public ProjectItem(string projectFile, string projectPlatform, Guid projectGuid) + : base(projectPlatform, projectGuid) + { + ProjectName = Path.GetFileNameWithoutExtension(projectFile); + FilePath = projectFile; + } + + /// + /// Gets the name of the project. + /// + public string ProjectName { get; } + + /// + /// Gets the full path to the project file. + /// + public string FilePath { get; } + } + + /// + /// A container class that holds platform and GUID information about a Visual Studio project. + /// + public class ProjectId + { + /// + /// Initializes a new instance of the class. + /// + /// The project platform. + /// The project GUID. + /// + /// Thrown when + /// or is null or empty. + public ProjectId(string projectPlatform, Guid projectGuid) + { + if (string.IsNullOrWhiteSpace(projectPlatform)) { + throw new ArgumentException("The platform cannot be null or empty.", nameof(projectPlatform)); + } + + Guid = projectGuid; + PlatformName = projectPlatform; + } + + /// + /// Gets the GUID of this project. + /// + public Guid Guid { get; } + + /// + /// Gets the platform name of this project. Only single platform per project is supported. + /// + public string PlatformName { get; } + } + + /// + /// A helper class that can write a Visual Studio Solution file for the provided projects. + /// + public static class SolutionCreator + { + private static readonly XNamespace ProjectFileNamespace = XNamespace.Get("http://schemas.microsoft.com/developer/msbuild/2003"); + + /// + /// Writes a solution file to the specified . + /// + /// The full path of the file to write. + /// The projects contained in this solution. + /// + /// Thrown when is null or empty. + /// Thrown when is null. + /// Thrown when contains no items. + public static void WriteSolutionFile(string targetFile, IEnumerable projects) + { + if (string.IsNullOrWhiteSpace(targetFile)) { + throw new ArgumentException("The target file cannot be null or empty.", nameof(targetFile)); + } + + if (projects == null) { + throw new ArgumentNullException(nameof(projects)); + } + + if (!projects.Any()) { + throw new InvalidOperationException("At least one project is expected."); + } + + using (var writer = new StreamWriter(targetFile)) { + WriteSolutionFile(writer, projects, Path.GetDirectoryName(targetFile)); + } + + FixProjectReferences(projects); + } + + private static void WriteSolutionFile(TextWriter writer, IEnumerable projects, string solutionPath) + { + WriteHeader(writer); + WriteProjects(writer, projects, solutionPath); + + writer.WriteLine("Global"); + + var platforms = WriteSolutionConfigurations(writer, projects); + WriteProjectConfigurations(writer, projects, platforms); + + writer.WriteLine("\tGlobalSection(SolutionProperties) = preSolution"); + writer.WriteLine("\t\tHideSolutionNode = FALSE"); + writer.WriteLine("\tEndGlobalSection"); + + writer.WriteLine("EndGlobal"); + } + + private static void WriteHeader(TextWriter writer) + { + writer.WriteLine("Microsoft Visual Studio Solution File, Format Version 12.00"); + writer.WriteLine("# Visual Studio 14"); + writer.WriteLine("VisualStudioVersion = 14.0.24720.0"); + writer.WriteLine("MinimumVisualStudioVersion = 10.0.40219.1"); + } + + private static void WriteProjects(TextWriter writer, IEnumerable projects, string solutionPath) + { + var solutionGuid = Guid.NewGuid().ToString("B").ToUpperInvariant(); + + foreach (var project in projects) { + var projectRelativePath = GetRelativePath(solutionPath, project.FilePath); + var projectGuid = project.Guid.ToString("B").ToUpperInvariant(); + + writer.WriteLine($"Project(\"{solutionGuid}\") = \"{project.ProjectName}\", \"{projectRelativePath}\", \"{projectGuid}\""); + writer.WriteLine("EndProject"); + } + } + + private static IEnumerable WriteSolutionConfigurations(TextWriter writer, IEnumerable projects) + { + var platforms = projects.GroupBy(p => p.PlatformName).Select(g => g.Key).ToList(); + + platforms.Sort(); + + writer.WriteLine("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution"); + foreach (var platform in platforms) { + writer.WriteLine($"\t\tDebug|{platform} = Debug|{platform}"); + } + + foreach (var platform in platforms) { + writer.WriteLine($"\t\tRelease|{platform} = Release|{platform}"); + } + + writer.WriteLine("\tEndGlobalSection"); + + return platforms; + } + + private static void WriteProjectConfigurations( + TextWriter writer, + IEnumerable projects, + IEnumerable solutionPlatforms) + { + writer.WriteLine("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution"); + + foreach (var project in projects) { + var projectGuid = project.Guid.ToString("B").ToUpperInvariant(); + + foreach (var platform in solutionPlatforms) { + writer.WriteLine($"\t\t{projectGuid}.Debug|{platform}.ActiveCfg = Debug|{project.PlatformName}"); + writer.WriteLine($"\t\t{projectGuid}.Debug|{platform}.Build.0 = Debug|{project.PlatformName}"); + } + + foreach (var platform in solutionPlatforms) { + writer.WriteLine($"\t\t{projectGuid}.Release|{platform}.ActiveCfg = Release|{project.PlatformName}"); + writer.WriteLine($"\t\t{projectGuid}.Release|{platform}.Build.0 = Release|{project.PlatformName}"); + } + } + + writer.WriteLine("\tEndGlobalSection"); + } + + private static void FixProjectReferences(IEnumerable projects) + { + var projectsMap = projects.ToDictionary(p => p.ProjectName, p => p); + + foreach (var project in projects) { + var projectDirectory = Path.GetDirectoryName(project.FilePath); + XDocument projectDoc = XDocument.Load(project.FilePath); + + var referencesItemGroups = projectDoc.Root + .Elements(ProjectFileNamespace + "ItemGroup") + .Where(e => e.Elements(ProjectFileNamespace + "Reference").Any()); + + foreach (var itemGroup in referencesItemGroups) { + FixProjectReferences(projectDirectory, itemGroup, projectsMap); + } + + projectDoc.Save(project.FilePath); + } + } + + private static void FixProjectReferences(string projectDirectory, XElement itemGroup, IDictionary projects) + { + foreach (var item in itemGroup.Elements(ProjectFileNamespace + "Reference").ToList()) { + var assemblyName = item.Attribute("Include")?.Value; + if (assemblyName != null && projects.TryGetValue(assemblyName, out var referencedProject)) { + item.Remove(); + + var projectReference = new XElement(ProjectFileNamespace + "ProjectReference", + new XElement(ProjectFileNamespace + "Project", referencedProject.Guid.ToString("B").ToUpperInvariant()), + new XElement(ProjectFileNamespace + "Name", referencedProject.ProjectName)); + projectReference.SetAttributeValue("Include", GetRelativePath(projectDirectory, referencedProject.FilePath)); + + itemGroup.Add(projectReference); + } + } + } + + private static string GetRelativePath(string fromPath, string toPath) + { + Uri fromUri = new Uri(AppendDirectorySeparatorChar(fromPath)); + Uri toUri = new Uri(AppendDirectorySeparatorChar(toPath)); + + if (fromUri.Scheme != toUri.Scheme) { + return toPath; + } + + Uri relativeUri = fromUri.MakeRelativeUri(toUri); + string relativePath = Uri.UnescapeDataString(relativeUri.ToString()); + + if (string.Equals(toUri.Scheme, Uri.UriSchemeFile, StringComparison.OrdinalIgnoreCase)) { + relativePath = relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); + } + + return relativePath; + } + + private static string AppendDirectorySeparatorChar(string path) + { + return Path.HasExtension(path) || path.EndsWith(Path.DirectorySeparatorChar.ToString()) + ? path + : path + Path.DirectorySeparatorChar; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs b/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs index d467ba883..6ec5d6802 100644 --- a/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs @@ -101,7 +101,7 @@ namespace ICSharpCode.Decompiler.CSharp } } - public void DecompileProject(PEFile moduleDefinition, string targetDirectory, TextWriter projectFileWriter, CancellationToken cancellationToken = default(CancellationToken)) + public ProjectId DecompileProject(PEFile moduleDefinition, string targetDirectory, TextWriter projectFileWriter, CancellationToken cancellationToken = default(CancellationToken)) { if (string.IsNullOrEmpty(targetDirectory)) { throw new InvalidOperationException("Must set TargetDirectory"); @@ -110,7 +110,7 @@ namespace ICSharpCode.Decompiler.CSharp directories.Clear(); var files = WriteCodeFilesInProject(moduleDefinition, cancellationToken).ToList(); files.AddRange(WriteResourceFilesInProject(moduleDefinition)); - WriteProjectFile(projectFileWriter, files, moduleDefinition); + return WriteProjectFile(projectFileWriter, files, moduleDefinition); } enum LanguageTargets @@ -120,11 +120,12 @@ namespace ICSharpCode.Decompiler.CSharp } #region WriteProjectFile - void WriteProjectFile(TextWriter writer, IEnumerable> files, Metadata.PEFile module) + ProjectId WriteProjectFile(TextWriter writer, IEnumerable> files, Metadata.PEFile module) { const string ns = "http://schemas.microsoft.com/developer/msbuild/2003"; string platformName = GetPlatformName(module); Guid guid = this.ProjectGuid ?? Guid.NewGuid(); + using (XmlTextWriter w = new XmlTextWriter(writer)) { w.Formatting = Formatting.Indented; w.WriteStartDocument(); @@ -281,6 +282,8 @@ namespace ICSharpCode.Decompiler.CSharp w.WriteEndDocument(); } + + return new ProjectId(platformName, guid); } protected virtual bool IsGacAssembly(Metadata.IAssemblyReference r, Metadata.PEFile asm) diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 8ab2fbd69..6efca3120 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -61,6 +61,7 @@ + diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index 2441e0eb2..57cab0ee8 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -343,12 +343,12 @@ namespace ICSharpCode.ILSpy } } - public override void DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) + public override object DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) { var module = assembly.GetPEFileOrNull(); if (options.FullDecompilation && options.SaveAsProjectDirectory != null) { var decompiler = new ILSpyWholeProjectDecompiler(assembly, options); - decompiler.DecompileProject(module, options.SaveAsProjectDirectory, new TextOutputWriter(output), options.CancellationToken); + return decompiler.DecompileProject(module, options.SaveAsProjectDirectory, new TextOutputWriter(output), options.CancellationToken); } else { AddReferenceAssemblyWarningMessage(module, output); AddReferenceWarningMessage(module, output); @@ -415,6 +415,8 @@ namespace ICSharpCode.ILSpy } WriteCode(output, options.DecompilerSettings, st, decompiler.TypeSystem); } + + return true; } } diff --git a/ILSpy/Languages/ILLanguage.cs b/ILSpy/Languages/ILLanguage.cs index e2ab9d1f3..da26caeb2 100644 --- a/ILSpy/Languages/ILLanguage.cs +++ b/ILSpy/Languages/ILLanguage.cs @@ -150,7 +150,7 @@ namespace ICSharpCode.ILSpy dis.DisassembleNamespace(nameSpace, module, types.Select(t => (TypeDefinitionHandle)t.MetadataToken)); } - public override void DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) + public override object DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) { output.WriteLine("// " + assembly.FileName); output.WriteLine(); @@ -174,6 +174,8 @@ namespace ICSharpCode.ILSpy dis.WriteModuleContents(module); } } + + return true; } } } diff --git a/ILSpy/Languages/Language.cs b/ILSpy/Languages/Language.cs index a1aad8d64..76f435244 100644 --- a/ILSpy/Languages/Language.cs +++ b/ILSpy/Languages/Language.cs @@ -131,11 +131,11 @@ namespace ICSharpCode.ILSpy WriteCommentLine(output, nameSpace); } - public virtual void DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) + public virtual object DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, assembly.FileName); var asm = assembly.GetPEFileOrNull(); - if (asm == null) return; + if (asm == null) return false; var metadata = asm.Metadata; if (metadata.IsAssembly) { var name = metadata.GetAssemblyDefinition(); @@ -147,6 +147,8 @@ namespace ICSharpCode.ILSpy } else { WriteCommentLine(output, metadata.GetString(metadata.GetModuleDefinition().Name)); } + + return true; } public virtual void WriteCommentLine(ITextOutput output, string comment) diff --git a/ILSpy/SolutionWriter.cs b/ILSpy/SolutionWriter.cs index a4ebf5779..45fd3ce67 100644 --- a/ILSpy/SolutionWriter.cs +++ b/ILSpy/SolutionWriter.cs @@ -27,6 +27,7 @@ using System.Threading; using System.Threading.Tasks; using System.Windows; using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.CSharp; using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TreeNodes; using Microsoft.Win32; @@ -130,6 +131,8 @@ namespace ICSharpCode.ILSpy { var solutionDirectory = Path.GetDirectoryName(solutionFilePath); var statusOutput = new ConcurrentBag(); + var projects = new ConcurrentBag(); + var result = new AvalonEditTextOutput(); var duplicates = new HashSet(); @@ -140,20 +143,45 @@ namespace ICSharpCode.ILSpy Stopwatch stopwatch = Stopwatch.StartNew(); - await Task.Run(() => Parallel.ForEach(assemblyNodes, n => WriteProject(n, solutionDirectory, statusOutput, ct))) - .ConfigureAwait(false); + try { + await Task.Run(() => Parallel.ForEach(assemblyNodes, n => WriteProject(n, solutionDirectory, statusOutput, projects, ct))) + .ConfigureAwait(false); + + await Task.Run(() => SolutionCreator.WriteSolutionFile(solutionFilePath, projects)) + .ConfigureAwait(false); + } catch (AggregateException ae) { + if (ae.Flatten().InnerExceptions.All(e => e is OperationCanceledException)) { + result.WriteLine(); + result.WriteLine("Generation was cancelled."); + return result; + } + result.WriteLine(); + result.WriteLine("Failed to generate the Visual Studio Solution. Errors:"); + ae.Handle(e => { + result.WriteLine(e.Message); + return true; + }); + + return result; + } foreach (var item in statusOutput) { result.WriteLine(item); } if (statusOutput.Count == 0) { - result.WriteLine("Successfully decompiled the following assemblies to a Visual Studio Solution:"); + result.WriteLine("Successfully decompiled the following assemblies into Visual Studio projects:"); foreach (var item in assemblyNodes.Select(n => n.Text.ToString())) { result.WriteLine(item); } + result.WriteLine(); + + if (assemblyNodes.Count() == projects.Count) { + result.WriteLine("Created the Visual Studio Solution file."); + } + result.WriteLine(); result.WriteLine("Elapsed time: " + stopwatch.Elapsed.TotalSeconds.ToString("F1") + " seconds."); result.WriteLine(); @@ -163,7 +191,12 @@ namespace ICSharpCode.ILSpy return result; } - private static void WriteProject(AssemblyTreeNode assemblyNode, string targetDirectory, ConcurrentBag statusOutput, CancellationToken ct) + private static void WriteProject( + AssemblyTreeNode assemblyNode, + string targetDirectory, + ConcurrentBag statusOutput, + ConcurrentBag targetContainer, + CancellationToken ct) { var loadedAssembly = assemblyNode.LoadedAssembly; @@ -186,12 +219,17 @@ namespace ICSharpCode.ILSpy FullDecompilation = true, CancellationToken = ct, SaveAsProjectDirectory = targetDirectory }; - - assemblyNode.Decompile(assemblyNode.Language, projectFileOutput, options); + + if (assemblyNode.Decompile(assemblyNode.Language, projectFileOutput, options) is ProjectId projectInfo) { + targetContainer.Add(new ProjectItem(projectFileName, projectInfo.PlatformName, projectInfo.Guid)); + } } - } catch (Exception e) { + } + catch (OperationCanceledException) { + throw; + } + catch (Exception e) { statusOutput.Add($"Failed to decompile the assembly '{loadedAssembly.FileName}':{Environment.NewLine}{e}"); - return; } } } diff --git a/ILSpy/TreeNodes/AssemblyListTreeNode.cs b/ILSpy/TreeNodes/AssemblyListTreeNode.cs index e53a3a883..3b8e5a519 100644 --- a/ILSpy/TreeNodes/AssemblyListTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyListTreeNode.cs @@ -141,7 +141,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public Action Select = delegate { }; - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, "List: " + assemblyList.ListName); output.WriteLine(); @@ -150,6 +150,8 @@ namespace ICSharpCode.ILSpy.TreeNodes output.WriteLine(); asm.Decompile(language, output, options); } + + return true; } #region Find*Node diff --git a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs index 8aa8f8c0d..192820246 100644 --- a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs @@ -79,7 +79,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { var loaded = parentAssembly.LoadedAssembly.LoadedAssemblyReferencesInfo.TryGetInfo(r.FullName, out var info); if (r.IsWindowsRuntime) { @@ -98,6 +98,8 @@ namespace ICSharpCode.ILSpy.TreeNodes output.Unindent(); output.WriteLine(); } + + return true; } } } diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index 60f34e6b2..39669c15f 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -240,7 +240,7 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Recurse; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { void HandleException(Exception ex, string message) { @@ -259,21 +259,21 @@ namespace ICSharpCode.ILSpy.TreeNodes switch (ex.InnerException) { case BadImageFormatException badImage: HandleException(badImage, "This file does not contain a managed assembly."); - return; + return null; case FileNotFoundException fileNotFound: HandleException(fileNotFound, "The file was not found."); - return; + return null; case DirectoryNotFoundException dirNotFound: HandleException(dirNotFound, "The directory was not found."); - return; + return null; case PEFileNotSupportedException notSupported: HandleException(notSupported, notSupported.Message); - return; + return null; default: throw; } } - language.DecompileAssembly(LoadedAssembly, output, options); + return language.DecompileAssembly(LoadedAssembly, output, options); } public override bool Save(DecompilerTextView textView) diff --git a/ILSpy/TreeNodes/BaseTypesEntryNode.cs b/ILSpy/TreeNodes/BaseTypesEntryNode.cs index 3c96c40dd..41edd0f8c 100644 --- a/ILSpy/TreeNodes/BaseTypesEntryNode.cs +++ b/ILSpy/TreeNodes/BaseTypesEntryNode.cs @@ -97,9 +97,10 @@ namespace ICSharpCode.ILSpy.TreeNodes return false; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, language.TypeToString(type, includeNamespace: true)); + return true; } IEntity IMemberTreeNode.Member { diff --git a/ILSpy/TreeNodes/BaseTypesTreeNode.cs b/ILSpy/TreeNodes/BaseTypesTreeNode.cs index 8cbdb2304..58565a938 100644 --- a/ILSpy/TreeNodes/BaseTypesTreeNode.cs +++ b/ILSpy/TreeNodes/BaseTypesTreeNode.cs @@ -69,12 +69,14 @@ namespace ICSharpCode.ILSpy.TreeNodes } } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren)); foreach (ILSpyTreeNode child in this.Children) { child.Decompile(language, output, options); } + + return true; } } } \ No newline at end of file diff --git a/ILSpy/TreeNodes/DerivedTypesEntryNode.cs b/ILSpy/TreeNodes/DerivedTypesEntryNode.cs index 91da3e033..f3d9fe29f 100644 --- a/ILSpy/TreeNodes/DerivedTypesEntryNode.cs +++ b/ILSpy/TreeNodes/DerivedTypesEntryNode.cs @@ -90,9 +90,10 @@ namespace ICSharpCode.ILSpy.TreeNodes e.Handled = BaseTypesEntryNode.ActivateItem(this, type); } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, language.TypeToString(type, includeNamespace: true)); + return true; } IEntity IMemberTreeNode.Member => type; diff --git a/ILSpy/TreeNodes/DerivedTypesTreeNode.cs b/ILSpy/TreeNodes/DerivedTypesTreeNode.cs index 3b6e50a30..8518e08e3 100644 --- a/ILSpy/TreeNodes/DerivedTypesTreeNode.cs +++ b/ILSpy/TreeNodes/DerivedTypesTreeNode.cs @@ -92,9 +92,10 @@ namespace ICSharpCode.ILSpy.TreeNodes return typeRef.GetFullTypeName(referenceMetadata) == typeDef.GetFullTypeName(definitionMetadata); } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { threading.Decompile(language, output, options, EnsureLazyChildren); + return true; } } } \ No newline at end of file diff --git a/ILSpy/TreeNodes/EventTreeNode.cs b/ILSpy/TreeNodes/EventTreeNode.cs index 1aadff896..c1797f794 100644 --- a/ILSpy/TreeNodes/EventTreeNode.cs +++ b/ILSpy/TreeNodes/EventTreeNode.cs @@ -67,9 +67,10 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Hidden; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.DecompileEvent(EventDefinition, output, options); + return true; } public override bool IsPublicAPI { diff --git a/ILSpy/TreeNodes/FieldTreeNode.cs b/ILSpy/TreeNodes/FieldTreeNode.cs index 5d4d2f7b0..5d3323beb 100644 --- a/ILSpy/TreeNodes/FieldTreeNode.cs +++ b/ILSpy/TreeNodes/FieldTreeNode.cs @@ -68,9 +68,10 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Hidden; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.DecompileField(FieldDefinition, output, options); + return true; } public override bool IsPublicAPI { diff --git a/ILSpy/TreeNodes/ILSpyTreeNode.cs b/ILSpy/TreeNodes/ILSpyTreeNode.cs index 11fe3b278..515188374 100644 --- a/ILSpy/TreeNodes/ILSpyTreeNode.cs +++ b/ILSpy/TreeNodes/ILSpyTreeNode.cs @@ -57,7 +57,7 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Hidden; } - public abstract void Decompile(Language language, ITextOutput output, DecompilationOptions options); + public abstract object Decompile(Language language, ITextOutput output, DecompilationOptions options); /// /// Used to implement special view logic for some items. diff --git a/ILSpy/TreeNodes/MethodTreeNode.cs b/ILSpy/TreeNodes/MethodTreeNode.cs index 9e4d95e45..89c12ec10 100644 --- a/ILSpy/TreeNodes/MethodTreeNode.cs +++ b/ILSpy/TreeNodes/MethodTreeNode.cs @@ -83,9 +83,10 @@ namespace ICSharpCode.ILSpy.TreeNodes } } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.DecompileMethod(MethodDefinition, output, options); + return true; } public override FilterResult Filter(FilterSettings settings) diff --git a/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs b/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs index 4d51f46af..a363c46db 100644 --- a/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs @@ -72,10 +72,11 @@ namespace ICSharpCode.ILSpy.TreeNodes } } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, moduleName); language.WriteCommentLine(output, containsMetadata ? "contains metadata" : "contains no metadata"); + return true; } } } diff --git a/ILSpy/TreeNodes/NamespaceTreeNode.cs b/ILSpy/TreeNodes/NamespaceTreeNode.cs index d054603dc..3b9ff4845 100644 --- a/ILSpy/TreeNodes/NamespaceTreeNode.cs +++ b/ILSpy/TreeNodes/NamespaceTreeNode.cs @@ -56,9 +56,10 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Recurse; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.DecompileNamespace(name, this.Children.OfType().Select(t => t.TypeDefinition), output, options); + return true; } } } diff --git a/ILSpy/TreeNodes/PropertyTreeNode.cs b/ILSpy/TreeNodes/PropertyTreeNode.cs index 14769ea66..3267a1e1a 100644 --- a/ILSpy/TreeNodes/PropertyTreeNode.cs +++ b/ILSpy/TreeNodes/PropertyTreeNode.cs @@ -74,9 +74,10 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Hidden; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.DecompileProperty(PropertyDefinition, output, options); + return true; } public override bool IsPublicAPI { diff --git a/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs b/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs index 331f8da08..9121e213f 100644 --- a/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs +++ b/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs @@ -62,7 +62,7 @@ namespace ICSharpCode.ILSpy.TreeNodes this.Children.Add(new ModuleReferenceTreeNode(parentAssembly, r, metadata)); } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, $"Detected Target-Framework-Id: {parentAssembly.LoadedAssembly.GetTargetFrameworkIdAsync().Result}"); App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren)); @@ -86,7 +86,7 @@ namespace ICSharpCode.ILSpy.TreeNodes output.Unindent(); output.WriteLine(); } - + return true; } } } diff --git a/ILSpy/TreeNodes/ResourceListTreeNode.cs b/ILSpy/TreeNodes/ResourceListTreeNode.cs index c67c9d4b1..79cc1df50 100644 --- a/ILSpy/TreeNodes/ResourceListTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceListTreeNode.cs @@ -64,13 +64,15 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Recurse; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren)); foreach (ILSpyTreeNode child in this.Children) { child.Decompile(language, output, options); output.WriteLine(); } + + return true; } } } diff --git a/ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs index fb8b2199f..3805db739 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs @@ -75,9 +75,10 @@ namespace ICSharpCode.ILSpy.TreeNodes } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { EnsureLazyChildren(); + return true; } } } diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs index ec74ad193..cf5a3d5ac 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs @@ -73,9 +73,10 @@ namespace ICSharpCode.ILSpy.TreeNodes return result; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, string.Format("{0} = {1}", key, data)); + return true; } public override bool Save(DecompilerTextView textView) diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs index 3433f70d7..c9adaa49c 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs @@ -67,7 +67,7 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Hidden; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, string.Format("{0} ({1}, {2})", r.Name, r.ResourceType, r.Attributes)); @@ -76,6 +76,8 @@ namespace ICSharpCode.ILSpy.TreeNodes smartOutput.AddButton(Images.Save, Resources.Save, delegate { Save(MainWindow.Instance.TextView); }); output.WriteLine(); } + + return true; } public override bool View(DecompilerTextView textView) diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs index c1d908481..a44edeae4 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs @@ -141,7 +141,7 @@ namespace ICSharpCode.ILSpy.TreeNodes return true; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { EnsureLazyChildren(); base.Decompile(language, output, options); @@ -168,6 +168,8 @@ namespace ICSharpCode.ILSpy.TreeNodes } output.WriteLine(); } + + return true; } internal class SerializedObjectRepresentation diff --git a/ILSpy/TreeNodes/ThreadingSupport.cs b/ILSpy/TreeNodes/ThreadingSupport.cs index e95787efc..8df078cad 100644 --- a/ILSpy/TreeNodes/ThreadingSupport.cs +++ b/ILSpy/TreeNodes/ThreadingSupport.cs @@ -128,8 +128,9 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Match; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { + return false; } } @@ -151,8 +152,9 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Match; } - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { + return false; } } diff --git a/ILSpy/TreeNodes/TypeTreeNode.cs b/ILSpy/TreeNodes/TypeTreeNode.cs index 3ef91c411..4464a3521 100644 --- a/ILSpy/TreeNodes/TypeTreeNode.cs +++ b/ILSpy/TreeNodes/TypeTreeNode.cs @@ -103,9 +103,10 @@ namespace ICSharpCode.ILSpy.TreeNodes public override bool CanExpandRecursively => true; - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.DecompileType(TypeDefinition, output, options); + return true; } public override object Icon => GetIcon(TypeDefinition); From bd77b8301f6dacc7a028a9516b66fb3afd7ae801 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 24 Jun 2019 17:51:13 +0200 Subject: [PATCH 014/221] Basic implementation of local functions. --- .../CSharp/CSharpDecompiler.cs | 3 +- .../OutputVisitor/CSharpOutputVisitor.cs | 19 ++++ .../CSharp/StatementBuilder.cs | 11 ++ .../CSharp/Syntax/DepthFirstAstVisitor.cs | 21 +++- .../CSharp/Syntax/IAstVisitor.cs | 3 + .../LocalFunctionDeclarationStatement.cs | 94 ++++++++++++++++ .../CSharp/Transforms/DeclareVariables.cs | 2 +- ICSharpCode.Decompiler/DecompilerSettings.cs | 2 +- .../ICSharpCode.Decompiler.csproj | 1 + .../IL/ControlFlow/YieldReturnDecompiler.cs | 2 +- ICSharpCode.Decompiler/IL/ILReader.cs | 4 +- .../IL/Instructions/Block.cs | 15 +++ .../IL/Instructions/ILFunction.cs | 64 ++++++++++- .../IL/Transforms/DelegateConstruction.cs | 8 +- .../IL/Transforms/LocalFunctionDecompiler.cs | 103 +++++++++++++++++- .../IL/Transforms/ProxyCallReplacer.cs | 2 +- .../IL/Transforms/TransformExpressionTrees.cs | 7 +- .../Rewrite/ConnectionIdRewritePass.cs | 2 +- ILSpy/Languages/ILAstLanguage.cs | 2 +- 19 files changed, 343 insertions(+), 22 deletions(-) create mode 100644 ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 5863da05e..891763b90 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -156,6 +156,7 @@ namespace ICSharpCode.Decompiler.CSharp }, new ProxyCallReplacer(), new DelegateConstruction(), + new LocalFunctionDecompiler(), new HighLevelLoopTransform(), new ReduceNestingTransform(), new IntroduceDynamicTypeOnLocals(), @@ -395,7 +396,7 @@ namespace ICSharpCode.Decompiler.CSharp return new DecompilerTypeSystem(file, resolver); } - TypeSystemAstBuilder CreateAstBuilder(ITypeResolveContext decompilationContext) + internal static TypeSystemAstBuilder CreateAstBuilder(ITypeResolveContext decompilationContext) { var typeSystemAstBuilder = new TypeSystemAstBuilder(); typeSystemAstBuilder.ShowAttributes = true; diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs index d3bef7f73..038c44455 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -1883,6 +1883,25 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Semicolon(); EndNode(variableDeclarationStatement); } + + public virtual void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement) + { + StartNode(localFunctionDeclarationStatement); + + WriteModifiers(localFunctionDeclarationStatement.ModifierTokens); + localFunctionDeclarationStatement.ReturnType.AcceptVisitor(this); + Space(); + WriteIdentifier(localFunctionDeclarationStatement.NameToken); + WriteTypeParameters(localFunctionDeclarationStatement.TypeParameters); + Space(policy.SpaceBeforeMethodDeclarationParentheses); + WriteCommaSeparatedListInParenthesis(localFunctionDeclarationStatement.Parameters, policy.SpaceWithinMethodDeclarationParentheses); + foreach (Constraint constraint in localFunctionDeclarationStatement.Constraints) { + constraint.AcceptVisitor(this); + } + WriteMethodBody(localFunctionDeclarationStatement.Body, policy.MethodBraceStyle); + + EndNode(localFunctionDeclarationStatement); + } public virtual void VisitWhileStatement(WhileStatement whileStatement) { diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index a799c9cf4..70f58b7eb 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -1015,5 +1015,16 @@ namespace ICSharpCode.Decompiler.CSharp stmt.InsertChildAfter(null, new Comment(" IL cpblk instruction"), Roles.Comment); return stmt; } + + protected internal override Statement VisitILFunction(ILFunction function) + { + var stmt = new LocalFunctionDeclarationStatement(); + var tsab = CSharpDecompiler.CreateAstBuilder(null); + stmt.Name = function.Method.Name; + stmt.Parameters.AddRange(function.Method.Parameters.Select(tsab.ConvertParameter)); + stmt.ReturnType = tsab.ConvertType(function.Method.ReturnType); + stmt.Body = ConvertAsBlock(function.Body); + return stmt; + } } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs b/ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs index 44add8be6..3b3a0b216 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/DepthFirstAstVisitor.cs @@ -390,7 +390,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { VisitChildren (variableDeclarationStatement); } - + + public virtual void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement) + { + VisitChildren(localFunctionDeclarationStatement); + } + public virtual void VisitWhileStatement (WhileStatement whileStatement) { VisitChildren (whileStatement); @@ -1037,7 +1042,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { return VisitChildren (variableDeclarationStatement); } - + + public virtual T VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement) + { + return VisitChildren(localFunctionDeclarationStatement); + } + public virtual T VisitWhileStatement (WhileStatement whileStatement) { return VisitChildren (whileStatement); @@ -1684,7 +1694,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { return VisitChildren (variableDeclarationStatement, data); } - + + public virtual S VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement, T data) + { + return VisitChildren(localFunctionDeclarationStatement, data); + } + public virtual S VisitWhileStatement (WhileStatement whileStatement, T data) { return VisitChildren (whileStatement, data); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs b/ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs index 2d5bd8760..9ed151865 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/IAstVisitor.cs @@ -110,6 +110,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax void VisitUnsafeStatement(UnsafeStatement unsafeStatement); void VisitUsingStatement(UsingStatement usingStatement); void VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement); + void VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement); void VisitWhileStatement(WhileStatement whileStatement); void VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement); void VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement); @@ -251,6 +252,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax S VisitUnsafeStatement(UnsafeStatement unsafeStatement); S VisitUsingStatement(UsingStatement usingStatement); S VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement); + S VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement); S VisitWhileStatement(WhileStatement whileStatement); S VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement); S VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement); @@ -392,6 +394,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax S VisitUnsafeStatement(UnsafeStatement unsafeStatement, T data); S VisitUsingStatement(UsingStatement usingStatement, T data); S VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, T data); + S VisitLocalFunctionDeclarationStatement(LocalFunctionDeclarationStatement localFunctionDeclarationStatement, T data); S VisitWhileStatement(WhileStatement whileStatement, T data); S VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement, T data); S VisitYieldReturnStatement(YieldReturnStatement yieldReturnStatement, T data); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs new file mode 100644 index 000000000..581b53bfa --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Text; +using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; + +namespace ICSharpCode.Decompiler.CSharp.Syntax +{ + public class LocalFunctionDeclarationStatement : Statement + { + public AstNodeCollection TypeParameters { + get { return GetChildrenByRole(Roles.TypeParameter); } + } + + public CSharpTokenNode LParToken { + get { return GetChildByRole(Roles.LPar); } + } + + public AstNodeCollection Parameters { + get { return GetChildrenByRole(Roles.Parameter); } + } + + public CSharpTokenNode RParToken { + get { return GetChildByRole(Roles.RPar); } + } + + public AstNodeCollection Constraints { + get { return GetChildrenByRole(Roles.Constraint); } + } + + public BlockStatement Body { + get { return GetChildByRole(Roles.Body); } + set { SetChildByRole(Roles.Body, value); } + } + + public Modifiers Modifiers { + get { return EntityDeclaration.GetModifiers(this); } + set { EntityDeclaration.SetModifiers(this, value); } + } + + public bool HasModifier(Modifiers mod) + { + return (Modifiers & mod) == mod; + } + + public IEnumerable ModifierTokens { + get { return GetChildrenByRole(EntityDeclaration.ModifierRole); } + } + + public virtual string Name { + get { + return GetChildByRole(Roles.Identifier).Name; + } + set { + SetChildByRole(Roles.Identifier, Identifier.Create(value, TextLocation.Empty)); + } + } + + public virtual Identifier NameToken { + get { return GetChildByRole(Roles.Identifier); } + set { SetChildByRole(Roles.Identifier, value); } + } + + public virtual AstType ReturnType { + get { return GetChildByRole(Roles.Type); } + set { SetChildByRole(Roles.Type, value); } + } + + public override void AcceptVisitor(IAstVisitor visitor) + { + visitor.VisitLocalFunctionDeclarationStatement(this); + } + + public override T AcceptVisitor(IAstVisitor visitor) + { + return visitor.VisitLocalFunctionDeclarationStatement(this); + } + + public override S AcceptVisitor(IAstVisitor visitor, T data) + { + return visitor.VisitLocalFunctionDeclarationStatement(this, data); + } + + protected internal override bool DoMatch(AstNode other, Match match) + { + LocalFunctionDeclarationStatement o = other as LocalFunctionDeclarationStatement; + return o != null && MatchString(this.Name, o.Name) + && (this.Modifiers == Modifiers.Any || this.Modifiers == o.Modifiers) + && this.ReturnType.DoMatch(o.ReturnType, match) + && this.TypeParameters.DoMatch(o.TypeParameters, match) + && this.Parameters.DoMatch(o.Parameters, match) && this.Constraints.DoMatch(o.Constraints, match) + && this.Body.DoMatch(o.Body, match); + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs index 4b2051cc9..f9cb5633e 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs @@ -260,7 +260,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } else { newPoint = new InsertionPoint { level = nodeLevel, nextNode = identExpr }; if (variable.HasInitialValue) { - // Uninitialized variables are logically initialized at the beginning of the functin + // Uninitialized variables are logically initialized at the beginning of the function // Because it's possible that the variable has a loop-carried dependency, // declare it outside of any loops. while (startIndex >= 0) { diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index 163d58346..f6e7a94dd 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -942,7 +942,7 @@ namespace ICSharpCode.Decompiler } } - bool localFunctions = false; + bool localFunctions = true; /// /// Gets/Sets whether C# 7.0 local functions should be used. diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 8ab2fbd69..5a8d439e2 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -161,6 +161,7 @@ + diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs index fc6f6dcc0..c4db8d89a 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs @@ -390,7 +390,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow methodTypeParameters: null); var body = context.TypeSystem.MainModule.PEFile.Reader.GetMethodBody(methodDef.RelativeVirtualAddress); var il = context.CreateILReader() - .ReadIL(method, body, genericContext, context.CancellationToken); + .ReadIL(method, body, genericContext, context.Function.Kind, context.CancellationToken); il.RunTransforms(CSharpDecompiler.EarlyILTransforms(true), new ILTransformContext(il, context.TypeSystem, context.DebugInfo, context.Settings) { CancellationToken = context.CancellationToken, diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 4d98af2fd..2c3c8b654 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -480,14 +480,14 @@ namespace ICSharpCode.Decompiler.IL /// /// Decodes the specified method body and returns an ILFunction. /// - public ILFunction ReadIL(MethodDefinitionHandle method, MethodBodyBlock body, GenericContext genericContext = default, CancellationToken cancellationToken = default) + public ILFunction ReadIL(MethodDefinitionHandle method, MethodBodyBlock body, GenericContext genericContext = default, ILFunctionKind kind = ILFunctionKind.TopLevelFunction, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); Init(method, body, genericContext); ReadInstructions(cancellationToken); var blockBuilder = new BlockBuilder(body, variableByExceptionHandler); blockBuilder.CreateBlocks(mainContainer, instructionBuilder, isBranchTarget, cancellationToken); - var function = new ILFunction(this.method, body.GetCodeSize(), this.genericContext, mainContainer); + var function = new ILFunction(this.method, body.GetCodeSize(), this.genericContext, mainContainer, kind); CollectionExtensions.AddRange(function.Variables, parameterVariables); CollectionExtensions.AddRange(function.Variables, localVariables); CollectionExtensions.AddRange(function.Variables, stackVariables); diff --git a/ICSharpCode.Decompiler/IL/Instructions/Block.cs b/ICSharpCode.Decompiler/IL/Instructions/Block.cs index ab04f550e..157c37b79 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/Block.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/Block.cs @@ -280,6 +280,21 @@ namespace ICSharpCode.Decompiler.IL return inst; } + /// + /// Gets the closest parent Block. + /// Returns null, if the instruction is not a descendant of a Block. + /// + public static Block FindClosestBlock(ILInstruction inst) + { + var curr = inst; + while (curr != null) { + if (curr is Block) + return (Block)curr; + curr = curr.Parent; + } + return null; + } + public bool MatchInlineAssignBlock(out CallInstruction call, out ILInstruction value) { call = null; diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs index f77106260..cb4acd62c 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs @@ -99,13 +99,22 @@ namespace ICSharpCode.Decompiler.IL /// public IType DelegateType; - public bool IsExpressionTree => DelegateType != null && DelegateType.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1; + ILFunctionKind kind; + + public ILFunctionKind Kind { + get => kind; + set { + if (kind == ILFunctionKind.TopLevelFunction || kind == ILFunctionKind.LocalFunction) + throw new InvalidOperationException("ILFunction.Kind of a top-level or local function may not be changed."); + kind = value; + } + } public readonly IType ReturnType; public readonly IReadOnlyList Parameters; - public ILFunction(IMethod method, int codeSize, GenericContext genericContext, ILInstruction body) : base(OpCode.ILFunction) + public ILFunction(IMethod method, int codeSize, GenericContext genericContext, ILInstruction body, ILFunctionKind kind = ILFunctionKind.TopLevelFunction) : base(OpCode.ILFunction) { this.Method = method; this.CodeSize = codeSize; @@ -114,6 +123,7 @@ namespace ICSharpCode.Decompiler.IL this.ReturnType = Method?.ReturnType; this.Parameters = Method?.Parameters; this.Variables = new ILVariableCollection(this); + this.kind = kind; } public ILFunction(IType returnType, IReadOnlyList parameters, GenericContext genericContext, ILInstruction body) : base(OpCode.ILFunction) @@ -123,10 +133,31 @@ namespace ICSharpCode.Decompiler.IL this.ReturnType = returnType; this.Parameters = parameters; this.Variables = new ILVariableCollection(this); + this.kind = ILFunctionKind.ExpressionTree; } internal override void CheckInvariant(ILPhase phase) { + switch (kind) { + case ILFunctionKind.TopLevelFunction: + Debug.Assert(Parent == null); + Debug.Assert(DelegateType == null); + break; + case ILFunctionKind.Delegate: + Debug.Assert(Parent != null && !(Parent is Block)); + Debug.Assert(DelegateType != null); + Debug.Assert(!(DelegateType?.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1)); + break; + case ILFunctionKind.ExpressionTree: + Debug.Assert(Parent != null && !(Parent is Block)); + Debug.Assert(DelegateType != null); + Debug.Assert(DelegateType?.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1); + break; + case ILFunctionKind.LocalFunction: + Debug.Assert(Parent is Block); + Debug.Assert(DelegateType == null); + break; + } for (int i = 0; i < Variables.Count; i++) { Debug.Assert(Variables[i].Function == this); Debug.Assert(Variables[i].IndexInFunction == i); @@ -148,8 +179,13 @@ namespace ICSharpCode.Decompiler.IL output.Write(' '); Method.WriteTo(output); } - if (IsExpressionTree) { - output.Write(".ET"); + switch (kind) { + case ILFunctionKind.ExpressionTree: + output.Write(".ET"); + break; + case ILFunctionKind.LocalFunction: + output.Write(".local"); + break; } if (DelegateType != null) { output.Write("["); @@ -290,4 +326,24 @@ namespace ICSharpCode.Decompiler.IL Debug.Assert(ok); } } + + public enum ILFunctionKind + { + /// + /// ILFunction is a "top-level" function, i.e., method, accessor, constructor, destructor or operator. + /// + TopLevelFunction, + /// + /// ILFunction is a delegate or lambda expression. + /// + Delegate, + /// + /// ILFunction is an expression tree lambda. + /// + ExpressionTree, + /// + /// ILFunction is a C# 7.0 local function. + /// + LocalFunction + } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs index aa4148c33..c39caba78 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs @@ -175,11 +175,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms var targetMethod = ((IInstructionWithMethodOperand)value.Arguments[1]).Method; if (!IsAnonymousMethod(decompilationContext.CurrentTypeDefinition, targetMethod)) return null; - if (LocalFunctionDecompiler.IsLocalFunctionMethod(targetMethod.ParentModule.PEFile, (MethodDefinitionHandle)targetMethod.MetadataToken)) - return null; - target = value.Arguments[0]; if (targetMethod.MetadataToken.IsNil) return null; + if (LocalFunctionDecompiler.IsLocalFunctionMethod(targetMethod)) + return null; + target = value.Arguments[0]; var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken); if (!methodDefinition.HasBody()) return null; @@ -188,7 +188,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return null; var ilReader = context.CreateILReader(); var body = context.PEFile.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress); - var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, context.CancellationToken); + var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, ILFunctionKind.Delegate, context.CancellationToken); function.DelegateType = value.Method.DeclaringType; function.CheckInvariant(ILPhase.Normal); // Embed the lambda into the parent function's ILAst, so that "Show steps" can show diff --git a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs index b54adc273..cd38b463c 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs @@ -1,20 +1,121 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using System.Reflection; using System.Reflection.Metadata; using System.Text; using System.Text.RegularExpressions; +using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { class LocalFunctionDecompiler : IILTransform { + ILTransformContext context; + ITypeResolveContext decompilationContext; + public void Run(ILFunction function, ILTransformContext context) { - throw new NotImplementedException(); + if (!context.Settings.LocalFunctions) + return; + this.context = context; + this.decompilationContext = new SimpleTypeResolveContext(function.Method); + var localFunctions = new Dictionary>(); + var cancellationToken = context.CancellationToken; + // Find use-sites + foreach (var inst in function.Descendants) { + cancellationToken.ThrowIfCancellationRequested(); + if (inst is Call call && IsLocalFunctionMethod(call.Method)) { + context.StepStartGroup($"LocalFunctionDecompiler {call.StartILOffset}", call); + if (!localFunctions.TryGetValue(call.Method, out var info)) { + info = new List() { call }; + localFunctions.Add(call.Method, info); + } else { + info.Add(call); + } + context.StepEndGroup(); + } + } + + foreach (var (method, useSites) in localFunctions) { + var insertionPoint = FindInsertionPoint(useSites); + if (TransformLocalFunction(method, (Block)insertionPoint.Parent, insertionPoint.ChildIndex + 1) == null) + continue; + } + } + + static ILInstruction FindInsertionPoint(List useSites) + { + ILInstruction insertionPoint = null; + foreach (var call in useSites) { + if (insertionPoint == null) { + insertionPoint = GetStatement(call); + continue; + } + + var ancestor = FindCommonAncestorInstruction(insertionPoint, GetStatement(call)); + + if (ancestor == null) + return null; + + insertionPoint = ancestor; + } + + switch (insertionPoint) { + case BlockContainer bc: + return insertionPoint; + case Block b: + return insertionPoint; + default: + return insertionPoint; + } + } + + static ILInstruction FindCommonAncestorInstruction(ILInstruction a, ILInstruction b) + { + var ancestorsOfB = new HashSet(b.Ancestors); + return a.Ancestors.FirstOrDefault(ancestorsOfB.Contains); + } + + static ILInstruction GetStatement(ILInstruction inst) + { + while (inst.Parent != null) { + if (inst.Parent is Block) + return inst; + inst = inst.Parent; + } + return inst; + } + + private ILFunction TransformLocalFunction(IMethod targetMethod, Block parent, int insertionPoint) + { + var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken); + if (!methodDefinition.HasBody()) + return null; + var genericContext = DelegateConstruction.GenericContextFromTypeArguments(targetMethod.Substitution); + if (genericContext == null) + return null; + var ilReader = context.CreateILReader(); + var body = context.PEFile.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress); + var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, ILFunctionKind.LocalFunction, context.CancellationToken); + // Embed the local function into the parent function's ILAst, so that "Show steps" can show + // how the local function body is being transformed. + parent.Instructions.Insert(insertionPoint, function); + function.CheckInvariant(ILPhase.Normal); + + var nestedContext = new ILTransformContext(context, function); + function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is LocalFunctionDecompiler)), nestedContext); + + return function; + } + + public static bool IsLocalFunctionMethod(IMethod method) + { + return IsLocalFunctionMethod(method.ParentModule.PEFile, (MethodDefinitionHandle)method.MetadataToken); } public static bool IsLocalFunctionMethod(PEFile module, MethodDefinitionHandle methodHandle) diff --git a/ICSharpCode.Decompiler/IL/Transforms/ProxyCallReplacer.cs b/ICSharpCode.Decompiler/IL/Transforms/ProxyCallReplacer.cs index 6b5383150..d9279f1bb 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ProxyCallReplacer.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ProxyCallReplacer.cs @@ -36,7 +36,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // partially copied from CSharpDecompiler var ilReader = context.CreateILReader(); var body = context.PEFile.Reader.GetMethodBody(methodDef.RelativeVirtualAddress); - var proxyFunction = ilReader.ReadIL(handle, body, genericContext.Value, context.CancellationToken); + var proxyFunction = ilReader.ReadIL(handle, body, genericContext.Value, ILFunctionKind.TopLevelFunction, context.CancellationToken); var transformContext = new ILTransformContext(context, proxyFunction); proxyFunction.RunTransforms(CSharp.CSharpDecompiler.EarlyILTransforms(), transformContext); if (!(proxyFunction.Body is BlockContainer blockContainer)) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs index 89c92bb69..8713dc87c 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs @@ -197,6 +197,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms void SetExpressionTreeFlag(ILFunction lambda, CallInstruction call) { + lambda.Kind = IsExpressionTree(call.Method.ReturnType) ? ILFunctionKind.ExpressionTree : ILFunctionKind.Delegate; lambda.DelegateType = call.Method.ReturnType; } @@ -341,7 +342,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } return (null, SpecialType.UnknownType); case ILFunction function: - if (function.IsExpressionTree) { + if (function.Kind == ILFunctionKind.ExpressionTree) { function.DelegateType = UnwrapExpressionTree(function.DelegateType); } return (function, function.DelegateType); @@ -372,6 +373,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } + bool IsExpressionTree(IType delegateType) => delegateType is ParameterizedType pt + && pt.FullName == "System.Linq.Expressions.Expression" + && pt.TypeArguments.Count == 1; + IType UnwrapExpressionTree(IType delegateType) { if (delegateType is ParameterizedType pt && pt.FullName == "System.Linq.Expressions.Expression" && pt.TypeArguments.Count == 1) { diff --git a/ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs b/ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs index 82ab77302..d4875c0af 100644 --- a/ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs +++ b/ILSpy.BamlDecompiler/Rewrite/ConnectionIdRewritePass.cs @@ -94,7 +94,7 @@ namespace ILSpy.BamlDecompiler.Rewrite // decompile method and optimize the switch var ilReader = new ILReader(ctx.TypeSystem.MainModule); - var function = ilReader.ReadIL((MethodDefinitionHandle)method.MetadataToken, body, genericContext, ctx.CancellationToken); + var function = ilReader.ReadIL((MethodDefinitionHandle)method.MetadataToken, body, genericContext, ILFunctionKind.TopLevelFunction, ctx.CancellationToken); var context = new ILTransformContext(function, ctx.TypeSystem, null) { CancellationToken = ctx.CancellationToken diff --git a/ILSpy/Languages/ILAstLanguage.cs b/ILSpy/Languages/ILAstLanguage.cs index fa278e490..3d0fbdc8a 100644 --- a/ILSpy/Languages/ILAstLanguage.cs +++ b/ILSpy/Languages/ILAstLanguage.cs @@ -119,7 +119,7 @@ namespace ICSharpCode.ILSpy var reader = new ILReader(typeSystem.MainModule); reader.UseDebugSymbols = options.DecompilerSettings.UseDebugSymbols; var methodBody = module.Reader.GetMethodBody(methodDef.RelativeVirtualAddress); - ILFunction il = reader.ReadIL((SRM.MethodDefinitionHandle)method.MetadataToken, methodBody, cancellationToken: options.CancellationToken); + ILFunction il = reader.ReadIL((SRM.MethodDefinitionHandle)method.MetadataToken, methodBody, kind: ILFunctionKind.TopLevelFunction, cancellationToken: options.CancellationToken); var namespaces = new HashSet(); var decompiler = new CSharpDecompiler(typeSystem, options.DecompilerSettings) { CancellationToken = options.CancellationToken }; ILTransformContext context = decompiler.CreateILTransformContext(il); From 5a2302089bd7154cae9c6829f9848e7b9786650e Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 29 Jun 2019 11:18:42 +0200 Subject: [PATCH 015/221] Refactor TransformDisplayClassUsage into separate transform. Make display class detection pattern-based instead of name-based. Fixes #1554 --- .../CSharp/CSharpDecompiler.cs | 1 + .../ICSharpCode.Decompiler.csproj | 1 + .../IL/Transforms/DelegateConstruction.cs | 197 +-------------- ...ransformCollectionAndObjectInitializers.cs | 4 +- .../Transforms/TransformDisplayClassUsage.cs | 229 ++++++++++++++++++ .../IL/Transforms/TransformExpressionTrees.cs | 18 +- 6 files changed, 259 insertions(+), 191 deletions(-) create mode 100644 ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 5863da05e..bb85153bf 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -156,6 +156,7 @@ namespace ICSharpCode.Decompiler.CSharp }, new ProxyCallReplacer(), new DelegateConstruction(), + new TransformDisplayClassUsage(), new HighLevelLoopTransform(), new ReduceNestingTransform(), new IntroduceDynamicTypeOnLocals(), diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 8ab2fbd69..3fca3af6e 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -272,6 +272,7 @@ + diff --git a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs index aa4148c33..021f34767 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs @@ -37,7 +37,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms return; this.context = context; this.decompilationContext = new SimpleTypeResolveContext(function.Method); - var orphanedVariableInits = new List(); var targetsToReplace = new List(); var translatedDisplayClasses = new HashSet(); var cancellationToken = context.CancellationToken; @@ -50,48 +49,21 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (instWithVar.Variable.Kind == VariableKind.Local) { instWithVar.Variable.Kind = VariableKind.DisplayClassLocal; } + var displayClassTypeDef = instWithVar.Variable.Type.GetDefinition(); + if (instWithVar.Variable.IsSingleDefinition && instWithVar.Variable.StoreInstructions.SingleOrDefault() is StLoc store) { + if (store.Value is NewObj newObj) { + instWithVar.Variable.CaptureScope = BlockContainer.FindClosestContainer(store); + } + } + if (displayClassTypeDef != null) + translatedDisplayClasses.Add(displayClassTypeDef); targetsToReplace.Add(instWithVar); } context.StepEndGroup(); } - if (inst.MatchStLoc(out ILVariable targetVariable, out ILInstruction value)) { - var newObj = value as NewObj; - // TODO : it is probably not a good idea to remove *all* display-classes - // is there a way to minimize the false-positives? - if (newObj != null && IsInSimpleDisplayClass(newObj.Method)) { - targetVariable.CaptureScope = BlockContainer.FindClosestContainer(inst); - targetsToReplace.Add((IInstructionWithVariableOperand)inst); - translatedDisplayClasses.Add(newObj.Method.DeclaringTypeDefinition); - } - } - } - foreach (var target in targetsToReplace.OrderByDescending(t => ((ILInstruction)t).StartILOffset)) { - context.Step($"TransformDisplayClassUsages {target.Variable}", (ILInstruction)target); - function.AcceptVisitor(new TransformDisplayClassUsages(function, target, target.Variable.CaptureScope, orphanedVariableInits, translatedDisplayClasses)); - } - context.Step($"Remove orphanedVariableInits", function); - foreach (var store in orphanedVariableInits) { - if (store.Parent is Block containingBlock) - containingBlock.Instructions.Remove(store); } } - static bool IsInSimpleDisplayClass(IMethod method) - { - if (!method.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) - return false; - return IsSimpleDisplayClass(method.DeclaringType); - } - - internal static bool IsSimpleDisplayClass(IType type) - { - if (!type.HasGeneratedName() || (!type.Name.Contains("DisplayClass") && !type.Name.Contains("AnonStorey"))) - return false; - if (type.DirectBaseTypes.Any(t => !t.IsKnownType(KnownTypeCode.Object))) - return false; - return true; - } - #region TransformDelegateConstruction internal static bool IsDelegateConstruction(NewObj inst, bool allowTransformed = false) { @@ -101,12 +73,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms return opCode == OpCode.LdFtn || opCode == OpCode.LdVirtFtn || (allowTransformed && opCode == OpCode.ILFunction); } - - internal static bool IsPotentialClosure(ILTransformContext context, NewObj inst) - { - var decompilationContext = new SimpleTypeResolveContext(context.Function.Method); - return IsPotentialClosure(decompilationContext.CurrentTypeDefinition, inst.Method.DeclaringTypeDefinition); - } static bool IsAnonymousMethod(ITypeDefinition decompiledTypeDefinition, IMethod method) { @@ -115,7 +81,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!(method.HasGeneratedName() || method.Name.Contains("$") || method.IsCompilerGeneratedOrIsInCompilerGeneratedClass() - || IsPotentialClosure(decompiledTypeDefinition, method.DeclaringTypeDefinition) + || TransformDisplayClassUsage.IsPotentialClosure(decompiledTypeDefinition, method.DeclaringTypeDefinition) || ContainsAnonymousType(method))) return false; return true; @@ -131,18 +97,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms } return false; } - - static bool IsPotentialClosure(ITypeDefinition decompiledTypeDefinition, ITypeDefinition potentialDisplayClass) - { - if (potentialDisplayClass == null || !potentialDisplayClass.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) - return false; - while (potentialDisplayClass != decompiledTypeDefinition) { - potentialDisplayClass = potentialDisplayClass.DeclaringTypeDefinition; - if (potentialDisplayClass == null) - return false; - } - return true; - } internal static GenericContext? GenericContextFromTypeArguments(TypeParameterSubstitution subst) { @@ -259,139 +213,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms base.VisitLdObj(inst); } } - - /// - /// 1. Stores to display class fields are replaced with stores to local variables (in some - /// cases existing variables are used; otherwise fresh variables are added to the - /// ILFunction-container) and all usages of those fields are replaced with the local variable. - /// (see initValues) - /// 2. Usages of the display class container (or any copy) are removed. - /// - class TransformDisplayClassUsages : ILVisitor - { - ILFunction currentFunction; - BlockContainer captureScope; - readonly IInstructionWithVariableOperand targetLoad; - readonly List targetAndCopies = new List(); - readonly List orphanedVariableInits; - readonly HashSet translatedDisplayClasses; - readonly Dictionary initValues = new Dictionary(); - - struct DisplayClassVariable - { - public ILVariable variable; - public ILInstruction value; - } - - public TransformDisplayClassUsages(ILFunction function, IInstructionWithVariableOperand targetLoad, BlockContainer captureScope, List orphanedVariableInits, HashSet translatedDisplayClasses) - { - this.currentFunction = function; - this.targetLoad = targetLoad; - this.captureScope = captureScope; - this.orphanedVariableInits = orphanedVariableInits; - this.translatedDisplayClasses = translatedDisplayClasses; - this.targetAndCopies.Add(targetLoad.Variable); - } - - protected override void Default(ILInstruction inst) - { - foreach (var child in inst.Children) { - child.AcceptVisitor(this); - } - } - - protected internal override void VisitStLoc(StLoc inst) - { - base.VisitStLoc(inst); - if (targetLoad is ILInstruction instruction && instruction.MatchLdThis()) - return; - if (inst.Variable == targetLoad.Variable) - orphanedVariableInits.Add(inst); - if (MatchesTargetOrCopyLoad(inst.Value)) { - targetAndCopies.Add(inst.Variable); - orphanedVariableInits.Add(inst); - } - } - - bool MatchesTargetOrCopyLoad(ILInstruction inst) - { - return targetAndCopies.Any(v => inst.MatchLdLoc(v)); - } - - protected internal override void VisitStObj(StObj inst) - { - base.VisitStObj(inst); - if (!inst.Target.MatchLdFlda(out ILInstruction target, out IField field) || !MatchesTargetOrCopyLoad(target) || target.MatchLdThis()) - return; - field = (IField)field.MemberDefinition; - ILInstruction value; - if (initValues.TryGetValue(field, out DisplayClassVariable info)) { - inst.ReplaceWith(new StLoc(info.variable, inst.Value).WithILRange(inst)); - } else { - if (inst.Value.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter && currentFunction == v.Function) { - // special case for parameters: remove copies of parameter values. - orphanedVariableInits.Add(inst); - value = inst.Value; - } else { - if (!translatedDisplayClasses.Contains(field.DeclaringTypeDefinition)) - return; - v = currentFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name); - v.CaptureScope = captureScope; - inst.ReplaceWith(new StLoc(v, inst.Value).WithILRange(inst)); - value = new LdLoc(v); - } - initValues.Add(field, new DisplayClassVariable { value = value, variable = v }); - } - } - - protected internal override void VisitLdObj(LdObj inst) - { - base.VisitLdObj(inst); - if (!inst.Target.MatchLdFlda(out ILInstruction target, out IField field)) - return; - if (!initValues.TryGetValue((IField)field.MemberDefinition, out DisplayClassVariable info)) - return; - var replacement = info.value.Clone(); - replacement.SetILRange(inst); - inst.ReplaceWith(replacement); - } - - protected internal override void VisitLdFlda(LdFlda inst) - { - base.VisitLdFlda(inst); - if (inst.Target.MatchLdThis() && inst.Field.Name == "$this" - && inst.Field.MemberDefinition.ReflectionName.Contains("c__Iterator")) { - var variable = currentFunction.Variables.First((f) => f.Index == -1); - inst.ReplaceWith(new LdLoca(variable).WithILRange(inst)); - } - if (inst.Parent is LdObj || inst.Parent is StObj) - return; - if (!MatchesTargetOrCopyLoad(inst.Target)) - return; - var field = (IField)inst.Field.MemberDefinition; - if (!initValues.TryGetValue(field, out DisplayClassVariable info)) { - if (!translatedDisplayClasses.Contains(field.DeclaringTypeDefinition)) - return; - var v = currentFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name); - v.CaptureScope = captureScope; - inst.ReplaceWith(new LdLoca(v).WithILRange(inst)); - var value = new LdLoc(v); - initValues.Add(field, new DisplayClassVariable { value = value, variable = v }); - } else if (info.value is LdLoc l) { - inst.ReplaceWith(new LdLoca(l.Variable).WithILRange(inst)); - } else { - Debug.Fail("LdFlda pattern not supported!"); - } - } - - protected internal override void VisitNumericCompoundAssign(NumericCompoundAssign inst) - { - base.VisitNumericCompoundAssign(inst); - if (inst.Target.MatchLdLoc(out var v)) { - inst.ReplaceWith(new StLoc(v, new BinaryNumericInstruction(inst.Operator, inst.Target, inst.Value, inst.CheckForOverflow, inst.Sign).WithILRange(inst))); - } - } - } #endregion } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs index c982f5f35..14e939cfd 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs @@ -62,9 +62,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms } // Do not try to transform display class usages or delegate construction. // DelegateConstruction transform cannot deal with this. - if (DelegateConstruction.IsSimpleDisplayClass(newObjInst.Method.DeclaringType)) + if (TransformDisplayClassUsage.IsSimpleDisplayClass(newObjInst.Method.DeclaringType)) return false; - if (DelegateConstruction.IsDelegateConstruction(newObjInst) || DelegateConstruction.IsPotentialClosure(context, newObjInst)) + if (DelegateConstruction.IsDelegateConstruction(newObjInst) || TransformDisplayClassUsage.IsPotentialClosure(context, newObjInst)) return false; // Cannot build a collection/object initializer attached to an AnonymousTypeCreateExpression:s // anon = new { A = 5 } { 3,4,5 } is invalid syntax. diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs new file mode 100644 index 000000000..5e26c5a1b --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -0,0 +1,229 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using ICSharpCode.Decompiler.TypeSystem; + +namespace ICSharpCode.Decompiler.IL.Transforms +{ + /// + /// Transforms closure fields to local variables. + /// + /// This is a post-processing step of and . + /// + class TransformDisplayClassUsage : ILVisitor, IILTransform + { + class DisplayClass + { + public ILInstruction Initializer; + public ILVariable Variable; + public ITypeDefinition Definition; + public Dictionary Variables; + public BlockContainer CaptureScope; + } + + struct DisplayClassVariable + { + public ILVariable Variable; + public ILInstruction Value; + } + + ILTransformContext context; + ILFunction currentFunction; + readonly Dictionary displayClasses = new Dictionary(); + readonly List orphanedVariableInits = new List(); + + public void Run(ILFunction function, ILTransformContext context) + { + try { + if (this.context != null || this.currentFunction != null) + throw new InvalidOperationException("Reentrancy in " + nameof(TransformDisplayClassUsage)); + this.context = context; + // Traverse nested functions in post-order: + // Inner functions are transformed before outer functions + foreach (var f in function.Descendants.OfType()) { + foreach (var v in f.Variables) { + if (!(v.IsSingleDefinition && v.StoreInstructions.SingleOrDefault() is StLoc inst)) + continue; + if (IsClosureInit(inst, out var closureType)) { + displayClasses.Add(inst.Variable, new DisplayClass { + Initializer = inst, + Variable = v, + Definition = closureType, + Variables = new Dictionary(), + CaptureScope = inst.Variable.CaptureScope + }); + orphanedVariableInits.Add(inst); + } + } + foreach (var displayClass in displayClasses.Values.OrderByDescending(d => d.Initializer.StartILOffset).ToArray()) { + context.Step($"Transform references to " + displayClass.Variable, displayClass.Initializer); + this.currentFunction = f; + VisitILFunction(f); + } + } + context.Step($"Remove orphanedVariableInits", function); + foreach (var store in orphanedVariableInits) { + if (store.Parent is Block containingBlock) + containingBlock.Instructions.Remove(store); + } + } finally { + orphanedVariableInits.Clear(); + displayClasses.Clear(); + this.context = null; + this.currentFunction = null; + } + } + + internal static bool IsSimpleDisplayClass(IType type) + { + if (!type.HasGeneratedName() || (!type.Name.Contains("DisplayClass") && !type.Name.Contains("AnonStorey"))) + return false; + if (type.DirectBaseTypes.Any(t => !t.IsKnownType(KnownTypeCode.Object))) + return false; + return true; + } + + internal static bool IsPotentialClosure(ILTransformContext context, NewObj inst) + { + var decompilationContext = new SimpleTypeResolveContext(context.Function.Method); + return IsPotentialClosure(decompilationContext.CurrentTypeDefinition, inst.Method.DeclaringTypeDefinition); + } + + internal static bool IsPotentialClosure(ITypeDefinition decompiledTypeDefinition, ITypeDefinition potentialDisplayClass) + { + if (potentialDisplayClass == null || !potentialDisplayClass.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) + return false; + while (potentialDisplayClass != decompiledTypeDefinition) { + potentialDisplayClass = potentialDisplayClass.DeclaringTypeDefinition; + if (potentialDisplayClass == null) + return false; + } + return true; + } + + protected override void Default(ILInstruction inst) + { + foreach (var child in inst.Children) { + child.AcceptVisitor(this); + } + } + + protected internal override void VisitStLoc(StLoc inst) + { + base.VisitStLoc(inst); + // Sometimes display class references are copied into other local variables. + // We remove the assignment and store the relationship between the display class and the variable in the + // displayClasses dictionary. + if (inst.Value.MatchLdLoc(out var closureVariable) && displayClasses.TryGetValue(closureVariable, out var displayClass)) { + displayClasses[inst.Variable] = displayClass; + orphanedVariableInits.Add(inst); + } + } + + bool IsClosureInit(StLoc inst, out ITypeDefinition closureType) + { + closureType = null; + if (!(inst.Value is NewObj newObj)) + return false; + closureType = newObj.Method.DeclaringTypeDefinition; + return closureType != null && IsPotentialClosure(this.context, newObj); + } + + protected internal override void VisitStObj(StObj inst) + { + base.VisitStObj(inst); + // This instruction has been marked deletable, do not transform it further + if (orphanedVariableInits.Contains(inst)) + return; + // The target of the store instruction must be a field reference + if (!inst.Target.MatchLdFlda(out ILInstruction target, out IField field)) + return; + // Skip assignments that reference fields of the outer class, this is not a closure assignment. + if (target.MatchLdThis()) + return; + // Get display class info + if (!(target is LdLoc displayClassLoad && displayClasses.TryGetValue(displayClassLoad.Variable, out var displayClass))) + return; + field = (IField)field.MemberDefinition; + if (displayClass.Variables.TryGetValue(field, out DisplayClassVariable info)) { + // If the display class field was previously initialized, we use a simple assignment. + inst.ReplaceWith(new StLoc(info.Variable, inst.Value).WithILRange(inst)); + } else { + // This is an uninitialized variable: + ILInstruction value; + if (inst.Value.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter && currentFunction == v.Function) { + // Special case for parameters: remove copies of parameter values. + orphanedVariableInits.Add(inst); + value = inst.Value; + } else { + Debug.Assert(displayClass.Definition == field.DeclaringTypeDefinition); + // Introduce a fresh variable for the display class field. + v = currentFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name); + v.CaptureScope = displayClass.CaptureScope; + inst.ReplaceWith(new StLoc(v, inst.Value).WithILRange(inst)); + value = new LdLoc(v); + } + displayClass.Variables.Add(field, new DisplayClassVariable { Value = value, Variable = v }); + } + } + + protected internal override void VisitLdObj(LdObj inst) + { + base.VisitLdObj(inst); + // The target of the store instruction must be a field reference + if (!inst.Target.MatchLdFlda(out var target, out IField field)) + return; + // Get display class info + if (!(target is LdLoc displayClassLoad && displayClasses.TryGetValue(displayClassLoad.Variable, out var displayClass))) + return; + // Get display class variable info + if (!displayClass.Variables.TryGetValue((IField)field.MemberDefinition, out DisplayClassVariable info)) + return; + // Replace usage of display class field with the variable. + var replacement = info.Value.Clone(); + replacement.SetILRange(inst); + inst.ReplaceWith(replacement); + } + + protected internal override void VisitLdFlda(LdFlda inst) + { + base.VisitLdFlda(inst); + // TODO : Figure out why this was added in https://github.com/icsharpcode/ILSpy/pull/1303 + if (inst.Target.MatchLdThis() && inst.Field.Name == "$this" + && inst.Field.MemberDefinition.ReflectionName.Contains("c__Iterator")) { + var variable = currentFunction.Variables.First((f) => f.Index == -1); + inst.ReplaceWith(new LdLoca(variable).WithILRange(inst)); + } + // Skip stfld/ldfld + if (inst.Parent is LdObj || inst.Parent is StObj) + return; + // Get display class info + if (!(inst.Target is LdLoc displayClassLoad && displayClasses.TryGetValue(displayClassLoad.Variable, out var displayClass))) + return; + var field = (IField)inst.Field.MemberDefinition; + if (!displayClass.Variables.TryGetValue(field, out DisplayClassVariable info)) { + // Introduce a fresh variable for the display class field. + Debug.Assert(displayClass.Definition == field.DeclaringTypeDefinition); + var v = currentFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name); + v.CaptureScope = displayClass.CaptureScope; + inst.ReplaceWith(new LdLoca(v).WithILRange(inst)); + displayClass.Variables.Add(field, new DisplayClassVariable { Value = new LdLoc(v), Variable = v }); + } else if (info.Value is LdLoc l) { + inst.ReplaceWith(new LdLoca(l.Variable).WithILRange(inst)); + } else { + Debug.Fail("LdFlda pattern not supported!"); + } + } + + protected internal override void VisitNumericCompoundAssign(NumericCompoundAssign inst) + { + base.VisitNumericCompoundAssign(inst); + // NumericCompoundAssign is only valid when used with fields: -> replace it with a BinaryNumericInstruction. + if (inst.Target.MatchLdLoc(out var v)) { + inst.ReplaceWith(new StLoc(v, new BinaryNumericInstruction(inst.Operator, inst.Target, inst.Value, inst.CheckForOverflow, inst.Sign).WithILRange(inst))); + } + } + + } +} diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs index 89c92bb69..b3ac3e596 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs @@ -21,7 +21,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using ICSharpCode.Decompiler.CSharp.Resolver; -using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; @@ -1071,6 +1070,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms && v.StackType.IsIntegerType()) return new LdLoca(v); return null; + } else if (IsClosureReference(ldloc.Variable)) { + if (ldloc.Variable.Kind == VariableKind.Local) { + ldloc.Variable.Kind = VariableKind.DisplayClassLocal; + } + if (ldloc.Variable.CaptureScope == null) { + ldloc.Variable.CaptureScope = BlockContainer.FindClosestContainer(context); + } + return ldloc; } else { return ldloc; } @@ -1079,6 +1086,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } + bool IsClosureReference(ILVariable variable) + { + if (!variable.IsSingleDefinition || !(variable.StoreInstructions.SingleOrDefault() is StLoc store)) + return false; + if (!(store.Value is NewObj newObj)) + return false; + return TransformDisplayClassUsage.IsPotentialClosure(this.context, newObj); + } + bool IsExpressionTreeParameter(ILVariable variable) { return variable.Type.FullName == "System.Linq.Expressions.ParameterExpression"; From 94708105353a09bc3c2789133451d196673fb81c Mon Sep 17 00:00:00 2001 From: Hans De Mulder Date: Sun, 30 Jun 2019 15:39:43 +0200 Subject: [PATCH 016/221] add .NET Core SDK 3 requirement Add the .NET Core SDK 3 requirement, otherwise the projects fail to load. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8901e4bea..c90645860 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ Windows: - Individual Component "VC++ 2017 version 15.9 v14.16 latest v141 tools" (or similar) - The VC++ toolset is optional; if present it is used for `editbin.exe` to modify the stack size used by ILSpy.exe from 1MB to 16MB, because the decompiler makes heavy use of recursion, where small stack sizes lead to problems in very complex methods. - Install the [.NET Core SDK 2.2](https://dotnet.microsoft.com/download) +- Install the [.NET Core SDK 3](https://dotnet.microsoft.com/download/dotnet-core) - Check out the ILSpy repository using git. - Execute `git submodule update --init --recursive` to download the ILSpy-Tests submodule (used by some test cases). - Open ILSpy.sln in Visual Studio. @@ -63,6 +64,7 @@ Windows: Unix: - Make sure .NET Core 2.2 is installed (you can get it here: https://get.dot.net). +- Make sure [.NET Core SDK 3](https://dotnet.microsoft.com/download/dotnet-core) is installed. - Check out the repository using git. - Execute `git submodule update --init --recursive` to download the ILSpy-Tests submodule (used by some test cases). - Use `dotnet build Frontends.sln` to build the non-Windows flavors of ILSpy (cli and powershell core). From afc0f0ee0061caa7ed60bee78eb6431107dd80e5 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Tue, 2 Jul 2019 09:18:50 +0200 Subject: [PATCH 017/221] Switch from very old VS2019 preview image to late-May official VS2019 image --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 08de22677..6e8bdb2cf 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ platform: Any CPU configuration: - Debug - Release -image: Visual Studio 2019 Preview +image: Visual Studio 2019 install: - git submodule update --init --recursive - ps: .\BuildTools\appveyor-install.ps1 From 0993880f4306f21f88d2aa71f19246cd2eda2d4a Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Tue, 2 Jul 2019 09:32:17 +0200 Subject: [PATCH 018/221] Install current netcore3 sdk --- appveyor.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 6e8bdb2cf..13806b0ce 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,28 +1,39 @@ platform: Any CPU + configuration: - Debug - Release + image: Visual Studio 2019 + install: +- cmd: choco install dotnetcore-sdk --pre - git submodule update --init --recursive - ps: .\BuildTools\appveyor-install.ps1 + nuget: account_feed: false project_feed: true disable_publish_on_pr: true + before_build: - nuget restore ILSpy.sln + build_script: - msbuild ILSpy.sln /v:minimal /p:ResolveNuGetPackages=false "/logger:%ProgramFiles%\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" + after_build: - 7z a ILSpy_binaries.zip %APPVEYOR_BUILD_FOLDER%\ILSpy\bin\%configuration%\net462\*.dll %APPVEYOR_BUILD_FOLDER%\ILSpy\bin\%configuration%\net462\*.exe %APPVEYOR_BUILD_FOLDER%\ILSpy\bin\%configuration%\net462\*.config %APPVEYOR_BUILD_FOLDER%\ILSpy\bin\%configuration%\net462\*\ILSpy.resources.dll + test: assemblies: - 'ICSharpCode.Decompiler.Tests\bin\%configuration%\net462\ICSharpCode.Decompiler.Tests.exe' - 'ILSpy.Tests\bin\%configuration%\net462\ILSpy.Tests.exe' - 'ILSpy.BamlDecompiler.Tests\bin\%configuration%\net462\ILSpy.BamlDecompiler.Tests.dll' + after_test: - python BuildTools\tidy.py + for: - branches: except: From 475c5e830e0810022bec1e8b9404e4e212669b67 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Tue, 2 Jul 2019 15:09:29 +0200 Subject: [PATCH 019/221] Azure Pipelines badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c90645860..de4611456 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ILSpy [![Join the chat at https://gitter.im/icsharpcode/ILSpy](https://badges.gitter.im/icsharpcode/ILSpy.svg)](https://gitter.im/icsharpcode/ILSpy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![NuGet](https://img.shields.io/nuget/v/ICSharpCode.Decompiler.svg)](https://nuget.org/packages/ICSharpCode.Decompiler) [![Build status](https://ci.appveyor.com/api/projects/status/imgec05g0wwv25ij/branch/master?svg=true)](https://ci.appveyor.com/project/icsharpcode/ilspy/branch/master) [![Twitter Follow](https://img.shields.io/twitter/follow/ILSpy.svg?label=Follow%20@ILSpy)](https://twitter.com/ilspy) [![ilspy.net](https://img.shields.io/badge/@-ilspy.net-blue.svg)](http://www.ilspy.net) [![ILSpy VS extension](https://img.shields.io/badge/VS%20Extension-ILSpy-blue.svg)](https://visualstudiogallery.msdn.microsoft.com/8ef1d688-f80c-4380-8004-2ec7f814e7de) +# ILSpy [![Join the chat at https://gitter.im/icsharpcode/ILSpy](https://badges.gitter.im/icsharpcode/ILSpy.svg)](https://gitter.im/icsharpcode/ILSpy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![NuGet](https://img.shields.io/nuget/v/ICSharpCode.Decompiler.svg)](https://nuget.org/packages/ICSharpCode.Decompiler) [![Build status](https://ci.appveyor.com/api/projects/status/imgec05g0wwv25ij/branch/master?svg=true)](https://ci.appveyor.com/project/icsharpcode/ilspy/branch/master) [![Twitter Follow](https://img.shields.io/twitter/follow/ILSpy.svg?label=Follow%20@ILSpy)](https://twitter.com/ilspy) [![ILSpy VS extension](https://img.shields.io/badge/VS%20Extension-ILSpy-blue.svg)](https://visualstudiogallery.msdn.microsoft.com/8ef1d688-f80c-4380-8004-2ec7f814e7de) [![Build Status](https://icsharpcode.visualstudio.com/icsharpcode-pipelines/_apis/build/status/icsharpcode.ILSpy?branchName=master)](https://icsharpcode.visualstudio.com/icsharpcode-pipelines/_build/latest?definitionId=1&branchName=master) ILSpy is the open-source .NET assembly browser and decompiler. From 9590cfbf5950b62f67a5c221e9aeae99072b7c25 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 6 Jul 2019 22:29:30 +0200 Subject: [PATCH 020/221] Update tests to latest Roslyn implementation. --- .../ICSharpCode.Decompiler.Tests.csproj | 8 ++-- .../IL/ILInstructionExtensions.cs | 9 ++++ .../IL/Instructions/Block.cs | 46 ++++++++++++------- .../IL/Transforms/ExpressionTransforms.cs | 5 +- .../ILSpy.BamlDecompiler.Tests.csproj | 2 +- ILSpy.BamlDecompiler.Tests/app.config | 8 +++- 6 files changed, 55 insertions(+), 23 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index e3e43be69..51d24fc07 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -42,13 +42,13 @@ - - + + - - + + diff --git a/ICSharpCode.Decompiler/IL/ILInstructionExtensions.cs b/ICSharpCode.Decompiler/IL/ILInstructionExtensions.cs index 222d82daa..32310150c 100644 --- a/ICSharpCode.Decompiler/IL/ILInstructionExtensions.cs +++ b/ICSharpCode.Decompiler/IL/ILInstructionExtensions.cs @@ -18,5 +18,14 @@ namespace ICSharpCode.Decompiler.IL target.AddILRange(range); return target; } + + public static ILInstruction GetNextSibling(this ILInstruction instruction) + { + if (instruction?.Parent == null) + return null; + if (instruction.ChildIndex + 1 >= instruction.Parent.Children.Count) + return null; + return instruction.Parent.Children[instruction.ChildIndex + 1]; + } } } diff --git a/ICSharpCode.Decompiler/IL/Instructions/Block.cs b/ICSharpCode.Decompiler/IL/Instructions/Block.cs index ab04f550e..dcaa0b580 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/Block.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/Block.cs @@ -43,11 +43,11 @@ namespace ICSharpCode.Decompiler.IL { public static readonly SlotInfo InstructionSlot = new SlotInfo("Instruction", isCollection: true); public static readonly SlotInfo FinalInstructionSlot = new SlotInfo("FinalInstruction"); - + public readonly BlockKind Kind; public readonly InstructionCollection Instructions; ILInstruction finalInstruction; - + /// /// For blocks in a block container, this field holds /// the number of incoming control flow edges to this block. @@ -77,21 +77,21 @@ namespace ICSharpCode.Decompiler.IL SetChildInstruction(ref finalInstruction, value, Instructions.Count); } } - + protected internal override void InstructionCollectionUpdateComplete() { base.InstructionCollectionUpdateComplete(); if (finalInstruction.Parent == this) finalInstruction.ChildIndex = Instructions.Count; } - + public Block(BlockKind kind = BlockKind.ControlFlow) : base(OpCode.Block) { this.Kind = kind; this.Instructions = new InstructionCollection(this, 0); this.FinalInstruction = new Nop(); } - + public override ILInstruction Clone() { Block clone = new Block(Kind); @@ -100,7 +100,7 @@ namespace ICSharpCode.Decompiler.IL clone.FinalInstruction = this.FinalInstruction.Clone(); return clone; } - + internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); @@ -133,18 +133,17 @@ namespace ICSharpCode.Decompiler.IL break; } } - + public override StackType ResultType { get { return finalInstruction.ResultType; } } - + /// /// Gets the name of this block. /// - public string Label - { + public string Label { get { return Disassembler.DisassemblerHelpers.OffsetToString(this.StartILOffset); } } @@ -179,19 +178,19 @@ namespace ICSharpCode.Decompiler.IL output.Write("}"); output.MarkFoldEnd(); } - + protected override int GetChildCount() { return Instructions.Count + 1; } - + protected override ILInstruction GetChild(int index) { if (index == Instructions.Count) return finalInstruction; return Instructions[index]; } - + protected override void SetChild(int index, ILInstruction value) { if (index == Instructions.Count) @@ -199,7 +198,7 @@ namespace ICSharpCode.Decompiler.IL else Instructions[index] = value; } - + protected override SlotInfo GetChildSlot(int index) { if (index == Instructions.Count) @@ -207,7 +206,7 @@ namespace ICSharpCode.Decompiler.IL else return InstructionSlot; } - + protected override InstructionFlags ComputeFlags() { var flags = InstructionFlags.None; @@ -217,7 +216,7 @@ namespace ICSharpCode.Decompiler.IL flags |= FinalInstruction.Flags; return flags; } - + public override InstructionFlags DirectFlags { get { return InstructionFlags.None; @@ -280,6 +279,21 @@ namespace ICSharpCode.Decompiler.IL return inst; } + /// + /// Gets the closest parent Block. + /// Returns null, if the instruction is not a descendant of a Block. + /// + public static Block FindClosestBlock(ILInstruction inst) + { + var curr = inst; + while (curr != null) { + if (curr is Block) + return (Block)curr; + curr = curr.Parent; + } + return null; + } + public bool MatchInlineAssignBlock(out CallInstruction call, out ILInstruction value) { call = null; diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 05f7d2f5f..0a19dd86a 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -271,6 +271,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } Block block; if (TransformSpanTCtorContainingStackAlloc(inst, out ILInstruction locallocSpan)) { + context.Step("new Span(stackalloc) -> stackalloc Span", inst); inst.ReplaceWith(locallocSpan); block = null; ILInstruction stmt = locallocSpan; @@ -281,7 +282,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms } stmt = stmt.Parent; } - //ILInlining.InlineIfPossible(block, stmt.ChildIndex - 1, context); + // Special case to eliminate extra store + if (stmt.GetNextSibling() is StLoc) + ILInlining.InlineIfPossible(block, stmt.ChildIndex, context); return; } if (TransformArrayInitializers.TransformSpanTArrayInitialization(inst, context, out block)) { diff --git a/ILSpy.BamlDecompiler.Tests/ILSpy.BamlDecompiler.Tests.csproj b/ILSpy.BamlDecompiler.Tests/ILSpy.BamlDecompiler.Tests.csproj index 0f4829272..1958ed447 100644 --- a/ILSpy.BamlDecompiler.Tests/ILSpy.BamlDecompiler.Tests.csproj +++ b/ILSpy.BamlDecompiler.Tests/ILSpy.BamlDecompiler.Tests.csproj @@ -31,7 +31,7 @@ - + diff --git a/ILSpy.BamlDecompiler.Tests/app.config b/ILSpy.BamlDecompiler.Tests/app.config index 5095b0e47..8eaf7eb82 100644 --- a/ILSpy.BamlDecompiler.Tests/app.config +++ b/ILSpy.BamlDecompiler.Tests/app.config @@ -7,8 +7,14 @@ + + + + + + - + From fe80e1b767b8cb466afdfdcafbb38dd5a6447a1d Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 6 Jul 2019 22:56:50 +0200 Subject: [PATCH 021/221] Fix 1553: Analyzer does not find generic type references --- ILSpy/Analyzers/AnalyzerScope.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ILSpy/Analyzers/AnalyzerScope.cs b/ILSpy/Analyzers/AnalyzerScope.cs index 976491573..6a6e27344 100644 --- a/ILSpy/Analyzers/AnalyzerScope.cs +++ b/ILSpy/Analyzers/AnalyzerScope.cs @@ -130,6 +130,10 @@ namespace ICSharpCode.ILSpy.Analyzers { yield return self; + string reflectionTypeScopeName = typeScope.Name; + if (typeScope.TypeParameterCount > 0) + reflectionTypeScopeName += "`" + typeScope.TypeParameterCount; + foreach (var assembly in AssemblyList.GetAssemblies()) { ct.ThrowIfCancellationRequested(); bool found = false; @@ -145,7 +149,7 @@ namespace ICSharpCode.ILSpy.Analyzers } } } - if (found && ModuleReferencesScopeType(module.Metadata, typeScope.Name, typeScope.Namespace)) + if (found && ModuleReferencesScopeType(module.Metadata, reflectionTypeScopeName, typeScope.Namespace)) yield return module; } } From 6a1229028cce31e2e93715d34c878594416e504e Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 7 Jul 2019 07:48:58 +0200 Subject: [PATCH 022/221] Only try to inline stackalloc Span if the next statement is a plain assignment. --- ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 0a19dd86a..623d69ae8 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -283,7 +283,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms stmt = stmt.Parent; } // Special case to eliminate extra store - if (stmt.GetNextSibling() is StLoc) + if (stmt.GetNextSibling() is StLoc storeStmt && storeStmt.Value is LdLoc) ILInlining.InlineIfPossible(block, stmt.ChildIndex, context); return; } From 17a8a194add633f77b216aca79f53ab3f8cc44b1 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Sun, 7 Jul 2019 08:13:41 +0200 Subject: [PATCH 023/221] Include OSVersionHelper.dll in VSIX (issue #1516, introduced via https://github.com/icsharpcode/ILSpy/pull/1522/files#diff-969eddb6d4efef25c08c59535991b8cdR55) --- ILSpy.AddIn/ILSpy.AddIn.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/ILSpy.AddIn/ILSpy.AddIn.csproj b/ILSpy.AddIn/ILSpy.AddIn.csproj index a43410ba5..e61b4e2a5 100644 --- a/ILSpy.AddIn/ILSpy.AddIn.csproj +++ b/ILSpy.AddIn/ILSpy.AddIn.csproj @@ -131,6 +131,7 @@ + From 7f8856b10fb5d29a3d6450ee2cb014824f4d0d9e Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 7 Jul 2019 10:57:04 +0200 Subject: [PATCH 024/221] Fix #1026: Improve support for mcs in TransformDisplayClassUsage --- .../PrettyTestRunner.cs | 2 +- ICSharpCode.Decompiler/IL/ILVariable.cs | 5 + .../Transforms/TransformDisplayClassUsage.cs | 100 +++++++++++++++--- 3 files changed, 92 insertions(+), 15 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index 1e15135f1..a8b4b980b 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -134,7 +134,7 @@ namespace ICSharpCode.Decompiler.Tests } [Test] - public void DelegateConstruction([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) + public void DelegateConstruction([ValueSource(nameof(defaultOptionsWithMcs))] CompilerOptions cscOptions) { RunForLibrary(cscOptions: cscOptions); } diff --git a/ICSharpCode.Decompiler/IL/ILVariable.cs b/ICSharpCode.Decompiler/IL/ILVariable.cs index a81f355e0..8a0e5fd17 100644 --- a/ICSharpCode.Decompiler/IL/ILVariable.cs +++ b/ICSharpCode.Decompiler/IL/ILVariable.cs @@ -74,6 +74,11 @@ namespace ICSharpCode.Decompiler.IL static class VariableKindExtensions { + public static bool IsThis(this ILVariable v) + { + return v.Kind == VariableKind.Parameter && v.Index < 0; + } + public static bool IsLocal(this VariableKind kind) { switch (kind) { diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index 5e26c5a1b..23b8d4467 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -15,6 +15,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms { class DisplayClass { + public bool IsMono; public ILInstruction Initializer; public ILVariable Variable; public ITypeDefinition Definition; @@ -31,7 +32,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms ILTransformContext context; ILFunction currentFunction; readonly Dictionary displayClasses = new Dictionary(); - readonly List orphanedVariableInits = new List(); + readonly List instructionsToRemove = new List(); public void Run(ILFunction function, ILTransformContext context) { @@ -39,21 +40,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (this.context != null || this.currentFunction != null) throw new InvalidOperationException("Reentrancy in " + nameof(TransformDisplayClassUsage)); this.context = context; + var decompilationContext = new SimpleTypeResolveContext(context.Function.Method); // Traverse nested functions in post-order: // Inner functions are transformed before outer functions foreach (var f in function.Descendants.OfType()) { - foreach (var v in f.Variables) { + foreach (var v in f.Variables.ToArray()) { + if (HandleMonoStateMachine(function, v, decompilationContext, f)) + continue; if (!(v.IsSingleDefinition && v.StoreInstructions.SingleOrDefault() is StLoc inst)) continue; - if (IsClosureInit(inst, out var closureType)) { + if (IsClosureInit(inst, out ITypeDefinition closureType)) { + // TODO : figure out whether it is a mono compiled closure, without relying on the type name + bool isMono = f.StateMachineCompiledWithMono || closureType.Name.Contains("AnonStorey"); displayClasses.Add(inst.Variable, new DisplayClass { + IsMono = isMono, Initializer = inst, Variable = v, Definition = closureType, Variables = new Dictionary(), - CaptureScope = inst.Variable.CaptureScope + CaptureScope = isMono && IsMonoNestedCaptureScope(closureType) ? null : inst.Variable.CaptureScope }); - orphanedVariableInits.Add(inst); + instructionsToRemove.Add(inst); } } foreach (var displayClass in displayClasses.Values.OrderByDescending(d => d.Initializer.StartILOffset).ToArray()) { @@ -62,19 +69,83 @@ namespace ICSharpCode.Decompiler.IL.Transforms VisitILFunction(f); } } - context.Step($"Remove orphanedVariableInits", function); - foreach (var store in orphanedVariableInits) { + context.Step($"Remove instructions", function); + foreach (var store in instructionsToRemove) { if (store.Parent is Block containingBlock) containingBlock.Instructions.Remove(store); } } finally { - orphanedVariableInits.Clear(); + instructionsToRemove.Clear(); displayClasses.Clear(); this.context = null; this.currentFunction = null; } } + bool IsOuterClosureReference(IField field) + { + return displayClasses.Values.Any(disp => disp.Definition == field.DeclaringTypeDefinition); + } + + bool IsMonoNestedCaptureScope(ITypeDefinition closureType) + { + var decompilationContext = new SimpleTypeResolveContext(context.Function.Method); + return closureType.Fields.Any(f => IsPotentialClosure(decompilationContext.CurrentTypeDefinition, f.ReturnType.GetDefinition())); + } + + /// + /// mcs likes to optimize closures in yield state machines away by moving the captured variables' fields into the state machine type, + /// We construct a that spans the whole method body. + /// + bool HandleMonoStateMachine(ILFunction currentFunction, ILVariable thisVariable, SimpleTypeResolveContext decompilationContext, ILFunction nestedFunction) + { + if (!(nestedFunction.StateMachineCompiledWithMono && thisVariable.IsThis())) + return false; + // Special case for Mono-compiled yield state machines + ITypeDefinition closureType = thisVariable.Type.GetDefinition(); + if (!(closureType != decompilationContext.CurrentTypeDefinition + && IsPotentialClosure(decompilationContext.CurrentTypeDefinition, closureType))) + return false; + + var displayClass = new DisplayClass { + IsMono = true, + Initializer = nestedFunction.Body, + Variable = thisVariable, + Definition = thisVariable.Type.GetDefinition(), + Variables = new Dictionary(), + CaptureScope = (BlockContainer)nestedFunction.Body + }; + displayClasses.Add(thisVariable, displayClass); + foreach (var stateMachineVariable in nestedFunction.Variables) { + if (stateMachineVariable.StateMachineField == null) + continue; + displayClass.Variables.Add(stateMachineVariable.StateMachineField, new DisplayClassVariable { + Variable = stateMachineVariable, + Value = new LdLoc(stateMachineVariable) + }); + } + if (!currentFunction.Method.IsStatic && FindThisField(out var thisField)) { + var thisVar = currentFunction.Variables + .FirstOrDefault(t => t.IsThis() && t.Type.GetDefinition() == decompilationContext.CurrentTypeDefinition); + if (thisVar == null) { + thisVar = new ILVariable(VariableKind.Parameter, decompilationContext.CurrentTypeDefinition, -1) { Name = "this" }; + currentFunction.Variables.Add(thisVar); + } + displayClass.Variables.Add(thisField, new DisplayClassVariable { Variable = thisVar, Value = new LdLoc(thisVar) }); + } + return true; + + bool FindThisField(out IField foundField) + { + foundField = null; + foreach (var field in closureType.GetFields(f2 => !f2.IsStatic && !displayClass.Variables.ContainsKey(f2) && f2.Type.GetDefinition() == decompilationContext.CurrentTypeDefinition)) { + thisField = field; + return true; + } + return false; + } + } + internal static bool IsSimpleDisplayClass(IType type) { if (!type.HasGeneratedName() || (!type.Name.Contains("DisplayClass") && !type.Name.Contains("AnonStorey"))) @@ -117,7 +188,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // displayClasses dictionary. if (inst.Value.MatchLdLoc(out var closureVariable) && displayClasses.TryGetValue(closureVariable, out var displayClass)) { displayClasses[inst.Variable] = displayClass; - orphanedVariableInits.Add(inst); + instructionsToRemove.Add(inst); } } @@ -134,14 +205,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms { base.VisitStObj(inst); // This instruction has been marked deletable, do not transform it further - if (orphanedVariableInits.Contains(inst)) + if (instructionsToRemove.Contains(inst)) return; // The target of the store instruction must be a field reference if (!inst.Target.MatchLdFlda(out ILInstruction target, out IField field)) return; - // Skip assignments that reference fields of the outer class, this is not a closure assignment. - if (target.MatchLdThis()) - return; // Get display class info if (!(target is LdLoc displayClassLoad && displayClasses.TryGetValue(displayClassLoad.Variable, out var displayClass))) return; @@ -154,12 +222,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms ILInstruction value; if (inst.Value.MatchLdLoc(out var v) && v.Kind == VariableKind.Parameter && currentFunction == v.Function) { // Special case for parameters: remove copies of parameter values. - orphanedVariableInits.Add(inst); + instructionsToRemove.Add(inst); value = inst.Value; } else { Debug.Assert(displayClass.Definition == field.DeclaringTypeDefinition); // Introduce a fresh variable for the display class field. v = currentFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name); + if (displayClass.IsMono && displayClass.CaptureScope == null && !IsOuterClosureReference(field)) { + displayClass.CaptureScope = BlockContainer.FindClosestContainer(inst); + } v.CaptureScope = displayClass.CaptureScope; inst.ReplaceWith(new StLoc(v, inst.Value).WithILRange(inst)); value = new LdLoc(v); @@ -192,6 +263,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // TODO : Figure out why this was added in https://github.com/icsharpcode/ILSpy/pull/1303 if (inst.Target.MatchLdThis() && inst.Field.Name == "$this" && inst.Field.MemberDefinition.ReflectionName.Contains("c__Iterator")) { + //Debug.Assert(false, "This should not be executed!"); var variable = currentFunction.Variables.First((f) => f.Index == -1); inst.ReplaceWith(new LdLoca(variable).WithILRange(inst)); } From c1ac461c267d1b7da38c1ad1b52d707b055d063b Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 7 Jul 2019 11:51:51 +0200 Subject: [PATCH 025/221] Enable MCS yield return pretty tests. --- ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs | 2 +- .../IL/Transforms/RemoveDeadVariableInit.cs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index a8b4b980b..6c66f55c3 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -420,7 +420,7 @@ namespace ICSharpCode.Decompiler.Tests } [Test] - public void YieldReturn([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) + public void YieldReturn([ValueSource(nameof(defaultOptionsWithMcs))] CompilerOptions cscOptions) { RunForLibrary(cscOptions: cscOptions); } diff --git a/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs b/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs index ff8f08ac4..a6dab356b 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs @@ -53,6 +53,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms var v = variableQueue.Dequeue(); if (v.Kind != VariableKind.Local && v.Kind != VariableKind.StackSlot) continue; + // Skip variables that are captured in a mcs yield state-machine + // loads of these will only be visible after DelegateConstruction step. + if (function.StateMachineCompiledWithMono && v.StateMachineField != null) + continue; if (v.LoadCount != 0 || v.AddressCount != 0) continue; foreach (var stloc in v.StoreInstructions.OfType().ToArray()) { From b4a59ae4ddd3c66ba69768885031bb7b564942ef Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 7 Jul 2019 12:18:06 +0200 Subject: [PATCH 026/221] Remove obsolete code from DelegateConstruction.cs --- .../IL/Transforms/DelegateConstruction.cs | 9 --------- .../Transforms/TransformDisplayClassUsage.cs | 20 ++++++++++++++++++- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs index 021f34767..ce19a3e84 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs @@ -16,9 +16,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Reflection.Metadata; using ICSharpCode.Decompiler.CSharp; @@ -37,8 +35,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms return; this.context = context; this.decompilationContext = new SimpleTypeResolveContext(function.Method); - var targetsToReplace = new List(); - var translatedDisplayClasses = new HashSet(); var cancellationToken = context.CancellationToken; foreach (var inst in function.Descendants) { cancellationToken.ThrowIfCancellationRequested(); @@ -55,16 +51,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms instWithVar.Variable.CaptureScope = BlockContainer.FindClosestContainer(store); } } - if (displayClassTypeDef != null) - translatedDisplayClasses.Add(displayClassTypeDef); - targetsToReplace.Add(instWithVar); } context.StepEndGroup(); } } } - #region TransformDelegateConstruction internal static bool IsDelegateConstruction(NewObj inst, bool allowTransformed = false) { if (inst == null || inst.Arguments.Count != 2 || inst.Method.DeclaringType.Kind != TypeKind.Delegate) @@ -213,6 +205,5 @@ namespace ICSharpCode.Decompiler.IL.Transforms base.VisitLdObj(inst); } } - #endregion } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index 23b8d4467..887f345d7 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -1,4 +1,22 @@ -using System; +// Copyright (c) 2019 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; From 6d05f36821302071fb086daa098dba3e0f9f428d Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 7 Jul 2019 17:34:14 +0200 Subject: [PATCH 027/221] Fix #1562: Do not transform display class initialization to object initializer. --- .../IL/Transforms/TransformDisplayClassUsage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index 887f345d7..317b4da04 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -175,7 +175,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms internal static bool IsPotentialClosure(ILTransformContext context, NewObj inst) { - var decompilationContext = new SimpleTypeResolveContext(context.Function.Method); + var decompilationContext = new SimpleTypeResolveContext(context.Function.Ancestors.OfType().Last().Method); return IsPotentialClosure(decompilationContext.CurrentTypeDefinition, inst.Method.DeclaringTypeDefinition); } From 1ecf0f1357402b6553728fd079c93f9eb7f61741 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 8 Jul 2019 07:54:21 +0200 Subject: [PATCH 028/221] Prepare preview3 release. --- ILSpy/Properties/AssemblyInfo.template.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ILSpy/Properties/AssemblyInfo.template.cs b/ILSpy/Properties/AssemblyInfo.template.cs index 4f475f577..56f5818b6 100644 --- a/ILSpy/Properties/AssemblyInfo.template.cs +++ b/ILSpy/Properties/AssemblyInfo.template.cs @@ -39,7 +39,7 @@ internal static class RevisionClass public const string Minor = "0"; public const string Build = "0"; public const string Revision = "$INSERTREVISION$"; - public const string VersionName = "preview2"; + public const string VersionName = "preview3"; public const string FullVersion = Major + "." + Minor + "." + Build + ".$INSERTREVISION$$INSERTBRANCHPOSTFIX$$INSERTVERSIONNAMEPOSTFIX$"; } From 707551afc1af068b04d80345ad22ac840e7c43d2 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Mon, 8 Jul 2019 14:00:30 +0200 Subject: [PATCH 029/221] Update frontends and workbook for preview3 --- DecompilerNuGetDemos.workbook | 2 +- .../ICSharpCode.Decompiler.Console.csproj | 4 ++-- .../ICSharpCode.Decompiler.PowerShell.csproj | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DecompilerNuGetDemos.workbook b/DecompilerNuGetDemos.workbook index b6349e200..b07fe4f3f 100644 --- a/DecompilerNuGetDemos.workbook +++ b/DecompilerNuGetDemos.workbook @@ -6,7 +6,7 @@ platforms: - DotNetCore packages: - id: ICSharpCode.Decompiler - version: 5.0.0.4793-preview2 + version: 5.0.0.4861-preview3 --- Setup: load the references required to work with the decompiler diff --git a/ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj b/ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj index 1fd74a245..09f8a362e 100644 --- a/ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj +++ b/ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj @@ -7,7 +7,7 @@ true ilspycmd ilspycmd - 5.0.0-preview2 + 5.0.0-preview3 Command-line decompiler using the ILSpy decompilation engine Copyright 2011-2019 AlphaSierraPapa https://github.com/icsharpcode/ILSpy/ @@ -28,7 +28,7 @@ - + diff --git a/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj b/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj index 4dd7f895e..c60026221 100644 --- a/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj +++ b/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj @@ -8,7 +8,7 @@ - + From 0cafc679f2141b469bb4e65d3007facd640e2c69 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 12 Jul 2019 16:56:23 +0200 Subject: [PATCH 030/221] Do not fold using declarations, if there's only one. --- .../Output/TextTokenWriter.cs | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/ICSharpCode.Decompiler/Output/TextTokenWriter.cs b/ICSharpCode.Decompiler/Output/TextTokenWriter.cs index 268b09ba3..f19c8cfda 100644 --- a/ICSharpCode.Decompiler/Output/TextTokenWriter.cs +++ b/ICSharpCode.Decompiler/Output/TextTokenWriter.cs @@ -90,7 +90,7 @@ namespace ICSharpCode.Decompiler return; } - if (firstUsingDeclaration) { + if (firstUsingDeclaration && !lastUsingDeclaration) { output.MarkFoldStart(defaultCollapsed: !settings.ExpandUsingDeclarations); firstUsingDeclaration = false; } @@ -264,11 +264,10 @@ namespace ICSharpCode.Decompiler public override void NewLine() { - if (lastUsingDeclaration) { + if (!firstUsingDeclaration && lastUsingDeclaration) { output.MarkFoldEnd(); lastUsingDeclaration = false; } -// lastEndOfLine = output.Location; output.WriteLine(); } @@ -366,9 +365,6 @@ namespace ICSharpCode.Decompiler } } -// Stack startLocations = new Stack(); -// Stack symbolsStack = new Stack(); - public override void StartNode(AstNode node) { if (nodeStack.Count == 0) { @@ -381,15 +377,6 @@ namespace ICSharpCode.Decompiler } } nodeStack.Push(node); -// startLocations.Push(output.Location); - -// if (node is EntityDeclaration && node.GetSymbol() != null && node.GetChildByRole(Roles.Identifier).IsNull) -// output.WriteDefinition("", node.GetSymbol(), false); - -// if (node.Annotation() != null) { -// symbolsStack.Push(node.Annotation()); -// symbolsStack.Peek().StartLocation = startLocations.Peek(); -// } } private bool IsUsingDeclaration(AstNode node) From 03e45fc80a0db48039a4811d58097540a788b4af Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 12 Jul 2019 17:22:37 +0200 Subject: [PATCH 031/221] Fix #1568: Mimic behavior of ILReader.CreateILVariable in case of empty parameter names of anonymous methods. --- ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 8197c4e35..1cfb75ecd 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -1829,6 +1829,10 @@ namespace ICSharpCode.Decompiler.CSharp int i = 0; foreach (var parameter in parameters) { var pd = astBuilder.ConvertParameter(parameter); + if (string.IsNullOrEmpty(pd.Name) && !pd.Type.IsArgList()) { + // needs to be consistent with logic in ILReader.CreateILVarable(ParameterDefinition) + pd.Name = "P_" + i; + } if (settings.AnonymousTypes && parameter.Type.ContainsAnonymousType()) pd.Type = null; ILVariable v; From e99bc2b14598edea74bc18b6ed5ab84c4372cc8d Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 12 Jul 2019 17:40:55 +0200 Subject: [PATCH 032/221] Fix #1567: Do not transform string.Concat method calls to + operators if named arguments are used. --- .../TestCases/Pretty/OptionalArguments.cs | 10 ++++++++++ .../Transforms/ReplaceMethodCallsWithOperators.cs | 12 ++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs index 80631f96a..cf97f492f 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs @@ -97,6 +97,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty }; } + private static string GetStr(int unused) + { + return " "; + } + + public static string Issue1567(string str1, string str2) + { + return string.Concat(str1.Replace('"', '\''), str2: str2.Replace('"', '\''), str1: GetStr(42)); + } + private void CallerMemberName([CallerMemberName] string memberName = null) { diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs index 62b97c24f..7aea2a4a8 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs @@ -154,8 +154,16 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (arguments.Length < 2) return false; - return arguments[0].GetResolveResult().Type.IsKnownType(KnownTypeCode.String) || - arguments[1].GetResolveResult().Type.IsKnownType(KnownTypeCode.String); + bool valid = false; + + foreach (var argument in arguments) { + if (argument is NamedArgumentExpression) + return false; + if (argument.GetResolveResult().Type.IsKnownType(KnownTypeCode.String)) + valid = true; + } + + return valid; } static BinaryOperatorType? GetBinaryOperatorTypeFromMetadataName(string name) From 01d1e2ef57f908c9800323fe4bcceed1d4260871 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 12 Jul 2019 18:00:02 +0200 Subject: [PATCH 033/221] Fix #1569: Look for first non-static field in enum definitions to determine enum underlying type. --- ICSharpCode.Decompiler/SRMExtensions.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/ICSharpCode.Decompiler/SRMExtensions.cs b/ICSharpCode.Decompiler/SRMExtensions.cs index a59d37c7b..fc8fb2eb4 100644 --- a/ICSharpCode.Decompiler/SRMExtensions.cs +++ b/ICSharpCode.Decompiler/SRMExtensions.cs @@ -69,12 +69,17 @@ namespace ICSharpCode.Decompiler return false; if (!baseType.IsKnownType(reader, KnownTypeCode.Enum)) return false; - var field = reader.GetFieldDefinition(typeDefinition.GetFields().First()); - var blob = reader.GetBlobReader(field.Signature); - if (blob.ReadSignatureHeader().Kind != SignatureKind.Field) - return false; - underlyingType = (PrimitiveTypeCode)blob.ReadByte(); - return true; + foreach (var handle in typeDefinition.GetFields()) { + var field = reader.GetFieldDefinition(handle); + if ((field.Attributes & FieldAttributes.Static) != 0) + continue; + var blob = reader.GetBlobReader(field.Signature); + if (blob.ReadSignatureHeader().Kind != SignatureKind.Field) + return false; + underlyingType = (PrimitiveTypeCode)blob.ReadByte(); + return true; + } + return false; } public static bool IsDelegate(this TypeDefinitionHandle handle, MetadataReader reader) From 62861b7773c79cceca4118faa747174f808eb051 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 12 Jul 2019 18:10:41 +0200 Subject: [PATCH 034/221] Fix #1566: Generic derived types are missing type parameters --- ILSpy/TreeNodes/DerivedTypesEntryNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ILSpy/TreeNodes/DerivedTypesEntryNode.cs b/ILSpy/TreeNodes/DerivedTypesEntryNode.cs index 91da3e033..e75214afe 100644 --- a/ILSpy/TreeNodes/DerivedTypesEntryNode.cs +++ b/ILSpy/TreeNodes/DerivedTypesEntryNode.cs @@ -42,7 +42,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public override object Text { - get { return type.FullName + type.MetadataToken.ToSuffixString(); } + get { return Language.TypeToString(type, includeNamespace: true) + type.MetadataToken.ToSuffixString(); } } public override object Icon => TypeTreeNode.GetIcon(type); From 15488acdc7691f6fa9dbb66c9d64170cd70ec9ba Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 12 Jul 2019 20:21:58 +0200 Subject: [PATCH 035/221] Fix #1559: switch on string inside try block. --- .../IL/Transforms/SwitchOnStringTransform.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index cb3a2d6b8..f1d1f126c 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -409,11 +409,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!switchValueVar.Type.IsKnownType(KnownTypeCode.String)) return false; // either br nullCase or leave container - var leaveContainer = BlockContainer.FindClosestContainer(instructions[i]); - if (leaveContainer.Parent is TryInstruction) { - leaveContainer = BlockContainer.FindClosestContainer(leaveContainer.Parent); - } - if (!exitBlockJump.MatchBranch(out var nullValueCaseBlock) && !exitBlockJump.MatchLeave(leaveContainer)) + BlockContainer leaveContainer = null; + if (!exitBlockJump.MatchBranch(out var nullValueCaseBlock) && !exitBlockJump.MatchLeave(out leaveContainer)) return false; var nextBlockJump = instructions.ElementAtOrDefault(i + 1) as Branch; if (nextBlockJump == null || nextBlockJump.TargetBlock.IncomingEdgeCount != 1) From 3314f3f37adb7e2fb5c56eb3c2f60f166a988372 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 13 Jul 2019 19:13:40 +0200 Subject: [PATCH 036/221] Fix #1571: Expression transforms not running for values of inline assignments. --- .../TestCases/Pretty/InlineAssignmentTest.cs | 11 +++++++++++ .../IL/Transforms/ExpressionTransforms.cs | 9 +++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.cs index 9620accb5..8ca021f08 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InlineAssignmentTest.cs @@ -32,11 +32,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty get; set; } + public static int StaticProperty { get; set; } + public bool BoolProperty { + get; + set; + } + public void SimpleInlineWithLocals() { int index; @@ -136,5 +142,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { return InstanceProperty = GetIndex(); } + + public bool BoolPropertyTest(object x) + { + return BoolProperty = (x != null); + } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 623d69ae8..a61d99fce 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -64,8 +64,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms protected internal override void VisitBlock(Block block) { - // Don't visit child blocks; since this is a block transform - // we know those were already handled previously. + if (block.Kind == BlockKind.ControlFlow) { + // Don't visit child control flow blocks; + // since this is a block transform + // we know those were already handled previously. + return; + } + base.VisitBlock(block); } protected internal override void VisitComp(Comp inst) From 8d1522f387e0b6363aacf593b349e9d54cc97bc4 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 13 Jul 2019 20:29:30 +0200 Subject: [PATCH 037/221] Transform display classes used in local functions. --- ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 66 +++++++++---- .../CSharp/ExpressionBuilder.cs | 45 +++++++-- .../Resolver/CSharpInvocationResolveResult.cs | 9 +- .../LocalFunctionReferenceResolveResult.cs | 37 +++++++ .../CSharp/StatementBuilder.cs | 11 ++- .../LocalFunctionDeclarationStatement.cs | 20 +++- .../Transforms/IntroduceExtensionMethods.cs | 2 +- .../ICSharpCode.Decompiler.csproj | 1 + .../IL/Instructions/ILFunction.cs | 2 + .../IL/Transforms/AssignVariableNames.cs | 14 +++ .../IL/Transforms/DelegateConstruction.cs | 2 +- .../IL/Transforms/LocalFunctionDecompiler.cs | 36 ++++++- .../Transforms/TransformDisplayClassUsage.cs | 96 ++++++++++++++----- .../Output/TextTokenWriter.cs | 10 +- .../TypeSystem/DecompilerTypeSystem.cs | 4 + ICSharpCode.Decompiler/TypeSystem/IMethod.cs | 2 +- 16 files changed, 293 insertions(+), 64 deletions(-) create mode 100644 ICSharpCode.Decompiler/CSharp/Resolver/LocalFunctionReferenceResolveResult.cs diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 61494fceb..7ec5ee19d 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -38,6 +38,7 @@ namespace ICSharpCode.Decompiler.CSharp { public OpCode CallOpCode; public bool NeedsBoxingConversion; + public bool IsLocalFunction; } struct ArgumentList @@ -52,13 +53,23 @@ namespace ICSharpCode.Decompiler.CSharp public bool AddNamesToPrimitiveValues; public bool IsExpandedForm; + public int LocalFunctionParameterCount; public int Length => Arguments.Length; + private int GetActualArgumentCount() + { + if (LocalFunctionParameterCount < 0 && FirstOptionalArgumentIndex < 0) + return Arguments.Length; + if (LocalFunctionParameterCount < 0) + return FirstOptionalArgumentIndex; + if (FirstOptionalArgumentIndex < 0) + return LocalFunctionParameterCount; + return Math.Min(FirstOptionalArgumentIndex, LocalFunctionParameterCount); + } + public IEnumerable GetArgumentResolveResults(int skipCount = 0) { - return FirstOptionalArgumentIndex < 0 - ? Arguments.Skip(skipCount).Select(a => a.ResolveResult) - : Arguments.Skip(skipCount).Take(FirstOptionalArgumentIndex).Select(a => a.ResolveResult); + return Arguments.Skip(skipCount).Take(GetActualArgumentCount()).Select(a => a.ResolveResult); } public IEnumerable GetArgumentExpressions(int skipCount = 0) @@ -77,22 +88,12 @@ namespace ICSharpCode.Decompiler.CSharp } } } + int argumentCount = GetActualArgumentCount(); if (ArgumentNames == null) { - if (FirstOptionalArgumentIndex < 0) - return Arguments.Skip(skipCount).Select(arg => arg.Expression); - return Arguments.Skip(skipCount).Take(FirstOptionalArgumentIndex).Select(arg => arg.Expression); + return Arguments.Skip(skipCount).Take(argumentCount).Select(arg => arg.Expression); } else { Debug.Assert(skipCount == 0); - if (FirstOptionalArgumentIndex < 0) { - return Arguments.Zip(ArgumentNames, - (arg, name) => { - if (name == null) - return arg.Expression; - else - return new NamedArgumentExpression(name, arg); - }); - } - return Arguments.Take(FirstOptionalArgumentIndex).Zip(ArgumentNames.Take(FirstOptionalArgumentIndex), + return Arguments.Take(argumentCount).Zip(ArgumentNames.Take(argumentCount), (arg, name) => { if (name == null) return arg.Expression; @@ -179,11 +180,17 @@ namespace ICSharpCode.Decompiler.CSharp { // Used for Call, CallVirt and NewObj var expectedTargetDetails = new ExpectedTargetDetails { - CallOpCode = callOpCode + CallOpCode = callOpCode, + IsLocalFunction = expressionBuilder.IsLocalFunction(method) }; TranslatedExpression target; if (callOpCode == OpCode.NewObj) { target = default(TranslatedExpression); // no target + } else if (expectedTargetDetails.IsLocalFunction) { + var localFunction = expressionBuilder.ResolveLocalFunction(method); + target = new IdentifierExpression(localFunction.Name) + .WithoutILInstruction() + .WithRR(new LocalFunctionReferenceResolveResult(localFunction.Definition)); } else { target = expressionBuilder.TranslateTarget( callArguments.FirstOrDefault(), @@ -210,6 +217,22 @@ namespace ICSharpCode.Decompiler.CSharp var argumentList = BuildArgumentList(expectedTargetDetails, target.ResolveResult, method, firstParamIndex, callArguments, argumentToParameterMap); + + if (expectedTargetDetails.IsLocalFunction) { + int parameterCount = 0; + foreach (var param in method.Parameters) { + if (param.IsRef && param.Type is ByReferenceType byRef) { + var type = byRef.ElementType.GetDefinition(); + if (type != null && type.IsCompilerGenerated()) + break; + } + parameterCount++; + } + argumentList.LocalFunctionParameterCount = parameterCount; + return new InvocationExpression(target, argumentList.GetArgumentExpressions()) + .WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method, + argumentList.GetArgumentResolveResults().ToList(), isExpandedForm: argumentList.IsExpandedForm, isLocalFunctionInvocation: true)); + } if (method is VarArgInstanceMethod) { argumentList.FirstOptionalArgumentIndex = -1; @@ -566,10 +589,11 @@ namespace ICSharpCode.Decompiler.CSharp } - private ArgumentList BuildArgumentList(ExpectedTargetDetails expectedTargetDetails, ResolveResult target, IMethod method, int firstParamIndex, - IReadOnlyList callArguments, IReadOnlyList argumentToParameterMap) + private ArgumentList BuildArgumentList(ExpectedTargetDetails expectedTargetDetails, ResolveResult target, IMethod method, + int firstParamIndex, IReadOnlyList callArguments, IReadOnlyList argumentToParameterMap) { ArgumentList list = new ArgumentList(); + list.LocalFunctionParameterCount = -1; // Translate arguments to the expected parameter types var arguments = new List(method.Parameters.Count); @@ -748,7 +772,9 @@ namespace ICSharpCode.Decompiler.CSharp if (expressionBuilder.HidesVariableWithName(method.Name)) { requireTarget = true; } else { - if (method.IsStatic) + if (expectedTargetDetails.IsLocalFunction) + requireTarget = false; + else if (method.IsStatic) requireTarget = !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) || method.Name == ".cctor"; else if (method.Name == ".ctor") requireTarget = true; // always use target for base/this-ctor-call, the constructor initializer pattern depends on this diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 1cfb75ecd..688475cdd 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -68,7 +68,7 @@ namespace ICSharpCode.Decompiler.CSharp class ExpressionBuilder : ILVisitor { readonly IDecompilerTypeSystem typeSystem; - readonly ITypeResolveContext decompilationContext; + internal readonly ITypeResolveContext decompilationContext; internal readonly ILFunction currentFunction; internal readonly ICompilation compilation; internal readonly CSharpResolver resolver; @@ -189,7 +189,38 @@ namespace ICSharpCode.Decompiler.CSharp internal bool HidesVariableWithName(string name) { - return currentFunction.Ancestors.OfType().SelectMany(f => f.Variables).Any(v => v.Name == name); + return currentFunction.Ancestors.OfType().Any(HidesVariableOrNestedFunction); + + bool HidesVariableOrNestedFunction(ILFunction function) + { + foreach (var v in function.Variables) { + if (v.Name == name) + return true; + } + + foreach (var (key, (functionName, definition)) in function.LocalFunctions) { + if (functionName == name) + return true; + } + + return false; + } + } + + internal bool IsLocalFunction(IMethod method) + { + return settings.LocalFunctions && IL.Transforms.LocalFunctionDecompiler.IsLocalFunctionMethod(method); + } + + internal (string Name, ILFunction Definition) ResolveLocalFunction(IMethod method) + { + Debug.Assert(IsLocalFunction(method)); + foreach (var parent in currentFunction.Ancestors.OfType()) { + if (parent.LocalFunctions.TryGetValue(method, out var info)) { + return info; + } + } + return (null, null); } bool RequiresQualifier(IMember member, TranslatedExpression target) @@ -1823,7 +1854,7 @@ namespace ICSharpCode.Decompiler.CSharp return SpecialType.UnknownType; } - IEnumerable MakeParameters(IReadOnlyList parameters, ILFunction function) + internal IEnumerable MakeParameters(IReadOnlyList parameters, ILFunction function) { var variables = function.Variables.Where(v => v.Kind == VariableKind.Parameter).ToDictionary(v => v.Index); int i = 0; @@ -1832,17 +1863,19 @@ namespace ICSharpCode.Decompiler.CSharp if (string.IsNullOrEmpty(pd.Name) && !pd.Type.IsArgList()) { // needs to be consistent with logic in ILReader.CreateILVarable(ParameterDefinition) pd.Name = "P_" + i; + // if this is a local function, we have to skip the parameters for closure references + if (settings.LocalFunctions && function.Kind == ILFunctionKind.LocalFunction && IL.Transforms.LocalFunctionDecompiler.IsClosureParameter(parameter)) + break; } if (settings.AnonymousTypes && parameter.Type.ContainsAnonymousType()) pd.Type = null; - ILVariable v; - if (variables.TryGetValue(i, out v)) + if (variables.TryGetValue(i, out var v)) pd.AddAnnotation(new ILVariableResolveResult(v, parameters[i].Type)); yield return pd; i++; } } - + internal TranslatedExpression TranslateTarget(ILInstruction target, bool nonVirtualInvocation, bool memberStatic, IType memberDeclaringType) { diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs index ee66cb8d9..823887940 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs @@ -46,7 +46,12 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver /// Gets whether a params-Array is being used in its expanded form. /// public readonly bool IsExpandedForm; - + + /// + /// Gets whether this invocation is calling a local function. + /// + public readonly bool IsLocalFunctionInvocation; + readonly IReadOnlyList argumentToParameterMap; /// @@ -70,6 +75,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver bool isExtensionMethodInvocation = false, bool isExpandedForm = false, bool isDelegateInvocation = false, + bool isLocalFunctionInvocation = false, IReadOnlyList argumentToParameterMap = null, IList initializerStatements = null, IType returnTypeOverride = null @@ -80,6 +86,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver this.IsExtensionMethodInvocation = isExtensionMethodInvocation; this.IsExpandedForm = isExpandedForm; this.IsDelegateInvocation = isDelegateInvocation; + this.IsLocalFunctionInvocation = isLocalFunctionInvocation; this.argumentToParameterMap = argumentToParameterMap; } diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/LocalFunctionReferenceResolveResult.cs b/ICSharpCode.Decompiler/CSharp/Resolver/LocalFunctionReferenceResolveResult.cs new file mode 100644 index 000000000..e5253eba4 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Resolver/LocalFunctionReferenceResolveResult.cs @@ -0,0 +1,37 @@ +// Copyright (c) 2019 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +using System; +using System.Collections.Generic; +using System.Text; +using ICSharpCode.Decompiler.IL; +using ICSharpCode.Decompiler.Semantics; +using ICSharpCode.Decompiler.TypeSystem; + +namespace ICSharpCode.Decompiler.CSharp.Resolver +{ + class LocalFunctionReferenceResolveResult : ResolveResult + { + public readonly ILFunction Function; + + public LocalFunctionReferenceResolveResult(ILFunction function) + : base(SpecialType.NoType) + { + this.Function = function; + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index 70f58b7eb..759bbe8a9 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -28,6 +28,7 @@ using System; using System.Threading; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; +using ICSharpCode.Decompiler.CSharp.Resolver; namespace ICSharpCode.Decompiler.CSharp { @@ -1020,10 +1021,14 @@ namespace ICSharpCode.Decompiler.CSharp { var stmt = new LocalFunctionDeclarationStatement(); var tsab = CSharpDecompiler.CreateAstBuilder(null); - stmt.Name = function.Method.Name; - stmt.Parameters.AddRange(function.Method.Parameters.Select(tsab.ConvertParameter)); + var nestedBuilder = new StatementBuilder(typeSystem, exprBuilder.decompilationContext, function, settings, cancellationToken); + var localFunction = exprBuilder.ResolveLocalFunction(function.Method); + Debug.Assert(localFunction.Definition == function); + stmt.Name = localFunction.Name; + stmt.Parameters.AddRange(exprBuilder.MakeParameters(function.Parameters, function)); stmt.ReturnType = tsab.ConvertType(function.Method.ReturnType); - stmt.Body = ConvertAsBlock(function.Body); + stmt.Body = nestedBuilder.ConvertAsBlock(function.Body); + stmt.AddAnnotation(new LocalFunctionReferenceResolveResult(function)); return stmt; } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs index 581b53bfa..47ff1641c 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Statements/LocalFunctionDeclarationStatement.cs @@ -1,6 +1,22 @@ -using System; +// Copyright (c) 2019 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + using System.Collections.Generic; -using System.Text; using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; namespace ICSharpCode.Decompiler.CSharp.Syntax diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs index e15d0d765..54d9b6a58 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs @@ -154,7 +154,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms var newResolveResult = new CSharpInvocationResolveResult( irr.TargetResult, irr.Member, irr.Arguments, irr.OverloadResolutionErrors, isExtensionMethodInvocation: true, irr.IsExpandedForm, irr.IsDelegateInvocation, - irr.GetArgumentToParameterMap(), irr.InitializerStatements); + irr.IsLocalFunctionInvocation, irr.GetArgumentToParameterMap(), irr.InitializerStatements); invocationExpression.AddAnnotation(newResolveResult); } } diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index eaf007f65..c0ef7943d 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -60,6 +60,7 @@ + diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs index cb4acd62c..e0c61ead1 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs @@ -110,6 +110,8 @@ namespace ICSharpCode.Decompiler.IL } } + public Dictionary LocalFunctions { get; } = new Dictionary(); + public readonly IType ReturnType; public readonly IReadOnlyList Parameters; diff --git a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs index 9cdf8bcb4..5e377d221 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs @@ -162,6 +162,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms break; } } + foreach (var method in function.LocalFunctions.Keys.ToArray()) { + var info = function.LocalFunctions[method]; + if (!LocalFunctionDecompiler.ParseLocalFunctionName(method.Name, out _, out var newName) || !IsValidName(newName)) + newName = null; + function.LocalFunctions[method] = (newName, info.Declaration); + } // Now generate names: var mapping = new Dictionary(ILVariableEqualityComparer.Instance); foreach (var inst in function.Descendants.OfType()) { @@ -174,6 +180,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms v.Name = name; } } + foreach (var method in function.LocalFunctions.Keys.ToArray()) { + var info = function.LocalFunctions[method]; + var newName = info.Name; + if (newName == null) { + newName = GetAlternativeName("f"); + } + function.LocalFunctions[method] = (newName, info.Declaration); + } } /// diff --git a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs index e016ef970..5a332fa76 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs @@ -136,10 +136,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms var body = context.PEFile.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress); var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, ILFunctionKind.Delegate, context.CancellationToken); function.DelegateType = value.Method.DeclaringType; - function.CheckInvariant(ILPhase.Normal); // Embed the lambda into the parent function's ILAst, so that "Show steps" can show // how the lambda body is being transformed. value.ReplaceWith(function); + function.CheckInvariant(ILPhase.Normal); var contextPrefix = targetMethod.Name; foreach (ILVariable v in function.Variables.Where(v => v.Kind != VariableKind.Parameter)) { diff --git a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs index cd38b463c..9b69af499 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs @@ -1,4 +1,22 @@ -using System; +// Copyright (c) 2019 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -43,8 +61,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms foreach (var (method, useSites) in localFunctions) { var insertionPoint = FindInsertionPoint(useSites); - if (TransformLocalFunction(method, (Block)insertionPoint.Parent, insertionPoint.ChildIndex + 1) == null) + ILFunction localFunction = TransformLocalFunction(method, (Block)insertionPoint.Parent, insertionPoint.ChildIndex + 1); + if (localFunction == null) continue; + function.LocalFunctions.Add(localFunction.Method, (localFunction.Method.Name, localFunction)); } } @@ -81,7 +101,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms return a.Ancestors.FirstOrDefault(ancestorsOfB.Contains); } - static ILInstruction GetStatement(ILInstruction inst) + internal static bool IsClosureParameter(IParameter parameter) + { + return parameter.IsRef + && ((ByReferenceType)parameter.Type).ElementType.GetDefinition()?.IsCompilerGenerated() == true; + } + + internal static ILInstruction GetStatement(ILInstruction inst) { while (inst.Parent != null) { if (inst.Parent is Block) @@ -161,9 +187,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// Newer Roslyn versions use the format "<callerName>g__functionName|x_y" /// Older versions use "<callerName>g__functionNamex_y" /// - static readonly Regex functionNameRegex = new Regex(@"^<(.*)>g__(.*)\|{0,1}\d+(_\d+)?$", RegexOptions.Compiled); + static readonly Regex functionNameRegex = new Regex(@"^<(.*)>g__([^\|]*)\|{0,1}\d+(_\d+)?$", RegexOptions.Compiled); - static bool ParseLocalFunctionName(string name, out string callerName, out string functionName) + internal static bool ParseLocalFunctionName(string name, out string callerName, out string functionName) { callerName = null; functionName = null; diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index 317b4da04..9034a8fdd 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -65,20 +65,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms foreach (var v in f.Variables.ToArray()) { if (HandleMonoStateMachine(function, v, decompilationContext, f)) continue; - if (!(v.IsSingleDefinition && v.StoreInstructions.SingleOrDefault() is StLoc inst)) - continue; - if (IsClosureInit(inst, out ITypeDefinition closureType)) { - // TODO : figure out whether it is a mono compiled closure, without relying on the type name - bool isMono = f.StateMachineCompiledWithMono || closureType.Name.Contains("AnonStorey"); - displayClasses.Add(inst.Variable, new DisplayClass { - IsMono = isMono, - Initializer = inst, - Variable = v, - Definition = closureType, - Variables = new Dictionary(), - CaptureScope = isMono && IsMonoNestedCaptureScope(closureType) ? null : inst.Variable.CaptureScope - }); - instructionsToRemove.Add(inst); + if (IsClosure(v, out ITypeDefinition closureType, out var inst)) { + AddOrUpdateDisplayClass(f, v, closureType, inst); + } + if (v.Kind == VariableKind.Parameter && v.Index > -1 && f.Method.Parameters[v.Index ?? -1] is IParameter p && LocalFunctionDecompiler.IsClosureParameter(p)) { + AddOrUpdateDisplayClass(f, v, ((ByReferenceType)p.Type).ElementType.GetDefinition(), f.Body); } } foreach (var displayClass in displayClasses.Values.OrderByDescending(d => d.Initializer.StartILOffset).ToArray()) { @@ -100,6 +91,56 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } + private void AddOrUpdateDisplayClass(ILFunction f, ILVariable v, ITypeDefinition closureType, ILInstruction inst) + { + var displayClass = displayClasses.Values.FirstOrDefault(c => c.Definition == closureType); + if (displayClass == null) { + // TODO : figure out whether it is a mono compiled closure, without relying on the type name + bool isMono = f.StateMachineCompiledWithMono || closureType.Name.Contains("AnonStorey"); + displayClasses.Add(v, new DisplayClass { + IsMono = isMono, + Initializer = inst, + Variable = v, + Definition = closureType, + Variables = new Dictionary(), + CaptureScope = isMono && IsMonoNestedCaptureScope(closureType) ? null : v.CaptureScope + }); + } else { + displayClass.Variable = v; + displayClass.Initializer = inst; + displayClasses.Add(v, displayClass); + } + } + + bool IsClosure(ILVariable variable, out ITypeDefinition closureType, out ILInstruction initializer) + { + closureType = null; + initializer = null; + if (variable.IsSingleDefinition && variable.StoreInstructions.SingleOrDefault() is StLoc inst) { + initializer = inst; + if (IsClosureInit(inst, out closureType)) { + instructionsToRemove.Add(inst); + return true; + } + } + closureType = variable.Type.GetDefinition(); + if (closureType?.Kind == TypeKind.Struct && variable.HasInitialValue) { + initializer = LocalFunctionDecompiler.GetStatement(variable.AddressInstructions.OrderBy(i => i.StartILOffset).First()); + return IsPotentialClosure(this.context, closureType); + } + return false; + } + + bool IsClosureInit(StLoc inst, out ITypeDefinition closureType) + { + if (inst.Value is NewObj newObj) { + closureType = newObj.Method.DeclaringTypeDefinition; + return closureType != null && IsPotentialClosure(this.context, newObj); + } + closureType = null; + return false; + } + bool IsOuterClosureReference(IField field) { return displayClasses.Values.Any(disp => disp.Definition == field.DeclaringTypeDefinition); @@ -179,6 +220,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms return IsPotentialClosure(decompilationContext.CurrentTypeDefinition, inst.Method.DeclaringTypeDefinition); } + internal static bool IsPotentialClosure(ILTransformContext context, ITypeDefinition potentialDisplayClass) + { + var decompilationContext = new SimpleTypeResolveContext(context.Function.Ancestors.OfType().Last().Method); + return IsPotentialClosure(decompilationContext.CurrentTypeDefinition, potentialDisplayClass); + } + internal static bool IsPotentialClosure(ITypeDefinition decompiledTypeDefinition, ITypeDefinition potentialDisplayClass) { if (potentialDisplayClass == null || !potentialDisplayClass.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) @@ -191,6 +238,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } + bool IsDisplayClassLoad(ILInstruction target, out ILVariable variable) + { + if (target.MatchLdLoc(out variable) || target.MatchLdLoca(out variable)) + return true; + if (target.MatchAddressOf(out var load) && load.MatchLdLoc(out variable)) + return true; + return false; + } + protected override void Default(ILInstruction inst) { foreach (var child in inst.Children) { @@ -210,13 +266,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } - bool IsClosureInit(StLoc inst, out ITypeDefinition closureType) + protected internal override void VisitLdLoc(LdLoc inst) { - closureType = null; - if (!(inst.Value is NewObj newObj)) - return false; - closureType = newObj.Method.DeclaringTypeDefinition; - return closureType != null && IsPotentialClosure(this.context, newObj); + base.VisitLdLoc(inst); } protected internal override void VisitStObj(StObj inst) @@ -229,7 +281,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!inst.Target.MatchLdFlda(out ILInstruction target, out IField field)) return; // Get display class info - if (!(target is LdLoc displayClassLoad && displayClasses.TryGetValue(displayClassLoad.Variable, out var displayClass))) + if (!IsDisplayClassLoad(target, out var displayClassLoad) || !displayClasses.TryGetValue(displayClassLoad, out var displayClass)) return; field = (IField)field.MemberDefinition; if (displayClass.Variables.TryGetValue(field, out DisplayClassVariable info)) { @@ -264,7 +316,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!inst.Target.MatchLdFlda(out var target, out IField field)) return; // Get display class info - if (!(target is LdLoc displayClassLoad && displayClasses.TryGetValue(displayClassLoad.Variable, out var displayClass))) + if (!IsDisplayClassLoad(target, out var displayClassLoad) || !displayClasses.TryGetValue(displayClassLoad, out var displayClass)) return; // Get display class variable info if (!displayClass.Variables.TryGetValue((IField)field.MemberDefinition, out DisplayClassVariable info)) diff --git a/ICSharpCode.Decompiler/Output/TextTokenWriter.cs b/ICSharpCode.Decompiler/Output/TextTokenWriter.cs index f19c8cfda..3b572088f 100644 --- a/ICSharpCode.Decompiler/Output/TextTokenWriter.cs +++ b/ICSharpCode.Decompiler/Output/TextTokenWriter.cs @@ -21,6 +21,7 @@ using System.Collections.Generic; using System.Linq; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.CSharp.OutputVisitor; +using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.TypeSystem; @@ -169,13 +170,18 @@ namespace ICSharpCode.Decompiler return variable; } - var label = node as LabelStatement; - if (label != null) { + if (node is LabelStatement label) { var method = nodeStack.Select(nd => nd.GetSymbol() as IMethod).FirstOrDefault(mr => mr != null); if (method != null) return method + label.Label; } + if (node is LocalFunctionDeclarationStatement) { + var localFunction = node.GetResolveResult() as LocalFunctionReferenceResolveResult; + if (localFunction != null) + return localFunction.Function.Method; + } + return null; } diff --git a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs index 04ce3ef8f..06f07cdd9 100644 --- a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs +++ b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs @@ -104,6 +104,10 @@ namespace ICSharpCode.Decompiler.TypeSystem /// NullabilityAnnotations = 0x400, /// + /// If this option is active, + /// + LocalFunctions = 0x800, + /// /// Default settings: typical options for the decompiler, with all C# languages features enabled. /// Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants | ReadOnlyStructsAndParameters diff --git a/ICSharpCode.Decompiler/TypeSystem/IMethod.cs b/ICSharpCode.Decompiler/TypeSystem/IMethod.cs index 64c8b3303..17f29aab6 100644 --- a/ICSharpCode.Decompiler/TypeSystem/IMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/IMethod.cs @@ -82,7 +82,7 @@ namespace ICSharpCode.Decompiler.TypeSystem /// /// If this method is reduced from an extension method return the original method, null otherwise. - /// A reduced method doesn't contain the extension method parameter. That means that has one parameter less than it's definition. + /// A reduced method doesn't contain the extension method parameter. That means that it has one parameter less than its definition. /// IMethod ReducedFrom { get; } From 119c679fad319f92be3fe174735a3f49bc88a4a1 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 13 Jul 2019 20:32:00 +0200 Subject: [PATCH 038/221] Add addressof(ldloc V) => ldloca V transform for read access on value types. --- .../IL/Transforms/ExpressionTransforms.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 623d69ae8..08c0a965c 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -389,6 +389,22 @@ namespace ICSharpCode.Decompiler.IL.Transforms { base.VisitLdObj(inst); EarlyExpressionTransforms.LdObjToLdLoc(inst, context); + // ldobj(...(addressof(ldloc V)) where ... can be zero or more ldflda instructions + // => + // ldobj(...(ldloca V)) + var temp = inst.Target; + var range = temp.ILRanges; + while (temp.MatchLdFlda(out var ldfldaTarget, out _)) { + temp = ldfldaTarget; + range = range.Concat(temp.ILRanges); + } + if (temp.MatchAddressOf(out var addressOfTarget) && addressOfTarget.MatchLdLoc(out var v)) { + var replacement = new LdLoca(v).WithILRange(addressOfTarget); + foreach (var r in range) { + replacement = replacement.WithILRange(r); + } + temp.ReplaceWith(replacement); + } } protected internal override void VisitStObj(StObj inst) From d841d964062d7a5ba4535f4a60bebc206963c75a Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 13 Jul 2019 22:21:51 +0200 Subject: [PATCH 039/221] Fix unnecessary casts when calling user-defined operators on nullable types. --- ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 61494fceb..2a287a7e3 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -979,11 +979,14 @@ namespace ICSharpCode.Decompiler.CSharp } else if (method.IsOperator) { IEnumerable operatorCandidates; if (arguments.Count == 1) { - operatorCandidates = resolver.GetUserDefinedOperatorCandidates(arguments[0].Type, method.Name); + IType argType = NullableType.GetUnderlyingType(arguments[0].Type); + operatorCandidates = resolver.GetUserDefinedOperatorCandidates(argType, method.Name); } else if (arguments.Count == 2) { + IType lhsType = NullableType.GetUnderlyingType(arguments[0].Type); + IType rhsType = NullableType.GetUnderlyingType(arguments[1].Type); var hashSet = new HashSet(); - hashSet.UnionWith(resolver.GetUserDefinedOperatorCandidates(arguments[0].Type, method.Name)); - hashSet.UnionWith(resolver.GetUserDefinedOperatorCandidates(arguments[1].Type, method.Name)); + hashSet.UnionWith(resolver.GetUserDefinedOperatorCandidates(lhsType, method.Name)); + hashSet.UnionWith(resolver.GetUserDefinedOperatorCandidates(rhsType, method.Name)); operatorCandidates = hashSet; } else { operatorCandidates = EmptyList.Instance; From e6489d543e07f84185a0d52257bf8d48d3a30d35 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 13 Jul 2019 22:23:15 +0200 Subject: [PATCH 040/221] Fix #1574: When re-introducing an explicit cast for an implicit user-defined conversion; ensure we use a direct cast and don't go through the code path for builtin conversions. --- .../ICSharpCode.Decompiler.Tests.csproj | 1 + .../PrettyTestRunner.cs | 6 + .../Pretty/UserDefinedConversions.cs | 134 ++++++++++++++++++ ICSharpCode.Decompiler/CSharp/Annotations.cs | 14 ++ .../CSharp/TranslatedExpression.cs | 23 ++- 5 files changed, 175 insertions(+), 3 deletions(-) create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Pretty/UserDefinedConversions.cs diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 51d24fc07..51ed72689 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -89,6 +89,7 @@ + diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index 6c66f55c3..e7786ac67 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -425,6 +425,12 @@ namespace ICSharpCode.Decompiler.Tests RunForLibrary(cscOptions: cscOptions); } + [Test] + public void UserDefinedConversions([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) + { + RunForLibrary(cscOptions: cscOptions); + } + [Test] public void Discards([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UserDefinedConversions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UserDefinedConversions.cs new file mode 100644 index 000000000..a91ca7a82 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/UserDefinedConversions.cs @@ -0,0 +1,134 @@ +// Copyright (c) 2019 Daniel Grunwald +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +{ + internal class T01Issue1574 + { + private struct A + { + private bool val; + + public static implicit operator bool(A a) + { + return a.val; + } + } + + private struct C + { + private int val; + + public static implicit operator C(bool b) + { + return default(C); + } + } + + private C ChainedConversion() + { + return (bool)default(A); + } + + public void Call_Overloaded() + { + Overloaded((bool)default(A)); + } + + private void Overloaded(A a) + { + } + + private void Overloaded(bool a) + { + } + } + + internal class T02BothDirectAndChainedConversionPossible + { + private struct A + { + private bool val; + + public static implicit operator bool(A a) + { + return a.val; + } + } + + private struct C + { + private int val; + + public static implicit operator C(bool b) + { + return default(C); + } + + public static implicit operator C(A a) + { + return default(C); + } + + public static bool operator ==(C a, C b) + { + return true; + } + public static bool operator !=(C a, C b) + { + return false; + } + } + + private C DirectConvert(A a) + { + return a; + } + + private C IndirectConvert(A a) + { + return (bool)a; + } + + private C? LiftedDirectConvert(A? a) + { + return a; + } + + private C? LiftedIndirectConvert(A? a) + { + return (bool?)a; + } + + private bool Compare(A a, C c) + { + return a == c; + } + + private void LiftedCompare(A? a, C? c) + { + UseBool(a == c); + UseBool(a == default(C)); + UseBool(c == default(A)); + } + + private void UseBool(bool b) + { + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/Annotations.cs b/ICSharpCode.Decompiler/CSharp/Annotations.cs index 064da15f2..ea813e554 100644 --- a/ICSharpCode.Decompiler/CSharp/Annotations.cs +++ b/ICSharpCode.Decompiler/CSharp/Annotations.cs @@ -240,4 +240,18 @@ namespace ICSharpCode.Decompiler.CSharp this.Leave = leave; } } + + /// + /// Annotates an expression when an implicit user-defined conversion was omitted. + /// + public class ImplicitConversionAnnotation + { + public readonly ConversionResolveResult ConversionResolveResult; + public IType TargetType => ConversionResolveResult.Type; + + public ImplicitConversionAnnotation(ConversionResolveResult conversionResolveResult) + { + this.ConversionResolveResult = conversionResolveResult; + } + } } diff --git a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs index e0d3dbae6..e270f5563 100644 --- a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs @@ -191,7 +191,11 @@ namespace ICSharpCode.Decompiler.CSharp conversion.Input.Type, type, targetType )) { - return this.UnwrapChild(cast.Expression); + var result = this.UnwrapChild(cast.Expression); + if (conversion.Conversion.IsUserDefined) { + result.Expression.AddAnnotation(new ImplicitConversionAnnotation(conversion)); + } + return result; } else if (Expression is ObjectCreateExpression oce && conversion.Conversion.IsMethodGroupConversion && oce.Arguments.Count == 1 && expressionBuilder.settings.UseImplicitMethodGroupConversion) { return this.UnwrapChild(oce.Arguments.Single()); @@ -211,6 +215,18 @@ namespace ICSharpCode.Decompiler.CSharp if (targetType.Kind == TypeKind.Unknown || targetType.Kind == TypeKind.Void || targetType.Kind == TypeKind.None) { return this; // don't attempt to insert cast to '?' or 'void' as these are not valid. } + var convAnnotation = this.Expression.Annotation(); + if (convAnnotation != null) { + // If an implicit user-defined conversion was stripped from this expression; + // it needs to be re-introduced before we can apply other casts to this expression. + // This happens when the CallBuilder discovers that the conversion is necessary in + // order to choose the correct overload. + this.Expression.RemoveAnnotations(); + return new CastExpression(expressionBuilder.ConvertType(convAnnotation.TargetType), Expression) + .WithoutILInstruction() + .WithRR(convAnnotation.ConversionResolveResult) + .ConvertTo(targetType, expressionBuilder, checkForOverflow, allowImplicitConversion); + } if (Expression is TupleExpression tupleExpr && targetType is TupleType targetTupleType && tupleExpr.Elements.Count == targetTupleType.ElementTypes.Length) { @@ -231,8 +247,9 @@ namespace ICSharpCode.Decompiler.CSharp } var compilation = expressionBuilder.compilation; var conversions = Resolver.CSharpConversions.Get(compilation); - if (ResolveResult is ConversionResolveResult conv && Expression is CastExpression cast2 && - CastCanBeMadeImplicit(conversions, conv.Conversion, conv.Input.Type, type, targetType)) + if (ResolveResult is ConversionResolveResult conv && Expression is CastExpression cast2 + && !conv.Conversion.IsUserDefined + && CastCanBeMadeImplicit(conversions, conv.Conversion, conv.Input.Type, type, targetType)) { var unwrapped = this.UnwrapChild(cast2.Expression); if (allowImplicitConversion) From cd8dd7af9fb60ca5939e547ed7cfbda3afc11280 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 13 Jul 2019 21:54:11 +0200 Subject: [PATCH 041/221] Disassembler: Fix output of calling convention and flags in standalone method signature --- .../IL/InstructionOutputExtensions.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs b/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs index 3e3134d71..154885aee 100644 --- a/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs +++ b/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs @@ -319,13 +319,21 @@ namespace ICSharpCode.Decompiler.IL var standaloneSig = metadata.GetStandaloneSignature((StandaloneSignatureHandle)entity); switch (standaloneSig.GetKind()) { case StandaloneSignatureKind.Method: - var methodSig = standaloneSig.DecodeMethodSignature(new DisassemblerSignatureProvider(module, output), genericContext); - methodSig.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); + methodSignature = standaloneSig.DecodeMethodSignature(new DisassemblerSignatureProvider(module, output), genericContext); + if (methodSignature.Header.HasExplicitThis) { + output.Write("instance explicit "); + } else if (methodSignature.Header.IsInstance) { + output.Write("instance "); + } + if (methodSignature.Header.CallingConvention == SignatureCallingConvention.VarArgs) { + output.Write("vararg "); + } + methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); output.Write('('); - for (int i = 0; i < methodSig.ParameterTypes.Length; i++) { + for (int i = 0; i < methodSignature.ParameterTypes.Length; i++) { if (i > 0) output.Write(", "); - methodSig.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters); + methodSignature.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters); } output.Write(')'); break; From ab892a603bfec5fcf115266011234a24ca33a156 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 13 Jul 2019 22:23:37 +0200 Subject: [PATCH 042/221] Disassembler: Refactor InstructionOutputExtensions.WriteTo: Reduce code duplication; Add support for unmanaged calling conventions --- .../IL/InstructionOutputExtensions.cs | 153 +++++++----------- 1 file changed, 62 insertions(+), 91 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs b/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs index 154885aee..d1aab3e59 100644 --- a/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs +++ b/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs @@ -133,14 +133,7 @@ namespace ICSharpCode.Decompiler.IL case HandleKind.MethodDefinition: { var md = metadata.GetMethodDefinition((MethodDefinitionHandle)entity); methodSignature = md.DecodeSignature(new DisassemblerSignatureProvider(module, output), new Metadata.GenericContext((MethodDefinitionHandle)entity, module)); - if (methodSignature.Header.HasExplicitThis) { - output.Write("instance explicit "); - } else if (methodSignature.Header.IsInstance) { - output.Write("instance "); - } - if (methodSignature.Header.CallingConvention == SignatureCallingConvention.VarArgs) { - output.Write("vararg "); - } + WriteSignatureHeader(output, methodSignature); methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); output.Write(' '); var declaringType = md.GetDeclaringType(); @@ -189,13 +182,7 @@ namespace ICSharpCode.Decompiler.IL } output.Write('>'); } - output.Write("("); - for (int i = 0; i < methodSignature.ParameterTypes.Length; ++i) { - if (i > 0) - output.Write(", "); - methodSignature.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters); - } - output.Write(")"); + WriteParameterList(output, methodSignature); break; } case HandleKind.MemberReference: @@ -204,28 +191,13 @@ namespace ICSharpCode.Decompiler.IL switch (mr.GetKind()) { case MemberReferenceKind.Method: methodSignature = mr.DecodeMethodSignature(new DisassemblerSignatureProvider(module, output), genericContext); - if (methodSignature.Header.HasExplicitThis) { - output.Write("instance explicit "); - } else if (methodSignature.Header.IsInstance) { - output.Write("instance "); - } - if (methodSignature.Header.CallingConvention == SignatureCallingConvention.VarArgs) { - output.Write("vararg "); - } + WriteSignatureHeader(output, methodSignature); methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); output.Write(' '); WriteParent(output, module, metadata, mr.Parent, genericContext, syntax); output.Write("::"); output.WriteReference(module, entity, DisassemblerHelpers.Escape(memberName)); - output.Write("("); - for (int i = 0; i < methodSignature.ParameterTypes.Length; ++i) { - if (i > 0) - output.Write(", "); - if (i == methodSignature.RequiredParameterCount) - output.Write("..., "); - methodSignature.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters); - } - output.Write(")"); + WriteParameterList(output, methodSignature); break; case MemberReferenceKind.Field: var fieldSignature = mr.DecodeFieldSignature(new DisassemblerSignatureProvider(module, output), genericContext); @@ -245,14 +217,7 @@ namespace ICSharpCode.Decompiler.IL var methodDefinition = metadata.GetMethodDefinition((MethodDefinitionHandle)ms.Method); var methodName = metadata.GetString(methodDefinition.Name); methodSignature = methodDefinition.DecodeSignature(new DisassemblerSignatureProvider(module, output), genericContext); - if (methodSignature.Header.HasExplicitThis) { - output.Write("instance explicit "); - } else if (methodSignature.Header.IsInstance) { - output.Write("instance "); - } - if (methodSignature.Header.CallingConvention == SignatureCallingConvention.VarArgs) { - output.Write("vararg "); - } + WriteSignatureHeader(output, methodSignature); methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); output.Write(' '); var declaringType = methodDefinition.GetDeclaringType(); @@ -266,52 +231,21 @@ namespace ICSharpCode.Decompiler.IL } else { output.Write(DisassemblerHelpers.Escape(methodName)); } - output.Write('<'); - for (int i = 0; i < substitution.Length; i++) { - if (i > 0) - output.Write(", "); - substitution[i](syntax); - } - output.Write('>'); - output.Write("("); - for (int i = 0; i < methodSignature.ParameterTypes.Length; ++i) { - if (i > 0) - output.Write(", "); - methodSignature.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters); - } - output.Write(")"); + WriteTypeParameterList(output, syntax, substitution); + WriteParameterList(output, methodSignature); break; case HandleKind.MemberReference: var memberReference = metadata.GetMemberReference((MemberReferenceHandle)ms.Method); memberName = metadata.GetString(memberReference.Name); methodSignature = memberReference.DecodeMethodSignature(new DisassemblerSignatureProvider(module, output), genericContext); - if (methodSignature.Header.HasExplicitThis) { - output.Write("instance explicit "); - } else if (methodSignature.Header.IsInstance) { - output.Write("instance "); - } - if (methodSignature.Header.CallingConvention == SignatureCallingConvention.VarArgs) { - output.Write("vararg "); - } + WriteSignatureHeader(output, methodSignature); methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); output.Write(' '); WriteParent(output, module, metadata, memberReference.Parent, genericContext, syntax); output.Write("::"); output.Write(DisassemblerHelpers.Escape(memberName)); - output.Write('<'); - for (int i = 0; i < substitution.Length; i++) { - if (i > 0) - output.Write(", "); - substitution[i](syntax); - } - output.Write('>'); - output.Write("("); - for (int i = 0; i < methodSignature.ParameterTypes.Length; ++i) { - if (i > 0) - output.Write(", "); - methodSignature.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters); - } - output.Write(")"); + WriteTypeParameterList(output, syntax, substitution); + WriteParameterList(output, methodSignature); break; } break; @@ -320,22 +254,9 @@ namespace ICSharpCode.Decompiler.IL switch (standaloneSig.GetKind()) { case StandaloneSignatureKind.Method: methodSignature = standaloneSig.DecodeMethodSignature(new DisassemblerSignatureProvider(module, output), genericContext); - if (methodSignature.Header.HasExplicitThis) { - output.Write("instance explicit "); - } else if (methodSignature.Header.IsInstance) { - output.Write("instance "); - } - if (methodSignature.Header.CallingConvention == SignatureCallingConvention.VarArgs) { - output.Write("vararg "); - } + WriteSignatureHeader(output, methodSignature); methodSignature.ReturnType(ILNameSyntax.SignatureNoNamedTypeParameters); - output.Write('('); - for (int i = 0; i < methodSignature.ParameterTypes.Length; i++) { - if (i > 0) - output.Write(", "); - methodSignature.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters); - } - output.Write(')'); + WriteParameterList(output, methodSignature); break; case StandaloneSignatureKind.LocalVariables: default: @@ -349,6 +270,56 @@ namespace ICSharpCode.Decompiler.IL } } + static void WriteTypeParameterList(ITextOutput output, ILNameSyntax syntax, System.Collections.Immutable.ImmutableArray> substitution) + { + output.Write('<'); + for (int i = 0; i < substitution.Length; i++) { + if (i > 0) + output.Write(", "); + substitution[i](syntax); + } + output.Write('>'); + } + + static void WriteParameterList(ITextOutput output, MethodSignature> methodSignature) + { + output.Write("("); + for (int i = 0; i < methodSignature.ParameterTypes.Length; ++i) { + if (i > 0) + output.Write(", "); + if (i == methodSignature.RequiredParameterCount) + output.Write("..., "); + methodSignature.ParameterTypes[i](ILNameSyntax.SignatureNoNamedTypeParameters); + } + output.Write(")"); + } + + static void WriteSignatureHeader(ITextOutput output, MethodSignature> methodSignature) + { + if (methodSignature.Header.HasExplicitThis) { + output.Write("instance explicit "); + } else if (methodSignature.Header.IsInstance) { + output.Write("instance "); + } + switch (methodSignature.Header.CallingConvention) { + case SignatureCallingConvention.CDecl: + output.Write("unmanaged cdecl "); + break; + case SignatureCallingConvention.StdCall: + output.Write("unmanaged stdcall "); + break; + case SignatureCallingConvention.ThisCall: + output.Write("unmanaged thiscall "); + break; + case SignatureCallingConvention.FastCall: + output.Write("unmanaged fastcall "); + break; + case SignatureCallingConvention.VarArgs: + output.Write("vararg "); + break; + } + } + static void WriteParent(ITextOutput output, PEFile module, MetadataReader metadata, EntityHandle parentHandle, Metadata.GenericContext genericContext, ILNameSyntax syntax) { switch (parentHandle.Kind) { From ed5d71b3656a99498b3828e10fb31b8c83e98274 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 13 Jul 2019 22:38:39 +0200 Subject: [PATCH 043/221] Fix #1537: KeyNotFoundException in ReduceNestingTransform.ReduceSwitchNesting() --- ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs index 03e3297f3..cdd1822a0 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs @@ -216,6 +216,8 @@ namespace ICSharpCode.Decompiler.IL var defaultSection = switchInst.Sections.MaxBy(s => s.Labels.Count()); if (!defaultSection.Body.MatchBranch(out var defaultBlock) || defaultBlock.IncomingEdgeCount != 1) return false; + if (defaultBlock.Parent != switchContainer) + return false; // tally stats for heuristic from each case block int maxStatements = 0, maxDepth = 0; From a6def4cdf5edfe03d1ac65950df2a947aebef505 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 13 Jul 2019 23:08:30 +0200 Subject: [PATCH 044/221] Fix incorrect type for numeric.compound.assign when the pointer type is incompatible with the store type. Closes #1511, #1530, #1533. --- .../IL/Instructions/CompoundAssignmentInstruction.cs | 1 + .../IL/Transforms/TransformAssignment.cs | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs index b3d3fe8ce..972be8aa0 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs @@ -97,6 +97,7 @@ namespace ICSharpCode.Decompiler.IL this.AddILRange(binary); Debug.Assert(compoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue || (Operator == BinaryNumericOperator.Add || Operator == BinaryNumericOperator.Sub)); Debug.Assert(IsValidCompoundAssignmentTarget(Target)); + Debug.Assert(this.ResultType == (IsLifted ? StackType.O : UnderlyingResultType)); } /// diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs index 1f337c420..f7a6bee67 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs @@ -478,9 +478,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms // Try to determine the real type of the object we're modifying: storeType = stobj.Target.InferType(compilation); if (storeType is ByReferenceType refType) { - storeType = refType.ElementType; + if (TypeUtils.IsCompatibleTypeForMemoryAccess(refType.ElementType, stobj.Type)) { + storeType = refType.ElementType; + } else { + storeType = stobj.Type; + } } else if (storeType is PointerType pointerType) { - storeType = pointerType.ElementType; + if (TypeUtils.IsCompatibleTypeForMemoryAccess(pointerType.ElementType, stobj.Type)) { + storeType = pointerType.ElementType; + } else { + storeType = stobj.Type; + } } else { storeType = stobj.Type; } From d50b8d66d13b3d46b4f1f5023e30a748c9eec930 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 14 Jul 2019 07:58:47 +0200 Subject: [PATCH 045/221] Fix #1373, fix #1541: add support for instance calls to CallIndirect. --- ICSharpCode.Decompiler/IL/ILReader.cs | 11 +++++++--- .../IL/Instructions/CallIndirect.cs | 22 ++++++++++++++----- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 4d98af2fd..d9e3a2b83 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -1401,12 +1401,17 @@ namespace ICSharpCode.Decompiler.IL var signatureHandle = (StandaloneSignatureHandle)ReadAndDecodeMetadataToken(); var signature = module.DecodeMethodSignature(signatureHandle, genericContext); var functionPointer = Pop(StackType.I); - Debug.Assert(!signature.Header.IsInstance); - var arguments = new ILInstruction[signature.ParameterTypes.Length]; + int firstArgument = signature.Header.IsInstance ? 1 : 0; + var arguments = new ILInstruction[firstArgument + signature.ParameterTypes.Length]; for (int i = signature.ParameterTypes.Length - 1; i >= 0; i--) { - arguments[i] = Pop(signature.ParameterTypes[i].GetStackType()); + arguments[firstArgument + i] = Pop(signature.ParameterTypes[i].GetStackType()); + } + if (firstArgument == 1) { + arguments[0] = Pop(); } var call = new CallIndirect( + signature.Header.IsInstance, + signature.Header.HasExplicitThis, signature.Header.CallingConvention, signature.ReturnType, signature.ParameterTypes, diff --git a/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs b/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs index 07f980fa7..e2b751218 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/CallIndirect.cs @@ -32,7 +32,8 @@ namespace ICSharpCode.Decompiler.IL public readonly InstructionCollection Arguments; ILInstruction functionPointer; - + public bool IsInstance { get; } + public bool HasExplicitThis { get; } public System.Reflection.Metadata.SignatureCallingConvention CallingConvention { get; } public IType ReturnType { get; } public ImmutableArray ParameterTypes { get; } @@ -61,9 +62,11 @@ namespace ICSharpCode.Decompiler.IL functionPointer.ChildIndex = Arguments.Count; } - public CallIndirect(System.Reflection.Metadata.SignatureCallingConvention callingConvention, IType returnType, ImmutableArray parameterTypes, + public CallIndirect(bool isInstance, bool hasExplicitThis, System.Reflection.Metadata.SignatureCallingConvention callingConvention, IType returnType, ImmutableArray parameterTypes, IEnumerable arguments, ILInstruction functionPointer) : base(OpCode.CallIndirect) { + this.IsInstance = isInstance; + this.HasExplicitThis = hasExplicitThis; this.CallingConvention = callingConvention; this.ReturnType = returnType ?? throw new ArgumentNullException("returnType"); this.ParameterTypes = parameterTypes.ToImmutableArray(); @@ -74,7 +77,7 @@ namespace ICSharpCode.Decompiler.IL public override ILInstruction Clone() { - return new CallIndirect(CallingConvention, ReturnType, ParameterTypes, + return new CallIndirect(IsInstance, HasExplicitThis, CallingConvention, ReturnType, ParameterTypes, this.Arguments.Select(inst => inst.Clone()), functionPointer.Clone() ).WithILRange(this); } @@ -84,7 +87,7 @@ namespace ICSharpCode.Decompiler.IL internal override void CheckInvariant(ILPhase phase) { base.CheckInvariant(phase); - Debug.Assert(Arguments.Count == ParameterTypes.Length); + Debug.Assert(Arguments.Count == ParameterTypes.Length + (IsInstance ? 1 : 0)); } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) @@ -94,7 +97,12 @@ namespace ICSharpCode.Decompiler.IL ReturnType.WriteTo(output); output.Write('('); bool first = true; - foreach (var (inst, type) in Arguments.Zip(ParameterTypes, (a,b) => (a,b))) { + int firstArgument = IsInstance ? 1 : 0; + if (firstArgument == 1) { + Arguments[0].WriteTo(output, options); + first = false; + } + foreach (var (inst, type) in Arguments.Skip(firstArgument).Zip(ParameterTypes, (a,b) => (a,b))) { if (first) first = false; else @@ -155,6 +163,10 @@ namespace ICSharpCode.Decompiler.IL bool EqualSignature(CallIndirect other) { + if (IsInstance != other.IsInstance) + return false; + if (HasExplicitThis != other.HasExplicitThis) + return false; if (CallingConvention != other.CallingConvention) return false; if (ParameterTypes.Length != other.ParameterTypes.Length) From 6338bd1b8538554471c276a7aade7b781d326624 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 14 Jul 2019 10:13:28 +0200 Subject: [PATCH 046/221] Partially revert CheckNoNamedOrOptionalArguments. --- .../Transforms/ReplaceMethodCallsWithOperators.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs index 7aea2a4a8..0486ce871 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs @@ -154,16 +154,9 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (arguments.Length < 2) return false; - bool valid = false; - - foreach (var argument in arguments) { - if (argument is NamedArgumentExpression) - return false; - if (argument.GetResolveResult().Type.IsKnownType(KnownTypeCode.String)) - valid = true; - } - - return valid; + return !arguments.Any(arg => arg is NamedArgumentExpression) && + (arguments[0].GetResolveResult().Type.IsKnownType(KnownTypeCode.String) || + arguments[1].GetResolveResult().Type.IsKnownType(KnownTypeCode.String)); } static BinaryOperatorType? GetBinaryOperatorTypeFromMetadataName(string name) From 7d95516e8526511b2f53d539d8d2fb2f41f046da Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 14 Jul 2019 10:14:34 +0200 Subject: [PATCH 047/221] Fix #1518: Missing Detach()-call in AST manipulation. --- ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 2a287a7e3..0a6114889 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -334,7 +334,7 @@ namespace ICSharpCode.Decompiler.CSharp // settings.AlwaysCastTargetsOfExplicitInterfaceImplementationCalls == true is used in Windows Forms' InitializeComponent methods. if (method.IsExplicitInterfaceImplementation && (target.Expression is ThisReferenceExpression || settings.AlwaysCastTargetsOfExplicitInterfaceImplementationCalls)) { var interfaceMember = method.ExplicitlyImplementedInterfaceMembers.First(); - var castExpression = new CastExpression(expressionBuilder.ConvertType(interfaceMember.DeclaringType), target.Expression); + var castExpression = new CastExpression(expressionBuilder.ConvertType(interfaceMember.DeclaringType), target.Expression.Detach()); methodName = interfaceMember.Name; targetExpr = new MemberReferenceExpression(castExpression, methodName); typeArgumentList = ((MemberReferenceExpression)targetExpr).TypeArguments; From 5c9b3233cdac45061b0fb59816205d93981b521e Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 14 Jul 2019 10:50:09 +0200 Subject: [PATCH 048/221] Fix bug overwriting settings default values on first load. --- ILSpy/Options/DecompilerSettingsPanel.xaml.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ILSpy/Options/DecompilerSettingsPanel.xaml.cs b/ILSpy/Options/DecompilerSettingsPanel.xaml.cs index c1c734173..134f9faf0 100644 --- a/ILSpy/Options/DecompilerSettingsPanel.xaml.cs +++ b/ILSpy/Options/DecompilerSettingsPanel.xaml.cs @@ -54,7 +54,9 @@ namespace ICSharpCode.ILSpy.Options var properties = typeof(Decompiler.DecompilerSettings).GetProperties() .Where(p => p.GetCustomAttribute()?.Browsable != false); foreach (var p in properties) { - p.SetValue(newSettings, (bool?)e.Attribute(p.Name) ?? true); + var value = (bool?)e.Attribute(p.Name); + if (value.HasValue) + p.SetValue(newSettings, value.Value); } return newSettings; } From 688dffff2b4303963e20f08656239c8ff823157f Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 14 Jul 2019 14:18:47 +0200 Subject: [PATCH 049/221] Add some string.Concat()-tests --- .../TestCases/Correctness/TrickyTypes.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/TrickyTypes.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/TrickyTypes.cs index 83b609b2f..437c37e54 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/TrickyTypes.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/TrickyTypes.cs @@ -26,6 +26,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness { InterestingConstants(); TruncatedComp(); + StringConcat(); } static void Print(T val) @@ -92,5 +93,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness Print(val1 <= val2); Print((int)val1 <= val2); } + + static void StringConcat() + { + // Some string.Concat()-cases that cannot be replaced using operator+ + Print(string.Concat("String concat:")); + Print(string.Concat(1, 2)); + Print(string.Concat(1, 2, "str")); + } } } From dec9c83c4c85f02b886a0b4ae0c3f7068515e061 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 14 Jul 2019 14:31:15 +0200 Subject: [PATCH 050/221] Fix #1570: Handle ReflectionNameParseException when decoding invalid attribute arguments --- ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs b/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs index ccf21ac9c..0ab8f4faa 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeProvider.cs @@ -141,8 +141,12 @@ namespace ICSharpCode.Decompiler.TypeSystem if (name == null) { return null; } - return ReflectionHelper.ParseReflectionName(name) - .Resolve(module != null ? new SimpleTypeResolveContext(module) : new SimpleTypeResolveContext(compilation)); + try { + return ReflectionHelper.ParseReflectionName(name) + .Resolve(module != null ? new SimpleTypeResolveContext(module) : new SimpleTypeResolveContext(compilation)); + } catch (ReflectionNameParseException ex) { + throw new BadImageFormatException($"Invalid type name: \"{name}\": {ex.Message}"); + } } public IType GetTypeFromSpecification(SRM.MetadataReader reader, GenericContext genericContext, SRM.TypeSpecificationHandle handle, byte rawTypeKind) From 16d6e16da77afbe35fe5df7d7510af3f41e2630f Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 14 Jul 2019 16:22:00 +0200 Subject: [PATCH 051/221] Add support for local function references and recursive local functions. --- ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 122 ++++++++++-------- .../IL/Transforms/DelegateConstruction.cs | 2 +- .../IL/Transforms/LocalFunctionDecompiler.cs | 87 ++++++++++--- .../Transforms/TransformDisplayClassUsage.cs | 8 +- 4 files changed, 141 insertions(+), 78 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 7ec5ee19d..489a12f1c 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -183,11 +183,14 @@ namespace ICSharpCode.Decompiler.CSharp CallOpCode = callOpCode, IsLocalFunction = expressionBuilder.IsLocalFunction(method) }; + (string Name, ILFunction Definition) localFunction = default; + if (expectedTargetDetails.IsLocalFunction && (localFunction = expressionBuilder.ResolveLocalFunction(method)).Definition == null) { + expectedTargetDetails.IsLocalFunction = false; + } TranslatedExpression target; if (callOpCode == OpCode.NewObj) { target = default(TranslatedExpression); // no target } else if (expectedTargetDetails.IsLocalFunction) { - var localFunction = expressionBuilder.ResolveLocalFunction(method); target = new IdentifierExpression(localFunction.Name) .WithoutILInstruction() .WithRR(new LocalFunctionReferenceResolveResult(localFunction.Definition)); @@ -1236,71 +1239,82 @@ namespace ICSharpCode.Decompiler.CSharp TranslatedExpression target; IType targetType; bool requireTarget; - if (method.IsExtensionMethod && invokeMethod != null && method.Parameters.Count - 1 == invokeMethod.Parameters.Count) { - targetType = method.Parameters[0].Type; - if (targetType.Kind == TypeKind.ByReference && thisArg is Box thisArgBox) { - targetType = ((ByReferenceType)targetType).ElementType; - thisArg = thisArgBox.Argument; - } - target = expressionBuilder.Translate(thisArg, targetType); - requireTarget = true; - } else { - targetType = method.DeclaringType; - if (targetType.IsReferenceType == false && thisArg is Box thisArgBox) { - // Normal struct instance method calls (which TranslateTarget is meant for) expect a 'ref T', - // but delegate construction uses a 'box T'. - if (thisArgBox.Argument is LdObj ldobj) { - thisArg = ldobj.Target; - } else { - thisArg = new AddressOf(thisArgBox.Argument); - } - } - target = expressionBuilder.TranslateTarget(thisArg, - nonVirtualInvocation: func.OpCode == OpCode.LdFtn, - memberStatic: method.IsStatic, - memberDeclaringType: method.DeclaringType); - requireTarget = expressionBuilder.HidesVariableWithName(method.Name) - || (method.IsStatic ? !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) : !(target.Expression is ThisReferenceExpression)); - } var expectedTargetDetails = new ExpectedTargetDetails { - CallOpCode = inst.OpCode + CallOpCode = inst.OpCode, + IsLocalFunction = expressionBuilder.IsLocalFunction(method) }; - bool needsCast = false; ResolveResult result = null; - var or = new OverloadResolution(resolver.Compilation, method.Parameters.SelectReadOnlyArray(p => new TypeResolveResult(p.Type))); - if (!requireTarget) { - result = resolver.ResolveSimpleName(method.Name, method.TypeArguments, isInvocationTarget: false); - if (result is MethodGroupResolveResult mgrr) { - or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray()); - requireTarget = (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(expectedTargetDetails, method, or.BestCandidate)); - } else { + string methodName; + if (expectedTargetDetails.IsLocalFunction) { + requireTarget = false; + var localFunction = expressionBuilder.ResolveLocalFunction(method); + result = new LocalFunctionReferenceResolveResult(localFunction.Definition); + target = default; + methodName = localFunction.Name; + } else { + methodName = method.Name; + if (method.IsExtensionMethod && invokeMethod != null && method.Parameters.Count - 1 == invokeMethod.Parameters.Count) { + targetType = method.Parameters[0].Type; + if (targetType.Kind == TypeKind.ByReference && thisArg is Box thisArgBox) { + targetType = ((ByReferenceType)targetType).ElementType; + thisArg = thisArgBox.Argument; + } + target = expressionBuilder.Translate(thisArg, targetType); requireTarget = true; + } else { + targetType = method.DeclaringType; + if (targetType.IsReferenceType == false && thisArg is Box thisArgBox) { + // Normal struct instance method calls (which TranslateTarget is meant for) expect a 'ref T', + // but delegate construction uses a 'box T'. + if (thisArgBox.Argument is LdObj ldobj) { + thisArg = ldobj.Target; + } else { + thisArg = new AddressOf(thisArgBox.Argument); + } + } + target = expressionBuilder.TranslateTarget(thisArg, + nonVirtualInvocation: func.OpCode == OpCode.LdFtn, + memberStatic: method.IsStatic, + memberDeclaringType: method.DeclaringType); + requireTarget = expressionBuilder.HidesVariableWithName(method.Name) + || (method.IsStatic ? !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) : !(target.Expression is ThisReferenceExpression)); } - } - MemberLookup lookup = null; - if (requireTarget) { - lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule); - var rr = lookup.Lookup(target.ResolveResult, method.Name, method.TypeArguments, false) ; - needsCast = true; - result = rr; - if (rr is MethodGroupResolveResult mgrr) { - or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray()); - needsCast = (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(expectedTargetDetails, method, or.BestCandidate)); + var or = new OverloadResolution(resolver.Compilation, method.Parameters.SelectReadOnlyArray(p => new TypeResolveResult(p.Type))); + if (!requireTarget) { + result = resolver.ResolveSimpleName(method.Name, method.TypeArguments, isInvocationTarget: false); + if (result is MethodGroupResolveResult mgrr) { + or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray()); + requireTarget = (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(expectedTargetDetails, method, or.BestCandidate)); + } else { + requireTarget = true; + } + } + MemberLookup lookup = null; + bool needsCast = false; + if (requireTarget) { + lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule); + var rr = lookup.Lookup(target.ResolveResult, method.Name, method.TypeArguments, false); + needsCast = true; + result = rr; + if (rr is MethodGroupResolveResult mgrr) { + or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray()); + needsCast = (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(expectedTargetDetails, method, or.BestCandidate)); + } + } + if (needsCast) { + Debug.Assert(requireTarget); + target = target.ConvertTo(targetType, expressionBuilder); + result = lookup.Lookup(target.ResolveResult, method.Name, method.TypeArguments, false); } - } - if (needsCast) { - Debug.Assert(requireTarget); - target = target.ConvertTo(targetType, expressionBuilder); - result = lookup.Lookup(target.ResolveResult, method.Name, method.TypeArguments, false); } Expression targetExpression; if (requireTarget) { - var mre = new MemberReferenceExpression(target, method.Name); + var mre = new MemberReferenceExpression(target, methodName); mre.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType)); mre.WithRR(result); targetExpression = mre; } else { - var ide = new IdentifierExpression(method.Name) + var ide = new IdentifierExpression(methodName) .WithRR(result); targetExpression = ide; } @@ -1308,7 +1322,7 @@ namespace ICSharpCode.Decompiler.CSharp .WithILInstruction(inst) .WithRR(new ConversionResolveResult( inst.Method.DeclaringType, - new MemberResolveResult(target.ResolveResult, method), + target.ResolveResult != null ? new MemberResolveResult(target.ResolveResult, method) : result, Conversion.MethodGroupConversion(method, func.OpCode == OpCode.LdVirtFtn, false))); return oce; } diff --git a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs index 5a332fa76..4b03fcfe8 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs @@ -169,7 +169,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// Replaces loads of 'this' with the target expression. /// Async delegates use: ldobj(ldloca this). /// - class ReplaceDelegateTargetVisitor : ILVisitor + internal class ReplaceDelegateTargetVisitor : ILVisitor { readonly ILVariable thisVariable; readonly ILInstruction target; diff --git a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs index 9b69af499..b8f1e99fc 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Metadata; @@ -42,33 +43,46 @@ namespace ICSharpCode.Decompiler.IL.Transforms return; this.context = context; this.decompilationContext = new SimpleTypeResolveContext(function.Method); - var localFunctions = new Dictionary>(); + var localFunctions = new Dictionary>(); var cancellationToken = context.CancellationToken; // Find use-sites foreach (var inst in function.Descendants) { cancellationToken.ThrowIfCancellationRequested(); - if (inst is Call call && IsLocalFunctionMethod(call.Method)) { - context.StepStartGroup($"LocalFunctionDecompiler {call.StartILOffset}", call); + if (inst is CallInstruction call && IsLocalFunctionMethod(call.Method)) { + if (function.Ancestors.OfType().Any(f => f.LocalFunctions.ContainsKey(call.Method))) + continue; if (!localFunctions.TryGetValue(call.Method, out var info)) { - info = new List() { call }; + info = new List() { call }; localFunctions.Add(call.Method, info); } else { info.Add(call); } + } else if (inst is LdFtn ldftn && ldftn.Parent is NewObj newObj && IsLocalFunctionMethod(ldftn.Method) && DelegateConstruction.IsDelegateConstruction(newObj)) { + if (function.Ancestors.OfType().Any(f => f.LocalFunctions.ContainsKey(ldftn.Method))) + continue; + context.StepStartGroup($"LocalFunctionDecompiler {ldftn.StartILOffset}", ldftn); + if (!localFunctions.TryGetValue(ldftn.Method, out var info)) { + info = new List() { newObj }; + localFunctions.Add(ldftn.Method, info); + } else { + info.Add(newObj); + } context.StepEndGroup(); } } foreach (var (method, useSites) in localFunctions) { var insertionPoint = FindInsertionPoint(useSites); - ILFunction localFunction = TransformLocalFunction(method, (Block)insertionPoint.Parent, insertionPoint.ChildIndex + 1); - if (localFunction == null) - continue; - function.LocalFunctions.Add(localFunction.Method, (localFunction.Method.Name, localFunction)); + context.StepStartGroup($"LocalFunctionDecompiler {insertionPoint.StartILOffset}", insertionPoint); + try { + TransformLocalFunction(function, method, useSites, (Block)insertionPoint.Parent, insertionPoint.ChildIndex + 1); + } finally { + context.StepEndGroup(); + } } } - static ILInstruction FindInsertionPoint(List useSites) + static ILInstruction FindInsertionPoint(List useSites) { ILInstruction insertionPoint = null; foreach (var call in useSites) { @@ -85,14 +99,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms insertionPoint = ancestor; } - switch (insertionPoint) { - case BlockContainer bc: - return insertionPoint; - case Block b: - return insertionPoint; - default: - return insertionPoint; - } + return insertionPoint; } static ILInstruction FindCommonAncestorInstruction(ILInstruction a, ILInstruction b) @@ -117,7 +124,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return inst; } - private ILFunction TransformLocalFunction(IMethod targetMethod, Block parent, int insertionPoint) + private ILFunction TransformLocalFunction(ILFunction parentFunction, IMethod targetMethod, List useSites, Block parent, int insertionPoint) { var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken); if (!methodDefinition.HasBody()) @@ -132,13 +139,55 @@ namespace ICSharpCode.Decompiler.IL.Transforms // how the local function body is being transformed. parent.Instructions.Insert(insertionPoint, function); function.CheckInvariant(ILPhase.Normal); - var nestedContext = new ILTransformContext(context, function); function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is LocalFunctionDecompiler)), nestedContext); + if (IsNonLocalTarget(targetMethod, useSites, out var target)) { + Debug.Assert(target != null); + nestedContext.Step("LocalFunctionDecompiler (ReplaceDelegateTargetVisitor)", function); + var thisVar = function.Variables.SingleOrDefault(v => v.Index == -1 && v.Kind == VariableKind.Parameter); + function.AcceptVisitor(new DelegateConstruction.ReplaceDelegateTargetVisitor(target, thisVar)); + } + parentFunction.LocalFunctions.Add(function.Method, (function.Method.Name, function)); + // handle nested functions + nestedContext.StepStartGroup("LocalFunctionDecompiler (nested functions)", function); + new LocalFunctionDecompiler().Run(function, nestedContext); + nestedContext.StepEndGroup(); return function; } + bool IsNonLocalTarget(IMethod targetMethod, List useSites, out ILInstruction target) + { + target = null; + if (targetMethod.IsStatic) + return false; + ValidateUseSites(useSites); + target = useSites.Select(call => call.Arguments.First()).First(); + return !target.MatchLdThis(); + } + + [Conditional("DEBUG")] + static void ValidateUseSites(List useSites) + { + ILInstruction targetInstruction = null; + foreach (var site in useSites) { + if (targetInstruction == null) + targetInstruction = site.Arguments.First(); + else + Debug.Assert(targetInstruction.Match(site.Arguments[0]).Success); + } + } + + internal static bool IsLocalFunctionReference(NewObj inst) + { + if (inst == null || inst.Arguments.Count != 2 || inst.Method.DeclaringType.Kind != TypeKind.Delegate) + return false; + var opCode = inst.Arguments[1].OpCode; + + return (opCode == OpCode.LdFtn || opCode == OpCode.LdVirtFtn) + && IsLocalFunctionMethod(((IInstructionWithMethodOperand)inst.Arguments[1]).Method); + } + public static bool IsLocalFunctionMethod(IMethod method) { return IsLocalFunctionMethod(method.ParentModule.PEFile, (MethodDefinitionHandle)method.MetadataToken); diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index 9034a8fdd..f5d63ac10 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -27,7 +27,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// Transforms closure fields to local variables. /// - /// This is a post-processing step of and . + /// This is a post-processing step of , and . /// class TransformDisplayClassUsage : ILVisitor, IILTransform { @@ -68,7 +68,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (IsClosure(v, out ITypeDefinition closureType, out var inst)) { AddOrUpdateDisplayClass(f, v, closureType, inst); } - if (v.Kind == VariableKind.Parameter && v.Index > -1 && f.Method.Parameters[v.Index ?? -1] is IParameter p && LocalFunctionDecompiler.IsClosureParameter(p)) { + if (f.Kind == ILFunctionKind.LocalFunction && v.Kind == VariableKind.Parameter && v.Index > -1 && f.Method.Parameters[v.Index.Value] is IParameter p && LocalFunctionDecompiler.IsClosureParameter(p)) { AddOrUpdateDisplayClass(f, v, ((ByReferenceType)p.Type).ElementType.GetDefinition(), f.Body); } } @@ -124,9 +124,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } closureType = variable.Type.GetDefinition(); - if (closureType?.Kind == TypeKind.Struct && variable.HasInitialValue) { + if (closureType?.Kind == TypeKind.Struct && variable.HasInitialValue && IsPotentialClosure(this.context, closureType)) { initializer = LocalFunctionDecompiler.GetStatement(variable.AddressInstructions.OrderBy(i => i.StartILOffset).First()); - return IsPotentialClosure(this.context, closureType); + return true; } return false; } From b42d4963e7be04a23a8039c04c4a7b8534d87bc4 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 14 Jul 2019 16:22:26 +0200 Subject: [PATCH 052/221] Fix assertions in TransformExpressionTrees. --- ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs | 2 ++ .../IL/Transforms/TransformExpressionTrees.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs index e0c61ead1..de6883a4a 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs @@ -144,6 +144,7 @@ namespace ICSharpCode.Decompiler.IL case ILFunctionKind.TopLevelFunction: Debug.Assert(Parent == null); Debug.Assert(DelegateType == null); + Debug.Assert(Method != null); break; case ILFunctionKind.Delegate: Debug.Assert(Parent != null && !(Parent is Block)); @@ -158,6 +159,7 @@ namespace ICSharpCode.Decompiler.IL case ILFunctionKind.LocalFunction: Debug.Assert(Parent is Block); Debug.Assert(DelegateType == null); + Debug.Assert(Method != null); break; } for (int i = 0; i < Variables.Count; i++) { diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs index 3487977ee..5d6794bf9 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs @@ -156,6 +156,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms var returnType = functionType.GetDelegateInvokeMethod()?.ReturnType; var function = new ILFunction(returnType, parameterList, context.Function.GenericContext, container); function.DelegateType = functionType; + function.Kind = IsExpressionTree(functionType) ? ILFunctionKind.ExpressionTree : ILFunctionKind.Delegate; function.Variables.AddRange(parameterVariablesList); function.AddILRange(instruction); lambdaStack.Push(function); @@ -343,6 +344,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms case ILFunction function: if (function.Kind == ILFunctionKind.ExpressionTree) { function.DelegateType = UnwrapExpressionTree(function.DelegateType); + function.Kind = ILFunctionKind.Delegate; } return (function, function.DelegateType); case LdLoc ldloc: From 89a50e64fccda5a7d74269def436340e037c27fe Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 15 Jul 2019 10:06:59 +0200 Subject: [PATCH 053/221] Extension method syntax on lambda expressions/delegates is not allowed. --- .../TestCases/Pretty/DelegateConstruction.cs | 16 ++++++++++++++++ .../Transforms/IntroduceExtensionMethods.cs | 2 ++ 2 files changed, 18 insertions(+) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs index 1a748024d..3fd129fa1 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs @@ -159,6 +159,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { } + public static Predicate And(this Predicate filter1, Predicate filter2) + { + if (filter1 == null) { + return filter2; + } + if (filter2 == null) { + return filter1; + } + return (T m) => filter1(m) && filter2(m); + } + public static Action ExtensionMethodUnbound() { return Test; @@ -174,6 +185,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return ((string)null).Test; } + public static Predicate NoExtensionMethodOnLambda() + { + return And((int x) => x >= 0, (int x) => x <= 100); + } + public static object StaticMethod() { return new Func(ExtensionMethodBound); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs index e15d0d765..3b11cd87f 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs @@ -162,6 +162,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms public static bool CanTransformToExtensionMethodCall(CSharpResolver resolver, IMethod method, IReadOnlyList typeArguments, ResolveResult target, ResolveResult[] arguments, string[] argumentNames) { + if (target is LambdaResolveResult) + return false; var rr = resolver.ResolveMemberAccess(target, method.Name, typeArguments, NameLookupMode.InvocationTarget) as MethodGroupResolveResult; if (rr == null) return false; From 305b47245e1739eef244d10c87f69c1f52d9a3f0 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 15 Jul 2019 10:10:40 +0200 Subject: [PATCH 054/221] Refactor representation of local functions in ILAst. --- .../CSharp/CSharpDecompiler.cs | 22 +++++ ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 8 +- .../CSharp/ExpressionBuilder.cs | 13 +-- .../CSharp/StatementBuilder.cs | 60 ++++++++++---- ICSharpCode.Decompiler/IL/ILVariable.cs | 3 +- ICSharpCode.Decompiler/IL/Instructions.cs | 47 ++++++----- ICSharpCode.Decompiler/IL/Instructions.tt | 14 ++-- .../IL/Instructions/ILFunction.cs | 34 ++++++-- .../IL/Transforms/AssignVariableNames.cs | 14 ++-- .../IL/Transforms/LocalFunctionDecompiler.cs | 82 +++++++++++-------- .../Transforms/TransformDisplayClassUsage.cs | 20 ++--- 11 files changed, 207 insertions(+), 110 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index f8e8b1a72..c7c5ff137 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -687,6 +687,28 @@ namespace ICSharpCode.Decompiler.CSharp connectedMethods.Enqueue((MethodDefinitionHandle)token); } break; + case ILOpCode.Call: + case ILOpCode.Callvirt: + // deal with call/callvirt instructions, i.e., local function invocations + token = MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32()); + if (token.IsNil) + continue; + switch (token.Kind) { + case HandleKind.MethodDefinition: + break; + case HandleKind.MethodSpecification: + var methodSpec = module.Metadata.GetMethodSpecification((MethodSpecificationHandle)token); + if (methodSpec.Method.IsNil || methodSpec.Method.Kind != HandleKind.MethodDefinition) + continue; + token = methodSpec.Method; + break; + default: + continue; + } + if (LocalFunctionDecompiler.IsLocalFunctionMethod(module, (MethodDefinitionHandle)token)) { + connectedMethods.Enqueue((MethodDefinitionHandle)token); + } + break; default: blob.SkipOperand(code); break; diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 489a12f1c..7ca971359 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -183,8 +183,8 @@ namespace ICSharpCode.Decompiler.CSharp CallOpCode = callOpCode, IsLocalFunction = expressionBuilder.IsLocalFunction(method) }; - (string Name, ILFunction Definition) localFunction = default; - if (expectedTargetDetails.IsLocalFunction && (localFunction = expressionBuilder.ResolveLocalFunction(method)).Definition == null) { + ILFunction localFunction = null; + if (expectedTargetDetails.IsLocalFunction && (localFunction = expressionBuilder.ResolveLocalFunction(method)) == null) { expectedTargetDetails.IsLocalFunction = false; } TranslatedExpression target; @@ -193,7 +193,7 @@ namespace ICSharpCode.Decompiler.CSharp } else if (expectedTargetDetails.IsLocalFunction) { target = new IdentifierExpression(localFunction.Name) .WithoutILInstruction() - .WithRR(new LocalFunctionReferenceResolveResult(localFunction.Definition)); + .WithRR(new LocalFunctionReferenceResolveResult(localFunction)); } else { target = expressionBuilder.TranslateTarget( callArguments.FirstOrDefault(), @@ -1248,7 +1248,7 @@ namespace ICSharpCode.Decompiler.CSharp if (expectedTargetDetails.IsLocalFunction) { requireTarget = false; var localFunction = expressionBuilder.ResolveLocalFunction(method); - result = new LocalFunctionReferenceResolveResult(localFunction.Definition); + result = new LocalFunctionReferenceResolveResult(localFunction); target = default; methodName = localFunction.Name; } else { diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 688475cdd..2c95aa686 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -198,8 +198,8 @@ namespace ICSharpCode.Decompiler.CSharp return true; } - foreach (var (key, (functionName, definition)) in function.LocalFunctions) { - if (functionName == name) + foreach (var f in function.LocalFunctions.OfType()) { + if (f.Name == name) return true; } @@ -212,15 +212,16 @@ namespace ICSharpCode.Decompiler.CSharp return settings.LocalFunctions && IL.Transforms.LocalFunctionDecompiler.IsLocalFunctionMethod(method); } - internal (string Name, ILFunction Definition) ResolveLocalFunction(IMethod method) + internal ILFunction ResolveLocalFunction(IMethod method) { Debug.Assert(IsLocalFunction(method)); foreach (var parent in currentFunction.Ancestors.OfType()) { - if (parent.LocalFunctions.TryGetValue(method, out var info)) { - return info; + var definition = parent.LocalFunctions.FirstOrDefault(f => f.Method == method); + if (definition != null) { + return definition; } } - return (null, null); + return null; } bool RequiresQualifier(IMember member, TranslatedExpression target) diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index 759bbe8a9..73b07c70b 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -733,11 +733,24 @@ namespace ICSharpCode.Decompiler.CSharp { if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(usingContainer))) return false; - if (storeInst.Variable.AddressInstructions.Any(la => !la.IsDescendantOf(usingContainer) || !ILInlining.IsUsedAsThisPointerInCall(la) || IsTargetOfSetterCall(la, la.Variable.Type))) + if (storeInst.Variable.AddressInstructions.Any(inst => !AddressUseAllowed(inst))) return false; if (storeInst.Variable.StoreInstructions.OfType().Any(st => st != storeInst)) return false; return true; + + bool AddressUseAllowed(LdLoca la) + { + if (!la.IsDescendantOf(usingContainer)) + return false; + if (ILInlining.IsUsedAsThisPointerInCall(la) && !IsTargetOfSetterCall(la, la.Variable.Type)) + return true; + var current = la.Parent; + while (current is LdFlda next) { + current = next.Parent; + } + return current is LdObj; + } } /// @@ -850,6 +863,7 @@ namespace ICSharpCode.Decompiler.CSharp if (blockStatement.LastOrDefault() is ContinueStatement continueStmt) continueStmt.Remove(); + DeclareLocalFunctions(currentFunction, container, blockStatement); return new WhileStatement(new PrimitiveExpression(true), blockStatement); case ContainerKind.While: continueTarget = container.EntryPoint; @@ -871,6 +885,7 @@ namespace ICSharpCode.Decompiler.CSharp if (blockStatement.LastOrDefault() is ContinueStatement continueStmt2) continueStmt2.Remove(); + DeclareLocalFunctions(currentFunction, container, blockStatement); return new WhileStatement(exprBuilder.TranslateCondition(condition), blockStatement); case ContainerKind.DoWhile: continueTarget = container.Blocks.Last(); @@ -889,6 +904,7 @@ namespace ICSharpCode.Decompiler.CSharp // to continue statements, we have to introduce an extra label. blockStatement.Add(new LabelStatement { Label = continueTarget.Label }); } + DeclareLocalFunctions(currentFunction, container, blockStatement); if (blockStatement.Statements.Count == 0) { return new WhileStatement { Condition = exprBuilder.TranslateCondition(condition), @@ -920,6 +936,7 @@ namespace ICSharpCode.Decompiler.CSharp } if (continueTarget.IncomingEdgeCount > continueCount) blockStatement.Add(new LabelStatement { Label = continueTarget.Label }); + DeclareLocalFunctions(currentFunction, container, blockStatement); return forStmt; default: throw new ArgumentOutOfRangeException(); @@ -928,7 +945,31 @@ namespace ICSharpCode.Decompiler.CSharp BlockStatement ConvertBlockContainer(BlockContainer container, bool isLoop) { - return ConvertBlockContainer(new BlockStatement(), container, container.Blocks, isLoop); + var blockStatement = ConvertBlockContainer(new BlockStatement(), container, container.Blocks, isLoop); + DeclareLocalFunctions(currentFunction, container, blockStatement); + return blockStatement; + } + + void DeclareLocalFunctions(ILFunction currentFunction, BlockContainer container, BlockStatement blockStatement) + { + foreach (var localFunction in currentFunction.LocalFunctions) { + if (localFunction.DeclarationScope != container) + continue; + blockStatement.Add(TranslateFunction(localFunction)); + } + + LocalFunctionDeclarationStatement TranslateFunction(ILFunction function) + { + var stmt = new LocalFunctionDeclarationStatement(); + var tsab = CSharpDecompiler.CreateAstBuilder(null); + var nestedBuilder = new StatementBuilder(typeSystem, exprBuilder.decompilationContext, function, settings, cancellationToken); + stmt.Name = function.Name; + stmt.Parameters.AddRange(exprBuilder.MakeParameters(function.Parameters, function)); + stmt.ReturnType = tsab.ConvertType(function.Method.ReturnType); + stmt.Body = nestedBuilder.ConvertAsBlock(function.Body); + stmt.AddAnnotation(new LocalFunctionReferenceResolveResult(function)); + return stmt; + } } BlockStatement ConvertBlockContainer(BlockStatement blockStatement, BlockContainer container, IEnumerable blocks, bool isLoop) @@ -1016,20 +1057,5 @@ namespace ICSharpCode.Decompiler.CSharp stmt.InsertChildAfter(null, new Comment(" IL cpblk instruction"), Roles.Comment); return stmt; } - - protected internal override Statement VisitILFunction(ILFunction function) - { - var stmt = new LocalFunctionDeclarationStatement(); - var tsab = CSharpDecompiler.CreateAstBuilder(null); - var nestedBuilder = new StatementBuilder(typeSystem, exprBuilder.decompilationContext, function, settings, cancellationToken); - var localFunction = exprBuilder.ResolveLocalFunction(function.Method); - Debug.Assert(localFunction.Definition == function); - stmt.Name = localFunction.Name; - stmt.Parameters.AddRange(exprBuilder.MakeParameters(function.Parameters, function)); - stmt.ReturnType = tsab.ConvertType(function.Method.ReturnType); - stmt.Body = nestedBuilder.ConvertAsBlock(function.Body); - stmt.AddAnnotation(new LocalFunctionReferenceResolveResult(function)); - return stmt; - } } } diff --git a/ICSharpCode.Decompiler/IL/ILVariable.cs b/ICSharpCode.Decompiler/IL/ILVariable.cs index 8a0e5fd17..1983089df 100644 --- a/ICSharpCode.Decompiler/IL/ILVariable.cs +++ b/ICSharpCode.Decompiler/IL/ILVariable.cs @@ -415,7 +415,8 @@ namespace ICSharpCode.Decompiler.IL output.Write(" init"); } if (CaptureScope != null) { - output.Write(" captured in " + CaptureScope.EntryPoint.Label); + output.Write(" captured in "); + output.WriteLocalReference(CaptureScope.EntryPoint.Label, CaptureScope); } if (StateMachineField != null) { output.Write(" from state-machine"); diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index 27e0af93d..6c41e3aa6 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -476,7 +476,7 @@ namespace ICSharpCode.Decompiler.IL { switch (index) { default: - this.Arguments[index - 0] = value; + this.Arguments[index - 0] = (ILInstruction)value; break; } } @@ -491,7 +491,7 @@ namespace ICSharpCode.Decompiler.IL { var clone = (CallInstruction)ShallowClone(); clone.Arguments = new InstructionCollection(clone, 0); - clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone())); + clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone())); return clone; } protected override InstructionFlags ComputeFlags() @@ -742,9 +742,11 @@ namespace ICSharpCode.Decompiler.IL SetChildInstruction(ref this.body, value, 0); } } + public static readonly SlotInfo LocalFunctionsSlot = new SlotInfo("LocalFunctions"); + public InstructionCollection LocalFunctions { get; private set; } protected sealed override int GetChildCount() { - return 1; + return 1 + LocalFunctions.Count; } protected sealed override ILInstruction GetChild(int index) { @@ -752,7 +754,7 @@ namespace ICSharpCode.Decompiler.IL case 0: return this.body; default: - throw new IndexOutOfRangeException(); + return this.LocalFunctions[index - 1]; } } protected sealed override void SetChild(int index, ILInstruction value) @@ -762,7 +764,8 @@ namespace ICSharpCode.Decompiler.IL this.Body = value; break; default: - throw new IndexOutOfRangeException(); + this.LocalFunctions[index - 1] = (ILFunction)value; + break; } } protected sealed override SlotInfo GetChildSlot(int index) @@ -771,13 +774,15 @@ namespace ICSharpCode.Decompiler.IL case 0: return BodySlot; default: - throw new IndexOutOfRangeException(); + return LocalFunctionsSlot; } } public sealed override ILInstruction Clone() { var clone = (ILFunction)ShallowClone(); clone.Body = this.body.Clone(); + clone.LocalFunctions = new InstructionCollection(clone, 1); + clone.LocalFunctions.AddRange(this.LocalFunctions.Select(arg => (ILFunction)arg.Clone())); clone.CloneVariables(); return clone; } @@ -797,7 +802,7 @@ namespace ICSharpCode.Decompiler.IL protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) { var o = other as ILFunction; - return o != null && this.body.PerformMatch(o.body, ref match); + return o != null && this.body.PerformMatch(o.body, ref match) && Patterns.ListMatch.DoMatch(this.LocalFunctions, o.LocalFunctions, ref match); } } } @@ -4254,7 +4259,7 @@ namespace ICSharpCode.Decompiler.IL { switch (index) { default: - this.Indices[index - 0] = value; + this.Indices[index - 0] = (ILInstruction)value; break; } } @@ -4269,7 +4274,7 @@ namespace ICSharpCode.Decompiler.IL { var clone = (NewArr)ShallowClone(); clone.Indices = new InstructionCollection(clone, 0); - clone.Indices.AddRange(this.Indices.Select(arg => arg.Clone())); + clone.Indices.AddRange(this.Indices.Select(arg => (ILInstruction)arg.Clone())); return clone; } public override StackType ResultType { get { return StackType.O; } } @@ -4607,7 +4612,7 @@ namespace ICSharpCode.Decompiler.IL this.Array = value; break; default: - this.Indices[index - 1] = value; + this.Indices[index - 1] = (ILInstruction)value; break; } } @@ -4625,7 +4630,7 @@ namespace ICSharpCode.Decompiler.IL var clone = (LdElema)ShallowClone(); clone.Array = this.array.Clone(); clone.Indices = new InstructionCollection(clone, 1); - clone.Indices.AddRange(this.Indices.Select(arg => arg.Clone())); + clone.Indices.AddRange(this.Indices.Select(arg => (ILInstruction)arg.Clone())); return clone; } public bool DelayExceptions; // NullReferenceException/IndexOutOfBoundsException only occurs when the reference is dereferenced @@ -5578,7 +5583,7 @@ namespace ICSharpCode.Decompiler.IL { switch (index) { default: - this.Arguments[index - 0] = value; + this.Arguments[index - 0] = (ILInstruction)value; break; } } @@ -5593,7 +5598,7 @@ namespace ICSharpCode.Decompiler.IL { var clone = (DynamicGetIndexInstruction)ShallowClone(); clone.Arguments = new InstructionCollection(clone, 0); - clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone())); + clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone())); return clone; } protected override InstructionFlags ComputeFlags() @@ -5646,7 +5651,7 @@ namespace ICSharpCode.Decompiler.IL { switch (index) { default: - this.Arguments[index - 0] = value; + this.Arguments[index - 0] = (ILInstruction)value; break; } } @@ -5661,7 +5666,7 @@ namespace ICSharpCode.Decompiler.IL { var clone = (DynamicSetIndexInstruction)ShallowClone(); clone.Arguments = new InstructionCollection(clone, 0); - clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone())); + clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone())); return clone; } protected override InstructionFlags ComputeFlags() @@ -5714,7 +5719,7 @@ namespace ICSharpCode.Decompiler.IL { switch (index) { default: - this.Arguments[index - 0] = value; + this.Arguments[index - 0] = (ILInstruction)value; break; } } @@ -5729,7 +5734,7 @@ namespace ICSharpCode.Decompiler.IL { var clone = (DynamicInvokeMemberInstruction)ShallowClone(); clone.Arguments = new InstructionCollection(clone, 0); - clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone())); + clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone())); return clone; } protected override InstructionFlags ComputeFlags() @@ -5782,7 +5787,7 @@ namespace ICSharpCode.Decompiler.IL { switch (index) { default: - this.Arguments[index - 0] = value; + this.Arguments[index - 0] = (ILInstruction)value; break; } } @@ -5797,7 +5802,7 @@ namespace ICSharpCode.Decompiler.IL { var clone = (DynamicInvokeConstructorInstruction)ShallowClone(); clone.Arguments = new InstructionCollection(clone, 0); - clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone())); + clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone())); return clone; } protected override InstructionFlags ComputeFlags() @@ -5850,7 +5855,7 @@ namespace ICSharpCode.Decompiler.IL { switch (index) { default: - this.Arguments[index - 0] = value; + this.Arguments[index - 0] = (ILInstruction)value; break; } } @@ -5865,7 +5870,7 @@ namespace ICSharpCode.Decompiler.IL { var clone = (DynamicInvokeInstruction)ShallowClone(); clone.Arguments = new InstructionCollection(clone, 0); - clone.Arguments.AddRange(this.Arguments.Select(arg => arg.Clone())); + clone.Arguments.AddRange(this.Arguments.Select(arg => (ILInstruction)arg.Clone())); return clone; } protected override InstructionFlags ComputeFlags() diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index 4d63cc974..a6c1d13f5 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -49,7 +49,8 @@ VoidResult, NoArguments, CustomWriteTo), new OpCode("ILFunction", "A container of IL blocks.", CustomChildren(new [] { - new ChildInfo("body") + new ChildInfo("body"), + new ChildInfo("localFunctions") { IsCollection = true, Type = "ILFunction" } }), CustomConstructor, CustomWriteTo, CustomComputeFlags, CustomVariableName("function"), ResultType("O") ), new OpCode("BlockContainer", "A container of IL blocks.", @@ -769,6 +770,7 @@ namespace ICSharpCode.Decompiler.IL public readonly string SlotName; public bool IsCollection; + public string Type = "ILInstruction"; public bool CanInlineInto; public string[] ExpectedTypes; @@ -814,7 +816,7 @@ namespace ICSharpCode.Decompiler.IL childCount = children.Length - 1; opCode.Flags.Add(argProp + ".Aggregate(InstructionFlags.None, (f, arg) => f | arg.Flags)"); opCode.ConstructorParameters.Add("params ILInstruction[] " + arg); - opCode.ConstructorBody.Add("this." + argProp + " = new InstructionCollection(this, " + i + ");"); + opCode.ConstructorBody.Add("this." + argProp + " = new InstructionCollection<" + children[i].Type + ">(this, " + i + ");"); opCode.ConstructorBody.Add("this." + argProp + ".AddRange(" + arg + ");"); opCode.PerformMatchConditions.Add("Patterns.ListMatch.DoMatch(this." + argProp + ", o." + argProp + ", ref match)"); if (i == 0) @@ -827,7 +829,7 @@ namespace ICSharpCode.Decompiler.IL opCode.WriteArguments.Add("\t" + arg + ".WriteTo(output, options);"); opCode.WriteArguments.Add("}"); opCode.Members.Add("public static readonly SlotInfo " + children[i].SlotName + " = " + children[i].GetSlotInit() + ";"); - opCode.Members.Add("public InstructionCollection " + argProp + " { get; private set; }"); + opCode.Members.Add("public InstructionCollection<" + children[i].Type + "> " + argProp + " { get; private set; }"); } else { opCode.Flags.Add(arg + ".Flags"); opCode.ConstructorParameters.Add("ILInstruction " + arg); @@ -906,7 +908,7 @@ namespace ICSharpCode.Decompiler.IL if (collection == null) b.AppendLine("\t\t\tthrow new IndexOutOfRangeException();"); else { - b.AppendLine("\t\t\tthis." + collection.PropertyName + "[index - " + childCount + "] = value;"); + b.AppendLine("\t\t\tthis." + collection.PropertyName + "[index - " + childCount + "] = (" + collection.Type + ")value;"); b.AppendLine("\t\t\tbreak;"); } b.AppendLine("\t}"); @@ -936,8 +938,8 @@ namespace ICSharpCode.Decompiler.IL b.AppendLine("\tvar clone = (" + opCode.Name + ")ShallowClone();"); for (int i = 0; i < children.Length; i++) { if (children[i].IsCollection) { - b.AppendLine("\tclone." + children[i].PropertyName + " = new InstructionCollection(clone, " + i + ");"); - b.AppendLine("\tclone." + children[i].PropertyName + ".AddRange(this." + children[i].PropertyName + ".Select(arg => arg.Clone()));"); + b.AppendLine("\tclone." + children[i].PropertyName + " = new InstructionCollection<" + children[i].Type + ">(clone, " + i + ");"); + b.AppendLine("\tclone." + children[i].PropertyName + ".AddRange(this." + children[i].PropertyName + ".Select(arg => (" + children[i].Type + ")arg.Clone()));"); } else { b.AppendLine("\tclone." + children[i].PropertyName + " = this." + children[i].Name + ".Clone();"); } diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs index de6883a4a..95eadfcb0 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs @@ -18,11 +18,12 @@ using System; using System.Collections.Generic; -using ICSharpCode.Decompiler.IL.Transforms; +using System.Diagnostics; using System.Linq; + +using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; -using System.Diagnostics; namespace ICSharpCode.Decompiler.IL { @@ -30,6 +31,8 @@ namespace ICSharpCode.Decompiler.IL { public readonly IMethod Method; public readonly GenericContext GenericContext; + public string Name; + /// /// Size of the IL code in this function. /// Note: after async/await transform, this is the code size of the MoveNext function. @@ -37,6 +40,12 @@ namespace ICSharpCode.Decompiler.IL public int CodeSize; public readonly ILVariableCollection Variables; + /// + /// Gets the scope in which the local function is declared. + /// Returns null, if this is not a local function. + /// + public BlockContainer DeclarationScope { get; internal set; } + /// /// List of warnings of ILReader. /// @@ -110,8 +119,6 @@ namespace ICSharpCode.Decompiler.IL } } - public Dictionary LocalFunctions { get; } = new Dictionary(); - public readonly IType ReturnType; public readonly IReadOnlyList Parameters; @@ -119,12 +126,14 @@ namespace ICSharpCode.Decompiler.IL public ILFunction(IMethod method, int codeSize, GenericContext genericContext, ILInstruction body, ILFunctionKind kind = ILFunctionKind.TopLevelFunction) : base(OpCode.ILFunction) { this.Method = method; + this.Name = method.Name; this.CodeSize = codeSize; this.GenericContext = genericContext; this.Body = body; this.ReturnType = Method?.ReturnType; this.Parameters = Method?.Parameters; this.Variables = new ILVariableCollection(this); + this.LocalFunctions = new InstructionCollection(this, 1); this.kind = kind; } @@ -135,6 +144,7 @@ namespace ICSharpCode.Decompiler.IL this.ReturnType = returnType; this.Parameters = parameters; this.Variables = new ILVariableCollection(this); + this.LocalFunctions = new InstructionCollection(this, 1); this.kind = ILFunctionKind.ExpressionTree; } @@ -144,20 +154,24 @@ namespace ICSharpCode.Decompiler.IL case ILFunctionKind.TopLevelFunction: Debug.Assert(Parent == null); Debug.Assert(DelegateType == null); + Debug.Assert(DeclarationScope == null); Debug.Assert(Method != null); break; case ILFunctionKind.Delegate: Debug.Assert(Parent != null && !(Parent is Block)); Debug.Assert(DelegateType != null); + Debug.Assert(DeclarationScope == null); Debug.Assert(!(DelegateType?.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1)); break; case ILFunctionKind.ExpressionTree: Debug.Assert(Parent != null && !(Parent is Block)); Debug.Assert(DelegateType != null); + Debug.Assert(DeclarationScope == null); Debug.Assert(DelegateType?.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1); break; case ILFunctionKind.LocalFunction: - Debug.Assert(Parent is Block); + Debug.Assert(Parent is ILFunction); + Debug.Assert(DeclarationScope != null); Debug.Assert(DelegateType == null); Debug.Assert(Method != null); break; @@ -205,6 +219,11 @@ namespace ICSharpCode.Decompiler.IL if (IsIterator) { output.WriteLine(".iterator"); } + if (DeclarationScope != null) { + output.Write("declared as " + Name + " in "); + output.WriteLocalReference(DeclarationScope.EntryPoint.Label, DeclarationScope); + output.WriteLine(); + } output.MarkFoldStart(Variables.Count + " variable(s)", true); foreach (var variable in Variables) { @@ -221,6 +240,11 @@ namespace ICSharpCode.Decompiler.IL body.WriteTo(output, options); output.WriteLine(); + foreach (var localFunction in LocalFunctions) { + output.WriteLine(); + localFunction.WriteTo(output, options); + } + if (options.ShowILRanges) { var unusedILRanges = FindUnusedILRanges(); if (!unusedILRanges.IsEmpty) { diff --git a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs index 5e377d221..71d4a545c 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs @@ -162,11 +162,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms break; } } - foreach (var method in function.LocalFunctions.Keys.ToArray()) { - var info = function.LocalFunctions[method]; - if (!LocalFunctionDecompiler.ParseLocalFunctionName(method.Name, out _, out var newName) || !IsValidName(newName)) + foreach (var localFunction in function.LocalFunctions) { + if (!LocalFunctionDecompiler.ParseLocalFunctionName(localFunction.Name, out _, out var newName) || !IsValidName(newName)) newName = null; - function.LocalFunctions[method] = (newName, info.Declaration); + localFunction.Name = newName; } // Now generate names: var mapping = new Dictionary(ILVariableEqualityComparer.Instance); @@ -180,13 +179,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms v.Name = name; } } - foreach (var method in function.LocalFunctions.Keys.ToArray()) { - var info = function.LocalFunctions[method]; - var newName = info.Name; + foreach (var localFunction in function.LocalFunctions) { + var newName = localFunction.Name; if (newName == null) { newName = GetAlternativeName("f"); } - function.LocalFunctions[method] = (newName, info.Declaration); + localFunction.Name = newName; } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs index b8f1e99fc..b65420b0f 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs @@ -49,7 +49,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms foreach (var inst in function.Descendants) { cancellationToken.ThrowIfCancellationRequested(); if (inst is CallInstruction call && IsLocalFunctionMethod(call.Method)) { - if (function.Ancestors.OfType().Any(f => f.LocalFunctions.ContainsKey(call.Method))) + if (function.Ancestors.OfType().SelectMany(f => f.LocalFunctions).Any(f => f.Method == call.Method)) continue; if (!localFunctions.TryGetValue(call.Method, out var info)) { info = new List() { call }; @@ -58,7 +58,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms info.Add(call); } } else if (inst is LdFtn ldftn && ldftn.Parent is NewObj newObj && IsLocalFunctionMethod(ldftn.Method) && DelegateConstruction.IsDelegateConstruction(newObj)) { - if (function.Ancestors.OfType().Any(f => f.LocalFunctions.ContainsKey(ldftn.Method))) + if (function.Ancestors.OfType().SelectMany(f => f.LocalFunctions).Any(f => f.Method == ldftn.Method)) continue; context.StepStartGroup($"LocalFunctionDecompiler {ldftn.StartILOffset}", ldftn); if (!localFunctions.TryGetValue(ldftn.Method, out var info)) { @@ -72,40 +72,40 @@ namespace ICSharpCode.Decompiler.IL.Transforms } foreach (var (method, useSites) in localFunctions) { - var insertionPoint = FindInsertionPoint(useSites); - context.StepStartGroup($"LocalFunctionDecompiler {insertionPoint.StartILOffset}", insertionPoint); + context.StepStartGroup($"LocalFunctionDecompiler {useSites[0].StartILOffset}", useSites[0]); try { - TransformLocalFunction(function, method, useSites, (Block)insertionPoint.Parent, insertionPoint.ChildIndex + 1); + TransformLocalFunction(function, method, useSites); } finally { context.StepEndGroup(); } } - } - - static ILInstruction FindInsertionPoint(List useSites) - { - ILInstruction insertionPoint = null; - foreach (var call in useSites) { - if (insertionPoint == null) { - insertionPoint = GetStatement(call); - continue; - } - - var ancestor = FindCommonAncestorInstruction(insertionPoint, GetStatement(call)); - if (ancestor == null) - return null; - - insertionPoint = ancestor; + foreach (var f in function.LocalFunctions) { + // handle nested functions + var nestedContext = new ILTransformContext(context, f); + nestedContext.StepStartGroup("LocalFunctionDecompiler (nested functions)", f); + new LocalFunctionDecompiler().Run(f, nestedContext); + nestedContext.StepEndGroup(); } - return insertionPoint; + if (function.Kind == ILFunctionKind.TopLevelFunction) { + var movableFunctions = TreeTraversal.PostOrder(function, f => f.LocalFunctions) + .Where(f => f.Kind == ILFunctionKind.LocalFunction && f.DeclarationScope == null) + .ToArray(); + foreach (var f in movableFunctions) { + var parent = (ILFunction)f.Parent; + f.DeclarationScope = (BlockContainer)function.Body; + parent.LocalFunctions.Remove(f); + function.LocalFunctions.Add(f); + } + } } - static ILInstruction FindCommonAncestorInstruction(ILInstruction a, ILInstruction b) + static T FindCommonAncestorInstruction(ILInstruction a, ILInstruction b) + where T : ILInstruction { - var ancestorsOfB = new HashSet(b.Ancestors); - return a.Ancestors.FirstOrDefault(ancestorsOfB.Contains); + var ancestorsOfB = new HashSet(b.Ancestors.OfType()); + return a.Ancestors.OfType().FirstOrDefault(ancestorsOfB.Contains); } internal static bool IsClosureParameter(IParameter parameter) @@ -124,7 +124,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return inst; } - private ILFunction TransformLocalFunction(ILFunction parentFunction, IMethod targetMethod, List useSites, Block parent, int insertionPoint) + private ILFunction TransformLocalFunction(ILFunction parentFunction, IMethod targetMethod, List useSites) { var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken); if (!methodDefinition.HasBody()) @@ -137,7 +137,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, ILFunctionKind.LocalFunction, context.CancellationToken); // Embed the local function into the parent function's ILAst, so that "Show steps" can show // how the local function body is being transformed. - parent.Instructions.Insert(insertionPoint, function); + parentFunction.LocalFunctions.Add(function); + function.DeclarationScope = (BlockContainer)parentFunction.Body; function.CheckInvariant(ILPhase.Normal); var nestedContext = new ILTransformContext(context, function); function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is LocalFunctionDecompiler)), nestedContext); @@ -147,11 +148,26 @@ namespace ICSharpCode.Decompiler.IL.Transforms var thisVar = function.Variables.SingleOrDefault(v => v.Index == -1 && v.Kind == VariableKind.Parameter); function.AcceptVisitor(new DelegateConstruction.ReplaceDelegateTargetVisitor(target, thisVar)); } - parentFunction.LocalFunctions.Add(function.Method, (function.Method.Name, function)); - // handle nested functions - nestedContext.StepStartGroup("LocalFunctionDecompiler (nested functions)", function); - new LocalFunctionDecompiler().Run(function, nestedContext); - nestedContext.StepEndGroup(); + function.DeclarationScope = null; + foreach (var useSite in useSites) { + for (int i = useSite.Arguments.Count - 1; i >= 0; i--) { + if (!useSite.Arguments[i].MatchLdLocRef(out var closureVar)) + break; + if (!TransformDisplayClassUsage.IsPotentialClosure(context, closureVar.Type.GetDefinition())) + break; + var instructions = closureVar.StoreInstructions.OfType() + .Concat(closureVar.AddressInstructions).OrderBy(inst => inst.StartILOffset); + var additionalScope = BlockContainer.FindClosestContainer(instructions.First()); + if (closureVar.CaptureScope == null) + closureVar.CaptureScope = additionalScope; + else + closureVar.CaptureScope = FindCommonAncestorInstruction(closureVar.CaptureScope, additionalScope); + if (function.DeclarationScope == null) + function.DeclarationScope = closureVar.CaptureScope; + else + function.DeclarationScope = FindCommonAncestorInstruction(function.DeclarationScope, closureVar.CaptureScope); + } + } return function; } @@ -190,6 +206,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms public static bool IsLocalFunctionMethod(IMethod method) { + if (method.MetadataToken.IsNil) + return false; return IsLocalFunctionMethod(method.ParentModule.PEFile, (MethodDefinitionHandle)method.MetadataToken); } diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index f5d63ac10..eb2d39d65 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -66,10 +66,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (HandleMonoStateMachine(function, v, decompilationContext, f)) continue; if (IsClosure(v, out ITypeDefinition closureType, out var inst)) { - AddOrUpdateDisplayClass(f, v, closureType, inst); + AddOrUpdateDisplayClass(f, v, closureType, inst, localFunctionClosureParameter: false); } - if (f.Kind == ILFunctionKind.LocalFunction && v.Kind == VariableKind.Parameter && v.Index > -1 && f.Method.Parameters[v.Index.Value] is IParameter p && LocalFunctionDecompiler.IsClosureParameter(p)) { - AddOrUpdateDisplayClass(f, v, ((ByReferenceType)p.Type).ElementType.GetDefinition(), f.Body); + if (context.Settings.LocalFunctions && f.Kind == ILFunctionKind.LocalFunction && v.Kind == VariableKind.Parameter && v.Index > -1 && f.Method.Parameters[v.Index.Value] is IParameter p && LocalFunctionDecompiler.IsClosureParameter(p)) { + AddOrUpdateDisplayClass(f, v, ((ByReferenceType)p.Type).ElementType.GetDefinition(), f.Body, localFunctionClosureParameter: true); } } foreach (var displayClass in displayClasses.Values.OrderByDescending(d => d.Initializer.StartILOffset).ToArray()) { @@ -91,21 +91,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } - private void AddOrUpdateDisplayClass(ILFunction f, ILVariable v, ITypeDefinition closureType, ILInstruction inst) + private void AddOrUpdateDisplayClass(ILFunction f, ILVariable v, ITypeDefinition closureType, ILInstruction inst, bool localFunctionClosureParameter) { var displayClass = displayClasses.Values.FirstOrDefault(c => c.Definition == closureType); + // TODO : figure out whether it is a mono compiled closure, without relying on the type name + bool isMono = f.StateMachineCompiledWithMono || closureType.Name.Contains("AnonStorey"); if (displayClass == null) { - // TODO : figure out whether it is a mono compiled closure, without relying on the type name - bool isMono = f.StateMachineCompiledWithMono || closureType.Name.Contains("AnonStorey"); displayClasses.Add(v, new DisplayClass { IsMono = isMono, Initializer = inst, Variable = v, Definition = closureType, Variables = new Dictionary(), - CaptureScope = isMono && IsMonoNestedCaptureScope(closureType) ? null : v.CaptureScope + CaptureScope = (isMono && IsMonoNestedCaptureScope(closureType)) || localFunctionClosureParameter ? null : v.CaptureScope }); } else { + if (displayClass.CaptureScope == null && !localFunctionClosureParameter) + displayClass.CaptureScope = isMono && IsMonoNestedCaptureScope(closureType) ? null : v.CaptureScope; displayClass.Variable = v; displayClass.Initializer = inst; displayClasses.Add(v, displayClass); @@ -124,7 +126,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } closureType = variable.Type.GetDefinition(); - if (closureType?.Kind == TypeKind.Struct && variable.HasInitialValue && IsPotentialClosure(this.context, closureType)) { + if (context.Settings.LocalFunctions && closureType?.Kind == TypeKind.Struct && variable.HasInitialValue && IsPotentialClosure(this.context, closureType)) { initializer = LocalFunctionDecompiler.GetStatement(variable.AddressInstructions.OrderBy(i => i.StartILOffset).First()); return true; } @@ -242,8 +244,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms { if (target.MatchLdLoc(out variable) || target.MatchLdLoca(out variable)) return true; - if (target.MatchAddressOf(out var load) && load.MatchLdLoc(out variable)) - return true; return false; } From 9f661f99f2343b88887c90a99c48afadcd95d394 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Mon, 15 Jul 2019 10:29:52 +0200 Subject: [PATCH 055/221] Add Microsoft.VisualStudio.SDK.Analyzers as per https://devblogs.microsoft.com/visualstudio/checklist-for-writing-great-visual-studio-extensions/ - Warnings not fixed --- ILSpy.AddIn/ILSpy.AddIn.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/ILSpy.AddIn/ILSpy.AddIn.csproj b/ILSpy.AddIn/ILSpy.AddIn.csproj index e61b4e2a5..1126fcf82 100644 --- a/ILSpy.AddIn/ILSpy.AddIn.csproj +++ b/ILSpy.AddIn/ILSpy.AddIn.csproj @@ -51,6 +51,7 @@ + From ec34ffd8d61b62587abb8bf825438364a18397dc Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 15 Jul 2019 11:45:51 +0200 Subject: [PATCH 056/221] ILSpy-tests submodule updated --- ILSpy-tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ILSpy-tests b/ILSpy-tests index 28f74a2a8..4d29b27bd 160000 --- a/ILSpy-tests +++ b/ILSpy-tests @@ -1 +1 @@ -Subproject commit 28f74a2a8f4050fe455a982dbd361a981841a9bd +Subproject commit 4d29b27bd7262790efe52edfcb9c100bb62a0382 From 6f98ed541536a2c829f7bc142c1cd26f2a026f37 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 15 Jul 2019 11:50:22 +0200 Subject: [PATCH 057/221] Fix unit tests. --- ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs | 4 ++-- ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs b/ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs index dea1e49f5..d6f969ec3 100644 --- a/ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs +++ b/ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs @@ -71,8 +71,8 @@ namespace ICSharpCode.Decompiler.Tests { try { RunWithTest("ICSharpCode.Decompiler", "ICSharpCode.Decompiler.dll", "ICSharpCode.Decompiler.Tests.exe"); - } catch (CompilationFailedException) { - Assert.Ignore("C# 7 local functions not yet supported."); + } catch (TestRunFailedException) { + Assert.Ignore("CorrectnessTestRunner.UndocumentedExpressions fails on recompilation."); } } diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs index 95eadfcb0..38747065c 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs @@ -126,7 +126,7 @@ namespace ICSharpCode.Decompiler.IL public ILFunction(IMethod method, int codeSize, GenericContext genericContext, ILInstruction body, ILFunctionKind kind = ILFunctionKind.TopLevelFunction) : base(OpCode.ILFunction) { this.Method = method; - this.Name = method.Name; + this.Name = Method?.Name; this.CodeSize = codeSize; this.GenericContext = genericContext; this.Body = body; From 268d0af4e095c14d89757f26b6ef4a6302392cc2 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Mon, 15 Jul 2019 13:30:27 +0200 Subject: [PATCH 058/221] Include documentation XML in NuGet package (similar to PR https://github.com/icsharpcode/ILSpy/pull/1432 but built for v5.0) --- ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj | 2 +- ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 3fca3af6e..c14770c9e 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -16,7 +16,7 @@ false 7.2 - + $(TargetDir)ICSharpCode.Decompiler.xml True ICSharpCode.Decompiler.snk diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template index 7f01f6e4a..314edbd73 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.nuspec.template @@ -24,5 +24,6 @@ + \ No newline at end of file From b83cb41f2ef6e7aa150f5675ca6cee9a43d94a18 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 15 Jul 2019 17:58:53 +0200 Subject: [PATCH 059/221] Add pretty tests for local functions. --- .../CorrectnessTestRunner.cs | 6 - .../ICSharpCode.Decompiler.Tests.csproj | 2 +- .../PrettyTestRunner.cs | 6 + .../TestCases/Correctness/LocalFunctions.cs | 76 ------ .../TestCases/Pretty/LocalFunctions.cs | 226 ++++++++++++++++++ .../CSharp/StatementBuilder.cs | 2 +- .../Transforms/TransformDisplayClassUsage.cs | 7 +- 7 files changed, 236 insertions(+), 89 deletions(-) delete mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Correctness/LocalFunctions.cs create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs diff --git a/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs b/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs index e5f200486..2239f26aa 100644 --- a/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs @@ -295,12 +295,6 @@ namespace ICSharpCode.Decompiler.Tests RunCS(options: options); } - [Test] - public void LocalFunctions([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions options) - { - RunCS(options: options); - } - void RunCS([CallerMemberName] string testName = null, CompilerOptions options = CompilerOptions.UseDebug) { string testFileName = testName + ".cs"; diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 51ed72689..68a1c2613 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -83,7 +83,7 @@ - + diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index e7786ac67..fa1bb5dd8 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -184,6 +184,12 @@ namespace ICSharpCode.Decompiler.Tests }); } + [Test] + public void LocalFunctions([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions) + { + RunForLibrary(cscOptions: cscOptions); + } + [Test] public void PropertiesAndEvents([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/LocalFunctions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/LocalFunctions.cs deleted file mode 100644 index f05d95f27..000000000 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/LocalFunctions.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace LocalFunctions -{ - class LocalFunctions - { - int field; - - public static void Main(string[] args) - { - StaticContextNoCapture(10); - StaticContextSimpleCapture(10); - StaticContextCaptureInForLoop(10); - var inst = new LocalFunctions() { field = 10 }; - inst.ContextNoCapture(); - inst.ContextSimpleCapture(); - inst.ContextCaptureInForLoop(); - } - - public static void StaticContextNoCapture(int length) - { - for (int i = 0; i < length; i++) { - LocalWrite("Hello " + i); - } - - void LocalWrite(string s) => Console.WriteLine(s); - } - - public static void StaticContextSimpleCapture(int length) - { - for (int i = 0; i < length; i++) { - LocalWrite(); - } - - void LocalWrite() => Console.WriteLine("Hello " + length); - } - - public static void StaticContextCaptureInForLoop(int length) - { - for (int i = 0; i < length; i++) { - void LocalWrite() => Console.WriteLine("Hello " + i + "/" + length); - LocalWrite(); - } - } - - public void ContextNoCapture() - { - for (int i = 0; i < field; i++) { - LocalWrite("Hello " + i); - } - - void LocalWrite(string s) => Console.WriteLine(s); - } - - public void ContextSimpleCapture() - { - for (int i = 0; i < field; i++) { - LocalWrite(); - } - - void LocalWrite() => Console.WriteLine("Hello " + field); - } - - public void ContextCaptureInForLoop() - { - for (int i = 0; i < field; i++) { - void LocalWrite() => Console.WriteLine("Hello " + i + "/" + field); - LocalWrite(); - } - } - } -} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs new file mode 100644 index 000000000..e06d9a892 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs @@ -0,0 +1,226 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +using System; +using System.Collections.Generic; +using System.Linq; + +namespace LocalFunctions +{ + internal class LocalFunctions + { + private int field; + + private static void Test(int x) + { + } + + private static int GetInt(string a) + { + return a.Length; + } + + private static string GetString(int a) + { + return a.ToString(); + } + + public static void StaticContextNoCapture(int length) + { + for (int i = 0; i < length; i++) { + LocalWrite("Hello " + i); + } + + void LocalWrite(string s) + { + Console.WriteLine(s); + } + } + + public static void StaticContextSimpleCapture(int length) + { + for (int i = 0; i < length; i++) { + LocalWrite(); + } + + void LocalWrite() + { + Console.WriteLine("Hello " + length); + } + } + + public static void StaticContextCaptureForLoopVariable(int length) + { + int i; + for (i = 0; i < length; i++) { + LocalWrite(); + } + void LocalWrite() + { + Console.WriteLine("Hello " + i + "/" + length); + } + } + + public void ContextNoCapture() + { + for (int i = 0; i < field; i++) { + LocalWrite("Hello " + i); + } + + void LocalWrite(string s) + { + Console.WriteLine(s); + } + } + + public void ContextSimpleCapture() + { + for (int i = 0; i < field; i++) { + LocalWrite(); + } + + void LocalWrite() + { + Console.WriteLine("Hello " + field); + } + } + + public void ContextCaptureForLoopVariable() + { + int i; + for (i = 0; i < field; i++) { + LocalWrite(); + } + void LocalWrite() + { + Console.WriteLine("Hello " + i + "/" + field); + } + } + + public void CapturedOutsideLoop() + { + int i = 0; + while (i < field) { + i = GetInt("asdf"); + LocalWrite(); + } + + void LocalWrite() + { + Console.WriteLine("Hello " + i + "/" + field); + } + } + + public void CapturedInForeachLoop(IEnumerable args) + { + foreach (string arg2 in args) { + string arg = arg2; + LocalWrite(); + void LocalWrite() + { + Console.WriteLine("Hello " + arg); + } + } + } + + public void Overloading() + { + Test(5); + LocalFunctions.Test(2); + + void Test(int x) + { + Console.WriteLine("x: {0}", x); + } + } + + public void NamedArgument() + { + Use(Get(1), Get(2), Get(3)); + Use(Get(1), c: Get(2), b: Get(3)); + + int Get(int i) + { + return i; + } + + void Use(int a, int b, int c) + { + Console.WriteLine(a + b + c); + } + } + + public static Func LambdaInLocalFunction() + { + int x = (int)Math.Pow(2.0, 10.0); + Enumerable.Range(1, 100).Select((Func)Transform); + return Create(); + + Func Create() + { + return () => x; + } + + int Transform(int y) + { + return 2 * y; + } + } + + public static Func MethodRef() + { + int x = (int)Math.Pow(2.0, 10.0); + Enumerable.Range(1, 100).Select((Func)F); + return null; + + int F(int y) + { + return x * y; + } + } + + public static int Fib(int i) + { + return FibHelper(i); + + int FibHelper(int n) + { + if (n <= 0) { + return 0; + } + + return FibHelper(n - 1) + FibHelper(n - 2); + } + } + + public static int NestedLocalFunctions(int i) + { + return A(); + + int A() + { + double x = Math.Pow(10.0, 2.0); + return B(); + + int B() + { + return i + (int)x; + } + } + } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index 73b07c70b..bd72ac66d 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -952,7 +952,7 @@ namespace ICSharpCode.Decompiler.CSharp void DeclareLocalFunctions(ILFunction currentFunction, BlockContainer container, BlockStatement blockStatement) { - foreach (var localFunction in currentFunction.LocalFunctions) { + foreach (var localFunction in currentFunction.LocalFunctions.OrderBy(f => f.Name)) { if (localFunction.DeclarationScope != container) continue; blockStatement.Add(TranslateFunction(localFunction)); diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index eb2d39d65..38020dc3b 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -263,14 +263,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (inst.Value.MatchLdLoc(out var closureVariable) && displayClasses.TryGetValue(closureVariable, out var displayClass)) { displayClasses[inst.Variable] = displayClass; instructionsToRemove.Add(inst); + } else if (inst.Variable.Kind == VariableKind.Local && inst.Variable.IsSingleDefinition && inst.Variable.LoadCount == 0 && inst.Value is StLoc) { + inst.ReplaceWith(inst.Value); } } - protected internal override void VisitLdLoc(LdLoc inst) - { - base.VisitLdLoc(inst); - } - protected internal override void VisitStObj(StObj inst) { base.VisitStObj(inst); From 01e42b772c60260c0c0787eca96bf5fdb6d469cc Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 15 Jul 2019 18:37:59 +0200 Subject: [PATCH 060/221] Fix local functions inside lambdas. --- .../TestCases/Pretty/LocalFunctions.cs | 12 ++++++++++++ .../IL/Transforms/LocalFunctionDecompiler.cs | 5 +++++ 2 files changed, 17 insertions(+) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs index e06d9a892..4c39b485e 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs @@ -222,5 +222,17 @@ namespace LocalFunctions } } } + + public static int LocalFunctionInLambda(IEnumerable xs) + { + return xs.First(delegate(int x) { + return Do(); + + bool Do() + { + return x == 3; + } + }); + } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs index b65420b0f..ab3fd8523 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs @@ -169,6 +169,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } + if (function.DeclarationScope != null && function.DeclarationScope.Parent is ILFunction betterParentFunction) { + parentFunction.LocalFunctions.Remove(function); + betterParentFunction.LocalFunctions.Add(function); + } + return function; } From 19a78987862a16a1c66921947ded38557ec42e63 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 15 Jul 2019 23:34:23 +0200 Subject: [PATCH 061/221] Add addressof(ldloc) => ldloca transform to EarlyExpressionTransforms --- .../Transforms/EarlyExpressionTransforms.cs | 22 +++++++++++++++++++ .../IL/Transforms/ExpressionTransforms.cs | 17 +------------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs index f60e2d490..5ee5b2fbe 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs @@ -66,6 +66,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms protected internal override void VisitLdObj(LdObj inst) { base.VisitLdObj(inst); + AddressOfLdLocToLdLoca(inst, context); LdObjToLdLoc(inst, context); } @@ -83,6 +84,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } + internal static void AddressOfLdLocToLdLoca(LdObj inst, ILTransformContext context) + { + // ldobj(...(addressof(ldloc V))) where ... can be zero or more ldflda instructions + // => + // ldobj(...(ldloca V)) + var temp = inst.Target; + var range = temp.ILRanges; + while (temp.MatchLdFlda(out var ldfldaTarget, out _)) { + temp = ldfldaTarget; + range = range.Concat(temp.ILRanges); + } + if (temp.MatchAddressOf(out var addressOfTarget) && addressOfTarget.MatchLdLoc(out var v)) { + context.Step($"ldobj(...(addressof(ldloca {v.Name}))) => ldobj(...(ldloca {v.Name}))", inst); + var replacement = new LdLoca(v).WithILRange(addressOfTarget); + foreach (var r in range) { + replacement = replacement.WithILRange(r); + } + temp.ReplaceWith(replacement); + } + } + protected internal override void VisitCall(Call inst) { var expr = HandleCall(inst, context); diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 93575cae4..834e0d804 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -393,23 +393,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms protected internal override void VisitLdObj(LdObj inst) { base.VisitLdObj(inst); + EarlyExpressionTransforms.AddressOfLdLocToLdLoca(inst, context); EarlyExpressionTransforms.LdObjToLdLoc(inst, context); - // ldobj(...(addressof(ldloc V)) where ... can be zero or more ldflda instructions - // => - // ldobj(...(ldloca V)) - var temp = inst.Target; - var range = temp.ILRanges; - while (temp.MatchLdFlda(out var ldfldaTarget, out _)) { - temp = ldfldaTarget; - range = range.Concat(temp.ILRanges); - } - if (temp.MatchAddressOf(out var addressOfTarget) && addressOfTarget.MatchLdLoc(out var v)) { - var replacement = new LdLoca(v).WithILRange(addressOfTarget); - foreach (var r in range) { - replacement = replacement.WithILRange(r); - } - temp.ReplaceWith(replacement); - } } protected internal override void VisitStObj(StObj inst) From bb066dbd04565b5508ae6624556cd1be0586fa24 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 15 Jul 2019 23:35:04 +0200 Subject: [PATCH 062/221] Additional test (deactivated) --- .../TestCases/Pretty/LocalFunctions.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs index 4c39b485e..33bcab1e2 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs @@ -234,5 +234,16 @@ namespace LocalFunctions } }); } + //public static void LocalFunctionInUsing() + //{ + // using (MemoryStream memoryStream = new MemoryStream()) { + // Do(); + + // void Do() + // { + // memoryStream.WriteByte(42); + // } + // } + //} } } From f2151972a0b0b1e2a050a3991bd7363c5429f8a7 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 16 Jul 2019 00:11:08 +0200 Subject: [PATCH 063/221] Redesign CompoundAssignmentInstruction to support an 'Address' mode where we don't implicitly turn an LdObj into an StObj, but instead directly operate on an address. This means we can get rid of the special case in TransformDisplayClassUsage, as compound.assign can now also be used with the address of a local variable. --- .../CSharp/ExpressionBuilder.cs | 65 +++++--- ICSharpCode.Decompiler/IL/Instructions.cs | 4 +- .../CompoundAssignmentInstruction.cs | 153 +++++++++++++----- .../IL/Transforms/ILInlining.cs | 3 +- .../IL/Transforms/TransformAssignment.cs | 56 ++++--- .../Transforms/TransformDisplayClassUsage.cs | 10 -- 6 files changed, 192 insertions(+), 99 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 1cfb75ecd..8ebec3d6b 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -1326,7 +1326,13 @@ namespace ICSharpCode.Decompiler.CSharp protected internal override TranslatedExpression VisitUserDefinedCompoundAssign(UserDefinedCompoundAssign inst, TranslationContext context) { - var target = Translate(inst.Target); + IType loadType = inst.Method.Parameters[0].Type; + ExpressionWithResolveResult target; + if (inst.TargetKind == CompoundTargetKind.Address) { + target = LdObj(inst.Target, loadType); + } else { + target = Translate(inst.Target, loadType); + } if (UserDefinedCompoundAssign.IsStringConcat(inst.Method)) { Debug.Assert(inst.Method.Parameters.Count == 2); var value = Translate(inst.Value).ConvertTo(inst.Method.Parameters[1].Type, this, allowImplicitConversion: true); @@ -1342,7 +1348,7 @@ namespace ICSharpCode.Decompiler.CSharp .WithILInstruction(inst) .WithRR(new OperatorResolveResult(inst.Method.ReturnType, AssignmentExpression.GetLinqNodeType(op.Value, false), inst.Method, inst.IsLifted, new[] { target.ResolveResult, value.ResolveResult })); } else { - UnaryOperatorType? op = GetUnaryOperatorTypeFromMetadataName(inst.Method.Name, inst.CompoundAssignmentType == CompoundAssignmentType.EvaluatesToOldValue); + UnaryOperatorType? op = GetUnaryOperatorTypeFromMetadataName(inst.Method.Name, inst.EvalMode == CompoundEvalMode.EvaluatesToOldValue); Debug.Assert(op != null); return new UnaryOperatorExpression(op.Value, target) @@ -1421,10 +1427,15 @@ namespace ICSharpCode.Decompiler.CSharp TranslatedExpression HandleCompoundAssignment(NumericCompoundAssign inst, AssignmentOperatorType op) { - var target = Translate(inst.Target); + ExpressionWithResolveResult target; + if (inst.TargetKind == CompoundTargetKind.Address) { + target = LdObj(inst.Target, inst.Type); + } else { + target = Translate(inst.Target, inst.Type); + } TranslatedExpression resultExpr; - if (inst.CompoundAssignmentType == CompoundAssignmentType.EvaluatesToOldValue) { + if (inst.EvalMode == CompoundEvalMode.EvaluatesToOldValue) { Debug.Assert(op == AssignmentOperatorType.Add || op == AssignmentOperatorType.Subtract); Debug.Assert(inst.Value.MatchLdcI(1)); UnaryOperatorType unary; @@ -1485,8 +1496,13 @@ namespace ICSharpCode.Decompiler.CSharp TranslatedExpression HandleCompoundShift(NumericCompoundAssign inst, AssignmentOperatorType op) { - Debug.Assert(inst.CompoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue); - var target = Translate(inst.Target); + Debug.Assert(inst.EvalMode == CompoundEvalMode.EvaluatesToNewValue); + ExpressionWithResolveResult target; + if (inst.TargetKind == CompoundTargetKind.Address) { + target = LdObj(inst.Target, inst.Type); + } else { + target = Translate(inst.Target, inst.Type); + } var value = Translate(inst.Value); // Shift operators in C# always expect type 'int' on the right-hand-side @@ -1901,46 +1917,46 @@ namespace ICSharpCode.Decompiler.CSharp protected internal override TranslatedExpression VisitLdObj(LdObj inst, TranslationContext context) { - var target = Translate(inst.Target); - if (TypeUtils.IsCompatiblePointerTypeForMemoryAccess(target.Type, inst.Type)) { - TranslatedExpression result; + var result = LdObj(inst.Target, inst.Type); + //if (target.Type.IsSmallIntegerType() && loadType.IsSmallIntegerType() && target.Type.GetSign() != loadType.GetSign()) + // return result.ConvertTo(loadType, this); + return result.WithILInstruction(inst); + } + + ExpressionWithResolveResult LdObj(ILInstruction address, IType loadType) + { + var target = Translate(address); + if (TypeUtils.IsCompatiblePointerTypeForMemoryAccess(target.Type, loadType)) { + ExpressionWithResolveResult result; if (target.Expression is DirectionExpression dirExpr) { // we can dereference the managed reference by stripping away the 'ref' result = target.UnwrapChild(dirExpr.Expression); - result.Expression.AddAnnotation(inst); // add LdObj in addition to the existing ILInstruction annotation } else if (target.Type is PointerType pointerType) { if (target.Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.AddressOf) { // We can dereference the pointer by stripping away the '&' result = target.UnwrapChild(uoe.Expression); - result.Expression.AddAnnotation(inst); // add LdObj in addition to the existing ILInstruction annotation } else { // Dereference the existing pointer result = new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression) - .WithILInstruction(inst) .WithRR(new ResolveResult(pointerType.ElementType)); } } else { // reference type behind non-DirectionExpression? // this case should be impossible, but we can use a pointer cast // just to make sure - target = target.ConvertTo(new PointerType(inst.Type), this); + target = target.ConvertTo(new PointerType(loadType), this); return new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression) - .WithILInstruction(inst) - .WithRR(new ResolveResult(inst.Type)); + .WithRR(new ResolveResult(loadType)); } // we don't convert result to inst.Type, because the LdObj type // might be inaccurate (it's often System.Object for all reference types), // and our parent node should already insert casts where necessary - - if (target.Type.IsSmallIntegerType() && inst.Type.IsSmallIntegerType() && target.Type.GetSign() != inst.Type.GetSign()) - return result.ConvertTo(inst.Type, this); return result; } else { // We need to cast the pointer type: - target = target.ConvertTo(new PointerType(inst.Type), this); + target = target.ConvertTo(new PointerType(loadType), this); return new UnaryOperatorExpression(UnaryOperatorType.Dereference, target.Expression) - .WithILInstruction(inst) - .WithRR(new ResolveResult(inst.Type)); + .WithRR(new ResolveResult(loadType)); } } @@ -3039,7 +3055,12 @@ namespace ICSharpCode.Decompiler.CSharp protected internal override TranslatedExpression VisitDynamicCompoundAssign(DynamicCompoundAssign inst, TranslationContext context) { - var target = TranslateDynamicArgument(inst.Target, inst.TargetArgumentInfo); + ExpressionWithResolveResult target; + if (inst.TargetKind == CompoundTargetKind.Address) { + target = LdObj(inst.Target, SpecialType.Dynamic); + } else { + target = TranslateDynamicArgument(inst.Target, inst.TargetArgumentInfo); + } var value = TranslateDynamicArgument(inst.Value, inst.ValueArgumentInfo); var ae = new AssignmentExpression(target, AssignmentExpression.GetAssignmentOperatorTypeFromExpressionType(inst.Operation).Value, value); diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index 27e0af93d..52520b8e1 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -1092,7 +1092,7 @@ namespace ICSharpCode.Decompiler.IL protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) { var o = other as UserDefinedCompoundAssign; - return o != null && this.Method.Equals(o.Method) && this.CompoundAssignmentType == o.CompoundAssignmentType && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); + return o != null && this.Method.Equals(o.Method) && this.EvalMode == o.EvalMode && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); } } } @@ -1126,7 +1126,7 @@ namespace ICSharpCode.Decompiler.IL protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) { var o = other as DynamicCompoundAssign; - return o != null && this.CompoundAssignmentType == o.CompoundAssignmentType && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); + return o != null && this.EvalMode == o.EvalMode && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); } } } diff --git a/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs index 972be8aa0..ba8c61586 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs @@ -23,36 +23,101 @@ using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL { - public enum CompoundAssignmentType : byte + public enum CompoundEvalMode : byte { + /// + /// The compound.assign instruction will evaluate to the old value. + /// This mode is used only for post-increment/decrement. + /// EvaluatesToOldValue, + /// + /// The compound.assign instruction will evaluate to the new value. + /// This mode is used for compound assignments and pre-increment/decrement. + /// EvaluatesToNewValue } + public enum CompoundTargetKind : byte + { + /// + /// The target is an instruction computing an address, + /// and the compound.assign will implicitly load/store from/to that address. + /// + Address, + /// + /// The Target must be a call to a property getter, + /// and the compound.assign will implicitly call the corresponding property setter. + /// + Property, + /// + /// The target is a dynamic call. + /// + Dynamic + } + public abstract partial class CompoundAssignmentInstruction : ILInstruction { - public readonly CompoundAssignmentType CompoundAssignmentType; + public readonly CompoundEvalMode EvalMode; + + /// + /// If TargetIsProperty is true, the Target must be a call to a property getter, + /// and the compound.assign will implicitly call the corresponding property setter. + /// Otherwise, the Target can be any instruction that evaluates to an address, + /// and the compound.assign will implicit load and store from/to that address. + /// + public readonly CompoundTargetKind TargetKind; - public CompoundAssignmentInstruction(OpCode opCode, CompoundAssignmentType compoundAssignmentType, ILInstruction target, ILInstruction value) + public CompoundAssignmentInstruction(OpCode opCode, CompoundEvalMode evalMode, ILInstruction target, CompoundTargetKind targetKind, ILInstruction value) : base(opCode) { - this.CompoundAssignmentType = compoundAssignmentType; + this.EvalMode = evalMode; this.Target = target; + this.TargetKind = targetKind; this.Value = value; + CheckValidTarget(); } - internal static bool IsValidCompoundAssignmentTarget(ILInstruction inst) + internal override void CheckInvariant(ILPhase phase) { - switch (inst.OpCode) { - // case OpCode.LdLoc: -- not valid -- does not mark the variable as written to - case OpCode.LdObj: - return true; - case OpCode.Call: - case OpCode.CallVirt: - var owner = ((CallInstruction)inst).Method.AccessorOwner as IProperty; - return owner != null && owner.CanSet; - default: - return false; + base.CheckInvariant(phase); + CheckValidTarget(); + } + + [Conditional("DEBUG")] + void CheckValidTarget() + { + switch (TargetKind) { + case CompoundTargetKind.Address: + Debug.Assert(target.ResultType == StackType.Ref || target.ResultType == StackType.I); + break; + case CompoundTargetKind.Property: + Debug.Assert(target.OpCode == OpCode.Call || target.OpCode == OpCode.CallVirt); + var owner = ((CallInstruction)target).Method.AccessorOwner as IProperty; + Debug.Assert(owner != null && owner.CanSet); + break; + case CompoundTargetKind.Dynamic: + Debug.Assert(target.OpCode == OpCode.DynamicGetMemberInstruction || target.OpCode == OpCode.DynamicGetIndexInstruction); + break; + } + } + + protected void WriteSuffix(ITextOutput output) + { + switch (TargetKind) { + case CompoundTargetKind.Address: + output.Write(".address"); + break; + case CompoundTargetKind.Property: + output.Write(".property"); + break; + } + switch (EvalMode) { + case CompoundEvalMode.EvaluatesToNewValue: + output.Write(".new"); + break; + case CompoundEvalMode.EvaluatesToOldValue: + output.Write(".old"); + break; } } } @@ -82,8 +147,9 @@ namespace ICSharpCode.Decompiler.IL public bool IsLifted { get; } - public NumericCompoundAssign(BinaryNumericInstruction binary, ILInstruction target, ILInstruction value, IType type, CompoundAssignmentType compoundAssignmentType) - : base(OpCode.NumericCompoundAssign, compoundAssignmentType, target, value) + public NumericCompoundAssign(BinaryNumericInstruction binary, ILInstruction target, + CompoundTargetKind targetKind, ILInstruction value, IType type, CompoundEvalMode evalMode) + : base(OpCode.NumericCompoundAssign, evalMode, target, targetKind, value) { Debug.Assert(IsBinaryCompatibleWithType(binary, type)); this.CheckForOverflow = binary.CheckForOverflow; @@ -95,8 +161,7 @@ namespace ICSharpCode.Decompiler.IL this.IsLifted = binary.IsLifted; this.type = type; this.AddILRange(binary); - Debug.Assert(compoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue || (Operator == BinaryNumericOperator.Add || Operator == BinaryNumericOperator.Sub)); - Debug.Assert(IsValidCompoundAssignmentTarget(Target)); + Debug.Assert(evalMode == CompoundEvalMode.EvaluatesToNewValue || (Operator == BinaryNumericOperator.Add || Operator == BinaryNumericOperator.Sub)); Debug.Assert(this.ResultType == (IsLifted ? StackType.O : UnderlyingResultType)); } @@ -176,16 +241,20 @@ namespace ICSharpCode.Decompiler.IL WriteILRange(output, options); output.Write(OpCode); output.Write("." + BinaryNumericInstruction.GetOperatorName(Operator)); - if (CompoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue) - output.Write(".new"); - else - output.Write(".old"); - if (CheckForOverflow) + if (CheckForOverflow) { output.Write(".ovf"); - if (Sign == Sign.Unsigned) + } + if (Sign == Sign.Unsigned) { output.Write(".unsigned"); - else if (Sign == Sign.Signed) + } else if (Sign == Sign.Signed) { output.Write(".signed"); + } + output.Write('.'); + output.Write(UnderlyingResultType.ToString().ToLowerInvariant()); + if (IsLifted) { + output.Write(".lifted"); + } + base.WriteSuffix(output); output.Write('('); Target.WriteTo(output, options); output.Write(", "); @@ -199,13 +268,13 @@ namespace ICSharpCode.Decompiler.IL public readonly IMethod Method; public bool IsLifted => false; // TODO: implement lifted user-defined compound assignments - public UserDefinedCompoundAssign(IMethod method, CompoundAssignmentType compoundAssignmentType, ILInstruction target, ILInstruction value) - : base(OpCode.UserDefinedCompoundAssign, compoundAssignmentType, target, value) + public UserDefinedCompoundAssign(IMethod method, CompoundEvalMode evalMode, + ILInstruction target, CompoundTargetKind targetKind, ILInstruction value) + : base(OpCode.UserDefinedCompoundAssign, evalMode, target, targetKind, value) { this.Method = method; Debug.Assert(Method.IsOperator || IsStringConcat(method)); - Debug.Assert(compoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue || (Method.Name == "op_Increment" || Method.Name == "op_Decrement")); - Debug.Assert(IsValidCompoundAssignmentTarget(Target)); + Debug.Assert(evalMode == CompoundEvalMode.EvaluatesToNewValue || (Method.Name == "op_Increment" || Method.Name == "op_Decrement")); } public static bool IsStringConcat(IMethod method) @@ -219,11 +288,7 @@ namespace ICSharpCode.Decompiler.IL { WriteILRange(output, options); output.Write(OpCode); - - if (CompoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue) - output.Write(".new"); - else - output.Write(".old"); + base.WriteSuffix(output); output.Write(' '); Method.WriteTo(output); output.Write('('); @@ -241,8 +306,11 @@ namespace ICSharpCode.Decompiler.IL public CSharpArgumentInfo ValueArgumentInfo { get; } public CSharpBinderFlags BinderFlags { get; } - public DynamicCompoundAssign(ExpressionType op, CSharpBinderFlags binderFlags, ILInstruction target, CSharpArgumentInfo targetArgumentInfo, ILInstruction value, CSharpArgumentInfo valueArgumentInfo) - : base(OpCode.DynamicCompoundAssign, CompoundAssignmentTypeFromOperation(op), target, value) + public DynamicCompoundAssign(ExpressionType op, CSharpBinderFlags binderFlags, + ILInstruction target, CSharpArgumentInfo targetArgumentInfo, + ILInstruction value, CSharpArgumentInfo valueArgumentInfo, + CompoundTargetKind targetKind = CompoundTargetKind.Dynamic) + : base(OpCode.DynamicCompoundAssign, CompoundEvalModeFromOperation(op), target, targetKind, value) { if (!IsExpressionTypeSupported(op)) throw new ArgumentOutOfRangeException("op"); @@ -258,10 +326,7 @@ namespace ICSharpCode.Decompiler.IL output.Write(OpCode); output.Write("." + Operation.ToString().ToLower()); DynamicInstruction.WriteBinderFlags(BinderFlags, output, options); - if (CompoundAssignmentType == CompoundAssignmentType.EvaluatesToNewValue) - output.Write(".new"); - else - output.Write(".old"); + base.WriteSuffix(output); output.Write(' '); DynamicInstruction.WriteArgumentList(output, options, (Target, TargetArgumentInfo), (Value, ValueArgumentInfo)); } @@ -287,14 +352,14 @@ namespace ICSharpCode.Decompiler.IL || type == ExpressionType.SubtractAssignChecked; } - static CompoundAssignmentType CompoundAssignmentTypeFromOperation(ExpressionType op) + static CompoundEvalMode CompoundEvalModeFromOperation(ExpressionType op) { switch (op) { case ExpressionType.PostIncrementAssign: case ExpressionType.PostDecrementAssign: - return CompoundAssignmentType.EvaluatesToOldValue; + return CompoundEvalMode.EvaluatesToOldValue; default: - return CompoundAssignmentType.EvaluatesToNewValue; + return CompoundEvalMode.EvaluatesToNewValue; } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index 16a0865b2..d4ec96350 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -424,10 +424,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; // inline into (left slot of) user-defined && or || operator case OpCode.DynamicGetMemberInstruction: case OpCode.DynamicGetIndexInstruction: - case OpCode.LdObj: if (parent.Parent.OpCode == OpCode.DynamicCompoundAssign) return true; // inline into dynamic compound assignments break; + case OpCode.DynamicCompoundAssign: + return true; case OpCode.ArrayToPointer: case OpCode.LocAllocSpan: return true; // inline size-expressions into localloc.span diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs index f7a6bee67..70d101c9e 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs @@ -276,18 +276,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms } ILInstruction newInst; if (UnwrapSmallIntegerConv(setterValue, out var smallIntConv) is BinaryNumericInstruction binary) { - if (!IsMatchingCompoundLoad(binary.Left, compoundStore, forbiddenVariable: storeInSetter?.Variable)) + if (!IsMatchingCompoundLoad(binary.Left, compoundStore, out var target, out var targetKind, forbiddenVariable: storeInSetter?.Variable)) return false; if (!ValidateCompoundAssign(binary, smallIntConv, targetType)) return false; context.Step($"Compound assignment (binary.numeric)", compoundStore); newInst = new NumericCompoundAssign( - binary, binary.Left, binary.Right, - targetType, CompoundAssignmentType.EvaluatesToNewValue); + binary, target, targetKind, binary.Right, + targetType, CompoundEvalMode.EvaluatesToNewValue); } else if (setterValue is Call operatorCall && operatorCall.Method.IsOperator) { if (operatorCall.Arguments.Count == 0) return false; - if (!IsMatchingCompoundLoad(operatorCall.Arguments[0], compoundStore, forbiddenVariable: storeInSetter?.Variable)) + if (!IsMatchingCompoundLoad(operatorCall.Arguments[0], compoundStore, out var target, out var targetKind, forbiddenVariable: storeInSetter?.Variable)) return false; ILInstruction rhs; if (operatorCall.Arguments.Count == 2) { @@ -305,24 +305,24 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (operatorCall.IsLifted) return false; // TODO: add tests and think about whether nullables need special considerations context.Step($"Compound assignment (user-defined binary)", compoundStore); - newInst = new UserDefinedCompoundAssign(operatorCall.Method, CompoundAssignmentType.EvaluatesToNewValue, - operatorCall.Arguments[0], rhs); + newInst = new UserDefinedCompoundAssign(operatorCall.Method, CompoundEvalMode.EvaluatesToNewValue, + target, targetKind, rhs); } else if (setterValue is DynamicBinaryOperatorInstruction dynamicBinaryOp) { - if (!IsMatchingCompoundLoad(dynamicBinaryOp.Left, compoundStore, forbiddenVariable: storeInSetter?.Variable)) + if (!IsMatchingCompoundLoad(dynamicBinaryOp.Left, compoundStore, out var target, out var targetKind, forbiddenVariable: storeInSetter?.Variable)) return false; context.Step($"Compound assignment (dynamic binary)", compoundStore); - newInst = new DynamicCompoundAssign(dynamicBinaryOp.Operation, dynamicBinaryOp.BinderFlags, dynamicBinaryOp.Left, dynamicBinaryOp.LeftArgumentInfo, dynamicBinaryOp.Right, dynamicBinaryOp.RightArgumentInfo); + newInst = new DynamicCompoundAssign(dynamicBinaryOp.Operation, dynamicBinaryOp.BinderFlags, target, dynamicBinaryOp.LeftArgumentInfo, dynamicBinaryOp.Right, dynamicBinaryOp.RightArgumentInfo, targetKind); } else if (setterValue is Call concatCall && UserDefinedCompoundAssign.IsStringConcat(concatCall.Method)) { // setterValue is a string.Concat() invocation if (concatCall.Arguments.Count != 2) return false; // for now we only support binary compound assignments if (!targetType.IsKnownType(KnownTypeCode.String)) return false; - if (!IsMatchingCompoundLoad(concatCall.Arguments[0], compoundStore, forbiddenVariable: storeInSetter?.Variable)) + if (!IsMatchingCompoundLoad(concatCall.Arguments[0], compoundStore, out var target, out var targetKind, forbiddenVariable: storeInSetter?.Variable)) return false; context.Step($"Compound assignment (string concatenation)", compoundStore); - newInst = new UserDefinedCompoundAssign(concatCall.Method, CompoundAssignmentType.EvaluatesToNewValue, - concatCall.Arguments[0], concatCall.Arguments[1]); + newInst = new UserDefinedCompoundAssign(concatCall.Method, CompoundEvalMode.EvaluatesToNewValue, + target, targetKind, concatCall.Arguments[1]); } else { return false; } @@ -469,7 +469,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// Gets whether 'inst' is a possible store for use as a compound store. /// - static bool IsCompoundStore(ILInstruction inst, out IType storeType, out ILInstruction value, ICompilation compilation) + /// + /// Output parameters: + /// storeType: The type of the value being stored. + /// value: The value being stored (will be analyzed further to detect compound assignments) + /// + /// Every IsCompoundStore() call should be followed by an IsMatchingCompoundLoad() call. + /// + static bool IsCompoundStore(ILInstruction inst, out IType storeType, + out ILInstruction value, ICompilation compilation) { value = null; storeType = null; @@ -511,18 +519,26 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } - static bool IsMatchingCompoundLoad(ILInstruction load, ILInstruction store, ILVariable forbiddenVariable) + static bool IsMatchingCompoundLoad(ILInstruction load, ILInstruction store, + out ILInstruction target, out CompoundTargetKind targetKind, + ILVariable forbiddenVariable = null) { + target = null; + targetKind = 0; if (load is LdObj ldobj && store is StObj stobj) { Debug.Assert(SemanticHelper.IsPure(stobj.Target.Flags)); if (!SemanticHelper.IsPure(ldobj.Target.Flags)) return false; if (forbiddenVariable != null && forbiddenVariable.IsUsedWithin(ldobj.Target)) return false; + target = ldobj.Target; + targetKind = CompoundTargetKind.Address; return ldobj.Target.Match(stobj.Target).Success; } else if (MatchingGetterAndSetterCalls(load as CallInstruction, store as CallInstruction)) { if (forbiddenVariable != null && forbiddenVariable.IsUsedWithin(load)) return false; + target = load; + targetKind = CompoundTargetKind.Property; return true; } else { return false; @@ -574,18 +590,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!(stloc.Variable.Kind == VariableKind.Local || stloc.Variable.Kind == VariableKind.StackSlot)) return false; - if (!IsMatchingCompoundLoad(stloc.Value, store, stloc.Variable)) + if (!IsMatchingCompoundLoad(stloc.Value, store, out var target, out var targetKind, forbiddenVariable: stloc.Variable)) return false; if (IsImplicitTruncation(stloc.Value, stloc.Variable.Type, context.TypeSystem)) return false; context.Step("TransformPostIncDecOperatorWithInlineStore", store); if (binary != null) { block.Instructions[pos] = new StLoc(stloc.Variable, new NumericCompoundAssign( - binary, stloc.Value, binary.Right, targetType, CompoundAssignmentType.EvaluatesToOldValue)); + binary, target, targetKind, binary.Right, targetType, CompoundEvalMode.EvaluatesToOldValue)); } else { Call operatorCall = (Call)value; block.Instructions[pos] = new StLoc(stloc.Variable, new UserDefinedCompoundAssign( - operatorCall.Method, CompoundAssignmentType.EvaluatesToOldValue, stloc.Value, new LdcI4(1))); + operatorCall.Method, CompoundEvalMode.EvaluatesToOldValue, target, targetKind, new LdcI4(1))); } return true; } @@ -612,7 +628,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // 'stloc l' is implicitly truncating the value return false; } - if (!IsMatchingCompoundLoad(inst.Value, store, inst.Variable)) + if (!IsMatchingCompoundLoad(inst.Value, store, out var target, out var targetKind, forbiddenVariable: inst.Variable)) return false; if (UnwrapSmallIntegerConv(value, out var conv) is BinaryNumericInstruction binary) { if (!binary.Left.MatchLdLoc(inst.Variable) || !binary.Right.MatchLdcI(1)) @@ -622,8 +638,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!ValidateCompoundAssign(binary, conv, targetType)) return false; context.Step("TransformPostIncDecOperator (builtin)", inst); - inst.Value = new NumericCompoundAssign(binary, inst.Value, binary.Right, - targetType, CompoundAssignmentType.EvaluatesToOldValue); + inst.Value = new NumericCompoundAssign(binary, target, targetKind, binary.Right, + targetType, CompoundEvalMode.EvaluatesToOldValue); } else if (value is Call operatorCall && operatorCall.Method.IsOperator && operatorCall.Arguments.Count == 1) { if (!operatorCall.Arguments[0].MatchLdLoc(inst.Variable)) return false; @@ -633,7 +649,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; // TODO: add tests and think about whether nullables need special considerations context.Step("TransformPostIncDecOperator (user-defined)", inst); inst.Value = new UserDefinedCompoundAssign(operatorCall.Method, - CompoundAssignmentType.EvaluatesToOldValue, inst.Value, new LdcI4(1)); + CompoundEvalMode.EvaluatesToOldValue, target, targetKind, new LdcI4(1)); } else { return false; } diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index 317b4da04..4096d28c6 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -305,15 +305,5 @@ namespace ICSharpCode.Decompiler.IL.Transforms Debug.Fail("LdFlda pattern not supported!"); } } - - protected internal override void VisitNumericCompoundAssign(NumericCompoundAssign inst) - { - base.VisitNumericCompoundAssign(inst); - // NumericCompoundAssign is only valid when used with fields: -> replace it with a BinaryNumericInstruction. - if (inst.Target.MatchLdLoc(out var v)) { - inst.ReplaceWith(new StLoc(v, new BinaryNumericInstruction(inst.Operator, inst.Target, inst.Value, inst.CheckForOverflow, inst.Sign).WithILRange(inst))); - } - } - } } From b32cae412ae8fb3dfa8da7babc400eeec8560d60 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 17 Jul 2019 00:33:42 +0200 Subject: [PATCH 064/221] Eliminate BlockKind.PostfixOperator. We can now represent all compound assignments using the CompoundAssignInstruction, including those on local variables. Closes #1583. --- .../CSharp/ExpressionBuilder.cs | 15 ---- .../OutputVisitor/TextWriterTokenWriter.cs | 2 + .../IL/Instructions/Block.cs | 7 -- .../IL/Instructions/ILFunction.cs | 2 + .../IL/Transforms/HighLevelLoopTransform.cs | 16 ++-- .../IL/Transforms/TransformAssignment.cs | 84 ++++++++----------- 6 files changed, 47 insertions(+), 79 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 8ebec3d6b..ca5231bf3 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -2210,8 +2210,6 @@ namespace ICSharpCode.Decompiler.CSharp case BlockKind.CollectionInitializer: case BlockKind.ObjectInitializer: return TranslateObjectAndCollectionInitializer(block); - case BlockKind.PostfixOperator: - return TranslatePostfixOperator(block); case BlockKind.CallInlineAssign: return TranslateSetterCallAssignment(block); case BlockKind.CallWithNamedArgs: @@ -2546,19 +2544,6 @@ namespace ICSharpCode.Decompiler.CSharp .WithRR(new ResolveResult(stloc.Variable.Type)); } - TranslatedExpression TranslatePostfixOperator(Block block) - { - var targetInst = (block.Instructions.ElementAtOrDefault(0) as StLoc)?.Value; - var inst = (block.Instructions.ElementAtOrDefault(1) as StLoc)?.Value as BinaryNumericInstruction; - if (targetInst == null || inst == null || (inst.Operator != BinaryNumericOperator.Add && inst.Operator != BinaryNumericOperator.Sub)) - throw new ArgumentException("given Block is invalid!"); - var op = inst.Operator == BinaryNumericOperator.Add ? UnaryOperatorType.PostIncrement : UnaryOperatorType.PostDecrement; - var target = Translate(targetInst); - return new UnaryOperatorExpression(op, target) - .WithILInstruction(block) - .WithRR(resolver.WithCheckForOverflow(inst.CheckForOverflow).ResolveUnaryOperator(op, target.ResolveResult)); - } - /// /// If expr is a constant integer expression, and its value fits into type, /// convert the expression into the target type. diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs index 19e31d3bf..162f19fd2 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/TextWriterTokenWriter.cs @@ -382,6 +382,8 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor // ASCII characters we allow directly in the output even though we don't use // other Unicode characters of the same category. return null; + case '\ufffd': + return "\\u" + ((int)ch).ToString("x4"); default: switch (char.GetUnicodeCategory(ch)) { case UnicodeCategory.ModifierLetter: diff --git a/ICSharpCode.Decompiler/IL/Instructions/Block.cs b/ICSharpCode.Decompiler/IL/Instructions/Block.cs index dcaa0b580..20b9da814 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/Block.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/Block.cs @@ -329,13 +329,6 @@ namespace ICSharpCode.Decompiler.IL ObjectInitializer, StackAllocInitializer, /// - /// Block is used for postfix operator on local variable. - /// - /// - /// Postfix operators on non-locals use CompoundAssignmentInstruction with CompoundAssignmentType.EvaluatesToOldValue. - /// - PostfixOperator, - /// /// Block is used for using the result of a property setter inline. /// Example: Use(this.Property = value); /// This is only for inline assignments to property or indexers; other inline assignments work diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs index f77106260..b400c9cf4 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs @@ -276,6 +276,8 @@ namespace ICSharpCode.Decompiler.IL /// internal void RecombineVariables(ILVariable variable1, ILVariable variable2) { + if (variable1 == variable2) + return; Debug.Assert(ILVariableEqualityComparer.Instance.Equals(variable1, variable2)); foreach (var ldloc in variable2.LoadInstructions.ToArray()) { ldloc.Variable = variable1; diff --git a/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs index 0573d4114..2e4df8080 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/HighLevelLoopTransform.cs @@ -434,18 +434,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// Returns true if the instruction is stloc v(add(ldloc v, arg)) - /// or stloc v(compound.assign(ldloc v, arg)) + /// or compound.assign(ldloca v, arg) /// public static bool MatchIncrement(ILInstruction inst, out ILVariable variable) { - if (!inst.MatchStLoc(out variable, out var value)) - return false; - if (!value.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right)) { - if (value is CompoundAssignmentInstruction cai) { - left = cai.Target; - } else return false; + if (inst.MatchStLoc(out variable, out var value)) { + if (value.MatchBinaryNumericInstruction(BinaryNumericOperator.Add, out var left, out var right)) { + return left.MatchLdLoc(variable); + } + } else if (inst is CompoundAssignmentInstruction cai) { + return cai.TargetKind == CompoundTargetKind.Address && cai.Target.MatchLdLoca(out variable); } - return left.MatchLdLoc(variable); + return false; } /// diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs index 70d101c9e..1644416ec 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs @@ -44,8 +44,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } if (context.Settings.IntroduceIncrementAndDecrement) { if (TransformPostIncDecOperatorWithInlineStore(block, pos) - || TransformPostIncDecOperator(block, pos) - || TransformPostIncDecOperatorLocal(block, pos)) { + || TransformPostIncDecOperator(block, pos)) { // again, new top-level stloc might need inlining: context.RequestRerun(); return; @@ -428,44 +427,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - /// - /// stloc s(ldloc l) - /// stloc l(binary.op(ldloc s, ldc.i4 1)) - /// --> - /// stloc s(block { - /// stloc s2(ldloc l) - /// stloc l(binary.op(ldloc s2, ldc.i4 1)) - /// final: ldloc s2 - /// }) - /// - bool TransformPostIncDecOperatorLocal(Block block, int pos) - { - var inst = block.Instructions[pos] as StLoc; - var nextInst = block.Instructions.ElementAtOrDefault(pos + 1) as StLoc; - if (inst == null || nextInst == null || !inst.Value.MatchLdLoc(out var loadVar) || !ILVariableEqualityComparer.Instance.Equals(loadVar, nextInst.Variable)) - return false; - var binary = nextInst.Value as BinaryNumericInstruction; - if (inst.Variable.Kind != VariableKind.StackSlot || nextInst.Variable.Kind == VariableKind.StackSlot || binary == null) - return false; - if (binary.IsLifted) - return false; - if ((binary.Operator != BinaryNumericOperator.Add && binary.Operator != BinaryNumericOperator.Sub) || !binary.Left.MatchLdLoc(inst.Variable) || !binary.Right.MatchLdcI4(1)) - return false; - context.Step($"TransformPostIncDecOperatorLocal", inst); - if (loadVar != nextInst.Variable) { - // load and store are two different variables, that were split from the same variable - context.Function.RecombineVariables(loadVar, nextInst.Variable); - } - var tempStore = context.Function.RegisterVariable(VariableKind.StackSlot, inst.Variable.Type); - var assignment = new Block(BlockKind.PostfixOperator); - assignment.Instructions.Add(new StLoc(tempStore, new LdLoc(loadVar))); - assignment.Instructions.Add(new StLoc(loadVar, new BinaryNumericInstruction(binary.Operator, new LdLoc(tempStore), new LdcI4(1), binary.CheckForOverflow, binary.Sign))); - assignment.FinalInstruction = new LdLoc(tempStore); - inst.Value = assignment; - block.Instructions.RemoveAt(pos + 1); // remove nextInst - return true; - } - /// /// Gets whether 'inst' is a possible store for use as a compound store. /// @@ -514,6 +475,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms storeType = call.Method.Parameters.Last().Type; value = call.Arguments.Last(); return IsSameMember(call.Method, (call.Method.AccessorOwner as IProperty)?.Setter); + } else if (inst is StLoc stloc && (stloc.Variable.Kind == VariableKind.Local || stloc.Variable.Kind == VariableKind.Parameter)) { + storeType = stloc.Variable.Type; + value = stloc.Value; + return true; } else { return false; } @@ -521,6 +486,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms static bool IsMatchingCompoundLoad(ILInstruction load, ILInstruction store, out ILInstruction target, out CompoundTargetKind targetKind, + ILFunction contextFunction = null, ILVariable forbiddenVariable = null) { target = null; @@ -540,6 +506,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms target = load; targetKind = CompoundTargetKind.Property; return true; + } else if (load is LdLoc ldloc && store is StLoc stloc && ILVariableEqualityComparer.Instance.Equals(ldloc.Variable, stloc.Variable)) { + if (ILVariableEqualityComparer.Instance.Equals(ldloc.Variable, forbiddenVariable)) + return false; + if (contextFunction == null) + return false; // locals only supported for the callers that specify the context + target = new LdLoca(ldloc.Variable).WithILRange(ldloc); + targetKind = CompoundTargetKind.Address; + contextFunction.RecombineVariables(ldloc.Variable, stloc.Variable); + return true; } else { return false; } @@ -567,8 +542,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms bool TransformPostIncDecOperatorWithInlineStore(Block block, int pos) { var store = block.Instructions[pos]; - if (!IsCompoundStore(store, out var targetType, out var value, context.TypeSystem)) + if (!IsCompoundStore(store, out var targetType, out var value, context.TypeSystem)) { return false; + } StLoc stloc; var binary = UnwrapSmallIntegerConv(value, out var conv) as BinaryNumericInstruction; if (binary != null && binary.Right.MatchLdcI(1)) { @@ -607,11 +583,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms } /// - /// stloc l(ldobj(target)) - /// stobj(target, binary.op(ldloc l, ldc.i4 1)) - /// target is pure and does not use 'l', 'stloc does not truncate' + /// stloc tmp(ldobj(target)) + /// stobj(target, binary.op(ldloc tmp, ldc.i4 1)) + /// target is pure and does not use 'tmp', 'stloc does not truncate' /// --> - /// stloc l(compound.op.old(ldobj(target), ldc.i4 1)) + /// stloc tmp(compound.op.old(ldobj(target), ldc.i4 1)) + /// + /// This is usually followed by inlining or eliminating 'tmp'. + /// + /// Local variables use a similar pattern, also detected by this function: + /// + /// stloc tmp(ldloc target) + /// stloc target(binary.op(ldloc tmp, ldc.i4 1)) + /// --> + /// stloc tmp(compound.op.old(ldloca target, ldc.i4 1)) /// /// /// This pattern occurs with legacy csc for static fields, and with Roslyn for most post-increments. @@ -622,16 +607,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms var store = block.Instructions.ElementAtOrDefault(i + 1); if (inst == null || store == null) return false; + var tmpVar = inst.Variable; if (!IsCompoundStore(store, out var targetType, out var value, context.TypeSystem)) return false; if (IsImplicitTruncation(inst.Value, targetType, context.TypeSystem)) { - // 'stloc l' is implicitly truncating the value + // 'stloc tmp' is implicitly truncating the value return false; } - if (!IsMatchingCompoundLoad(inst.Value, store, out var target, out var targetKind, forbiddenVariable: inst.Variable)) + if (!IsMatchingCompoundLoad(inst.Value, store, out var target, out var targetKind, context.Function, forbiddenVariable: inst.Variable)) return false; if (UnwrapSmallIntegerConv(value, out var conv) is BinaryNumericInstruction binary) { - if (!binary.Left.MatchLdLoc(inst.Variable) || !binary.Right.MatchLdcI(1)) + if (!binary.Left.MatchLdLoc(tmpVar) || !binary.Right.MatchLdcI(1)) return false; if (!(binary.Operator == BinaryNumericOperator.Add || binary.Operator == BinaryNumericOperator.Sub)) return false; @@ -641,7 +627,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms inst.Value = new NumericCompoundAssign(binary, target, targetKind, binary.Right, targetType, CompoundEvalMode.EvaluatesToOldValue); } else if (value is Call operatorCall && operatorCall.Method.IsOperator && operatorCall.Arguments.Count == 1) { - if (!operatorCall.Arguments[0].MatchLdLoc(inst.Variable)) + if (!operatorCall.Arguments[0].MatchLdLoc(tmpVar)) return false; if (!(operatorCall.Method.Name == "op_Increment" || operatorCall.Method.Name == "op_Decrement")) return false; From 0719aa9f08f1df4514c9dcd504c744ecb088b5a4 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 17 Jul 2019 18:20:12 +0200 Subject: [PATCH 065/221] Enable DecompilerSettings.IntroduceLocalFunctions --- ICSharpCode.Decompiler/CSharp/StatementBuilder.cs | 3 ++- ICSharpCode.Decompiler/DecompilerSettings.cs | 11 ++++------- ILSpy/Properties/Resources.Designer.cs | 6 +++--- ILSpy/Properties/Resources.resx | 4 ++-- ILSpy/Properties/Resources.zh-Hans.resx | 6 +++--- 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index bd72ac66d..25d8658ba 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -726,7 +726,8 @@ namespace ICSharpCode.Decompiler.CSharp /// /// Determines whether storeInst.Variable is only assigned once and used only inside . - /// Loads by reference (ldloca) are only allowed in the context of this pointer in call instructions. + /// Loads by reference (ldloca) are only allowed in the context of this pointer in call instructions, + /// or as target of ldobj. /// (This only applies to value types.) /// bool VariableIsOnlyUsedInBlock(StLoc storeInst, BlockContainer usingContainer) diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index f6e7a94dd..ed6f98191 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -945,19 +945,16 @@ namespace ICSharpCode.Decompiler bool localFunctions = true; /// - /// Gets/Sets whether C# 7.0 local functions should be used. - /// Note: this language feature is currently not implemented and this setting is always false. + /// Gets/Sets whether C# 7.0 local functions should be transformed. /// [Category("C# 7.0 / VS 2017")] - [Description("DecompilerSettings.IntroduceLocalFunctionsNOTIMPLEMENTED")] - [Browsable(false)] + [Description("DecompilerSettings.IntroduceLocalFunctions")] public bool LocalFunctions { get { return localFunctions; } set { if (localFunctions != value) { - throw new NotImplementedException("C# 7.0 local functions are not implemented!"); - //localFunctions = value; - //OnPropertyChanged(); + localFunctions = value; + OnPropertyChanged(); } } } diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index 5c86e06e2..9a8af3ff6 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -700,11 +700,11 @@ namespace ICSharpCode.ILSpy.Properties { } /// - /// Looks up a localized string similar to Introduce local functions (NOT IMPLEMENTED!). + /// Looks up a localized string similar to Introduce local functions. /// - public static string DecompilerSettings_IntroduceLocalFunctionsNOTIMPLEMENTED { + public static string DecompilerSettings_IntroduceLocalFunctions { get { - return ResourceManager.GetString("DecompilerSettings.IntroduceLocalFunctionsNOTIMPLEMENTED", resourceCulture); + return ResourceManager.GetString("DecompilerSettings.IntroduceLocalFunctions", resourceCulture); } } diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index 0128389d9..60701dc91 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -702,8 +702,8 @@ Remove optional arguments, if possible - - Introduce local functions (NOT IMPLEMENTED!) + + Introduce local functions Nullable reference types diff --git a/ILSpy/Properties/Resources.zh-Hans.resx b/ILSpy/Properties/Resources.zh-Hans.resx index 4ab21794f..4f04dba0b 100644 --- a/ILSpy/Properties/Resources.zh-Hans.resx +++ b/ILSpy/Properties/Resources.zh-Hans.resx @@ -676,7 +676,7 @@ IsByRefLikeAttribute应替换为结构上的 "ref" 修饰符 - IsReadOnlyAttribute 应替为结构参数上的 "readonly"/"中的修饰符 + IsReadOnlyAttribute 应替为结构参数上的 "readonly"/"in"中的修饰符 类型参数上的IsUnmanagedAttribute 应替换为 "非托管" 约束 @@ -702,8 +702,8 @@ 如果可能, 删除可选参数 - - 引入本地功能 (未实现!) + + 引入本地功能 C# 7.0 本地函数未实现! From ccdabf2325bde88ede0976092d6305a5fe45c42c Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 17 Jul 2019 19:15:43 +0200 Subject: [PATCH 066/221] Fix a bunch of XmlDoc compiler warnings. --- .../ICSharpCode.Decompiler.Tests.csproj | 2 +- .../OutputVisitor/FormattingOptionsFactory.cs | 2 +- .../CSharp/Resolver/CSharpConversions.cs | 2 +- .../Resolver/MethodGroupResolveResult.cs | 4 +- .../CSharp/Resolver/OverloadResolution.cs | 1 + .../Resolver/RenameCallbackArguments.cs | 42 ------------------- .../Expressions/AssignmentExpression.cs | 6 +-- .../Expressions/UnaryOperatorExpression.cs | 2 +- .../GeneralScope/ExternAliasDeclaration.cs | 2 +- .../CSharp/Syntax/TextLocation.cs | 4 -- .../ICSharpCode.Decompiler.csproj | 5 ++- .../IL/ControlFlow/ConditionDetection.cs | 2 +- .../IL/ControlFlow/LoopDetection.cs | 1 - .../IL/ControlFlow/SwitchDetection.cs | 2 +- .../IL/ControlFlow/YieldReturnDecompiler.cs | 2 +- ICSharpCode.Decompiler/IL/Instructions.cs | 14 +++---- ICSharpCode.Decompiler/IL/Instructions.tt | 12 ++++-- .../IL/Instructions/IfInstruction.cs | 2 +- .../IL/Transforms/ILInlining.cs | 2 +- .../IL/Transforms/LocalFunctionDecompiler.cs | 4 +- .../IL/Transforms/NullableLiftingTransform.cs | 10 ++--- .../IL/Transforms/Stepper.cs | 2 +- .../TypeSystem/IParameter.cs | 4 +- .../TypeSystem/ITypeParameter.cs | 4 +- .../TypeSystem/Implementation/ThreeState.cs | 2 +- ICSharpCode.Decompiler/TypeSystem/TypeKind.cs | 7 +--- .../TypeSystem/TypeSystemExtensions.cs | 8 ++-- 27 files changed, 54 insertions(+), 96 deletions(-) delete mode 100644 ICSharpCode.Decompiler/CSharp/Resolver/RenameCallbackArguments.cs diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 51ed72689..d4e92647f 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -7,7 +7,7 @@ True - 1701;1702;1705,67,169,1058,728,1720,649,168,251 + 1701;1702;1705,67,169,1058,728,1720,649,168,251,660,661,675 False diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/FormattingOptionsFactory.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/FormattingOptionsFactory.cs index 8fbeb4bbd..6391cf62f 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/FormattingOptionsFactory.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/FormattingOptionsFactory.cs @@ -209,7 +209,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } /// - /// The K&R style, so named because it was used in Kernighan and Ritchie's book The C Programming Language, + /// The K&R style, so named because it was used in Kernighan and Ritchie's book The C Programming Language, /// is commonly used in C. It is less common for C++, C#, and others. /// public static CSharpFormattingOptions CreateKRStyle() diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs index ff0ff5854..021e75397 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs @@ -647,7 +647,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver { TypeKind kind = type.Kind; return kind == TypeKind.Class && type.GetDefinition().IsSealed - || kind == TypeKind.Delegate || kind == TypeKind.Anonymous; + || kind == TypeKind.Delegate; } #endregion diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/MethodGroupResolveResult.cs b/ICSharpCode.Decompiler/CSharp/Resolver/MethodGroupResolveResult.cs index 83207005d..4f6022208 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/MethodGroupResolveResult.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/MethodGroupResolveResult.cs @@ -174,12 +174,12 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver } return extensionMethods ?? Enumerable.Empty>(); } - + /// /// Gets the eligible extension methods. /// /// - /// Specifies whether to produce a + /// Specifies whether to produce a SpecializedMethod /// when type arguments could be inferred from . /// This setting is only used for inferred types and has no effect if the type parameters are /// specified explicitly. diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs b/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs index 21643562e..c2af071b9 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs @@ -940,6 +940,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver /// /// Statements for Objects/Collections initializer. /// + /// /// /// If not null, use this instead of the ReturnType of the member as the type of the created resolve result. /// diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/RenameCallbackArguments.cs b/ICSharpCode.Decompiler/CSharp/Resolver/RenameCallbackArguments.cs deleted file mode 100644 index 082291dd0..000000000 --- a/ICSharpCode.Decompiler/CSharp/Resolver/RenameCallbackArguments.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2010-2014 AlphaSierraPapa for the SharpDevelop Team -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System; -using ICSharpCode.Decompiler.CSharp.Syntax; - -namespace ICSharpCode.Decompiler.CSharp.Resolver -{ - /// - /// Arguments for the callback of . - /// - public class RenameCallbackArguments - { - public AstNode NodeToReplace { get; private set; } - public AstNode NewNode { get; private set; } - - public RenameCallbackArguments(AstNode nodeToReplace, AstNode newNode) - { - if (nodeToReplace == null) - throw new ArgumentNullException("nodeToReplace"); - if (newNode == null) - throw new ArgumentNullException("newNode"); - this.NodeToReplace = nodeToReplace; - this.NewNode = newNode; - } - } -} diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs index f9a9f2d95..da02b77e7 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs @@ -250,13 +250,13 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax Divide, /// left %= right Modulus, - - /// left <<= right + + /// left <<= right ShiftLeft, /// left >>= right ShiftRight, - /// left &= right + /// left &= right BitwiseAnd, /// left |= right BitwiseOr, diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs index 60213cc7d..31e9bd9f1 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/UnaryOperatorExpression.cs @@ -182,7 +182,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax PostDecrement, /// Dereferencing (*a) Dereference, - /// Get address (&a) + /// Get address (&a) AddressOf, /// C# 5.0 await Await, diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/ExternAliasDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/ExternAliasDeclaration.cs index e0b10c17f..b3c6419ba 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/ExternAliasDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/ExternAliasDeclaration.cs @@ -28,7 +28,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { /// - /// extern alias ; + /// extern alias IDENTIFIER; /// public class ExternAliasDeclaration : AstNode { diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TextLocation.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TextLocation.cs index b49bbf7dd..8f9746d8d 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TextLocation.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TextLocation.cs @@ -26,10 +26,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax /// A line/column position. /// Text editor lines/columns are counted started from one. /// - /// - /// The document provides the methods and - /// to convert between offsets and TextLocations. - /// [Serializable] [TypeConverter(typeof(TextLocationConverter))] public struct TextLocation : IComparable, IEquatable diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index c14770c9e..fb144691c 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -48,6 +48,10 @@ true + + 1701;1702;1591;1573 + + @@ -223,7 +227,6 @@ - diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs index f37cb1fed..7025208f1 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs @@ -403,7 +403,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// /// if (cond) { if (nestedCond) { nestedThen... } } /// -> - /// if (cond && nestedCond) { nestedThen... } + /// if (cond && nestedCond) { nestedThen... } /// private void IntroduceShortCircuit(IfInstruction ifInst) { diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs index 8b096630f..a8ec926a9 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs @@ -458,7 +458,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// or that leave the block Container. /// /// Entry point of the loop. - /// Whether to ignore branches that map to C# 'continue' statements. /// out: The number of different CFG nodes. /// Possible values: /// 0 = no CFG nodes used as exit nodes (although edges leaving the block container might still be exits); diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs index 6a4429ec4..a1fca3d89 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs @@ -441,7 +441,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// s c /// /// where: - /// p|n: if (a && b) goto c; goto s; + /// p|n: if (a && b) goto c; goto s; /// /// Note that if n has only 1 successor, but is still a flow node, then a short circuit expression /// has a target (c) with no corresponding block (leave) diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs index fc6f6dcc0..c015dbbcf 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs @@ -70,7 +70,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// Set in AnalyzeCurrentProperty() IField currentField; - /// The disposing field of the compiler-generated enumerator class./summary> + /// The disposing field of the compiler-generated enumerator class. /// Set in ConstructExceptionTable() for assembly compiled with Mono IField disposingField; diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index 52520b8e1..3deccd7e1 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -115,7 +115,7 @@ namespace ICSharpCode.Decompiler.IL /// In case 3 (managed reference), the dereferenced value is the input being tested, and the nullable.unwrap instruction returns the managed reference unmodified (if the value is non-null). NullableUnwrap, /// Serves as jump target for the nullable.unwrap instruction. - /// If the input evaluates normally, evaluates to the input value (wrapped in Nullable if the input is a non-nullable value type).If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap instruction evaluates to null. + /// If the input evaluates normally, evaluates to the input value (wrapped in Nullable<T> if the input is a non-nullable value type).If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap instruction evaluates to null. NullableRewrap, /// Loads a constant string. LdStr, @@ -189,7 +189,7 @@ namespace ICSharpCode.Decompiler.IL StringToInt, /// ILAst representation of Expression.Convert. ExpressionTreeCast, - /// Use of user-defined && or || operator. + /// Use of user-defined && or || operator. UserDefinedLogicOperator, /// ILAst representation of a short-circuiting binary operator inside a dynamic expression. DynamicLogicOperatorInstruction, @@ -1058,7 +1058,7 @@ namespace ICSharpCode.Decompiler.IL protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) { var o = other as NumericCompoundAssign; - return o != null && type.Equals(o.type) && CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); + return o != null && type.Equals(o.type) && CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator && this.EvalMode == o.EvalMode && this.TargetKind == o.TargetKind && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); } } } @@ -1092,7 +1092,7 @@ namespace ICSharpCode.Decompiler.IL protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) { var o = other as UserDefinedCompoundAssign; - return o != null && this.Method.Equals(o.Method) && this.EvalMode == o.EvalMode && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); + return o != null && this.Method.Equals(o.Method) && this.EvalMode == o.EvalMode && this.TargetKind == o.TargetKind && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); } } } @@ -1126,7 +1126,7 @@ namespace ICSharpCode.Decompiler.IL protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) { var o = other as DynamicCompoundAssign; - return o != null && this.EvalMode == o.EvalMode && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); + return o != null && this.EvalMode == o.EvalMode && this.TargetKind == o.TargetKind && Target.PerformMatch(o.Target, ref match) && Value.PerformMatch(o.Value, ref match); } } } @@ -2709,7 +2709,7 @@ namespace ICSharpCode.Decompiler.IL namespace ICSharpCode.Decompiler.IL { /// Serves as jump target for the nullable.unwrap instruction. - /// If the input evaluates normally, evaluates to the input value (wrapped in Nullable if the input is a non-nullable value type).If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap instruction evaluates to null. + /// If the input evaluates normally, evaluates to the input value (wrapped in Nullable<T> if the input is a non-nullable value type).If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction,the nullable.rewrap instruction evaluates to null. public sealed partial class NullableRewrap : UnaryInstruction { public NullableRewrap(ILInstruction argument) : base(OpCode.NullableRewrap, argument) @@ -4905,7 +4905,7 @@ namespace ICSharpCode.Decompiler.IL } namespace ICSharpCode.Decompiler.IL { - /// Use of user-defined && or || operator. + /// Use of user-defined && or || operator. public sealed partial class UserDefinedLogicOperator : ILInstruction, IInstructionWithMethodOperand { public UserDefinedLogicOperator(IMethod method, ILInstruction left, ILInstruction right) : base(OpCode.UserDefinedLogicOperator) diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index 4d63cc974..13552045b 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -74,19 +74,23 @@ CustomClassName("NumericCompoundAssign"), BaseClass("CompoundAssignmentInstruction"), CustomConstructor, CustomComputeFlags, MayThrow, HasTypeOperand, ResultType("type.GetStackType()"), CustomWriteTo, MatchCondition("CheckForOverflow == o.CheckForOverflow && Sign == o.Sign && Operator == o.Operator"), + MatchCondition("this.EvalMode == o.EvalMode"), + MatchCondition("this.TargetKind == o.TargetKind"), MatchCondition("Target.PerformMatch(o.Target, ref match)"), MatchCondition("Value.PerformMatch(o.Value, ref match)")), new OpCode("user.compound", "Common instruction for user-defined compound assignments.", CustomClassName("UserDefinedCompoundAssign"), BaseClass("CompoundAssignmentInstruction"), CustomConstructor, MayThrow, SideEffect, CustomWriteTo, MatchCondition("this.Method.Equals(o.Method)"), - MatchCondition("this.CompoundAssignmentType == o.CompoundAssignmentType"), + MatchCondition("this.EvalMode == o.EvalMode"), + MatchCondition("this.TargetKind == o.TargetKind"), MatchCondition("Target.PerformMatch(o.Target, ref match)"), MatchCondition("Value.PerformMatch(o.Value, ref match)")), new OpCode("dynamic.compound", "Common instruction for dynamic compound assignments.", CustomClassName("DynamicCompoundAssign"), BaseClass("CompoundAssignmentInstruction"), MayThrow, SideEffect, CustomWriteTo, CustomConstructor, ResultType("O"), - MatchCondition("this.CompoundAssignmentType == o.CompoundAssignmentType"), + MatchCondition("this.EvalMode == o.EvalMode"), + MatchCondition("this.TargetKind == o.TargetKind"), MatchCondition("Target.PerformMatch(o.Target, ref match)"), MatchCondition("Value.PerformMatch(o.Value, ref match)")), new OpCode("bit.not", "Bitwise NOT", Unary, CustomConstructor, MatchCondition("IsLifted == o.IsLifted && UnderlyingResultType == o.UnderlyingResultType")), @@ -189,7 +193,7 @@ + "returns the managed reference unmodified (if the value is non-null).", Unary, CustomConstructor, CustomWriteTo, HasFlag("InstructionFlags.MayUnwrapNull")), new OpCode("nullable.rewrap", "Serves as jump target for the nullable.unwrap instruction." + Environment.NewLine - + "If the input evaluates normally, evaluates to the input value (wrapped in Nullable if the input is a non-nullable value type)." + + "If the input evaluates normally, evaluates to the input value (wrapped in Nullable<T> if the input is a non-nullable value type)." + "If a nullable.unwrap instruction encounters a null input and jumps to the (endpoint of the) nullable.rewrap instruction," + "the nullable.rewrap instruction evaluates to null.", Unary, CustomComputeFlags), @@ -281,7 +285,7 @@ CustomClassName("ExpressionTreeCast"), Unary, HasTypeOperand, MayThrow, CustomConstructor, CustomWriteTo, ResultType("type.GetStackType()"), MatchCondition("this.IsChecked == o.IsChecked")), - new OpCode("user.logic.operator", "Use of user-defined && or || operator.", + new OpCode("user.logic.operator", "Use of user-defined && or || operator.", CustomClassName("UserDefinedLogicOperator"), HasMethodOperand, ResultType("O"), CustomChildren(new []{ diff --git a/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs index 9f22e0d34..fb28f1c7b 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/IfInstruction.cs @@ -30,7 +30,7 @@ namespace ICSharpCode.Decompiler.IL /// /// IfInstruction is also used to represent logical operators: /// "a || b" ==> if (a) (ldc.i4 1) else (b) - /// "a && b" ==> if (a) (b) else (ldc.i4 0) + /// "a && b" ==> if (a) (b) else (ldc.i4 0) /// "a ? b : c" ==> if (a) (b) else (c) /// partial class IfInstruction : ILInstruction diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index d4ec96350..6ed29237d 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -373,7 +373,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// Determines whether a variable should be inlined in non-aggressive mode, even though it is not a generated variable. /// /// The next top-level expression - /// The load within 'next' + /// The variable being eliminated by inlining. /// The expression being inlined static bool NonAggressiveInlineInto(ILInstruction next, FindResult findResult, ILInstruction inlinedExpression, ILVariable v) { diff --git a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs index b54adc273..b10c15239 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs @@ -57,8 +57,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms } /// - /// Newer Roslyn versions use the format "<callerName>g__functionName|x_y" - /// Older versions use "<callerName>g__functionNamex_y" + /// Newer Roslyn versions use the format "<callerName>g__functionName|x_y" + /// Older versions use "<callerName>g__functionNamex_y" /// static readonly Regex functionNameRegex = new Regex(@"^<(.*)>g__(.*)\|{0,1}\d+(_\d+)?$", RegexOptions.Compiled); diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs index 85e2bcc76..0b1f617d7 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs @@ -66,10 +66,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// VS2017.8 / Roslyn 2.9 started optimizing some cases of - /// "a.GetValueOrDefault() == b.GetValueOrDefault() && (a.HasValue & b.HasValue)" + /// "a.GetValueOrDefault() == b.GetValueOrDefault() && (a.HasValue & b.HasValue)" /// to - /// "(a.GetValueOrDefault() == b.GetValueOrDefault()) & (a.HasValue & b.HasValue)" - /// so this secondary entry point analyses logic.and as-if it was a short-circuting &&. + /// "(a.GetValueOrDefault() == b.GetValueOrDefault()) & (a.HasValue & b.HasValue)" + /// so this secondary entry point analyses logic.and as-if it was a short-circuting &&. /// public bool Run(BinaryNumericInstruction bni) { @@ -85,7 +85,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms public bool RunStatements(Block block, int pos) { - /// e.g.: + // e.g.: // if (!condition) Block { // leave IL_0000 (default.value System.Nullable`1[[System.Int64]]) // } @@ -541,7 +541,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// Performs nullable lifting. /// /// Produces a lifted instruction with semantics equivalent to: - /// (v1 != null && ... && vn != null) ? trueInst : falseInst, + /// (v1 != null && ... && vn != null) ? trueInst : falseInst, /// where the v1,...,vn are the this.nullableVars. /// If lifting fails, returns null. /// diff --git a/ICSharpCode.Decompiler/IL/Transforms/Stepper.cs b/ICSharpCode.Decompiler/IL/Transforms/Stepper.cs index 87175e5c2..0645066bc 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/Stepper.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/Stepper.cs @@ -25,7 +25,7 @@ using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { /// - /// Exception thrown when an IL transform runs into the limit. + /// Exception thrown when an IL transform runs into the . /// public class StepLimitReachedException : Exception { diff --git a/ICSharpCode.Decompiler/TypeSystem/IParameter.cs b/ICSharpCode.Decompiler/TypeSystem/IParameter.cs index 68f10c532..9a871d36b 100644 --- a/ICSharpCode.Decompiler/TypeSystem/IParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/IParameter.cs @@ -47,10 +47,10 @@ namespace ICSharpCode.Decompiler.TypeSystem /// Gets whether this parameter is a C# 'params' parameter. /// bool IsParams { get; } - + /// /// Gets whether this parameter is optional. - /// The default value is given by the property. + /// The default value is given by the function. /// bool IsOptional { get; } diff --git a/ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs index 722b550d5..0db477fb8 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs @@ -30,10 +30,10 @@ namespace ICSharpCode.Decompiler.TypeSystem /// /// SymbolKind.TypeDefinition or SymbolKind.Method SymbolKind OwnerType { get; } - + /// /// Gets the owning method/class. - /// This property may return null (for example for the dummy type parameters used by ). + /// This property may return null (for example for the dummy type parameters used by ). /// /// /// For "class Outer<T> { class Inner {} }", diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/ThreeState.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/ThreeState.cs index f558911e0..6b679f39e 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/ThreeState.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/ThreeState.cs @@ -19,7 +19,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation { /// - /// Constants used instead of + /// Constants used instead of bool? /// in multithreaded code, as bool? might produce torn reads. /// static class ThreeState diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeKind.cs b/ICSharpCode.Decompiler/TypeSystem/TypeKind.cs index 9556ef249..5f107d6fa 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeKind.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeKind.cs @@ -41,7 +41,7 @@ namespace ICSharpCode.Decompiler.TypeSystem Enum, /// The System.Void type. - /// + /// Void, /// Type used for invalid expressions and for types whose definition could not be found. @@ -58,7 +58,7 @@ namespace ICSharpCode.Decompiler.TypeSystem Dynamic, /// Represents missing type arguments in partially parameterized types. /// - /// + /// IType.GetNestedTypes(Predicate{ITypeDefinition}, GetMemberOptions) UnboundTypeArgument, /// The type is a type parameter. @@ -74,9 +74,6 @@ namespace ICSharpCode.Decompiler.TypeSystem /// A managed reference type /// ByReference, - /// An anonymous type - /// - Anonymous, /// Intersection of several types /// diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs index 34631f0a1..78c5a1892 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs @@ -384,9 +384,9 @@ namespace ICSharpCode.Decompiler.TypeSystem /// (if the given in an override) /// should be returned. /// - public static bool HasAttribute(this IEntity entity, KnownAttribute attrType, bool inherit=false) + public static bool HasAttribute(this IEntity entity, KnownAttribute attributeType, bool inherit=false) { - return GetAttribute(entity, attrType, inherit) != null; + return GetAttribute(entity, attributeType, inherit) != null; } /// @@ -445,9 +445,9 @@ namespace ICSharpCode.Decompiler.TypeSystem /// /// The parameter on which the attributes are declared. /// The attribute type to look for. - public static bool HasAttribute(this IParameter parameter, KnownAttribute attrType) + public static bool HasAttribute(this IParameter parameter, KnownAttribute attributeType) { - return GetAttribute(parameter, attrType) != null; + return GetAttribute(parameter, attributeType) != null; } /// From f3f38783df5464a349126a140eb376d8fbeeb657 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 17 Jul 2019 21:45:52 +0200 Subject: [PATCH 067/221] ICSharpCode.Decompiler roundtrip test now completes --- ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs | 6 +----- .../IL/Transforms/LocalFunctionDecompiler.cs | 4 ++-- .../IL/Transforms/TransformDisplayClassUsage.cs | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs b/ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs index d6f969ec3..975c92511 100644 --- a/ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs +++ b/ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs @@ -69,11 +69,7 @@ namespace ICSharpCode.Decompiler.Tests [Test] public void ICSharpCode_Decompiler() { - try { - RunWithTest("ICSharpCode.Decompiler", "ICSharpCode.Decompiler.dll", "ICSharpCode.Decompiler.Tests.exe"); - } catch (TestRunFailedException) { - Assert.Ignore("CorrectnessTestRunner.UndocumentedExpressions fails on recompilation."); - } + RunWithTest("ICSharpCode.Decompiler", "ICSharpCode.Decompiler.dll", "ICSharpCode.Decompiler.Tests.exe"); } [Test] diff --git a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs index ab3fd8523..822054648 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs @@ -256,8 +256,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms } /// - /// Newer Roslyn versions use the format "<callerName>g__functionName|x_y" - /// Older versions use "<callerName>g__functionNamex_y" + /// Newer Roslyn versions use the format "<callerName>g__functionName|x_y" + /// Older versions use "<callerName>g__functionNamex_y" /// static readonly Regex functionNameRegex = new Regex(@"^<(.*)>g__([^\|]*)\|{0,1}\d+(_\d+)?$", RegexOptions.Compiled); diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index 6caa8ed41..027d96722 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -338,7 +338,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (inst.Parent is LdObj || inst.Parent is StObj) return; // Get display class info - if (!(inst.Target is LdLoc displayClassLoad && displayClasses.TryGetValue(displayClassLoad.Variable, out var displayClass))) + if (!IsDisplayClassLoad(inst.Target, out var displayClassLoad) || !displayClasses.TryGetValue(displayClassLoad, out var displayClass)) return; var field = (IField)inst.Field.MemberDefinition; if (!displayClass.Variables.TryGetValue(field, out DisplayClassVariable info)) { From 33c7425fa2564dd57ceaeb6c8f843eb8f1a0ac72 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Thu, 18 Jul 2019 20:42:01 +0200 Subject: [PATCH 068/221] Fix several bugs with compound assignments on indexers. Closes #1580. --- .../Pretty/CompoundAssignmentTest.cs | 28 +++++-- .../IL/Transforms/ILInlining.cs | 8 ++ .../IL/Transforms/TransformAssignment.cs | 84 +++++++++++++++---- .../Transforms/TransformDisplayClassUsage.cs | 10 ++- 4 files changed, 104 insertions(+), 26 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs index 5244e21c0..5826fb09f 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs @@ -4577,12 +4577,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty new CustomClass().StringProp += 1; } -#if false public uint PreIncrementIndexer(string name) { return ++M()[name]; } -#endif + public int PreIncrementByRef(ref int i) { return ++i; @@ -4593,6 +4592,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return ++(*GetPointer()); } + public unsafe int PreIncrementOfPointer(int* ptr) + { + return *(++ptr); + } + public int PreIncrement2DArray() { return ++Array()[1, 2]; @@ -4627,12 +4631,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { return array[Environment.TickCount] *= 10; } -#if false + public uint CompoundAssignIndexer(string name) { - return M()[name] -= 2; + return M()[name] -= 2u; } -#endif + + public uint CompoundAssignIndexerComplexIndex(string name) + { + return M()[ToString()] -= 2u; + } + public int CompoundAssignIncrement2DArray() { return Array()[1, 2] %= 10; @@ -4643,6 +4652,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return i <<= 2; } + public unsafe int* CompoundAssignOfPointer(int* ptr) + { + return ptr += 10; + } + public unsafe double CompoundAssignByPointer(double* ptr) { return *ptr /= 1.5; @@ -4669,17 +4683,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { return array[pos]--; } -#if false + public uint PostIncrementIndexer(string name) { return M()[name]++; } +#if false public unsafe int PostIncrementOfPointer(int* ptr) { return *(ptr++); } #endif + public int PostDecrementInstanceField() { return M().Field--; diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index 6ed29237d..46559fd4f 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -432,6 +432,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms case OpCode.ArrayToPointer: case OpCode.LocAllocSpan: return true; // inline size-expressions into localloc.span + case OpCode.Call: + case OpCode.CallVirt: + // Aggressive inline into property/indexer getter calls for compound assignment calls + // (The compiler generates locals for these because it doesn't want to evalute the args twice for getter+setter) + if (parent.SlotInfo == CompoundAssignmentInstruction.TargetSlot) { + return true; + } + break; } // decide based on the top-level target instruction into which we are inlining: switch (next.OpCode) { diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs index 1644416ec..5fe7298ce 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs @@ -174,10 +174,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // because the ExpressionTransforms don't look into inline blocks, manually trigger HandleCallCompoundAssign if (HandleCompoundAssign(call, context)) { // if we did construct a compound assignment, it should have made our inline block redundant: - if (inlineBlock.Instructions.Single().MatchStLoc(newVar, out var compoundAssign)) { - Debug.Assert(newVar.IsSingleDefinition && newVar.LoadCount == 1); - inlineBlock.ReplaceWith(compoundAssign); - } + Debug.Assert(!inlineBlock.IsConnected); } return true; } else { @@ -205,8 +202,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - static bool MatchingGetterAndSetterCalls(CallInstruction getterCall, CallInstruction setterCall) + static bool MatchingGetterAndSetterCalls(CallInstruction getterCall, CallInstruction setterCall, out Action finalizeMatch) { + finalizeMatch = null; if (getterCall == null || setterCall == null || !IsSameMember(getterCall.Method.AccessorOwner, setterCall.Method.AccessorOwner)) return false; if (setterCall.OpCode != getterCall.OpCode) @@ -218,12 +216,33 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; // Ensure that same arguments are passed to getterCall and setterCall: for (int j = 0; j < getterCall.Arguments.Count; j++) { + if (setterCall.Arguments[j].MatchStLoc(out var v) && v.IsSingleDefinition && v.LoadCount == 1) { + if (getterCall.Arguments[j].MatchLdLoc(v)) { + // OK, setter call argument is saved in temporary that is re-used for getter call + if (finalizeMatch == null) { + finalizeMatch = AdjustArguments; + } + continue; + } + } if (!SemanticHelper.IsPure(getterCall.Arguments[j].Flags)) return false; if (!getterCall.Arguments[j].Match(setterCall.Arguments[j]).Success) return false; } return true; + + void AdjustArguments(ILTransformContext context) + { + Debug.Assert(setterCall.Arguments.Count == getterCall.Arguments.Count + 1); + for (int j = 0; j < getterCall.Arguments.Count; j++) { + if (setterCall.Arguments[j].MatchStLoc(out var v, out var value)) { + Debug.Assert(v.IsSingleDefinition && v.LoadCount == 1); + Debug.Assert(getterCall.Arguments[j].MatchLdLoc(v)); + getterCall.Arguments[j] = value; + } + } + } } /// @@ -275,18 +294,19 @@ namespace ICSharpCode.Decompiler.IL.Transforms } ILInstruction newInst; if (UnwrapSmallIntegerConv(setterValue, out var smallIntConv) is BinaryNumericInstruction binary) { - if (!IsMatchingCompoundLoad(binary.Left, compoundStore, out var target, out var targetKind, forbiddenVariable: storeInSetter?.Variable)) + if (!IsMatchingCompoundLoad(binary.Left, compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable)) return false; if (!ValidateCompoundAssign(binary, smallIntConv, targetType)) return false; context.Step($"Compound assignment (binary.numeric)", compoundStore); + finalizeMatch?.Invoke(context); newInst = new NumericCompoundAssign( binary, target, targetKind, binary.Right, targetType, CompoundEvalMode.EvaluatesToNewValue); } else if (setterValue is Call operatorCall && operatorCall.Method.IsOperator) { if (operatorCall.Arguments.Count == 0) return false; - if (!IsMatchingCompoundLoad(operatorCall.Arguments[0], compoundStore, out var target, out var targetKind, forbiddenVariable: storeInSetter?.Variable)) + if (!IsMatchingCompoundLoad(operatorCall.Arguments[0], compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable)) return false; ILInstruction rhs; if (operatorCall.Arguments.Count == 2) { @@ -304,12 +324,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (operatorCall.IsLifted) return false; // TODO: add tests and think about whether nullables need special considerations context.Step($"Compound assignment (user-defined binary)", compoundStore); + finalizeMatch?.Invoke(context); newInst = new UserDefinedCompoundAssign(operatorCall.Method, CompoundEvalMode.EvaluatesToNewValue, target, targetKind, rhs); } else if (setterValue is DynamicBinaryOperatorInstruction dynamicBinaryOp) { - if (!IsMatchingCompoundLoad(dynamicBinaryOp.Left, compoundStore, out var target, out var targetKind, forbiddenVariable: storeInSetter?.Variable)) + if (!IsMatchingCompoundLoad(dynamicBinaryOp.Left, compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable)) return false; context.Step($"Compound assignment (dynamic binary)", compoundStore); + finalizeMatch?.Invoke(context); newInst = new DynamicCompoundAssign(dynamicBinaryOp.Operation, dynamicBinaryOp.BinderFlags, target, dynamicBinaryOp.LeftArgumentInfo, dynamicBinaryOp.Right, dynamicBinaryOp.RightArgumentInfo, targetKind); } else if (setterValue is Call concatCall && UserDefinedCompoundAssign.IsStringConcat(concatCall.Method)) { // setterValue is a string.Concat() invocation @@ -317,9 +339,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; // for now we only support binary compound assignments if (!targetType.IsKnownType(KnownTypeCode.String)) return false; - if (!IsMatchingCompoundLoad(concatCall.Arguments[0], compoundStore, out var target, out var targetKind, forbiddenVariable: storeInSetter?.Variable)) + if (!IsMatchingCompoundLoad(concatCall.Arguments[0], compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable)) return false; context.Step($"Compound assignment (string concatenation)", compoundStore); + finalizeMatch?.Invoke(context); newInst = new UserDefinedCompoundAssign(concatCall.Method, CompoundEvalMode.EvaluatesToNewValue, target, targetKind, concatCall.Arguments[1]); } else { @@ -332,6 +355,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms context.RequestRerun(); // moving stloc to top-level might trigger inlining } compoundStore.ReplaceWith(newInst); + if (newInst.Parent is Block inlineAssignBlock && inlineAssignBlock.Kind == BlockKind.CallInlineAssign) { + // It's possible that we first replaced the instruction in an inline-assign helper block. + // In such a situation, we know from the block invariant that we're have a storeInSetter. + Debug.Assert(storeInSetter != null); + Debug.Assert(storeInSetter.Variable.IsSingleDefinition && storeInSetter.Variable.LoadCount == 1); + Debug.Assert(inlineAssignBlock.Instructions.Single() == storeInSetter); + Debug.Assert(inlineAssignBlock.FinalInstruction.MatchLdLoc(storeInSetter.Variable)); + // Block CallInlineAssign { stloc I_0(compound.op(...)); final: ldloc I_0 } + // --> compound.op(...) + inlineAssignBlock.ReplaceWith(storeInSetter.Value); + } return true; } @@ -468,6 +502,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } foreach (var arg in call.Arguments.SkipLast(1)) { + if (arg.MatchStLoc(out var v) && v.IsSingleDefinition && v.LoadCount == 1) { + continue; // OK, IsMatchingCompoundLoad can perform an adjustment in this special case + } if (!SemanticHelper.IsPure(arg.Flags)) { return false; } @@ -484,13 +521,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } + /// + /// Checks whether 'load' and 'store' both access the same store, and can be combined to a compound assignment. + /// + /// The load instruction to test. + /// The compound store to test against. Must have previously been tested via IsCompoundStore() + /// The target to use for the compound assignment instruction. + /// The target kind to use for the compound assignment instruction. + /// If set to a non-null value, call this delegate to fix up minor mismatches between getter and setter. + /// + /// If given a non-null value, this function returns false if the forbiddenVariable is used in the load/store instructions. + /// Some transforms effectively move a store around, + /// which is only valid if the variable stored to does not occur in the compound load/store. + /// static bool IsMatchingCompoundLoad(ILInstruction load, ILInstruction store, out ILInstruction target, out CompoundTargetKind targetKind, - ILFunction contextFunction = null, + out Action finalizeMatch, ILVariable forbiddenVariable = null) { target = null; targetKind = 0; + finalizeMatch = null; if (load is LdObj ldobj && store is StObj stobj) { Debug.Assert(SemanticHelper.IsPure(stobj.Target.Flags)); if (!SemanticHelper.IsPure(ldobj.Target.Flags)) @@ -500,7 +551,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms target = ldobj.Target; targetKind = CompoundTargetKind.Address; return ldobj.Target.Match(stobj.Target).Success; - } else if (MatchingGetterAndSetterCalls(load as CallInstruction, store as CallInstruction)) { + } else if (MatchingGetterAndSetterCalls(load as CallInstruction, store as CallInstruction, out finalizeMatch)) { if (forbiddenVariable != null && forbiddenVariable.IsUsedWithin(load)) return false; target = load; @@ -509,11 +560,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms } else if (load is LdLoc ldloc && store is StLoc stloc && ILVariableEqualityComparer.Instance.Equals(ldloc.Variable, stloc.Variable)) { if (ILVariableEqualityComparer.Instance.Equals(ldloc.Variable, forbiddenVariable)) return false; - if (contextFunction == null) - return false; // locals only supported for the callers that specify the context target = new LdLoca(ldloc.Variable).WithILRange(ldloc); targetKind = CompoundTargetKind.Address; - contextFunction.RecombineVariables(ldloc.Variable, stloc.Variable); + finalizeMatch = context => context.Function.RecombineVariables(ldloc.Variable, stloc.Variable); return true; } else { return false; @@ -566,11 +615,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!(stloc.Variable.Kind == VariableKind.Local || stloc.Variable.Kind == VariableKind.StackSlot)) return false; - if (!IsMatchingCompoundLoad(stloc.Value, store, out var target, out var targetKind, forbiddenVariable: stloc.Variable)) + if (!IsMatchingCompoundLoad(stloc.Value, store, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: stloc.Variable)) return false; if (IsImplicitTruncation(stloc.Value, stloc.Variable.Type, context.TypeSystem)) return false; context.Step("TransformPostIncDecOperatorWithInlineStore", store); + finalizeMatch?.Invoke(context); if (binary != null) { block.Instructions[pos] = new StLoc(stloc.Variable, new NumericCompoundAssign( binary, target, targetKind, binary.Right, targetType, CompoundEvalMode.EvaluatesToOldValue)); @@ -614,7 +664,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // 'stloc tmp' is implicitly truncating the value return false; } - if (!IsMatchingCompoundLoad(inst.Value, store, out var target, out var targetKind, context.Function, forbiddenVariable: inst.Variable)) + if (!IsMatchingCompoundLoad(inst.Value, store, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: inst.Variable)) return false; if (UnwrapSmallIntegerConv(value, out var conv) is BinaryNumericInstruction binary) { if (!binary.Left.MatchLdLoc(tmpVar) || !binary.Right.MatchLdcI(1)) @@ -624,6 +674,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!ValidateCompoundAssign(binary, conv, targetType)) return false; context.Step("TransformPostIncDecOperator (builtin)", inst); + finalizeMatch?.Invoke(context); inst.Value = new NumericCompoundAssign(binary, target, targetKind, binary.Right, targetType, CompoundEvalMode.EvaluatesToOldValue); } else if (value is Call operatorCall && operatorCall.Method.IsOperator && operatorCall.Arguments.Count == 1) { @@ -634,6 +685,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (operatorCall.IsLifted) return false; // TODO: add tests and think about whether nullables need special considerations context.Step("TransformPostIncDecOperator (user-defined)", inst); + finalizeMatch?.Invoke(context); inst.Value = new UserDefinedCompoundAssign(operatorCall.Method, CompoundEvalMode.EvaluatesToOldValue, target, targetKind, new LdcI4(1)); } else { diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index 4096d28c6..913f97669 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -87,10 +87,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms VisitILFunction(f); } } - context.Step($"Remove instructions", function); - foreach (var store in instructionsToRemove) { - if (store.Parent is Block containingBlock) - containingBlock.Instructions.Remove(store); + if (instructionsToRemove.Count > 0) { + context.Step($"Remove instructions", function); + foreach (var store in instructionsToRemove) { + if (store.Parent is Block containingBlock) + containingBlock.Instructions.Remove(store); + } } } finally { instructionsToRemove.Clear(); From fb2cbf3d62b7cb81d612fca7c3f1f0af7a35849a Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Thu, 18 Jul 2019 20:50:15 +0200 Subject: [PATCH 069/221] Use instead of specifying the file name. --- ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index fb144691c..26610065b 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -16,7 +16,7 @@ false 7.2 - $(TargetDir)ICSharpCode.Decompiler.xml + true True ICSharpCode.Decompiler.snk From 37e14f43e3cadf9122032d66923d2960e40585d1 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 18 Jul 2019 20:13:37 +0200 Subject: [PATCH 070/221] Refactor/Clean up local functions representation in type system. --- .../TestCases/Pretty/LocalFunctions.cs | 10 +- ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 68 ++-- .../CSharp/ExpressionBuilder.cs | 3 +- .../Resolver/CSharpInvocationResolveResult.cs | 14 - .../LocalFunctionReferenceResolveResult.cs | 37 --- .../CSharp/Resolver/ReducedExtensionMethod.cs | 306 ------------------ .../CSharp/StatementBuilder.cs | 2 +- .../ICSharpCode.Decompiler.csproj | 3 +- .../IL/Instructions/ILFunction.cs | 69 +++- .../IL/Transforms/DelegateConstruction.cs | 5 +- .../IL/Transforms/LocalFunctionDecompiler.cs | 177 +++++++--- .../Output/TextTokenWriter.cs | 5 +- ICSharpCode.Decompiler/TypeSystem/IMethod.cs | 4 +- .../TypeSystem/Implementation/FakeMember.cs | 1 + .../Implementation/LocalFunctionMethod.cs | 139 ++++++++ .../Implementation/MetadataMethod.cs | 1 + .../Implementation/SpecializedMethod.cs | 8 +- .../TypeSystem/VarArgInstanceMethod.cs | 2 + 18 files changed, 398 insertions(+), 456 deletions(-) delete mode 100644 ICSharpCode.Decompiler/CSharp/Resolver/LocalFunctionReferenceResolveResult.cs delete mode 100644 ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs create mode 100644 ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs index 33bcab1e2..806e9a57f 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs @@ -167,27 +167,21 @@ namespace LocalFunctions public static Func LambdaInLocalFunction() { int x = (int)Math.Pow(2.0, 10.0); - Enumerable.Range(1, 100).Select((Func)Transform); return Create(); Func Create() { return () => x; } - - int Transform(int y) - { - return 2 * y; - } } public static Func MethodRef() { int x = (int)Math.Pow(2.0, 10.0); - Enumerable.Range(1, 100).Select((Func)F); + Enumerable.Range(1, 100).Select(LocalFunction); return null; - int F(int y) + int LocalFunction(int y) { return x * y; } diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index c674d8589..3cc2ed7e2 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -38,7 +38,6 @@ namespace ICSharpCode.Decompiler.CSharp { public OpCode CallOpCode; public bool NeedsBoxingConversion; - public bool IsLocalFunction; } struct ArgumentList @@ -53,18 +52,13 @@ namespace ICSharpCode.Decompiler.CSharp public bool AddNamesToPrimitiveValues; public bool IsExpandedForm; - public int LocalFunctionParameterCount; public int Length => Arguments.Length; private int GetActualArgumentCount() { - if (LocalFunctionParameterCount < 0 && FirstOptionalArgumentIndex < 0) - return Arguments.Length; - if (LocalFunctionParameterCount < 0) - return FirstOptionalArgumentIndex; if (FirstOptionalArgumentIndex < 0) - return LocalFunctionParameterCount; - return Math.Min(FirstOptionalArgumentIndex, LocalFunctionParameterCount); + return Arguments.Length; + return FirstOptionalArgumentIndex; } public IEnumerable GetArgumentResolveResults(int skipCount = 0) @@ -180,20 +174,19 @@ namespace ICSharpCode.Decompiler.CSharp { // Used for Call, CallVirt and NewObj var expectedTargetDetails = new ExpectedTargetDetails { - CallOpCode = callOpCode, - IsLocalFunction = expressionBuilder.IsLocalFunction(method) + CallOpCode = callOpCode }; ILFunction localFunction = null; - if (expectedTargetDetails.IsLocalFunction && (localFunction = expressionBuilder.ResolveLocalFunction(method)) == null) { - expectedTargetDetails.IsLocalFunction = false; + if (method.IsLocalFunction) { + localFunction = expressionBuilder.ResolveLocalFunction(method); } TranslatedExpression target; if (callOpCode == OpCode.NewObj) { target = default(TranslatedExpression); // no target - } else if (expectedTargetDetails.IsLocalFunction) { + } else if (method.IsLocalFunction) { target = new IdentifierExpression(localFunction.Name) .WithoutILInstruction() - .WithRR(new LocalFunctionReferenceResolveResult(localFunction)); + .WithRR(ToMethodGroup(method, localFunction)); } else { target = expressionBuilder.TranslateTarget( callArguments.FirstOrDefault(), @@ -221,17 +214,7 @@ namespace ICSharpCode.Decompiler.CSharp var argumentList = BuildArgumentList(expectedTargetDetails, target.ResolveResult, method, firstParamIndex, callArguments, argumentToParameterMap); - if (expectedTargetDetails.IsLocalFunction) { - int parameterCount = 0; - foreach (var param in method.Parameters) { - if (param.IsRef && param.Type is ByReferenceType byRef) { - var type = byRef.ElementType.GetDefinition(); - if (type != null && type.IsCompilerGenerated()) - break; - } - parameterCount++; - } - argumentList.LocalFunctionParameterCount = parameterCount; + if (method.IsLocalFunction) { return new InvocationExpression(target, argumentList.GetArgumentExpressions()) .WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method, argumentList.GetArgumentResolveResults().ToList(), isExpandedForm: argumentList.IsExpandedForm, isLocalFunctionInvocation: true)); @@ -412,7 +395,7 @@ namespace ICSharpCode.Decompiler.CSharp argumentList.ArgumentNames = null; argumentList.AddNamesToPrimitiveValues = false; var transform = GetRequiredTransformationsForCall(expectedTargetDetails, method, ref unused, - ref argumentList, CallTransformation.None, out IParameterizedMember foundMethod); + ref argumentList, CallTransformation.None, out _); Debug.Assert(transform == CallTransformation.None || transform == CallTransformation.NoOptionalArgumentAllowed); // Calls with only one argument do not need an array initializer expression to wrap them. @@ -591,12 +574,10 @@ namespace ICSharpCode.Decompiler.CSharp } } - private ArgumentList BuildArgumentList(ExpectedTargetDetails expectedTargetDetails, ResolveResult target, IMethod method, int firstParamIndex, IReadOnlyList callArguments, IReadOnlyList argumentToParameterMap) { ArgumentList list = new ArgumentList(); - list.LocalFunctionParameterCount = -1; // Translate arguments to the expected parameter types var arguments = new List(method.Parameters.Count); @@ -775,7 +756,7 @@ namespace ICSharpCode.Decompiler.CSharp if (expressionBuilder.HidesVariableWithName(method.Name)) { requireTarget = true; } else { - if (expectedTargetDetails.IsLocalFunction) + if (method.IsLocalFunction) requireTarget = false; else if (method.IsStatic) requireTarget = !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) || method.Name == ".cctor"; @@ -1139,8 +1120,7 @@ namespace ICSharpCode.Decompiler.CSharp } var op = AssignmentOperatorType.Assign; - var parentEvent = method.AccessorOwner as IEvent; - if (parentEvent != null) { + if (method.AccessorOwner is IEvent parentEvent) { if (method.Equals(parentEvent.AddAccessor)) { op = AssignmentOperatorType.Add; } @@ -1243,15 +1223,14 @@ namespace ICSharpCode.Decompiler.CSharp IType targetType; bool requireTarget; var expectedTargetDetails = new ExpectedTargetDetails { - CallOpCode = inst.OpCode, - IsLocalFunction = expressionBuilder.IsLocalFunction(method) + CallOpCode = inst.OpCode }; ResolveResult result = null; string methodName; - if (expectedTargetDetails.IsLocalFunction) { + if (method.IsLocalFunction) { requireTarget = false; var localFunction = expressionBuilder.ResolveLocalFunction(method); - result = new LocalFunctionReferenceResolveResult(localFunction); + result = ToMethodGroup(method, localFunction); target = default; methodName = localFunction.Name; } else { @@ -1321,15 +1300,32 @@ namespace ICSharpCode.Decompiler.CSharp .WithRR(result); targetExpression = ide; } + if (target.ResolveResult != null) { + result = new MemberResolveResult(target.ResolveResult, method); + } var oce = new ObjectCreateExpression(expressionBuilder.ConvertType(inst.Method.DeclaringType), targetExpression) .WithILInstruction(inst) .WithRR(new ConversionResolveResult( inst.Method.DeclaringType, - target.ResolveResult != null ? new MemberResolveResult(target.ResolveResult, method) : result, + result, Conversion.MethodGroupConversion(method, func.OpCode == OpCode.LdVirtFtn, false))); return oce; } + static MethodGroupResolveResult ToMethodGroup(IMethod method, ILFunction localFunction) + { + return new MethodGroupResolveResult( + null, + localFunction.Name, + new[] { + new MethodListWithDeclaringType( + method.DeclaringType, + new IParameterizedMember[] { method } + ) + }, EmptyList.Instance + ); + } + internal TranslatedExpression CallWithNamedArgs(Block block) { Debug.Assert(block.Kind == BlockKind.CallWithNamedArgs); diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index c6f5dca21..5fb95a319 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -214,7 +214,8 @@ namespace ICSharpCode.Decompiler.CSharp internal ILFunction ResolveLocalFunction(IMethod method) { - Debug.Assert(IsLocalFunction(method)); + Debug.Assert(method.IsLocalFunction); + method = method.ReducedFrom; foreach (var parent in currentFunction.Ancestors.OfType()) { var definition = parent.LocalFunctions.FirstOrDefault(f => f.Method == method); if (definition != null) { diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs index 823887940..784c6b9e5 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs @@ -53,20 +53,6 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver public readonly bool IsLocalFunctionInvocation; readonly IReadOnlyList argumentToParameterMap; - - /// - /// If IsExtensionMethodInvocation is true this property holds the reduced method. - /// - IMethod reducedMethod; - public IMethod ReducedMethod { - get { - if (!IsExtensionMethodInvocation) - return null; - if (reducedMethod == null && Member is IMethod) - reducedMethod = new ReducedExtensionMethod ((IMethod)Member); - return reducedMethod; - } - } public CSharpInvocationResolveResult( ResolveResult targetResult, IParameterizedMember member, diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/LocalFunctionReferenceResolveResult.cs b/ICSharpCode.Decompiler/CSharp/Resolver/LocalFunctionReferenceResolveResult.cs deleted file mode 100644 index e5253eba4..000000000 --- a/ICSharpCode.Decompiler/CSharp/Resolver/LocalFunctionReferenceResolveResult.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2019 Siegfried Pammer -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. -using System; -using System.Collections.Generic; -using System.Text; -using ICSharpCode.Decompiler.IL; -using ICSharpCode.Decompiler.Semantics; -using ICSharpCode.Decompiler.TypeSystem; - -namespace ICSharpCode.Decompiler.CSharp.Resolver -{ - class LocalFunctionReferenceResolveResult : ResolveResult - { - public readonly ILFunction Function; - - public LocalFunctionReferenceResolveResult(ILFunction function) - : base(SpecialType.NoType) - { - this.Function = function; - } - } -} diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs b/ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs deleted file mode 100644 index 185e626c0..000000000 --- a/ICSharpCode.Decompiler/CSharp/Resolver/ReducedExtensionMethod.cs +++ /dev/null @@ -1,306 +0,0 @@ -// -// ReducedExtensionMethod.cs -// -// Author: -// Mike Krüger -// -// Copyright (c) 2013 Xamarin Inc. (http://xamarin.com) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using ICSharpCode.Decompiler.TypeSystem; - -namespace ICSharpCode.Decompiler.CSharp.Resolver -{ - /// - /// An invocated extension method hides the extension parameter in its parameter list. - /// It's used to hide the internals of extension method invocation in certain situation to simulate the - /// syntactic way of writing extension methods on semantic level. - /// - public class ReducedExtensionMethod : IMethod - { - readonly IMethod baseMethod; - - public ReducedExtensionMethod(IMethod baseMethod) - { - this.baseMethod = baseMethod; - } - - public bool Equals(IMember obj, TypeVisitor typeNormalization) - { - var other = obj as ReducedExtensionMethod; - if (other == null) - return false; - return baseMethod.Equals(other.baseMethod, typeNormalization); - } - - public override bool Equals(object obj) - { - var other = obj as ReducedExtensionMethod; - if (other == null) - return false; - return baseMethod.Equals(other.baseMethod); - } - - public override int GetHashCode() - { - unchecked { - return baseMethod.GetHashCode() + 1; - } - } - - public override string ToString() - { - return string.Format("[ReducedExtensionMethod: ReducedFrom={0}]", ReducedFrom); - } - - #region IMember implementation - public IMember MemberDefinition { - get { - return baseMethod.MemberDefinition; - } - } - - public IType ReturnType { - get { - return baseMethod.ReturnType; - } - } - - public IEnumerable ExplicitlyImplementedInterfaceMembers { - get { - return baseMethod.ExplicitlyImplementedInterfaceMembers; - } - } - - public bool IsExplicitInterfaceImplementation { - get { - return baseMethod.IsExplicitInterfaceImplementation; - } - } - - public bool IsVirtual { - get { - return baseMethod.IsVirtual; - } - } - - public bool IsOverride { - get { - return baseMethod.IsOverride; - } - } - - public bool IsOverridable { - get { - return baseMethod.IsOverridable; - } - } - - public TypeParameterSubstitution Substitution { - get { - return baseMethod.Substitution; - } - } - - public IMethod Specialize(TypeParameterSubstitution substitution) - { - return new ReducedExtensionMethod((IMethod)baseMethod.Specialize(substitution)); - } - - IMember IMember.Specialize(TypeParameterSubstitution substitution) - { - return Specialize(substitution); - } - - #endregion - - #region IMethod implementation - - public IReadOnlyList TypeParameters { - get { - return baseMethod.TypeParameters; - } - } - - public bool IsExtensionMethod { - get { - return true; - } - } - - public bool IsConstructor { - get { - return baseMethod.IsConstructor; - } - } - - public bool IsDestructor { - get { - return baseMethod.IsDestructor; - } - } - - public bool IsOperator { - get { - return baseMethod.IsOperator; - } - } - - public bool HasBody { - get { - return baseMethod.HasBody; - } - } - - public bool IsAccessor => baseMethod.IsAccessor; - public IMember AccessorOwner => baseMethod.AccessorOwner; - public MethodSemanticsAttributes AccessorKind => baseMethod.AccessorKind; - - public IMethod ReducedFrom { - get { - return baseMethod; - } - } - - public IReadOnlyList TypeArguments { - get { - return baseMethod.TypeArguments; - } - } - #endregion - - #region IParameterizedMember implementation - List parameters; - public IReadOnlyList Parameters { - get { - if (parameters == null) - parameters = new List (baseMethod.Parameters.Skip (1)); - return parameters; - } - } - - #endregion - - #region IEntity implementation - - public System.Reflection.Metadata.EntityHandle MetadataToken => baseMethod.MetadataToken; - - public SymbolKind SymbolKind { - get { - return baseMethod.SymbolKind; - } - } - - public ITypeDefinition DeclaringTypeDefinition { - get { - return baseMethod.DeclaringTypeDefinition; - } - } - - public IType DeclaringType { - get { - return baseMethod.DeclaringType; - } - } - - public IModule ParentModule { - get { - return baseMethod.ParentModule; - } - } - - IEnumerable IEntity.GetAttributes() => baseMethod.GetAttributes(); - IEnumerable IMethod.GetReturnTypeAttributes() => baseMethod.GetReturnTypeAttributes(); - bool IMethod.ReturnTypeIsRefReadOnly => baseMethod.ReturnTypeIsRefReadOnly; - - public bool IsStatic { - get { - return false; - } - } - - public bool IsAbstract { - get { - return baseMethod.IsAbstract; - } - } - - public bool IsSealed { - get { - return baseMethod.IsSealed; - } - } - - #endregion - - #region IHasAccessibility implementation - - public Accessibility Accessibility { - get { - return baseMethod.Accessibility; - } - } - - #endregion - - #region INamedElement implementation - - public string FullName { - get { - return baseMethod.FullName; - } - } - - public string Name { - get { - return baseMethod.Name; - } - } - - public string ReflectionName { - get { - return baseMethod.ReflectionName; - } - } - - public string Namespace { - get { - return baseMethod.Namespace; - } - } - - #endregion - - #region ICompilationProvider implementation - - public ICompilation Compilation { - get { - return baseMethod.Compilation; - } - } - - #endregion - } -} - diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index 25d8658ba..9730fc1f9 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -968,7 +968,7 @@ namespace ICSharpCode.Decompiler.CSharp stmt.Parameters.AddRange(exprBuilder.MakeParameters(function.Parameters, function)); stmt.ReturnType = tsab.ConvertType(function.Method.ReturnType); stmt.Body = nestedBuilder.ConvertAsBlock(function.Body); - stmt.AddAnnotation(new LocalFunctionReferenceResolveResult(function)); + stmt.AddAnnotation(new MemberResolveResult(null, function.ReducedMethod)); return stmt; } } diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 85dac7988..0de498900 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -60,7 +60,6 @@ - @@ -224,7 +223,7 @@ - + diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs index 49d7b8011..29370678b 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs @@ -29,8 +29,30 @@ namespace ICSharpCode.Decompiler.IL { partial class ILFunction { + /// + /// Gets the method definition from metadata. + /// May be null for functions that were not constructed from metadata, + /// e.g., expression trees. + /// public readonly IMethod Method; + + /// + /// Gets the generic context of this function. + /// public readonly GenericContext GenericContext; + + /// + /// Gets the name of this function, usually this returns the name from metadata. + /// + /// For local functions: + /// This is the name that is used to declare and use the function. + /// It may not conflict with the names of local variables of ancestor functions + /// and may be overwritten by the AssignVariableNames step. + /// + /// For top-level functions, delegates and expressions trees modifying this usually + /// has no effect, as the name should not be used in the final AST construction. + /// + /// public string Name; /// @@ -38,6 +60,10 @@ namespace ICSharpCode.Decompiler.IL /// Note: after async/await transform, this is the code size of the MoveNext function. /// public int CodeSize; + + /// + /// List of ILVariables used in this function. + /// public readonly ILVariableCollection Variables; /// @@ -60,6 +86,9 @@ namespace ICSharpCode.Decompiler.IL /// public bool IsIterator; + /// + /// Gets whether the YieldReturnDecompiler determined that the Mono C# compiler was used to compile this function. + /// public bool StateMachineCompiledWithMono; /// @@ -79,6 +108,11 @@ namespace ICSharpCode.Decompiler.IL /// public IMethod MoveNextMethod; + /// + /// If this function is a local function, this field stores the reduced version of the function. + /// + internal TypeSystem.Implementation.LocalFunctionMethod ReducedMethod; + internal DebugInfo.AsyncDebugInfo AsyncDebugInfo; int ctorCallStart = int.MinValue; @@ -105,24 +139,43 @@ namespace ICSharpCode.Decompiler.IL /// /// If this is an expression tree or delegate, returns the expression tree type Expression{T} or T. /// T is the delegate type that matches the signature of this method. + /// Otherwise this must be null. /// public IType DelegateType; ILFunctionKind kind; + /// + /// Gets which kind of function this is. + /// public ILFunctionKind Kind { get => kind; - set { + internal set { if (kind == ILFunctionKind.TopLevelFunction || kind == ILFunctionKind.LocalFunction) throw new InvalidOperationException("ILFunction.Kind of a top-level or local function may not be changed."); kind = value; } } + /// + /// Return type of this function. + /// Might be null, if this function was not created from metadata. + /// public readonly IType ReturnType; + /// + /// List of parameters of this function. + /// Might be null, if this function was not created from metadata. + /// public readonly IReadOnlyList Parameters; + /// + /// Constructs a new ILFunction from the given metadata and with the given ILAst body. + /// + /// + /// Use to create ILAst. + /// may be null. + /// public ILFunction(IMethod method, int codeSize, GenericContext genericContext, ILInstruction body, ILFunctionKind kind = ILFunctionKind.TopLevelFunction) : base(OpCode.ILFunction) { this.Method = method; @@ -137,7 +190,10 @@ namespace ICSharpCode.Decompiler.IL this.kind = kind; } - public ILFunction(IType returnType, IReadOnlyList parameters, GenericContext genericContext, ILInstruction body) : base(OpCode.ILFunction) + /// + /// This constructor is only to be used by the TransformExpressionTrees step. + /// + internal ILFunction(IType returnType, IReadOnlyList parameters, GenericContext genericContext, ILInstruction body) : base(OpCode.ILFunction) { this.GenericContext = genericContext; this.Body = body; @@ -366,14 +422,23 @@ namespace ICSharpCode.Decompiler.IL /// /// ILFunction is a delegate or lambda expression. /// + /// + /// This kind is introduced by the DelegateConstruction and TransformExpressionTrees steps in the decompiler pipeline. + /// Delegate, /// /// ILFunction is an expression tree lambda. /// + /// + /// This kind is introduced by the TransformExpressionTrees step in the decompiler pipeline. + /// ExpressionTree, /// /// ILFunction is a C# 7.0 local function. /// + /// + /// This kind is introduced by the LocalFunctionDecompiler step in the decompiler pipeline. + /// LocalFunction } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs index 4b03fcfe8..b3f617029 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs @@ -24,7 +24,10 @@ using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.Transforms { - public class DelegateConstruction : IILTransform + /// + /// + /// + class DelegateConstruction : IILTransform { ILTransformContext context; ITypeResolveContext decompilationContext; diff --git a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs index 822054648..228611a51 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs @@ -28,38 +28,45 @@ using System.Text.RegularExpressions; using ICSharpCode.Decompiler.CSharp; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { + /// + /// Decompiler step for C# 7.0 local functions + /// class LocalFunctionDecompiler : IILTransform { ILTransformContext context; - ITypeResolveContext decompilationContext; + /// + /// The transform works like this: + /// + /// + /// local functions can either be used in method calls, i.e., call and callvirt instructions, + /// or can be used as part of the "delegate construction" pattern, i.e., newobj Delegate(<target-expression>, ldftn <method>). Note that this pattern would also be possible with ldvirtftn, but I haven't yet disovered a case where a compiler generates such code. + /// + /// As local functions can be declared practically anywhere, we have to take a look at all use-sites and infer the declaration location from that. + /// public void Run(ILFunction function, ILTransformContext context) { if (!context.Settings.LocalFunctions) return; this.context = context; - this.decompilationContext = new SimpleTypeResolveContext(function.Method); var localFunctions = new Dictionary>(); var cancellationToken = context.CancellationToken; // Find use-sites foreach (var inst in function.Descendants) { cancellationToken.ThrowIfCancellationRequested(); - if (inst is CallInstruction call && IsLocalFunctionMethod(call.Method)) { - if (function.Ancestors.OfType().SelectMany(f => f.LocalFunctions).Any(f => f.Method == call.Method)) - continue; + if (inst is CallInstruction call && IsLocalFunctionMethod(call.Method) && !call.Method.IsLocalFunction) { if (!localFunctions.TryGetValue(call.Method, out var info)) { info = new List() { call }; localFunctions.Add(call.Method, info); } else { info.Add(call); } - } else if (inst is LdFtn ldftn && ldftn.Parent is NewObj newObj && IsLocalFunctionMethod(ldftn.Method) && DelegateConstruction.IsDelegateConstruction(newObj)) { - if (function.Ancestors.OfType().SelectMany(f => f.LocalFunctions).Any(f => f.Method == ldftn.Method)) - continue; + } else if (inst is LdFtn ldftn && !ldftn.Method.IsLocalFunction && ldftn.Parent is NewObj newObj && IsLocalFunctionMethod(ldftn.Method) && DelegateConstruction.IsDelegateConstruction(newObj)) { context.StepStartGroup($"LocalFunctionDecompiler {ldftn.StartILOffset}", ldftn); if (!localFunctions.TryGetValue(ldftn.Method, out var info)) { info = new List() { newObj }; @@ -111,7 +118,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms internal static bool IsClosureParameter(IParameter parameter) { return parameter.IsRef - && ((ByReferenceType)parameter.Type).ElementType.GetDefinition()?.IsCompilerGenerated() == true; + && ((ByReferenceType)parameter.Type).ElementType + .GetDefinition()?.IsCompilerGenerated() == true; + } + + static IType UnwrapByRef(IType type) + { + if (type is ByReferenceType byRef) { + type = byRef.ElementType; + } + return type; } internal static ILInstruction GetStatement(ILInstruction inst) @@ -132,44 +148,49 @@ namespace ICSharpCode.Decompiler.IL.Transforms var genericContext = DelegateConstruction.GenericContextFromTypeArguments(targetMethod.Substitution); if (genericContext == null) return null; - var ilReader = context.CreateILReader(); - var body = context.PEFile.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress); - var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, ILFunctionKind.LocalFunction, context.CancellationToken); - // Embed the local function into the parent function's ILAst, so that "Show steps" can show - // how the local function body is being transformed. - parentFunction.LocalFunctions.Add(function); - function.DeclarationScope = (BlockContainer)parentFunction.Body; - function.CheckInvariant(ILPhase.Normal); - var nestedContext = new ILTransformContext(context, function); - function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is LocalFunctionDecompiler)), nestedContext); - if (IsNonLocalTarget(targetMethod, useSites, out var target)) { - Debug.Assert(target != null); - nestedContext.Step("LocalFunctionDecompiler (ReplaceDelegateTargetVisitor)", function); - var thisVar = function.Variables.SingleOrDefault(v => v.Index == -1 && v.Kind == VariableKind.Parameter); - function.AcceptVisitor(new DelegateConstruction.ReplaceDelegateTargetVisitor(target, thisVar)); + var function = parentFunction.Ancestors.OfType().SelectMany(f => f.LocalFunctions).FirstOrDefault(f => f.Method == targetMethod); + if (function == null) { + var ilReader = context.CreateILReader(); + var body = context.PEFile.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress); + function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, ILFunctionKind.LocalFunction, context.CancellationToken); + // Embed the local function into the parent function's ILAst, so that "Show steps" can show + // how the local function body is being transformed. + parentFunction.LocalFunctions.Add(function); + function.DeclarationScope = (BlockContainer)parentFunction.Body; + function.CheckInvariant(ILPhase.Normal); + var nestedContext = new ILTransformContext(context, function); + function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is LocalFunctionDecompiler)), nestedContext); + if (IsNonLocalTarget(targetMethod, useSites, out var target)) { + Debug.Assert(target != null); + nestedContext.Step("LocalFunctionDecompiler (ReplaceDelegateTargetVisitor)", function); + var thisVar = function.Variables.SingleOrDefault(v => v.Index == -1 && v.Kind == VariableKind.Parameter); + function.AcceptVisitor(new DelegateConstruction.ReplaceDelegateTargetVisitor(target, thisVar)); + } + function.DeclarationScope = null; + function.ReducedMethod = ReduceToLocalFunction(targetMethod); + + foreach (var innerUseSite in function.Descendants.OfType()) { + if (innerUseSite.Method != function.Method) + continue; + if (innerUseSite.OpCode == OpCode.NewObj) { + TransformToLocalFunctionReference(function, innerUseSite); + } else { + TransformToLocalFunctionInvocation(function.ReducedMethod, innerUseSite); + } + } } - function.DeclarationScope = null; foreach (var useSite in useSites) { - for (int i = useSite.Arguments.Count - 1; i >= 0; i--) { - if (!useSite.Arguments[i].MatchLdLocRef(out var closureVar)) - break; - if (!TransformDisplayClassUsage.IsPotentialClosure(context, closureVar.Type.GetDefinition())) - break; - var instructions = closureVar.StoreInstructions.OfType() - .Concat(closureVar.AddressInstructions).OrderBy(inst => inst.StartILOffset); - var additionalScope = BlockContainer.FindClosestContainer(instructions.First()); - if (closureVar.CaptureScope == null) - closureVar.CaptureScope = additionalScope; - else - closureVar.CaptureScope = FindCommonAncestorInstruction(closureVar.CaptureScope, additionalScope); - if (function.DeclarationScope == null) - function.DeclarationScope = closureVar.CaptureScope; - else - function.DeclarationScope = FindCommonAncestorInstruction(function.DeclarationScope, closureVar.CaptureScope); + if (useSite.OpCode == OpCode.NewObj) { + TransformToLocalFunctionReference(function, useSite); + } else { + DetermineCaptureAndDeclarationScope(function, useSite); + TransformToLocalFunctionInvocation(function.ReducedMethod, useSite); } } - if (function.DeclarationScope != null && function.DeclarationScope.Parent is ILFunction betterParentFunction) { + if (function.DeclarationScope != null + && parentFunction.LocalFunctions.Contains(function) + && function.DeclarationScope.Parent is ILFunction betterParentFunction) { parentFunction.LocalFunctions.Remove(function); betterParentFunction.LocalFunctions.Add(function); } @@ -177,6 +198,76 @@ namespace ICSharpCode.Decompiler.IL.Transforms return function; } + LocalFunctionMethod ReduceToLocalFunction(IMethod method) + { + int parametersToRemove = 0; + for (int i = method.Parameters.Count - 1; i >= 0; i--) { + if (!IsClosureParameter(method.Parameters[i])) + break; + parametersToRemove++; + } + return new LocalFunctionMethod(method, parametersToRemove); + } + + static void TransformToLocalFunctionReference(ILFunction function, CallInstruction useSite) + { + useSite.Arguments[0].ReplaceWith(new LdNull().WithILRange(useSite.Arguments[0])); + var fnptr = (IInstructionWithMethodOperand)useSite.Arguments[1]; + var replacement = new LdFtn(function.ReducedMethod).WithILRange((ILInstruction)fnptr); + useSite.Arguments[1].ReplaceWith(replacement); + } + + void TransformToLocalFunctionInvocation(LocalFunctionMethod reducedMethod, CallInstruction useSite) + { + bool wasInstanceCall = !useSite.Method.IsStatic; + var replacement = new Call(reducedMethod); + int firstArgumentIndex = wasInstanceCall ? 1 : 0; + int argumentCount = useSite.Arguments.Count; + int reducedArgumentCount = argumentCount - (reducedMethod.NumberOfCompilerGeneratedParameters + firstArgumentIndex); + replacement.Arguments.AddRange(useSite.Arguments.Skip(firstArgumentIndex).Take(reducedArgumentCount)); + // copy flags: + replacement.ConstrainedTo = useSite.ConstrainedTo; + replacement.ILStackWasEmpty = useSite.ILStackWasEmpty; + replacement.IsTail = useSite.IsTail; + // copy IL ranges + replacement = replacement.WithILRange(useSite); + if (wasInstanceCall) { + replacement = replacement.WithILRange(useSite.Arguments[0]); + } + for (int i = 0; i < reducedMethod.NumberOfCompilerGeneratedParameters; i++) { + replacement = replacement.WithILRange(useSite.Arguments[argumentCount - i - 1]); + } + useSite.ReplaceWith(replacement); + } + + void DetermineCaptureAndDeclarationScope(ILFunction function, CallInstruction useSite) + { + for (int i = useSite.Arguments.Count - 1; i >= 0; i--) { + var arg = useSite.Arguments[i]; + ILVariable closureVar; + if (!(arg.MatchLdLoc(out closureVar) || arg.MatchLdLoca(out closureVar))) + break; + if (closureVar.Kind == VariableKind.NamedArgument) + break; + if (!TransformDisplayClassUsage.IsPotentialClosure(context, UnwrapByRef(closureVar.Type).GetDefinition())) + break; + if (closureVar.AddressCount == 0 && closureVar.StoreInstructions.Count == 0) + continue; + // determine the capture scope of closureVar and the declaration scope of the function + var instructions = closureVar.StoreInstructions.OfType() + .Concat(closureVar.AddressInstructions).OrderBy(inst => inst.StartILOffset); + var additionalScope = BlockContainer.FindClosestContainer(instructions.First()); + if (closureVar.CaptureScope == null) + closureVar.CaptureScope = additionalScope; + else + closureVar.CaptureScope = FindCommonAncestorInstruction(closureVar.CaptureScope, additionalScope); + if (function.DeclarationScope == null) + function.DeclarationScope = closureVar.CaptureScope; + else + function.DeclarationScope = FindCommonAncestorInstruction(function.DeclarationScope, closureVar.CaptureScope); + } + } + bool IsNonLocalTarget(IMethod targetMethod, List useSites, out ILInstruction target) { target = null; @@ -275,7 +366,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms struct FindTypeDecoder : ISignatureTypeProvider { - TypeDefinitionHandle handle; + readonly TypeDefinitionHandle handle; public FindTypeDecoder(TypeDefinitionHandle handle) { diff --git a/ICSharpCode.Decompiler/Output/TextTokenWriter.cs b/ICSharpCode.Decompiler/Output/TextTokenWriter.cs index 3b572088f..85d62c704 100644 --- a/ICSharpCode.Decompiler/Output/TextTokenWriter.cs +++ b/ICSharpCode.Decompiler/Output/TextTokenWriter.cs @@ -24,6 +24,7 @@ using ICSharpCode.Decompiler.CSharp.OutputVisitor; using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.IL; +using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler @@ -177,9 +178,9 @@ namespace ICSharpCode.Decompiler } if (node is LocalFunctionDeclarationStatement) { - var localFunction = node.GetResolveResult() as LocalFunctionReferenceResolveResult; + var localFunction = node.GetResolveResult() as MemberResolveResult; if (localFunction != null) - return localFunction.Function.Method; + return localFunction.Member; } return null; diff --git a/ICSharpCode.Decompiler/TypeSystem/IMethod.cs b/ICSharpCode.Decompiler/TypeSystem/IMethod.cs index 17f29aab6..c53d1cc60 100644 --- a/ICSharpCode.Decompiler/TypeSystem/IMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/IMethod.cs @@ -53,6 +53,7 @@ namespace ICSharpCode.Decompiler.TypeSystem IReadOnlyList TypeArguments { get; } bool IsExtensionMethod { get; } + bool IsLocalFunction { get; } bool IsConstructor { get; } bool IsDestructor { get; } bool IsOperator { get; } @@ -81,8 +82,9 @@ namespace ICSharpCode.Decompiler.TypeSystem MethodSemanticsAttributes AccessorKind { get; } /// - /// If this method is reduced from an extension method return the original method, null otherwise. + /// If this method is reduced from an extension method or a local function returns the original method, null otherwise. /// A reduced method doesn't contain the extension method parameter. That means that it has one parameter less than its definition. + /// A local function doesn't contain compiler-generated method parameters at the end. /// IMethod ReducedFrom { get; } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs index 02a84fa79..02d439f84 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/FakeMember.cs @@ -139,6 +139,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation IReadOnlyList IMethod.TypeArguments => TypeParameters; bool IMethod.IsExtensionMethod => false; + bool IMethod.IsLocalFunction => false; bool IMethod.IsConstructor => symbolKind == SymbolKind.Constructor; bool IMethod.IsDestructor => symbolKind == SymbolKind.Destructor; bool IMethod.IsOperator => symbolKind == SymbolKind.Operator; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs new file mode 100644 index 000000000..be3201461 --- /dev/null +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs @@ -0,0 +1,139 @@ +// Copyright (c) 2019 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Reflection; +using ICSharpCode.Decompiler.Util; + +namespace ICSharpCode.Decompiler.TypeSystem.Implementation +{ + /// + /// A local function has zero or more compiler-generated parameters added at the end. + /// + class LocalFunctionMethod : IMethod + { + readonly IMethod baseMethod; + + public LocalFunctionMethod(IMethod baseMethod, int numberOfCompilerGeneratedParameters) + { + this.baseMethod = baseMethod; + this.NumberOfCompilerGeneratedParameters = numberOfCompilerGeneratedParameters; + } + + + public bool Equals(IMember obj, TypeVisitor typeNormalization) + { + if (!(obj is LocalFunctionMethod other)) + return false; + return baseMethod.Equals(other.baseMethod, typeNormalization); + } + + public override bool Equals(object obj) + { + var other = obj as LocalFunctionMethod; + if (other == null) + return false; + return baseMethod.Equals(other.baseMethod); + } + + public override int GetHashCode() + { + unchecked { + return baseMethod.GetHashCode() + 1; + } + } + + public override string ToString() + { + return string.Format("[LocalFunctionMethod: ReducedFrom={0}, NumberOfGeneratedParameters={1}]", ReducedFrom, NumberOfCompilerGeneratedParameters); + } + + internal int NumberOfCompilerGeneratedParameters { get; } + + public IMember MemberDefinition { + get { + if (baseMethod.MemberDefinition == baseMethod) + return this; + return new LocalFunctionMethod((IMethod)baseMethod.MemberDefinition, NumberOfCompilerGeneratedParameters); + } + } + + public IType ReturnType => baseMethod.ReturnType; + IEnumerable IMember.ExplicitlyImplementedInterfaceMembers => baseMethod.ExplicitlyImplementedInterfaceMembers; + bool IMember.IsExplicitInterfaceImplementation => baseMethod.IsExplicitInterfaceImplementation; + public bool IsVirtual => baseMethod.IsVirtual; + public bool IsOverride => baseMethod.IsOverride; + public bool IsOverridable => baseMethod.IsOverridable; + public TypeParameterSubstitution Substitution => baseMethod.Substitution; + + public IMethod Specialize(TypeParameterSubstitution substitution) + { + return SpecializedMethod.Create(this, substitution); + } + + IMember IMember.Specialize(TypeParameterSubstitution substitution) + { + return Specialize(substitution); + } + + public IReadOnlyList TypeParameters => baseMethod.TypeParameters; + public bool IsExtensionMethod => baseMethod.IsExtensionMethod; + public bool IsLocalFunction => true; + public bool IsConstructor => baseMethod.IsConstructor; + public bool IsDestructor => baseMethod.IsDestructor; + public bool IsOperator => baseMethod.IsOperator; + public bool HasBody => baseMethod.HasBody; + public bool IsAccessor => baseMethod.IsAccessor; + public IMember AccessorOwner => baseMethod.AccessorOwner; + public MethodSemanticsAttributes AccessorKind => baseMethod.AccessorKind; + public IMethod ReducedFrom => baseMethod; + public IReadOnlyList TypeArguments => baseMethod.TypeArguments; + + List parameters; + public IReadOnlyList Parameters { + get { + if (parameters == null) + parameters = new List(baseMethod.Parameters.SkipLast(NumberOfCompilerGeneratedParameters)); + return parameters; + } + } + + public System.Reflection.Metadata.EntityHandle MetadataToken => baseMethod.MetadataToken; + public SymbolKind SymbolKind => baseMethod.SymbolKind; + public ITypeDefinition DeclaringTypeDefinition => baseMethod.DeclaringTypeDefinition; + public IType DeclaringType => baseMethod.DeclaringType; + public IModule ParentModule => baseMethod.ParentModule; + IEnumerable IEntity.GetAttributes() => baseMethod.GetAttributes(); + IEnumerable IMethod.GetReturnTypeAttributes() => baseMethod.GetReturnTypeAttributes(); + bool IMethod.ReturnTypeIsRefReadOnly => baseMethod.ReturnTypeIsRefReadOnly; + public bool IsStatic => true; + public bool IsAbstract => baseMethod.IsAbstract; + public bool IsSealed => baseMethod.IsSealed; + + public Accessibility Accessibility => baseMethod.Accessibility; + + public string FullName => baseMethod.FullName; + public string Name => baseMethod.Name; + public string ReflectionName => baseMethod.ReflectionName; + public string Namespace => baseMethod.Namespace; + + public ICompilation Compilation => baseMethod.Compilation; + } +} + diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs index 2406051e3..a05ec147e 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs @@ -41,6 +41,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation readonly EntityHandle accessorOwner; public MethodSemanticsAttributes AccessorKind { get; } public bool IsExtensionMethod { get; } + bool IMethod.IsLocalFunction => false; // lazy-loaded fields: ITypeDefinition declaringType; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs index 8f9937d87..9929e103b 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs @@ -102,11 +102,15 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation return specializedTypeParameters ?? methodDefinition.TypeParameters; } } - + public bool IsExtensionMethod { get { return methodDefinition.IsExtensionMethod; } } - + + public bool IsLocalFunction { + get { return methodDefinition.IsLocalFunction; } + } + public bool IsConstructor { get { return methodDefinition.IsConstructor; } } diff --git a/ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs b/ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs index 0356f544a..d88900caf 100644 --- a/ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs @@ -129,6 +129,8 @@ namespace ICSharpCode.Decompiler.TypeSystem get { return baseMethod.IsExtensionMethod; } } + bool IMethod.IsLocalFunction => false; + public bool IsConstructor { get { return baseMethod.IsConstructor; } } From e215f69b2d0968c3d35888a4d1029651cff88d33 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 18 Jul 2019 21:00:04 +0200 Subject: [PATCH 071/221] Extend description of LocalFunctionDecompiler --- .../IL/Transforms/LocalFunctionDecompiler.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs index 228611a51..7937cf2a7 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs @@ -45,9 +45,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// /// local functions can either be used in method calls, i.e., call and callvirt instructions, - /// or can be used as part of the "delegate construction" pattern, i.e., newobj Delegate(<target-expression>, ldftn <method>). Note that this pattern would also be possible with ldvirtftn, but I haven't yet disovered a case where a compiler generates such code. + /// or can be used as part of the "delegate construction" pattern, i.e., newobj Delegate(<target-expression>, ldftn <method>). /// - /// As local functions can be declared practically anywhere, we have to take a look at all use-sites and infer the declaration location from that. + /// As local functions can be declared practically anywhere, we have to take a look at all use-sites and infer the declaration location from that. Use-sites can be call, callvirt and ldftn instructions. + /// After all use-sites are collected we construct the ILAst of the local function and add it to the parent function. + /// Then all use-sites of the local-function are transformed to a call to the LocalFunctionMethod or a ldftn of the LocalFunctionMethod. + /// In a next step we handle all nested local functions. + /// After all local functions are transformed, we move all local functions that do not capture any variables to the top-level function. /// public void Run(ILFunction function, ILTransformContext context) { From 647f4fd5455caa267966add13048c6245c0a8893 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 18 Jul 2019 21:42:25 +0200 Subject: [PATCH 072/221] Do not crash if the local function was not correctly transformed. This should only happen for generic local functions, which are currently not supported. --- ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 3cc2ed7e2..510930f1e 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -179,11 +179,12 @@ namespace ICSharpCode.Decompiler.CSharp ILFunction localFunction = null; if (method.IsLocalFunction) { localFunction = expressionBuilder.ResolveLocalFunction(method); + Debug.Assert(localFunction != null); } TranslatedExpression target; if (callOpCode == OpCode.NewObj) { target = default(TranslatedExpression); // no target - } else if (method.IsLocalFunction) { + } else if (method.IsLocalFunction && localFunction != null) { target = new IdentifierExpression(localFunction.Name) .WithoutILInstruction() .WithRR(ToMethodGroup(method, localFunction)); From 763ea386447b4061e4e1e39e1450ba0198846535 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 18 Jul 2019 23:20:36 +0200 Subject: [PATCH 073/221] Clean up CallBuilder.HandleDelegateConstruction --- .../TestCases/Pretty/LocalFunctions.cs | 17 ++++ ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 98 +++++++++---------- .../Resolver/CSharpInvocationResolveResult.cs | 5 - .../Transforms/IntroduceExtensionMethods.cs | 2 +- 4 files changed, 66 insertions(+), 56 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs index 806e9a57f..03c37273a 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs @@ -148,6 +148,23 @@ namespace LocalFunctions } } + private void Name() + { + + } + + private void LocalFunctionHidingMethod() + { + Action action = this.Name; + Name(); + action(); + + void Name() + { + + } + } + public void NamedArgument() { Use(Get(1), Get(2), Get(3)); diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 510930f1e..fc50633d5 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -1227,69 +1227,67 @@ namespace ICSharpCode.Decompiler.CSharp CallOpCode = inst.OpCode }; ResolveResult result = null; - string methodName; + string methodName = method.Name; if (method.IsLocalFunction) { requireTarget = false; var localFunction = expressionBuilder.ResolveLocalFunction(method); result = ToMethodGroup(method, localFunction); target = default; + targetType = default; methodName = localFunction.Name; - } else { - methodName = method.Name; - if (method.IsExtensionMethod && invokeMethod != null && method.Parameters.Count - 1 == invokeMethod.Parameters.Count) { - targetType = method.Parameters[0].Type; - if (targetType.Kind == TypeKind.ByReference && thisArg is Box thisArgBox) { - targetType = ((ByReferenceType)targetType).ElementType; - thisArg = thisArgBox.Argument; - } - target = expressionBuilder.Translate(thisArg, targetType); - requireTarget = true; - } else { - targetType = method.DeclaringType; - if (targetType.IsReferenceType == false && thisArg is Box thisArgBox) { - // Normal struct instance method calls (which TranslateTarget is meant for) expect a 'ref T', - // but delegate construction uses a 'box T'. - if (thisArgBox.Argument is LdObj ldobj) { - thisArg = ldobj.Target; - } else { - thisArg = new AddressOf(thisArgBox.Argument); - } - } - target = expressionBuilder.TranslateTarget(thisArg, - nonVirtualInvocation: func.OpCode == OpCode.LdFtn, - memberStatic: method.IsStatic, - memberDeclaringType: method.DeclaringType); - requireTarget = expressionBuilder.HidesVariableWithName(method.Name) - || (method.IsStatic ? !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) : !(target.Expression is ThisReferenceExpression)); + } else if (method.IsExtensionMethod && invokeMethod != null && method.Parameters.Count - 1 == invokeMethod.Parameters.Count) { + targetType = method.Parameters[0].Type; + if (targetType.Kind == TypeKind.ByReference && thisArg is Box thisArgBox) { + targetType = ((ByReferenceType)targetType).ElementType; + thisArg = thisArgBox.Argument; } - var or = new OverloadResolution(resolver.Compilation, method.Parameters.SelectReadOnlyArray(p => new TypeResolveResult(p.Type))); - if (!requireTarget) { - result = resolver.ResolveSimpleName(method.Name, method.TypeArguments, isInvocationTarget: false); - if (result is MethodGroupResolveResult mgrr) { - or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray()); - requireTarget = (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(expectedTargetDetails, method, or.BestCandidate)); + target = expressionBuilder.Translate(thisArg, targetType); + requireTarget = true; + } else { + targetType = method.DeclaringType; + if (targetType.IsReferenceType == false && thisArg is Box thisArgBox) { + // Normal struct instance method calls (which TranslateTarget is meant for) expect a 'ref T', + // but delegate construction uses a 'box T'. + if (thisArgBox.Argument is LdObj ldobj) { + thisArg = ldobj.Target; } else { - requireTarget = true; + thisArg = new AddressOf(thisArgBox.Argument); } } - MemberLookup lookup = null; - bool needsCast = false; - if (requireTarget) { - lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule); - var rr = lookup.Lookup(target.ResolveResult, method.Name, method.TypeArguments, false); - needsCast = true; - result = rr; - if (rr is MethodGroupResolveResult mgrr) { - or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray()); - needsCast = (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(expectedTargetDetails, method, or.BestCandidate)); - } + target = expressionBuilder.TranslateTarget(thisArg, + nonVirtualInvocation: func.OpCode == OpCode.LdFtn, + memberStatic: method.IsStatic, + memberDeclaringType: method.DeclaringType); + requireTarget = expressionBuilder.HidesVariableWithName(method.Name) + || (method.IsStatic ? !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) : !(target.Expression is ThisReferenceExpression)); + } + var or = new OverloadResolution(resolver.Compilation, method.Parameters.SelectReadOnlyArray(p => new TypeResolveResult(p.Type))); + if (!method.IsLocalFunction && !requireTarget) { + result = resolver.ResolveSimpleName(method.Name, method.TypeArguments, isInvocationTarget: false); + if (result is MethodGroupResolveResult mgrr) { + or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray()); + requireTarget = (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(expectedTargetDetails, method, or.BestCandidate)); + } else { + requireTarget = true; } - if (needsCast) { - Debug.Assert(requireTarget); - target = target.ConvertTo(targetType, expressionBuilder); - result = lookup.Lookup(target.ResolveResult, method.Name, method.TypeArguments, false); + } + MemberLookup lookup = null; + bool needsCast = false; + if (requireTarget) { + lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule); + var rr = lookup.Lookup(target.ResolveResult, method.Name, method.TypeArguments, false); + needsCast = true; + result = rr; + if (rr is MethodGroupResolveResult mgrr) { + or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray()); + needsCast = (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(expectedTargetDetails, method, or.BestCandidate)); } } + if (needsCast) { + Debug.Assert(requireTarget); + target = target.ConvertTo(targetType, expressionBuilder); + result = lookup.Lookup(target.ResolveResult, method.Name, method.TypeArguments, false); + } Expression targetExpression; if (requireTarget) { var mre = new MemberReferenceExpression(target, methodName); diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs index 784c6b9e5..2b71dfc82 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs @@ -47,11 +47,6 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver /// public readonly bool IsExpandedForm; - /// - /// Gets whether this invocation is calling a local function. - /// - public readonly bool IsLocalFunctionInvocation; - readonly IReadOnlyList argumentToParameterMap; public CSharpInvocationResolveResult( diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs index e3bd8d7fc..3b11cd87f 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs @@ -154,7 +154,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms var newResolveResult = new CSharpInvocationResolveResult( irr.TargetResult, irr.Member, irr.Arguments, irr.OverloadResolutionErrors, isExtensionMethodInvocation: true, irr.IsExpandedForm, irr.IsDelegateInvocation, - irr.IsLocalFunctionInvocation, irr.GetArgumentToParameterMap(), irr.InitializerStatements); + irr.GetArgumentToParameterMap(), irr.InitializerStatements); invocationExpression.AddAnnotation(newResolveResult); } } From dc74e4ee9d0c2c5892f67e979c1b75e4c3d25726 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 18 Jul 2019 23:22:52 +0200 Subject: [PATCH 074/221] Fix build. --- .../CSharp/Resolver/CSharpInvocationResolveResult.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs index 2b71dfc82..61e64a7c0 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpInvocationResolveResult.cs @@ -56,7 +56,6 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver bool isExtensionMethodInvocation = false, bool isExpandedForm = false, bool isDelegateInvocation = false, - bool isLocalFunctionInvocation = false, IReadOnlyList argumentToParameterMap = null, IList initializerStatements = null, IType returnTypeOverride = null @@ -67,7 +66,6 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver this.IsExtensionMethodInvocation = isExtensionMethodInvocation; this.IsExpandedForm = isExpandedForm; this.IsDelegateInvocation = isDelegateInvocation; - this.IsLocalFunctionInvocation = isLocalFunctionInvocation; this.argumentToParameterMap = argumentToParameterMap; } From 5a8796c05c00952170a22c6c7a08b64892777a70 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Thu, 18 Jul 2019 23:30:49 +0200 Subject: [PATCH 075/221] Reuse the existing TSAB. --- ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs | 2 +- ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 3 --- ICSharpCode.Decompiler/CSharp/StatementBuilder.cs | 3 +-- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index c7c5ff137..01a8ad23f 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -397,7 +397,7 @@ namespace ICSharpCode.Decompiler.CSharp return new DecompilerTypeSystem(file, resolver); } - internal static TypeSystemAstBuilder CreateAstBuilder(ITypeResolveContext decompilationContext) + static TypeSystemAstBuilder CreateAstBuilder(ITypeResolveContext decompilationContext) { var typeSystemAstBuilder = new TypeSystemAstBuilder(); typeSystemAstBuilder.ShowAttributes = true; diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index fc50633d5..05df1d6d4 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -1299,9 +1299,6 @@ namespace ICSharpCode.Decompiler.CSharp .WithRR(result); targetExpression = ide; } - if (target.ResolveResult != null) { - result = new MemberResolveResult(target.ResolveResult, method); - } var oce = new ObjectCreateExpression(expressionBuilder.ConvertType(inst.Method.DeclaringType), targetExpression) .WithILInstruction(inst) .WithRR(new ConversionResolveResult( diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index 9730fc1f9..f10d53493 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -962,11 +962,10 @@ namespace ICSharpCode.Decompiler.CSharp LocalFunctionDeclarationStatement TranslateFunction(ILFunction function) { var stmt = new LocalFunctionDeclarationStatement(); - var tsab = CSharpDecompiler.CreateAstBuilder(null); var nestedBuilder = new StatementBuilder(typeSystem, exprBuilder.decompilationContext, function, settings, cancellationToken); stmt.Name = function.Name; stmt.Parameters.AddRange(exprBuilder.MakeParameters(function.Parameters, function)); - stmt.ReturnType = tsab.ConvertType(function.Method.ReturnType); + stmt.ReturnType = exprBuilder.ConvertType(function.Method.ReturnType); stmt.Body = nestedBuilder.ConvertAsBlock(function.Body); stmt.AddAnnotation(new MemberResolveResult(null, function.ReducedMethod)); return stmt; From b7bf6b7720abd672874c58407840a85a4051f144 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 19 Jul 2019 14:04:22 +0200 Subject: [PATCH 076/221] Fix build again. --- ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 05df1d6d4..b65aeb71b 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -218,7 +218,7 @@ namespace ICSharpCode.Decompiler.CSharp if (method.IsLocalFunction) { return new InvocationExpression(target, argumentList.GetArgumentExpressions()) .WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method, - argumentList.GetArgumentResolveResults().ToList(), isExpandedForm: argumentList.IsExpandedForm, isLocalFunctionInvocation: true)); + argumentList.GetArgumentResolveResults().ToList(), isExpandedForm: argumentList.IsExpandedForm)); } if (method is VarArgInstanceMethod) { From 6234ff7c9acdea017e655184007de2a3840cc532 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 20 Jul 2019 07:40:43 +0200 Subject: [PATCH 077/221] Apply changes as requested per code review. --- .../TypeSystem/DecompilerTypeSystem.cs | 4 ---- .../Implementation/LocalFunctionMethod.cs | 23 +++++++++---------- .../TypeSystem/VarArgInstanceMethod.cs | 4 +++- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs index 06f07cdd9..04ce3ef8f 100644 --- a/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs +++ b/ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs @@ -104,10 +104,6 @@ namespace ICSharpCode.Decompiler.TypeSystem /// NullabilityAnnotations = 0x400, /// - /// If this option is active, - /// - LocalFunctions = 0x800, - /// /// Default settings: typical options for the decompiler, with all C# languages features enabled. /// Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants | ReadOnlyStructsAndParameters diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs index be3201461..6a42b4364 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/LocalFunctionMethod.cs @@ -41,21 +41,22 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation { if (!(obj is LocalFunctionMethod other)) return false; - return baseMethod.Equals(other.baseMethod, typeNormalization); + return baseMethod.Equals(other.baseMethod, typeNormalization) + && NumberOfCompilerGeneratedParameters == other.NumberOfCompilerGeneratedParameters; } public override bool Equals(object obj) { - var other = obj as LocalFunctionMethod; - if (other == null) + if (!(obj is LocalFunctionMethod other)) return false; - return baseMethod.Equals(other.baseMethod); + return baseMethod.Equals(other.baseMethod) + && NumberOfCompilerGeneratedParameters == other.NumberOfCompilerGeneratedParameters; } public override int GetHashCode() { unchecked { - return baseMethod.GetHashCode() + 1; + return baseMethod.GetHashCode() + NumberOfCompilerGeneratedParameters + 1; } } @@ -66,13 +67,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation internal int NumberOfCompilerGeneratedParameters { get; } - public IMember MemberDefinition { - get { - if (baseMethod.MemberDefinition == baseMethod) - return this; - return new LocalFunctionMethod((IMethod)baseMethod.MemberDefinition, NumberOfCompilerGeneratedParameters); - } - } + public IMember MemberDefinition => this; public IType ReturnType => baseMethod.ReturnType; IEnumerable IMember.ExplicitlyImplementedInterfaceMembers => baseMethod.ExplicitlyImplementedInterfaceMembers; @@ -122,6 +117,10 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation IEnumerable IEntity.GetAttributes() => baseMethod.GetAttributes(); IEnumerable IMethod.GetReturnTypeAttributes() => baseMethod.GetReturnTypeAttributes(); bool IMethod.ReturnTypeIsRefReadOnly => baseMethod.ReturnTypeIsRefReadOnly; + /// + /// We consider local functions as always static, because they do not have a "this parameter". + /// Even local functions in instance methods capture this. + /// public bool IsStatic => true; public bool IsAbstract => baseMethod.IsAbstract; public bool IsSealed => baseMethod.IsSealed; diff --git a/ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs b/ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs index d88900caf..a151d1464 100644 --- a/ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/VarArgInstanceMethod.cs @@ -129,7 +129,9 @@ namespace ICSharpCode.Decompiler.TypeSystem get { return baseMethod.IsExtensionMethod; } } - bool IMethod.IsLocalFunction => false; + bool IMethod.IsLocalFunction { + get { return baseMethod.IsLocalFunction; } + } public bool IsConstructor { get { return baseMethod.IsConstructor; } From a109b7785899d3980ece0188c41a1175b074731a Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 23 Jul 2019 01:13:12 +0200 Subject: [PATCH 078/221] Refactor LocalFunctionDecompiler to allow mutually recursive local functions to be decompiled correctly. --- .../TestCases/Pretty/LocalFunctions.cs | 28 +++ .../CSharp/ExpressionBuilder.cs | 5 - .../IL/Transforms/DelegateConstruction.cs | 2 +- .../IL/Transforms/LocalFunctionDecompiler.cs | 224 ++++++++---------- .../Transforms/TransformDisplayClassUsage.cs | 8 +- 5 files changed, 130 insertions(+), 137 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs index 03c37273a..f66883c7b 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs @@ -217,6 +217,34 @@ namespace LocalFunctions return FibHelper(n - 1) + FibHelper(n - 2); } } + public int MutuallyRecursiveLocalFunctions() + { + return B(4) + C(3); + + int A(int i) + { + if (i > 0) { + return A(i - 1) + 2 * B(i - 1) + 3 * C(i - 1); + } + return 1; + } + + int B(int i) + { + if (i > 0) { + return 3 * A(i - 1) + B(i - 1); + } + return 1; + } + + int C(int i) + { + if (i > 0) { + return 2 * A(i - 1) + C(i - 1); + } + return 1; + } + } public static int NestedLocalFunctions(int i) { diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 5fb95a319..87602db3f 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -207,11 +207,6 @@ namespace ICSharpCode.Decompiler.CSharp } } - internal bool IsLocalFunction(IMethod method) - { - return settings.LocalFunctions && IL.Transforms.LocalFunctionDecompiler.IsLocalFunctionMethod(method); - } - internal ILFunction ResolveLocalFunction(IMethod method) { Debug.Assert(method.IsLocalFunction); diff --git a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs index b3f617029..7edfeb1ef 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs @@ -126,7 +126,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return null; if (targetMethod.MetadataToken.IsNil) return null; - if (LocalFunctionDecompiler.IsLocalFunctionMethod(targetMethod)) + if (LocalFunctionDecompiler.IsLocalFunctionMethod(targetMethod, context)) return null; target = value.Arguments[0]; var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken); diff --git a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs index 7937cf2a7..dafda4357 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs @@ -40,6 +40,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms { ILTransformContext context; + struct LocalFunctionInfo + { + public List UseSites; + public ILFunction Definition; + } + /// /// The transform works like this: /// @@ -51,67 +57,103 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// After all use-sites are collected we construct the ILAst of the local function and add it to the parent function. /// Then all use-sites of the local-function are transformed to a call to the LocalFunctionMethod or a ldftn of the LocalFunctionMethod. /// In a next step we handle all nested local functions. - /// After all local functions are transformed, we move all local functions that do not capture any variables to the top-level function. + /// After all local functions are transformed, we move all local functions that capture any variables to their respective declaration scope. /// public void Run(ILFunction function, ILTransformContext context) { if (!context.Settings.LocalFunctions) return; this.context = context; - var localFunctions = new Dictionary>(); + var localFunctions = new Dictionary(); var cancellationToken = context.CancellationToken; - // Find use-sites - foreach (var inst in function.Descendants) { + // Find all local functions declared inside this method, including nested local functions or local functions declared in lambdas. + FindUseSites(function, context, localFunctions); + foreach (var (method, info) in localFunctions) { cancellationToken.ThrowIfCancellationRequested(); - if (inst is CallInstruction call && IsLocalFunctionMethod(call.Method) && !call.Method.IsLocalFunction) { - if (!localFunctions.TryGetValue(call.Method, out var info)) { - info = new List() { call }; - localFunctions.Add(call.Method, info); - } else { - info.Add(call); + var firstUseSite = info.UseSites[0]; + context.StepStartGroup($"Transform " + info.Definition.Name, info.Definition); + try { + var localFunction = info.Definition; + if (!localFunction.Method.IsStatic) { + var target = firstUseSite.Arguments[0]; + context.Step($"Replace 'this' with {target}", localFunction); + var thisVar = localFunction.Variables.SingleOrDefault(VariableKindExtensions.IsThis); + localFunction.AcceptVisitor(new DelegateConstruction.ReplaceDelegateTargetVisitor(target, thisVar)); } - } else if (inst is LdFtn ldftn && !ldftn.Method.IsLocalFunction && ldftn.Parent is NewObj newObj && IsLocalFunctionMethod(ldftn.Method) && DelegateConstruction.IsDelegateConstruction(newObj)) { - context.StepStartGroup($"LocalFunctionDecompiler {ldftn.StartILOffset}", ldftn); - if (!localFunctions.TryGetValue(ldftn.Method, out var info)) { - info = new List() { newObj }; - localFunctions.Add(ldftn.Method, info); - } else { - info.Add(newObj); + + foreach (var useSite in info.UseSites) { + context.Step("Transform use site at " + useSite.StartILOffset, useSite); + if (useSite.OpCode == OpCode.NewObj) { + TransformToLocalFunctionReference(localFunction, useSite); + } else { + DetermineCaptureAndDeclarationScope(localFunction, useSite); + TransformToLocalFunctionInvocation(localFunction.ReducedMethod, useSite); + } } - context.StepEndGroup(); - } - } - foreach (var (method, useSites) in localFunctions) { - context.StepStartGroup($"LocalFunctionDecompiler {useSites[0].StartILOffset}", useSites[0]); - try { - TransformLocalFunction(function, method, useSites); + if (localFunction.DeclarationScope == null) { + localFunction.DeclarationScope = (BlockContainer)function.Body; + } else if (localFunction.DeclarationScope != function.Body && localFunction.DeclarationScope.Parent is ILFunction declaringFunction) { + function.LocalFunctions.Remove(localFunction); + declaringFunction.LocalFunctions.Add(localFunction); + } } finally { context.StepEndGroup(); } } + } - foreach (var f in function.LocalFunctions) { - // handle nested functions - var nestedContext = new ILTransformContext(context, f); - nestedContext.StepStartGroup("LocalFunctionDecompiler (nested functions)", f); - new LocalFunctionDecompiler().Run(f, nestedContext); - nestedContext.StepEndGroup(); + void FindUseSites(ILFunction function, ILTransformContext context, Dictionary localFunctions) + { + foreach (var inst in function.Body.Descendants) { + context.CancellationToken.ThrowIfCancellationRequested(); + if (inst is CallInstruction call && !call.Method.IsLocalFunction && IsLocalFunctionMethod(call.Method, context)) { + HandleUseSite(call.Method, call); + } else if (inst is LdFtn ldftn && !ldftn.Method.IsLocalFunction && ldftn.Parent is NewObj newObj && IsLocalFunctionMethod(ldftn.Method, context) && DelegateConstruction.IsDelegateConstruction(newObj)) { + HandleUseSite(ldftn.Method, newObj); + } } - if (function.Kind == ILFunctionKind.TopLevelFunction) { - var movableFunctions = TreeTraversal.PostOrder(function, f => f.LocalFunctions) - .Where(f => f.Kind == ILFunctionKind.LocalFunction && f.DeclarationScope == null) - .ToArray(); - foreach (var f in movableFunctions) { - var parent = (ILFunction)f.Parent; - f.DeclarationScope = (BlockContainer)function.Body; - parent.LocalFunctions.Remove(f); - function.LocalFunctions.Add(f); + void HandleUseSite(IMethod targetMethod, CallInstruction inst) + { + if (!localFunctions.TryGetValue((MethodDefinitionHandle)targetMethod.MetadataToken, out var info)) { + context.StepStartGroup($"Read local function '{targetMethod.Name}'", inst); + info = new LocalFunctionInfo() { + UseSites = new List() { inst }, + Definition = ReadLocalFunctionDefinition(context.Function, targetMethod) + }; + localFunctions.Add((MethodDefinitionHandle)targetMethod.MetadataToken, info); + FindUseSites(info.Definition, context, localFunctions); + context.StepEndGroup(); + } else { + info.UseSites.Add(inst); } } } + ILFunction ReadLocalFunctionDefinition(ILFunction rootFunction, IMethod targetMethod) + { + var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken); + if (!methodDefinition.HasBody()) + return null; + var genericContext = DelegateConstruction.GenericContextFromTypeArguments(targetMethod.Substitution); + if (genericContext == null) + return null; + var ilReader = context.CreateILReader(); + var body = context.PEFile.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress); + var function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, ILFunctionKind.LocalFunction, context.CancellationToken); + // Embed the local function into the parent function's ILAst, so that "Show steps" can show + // how the local function body is being transformed. + rootFunction.LocalFunctions.Add(function); + function.DeclarationScope = (BlockContainer)rootFunction.Body; + function.CheckInvariant(ILPhase.Normal); + var nestedContext = new ILTransformContext(context, function); + function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is LocalFunctionDecompiler)), nestedContext); + function.DeclarationScope = null; + function.ReducedMethod = ReduceToLocalFunction(targetMethod); + return function; + } + static T FindCommonAncestorInstruction(ILInstruction a, ILInstruction b) where T : ILInstruction { @@ -137,71 +179,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms internal static ILInstruction GetStatement(ILInstruction inst) { while (inst.Parent != null) { - if (inst.Parent is Block) + if (inst.Parent is Block b && b.Kind == BlockKind.ControlFlow) return inst; inst = inst.Parent; } return inst; } - private ILFunction TransformLocalFunction(ILFunction parentFunction, IMethod targetMethod, List useSites) - { - var methodDefinition = context.PEFile.Metadata.GetMethodDefinition((MethodDefinitionHandle)targetMethod.MetadataToken); - if (!methodDefinition.HasBody()) - return null; - var genericContext = DelegateConstruction.GenericContextFromTypeArguments(targetMethod.Substitution); - if (genericContext == null) - return null; - var function = parentFunction.Ancestors.OfType().SelectMany(f => f.LocalFunctions).FirstOrDefault(f => f.Method == targetMethod); - if (function == null) { - var ilReader = context.CreateILReader(); - var body = context.PEFile.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress); - function = ilReader.ReadIL((MethodDefinitionHandle)targetMethod.MetadataToken, body, genericContext.Value, ILFunctionKind.LocalFunction, context.CancellationToken); - // Embed the local function into the parent function's ILAst, so that "Show steps" can show - // how the local function body is being transformed. - parentFunction.LocalFunctions.Add(function); - function.DeclarationScope = (BlockContainer)parentFunction.Body; - function.CheckInvariant(ILPhase.Normal); - var nestedContext = new ILTransformContext(context, function); - function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is LocalFunctionDecompiler)), nestedContext); - if (IsNonLocalTarget(targetMethod, useSites, out var target)) { - Debug.Assert(target != null); - nestedContext.Step("LocalFunctionDecompiler (ReplaceDelegateTargetVisitor)", function); - var thisVar = function.Variables.SingleOrDefault(v => v.Index == -1 && v.Kind == VariableKind.Parameter); - function.AcceptVisitor(new DelegateConstruction.ReplaceDelegateTargetVisitor(target, thisVar)); - } - function.DeclarationScope = null; - function.ReducedMethod = ReduceToLocalFunction(targetMethod); - - foreach (var innerUseSite in function.Descendants.OfType()) { - if (innerUseSite.Method != function.Method) - continue; - if (innerUseSite.OpCode == OpCode.NewObj) { - TransformToLocalFunctionReference(function, innerUseSite); - } else { - TransformToLocalFunctionInvocation(function.ReducedMethod, innerUseSite); - } - } - } - foreach (var useSite in useSites) { - if (useSite.OpCode == OpCode.NewObj) { - TransformToLocalFunctionReference(function, useSite); - } else { - DetermineCaptureAndDeclarationScope(function, useSite); - TransformToLocalFunctionInvocation(function.ReducedMethod, useSite); - } - } - - if (function.DeclarationScope != null - && parentFunction.LocalFunctions.Contains(function) - && function.DeclarationScope.Parent is ILFunction betterParentFunction) { - parentFunction.LocalFunctions.Remove(function); - betterParentFunction.LocalFunctions.Add(function); - } - - return function; - } - LocalFunctionMethod ReduceToLocalFunction(IMethod method) { int parametersToRemove = 0; @@ -272,47 +256,28 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } - bool IsNonLocalTarget(IMethod targetMethod, List useSites, out ILInstruction target) - { - target = null; - if (targetMethod.IsStatic) - return false; - ValidateUseSites(useSites); - target = useSites.Select(call => call.Arguments.First()).First(); - return !target.MatchLdThis(); - } - - [Conditional("DEBUG")] - static void ValidateUseSites(List useSites) - { - ILInstruction targetInstruction = null; - foreach (var site in useSites) { - if (targetInstruction == null) - targetInstruction = site.Arguments.First(); - else - Debug.Assert(targetInstruction.Match(site.Arguments[0]).Success); - } - } - - internal static bool IsLocalFunctionReference(NewObj inst) + internal static bool IsLocalFunctionReference(NewObj inst, ILTransformContext context) { if (inst == null || inst.Arguments.Count != 2 || inst.Method.DeclaringType.Kind != TypeKind.Delegate) return false; var opCode = inst.Arguments[1].OpCode; return (opCode == OpCode.LdFtn || opCode == OpCode.LdVirtFtn) - && IsLocalFunctionMethod(((IInstructionWithMethodOperand)inst.Arguments[1]).Method); + && IsLocalFunctionMethod(((IInstructionWithMethodOperand)inst.Arguments[1]).Method, context); } - public static bool IsLocalFunctionMethod(IMethod method) + public static bool IsLocalFunctionMethod(IMethod method, ILTransformContext context) { if (method.MetadataToken.IsNil) return false; - return IsLocalFunctionMethod(method.ParentModule.PEFile, (MethodDefinitionHandle)method.MetadataToken); + return IsLocalFunctionMethod(method.ParentModule.PEFile, (MethodDefinitionHandle)method.MetadataToken, context); } - public static bool IsLocalFunctionMethod(PEFile module, MethodDefinitionHandle methodHandle) + public static bool IsLocalFunctionMethod(PEFile module, MethodDefinitionHandle methodHandle, ILTransformContext context = null) { + if (context != null && context.PEFile != module) + return false; + var metadata = module.Metadata; var method = metadata.GetMethodDefinition(methodHandle); var declaringType = method.GetDeclaringType(); @@ -326,12 +291,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - public static bool IsLocalFunctionDisplayClass(PEFile module, TypeDefinitionHandle typeHandle) + public static bool IsLocalFunctionDisplayClass(PEFile module, TypeDefinitionHandle typeHandle, ILTransformContext context = null) { + if (context != null && context.PEFile != module) + return false; + var metadata = module.Metadata; var type = metadata.GetTypeDefinition(typeHandle); - if ((type.Attributes & TypeAttributes.NestedPrivate) == 0) + if ((type.Attributes & TypeAttributes.VisibilityMask) != TypeAttributes.NestedPrivate) return false; if (!type.HasGeneratedName(metadata)) return false; @@ -340,7 +308,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms var declaringType = metadata.GetTypeDefinition(declaringTypeHandle); foreach (var method in declaringType.GetMethods()) { - if (!IsLocalFunctionMethod(module, method)) + if (!IsLocalFunctionMethod(module, method, context)) continue; var md = metadata.GetMethodDefinition(method); if (md.DecodeSignature(new FindTypeDecoder(typeHandle), default).ParameterTypes.Any()) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index 4fb8ddb9e..382abe96a 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -39,6 +39,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms public ITypeDefinition Definition; public Dictionary Variables; public BlockContainer CaptureScope; + public ILFunction DeclaringFunction; } struct DisplayClassVariable @@ -105,7 +106,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms Variable = v, Definition = closureType, Variables = new Dictionary(), - CaptureScope = (isMono && IsMonoNestedCaptureScope(closureType)) || localFunctionClosureParameter ? null : v.CaptureScope + CaptureScope = (isMono && IsMonoNestedCaptureScope(closureType)) || localFunctionClosureParameter ? null : v.CaptureScope, + DeclaringFunction = localFunctionClosureParameter ? f.DeclarationScope.Ancestors.OfType().First() : f }); } else { if (displayClass.CaptureScope == null && !localFunctionClosureParameter) @@ -296,10 +298,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms } else { Debug.Assert(displayClass.Definition == field.DeclaringTypeDefinition); // Introduce a fresh variable for the display class field. - v = currentFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name); if (displayClass.IsMono && displayClass.CaptureScope == null && !IsOuterClosureReference(field)) { displayClass.CaptureScope = BlockContainer.FindClosestContainer(inst); } + v = displayClass.DeclaringFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name); v.CaptureScope = displayClass.CaptureScope; inst.ReplaceWith(new StLoc(v, inst.Value).WithILRange(inst)); value = new LdLoc(v); @@ -346,7 +348,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!displayClass.Variables.TryGetValue(field, out DisplayClassVariable info)) { // Introduce a fresh variable for the display class field. Debug.Assert(displayClass.Definition == field.DeclaringTypeDefinition); - var v = currentFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name); + var v = displayClass.DeclaringFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name); v.CaptureScope = displayClass.CaptureScope; inst.ReplaceWith(new LdLoca(v).WithILRange(inst)); displayClass.Variables.Add(field, new DisplayClassVariable { Value = new LdLoc(v), Variable = v }); From c45ce41d00c93422db34530bc518dfe49966f0c6 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 23 Jul 2019 08:51:48 +0200 Subject: [PATCH 079/221] Refactor ParameterModifier.This to allow 'this ref' parameters. --- .../OutputVisitor/CSharpOutputVisitor.cs | 6 ++-- .../Syntax/TypeMembers/MethodDeclaration.cs | 2 +- .../TypeMembers/ParameterDeclaration.cs | 31 +++++++++++++++---- .../CSharp/Syntax/TypeSystemAstBuilder.cs | 4 +-- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs index d3bef7f73..e0f274a28 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -2173,6 +2173,9 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { StartNode(parameterDeclaration); WriteAttributes(parameterDeclaration.Attributes); + if (parameterDeclaration.HasThisModifier) { + WriteKeyword(ParameterDeclaration.ThisModifierRole); + } switch (parameterDeclaration.ParameterModifier) { case ParameterModifier.Ref: WriteKeyword(ParameterDeclaration.RefModifierRole); @@ -2183,9 +2186,6 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor case ParameterModifier.Params: WriteKeyword(ParameterDeclaration.ParamsModifierRole); break; - case ParameterModifier.This: - WriteKeyword(ParameterDeclaration.ThisModifierRole); - break; case ParameterModifier.In: WriteKeyword(ParameterDeclaration.InModifierRole); break; diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/MethodDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/MethodDeclaration.cs index ee91ad37d..c7da3d4ea 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/MethodDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/MethodDeclaration.cs @@ -71,7 +71,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public bool IsExtensionMethod { get { ParameterDeclaration pd = (ParameterDeclaration)GetChildByRole (Roles.Parameter); - return pd != null && pd.ParameterModifier == ParameterModifier.This; + return pd != null && pd.HasThisModifier; } } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs index 4e4211ff8..b100f12bd 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ParameterDeclaration.cs @@ -27,15 +27,15 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { - public enum ParameterModifier { + public enum ParameterModifier + { None, Ref, Out, Params, - This, In } - + public class ParameterDeclaration : AstNode { public static readonly Role AttributeRole = EntityDeclaration.AttributeRole; @@ -96,11 +96,30 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax return NodeType.Unknown; } } - + public AstNodeCollection Attributes { - get { return GetChildrenByRole (AttributeRole); } + get { return GetChildrenByRole(AttributeRole); } } - + + bool hasThisModifier; + + public CSharpTokenNode ThisKeyword { + get { + if (hasThisModifier) { + return GetChildByRole(ThisModifierRole); + } + return CSharpTokenNode.Null; + } + } + + public bool HasThisModifier { + get { return hasThisModifier; } + set { + ThrowIfFrozen(); + hasThisModifier = value; + } + } + ParameterModifier parameterModifier; public ParameterModifier ParameterModifier { diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index ae680a94a..901a1149e 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -1633,8 +1633,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax foreach (IParameter p in method.Parameters) { decl.Parameters.Add(ConvertParameter(p)); } - if (method.IsExtensionMethod && method.ReducedFrom == null && decl.Parameters.Any() && decl.Parameters.First().ParameterModifier == ParameterModifier.None) - decl.Parameters.First().ParameterModifier = ParameterModifier.This; + if (method.IsExtensionMethod && method.ReducedFrom == null && decl.Parameters.Any()) + decl.Parameters.First().HasThisModifier = true; if (this.ShowTypeParameters && this.ShowTypeParameterConstraints && !method.IsOverride && !method.IsExplicitInterfaceImplementation) { foreach (ITypeParameter tp in method.TypeParameters) { From 537df7bab37604047aedf9103474f4e6eaefb4c0 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 23 Jul 2019 10:56:42 +0200 Subject: [PATCH 080/221] Add option: 'ref' extension methods --- .../TestCases/Pretty/QualifierTests.cs | 11 +++++++++- .../Transforms/IntroduceExtensionMethods.cs | 10 +++++++++- ICSharpCode.Decompiler/DecompilerSettings.cs | 20 ++++++++++++++++++- ILSpy/Properties/Resources.Designer.cs | 9 +++++++++ ILSpy/Properties/Resources.resx | 3 +++ 5 files changed, 50 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/QualifierTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/QualifierTests.cs index 46cbd3930..3f3c5cf59 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/QualifierTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/QualifierTests.cs @@ -221,12 +221,21 @@ namespace ICSharpCode.Decompiler.Tests.Pretty { } +#if CS72 + public static void Do(this ref DateTime test) + { + + } +#endif - public static void Do2(this int test) + public static void Do2(this int test, DateTime date) { test.Do(); ((IEnumerable)null).Any(); ((object)null).Do(); +#if CS72 + date.Do(); +#endif } } } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs index 3b11cd87f..7c64e687d 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs @@ -137,7 +137,15 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } if (!CanTransformToExtensionMethodCall(resolver, method, typeArguments, target, args, argNames)) return; - if (firstArgument is NullReferenceExpression) { + if (firstArgument is DirectionExpression dirExpr) { + if (dirExpr.FieldDirection != FieldDirection.Ref) + return; + if (!context.Settings.RefExtensionMethods) + return; + firstArgument = dirExpr.Expression; + target = firstArgument.GetResolveResult(); + dirExpr.Detach(); + } else if (firstArgument is NullReferenceExpression) { Debug.Assert(context.RequiredNamespacesSuperset.Contains(method.Parameters[0].Type.Namespace)); firstArgument = firstArgument.ReplaceWith(expr => new CastExpression(context.TypeSystemAstBuilder.ConvertType(method.Parameters[0].Type), expr.Detach())); } diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index 163d58346..8e130da03 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -97,6 +97,7 @@ namespace ICSharpCode.Decompiler introduceReadonlyAndInModifiers = false; introduceRefModifiersOnStructs = false; nonTrailingNamedArguments = false; + refExtensionMethods = false; } if (languageVersion < CSharp.LanguageVersion.CSharp7_3) { introduceUnmanagedConstraint = false; @@ -114,7 +115,7 @@ namespace ICSharpCode.Decompiler return CSharp.LanguageVersion.CSharp8_0; if (introduceUnmanagedConstraint || tupleComparisons || stackAllocInitializers) return CSharp.LanguageVersion.CSharp7_3; - if (introduceRefModifiersOnStructs || introduceReadonlyAndInModifiers || nonTrailingNamedArguments) + if (introduceRefModifiersOnStructs || introduceReadonlyAndInModifiers || nonTrailingNamedArguments || refExtensionMethods) return CSharp.LanguageVersion.CSharp7_2; // C# 7.1 missing if (outVariables || tupleTypes || tupleConversions || discards || localFunctions) @@ -626,6 +627,23 @@ namespace ICSharpCode.Decompiler } } + bool refExtensionMethods = true; + + /// + /// Gets/Sets whether to use C# 7.2 'ref' extension methods. + /// + [Category("C# 7.2 / VS 2017.4")] + [Description("DecompilerSettings.AllowExtensionMethodSyntaxOnRef")] + public bool RefExtensionMethods { + get { return refExtensionMethods; } + set { + if (refExtensionMethods != value) { + refExtensionMethods = value; + OnPropertyChanged(); + } + } + } + bool stringInterpolation = true; /// diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index 5c86e06e2..60474f712 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -474,6 +474,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Use 'ref' extension methods. + /// + public static string DecompilerSettings_AllowExtensionMethodSyntaxOnRef { + get { + return ResourceManager.GetString("DecompilerSettings.AllowExtensionMethodSyntaxOnRef", resourceCulture); + } + } + /// /// Looks up a localized string similar to Always cast targets of explicit interface implementation calls. /// diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index 0128389d9..ac22077ae 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -729,4 +729,7 @@ Entity could not be resolved. Cannot analyze entities from missing assembly references. Add the missing reference and try again. + + Use 'ref' extension methods + \ No newline at end of file From f10ab693281a9ebea2502fd16c7e04bd258dff6e Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 23 Jul 2019 18:29:48 +0200 Subject: [PATCH 081/221] Improve local-function detection to minimize false positives. --- .../CSharp/ExpressionBuilder.cs | 2 +- .../IL/Transforms/LocalFunctionDecompiler.cs | 29 ++++++++++++------- .../Transforms/TransformDisplayClassUsage.cs | 2 +- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 87602db3f..e7e560d16 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -1877,7 +1877,7 @@ namespace ICSharpCode.Decompiler.CSharp // needs to be consistent with logic in ILReader.CreateILVarable(ParameterDefinition) pd.Name = "P_" + i; // if this is a local function, we have to skip the parameters for closure references - if (settings.LocalFunctions && function.Kind == ILFunctionKind.LocalFunction && IL.Transforms.LocalFunctionDecompiler.IsClosureParameter(parameter)) + if (settings.LocalFunctions && function.Kind == ILFunctionKind.LocalFunction && IL.Transforms.LocalFunctionDecompiler.IsClosureParameter(parameter, decompilationContext)) break; } if (settings.AnonymousTypes && parameter.Type.ContainsAnonymousType()) diff --git a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs index dafda4357..85753bd9e 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs @@ -39,6 +39,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms class LocalFunctionDecompiler : IILTransform { ILTransformContext context; + ITypeResolveContext resolveContext; struct LocalFunctionInfo { @@ -64,6 +65,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!context.Settings.LocalFunctions) return; this.context = context; + this.resolveContext = new SimpleTypeResolveContext(function.Method); var localFunctions = new Dictionary(); var cancellationToken = context.CancellationToken; // Find all local functions declared inside this method, including nested local functions or local functions declared in lambdas. @@ -161,11 +163,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms return a.Ancestors.OfType().FirstOrDefault(ancestorsOfB.Contains); } - internal static bool IsClosureParameter(IParameter parameter) + internal static bool IsClosureParameter(IParameter parameter, ITypeResolveContext context) { - return parameter.IsRef - && ((ByReferenceType)parameter.Type).ElementType - .GetDefinition()?.IsCompilerGenerated() == true; + if (!parameter.IsRef) + return false; + var type = ((ByReferenceType)parameter.Type).ElementType.GetDefinition(); + return type != null + && type.Kind == TypeKind.Struct + && TransformDisplayClassUsage.IsPotentialClosure(context.CurrentTypeDefinition, type); } static IType UnwrapByRef(IType type) @@ -190,7 +195,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms { int parametersToRemove = 0; for (int i = method.Parameters.Count - 1; i >= 0; i--) { - if (!IsClosureParameter(method.Parameters[i])) + if (!IsClosureParameter(method.Parameters[i], resolveContext)) break; parametersToRemove++; } @@ -213,23 +218,24 @@ namespace ICSharpCode.Decompiler.IL.Transforms int argumentCount = useSite.Arguments.Count; int reducedArgumentCount = argumentCount - (reducedMethod.NumberOfCompilerGeneratedParameters + firstArgumentIndex); replacement.Arguments.AddRange(useSite.Arguments.Skip(firstArgumentIndex).Take(reducedArgumentCount)); - // copy flags: + // copy flags replacement.ConstrainedTo = useSite.ConstrainedTo; replacement.ILStackWasEmpty = useSite.ILStackWasEmpty; replacement.IsTail = useSite.IsTail; // copy IL ranges - replacement = replacement.WithILRange(useSite); + replacement.AddILRange(useSite); if (wasInstanceCall) { - replacement = replacement.WithILRange(useSite.Arguments[0]); + replacement.AddILRange(useSite.Arguments[0]); } for (int i = 0; i < reducedMethod.NumberOfCompilerGeneratedParameters; i++) { - replacement = replacement.WithILRange(useSite.Arguments[argumentCount - i - 1]); + replacement.AddILRange(useSite.Arguments[argumentCount - i - 1]); } useSite.ReplaceWith(replacement); } void DetermineCaptureAndDeclarationScope(ILFunction function, CallInstruction useSite) { + int firstArgumentIndex = function.Method.IsStatic ? 0 : 1; for (int i = useSite.Arguments.Count - 1; i >= 0; i--) { var arg = useSite.Arguments[i]; ILVariable closureVar; @@ -239,6 +245,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms break; if (!TransformDisplayClassUsage.IsPotentialClosure(context, UnwrapByRef(closureVar.Type).GetDefinition())) break; + if (i - firstArgumentIndex >= 0) { + Debug.Assert(i - firstArgumentIndex < function.Method.Parameters.Count && IsClosureParameter(function.Method.Parameters[i - firstArgumentIndex], resolveContext)); + } if (closureVar.AddressCount == 0 && closureVar.StoreInstructions.Count == 0) continue; // determine the capture scope of closureVar and the declaration scope of the function @@ -262,7 +271,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; var opCode = inst.Arguments[1].OpCode; - return (opCode == OpCode.LdFtn || opCode == OpCode.LdVirtFtn) + return opCode == OpCode.LdFtn && IsLocalFunctionMethod(((IInstructionWithMethodOperand)inst.Arguments[1]).Method, context); } diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index 382abe96a..d12191033 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -69,7 +69,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (IsClosure(v, out ITypeDefinition closureType, out var inst)) { AddOrUpdateDisplayClass(f, v, closureType, inst, localFunctionClosureParameter: false); } - if (context.Settings.LocalFunctions && f.Kind == ILFunctionKind.LocalFunction && v.Kind == VariableKind.Parameter && v.Index > -1 && f.Method.Parameters[v.Index.Value] is IParameter p && LocalFunctionDecompiler.IsClosureParameter(p)) { + if (context.Settings.LocalFunctions && f.Kind == ILFunctionKind.LocalFunction && v.Kind == VariableKind.Parameter && v.Index > -1 && f.Method.Parameters[v.Index.Value] is IParameter p && LocalFunctionDecompiler.IsClosureParameter(p, decompilationContext)) { AddOrUpdateDisplayClass(f, v, ((ByReferenceType)p.Type).ElementType.GetDefinition(), f.Body, localFunctionClosureParameter: true); } } From beed6b5e24f990304a8e0696ad4135bf711e3584 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 23 Jul 2019 23:28:19 +0200 Subject: [PATCH 082/221] Remove MarkGeneratedVariables() step in AsyncAwaitDecompiler. This was left-over from earlier versions; but ILSpy stopped caring so much about variable vs. stack slot since Roslyn started to optimize more aggressively. The change of variable type caused problems for debug information and could even cause an assertion. Closes #1456, closes #1562. --- ICSharpCode.Decompiler/CSharp/StatementBuilder.cs | 4 +++- .../IL/ControlFlow/AsyncAwaitDecompiler.cs | 12 ------------ .../IL/Transforms/UsingTransform.cs | 2 ++ 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index f10d53493..3a36ebd9a 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -712,7 +712,9 @@ namespace ICSharpCode.Decompiler.CSharp // Must be a plain assignment expression and variable must only be used in 'body' + only assigned once. if (stloc.Parent == loopBody && VariableIsOnlyUsedInBlock(stloc, usingContainer)) { foreachVariable = stloc.Variable; - return RequiredGetCurrentTransformation.UseExistingVariable; + if (foreachVariable.Kind == VariableKind.Local) { + return RequiredGetCurrentTransformation.UseExistingVariable; + } } } // In optimized Roslyn code it can happen that the foreach variable is referenced via addressof diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs index f7366b383..211fd6b08 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs @@ -128,7 +128,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow CleanDoFinallyBodies(function); context.Step("Translate fields to local accesses", function); - MarkGeneratedVariables(function); YieldReturnDecompiler.TranslateFieldsToLocalAccess(function, function, fieldToParameterMap); TranslateCachedFieldsToLocals(); @@ -902,17 +901,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } #endregion - void MarkGeneratedVariables(ILFunction function) - { - // Variables after the awaiters are usually compiler-generated; - // so mark them as stack slots. - foreach (var v in function.Variables) { - if (v.Kind == VariableKind.Local && v.Index >= smallestAwaiterVarIndex) { - v.Kind = VariableKind.StackSlot; - } - } - } - /// /// Eliminates usage of doFinallyBodies /// diff --git a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs index adccfffa6..452ccec52 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs @@ -76,6 +76,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!(storeInst.Value.MatchLdNull() || CheckResourceType(storeInst.Variable.Type))) return false; + if (storeInst.Variable.Kind != VariableKind.Local) + return false; if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(tryFinally))) return false; if (storeInst.Variable.AddressInstructions.Any(la => !la.IsDescendantOf(tryFinally) || (la.IsDescendantOf(tryFinally.TryBlock) && !ILInlining.IsUsedAsThisPointerInCall(la)))) From 856cedc95e647fa744ccfa08734118d634b65e1c Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 23 Jul 2019 23:38:03 +0200 Subject: [PATCH 083/221] #1456: add test case; add additional checks to ensure we only transform normal locals into using/foreach-locals --- .../TestCases/Pretty/Async.cs | 11 +++++++++++ ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs | 2 +- ICSharpCode.Decompiler/CSharp/StatementBuilder.cs | 3 ++- .../CSharp/Transforms/PatternStatementTransform.cs | 1 + ICSharpCode.Decompiler/IL/ILVariable.cs | 10 +++++++--- .../IL/Transforms/UsingTransform.cs | 2 ++ 6 files changed, 24 insertions(+), 5 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs index 14e6a8858..49a9a47f3 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs @@ -18,6 +18,7 @@ #pragma warning disable 1998 using System; +using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Threading.Tasks; @@ -126,6 +127,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } #endif + + public static async Task GetIntegerSumAsync(IEnumerable items) + { + await Task.Delay(100); + int num = 0; + foreach (int item in items) { + num += item; + } + return num; + } } public struct HopToThreadPoolAwaitable : INotifyCompletion diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index e7e560d16..c0bbd2dc6 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -65,7 +65,7 @@ namespace ICSharpCode.Decompiler.CSharp /// * Otherwise, the C# type of the resulting expression shall match the IL stack type, /// and the evaluated values shall be the same. /// - class ExpressionBuilder : ILVisitor + sealed class ExpressionBuilder : ILVisitor { readonly IDecompilerTypeSystem typeSystem; internal readonly ITypeResolveContext decompilationContext; diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index 3a36ebd9a..c0a6a31bb 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -32,7 +32,7 @@ using ICSharpCode.Decompiler.CSharp.Resolver; namespace ICSharpCode.Decompiler.CSharp { - class StatementBuilder : ILVisitor + sealed class StatementBuilder : ILVisitor { internal readonly ExpressionBuilder exprBuilder; readonly ILFunction currentFunction; @@ -412,6 +412,7 @@ namespace ICSharpCode.Decompiler.CSharp AstNode usingInit = resource; var var = inst.Variable; if (!inst.ResourceExpression.MatchLdNull() && !NullableType.GetUnderlyingType(var.Type).GetAllBaseTypes().Any(b => b.IsKnownType(KnownTypeCode.IDisposable))) { + Debug.Assert(var.Kind == VariableKind.UsingLocal); var.Kind = VariableKind.Local; var disposeType = exprBuilder.compilation.FindType(KnownTypeCode.IDisposable); var disposeVariable = currentFunction.RegisterVariable( diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs index 7d23edfa8..8aa5cfedb 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs @@ -467,6 +467,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms statementsToDelete.Add(stmt.GetNextStatement()); var itemVariable = foreachVariable.GetILVariable(); if (itemVariable == null || !itemVariable.IsSingleDefinition + || (itemVariable.Kind != IL.VariableKind.Local && itemVariable.Kind != IL.VariableKind.StackSlot) || !upperBounds.All(ub => ub.IsSingleDefinition && ub.LoadCount == 1) || !lowerBounds.All(lb => lb.StoreCount == 2 && lb.LoadCount == 3 && lb.AddressCount == 0)) return null; diff --git a/ICSharpCode.Decompiler/IL/ILVariable.cs b/ICSharpCode.Decompiler/IL/ILVariable.cs index 1983089df..9b77c5ecc 100644 --- a/ICSharpCode.Decompiler/IL/ILVariable.cs +++ b/ICSharpCode.Decompiler/IL/ILVariable.cs @@ -107,8 +107,12 @@ namespace ICSharpCode.Decompiler.IL internal set { if (kind == VariableKind.Parameter) throw new InvalidOperationException("Kind=Parameter cannot be changed!"); - if (Index != null && value.IsLocal()) - Debug.Assert(kind.IsLocal()); + if (Index != null && value.IsLocal() && !kind.IsLocal()) { + // For variables, Index has different meaning than for stack slots, + // so we need to reset it to null. + // StackSlot -> ForeachLocal can happen sometimes (e.g. PST.TransformForeachOnArray) + Index = null; + } kind = value; } } @@ -138,7 +142,7 @@ namespace ICSharpCode.Decompiler.IL /// For ExceptionStackSlot, the index is the IL offset of the exception handler. /// For other kinds, the index has no meaning, and is usually null. /// - public readonly int? Index; + public int? Index { get; private set; } [Conditional("DEBUG")] internal void CheckInvariant() diff --git a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs index 452ccec52..471319713 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/UsingTransform.cs @@ -127,6 +127,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!(storeInst.Value.MatchLdNull() || CheckResourceType(storeInst.Variable.Type))) return false; + if (storeInst.Variable.Kind != VariableKind.Local) + return false; if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(tryFinally))) return false; if (storeInst.Variable.AddressInstructions.Any(la => !la.IsDescendantOf(tryFinally) || (la.IsDescendantOf(tryFinally.TryBlock) && !ILInlining.IsUsedAsThisPointerInCall(la)))) From 0a54a93be02cfe9b19fed83684f85f864300654e Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 23 Jul 2019 23:58:54 +0200 Subject: [PATCH 084/221] Relax foreach transform to allow stack slots as foreach-variable again. --- ICSharpCode.Decompiler/CSharp/StatementBuilder.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index c0a6a31bb..af9e8cff9 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -709,13 +709,11 @@ namespace ICSharpCode.Decompiler.CSharp while (inst.Parent is UnboxAny || inst.Parent is CastClass) inst = inst.Parent; // One variable was found. - if (inst.Parent is StLoc stloc) { + if (inst.Parent is StLoc stloc && (stloc.Variable.Kind == VariableKind.Local || stloc.Variable.Kind == VariableKind.StackSlot)) { // Must be a plain assignment expression and variable must only be used in 'body' + only assigned once. if (stloc.Parent == loopBody && VariableIsOnlyUsedInBlock(stloc, usingContainer)) { foreachVariable = stloc.Variable; - if (foreachVariable.Kind == VariableKind.Local) { - return RequiredGetCurrentTransformation.UseExistingVariable; - } + return RequiredGetCurrentTransformation.UseExistingVariable; } } // In optimized Roslyn code it can happen that the foreach variable is referenced via addressof From a63e94e5b4bc22d0ea3687b6bfc13c983c95c725 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 24 Jul 2019 00:56:54 +0200 Subject: [PATCH 085/221] Refactor Solution decompilation to use Language instead of AssemblyTreeNode. --- .../CSharp/WholeProjectDecompiler.cs | 1 + .../ICSharpCode.Decompiler.csproj | 4 +- ICSharpCode.Decompiler/Solution/ProjectId.cs | 56 +++++++ .../Solution/ProjectItem.cs | 55 +++++++ .../{CSharp => Solution}/SolutionCreator.cs | 70 +-------- ILSpy/Languages/CSharpLanguage.cs | 8 +- ILSpy/Languages/ILLanguage.cs | 7 +- ILSpy/Languages/Language.cs | 8 +- ILSpy/MainWindow.xaml.cs | 85 ++++++++--- ILSpy/SolutionWriter.cs | 140 ++++++------------ ILSpy/TreeNodes/AssemblyListTreeNode.cs | 4 +- ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs | 4 +- ILSpy/TreeNodes/AssemblyTreeNode.cs | 12 +- ILSpy/TreeNodes/BaseTypesEntryNode.cs | 3 +- ILSpy/TreeNodes/BaseTypesTreeNode.cs | 4 +- ILSpy/TreeNodes/DerivedTypesEntryNode.cs | 3 +- ILSpy/TreeNodes/DerivedTypesTreeNode.cs | 3 +- ILSpy/TreeNodes/EventTreeNode.cs | 3 +- ILSpy/TreeNodes/FieldTreeNode.cs | 3 +- ILSpy/TreeNodes/ILSpyTreeNode.cs | 2 +- ILSpy/TreeNodes/MethodTreeNode.cs | 3 +- ILSpy/TreeNodes/ModuleReferenceTreeNode.cs | 3 +- ILSpy/TreeNodes/NamespaceTreeNode.cs | 3 +- ILSpy/TreeNodes/PropertyTreeNode.cs | 3 +- ILSpy/TreeNodes/ReferenceFolderTreeNode.cs | 3 +- ILSpy/TreeNodes/ResourceListTreeNode.cs | 4 +- .../ImageListResourceEntryNode.cs | 3 +- .../ResourceNodes/ResourceEntryNode.cs | 3 +- .../ResourceNodes/ResourceTreeNode.cs | 4 +- .../ResourceNodes/ResourcesFileTreeNode.cs | 4 +- ILSpy/TreeNodes/ThreadingSupport.cs | 6 +- ILSpy/TreeNodes/TypeTreeNode.cs | 3 +- 32 files changed, 268 insertions(+), 249 deletions(-) create mode 100644 ICSharpCode.Decompiler/Solution/ProjectId.cs create mode 100644 ICSharpCode.Decompiler/Solution/ProjectItem.cs rename ICSharpCode.Decompiler/{CSharp => Solution}/SolutionCreator.cs (78%) diff --git a/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs b/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs index 6ec5d6802..377a617d5 100644 --- a/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs @@ -35,6 +35,7 @@ using System.Reflection.Metadata; using static ICSharpCode.Decompiler.Metadata.DotNetCorePathFinderExtensions; using static ICSharpCode.Decompiler.Metadata.MetadataExtensions; using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.Solution; namespace ICSharpCode.Decompiler.CSharp { diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 6efca3120..afb73dcf4 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -61,7 +61,9 @@ - + + + diff --git a/ICSharpCode.Decompiler/Solution/ProjectId.cs b/ICSharpCode.Decompiler/Solution/ProjectId.cs new file mode 100644 index 000000000..65a4e8d9e --- /dev/null +++ b/ICSharpCode.Decompiler/Solution/ProjectId.cs @@ -0,0 +1,56 @@ +// Copyright (c) 2019 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; + +namespace ICSharpCode.Decompiler.Solution +{ + /// + /// A container class that holds platform and GUID information about a Visual Studio project. + /// + public class ProjectId + { + /// + /// Initializes a new instance of the class. + /// + /// The project platform. + /// The project GUID. + /// + /// Thrown when + /// or is null or empty. + public ProjectId(string projectPlatform, Guid projectGuid) + { + if (string.IsNullOrWhiteSpace(projectPlatform)) { + throw new ArgumentException("The platform cannot be null or empty.", nameof(projectPlatform)); + } + + Guid = projectGuid; + PlatformName = projectPlatform; + } + + /// + /// Gets the GUID of this project. + /// + public Guid Guid { get; } + + /// + /// Gets the platform name of this project. Only single platform per project is supported. + /// + public string PlatformName { get; } + } +} diff --git a/ICSharpCode.Decompiler/Solution/ProjectItem.cs b/ICSharpCode.Decompiler/Solution/ProjectItem.cs new file mode 100644 index 000000000..bf1222368 --- /dev/null +++ b/ICSharpCode.Decompiler/Solution/ProjectItem.cs @@ -0,0 +1,55 @@ +// Copyright (c) 2019 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.IO; + +namespace ICSharpCode.Decompiler.Solution +{ + /// + /// A container class that holds information about a Visual Studio project. + /// + public sealed class ProjectItem : ProjectId + { + /// + /// Initializes a new instance of the class. + /// + /// The full path of the project file. + /// The project platform. + /// The project GUID. + /// + /// Thrown when + /// or is null or empty. + public ProjectItem(string projectFile, string projectPlatform, Guid projectGuid) + : base(projectPlatform, projectGuid) + { + ProjectName = Path.GetFileNameWithoutExtension(projectFile); + FilePath = projectFile; + } + + /// + /// Gets the name of the project. + /// + public string ProjectName { get; } + + /// + /// Gets the full path to the project file. + /// + public string FilePath { get; } + } +} diff --git a/ICSharpCode.Decompiler/CSharp/SolutionCreator.cs b/ICSharpCode.Decompiler/Solution/SolutionCreator.cs similarity index 78% rename from ICSharpCode.Decompiler/CSharp/SolutionCreator.cs rename to ICSharpCode.Decompiler/Solution/SolutionCreator.cs index 7c3f26e0e..fc63beca5 100644 --- a/ICSharpCode.Decompiler/CSharp/SolutionCreator.cs +++ b/ICSharpCode.Decompiler/Solution/SolutionCreator.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2016 Daniel Grunwald +// Copyright (c) 2019 AlphaSierraPapa for the SharpDevelop Team // // Permission is hereby granted, free of charge, to any person obtaining a copy of this // software and associated documentation files (the "Software"), to deal in the Software @@ -22,74 +22,8 @@ using System.IO; using System.Linq; using System.Xml.Linq; -namespace ICSharpCode.Decompiler.CSharp +namespace ICSharpCode.Decompiler.Solution { - /// - /// A container class that holds information about a Visual Studio project. - /// - public sealed class ProjectItem : ProjectId - { - /// - /// Initializes a new instance of the class. - /// - /// The full path of the project file. - /// The project platform. - /// The project GUID. - /// - /// Thrown when - /// or is null or empty. - public ProjectItem(string projectFile, string projectPlatform, Guid projectGuid) - : base(projectPlatform, projectGuid) - { - ProjectName = Path.GetFileNameWithoutExtension(projectFile); - FilePath = projectFile; - } - - /// - /// Gets the name of the project. - /// - public string ProjectName { get; } - - /// - /// Gets the full path to the project file. - /// - public string FilePath { get; } - } - - /// - /// A container class that holds platform and GUID information about a Visual Studio project. - /// - public class ProjectId - { - /// - /// Initializes a new instance of the class. - /// - /// The project platform. - /// The project GUID. - /// - /// Thrown when - /// or is null or empty. - public ProjectId(string projectPlatform, Guid projectGuid) - { - if (string.IsNullOrWhiteSpace(projectPlatform)) { - throw new ArgumentException("The platform cannot be null or empty.", nameof(projectPlatform)); - } - - Guid = projectGuid; - PlatformName = projectPlatform; - } - - /// - /// Gets the GUID of this project. - /// - public Guid Guid { get; } - - /// - /// Gets the platform name of this project. Only single platform per project is supported. - /// - public string PlatformName { get; } - } - /// /// A helper class that can write a Visual Studio Solution file for the provided projects. /// diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index 57cab0ee8..35f614764 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -34,6 +34,7 @@ using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Transforms; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Output; +using ICSharpCode.Decompiler.Solution; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; using ICSharpCode.ILSpy.TreeNodes; @@ -343,7 +344,7 @@ namespace ICSharpCode.ILSpy } } - public override object DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) + public override ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) { var module = assembly.GetPEFileOrNull(); if (options.FullDecompilation && options.SaveAsProjectDirectory != null) { @@ -389,7 +390,7 @@ namespace ICSharpCode.ILSpy } if (metadata.IsAssembly) { var asm = metadata.GetAssemblyDefinition(); - if (asm.HashAlgorithm != System.Reflection.AssemblyHashAlgorithm.None) + if (asm.HashAlgorithm != AssemblyHashAlgorithm.None) output.WriteLine("// Hash algorithm: " + asm.HashAlgorithm.ToString().ToUpper()); if (!asm.PublicKey.IsNil) { output.Write("// Public key: "); @@ -415,8 +416,7 @@ namespace ICSharpCode.ILSpy } WriteCode(output, options.DecompilerSettings, st, decompiler.TypeSystem); } - - return true; + return null; } } diff --git a/ILSpy/Languages/ILLanguage.cs b/ILSpy/Languages/ILLanguage.cs index da26caeb2..3dd1ec1ab 100644 --- a/ILSpy/Languages/ILLanguage.cs +++ b/ILSpy/Languages/ILLanguage.cs @@ -27,6 +27,8 @@ using System.Reflection.Metadata.Ecma335; using System.Linq; using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.Decompiler.Util; +using ICSharpCode.Decompiler.Solution; namespace ICSharpCode.ILSpy { @@ -150,7 +152,7 @@ namespace ICSharpCode.ILSpy dis.DisassembleNamespace(nameSpace, module, types.Select(t => (TypeDefinitionHandle)t.MetadataToken)); } - public override object DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) + public override ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) { output.WriteLine("// " + assembly.FileName); output.WriteLine(); @@ -174,8 +176,7 @@ namespace ICSharpCode.ILSpy dis.WriteModuleContents(module); } } - - return true; + return null; } } } diff --git a/ILSpy/Languages/Language.cs b/ILSpy/Languages/Language.cs index 76f435244..e97696fea 100644 --- a/ILSpy/Languages/Language.cs +++ b/ILSpy/Languages/Language.cs @@ -23,6 +23,7 @@ using System.Reflection.PortableExecutable; using System.Text; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.Solution; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; @@ -131,11 +132,11 @@ namespace ICSharpCode.ILSpy WriteCommentLine(output, nameSpace); } - public virtual object DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) + public virtual ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, assembly.FileName); var asm = assembly.GetPEFileOrNull(); - if (asm == null) return false; + if (asm == null) return null; var metadata = asm.Metadata; if (metadata.IsAssembly) { var name = metadata.GetAssemblyDefinition(); @@ -147,8 +148,7 @@ namespace ICSharpCode.ILSpy } else { WriteCommentLine(output, metadata.GetString(metadata.GetModuleDefinition().Name)); } - - return true; + return null; } public virtual void WriteCommentLine(ITextOutput output, string comment) diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 238c74307..fe7c45374 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -914,29 +914,80 @@ namespace ICSharpCode.ILSpy void SaveCommandExecuted(object sender, ExecutedRoutedEventArgs e) { var selectedNodes = SelectedNodes.ToList(); - if (selectedNodes.Count > 1) { - var assemblyNodes = selectedNodes - .OfType() - .Where(n => n.Language is CSharpLanguage) - .ToList(); - - if (assemblyNodes.Count == selectedNodes.Count) { - var initialPath = Path.GetDirectoryName(assemblyNodes[0].LoadedAssembly.FileName); - var selectedPath = SolutionWriter.SelectSolutionFile(initialPath); - - if (!string.IsNullOrEmpty(selectedPath)) { - SolutionWriter.CreateSolution(TextView, selectedPath, assemblyNodes); - } + if (selectedNodes.Count == 1) { + // if there's only one treenode selected + // we will invoke the custom Save logic + if (selectedNodes[0].Save(TextView)) return; + } else if (selectedNodes.All(n => n is AssemblyTreeNode)) { + var initialPath = Path.GetDirectoryName(((AssemblyTreeNode)selectedNodes[0]).LoadedAssembly.FileName); + var selectedPath = SelectSolutionFile(initialPath); + + if (!string.IsNullOrEmpty(selectedPath)) { + var assemblies = selectedNodes.OfType() + .Select(n => n.LoadedAssembly) + .Where(a => a != null).ToArray(); + SolutionWriter.CreateSolution(TextView, selectedPath, CurrentLanguage, assemblies); } + return; } - if (selectedNodes.Count != 1 || !selectedNodes[0].Save(TextView)) { - var options = new DecompilationOptions() { FullDecompilation = true }; - TextView.SaveToDisk(CurrentLanguage, selectedNodes, options); + // Fallback: if nobody was able to handle the request, use default behavior. + // try to save all nodes to disk. + var options = new DecompilationOptions() { FullDecompilation = true }; + TextView.SaveToDisk(CurrentLanguage, selectedNodes, options); + } + + /// + /// Shows a File Selection dialog where the user can select the target file for the solution. + /// + /// The initial path to show in the dialog. If not specified, the 'Documents' directory + /// will be used. + /// + /// The full path of the selected target file, or null if the user canceled. + string SelectSolutionFile(string path) + { + const string SolutionExtension = ".sln"; + const string DefaultSolutionName = "Solution"; + + if (string.IsNullOrWhiteSpace(path)) { + path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); } + + SaveFileDialog dlg = new SaveFileDialog(); + dlg.InitialDirectory = path; + dlg.FileName = Path.Combine(path, DefaultSolutionName + SolutionExtension); + dlg.Filter = "Visual Studio Solution file|*" + SolutionExtension; + + bool targetInvalid; + do { + if (dlg.ShowDialog() != true) { + return null; + } + + string selectedPath = Path.GetDirectoryName(dlg.FileName); + try { + targetInvalid = Directory.EnumerateFileSystemEntries(selectedPath).Any(); + } catch (Exception e) when (e is IOException || e is UnauthorizedAccessException || e is System.Security.SecurityException) { + MessageBox.Show( + "The directory cannot be accessed. Please ensure it exists and you have sufficient rights to access it.", + "Solution directory not accessible", + MessageBoxButton.OK, MessageBoxImage.Error); + targetInvalid = true; + continue; + } + + if (targetInvalid) { + MessageBox.Show( + "The directory is not empty. Please select an empty directory.", + "Solution directory not empty", + MessageBoxButton.OK, MessageBoxImage.Warning); + } + } while (targetInvalid); + + return dlg.FileName; } - + public void RefreshDecompiledView() { try { diff --git a/ILSpy/SolutionWriter.cs b/ILSpy/SolutionWriter.cs index 45fd3ce67..d892e22ec 100644 --- a/ILSpy/SolutionWriter.cs +++ b/ILSpy/SolutionWriter.cs @@ -22,15 +22,12 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; -using System.Security; using System.Threading; using System.Threading.Tasks; -using System.Windows; using ICSharpCode.Decompiler; -using ICSharpCode.Decompiler.CSharp; +using ICSharpCode.Decompiler.Solution; +using ICSharpCode.Decompiler.Util; using ICSharpCode.ILSpy.TextView; -using ICSharpCode.ILSpy.TreeNodes; -using Microsoft.Win32; namespace ICSharpCode.ILSpy { @@ -38,73 +35,23 @@ namespace ICSharpCode.ILSpy /// An utility class that creates a Visual Studio solution containing projects for the /// decompiled assemblies. /// - internal static class SolutionWriter + internal class SolutionWriter { - private const string SolutionExtension = ".sln"; - private const string DefaultSolutionName = "Solution"; - - /// - /// Shows a File Selection dialog where the user can select the target file for the solution. - /// - /// The initial path to show in the dialog. If not specified, the 'Documents' directory - /// will be used. - /// - /// The full path of the selected target file, or null if the user canceled. - public static string SelectSolutionFile(string path) - { - if (string.IsNullOrWhiteSpace(path)) { - path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); - } - - SaveFileDialog dlg = new SaveFileDialog(); - dlg.InitialDirectory = path; - dlg.FileName = Path.Combine(path, DefaultSolutionName + SolutionExtension); - dlg.Filter = "Visual Studio Solution file|*" + SolutionExtension; - - bool targetInvalid; - do { - if (dlg.ShowDialog() != true) { - return null; - } - - string selectedPath = Path.GetDirectoryName(dlg.FileName); - try { - targetInvalid = Directory.EnumerateFileSystemEntries(selectedPath).Any(); - } catch (Exception e) when (e is IOException || e is UnauthorizedAccessException || e is SecurityException) { - MessageBox.Show( - "The directory cannot be accessed. Please ensure it exists and you have sufficient rights to access it.", - "Solution directory not accessible", - MessageBoxButton.OK, MessageBoxImage.Error); - targetInvalid = true; - continue; - } - - if (targetInvalid) { - MessageBox.Show( - "The directory is not empty. Please select an empty directory.", - "Solution directory not empty", - MessageBoxButton.OK, MessageBoxImage.Warning); - } - } while (targetInvalid); - - return dlg.FileName; - } - /// /// Creates a Visual Studio solution that contains projects with decompiled code - /// of the specified . The solution file will be saved + /// of the specified . The solution file will be saved /// to the . The directory of this file must either /// be empty or not exist. /// /// A reference to the instance. /// The target file path of the solution file. - /// The assembly nodes to decompile. + /// The assembly nodes to decompile. /// /// Thrown when is null, /// an empty or a whitespace string. /// Thrown when > or - /// is null. - public static void CreateSolution(DecompilerTextView textView, string solutionFilePath, IEnumerable assemblyNodes) + /// is null. + public static void CreateSolution(DecompilerTextView textView, string solutionFilePath, Language language, IEnumerable assemblies) { if (textView == null) { throw new ArgumentNullException(nameof(textView)); @@ -114,29 +61,37 @@ namespace ICSharpCode.ILSpy throw new ArgumentException("The solution file path cannot be null or empty.", nameof(solutionFilePath)); } - if (assemblyNodes == null) { - throw new ArgumentNullException(nameof(assemblyNodes)); + if (assemblies == null) { + throw new ArgumentNullException(nameof(assemblies)); } + var writer = new SolutionWriter(solutionFilePath); + textView - .RunWithCancellation(ct => CreateSolution(solutionFilePath, assemblyNodes, ct)) + .RunWithCancellation(ct => writer.CreateSolution(assemblies, language, ct)) .Then(output => textView.ShowText(output)) .HandleExceptions(); } - private static async Task CreateSolution( - string solutionFilePath, - IEnumerable assemblyNodes, - CancellationToken ct) + readonly string solutionFilePath; + readonly string solutionDirectory; + readonly ConcurrentBag projects; + readonly ConcurrentBag statusOutput; + + SolutionWriter(string solutionFilePath) { - var solutionDirectory = Path.GetDirectoryName(solutionFilePath); - var statusOutput = new ConcurrentBag(); - var projects = new ConcurrentBag(); + this.solutionFilePath = solutionFilePath; + solutionDirectory = Path.GetDirectoryName(solutionFilePath); + statusOutput = new ConcurrentBag(); + projects = new ConcurrentBag(); + } + async Task CreateSolution(IEnumerable assemblies, Language language, CancellationToken ct) + { var result = new AvalonEditTextOutput(); var duplicates = new HashSet(); - if (assemblyNodes.Any(n => !duplicates.Add(n.LoadedAssembly.ShortName))) { + if (assemblies.Any(asm => !duplicates.Add(asm.ShortName))) { result.WriteLine("Duplicate assembly names selected, cannot generate a solution."); return result; } @@ -144,7 +99,7 @@ namespace ICSharpCode.ILSpy Stopwatch stopwatch = Stopwatch.StartNew(); try { - await Task.Run(() => Parallel.ForEach(assemblyNodes, n => WriteProject(n, solutionDirectory, statusOutput, projects, ct))) + await Task.Run(() => Parallel.ForEach(assemblies, n => WriteProject(n, language, solutionDirectory, ct))) .ConfigureAwait(false); await Task.Run(() => SolutionCreator.WriteSolutionFile(solutionFilePath, projects)) @@ -162,7 +117,7 @@ namespace ICSharpCode.ILSpy result.WriteLine(e.Message); return true; }); - + return result; } @@ -172,13 +127,13 @@ namespace ICSharpCode.ILSpy if (statusOutput.Count == 0) { result.WriteLine("Successfully decompiled the following assemblies into Visual Studio projects:"); - foreach (var item in assemblyNodes.Select(n => n.Text.ToString())) { + foreach (var item in assemblies.Select(n => n.Text.ToString())) { result.WriteLine(item); } result.WriteLine(); - if (assemblyNodes.Count() == projects.Count) { + if (assemblies.Count() == projects.Count) { result.WriteLine("Created the Visual Studio Solution file."); } @@ -191,17 +146,10 @@ namespace ICSharpCode.ILSpy return result; } - private static void WriteProject( - AssemblyTreeNode assemblyNode, - string targetDirectory, - ConcurrentBag statusOutput, - ConcurrentBag targetContainer, - CancellationToken ct) + void WriteProject(LoadedAssembly loadedAssembly, Language language, string targetDirectory, CancellationToken ct) { - var loadedAssembly = assemblyNode.LoadedAssembly; - targetDirectory = Path.Combine(targetDirectory, loadedAssembly.ShortName); - string projectFileName = Path.Combine(targetDirectory, loadedAssembly.ShortName + assemblyNode.Language.ProjectFileExtension); + string projectFileName = Path.Combine(targetDirectory, loadedAssembly.ShortName + language.ProjectFileExtension); if (!Directory.Exists(targetDirectory)) { try { @@ -214,21 +162,19 @@ namespace ICSharpCode.ILSpy try { using (var projectFileWriter = new StreamWriter(projectFileName)) { - var projectFileOutput = new PlainTextOutput(projectFileWriter); - var options = new DecompilationOptions() { - FullDecompilation = true, - CancellationToken = ct, - SaveAsProjectDirectory = targetDirectory }; - - if (assemblyNode.Decompile(assemblyNode.Language, projectFileOutput, options) is ProjectId projectInfo) { - targetContainer.Add(new ProjectItem(projectFileName, projectInfo.PlatformName, projectInfo.Guid)); + var projectFileOutput = new PlainTextOutput(projectFileWriter); + var options = new DecompilationOptions() { + FullDecompilation = true, + CancellationToken = ct, + SaveAsProjectDirectory = targetDirectory + }; + + var projectInfo = language.DecompileAssembly(loadedAssembly, projectFileOutput, options); + if (projectInfo != null) { + projects.Add(new ProjectItem(projectFileName, projectInfo.PlatformName, projectInfo.Guid)); } } - } - catch (OperationCanceledException) { - throw; - } - catch (Exception e) { + } catch (Exception e) when (!(e is OperationCanceledException)) { statusOutput.Add($"Failed to decompile the assembly '{loadedAssembly.FileName}':{Environment.NewLine}{e}"); } } diff --git a/ILSpy/TreeNodes/AssemblyListTreeNode.cs b/ILSpy/TreeNodes/AssemblyListTreeNode.cs index 3b8e5a519..e53a3a883 100644 --- a/ILSpy/TreeNodes/AssemblyListTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyListTreeNode.cs @@ -141,7 +141,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public Action Select = delegate { }; - public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, "List: " + assemblyList.ListName); output.WriteLine(); @@ -150,8 +150,6 @@ namespace ICSharpCode.ILSpy.TreeNodes output.WriteLine(); asm.Decompile(language, output, options); } - - return true; } #region Find*Node diff --git a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs index 192820246..8aa8f8c0d 100644 --- a/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs @@ -79,7 +79,7 @@ namespace ICSharpCode.ILSpy.TreeNodes } } - public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { var loaded = parentAssembly.LoadedAssembly.LoadedAssemblyReferencesInfo.TryGetInfo(r.FullName, out var info); if (r.IsWindowsRuntime) { @@ -98,8 +98,6 @@ namespace ICSharpCode.ILSpy.TreeNodes output.Unindent(); output.WriteLine(); } - - return true; } } } diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index 39669c15f..60f34e6b2 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -240,7 +240,7 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Recurse; } - public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { void HandleException(Exception ex, string message) { @@ -259,21 +259,21 @@ namespace ICSharpCode.ILSpy.TreeNodes switch (ex.InnerException) { case BadImageFormatException badImage: HandleException(badImage, "This file does not contain a managed assembly."); - return null; + return; case FileNotFoundException fileNotFound: HandleException(fileNotFound, "The file was not found."); - return null; + return; case DirectoryNotFoundException dirNotFound: HandleException(dirNotFound, "The directory was not found."); - return null; + return; case PEFileNotSupportedException notSupported: HandleException(notSupported, notSupported.Message); - return null; + return; default: throw; } } - return language.DecompileAssembly(LoadedAssembly, output, options); + language.DecompileAssembly(LoadedAssembly, output, options); } public override bool Save(DecompilerTextView textView) diff --git a/ILSpy/TreeNodes/BaseTypesEntryNode.cs b/ILSpy/TreeNodes/BaseTypesEntryNode.cs index 41edd0f8c..3c96c40dd 100644 --- a/ILSpy/TreeNodes/BaseTypesEntryNode.cs +++ b/ILSpy/TreeNodes/BaseTypesEntryNode.cs @@ -97,10 +97,9 @@ namespace ICSharpCode.ILSpy.TreeNodes return false; } - public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, language.TypeToString(type, includeNamespace: true)); - return true; } IEntity IMemberTreeNode.Member { diff --git a/ILSpy/TreeNodes/BaseTypesTreeNode.cs b/ILSpy/TreeNodes/BaseTypesTreeNode.cs index 58565a938..8cbdb2304 100644 --- a/ILSpy/TreeNodes/BaseTypesTreeNode.cs +++ b/ILSpy/TreeNodes/BaseTypesTreeNode.cs @@ -69,14 +69,12 @@ namespace ICSharpCode.ILSpy.TreeNodes } } - public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren)); foreach (ILSpyTreeNode child in this.Children) { child.Decompile(language, output, options); } - - return true; } } } \ No newline at end of file diff --git a/ILSpy/TreeNodes/DerivedTypesEntryNode.cs b/ILSpy/TreeNodes/DerivedTypesEntryNode.cs index f3d9fe29f..91da3e033 100644 --- a/ILSpy/TreeNodes/DerivedTypesEntryNode.cs +++ b/ILSpy/TreeNodes/DerivedTypesEntryNode.cs @@ -90,10 +90,9 @@ namespace ICSharpCode.ILSpy.TreeNodes e.Handled = BaseTypesEntryNode.ActivateItem(this, type); } - public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, language.TypeToString(type, includeNamespace: true)); - return true; } IEntity IMemberTreeNode.Member => type; diff --git a/ILSpy/TreeNodes/DerivedTypesTreeNode.cs b/ILSpy/TreeNodes/DerivedTypesTreeNode.cs index 8518e08e3..3b6e50a30 100644 --- a/ILSpy/TreeNodes/DerivedTypesTreeNode.cs +++ b/ILSpy/TreeNodes/DerivedTypesTreeNode.cs @@ -92,10 +92,9 @@ namespace ICSharpCode.ILSpy.TreeNodes return typeRef.GetFullTypeName(referenceMetadata) == typeDef.GetFullTypeName(definitionMetadata); } - public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { threading.Decompile(language, output, options, EnsureLazyChildren); - return true; } } } \ No newline at end of file diff --git a/ILSpy/TreeNodes/EventTreeNode.cs b/ILSpy/TreeNodes/EventTreeNode.cs index c1797f794..1aadff896 100644 --- a/ILSpy/TreeNodes/EventTreeNode.cs +++ b/ILSpy/TreeNodes/EventTreeNode.cs @@ -67,10 +67,9 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Hidden; } - public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.DecompileEvent(EventDefinition, output, options); - return true; } public override bool IsPublicAPI { diff --git a/ILSpy/TreeNodes/FieldTreeNode.cs b/ILSpy/TreeNodes/FieldTreeNode.cs index 5d3323beb..5d4d2f7b0 100644 --- a/ILSpy/TreeNodes/FieldTreeNode.cs +++ b/ILSpy/TreeNodes/FieldTreeNode.cs @@ -68,10 +68,9 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Hidden; } - public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.DecompileField(FieldDefinition, output, options); - return true; } public override bool IsPublicAPI { diff --git a/ILSpy/TreeNodes/ILSpyTreeNode.cs b/ILSpy/TreeNodes/ILSpyTreeNode.cs index 515188374..11fe3b278 100644 --- a/ILSpy/TreeNodes/ILSpyTreeNode.cs +++ b/ILSpy/TreeNodes/ILSpyTreeNode.cs @@ -57,7 +57,7 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Hidden; } - public abstract object Decompile(Language language, ITextOutput output, DecompilationOptions options); + public abstract void Decompile(Language language, ITextOutput output, DecompilationOptions options); /// /// Used to implement special view logic for some items. diff --git a/ILSpy/TreeNodes/MethodTreeNode.cs b/ILSpy/TreeNodes/MethodTreeNode.cs index 89c12ec10..9e4d95e45 100644 --- a/ILSpy/TreeNodes/MethodTreeNode.cs +++ b/ILSpy/TreeNodes/MethodTreeNode.cs @@ -83,10 +83,9 @@ namespace ICSharpCode.ILSpy.TreeNodes } } - public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.DecompileMethod(MethodDefinition, output, options); - return true; } public override FilterResult Filter(FilterSettings settings) diff --git a/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs b/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs index a363c46db..4d51f46af 100644 --- a/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs +++ b/ILSpy/TreeNodes/ModuleReferenceTreeNode.cs @@ -72,11 +72,10 @@ namespace ICSharpCode.ILSpy.TreeNodes } } - public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, moduleName); language.WriteCommentLine(output, containsMetadata ? "contains metadata" : "contains no metadata"); - return true; } } } diff --git a/ILSpy/TreeNodes/NamespaceTreeNode.cs b/ILSpy/TreeNodes/NamespaceTreeNode.cs index 3b9ff4845..d054603dc 100644 --- a/ILSpy/TreeNodes/NamespaceTreeNode.cs +++ b/ILSpy/TreeNodes/NamespaceTreeNode.cs @@ -56,10 +56,9 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Recurse; } - public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.DecompileNamespace(name, this.Children.OfType().Select(t => t.TypeDefinition), output, options); - return true; } } } diff --git a/ILSpy/TreeNodes/PropertyTreeNode.cs b/ILSpy/TreeNodes/PropertyTreeNode.cs index 3267a1e1a..14769ea66 100644 --- a/ILSpy/TreeNodes/PropertyTreeNode.cs +++ b/ILSpy/TreeNodes/PropertyTreeNode.cs @@ -74,10 +74,9 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Hidden; } - public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.DecompileProperty(PropertyDefinition, output, options); - return true; } public override bool IsPublicAPI { diff --git a/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs b/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs index 9121e213f..fde4630f1 100644 --- a/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs +++ b/ILSpy/TreeNodes/ReferenceFolderTreeNode.cs @@ -62,7 +62,7 @@ namespace ICSharpCode.ILSpy.TreeNodes this.Children.Add(new ModuleReferenceTreeNode(parentAssembly, r, metadata)); } - public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, $"Detected Target-Framework-Id: {parentAssembly.LoadedAssembly.GetTargetFrameworkIdAsync().Result}"); App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren)); @@ -86,7 +86,6 @@ namespace ICSharpCode.ILSpy.TreeNodes output.Unindent(); output.WriteLine(); } - return true; } } } diff --git a/ILSpy/TreeNodes/ResourceListTreeNode.cs b/ILSpy/TreeNodes/ResourceListTreeNode.cs index 79cc1df50..c67c9d4b1 100644 --- a/ILSpy/TreeNodes/ResourceListTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceListTreeNode.cs @@ -64,15 +64,13 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Recurse; } - public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren)); foreach (ILSpyTreeNode child in this.Children) { child.Decompile(language, output, options); output.WriteLine(); } - - return true; } } } diff --git a/ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs index 3805db739..fb8b2199f 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs @@ -75,10 +75,9 @@ namespace ICSharpCode.ILSpy.TreeNodes } - public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { EnsureLazyChildren(); - return true; } } } diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs index cf5a3d5ac..ec74ad193 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs @@ -73,10 +73,9 @@ namespace ICSharpCode.ILSpy.TreeNodes return result; } - public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, string.Format("{0} = {1}", key, data)); - return true; } public override bool Save(DecompilerTextView textView) diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs index c9adaa49c..3433f70d7 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs @@ -67,7 +67,7 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Hidden; } - public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.WriteCommentLine(output, string.Format("{0} ({1}, {2})", r.Name, r.ResourceType, r.Attributes)); @@ -76,8 +76,6 @@ namespace ICSharpCode.ILSpy.TreeNodes smartOutput.AddButton(Images.Save, Resources.Save, delegate { Save(MainWindow.Instance.TextView); }); output.WriteLine(); } - - return true; } public override bool View(DecompilerTextView textView) diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs index a44edeae4..c1d908481 100644 --- a/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs @@ -141,7 +141,7 @@ namespace ICSharpCode.ILSpy.TreeNodes return true; } - public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { EnsureLazyChildren(); base.Decompile(language, output, options); @@ -168,8 +168,6 @@ namespace ICSharpCode.ILSpy.TreeNodes } output.WriteLine(); } - - return true; } internal class SerializedObjectRepresentation diff --git a/ILSpy/TreeNodes/ThreadingSupport.cs b/ILSpy/TreeNodes/ThreadingSupport.cs index 8df078cad..e95787efc 100644 --- a/ILSpy/TreeNodes/ThreadingSupport.cs +++ b/ILSpy/TreeNodes/ThreadingSupport.cs @@ -128,9 +128,8 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Match; } - public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { - return false; } } @@ -152,9 +151,8 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Match; } - public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { - return false; } } diff --git a/ILSpy/TreeNodes/TypeTreeNode.cs b/ILSpy/TreeNodes/TypeTreeNode.cs index 4464a3521..3ef91c411 100644 --- a/ILSpy/TreeNodes/TypeTreeNode.cs +++ b/ILSpy/TreeNodes/TypeTreeNode.cs @@ -103,10 +103,9 @@ namespace ICSharpCode.ILSpy.TreeNodes public override bool CanExpandRecursively => true; - public override object Decompile(Language language, ITextOutput output, DecompilationOptions options) + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) { language.DecompileType(TypeDefinition, output, options); - return true; } public override object Icon => GetIcon(TypeDefinition); From c56714c607a22e9a2896f2a35b70de3877c6c4aa Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 24 Jul 2019 00:57:44 +0200 Subject: [PATCH 086/221] Fix assertion with async lambdas; fix async local functions. --- .../TestCases/Pretty/Async.cs | 30 +++++++++++++++++++ .../CSharp/StatementBuilder.cs | 3 ++ .../IL/ControlFlow/YieldReturnDecompiler.cs | 2 +- .../IL/Instructions/ILFunction.cs | 2 +- 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs index 49a9a47f3..95c1f2def 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs @@ -137,6 +137,36 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } return num; } + + public static Func> AsyncLambda() + { + return async () => await GetIntegerSumAsync(new int[3] { + 1, + 2, + 3 + }); + } + + public static Func> AsyncDelegate() + { + return async delegate { + await Task.Delay(10); + return 2; + }; + } + +#if CS70 + public static async Task AsyncLocalFunctions() + { + return await Nested(1) + await Nested(2); + + async Task Nested(int i) + { + await Task.Delay(i); + return i; + } + } +#endif } public struct HopToThreadPoolAwaitable : INotifyCompletion diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index af9e8cff9..06ea73131 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -968,6 +968,9 @@ namespace ICSharpCode.Decompiler.CSharp stmt.Parameters.AddRange(exprBuilder.MakeParameters(function.Parameters, function)); stmt.ReturnType = exprBuilder.ConvertType(function.Method.ReturnType); stmt.Body = nestedBuilder.ConvertAsBlock(function.Body); + if (function.IsAsync) { + stmt.Modifiers |= Modifiers.Async; + } stmt.AddAnnotation(new MemberResolveResult(null, function.ReducedMethod)); return stmt; } diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs index bbdafe4e1..6b86e5d42 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs @@ -390,7 +390,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow methodTypeParameters: null); var body = context.TypeSystem.MainModule.PEFile.Reader.GetMethodBody(methodDef.RelativeVirtualAddress); var il = context.CreateILReader() - .ReadIL(method, body, genericContext, context.Function.Kind, context.CancellationToken); + .ReadIL(method, body, genericContext, ILFunctionKind.TopLevelFunction, context.CancellationToken); il.RunTransforms(CSharpDecompiler.EarlyILTransforms(true), new ILTransformContext(il, context.TypeSystem, context.DebugInfo, context.Settings) { CancellationToken = context.CancellationToken, diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs index 29370678b..0086b3f2d 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs @@ -226,7 +226,7 @@ namespace ICSharpCode.Decompiler.IL Debug.Assert(DelegateType?.FullName == "System.Linq.Expressions.Expression" && DelegateType.TypeParameterCount == 1); break; case ILFunctionKind.LocalFunction: - Debug.Assert(Parent is ILFunction); + Debug.Assert(Parent is ILFunction && SlotInfo == ILFunction.LocalFunctionsSlot); Debug.Assert(DeclarationScope != null); Debug.Assert(DelegateType == null); Debug.Assert(Method != null); From 24e492bfe306bdbd3718f76b5254d684ee2fe566 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 24 Jul 2019 01:10:19 +0200 Subject: [PATCH 087/221] Adjust SaveCommandCanExecute to match SaveCommandExecuted. --- ILSpy/MainWindow.xaml | 2 +- ILSpy/MainWindow.xaml.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml index 166c8ac80..ed8cc1b1b 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -28,7 +28,7 @@ Executed="RefreshCommandExecuted" /> n is AssemblyTreeNode); + e.CanExecute = selectedNodes.Count == 1 || (selectedNodes.Count > 1 && selectedNodes.All(n => n is AssemblyTreeNode)); } void SaveCommandExecuted(object sender, ExecutedRoutedEventArgs e) @@ -919,7 +919,7 @@ namespace ICSharpCode.ILSpy // we will invoke the custom Save logic if (selectedNodes[0].Save(TextView)) return; - } else if (selectedNodes.All(n => n is AssemblyTreeNode)) { + } else if (selectedNodes.Count > 1 && selectedNodes.All(n => n is AssemblyTreeNode)) { var initialPath = Path.GetDirectoryName(((AssemblyTreeNode)selectedNodes[0]).LoadedAssembly.FileName); var selectedPath = SelectSolutionFile(initialPath); From 01b8b833605c086dba86ba1ad9b9a53ca5f3c136 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 19 Jul 2019 16:24:16 +0200 Subject: [PATCH 088/221] Fix CSharpDecompiler.ReadCodeMappingInfo not taking generic lambdas into account. --- .../CSharp/CSharpDecompiler.cs | 104 +++++++++++------- 1 file changed, 66 insertions(+), 38 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 01a8ad23f..e233ec849 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -622,39 +622,7 @@ namespace ICSharpCode.Decompiler.CSharp var memberRef = module.Metadata.GetMemberReference((MemberReferenceHandle)token); if (memberRef.GetKind() != MemberReferenceKind.Field) continue; - switch (memberRef.Parent.Kind) { - case HandleKind.TypeReference: - // This should never happen in normal code, because we are looking at nested types - // If it's not a nested type, it can't be a reference to the state machine anyway, and - // those should be either TypeDef or TypeSpec. - continue; - case HandleKind.TypeDefinition: - fsmTypeDef = (TypeDefinitionHandle)memberRef.Parent; - break; - case HandleKind.TypeSpecification: - var ts = module.Metadata.GetTypeSpecification((TypeSpecificationHandle)memberRef.Parent); - if (ts.Signature.IsNil) - continue; - // Do a quick scan using BlobReader - var signature = module.Metadata.GetBlobReader(ts.Signature); - // When dealing with FSM implementations, we can safely assume that if it's a type spec, - // it must be a generic type instance. - if (signature.ReadByte() != (byte)SignatureTypeCode.GenericTypeInstance) - continue; - // Skip over the rawTypeKind: value type or class - var rawTypeKind = signature.ReadCompressedInteger(); - if (rawTypeKind < 17 || rawTypeKind > 18) - continue; - // Only read the generic type, ignore the type arguments - var genericType = signature.ReadTypeHandle(); - // Again, we assume this is a type def, because we are only looking at nested types - if (genericType.Kind != HandleKind.TypeDefinition) - continue; - fsmTypeDef = (TypeDefinitionHandle)genericType; - break; - default: - continue; - } + fsmTypeDef = ExtractDeclaringType(memberRef); break; default: continue; @@ -664,10 +632,10 @@ namespace ICSharpCode.Decompiler.CSharp // Must be a nested type of the containing type. if (fsmType.GetDeclaringType() != declaringType) break; - if (!processedNestedTypes.Add(fsmTypeDef)) - break; if (YieldReturnDecompiler.IsCompilerGeneratorEnumerator(fsmTypeDef, module.Metadata) || AsyncAwaitDecompiler.IsCompilerGeneratedStateMachine(fsmTypeDef, module.Metadata)) { + if (!processedNestedTypes.Add(fsmTypeDef)) + break; foreach (var h in fsmType.GetMethods()) { if (module.MethodSemanticsLookup.GetSemantics(h).Item2 != 0) continue; @@ -682,9 +650,35 @@ namespace ICSharpCode.Decompiler.CSharp case ILOpCode.Ldftn: // deal with ldftn instructions, i.e., lambdas token = MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32()); - if (!token.IsNil && token.Kind == HandleKind.MethodDefinition) { - if (((MethodDefinitionHandle)token).IsCompilerGeneratedOrIsInCompilerGeneratedClass(module.Metadata)) - connectedMethods.Enqueue((MethodDefinitionHandle)token); + if (token.IsNil) + continue; + TypeDefinitionHandle closureTypeHandle; + switch (token.Kind) { + case HandleKind.MethodDefinition: + if (((MethodDefinitionHandle)token).IsCompilerGeneratedOrIsInCompilerGeneratedClass(module.Metadata)) { + connectedMethods.Enqueue((MethodDefinitionHandle)token); + } + continue; + case HandleKind.MemberReference: + var memberRef = module.Metadata.GetMemberReference((MemberReferenceHandle)token); + if (memberRef.GetKind() != MemberReferenceKind.Method) + continue; + closureTypeHandle = ExtractDeclaringType(memberRef); + if (!closureTypeHandle.IsNil) { + var closureType = module.Metadata.GetTypeDefinition(closureTypeHandle); + // Must be a nested type of the containing type. + if (closureType.GetDeclaringType() != declaringType) + break; + if (!processedNestedTypes.Add(closureTypeHandle)) + break; + foreach (var m in closureType.GetMethods()) { + connectedMethods.Enqueue(m); + } + break; + } + break; + default: + continue; } break; case ILOpCode.Call: @@ -716,6 +710,40 @@ namespace ICSharpCode.Decompiler.CSharp } info.AddMapping(parent, part); + + TypeDefinitionHandle ExtractDeclaringType(MemberReference memberRef) + { + switch (memberRef.Parent.Kind) { + case HandleKind.TypeReference: + // This should never happen in normal code, because we are looking at nested types + // If it's not a nested type, it can't be a reference to the state machine or lambda anyway, and + // those should be either TypeDef or TypeSpec. + return default; + case HandleKind.TypeDefinition: + return (TypeDefinitionHandle)memberRef.Parent; + case HandleKind.TypeSpecification: + var ts = module.Metadata.GetTypeSpecification((TypeSpecificationHandle)memberRef.Parent); + if (ts.Signature.IsNil) + return default; + // Do a quick scan using BlobReader + var signature = module.Metadata.GetBlobReader(ts.Signature); + // When dealing with FSM implementations, we can safely assume that if it's a type spec, + // it must be a generic type instance. + if (signature.ReadByte() != (byte)SignatureTypeCode.GenericTypeInstance) + return default; + // Skip over the rawTypeKind: value type or class + var rawTypeKind = signature.ReadCompressedInteger(); + if (rawTypeKind < 17 || rawTypeKind > 18) + return default; + // Only read the generic type, ignore the type arguments + var genericType = signature.ReadTypeHandle(); + // Again, we assume this is a type def, because we are only looking at nested types + if (genericType.Kind != HandleKind.TypeDefinition) + return default; + return (TypeDefinitionHandle)genericType; + } + return default; + } } /// From 9dd22011d65fcdd91cb4f7a9b062d0fca6034722 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 24 Jul 2019 08:05:37 +0200 Subject: [PATCH 089/221] Remove ILSpyTreeNode.Save accepting a file name, as it is currently not necessary. Remove focused element check in SaveCommandCanExecute, because saving the text view content independently from the tree view selection is currently not supported. --- ILSpy/Controls/ExtensionMethods.cs | 34 ----------------- ILSpy/MainWindow.xaml.cs | 7 +--- ILSpy/Properties/Resources.Designer.cs | 18 +++++++++ ILSpy/Properties/Resources.resx | 6 +++ ILSpy/TreeNodes/AssemblyTreeNode.cs | 52 ++++++++++---------------- ILSpy/TreeNodes/ILSpyTreeNode.cs | 9 ----- 6 files changed, 44 insertions(+), 82 deletions(-) diff --git a/ILSpy/Controls/ExtensionMethods.cs b/ILSpy/Controls/ExtensionMethods.cs index 1c5e9f794..095ddc41e 100644 --- a/ILSpy/Controls/ExtensionMethods.cs +++ b/ILSpy/Controls/ExtensionMethods.cs @@ -19,7 +19,6 @@ using System; using System.Windows; using System.Windows.Markup; -using System.Windows.Media; namespace ICSharpCode.ILSpy.Controls { @@ -28,39 +27,6 @@ namespace ICSharpCode.ILSpy.Controls /// public static class ExtensionMethods { - /// - /// Checks if the current is contained in the visual tree of the - /// object. - /// - /// The object to check, may be null. - /// The object whose visual tree will be inspected. - /// - /// true if this object is contained in the visual tree of the ; - /// otherwise, false. - /// - /// Thrown when is null. - public static bool IsInVisualTreeOf(this DependencyObject thisObject, DependencyObject dependencyObject) - { - if (dependencyObject == null) { - throw new ArgumentNullException(nameof(dependencyObject)); - } - - if (thisObject is null) { - return false; - } - - var parent = VisualTreeHelper.GetParent(thisObject); - while (parent != null) { - if (parent == dependencyObject) { - return true; - } - - parent = VisualTreeHelper.GetParent(parent); - } - - return false; - } - /// /// Sets the value of a dependency property on using a markup extension. /// diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 5bfb8366b..1fabedcd1 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -902,11 +902,6 @@ namespace ICSharpCode.ILSpy void SaveCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { e.Handled = true; - var focusedElement = FocusManager.GetFocusedElement(this) as DependencyObject; - if (focusedElement.IsInVisualTreeOf(TextView)) { - e.CanExecute = true; - return; - } var selectedNodes = SelectedNodes.ToList(); e.CanExecute = selectedNodes.Count == 1 || (selectedNodes.Count > 1 && selectedNodes.All(n => n is AssemblyTreeNode)); } @@ -926,7 +921,7 @@ namespace ICSharpCode.ILSpy if (!string.IsNullOrEmpty(selectedPath)) { var assemblies = selectedNodes.OfType() .Select(n => n.LoadedAssembly) - .Where(a => a != null).ToArray(); + .Where(a => !a.HasLoadError).ToArray(); SolutionWriter.CreateSolution(TextView, selectedPath, CurrentLanguage, assemblies); } return; diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index 5c86e06e2..859948997 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -303,6 +303,24 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to The directory is not empty. File will be overwritten.\r\nAre you sure you want to continue?. + /// + public static string AssemblySaveCodeDirectoryNotEmpty { + get { + return ResourceManager.GetString("AssemblySaveCodeDirectoryNotEmpty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Project Directory not empty. + /// + public static string AssemblySaveCodeDirectoryNotEmptyTitle { + get { + return ResourceManager.GetString("AssemblySaveCodeDirectoryNotEmptyTitle", resourceCulture); + } + } + /// /// Looks up a localized string similar to Automatically check for updates every week. /// diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index 0128389d9..899febe76 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -729,4 +729,10 @@ Entity could not be resolved. Cannot analyze entities from missing assembly references. Add the missing reference and try again. + + The directory is not empty. File will be overwritten.\r\nAre you sure you want to continue? + + + Project Directory not empty + \ No newline at end of file diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index 60f34e6b2..13e2c1df2 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -279,44 +279,30 @@ namespace ICSharpCode.ILSpy.TreeNodes public override bool Save(DecompilerTextView textView) { Language language = this.Language; - if (string.IsNullOrEmpty(language.ProjectFileExtension)) { + if (string.IsNullOrEmpty(language.ProjectFileExtension)) return false; - } - SaveFileDialog dlg = new SaveFileDialog(); dlg.FileName = DecompilerTextView.CleanUpName(LoadedAssembly.ShortName) + language.ProjectFileExtension; - dlg.Filter = language.Name + " project|*" + language.ProjectFileExtension; - if (dlg.ShowDialog() != true) { - return true; - } - - var targetDirectory = Path.GetDirectoryName(dlg.FileName); - var existingFiles = Directory.GetFileSystemEntries(targetDirectory); - - if (existingFiles.Any(e => !string.Equals(e, dlg.FileName, StringComparison.OrdinalIgnoreCase))) { - var result = MessageBox.Show( - "The directory is not empty. File will be overwritten." + Environment.NewLine + - "Are you sure you want to continue?", - "Project Directory not empty", - MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No); - if (result == MessageBoxResult.No) { - return true; // don't save, but mark the Save operation as handled + dlg.Filter = language.Name + " project|*" + language.ProjectFileExtension + "|" + language.Name + " single file|*" + language.FileExtension + "|All files|*.*"; + if (dlg.ShowDialog() == true) { + DecompilationOptions options = new DecompilationOptions(); + options.FullDecompilation = true; + if (dlg.FilterIndex == 1) { + options.SaveAsProjectDirectory = Path.GetDirectoryName(dlg.FileName); + foreach (string entry in Directory.GetFileSystemEntries(options.SaveAsProjectDirectory)) { + if (!string.Equals(entry, dlg.FileName, StringComparison.OrdinalIgnoreCase)) { + var result = MessageBox.Show( + Resources.AssemblySaveCodeDirectoryNotEmpty, + Resources.AssemblySaveCodeDirectoryNotEmptyTitle, + MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No); + if (result == MessageBoxResult.No) + return true; // don't save, but mark the Save operation as handled + break; + } + } } + textView.SaveToDisk(language, new[] { this }, options, dlg.FileName); } - - Save(textView, dlg.FileName); - return true; - } - - public override bool Save(DecompilerTextView textView, string fileName) - { - var targetDirectory = Path.GetDirectoryName(fileName); - DecompilationOptions options = new DecompilationOptions { - FullDecompilation = true, - SaveAsProjectDirectory = targetDirectory - }; - - textView.SaveToDisk(Language, new[] { this }, options, fileName); return true; } diff --git a/ILSpy/TreeNodes/ILSpyTreeNode.cs b/ILSpy/TreeNodes/ILSpyTreeNode.cs index 11fe3b278..fa1e663e4 100644 --- a/ILSpy/TreeNodes/ILSpyTreeNode.cs +++ b/ILSpy/TreeNodes/ILSpyTreeNode.cs @@ -79,15 +79,6 @@ namespace ICSharpCode.ILSpy.TreeNodes return false; } - /// - /// Saves the content this node represents to the specified . - /// The file will be silently overwritten. - /// - /// A reference to a instance. - /// The target full path to save the content to. - /// true on success; otherwise, false. - public virtual bool Save(TextView.DecompilerTextView textView, string fileName) => Save(textView); - protected override void OnChildrenChanged(NotifyCollectionChangedEventArgs e) { if (e.NewItems != null) { From 61231b7725520da9106c62f2173f0022ab50c6cf Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 24 Jul 2019 12:55:10 +0200 Subject: [PATCH 090/221] Fix #1593: TransformCollectionAndObjectInitializers uses nested indexer call as target of object initializer. --- ...ransformCollectionAndObjectInitializers.cs | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs index 14e939cfd..894194b43 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs @@ -216,15 +216,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms case AccessPathKind.Setter: if (isCollection || !pathStack.Peek().Add(lastElement)) return false; - if (values.Count == 1) { - blockKind = BlockKind.ObjectInitializer; - return true; - } - return false; + if (values.Count != 1 || !IsValidObjectInitializerTarget(currentPath)) + return false; + blockKind = BlockKind.ObjectInitializer; + return true; default: return false; } } + + bool IsValidObjectInitializerTarget(List path) + { + if (path.Count == 0) + return true; + var element = path.Last(); + var previous = path.SkipLast(1).LastOrDefault(); + if (!(element.Member is IProperty p)) + return true; + return !p.IsIndexer || (previous.Member?.ReturnType.Equals(element.Member.DeclaringType) == true); + } } public enum AccessPathKind From 539e3a906dff5231fde0f6f0710ed11680f4f905 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 24 Jul 2019 17:55:27 +0200 Subject: [PATCH 091/221] Fix #1552: user-defined and decimal increments For user-defined increments, there were problems with Roslyn was optimizing out one of the stores. The new transform FixRemainingIncrements now takes increments/decrements that were not detected by TransformAssignment and introduces a temporary variable that can be incremented. This sometimes requires un-inlining via the new ILInstruction.Extract() operation. Extract() is not supported in all possible contexts, so it is possible but unlikely that some op_Increment calls remain. For decimals, the situation is different: legacy csc actually was optimizing "d + 1m" to "op_Increment(d)", so we can get rid of any left-over increments by undoing this optimization. This now happens in ReplaceMethodCallsWithOperators. --- .../Pretty/CompoundAssignmentTest.cs | 45 ++++++++ .../TestCases/Pretty/ConstantsTests.cs | 15 +++ .../CSharp/CSharpDecompiler.cs | 1 + .../ReplaceMethodCallsWithOperators.cs | 16 +++ .../ICSharpCode.Decompiler.csproj | 2 + .../IL/Instructions/ILInstruction.cs | 36 ++++++ .../IL/Instructions/NullableInstructions.cs | 7 ++ .../IL/Transforms/ExpressionTransforms.cs | 34 +++++- .../IL/Transforms/FixRemainingIncrements.cs | 65 +++++++++++ .../IL/Transforms/ILExtraction.cs | 103 ++++++++++++++++++ .../IL/Transforms/TransformAssignment.cs | 13 +++ 11 files changed, 336 insertions(+), 1 deletion(-) create mode 100644 ICSharpCode.Decompiler/IL/Transforms/FixRemainingIncrements.cs create mode 100644 ICSharpCode.Decompiler/IL/Transforms/ILExtraction.cs diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs index 5826fb09f..109831418 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs @@ -4720,5 +4720,50 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { return (*GetPointer())++; } + + public void Issue1552Pre(CustomStruct a, CustomStruct b) + { + CustomStruct customStruct = a + b; + Console.WriteLine(++customStruct); + } + + public void Issue1552Stmt(CustomStruct a, CustomStruct b) + { + CustomStruct customStruct = a + b; + ++customStruct; + } + + public void Issue1552StmtUseLater(CustomStruct a, CustomStruct b) + { + CustomStruct lhs = a + b; + ++lhs; + Console.WriteLine(); + Console.WriteLine(lhs * b); + } + + public void Issue1552Decimal(decimal a) + { + // Legacy csc compiles this using op_Increment, + // ensure we don't misdetect this as an invalid pre-increment "++(a * 10m)" + Console.WriteLine(a * 10m + 1m); + } + +#if !(ROSLYN && OPT) + // Roslyn opt no longer has a detectable post-increment pattern + // due to optimizing out some of the stores. + // Our emitted code is valid but has some additional temporaries. + public void Issue1552Post(CustomStruct a, CustomStruct b) + { + CustomStruct customStruct = a + b; + Console.WriteLine(customStruct++); + } + + public void Issue1552StmtTwice(CustomStruct a, CustomStruct b) + { + CustomStruct customStruct = a + b; + ++customStruct; + ++customStruct; + } +#endif } } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ConstantsTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ConstantsTests.cs index f2cb636dd..83d9eb8bf 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ConstantsTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ConstantsTests.cs @@ -35,5 +35,20 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty private void Test(bool expr) { } + + private void Test(decimal expr) + { + } + + public void Decimal() + { + // Roslyn and legacy csc both normalize the decimal constant references, + // but to a different representation (ctor call vs. field use) + Test(0m); + Test(1m); + Test(-1m); + Test(decimal.MinValue); + Test(decimal.MaxValue); + } } } diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index e233ec849..9c73c07ce 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -155,6 +155,7 @@ namespace ICSharpCode.Decompiler.CSharp } }, new ProxyCallReplacer(), + new FixRemainingIncrements(), new DelegateConstruction(), new LocalFunctionDecompiler(), new TransformDisplayClassUsage(), diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs index 0486ce871..7949e1df6 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs @@ -122,6 +122,22 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } UnaryOperatorType? uop = GetUnaryOperatorTypeFromMetadataName(method.Name); if (uop != null && arguments.Length == 1) { + if (uop == UnaryOperatorType.Increment || uop == UnaryOperatorType.Decrement) { + // `op_Increment(a)` is not equivalent to `++a`, + // because it doesn't assign the incremented value to a. + if (method.DeclaringType.IsKnownType(KnownTypeCode.Decimal)) { + // Legacy csc optimizes "d + 1m" to "op_Increment(d)", + // so reverse that optimization here: + invocationExpression.ReplaceWith( + new BinaryOperatorExpression( + arguments[0].Detach(), + (uop == UnaryOperatorType.Increment ? BinaryOperatorType.Add : BinaryOperatorType.Subtract), + new PrimitiveExpression(1m) + ).CopyAnnotationsFrom(invocationExpression) + ); + } + return; + } arguments[0].Remove(); // detach argument invocationExpression.ReplaceWith( new UnaryOperatorExpression(uop.Value, arguments[0]).CopyAnnotationsFrom(invocationExpression) diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 80315ede2..e2080e799 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -227,6 +227,8 @@ + + diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs index cf5677f7d..d1d7e17f8 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Threading; using ICSharpCode.Decompiler.IL.Patterns; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; @@ -758,6 +759,41 @@ namespace ICSharpCode.Decompiler.IL } return false; } + + /// + /// Extracts the this instruction. + /// The instruction is replaced with a load of a new temporary variable; + /// and the instruction is moved to a store to said variable at block-level. + /// Returns the new variable. + /// + /// If extraction is not possible, the ILAst is left unmodified and the function returns null. + /// May return null if extraction is not possible. + /// + public ILVariable Extract() + { + return Transforms.ExtractionContext.Extract(this); + } + + /// + /// Prepares "extracting" a descendant instruction out of this instruction. + /// This is the opposite of ILInlining. It may involve re-compiling high-level constructs into lower-level constructs. + /// + /// True if extraction is possible; false otherwise. + internal virtual bool PrepareExtract(int childIndex, Transforms.ExtractionContext ctx) + { + if (!GetChildSlot(childIndex).CanInlineInto) { + return false; + } + // Check whether re-ordering with predecessors is valid: + for (int i = childIndex - 1; i >= 0; --i) { + ILInstruction predecessor = GetChild(i); + if (!GetChildSlot(i).CanInlineInto) { + return false; + } + ctx.RegisterMoveIfNecessary(predecessor); + } + return true; + } } public interface IInstructionWithTypeOperand diff --git a/ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs b/ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs index 8e638ba33..bfe56d08f 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs @@ -18,6 +18,7 @@ using System.Diagnostics; using System.Linq; +using ICSharpCode.Decompiler.IL.Transforms; namespace ICSharpCode.Decompiler.IL { @@ -125,5 +126,11 @@ namespace ICSharpCode.Decompiler.IL return StackType.O; } } + + internal override bool PrepareExtract(int childIndex, ExtractionContext ctx) + { + return base.PrepareExtract(childIndex, ctx) + && (ctx.FlagsBeingMoved & InstructionFlags.MayUnwrapNull) == 0; + } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 834e0d804..cebcd3ef8 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -390,11 +390,37 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } + bool TransformDecimalFieldToConstant(LdObj inst, out LdcDecimal result) + { + if (inst.MatchLdsFld(out var field) && field.DeclaringType.IsKnownType(KnownTypeCode.Decimal)) { + decimal? value = null; + if (field.Name == "One") { + value = decimal.One; + } else if (field.Name == "MinusOne") { + value = decimal.MinusOne; + } else if (field.Name == "Zero") { + value = decimal.Zero; + } + if (value != null) { + result = new LdcDecimal(value.Value).WithILRange(inst).WithILRange(inst.Target); + return true; + } + } + result = null; + return false; + } + protected internal override void VisitLdObj(LdObj inst) { base.VisitLdObj(inst); EarlyExpressionTransforms.AddressOfLdLocToLdLoca(inst, context); - EarlyExpressionTransforms.LdObjToLdLoc(inst, context); + if (EarlyExpressionTransforms.LdObjToLdLoc(inst, context)) + return; + if (TransformDecimalFieldToConstant(inst, out LdcDecimal decimalConstant)) { + context.Step("TransformDecimalFieldToConstant", inst); + inst.ReplaceWith(decimalConstant); + return; + } } protected internal override void VisitStObj(StObj inst) @@ -407,6 +433,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms TransformAssignment.HandleCompoundAssign(inst, context); } + protected internal override void VisitStLoc(StLoc inst) + { + base.VisitStLoc(inst); + TransformAssignment.HandleCompoundAssign(inst, context); + } + protected internal override void VisitIfInstruction(IfInstruction inst) { inst.TrueInst.AcceptVisitor(this); diff --git a/ICSharpCode.Decompiler/IL/Transforms/FixRemainingIncrements.cs b/ICSharpCode.Decompiler/IL/Transforms/FixRemainingIncrements.cs new file mode 100644 index 000000000..f48c345b9 --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Transforms/FixRemainingIncrements.cs @@ -0,0 +1,65 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Text; +using System.Diagnostics; +using ICSharpCode.Decompiler.TypeSystem; + +namespace ICSharpCode.Decompiler.IL.Transforms +{ + public class FixRemainingIncrements : IILTransform + { + void IILTransform.Run(ILFunction function, ILTransformContext context) + { + var callsToFix = new List(); + foreach (var call in function.Descendants.OfType()) { + if (!(call.Method.IsOperator && (call.Method.Name == "op_Increment" || call.Method.Name == "op_Decrement"))) + continue; + if (call.Arguments.Count != 1) + continue; + if (call.Method.DeclaringType.IsKnownType(KnownTypeCode.Decimal)) { + // For decimal, legacy csc can optimize "d + 1m" to "op_Increment(d)". + // We can handle these calls in ReplaceMethodCallsWithOperators. + continue; + } + callsToFix.Add(call); + } + foreach (var call in callsToFix) { + // A user-defined increment/decrement that was not handled by TransformAssignment. + // This can happen because the variable-being-incremented was optimized out by Roslyn, + // e.g. + // public void Issue1552Pre(UserType a, UserType b) + // { + // UserType num = a + b; + // Console.WriteLine(++num); + // } + // can end up being compiled to: + // Console.WriteLine(UserType.op_Increment(a + b)); + if (call.SlotInfo == StLoc.ValueSlot && call.Parent.SlotInfo == Block.InstructionSlot) { + var store = (StLoc)call.Parent; + var block = (Block)store.Parent; + context.Step($"Fix {call.Method.Name} call at 0x{call.StartILOffset:x4} using {store.Variable.Name}", call); + // stloc V(call op_Increment(...)) + // -> + // stloc V(...) + // compound.assign op_Increment(V) + call.ReplaceWith(call.Arguments[0]); + block.Instructions.Insert(store.ChildIndex + 1, + new UserDefinedCompoundAssign(call.Method, CompoundEvalMode.EvaluatesToNewValue, + new LdLoca(store.Variable), CompoundTargetKind.Address, new LdcI4(1)).WithILRange(call)); + } else { + context.Step($"Fix {call.Method.Name} call at 0x{call.StartILOffset:x4} using new local", call); + var newVariable = call.Arguments[0].Extract(); + if (newVariable == null) { + Debug.Fail("Failed to extract argument of remaining increment/decrement"); + continue; + } + newVariable.Type = call.GetParameter(0).Type; + Debug.Assert(call.Arguments[0].MatchLdLoc(newVariable)); + call.ReplaceWith(new UserDefinedCompoundAssign(call.Method, CompoundEvalMode.EvaluatesToNewValue, + new LdLoca(newVariable), CompoundTargetKind.Address, new LdcI4(1)).WithILRange(call)); + } + } + } + } +} diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILExtraction.cs b/ICSharpCode.Decompiler/IL/Transforms/ILExtraction.cs new file mode 100644 index 000000000..5a1cbf01d --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Transforms/ILExtraction.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; + +namespace ICSharpCode.Decompiler.IL.Transforms +{ + /// + /// Context object for the ILInstruction.Extract() operation. + /// + class ExtractionContext + { + /// + /// Nearest function, used for registering the new locals that are created by extraction. + /// + readonly ILFunction Function; + + /// + /// Combined flags of all instructions being moved. + /// + internal InstructionFlags FlagsBeingMoved; + + /// + /// List of actions to be executed when performing the extraction. + /// + /// Each function in this list has the side-effect of replacing the instruction-to-be-moved + /// with a load of a fresh temporary variable; and returns the the store to the temporary variable, + /// which will be inserted at block-level. + /// + readonly List> MoveActions = new List>(); + + ExtractionContext(ILFunction function) + { + Debug.Assert(function != null); + this.Function = function; + } + + internal void RegisterMove(ILInstruction predecessor) + { + FlagsBeingMoved |= predecessor.Flags; + MoveActions.Add(delegate { + var v = Function.RegisterVariable(VariableKind.StackSlot, predecessor.ResultType); + predecessor.ReplaceWith(new LdLoc(v)); + return new StLoc(v, predecessor); + }); + } + + internal void RegisterMoveIfNecessary(ILInstruction predecessor) + { + if (!CanReorderWithInstructionsBeingMoved(predecessor)) { + RegisterMove(predecessor); + } + } + + /// + /// Currently, predecessor is evaluated before the instructions being moved. + /// If this function returns true, predecessor can stay as-is, despite the move changing the evaluation order. + /// If this function returns false, predecessor will need to also move, to ensure the evaluation order stays unchanged. + /// + public bool CanReorderWithInstructionsBeingMoved(ILInstruction predecessor) + { + // We could track the instructions being moved and be smarter about unnecessary moves, + // but given the limited scenarios where extraction is used so far, + // this seems unnecessary. + return predecessor.Flags == InstructionFlags.None; + } + + /// + /// Extracts the specified instruction: + /// The instruction is replaced with a load of a new temporary variable; + /// and the instruction is moved to a store to said variable at block-level. + /// + /// May return null if extraction is not possible. + /// + public static ILVariable Extract(ILInstruction instToExtract) + { + var function = instToExtract.Ancestors.OfType().First(); + ExtractionContext ctx = new ExtractionContext(function); + ctx.FlagsBeingMoved = instToExtract.Flags; + ILInstruction inst = instToExtract; + while (inst != null) { + if (inst.Parent is Block block && block.Kind == BlockKind.ControlFlow) { + // We've reached the target block, and extraction is possible all the way. + int insertIndex = inst.ChildIndex; + // Move instToExtract itself: + var v = function.RegisterVariable(VariableKind.StackSlot, instToExtract.ResultType); + instToExtract.ReplaceWith(new LdLoc(v)); + block.Instructions.Insert(insertIndex, new StLoc(v, instToExtract)); + // Apply the other move actions: + foreach (var moveAction in ctx.MoveActions) { + block.Instructions.Insert(insertIndex, moveAction()); + } + return v; + } + if (!inst.Parent.PrepareExtract(inst.ChildIndex, ctx)) + return null; + inst = inst.Parent; + } + return null; + } + } +} diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs index 5fe7298ce..ef086733d 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs @@ -28,6 +28,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// Constructs compound assignments and inline assignments. /// + /// + /// This is a statement transform; + /// but some portions are executed as an expression transform instead + /// (with HandleCompoundAssign() as entry point) + /// public class TransformAssignment : IStatementTransform { StatementTransformContext context; @@ -294,6 +299,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms } ILInstruction newInst; if (UnwrapSmallIntegerConv(setterValue, out var smallIntConv) is BinaryNumericInstruction binary) { + if (compoundStore is StLoc) { + // transform local variables only for user-defined operators + return false; + } if (!IsMatchingCompoundLoad(binary.Left, compoundStore, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: storeInSetter?.Variable)) return false; if (!ValidateCompoundAssign(binary, smallIntConv, targetType)) @@ -335,6 +344,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms newInst = new DynamicCompoundAssign(dynamicBinaryOp.Operation, dynamicBinaryOp.BinderFlags, target, dynamicBinaryOp.LeftArgumentInfo, dynamicBinaryOp.Right, dynamicBinaryOp.RightArgumentInfo, targetKind); } else if (setterValue is Call concatCall && UserDefinedCompoundAssign.IsStringConcat(concatCall.Method)) { // setterValue is a string.Concat() invocation + if (compoundStore is StLoc) { + // transform local variables only for user-defined operators + return false; + } if (concatCall.Arguments.Count != 2) return false; // for now we only support binary compound assignments if (!targetType.IsKnownType(KnownTypeCode.String)) From b75c2521939aea391acda7b660707fe98ce676be Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 24 Jul 2019 23:10:33 +0200 Subject: [PATCH 092/221] Upgrade C# 8.0 nullability support for Roslyn 3.2.0-beta4. --- .../Helpers/RemoveCompilerAttribute.cs | 1 + .../ICSharpCode.Decompiler.Tests.csproj | 4 +- .../TestCases/Pretty/NullableRefTypes.cs | 52 +++++++++++++++++- ICSharpCode.Decompiler/CSharp/Resolver/Log.cs | 2 +- .../CSharp/Resolver/TypeInference.cs | 6 +- .../CSharp/Syntax/ComposedType.cs | 6 ++ .../Transforms/PatternStatementTransform.cs | 12 +++- .../CSharp/TranslatedExpression.cs | 11 +++- ICSharpCode.Decompiler/SRMExtensions.cs | 25 ++++++++- .../TypeSystem/ApplyAttributeTypeVisitor.cs | 55 +++++++++++++++---- .../TypeSystem/ITypeDefinition.cs | 6 ++ .../Implementation/AttributeListBuilder.cs | 2 + .../Implementation/KnownAttributes.cs | 2 + .../Implementation/MetadataEvent.cs | 6 +- .../Implementation/MetadataField.cs | 3 +- .../Implementation/MetadataMethod.cs | 20 +++++-- .../Implementation/MetadataProperty.cs | 21 +++++-- .../Implementation/MetadataTypeDefinition.cs | 7 ++- .../Implementation/MetadataTypeParameter.cs | 10 +++- .../Implementation/MinimalCorlib.cs | 1 + .../NullabilityAnnotatedType.cs | 13 ++++- .../Implementation/SpecializedMethod.cs | 4 +- .../Implementation/TypeWithElementType.cs | 7 ++- .../TypeSystem/Implementation/UnknownType.cs | 10 +++- .../TypeSystem/MetadataModule.cs | 14 +++-- .../TypeSystem/NormalizeTypeVisitor.cs | 10 ++++ 26 files changed, 258 insertions(+), 52 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs b/ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs index 2deaaab97..214dda115 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/RemoveCompilerAttribute.cs @@ -40,6 +40,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers "System.Runtime.CompilerServices.IsByRefLikeAttribute", "System.Runtime.CompilerServices.IsUnmanagedAttribute", "System.Runtime.CompilerServices.NullableAttribute", + "System.Runtime.CompilerServices.NullableContextAttribute", "Microsoft.CodeAnalysis.EmbeddedAttribute", }; diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index b237cdf3a..4ce11c1db 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -42,8 +42,8 @@ - - + + diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs index 0c2cd8417..1bc660bd4 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs @@ -1,15 +1,20 @@ #nullable enable +using System; using System.Collections.Generic; namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { - public class NullableRefTypes + public class T01_NullableRefTypes { private string field_string; private string? field_nullable_string; private dynamic? field_nullable_dynamic; private Dictionary field_generic; + private Dictionary field_generic2; + private Dictionary field_generic3; + private KeyValuePair field_generic_value_type; + private KeyValuePair? field_generic_nullable_value_type; private (string, string?, string) field_tuple; private string[]?[] field_array; private Dictionary<(string, string?), (int, string[]?, string?[])> field_complex; @@ -28,5 +33,50 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { return field_nullable_string?.Length + arr?.Length; } + + public void GenericNullable((T1?, T1, T2, T2?, T1, T1?) x) where T1 : class where T2 : struct + { + } + + public T ByRef(ref T t) + { + return t; + } + + public void CallByRef(ref string a, ref string? b) + { + ByRef(ref a).ToString(); + ByRef(ref b)!.ToString(); + } + } + + public class T02_EverythingIsNullableInHere + { + private string? field1; + private object? field2; + // value types are irrelevant for the nullability attributes: + private int field3; + private int? field4; + + public string? Property { + get; + set; + } + public event EventHandler? Event; + } + + public class T03_EverythingIsNotNullableInHere + { + private string field1; + private object field2; + // value types are irrelevant for the nullability attributes: + private int field3; + private int? field4; + + public string Property { + get; + set; + } + public event EventHandler Event; } } diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/Log.cs b/ICSharpCode.Decompiler/CSharp/Resolver/Log.cs index e8884a0e0..8b922253f 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/Log.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/Log.cs @@ -29,7 +29,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver /// static class Log { - const bool logEnabled = false; + const bool logEnabled = true; #if __MonoCS__ [Conditional("MCS_DEBUG")] #else diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs b/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs index 1dd855800..86a70872c 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs @@ -570,7 +570,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver void MakeExactInference(IType U, IType V) { Log.WriteLine("MakeExactInference from " + U + " to " + V); - + + if (V is NullabilityAnnotatedTypeParameter nullableTP) { + V = nullableTP.OriginalTypeParameter; + } + // If V is one of the unfixed Xi then U is added to the set of bounds for Xi. TP tp = GetTPForType(V); if (tp != null && tp.IsFixed == false) { diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/ComposedType.cs b/ICSharpCode.Decompiler/CSharp/Syntax/ComposedType.cs index f4b768ad2..e05857a11 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/ComposedType.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/ComposedType.cs @@ -83,6 +83,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } } + public bool HasOnlyNullableSpecifier { + get { + return HasNullableSpecifier && !HasRefSpecifier && !HasReadOnlySpecifier && PointerRank == 0 && ArraySpecifiers.Count == 0; + } + } + public CSharpTokenNode NullableSpecifierToken { get { return GetChildByRole(NullableRole); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs index 8aa5cfedb..df4d62112 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs @@ -777,8 +777,16 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms default: return false; } - if (!ev.ReturnType.IsMatch(m.Get("type").Single())) - return false; // variable types must match event type + if (!ev.ReturnType.IsMatch(m.Get("type").Single())) { + // Variable types must match event type, + // except that the event type may have an additional nullability annotation + if (ev.ReturnType is ComposedType ct && ct.HasOnlyNullableSpecifier) { + if (!ct.BaseType.IsMatch(m.Get("type").Single())) + return false; + } else { + return false; + } + } var combineMethod = m.Get("delegateCombine").Single().Parent.GetSymbol() as IMethod; if (combineMethod == null || combineMethod.Name != (isAddAccessor ? "Combine" : "Remove")) return false; diff --git a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs index e270f5563..62c801cd4 100644 --- a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs @@ -413,8 +413,15 @@ namespace ICSharpCode.Decompiler.CSharp .WithILInstruction(this.ILInstructions) .WithRR(new ConstantResolveResult(targetType, null)); } - if (allowImplicitConversion && conversions.ImplicitConversion(ResolveResult, targetType).IsValid) { - return this; + if (allowImplicitConversion) { + if (conversions.ImplicitConversion(ResolveResult, targetType).IsValid) { + return this; + } + } else { + if (NormalizeTypeVisitor.RemoveModifiersAndNullability.EquivalentTypes(type, targetType)) { + // avoid an explicit cast when types differ only in nullability of reference types + return this; + } } var castExpr = new CastExpression(expressionBuilder.ConvertType(targetType), Expression); bool needsCheckAnnotation = targetUType.GetStackType().IsIntegerType(); diff --git a/ICSharpCode.Decompiler/SRMExtensions.cs b/ICSharpCode.Decompiler/SRMExtensions.cs index fc8fb2eb4..44fd7af01 100644 --- a/ICSharpCode.Decompiler/SRMExtensions.cs +++ b/ICSharpCode.Decompiler/SRMExtensions.cs @@ -1,13 +1,10 @@ using System; -using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using System.Reflection; using System.Reflection.Metadata; using SRM = System.Reflection.Metadata; using System.Reflection.PortableExecutable; using ICSharpCode.Decompiler.TypeSystem; -using ICSharpCode.Decompiler.TypeSystem.Implementation; using ICSharpCode.Decompiler.Util; using System.Reflection.Metadata.Ecma335; @@ -371,6 +368,28 @@ namespace ICSharpCode.Decompiler { return attr.GetAttributeType(metadata).IsKnownType(metadata, attrType); } + + public static Nullability? GetNullableContext(this CustomAttributeHandleCollection customAttributes, MetadataReader metadata) + { + foreach (var handle in customAttributes) { + var customAttribute = metadata.GetCustomAttribute(handle); + if (customAttribute.IsKnownAttribute(metadata, KnownAttribute.NullableContext)) { + // Decode + CustomAttributeValue value; + try { + value = customAttribute.DecodeValue(Metadata.MetadataExtensions.MinimalAttributeTypeProvider); + } catch (BadImageFormatException) { + continue; + } catch (Metadata.EnumUnderlyingTypeResolveException) { + continue; + } + if (value.FixedArguments.Length == 1 && value.FixedArguments[0].Value is byte b && b <= 2) { + return (Nullability)b; + } + } + } + return null; + } #endregion public static unsafe SRM.BlobReader GetInitialValue(this FieldDefinition field, PEReader pefile, ICompilation typeSystem) diff --git a/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs b/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs index a41841a0e..daef269bc 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ApplyAttributeTypeVisitor.cs @@ -37,14 +37,19 @@ namespace ICSharpCode.Decompiler.TypeSystem SRM.CustomAttributeHandleCollection? attributes, SRM.MetadataReader metadata, TypeSystemOptions options, + Nullability nullableContext, bool typeChildrenOnly = false) { bool hasDynamicAttribute = false; bool[] dynamicAttributeData = null; string[] tupleElementNames = null; - bool hasNullableAttribute = false; - Nullability nullability = Nullability.Oblivious; + Nullability nullability; Nullability[] nullableAttributeData = null; + if ((options & TypeSystemOptions.NullabilityAnnotations) != 0) { + nullability = nullableContext; + } else { + nullability = Nullability.Oblivious; + } const TypeSystemOptions relevantOptions = TypeSystemOptions.Dynamic | TypeSystemOptions.Tuple | TypeSystemOptions.NullabilityAnnotations; if (attributes != null && (options & relevantOptions) != 0) { foreach (var attrHandle in attributes.Value) { @@ -70,7 +75,6 @@ namespace ICSharpCode.Decompiler.TypeSystem } } } else if ((options & TypeSystemOptions.NullabilityAnnotations) != 0 && attrType.IsKnownType(metadata, KnownAttribute.Nullable)) { - hasNullableAttribute = true; var ctor = attr.DecodeValue(Metadata.MetadataExtensions.minimalCorlibTypeProvider); if (ctor.FixedArguments.Length == 1) { var arg = ctor.FixedArguments[0]; @@ -78,13 +82,15 @@ namespace ICSharpCode.Decompiler.TypeSystem && values.All(v => v.Value is byte b && b <= 2)) { nullableAttributeData = values.SelectArray(v => (Nullability)(byte)v.Value); } else if (arg.Value is byte b && b <= 2) { - nullability = (Nullability)(byte)arg.Value; + nullability = (Nullability)b; } } } } } - if (hasDynamicAttribute || hasNullableAttribute || (options & (TypeSystemOptions.Tuple | TypeSystemOptions.KeepModifiers)) != TypeSystemOptions.KeepModifiers) { + if (hasDynamicAttribute || nullability != Nullability.Oblivious || nullableAttributeData != null + || (options & (TypeSystemOptions.Tuple | TypeSystemOptions.KeepModifiers)) != TypeSystemOptions.KeepModifiers) + { var visitor = new ApplyAttributeTypeVisitor( compilation, hasDynamicAttribute, dynamicAttributeData, options, tupleElementNames, @@ -148,16 +154,21 @@ namespace ICSharpCode.Decompiler.TypeSystem Nullability GetNullability() { if (nullabilityTypeIndex < nullableAttributeData?.Length) - return nullableAttributeData[nullabilityTypeIndex]; + return nullableAttributeData[nullabilityTypeIndex++]; else return defaultNullability; } + void ExpectDummyNullabilityForGenericValueType() + { + var n = GetNullability(); + Debug.Assert(n == Nullability.Oblivious); + } + public override IType VisitArrayType(ArrayType type) { var nullability = GetNullability(); dynamicTypeIndex++; - nullabilityTypeIndex++; return base.VisitArrayType(type).ChangeNullability(nullability); } @@ -181,18 +192,18 @@ namespace ICSharpCode.Decompiler.TypeSystem elementNames = ImmutableArray.CreateRange(extractedValues); } tupleTypeIndex += tupleCardinality; + ExpectDummyNullabilityForGenericValueType(); var elementTypes = ImmutableArray.CreateBuilder(tupleCardinality); do { int normalArgCount = Math.Min(type.TypeArguments.Count, TupleType.RestPosition - 1); for (int i = 0; i < normalArgCount; i++) { dynamicTypeIndex++; - nullabilityTypeIndex++; elementTypes.Add(type.TypeArguments[i].AcceptVisitor(this)); } if (type.TypeArguments.Count == TupleType.RestPosition) { type = type.TypeArguments.Last() as ParameterizedType; + ExpectDummyNullabilityForGenericValueType(); dynamicTypeIndex++; - nullabilityTypeIndex++; if (type != null && TupleType.IsTupleCompatible(type, out int nestedCardinality)) { tupleTypeIndex += nestedCardinality; } else { @@ -218,11 +229,13 @@ namespace ICSharpCode.Decompiler.TypeSystem // Visit generic type and type arguments. // Like base implementation, except that it increments dynamicTypeIndex. var genericType = type.GenericType.AcceptVisitor(this); + if (genericType.IsReferenceType != true && !genericType.IsKnownType(KnownTypeCode.NullableOfT)) { + ExpectDummyNullabilityForGenericValueType(); + } bool changed = type.GenericType != genericType; var arguments = new IType[type.TypeArguments.Count]; for (int i = 0; i < type.TypeArguments.Count; i++) { dynamicTypeIndex++; - nullabilityTypeIndex++; arguments[i] = type.TypeArguments[i].AcceptVisitor(this); changed = changed || arguments[i] != type.TypeArguments[i]; } @@ -240,8 +253,28 @@ namespace ICSharpCode.Decompiler.TypeSystem else if (dynamicAttributeData[dynamicTypeIndex]) newType = SpecialType.Dynamic; } + if (type.IsReferenceType == true) { + Nullability nullability = GetNullability(); + return newType.ChangeNullability(nullability); + } else { + return newType; + } + } + + public override IType VisitOtherType(IType type) + { + type = base.VisitOtherType(type); + if (type.Kind == TypeKind.Unknown && type.IsReferenceType == true) { + Nullability nullability = GetNullability(); + type = type.ChangeNullability(nullability); + } + return type; + } + + public override IType VisitTypeParameter(ITypeParameter type) + { Nullability nullability = GetNullability(); - return newType.ChangeNullability(nullability); + return type.ChangeNullability(nullability); } } } diff --git a/ICSharpCode.Decompiler/TypeSystem/ITypeDefinition.cs b/ICSharpCode.Decompiler/TypeSystem/ITypeDefinition.cs index 2a3d750ed..19b727adc 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ITypeDefinition.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ITypeDefinition.cs @@ -67,5 +67,11 @@ namespace ICSharpCode.Decompiler.TypeSystem /// /// This property is used to speed up the search for extension methods. bool HasExtensionMethods { get; } + + /// + /// The nullability specified in the [NullableContext] attribute on the type. + /// This serves as default nullability for members of the type that do not have a [Nullable] attribute. + /// + Nullability NullableContext { get; } } } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs index 3f08466ef..1bc076ad9 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AttributeListBuilder.cs @@ -208,6 +208,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation return (options & TypeSystemOptions.UnmanagedConstraints) != 0 && target == SymbolKind.TypeParameter; case "NullableAttribute": return (options & TypeSystemOptions.NullabilityAnnotations) != 0; + case "NullableContextAttribute": + return (options & TypeSystemOptions.NullabilityAnnotations) != 0 && (target == SymbolKind.TypeDefinition || target == SymbolKind.Method); default: return false; } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs index 7cee38c46..209d198eb 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs @@ -40,6 +40,7 @@ namespace ICSharpCode.Decompiler.TypeSystem Dynamic, TupleElementNames, Nullable, + NullableContext, Conditional, Obsolete, IsReadOnly, @@ -107,6 +108,7 @@ namespace ICSharpCode.Decompiler.TypeSystem new TopLevelTypeName("System.Runtime.CompilerServices", nameof(DynamicAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", nameof(TupleElementNamesAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", "NullableAttribute"), + new TopLevelTypeName("System.Runtime.CompilerServices", "NullableContextAttribute"), new TopLevelTypeName("System.Diagnostics", nameof(ConditionalAttribute)), new TopLevelTypeName("System", nameof(ObsoleteAttribute)), new TopLevelTypeName("System.Runtime.CompilerServices", "IsReadOnlyAttribute"), diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataEvent.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataEvent.cs index 43c4a868a..e85a559d3 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataEvent.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataEvent.cs @@ -79,8 +79,10 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation return returnType; var metadata = module.metadata; var ev = metadata.GetEventDefinition(handle); - var context = new GenericContext(DeclaringTypeDefinition?.TypeParameters); - returnType = module.ResolveType(ev.Type, context, ev.GetCustomAttributes()); + var declaringTypeDef = DeclaringTypeDefinition; + var context = new GenericContext(declaringTypeDef?.TypeParameters); + var nullableContext = declaringTypeDef?.NullableContext ?? Nullability.Oblivious; + returnType = module.ResolveType(ev.Type, context, ev.GetCustomAttributes(), nullableContext); return LazyInit.GetOrSet(ref this.returnType, returnType); } } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs index 00d034bb5..18f513939 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataField.cs @@ -186,7 +186,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation ty = mod.ElementType; } ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, - fieldDef.GetCustomAttributes(), metadata, module.TypeSystemOptions); + fieldDef.GetCustomAttributes(), metadata, module.TypeSystemOptions, + DeclaringTypeDefinition?.NullableContext ?? Nullability.Oblivious); } catch (BadImageFormatException) { ty = SpecialType.UnknownType; } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs index a05ec147e..2705ef4be 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs @@ -149,6 +149,13 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } } + internal Nullability NullableContext { + get { + var methodDef = module.metadata.GetMethodDefinition(handle); + return methodDef.GetCustomAttributes().GetNullableContext(module.metadata) ?? DeclaringTypeDefinition.NullableContext; + } + } + private void DecodeSignature() { var methodDef = module.metadata.GetMethodDefinition(handle); @@ -156,8 +163,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation IType returnType; IParameter[] parameters; try { + var nullableContext = methodDef.GetCustomAttributes().GetNullableContext(module.metadata) ?? DeclaringTypeDefinition.NullableContext; var signature = methodDef.DecodeSignature(module.TypeProvider, genericContext); - (returnType, parameters) = DecodeSignature(module, this, signature, methodDef.GetParameters()); + (returnType, parameters) = DecodeSignature(module, this, signature, methodDef.GetParameters(), nullableContext); } catch (BadImageFormatException) { returnType = SpecialType.UnknownType; parameters = Empty.Array; @@ -166,7 +174,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation LazyInit.GetOrSet(ref this.parameters, parameters); } - internal static (IType, IParameter[]) DecodeSignature(MetadataModule module, IParameterizedMember owner, MethodSignature signature, ParameterHandleCollection? parameterHandles) + internal static (IType, IParameter[]) DecodeSignature(MetadataModule module, IParameterizedMember owner, MethodSignature signature, ParameterHandleCollection? parameterHandles, Nullability nullableContext) { var metadata = module.metadata; int i = 0; @@ -187,14 +195,14 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation // Fill gaps in the sequence with non-metadata parameters: while (i < par.SequenceNumber - 1) { parameterType = ApplyAttributeTypeVisitor.ApplyAttributesToType( - signature.ParameterTypes[i], module.Compilation, null, metadata, module.TypeSystemOptions); + signature.ParameterTypes[i], module.Compilation, null, metadata, module.TypeSystemOptions, nullableContext); parameters[i] = new DefaultParameter(parameterType, name: string.Empty, owner, isRef: parameterType.Kind == TypeKind.ByReference); i++; } parameterType = ApplyAttributeTypeVisitor.ApplyAttributesToType( signature.ParameterTypes[i], module.Compilation, - par.GetCustomAttributes(), metadata, module.TypeSystemOptions); + par.GetCustomAttributes(), metadata, module.TypeSystemOptions, nullableContext); parameters[i] = new MetadataParameter(module, owner, parameterType, parameterHandle); i++; } @@ -202,7 +210,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } while (i < signature.RequiredParameterCount) { parameterType = ApplyAttributeTypeVisitor.ApplyAttributesToType( - signature.ParameterTypes[i], module.Compilation, null, metadata, module.TypeSystemOptions); + signature.ParameterTypes[i], module.Compilation, null, metadata, module.TypeSystemOptions, nullableContext); parameters[i] = new DefaultParameter(parameterType, name: string.Empty, owner, isRef: parameterType.Kind == TypeKind.ByReference); i++; @@ -213,7 +221,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } Debug.Assert(i == parameters.Length); var returnType = ApplyAttributeTypeVisitor.ApplyAttributesToType(signature.ReturnType, - module.Compilation, returnTypeAttributes, metadata, module.TypeSystemOptions); + module.Compilation, returnTypeAttributes, metadata, module.TypeSystemOptions, nullableContext); return (returnType, parameters); } #endregion diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs index 65eaeda55..264bb5e02 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataProperty.cs @@ -123,13 +123,22 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation var signature = propertyDef.DecodeSignature(module.TypeProvider, genericContext); var accessors = propertyDef.GetAccessors(); ParameterHandleCollection? parameterHandles; - if (!accessors.Getter.IsNil) - parameterHandles = module.metadata.GetMethodDefinition(accessors.Getter).GetParameters(); - else if (!accessors.Setter.IsNil) - parameterHandles = module.metadata.GetMethodDefinition(accessors.Setter).GetParameters(); - else + Nullability nullableContext; + if (!accessors.Getter.IsNil) { + var getter = module.metadata.GetMethodDefinition(accessors.Getter); + parameterHandles = getter.GetParameters(); + nullableContext = getter.GetCustomAttributes().GetNullableContext(module.metadata) + ?? DeclaringTypeDefinition?.NullableContext ?? Nullability.Oblivious; + } else if (!accessors.Setter.IsNil) { + var setter = module.metadata.GetMethodDefinition(accessors.Setter); + parameterHandles = setter.GetParameters(); + nullableContext = setter.GetCustomAttributes().GetNullableContext(module.metadata) + ?? DeclaringTypeDefinition?.NullableContext ?? Nullability.Oblivious; + } else { parameterHandles = null; - (returnType, parameters) = MetadataMethod.DecodeSignature(module, this, signature, parameterHandles); + nullableContext = DeclaringTypeDefinition?.NullableContext ?? Nullability.Oblivious; + } + (returnType, parameters) = MetadataMethod.DecodeSignature(module, this, signature, parameterHandles, nullableContext); } catch (BadImageFormatException) { returnType = SpecialType.UnknownType; parameters = Empty.Array; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs index 91ad0be3b..45fbaf158 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs @@ -51,6 +51,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public KnownTypeCode KnownTypeCode { get; } public IType EnumUnderlyingType { get; } public bool HasExtensionMethods { get; } + public Nullability NullableContext { get; } // lazy-loaded: IMember[] members; @@ -77,10 +78,14 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation // Create type parameters: this.TypeParameters = MetadataTypeParameter.Create(module, this.DeclaringTypeDefinition, this, td.GetGenericParameters()); + + this.NullableContext = td.GetCustomAttributes().GetNullableContext(metadata) ?? this.DeclaringTypeDefinition.NullableContext; } else { // Create type parameters: this.TypeParameters = MetadataTypeParameter.Create(module, this, td.GetGenericParameters()); + this.NullableContext = td.GetCustomAttributes().GetNullableContext(metadata) ?? module.NullableContext; + var topLevelTypeName = fullTypeName.TopLevelTypeName; for (int i = 0; i < KnownTypeReference.KnownTypeCodeCount; i++) { var ktr = KnownTypeReference.Get((KnownTypeCode)i); @@ -301,7 +306,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } foreach (var h in interfaceImplCollection) { var iface = metadata.GetInterfaceImplementation(h); - baseTypes.Add(module.ResolveType(iface.Interface, context, iface.GetCustomAttributes())); + baseTypes.Add(module.ResolveType(iface.Interface, context, iface.GetCustomAttributes(), Nullability.Oblivious)); } return LazyInit.GetOrSet(ref this.directBaseTypes, baseTypes); } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs index 8c84c0500..862fc4482 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs @@ -177,13 +177,21 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation { var metadata = module.metadata; var gp = metadata.GetGenericParameter(handle); + Nullability nullableContext; + if (Owner is ITypeDefinition typeDef) { + nullableContext = typeDef.NullableContext; + } else if (Owner is MetadataMethod method) { + nullableContext = method.NullableContext; + } else { + nullableContext = Nullability.Oblivious; + } var constraintHandleCollection = gp.GetConstraints(); List result = new List(constraintHandleCollection.Count + 1); bool hasNonInterfaceConstraint = false; foreach (var constraintHandle in constraintHandleCollection) { var constraint = metadata.GetGenericParameterConstraint(constraintHandle); - var ty = module.ResolveType(constraint.Type, new GenericContext(Owner), constraint.GetCustomAttributes()); + var ty = module.ResolveType(constraint.Type, new GenericContext(Owner), constraint.GetCustomAttributes(), nullableContext); result.Add(ty); hasNonInterfaceConstraint |= (ty.Kind != TypeKind.Interface); } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs index face71f47..fbab0db4b 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs @@ -177,6 +177,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation bool IType.IsByRefLike => false; Nullability IType.Nullability => Nullability.Oblivious; + Nullability ITypeDefinition.NullableContext => Nullability.Oblivious; IType IType.ChangeNullability(Nullability nullability) { diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs index cb0b8c67f..fa87dd4ec 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs @@ -19,6 +19,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation // the NullabilityAnnotatedType wrapper only in some limited places. Debug.Assert(type is ITypeDefinition || type.Kind == TypeKind.Dynamic + || type.Kind == TypeKind.Unknown || (type is ITypeParameter && this is ITypeParameter)); this.nullability = nullability; } @@ -50,10 +51,16 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public override IType VisitChildren(TypeVisitor visitor) { IType newBase = baseType.AcceptVisitor(visitor); - if (newBase != baseType) + if (newBase != baseType) { + if (newBase.Nullability == Nullability.Nullable) { + // T?! -> T? + // This happens during type substitution for generic methods. + return newBase; + } return newBase.ChangeNullability(nullability); - else + } else { return this; + } } public override string ToString() @@ -80,6 +87,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation this.baseType = type; } + public ITypeParameter OriginalTypeParameter => baseType; + SymbolKind ITypeParameter.OwnerType => baseType.OwnerType; IEntity ITypeParameter.Owner => baseType.Owner; int ITypeParameter.Index => baseType.Index; diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs index 9929e103b..3ffe94f96 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs @@ -203,7 +203,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation b.Append('['); for (int i = 0; i < this.TypeArguments.Count; i++) { if (i > 0) b.Append(", "); - b.Append(this.TypeArguments[i].ReflectionName); + b.Append(this.TypeArguments[i].ToString()); } b.Append(']'); } else if (this.TypeParameters.Count > 0) { @@ -216,7 +216,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation b.Append(this.Parameters[i].ToString()); } b.Append("):"); - b.Append(this.ReturnType.ReflectionName); + b.Append(this.ReturnType.ToString()); b.Append(']'); return b.ToString(); } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/TypeWithElementType.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/TypeWithElementType.cs index eadea20cd..853d6acc7 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/TypeWithElementType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/TypeWithElementType.cs @@ -46,7 +46,12 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public override string ReflectionName { get { return elementType.ReflectionName + NameSuffix; } } - + + public override string ToString() + { + return elementType.ToString() + NameSuffix; + } + public abstract string NameSuffix { get; } public IType ElementType { diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs index 55c8aca94..83ae0801c 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/UnknownType.cs @@ -94,7 +94,15 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public override bool? IsReferenceType { get { return isReferenceType; } } - + + public override IType ChangeNullability(Nullability nullability) + { + if (nullability == Nullability.Oblivious) + return this; + else + return new NullabilityAnnotatedType(this, nullability); + } + public override int GetHashCode() { return (namespaceKnown ? 812571 : 12651) ^ fullTypeName.GetHashCode(); diff --git a/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs b/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs index ec6b0a9e7..110a4802d 100644 --- a/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs +++ b/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs @@ -40,6 +40,7 @@ namespace ICSharpCode.Decompiler.TypeSystem internal readonly MetadataReader metadata; readonly TypeSystemOptions options; internal readonly TypeProvider TypeProvider; + internal readonly Nullability NullableContext; readonly MetadataNamespace rootNamespace; readonly MetadataTypeDefinition[] typeDefs; @@ -66,6 +67,7 @@ namespace ICSharpCode.Decompiler.TypeSystem this.AssemblyName = metadata.GetString(moddef.Name); this.FullAssemblyName = this.AssemblyName; } + this.NullableContext = metadata.GetModuleDefinition().GetCustomAttributes().GetNullableContext(metadata) ?? Nullability.Oblivious; this.rootNamespace = new MetadataNamespace(this, null, string.Empty, metadata.GetNamespaceDefinitionRoot()); if (!options.HasFlag(TypeSystemOptions.Uncached)) { @@ -267,12 +269,12 @@ namespace ICSharpCode.Decompiler.TypeSystem #endregion #region Resolve Type - public IType ResolveType(EntityHandle typeRefDefSpec, GenericContext context, CustomAttributeHandleCollection? typeAttributes = null) + public IType ResolveType(EntityHandle typeRefDefSpec, GenericContext context, CustomAttributeHandleCollection? typeAttributes = null, Nullability nullableContext = Nullability.Oblivious) { - return ResolveType(typeRefDefSpec, context, options, typeAttributes); + return ResolveType(typeRefDefSpec, context, options, typeAttributes, nullableContext); } - public IType ResolveType(EntityHandle typeRefDefSpec, GenericContext context, TypeSystemOptions customOptions, CustomAttributeHandleCollection? typeAttributes = null) + public IType ResolveType(EntityHandle typeRefDefSpec, GenericContext context, TypeSystemOptions customOptions, CustomAttributeHandleCollection? typeAttributes = null, Nullability nullableContext = Nullability.Oblivious) { if (typeRefDefSpec.IsNil) return SpecialType.UnknownType; @@ -293,7 +295,7 @@ namespace ICSharpCode.Decompiler.TypeSystem default: throw new BadImageFormatException("Not a type handle"); } - ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, typeAttributes, metadata, customOptions); + ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, typeAttributes, metadata, customOptions, nullableContext); return ty; } @@ -303,14 +305,14 @@ namespace ICSharpCode.Decompiler.TypeSystem var ty = ResolveType(declaringTypeReference, context, options & ~(TypeSystemOptions.Dynamic | TypeSystemOptions.Tuple | TypeSystemOptions.NullabilityAnnotations)); // but substitute tuple types in type arguments: - ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, null, metadata, options, typeChildrenOnly: true); + ty = ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, null, metadata, options, Nullability.Oblivious, typeChildrenOnly: true); return ty; } IType IntroduceTupleTypes(IType ty) { // run ApplyAttributeTypeVisitor without attributes, in order to introduce tuple types - return ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, null, metadata, options); + return ApplyAttributeTypeVisitor.ApplyAttributesToType(ty, Compilation, null, metadata, options, Nullability.Oblivious); } #endregion diff --git a/ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs b/ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs index 203e1e971..291eeac47 100644 --- a/ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs +++ b/ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs @@ -21,6 +21,16 @@ namespace ICSharpCode.Decompiler.TypeSystem RemoveNullability = true, }; + internal static readonly NormalizeTypeVisitor RemoveModifiersAndNullability = new NormalizeTypeVisitor { + ReplaceClassTypeParametersWithDummy = false, + ReplaceMethodTypeParametersWithDummy = false, + DynamicAndObject = false, + TupleToUnderlyingType = false, + RemoveModOpt = true, + RemoveModReq = true, + RemoveNullability = true, + }; + public bool EquivalentTypes(IType a, IType b) { a = a.AcceptVisitor(this); From 7223806967ddef3b61e0db24cd8a3618e39e57a6 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Thu, 25 Jul 2019 00:31:54 +0200 Subject: [PATCH 093/221] Disable resolver log again. --- ICSharpCode.Decompiler/CSharp/Resolver/Log.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/Log.cs b/ICSharpCode.Decompiler/CSharp/Resolver/Log.cs index 8b922253f..e8884a0e0 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/Log.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/Log.cs @@ -29,7 +29,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver /// static class Log { - const bool logEnabled = true; + const bool logEnabled = false; #if __MonoCS__ [Conditional("MCS_DEBUG")] #else From 3d60c7bd70ea7e7fed7556c98c56fbe6c868ce32 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 25 Jul 2019 09:03:19 +0200 Subject: [PATCH 094/221] Fix #1590: Cast from override method to Delegate is not properly simplified --- .../TestCases/Pretty/DelegateConstruction.cs | 53 ++++++++ .../TestCases/Pretty/ExpressionTrees.cs | 2 +- ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 116 +++++++++++++----- 3 files changed, 138 insertions(+), 33 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs index 3fd129fa1..faa0a7d5c 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs @@ -146,6 +146,59 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } + + public interface IM3 + { + void M3(); + } + public class BaseClass : IM3 + { + protected virtual void M1() + { + } + protected virtual void M2() + { + } + public virtual void M3() + { + } + } + public class SubClass : BaseClass + { + protected override void M2() + { + } + public new void M3() + { + } + + public void Test() + { + Noop("M1.base", base.M1); + Noop("M1", M1); + Noop("M2.base", base.M2); + Noop("M2", M2); + Noop("M3.base", base.M3); + Noop("M3.base_virt", ((BaseClass)this).M3); + Noop("M3.base_interface", ((IM3)this).M3); +#if CS70 + Noop("M3", this.M3); + Noop("M3", M3); + + void M3() + { + + } +#else + Noop("M3", M3); +#endif + } + + private void Noop(string name, Action _) + { + } + } + public static Func test0 = (string a, string b) => string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b); public static Func test1 = (string a, string b) => string.IsNullOrEmpty(a) || !string.IsNullOrEmpty(b); public static Func test2 = (string a, string b) => !string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b); diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs index 1e9429978..a6bf33359 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs @@ -585,7 +585,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty 2004, 2008, 2012 - }).Any)); + }).Any)); } public void MethodGroupConstant() diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index b65aeb71b..c40282243 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -1209,12 +1209,15 @@ namespace ICSharpCode.Decompiler.CSharp ILInstruction thisArg = inst.Arguments[0]; ILInstruction func = inst.Arguments[1]; IMethod method; + ExpectedTargetDetails expectedTargetDetails = default; switch (func.OpCode) { case OpCode.LdFtn: method = ((LdFtn)func).Method; + expectedTargetDetails.CallOpCode = OpCode.Call; break; case OpCode.LdVirtFtn: method = ((LdVirtFtn)func).Method; + expectedTargetDetails.CallOpCode = OpCode.CallVirt; break; default: throw new ArgumentException($"Unknown instruction type: {func.OpCode}"); @@ -1223,26 +1226,43 @@ namespace ICSharpCode.Decompiler.CSharp TranslatedExpression target; IType targetType; bool requireTarget; - var expectedTargetDetails = new ExpectedTargetDetails { - CallOpCode = inst.OpCode - }; ResolveResult result = null; string methodName = method.Name; + // There are three possible adjustments, we can make, to solve conflicts: + // 1. add target (represented as bit 0) + // 2. add type arguments (represented as bit 1) + // 3. cast target (represented as bit 2) + int step; if (method.IsLocalFunction) { + step = 0; requireTarget = false; var localFunction = expressionBuilder.ResolveLocalFunction(method); result = ToMethodGroup(method, localFunction); target = default; targetType = default; methodName = localFunction.Name; + // TODO : think about how to handle generic local functions } else if (method.IsExtensionMethod && invokeMethod != null && method.Parameters.Count - 1 == invokeMethod.Parameters.Count) { + step = 5; targetType = method.Parameters[0].Type; if (targetType.Kind == TypeKind.ByReference && thisArg is Box thisArgBox) { targetType = ((ByReferenceType)targetType).ElementType; thisArg = thisArgBox.Argument; } target = expressionBuilder.Translate(thisArg, targetType); + // TODO : check if cast is necessary + target = target.ConvertTo(targetType, expressionBuilder); requireTarget = true; + result = new MethodGroupResolveResult( + target.ResolveResult, method.Name, + new MethodListWithDeclaringType[] { + new MethodListWithDeclaringType( + null, + new[] { method } + ) + }, + method.TypeArguments + ); } else { targetType = method.DeclaringType; if (targetType.IsReferenceType == false && thisArg is Box thisArgBox) { @@ -1260,43 +1280,48 @@ namespace ICSharpCode.Decompiler.CSharp memberDeclaringType: method.DeclaringType); requireTarget = expressionBuilder.HidesVariableWithName(method.Name) || (method.IsStatic ? !expressionBuilder.IsCurrentOrContainingType(method.DeclaringTypeDefinition) : !(target.Expression is ThisReferenceExpression)); - } - var or = new OverloadResolution(resolver.Compilation, method.Parameters.SelectReadOnlyArray(p => new TypeResolveResult(p.Type))); - if (!method.IsLocalFunction && !requireTarget) { - result = resolver.ResolveSimpleName(method.Name, method.TypeArguments, isInvocationTarget: false); - if (result is MethodGroupResolveResult mgrr) { - or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray()); - requireTarget = (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(expectedTargetDetails, method, or.BestCandidate)); - } else { - requireTarget = true; - } - } - MemberLookup lookup = null; - bool needsCast = false; - if (requireTarget) { - lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule); - var rr = lookup.Lookup(target.ResolveResult, method.Name, method.TypeArguments, false); - needsCast = true; - result = rr; - if (rr is MethodGroupResolveResult mgrr) { - or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray()); - needsCast = (or.BestCandidateErrors != OverloadResolutionErrors.None || !IsAppropriateCallTarget(expectedTargetDetails, method, or.BestCandidate)); + + var savedTarget = target; + for (step = requireTarget ? 1 : 0; step < 7; step++) { + ResolveResult targetResolveResult; + if (!method.IsLocalFunction && (step & 1) != 0) { + targetResolveResult = savedTarget.ResolveResult; + target = savedTarget; + } else { + targetResolveResult = null; + } + IReadOnlyList typeArguments; + if ((step & 2) != 0) { + typeArguments = method.TypeArguments; + } else { + typeArguments = EmptyList.Instance; + } + if (targetResolveResult != null && targetType != null && (step & 4) != 0) { + target = target.ConvertTo(targetType, expressionBuilder); + targetResolveResult = target.ResolveResult; + } + bool success = IsUnambiguousMethodReference(expectedTargetDetails, method, targetResolveResult, typeArguments, out var newResult); + if (newResult is MethodGroupResolveResult || result == null) + result = newResult; + if (success) + break; } } - if (needsCast) { - Debug.Assert(requireTarget); - target = target.ConvertTo(targetType, expressionBuilder); - result = lookup.Lookup(target.ResolveResult, method.Name, method.TypeArguments, false); - } + requireTarget = !method.IsLocalFunction && (step & 1) != 0; Expression targetExpression; + Debug.Assert(result != null); if (requireTarget) { + Debug.Assert(target.Expression != null); var mre = new MemberReferenceExpression(target, methodName); - mre.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType)); + if ((step & 2) != 0) + mre.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType)); mre.WithRR(result); targetExpression = mre; } else { - var ide = new IdentifierExpression(methodName) - .WithRR(result); + var ide = new IdentifierExpression(methodName); + if ((step & 2) != 0) + ide.TypeArguments.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType)); + ide.WithRR(result); targetExpression = ide; } var oce = new ObjectCreateExpression(expressionBuilder.ConvertType(inst.Method.DeclaringType), targetExpression) @@ -1308,6 +1333,33 @@ namespace ICSharpCode.Decompiler.CSharp return oce; } + bool IsUnambiguousMethodReference(ExpectedTargetDetails expectedTargetDetails, IMethod method, ResolveResult target, IReadOnlyList typeArguments, out ResolveResult result) + { + var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule); + var or = new OverloadResolution(resolver.Compilation, + arguments: method.Parameters.SelectReadOnlyArray(p => new TypeResolveResult(p.Type)), // there are no arguments, use parameter types + argumentNames: null, // argument names are not possible + typeArguments.ToArray(), + conversions: expressionBuilder.resolver.conversions + ); + if (target == null) { + result = resolver.ResolveSimpleName(method.Name, typeArguments, isInvocationTarget: false); + if (!(result is MethodGroupResolveResult mgrr)) + return false; + or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray()); + } else { + result = lookup.Lookup(target, method.Name, typeArguments, isInvocation: false); + if (!(result is MethodGroupResolveResult mgrr)) + return false; + or.AddMethodLists(mgrr.MethodsGroupedByDeclaringType.ToArray()); + } + + var foundMethod = or.GetBestCandidateWithSubstitutedTypeArguments(); + if (!IsAppropriateCallTarget(expectedTargetDetails, method, foundMethod)) + return false; + return result is MethodGroupResolveResult; + } + static MethodGroupResolveResult ToMethodGroup(IMethod method, ILFunction localFunction) { return new MethodGroupResolveResult( From 7e3b36aaa7421bf83eb66446676e3406edbd13d9 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Thu, 25 Jul 2019 16:42:40 +0200 Subject: [PATCH 095/221] #1563: Where possible, replace an explicit interface implementation call with a call to the interface member. --- .../ICSharpCode.Decompiler.Tests.csproj | 2 ++ .../ILPrettyTestRunner.cs | 6 ++++ .../DirectCallToExplicitInterfaceImpl.cs | 14 ++++++++ .../DirectCallToExplicitInterfaceImpl.il | 36 +++++++++++++++++++ ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 10 ++++++ 5 files changed, 68 insertions(+) create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.cs create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.il diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 4ce11c1db..7ee64ffc4 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -66,6 +66,7 @@ + @@ -74,6 +75,7 @@ + diff --git a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs index bfdbf2156..d7c4aea81 100644 --- a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs @@ -100,6 +100,12 @@ namespace ICSharpCode.Decompiler.Tests Run(settings: new DecompilerSettings { RemoveDeadCode = true }); } + [Test] + public void DirectCallToExplicitInterfaceImpl() + { + Run(); + } + [Test] public void CS1xSwitch_Debug() { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.cs new file mode 100644 index 000000000..7e8fc4ee3 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.cs @@ -0,0 +1,14 @@ +using System; + +public sealed class TestClass : IDisposable +{ + void IDisposable.Dispose() + { + } + + public void Test(TestClass other) + { + ((IDisposable)this).Dispose(); + ((IDisposable)other).Dispose(); + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.il b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.il new file mode 100644 index 000000000..2e5183e67 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.il @@ -0,0 +1,36 @@ +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly DirectCallToExplicitInterfaceImpl +{ + .ver 1:0:0:0 +} +.module DirectCallToExplicitInterfaceImpl.exe + +.class public auto ansi sealed TestClass + extends [mscorlib]System.Object + implements [mscorlib]System.IDisposable +{ + // Methods + + .method private final hidebysig newslot virtual + instance void System.IDisposable.Dispose () cil managed + { + .override method instance void [mscorlib]System.IDisposable::Dispose() + ret + } + + .method public hidebysig void Test (class TestClass other) cil managed + { + ldarg.0 + call instance void TestClass::System.IDisposable.Dispose() + + ldarg.1 + call instance void TestClass::System.IDisposable.Dispose() + + ret + } + +} // end of class DirectCallToExplicitInterfaceImpl diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index c40282243..c76b37932 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -172,6 +172,16 @@ namespace ICSharpCode.Decompiler.CSharp IReadOnlyList argumentToParameterMap = null, IType constrainedTo = null) { + if (method.IsExplicitInterfaceImplementation && callOpCode == OpCode.Call) { + // Direct non-virtual call to explicit interface implementation. + // This can't really be represented in C#, but at least in the case where + // the class is sealed, we can equivalently call the interface member instead: + var interfaceMembers = method.ExplicitlyImplementedInterfaceMembers.ToList(); + if (method.DeclaringTypeDefinition?.Kind == TypeKind.Class && method.DeclaringTypeDefinition.IsSealed && interfaceMembers.Count == 1) { + method = (IMethod)interfaceMembers.Single(); + callOpCode = OpCode.CallVirt; + } + } // Used for Call, CallVirt and NewObj var expectedTargetDetails = new ExpectedTargetDetails { CallOpCode = callOpCode From 8cd8a90c229d8469798d23d75bf7ce1b8934d134 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Thu, 25 Jul 2019 17:00:26 +0200 Subject: [PATCH 096/221] Fix #1563: `Unexpected return in MoveNext()` when mixed `using` and more than one `yield break` Roslyn re-uses the same "this.Finally(); return v;" block for both "yield break;" instructions, so the yield break pattern needs to support multiple stores to the helper variable. --- .../TestCases/Pretty/YieldReturn.cs | 21 +++++++++++++++++++ .../IL/ControlFlow/YieldReturnDecompiler.cs | 11 +++++----- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.cs index 3b2818941..0306343d3 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.cs @@ -335,5 +335,26 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty yield return val; } } + + public static IEnumerable MultipleYieldBreakInTryFinally(int i) + { + try { + if (i == 2) { + yield break; + } + + while (i < 40) { + if (i % 2 == 0) { + yield break; + } + i++; + + yield return i; + } + } finally { + Console.WriteLine("finally"); + } + Console.WriteLine("normal exit"); + } } } \ No newline at end of file diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs index 6b86e5d42..0247894ce 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs @@ -810,14 +810,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow break; case Leave leave: if (leave.MatchReturn(out var value)) { + bool validYieldBreak = value.MatchLdcI4(0); if (value.MatchLdLoc(out var v) && (v.Kind == VariableKind.Local || v.Kind == VariableKind.StackSlot) - && v.StoreInstructions.Count == 1 - && v.StoreInstructions[0] is StLoc stloc) { - returnStores.Add(stloc); - value = stloc.Value; + && v.StoreInstructions.All(store => store is StLoc stloc && stloc.Value.MatchLdcI4(0))) + { + validYieldBreak = true; + returnStores.AddRange(v.StoreInstructions.Cast()); } - if (value.MatchLdcI4(0)) { + if (validYieldBreak) { // yield break leave.ReplaceWith(new Leave(newBody).WithILRange(leave)); } else { From abcef43dba0e0196477b1ed3b3aa8ec6448a368e Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Thu, 25 Jul 2019 17:24:48 +0200 Subject: [PATCH 097/221] Fix #1532: Display metadata token in DecompilerException error message --- ICSharpCode.Decompiler/DecompilerException.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/DecompilerException.cs b/ICSharpCode.Decompiler/DecompilerException.cs index 04ef64d50..004da99ff 100644 --- a/ICSharpCode.Decompiler/DecompilerException.cs +++ b/ICSharpCode.Decompiler/DecompilerException.cs @@ -21,6 +21,7 @@ using System.Diagnostics; using System.IO; using System.Reflection; using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Security; @@ -44,7 +45,7 @@ namespace ICSharpCode.Decompiler public PEFile File { get; } public DecompilerException(MetadataModule module, IEntity decompiledEntity, Exception innerException, string message = null) - : base(message ?? "Error decompiling " + decompiledEntity?.FullName, innerException) + : base(message ?? GetDefaultMessage(decompiledEntity), innerException) { this.File = module.PEFile; this.Module = module; @@ -57,6 +58,13 @@ namespace ICSharpCode.Decompiler this.File = file; } + static string GetDefaultMessage(IEntity entity) + { + if (entity == null) + return "Error decompiling"; + return $"Error decompiling @{MetadataTokens.GetToken(entity.MetadataToken):X8} {entity.FullName}"; + } + // This constructor is needed for serialization. protected DecompilerException(SerializationInfo info, StreamingContext context) : base(info, context) { From 42eafb587f7db3af84523c11b397d07e96e8c0af Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Thu, 25 Jul 2019 18:58:20 +0200 Subject: [PATCH 098/221] Fix #684: Improve detection of variable type for stack slots. --- .../ICSharpCode.Decompiler.Tests.csproj | 2 + .../ILPrettyTestRunner.cs | 6 + .../DirectCallToExplicitInterfaceImpl.il | 2 +- .../TestCases/ILPretty/Issue684.cs | 39 +++++ .../TestCases/ILPretty/Issue684.il | 133 ++++++++++++++++++ .../CSharp/ExpressionBuilder.cs | 23 ++- 6 files changed, 203 insertions(+), 2 deletions(-) create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.cs create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.il diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 7ee64ffc4..f87cc0876 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -61,6 +61,8 @@ + + diff --git a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs index d7c4aea81..5d3d575e8 100644 --- a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs @@ -58,6 +58,12 @@ namespace ICSharpCode.Decompiler.Tests Run(); } + [Test] + public void Issue684() + { + Run(); + } + [Test] public void Issue959() { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.il b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.il index 2e5183e67..4a9a59309 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.il +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/DirectCallToExplicitInterfaceImpl.il @@ -33,4 +33,4 @@ ret } -} // end of class DirectCallToExplicitInterfaceImpl +} // end of class TestClass diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.cs new file mode 100644 index 000000000..5ec516778 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.cs @@ -0,0 +1,39 @@ +using System; + +public static class Issue684 +{ + static int Main(string[] A_0) + { + int[] array = new int[1000]; + int num = int.Parse(Console.ReadLine()); + // Point of this test was to ensure the stack slot here uses an appropriate type, + // (bool instead of int). Unfortunately our type fixup runs too late to affect variable names. + bool num2 = num >= 1000; + if (!num2) { + num2 = (num < 2); + } + if (num2) { + Console.WriteLine(-1); + } else { + int i = 2; + for (int num3 = 2; num3 <= num; num3 = i) { + Console.WriteLine(num3); + for (; i <= num; i += num3) { + int num4 = array[i] = 1; + } + i = num3; + while (true) { + bool num5 = i <= num; + if (num5) { + num5 = (array[i] != 0); + } + if (!num5) { + break; + } + i++; + } + } + } + return 0; + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.il b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.il new file mode 100644 index 000000000..55417b20b --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue684.il @@ -0,0 +1,133 @@ +.assembly extern mscorlib +{ + .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. + .ver 4:0:0:0 +} +.assembly Issue684 +{ + .ver 1:0:0:0 +} +.module Issue684.exe + +.class public auto ansi abstract sealed Issue684 + extends [mscorlib]System.Object +{ + // Methods + + .method static privatescope + int32 Main$PST06000001 ( + string[] '' + ) cil managed + { + // Method begins at RVA 0x2050 + // Code size 196 (0xc4) + .maxstack 11 + .entrypoint + .locals init ( + [0] int32, + [1] int32, + [2] int32, + [3] int32[], + [4] int32 + ) + + IL_0000: ldc.i4 1000 + IL_0005: newarr [mscorlib]System.Int32 + IL_000a: stloc.3 + IL_000b: call string [mscorlib]System.Console::ReadLine() + IL_0010: call int32 [mscorlib]System.Int32::Parse(string) + IL_0015: stloc.2 + IL_0016: ldloc.2 + IL_0017: ldc.i4 1000 + IL_001c: clt + IL_001e: ldc.i4.0 + IL_001f: ceq + IL_0021: dup + IL_0022: brtrue IL_0030 + + IL_0027: pop + IL_0028: ldloc.2 + IL_0029: ldc.i4 2 + IL_002e: clt + + IL_0030: brfalse IL_0045 + + IL_0035: ldc.i4 1 + IL_003a: neg + IL_003b: call void [mscorlib]System.Console::WriteLine(int32) + IL_0040: br IL_00c2 + + IL_0045: ldc.i4 2 + IL_004a: stloc.0 + IL_004b: ldc.i4 2 + IL_0050: stloc.1 + // loop start (head: IL_0051) + IL_0051: ldloc.1 + IL_0052: ldloc.2 + IL_0053: cgt + IL_0055: ldc.i4.0 + IL_0056: ceq + IL_0058: brfalse IL_00c2 + + IL_005d: ldloc.1 + IL_005e: call void [mscorlib]System.Console::WriteLine(int32) + // loop start (head: IL_0063) + IL_0063: ldloc.0 + IL_0064: ldloc.2 + IL_0065: cgt + IL_0067: ldc.i4.0 + IL_0068: ceq + IL_006a: brfalse IL_0088 + + IL_006f: ldc.i4 1 + IL_0074: stloc.s 4 + IL_0076: ldloc.3 + IL_0077: ldloc.0 + IL_0078: ldloc.s 4 + IL_007a: stelem.any [mscorlib]System.Int32 + IL_007f: ldloc.0 + IL_0080: ldloc.1 + IL_0081: add + IL_0082: stloc.0 + IL_0083: br IL_0063 + // end loop + + IL_0088: ldloc.1 + IL_0089: stloc.0 + // loop start (head: IL_008a) + IL_008a: ldloc.0 + IL_008b: ldloc.2 + IL_008c: cgt + IL_008e: ldc.i4.0 + IL_008f: ceq + IL_0091: dup + IL_0092: brfalse IL_00a9 + + IL_0097: pop + IL_0098: ldloc.3 + IL_0099: ldloc.0 + IL_009a: ldelem.any [mscorlib]System.Int32 + IL_009f: ldc.i4 0 + IL_00a4: ceq + IL_00a6: ldc.i4.0 + IL_00a7: ceq + + IL_00a9: brfalse IL_00bb + + IL_00ae: ldloc.0 + IL_00af: ldc.i4 1 + IL_00b4: add + IL_00b5: stloc.0 + IL_00b6: br IL_008a + // end loop + + IL_00bb: ldloc.0 + IL_00bc: stloc.1 + IL_00bd: br IL_0051 + // end loop + + IL_00c2: ldc.i4.0 + IL_00c3: ret + } // end of method Program::Main + +} // end of class Issue684 diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index c0bbd2dc6..ede9fa95c 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -613,7 +613,7 @@ namespace ICSharpCode.Decompiler.CSharp if (inst.Variable.Kind == VariableKind.StackSlot && !loadedVariablesSet.Contains(inst.Variable)) { // Stack slots in the ILAst have inaccurate types (e.g. System.Object for StackType.O) // so we should replace them with more accurate types where possible: - if ((inst.Variable.IsSingleDefinition || IsOtherValueType(translatedValue.Type) || inst.Variable.StackType == StackType.Ref) + if (CanUseTypeForStackSlot(inst.Variable, translatedValue.Type) && inst.Variable.StackType == translatedValue.Type.GetStackType() && translatedValue.Type.Kind != TypeKind.Null) { inst.Variable.Type = translatedValue.Type; @@ -633,10 +633,31 @@ namespace ICSharpCode.Decompiler.CSharp return Assignment(lhs, translatedValue).WithILInstruction(inst); } + bool CanUseTypeForStackSlot(ILVariable v, IType type) + { + return v.IsSingleDefinition + || IsOtherValueType(type) + || v.StackType == StackType.Ref + || AllStoresUseConsistentType(v.StoreInstructions, type); + } + bool IsOtherValueType(IType type) { return type.IsReferenceType == false && type.GetStackType() == StackType.O; } + + bool AllStoresUseConsistentType(IReadOnlyList storeInstructions, IType expectedType) + { + expectedType = expectedType.AcceptVisitor(NormalizeTypeVisitor.TypeErasure); + foreach (var store in storeInstructions) { + if (!(store is StLoc stloc)) + return false; + IType type = stloc.Value.InferType(compilation).AcceptVisitor(NormalizeTypeVisitor.TypeErasure); + if (!type.Equals(expectedType)) + return false; + } + return true; + } } protected internal override TranslatedExpression VisitComp(Comp inst, TranslationContext context) From 860f9db9506112f223b02ce8e1e48f2ecef60b88 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 25 Jul 2019 19:51:12 +0200 Subject: [PATCH 099/221] Extend type inference to allow ref return types. --- ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs b/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs index 1dd855800..05968e07f 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs @@ -640,7 +640,13 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver MakeLowerBoundInference(NullableType.GetUnderlyingType(U), NullableType.GetUnderlyingType(V)); return; } - + // Handle by reference types: + ByReferenceType brU = U as ByReferenceType; + ByReferenceType brV = V as ByReferenceType; + if (brU != null && brV != null) { + MakeExactInference(brU.ElementType, brV.ElementType); + return; + } // Handle array types: ArrayType arrU = U as ArrayType; ArrayType arrV = V as ArrayType; From c366235246d8157e005ce694ff80f03eb519bd96 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Thu, 25 Jul 2019 22:47:14 +0200 Subject: [PATCH 100/221] #1349: Fix type substitution for members of unknown generic types (due to unresolved references) Also, assume that unknown inputs to ldfld are temporaries, not unmanaged pointers. This avoids emitting weird pointer casts when accessing fields on unresolved value types. --- ICSharpCode.Decompiler/IL/ILReader.cs | 6 +++--- ICSharpCode.Decompiler/IL/Instructions/LdFlda.cs | 4 +++- ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs | 8 +++++++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 552f2468d..49ce05219 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -1187,14 +1187,14 @@ namespace ICSharpCode.Decompiler.IL return Pop(StackType.O); case false: // field of value type: ldfld can handle temporaries - if (PeekStackType() == StackType.O) + if (PeekStackType() == StackType.O || PeekStackType() == StackType.Unknown) return new AddressOf(Pop()); else return PopPointer(); default: // field in unresolved type - if (PeekStackType() == StackType.O) - return Pop(StackType.O); + if (PeekStackType() == StackType.O || PeekStackType() == StackType.Unknown) + return Pop(); else return PopPointer(); } diff --git a/ICSharpCode.Decompiler/IL/Instructions/LdFlda.cs b/ICSharpCode.Decompiler/IL/Instructions/LdFlda.cs index 87917b8d4..6fdd3e382 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/LdFlda.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/LdFlda.cs @@ -36,7 +36,9 @@ namespace ICSharpCode.Decompiler.IL break; case null: // field of unresolved type - Debug.Assert(target.ResultType == StackType.O || target.ResultType == StackType.I || target.ResultType == StackType.Ref); + Debug.Assert(target.ResultType == StackType.O || target.ResultType == StackType.I + || target.ResultType == StackType.Ref || target.ResultType == StackType.Unknown, + "Field of unresolved type with invalid target"); break; } } diff --git a/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs b/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs index 110a4802d..1fb75d51d 100644 --- a/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs +++ b/ICSharpCode.Decompiler/TypeSystem/MetadataModule.cs @@ -477,7 +477,9 @@ namespace ICSharpCode.Decompiler.TypeSystem typeParameters.Add(new DefaultTypeParameter(m, i)); } m.TypeParameters = typeParameters; - substitution = new TypeParameterSubstitution(null, typeParameters); + substitution = new TypeParameterSubstitution(declaringType.TypeArguments, typeParameters); + } else if (declaringType.TypeArguments.Count > 0) { + substitution = declaringType.GetSubstitution(); } var parameters = new List(); for (int i = 0; i < signature.RequiredParameterCount; i++) { @@ -553,6 +555,10 @@ namespace ICSharpCode.Decompiler.TypeSystem var field = declaringType.GetFields(f => f.Name == name && CompareTypes(f.ReturnType, signature), GetMemberOptions.IgnoreInheritedMembers).FirstOrDefault(); if (field == null) { + // If it's a field in a generic type, we need to substitute the type arguments: + if (declaringType.TypeArguments.Count > 0) { + signature = signature.AcceptVisitor(declaringType.GetSubstitution()); + } field = new FakeField(Compilation) { ReturnType = signature, Name = name, From 9c1bf7fbdfada39d2ae2bdf1b0d3bd5d4a56ca7f Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 25 Jul 2019 23:23:44 +0200 Subject: [PATCH 101/221] Introduce ReferenceKind in IParameter and ByReferenceResolveResult. --- .../TypeSystem/TypeSystemLoaderTests.cs | 45 ++++++------------- ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 4 +- .../CSharp/ExpressionBuilder.cs | 32 ++++++------- .../CSharp/Resolver/CSharpConversions.cs | 16 +++---- .../CSharp/Resolver/CSharpResolver.cs | 2 +- .../CSharp/Resolver/OverloadResolution.cs | 4 +- .../CSharp/Resolver/TypeInference.cs | 4 +- .../Transforms/IntroduceExtensionMethods.cs | 4 +- .../CSharp/TranslatedExpression.cs | 4 +- .../Semantics/ByReferenceResolveResult.cs | 20 +++++---- .../Semantics/LocalResolveResult.cs | 2 +- .../TypeSystem/IParameter.cs | 16 +++++++ .../Implementation/DefaultParameter.cs | 40 +++++++---------- .../Implementation/MetadataMethod.cs | 4 +- .../Implementation/MetadataParameter.cs | 15 ++++--- .../Implementation/SpecializedParameter.cs | 1 + .../TypeSystem/ParameterListComparer.cs | 6 +-- 17 files changed, 103 insertions(+), 116 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs b/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs index dc7d6bad1..273cf2b38 100644 --- a/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs +++ b/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemLoaderTests.cs @@ -707,8 +707,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem public void InOutParametersOnRefMethod() { IParameter p = GetTypeDefinition(typeof(NonCustomAttributes)).Methods.Single(m => m.Name == "DllMethod").Parameters.Single(); - Assert.IsTrue(p.IsRef); - Assert.IsFalse(p.IsOut); + Assert.AreEqual(ReferenceKind.Ref, p.ReferenceKind); var attr = p.GetAttributes().ToList(); Assert.AreEqual(2, attr.Count); Assert.AreEqual("System.Runtime.InteropServices.InAttribute", attr[0].AttributeType.FullName); @@ -728,9 +727,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOutParameter").Parameters.Single(); Assert.IsFalse(p.IsOptional); - Assert.IsFalse(p.IsRef); - Assert.IsTrue(p.IsOut); - Assert.IsFalse(p.IsIn); + Assert.AreEqual(ReferenceKind.Out, p.ReferenceKind); Assert.AreEqual(0, p.GetAttributes().Count()); Assert.IsTrue(p.Type.Kind == TypeKind.ByReference); } @@ -740,9 +737,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithRefParameter").Parameters.Single(); Assert.IsFalse(p.IsOptional); - Assert.IsTrue(p.IsRef); - Assert.IsFalse(p.IsOut); - Assert.IsFalse(p.IsIn); + Assert.AreEqual(ReferenceKind.Ref, p.ReferenceKind); Assert.AreEqual(0, p.GetAttributes().Count()); Assert.IsTrue(p.Type.Kind == TypeKind.ByReference); } @@ -752,9 +747,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithInParameter").Parameters.Single(); Assert.IsFalse(p.IsOptional); - Assert.IsFalse(p.IsRef); - Assert.IsFalse(p.IsOut); - Assert.IsTrue(p.IsIn); + Assert.AreEqual(ReferenceKind.In, p.ReferenceKind); Assert.AreEqual(0, p.GetAttributes().Count()); Assert.IsTrue(p.Type.Kind == TypeKind.ByReference); } @@ -764,8 +757,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithParamsArray").Parameters.Single(); Assert.IsFalse(p.IsOptional); - Assert.IsFalse(p.IsRef); - Assert.IsFalse(p.IsOut); + Assert.AreEqual(ReferenceKind.None, p.ReferenceKind); Assert.IsTrue(p.IsParams); Assert.AreEqual(0, p.GetAttributes().Count()); Assert.IsTrue(p.Type.Kind == TypeKind.Array); @@ -776,8 +768,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOptionalParameter").Parameters.Single(); Assert.IsTrue(p.IsOptional); - Assert.IsFalse(p.IsRef); - Assert.IsFalse(p.IsOut); + Assert.AreEqual(ReferenceKind.None, p.ReferenceKind); Assert.IsFalse(p.IsParams); Assert.IsTrue(p.HasConstantValueInSignature); Assert.AreEqual(0, p.GetAttributes().Count()); @@ -789,8 +780,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithExplicitOptionalParameter").Parameters.Single(); Assert.IsTrue(p.IsOptional); - Assert.IsFalse(p.IsRef); - Assert.IsFalse(p.IsOut); + Assert.AreEqual(ReferenceKind.None, p.ReferenceKind); Assert.IsFalse(p.IsParams); Assert.IsFalse(p.HasConstantValueInSignature); // explicit optional parameter appears in type system if it's read from C#, but not when read from IL @@ -802,8 +792,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithEnumOptionalParameter").Parameters.Single(); Assert.IsTrue(p.IsOptional); - Assert.IsFalse(p.IsRef); - Assert.IsFalse(p.IsOut); + Assert.AreEqual(ReferenceKind.None, p.ReferenceKind); Assert.IsFalse(p.IsParams); Assert.IsTrue(p.HasConstantValueInSignature); Assert.AreEqual(0, p.GetAttributes().Count()); @@ -815,8 +804,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOptionalNullableParameter").Parameters.Single(); Assert.IsTrue(p.IsOptional); - Assert.IsFalse(p.IsRef); - Assert.IsFalse(p.IsOut); + Assert.AreEqual(ReferenceKind.None, p.ReferenceKind); Assert.IsFalse(p.IsParams); Assert.IsTrue(p.HasConstantValueInSignature); Assert.AreEqual(0, p.GetAttributes().Count()); @@ -828,8 +816,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOptionalLongParameter").Parameters.Single(); Assert.IsTrue(p.IsOptional); - Assert.IsFalse(p.IsRef); - Assert.IsFalse(p.IsOut); + Assert.AreEqual(ReferenceKind.None, p.ReferenceKind); Assert.IsFalse(p.IsParams); Assert.IsTrue(p.HasConstantValueInSignature); Assert.AreEqual(1L, p.GetConstantValue()); @@ -841,8 +828,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOptionalNullableLongParameter").Parameters.Single(); Assert.IsTrue(p.IsOptional); - Assert.IsFalse(p.IsRef); - Assert.IsFalse(p.IsOut); + Assert.AreEqual(ReferenceKind.None, p.ReferenceKind); Assert.IsFalse(p.IsParams); Assert.IsTrue(p.HasConstantValueInSignature); Assert.AreEqual(1L, p.GetConstantValue()); @@ -854,8 +840,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "MethodWithOptionalDecimalParameter").Parameters.Single(); Assert.IsTrue(p.IsOptional); - Assert.IsFalse(p.IsRef); - Assert.IsFalse(p.IsOut); + Assert.AreEqual(ReferenceKind.None, p.ReferenceKind); Assert.IsFalse(p.IsParams); Assert.IsTrue(p.HasConstantValueInSignature); Assert.AreEqual(1M, p.GetConstantValue()); @@ -867,8 +852,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(ParameterTests)).Methods.Single(m => m.Name == "VarArgsMethod").Parameters.Single(); Assert.IsFalse(p.IsOptional); - Assert.IsFalse(p.IsRef); - Assert.IsFalse(p.IsOut); + Assert.AreEqual(ReferenceKind.None, p.ReferenceKind); Assert.IsFalse(p.IsParams); Assert.AreEqual(TypeKind.ArgList, p.Type.Kind); Assert.AreEqual("", p.Name); @@ -879,8 +863,7 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { IParameter p = GetTypeDefinition(typeof(VarArgsCtor)).Methods.Single(m => m.IsConstructor).Parameters.Single(); Assert.IsFalse(p.IsOptional); - Assert.IsFalse(p.IsRef); - Assert.IsFalse(p.IsOut); + Assert.AreEqual(ReferenceKind.None, p.ReferenceKind); Assert.IsFalse(p.IsParams); Assert.AreEqual(TypeKind.ArgList, p.Type.Kind); Assert.AreEqual("", p.Name); diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index c76b37932..020a402b5 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -652,8 +652,8 @@ namespace ICSharpCode.Decompiler.CSharp arg = arg.ConvertTo(parameterType, expressionBuilder, allowImplicitConversion: arg.Type.Kind != TypeKind.Dynamic); - if (parameter.IsOut) { - arg = ExpressionBuilder.ChangeDirectionExpressionToOut(arg); + if (parameter.ReferenceKind != ReferenceKind.None) { + arg = ExpressionBuilder.ChangeDirectionExpressionTo(arg, parameter.ReferenceKind); } arguments.Add(arg); diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index ede9fa95c..4dda771b0 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -181,7 +181,7 @@ namespace ICSharpCode.Decompiler.CSharp expr.WithRR(new ILVariableResolveResult(variable, elementType)); expr = new DirectionExpression(FieldDirection.Ref, expr); - return expr.WithRR(new ByReferenceResolveResult(elementType, isOut: false)); + return expr.WithRR(new ByReferenceResolveResult(elementType, ReferenceKind.Ref)); } else { return expr.WithRR(new ILVariableResolveResult(variable, variable.Type)); } @@ -604,7 +604,7 @@ namespace ICSharpCode.Decompiler.CSharp // because the DirectionExpression might get removed by dereferencing instructions such as LdObj return new DirectionExpression(FieldDirection.Ref, expr.Expression) .WithoutILInstruction() - .WithRR(new ByReferenceResolveResult(expr.ResolveResult, isOut: false)); + .WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref)); } protected internal override TranslatedExpression VisitStLoc(StLoc inst, TranslationContext context) @@ -1084,7 +1084,7 @@ namespace ICSharpCode.Decompiler.CSharp .WithILInstruction(inst) .WithRR(new ResolveResult(elementType)); return new DirectionExpression(FieldDirection.Ref, expr) - .WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.Type, isOut: false)); + .WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.Type, ReferenceKind.Ref)); } return CallUnsafeIntrinsic(name, new[] { left.Expression, Translate(offsetInst).Expression }, brt, inst); } else { @@ -1756,7 +1756,7 @@ namespace ICSharpCode.Decompiler.CSharp if (type.Kind == TypeKind.ByReference) { return new DirectionExpression(FieldDirection.Ref, expr.Expression) .WithoutILInstruction() - .WithRR(new ByReferenceResolveResult(expr.ResolveResult, isOut: false)); + .WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref)); } return expr; } @@ -2107,7 +2107,7 @@ namespace ICSharpCode.Decompiler.CSharp } else { // ldflda producing managed pointer return new DirectionExpression(FieldDirection.Ref, expr) - .WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.ResolveResult, isOut: false)); + .WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.ResolveResult, ReferenceKind.Ref)); } } @@ -2115,7 +2115,7 @@ namespace ICSharpCode.Decompiler.CSharp { var expr = ConvertField(inst.Field).WithILInstruction(inst); return new DirectionExpression(FieldDirection.Ref, expr) - .WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.Type, isOut: false)); + .WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.Type, ReferenceKind.Ref)); } protected internal override TranslatedExpression VisitLdElema(LdElema inst, TranslationContext context) @@ -2130,7 +2130,7 @@ namespace ICSharpCode.Decompiler.CSharp arrayExpr, inst.Indices.Select(i => TranslateArrayIndex(i).Expression) ).WithILInstruction(inst).WithRR(new ResolveResult(arrayType.ElementType)); return new DirectionExpression(FieldDirection.Ref, expr) - .WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.Type, isOut: false)); + .WithoutILInstruction().WithRR(new ByReferenceResolveResult(expr.Type, ReferenceKind.Ref)); } TranslatedExpression TranslateArrayIndex(ILInstruction i) @@ -2188,7 +2188,7 @@ namespace ICSharpCode.Decompiler.CSharp .WithRR(new ConversionResolveResult(inst.Type, arg.ResolveResult, Conversion.UnboxingConversion)); return new DirectionExpression(FieldDirection.Ref, castExpression) .WithILInstruction(inst) - .WithRR(new ByReferenceResolveResult(castExpression.ResolveResult, isOut: false)); + .WithRR(new ByReferenceResolveResult(castExpression.ResolveResult, ReferenceKind.Ref)); } protected internal override TranslatedExpression VisitBox(Box inst, TranslationContext context) @@ -2248,7 +2248,7 @@ namespace ICSharpCode.Decompiler.CSharp Arguments = { Translate(inst.Argument).Expression, new TypeReferenceExpression(ConvertType(inst.Type)) } }.WithRR(new ResolveResult(inst.Type)); return new DirectionExpression(FieldDirection.Ref, expr.WithILInstruction(inst)).WithoutILInstruction() - .WithRR(new ByReferenceResolveResult(inst.Type, false)); + .WithRR(new ByReferenceResolveResult(inst.Type, ReferenceKind.Ref)); } protected internal override TranslatedExpression VisitBlock(Block block, TranslationContext context) @@ -2732,7 +2732,7 @@ namespace ICSharpCode.Decompiler.CSharp new ConditionalExpression(condition.Expression, trueBranch.Expression, falseBranch.Expression) .WithILInstruction(inst) .WithRR(conditionalResolveResult) - ).WithoutILInstruction().WithRR(new ByReferenceResolveResult(conditionalResolveResult, isOut: false)); + ).WithoutILInstruction().WithRR(new ByReferenceResolveResult(conditionalResolveResult, ReferenceKind.Ref)); } else { return new ConditionalExpression(condition.Expression, trueBranch.Expression, falseBranch.Expression) .WithILInstruction(inst) @@ -2752,7 +2752,7 @@ namespace ICSharpCode.Decompiler.CSharp var value = Translate(inst.Value, targetTypeHint); return new DirectionExpression(FieldDirection.Ref, value) .WithILInstruction(inst) - .WithRR(new ByReferenceResolveResult(value.ResolveResult, false)); + .WithRR(new ByReferenceResolveResult(value.ResolveResult, ReferenceKind.Ref)); } protected internal override TranslatedExpression VisitAwait(Await inst, TranslationContext context) @@ -2907,7 +2907,7 @@ namespace ICSharpCode.Decompiler.CSharp translatedExpression = translatedExpression.ConvertTo(typeHint, this); } if (info.HasFlag(CSharpArgumentInfoFlags.IsOut)) { - translatedExpression = ChangeDirectionExpressionToOut(translatedExpression); + translatedExpression = ChangeDirectionExpressionTo(translatedExpression, ReferenceKind.Out); } if (info.HasFlag(CSharpArgumentInfoFlags.NamedArgument) && !string.IsNullOrWhiteSpace(info.Name)) { translatedExpression = new TranslatedExpression(new NamedArgumentExpression(info.Name, translatedExpression.Expression)); @@ -2916,16 +2916,16 @@ namespace ICSharpCode.Decompiler.CSharp return translatedExpression; } - internal static TranslatedExpression ChangeDirectionExpressionToOut(TranslatedExpression input) + internal static TranslatedExpression ChangeDirectionExpressionTo(TranslatedExpression input, ReferenceKind kind) { if (!(input.Expression is DirectionExpression dirExpr && input.ResolveResult is ByReferenceResolveResult brrr)) return input; - dirExpr.FieldDirection = FieldDirection.Out; + dirExpr.FieldDirection = (FieldDirection)kind; dirExpr.RemoveAnnotations(); if (brrr.ElementResult == null) - brrr = new ByReferenceResolveResult(brrr.ElementType, isOut: true); + brrr = new ByReferenceResolveResult(brrr.ElementType, kind); else - brrr = new ByReferenceResolveResult(brrr.ElementResult, isOut: true); + brrr = new ByReferenceResolveResult(brrr.ElementResult, kind); dirExpr.AddAnnotation(brrr); return new TranslatedExpression(dirExpr); } diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs index 021e75397..4747df060 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs @@ -1018,7 +1018,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver if (f.IsImplicitlyTyped) { // If F has an implicitly typed parameter list, D has no ref or out parameters. foreach (IParameter p in d.Parameters) { - if (p.IsOut || p.IsRef) + if (p.ReferenceKind != ReferenceKind.None) return Conversion.None; } } else { @@ -1027,7 +1027,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver for (int i = 0; i < f.Parameters.Count; i++) { IParameter pD = d.Parameters[i]; IParameter pF = f.Parameters[i]; - if (pD.IsRef != pF.IsRef || pD.IsOut != pF.IsOut) + if (pD.ReferenceKind != pF.ReferenceKind) return Conversion.None; if (!IdentityConversion(dParamTypes[i], pF.Type)) return Conversion.None; @@ -1071,9 +1071,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver for (int i = 0; i < args.Length; i++) { IParameter param = invoke.Parameters[i]; IType parameterType = param.Type; - if ((param.IsRef || param.IsOut) && parameterType.Kind == TypeKind.ByReference) { + if (param.ReferenceKind != ReferenceKind.None && parameterType.Kind == TypeKind.ByReference) { parameterType = ((ByReferenceType)parameterType).ElementType; - args[i] = new ByReferenceResolveResult(parameterType, param.IsOut); + args[i] = new ByReferenceResolveResult(parameterType, param.ReferenceKind); } else { args[i] = new ResolveResult(parameterType); } @@ -1132,11 +1132,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver for (int i = 0; i < invoke.Parameters.Count; i++) { var pm = m.Parameters[firstParameterInM + i]; var pd = invoke.Parameters[i]; - // ret/out must match - if (pm.IsRef != pd.IsRef || pm.IsOut != pd.IsOut) + // ret/out/in must match + if (pm.ReferenceKind != pd.ReferenceKind) return false; - if (pm.IsRef || pm.IsOut) { - // ref/out parameters must have same types + if (pm.ReferenceKind != ReferenceKind.None) { + // ref/out/in parameters must have same types if (!pm.Type.Equals(pd.Type)) return false; } else { diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs index d26e8d857..0ef09c1bb 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs @@ -2084,7 +2084,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver // create the parameter: ByReferenceResolveResult brrr = arguments[i] as ByReferenceResolveResult; if (brrr != null) { - list.Add(new DefaultParameter(arguments[i].Type, argumentNames[i], isRef: brrr.IsRef, isOut: brrr.IsOut)); + list.Add(new DefaultParameter(arguments[i].Type, argumentNames[i], referenceKind: brrr.ReferenceKind)); } else { // argument might be a lambda or delegate type, so we have to try to guess the delegate type IType type = arguments[i].Type; diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs b/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs index c2af071b9..ef1a5e3d4 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/OverloadResolution.cs @@ -589,10 +589,10 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver ByReferenceResolveResult brrr = arguments[i] as ByReferenceResolveResult; if (brrr != null) { - if ((brrr.IsOut && !candidate.Parameters[parameterIndex].IsOut) || (brrr.IsRef && !candidate.Parameters[parameterIndex].IsRef)) + if (brrr.ReferenceKind != candidate.Parameters[parameterIndex].ReferenceKind) candidate.AddError(OverloadResolutionErrors.ParameterPassingModeMismatch); } else { - if (candidate.Parameters[parameterIndex].IsOut || candidate.Parameters[parameterIndex].IsRef) + if (candidate.Parameters[parameterIndex].ReferenceKind != ReferenceKind.None) candidate.AddError(OverloadResolutionErrors.ParameterPassingModeMismatch); } IType parameterType = candidate.ParameterTypes[parameterIndex]; diff --git a/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs b/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs index 7d2bc973d..40a804025 100644 --- a/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs +++ b/ICSharpCode.Decompiler/CSharp/Resolver/TypeInference.cs @@ -513,9 +513,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver for (int i = 0; i < args.Length; i++) { IParameter param = m.Parameters[i]; IType parameterType = param.Type.AcceptVisitor(substitution); - if ((param.IsRef || param.IsOut) && parameterType.Kind == TypeKind.ByReference) { + if ((param.ReferenceKind != ReferenceKind.None) && parameterType.Kind == TypeKind.ByReference) { parameterType = ((ByReferenceType)parameterType).ElementType; - args[i] = new ByReferenceResolveResult(parameterType, param.IsOut); + args[i] = new ByReferenceResolveResult(parameterType, param.ReferenceKind); } else { args[i] = new ResolveResult(parameterType); } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs index 7c64e687d..b376815db 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs @@ -138,9 +138,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (!CanTransformToExtensionMethodCall(resolver, method, typeArguments, target, args, argNames)) return; if (firstArgument is DirectionExpression dirExpr) { - if (dirExpr.FieldDirection != FieldDirection.Ref) - return; - if (!context.Settings.RefExtensionMethods) + if (!context.Settings.RefExtensionMethods || dirExpr.FieldDirection == FieldDirection.Out) return; firstArgument = dirExpr.Expression; target = firstArgument.GetResolveResult(); diff --git a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs index 62c801cd4..9b22d9eb5 100644 --- a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs @@ -376,7 +376,7 @@ namespace ICSharpCode.Decompiler.CSharp var convertedTemp = this.UnwrapChild(thisDir.Expression).ConvertTo(elementType, expressionBuilder, checkForOverflow); return new DirectionExpression(FieldDirection.Ref, convertedTemp) .WithILInstruction(this.ILInstructions) - .WithRR(new ByReferenceResolveResult(convertedTemp.ResolveResult, false)); + .WithRR(new ByReferenceResolveResult(convertedTemp.ResolveResult, ReferenceKind.Ref)); } // Convert from integer/pointer to reference. // First, convert to the corresponding pointer type: @@ -396,7 +396,7 @@ namespace ICSharpCode.Decompiler.CSharp // And then take a reference: return new DirectionExpression(FieldDirection.Ref, expr) .WithoutILInstruction() - .WithRR(new ByReferenceResolveResult(elementRR, false)); + .WithRR(new ByReferenceResolveResult(elementRR, ReferenceKind.Ref)); } var rr = expressionBuilder.resolver.WithCheckForOverflow(checkForOverflow).ResolveCast(targetType, ResolveResult); if (rr.IsCompileTimeConstant && !rr.IsError) { diff --git a/ICSharpCode.Decompiler/Semantics/ByReferenceResolveResult.cs b/ICSharpCode.Decompiler/Semantics/ByReferenceResolveResult.cs index b5f927827..bbb525ca3 100644 --- a/ICSharpCode.Decompiler/Semantics/ByReferenceResolveResult.cs +++ b/ICSharpCode.Decompiler/Semantics/ByReferenceResolveResult.cs @@ -24,25 +24,27 @@ using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.Semantics { /// - /// Represents the resolve result of an 'ref x' or 'out x' expression. + /// Represents the resolve result of an 'ref x', 'in x' or 'out x' expression. /// public class ByReferenceResolveResult : ResolveResult { - public bool IsOut { get; private set; } - public bool IsRef { get { return !IsOut;} } - + public ReferenceKind ReferenceKind { get; } + public bool IsOut => ReferenceKind == ReferenceKind.Out; + public bool IsRef => ReferenceKind == ReferenceKind.Ref; + public bool IsIn => ReferenceKind == ReferenceKind.In; + public readonly ResolveResult ElementResult; - public ByReferenceResolveResult(ResolveResult elementResult, bool isOut) - : this(elementResult.Type, isOut) + public ByReferenceResolveResult(ResolveResult elementResult, ReferenceKind kind) + : this(elementResult.Type, kind) { this.ElementResult = elementResult; } - public ByReferenceResolveResult(IType elementType, bool isOut) + public ByReferenceResolveResult(IType elementType, ReferenceKind kind) : base(new ByReferenceType(elementType)) { - this.IsOut = isOut; + this.ReferenceKind = kind; } public IType ElementType { @@ -59,7 +61,7 @@ namespace ICSharpCode.Decompiler.Semantics public override string ToString() { - return string.Format(CultureInfo.InvariantCulture, "[{0} {1} {2}]", GetType().Name, IsOut ? "out" : "ref", ElementType); + return string.Format(CultureInfo.InvariantCulture, "[{0} {1} {2}]", GetType().Name, ReferenceKind.ToString().ToLowerInvariant(), ElementType); } } } diff --git a/ICSharpCode.Decompiler/Semantics/LocalResolveResult.cs b/ICSharpCode.Decompiler/Semantics/LocalResolveResult.cs index 67034a2d6..e1ea775f3 100644 --- a/ICSharpCode.Decompiler/Semantics/LocalResolveResult.cs +++ b/ICSharpCode.Decompiler/Semantics/LocalResolveResult.cs @@ -42,7 +42,7 @@ namespace ICSharpCode.Decompiler.Semantics IType type = variable.Type; if (type.Kind == TypeKind.ByReference) { IParameter p = variable as IParameter; - if (p != null && (p.IsRef || p.IsOut)) + if (p != null && p.ReferenceKind != ReferenceKind.None) return ((ByReferenceType)type).ElementType; } return type; diff --git a/ICSharpCode.Decompiler/TypeSystem/IParameter.cs b/ICSharpCode.Decompiler/TypeSystem/IParameter.cs index 9a871d36b..7801a0e09 100644 --- a/ICSharpCode.Decompiler/TypeSystem/IParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/IParameter.cs @@ -21,12 +21,28 @@ using System.Collections.Generic; namespace ICSharpCode.Decompiler.TypeSystem { + /// + /// Should match order in . + /// + public enum ReferenceKind + { + None, + Out, + Ref, + In + } + public interface IParameter : IVariable { /// /// Gets the attributes on this parameter. /// IEnumerable GetAttributes(); + + /// + /// Gets the reference kind of this parameter. + /// + ReferenceKind ReferenceKind { get; } /// /// Gets whether this parameter is a C# 'ref' parameter. diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultParameter.cs index e3ffc5688..c9b2d2b15 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultParameter.cs @@ -31,7 +31,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation readonly IType type; readonly string name; readonly IReadOnlyList attributes; - readonly bool isRef, isOut, isIn, isParams, isOptional; + readonly ReferenceKind referenceKind; + readonly bool isParams, isOptional; readonly object defaultValue; readonly IParameterizedMember owner; @@ -47,7 +48,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } public DefaultParameter(IType type, string name, IParameterizedMember owner = null, IReadOnlyList attributes = null, - bool isRef = false, bool isOut = false, bool isIn = false, bool isParams = false, bool isOptional = false, object defaultValue = null) + ReferenceKind referenceKind = ReferenceKind.None, bool isParams = false, bool isOptional = false, object defaultValue = null) { if (type == null) throw new ArgumentNullException("type"); @@ -57,9 +58,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation this.name = name; this.owner = owner; this.attributes = attributes ?? EmptyList.Instance; - this.isRef = isRef; - this.isOut = isOut; - this.isIn = isIn; + this.referenceKind = referenceKind; this.isParams = isParams; this.isOptional = isOptional; this.defaultValue = defaultValue; @@ -74,27 +73,16 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } public IEnumerable GetAttributes() => attributes; - - public bool IsRef { - get { return isRef; } - } - - public bool IsOut { - get { return isOut; } - } - public bool IsIn { - get { return isIn; } - } - - public bool IsParams { - get { return isParams; } - } - - public bool IsOptional { - get { return isOptional; } - } - + public ReferenceKind ReferenceKind => referenceKind; + public bool IsRef => referenceKind == ReferenceKind.Ref; + public bool IsOut => referenceKind == ReferenceKind.Out; + public bool IsIn => referenceKind == ReferenceKind.In; + + public bool IsParams => isParams; + + public bool IsOptional => isOptional; + public string Name { get { return name; } } @@ -128,6 +116,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation b.Append("ref "); if (parameter.IsOut) b.Append("out "); + if (parameter.IsIn) + b.Append("in "); if (parameter.IsParams) b.Append("params "); b.Append(parameter.Name); diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs index 2705ef4be..d97382984 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs @@ -197,7 +197,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation parameterType = ApplyAttributeTypeVisitor.ApplyAttributesToType( signature.ParameterTypes[i], module.Compilation, null, metadata, module.TypeSystemOptions, nullableContext); parameters[i] = new DefaultParameter(parameterType, name: string.Empty, owner, - isRef: parameterType.Kind == TypeKind.ByReference); + referenceKind: parameterType.Kind == TypeKind.ByReference ? ReferenceKind.Ref : ReferenceKind.None); i++; } parameterType = ApplyAttributeTypeVisitor.ApplyAttributesToType( @@ -212,7 +212,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation parameterType = ApplyAttributeTypeVisitor.ApplyAttributesToType( signature.ParameterTypes[i], module.Compilation, null, metadata, module.TypeSystemOptions, nullableContext); parameters[i] = new DefaultParameter(parameterType, name: string.Empty, owner, - isRef: parameterType.Kind == TypeKind.ByReference); + referenceKind: parameterType.Kind == TypeKind.ByReference ? ReferenceKind.Ref : ReferenceKind.None); i++; } if (signature.Header.CallingConvention == SignatureCallingConvention.VarArgs) { diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs index 2b31d1f61..562d14ec7 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs @@ -81,25 +81,26 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation const ParameterAttributes inOut = ParameterAttributes.In | ParameterAttributes.Out; - public bool IsRef => DetectRefKind() == CSharp.Syntax.ParameterModifier.Ref; + public ReferenceKind ReferenceKind => DetectRefKind(); + public bool IsRef => DetectRefKind() == ReferenceKind.Ref; public bool IsOut => Type.Kind == TypeKind.ByReference && (attributes & inOut) == ParameterAttributes.Out; - public bool IsIn => DetectRefKind() == CSharp.Syntax.ParameterModifier.In; + public bool IsIn => DetectRefKind() == ReferenceKind.In; public bool IsOptional => (attributes & ParameterAttributes.Optional) != 0; - CSharp.Syntax.ParameterModifier DetectRefKind() + ReferenceKind DetectRefKind() { if (Type.Kind != TypeKind.ByReference) - return CSharp.Syntax.ParameterModifier.None; + return ReferenceKind.None; if ((attributes & inOut) == ParameterAttributes.Out) - return CSharp.Syntax.ParameterModifier.Out; + return ReferenceKind.Out; if ((module.TypeSystemOptions & TypeSystemOptions.ReadOnlyStructsAndParameters) != 0) { var metadata = module.metadata; var parameterDef = metadata.GetParameter(handle); if (parameterDef.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.IsReadOnly)) - return CSharp.Syntax.ParameterModifier.In; + return ReferenceKind.In; } - return CSharp.Syntax.ParameterModifier.Ref; + return ReferenceKind.Ref; } public bool IsParams { diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedParameter.cs index 4beb9d038..2c3fa6a90 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedParameter.cs @@ -36,6 +36,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } IEnumerable IParameter.GetAttributes() => baseParameter.GetAttributes(); + ReferenceKind IParameter.ReferenceKind => baseParameter.ReferenceKind; bool IParameter.IsRef => baseParameter.IsRef; bool IParameter.IsOut => baseParameter.IsOut; bool IParameter.IsIn => baseParameter.IsIn; diff --git a/ICSharpCode.Decompiler/TypeSystem/ParameterListComparer.cs b/ICSharpCode.Decompiler/TypeSystem/ParameterListComparer.cs index 84e99aea2..20be982d6 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ParameterListComparer.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ParameterListComparer.cs @@ -66,11 +66,7 @@ namespace ICSharpCode.Decompiler.TypeSystem return false; if (includeModifiers) { - if (a.IsIn != b.IsIn) - return false; - if (a.IsOut != b.IsOut) - return false; - if (a.IsRef != b.IsRef) + if (a.ReferenceKind != b.ReferenceKind) return false; if (a.IsParams != b.IsParams) return false; From e9a020062df6048d81290062cb5d8669d16318fb Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 25 Jul 2019 23:25:56 +0200 Subject: [PATCH 102/221] Add more RefLocalsAndReturns pretty tests. --- .../CorrectnessTestRunner.cs | 6 - .../ICSharpCode.Decompiler.Tests.csproj | 1 - .../Correctness/RefLocalsAndReturns.cs | 79 ----------- .../TestCases/Pretty/RefLocalsAndReturns.cs | 127 ++++++++++++++++++ 4 files changed, 127 insertions(+), 86 deletions(-) delete mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Correctness/RefLocalsAndReturns.cs diff --git a/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs b/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs index 2239f26aa..6d75516da 100644 --- a/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/CorrectnessTestRunner.cs @@ -196,12 +196,6 @@ namespace ICSharpCode.Decompiler.Tests RunCS(options: options); } - [Test] - public void RefLocalsAndReturns([ValueSource("roslynOnlyOptions")] CompilerOptions options) - { - RunCS(options: options); - } - [Test] public void BitNot([Values(false, true)] bool force32Bit) { diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index f87cc0876..c6d570204 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -88,7 +88,6 @@ - diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/RefLocalsAndReturns.cs b/ICSharpCode.Decompiler.Tests/TestCases/Correctness/RefLocalsAndReturns.cs deleted file mode 100644 index 59f846290..000000000 --- a/ICSharpCode.Decompiler.Tests/TestCases/Correctness/RefLocalsAndReturns.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness -{ - class RefLocalsAndReturns - { - static int[] numbers = { 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023 }; - static string[] strings = { "Hello", "World" }; - static string NullString = ""; - static int DefaultInt = 0; - - public delegate ref TReturn RefFunc(T1 param1); - - public static TReturn Invoker(RefFunc action, T1 value) - { - return action(value); - } - - public static ref int FindNumber(int target) - { - for (int ctr = 0; ctr < numbers.Length; ctr++) { - if (numbers[ctr] >= target) - return ref numbers[ctr]; - } - return ref numbers[0]; - } - - public static ref int LastNumber() - { - return ref numbers[numbers.Length - 1]; - } - - public static ref int ElementAtOrDefault(int index) - { - return ref index < 0 || index >= numbers.Length ? ref DefaultInt : ref numbers[index]; - } - - public static ref int LastOrDefault() - { - return ref numbers.Length > 0 ? ref numbers[numbers.Length - 1] : ref DefaultInt; - } - - public static void DoubleNumber(ref int num) - { - Console.WriteLine("old: " + num); - num *= 2; - Console.WriteLine("new: " + num); - } - - public static ref string GetOrSetString(int index) - { - if (index < 0 || index >= strings.Length) - return ref NullString; - return ref strings[index]; - } - - public static void Main(string[] args) - { - DoubleNumber(ref FindNumber(32)); - Console.WriteLine(string.Join(", ", numbers)); - DoubleNumber(ref LastNumber()); - Console.WriteLine(string.Join(", ", numbers)); - Console.WriteLine(GetOrSetString(0)); - GetOrSetString(0) = "Goodbye"; - Console.WriteLine(string.Join(" ", strings)); - GetOrSetString(5) = "Here I mutated the null value!?"; - Console.WriteLine(GetOrSetString(-5)); - - Console.WriteLine(Invoker(x => ref numbers[x], 0)); - Console.WriteLine(LastOrDefault()); - LastOrDefault() = 10000; - Console.WriteLine(ElementAtOrDefault(-5)); - } - } -} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs index 67bc28561..484ffdebc 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs @@ -2,10 +2,33 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { + internal static class Ext + { + public static void ExtOnRef(this ref RefLocalsAndReturns.NormalStruct s) + { + } + public static void ExtOnIn(this in RefLocalsAndReturns.NormalStruct s) + { + } + public static void ExtOnRef(this ref RefLocalsAndReturns.ReadOnlyStruct s) + { + } + public static void ExtOnIn(this in RefLocalsAndReturns.ReadOnlyStruct s) + { + } + public static void ExtOnRef(this ref RefLocalsAndReturns.ReadOnlyRefStruct s) + { + } + public static void ExtOnIn(this in RefLocalsAndReturns.ReadOnlyRefStruct s) + { + } + } + internal class RefLocalsAndReturns { public delegate ref T RefFunc(); public delegate ref readonly T ReadOnlyRefFunc(); + public delegate ref TReturn RefFunc(T1 param1); public ref struct RefStruct { @@ -35,6 +58,28 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } + private static int[] numbers = new int[10] { + 1, + 3, + 7, + 15, + 31, + 63, + 127, + 255, + 511, + 1023 + }; + + private static string[] strings = new string[2] { + "Hello", + "World" + }; + + private static string NullString = ""; + + private static int DefaultInt = 0; + public static ref T GetRef() { throw new NotImplementedException(); @@ -80,5 +125,87 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty ReadOnlyStruct readOnlyStruct = rs; readOnlyStruct.Method(); } + + public static TReturn Invoker(RefFunc action, T1 value) + { + return action(value); + } + + public static ref int FindNumber(int target) + { + for (int i = 0; i < numbers.Length; i++) { + if (numbers[i] >= target) { + return ref numbers[i]; + } + } + return ref numbers[0]; + } + + public static ref int LastNumber() + { + return ref numbers[numbers.Length - 1]; + } + + public static ref int ElementAtOrDefault(int index) + { + if (index >= 0 && index < numbers.Length) { + return ref numbers[index]; + } + return ref DefaultInt; + } + + public static ref int LastOrDefault() + { + if (numbers.Length != 0) { + return ref numbers[numbers.Length - 1]; + } + return ref DefaultInt; + } + + public static void DoubleNumber(ref int num) + { + Console.WriteLine("old: " + num); + num *= 2; + Console.WriteLine("new: " + num); + } + + public static ref string GetOrSetString(int index) + { + if (index < 0 || index >= strings.Length) { + return ref NullString; + } + + return ref strings[index]; + } + + public void CallSiteTests(NormalStruct s, ReadOnlyStruct r, ReadOnlyRefStruct rr) + { + s.ExtOnIn(); + s.ExtOnRef(); + r.ExtOnIn(); + r.ExtOnRef(); + rr.ExtOnIn(); + rr.ExtOnRef(); + CallOnInParam(in s, in r); + } + + public static void Main(string[] args) + { + DoubleNumber(ref args.Length == 1 ? ref numbers[0] : ref DefaultInt); + DoubleNumber(ref FindNumber(32)); + Console.WriteLine(string.Join(", ", numbers)); + DoubleNumber(ref LastNumber()); + Console.WriteLine(string.Join(", ", numbers)); + Console.WriteLine(GetOrSetString(0)); + GetOrSetString(0) = "Goodbye"; + Console.WriteLine(string.Join(" ", strings)); + GetOrSetString(5) = "Here I mutated the null value!?"; + Console.WriteLine(GetOrSetString(-5)); + + Console.WriteLine(Invoker((int x) => ref numbers[x], 0)); + Console.WriteLine(LastOrDefault()); + LastOrDefault() = 10000; + Console.WriteLine(ElementAtOrDefault(-5)); + } } } From c3037afa7092d79bb8c8be9bf7b7a0cb6f9612aa Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Thu, 25 Jul 2019 23:30:40 +0200 Subject: [PATCH 103/221] Fix #1352: Assertion in NullableLiftingTransform.DoLift --- .../IL/Transforms/NullableLiftingTransform.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs index 0b1f617d7..e1c816028 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs @@ -710,7 +710,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return (newInst, bits); } } else if (inst is Comp comp && !comp.IsLifted && comp.Kind == ComparisonKind.Equality - && MatchGetValueOrDefault(comp.Left, out ILVariable v) + && MatchGetValueOrDefault(comp.Left, out ILVariable v) && nullableVars.Contains(v) && NullableType.GetUnderlyingType(v.Type).IsKnownType(KnownTypeCode.Boolean) && comp.Right.MatchLdcI4(0) ) { From 36afa0857d41ca31f744f3f35e5083b10af77b32 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 26 Jul 2019 01:04:14 +0200 Subject: [PATCH 104/221] Add tests for ref, out and in parameters. --- .../TestCases/Pretty/DynamicTests.cs | 28 +++++++++++++ .../TestCases/Pretty/TupleTests.cs | 40 +++++++++++++++++++ .../OutputVisitor/CSharpOutputVisitor.cs | 5 +++ .../CSharp/TranslatedExpression.cs | 2 +- 4 files changed, 74 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs index f328fc634..c12a38c6d 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs @@ -64,6 +64,34 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { } + private static void CallWithOut(out dynamic d) + { + d = null; + } + +#if CS70 + private static void CallWithIn(in dynamic d) + { + } +#endif + + private static void CallWithRef(ref dynamic d) + { + } + + private static void RefCallSiteTests() + { +#if CS70 + CallWithOut(out dynamic d); + CallWithIn(in d); +#else + dynamic d; + CallWithOut(out d); +#endif + CallWithRef(ref d); + d.SomeCall(); + } + private static void InvokeConstructor() { DynamicTests dynamicTests = new DynamicTests(); diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs index 6fd605ccd..f8fb99962 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs @@ -88,6 +88,36 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public object NotTargetTyping => ((string)null, (object)1, (Action)delegate { }); + public void UnnamedTupleOut(out (int, string, Action, dynamic) tuple) + { + tuple = (42, "Hello", Console.WriteLine, null); + } + + public void UnnamedTupleIn(in (int, string, Action, dynamic) tuple) + { + + } + + 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); + } + + public void NamedTupleIn(in (int A, string B, Action C, dynamic D) tuple) + { + + } + + public void NamedTupleRef(ref (int A, string B, Action C, dynamic D) tuple) + { + + } + public void UseDict() { if (TupleDict.Count > 10) { @@ -140,5 +170,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty (2, "b") }); } + + public void RefCallSites(out (int, string, Action, dynamic) tuple) + { + UnnamedTupleOut(out tuple); + UnnamedTupleIn(in tuple); + UnnamedTupleRef(ref tuple); + NamedTupleOut(out tuple); + NamedTupleIn(in tuple); + NamedTupleRef(ref tuple); + } } } diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs index 26d7f4f3a..da33b215e 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -2194,19 +2194,24 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteAttributes(parameterDeclaration.Attributes); if (parameterDeclaration.HasThisModifier) { WriteKeyword(ParameterDeclaration.ThisModifierRole); + Space(); } switch (parameterDeclaration.ParameterModifier) { case ParameterModifier.Ref: WriteKeyword(ParameterDeclaration.RefModifierRole); + Space(); break; case ParameterModifier.Out: WriteKeyword(ParameterDeclaration.OutModifierRole); + Space(); break; case ParameterModifier.Params: WriteKeyword(ParameterDeclaration.ParamsModifierRole); + Space(); break; case ParameterModifier.In: WriteKeyword(ParameterDeclaration.InModifierRole); + Space(); break; } parameterDeclaration.Type.AcceptVisitor(this); diff --git a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs index 9b22d9eb5..f695f369e 100644 --- a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs @@ -418,7 +418,7 @@ namespace ICSharpCode.Decompiler.CSharp return this; } } else { - if (NormalizeTypeVisitor.RemoveModifiersAndNullability.EquivalentTypes(type, targetType)) { + if (targetType.Kind != TypeKind.Dynamic && type.Kind != TypeKind.Dynamic && NormalizeTypeVisitor.TypeErasure.EquivalentTypes(type, targetType)) { // avoid an explicit cast when types differ only in nullability of reference types return this; } From d99f6c81a55da40361207d0b4ce9ec745e6dd64a Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 26 Jul 2019 01:14:37 +0200 Subject: [PATCH 105/221] Fix #1595: preserve C# type for field and tuple element access --- .../TestCases/Pretty/TupleTests.cs | 20 ++++++++++++++++ .../CSharp/ExpressionBuilder.cs | 24 ++++++++++++------- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs index 6fd605ccd..1555f02db 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs @@ -38,6 +38,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } + public struct GenericStruct + { + public T Field; + public T Property { + get; + set; + } + } + public ValueTuple VT0; public ValueTuple VT1; public ValueTuple VT7EmptyRest; @@ -140,5 +149,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty (2, "b") }); } + + public void DynamicTuple((dynamic A, dynamic B) a) + { + a.A.DynamicCall(); + a.B.Dynamic = 42; + } + + public void GenericStructWithElementNames(GenericStruct<(int A, int B)> s) + { + Console.WriteLine(s.Field.A + s.Property.B); + } } } diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index ede9fa95c..6182e025b 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -248,19 +248,20 @@ namespace ICSharpCode.Decompiler.CSharp bool targetCasted = false; var targetResolveResult = requireTarget ? target.ResolveResult : null; - bool IsUnambiguousAccess() + bool IsUnambiguousAccess(out MemberResolveResult result) { if (targetResolveResult == null) { - var result = resolver.ResolveSimpleName(field.Name, EmptyList.Instance, isInvocationTarget: false) as MemberResolveResult; + result = resolver.ResolveSimpleName(field.Name, EmptyList.Instance, isInvocationTarget: false) as MemberResolveResult; return !(result == null || result.IsError || !result.Member.Equals(field, NormalizeTypeVisitor.TypeErasure)); } else { var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule); - var result = lookup.Lookup(target.ResolveResult, field.Name, EmptyList.Instance, false) as MemberResolveResult; + result = lookup.Lookup(target.ResolveResult, field.Name, EmptyList.Instance, false) as MemberResolveResult; return !(result == null || result.IsError || !result.Member.Equals(field, NormalizeTypeVisitor.TypeErasure)); } } - while (!IsUnambiguousAccess()) { + MemberResolveResult mrr; + while (!IsUnambiguousAccess(out mrr)) { if (!requireTarget) { requireTarget = true; targetResolveResult = target.ResolveResult; @@ -272,13 +273,16 @@ namespace ICSharpCode.Decompiler.CSharp break; } } + if (mrr == null) { + mrr = new MemberResolveResult(target.ResolveResult, field); + } if (requireTarget) { return new MemberReferenceExpression(target, field.Name) - .WithRR(new MemberResolveResult(target.ResolveResult, field)); + .WithRR(mrr); } else { return new IdentifierExpression(field.Name) - .WithRR(new MemberResolveResult(target.ResolveResult, field)); + .WithRR(mrr); } } @@ -2086,14 +2090,16 @@ namespace ICSharpCode.Decompiler.CSharp nonVirtualInvocation: true, memberStatic: false, memberDeclaringType: underlyingTupleType); - if (translatedTarget.Type is TupleType tupleType && tupleType.UnderlyingType.Equals(underlyingTupleType) && position <= tupleType.ElementNames.Length) { + if (translatedTarget.Type is TupleType tupleType && NormalizeTypeVisitor.TypeErasure.EquivalentTypes(tupleType, underlyingTupleType) && position <= tupleType.ElementNames.Length) { string elementName = tupleType.ElementNames[position - 1]; if (elementName == null) { elementName = "Item" + position; } + // tupleType.ElementTypes are more accurate w.r.t. nullability/dynamic than inst.Field.Type + var rr = new MemberResolveResult(translatedTarget.ResolveResult, inst.Field, + returnTypeOverride: tupleType.ElementTypes[position - 1]); expr = new MemberReferenceExpression(translatedTarget, elementName) - .WithRR(new MemberResolveResult(translatedTarget.ResolveResult, inst.Field)) - .WithILInstruction(inst); + .WithRR(rr).WithILInstruction(inst); } else { expr = ConvertField(inst.Field, inst.Target).WithILInstruction(inst); } From 911a92f3f96237f61912f5fa5cc16377a22ee420 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 26 Jul 2019 01:53:48 +0200 Subject: [PATCH 106/221] Basic test for ref reassignment. --- .../TestCases/Pretty/RefLocalsAndReturns.cs | 8 ++++++++ .../CSharp/OutputVisitor/InsertParenthesesVisitor.cs | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs index 484ffdebc..7f7053b18 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs @@ -189,6 +189,14 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty CallOnInParam(in s, in r); } + public void RefReassignment(ref NormalStruct s) + { + ref NormalStruct @ref = ref GetRef(); + RefReassignment(ref @ref); + @ref = ref GetRef(); + RefReassignment(ref @ref.GetHashCode() == 4 ? ref @ref : ref s); + } + public static void Main(string[] args) { DoubleNumber(ref args.Length == 1 ? ref numbers[0] : ref DefaultInt); diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs index 4966b06a1..3af55614f 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs @@ -362,7 +362,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor { // assignment is right-associative ParenthesizeIfRequired(assignmentExpression.Left, Assignment + 1); - if (InsertParenthesesForReadability) { + if (InsertParenthesesForReadability && !(assignmentExpression.Right is DirectionExpression)) { ParenthesizeIfRequired(assignmentExpression.Right, RelationalAndTypeTesting + 1); } else { ParenthesizeIfRequired(assignmentExpression.Right, Assignment); From 8924ea054bf27dfff6ab25f46b18af40d25c22cd Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 26 Jul 2019 11:23:29 +0200 Subject: [PATCH 107/221] BAML decompiler: Fix NRE on unresolved member --- .../Handlers/Records/PropertyTypeReferenceHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ILSpy.BamlDecompiler/Handlers/Records/PropertyTypeReferenceHandler.cs b/ILSpy.BamlDecompiler/Handlers/Records/PropertyTypeReferenceHandler.cs index c9212db25..148ded0e2 100644 --- a/ILSpy.BamlDecompiler/Handlers/Records/PropertyTypeReferenceHandler.cs +++ b/ILSpy.BamlDecompiler/Handlers/Records/PropertyTypeReferenceHandler.cs @@ -40,7 +40,7 @@ namespace ILSpy.BamlDecompiler.Handlers { var elemAttr = ctx.ResolveProperty(record.AttributeId); elem.Xaml = new XElement(elemAttr.ToXName(ctx, null)); - if (attr.ResolvedMember.FullNameIs("System.Windows.Style", "TargetType")) { + if (attr.ResolvedMember?.FullNameIs("System.Windows.Style", "TargetType") == true) { parent.Xaml.Element.AddAnnotation(new TargetTypeAnnotation(type)); } From c51aea360125029b83a3df0bff57a64d245d7f54 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 26 Jul 2019 12:47:05 +0200 Subject: [PATCH 108/221] Fix decompilation of yield return in local functions. --- .../TestCases/Pretty/LocalFunctions.cs | 13 +++++++++++++ .../IL/ControlFlow/YieldReturnDecompiler.cs | 2 +- ICSharpCode.Decompiler/SRMExtensions.cs | 11 +++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs index f66883c7b..e8c8c1201 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs @@ -273,6 +273,19 @@ namespace LocalFunctions } }); } + + public static IEnumerable YieldReturn(int n) + { + return GetNumbers(); + + IEnumerable GetNumbers() + { + for (int i = 0; i < n; i++) { + yield return i; + } + } + } + //public static void LocalFunctionInUsing() //{ // using (MemoryStream memoryStream = new MemoryStream()) { diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs index 0247894ce..c2833e147 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs @@ -338,7 +338,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow public static bool IsCompilerGeneratorEnumerator(TypeDefinitionHandle type, MetadataReader metadata) { TypeDefinition td; - if (type.IsNil || !type.IsCompilerGenerated(metadata) || (td = metadata.GetTypeDefinition(type)).GetDeclaringType().IsNil) + if (type.IsNil || !type.IsCompilerGeneratedOrIsInCompilerGeneratedClass(metadata) || (td = metadata.GetTypeDefinition(type)).GetDeclaringType().IsNil) return false; foreach (var i in td.GetInterfaceImplementations()) { var tr = metadata.GetInterfaceImplementation(i).Interface.GetFullTypeName(metadata); diff --git a/ICSharpCode.Decompiler/SRMExtensions.cs b/ICSharpCode.Decompiler/SRMExtensions.cs index 44fd7af01..39af674cd 100644 --- a/ICSharpCode.Decompiler/SRMExtensions.cs +++ b/ICSharpCode.Decompiler/SRMExtensions.cs @@ -309,6 +309,17 @@ namespace ICSharpCode.Decompiler return false; } + public static bool IsCompilerGeneratedOrIsInCompilerGeneratedClass(this TypeDefinitionHandle handle, MetadataReader metadata) + { + TypeDefinition type = metadata.GetTypeDefinition(handle); + if (type.IsCompilerGenerated(metadata)) + return true; + TypeDefinitionHandle declaringTypeHandle = type.GetDeclaringType(); + if (!declaringTypeHandle.IsNil && declaringTypeHandle.IsCompilerGenerated(metadata)) + return true; + return false; + } + public static bool IsCompilerGenerated(this MethodDefinition method, MetadataReader metadata) { return method.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.CompilerGenerated); From 5fdeb223dde11dee57fac29e31dca37352af573d Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 24 Jul 2019 08:59:33 +0200 Subject: [PATCH 109/221] "Save Code" should only work with ILSpyTreeNodes. --- ILSpy/Commands/SaveCodeContextMenuEntry.cs | 105 +++++++++++++++++++-- ILSpy/MainWindow.xaml.cs | 77 +-------------- 2 files changed, 101 insertions(+), 81 deletions(-) diff --git a/ILSpy/Commands/SaveCodeContextMenuEntry.cs b/ILSpy/Commands/SaveCodeContextMenuEntry.cs index 85e5e301f..8ee945f0d 100644 --- a/ILSpy/Commands/SaveCodeContextMenuEntry.cs +++ b/ILSpy/Commands/SaveCodeContextMenuEntry.cs @@ -16,25 +16,118 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Windows; using System.Windows.Input; using ICSharpCode.ILSpy.Properties; +using ICSharpCode.ILSpy.TreeNodes; +using ICSharpCode.TreeView; +using Microsoft.Win32; namespace ICSharpCode.ILSpy.TextView { [ExportContextMenuEntry(Header = nameof(Resources._SaveCode), Category = nameof(Resources.Save), Icon = "Images/SaveFile.png")] sealed class SaveCodeContextMenuEntry : IContextMenuEntry { - private readonly ICommand saveCommand; + public void Execute(TextViewContext context) + { + Execute(context.SelectedTreeNodes); + } + + public bool IsEnabled(TextViewContext context) => true; + + public bool IsVisible(TextViewContext context) + { + return CanExecute(context.SelectedTreeNodes); + } - public SaveCodeContextMenuEntry() + public static bool CanExecute(IReadOnlyList selectedNodes) { - saveCommand = ApplicationCommands.Save; + if (selectedNodes == null || selectedNodes.Any(n => !(n is ILSpyTreeNode))) + return false; + return selectedNodes.Count == 1 + || (selectedNodes.Count > 1 && (selectedNodes.All(n => n is AssemblyTreeNode) || selectedNodes.All(n => n is IMemberTreeNode))); } - public void Execute(TextViewContext context) => saveCommand.Execute(parameter: null); + public static void Execute(IReadOnlyList selectedNodes) + { + var currentLanguage = MainWindow.Instance.CurrentLanguage; + var textView = MainWindow.Instance.TextView; + if (selectedNodes.Count == 1 && selectedNodes[0] is ILSpyTreeNode singleSelection) { + // if there's only one treenode selected + // we will invoke the custom Save logic + if (singleSelection.Save(textView)) + return; + } else if (selectedNodes.Count > 1 && selectedNodes.All(n => n is AssemblyTreeNode)) { + var initialPath = Path.GetDirectoryName(((AssemblyTreeNode)selectedNodes[0]).LoadedAssembly.FileName); + var selectedPath = SelectSolutionFile(initialPath); - public bool IsEnabled(TextViewContext context) => true; + if (!string.IsNullOrEmpty(selectedPath)) { + var assemblies = selectedNodes.OfType() + .Select(n => n.LoadedAssembly) + .Where(a => !a.HasLoadError).ToArray(); + SolutionWriter.CreateSolution(textView, selectedPath, currentLanguage, assemblies); + } + return; + } - public bool IsVisible(TextViewContext context) => context.TreeView != null && saveCommand.CanExecute(parameter: null); + // Fallback: if nobody was able to handle the request, use default behavior. + // try to save all nodes to disk. + var options = new DecompilationOptions() { FullDecompilation = true }; + textView.SaveToDisk(currentLanguage, selectedNodes.OfType(), options); + } + + /// + /// Shows a File Selection dialog where the user can select the target file for the solution. + /// + /// The initial path to show in the dialog. If not specified, the 'Documents' directory + /// will be used. + /// + /// The full path of the selected target file, or null if the user canceled. + static string SelectSolutionFile(string path) + { + const string SolutionExtension = ".sln"; + const string DefaultSolutionName = "Solution"; + + if (string.IsNullOrWhiteSpace(path)) { + path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); + } + + SaveFileDialog dlg = new SaveFileDialog(); + dlg.InitialDirectory = path; + dlg.FileName = Path.Combine(path, DefaultSolutionName + SolutionExtension); + dlg.Filter = "Visual Studio Solution file|*" + SolutionExtension; + + bool targetInvalid; + do { + if (dlg.ShowDialog() != true) { + return null; + } + + string selectedPath = Path.GetDirectoryName(dlg.FileName); + try { + targetInvalid = Directory.EnumerateFileSystemEntries(selectedPath).Any(); + } catch (Exception e) when (e is IOException || e is UnauthorizedAccessException || e is System.Security.SecurityException) { + MessageBox.Show( + "The directory cannot be accessed. Please ensure it exists and you have sufficient rights to access it.", + "Solution directory not accessible", + MessageBoxButton.OK, MessageBoxImage.Error); + targetInvalid = true; + continue; + } + + if (targetInvalid) { + MessageBox.Show( + "The directory is not empty. Please select an empty directory.", + "Solution directory not empty", + MessageBoxButton.OK, MessageBoxImage.Warning); + } + } while (targetInvalid); + + return dlg.FileName; + } } } diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 1fabedcd1..5732b5673 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -902,85 +902,12 @@ namespace ICSharpCode.ILSpy void SaveCommandCanExecute(object sender, CanExecuteRoutedEventArgs e) { e.Handled = true; - var selectedNodes = SelectedNodes.ToList(); - e.CanExecute = selectedNodes.Count == 1 || (selectedNodes.Count > 1 && selectedNodes.All(n => n is AssemblyTreeNode)); + e.CanExecute = SaveCodeContextMenuEntry.CanExecute(SelectedNodes.ToList()); } void SaveCommandExecuted(object sender, ExecutedRoutedEventArgs e) { - var selectedNodes = SelectedNodes.ToList(); - if (selectedNodes.Count == 1) { - // if there's only one treenode selected - // we will invoke the custom Save logic - if (selectedNodes[0].Save(TextView)) - return; - } else if (selectedNodes.Count > 1 && selectedNodes.All(n => n is AssemblyTreeNode)) { - var initialPath = Path.GetDirectoryName(((AssemblyTreeNode)selectedNodes[0]).LoadedAssembly.FileName); - var selectedPath = SelectSolutionFile(initialPath); - - if (!string.IsNullOrEmpty(selectedPath)) { - var assemblies = selectedNodes.OfType() - .Select(n => n.LoadedAssembly) - .Where(a => !a.HasLoadError).ToArray(); - SolutionWriter.CreateSolution(TextView, selectedPath, CurrentLanguage, assemblies); - } - return; - } - - // Fallback: if nobody was able to handle the request, use default behavior. - // try to save all nodes to disk. - var options = new DecompilationOptions() { FullDecompilation = true }; - TextView.SaveToDisk(CurrentLanguage, selectedNodes, options); - } - - /// - /// Shows a File Selection dialog where the user can select the target file for the solution. - /// - /// The initial path to show in the dialog. If not specified, the 'Documents' directory - /// will be used. - /// - /// The full path of the selected target file, or null if the user canceled. - string SelectSolutionFile(string path) - { - const string SolutionExtension = ".sln"; - const string DefaultSolutionName = "Solution"; - - if (string.IsNullOrWhiteSpace(path)) { - path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); - } - - SaveFileDialog dlg = new SaveFileDialog(); - dlg.InitialDirectory = path; - dlg.FileName = Path.Combine(path, DefaultSolutionName + SolutionExtension); - dlg.Filter = "Visual Studio Solution file|*" + SolutionExtension; - - bool targetInvalid; - do { - if (dlg.ShowDialog() != true) { - return null; - } - - string selectedPath = Path.GetDirectoryName(dlg.FileName); - try { - targetInvalid = Directory.EnumerateFileSystemEntries(selectedPath).Any(); - } catch (Exception e) when (e is IOException || e is UnauthorizedAccessException || e is System.Security.SecurityException) { - MessageBox.Show( - "The directory cannot be accessed. Please ensure it exists and you have sufficient rights to access it.", - "Solution directory not accessible", - MessageBoxButton.OK, MessageBoxImage.Error); - targetInvalid = true; - continue; - } - - if (targetInvalid) { - MessageBox.Show( - "The directory is not empty. Please select an empty directory.", - "Solution directory not empty", - MessageBoxButton.OK, MessageBoxImage.Warning); - } - } while (targetInvalid); - - return dlg.FileName; + SaveCodeContextMenuEntry.Execute(SelectedNodes.ToList()); } public void RefreshDecompiledView() From 8117dfff4a2b238a0e2794416c78b9d3656dbfb8 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 26 Jul 2019 16:42:45 +0200 Subject: [PATCH 110/221] Fix #1242: Discard unreachable code. Unreachable code is not part of the dominator tree, which most of our transforms are based on. In particular, dominance-based loop detection runs into the problem where unreachable code might have jumps into two independent loops. In that case, it's impossible to place the unreachable code in a way that avoids assertions / generating invalid C#. We establish the invariant that all blocks in a BlockContainer must be statically reachable from the entry point (-> every block is part of the dominator tree). This means transforms no longer have to deal with special cases for unreachable code. The "Remove dead and side effect free code" option still has an effect on dead stores, but unreachable code is now always removed (previously this also was dependent on this option). --- .../TestCases/ILPretty/Issue1047.cs | 4 -- .../ControlFlow/ControlFlowSimplification.cs | 3 -- .../IL/ControlFlow/LoopDetection.cs | 32 ++--------- .../IL/ControlFlow/YieldReturnDecompiler.cs | 2 +- ICSharpCode.Decompiler/IL/ILReader.cs | 12 ++++- .../IL/Instructions/BlockContainer.cs | 54 +++++++++++-------- .../IL/Transforms/BlockTransform.cs | 1 - 7 files changed, 48 insertions(+), 60 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1047.cs b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1047.cs index f5d32e786..93f375a02 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1047.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Issue1047.cs @@ -6,12 +6,8 @@ private void ProblemMethod() { - IL_0000: while (!dummy) { } - return; - IL_0014: - goto IL_0000; } } } \ No newline at end of file diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs index d06e00012..4627c73c8 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs @@ -158,9 +158,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } // Remove return blocks that are no longer reachable: container.Blocks.RemoveAll(b => b.IncomingEdgeCount == 0 && b.Instructions.Count == 0); - if (context.Settings.RemoveDeadCode) { - container.SortBlocks(deleteUnreachableBlocks: true); - } } } diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs index a8ec926a9..f06e83ba8 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs @@ -107,7 +107,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow IncludeNestedContainers(loop); // Try to extend the loop to reduce the number of exit points: ExtendLoop(h, loop, out var exitPoint); - IncludeUnreachablePredecessors(loop); // Sort blocks in the loop in reverse post-order to make the output look a bit nicer. // (if the loop doesn't contain nested loops, this is a topological sort) @@ -115,7 +114,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow Debug.Assert(loop[0] == h); foreach (var node in loop) { node.Visited = false; // reset visited flag so that we can find outer loops - Debug.Assert(h.Dominates(node) || !node.IsReachable, "The loop body must be dominated by the loop head"); + Debug.Assert(h.Dominates(node), "The loop body must be dominated by the loop head"); } ConstructLoop(loop, exitPoint); } @@ -150,7 +149,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // (the entry-point itself doesn't have a CFG node, because it's newly created by this transform) for (int i = 1; i < nestedContainer.Blocks.Count; i++) { var node = context.ControlFlowGraph.GetNode(nestedContainer.Blocks[i]); - Debug.Assert(loop[0].Dominates(node) || !node.IsReachable); + Debug.Assert(loop[0].Dominates(node)); if (!node.Visited) { node.Visited = true; loop.Add(node); @@ -602,30 +601,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } #endregion - /// - /// While our normal dominance logic ensures the loop has just a single reachable entry point, - /// it's possible that there are unreachable code blocks that have jumps into the loop. - /// We'll also include those into the loop. - /// - /// Requires and maintains the invariant that a node is marked as visited iff it is contained in the loop. - /// - private void IncludeUnreachablePredecessors(List loop) - { - for (int i = 1; i < loop.Count; i++) { - Debug.Assert(loop[i].Visited); - foreach (var pred in loop[i].Predecessors) { - if (!pred.Visited) { - if (pred.IsReachable) { - Debug.Fail("All jumps into the loop body should go through the entry point"); - } else { - pred.Visited = true; - loop.Add(pred); - } - } - } - } - } - /// /// Move the blocks associated with the loop into a new block container. /// @@ -708,7 +683,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } exitPoint = null; } - IncludeUnreachablePredecessors(nodesInSwitch); context.Step("Create BlockContainer for switch", switchInst); // Sort blocks in the loop in reverse post-order to make the output look a bit nicer. @@ -717,7 +691,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow Debug.Assert(nodesInSwitch[0] == h); foreach (var node in nodesInSwitch) { node.Visited = false; // reset visited flag so that we can find outer loops - Debug.Assert(h.Dominates(node) || !node.IsReachable, "The switch body must be dominated by the switch head"); + Debug.Assert(h.Dominates(node), "The switch body must be dominated by the switch head"); } BlockContainer switchContainer = new BlockContainer(ContainerKind.Switch); diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs index c2833e147..bcd9e047b 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs @@ -142,7 +142,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow function.Body = newBody; // register any locals used in newBody function.Variables.AddRange(newBody.Descendants.OfType().Select(inst => inst.Variable).Distinct()); - function.CheckInvariant(ILPhase.Normal); PrintFinallyMethodStateRanges(newBody); @@ -164,6 +163,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // Note: because this only deletes blocks outright, the 'stateChanges' entries remain valid // (though some may point to now-deleted blocks) newBody.SortBlocks(deleteUnreachableBlocks: true); + function.CheckInvariant(ILPhase.Normal); if (!isCompiledWithMono) { DecompileFinallyBlocks(); diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 49ce05219..88b48da73 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -493,8 +493,18 @@ namespace ICSharpCode.Decompiler.IL CollectionExtensions.AddRange(function.Variables, stackVariables); CollectionExtensions.AddRange(function.Variables, variableByExceptionHandler.Values); function.AddRef(); // mark the root node + var removedBlocks = new List(); foreach (var c in function.Descendants.OfType()) { - c.SortBlocks(); + var newOrder = c.TopologicalSort(deleteUnreachableBlocks: true); + if (newOrder.Count < c.Blocks.Count) { + removedBlocks.AddRange(c.Blocks.Except(newOrder)); + } + c.Blocks.ReplaceList(newOrder); + } + if (removedBlocks.Count > 0) { + removedBlocks.SortBy(b => b.StartILOffset); + function.Warnings.Add("Discarded unreachable code: " + + string.Join(", ", removedBlocks.Select(b => $"IL_{b.StartILOffset:x4}"))); } function.Warnings.AddRange(Warnings); return function; diff --git a/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs b/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs index 0e178853b..b824794a2 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs @@ -184,6 +184,7 @@ namespace ICSharpCode.Decompiler.IL Debug.Assert(EntryPoint == null || Parent is ILFunction || !HasILRange); Debug.Assert(Blocks.All(b => b.HasFlag(InstructionFlags.EndPointUnreachable))); Debug.Assert(Blocks.All(b => b.Kind == BlockKind.ControlFlow)); // this also implies that the blocks don't use FinalInstruction + Debug.Assert(TopologicalSort(deleteUnreachableBlocks: true).Count == Blocks.Count, "Container should not have any unreachable blocks"); Block bodyStartBlock; switch (Kind) { case ContainerKind.Normal: @@ -237,45 +238,56 @@ namespace ICSharpCode.Decompiler.IL return InstructionFlags.ControlFlow; } } - + /// - /// Sort the blocks in reverse post-order over the control flow graph between the blocks. + /// Topologically sort the blocks. + /// The new order is returned without modifying the BlockContainer. /// - public void SortBlocks(bool deleteUnreachableBlocks = false) + /// If true, unreachable blocks are not included in the new order. + public List TopologicalSort(bool deleteUnreachableBlocks = false) { - if (Blocks.Count < 2) - return; - // Visit blocks in post-order BitSet visited = new BitSet(Blocks.Count); List postOrder = new List(); - - Action visit = null; - visit = delegate(Block block) { + Visit(EntryPoint); + postOrder.Reverse(); + if (!deleteUnreachableBlocks) { + for (int i = 0; i < Blocks.Count; i++) { + if (!visited[i]) + postOrder.Add(Blocks[i]); + } + } + return postOrder; + + void Visit(Block block) + { Debug.Assert(block.Parent == this); if (!visited[block.ChildIndex]) { visited[block.ChildIndex] = true; foreach (var branch in block.Descendants.OfType()) { if (branch.TargetBlock.Parent == this) { - visit(branch.TargetBlock); + Visit(branch.TargetBlock); } } postOrder.Add(block); } }; - visit(EntryPoint); - - postOrder.Reverse(); - if (!deleteUnreachableBlocks) { - for (int i = 0; i < Blocks.Count; i++) { - if (!visited[i]) - postOrder.Add(Blocks[i]); - } - } - Debug.Assert(postOrder[0] == Blocks[0]); - Blocks.ReplaceList(postOrder); + } + + /// + /// Topologically sort the blocks. + /// + /// If true, delete unreachable blocks. + public void SortBlocks(bool deleteUnreachableBlocks = false) + { + if (Blocks.Count < 2) + return; + + var newOrder = TopologicalSort(deleteUnreachableBlocks); + Debug.Assert(newOrder[0] == Blocks[0]); + Blocks.ReplaceList(newOrder); } public static BlockContainer FindClosestContainer(ILInstruction inst) diff --git a/ICSharpCode.Decompiler/IL/Transforms/BlockTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/BlockTransform.cs index 07b151d4f..9836b1242 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/BlockTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/BlockTransform.cs @@ -85,7 +85,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms context.CancellationToken.ThrowIfCancellationRequested(); blockContext.ControlFlowGraph = new ControlFlowGraph(container, context.CancellationToken); VisitBlock(blockContext.ControlFlowGraph.GetNode(container.EntryPoint), blockContext); - // TODO: handle unreachable code? } } finally { running = false; From c4cb9df6873aac3b9c02e1a094f8f02f207627a0 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 26 Jul 2019 16:52:30 +0200 Subject: [PATCH 111/221] Fix switch in loops sometimes detecting an invalid switch body. --- .../IL/ControlFlow/LoopDetection.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs index f06e83ba8..0ae42262b 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/LoopDetection.cs @@ -257,6 +257,22 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow loop.Add(node); } } + // The loop/switch can only be entered through the entry point. + if (isSwitch) { + // In the case of a switch, false positives in the "continue;" detection logic + // can lead to falsely excludes some blocks from the body. + // Fix that by including all predecessors of included blocks. + Debug.Assert(loop[0] == loopHead); + for (int i = 1; i < loop.Count; i++) { + foreach (var p in loop[i].Predecessors) { + if (!p.Visited) { + p.Visited = true; + loop.Add(p); + } + } + } + } + Debug.Assert(loop.All(n => n == loopHead || n.Predecessors.All(p => p.Visited))); } else { // We are in case 2, but could not find a suitable exit point. // Heuristically try to minimize the number of exit points From 6c0216bbb9042c55ebee854d32d54afd25090747 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 26 Jul 2019 17:13:47 +0200 Subject: [PATCH 112/221] Fix assertion in ReduceNestingTransform: after copying the exitInst, it was possible that the old copy of the exitInst became unreachable. --- .../IL/ControlFlow/ConditionDetection.cs | 2 +- .../IL/Transforms/ReduceNestingTransform.cs | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs index 7025208f1..bb7a120b1 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/ConditionDetection.cs @@ -354,7 +354,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow //assert then block terminates var trueExitInst = GetExit(ifInst.TrueInst); var exitInst = GetExit(block); - context.Step("Negate if for desired branch "+trueExitInst, ifInst); + context.Step($"InvertIf at IL_{ifInst.StartILOffset:x4}", ifInst); //if the then block terminates, else blocks are redundant, and should not exist Debug.Assert(IsEmpty(ifInst.FalseInst)); diff --git a/ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs index cdd1822a0..e73fab690 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ReduceNestingTransform.cs @@ -194,6 +194,18 @@ namespace ICSharpCode.Decompiler.IL // if (cond) { ...; exit; } // ...; exit; EnsureEndPointUnreachable(ifInst.TrueInst, exitInst); + if (ifInst.FalseInst.HasFlag(InstructionFlags.EndPointUnreachable)) { + Debug.Assert(ifInst.HasFlag(InstructionFlags.EndPointUnreachable)); + Debug.Assert(ifInst.Parent == block); + int removeAfter = ifInst.ChildIndex + 1; + if (removeAfter < block.Instructions.Count) { + // Remove all instructions that ended up dead + // (this should just be exitInst itself) + Debug.Assert(block.Instructions.SecondToLastOrDefault() == ifInst); + Debug.Assert(block.Instructions.Last() == exitInst); + block.Instructions.RemoveRange(removeAfter, block.Instructions.Count - removeAfter); + } + } ExtractElseBlock(ifInst); ifInst = elseIfInst; } while (ifInst != null); From 470cd1ec23802759410d9b7af0f06da6b3635e3a Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 26 Jul 2019 17:48:04 +0200 Subject: [PATCH 113/221] Fix SolutionCreator.GetRelativePath so that it properly handles directories with '.' in the name. --- .../Solution/SolutionCreator.cs | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/ICSharpCode.Decompiler/Solution/SolutionCreator.cs b/ICSharpCode.Decompiler/Solution/SolutionCreator.cs index fc63beca5..824f03a23 100644 --- a/ICSharpCode.Decompiler/Solution/SolutionCreator.cs +++ b/ICSharpCode.Decompiler/Solution/SolutionCreator.cs @@ -55,16 +55,16 @@ namespace ICSharpCode.Decompiler.Solution } using (var writer = new StreamWriter(targetFile)) { - WriteSolutionFile(writer, projects, Path.GetDirectoryName(targetFile)); + WriteSolutionFile(writer, projects, targetFile); } FixProjectReferences(projects); } - private static void WriteSolutionFile(TextWriter writer, IEnumerable projects, string solutionPath) + private static void WriteSolutionFile(TextWriter writer, IEnumerable projects, string solutionFilePath) { WriteHeader(writer); - WriteProjects(writer, projects, solutionPath); + WriteProjects(writer, projects, solutionFilePath); writer.WriteLine("Global"); @@ -86,12 +86,12 @@ namespace ICSharpCode.Decompiler.Solution writer.WriteLine("MinimumVisualStudioVersion = 10.0.40219.1"); } - private static void WriteProjects(TextWriter writer, IEnumerable projects, string solutionPath) + private static void WriteProjects(TextWriter writer, IEnumerable projects, string solutionFilePath) { var solutionGuid = Guid.NewGuid().ToString("B").ToUpperInvariant(); foreach (var project in projects) { - var projectRelativePath = GetRelativePath(solutionPath, project.FilePath); + var projectRelativePath = GetRelativePath(solutionFilePath, project.FilePath); var projectGuid = project.Guid.ToString("B").ToUpperInvariant(); writer.WriteLine($"Project(\"{solutionGuid}\") = \"{project.ProjectName}\", \"{projectRelativePath}\", \"{projectGuid}\""); @@ -148,7 +148,6 @@ namespace ICSharpCode.Decompiler.Solution var projectsMap = projects.ToDictionary(p => p.ProjectName, p => p); foreach (var project in projects) { - var projectDirectory = Path.GetDirectoryName(project.FilePath); XDocument projectDoc = XDocument.Load(project.FilePath); var referencesItemGroups = projectDoc.Root @@ -156,14 +155,14 @@ namespace ICSharpCode.Decompiler.Solution .Where(e => e.Elements(ProjectFileNamespace + "Reference").Any()); foreach (var itemGroup in referencesItemGroups) { - FixProjectReferences(projectDirectory, itemGroup, projectsMap); + FixProjectReferences(project.FilePath, itemGroup, projectsMap); } projectDoc.Save(project.FilePath); } } - private static void FixProjectReferences(string projectDirectory, XElement itemGroup, IDictionary projects) + private static void FixProjectReferences(string projectFilePath, XElement itemGroup, IDictionary projects) { foreach (var item in itemGroup.Elements(ProjectFileNamespace + "Reference").ToList()) { var assemblyName = item.Attribute("Include")?.Value; @@ -173,20 +172,20 @@ namespace ICSharpCode.Decompiler.Solution var projectReference = new XElement(ProjectFileNamespace + "ProjectReference", new XElement(ProjectFileNamespace + "Project", referencedProject.Guid.ToString("B").ToUpperInvariant()), new XElement(ProjectFileNamespace + "Name", referencedProject.ProjectName)); - projectReference.SetAttributeValue("Include", GetRelativePath(projectDirectory, referencedProject.FilePath)); + projectReference.SetAttributeValue("Include", GetRelativePath(projectFilePath, referencedProject.FilePath)); itemGroup.Add(projectReference); } } } - private static string GetRelativePath(string fromPath, string toPath) + private static string GetRelativePath(string fromFilePath, string toFilePath) { - Uri fromUri = new Uri(AppendDirectorySeparatorChar(fromPath)); - Uri toUri = new Uri(AppendDirectorySeparatorChar(toPath)); + Uri fromUri = new Uri(fromFilePath); + Uri toUri = new Uri(toFilePath); if (fromUri.Scheme != toUri.Scheme) { - return toPath; + return toFilePath; } Uri relativeUri = fromUri.MakeRelativeUri(toUri); @@ -198,12 +197,5 @@ namespace ICSharpCode.Decompiler.Solution return relativePath; } - - private static string AppendDirectorySeparatorChar(string path) - { - return Path.HasExtension(path) || path.EndsWith(Path.DirectorySeparatorChar.ToString()) - ? path - : path + Path.DirectorySeparatorChar; - } } } From 129fd876d42f968b345fd4ad68d3b277c226b04a Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 26 Jul 2019 19:15:49 +0200 Subject: [PATCH 114/221] Use extension method syntax only if the extension method is eligible. --- .../CSharp/Transforms/IntroduceExtensionMethods.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs index b376815db..0e23d86cc 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceExtensionMethods.cs @@ -176,7 +176,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms var or = rr.PerformOverloadResolution(resolver.CurrentTypeResolveContext.Compilation, arguments, argumentNames, allowExtensionMethods: true); if (or == null || or.IsAmbiguous) return false; - return method.Equals(or.GetBestCandidateWithSubstitutedTypeArguments()); + return method.Equals(or.GetBestCandidateWithSubstitutedTypeArguments()) + && CSharpResolver.IsEligibleExtensionMethod(target.Type, method, useTypeInference: false, out _); } public static bool CanTransformToExtensionMethodCall(IMethod method, CSharpTypeResolveContext resolveContext, bool ignoreTypeArguments = false, bool ignoreArgumentNames = true) From 91af32ef948411fb41dadaa5fd20cf7d5884549c Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 26 Jul 2019 19:16:28 +0200 Subject: [PATCH 115/221] Do not wrap local functions in checked blocks. --- .../CSharp/Transforms/AddCheckedBlocks.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/AddCheckedBlocks.cs b/ICSharpCode.Decompiler/CSharp/Transforms/AddCheckedBlocks.cs index 0ce8a0bde..16f067a96 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/AddCheckedBlocks.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/AddCheckedBlocks.cs @@ -296,9 +296,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms costUncheckedContextCheckedBlockOpen += stmtResult.CostInCheckedContext; nodesUncheckedContextCheckedBlockOpen += stmtResult.NodesToInsertInCheckedContext; - if (statement is LabelStatement) { + if (statement is LabelStatement || statement is LocalFunctionDeclarationStatement) { // We can't move labels into blocks because that might cause goto-statements - // to be unable to just to the labels. + // to be unable to jump to the labels. + // Also, we can't move local functions into blocks, because that might cause + // them to become out of scope from the call-sites. costCheckedContextUncheckedBlockOpen = Cost.Infinite; costUncheckedContextCheckedBlockOpen = Cost.Infinite; } From 3067aa5519ef3a83e4512da79f6548d1f709ffbc Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 26 Jul 2019 19:18:49 +0200 Subject: [PATCH 116/221] FlattenSwitchBlocks: include local function and out var declarations in the list of exceptions. --- .../CSharp/Transforms/FlattenSwitchBlocks.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/FlattenSwitchBlocks.cs b/ICSharpCode.Decompiler/CSharp/Transforms/FlattenSwitchBlocks.cs index 68bb7c47f..5e9908aaf 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/FlattenSwitchBlocks.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/FlattenSwitchBlocks.cs @@ -16,12 +16,25 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms continue; var blockStatement = switchSection.Statements.First() as BlockStatement; - if (blockStatement == null || blockStatement.Statements.Any(st => st is VariableDeclarationStatement)) + if (blockStatement == null || blockStatement.Statements.Any(ContainsLocalDeclaration)) continue; blockStatement.Remove(); blockStatement.Statements.MoveTo(switchSection.Statements); } + + bool ContainsLocalDeclaration(AstNode node) + { + if (node is VariableDeclarationStatement || node is LocalFunctionDeclarationStatement || node is OutVarDeclarationExpression) + return true; + if (node is BlockStatement) + return false; + foreach (var child in node.Children) { + if (ContainsLocalDeclaration(child)) + return true; + } + return false; + } } } } From e5f95143cfdc21871be9401c2f40f29d010e8bec Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 26 Jul 2019 19:38:01 +0200 Subject: [PATCH 117/221] TransformDisplayClassUsage: set HasInitialValue = true for newly introduced locals. --- .../IL/Transforms/TransformDisplayClassUsage.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index d12191033..7a1a31e01 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -302,6 +302,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms displayClass.CaptureScope = BlockContainer.FindClosestContainer(inst); } v = displayClass.DeclaringFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name); + v.HasInitialValue = true; v.CaptureScope = displayClass.CaptureScope; inst.ReplaceWith(new StLoc(v, inst.Value).WithILRange(inst)); value = new LdLoc(v); @@ -349,6 +350,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // Introduce a fresh variable for the display class field. Debug.Assert(displayClass.Definition == field.DeclaringTypeDefinition); var v = displayClass.DeclaringFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name); + v.HasInitialValue = true; v.CaptureScope = displayClass.CaptureScope; inst.ReplaceWith(new LdLoca(v).WithILRange(inst)); displayClass.Variables.Add(field, new DisplayClassVariable { Value = new LdLoc(v), Variable = v }); From 7d62a7e0324ac5e47788db4fbe92336a270b6aef Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 27 Jul 2019 15:07:41 +0200 Subject: [PATCH 118/221] Reset HasInitialValue flag after TransformDisplayClassUsage. --- .../FlowAnalysis/DataFlowVisitor.cs | 71 +++++++++++++++++-- .../FlowAnalysis/DefiniteAssignmentVisitor.cs | 55 ++++++++++++++ .../IL/Transforms/RemoveDeadVariableInit.cs | 19 +++-- .../Transforms/TransformDisplayClassUsage.cs | 1 + 4 files changed, 135 insertions(+), 11 deletions(-) diff --git a/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs b/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs index ebef49e54..6d64b5dd1 100644 --- a/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs +++ b/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs @@ -222,6 +222,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis this.bottomState = initialState.Clone(); this.bottomState.ReplaceWithBottom(); Debug.Assert(bottomState.IsBottom); + this.stateOnNullableRewrap = bottomState.Clone(); this.currentStateOnException = state.Clone(); } @@ -254,7 +255,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis #endif [Conditional("DEBUG")] - void DebugStartPoint(ILInstruction inst) + protected void DebugStartPoint(ILInstruction inst) { #if DEBUG DebugPoint(debugInputState, inst); @@ -262,7 +263,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis } [Conditional("DEBUG")] - void DebugEndPoint(ILInstruction inst) + protected void DebugEndPoint(ILInstruction inst) { #if DEBUG DebugPoint(debugOutputState, inst); @@ -286,7 +287,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis foreach (var child in inst.Children) { child.AcceptVisitor(this); Debug.Assert(state.IsBottom || !child.HasFlag(InstructionFlags.EndPointUnreachable), - "Unreachable code must be in the bottom state."); + "Unreachable code must be in the bottom state."); } DebugEndPoint(inst); @@ -611,7 +612,53 @@ namespace ICSharpCode.Decompiler.FlowAnalysis state.JoinWith(afterTrueState); DebugEndPoint(inst); } - + + protected internal override void VisitNullCoalescingInstruction(NullCoalescingInstruction inst) + { + HandleBinaryWithOptionalEvaluation(inst, inst.ValueInst, inst.FallbackInst); + } + + protected internal override void VisitDynamicLogicOperatorInstruction(DynamicLogicOperatorInstruction inst) + { + HandleBinaryWithOptionalEvaluation(inst, inst.Left, inst.Right); + } + + protected internal override void VisitUserDefinedLogicOperator(UserDefinedLogicOperator inst) + { + HandleBinaryWithOptionalEvaluation(inst, inst.Left, inst.Right); + } + + void HandleBinaryWithOptionalEvaluation(ILInstruction parent, ILInstruction left, ILInstruction right) + { + DebugStartPoint(parent); + left.AcceptVisitor(this); + State branchState = state.Clone(); + right.AcceptVisitor(this); + state.JoinWith(branchState); + DebugEndPoint(parent); + } + + State stateOnNullableRewrap; + + protected internal override void VisitNullableRewrap(NullableRewrap inst) + { + DebugStartPoint(inst); + var oldState = stateOnNullableRewrap.Clone(); + stateOnNullableRewrap.ReplaceWithBottom(); + inst.Argument.AcceptVisitor(this); + state.JoinWith(stateOnNullableRewrap); + stateOnNullableRewrap = oldState; + DebugEndPoint(inst); + } + + protected internal override void VisitNullableUnwrap(NullableUnwrap inst) + { + DebugStartPoint(inst); + inst.Argument.AcceptVisitor(this); + stateOnNullableRewrap.JoinWith(state); + DebugEndPoint(inst); + } + protected internal override void VisitSwitchInstruction(SwitchInstruction inst) { DebugStartPoint(inst); @@ -635,6 +682,22 @@ namespace ICSharpCode.Decompiler.FlowAnalysis DebugEndPoint(inst); } + protected internal override void VisitUsingInstruction(UsingInstruction inst) + { + DebugStartPoint(inst); + inst.ResourceExpression.AcceptVisitor(this); + inst.Body.AcceptVisitor(this); + DebugEndPoint(inst); + } + + protected internal override void VisitLockInstruction(LockInstruction inst) + { + DebugStartPoint(inst); + inst.OnExpression.AcceptVisitor(this); + inst.Body.AcceptVisitor(this); + DebugEndPoint(inst); + } + protected internal override void VisitILFunction(ILFunction function) { throw new NotImplementedException(); diff --git a/ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs b/ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs index e05fbaccc..af6006009 100644 --- a/ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs +++ b/ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs @@ -20,6 +20,9 @@ using System.Diagnostics; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Util; using System.Threading; +using System; +using System.Collections.Generic; +using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.FlowAnalysis { @@ -117,6 +120,8 @@ namespace ICSharpCode.Decompiler.FlowAnalysis readonly CancellationToken cancellationToken; readonly ILFunction scope; readonly BitSet variablesWithUninitializedUsage; + readonly Dictionary stateOfLocalFunctionUse = new Dictionary(); + readonly HashSet localFunctionsNeedingAnalysis = new HashSet(); public DefiniteAssignmentVisitor(ILFunction scope, CancellationToken cancellationToken) { @@ -203,8 +208,34 @@ namespace ICSharpCode.Decompiler.FlowAnalysis HandleCall(inst); } + protected internal override void VisitILFunction(ILFunction inst) + { + DebugStartPoint(inst); + State stateBeforeFunction = state.Clone(); + State stateOnExceptionBeforeFunction = currentStateOnException.Clone(); + inst.Body.AcceptVisitor(this); + bool changed; + do { + changed = false; + foreach (var nestedFunction in inst.LocalFunctions) { + if (!localFunctionsNeedingAnalysis.Contains(nestedFunction.ReducedMethod)) + continue; + localFunctionsNeedingAnalysis.Remove(nestedFunction.ReducedMethod); + State stateOnEntry = stateOfLocalFunctionUse[nestedFunction.ReducedMethod]; + this.state.ReplaceWith(stateOnEntry); + this.currentStateOnException.ReplaceWithBottom(); + nestedFunction.Body.AcceptVisitor(this); + changed = true; + } + } while (changed); + currentStateOnException = stateOnExceptionBeforeFunction; + state = stateBeforeFunction; + DebugEndPoint(inst); + } + void HandleCall(CallInstruction call) { + DebugStartPoint(call); bool hasOutArgs = false; foreach (var arg in call.Arguments) { if (arg.MatchLdLoca(out var v) && call.GetParameter(arg.ChildIndex)?.IsOut == true) { @@ -223,6 +254,30 @@ namespace ICSharpCode.Decompiler.FlowAnalysis } } } + HandleLocalFunctionUse(call.Method); + DebugEndPoint(call); + } + + void HandleLocalFunctionUse(IMethod method) + { + if (method.IsLocalFunction) { + if (stateOfLocalFunctionUse.TryGetValue(method, out var stateOnEntry)) { + if (!state.LessThanOrEqual(stateOnEntry)) { + stateOnEntry.JoinWith(state); + localFunctionsNeedingAnalysis.Add(method); + } + } else { + stateOfLocalFunctionUse.Add(method, state.Clone()); + localFunctionsNeedingAnalysis.Add(method); + } + } + } + + protected internal override void VisitLdFtn(LdFtn inst) + { + DebugStartPoint(inst); + HandleLocalFunctionUse(inst.Method); + DebugEndPoint(inst); } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs b/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs index a6dab356b..9bdb3af18 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs @@ -35,13 +35,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms { public void Run(ILFunction function, ILTransformContext context) { - var visitor = new DefiniteAssignmentVisitor(function, context.CancellationToken); - function.Body.AcceptVisitor(visitor); - foreach (var v in function.Variables) { - if (v.Kind != VariableKind.Parameter && !visitor.IsPotentiallyUsedUninitialized(v)) { - v.HasInitialValue = false; - } - } + ResetHasInitialValueFlag(function, context); // Remove dead stores to variables that are never read from. // If the stored value has some side-effect, the value is unwrapped. // This is necessary to remove useless stores generated by some compilers, e.g., the F# compiler. @@ -94,5 +88,16 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } } + + internal static void ResetHasInitialValueFlag(ILFunction function, ILTransformContext context) + { + var visitor = new DefiniteAssignmentVisitor(function, context.CancellationToken); + function.AcceptVisitor(visitor); + foreach (var v in function.Variables) { + if (v.Kind != VariableKind.Parameter && !visitor.IsPotentiallyUsedUninitialized(v)) { + v.HasInitialValue = false; + } + } + } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index 7a1a31e01..3e34bd295 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -86,6 +86,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms containingBlock.Instructions.Remove(store); } } + RemoveDeadVariableInit.ResetHasInitialValueFlag(function, context); } finally { instructionsToRemove.Clear(); displayClasses.Clear(); From 25f625af9986f999afc6a078cbc5f3eca81e9aef Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 27 Jul 2019 15:08:51 +0200 Subject: [PATCH 119/221] Move copy propagation after expression and statement transforms. --- .../CSharp/CSharpDecompiler.cs | 2 +- .../IL/Transforms/CopyPropagation.cs | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 9c73c07ce..2bf09a7bf 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -131,7 +131,6 @@ namespace ICSharpCode.Decompiler.CSharp // copy-propated (turned into two separate assignments of the constant). // After is necessary because the assigned value might involve null coalescing/etc. new StatementTransform(new ILInlining(), new TransformAssignment()), - new CopyPropagation(), new StatementTransform( // per-block transforms that depend on each other, and thus need to // run interleaved (statement by statement). @@ -156,6 +155,7 @@ namespace ICSharpCode.Decompiler.CSharp }, new ProxyCallReplacer(), new FixRemainingIncrements(), + new CopyPropagation(), new DelegateConstruction(), new LocalFunctionDecompiler(), new TransformDisplayClassUsage(), diff --git a/ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs b/ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs index 3ca89cfb5..5b3f61a4c 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs @@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// then we can replace the variable with the argument. /// 2) assignments of address-loading instructions to local variables /// - public class CopyPropagation : IBlockTransform + public class CopyPropagation : IBlockTransform, IILTransform { public static void Propagate(StLoc store, ILTransformContext context) { @@ -42,6 +42,20 @@ namespace ICSharpCode.Decompiler.IL.Transforms } public void Run(Block block, BlockTransformContext context) + { + RunOnBlock(block, context); + } + + public void Run(ILFunction function, ILTransformContext context) + { + foreach (var block in function.Descendants.OfType()) { + if (block.Kind != BlockKind.ControlFlow) + continue; + RunOnBlock(block, context); + } + } + + static void RunOnBlock(Block block, ILTransformContext context) { for (int i = 0; i < block.Instructions.Count; i++) { ILVariable v; From 8cfc21dfab2c35137eb127f8341678cd53d8ce04 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 27 Jul 2019 15:43:51 +0200 Subject: [PATCH 120/221] Fix DefiniteAssignmentVisitor bugs handling ILFunction, and add some comments. --- .../FlowAnalysis/DataFlowVisitor.cs | 4 ++++ .../FlowAnalysis/DefiniteAssignmentVisitor.cs | 21 +++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs b/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs index 6d64b5dd1..a283fcad0 100644 --- a/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs +++ b/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs @@ -645,8 +645,11 @@ namespace ICSharpCode.Decompiler.FlowAnalysis DebugStartPoint(inst); var oldState = stateOnNullableRewrap.Clone(); stateOnNullableRewrap.ReplaceWithBottom(); + inst.Argument.AcceptVisitor(this); + // Join incoming control flow from the NullableUnwraps. state.JoinWith(stateOnNullableRewrap); + stateOnNullableRewrap = oldState; DebugEndPoint(inst); } @@ -655,6 +658,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis { DebugStartPoint(inst); inst.Argument.AcceptVisitor(this); + // Conditional control flow edge to the surrounding NullableRewrap. stateOnNullableRewrap.JoinWith(state); DebugEndPoint(inst); } diff --git a/ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs b/ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs index af6006009..5b465b735 100644 --- a/ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs +++ b/ICSharpCode.Decompiler/FlowAnalysis/DefiniteAssignmentVisitor.cs @@ -120,6 +120,7 @@ namespace ICSharpCode.Decompiler.FlowAnalysis readonly CancellationToken cancellationToken; readonly ILFunction scope; readonly BitSet variablesWithUninitializedUsage; + readonly Dictionary stateOfLocalFunctionUse = new Dictionary(); readonly HashSet localFunctionsNeedingAnalysis = new HashSet(); @@ -213,7 +214,19 @@ namespace ICSharpCode.Decompiler.FlowAnalysis DebugStartPoint(inst); State stateBeforeFunction = state.Clone(); State stateOnExceptionBeforeFunction = currentStateOnException.Clone(); + // Note: lambdas are handled at their point of declaration. + // We immediately visit their body, because captured variables need to be definitely initialized at this point. + // We ignore the state after the lambda body (by resetting to the state before), because we don't know + // when the lambda will be invoked. + // This also makes this logic unsuitable for reaching definitions, as we wouldn't see the effect of stores in lambdas. + // Only the simpler case of definite assignment can support lambdas. inst.Body.AcceptVisitor(this); + + // For local functions, the situation is similar to lambdas. + // However, we don't use the state of the declaration site when visiting local functions, + // but instead the state(s) of their point of use. + // Because we might discover additional points of use within the local functions, + // we use a fixed-point iteration. bool changed; do { changed = false; @@ -223,8 +236,8 @@ namespace ICSharpCode.Decompiler.FlowAnalysis localFunctionsNeedingAnalysis.Remove(nestedFunction.ReducedMethod); State stateOnEntry = stateOfLocalFunctionUse[nestedFunction.ReducedMethod]; this.state.ReplaceWith(stateOnEntry); - this.currentStateOnException.ReplaceWithBottom(); - nestedFunction.Body.AcceptVisitor(this); + this.currentStateOnException.ReplaceWith(stateOnEntry); + nestedFunction.AcceptVisitor(this); changed = true; } } while (changed); @@ -258,6 +271,10 @@ namespace ICSharpCode.Decompiler.FlowAnalysis DebugEndPoint(call); } + /// + /// For a use of a local function, remember the current state to use as stateOnEntry when + /// later processing the local function body. + /// void HandleLocalFunctionUse(IMethod method) { if (method.IsLocalFunction) { From afde03a04dcc29a72bc4bf24010e6f90c80da1fc Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 27 Jul 2019 15:53:38 +0200 Subject: [PATCH 121/221] Fix #1597: Incorrect handling of nullability annotations during generic type substitution. --- .../Implementation/NullabilityAnnotatedType.cs | 9 +++++++-- .../TypeSystem/ParameterizedType.cs | 14 ++++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs index fa87dd4ec..b13bede86 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs @@ -53,11 +53,16 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation IType newBase = baseType.AcceptVisitor(visitor); if (newBase != baseType) { if (newBase.Nullability == Nullability.Nullable) { - // T?! -> T? + // `T!` with substitution T=`U?` becomes `U?` // This happens during type substitution for generic methods. return newBase; } - return newBase.ChangeNullability(nullability); + if (newBase.Kind == TypeKind.TypeParameter || newBase.IsReferenceType == true) { + return newBase.ChangeNullability(nullability); + } else { + // `T!` with substitution T=`int` becomes `int`, not `int!` + return newBase; + } } else { return this; } diff --git a/ICSharpCode.Decompiler/TypeSystem/ParameterizedType.cs b/ICSharpCode.Decompiler/TypeSystem/ParameterizedType.cs index de7a25c2b..5dd907ace 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ParameterizedType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ParameterizedType.cs @@ -140,10 +140,20 @@ namespace ICSharpCode.Decompiler.TypeSystem return b.ToString(); } } - + public override string ToString() { - return ReflectionName; + StringBuilder b = new StringBuilder(genericType.ToString()); + b.Append('['); + for (int i = 0; i < typeArguments.Length; i++) { + if (i > 0) + b.Append(','); + b.Append('['); + b.Append(typeArguments[i].ToString()); + b.Append(']'); + } + b.Append(']'); + return b.ToString(); } public IReadOnlyList TypeArguments => typeArguments; From 14c76d1ba6f9563256eeea4b100ce06f49527cb3 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 27 Jul 2019 17:12:05 +0200 Subject: [PATCH 122/221] Fix failing initializer tests. --- .../TestCases/Pretty/InitializerTests.cs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs index 13416aaf1..693152c83 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs @@ -1678,13 +1678,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests }); } - private void NestedListWithIndexInitializer(MyEnum myEnum) + private List> NestedListWithIndexInitializer(MyEnum myEnum) { -#if !OPT - List> list = new List> { -#else - List> obj = new List> { -#endif + return new List> { [0] = { 1, 2, @@ -1717,13 +1713,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests }); } - public static void Issue1390(IEnumerable tokens, bool alwaysAllowAdministrators, char wireDelimiter) + public static List> Issue1390(IEnumerable tokens, bool alwaysAllowAdministrators, char wireDelimiter) { -#if OPT - List> obj = new List> { -#else - List> list = new List> { -#endif + return new List> { { "tokens", string.Join(wireDelimiter.ToString(), tokens), From 97d490a3df7f82a5b4c2ac7617fb907b96ac50f0 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 27 Jul 2019 17:53:10 +0200 Subject: [PATCH 123/221] Slightly generalize the post-increment transform so that it no longer depends on copy propagation. --- .../IL/Transforms/CopyPropagation.cs | 5 ++- .../IL/Transforms/TransformAssignment.cs | 37 +++++++++++++++++-- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs b/ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs index 5b3f61a4c..d4e867e7f 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs @@ -66,7 +66,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (SemanticHelper.IsPure(copiedExpr.Flags)) { // no-op -> delete context.Step("remove dead store to stack: no-op -> delete", block.Instructions[i]); - block.Instructions.RemoveAt(i--); + block.Instructions.RemoveAt(i); + // This can open up new inlining opportunities: + int c = ILInlining.InlineInto(block, i, InliningOptions.None, context: context); + i -= c + 1; } else { // evaluate the value for its side-effects context.Step("remove dead store to stack: evaluate the value for its side-effects", block.Instructions[i]); diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs index ef086733d..a55a1e7f7 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs @@ -547,10 +547,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// Some transforms effectively move a store around, /// which is only valid if the variable stored to does not occur in the compound load/store. /// + /// + /// Instruction preceding the load. + /// static bool IsMatchingCompoundLoad(ILInstruction load, ILInstruction store, out ILInstruction target, out CompoundTargetKind targetKind, out Action finalizeMatch, - ILVariable forbiddenVariable = null) + ILVariable forbiddenVariable = null, + ILInstruction previousInstruction = null) { target = null; targetKind = 0; @@ -563,7 +567,31 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; target = ldobj.Target; targetKind = CompoundTargetKind.Address; - return ldobj.Target.Match(stobj.Target).Success; + if (ldobj.Target.Match(stobj.Target).Success) { + return true; + } else if (IsDuplicatedAddressComputation()) { + // Use S_0 as target, so that S_0 can later be eliminated by inlining. + // (we can't eliminate previousInstruction right now, because it's before the transform's starting instruction) + target = stobj.Target; + return true; + } else { + return false; + } + + bool IsDuplicatedAddressComputation() + { + // Sometimes roslyn duplicates the address calculation: + // stloc S_0(ldloc refParam) + // stloc V_0(ldobj System.Int32(ldloc refParam)) + // stobj System.Int32(ldloc S_0, binary.add.i4(ldloc V_0, ldc.i4 1)) + if (!stobj.Target.MatchLdLoc(out var s)) + return false; + if (!(s.Kind == VariableKind.StackSlot && s.IsSingleDefinition && s != forbiddenVariable)) + return false; + if (s.StoreInstructions.SingleOrDefault() != previousInstruction) + return false; + return previousInstruction is StLoc addressStore && addressStore.Value.Match(ldobj.Target).Success; + } } else if (MatchingGetterAndSetterCalls(load as CallInstruction, store as CallInstruction, out finalizeMatch)) { if (forbiddenVariable != null && forbiddenVariable.IsUsedWithin(load)) return false; @@ -677,8 +705,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms // 'stloc tmp' is implicitly truncating the value return false; } - if (!IsMatchingCompoundLoad(inst.Value, store, out var target, out var targetKind, out var finalizeMatch, forbiddenVariable: inst.Variable)) + if (!IsMatchingCompoundLoad(inst.Value, store, out var target, out var targetKind, out var finalizeMatch, + forbiddenVariable: inst.Variable, + previousInstruction: block.Instructions.ElementAtOrDefault(i - 1))) { return false; + } if (UnwrapSmallIntegerConv(value, out var conv) is BinaryNumericInstruction binary) { if (!binary.Left.MatchLdLoc(tmpVar) || !binary.Right.MatchLdcI(1)) return false; From adc443e4e25ba37ff8fd710e20ed74a00fa2490e Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 27 Jul 2019 19:23:46 +0200 Subject: [PATCH 124/221] Fix dynamic compound assignments --- .../DynamicIsEventAssignmentTransform.cs | 37 ++++++++++++++++--- .../IL/Transforms/ExpressionTransforms.cs | 6 +-- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/DynamicIsEventAssignmentTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/DynamicIsEventAssignmentTransform.cs index bd9522458..c9c5b2405 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DynamicIsEventAssignmentTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DynamicIsEventAssignmentTransform.cs @@ -16,6 +16,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.Linq; + namespace ICSharpCode.Decompiler.IL.Transforms { public class DynamicIsEventAssignmentTransform : IStatementTransform @@ -24,6 +26,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// if (logic.not(ldloc V_1)) Block IL_004a { /// stloc V_2(dynamic.getmember B(target)) /// } + /// [stloc copyOfValue(value)] /// if (logic.not(ldloc V_1)) Block IL_0149 { /// dynamic.setmember.compound B(target, dynamic.binary.operator AddAssign(ldloc V_2, value)) /// } else Block IL_0151 { @@ -41,11 +44,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms return; if (!(flagVar.IsSingleDefinition && flagVar.LoadCount == 2)) return; - if (!(MatchLhsCacheIfInstruction(block.Instructions[pos + 1], flagVar, out var dynamicGetMemberStore))) + if (!MatchLhsCacheIfInstruction(block.Instructions[pos + 1], flagVar, out var dynamicGetMemberStore)) return; if (!(dynamicGetMemberStore.MatchStLoc(out var getMemberVar, out inst) && inst is DynamicGetMemberInstruction getMemberInst)) return; - foreach (var descendant in block.Instructions[pos + 2].Descendants) { + int offset = 2; + if (block.Instructions[pos + offset].MatchStLoc(out var valueVariable) + && pos + 4 < block.Instructions.Count && valueVariable.IsSingleDefinition && valueVariable.LoadCount == 2 + && valueVariable.LoadInstructions.All(ld => ld.Parent is DynamicInstruction)) { + offset++; + } + foreach (var descendant in block.Instructions[pos + offset].Descendants) { if (!MatchIsEventAssignmentIfInstruction(descendant, isEvent, flagVar, getMemberVar, out var setMemberInst, out var getMemberVarUse, out var isEventConditionUse)) continue; context.Step("DynamicIsEventAssignmentTransform", block.Instructions[pos]); @@ -86,12 +95,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms isEventConditionUse = condition; } else return false; - setMemberInst = Block.Unwrap(trueInst) as DynamicSetMemberInstruction; - if (!(setMemberInst != null)) + setMemberInst = UnwrapBlockAndUnusedStore(trueInst) as DynamicSetMemberInstruction; + if (setMemberInst == null) return false; if (!isEvent.Argument.Match(setMemberInst.Target).Success) return false; - if (!(Block.Unwrap(falseInst) is DynamicInvokeMemberInstruction invokeMemberInst && invokeMemberInst.Arguments.Count == 2)) + if (!(UnwrapBlockAndUnusedStore(falseInst) is DynamicInvokeMemberInstruction invokeMemberInst && invokeMemberInst.Arguments.Count == 2)) return false; if (!isEvent.Argument.Match(invokeMemberInst.Arguments[0]).Success) return false; @@ -101,6 +110,24 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } + /// + /// If is a Block unwraps the block, otherwise returns inst, + /// if the block contains a stloc to a stack slot that is unused, returns the value, + /// otherwise returns inst + /// + internal static ILInstruction UnwrapBlockAndUnusedStore(ILInstruction inst) + { + var unwrapped = Block.Unwrap(inst); + if (unwrapped is StLoc stloc + && stloc.Variable.IsSingleDefinition + && stloc.Variable.LoadCount == 0) + { + return stloc.Value; + } + + return unwrapped; + } + /// /// if (logic.not(ldloc V_1)) Block IL_004a { /// stloc V_2(dynamic.getmember B(target)) diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index cebcd3ef8..80d6cdf36 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -497,8 +497,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!(condition is DynamicIsEventInstruction isEvent)) return false; - trueInst = Block.Unwrap(trueInst); - falseInst = Block.Unwrap(falseInst); + trueInst = DynamicIsEventAssignmentTransform.UnwrapBlockAndUnusedStore(trueInst); + falseInst = DynamicIsEventAssignmentTransform.UnwrapBlockAndUnusedStore(falseInst); if (!(falseInst is DynamicCompoundAssign dynamicCompoundAssign)) return false; if (!(dynamicCompoundAssign.Target is DynamicGetMemberInstruction getMember)) @@ -582,7 +582,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms for (int j = 0; j < dynamicGetIndex.Arguments.Count; j++) { if (!SemanticHelper.IsPure(dynamicGetIndex.Arguments[j].Flags)) return; - if (!dynamicGetIndex.Arguments[j].Match(dynamicGetIndex.Arguments[j]).Success) + if (!dynamicGetIndex.Arguments[j].Match(inst.Arguments[j]).Success) return; } if (!DynamicCompoundAssign.IsExpressionTypeSupported(binaryOp.Operation)) From 18ee984ade1b08ebac063004351409f6100c585f Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 27 Jul 2019 19:31:13 +0200 Subject: [PATCH 125/221] Add support for "definitely assigned if true/false" to data flow analysis. --- .../ICSharpCode.Decompiler.Tests.csproj | 1 + .../PrettyTestRunner.cs | 6 +++ .../TestCases/Pretty/OutVariables.cs | 45 +++++++++++++++++ .../FlowAnalysis/DataFlowVisitor.cs | 50 +++++++++++++++++-- 4 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Pretty/OutVariables.cs diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index c6d570204..0d0fac14d 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -77,6 +77,7 @@ + diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index fa1bb5dd8..0f932c117 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -239,6 +239,12 @@ namespace ICSharpCode.Decompiler.Tests RunForLibrary(cscOptions: cscOptions, asmOptions: AssemblerOptions.UseOwnDisassembler); } + [Test] + public void OutVariables([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions) + { + RunForLibrary(cscOptions: cscOptions); + } + [Test] public void InitializerTests([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OutVariables.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OutVariables.cs new file mode 100644 index 000000000..5a6916b8e --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OutVariables.cs @@ -0,0 +1,45 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +{ + public class PatternMatching + { + public static void OutVarInShortCircuit(Dictionary d) + { + if (d.Count > 2 && d.TryGetValue(42, out string value)) { + Console.WriteLine(value); + } + } + + public static Action CapturedOutVarInShortCircuit(Dictionary d) + { + // Note: needs reasoning about "definitely assigned if true" + // to ensure that the value is initialized when the delegate is declared. + if (d.Count > 2 && d.TryGetValue(42, out string value)) { + return delegate { + Console.WriteLine(value); + }; + } + return null; + } + } +} diff --git a/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs b/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs index a283fcad0..a53fcfb79 100644 --- a/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs +++ b/ICSharpCode.Decompiler/FlowAnalysis/DataFlowVisitor.cs @@ -603,16 +603,60 @@ namespace ICSharpCode.Decompiler.FlowAnalysis protected internal override void VisitIfInstruction(IfInstruction inst) { DebugStartPoint(inst); - inst.Condition.AcceptVisitor(this); - State branchState = state.Clone(); + var (beforeThen, beforeElse) = EvaluateCondition(inst.Condition); + state = beforeThen; inst.TrueInst.AcceptVisitor(this); State afterTrueState = state; - state = branchState; + state = beforeElse; inst.FalseInst.AcceptVisitor(this); state.JoinWith(afterTrueState); DebugEndPoint(inst); } + /// + /// Evaluates the condition of an if. + /// + /// + /// A pair of: + /// * The state after the condition evaluates to true + /// * The state after the condition evaluates to false + /// + /// + /// this.state is invalid after this function was called, and must be overwritten + /// with one of the return values. + /// + (State OnTrue, State OnFalse) EvaluateCondition(ILInstruction inst) + { + if (inst is IfInstruction ifInst) { + // 'if (a?b:c)' or similar. + // This also includes conditions that are logic.not, logic.and, logic.or. + DebugStartPoint(ifInst); + var (beforeThen, beforeElse) = EvaluateCondition(ifInst.Condition); + state = beforeThen; + var (afterThenTrue, afterThenFalse) = EvaluateCondition(ifInst.TrueInst); + state = beforeElse; + var (afterElseTrue, afterElseFalse) = EvaluateCondition(ifInst.FalseInst); + + var onTrue = afterThenTrue; + onTrue.JoinWith(afterElseTrue); + var onFalse = afterThenFalse; + onFalse.JoinWith(afterElseFalse); + + DebugEndPoint(ifInst); + return (onTrue, onFalse); + } else if (inst is LdcI4 constant) { + if (constant.Value == 0) { + return (bottomState.Clone(), state); + } else { + return (state, bottomState.Clone()); + } + } else { + // other kind of condition + inst.AcceptVisitor(this); + return (state, state.Clone()); + } + } + protected internal override void VisitNullCoalescingInstruction(NullCoalescingInstruction inst) { HandleBinaryWithOptionalEvaluation(inst, inst.ValueInst, inst.FallbackInst); From 60422a0f7e3e0e3df3937567d75e9fa4372ee544 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 27 Jul 2019 20:13:19 +0200 Subject: [PATCH 126/221] Fix ResolveResult on typeof() expression. --- ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 5090304ee..15b5ad111 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -543,7 +543,9 @@ namespace ICSharpCode.Decompiler.CSharp protected internal override TranslatedExpression VisitLdTypeToken(LdTypeToken inst, TranslationContext context) { - return new MemberReferenceExpression(new TypeOfExpression(ConvertType(inst.Type)), "TypeHandle") + var typeofExpr = new TypeOfExpression(ConvertType(inst.Type)) + .WithRR(new TypeOfResolveResult(compilation.FindType(KnownTypeCode.Type), inst.Type)); + return new MemberReferenceExpression(typeofExpr, "TypeHandle") .WithILInstruction(inst) .WithRR(new TypeOfResolveResult(compilation.FindType(new TopLevelTypeName("System", "RuntimeTypeHandle")), inst.Type)); } From 2ca8c31d8e9c1ab47b3ef6567af7f730d57adb30 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 27 Jul 2019 21:33:59 +0200 Subject: [PATCH 127/221] Fix ArgumentException: ignore duplicate keys after variable splitting in TransformDisplayClassUsage --- .../IL/Transforms/TransformDisplayClassUsage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index 3e34bd295..bc69ff014 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -183,7 +183,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms }; displayClasses.Add(thisVariable, displayClass); foreach (var stateMachineVariable in nestedFunction.Variables) { - if (stateMachineVariable.StateMachineField == null) + if (stateMachineVariable.StateMachineField == null || displayClass.Variables.ContainsKey(stateMachineVariable.StateMachineField)) continue; displayClass.Variables.Add(stateMachineVariable.StateMachineField, new DisplayClassVariable { Variable = stateMachineVariable, From 752f0de978ee0c46616b762c6819526cfdd94e1c Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 27 Jul 2019 21:45:18 +0200 Subject: [PATCH 128/221] Further adjustments to transforms now that copy propagation no longer runs. --- .../ControlFlow/ControlFlowSimplification.cs | 18 +++++++++ .../IL/ControlFlow/DetectPinnedRegions.cs | 15 +++----- .../DynamicIsEventAssignmentTransform.cs | 22 +---------- .../IL/Transforms/ExpressionTransforms.cs | 4 +- .../IL/Transforms/ILInlining.cs | 2 + .../IL/Transforms/NullPropagationTransform.cs | 12 ++---- .../IL/Transforms/TransformAssignment.cs | 38 +++++++++++-------- 7 files changed, 56 insertions(+), 55 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs index 4627c73c8..09b228ad4 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs @@ -15,6 +15,7 @@ // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -45,6 +46,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow context.CancellationToken.ThrowIfCancellationRequested(); RemoveNopInstructions(block); + RemoveDeadStackStores(block); InlineVariableInReturnBlock(block, context); // 1st pass SimplifySwitchInstruction before SimplifyBranchChains() @@ -68,6 +70,22 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow block.Instructions.RemoveAll(inst => inst.OpCode == OpCode.Nop); } + private void RemoveDeadStackStores(Block block) + { + // Previously copy propagation did this; + // ideally the ILReader would already do this, + // for now do this here (even though it's not control-flow related). + for (int i = block.Instructions.Count - 1; i >= 0; i--) { + if (block.Instructions[i] is StLoc stloc && stloc.Variable.IsSingleDefinition && stloc.Variable.LoadCount == 0 && stloc.Variable.Kind == VariableKind.StackSlot) { + if (SemanticHelper.IsPure(stloc.Value.Flags)) { + block.Instructions.RemoveAt(i++); + } else { + stloc.ReplaceWith(stloc.Value); + } + } + } + } + void InlineVariableInReturnBlock(Block block, ILTransformContext context) { // In debug mode, the C#-compiler generates 'return blocks' that diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs b/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs index b2ff0ef2d..049ad5bdc 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/DetectPinnedRegions.cs @@ -504,26 +504,23 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // stloc nativeVar(conv o->i (ldloc pinnedVar)) // if (comp(ldloc nativeVar == conv i4->i (ldc.i4 0))) br targetBlock // br adjustOffsetToStringData - if (!body.EntryPoint.Instructions[0].MatchStLoc(out ILVariable nativeVar, out ILInstruction initInst)) - return; ILVariable newVar; - if (body.EntryPoint.Instructions.Count != 3) { + if (!body.EntryPoint.Instructions[0].MatchStLoc(out ILVariable nativeVar, out ILInstruction initInst)) { // potentially a special case with legacy csc and an unused pinned variable: - if (nativeVar.IsSingleDefinition && nativeVar.LoadCount == 0 && initInst.MatchLdLoc(pinnedRegion.Variable) - && pinnedRegion.Variable.LoadCount == 1) - { - // initInst is dead store - body.EntryPoint.Instructions.RemoveAt(0); + if (pinnedRegion.Variable.AddressCount == 0 && pinnedRegion.Variable.LoadCount == 0) { var charPtr = new PointerType(context.TypeSystem.FindType(KnownTypeCode.Char)); newVar = new ILVariable(VariableKind.PinnedLocal, charPtr, pinnedRegion.Variable.Index); newVar.Name = pinnedRegion.Variable.Name; newVar.HasGeneratedName = pinnedRegion.Variable.HasGeneratedName; - nativeVar.Function.Variables.Add(newVar); + pinnedRegion.Variable.Function.Variables.Add(newVar); pinnedRegion.Variable = newVar; pinnedRegion.Init = new ArrayToPointer(pinnedRegion.Init); } return; } + if (body.EntryPoint.Instructions.Count != 3) { + return; + } if (nativeVar.Type.GetStackType() != StackType.I) return; diff --git a/ICSharpCode.Decompiler/IL/Transforms/DynamicIsEventAssignmentTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/DynamicIsEventAssignmentTransform.cs index c9c5b2405..5903ecef3 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DynamicIsEventAssignmentTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DynamicIsEventAssignmentTransform.cs @@ -95,12 +95,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms isEventConditionUse = condition; } else return false; - setMemberInst = UnwrapBlockAndUnusedStore(trueInst) as DynamicSetMemberInstruction; + setMemberInst = Block.Unwrap(trueInst) as DynamicSetMemberInstruction; if (setMemberInst == null) return false; if (!isEvent.Argument.Match(setMemberInst.Target).Success) return false; - if (!(UnwrapBlockAndUnusedStore(falseInst) is DynamicInvokeMemberInstruction invokeMemberInst && invokeMemberInst.Arguments.Count == 2)) + if (!(Block.Unwrap(falseInst) is DynamicInvokeMemberInstruction invokeMemberInst && invokeMemberInst.Arguments.Count == 2)) return false; if (!isEvent.Argument.Match(invokeMemberInst.Arguments[0]).Success) return false; @@ -110,24 +110,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - /// - /// If is a Block unwraps the block, otherwise returns inst, - /// if the block contains a stloc to a stack slot that is unused, returns the value, - /// otherwise returns inst - /// - internal static ILInstruction UnwrapBlockAndUnusedStore(ILInstruction inst) - { - var unwrapped = Block.Unwrap(inst); - if (unwrapped is StLoc stloc - && stloc.Variable.IsSingleDefinition - && stloc.Variable.LoadCount == 0) - { - return stloc.Value; - } - - return unwrapped; - } - /// /// if (logic.not(ldloc V_1)) Block IL_004a { /// stloc V_2(dynamic.getmember B(target)) diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 80d6cdf36..826cb7f05 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -497,8 +497,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!(condition is DynamicIsEventInstruction isEvent)) return false; - trueInst = DynamicIsEventAssignmentTransform.UnwrapBlockAndUnusedStore(trueInst); - falseInst = DynamicIsEventAssignmentTransform.UnwrapBlockAndUnusedStore(falseInst); + trueInst = Block.Unwrap(trueInst); + falseInst = Block.Unwrap(falseInst); if (!(falseInst is DynamicCompoundAssign dynamicCompoundAssign)) return false; if (!(dynamicCompoundAssign.Target is DynamicGetMemberInstruction getMember)) diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index 46559fd4f..4f7b59d73 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -283,6 +283,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms return !method.IsStatic; case OpCode.Await: return true; + case OpCode.NullableUnwrap: + return ((NullableUnwrap)ldloca.Parent).RefInput; default: return false; } diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs index f65415587..675e94280 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs @@ -17,22 +17,15 @@ // DEALINGS IN THE SOFTWARE. using System; -using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics; -using System.Linq; -using System.Text; -using ICSharpCode.Decompiler.CSharp.Syntax; -using ICSharpCode.Decompiler.Semantics; using ICSharpCode.Decompiler.TypeSystem; -using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.IL.Transforms { /// /// Transform that converts code patterns like "v != null ? v.M() : null" to "v?.M()" /// - struct NullPropagationTransform + readonly struct NullPropagationTransform { internal static bool IsProtectedIfInst(IfInstruction ifInst) { @@ -214,6 +207,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms inst = ldLen.Array; } else if (inst is NullableUnwrap unwrap) { inst = unwrap.Argument; + if (unwrap.RefInput && inst is AddressOf addressOf) { + inst = addressOf.Value; + } } else if (inst is DynamicGetMemberInstruction dynGetMember) { inst = dynGetMember.Target; } else if (inst is DynamicInvokeMemberInstruction dynInvokeMember) { diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs index a55a1e7f7..de201b887 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs @@ -569,7 +569,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms targetKind = CompoundTargetKind.Address; if (ldobj.Target.Match(stobj.Target).Success) { return true; - } else if (IsDuplicatedAddressComputation()) { + } else if (IsDuplicatedAddressComputation(stobj.Target, ldobj.Target)) { // Use S_0 as target, so that S_0 can later be eliminated by inlining. // (we can't eliminate previousInstruction right now, because it's before the transform's starting instruction) target = stobj.Target; @@ -577,21 +577,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms } else { return false; } - - bool IsDuplicatedAddressComputation() - { - // Sometimes roslyn duplicates the address calculation: - // stloc S_0(ldloc refParam) - // stloc V_0(ldobj System.Int32(ldloc refParam)) - // stobj System.Int32(ldloc S_0, binary.add.i4(ldloc V_0, ldc.i4 1)) - if (!stobj.Target.MatchLdLoc(out var s)) - return false; - if (!(s.Kind == VariableKind.StackSlot && s.IsSingleDefinition && s != forbiddenVariable)) - return false; - if (s.StoreInstructions.SingleOrDefault() != previousInstruction) - return false; - return previousInstruction is StLoc addressStore && addressStore.Value.Match(ldobj.Target).Success; - } } else if (MatchingGetterAndSetterCalls(load as CallInstruction, store as CallInstruction, out finalizeMatch)) { if (forbiddenVariable != null && forbiddenVariable.IsUsedWithin(load)) return false; @@ -608,6 +593,27 @@ namespace ICSharpCode.Decompiler.IL.Transforms } else { return false; } + + bool IsDuplicatedAddressComputation(ILInstruction storeTarget, ILInstruction loadTarget) + { + // Sometimes roslyn duplicates the address calculation: + // stloc S_0(ldloc refParam) + // stloc V_0(ldobj System.Int32(ldloc refParam)) + // stobj System.Int32(ldloc S_0, binary.add.i4(ldloc V_0, ldc.i4 1)) + while (storeTarget is LdFlda storeLdFlda && loadTarget is LdFlda loadLdFlda) { + if (!storeLdFlda.Field.Equals(loadLdFlda.Field)) + return false; + storeTarget = storeLdFlda.Target; + loadTarget = loadLdFlda.Target; + } + if (!storeTarget.MatchLdLoc(out var s)) + return false; + if (!(s.Kind == VariableKind.StackSlot && s.IsSingleDefinition && s != forbiddenVariable)) + return false; + if (s.StoreInstructions.SingleOrDefault() != previousInstruction) + return false; + return previousInstruction is StLoc addressStore && addressStore.Value.Match(loadTarget).Success; + } } /// From b7f0e2e9298d183848c90e0cafe4db3b5aef5cf2 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 27 Jul 2019 22:28:28 +0200 Subject: [PATCH 129/221] Be less aggressive when removing dead code -- some of it may still be interesting to users. --- .../ICSharpCode.Decompiler.Tests.csproj | 8 ++++++++ .../ControlFlow/ControlFlowSimplification.cs | 19 ++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index c6d570204..50ac8a691 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -61,6 +61,9 @@ + + + @@ -77,6 +80,11 @@ + + + + + diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs index 09b228ad4..8bde22e66 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs @@ -46,7 +46,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow context.CancellationToken.ThrowIfCancellationRequested(); RemoveNopInstructions(block); - RemoveDeadStackStores(block); + RemoveDeadStackStores(block, aggressive: context.Settings.RemoveDeadCode); InlineVariableInReturnBlock(block, context); // 1st pass SimplifySwitchInstruction before SimplifyBranchChains() @@ -70,20 +70,33 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow block.Instructions.RemoveAll(inst => inst.OpCode == OpCode.Nop); } - private void RemoveDeadStackStores(Block block) + private void RemoveDeadStackStores(Block block, bool aggressive) { // Previously copy propagation did this; // ideally the ILReader would already do this, // for now do this here (even though it's not control-flow related). for (int i = block.Instructions.Count - 1; i >= 0; i--) { if (block.Instructions[i] is StLoc stloc && stloc.Variable.IsSingleDefinition && stloc.Variable.LoadCount == 0 && stloc.Variable.Kind == VariableKind.StackSlot) { - if (SemanticHelper.IsPure(stloc.Value.Flags)) { + if (aggressive ? SemanticHelper.IsPure(stloc.Value.Flags) : IsSimple(stloc.Value)) { + Debug.Assert(SemanticHelper.IsPure(stloc.Value.Flags)); block.Instructions.RemoveAt(i++); } else { + stloc.Value.AddILRange(stloc); stloc.ReplaceWith(stloc.Value); } } } + + bool IsSimple(ILInstruction inst) + { + switch (inst.OpCode) { + case OpCode.LdLoc: + case OpCode.LdStr: // C# 1.0 compiler sometimes emits redundant ldstr in switch-on-string pattern + return true; + default: + return false; + } + } } void InlineVariableInReturnBlock(Block block, ILTransformContext context) From 4a0ca21bbfa078bc9bc7c3dbecea986e6e765f14 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 28 Jul 2019 10:38:50 +0200 Subject: [PATCH 130/221] Add (failing) test case for throw expressions. --- .../ICSharpCode.Decompiler.Tests.csproj | 1 + .../PrettyTestRunner.cs | 6 + .../TestCases/Pretty/ThrowExpressions.cs | 201 ++++++++++++++++++ 3 files changed, 208 insertions(+) create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/Pretty/ThrowExpressions.cs diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index ad5e8bb47..7a447d2c9 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -81,6 +81,7 @@ + diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index 0f932c117..cfee6cb8b 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -329,6 +329,12 @@ namespace ICSharpCode.Decompiler.Tests RunForLibrary(cscOptions: cscOptions); } + [Test] + public void ThrowExpressions([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions) + { + RunForLibrary(cscOptions: cscOptions); + } + [Test] public void WellKnownConstants([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) { diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ThrowExpressions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ThrowExpressions.cs new file mode 100644 index 000000000..89742681d --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ThrowExpressions.cs @@ -0,0 +1,201 @@ +using System; + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +{ + internal class ThrowExpressions + { + private class ArgumentCheckingCtor + { + private int initializedFromCtor = CountSheep() ?? throw new Exception("No sheep?!"); + private object cacheObj = TryGetObj() ?? throw new Exception("What?"); + + private object simpleObj; + private int? nullableInt; + + public ArgumentCheckingCtor(object simpleObj, int? nullableInt) + { + this.simpleObj = simpleObj ?? throw new ArgumentNullException("simpleObj"); + this.nullableInt = nullableInt ?? throw new ArgumentNullException("nullableInt"); + } + + public ArgumentCheckingCtor(string input) + : this(input, GetIntOrNull(input ?? throw new ArgumentNullException("input"))) + { + + } + + public ArgumentCheckingCtor(DataObject obj) + : this(obj ?? throw new Exception(), GetIntOrNull(obj.NullableDataField?.NullableDataField.ToString() ?? throw new ArgumentNullException("input"))) + { + + } + + private static int? GetIntOrNull(string v) + { + if (int.TryParse(v, out int result)) + return result; + return null; + } + + private static int? CountSheep() + { + throw new NotImplementedException(); + } + + private static object TryGetObj() + { + return null; + } + + public override int GetHashCode() + { + return initializedFromCtor; + } + + public override bool Equals(object obj) + { + return true; + } + } + + public class DataObject + { + public int IntField; + public int? NullableIntField; + public Data DataField; + public Data? NullableDataField; + public int IntProperty { get; set; } + public int? NullableIntProperty { get; set; } + public Data DataProperty { get; } + public Data? NullableDataProperty { get; } + } + + public struct Data + { + public int IntField; + public int? NullableIntField; + public MoreData DataField; + public MoreData? NullableDataField; + public int IntProperty { get; set; } + public int? NullableIntProperty { get; set; } + public MoreData DataProperty { get; } + public MoreData? NullableDataProperty { get; } + } + + public struct MoreData + { + public int IntField; + public int? NullableIntField; + public int IntProperty { get; set; } + public int? NullableIntProperty { get; set; } + } + + public static int IntField; + public static int? NullableIntField; + public static object ObjectField; + public int InstIntField; + public int? InstNullableIntField; + public object InstObjectField; + public Data DataField; + public Data? NullableDataField; + public DataObject DataObjectField; + + public static int IntProperty { get; } + public static int? NullableIntProperty { get; } + public static object ObjProperty { get; } + public int InstIntProperty { get; } + public int? InstNullableIntProperty { get; } + public object InstObjProperty { get; } + public Data DataProperty { get; } + public Data? NullableDataProperty { get; } + public DataObject DataObjectProperty { get; } + + public static int ReturnIntField() + { + return NullableIntField ?? throw new Exception(); + } + public static int ReturnIntProperty() + { + return NullableIntProperty ?? throw new Exception(); + } + public static object ReturnObjField() + { + return ObjectField ?? throw new Exception(); + } + public static object ReturnObjProperty() + { + return ObjProperty ?? throw new Exception(); + } + public static int ReturnIntField(ThrowExpressions inst) + { + return inst.InstNullableIntField ?? throw new Exception(); + } + public static int ReturnIntProperty(ThrowExpressions inst) + { + return inst.InstNullableIntProperty ?? throw new Exception(); + } + public static object ReturnObjField(ThrowExpressions inst) + { + return inst.InstObjectField ?? throw new Exception(); + } + public static object ReturnObjProperty(ThrowExpressions inst) + { + return inst.InstObjProperty ?? throw new Exception(); + } + + public static void UseComplexNullableStruct(ThrowExpressions inst) + { + Use(inst.InstNullableIntField ?? throw new Exception()); + Use(inst.NullableDataField?.IntField ?? throw new Exception()); + Use(inst.NullableDataField?.NullableIntField ?? throw new Exception()); + Use(inst.NullableDataProperty?.IntField ?? throw new Exception()); + Use(inst.NullableDataProperty?.NullableIntField ?? throw new Exception()); + Use(inst.NullableDataField?.DataField.IntField ?? throw new Exception()); + Use(inst.NullableDataField?.DataField.NullableIntField ?? throw new Exception()); + Use(inst.NullableDataProperty?.DataField.IntField ?? throw new Exception()); + Use(inst.NullableDataProperty?.DataField.NullableIntField ?? throw new Exception()); + Use(inst.NullableDataField?.DataProperty.IntField ?? throw new Exception()); + Use(inst.NullableDataField?.DataProperty.NullableIntField ?? throw new Exception()); + Use(inst.NullableDataProperty?.DataProperty.IntField ?? throw new Exception()); + Use(inst.NullableDataProperty?.DataProperty.NullableIntField ?? throw new Exception()); + Use(inst.NullableDataField?.NullableDataField?.IntField ?? throw new Exception()); + Use(inst.NullableDataField?.NullableDataField?.NullableIntField ?? throw new Exception()); + Use(inst.NullableDataProperty?.NullableDataField?.IntField ?? throw new Exception()); + Use(inst.NullableDataProperty?.NullableDataField?.NullableIntField ?? throw new Exception()); + Use(inst.NullableDataField?.NullableDataProperty?.IntField ?? throw new Exception()); + Use(inst.NullableDataField?.NullableDataProperty?.NullableIntField ?? throw new Exception()); + Use(inst.NullableDataProperty?.NullableDataProperty?.IntField ?? throw new Exception()); + Use(inst.NullableDataProperty?.NullableDataProperty?.NullableIntField ?? throw new Exception()); + } + + public static void UseComplexNullableObject(DataObject inst) + { + Use(inst?.NullableIntField ?? throw new Exception()); + Use(inst?.NullableDataField?.IntField ?? throw new Exception()); + Use(inst?.NullableDataField?.NullableIntField ?? throw new Exception()); + Use(inst?.NullableDataProperty?.IntField ?? throw new Exception()); + Use(inst?.NullableDataProperty?.NullableIntField ?? throw new Exception()); + Use(inst?.NullableDataField?.DataField.IntField ?? throw new Exception()); + Use(inst?.NullableDataField?.DataField.NullableIntField ?? throw new Exception()); + Use(inst?.NullableDataProperty?.DataField.IntField ?? throw new Exception()); + Use(inst?.NullableDataProperty?.DataField.NullableIntField ?? throw new Exception()); + Use(inst?.NullableDataField?.DataProperty.IntField ?? throw new Exception()); + Use(inst?.NullableDataField?.DataProperty.NullableIntField ?? throw new Exception()); + Use(inst?.NullableDataProperty?.DataProperty.IntField ?? throw new Exception()); + Use(inst?.NullableDataProperty?.DataProperty.NullableIntField ?? throw new Exception()); + Use(inst?.NullableDataField?.NullableDataField?.IntField ?? throw new Exception()); + Use(inst?.NullableDataField?.NullableDataField?.NullableIntField ?? throw new Exception()); + Use(inst?.NullableDataProperty?.NullableDataField?.IntField ?? throw new Exception()); + Use(inst?.NullableDataProperty?.NullableDataField?.NullableIntField ?? throw new Exception()); + Use(inst?.NullableDataField?.NullableDataProperty?.IntField ?? throw new Exception()); + Use(inst?.NullableDataField?.NullableDataProperty?.NullableIntField ?? throw new Exception()); + Use(inst?.NullableDataProperty?.NullableDataProperty?.IntField ?? throw new Exception()); + Use(inst?.NullableDataProperty?.NullableDataProperty?.NullableIntField ?? throw new Exception()); + } + + public static void Use(T usage) + { + + } + } +} From 2b6c0c389270960bac3718d7330bfd07125ec72a Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 28 Jul 2019 13:40:26 +0200 Subject: [PATCH 131/221] Fix formatting in ExpressionBuilder. --- .../CSharp/ExpressionBuilder.cs | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 6125179eb..bc442745b 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -441,7 +441,7 @@ namespace ICSharpCode.Decompiler.CSharp .WithILInstruction(inst); } finally { astBuilder.PrintIntegralValuesAsHex = false; - } + } } protected internal override TranslatedExpression VisitLdcI8(LdcI8 inst, TranslationContext context) @@ -465,7 +465,7 @@ namespace ICSharpCode.Decompiler.CSharp .WithILInstruction(inst); } finally { astBuilder.PrintIntegralValuesAsHex = false; - } + } } private bool ShouldDisplayAsHex(long value, ILInstruction parent) @@ -556,7 +556,7 @@ namespace ICSharpCode.Decompiler.CSharp var argUType = NullableType.GetUnderlyingType(argument.Type); if (argUType.GetStackType().GetSize() < inst.UnderlyingResultType.GetSize() - || argUType.Kind == TypeKind.Enum && argUType.IsSmallIntegerType() + || argUType.Kind == TypeKind.Enum && argUType.IsSmallIntegerType() || argUType.GetStackType() == StackType.I || argUType.IsKnownType(KnownTypeCode.Boolean) || argUType.IsKnownType(KnownTypeCode.Char)) @@ -1522,7 +1522,7 @@ namespace ICSharpCode.Decompiler.CSharp var pao = GetPointerArithmeticOffset(inst.Value, value, ((PointerType)target.Type).ElementType, inst.CheckForOverflow); if (pao != null) { value = pao.Value; - } else { + } else { value.Expression.AddChild(new Comment("ILSpy Error: GetPointerArithmeticOffset() failed", CommentType.MultiLine), Roles.Comment); } } else { @@ -1939,13 +1939,13 @@ namespace ICSharpCode.Decompiler.CSharp // When accessing members on value types, ensure we use a reference of the correct type, // and not a pointer or a reference to a different type (issue #1333) if (!(translatedTarget.Type is ByReferenceType brt && NormalizeTypeVisitor.TypeErasure.EquivalentTypes(brt.ElementType, memberDeclaringType))) { - translatedTarget = translatedTarget.ConvertTo(new ByReferenceType(memberDeclaringType), this); - } + translatedTarget = translatedTarget.ConvertTo(new ByReferenceType(memberDeclaringType), this); + } } if (translatedTarget.Expression is DirectionExpression) { // (ref x).member => x.member translatedTarget = translatedTarget.UnwrapChild(((DirectionExpression)translatedTarget).Expression); - } else if (translatedTarget.Expression is UnaryOperatorExpression uoe + } else if (translatedTarget.Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.NullConditional && uoe.Expression is DirectionExpression) { // (ref x)?.member => x?.member @@ -2040,7 +2040,7 @@ namespace ICSharpCode.Decompiler.CSharp pointer = pointer.ConvertTo(new PointerType(value.Type), this); } else { pointer = pointer.ConvertTo(new PointerType(inst.Type), this); - } + } } if (pointer.Expression is UnaryOperatorExpression uoe && uoe.Operator == UnaryOperatorType.AddressOf) { // *&ptr -> ptr @@ -2053,7 +2053,7 @@ namespace ICSharpCode.Decompiler.CSharp } if (value.Expression == null) { value = Translate(inst.Value, typeHint: target.Type); - } + } return Assignment(target, value).WithILInstruction(inst); } @@ -2138,7 +2138,7 @@ namespace ICSharpCode.Decompiler.CSharp TranslatedExpression arrayExpr = Translate(inst.Array); var arrayType = arrayExpr.Type as ArrayType; if (arrayType == null || !TypeUtils.IsCompatibleTypeForMemoryAccess(arrayType.ElementType, inst.Type)) { - arrayType = new ArrayType(compilation, inst.Type, inst.Indices.Count); + arrayType = new ArrayType(compilation, inst.Type, inst.Indices.Count); arrayExpr = arrayExpr.ConvertTo(arrayType, this); } TranslatedExpression expr = new IndexerExpression( @@ -2185,8 +2185,7 @@ namespace ICSharpCode.Decompiler.CSharp // try via its effective base class. arg = arg.ConvertTo(((ITypeParameter)targetType).EffectiveBaseClass, this); } - } - else { + } else { // Before unboxing arg must be a object arg = arg.ConvertTo(compilation.FindType(KnownTypeCode.Object), this); } @@ -2425,7 +2424,7 @@ namespace ICSharpCode.Decompiler.CSharp } TranslatedExpression MakeInitializerAssignment(InitializedObjectResolveResult rr, IL.Transforms.AccessPathElement memberPath, - IL.Transforms.AccessPathElement valuePath, List values, + IL.Transforms.AccessPathElement valuePath, List values, Dictionary indexVariables) { TranslatedExpression value; @@ -2445,9 +2444,9 @@ namespace ICSharpCode.Decompiler.CSharp if (memberPath.Member is IProperty property) { index = new CallBuilder(this, typeSystem, settings) .BuildDictionaryInitializerExpression(valuePath.OpCode, property.Setter, rr, GetIndices(valuePath.Indices, indexVariables).ToList()); - } else { + } else { index = new IndexerExpression(null, GetIndices(valuePath.Indices, indexVariables).Select(i => Translate(i).Expression)); - } + } return new AssignmentExpression(index, value) .WithRR(new MemberResolveResult(rr, memberPath.Member)) .WithoutILInstruction(); @@ -2455,7 +2454,7 @@ namespace ICSharpCode.Decompiler.CSharp return new NamedExpression(valuePath.Member.Name, value) .WithRR(new MemberResolveResult(rr, valuePath.Member)) .WithoutILInstruction(); - } + } } class ArrayInitializer @@ -2469,7 +2468,7 @@ namespace ICSharpCode.Decompiler.CSharp public ArrayInitializerExpression Expression; // HACK: avoid using Expression.Elements.Count: https://github.com/icsharpcode/ILSpy/issues/1202 public int CurrentElementCount; - } + } TranslatedExpression TranslateArrayInitializer(Block block) { From 7d4b4c6433a46a958c06c36bbeb45fc6104750a2 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 28 Jul 2019 15:39:47 +0200 Subject: [PATCH 132/221] Implement NullCoalescingTransform with value types. --- .../CSharp/ExpressionBuilder.cs | 2 +- .../IL/Instructions/ILInstruction.cs | 2 +- .../IL/Transforms/NullCoalescingTransform.cs | 57 ++++++++++++++++++- .../IL/Transforms/NullableLiftingTransform.cs | 2 +- 4 files changed, 58 insertions(+), 5 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index bc442745b..77992c60d 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -134,7 +134,7 @@ namespace ICSharpCode.Decompiler.CSharp }; var cexpr = inst.AcceptVisitor(this, context); #if DEBUG - if (inst.ResultType != StackType.Void && cexpr.Type.Kind != TypeKind.Unknown && inst.ResultType != StackType.Unknown) { + if (inst.ResultType != StackType.Void && cexpr.Type.Kind != TypeKind.Unknown && inst.ResultType != StackType.Unknown && cexpr.Type.Kind != TypeKind.None) { // Validate the Translate post-condition (documented at beginning of this file): if (inst.ResultType.IsIntegerType()) { Debug.Assert(cexpr.Type.GetStackType().IsIntegerType(), "IL instructions of integer type must convert into C# expressions of integer type"); diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs index d1d7e17f8..609b31dc7 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs @@ -308,7 +308,7 @@ namespace ICSharpCode.Decompiler.IL protected abstract SlotInfo GetChildSlot(int index); #region ChildrenCollection + ChildrenEnumerator - public struct ChildrenCollection : IReadOnlyList + public readonly struct ChildrenCollection : IReadOnlyList { readonly ILInstruction inst; diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs index 2f534ef03..7734099ac 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs @@ -21,6 +21,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using ICSharpCode.Decompiler.TypeSystem; namespace ICSharpCode.Decompiler.IL.Transforms { @@ -34,7 +35,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms { public void Run(Block block, int pos, StatementTransformContext context) { - TransformRefTypes(block, pos, context); + if (!TransformRefTypes(block, pos, context)) { + TransformThrowExpressionValueTypes(block, pos, context); + } } /// @@ -89,7 +92,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // => // stloc obj(if.notnull(valueInst, throw(...))) if (context.Settings.ThrowExpressions && trueInst is Throw throwInst) { - context.Step("NullCoalescingTransform (throw expression)", stloc); + context.Step("NullCoalescingTransform (reference types + throw expression)", stloc); throwInst.resultType = StackType.O; stloc.Value = new NullCoalescingInstruction(NullCoalescingKind.Ref, stloc.Value, throwInst); block.Instructions.RemoveAt(pos + 1); // remove if instruction @@ -98,5 +101,55 @@ namespace ICSharpCode.Decompiler.IL.Transforms } return false; } + + /// + /// stloc v(value) + /// if (logic.not(call get_HasValue(ldloca v))) { + /// throw(...) + /// } + /// ... Call(arg1, arg2, call GetValueOrDefault(ldloca v), arg4) ... + /// => + /// ... Call(arg1, arg2, if.notnull(value, throw(...)), arg4) ... + /// + bool TransformThrowExpressionValueTypes(Block block, int pos, StatementTransformContext context) + { + if (pos + 2 >= block.Instructions.Count) + return false; + if (!(block.Instructions[pos] is StLoc stloc)) + return false; + if (!block.Instructions[pos + 1].MatchIfInstruction(out var condition, out var trueInst)) + return false; + if (!(Block.Unwrap(trueInst) is Throw throwInst)) + return false; + ILVariable v = stloc.Variable; + if (!(v.StoreCount == 1 && v.LoadCount == 0 && v.AddressCount == 2)) + return false; + if (!NullableLiftingTransform.MatchNegatedHasValueCall(condition, v)) + return false; + var throwInstParent = throwInst.Parent; + var throwInstChildIndex = throwInst.ChildIndex; + var nullcoalescingWithThrow = new NullCoalescingInstruction( + NullCoalescingKind.NullableWithValueFallback, + stloc.Value, + throwInst); + var resultType = NullableType.GetUnderlyingType(v.Type).GetStackType(); + nullcoalescingWithThrow.UnderlyingResultType = resultType; + var result = ILInlining.FindLoadInNext(block.Instructions[pos + 2], v, nullcoalescingWithThrow, InliningOptions.None); + if (result.Type == ILInlining.FindResultType.Found + && NullableLiftingTransform.MatchGetValueOrDefault(result.LoadInst.Parent, v)) + { + context.Step("NullCoalescingTransform (value types + throw expression)", stloc); + throwInst.resultType = resultType; + result.LoadInst.Parent.ReplaceWith(nullcoalescingWithThrow); + block.Instructions.RemoveRange(pos, 2); // remove store and if instruction + return true; + } else { + // reset the primary position (see remarks on ILInstruction.Parent) + stloc.Value = stloc.Value; + var children = throwInstParent.Children; + children[throwInstChildIndex] = throwInst; + return false; + } + } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs index e1c816028..bc3e05151 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullableLiftingTransform.cs @@ -833,7 +833,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// Matches 'logic.not(call get_HasValue(ldloca v))' /// - static bool MatchNegatedHasValueCall(ILInstruction inst, ILVariable v) + internal static bool MatchNegatedHasValueCall(ILInstruction inst, ILVariable v) { return inst.MatchLogicNot(out var arg) && MatchHasValueCall(arg, v); } From 903544598a1573b1117287a4e4c9a9c073c5d45b Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 28 Jul 2019 16:41:51 +0200 Subject: [PATCH 133/221] Add comments about expected result after a ConvertTo() call. This method has grown a few cases where it doesn't actually convert. --- .../CSharp/TranslatedExpression.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs index 3a5e32744..35582ec1d 100644 --- a/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs @@ -163,7 +163,7 @@ namespace ICSharpCode.Decompiler.CSharp } throw new ArgumentException("descendant must be a descendant of the current node"); } - + /// /// Adds casts (if necessary) to convert this expression to the specified target type. /// @@ -176,6 +176,17 @@ namespace ICSharpCode.Decompiler.CSharp /// /// From the caller's perspective, IntPtr/UIntPtr behave like normal C# integers except that they have native int size. /// All the special cases necessary to make IntPtr/UIntPtr behave sanely are handled internally in ConvertTo(). + /// + /// Post-condition: + /// The "expected evaluation result" is the value computed by this.Expression, + /// converted to targetType via an IL conv instruction. + /// + /// ConvertTo(targetType, allowImplicitConversion=false).Type must be equal to targetType (modulo identity conversions). + /// The value computed by the converted expression must match the "expected evaluation result". + /// + /// ConvertTo(targetType, allowImplicitConversion=true) must produce an expression that, + /// when evaluated in a context where it will be implicitly converted to targetType, + /// evaluates to the "expected evaluation result". /// public TranslatedExpression ConvertTo(IType targetType, ExpressionBuilder expressionBuilder, bool checkForOverflow = false, bool allowImplicitConversion = false) { From 7f8689c46429061f44f7d44d546f85c4e070247a Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 28 Jul 2019 16:42:44 +0200 Subject: [PATCH 134/221] Allow inlining of compiler-generated value-type-temporaries when field accesses are involved. --- .../TestCases/Pretty/ValueTypes.cs | 10 ++++++++++ .../IL/Transforms/ILInlining.cs | 16 ++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ValueTypes.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ValueTypes.cs index e03c5468e..346c3b13e 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ValueTypes.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ValueTypes.cs @@ -66,6 +66,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty S s = this; s.SetField(); } + + public void UseField(int val) + { + UseField(Get().Field); + } } #if CS72 @@ -263,5 +268,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty r.Property = 2; #endif } + + public static void CallOnFieldOfTemporary() + { + Get().Field.ToString(); + } } } \ No newline at end of file diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index 4f7b59d73..ce84921e4 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -272,10 +272,14 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (ldloca.Variable.Type.IsReferenceType ?? false) return false; - switch (ldloca.Parent.OpCode) { + ILInstruction inst = ldloca; + while (inst.Parent is LdFlda ldflda) { + inst = ldflda; + } + switch (inst.Parent.OpCode) { case OpCode.Call: case OpCode.CallVirt: - var method = ((CallInstruction)ldloca.Parent).Method; + var method = ((CallInstruction)inst.Parent).Method; if (method.IsAccessor && method.AccessorKind != MethodSemanticsAttributes.Getter) { // C# doesn't allow calling setters on temporary structs return false; @@ -284,7 +288,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms case OpCode.Await: return true; case OpCode.NullableUnwrap: - return ((NullableUnwrap)ldloca.Parent).RefInput; + return ((NullableUnwrap)inst.Parent).RefInput; default: return false; } @@ -346,13 +350,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms { switch (addr) { case LdFlda ldflda: - return ldflda.Field.IsReadOnly; + return ldflda.Field.IsReadOnly + || (ldflda.Field.DeclaringType.Kind == TypeKind.Struct && IsReadonlyReference(ldflda.Target)); case LdsFlda ldsflda: return ldsflda.Field.IsReadOnly; case LdLoc ldloc: return IsReadonlyRefLocal(ldloc.Variable); case Call call: return call.Method.ReturnTypeIsRefReadOnly; + case AddressOf _: + // C# doesn't allow mutation of value-type temporaries + return true; default: return false; } From 0e0179edff5d4eaa7e3856945d9830a8f19fde58 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 28 Jul 2019 17:33:17 +0200 Subject: [PATCH 135/221] Improve NullPropagation (?.) when fields of value-type are involved. --- .../TestCases/Pretty/NullPropagation.cs | 14 ++++++++++++++ .../IL/Transforms/NullPropagationTransform.cs | 6 ++++++ 2 files changed, 20 insertions(+) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.cs index 9ce4066cf..98b5df626 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullPropagation.cs @@ -25,6 +25,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty private class MyClass { public int IntVal; + public readonly int ReadonlyIntVal; + public MyStruct StructField; + public readonly MyStruct ReadonlyStructField; public string Text; public MyClass Field; public MyClass Property { @@ -45,6 +48,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty private struct MyStruct { public int IntVal; + public readonly int ReadonlyIntVal; public MyClass Field; public MyStruct? Property1 => null; public MyStruct Property2 => default(MyStruct); @@ -178,6 +182,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { Use(GetMyClass()?.Text ?? "Hello"); } + + public void CallOnValueTypeField() + { + Use(GetMyClass()?.IntVal.ToString()); + Use(GetMyStruct()?.IntVal.ToString()); + Use(GetMyClass()?.ReadonlyIntVal.ToString()); + Use(GetMyStruct()?.ReadonlyIntVal.ToString()); + GetMyClass()?.StructField.Done(); + GetMyClass()?.ReadonlyStructField.Done(); + } public void InvokeDelegate(EventHandler eh) { diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs index 675e94280..9288c447b 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullPropagationTransform.cs @@ -186,6 +186,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms return chainLength >= 1; } else if (inst.MatchLdFld(out var target, out _)) { inst = target; + } else if (inst.MatchLdFlda(out target, out var f)) { + if (target is AddressOf addressOf && f.DeclaringType.Kind == TypeKind.Struct) { + inst = addressOf.Value; + } else { + inst = target; + } } else if (inst is CallInstruction call && call.OpCode != OpCode.NewObj) { if (call.Arguments.Count == 0) { return false; From ddf4053a45a52609023047be326419b1ec8980df Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 29 Jul 2019 08:47:24 +0200 Subject: [PATCH 136/221] Fix merge conflict in Conversion.cs --- ICSharpCode.Decompiler/Semantics/Conversion.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.Decompiler/Semantics/Conversion.cs b/ICSharpCode.Decompiler/Semantics/Conversion.cs index 4895a1ab8..b90107392 100644 --- a/ICSharpCode.Decompiler/Semantics/Conversion.cs +++ b/ICSharpCode.Decompiler/Semantics/Conversion.cs @@ -77,12 +77,15 @@ namespace ICSharpCode.Decompiler.Semantics public static readonly Conversion TryCast = new BuiltinConversion(false, 9); /// - /// C# 7 throw expression being converted to an arbitrary type. + /// C# 6 string interpolation expression implicitly being converted to or . /// - public static readonly Conversion ThrowExpressionConversion = new BuiltinConversion(true, 10); - public static readonly Conversion ImplicitInterpolatedStringConversion = new BuiltinConversion(true, 10); + /// + /// C# 7 throw expression being converted to an arbitrary type. + /// + public static readonly Conversion ThrowExpressionConversion = new BuiltinConversion(true, 11); + public static Conversion UserDefinedConversion(IMethod operatorMethod, bool isImplicit, Conversion conversionBeforeUserDefinedOperator, Conversion conversionAfterUserDefinedOperator, bool isLifted = false, bool isAmbiguous = false) { if (operatorMethod == null) From 5c18c9e945e18733fb03f2eee9b77f825b14face Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 29 Jul 2019 23:58:31 +0200 Subject: [PATCH 137/221] Floating-point arithmetic isn't affected by checked/unchecked blocks; so avoid forcing it into an unchecked block. --- ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 15b5ad111..814e4b35f 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -1281,8 +1281,9 @@ namespace ICSharpCode.Decompiler.CSharp var resultExpr = new BinaryOperatorExpression(left.Expression, op, right.Expression) .WithILInstruction(inst) .WithRR(rr); - if (BinaryOperatorMightCheckForOverflow(op)) + if (BinaryOperatorMightCheckForOverflow(op) && !inst.UnderlyingResultType.IsFloatType()) { resultExpr.Expression.AddAnnotation(inst.CheckForOverflow ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation); + } return resultExpr; } @@ -1544,8 +1545,9 @@ namespace ICSharpCode.Decompiler.CSharp .WithILInstruction(inst) .WithRR(new OperatorResolveResult(target.Type, AssignmentExpression.GetLinqNodeType(op, inst.CheckForOverflow), target.ResolveResult, value.ResolveResult)); } - if (AssignmentOperatorMightCheckForOverflow(op)) + if (AssignmentOperatorMightCheckForOverflow(op) && !inst.UnderlyingResultType.IsFloatType()) { resultExpr.Expression.AddAnnotation(inst.CheckForOverflow ? AddCheckedBlocks.CheckedAnnotation : AddCheckedBlocks.UncheckedAnnotation); + } return resultExpr; } From df13f0ce01630c44040a61b96bebee1bca252c32 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 29 Jul 2019 23:59:19 +0200 Subject: [PATCH 138/221] Fix #1605: Inline ref locals more aggressively; this is necessary for VB compound assignments. --- .../Helpers/Tester.VB.cs | 4 ++-- .../ICSharpCode.Decompiler.Tests.csproj | 2 ++ .../TestCases/Pretty/RefLocalsAndReturns.cs | 4 +++- .../TestCases/VBPretty/.gitignore | 1 + .../TestCases/VBPretty/VBCompoundAssign.cs | 20 +++++++++++++++++++ .../TestCases/VBPretty/VBCompoundAssign.vb | 14 +++++++++++++ .../VBPrettyTestRunner.cs | 14 +++++++++++-- .../IL/Transforms/ILInlining.cs | 8 +++++++- 8 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/VBPretty/.gitignore create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.cs create mode 100644 ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.vb diff --git a/ICSharpCode.Decompiler.Tests/Helpers/Tester.VB.cs b/ICSharpCode.Decompiler.Tests/Helpers/Tester.VB.cs index e09cecda9..55f0f5a3e 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.VB.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.VB.cs @@ -56,10 +56,10 @@ namespace ICSharpCode.Decompiler.Tests.Helpers var provider = new VBCodeProvider(new Dictionary { { "CompilerVersion", "v4.0" } }); CompilerParameters options = new CompilerParameters(); options.GenerateExecutable = !flags.HasFlag(CompilerOptions.Library); - options.CompilerOptions = "/o" + (flags.HasFlag(CompilerOptions.Optimize) ? "+" : "-"); + options.CompilerOptions = "/optimize" + (flags.HasFlag(CompilerOptions.Optimize) ? "+" : "-"); options.CompilerOptions += (flags.HasFlag(CompilerOptions.UseDebug) ? " /debug" : ""); options.CompilerOptions += (flags.HasFlag(CompilerOptions.Force32Bit) ? " /platform:anycpu32bitpreferred" : ""); - options.CompilerOptions += "/optioninfer+ /optionexplicit+"; + options.CompilerOptions += " /optioninfer+ /optionexplicit+"; if (preprocessorSymbols.Count > 0) { options.CompilerOptions += " /d:" + string.Join(",", preprocessorSymbols.Select(p => $"{p.Key}={p.Value}")); } diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index ad5e8bb47..852443f6c 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -81,6 +81,7 @@ + @@ -243,6 +244,7 @@ + diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs index 7f7053b18..385e0932c 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs @@ -193,7 +193,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { ref NormalStruct @ref = ref GetRef(); RefReassignment(ref @ref); - @ref = ref GetRef(); + if (s.GetHashCode() == 0) { + @ref = ref GetRef(); + } RefReassignment(ref @ref.GetHashCode() == 4 ? ref @ref : ref s); } diff --git a/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/.gitignore b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/.gitignore new file mode 100644 index 000000000..6a7461313 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/.gitignore @@ -0,0 +1 @@ +*.dll diff --git a/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.cs b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.cs new file mode 100644 index 000000000..8b8807ec4 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.cs @@ -0,0 +1,20 @@ +using Microsoft.VisualBasic; +using Microsoft.VisualBasic.CompilerServices; + +[StandardModule] +internal sealed class VBCompoundAssign +{ + public static double[] Sum3(int[] v) + { + double[] array = new double[4]; + int num = Information.UBound(v); + checked { + for (int i = 0; i <= num; i += 3) { + array[0] += v[i]; + array[1] += v[i + 1]; + array[2] += v[i + 2]; + } + return array; + } + } +} diff --git a/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.vb b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.vb new file mode 100644 index 000000000..2dc0c7d96 --- /dev/null +++ b/ICSharpCode.Decompiler.Tests/TestCases/VBPretty/VBCompoundAssign.vb @@ -0,0 +1,14 @@ +Imports System +Imports Microsoft.VisualBasic + +Module VBCompoundAssign + Function Sum3(v As Int32()) As Double() + Dim arr(3) As Double + For i = 0 To UBound(v) Step 3 + arr(0) += v(i) + arr(1) += v(i + 1) + arr(2) += v(i + 2) + Next + Return arr + End Function +End Module diff --git a/ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs index e8d1d8071..8cf1c28c0 100644 --- a/ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/VBPrettyTestRunner.cs @@ -61,17 +61,27 @@ namespace ICSharpCode.Decompiler.Tests }; [Test, Ignore("Implement VB async/await")] - public void Async([ValueSource("defaultOptions")] CompilerOptions options) + public void Async([ValueSource(nameof(defaultOptions))] CompilerOptions options) { Run(options: options); } + [Test] // TODO: legacy VB compound assign + public void VBCompoundAssign([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions options) + { + Run(options: options | CompilerOptions.Library); + } + void Run([CallerMemberName] string testName = null, CompilerOptions options = CompilerOptions.UseDebug, DecompilerSettings settings = null) { var vbFile = Path.Combine(TestCasePath, testName + ".vb"); var csFile = Path.Combine(TestCasePath, testName + ".cs"); + var exeFile = Path.Combine(TestCasePath, testName) + Tester.GetSuffix(options) + ".exe"; + if (options.HasFlag(CompilerOptions.Library)) { + exeFile = Path.ChangeExtension(exeFile, ".dll"); + } - var executable = Tester.CompileVB(vbFile, options); + var executable = Tester.CompileVB(vbFile, options | CompilerOptions.ReferenceVisualBasic, exeFile); var decompiled = Tester.DecompileCSharp(executable.PathToAssembly, settings); CodeAssert.FilesAreEqual(csFile, decompiled); diff --git a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs index 4f7b59d73..f3f642562 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs @@ -404,7 +404,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms } break; } - + if (inlinedExpression.ResultType == StackType.Ref) { + // VB likes to use ref locals for compound assignment + // (the C# compiler uses ref stack slots instead). + // We want to avoid unnecessary ref locals, so we'll always inline them if possible. + return true; + } + var parent = loadInst.Parent; if (NullableLiftingTransform.MatchNullableCtor(parent, out _, out _)) { // inline into nullable ctor call in lifted operator From 1e3e8fdac161d2a7f0eb06417054fdd86bb726ec Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 30 Jul 2019 00:43:48 +0200 Subject: [PATCH 139/221] Allow ref-locals to be initialized from nested field addresses. --- .../IL/Transforms/SplitVariables.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs b/ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs index 4dcf0720a..850f50fc1 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SplitVariables.cs @@ -109,7 +109,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms // Address stored in local variable: also check all uses of that variable. if (!(stloc.Variable.Kind == VariableKind.StackSlot || stloc.Variable.Kind == VariableKind.Local)) return AddressUse.Unknown; - if (stloc.Value.OpCode != OpCode.LdLoca) { + var value = stloc.Value; + while (value is LdFlda ldFlda) { + value = ldFlda.Target; + } + if (value.OpCode != OpCode.LdLoca) { // GroupStores.HandleLoad() only detects ref-locals when they are directly initialized with ldloca return AddressUse.Unknown; } @@ -162,7 +166,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms return null; // only single-definition variables can be supported ref locals var store = ldloc.Variable.StoreInstructions.SingleOrDefault(); if (store is StLoc stloc) { - return stloc.Value as LdLoca; + var value = stloc.Value; + while (value is LdFlda ldFlda) { + value = ldFlda.Target; + } + return value as LdLoca; } return null; } From 50c4fa1c1313dcb59fa4eea16851d62793d81f9e Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 30 Jul 2019 00:44:42 +0200 Subject: [PATCH 140/221] Add two more patterns with throw expressions. --- .../IL/Transforms/NullCoalescingTransform.cs | 130 ++++++++++++++++-- 1 file changed, 117 insertions(+), 13 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs index 7734099ac..aedf1038b 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs @@ -36,7 +36,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms public void Run(Block block, int pos, StatementTransformContext context) { if (!TransformRefTypes(block, pos, context)) { - TransformThrowExpressionValueTypes(block, pos, context); + if (!TransformThrowExpressionValueTypes(block, pos, context)) { + TransformThrowExpressionOnAddress(block, pos, context); + } } } @@ -102,14 +104,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } + delegate bool PatternMatcher(ILInstruction input, out ILInstruction output); + /// /// stloc v(value) - /// if (logic.not(call get_HasValue(ldloca v))) { - /// throw(...) - /// } + /// if (logic.not(call get_HasValue(ldloca v))) throw(...) /// ... Call(arg1, arg2, call GetValueOrDefault(ldloca v), arg4) ... /// => /// ... Call(arg1, arg2, if.notnull(value, throw(...)), arg4) ... + /// + /// -or- + /// + /// stloc v(value) + /// stloc s(ldloca v) + /// if (logic.not(call get_HasValue(ldloc s))) throw(...) + /// ... Call(arg1, arg2, call GetValueOrDefault(ldloc s), arg4) ... + /// => + /// ... Call(arg1, arg2, if.notnull(value, throw(...)), arg4) ... /// bool TransformThrowExpressionValueTypes(Block block, int pos, StatementTransformContext context) { @@ -117,30 +128,114 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!(block.Instructions[pos] is StLoc stloc)) return false; - if (!block.Instructions[pos + 1].MatchIfInstruction(out var condition, out var trueInst)) + int offset = 1; + ILVariable v = stloc.Variable; + // alternative pattern using stack slot containing address of v + if (block.Instructions[pos + offset] is StLoc addrCopyStore + && addrCopyStore.Variable.Kind == VariableKind.StackSlot + && addrCopyStore.Value.MatchLdLoca(v) + && pos + 3 < block.Instructions.Count) + { + offset++; + // v turns into s in the pattern above. + v = addrCopyStore.Variable; + if (!(v.StoreCount == 1 && v.LoadCount == 2 && v.AddressCount == 0)) + return false; + } else { + if (!(v.StoreCount == 1 && v.LoadCount == 0 && v.AddressCount == 2)) + return false; + } + if (!block.Instructions[pos + offset].MatchIfInstruction(out var condition, out var trueInst)) return false; if (!(Block.Unwrap(trueInst) is Throw throwInst)) return false; - ILVariable v = stloc.Variable; - if (!(v.StoreCount == 1 && v.LoadCount == 0 && v.AddressCount == 2)) + if (!condition.MatchLogicNot(out var arg)) return false; - if (!NullableLiftingTransform.MatchNegatedHasValueCall(condition, v)) + if (!MatchNullableCall(arg, NullableLiftingTransform.MatchHasValueCall)) return false; var throwInstParent = throwInst.Parent; var throwInstChildIndex = throwInst.ChildIndex; - var nullcoalescingWithThrow = new NullCoalescingInstruction( + var nullCoalescingWithThrow = new NullCoalescingInstruction( NullCoalescingKind.NullableWithValueFallback, stloc.Value, throwInst); var resultType = NullableType.GetUnderlyingType(v.Type).GetStackType(); - nullcoalescingWithThrow.UnderlyingResultType = resultType; - var result = ILInlining.FindLoadInNext(block.Instructions[pos + 2], v, nullcoalescingWithThrow, InliningOptions.None); + nullCoalescingWithThrow.UnderlyingResultType = resultType; + var result = ILInlining.FindLoadInNext(block.Instructions[pos + offset + 1], v, nullCoalescingWithThrow, InliningOptions.None); if (result.Type == ILInlining.FindResultType.Found - && NullableLiftingTransform.MatchGetValueOrDefault(result.LoadInst.Parent, v)) + && MatchNullableCall(result.LoadInst.Parent, NullableLiftingTransform.MatchGetValueOrDefault)) { context.Step("NullCoalescingTransform (value types + throw expression)", stloc); throwInst.resultType = resultType; - result.LoadInst.Parent.ReplaceWith(nullcoalescingWithThrow); + result.LoadInst.Parent.ReplaceWith(nullCoalescingWithThrow); + block.Instructions.RemoveRange(pos, offset + 1); // remove store(s) and if instruction + return true; + } else { + // reset the primary position (see remarks on ILInstruction.Parent) + stloc.Value = stloc.Value; + var children = throwInstParent.Children; + children[throwInstChildIndex] = throwInst; + return false; + } + + bool MatchNullableCall(ILInstruction input, PatternMatcher matcher) + { + if (!matcher(input, out var loadInst)) + return false; + if (offset == 1) { // Pattern 1 + if (!loadInst.MatchLdLoca(v)) + return false; + } else { + if (!loadInst.MatchLdLoc(v)) + return false; + } + return true; + } + } + + /// + /// stloc s(addressOfValue) + /// if (logic.not(call get_HasValue(ldloc s))) throw(...) + /// ... Call(arg1, arg2, call GetValueOrDefault(ldloc s), arg4) ... + /// => + /// ... Call(arg1, arg2, if.notnull(value, throw(...)), arg4) ... + /// + bool TransformThrowExpressionOnAddress(Block block, int pos, StatementTransformContext context) + { + if (pos + 2 >= block.Instructions.Count) + return false; + if (!(block.Instructions[pos] is StLoc stloc)) + return false; + var s = stloc.Variable; + if (s.Kind != VariableKind.StackSlot) + return false; + if (!(s.StoreCount == 1 && s.LoadCount == 2 && s.AddressCount == 0)) + return false; + if (!(s.Type is ByReferenceType byRef && byRef.ElementType.IsReferenceType == false)) + return false; + if (!block.Instructions[pos + 1].MatchIfInstruction(out var condition, out var trueInst)) + return false; + if (!(Block.Unwrap(trueInst) is Throw throwInst)) + return false; + if (!condition.MatchLogicNot(out var arg)) + return false; + if (!MatchNullableCall(arg, NullableLiftingTransform.MatchHasValueCall)) + return false; + var throwInstParent = throwInst.Parent; + var throwInstChildIndex = throwInst.ChildIndex; + var ldobj = new LdObj(stloc.Value, byRef.ElementType); + var nullCoalescingWithThrow = new NullCoalescingInstruction( + NullCoalescingKind.NullableWithValueFallback, + ldobj, + throwInst); + var resultType = NullableType.GetUnderlyingType(byRef.ElementType).GetStackType(); + nullCoalescingWithThrow.UnderlyingResultType = resultType; + var result = ILInlining.FindLoadInNext(block.Instructions[pos + 2], s, nullCoalescingWithThrow, InliningOptions.None); + if (result.Type == ILInlining.FindResultType.Found + && MatchNullableCall(result.LoadInst.Parent, NullableLiftingTransform.MatchGetValueOrDefault)) { + context.Step("NullCoalescingTransform (address + throw expression)", stloc); + throwInst.resultType = resultType; + result.LoadInst.Parent.ReplaceWith(nullCoalescingWithThrow); block.Instructions.RemoveRange(pos, 2); // remove store and if instruction return true; } else { @@ -150,6 +245,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms children[throwInstChildIndex] = throwInst; return false; } + + bool MatchNullableCall(ILInstruction input, PatternMatcher matcher) + { + if (!matcher(input, out var loadInst)) + return false; + if (!loadInst.MatchLdLoc(s)) + return false; + return true; + } } } } From ce04964206769b9ef6912b817d8ba4c33d23e786 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 30 Jul 2019 00:45:03 +0200 Subject: [PATCH 141/221] Reformat ThrowExpressions tests. --- .../TestCases/Pretty/ThrowExpressions.cs | 102 +++++++++++++----- 1 file changed, 74 insertions(+), 28 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ThrowExpressions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ThrowExpressions.cs index 89742681d..df8e1f41c 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ThrowExpressions.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ThrowExpressions.cs @@ -14,8 +14,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public ArgumentCheckingCtor(object simpleObj, int? nullableInt) { - this.simpleObj = simpleObj ?? throw new ArgumentNullException("simpleObj"); - this.nullableInt = nullableInt ?? throw new ArgumentNullException("nullableInt"); + this.simpleObj = (simpleObj ?? throw new ArgumentNullException("simpleObj")); + this.nullableInt = (nullableInt ?? throw new ArgumentNullException("nullableInt")); } public ArgumentCheckingCtor(string input) @@ -32,8 +32,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty private static int? GetIntOrNull(string v) { - if (int.TryParse(v, out int result)) + if (int.TryParse(v, out int result)) { return result; + } + return null; } @@ -64,10 +66,20 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public int? NullableIntField; public Data DataField; public Data? NullableDataField; - public int IntProperty { get; set; } - public int? NullableIntProperty { get; set; } - public Data DataProperty { get; } - public Data? NullableDataProperty { get; } + public int IntProperty { + get; + set; + } + public int? NullableIntProperty { + get; + set; + } + public Data DataProperty { + get; + } + public Data? NullableDataProperty { + get; + } } public struct Data @@ -76,18 +88,34 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public int? NullableIntField; public MoreData DataField; public MoreData? NullableDataField; - public int IntProperty { get; set; } - public int? NullableIntProperty { get; set; } - public MoreData DataProperty { get; } - public MoreData? NullableDataProperty { get; } + public int IntProperty { + get; + set; + } + public int? NullableIntProperty { + get; + set; + } + public MoreData DataProperty { + get; + } + public MoreData? NullableDataProperty { + get; + } } public struct MoreData { public int IntField; public int? NullableIntField; - public int IntProperty { get; set; } - public int? NullableIntProperty { get; set; } + public int IntProperty { + get; + set; + } + public int? NullableIntProperty { + get; + set; + } } public static int IntField; @@ -100,15 +128,33 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public Data? NullableDataField; public DataObject DataObjectField; - public static int IntProperty { get; } - public static int? NullableIntProperty { get; } - public static object ObjProperty { get; } - public int InstIntProperty { get; } - public int? InstNullableIntProperty { get; } - public object InstObjProperty { get; } - public Data DataProperty { get; } - public Data? NullableDataProperty { get; } - public DataObject DataObjectProperty { get; } + public static int IntProperty { + get; + } + public static int? NullableIntProperty { + get; + } + public static object ObjProperty { + get; + } + public int InstIntProperty { + get; + } + public int? InstNullableIntProperty { + get; + } + public object InstObjProperty { + get; + } + public Data DataProperty { + get; + } + public Data? NullableDataProperty { + get; + } + public DataObject DataObjectProperty { + get; + } public static int ReturnIntField() { @@ -146,17 +192,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public static void UseComplexNullableStruct(ThrowExpressions inst) { Use(inst.InstNullableIntField ?? throw new Exception()); - Use(inst.NullableDataField?.IntField ?? throw new Exception()); + Use((inst.NullableDataField ?? throw new Exception()).IntField); Use(inst.NullableDataField?.NullableIntField ?? throw new Exception()); - Use(inst.NullableDataProperty?.IntField ?? throw new Exception()); + Use((inst.NullableDataProperty ?? throw new Exception()).IntField); Use(inst.NullableDataProperty?.NullableIntField ?? throw new Exception()); - Use(inst.NullableDataField?.DataField.IntField ?? throw new Exception()); + Use((inst.NullableDataField ?? throw new Exception()).DataField.IntField); Use(inst.NullableDataField?.DataField.NullableIntField ?? throw new Exception()); - Use(inst.NullableDataProperty?.DataField.IntField ?? throw new Exception()); + Use((inst.NullableDataProperty ?? throw new Exception()).DataField.IntField); Use(inst.NullableDataProperty?.DataField.NullableIntField ?? throw new Exception()); - Use(inst.NullableDataField?.DataProperty.IntField ?? throw new Exception()); + Use((inst.NullableDataField ?? throw new Exception()).DataProperty.IntField); Use(inst.NullableDataField?.DataProperty.NullableIntField ?? throw new Exception()); - Use(inst.NullableDataProperty?.DataProperty.IntField ?? throw new Exception()); + Use((inst.NullableDataProperty ?? throw new Exception()).DataProperty.IntField); Use(inst.NullableDataProperty?.DataProperty.NullableIntField ?? throw new Exception()); Use(inst.NullableDataField?.NullableDataField?.IntField ?? throw new Exception()); Use(inst.NullableDataField?.NullableDataField?.NullableIntField ?? throw new Exception()); From efb17882c3ade1bc0f30828c482c9703a3dadacb Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 30 Jul 2019 09:55:49 +0200 Subject: [PATCH 142/221] Update ILSpy-tests --- ILSpy-tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ILSpy-tests b/ILSpy-tests index 4d29b27bd..aa8f1197e 160000 --- a/ILSpy-tests +++ b/ILSpy-tests @@ -1 +1 @@ -Subproject commit 4d29b27bd7262790efe52edfcb9c100bb62a0382 +Subproject commit aa8f1197e6a513bcc10bcc38ec7d2143d27a2246 From 98406c64de16935e32691bf5c884b6d5a7bcd532 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Tue, 30 Jul 2019 14:54:23 +0200 Subject: [PATCH 143/221] Use AE 6 preview to verify API compat (as well as runtime functionality) --- ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj | 2 +- ILSpy/ILSpy.csproj | 2 +- ILSpy/Properties/app.config.template | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj b/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj index 87269f706..92f6ef0ff 100644 --- a/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj +++ b/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj @@ -37,7 +37,7 @@ - + diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 24f0d0b59..2338940e6 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -48,7 +48,7 @@ - + diff --git a/ILSpy/Properties/app.config.template b/ILSpy/Properties/app.config.template index f12271be6..58183137f 100644 --- a/ILSpy/Properties/app.config.template +++ b/ILSpy/Properties/app.config.template @@ -14,7 +14,7 @@ - + From 2e6a4988d395fc9859baad987e5aeea9d1a203da Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 3 Aug 2019 18:09:09 +0200 Subject: [PATCH 144/221] Fix #1594: Use Equals to compare generic IMethods. --- ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 814e4b35f..fefd35111 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -212,7 +212,7 @@ namespace ICSharpCode.Decompiler.CSharp Debug.Assert(method.IsLocalFunction); method = method.ReducedFrom; foreach (var parent in currentFunction.Ancestors.OfType()) { - var definition = parent.LocalFunctions.FirstOrDefault(f => f.Method == method); + var definition = parent.LocalFunctions.FirstOrDefault(f => f.Method.Equals(method)); if (definition != null) { return definition; } From e4b15256ababf09b6bcf18fd04705de5aef7d771 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 3 Aug 2019 20:48:56 +0200 Subject: [PATCH 145/221] #1025: Fix method groups not being clickable. --- ICSharpCode.Decompiler/CSharp/Annotations.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/CSharp/Annotations.cs b/ICSharpCode.Decompiler/CSharp/Annotations.cs index ea813e554..28edfe0bb 100644 --- a/ICSharpCode.Decompiler/CSharp/Annotations.cs +++ b/ICSharpCode.Decompiler/CSharp/Annotations.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Linq; +using ICSharpCode.Decompiler.CSharp.Resolver; using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Semantics; @@ -104,7 +105,17 @@ namespace ICSharpCode.Decompiler.CSharp public static ISymbol GetSymbol(this AstNode node) { var rr = node.Annotation(); - return rr != null ? rr.GetSymbol() : null; + if (rr is MethodGroupResolveResult) { + // delegate construction? + var newObj = node.Annotation(); + var funcptr = newObj?.Arguments.ElementAtOrDefault(1); + if (funcptr is LdFtn ldftn) { + return ldftn.Method; + } else if (funcptr is LdVirtFtn ldVirtFtn) { + return ldVirtFtn.Method; + } + } + return rr?.GetSymbol(); } /// From c0f954aaa6e6b495bcc882e226d93dbad4643b21 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 3 Aug 2019 20:52:31 +0200 Subject: [PATCH 146/221] Simplify nullable value types throw expression pattern. --- .../IL/Transforms/NullCoalescingTransform.cs | 119 +++--------------- 1 file changed, 20 insertions(+), 99 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs index aedf1038b..26cfd8b55 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs @@ -36,9 +36,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms public void Run(Block block, int pos, StatementTransformContext context) { if (!TransformRefTypes(block, pos, context)) { - if (!TransformThrowExpressionValueTypes(block, pos, context)) { - TransformThrowExpressionOnAddress(block, pos, context); - } + TransformThrowExpressionValueTypes(block, pos, context); } } @@ -112,15 +110,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// ... Call(arg1, arg2, call GetValueOrDefault(ldloca v), arg4) ... /// => /// ... Call(arg1, arg2, if.notnull(value, throw(...)), arg4) ... - /// - /// -or- - /// - /// stloc v(value) - /// stloc s(ldloca v) - /// if (logic.not(call get_HasValue(ldloc s))) throw(...) - /// ... Call(arg1, arg2, call GetValueOrDefault(ldloc s), arg4) ... - /// => - /// ... Call(arg1, arg2, if.notnull(value, throw(...)), arg4) ... /// bool TransformThrowExpressionValueTypes(Block block, int pos, StatementTransformContext context) { @@ -128,47 +117,42 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!(block.Instructions[pos] is StLoc stloc)) return false; - int offset = 1; + if (!block.Instructions[pos + 1].MatchIfInstruction(out var condition, out var trueInst)) + return false; + if (!(Block.Unwrap(trueInst) is Throw throwInst)) + return false; + if (!condition.MatchLogicNot(out var arg)) + return false; + if (!(arg is CallInstruction call && NullableLiftingTransform.MatchHasValueCall(call, out ILInstruction target))) + return false; ILVariable v = stloc.Variable; - // alternative pattern using stack slot containing address of v - if (block.Instructions[pos + offset] is StLoc addrCopyStore - && addrCopyStore.Variable.Kind == VariableKind.StackSlot - && addrCopyStore.Value.MatchLdLoca(v) - && pos + 3 < block.Instructions.Count) - { - offset++; - // v turns into s in the pattern above. - v = addrCopyStore.Variable; + if (v.Type is ByReferenceType byRefType && byRefType.ElementType.IsReferenceType == false) { if (!(v.StoreCount == 1 && v.LoadCount == 2 && v.AddressCount == 0)) return false; + if (!target.MatchLdLoc(v)) + return false; } else { if (!(v.StoreCount == 1 && v.LoadCount == 0 && v.AddressCount == 2)) return false; + if (!target.MatchLdLoca(v)) + return false; } - if (!block.Instructions[pos + offset].MatchIfInstruction(out var condition, out var trueInst)) - return false; - if (!(Block.Unwrap(trueInst) is Throw throwInst)) - return false; - if (!condition.MatchLogicNot(out var arg)) - return false; - if (!MatchNullableCall(arg, NullableLiftingTransform.MatchHasValueCall)) - return false; var throwInstParent = throwInst.Parent; var throwInstChildIndex = throwInst.ChildIndex; var nullCoalescingWithThrow = new NullCoalescingInstruction( NullCoalescingKind.NullableWithValueFallback, stloc.Value, throwInst); - var resultType = NullableType.GetUnderlyingType(v.Type).GetStackType(); + var resultType = NullableType.GetUnderlyingType(call.Method.DeclaringType).GetStackType(); nullCoalescingWithThrow.UnderlyingResultType = resultType; - var result = ILInlining.FindLoadInNext(block.Instructions[pos + offset + 1], v, nullCoalescingWithThrow, InliningOptions.None); + var result = ILInlining.FindLoadInNext(block.Instructions[pos + 2], v, nullCoalescingWithThrow, InliningOptions.None); if (result.Type == ILInlining.FindResultType.Found && MatchNullableCall(result.LoadInst.Parent, NullableLiftingTransform.MatchGetValueOrDefault)) { context.Step("NullCoalescingTransform (value types + throw expression)", stloc); throwInst.resultType = resultType; result.LoadInst.Parent.ReplaceWith(nullCoalescingWithThrow); - block.Instructions.RemoveRange(pos, offset + 1); // remove store(s) and if instruction + block.Instructions.RemoveRange(pos, 2); // remove store(s) and if instruction return true; } else { // reset the primary position (see remarks on ILInstruction.Parent) @@ -182,78 +166,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms { if (!matcher(input, out var loadInst)) return false; - if (offset == 1) { // Pattern 1 - if (!loadInst.MatchLdLoca(v)) + if (v.Type is ByReferenceType) { + if (!loadInst.MatchLdLoc(v)) return false; } else { - if (!loadInst.MatchLdLoc(v)) + if (!loadInst.MatchLdLoca(v)) return false; } return true; } } - - /// - /// stloc s(addressOfValue) - /// if (logic.not(call get_HasValue(ldloc s))) throw(...) - /// ... Call(arg1, arg2, call GetValueOrDefault(ldloc s), arg4) ... - /// => - /// ... Call(arg1, arg2, if.notnull(value, throw(...)), arg4) ... - /// - bool TransformThrowExpressionOnAddress(Block block, int pos, StatementTransformContext context) - { - if (pos + 2 >= block.Instructions.Count) - return false; - if (!(block.Instructions[pos] is StLoc stloc)) - return false; - var s = stloc.Variable; - if (s.Kind != VariableKind.StackSlot) - return false; - if (!(s.StoreCount == 1 && s.LoadCount == 2 && s.AddressCount == 0)) - return false; - if (!(s.Type is ByReferenceType byRef && byRef.ElementType.IsReferenceType == false)) - return false; - if (!block.Instructions[pos + 1].MatchIfInstruction(out var condition, out var trueInst)) - return false; - if (!(Block.Unwrap(trueInst) is Throw throwInst)) - return false; - if (!condition.MatchLogicNot(out var arg)) - return false; - if (!MatchNullableCall(arg, NullableLiftingTransform.MatchHasValueCall)) - return false; - var throwInstParent = throwInst.Parent; - var throwInstChildIndex = throwInst.ChildIndex; - var ldobj = new LdObj(stloc.Value, byRef.ElementType); - var nullCoalescingWithThrow = new NullCoalescingInstruction( - NullCoalescingKind.NullableWithValueFallback, - ldobj, - throwInst); - var resultType = NullableType.GetUnderlyingType(byRef.ElementType).GetStackType(); - nullCoalescingWithThrow.UnderlyingResultType = resultType; - var result = ILInlining.FindLoadInNext(block.Instructions[pos + 2], s, nullCoalescingWithThrow, InliningOptions.None); - if (result.Type == ILInlining.FindResultType.Found - && MatchNullableCall(result.LoadInst.Parent, NullableLiftingTransform.MatchGetValueOrDefault)) { - context.Step("NullCoalescingTransform (address + throw expression)", stloc); - throwInst.resultType = resultType; - result.LoadInst.Parent.ReplaceWith(nullCoalescingWithThrow); - block.Instructions.RemoveRange(pos, 2); // remove store and if instruction - return true; - } else { - // reset the primary position (see remarks on ILInstruction.Parent) - stloc.Value = stloc.Value; - var children = throwInstParent.Children; - children[throwInstChildIndex] = throwInst; - return false; - } - - bool MatchNullableCall(ILInstruction input, PatternMatcher matcher) - { - if (!matcher(input, out var loadInst)) - return false; - if (!loadInst.MatchLdLoc(s)) - return false; - return true; - } - } } } From fd24b5ea2675c9ac45261b97021bbb668b6d6899 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 3 Aug 2019 21:08:21 +0200 Subject: [PATCH 147/221] Simplify check for addresses --- .../IL/Transforms/NullCoalescingTransform.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs index 26cfd8b55..a9febd138 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs @@ -126,7 +126,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (!(arg is CallInstruction call && NullableLiftingTransform.MatchHasValueCall(call, out ILInstruction target))) return false; ILVariable v = stloc.Variable; - if (v.Type is ByReferenceType byRefType && byRefType.ElementType.IsReferenceType == false) { + if (v.StackType == StackType.Ref) { if (!(v.StoreCount == 1 && v.LoadCount == 2 && v.AddressCount == 0)) return false; if (!target.MatchLdLoc(v)) @@ -166,7 +166,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms { if (!matcher(input, out var loadInst)) return false; - if (v.Type is ByReferenceType) { + if (v.StackType == StackType.Ref) { if (!loadInst.MatchLdLoc(v)) return false; } else { From 65f9fe1076e631f9bb0e8f93a236751431678ac7 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 3 Aug 2019 21:21:24 +0200 Subject: [PATCH 148/221] Remove unused code. --- .../IL/Transforms/NullCoalescingTransform.cs | 35 +++---------------- 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs index a9febd138..2a0780636 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/NullCoalescingTransform.cs @@ -102,8 +102,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } - delegate bool PatternMatcher(ILInstruction input, out ILInstruction output); - /// /// stloc v(value) /// if (logic.not(call get_HasValue(ldloca v))) throw(...) @@ -117,26 +115,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!(block.Instructions[pos] is StLoc stloc)) return false; + ILVariable v = stloc.Variable; + if (!(v.StoreCount == 1 && v.LoadCount == 0 && v.AddressCount == 2)) + return false; if (!block.Instructions[pos + 1].MatchIfInstruction(out var condition, out var trueInst)) return false; if (!(Block.Unwrap(trueInst) is Throw throwInst)) return false; if (!condition.MatchLogicNot(out var arg)) return false; - if (!(arg is CallInstruction call && NullableLiftingTransform.MatchHasValueCall(call, out ILInstruction target))) + if (!(arg is CallInstruction call && NullableLiftingTransform.MatchHasValueCall(call, v))) return false; - ILVariable v = stloc.Variable; - if (v.StackType == StackType.Ref) { - if (!(v.StoreCount == 1 && v.LoadCount == 2 && v.AddressCount == 0)) - return false; - if (!target.MatchLdLoc(v)) - return false; - } else { - if (!(v.StoreCount == 1 && v.LoadCount == 0 && v.AddressCount == 2)) - return false; - if (!target.MatchLdLoca(v)) - return false; - } var throwInstParent = throwInst.Parent; var throwInstChildIndex = throwInst.ChildIndex; var nullCoalescingWithThrow = new NullCoalescingInstruction( @@ -147,7 +136,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms nullCoalescingWithThrow.UnderlyingResultType = resultType; var result = ILInlining.FindLoadInNext(block.Instructions[pos + 2], v, nullCoalescingWithThrow, InliningOptions.None); if (result.Type == ILInlining.FindResultType.Found - && MatchNullableCall(result.LoadInst.Parent, NullableLiftingTransform.MatchGetValueOrDefault)) + && NullableLiftingTransform.MatchGetValueOrDefault(result.LoadInst.Parent, v)) { context.Step("NullCoalescingTransform (value types + throw expression)", stloc); throwInst.resultType = resultType; @@ -161,20 +150,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms children[throwInstChildIndex] = throwInst; return false; } - - bool MatchNullableCall(ILInstruction input, PatternMatcher matcher) - { - if (!matcher(input, out var loadInst)) - return false; - if (v.StackType == StackType.Ref) { - if (!loadInst.MatchLdLoc(v)) - return false; - } else { - if (!loadInst.MatchLdLoca(v)) - return false; - } - return true; - } } } } From ec18094c65fab1eed25d046cf482e20692b007f4 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 3 Aug 2019 21:49:01 +0200 Subject: [PATCH 149/221] Implement transformation of delegate construction with ldvirtftn. --- .../TestCases/Pretty/DelegateConstruction.cs | 5 ++ ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 20 +++-- .../CSharp/ExpressionBuilder.cs | 7 +- ICSharpCode.Decompiler/IL/Instructions.cs | 89 +++++++++++++++++++ ICSharpCode.Decompiler/IL/Instructions.tt | 3 + .../IL/Transforms/ExpressionTransforms.cs | 26 ++++++ 6 files changed, 144 insertions(+), 6 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs index faa0a7d5c..4f3ed04a4 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DelegateConstruction.cs @@ -193,6 +193,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Noop("M3", M3); #endif } + public void Test2() + { + Noop("M3.new", new BaseClass().M3); + Noop("M3.new", new SubClass().M3); + } private void Noop(string name, Action _) { diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 020a402b5..9c5ee34c2 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -1232,7 +1232,17 @@ namespace ICSharpCode.Decompiler.CSharp default: throw new ArgumentException($"Unknown instruction type: {func.OpCode}"); } - var invokeMethod = inst.Method.DeclaringType.GetDelegateInvokeMethod(); + return HandleDelegateConstruction(inst.Method.DeclaringType, method, expectedTargetDetails, thisArg, inst); + } + + internal TranslatedExpression Build(LdVirtDelegate inst) + { + return HandleDelegateConstruction(inst.Type, inst.Method, new ExpectedTargetDetails { CallOpCode = OpCode.CallVirt }, inst.Argument, inst); + } + + TranslatedExpression HandleDelegateConstruction(IType delegateType, IMethod method, ExpectedTargetDetails expectedTargetDetails, ILInstruction thisArg, ILInstruction inst) + { + var invokeMethod = delegateType.GetDelegateInvokeMethod(); TranslatedExpression target; IType targetType; bool requireTarget; @@ -1285,7 +1295,7 @@ namespace ICSharpCode.Decompiler.CSharp } } target = expressionBuilder.TranslateTarget(thisArg, - nonVirtualInvocation: func.OpCode == OpCode.LdFtn, + nonVirtualInvocation: expectedTargetDetails.CallOpCode == OpCode.Call, memberStatic: method.IsStatic, memberDeclaringType: method.DeclaringType); requireTarget = expressionBuilder.HidesVariableWithName(method.Name) @@ -1334,12 +1344,12 @@ namespace ICSharpCode.Decompiler.CSharp ide.WithRR(result); targetExpression = ide; } - var oce = new ObjectCreateExpression(expressionBuilder.ConvertType(inst.Method.DeclaringType), targetExpression) + var oce = new ObjectCreateExpression(expressionBuilder.ConvertType(delegateType), targetExpression) .WithILInstruction(inst) .WithRR(new ConversionResolveResult( - inst.Method.DeclaringType, + delegateType, result, - Conversion.MethodGroupConversion(method, func.OpCode == OpCode.LdVirtFtn, false))); + Conversion.MethodGroupConversion(method, expectedTargetDetails.CallOpCode == OpCode.CallVirt, false))); return oce; } diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index fefd35111..0eee44f79 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -347,7 +347,12 @@ namespace ICSharpCode.Decompiler.CSharp } return new CallBuilder(this, typeSystem, settings).Build(inst); } - + + protected internal override TranslatedExpression VisitLdVirtDelegate(LdVirtDelegate inst, TranslationContext context) + { + return new CallBuilder(this, typeSystem, settings).Build(inst); + } + protected internal override TranslatedExpression VisitNewArr(NewArr inst, TranslationContext context) { var dimensions = inst.Indices.Count; diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index cfa1c9f0a..5788e8901 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -135,6 +135,8 @@ namespace ICSharpCode.Decompiler.IL LdFtn, /// Load method pointer LdVirtFtn, + /// Virtual delegate construction + LdVirtDelegate, /// Loads runtime representation of metadata token LdTypeToken, /// Loads runtime representation of metadata token @@ -3081,6 +3083,66 @@ namespace ICSharpCode.Decompiler.IL } } namespace ICSharpCode.Decompiler.IL +{ + /// Virtual delegate construction + public sealed partial class LdVirtDelegate : UnaryInstruction, IInstructionWithMethodOperand + { + public LdVirtDelegate(ILInstruction argument, IType type, IMethod method) : base(OpCode.LdVirtDelegate, argument) + { + this.type = type; + this.method = method; + } + IType type; + /// Returns the type operand. + public IType Type { + get { return type; } + set { type = value; InvalidateFlags(); } + } + readonly IMethod method; + /// Returns the method operand. + public IMethod Method { get { return method; } } + public override StackType ResultType { get { return StackType.O; } } + protected override InstructionFlags ComputeFlags() + { + return base.ComputeFlags() | InstructionFlags.MayThrow; + } + public override InstructionFlags DirectFlags { + get { + return base.DirectFlags | InstructionFlags.MayThrow; + } + } + public override void WriteTo(ITextOutput output, ILAstWritingOptions options) + { + WriteILRange(output, options); + output.Write(OpCode); + output.Write(' '); + type.WriteTo(output); + output.Write(' '); + method.WriteTo(output); + output.Write('('); + Argument.WriteTo(output, options); + output.Write(')'); + } + public override void AcceptVisitor(ILVisitor visitor) + { + visitor.VisitLdVirtDelegate(this); + } + public override T AcceptVisitor(ILVisitor visitor) + { + return visitor.VisitLdVirtDelegate(this); + } + public override T AcceptVisitor(ILVisitor visitor, C context) + { + return visitor.VisitLdVirtDelegate(this, context); + } + protected internal override bool PerformMatch(ILInstruction other, ref Patterns.Match match) + { + var o = other as LdVirtDelegate; + return o != null && this.Argument.PerformMatch(o.Argument, ref match) && type.Equals(o.type) && method.Equals(o.method); + } + } +} +namespace ICSharpCode.Decompiler.IL { /// Loads runtime representation of metadata token public sealed partial class LdTypeToken : SimpleInstruction @@ -6550,6 +6612,10 @@ namespace ICSharpCode.Decompiler.IL { Default(inst); } + protected internal virtual void VisitLdVirtDelegate(LdVirtDelegate inst) + { + Default(inst); + } protected internal virtual void VisitLdTypeToken(LdTypeToken inst) { Default(inst); @@ -6932,6 +6998,10 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst); } + protected internal virtual T VisitLdVirtDelegate(LdVirtDelegate inst) + { + return Default(inst); + } protected internal virtual T VisitLdTypeToken(LdTypeToken inst) { return Default(inst); @@ -7314,6 +7384,10 @@ namespace ICSharpCode.Decompiler.IL { return Default(inst, context); } + protected internal virtual T VisitLdVirtDelegate(LdVirtDelegate inst, C context) + { + return Default(inst, context); + } protected internal virtual T VisitLdTypeToken(LdTypeToken inst, C context) { return Default(inst, context); @@ -7544,6 +7618,7 @@ namespace ICSharpCode.Decompiler.IL "ldnull", "ldftn", "ldvirtftn", + "ldvirtdelegate", "ldtypetoken", "ldmembertoken", "localloc", @@ -7864,6 +7939,20 @@ namespace ICSharpCode.Decompiler.IL method = default(IMethod); return false; } + public bool MatchLdVirtDelegate(out ILInstruction argument, out IType type, out IMethod method) + { + var inst = this as LdVirtDelegate; + if (inst != null) { + argument = inst.Argument; + type = inst.Type; + method = inst.Method; + return true; + } + argument = default(ILInstruction); + type = default(IType); + method = default(IMethod); + return false; + } public bool MatchLdTypeToken(out IType type) { var inst = this as LdTypeToken; diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index 31407d783..1890424b7 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -217,6 +217,9 @@ CustomClassName("LdFtn"), NoArguments, HasMethodOperand, ResultType("I")), new OpCode("ldvirtftn", "Load method pointer", CustomClassName("LdVirtFtn"), Unary, HasMethodOperand, MayThrow, ResultType("I")), + new OpCode("ldvirtdelegate", "Virtual delegate construction", + CustomClassName("LdVirtDelegate"), Unary, HasTypeOperand, HasMethodOperand, + MayThrow, ResultType("O")), new OpCode("ldtypetoken", "Loads runtime representation of metadata token", CustomClassName("LdTypeToken"), NoArguments, HasTypeOperand, ResultType("O")), new OpCode("ldmembertoken", "Loads runtime representation of metadata token", diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 826cb7f05..b16fb6545 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -297,9 +297,35 @@ namespace ICSharpCode.Decompiler.IL.Transforms inst.ReplaceWith(block); return; } + if (TransformDelegateCtorLdVirtFtnToLdVirtDelegate(inst, out LdVirtDelegate ldVirtDelegate)) { + context.Step("new Delegate(target, ldvirtftn Method) -> ldvirtdelegate Delegate Method(target)", inst); + inst.ReplaceWith(ldVirtDelegate); + return; + } base.VisitNewObj(inst); } + /// + /// newobj Delegate..ctor(target, ldvirtftn TargetMethod(target)) + /// => + /// ldvirtdelegate System.Delegate TargetMethod(target) + /// + bool TransformDelegateCtorLdVirtFtnToLdVirtDelegate(NewObj inst, out LdVirtDelegate ldVirtDelegate) + { + ldVirtDelegate = null; + if (inst.Method.DeclaringType.Kind != TypeKind.Delegate) + return false; + if (inst.Arguments.Count != 2) + return false; + if (!(inst.Arguments[1] is LdVirtFtn ldVirtFtn)) + return false; + if (!inst.Arguments[0].Match(ldVirtFtn.Argument).Success) + return false; + ldVirtDelegate = new LdVirtDelegate(inst.Arguments[0], inst.Method.DeclaringType, ldVirtFtn.Method) + .WithILRange(inst).WithILRange(ldVirtFtn).WithILRange(ldVirtFtn.Argument); + return true; + } + /// /// newobj Span..ctor(localloc(conv i4->u <zero extend>(ldc.i4 sizeInBytes)), numberOfElementsExpr) /// => From c9d2e4f7286a7b0080c2ecf88b4a9247eef1e391 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 4 Aug 2019 11:02:01 +0200 Subject: [PATCH 150/221] Fix #1599: Ignore duplicate keys in resource dictionary. --- .../Rewrite/MarkupExtensionRewritePass.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/ILSpy.BamlDecompiler/Rewrite/MarkupExtensionRewritePass.cs b/ILSpy.BamlDecompiler/Rewrite/MarkupExtensionRewritePass.cs index e2411672f..bd2bf8e31 100644 --- a/ILSpy.BamlDecompiler/Rewrite/MarkupExtensionRewritePass.cs +++ b/ILSpy.BamlDecompiler/Rewrite/MarkupExtensionRewritePass.cs @@ -77,14 +77,16 @@ namespace ILSpy.BamlDecompiler.Rewrite { var attrName = elem.Name; if (attrName != key) attrName = property.ToXName(ctx, parent, property.IsAttachedTo(type)); - var attr = new XAttribute(attrName, extValue); - var list = new List(parent.Attributes()); - if (attrName == key) - list.Insert(0, attr); - else - list.Add(attr); - parent.RemoveAttributes(); - parent.ReplaceAttributes(list); + if (!parent.Attributes(attrName).Any()) { + var attr = new XAttribute(attrName, extValue); + var list = new List(parent.Attributes()); + if (attrName == key) + list.Insert(0, attr); + else + list.Add(attr); + parent.RemoveAttributes(); + parent.ReplaceAttributes(list); + } elem.Remove(); return true; From 423e553c6aa8427be1f2d12bac48a88be693f42c Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 4 Aug 2019 11:34:26 +0200 Subject: [PATCH 151/221] Fix #545: Add hyperlink to ctor method on parentheses in attributes --- .../CSharp/Syntax/TypeSystemAstBuilder.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index 901a1149e..2e3009fa3 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -527,12 +527,18 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { Attribute attr = new Attribute(); attr.Type = ConvertAttributeType(attribute.AttributeType); - SimpleType st = attr.Type as SimpleType; - MemberType mt = attr.Type as MemberType; - if (st != null && st.Identifier.EndsWith("Attribute", StringComparison.Ordinal)) { - st.Identifier = st.Identifier.Substring(0, st.Identifier.Length - 9); - } else if (mt != null && mt.MemberName.EndsWith("Attribute", StringComparison.Ordinal)) { - mt.MemberName = mt.MemberName.Substring(0, mt.MemberName.Length - 9); + switch (attr.Type) { + case SimpleType st: + if (st.Identifier.EndsWith("Attribute", StringComparison.Ordinal)) + st.Identifier = st.Identifier.Substring(0, st.Identifier.Length - 9); + break; + case MemberType mt: + if (mt.MemberName.EndsWith("Attribute", StringComparison.Ordinal)) + mt.MemberName = mt.MemberName.Substring(0, mt.MemberName.Length - 9); + break; + } + if (AddResolveResultAnnotations && attribute.Constructor != null) { + attr.AddAnnotation(new MemberResolveResult(null, attribute.Constructor)); } var parameters = attribute.Constructor?.Parameters ?? EmptyList.Instance; for (int i = 0; i < attribute.FixedArguments.Length; i++) { From fc73851bb4f90c09818845982b24aceb4d79e7f8 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 4 Aug 2019 14:12:15 +0200 Subject: [PATCH 152/221] #1610: Slightly more aggressive copy propagation. This helps clean up the mess left behind when stack slots are not eliminated by the normal transforms. We previously didn't do this because aggressive copy propagation could confuse the normal transforms; but this is no longer an issue with the new pass ordering. --- .../IL/Transforms/CopyPropagation.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs b/ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs index d4e867e7f..3ccf25e02 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/CopyPropagation.cs @@ -105,13 +105,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms // Parameters can be copied only if they aren't assigned to (directly or indirectly via ldarga) // note: the initialization by the caller is the first store -> StoreCount must be 1 return v.IsSingleDefinition; - case VariableKind.StackSlot: - case VariableKind.ExceptionStackSlot: - // Variables are be copied only if both they and the target copy variable are generated, - // and if the variable has only a single assignment - return v.IsSingleDefinition && target.Kind == VariableKind.StackSlot; default: - return false; + // Variables can be copied if both are single-definition. + // To avoid removing too many variables, we do this only if the target + // is either a stackslot or a ref local. + Debug.Assert(target.IsSingleDefinition); + return v.IsSingleDefinition && (target.Kind == VariableKind.StackSlot || target.StackType == StackType.Ref); } default: // All instructions without special behavior that target a stack-variable can be copied. From dbd70d1879cc98ea974f52fa00db9fcbb56f5ec1 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 4 Aug 2019 15:48:05 +0200 Subject: [PATCH 153/221] #1025: Extend GetSymbol(this AstNode) to support LdVirtDelegate --- ICSharpCode.Decompiler/CSharp/Annotations.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/Annotations.cs b/ICSharpCode.Decompiler/CSharp/Annotations.cs index 28edfe0bb..dcd3b23eb 100644 --- a/ICSharpCode.Decompiler/CSharp/Annotations.cs +++ b/ICSharpCode.Decompiler/CSharp/Annotations.cs @@ -108,11 +108,17 @@ namespace ICSharpCode.Decompiler.CSharp if (rr is MethodGroupResolveResult) { // delegate construction? var newObj = node.Annotation(); - var funcptr = newObj?.Arguments.ElementAtOrDefault(1); - if (funcptr is LdFtn ldftn) { - return ldftn.Method; - } else if (funcptr is LdVirtFtn ldVirtFtn) { - return ldVirtFtn.Method; + if (newObj != null) { + var funcptr = newObj.Arguments.ElementAtOrDefault(1); + if (funcptr is LdFtn ldftn) { + return ldftn.Method; + } else if (funcptr is LdVirtFtn ldVirtFtn) { + return ldVirtFtn.Method; + } + } + var ldVirtDelegate = node.Annotation(); + if (ldVirtDelegate != null) { + return ldVirtDelegate.Method; } } return rr?.GetSymbol(); From 679623e4c32d9f00503f3c9e3400003bcdccd42a Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 4 Aug 2019 16:21:23 +0200 Subject: [PATCH 154/221] Fix #1609: Decompilation of cached delegate-construction with unknown delegate type. --- ICSharpCode.Decompiler/IL/Instructions.cs | 2 +- ICSharpCode.Decompiler/IL/Instructions.tt | 2 +- .../IL/Transforms/CachedDelegateInitialization.cs | 2 +- .../IL/Transforms/DelegateConstruction.cs | 8 +++++--- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index 81190cd40..10136e36f 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -786,7 +786,7 @@ namespace ICSharpCode.Decompiler.IL clone.CloneVariables(); return clone; } - public override StackType ResultType { get { return StackType.O; } } + public override StackType ResultType { get { return DelegateType?.GetStackType() ?? StackType.O; } } public override void AcceptVisitor(ILVisitor visitor) { visitor.VisitILFunction(this); diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index 18e6bd61e..9067456f0 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -51,7 +51,7 @@ CustomChildren(new [] { new ChildInfo("body"), new ChildInfo("localFunctions") { IsCollection = true, Type = "ILFunction" } - }), CustomConstructor, CustomWriteTo, CustomComputeFlags, CustomVariableName("function"), ResultType("O") + }), CustomConstructor, CustomWriteTo, CustomComputeFlags, CustomVariableName("function"), ResultType("DelegateType?.GetStackType() ?? StackType.O") ), new OpCode("BlockContainer", "A container of IL blocks.", ResultType("this.ExpectedResultType"), CustomConstructor, CustomVariableName("container"), diff --git a/ICSharpCode.Decompiler/IL/Transforms/CachedDelegateInitialization.cs b/ICSharpCode.Decompiler/IL/Transforms/CachedDelegateInitialization.cs index a0e365f92..38ba30608 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/CachedDelegateInitialization.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/CachedDelegateInitialization.cs @@ -68,7 +68,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!storeInst.MatchStsFld(out IField field2, out ILInstruction value) || !field.Equals(field2) || !field.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) return false; - if (!DelegateConstruction.IsDelegateConstruction(value as NewObj, true)) + if (!DelegateConstruction.IsDelegateConstruction(value.UnwrapConv(ConversionKind.Invalid) as NewObj, true)) return false; var nextInstruction = inst.Parent.Children.ElementAtOrDefault(inst.ChildIndex + 1); if (nextInstruction == null) diff --git a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs index 7edfeb1ef..4d723a0b6 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs @@ -62,11 +62,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms internal static bool IsDelegateConstruction(NewObj inst, bool allowTransformed = false) { - if (inst == null || inst.Arguments.Count != 2 || inst.Method.DeclaringType.Kind != TypeKind.Delegate) + if (inst == null || inst.Arguments.Count != 2) return false; var opCode = inst.Arguments[1].OpCode; - - return opCode == OpCode.LdFtn || opCode == OpCode.LdVirtFtn || (allowTransformed && opCode == OpCode.ILFunction); + if (!(opCode == OpCode.LdFtn || opCode == OpCode.LdVirtFtn || (allowTransformed && opCode == OpCode.ILFunction))) + return false; + var typeKind = inst.Method.DeclaringType.Kind; + return typeKind == TypeKind.Delegate || typeKind == TypeKind.Unknown; } static bool IsAnonymousMethod(ITypeDefinition decompiledTypeDefinition, IMethod method) From 204ef2261a7ac89c1e5289fd429dde80a76146b0 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 4 Aug 2019 16:44:44 +0200 Subject: [PATCH 155/221] Fix \r\n in resource string. --- ILSpy/Properties/Resources.Designer.cs | 3 ++- ILSpy/Properties/Resources.resx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index 110c68eef..c2c607198 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -304,7 +304,8 @@ namespace ICSharpCode.ILSpy.Properties { } /// - /// Looks up a localized string similar to The directory is not empty. File will be overwritten.\r\nAre you sure you want to continue?. + /// Looks up a localized string similar to The directory is not empty. File will be overwritten. + ///Are you sure you want to continue?. /// public static string AssemblySaveCodeDirectoryNotEmpty { get { diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index 9b898ea00..6ef64ac91 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -736,7 +736,8 @@ Use 'ref' extension methods - The directory is not empty. File will be overwritten.\r\nAre you sure you want to continue? + The directory is not empty. File will be overwritten. +Are you sure you want to continue? Project Directory not empty From 3f996ff2c0e48502e1757cf13191eac0173af33d Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 4 Aug 2019 16:45:24 +0200 Subject: [PATCH 156/221] Fix #1604: async methods that had no 'await' but were capturing 'this' were not decompiled correctly --- .../TestCases/Pretty/Async.cs | 12 ++++++++++ .../IL/ControlFlow/AsyncAwaitDecompiler.cs | 23 ++++++++----------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs index 95c1f2def..9f6b5c58a 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs @@ -26,6 +26,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public class Async { + private int memberField; + public async void SimpleVoidMethod() { Console.WriteLine("Before"); @@ -72,6 +74,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine("No Await"); } + public async Task CapturingThis() + { + await Task.Delay(memberField); + } + + public async Task CapturingThisWithoutAwait() + { + Console.WriteLine(memberField); + } + public async Task SimpleBoolTaskMethod() { Console.WriteLine("Before"); diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs index 211fd6b08..001e97ec6 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs @@ -355,27 +355,22 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow if (blockContainer.EntryPoint.IncomingEdgeCount != 1) throw new SymbolicAnalysisFailedException(); int pos = 0; - if (blockContainer.EntryPoint.Instructions[0].MatchStLoc(out cachedStateVar, out var cachedStateInit)) { - // stloc(cachedState, ldfld(valuetype StateMachineStruct::<>1__state, ldloc(this))) - if (!cachedStateInit.MatchLdFld(out var target, out var loadedField)) - throw new SymbolicAnalysisFailedException(); - if (!target.MatchLdThis()) - throw new SymbolicAnalysisFailedException(); - if (loadedField.MemberDefinition != stateField) - throw new SymbolicAnalysisFailedException(); - ++pos; - } while (blockContainer.EntryPoint.Instructions[pos] is StLoc stloc) { // stloc V_1(ldfld <>4__this(ldloc this)) - if (!stloc.Variable.IsSingleDefinition) - throw new SymbolicAnalysisFailedException(); if (!stloc.Value.MatchLdFld(out var target, out var field)) throw new SymbolicAnalysisFailedException(); if (!target.MatchLdThis()) throw new SymbolicAnalysisFailedException(); - if (!fieldToParameterMap.TryGetValue((IField)field.MemberDefinition, out var param)) + if (field.MemberDefinition == stateField) { + // stloc(cachedState, ldfld(valuetype StateMachineStruct::<>1__state, ldloc(this))) + cachedStateVar = stloc.Variable; + } else if (fieldToParameterMap.TryGetValue((IField)field.MemberDefinition, out var param)) { + if (!stloc.Variable.IsSingleDefinition) + throw new SymbolicAnalysisFailedException(); + cachedFieldToParameterMap[stloc.Variable] = param; + } else { throw new SymbolicAnalysisFailedException(); - cachedFieldToParameterMap[stloc.Variable] = param; + } pos++; } mainTryCatch = blockContainer.EntryPoint.Instructions[pos] as TryCatch; From 5177c56d88c37a29565ded2bc590d2253fd72344 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 4 Aug 2019 18:20:53 +0200 Subject: [PATCH 157/221] Reject MoveNext() methods with more than one cachedStateVar. --- ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs index 001e97ec6..c122cabdd 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs @@ -354,6 +354,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow throw new SymbolicAnalysisFailedException(); if (blockContainer.EntryPoint.IncomingEdgeCount != 1) throw new SymbolicAnalysisFailedException(); + cachedStateVar = null; int pos = 0; while (blockContainer.EntryPoint.Instructions[pos] is StLoc stloc) { // stloc V_1(ldfld <>4__this(ldloc this)) @@ -361,7 +362,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow throw new SymbolicAnalysisFailedException(); if (!target.MatchLdThis()) throw new SymbolicAnalysisFailedException(); - if (field.MemberDefinition == stateField) { + if (field.MemberDefinition == stateField && cachedStateVar == null) { // stloc(cachedState, ldfld(valuetype StateMachineStruct::<>1__state, ldloc(this))) cachedStateVar = stloc.Variable; } else if (fieldToParameterMap.TryGetValue((IField)field.MemberDefinition, out var param)) { From a3dbc27f76253550c46ae238d328cbe0245419a8 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 4 Aug 2019 21:52:28 +0200 Subject: [PATCH 158/221] Fix #1573: if the expected type of the this argument of an expression tree call is unknown, insert an explicit conv *->unknown instruction --- .../IL/Transforms/TransformExpressionTrees.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs index 5d6794bf9..0ea143560 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs @@ -549,6 +549,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms return target; } default: + if (expectedType.Kind == TypeKind.Unknown && target.ResultType != StackType.Unknown) { + return new Conv(target, PrimitiveType.Unknown, false, Sign.None); + } return target; } } From 667ac1a980c997f051fa73e4d537198596e45306 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 4 Aug 2019 21:54:07 +0200 Subject: [PATCH 159/221] Fix #1538: Insert expression.tree.cast instructions to coerce references to outer expression tree parameter definitions to their actual type. --- .../IL/Transforms/TransformExpressionTrees.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs index 0ea143560..dc8c609a1 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs @@ -352,7 +352,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // Replace an already mapped parameter with the actual ILVariable, // we generated earlier. if (parameterMapping.TryGetValue(ldloc.Variable, out var v)) { - if (typeHint.SkipModifiers() is ByReferenceType brt && !v.Type.IsByRefLike) + if (typeHint.SkipModifiers() is ByReferenceType && !v.Type.IsByRefLike) return (new LdLoca(v), typeHint); return (new LdLoc(v), v.Type); } @@ -364,7 +364,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // with references to mapped parameters. if (ldloc.Variable.IsSingleDefinition && ldloc.Variable.StoreInstructions[0] is ILInstruction inst) { if (MatchParameterVariableAssignment(inst, out _, out var type, out _)) - return (ldloc, type); + return (new ExpressionTreeCast(type, ldloc, false), type); } } return (null, SpecialType.UnknownType); From 1531302c8d9250663a5c25e3d66defa90fa268ec Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 5 Aug 2019 09:50:00 +0200 Subject: [PATCH 160/221] Fix #1611: Always remove the initializedObj dummy expression in CallBuilder.BuildDictionaryInitializerExpression --- ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 020a402b5..608e60f0e 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -447,6 +447,9 @@ namespace ICSharpCode.Decompiler.CSharp var assignment = HandleAccessorCall(expectedTargetDetails, method, unused, argumentList.Arguments.ToList(), argumentList.ArgumentNames); + if (((AssignmentExpression)assignment).Left is IndexerExpression indexer) + indexer.Target.ReplaceWith(n => null); + if (value != null) return assignment; From c204b7c410bcfa81ce3b887829496f1e8bd3d7f6 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 5 Aug 2019 10:07:29 +0200 Subject: [PATCH 161/221] Update "How to build" section in README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index de4611456..7815ad7c0 100644 --- a/README.md +++ b/README.md @@ -48,10 +48,10 @@ How to build ------------ Windows: -- Install Visual Studio (minimum version: 2017.7) with the following components: +- Install Visual Studio (minimum version: 2019.2) with the following components: - Workload ".NET Desktop Development" - .NET Framework 4.6.2 Targeting Pack (if the VS installer does not offer this option, install the [.NET 4.6.2 developer pack](https://www.microsoft.com/en-us/download/details.aspx?id=53321) separately) - - Individual Component "VC++ 2017 version 15.9 v14.16 latest v141 tools" (or similar) + - Individual Component "MSVC v142 - VS 2019 C++ x64/x86 build tools (v14.22)" (or similar) - The VC++ toolset is optional; if present it is used for `editbin.exe` to modify the stack size used by ILSpy.exe from 1MB to 16MB, because the decompiler makes heavy use of recursion, where small stack sizes lead to problems in very complex methods. - Install the [.NET Core SDK 2.2](https://dotnet.microsoft.com/download) - Install the [.NET Core SDK 3](https://dotnet.microsoft.com/download/dotnet-core) From 7fb3c513261ddc62e559e4f614087fc826d925e6 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 5 Aug 2019 10:31:25 +0200 Subject: [PATCH 162/221] Suppress warnings in release mode as well, to speed up build process. --- ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 386673716..15072bb65 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -19,6 +19,7 @@ true True ICSharpCode.Decompiler.snk + 1701;1702;1591;1573 @@ -48,10 +49,6 @@ true - - 1701;1702;1591;1573 - - From c148ba6609c13f3d9253f20da9ab6be13321bee6 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 5 Aug 2019 10:39:07 +0200 Subject: [PATCH 163/221] Fix build. --- ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 608e60f0e..60d09015d 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -447,7 +447,7 @@ namespace ICSharpCode.Decompiler.CSharp var assignment = HandleAccessorCall(expectedTargetDetails, method, unused, argumentList.Arguments.ToList(), argumentList.ArgumentNames); - if (((AssignmentExpression)assignment).Left is IndexerExpression indexer) + if (((AssignmentExpression)assignment).Left is IndexerExpression indexer && !indexer.Target.IsNull) indexer.Target.ReplaceWith(n => null); if (value != null) From 0ca5ace52469d39a0c969c33529aa0b1a493384d Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 5 Aug 2019 14:38:26 +0200 Subject: [PATCH 164/221] #1349: Fix assertions triggered by incorrect local function decompilation. --- .../TestCases/Pretty/LocalFunctions.cs | 24 ++++++++++++++++++ .../IL/Transforms/LocalFunctionDecompiler.cs | 25 ++++++++++++++----- .../Transforms/TransformDisplayClassUsage.cs | 5 ++++ 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs index e8c8c1201..581f6eac6 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/LocalFunctions.cs @@ -25,6 +25,30 @@ namespace LocalFunctions { private int field; + private Lazy nonCapturinglocalFunctionInLambda = new Lazy(delegate { + return CreateValue(); + + object CreateValue() + { + return null; + } + }); + + private Lazy capturinglocalFunctionInLambda = new Lazy(delegate { + int x = 42; + return Do(); + + object Do() + { + return CreateValue(); + + int CreateValue() + { + return x; + } + } + }); + private static void Test(int x) { } diff --git a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs index 85753bd9e..1268989f7 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs @@ -91,6 +91,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms DetermineCaptureAndDeclarationScope(localFunction, useSite); TransformToLocalFunctionInvocation(localFunction.ReducedMethod, useSite); } + + if (function.Method.IsConstructor && localFunction.DeclarationScope == null) { + localFunction.DeclarationScope = BlockContainer.FindClosestContainer(useSite); + } } if (localFunction.DeclarationScope == null) { @@ -236,20 +240,28 @@ namespace ICSharpCode.Decompiler.IL.Transforms void DetermineCaptureAndDeclarationScope(ILFunction function, CallInstruction useSite) { int firstArgumentIndex = function.Method.IsStatic ? 0 : 1; - for (int i = useSite.Arguments.Count - 1; i >= 0; i--) { - var arg = useSite.Arguments[i]; + if (firstArgumentIndex > 0) { + HandleArgument(0, useSite.Arguments[0]); + } + for (int i = useSite.Arguments.Count - 1; i >= firstArgumentIndex; i--) { + if (!HandleArgument(i, useSite.Arguments[i])) + break; + } + + bool HandleArgument(int i, ILInstruction arg) + { ILVariable closureVar; if (!(arg.MatchLdLoc(out closureVar) || arg.MatchLdLoca(out closureVar))) - break; + return false; if (closureVar.Kind == VariableKind.NamedArgument) - break; + return false; if (!TransformDisplayClassUsage.IsPotentialClosure(context, UnwrapByRef(closureVar.Type).GetDefinition())) - break; + return false; if (i - firstArgumentIndex >= 0) { Debug.Assert(i - firstArgumentIndex < function.Method.Parameters.Count && IsClosureParameter(function.Method.Parameters[i - firstArgumentIndex], resolveContext)); } if (closureVar.AddressCount == 0 && closureVar.StoreInstructions.Count == 0) - continue; + return true; // determine the capture scope of closureVar and the declaration scope of the function var instructions = closureVar.StoreInstructions.OfType() .Concat(closureVar.AddressInstructions).OrderBy(inst => inst.StartILOffset); @@ -262,6 +274,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms function.DeclarationScope = closureVar.CaptureScope; else function.DeclarationScope = FindCommonAncestorInstruction(function.DeclarationScope, closureVar.CaptureScope); + return true; } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index bc69ff014..b9c28fdac 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -113,6 +113,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms } else { if (displayClass.CaptureScope == null && !localFunctionClosureParameter) displayClass.CaptureScope = isMono && IsMonoNestedCaptureScope(closureType) ? null : v.CaptureScope; + if (displayClass.CaptureScope != null && !localFunctionClosureParameter) { + displayClass.DeclaringFunction = displayClass.CaptureScope.Ancestors.OfType().First(); + } displayClass.Variable = v; displayClass.Initializer = inst; displayClasses.Add(v, displayClass); @@ -297,6 +300,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms instructionsToRemove.Add(inst); value = inst.Value; } else { + context.Step($"Introduce captured variable for {field.FullName}", inst); Debug.Assert(displayClass.Definition == field.DeclaringTypeDefinition); // Introduce a fresh variable for the display class field. if (displayClass.IsMono && displayClass.CaptureScope == null && !IsOuterClosureReference(field)) { @@ -348,6 +352,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return; var field = (IField)inst.Field.MemberDefinition; if (!displayClass.Variables.TryGetValue(field, out DisplayClassVariable info)) { + context.Step($"Introduce captured variable for {field.FullName}", inst); // Introduce a fresh variable for the display class field. Debug.Assert(displayClass.Definition == field.DeclaringTypeDefinition); var v = displayClass.DeclaringFunction.RegisterVariable(VariableKind.Local, field.Type, field.Name); From 6526363cb3f0316e279fecbbd4e16ea932640986 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 5 Aug 2019 15:22:44 +0200 Subject: [PATCH 165/221] #1349: Fix exception in HandleDelegateConstruction, if the target of NewObj is not a delegate. --- ICSharpCode.Decompiler/CSharp/CallBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs index 60d09015d..dadd1b836 100644 --- a/ICSharpCode.Decompiler/CSharp/CallBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/CallBuilder.cs @@ -142,7 +142,7 @@ namespace ICSharpCode.Decompiler.CSharp public TranslatedExpression Build(CallInstruction inst) { - if (inst is NewObj newobj && IL.Transforms.DelegateConstruction.IsDelegateConstruction(newobj, true)) { + if (inst is NewObj newobj && IL.Transforms.DelegateConstruction.IsDelegateConstruction(newobj)) { return HandleDelegateConstruction(newobj); } if (settings.TupleTypes && TupleTransform.MatchTupleConstruction(inst as NewObj, out var tupleElements) && tupleElements.Length >= 2) { From 3ea2ce4e2d28884310222752f295a382b9fc16f1 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 5 Aug 2019 15:21:03 +0200 Subject: [PATCH 166/221] #1349: Fix assertions triggered by incorrect reset implementation in TransformThrowExpressionsValueTypes. ILInstruction.SetChildInstruction should only return early, if index and parent are still the same. --- ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs index 609b31dc7..261cf9b63 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs @@ -628,7 +628,7 @@ namespace ICSharpCode.Decompiler.IL { ILInstruction oldValue = childPointer; Debug.Assert(oldValue == GetChild(index)); - if (oldValue == newValue) + if (oldValue == newValue && newValue?.parent == this && newValue.ChildIndex == index) return; childPointer = newValue; if (newValue != null) { From 0d92ee805c67bf1c8313272b64607d2bc9e21ace Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 5 Aug 2019 17:21:44 +0200 Subject: [PATCH 167/221] #1349: Insert conv instruction in expression tree, if inst.ResultType != typeHint.GetStackType() --- .../IL/Transforms/TransformExpressionTrees.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs index dc8c609a1..a73c11612 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs @@ -238,6 +238,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (result.Item1 != null) { Debug.Assert(result.Item2 != null, "IType must be non-null!"); Debug.Assert(result.Item1.ResultType == result.Item2.GetStackType(), "StackTypes must match!"); + if (typeHint != null) { + var inst = result.Item1; + if (inst.ResultType != typeHint.GetStackType()) { + return (new Conv(inst, typeHint.GetStackType().ToPrimitiveType(), false, typeHint.GetSign()), typeHint); + } + } } return result; From 9358ea6d7381daaae155eb315d84ba1c0b7831b8 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 5 Aug 2019 21:43:04 +0200 Subject: [PATCH 168/221] Fix ReadCodeMappingInfo for delegates that are embedded in the declaring type, not a nested type. --- .../CSharp/CSharpDecompiler.cs | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 2bf09a7bf..994be0c13 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -667,13 +667,22 @@ namespace ICSharpCode.Decompiler.CSharp closureTypeHandle = ExtractDeclaringType(memberRef); if (!closureTypeHandle.IsNil) { var closureType = module.Metadata.GetTypeDefinition(closureTypeHandle); - // Must be a nested type of the containing type. - if (closureType.GetDeclaringType() != declaringType) - break; - if (!processedNestedTypes.Add(closureTypeHandle)) - break; - foreach (var m in closureType.GetMethods()) { - connectedMethods.Enqueue(m); + if (closureTypeHandle != declaringType) { + // Must be a nested type of the containing type. + if (closureType.GetDeclaringType() != declaringType) + break; + if (!processedNestedTypes.Add(closureTypeHandle)) + break; + foreach (var m in closureType.GetMethods()) { + connectedMethods.Enqueue(m); + } + } else { + // Delegate body is declared in the same type + foreach (var m in closureType.GetMethods()) { + var methodDef = module.Metadata.GetMethodDefinition(m); + if (methodDef.Name == memberRef.Name) + connectedMethods.Enqueue(m); + } } break; } From fa7c1f574a50c1628549a437790a9ac42f090823 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 5 Aug 2019 21:49:18 +0200 Subject: [PATCH 169/221] Do not convert simple LINQ method calls to LINQ expressions. Closes #1501. --- .../TestCases/Pretty/ExpressionTrees.cs | 16 +++++++--------- .../TestCases/Pretty/InitializerTests.cs | 4 +--- .../Transforms/IntroduceQueryExpressions.cs | 14 ++++++++++++-- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs index a6bf33359..e05e5bd91 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs @@ -625,16 +625,15 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty //no params ToCode(X(), () => call(() => 42)); //one param - ToCode(X(), () => from x in new int[2] { + ToCode(X(), () => new int[2] { 37, 42 - } - select x * 2); + }.Select((int x) => x * 2)); //two params ToCode(X(), () => new int[2] { - 37, - 42 - }.Select((int x, int i) => x * 2)); + 37, + 42 + }.Select((int x, int i) => x * 2)); } public void CurriedLambda() @@ -731,13 +730,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public void QuotedWithAnonymous() { - ToCode(X(), () => (from o in new[] { + ToCode(X(), () => new[] { new { X = "a", Y = "b" } - } - select o.X + o.Y).Single()); + }.Select(o => o.X + o.Y).Single()); } public void StaticCall() diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs index 693152c83..0aa2ecae5 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs @@ -1468,9 +1468,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests DateTimeFormat = new DateTimeFormatInfo { ShortDatePattern = "ddmmyy" }, - NumberFormat = (from format in source - where format.CurrencySymbol == "$" - select format).First() + NumberFormat = source.Where((NumberFormatInfo format) => format.CurrencySymbol == "$").First() } }); } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs index f1309f429..02faac75e 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/IntroduceQueryExpressions.cs @@ -90,10 +90,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (mre == null || IsNullConditional(mre.Target)) return null; switch (mre.MemberName) { - case "Select": - { + case "Select": { if (invocation.Arguments.Count != 1) return null; + if (!IsComplexQuery(mre)) + return null; ParameterDeclaration parameter; Expression body; if (MatchSimpleLambda(invocation.Arguments.Single(), out parameter, out body)) { @@ -158,6 +159,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms { if (invocation.Arguments.Count != 1) return null; + if (!IsComplexQuery(mre)) + return null; ParameterDeclaration parameter; Expression body; if (MatchSimpleLambda(invocation.Arguments.Single(), out parameter, out body)) { @@ -175,6 +178,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms { if (invocation.Arguments.Count != 1) return null; + if (!IsComplexQuery(mre)) + return null; ParameterDeclaration parameter; Expression orderExpression; if (MatchSimpleLambda(invocation.Arguments.Single(), out parameter, out orderExpression)) { @@ -250,6 +255,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } } + static bool IsComplexQuery(MemberReferenceExpression mre) + { + return ((mre.Target is InvocationExpression && mre.Parent is InvocationExpression) || mre.Parent?.Parent is QueryClause); + } + QueryFromClause MakeFromClause(ParameterDeclaration parameter, Expression body) { QueryFromClause fromClause = new QueryFromClause { From 582b66d5cde2c027c6cde34ef3c3f2e352bebd16 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 5 Aug 2019 21:49:47 +0200 Subject: [PATCH 170/221] Enable parallel execution on PdbGenerationTestRunner. --- ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs b/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs index c13ee99a0..78de11172 100644 --- a/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PdbGenerationTestRunner.cs @@ -18,7 +18,7 @@ using NUnit.Framework; namespace ICSharpCode.Decompiler.Tests { - [TestFixture] + [TestFixture, Parallelizable(ParallelScope.All)] public class PdbGenerationTestRunner { static readonly string TestCasePath = Tester.TestCasePath + "/PdbGen"; From c477ccf68ea818133a3e01b057988d10d789bfca Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 5 Aug 2019 22:01:13 +0200 Subject: [PATCH 171/221] Add SemanticHelper.IsPure check as per code-review. --- ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index b16fb6545..65a61afe4 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -321,6 +321,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!inst.Arguments[0].Match(ldVirtFtn.Argument).Success) return false; + if (!SemanticHelper.IsPure(inst.Arguments[0].Flags)) + return false; ldVirtDelegate = new LdVirtDelegate(inst.Arguments[0], inst.Method.DeclaringType, ldVirtFtn.Method) .WithILRange(inst).WithILRange(ldVirtFtn).WithILRange(ldVirtFtn.Argument); return true; From 18cad9dd737d1ddfdd1d9ca5188c807f6cf168fc Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 5 Aug 2019 21:58:45 +0200 Subject: [PATCH 172/221] Make "Save as solution" logic more similar to "Save as project" logic: * don't propose an initial directory (#1598) * allow overwriting an existing directory --- ILSpy/Commands/SaveCodeContextMenuEntry.cs | 62 +++++++++------------- 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/ILSpy/Commands/SaveCodeContextMenuEntry.cs b/ILSpy/Commands/SaveCodeContextMenuEntry.cs index 8ee945f0d..ef395f265 100644 --- a/ILSpy/Commands/SaveCodeContextMenuEntry.cs +++ b/ILSpy/Commands/SaveCodeContextMenuEntry.cs @@ -62,8 +62,7 @@ namespace ICSharpCode.ILSpy.TextView if (singleSelection.Save(textView)) return; } else if (selectedNodes.Count > 1 && selectedNodes.All(n => n is AssemblyTreeNode)) { - var initialPath = Path.GetDirectoryName(((AssemblyTreeNode)selectedNodes[0]).LoadedAssembly.FileName); - var selectedPath = SelectSolutionFile(initialPath); + var selectedPath = SelectSolutionFile(); if (!string.IsNullOrEmpty(selectedPath)) { var assemblies = selectedNodes.OfType() @@ -87,45 +86,36 @@ namespace ICSharpCode.ILSpy.TextView /// will be used. /// /// The full path of the selected target file, or null if the user canceled. - static string SelectSolutionFile(string path) + static string SelectSolutionFile() { - const string SolutionExtension = ".sln"; - const string DefaultSolutionName = "Solution"; - - if (string.IsNullOrWhiteSpace(path)) { - path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); - } - SaveFileDialog dlg = new SaveFileDialog(); - dlg.InitialDirectory = path; - dlg.FileName = Path.Combine(path, DefaultSolutionName + SolutionExtension); - dlg.Filter = "Visual Studio Solution file|*" + SolutionExtension; + dlg.FileName = "Solution.sln"; + dlg.Filter = "Visual Studio Solution file|*.sln|All files|*.*"; - bool targetInvalid; - do { - if (dlg.ShowDialog() != true) { - return null; - } + if (dlg.ShowDialog() != true) { + return null; + } - string selectedPath = Path.GetDirectoryName(dlg.FileName); - try { - targetInvalid = Directory.EnumerateFileSystemEntries(selectedPath).Any(); - } catch (Exception e) when (e is IOException || e is UnauthorizedAccessException || e is System.Security.SecurityException) { - MessageBox.Show( - "The directory cannot be accessed. Please ensure it exists and you have sufficient rights to access it.", - "Solution directory not accessible", - MessageBoxButton.OK, MessageBoxImage.Error); - targetInvalid = true; - continue; - } + string selectedPath = Path.GetDirectoryName(dlg.FileName); + bool directoryNotEmpty; + try { + directoryNotEmpty = Directory.EnumerateFileSystemEntries(selectedPath).Any(); + } catch (Exception e) when (e is IOException || e is UnauthorizedAccessException || e is System.Security.SecurityException) { + MessageBox.Show( + "The directory cannot be accessed. Please ensure it exists and you have sufficient rights to access it.", + "Solution directory not accessible", + MessageBoxButton.OK, MessageBoxImage.Error); + return null; + } - if (targetInvalid) { - MessageBox.Show( - "The directory is not empty. Please select an empty directory.", - "Solution directory not empty", - MessageBoxButton.OK, MessageBoxImage.Warning); - } - } while (targetInvalid); + if (directoryNotEmpty) { + var result = MessageBox.Show( + Resources.AssemblySaveCodeDirectoryNotEmpty, + Resources.AssemblySaveCodeDirectoryNotEmptyTitle, + MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No); + if (result == MessageBoxResult.No) + return null; // -> abort + } return dlg.FileName; } From 2b9a40371b639ed9c784132a8bfa1731494971e7 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 5 Aug 2019 22:02:14 +0200 Subject: [PATCH 173/221] Remove the dead LoopingTransform. Make ILInstruction.IsDirty debug-only, as it is only used for assertions now. --- .../IL/Instructions/ILInstruction.cs | 31 +++++----- .../IL/Transforms/LoopingTransform.cs | 61 ------------------- 2 files changed, 17 insertions(+), 75 deletions(-) delete mode 100644 ICSharpCode.Decompiler/IL/Transforms/LoopingTransform.cs diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs index 261cf9b63..789634a57 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/ILInstruction.cs @@ -125,32 +125,35 @@ namespace ICSharpCode.Decompiler.IL Debug.Assert(a == b); return a; } - + +#if DEBUG /// /// Gets whether this node (or any subnode) was modified since the last ResetDirty() call. /// /// - /// IsDirty is used by the LoopingTransform, and must not be used by individual transforms within the loop. + /// IsDirty is used by the StatementTransform, and must not be used by individual transforms within the loop. /// - public bool IsDirty { get; private set; } - - protected void MakeDirty() - { - for (ILInstruction inst = this; inst != null && !inst.IsDirty; inst = inst.parent) - inst.IsDirty = true; - } - + internal bool IsDirty { get; private set; } + /// /// Marks this node (and all subnodes) as IsDirty=false. /// - /// - /// IsDirty is used by the LoopingTransform, and must not be used by individual transforms within the loop. - /// - public void ResetDirty() + internal void ResetDirty() { foreach (ILInstruction inst in Descendants) inst.IsDirty = false; } +#endif + + [Conditional("DEBUG")] + protected private void MakeDirty() + { +#if DEBUG + for (ILInstruction inst = this; inst != null && !inst.IsDirty; inst = inst.parent) { + inst.IsDirty = true; + } +#endif + } const InstructionFlags invalidFlags = (InstructionFlags)(-1); diff --git a/ICSharpCode.Decompiler/IL/Transforms/LoopingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/LoopingTransform.cs deleted file mode 100644 index 27f761feb..000000000 --- a/ICSharpCode.Decompiler/IL/Transforms/LoopingTransform.cs +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2016 Siegfried Pammer -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System; -using System.Collections.Generic; - -namespace ICSharpCode.Decompiler.IL.Transforms -{ - /// - /// Repeats the child transforms until the ILAst no longer changes (fixpoint iteration). - /// - public class LoopingBlockTransform : IBlockTransform - { - readonly IBlockTransform[] children; - bool running; - - public LoopingBlockTransform(params IBlockTransform[] children) - { - this.children = children; - } - - public void Run(Block block, BlockTransformContext context) - { - if (running) - throw new InvalidOperationException("LoopingBlockTransform already running. Transforms (and the CSharpDecompiler) are neither thread-safe nor re-entrant."); - running = true; - try { - int count = 1; - do { - block.ResetDirty(); - block.RunTransforms(children, context); - if (block.IsDirty) - context.Step($"Block is dirty; running loop iteration #{++count}.", block); - } while (block.IsDirty); - } finally { - running = false; - } - } - - public IReadOnlyCollection Transforms { - get { return children; } - } - } -} - - From 4bc15e0911337d8f174affaf95c1165022443550 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 5 Aug 2019 22:04:28 +0200 Subject: [PATCH 174/221] Fix build. Visual Studio really ought to save project changes sooner... --- ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 15072bb65..8f17d7ca4 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -441,7 +441,6 @@ - From 72c3e828f34b9d8a3e44d7b76ad8bdc5af806814 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 5 Aug 2019 22:11:09 +0200 Subject: [PATCH 175/221] Check IsPure before Match. --- .../IL/Transforms/ExpressionTransforms.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 65a61afe4..91229cffa 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -319,10 +319,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!(inst.Arguments[1] is LdVirtFtn ldVirtFtn)) return false; - if (!inst.Arguments[0].Match(ldVirtFtn.Argument).Success) - return false; if (!SemanticHelper.IsPure(inst.Arguments[0].Flags)) return false; + if (!inst.Arguments[0].Match(ldVirtFtn.Argument).Success) + return false; ldVirtDelegate = new LdVirtDelegate(inst.Arguments[0], inst.Method.DeclaringType, ldVirtFtn.Method) .WithILRange(inst).WithILRange(ldVirtFtn).WithILRange(ldVirtFtn.Argument); return true; @@ -531,10 +531,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; if (!(dynamicCompoundAssign.Target is DynamicGetMemberInstruction getMember)) return false; - if (!isEvent.Argument.Match(getMember.Target).Success) - return false; if (!SemanticHelper.IsPure(isEvent.Argument.Flags)) return false; + if (!isEvent.Argument.Match(getMember.Target).Success) + return false; if (!(trueInst is DynamicInvokeMemberInstruction invokeMember)) return false; if (!(invokeMember.BinderFlags.HasFlag(CSharpBinderFlags.InvokeSpecialName) && invokeMember.BinderFlags.HasFlag(CSharpBinderFlags.ResultDiscarded))) From 62a004604368b1442f51517284890e37bf3b1b53 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 5 Aug 2019 22:48:49 +0200 Subject: [PATCH 176/221] Fix #1371: assertion on invalid I4->O conversion. --- ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 06c00d1b9..f4420a761 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -1721,6 +1721,10 @@ namespace ICSharpCode.Decompiler.CSharp } else if (inst.TargetType == IL.PrimitiveType.Ref) { // converting to unknown ref-type targetType = new ByReferenceType(compilation.FindType(KnownTypeCode.Byte)); + } else if (inst.TargetType == IL.PrimitiveType.None) { + // convert to some object type + // (e.g. invalid I4->O conversion) + targetType = compilation.FindType(KnownTypeCode.Object); } else { targetType = GetType(inst.TargetType.ToKnownTypeCode()); } @@ -2061,7 +2065,7 @@ namespace ICSharpCode.Decompiler.CSharp protected internal override TranslatedExpression VisitLdLen(LdLen inst, TranslationContext context) { - TranslatedExpression arrayExpr = Translate(inst.Array); + TranslatedExpression arrayExpr = Translate(inst.Array, typeHint: compilation.FindType(KnownTypeCode.Array)); if (arrayExpr.Type.Kind != TypeKind.Array) { arrayExpr = arrayExpr.ConvertTo(compilation.FindType(KnownTypeCode.Array), this); } From 6b075326f04b58d61096839a6e25b1148a157045 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 5 Aug 2019 23:09:02 +0200 Subject: [PATCH 177/221] Fix #1615: Handle nil tokens gracefully. --- ICSharpCode.Decompiler/IL/ILReader.cs | 3 ++- ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 88b48da73..730198abf 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -124,9 +124,10 @@ namespace ICSharpCode.Decompiler.IL EntityHandle ReadAndDecodeMetadataToken() { int token = reader.ReadInt32(); - if (token < 0) { + if (token <= 0) { // SRM uses negative tokens as "virtual tokens" and can get confused // if we manually create them. + // Row-IDs < 1 are always invalid. throw new BadImageFormatException("Invalid metadata token"); } return MetadataTokens.EntityHandle(token); diff --git a/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs b/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs index d1aab3e59..b246ac5b9 100644 --- a/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs +++ b/ICSharpCode.Decompiler/IL/InstructionOutputExtensions.cs @@ -69,8 +69,10 @@ namespace ICSharpCode.Decompiler.IL public static void WriteTo(this EntityHandle entity, PEFile module, ITextOutput output, Metadata.GenericContext genericContext, ILNameSyntax syntax = ILNameSyntax.Signature) { - if (entity.IsNil) - throw new ArgumentNullException(nameof(entity)); + if (entity.IsNil) { + output.Write(""); + return; + } if (module == null) throw new ArgumentNullException(nameof(module)); var metadata = module.Metadata; From 84aaaeb84e9324aceec30325162ab6d7bdeab343 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 5 Aug 2019 23:24:20 +0200 Subject: [PATCH 178/221] Fix #1616: similar to 3ea2ce4e2d, let `a.Instructions[i] = a.Instructions[i]` refresh that instruction's primary position. --- ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs b/ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs index f9f311cf3..63d5fd97d 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/InstructionCollection.cs @@ -44,7 +44,7 @@ namespace ICSharpCode.Decompiler.IL get { return list[index]; } set { T oldValue = list[index]; - if (oldValue != value) { + if (!(oldValue == value && value.Parent == parentInstruction && value.ChildIndex == index)) { list[index] = value; value.ChildIndex = index + firstChildIndex; parentInstruction.InstructionCollectionAdded(value); From 6ecd99f893a2380cc2252689ea18537f99805f76 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 6 Aug 2019 00:16:35 +0200 Subject: [PATCH 179/221] Fix #1618: Unwrap in-arguments, when converting method calls to operators. --- .../CSharp/Syntax/SyntaxExtensions.cs | 7 +++++++ .../ReplaceMethodCallsWithOperators.cs | 16 ++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/SyntaxExtensions.cs b/ICSharpCode.Decompiler/CSharp/Syntax/SyntaxExtensions.cs index b4ffe1f74..632e6bbbb 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/SyntaxExtensions.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/SyntaxExtensions.cs @@ -75,5 +75,12 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax node.Remove(); return node; } + + public static Expression UnwrapInDirectionExpression(this Expression expr) + { + if (!(expr is DirectionExpression dir && dir.FieldDirection == FieldDirection.In)) + return expr; + return dir.Expression.Detach(); + } } } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs index 7949e1df6..9d9838d71 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs @@ -60,7 +60,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression Expression expr = arguments[0]; for (int i = 1; i < arguments.Length; i++) { - expr = new BinaryOperatorExpression(expr, BinaryOperatorType.Add, arguments[i]); + expr = new BinaryOperatorExpression(expr, BinaryOperatorType.Add, arguments[i].UnwrapInDirectionExpression()); } expr.CopyAnnotationsFrom(invocationExpression); invocationExpression.ReplaceWith(expr); @@ -116,7 +116,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (bop != null && arguments.Length == 2) { invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression invocationExpression.ReplaceWith( - new BinaryOperatorExpression(arguments[0], bop.Value, arguments[1]).CopyAnnotationsFrom(invocationExpression) + new BinaryOperatorExpression( + arguments[0].UnwrapInDirectionExpression(), + bop.Value, + arguments[1].UnwrapInDirectionExpression() + ).CopyAnnotationsFrom(invocationExpression) ); return; } @@ -130,7 +134,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms // so reverse that optimization here: invocationExpression.ReplaceWith( new BinaryOperatorExpression( - arguments[0].Detach(), + arguments[0].UnwrapInDirectionExpression().Detach(), (uop == UnaryOperatorType.Increment ? BinaryOperatorType.Add : BinaryOperatorType.Subtract), new PrimitiveExpression(1m) ).CopyAnnotationsFrom(invocationExpression) @@ -140,20 +144,20 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } arguments[0].Remove(); // detach argument invocationExpression.ReplaceWith( - new UnaryOperatorExpression(uop.Value, arguments[0]).CopyAnnotationsFrom(invocationExpression) + new UnaryOperatorExpression(uop.Value, arguments[0].UnwrapInDirectionExpression()).CopyAnnotationsFrom(invocationExpression) ); return; } if (method.Name == "op_Explicit" && arguments.Length == 1) { arguments[0].Remove(); // detach argument invocationExpression.ReplaceWith( - new CastExpression(context.TypeSystemAstBuilder.ConvertType(method.ReturnType), arguments[0]) + new CastExpression(context.TypeSystemAstBuilder.ConvertType(method.ReturnType), arguments[0].UnwrapInDirectionExpression()) .CopyAnnotationsFrom(invocationExpression) ); return; } if (method.Name == "op_True" && arguments.Length == 1 && invocationExpression.Role == Roles.Condition) { - invocationExpression.ReplaceWith(arguments[0]); + invocationExpression.ReplaceWith(arguments[0].UnwrapInDirectionExpression()); return; } From 89ea6c3675cfa56733aa14662e87a464c9501a27 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 6 Aug 2019 00:25:08 +0200 Subject: [PATCH 180/221] Fix TypeErasure not removing all nullability annotations. --- .../TypeSystem/NormalizeTypeVisitor.cs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs b/ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs index 291eeac47..0f8d8a2a4 100644 --- a/ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs +++ b/ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs @@ -21,16 +21,6 @@ namespace ICSharpCode.Decompiler.TypeSystem RemoveNullability = true, }; - internal static readonly NormalizeTypeVisitor RemoveModifiersAndNullability = new NormalizeTypeVisitor { - ReplaceClassTypeParametersWithDummy = false, - ReplaceMethodTypeParametersWithDummy = false, - DynamicAndObject = false, - TupleToUnderlyingType = false, - RemoveModOpt = true, - RemoveModReq = true, - RemoveNullability = true, - }; - public bool EquivalentTypes(IType a, IType b) { a = a.AcceptVisitor(this); @@ -52,6 +42,8 @@ namespace ICSharpCode.Decompiler.TypeSystem return DummyTypeParameter.GetMethodTypeParameter(type.Index); } else if (type.OwnerType == SymbolKind.TypeDefinition && ReplaceClassTypeParametersWithDummy) { return DummyTypeParameter.GetClassTypeParameter(type.Index); + } else if (RemoveNullability && type is NullabilityAnnotatedTypeParameter natp) { + return natp.TypeWithoutAnnotation.AcceptVisitor(this); } else { return base.VisitTypeParameter(type); } @@ -82,7 +74,7 @@ namespace ICSharpCode.Decompiler.TypeSystem public override IType VisitNullabilityAnnotatedType(NullabilityAnnotatedType type) { if (RemoveNullability) - return base.VisitNullabilityAnnotatedType(type).ChangeNullability(Nullability.Oblivious); + return type.TypeWithoutAnnotation.AcceptVisitor(this); else return base.VisitNullabilityAnnotatedType(type); } From 3d30b0548dfd9b8b0d94a037a4403ef017485221 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 6 Aug 2019 01:01:27 +0200 Subject: [PATCH 181/221] #1598: Fix .bat files to use VS2019. --- clean.bat | 8 ++++---- debugbuild.bat | 8 ++++---- preparerelease.bat | 8 ++++---- releasebuild.bat | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/clean.bat b/clean.bat index 690435b2d..b19f16be7 100644 --- a/clean.bat +++ b/clean.bat @@ -1,12 +1,12 @@ @setlocal enabledelayedexpansion @set MSBUILD= -@for /D %%M in ("%ProgramFiles(x86)%\Microsoft Visual Studio\2017"\*) do ( - @if exist "%%M\MSBuild\15.0\Bin\MSBuild.exe" ( - @set "MSBUILD=%%M\MSBuild\15.0\Bin\MSBuild.exe" +@for /D %%M in ("%ProgramFiles(x86)%\Microsoft Visual Studio\2019"\*) do ( + @if exist "%%M\MSBuild\Current\Bin\MSBuild.exe" ( + @set "MSBUILD=%%M\MSBuild\Current\Bin\MSBuild.exe" ) ) @if "%MSBUILD%" == "" ( - @echo Could not find VS2017 MSBuild + @echo Could not find VS2019 MSBuild @exit /b 1 ) "%MSBUILD%" /m ILSpy.sln /t:Clean /p:Configuration=Debug "/p:Platform=Any CPU" || pause diff --git a/debugbuild.bat b/debugbuild.bat index 08d5425e6..90325ccab 100644 --- a/debugbuild.bat +++ b/debugbuild.bat @@ -1,12 +1,12 @@ @setlocal enabledelayedexpansion @set MSBUILD= -@for /D %%M in ("%ProgramFiles(x86)%\Microsoft Visual Studio\2017"\*) do ( - @if exist "%%M\MSBuild\15.0\Bin\MSBuild.exe" ( - @set "MSBUILD=%%M\MSBuild\15.0\Bin\MSBuild.exe" +@for /D %%M in ("%ProgramFiles(x86)%\Microsoft Visual Studio\2019"\*) do ( + @if exist "%%M\MSBuild\Current\Bin\MSBuild.exe" ( + @set "MSBUILD=%%M\MSBuild\Current\Bin\MSBuild.exe" ) ) @if "%MSBUILD%" == "" ( - @echo Could not find VS2017 MSBuild + @echo Could not find VS2019 MSBuild @exit /b 1 ) "%MSBUILD%" ILSpy.sln /p:Configuration=Debug "/p:Platform=Any CPU" diff --git a/preparerelease.bat b/preparerelease.bat index 60fadaabe..d2b141357 100644 --- a/preparerelease.bat +++ b/preparerelease.bat @@ -1,12 +1,12 @@ @setlocal enabledelayedexpansion @set MSBUILD= -@for /D %%M in ("%ProgramFiles(x86)%\Microsoft Visual Studio\2017"\*) do ( - @if exist "%%M\MSBuild\15.0\Bin\MSBuild.exe" ( - @set "MSBUILD=%%M\MSBuild\15.0\Bin\MSBuild.exe" +@for /D %%M in ("%ProgramFiles(x86)%\Microsoft Visual Studio\2019"\*) do ( + @if exist "%%M\MSBuild\Current\Bin\MSBuild.exe" ( + @set "MSBUILD=%%M\MSBuild\Current\Bin\MSBuild.exe" ) ) @if "%MSBUILD%" == "" ( - @echo Could not find VS2017 MSBuild + @echo Could not find VS2019 MSBuild @exit /b 1 ) @del ICSharpCode.Decompiler\bin\Release\*.nupkg diff --git a/releasebuild.bat b/releasebuild.bat index a753390fa..cd881dd67 100644 --- a/releasebuild.bat +++ b/releasebuild.bat @@ -1,12 +1,12 @@ @setlocal enabledelayedexpansion @set MSBUILD= -@for /D %%M in ("%ProgramFiles(x86)%\Microsoft Visual Studio\2017"\*) do ( - @if exist "%%M\MSBuild\15.0\Bin\MSBuild.exe" ( - @set "MSBUILD=%%M\MSBuild\15.0\Bin\MSBuild.exe" +@for /D %%M in ("%ProgramFiles(x86)%\Microsoft Visual Studio\2019"\*) do ( + @if exist "%%M\MSBuild\Current\Bin\MSBuild.exe" ( + @set "MSBUILD=%%M\MSBuild\Current\Bin\MSBuild.exe" ) ) @if "%MSBUILD%" == "" ( - @echo Could not find VS2017 MSBuild + @echo Could not find VS2019 MSBuild @exit /b 1 ) "%MSBUILD%" ILSpy.sln /p:Configuration=Release "/p:Platform=Any CPU" From 123e570128229f306df0114d51fb8612a30e36bc Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 6 Aug 2019 08:43:10 +0200 Subject: [PATCH 182/221] Fix warning about doc comment. --- ICSharpCode.Decompiler/Solution/ProjectId.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/Solution/ProjectId.cs b/ICSharpCode.Decompiler/Solution/ProjectId.cs index 65a4e8d9e..cfa3feb7c 100644 --- a/ICSharpCode.Decompiler/Solution/ProjectId.cs +++ b/ICSharpCode.Decompiler/Solution/ProjectId.cs @@ -31,8 +31,7 @@ namespace ICSharpCode.Decompiler.Solution /// The project platform. /// The project GUID. /// - /// Thrown when - /// or is null or empty. + /// Thrown when is null or empty. public ProjectId(string projectPlatform, Guid projectGuid) { if (string.IsNullOrWhiteSpace(projectPlatform)) { From e1bc205d3a92e8f1b0ac1b25acd1865a0dc4f24a Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 6 Aug 2019 08:49:07 +0200 Subject: [PATCH 183/221] Add support for strong-name keys in WholeProjectDecompiler and RoundtripAssembly tests. --- .../Helpers/Tester.cs | 22 +++++++++++++++++++ .../RoundtripAssembly.cs | 11 ++++++---- .../CSharp/WholeProjectDecompiler.cs | 14 ++++++++++++ 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs index dfe0d7cb2..1ad55293d 100644 --- a/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs +++ b/ICSharpCode.Decompiler.Tests/Helpers/Tester.cs @@ -534,5 +534,27 @@ namespace ICSharpCode.Decompiler.Tests.Helpers // If the last try still fails, don't catch the exception action(); } + + public static void SignAssembly(string assemblyPath, string keyFilePath) + { + string snPath = SdkUtility.GetSdkPath("sn.exe"); + + ProcessStartInfo info = new ProcessStartInfo(snPath); + info.Arguments = $"-R \"{assemblyPath}\" \"{keyFilePath}\""; + info.RedirectStandardError = true; + info.RedirectStandardOutput = true; + info.UseShellExecute = false; + + Process process = Process.Start(info); + + var outputTask = process.StandardOutput.ReadToEndAsync(); + var errorTask = process.StandardError.ReadToEndAsync(); + + Task.WaitAll(outputTask, errorTask); + process.WaitForExit(); + + Console.WriteLine("output: " + outputTask.Result); + Console.WriteLine("errors: " + errorTask.Result); + } } } diff --git a/ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs b/ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs index 975c92511..210a45d13 100644 --- a/ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs +++ b/ICSharpCode.Decompiler.Tests/RoundtripAssembly.cs @@ -102,11 +102,11 @@ namespace ICSharpCode.Decompiler.Tests RunWithOutput("Random Tests\\TestCases", "TestCase-1.exe"); } - void RunWithTest(string dir, string fileToRoundtrip, string fileToTest) + void RunWithTest(string dir, string fileToRoundtrip, string fileToTest, string keyFile = null) { - RunInternal(dir, fileToRoundtrip, outputDir => RunTest(outputDir, fileToTest)); + RunInternal(dir, fileToRoundtrip, outputDir => RunTest(outputDir, fileToTest), keyFile); } - + void RunWithOutput(string dir, string fileToRoundtrip) { string inputDir = Path.Combine(TestDir, dir); @@ -114,7 +114,7 @@ namespace ICSharpCode.Decompiler.Tests outputDir => Tester.RunAndCompareOutput(fileToRoundtrip, Path.Combine(inputDir, fileToRoundtrip), Path.Combine(outputDir, fileToRoundtrip))); } - void RunInternal(string dir, string fileToRoundtrip, Action testAction) + void RunInternal(string dir, string fileToRoundtrip, Action testAction, string snkFilePath = null) { if (!Directory.Exists(TestDir)) { Assert.Ignore($"Assembly-roundtrip test ignored: test directory '{TestDir}' needs to be checked out separately." + Environment.NewLine + @@ -152,6 +152,9 @@ namespace ICSharpCode.Decompiler.Tests decompiler.Settings = new DecompilerSettings(LanguageVersion.CSharp7_3); // use a fixed GUID so that we can diff the output between different ILSpy runs without spurious changes decompiler.ProjectGuid = Guid.Parse("{127C83E4-4587-4CF9-ADCA-799875F3DFE6}"); + if (snkFilePath != null) { + decompiler.StrongNameKeyFile = Path.Combine(inputDir, snkFilePath); + } decompiler.DecompileProject(module, decompiledDir); Console.WriteLine($"Decompiled {fileToRoundtrip} in {w.Elapsed.TotalSeconds:f2}"); projectFile = Path.Combine(decompiledDir, module.Name + ".csproj"); diff --git a/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs b/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs index 377a617d5..a757e2921 100644 --- a/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs @@ -79,6 +79,12 @@ namespace ICSharpCode.Decompiler.CSharp /// public Guid? ProjectGuid { get; set; } + /// + /// Path to the snk file to use for signing. + /// null to not sign. + /// + public string StrongNameKeyFile { get; set; } + public int MaxDegreeOfParallelism { get; set; } = Environment.ProcessorCount; #endregion @@ -111,6 +117,9 @@ namespace ICSharpCode.Decompiler.CSharp directories.Clear(); var files = WriteCodeFilesInProject(moduleDefinition, cancellationToken).ToList(); files.AddRange(WriteResourceFilesInProject(moduleDefinition)); + if (StrongNameKeyFile != null) { + File.Copy(StrongNameKeyFile, Path.Combine(targetDirectory, Path.GetFileName(StrongNameKeyFile))); + } return WriteProjectFile(projectFileWriter, files, moduleDefinition); } @@ -215,6 +224,11 @@ namespace ICSharpCode.Decompiler.CSharp } w.WriteElementString("WarningLevel", "4"); w.WriteElementString("AllowUnsafeBlocks", "True"); + + if (StrongNameKeyFile != null) { + w.WriteElementString("SignAssembly", "True"); + w.WriteElementString("AssemblyOriginatorKeyFile", Path.GetFileName(StrongNameKeyFile)); + } w.WriteEndElement(); // From 3203491ad8a32644d8b6d60564487bd512ed7f55 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 6 Aug 2019 09:02:37 +0200 Subject: [PATCH 184/221] Set VersionName = "preview4" --- ILSpy/Properties/AssemblyInfo.template.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ILSpy/Properties/AssemblyInfo.template.cs b/ILSpy/Properties/AssemblyInfo.template.cs index 56f5818b6..cdbdfecbf 100644 --- a/ILSpy/Properties/AssemblyInfo.template.cs +++ b/ILSpy/Properties/AssemblyInfo.template.cs @@ -39,7 +39,7 @@ internal static class RevisionClass public const string Minor = "0"; public const string Build = "0"; public const string Revision = "$INSERTREVISION$"; - public const string VersionName = "preview3"; + public const string VersionName = "preview4"; public const string FullVersion = Major + "." + Minor + "." + Build + ".$INSERTREVISION$$INSERTBRANCHPOSTFIX$$INSERTVERSIONNAMEPOSTFIX$"; } From 00b3031c19f5d2948282aeb8dd25521d4cad62ca Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 6 Aug 2019 09:42:22 +0200 Subject: [PATCH 185/221] #1498: Display error message about the required elevated privileges. --- ILSpy/GacInterop.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ILSpy/GacInterop.cs b/ILSpy/GacInterop.cs index a260a9daf..7955b37c8 100644 --- a/ILSpy/GacInterop.cs +++ b/ILSpy/GacInterop.cs @@ -20,6 +20,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Text; +using System.Windows; using ICSharpCode.Decompiler.Metadata; namespace ICSharpCode.ILSpy @@ -38,7 +39,11 @@ namespace ICSharpCode.ILSpy IAssemblyEnum assemblyEnum = null; IAssemblyName assemblyName = null; - Fusion.CreateAssemblyEnum(out assemblyEnum, null, null, 2, 0); + uint result = unchecked((uint)Fusion.CreateAssemblyEnum(out assemblyEnum, null, null, 2, 0)); + if (result == 0x80070005) { + MessageBox.Show($"Cannot access GAC, please restart with elevated privileges! (HRESULT 0x{result:X})", "ILSpy", MessageBoxButton.OK, MessageBoxImage.Error); + yield break; + } while (assemblyEnum.GetNextAssembly(out applicationContext, out assemblyName, 0) == 0) { if (assemblyName == null) continue; uint nChars = 0; From 3ad8c88d3984ea118feb2bbd38d6eb87173a353c Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 9 Aug 2019 10:28:44 +0200 Subject: [PATCH 186/221] Fix #1622: Transform local functions calls with named arguments correctly --- .../IL/Transforms/LocalFunctionDecompiler.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs index 1268989f7..9ae8abff4 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs @@ -230,6 +230,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms replacement.AddILRange(useSite); if (wasInstanceCall) { replacement.AddILRange(useSite.Arguments[0]); + if (useSite.Arguments[0].MatchLdLocRef(out var variable) && variable.Kind == VariableKind.NamedArgument) { + // remove the store instruction of the simple load, if it is a named argument. + var storeInst = (ILInstruction)variable.StoreInstructions[0]; + ((Block)storeInst.Parent).Instructions.RemoveAt(storeInst.ChildIndex); + } } for (int i = 0; i < reducedMethod.NumberOfCompilerGeneratedParameters; i++) { replacement.AddILRange(useSite.Arguments[argumentCount - i - 1]); From fdf4228a1755efd876bb457e9fb5d47c82bc4912 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 9 Aug 2019 10:34:16 +0200 Subject: [PATCH 187/221] Fix #1623: Nest local functions correctly, if captured variables are used. --- .../IL/Transforms/LocalFunctionDecompiler.cs | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs index 9ae8abff4..d8f4d294e 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs @@ -84,7 +84,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } foreach (var useSite in info.UseSites) { - context.Step("Transform use site at " + useSite.StartILOffset, useSite); + context.Step($"Transform use site at IL_{useSite.StartILOffset:x4}", useSite); if (useSite.OpCode == OpCode.NewObj) { TransformToLocalFunctionReference(localFunction, useSite); } else { @@ -245,22 +245,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms void DetermineCaptureAndDeclarationScope(ILFunction function, CallInstruction useSite) { int firstArgumentIndex = function.Method.IsStatic ? 0 : 1; - if (firstArgumentIndex > 0) { - HandleArgument(0, useSite.Arguments[0]); - } for (int i = useSite.Arguments.Count - 1; i >= firstArgumentIndex; i--) { if (!HandleArgument(i, useSite.Arguments[i])) break; } + if (firstArgumentIndex > 0) { + HandleArgument(0, useSite.Arguments[0], skipForDeclarationScope: true); + } - bool HandleArgument(int i, ILInstruction arg) + bool HandleArgument(int i, ILInstruction arg, bool skipForDeclarationScope = false) { ILVariable closureVar; if (!(arg.MatchLdLoc(out closureVar) || arg.MatchLdLoca(out closureVar))) return false; if (closureVar.Kind == VariableKind.NamedArgument) return false; - if (!TransformDisplayClassUsage.IsPotentialClosure(context, UnwrapByRef(closureVar.Type).GetDefinition())) + ITypeDefinition potentialDisplayClass = UnwrapByRef(closureVar.Type).GetDefinition(); + if (!TransformDisplayClassUsage.IsPotentialClosure(context, potentialDisplayClass)) return false; if (i - firstArgumentIndex >= 0) { Debug.Assert(i - firstArgumentIndex < function.Method.Parameters.Count && IsClosureParameter(function.Method.Parameters[i - firstArgumentIndex], resolveContext)); @@ -269,7 +270,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; // determine the capture scope of closureVar and the declaration scope of the function var instructions = closureVar.StoreInstructions.OfType() - .Concat(closureVar.AddressInstructions).OrderBy(inst => inst.StartILOffset); + .Concat(closureVar.AddressInstructions).OrderBy(inst => inst.StartILOffset).ToList(); var additionalScope = BlockContainer.FindClosestContainer(instructions.First()); if (closureVar.CaptureScope == null) closureVar.CaptureScope = additionalScope; @@ -277,12 +278,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms closureVar.CaptureScope = FindCommonAncestorInstruction(closureVar.CaptureScope, additionalScope); if (function.DeclarationScope == null) function.DeclarationScope = closureVar.CaptureScope; - else + else if (!IsInNestedLocalFunction(function.DeclarationScope, closureVar.CaptureScope.Ancestors.OfType().First())) function.DeclarationScope = FindCommonAncestorInstruction(function.DeclarationScope, closureVar.CaptureScope); return true; } } + bool IsInNestedLocalFunction(BlockContainer declarationScope, ILFunction function) + { + return TreeTraversal.PreOrder(function, f => f.LocalFunctions).Any(f => declarationScope.IsDescendantOf(f.Body)); + } + internal static bool IsLocalFunctionReference(NewObj inst, ILTransformContext context) { if (inst == null || inst.Arguments.Count != 2 || inst.Method.DeclaringType.Kind != TypeKind.Delegate) From 4246a178f413fde6ca9ae561ae813f8a206f460b Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 9 Aug 2019 11:23:37 +0200 Subject: [PATCH 188/221] Remove unused parameter. --- .../IL/Transforms/LocalFunctionDecompiler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs index d8f4d294e..8c52794f0 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs @@ -250,10 +250,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms break; } if (firstArgumentIndex > 0) { - HandleArgument(0, useSite.Arguments[0], skipForDeclarationScope: true); + HandleArgument(0, useSite.Arguments[0]); } - bool HandleArgument(int i, ILInstruction arg, bool skipForDeclarationScope = false) + bool HandleArgument(int i, ILInstruction arg) { ILVariable closureVar; if (!(arg.MatchLdLoc(out closureVar) || arg.MatchLdLoca(out closureVar))) From 87353aac5a45294a5eb0b4ff989f0b4e04df7765 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 9 Aug 2019 11:25:56 +0200 Subject: [PATCH 189/221] Fix #1624: Stack type mismatch in expression trees. --- .../IL/Transforms/TransformExpressionTrees.cs | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs index a73c11612..07ba5adff 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs @@ -510,14 +510,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (arguments == null) return (null, SpecialType.UnknownType); IMethod method = (IMethod)member; - Debug.Assert(arguments.Count == method.Parameters.Count); - for (int i = 0; i < arguments.Count; i++) { - var expectedType = method.Parameters[i].Type; - var (argument, argumentType) = ConvertInstruction(arguments[i], expectedType); - if (argument == null) - return (null, SpecialType.UnknownType); - arguments[i] = argument; - } + if (!ConvertCallArguments(arguments, method)) + return (null, SpecialType.UnknownType); if (method.FullName == "System.Reflection.MethodInfo.CreateDelegate" && method.Parameters.Count == 2) { if (!MatchGetMethodFromHandle(target, out var targetMethod)) return (null, SpecialType.UnknownType); @@ -562,6 +556,19 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } + bool ConvertCallArguments(IList arguments, IMethod method) + { + Debug.Assert(arguments.Count == method.Parameters.Count); + for (int i = 0; i < arguments.Count; i++) { + var expectedType = method.Parameters[i].Type; + var argument = ConvertInstruction(arguments[i], expectedType).Item1; + if (argument == null) + return false; + arguments[i] = argument; + } + return true; + } + (ILInstruction, IType) ConvertCast(CallInstruction invocation, bool isChecked) { if (invocation.Arguments.Count < 2) @@ -737,12 +744,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms return (null, SpecialType.UnknownType); if (!MatchArgumentList(invocation.Arguments[1], out var arguments)) return (null, SpecialType.UnknownType); - for (int i = 0; i < arguments.Count; i++) { - var arg = ConvertInstruction(arguments[i]).Item1; - if (arg == null) - return (null, SpecialType.UnknownType); - arguments[i] = arg; - } + if (!ConvertCallArguments(arguments, invokeMethod)) + return (null, SpecialType.UnknownType); var call = new CallVirt(invokeMethod); call.Arguments.Add(target); call.Arguments.AddRange(arguments); @@ -925,22 +928,22 @@ namespace ICSharpCode.Decompiler.IL.Transforms return (null, SpecialType.UnknownType); if (!MatchArgumentList(invocation.Arguments[1], out arguments)) return (null, SpecialType.UnknownType); - var args = arguments.SelectArray(arg => ConvertInstruction(arg).Item1); - if (args.Any(a => a == null)) + IMethod method = (IMethod)member; + if (!ConvertCallArguments(arguments, method)) return (null, SpecialType.UnknownType); - newObj = new NewObj((IMethod)member); - newObj.Arguments.AddRange(args); + newObj = new NewObj(method); + newObj.Arguments.AddRange(arguments); return (newObj, member.DeclaringType); case 3: if (!MatchGetConstructorFromHandle(invocation.Arguments[0], out member)) return (null, SpecialType.UnknownType); if (!MatchArgumentList(invocation.Arguments[1], out arguments)) return (null, SpecialType.UnknownType); - var args2 = arguments.SelectArray(arg => ConvertInstruction(arg).Item1); - if (args2.Any(a => a == null)) + method = (IMethod)member; + if (!ConvertCallArguments(arguments, method)) return (null, SpecialType.UnknownType); - newObj = new NewObj((IMethod)member); - newObj.Arguments.AddRange(args2); + newObj = new NewObj(method); + newObj.Arguments.AddRange(arguments); return (newObj, member.DeclaringType); } return (null, SpecialType.UnknownType); @@ -984,11 +987,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (invocation.Arguments.Count != 3 || !MatchArgumentList(invocation.Arguments[2], out arguments)) { arguments = new List(); } else { - for (int i = 0; i < arguments.Count; i++) { - arguments[i] = ConvertInstruction(arguments[i]).Item1; - if (arguments[i] == null) - return (null, SpecialType.UnknownType); - } + if (!ConvertCallArguments(arguments, (IMethod)member)) + return (null, SpecialType.UnknownType); } CallInstruction call; if (member.IsAbstract || member.IsVirtual || member.IsOverride) { From 71644bcae90e6e240f6b92554b58e771acb1b8b6 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 9 Aug 2019 11:45:49 +0200 Subject: [PATCH 190/221] #1625: Improve error message, if known assembly cannot be resolved. --- ILSpy.BamlDecompiler/Baml/KnownThings.cs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/ILSpy.BamlDecompiler/Baml/KnownThings.cs b/ILSpy.BamlDecompiler/Baml/KnownThings.cs index 5b82a0221..c5ab16072 100644 --- a/ILSpy.BamlDecompiler/Baml/KnownThings.cs +++ b/ILSpy.BamlDecompiler/Baml/KnownThings.cs @@ -45,11 +45,15 @@ namespace ILSpy.BamlDecompiler.Baml { strings = new Dictionary(); resources = new Dictionary(); - InitAssemblies(); - InitTypes(); - InitMembers(); - InitStrings(); - InitResources(); + try { + InitAssemblies(); + InitTypes(); + InitMembers(); + InitStrings(); + InitResources(); + } catch (Exception ex) { + throw new ICSharpCode.Decompiler.DecompilerException(typeSystem.MainModule.PEFile, ex.Message, ex); + } } public Func Types => id => types[id]; @@ -57,7 +61,14 @@ namespace ILSpy.BamlDecompiler.Baml { public Func Strings => id => strings[id]; public Func Resources => id => resources[id]; public IModule FrameworkAssembly => assemblies[0]; - IModule ResolveAssembly(string name) => typeSystem.Modules.First(m => m.FullAssemblyName == name); + IModule ResolveAssembly(string name) + { + IModule module = typeSystem.Modules.FirstOrDefault(m => m.FullAssemblyName == name); + if (module == null) + throw new Exception("Could not resolve known assembly '" + name + "'!"); + return module; + } + ITypeDefinition InitType(IModule assembly, string ns, string name) => assembly.GetTypeDefinition(new TopLevelTypeName(ns, name)); KnownMember InitMember(KnownTypes parent, string name, ITypeDefinition type) => new KnownMember(parent, types[parent], name, type); } From 0f667a126dd851ce6428c389db4449a149111241 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 9 Aug 2019 12:49:32 +0200 Subject: [PATCH 191/221] Update tests to Roslyn 3.3.0-beta1-final --- .../ICSharpCode.Decompiler.Tests.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj index 67fc813d1..fe066f25f 100644 --- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj @@ -42,8 +42,8 @@ - - + + From 722e2c8325f761946ca2f913ffc41d5a43fa9e79 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 9 Aug 2019 18:53:12 +0200 Subject: [PATCH 192/221] Add bracket highlighting to ILSpy. --- ILSpy/ILSpy.csproj | 2 ++ .../CSharpHighlightingTokenWriter.cs | 20 ++++++++++++++++++- ILSpy/Languages/CSharpLanguage.cs | 5 +++++ ILSpy/Languages/Language.cs | 6 ++++++ ILSpy/Options/DisplaySettings.cs | 13 ++++++++++++ ILSpy/Options/DisplaySettingsPanel.xaml | 1 + ILSpy/Options/DisplaySettingsPanel.xaml.cs | 2 ++ ILSpy/Properties/Resources.Designer.cs | 9 +++++++++ ILSpy/Properties/Resources.resx | 3 +++ ILSpy/TextView/DecompilerTextView.cs | 15 ++++++++++++++ 10 files changed, 75 insertions(+), 1 deletion(-) diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 2338940e6..ea4846842 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -140,6 +140,7 @@ + @@ -206,6 +207,7 @@ + diff --git a/ILSpy/Languages/CSharpHighlightingTokenWriter.cs b/ILSpy/Languages/CSharpHighlightingTokenWriter.cs index 827e774b3..cf0464067 100644 --- a/ILSpy/Languages/CSharpHighlightingTokenWriter.cs +++ b/ILSpy/Languages/CSharpHighlightingTokenWriter.cs @@ -1,4 +1,22 @@ -using System; +// Copyright (c) 2018 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; using System.Collections.Generic; using System.Linq; using ICSharpCode.AvalonEdit.Highlighting; diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index 35f614764..e952647e1 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -37,6 +37,7 @@ using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.Solution; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.Util; +using ICSharpCode.ILSpy.TextView; using ICSharpCode.ILSpy.TreeNodes; namespace ICSharpCode.ILSpy @@ -634,5 +635,9 @@ namespace ICSharpCode.ILSpy { return CSharpDecompiler.GetCodeMappingInfo(module, member); } + + CSharpBracketSearcher bracketSearcher = new CSharpBracketSearcher(); + + public override IBracketSearcher BracketSearcher => bracketSearcher; } } diff --git a/ILSpy/Languages/Language.cs b/ILSpy/Languages/Language.cs index e97696fea..701399c70 100644 --- a/ILSpy/Languages/Language.cs +++ b/ILSpy/Languages/Language.cs @@ -102,6 +102,12 @@ namespace ICSharpCode.ILSpy } } + public virtual TextView.IBracketSearcher BracketSearcher { + get { + return TextView.DefaultBracketSearcher.DefaultInstance; + } + } + public virtual void DecompileMethod(IMethod method, ITextOutput output, DecompilationOptions options) { WriteCommentLine(output, TypeToString(method.DeclaringTypeDefinition, includeNamespace: true) + "." + method.Name); diff --git a/ILSpy/Options/DisplaySettings.cs b/ILSpy/Options/DisplaySettings.cs index 3d6053961..a887e2913 100644 --- a/ILSpy/Options/DisplaySettings.cs +++ b/ILSpy/Options/DisplaySettings.cs @@ -215,6 +215,18 @@ namespace ICSharpCode.ILSpy.Options } } + bool highlightMatchingBraces = true; + + public bool HighlightMatchingBraces { + get { return highlightMatchingBraces; } + set { + if (highlightMatchingBraces != value) { + highlightMatchingBraces = value; + OnPropertyChanged(); + } + } + } + public void CopyValues(DisplaySettings s) { this.SelectedFont = s.selectedFont; @@ -231,6 +243,7 @@ namespace ICSharpCode.ILSpy.Options this.IndentationUseTabs = s.indentationUseTabs; this.IndentationTabSize = s.indentationTabSize; this.IndentationSize = s.indentationSize; + this.HighlightMatchingBraces = s.highlightMatchingBraces; } } } diff --git a/ILSpy/Options/DisplaySettingsPanel.xaml b/ILSpy/Options/DisplaySettingsPanel.xaml index ab2ad7398..13d149e6d 100644 --- a/ILSpy/Options/DisplaySettingsPanel.xaml +++ b/ILSpy/Options/DisplaySettingsPanel.xaml @@ -80,6 +80,7 @@ + diff --git a/ILSpy/Options/DisplaySettingsPanel.xaml.cs b/ILSpy/Options/DisplaySettingsPanel.xaml.cs index b08a139fb..56ff2622d 100644 --- a/ILSpy/Options/DisplaySettingsPanel.xaml.cs +++ b/ILSpy/Options/DisplaySettingsPanel.xaml.cs @@ -114,6 +114,7 @@ namespace ICSharpCode.ILSpy.Options s.IndentationUseTabs = (bool?)e.Attribute("IndentationUseTabs") ?? true; s.IndentationSize = (int?)e.Attribute("IndentationSize") ?? 4; s.IndentationTabSize = (int?)e.Attribute("IndentationTabSize") ?? 4; + s.HighlightMatchingBraces = (bool?)e.Attribute("HighlightMatchingBraces") ?? true; return s; } @@ -136,6 +137,7 @@ namespace ICSharpCode.ILSpy.Options section.SetAttributeValue("IndentationUseTabs", s.IndentationUseTabs); section.SetAttributeValue("IndentationSize", s.IndentationSize); section.SetAttributeValue("IndentationTabSize", s.IndentationTabSize); + section.SetAttributeValue("HighlightMatchingBraces", s.HighlightMatchingBraces); XElement existingElement = root.Element("DisplaySettings"); if (existingElement != null) diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index c2c607198..9cb673c88 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -1152,6 +1152,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Highlight matching braces. + /// + public static string HighlightMatchingBraces { + get { + return ResourceManager.GetString("HighlightMatchingBraces", resourceCulture); + } + } + /// /// Looks up a localized string similar to ILSpy version . /// diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index 6ef64ac91..08bb57a15 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -742,4 +742,7 @@ Are you sure you want to continue? Project Directory not empty + + Highlight matching braces + \ No newline at end of file diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 8a4f05152..a370e4984 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -65,6 +65,7 @@ namespace ICSharpCode.ILSpy.TextView readonly UIElementGenerator uiElementGenerator; List activeCustomElementGenerators = new List(); RichTextColorizer activeRichTextColorizer; + BracketHighlightRenderer bracketHighlightRenderer; FoldingManager foldingManager; ILSpyTreeNode[] decompiledNodes; @@ -103,12 +104,14 @@ namespace ICSharpCode.ILSpy.TextView this.referenceElementGenerator = new ReferenceElementGenerator(this.JumpToReference, this.IsLink); textEditor.TextArea.TextView.ElementGenerators.Add(referenceElementGenerator); this.uiElementGenerator = new UIElementGenerator(); + this.bracketHighlightRenderer = new BracketHighlightRenderer(textEditor.TextArea.TextView); textEditor.TextArea.TextView.ElementGenerators.Add(uiElementGenerator); textEditor.Options.RequireControlModifierForHyperlinkClick = false; textEditor.TextArea.TextView.MouseHover += TextViewMouseHover; textEditor.TextArea.TextView.MouseHoverStopped += TextViewMouseHoverStopped; textEditor.TextArea.PreviewMouseDown += TextAreaMouseDown; textEditor.TextArea.PreviewMouseUp += TextAreaMouseUp; + textEditor.TextArea.Caret.PositionChanged += HighlightBrackets; textEditor.SetBinding(Control.FontFamilyProperty, new Binding { Source = DisplaySettingsPanel.CurrentDisplaySettings, Path = new PropertyPath("SelectedFont") }); textEditor.SetBinding(Control.FontSizeProperty, new Binding { Source = DisplaySettingsPanel.CurrentDisplaySettings, Path = new PropertyPath("SelectedFontSize") }); textEditor.SetBinding(TextEditor.WordWrapProperty, new Binding { Source = DisplaySettingsPanel.CurrentDisplaySettings, Path = new PropertyPath("EnableWordWrap") }); @@ -245,6 +248,18 @@ namespace ICSharpCode.ILSpy.TextView } #endregion + #region Highlight brackets + void HighlightBrackets(object sender, EventArgs e) + { + if (DisplaySettingsPanel.CurrentDisplaySettings.HighlightMatchingBraces) { + var result = MainWindow.Instance.CurrentLanguage.BracketSearcher.SearchBracket(textEditor.Document, textEditor.CaretOffset); + bracketHighlightRenderer.SetHighlight(result); + } else { + bracketHighlightRenderer.SetHighlight(null); + } + } + #endregion + #region RunWithCancellation /// /// Switches the GUI into "waiting" mode, then calls to create From 1aa9ea5f07f26d91591882d23726c002c8e4858b Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 9 Aug 2019 19:23:12 +0200 Subject: [PATCH 193/221] Add missing files. --- ILSpy/Languages/CSharpBracketSearcher.cs | 369 +++++++++++++++++++++ ILSpy/TextView/BracketHighlightRenderer.cs | 112 +++++++ 2 files changed, 481 insertions(+) create mode 100644 ILSpy/Languages/CSharpBracketSearcher.cs create mode 100644 ILSpy/TextView/BracketHighlightRenderer.cs diff --git a/ILSpy/Languages/CSharpBracketSearcher.cs b/ILSpy/Languages/CSharpBracketSearcher.cs new file mode 100644 index 000000000..18ad4dfbb --- /dev/null +++ b/ILSpy/Languages/CSharpBracketSearcher.cs @@ -0,0 +1,369 @@ +// Copyright (c) 2018 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using ICSharpCode.AvalonEdit.Document; +using ICSharpCode.ILSpy.TextView; + +namespace ICSharpCode.ILSpy +{ + /// + /// Searches matching brackets for C#. + /// + class CSharpBracketSearcher : IBracketSearcher + { + string openingBrackets = "([{"; + string closingBrackets = ")]}"; + + public BracketSearchResult SearchBracket(IDocument document, int offset) + { + if (offset > 0) { + char c = document.GetCharAt(offset - 1); + int index = openingBrackets.IndexOf(c); + int otherOffset = -1; + if (index > -1) + otherOffset = SearchBracketForward(document, offset, openingBrackets[index], closingBrackets[index]); + + index = closingBrackets.IndexOf(c); + if (index > -1) + otherOffset = SearchBracketBackward(document, offset - 2, openingBrackets[index], closingBrackets[index]); + + if (otherOffset > -1) { + var result = new BracketSearchResult(Math.Min(offset - 1, otherOffset), 1, + Math.Max(offset - 1, otherOffset), 1); + return result; + } + } + + return null; + } + + bool IsBracketOnly(IDocument document, IDocumentLine documentLine) + { + string lineText = document.GetText(documentLine).Trim(); + return lineText == "{" || string.IsNullOrEmpty(lineText) + || lineText.StartsWith("//", StringComparison.Ordinal) + || lineText.StartsWith("/*", StringComparison.Ordinal) + || lineText.StartsWith("*", StringComparison.Ordinal) + || lineText.StartsWith("'", StringComparison.Ordinal); + } + + #region SearchBracket helper functions + static int ScanLineStart(IDocument document, int offset) + { + for (int i = offset - 1; i > 0; --i) { + if (document.GetCharAt(i) == '\n') + return i + 1; + } + return 0; + } + + /// + /// Gets the type of code at offset.
+ /// 0 = Code,
+ /// 1 = Comment,
+ /// 2 = String
+ /// Block comments and multiline strings are not supported. + ///
+ static int GetStartType(IDocument document, int linestart, int offset) + { + bool inString = false; + bool inChar = false; + bool verbatim = false; + int result = 0; + for (int i = linestart; i < offset; i++) { + switch (document.GetCharAt(i)) { + case '/': + if (!inString && !inChar && i + 1 < document.TextLength) { + if (document.GetCharAt(i + 1) == '/') { + result = 1; + } + } + break; + case '"': + if (!inChar) { + if (inString && verbatim) { + if (i + 1 < document.TextLength && document.GetCharAt(i + 1) == '"') { + ++i; // skip escaped quote + inString = false; // let the string go on + } else { + verbatim = false; + } + } else if (!inString && i > 0 && document.GetCharAt(i - 1) == '@') { + verbatim = true; + } + inString = !inString; + } + break; + case '\'': + if (!inString) inChar = !inChar; + break; + case '\\': + if ((inString && !verbatim) || inChar) + ++i; // skip next character + break; + } + } + + return (inString || inChar) ? 2 : result; + } + #endregion + + #region SearchBracketBackward + int SearchBracketBackward(IDocument document, int offset, char openBracket, char closingBracket) + { + if (offset + 1 >= document.TextLength) return -1; + // this method parses a c# document backwards to find the matching bracket + + // first try "quick find" - find the matching bracket if there is no string/comment in the way + int quickResult = QuickSearchBracketBackward(document, offset, openBracket, closingBracket); + if (quickResult >= 0) return quickResult; + + // we need to parse the line from the beginning, so get the line start position + int linestart = ScanLineStart(document, offset + 1); + + // we need to know where offset is - in a string/comment or in normal code? + // ignore cases where offset is in a block comment + int starttype = GetStartType(document, linestart, offset + 1); + if (starttype == 1) { + return -1; // start position is in a comment + } + + // I don't see any possibility to parse a C# document backwards... + // We have to do it forwards and push all bracket positions on a stack. + Stack bracketStack = new Stack(); + bool blockComment = false; + bool lineComment = false; + bool inChar = false; + bool inString = false; + bool verbatim = false; + + for (int i = 0; i <= offset; ++i) { + char ch = document.GetCharAt(i); + switch (ch) { + case '\r': + case '\n': + lineComment = false; + inChar = false; + if (!verbatim) inString = false; + break; + case '/': + if (blockComment) { + Debug.Assert(i > 0); + if (document.GetCharAt(i - 1) == '*') { + blockComment = false; + } + } + if (!inString && !inChar && i + 1 < document.TextLength) { + if (!blockComment && document.GetCharAt(i + 1) == '/') { + lineComment = true; + } + if (!lineComment && document.GetCharAt(i + 1) == '*') { + blockComment = true; + } + } + break; + case '"': + if (!(inChar || lineComment || blockComment)) { + if (inString && verbatim) { + if (i + 1 < document.TextLength && document.GetCharAt(i + 1) == '"') { + ++i; // skip escaped quote + inString = false; // let the string go + } else { + verbatim = false; + } + } else if (!inString && offset > 0 && document.GetCharAt(i - 1) == '@') { + verbatim = true; + } + inString = !inString; + } + break; + case '\'': + if (!(inString || lineComment || blockComment)) { + inChar = !inChar; + } + break; + case '\\': + if ((inString && !verbatim) || inChar) + ++i; // skip next character + break; + default: + if (ch == openBracket) { + if (!(inString || inChar || lineComment || blockComment)) { + bracketStack.Push(i); + } + } else if (ch == closingBracket) { + if (!(inString || inChar || lineComment || blockComment)) { + if (bracketStack.Count > 0) + bracketStack.Pop(); + } + } + break; + } + } + if (bracketStack.Count > 0) return (int)bracketStack.Pop(); + return -1; + } + #endregion + + #region SearchBracketForward + int SearchBracketForward(IDocument document, int offset, char openBracket, char closingBracket) + { + bool inString = false; + bool inChar = false; + bool verbatim = false; + + bool lineComment = false; + bool blockComment = false; + + if (offset < 0) return -1; + + // first try "quick find" - find the matching bracket if there is no string/comment in the way + int quickResult = QuickSearchBracketForward(document, offset, openBracket, closingBracket); + if (quickResult >= 0) return quickResult; + + // we need to parse the line from the beginning, so get the line start position + int linestart = ScanLineStart(document, offset); + + // we need to know where offset is - in a string/comment or in normal code? + // ignore cases where offset is in a block comment + int starttype = GetStartType(document, linestart, offset); + if (starttype != 0) return -1; // start position is in a comment/string + + int brackets = 1; + + while (offset < document.TextLength) { + char ch = document.GetCharAt(offset); + switch (ch) { + case '\r': + case '\n': + lineComment = false; + inChar = false; + if (!verbatim) inString = false; + break; + case '/': + if (blockComment) { + Debug.Assert(offset > 0); + if (document.GetCharAt(offset - 1) == '*') { + blockComment = false; + } + } + if (!inString && !inChar && offset + 1 < document.TextLength) { + if (!blockComment && document.GetCharAt(offset + 1) == '/') { + lineComment = true; + } + if (!lineComment && document.GetCharAt(offset + 1) == '*') { + blockComment = true; + } + } + break; + case '"': + if (!(inChar || lineComment || blockComment)) { + if (inString && verbatim) { + if (offset + 1 < document.TextLength && document.GetCharAt(offset + 1) == '"') { + ++offset; // skip escaped quote + inString = false; // let the string go + } else { + verbatim = false; + } + } else if (!inString && offset > 0 && document.GetCharAt(offset - 1) == '@') { + verbatim = true; + } + inString = !inString; + } + break; + case '\'': + if (!(inString || lineComment || blockComment)) { + inChar = !inChar; + } + break; + case '\\': + if ((inString && !verbatim) || inChar) + ++offset; // skip next character + break; + default: + if (ch == openBracket) { + if (!(inString || inChar || lineComment || blockComment)) { + ++brackets; + } + } else if (ch == closingBracket) { + if (!(inString || inChar || lineComment || blockComment)) { + --brackets; + if (brackets == 0) { + return offset; + } + } + } + break; + } + ++offset; + } + return -1; + } + #endregion + + int QuickSearchBracketBackward(IDocument document, int offset, char openBracket, char closingBracket) + { + int brackets = -1; + // first try "quick find" - find the matching bracket if there is no string/comment in the way + for (int i = offset; i >= 0; --i) { + char ch = document.GetCharAt(i); + if (ch == openBracket) { + ++brackets; + if (brackets == 0) return i; + } else if (ch == closingBracket) { + --brackets; + } else if (ch == '"') { + break; + } else if (ch == '\'') { + break; + } else if (ch == '/' && i > 0) { + if (document.GetCharAt(i - 1) == '/') break; + if (document.GetCharAt(i - 1) == '*') break; + } + } + return -1; + } + + int QuickSearchBracketForward(IDocument document, int offset, char openBracket, char closingBracket) + { + int brackets = 1; + // try "quick find" - find the matching bracket if there is no string/comment in the way + for (int i = offset; i < document.TextLength; ++i) { + char ch = document.GetCharAt(i); + if (ch == openBracket) { + ++brackets; + } else if (ch == closingBracket) { + --brackets; + if (brackets == 0) return i; + } else if (ch == '"') { + break; + } else if (ch == '\'') { + break; + } else if (ch == '/' && i > 0) { + if (document.GetCharAt(i - 1) == '/') break; + } else if (ch == '*' && i > 0) { + if (document.GetCharAt(i - 1) == '/') break; + } + } + return -1; + } + } +} diff --git a/ILSpy/TextView/BracketHighlightRenderer.cs b/ILSpy/TextView/BracketHighlightRenderer.cs new file mode 100644 index 000000000..6a814b7c4 --- /dev/null +++ b/ILSpy/TextView/BracketHighlightRenderer.cs @@ -0,0 +1,112 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) + +using System; +using System.Windows.Media; +using ICSharpCode.AvalonEdit.Document; +using ICSharpCode.AvalonEdit.Rendering; + +namespace ICSharpCode.ILSpy.TextView +{ + /// + /// Allows language specific search for matching brackets. + /// + public interface IBracketSearcher + { + /// + /// Searches for a matching bracket from the given offset to the start of the document. + /// + /// A BracketSearchResult that contains the positions and lengths of the brackets. Return null if there is nothing to highlight. + BracketSearchResult SearchBracket(IDocument document, int offset); + } + + public class DefaultBracketSearcher : IBracketSearcher + { + public static readonly DefaultBracketSearcher DefaultInstance = new DefaultBracketSearcher(); + + public BracketSearchResult SearchBracket(IDocument document, int offset) + { + return null; + } + } + + /// + /// Describes a pair of matching brackets found by . + /// + public class BracketSearchResult + { + public int OpeningBracketOffset { get; private set; } + + public int OpeningBracketLength { get; private set; } + + public int ClosingBracketOffset { get; private set; } + + public int ClosingBracketLength { get; private set; } + + public BracketSearchResult(int openingBracketOffset, int openingBracketLength, + int closingBracketOffset, int closingBracketLength) + { + this.OpeningBracketOffset = openingBracketOffset; + this.OpeningBracketLength = openingBracketLength; + this.ClosingBracketOffset = closingBracketOffset; + this.ClosingBracketLength = closingBracketLength; + } + } + + public class BracketHighlightRenderer : IBackgroundRenderer + { + BracketSearchResult result; + Pen borderPen; + Brush backgroundBrush; + ICSharpCode.AvalonEdit.Rendering.TextView textView; + + public void SetHighlight(BracketSearchResult result) + { + if (this.result != result) { + this.result = result; + textView.InvalidateLayer(this.Layer); + } + } + + public BracketHighlightRenderer(ICSharpCode.AvalonEdit.Rendering.TextView textView) + { + if (textView == null) + throw new ArgumentNullException("textView"); + + this.borderPen = new Pen(new SolidColorBrush(Color.FromArgb(52, 0, 0, 255)), 1); + this.borderPen.Freeze(); + + this.backgroundBrush = new SolidColorBrush(Color.FromArgb(22, 0, 0, 255)); + this.backgroundBrush.Freeze(); + + this.textView = textView; + + this.textView.BackgroundRenderers.Add(this); + } + + public KnownLayer Layer { + get { + return KnownLayer.Selection; + } + } + + public void Draw(ICSharpCode.AvalonEdit.Rendering.TextView textView, DrawingContext drawingContext) + { + if (this.result == null) + return; + + BackgroundGeometryBuilder builder = new BackgroundGeometryBuilder(); + + builder.CornerRadius = 1; + + builder.AddSegment(textView, new TextSegment() { StartOffset = result.OpeningBracketOffset, Length = result.OpeningBracketLength }); + builder.CloseFigure(); // prevent connecting the two segments + builder.AddSegment(textView, new TextSegment() { StartOffset = result.ClosingBracketOffset, Length = result.ClosingBracketLength }); + + Geometry geometry = builder.CreateGeometry(); + if (geometry != null) { + drawingContext.DrawGeometry(backgroundBrush, borderPen, geometry); + } + } + } +} \ No newline at end of file From cccd02c2dac3fd573e48863b11bc946f7644d8e4 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 10 Aug 2019 10:52:03 +0200 Subject: [PATCH 194/221] Remove unused code. --- ILSpy/Languages/CSharpBracketSearcher.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/ILSpy/Languages/CSharpBracketSearcher.cs b/ILSpy/Languages/CSharpBracketSearcher.cs index 18ad4dfbb..bc12dd7f5 100644 --- a/ILSpy/Languages/CSharpBracketSearcher.cs +++ b/ILSpy/Languages/CSharpBracketSearcher.cs @@ -55,16 +55,6 @@ namespace ICSharpCode.ILSpy return null; } - bool IsBracketOnly(IDocument document, IDocumentLine documentLine) - { - string lineText = document.GetText(documentLine).Trim(); - return lineText == "{" || string.IsNullOrEmpty(lineText) - || lineText.StartsWith("//", StringComparison.Ordinal) - || lineText.StartsWith("/*", StringComparison.Ordinal) - || lineText.StartsWith("*", StringComparison.Ordinal) - || lineText.StartsWith("'", StringComparison.Ordinal); - } - #region SearchBracket helper functions static int ScanLineStart(IDocument document, int offset) { From f49cc11c76147edfb830263e0558ecfcc43724ad Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 10 Aug 2019 10:52:49 +0200 Subject: [PATCH 195/221] Fix #1627: Allow constants as targets of dynamic calls. --- .../TestCases/Pretty/DynamicTests.cs | 5 +++++ ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs index c12a38c6d..2e299b63e 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs @@ -410,6 +410,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } + private static bool ConstantTarget(dynamic a) + { + return true.Equals(a); + } + private static dynamic GetDynamic(int i) { return null; diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 2a1b4ead4..575eb9d02 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -2891,7 +2891,6 @@ namespace ICSharpCode.Decompiler.CSharp { Debug.Assert(!argumentInfo.HasFlag(CSharpArgumentInfoFlags.NamedArgument)); Debug.Assert(!argumentInfo.HasFlag(CSharpArgumentInfoFlags.IsOut)); - Debug.Assert(!argumentInfo.HasFlag(CSharpArgumentInfoFlags.Constant)); if (argumentInfo.HasFlag(CSharpArgumentInfoFlags.IsStaticType) && IL.Transforms.TransformExpressionTrees.MatchGetTypeFromHandle(inst, out var callTargetType)) { return new TypeReferenceExpression(ConvertType(callTargetType)) From 13fa499c3547112c3c11200b7d33796c9f0c8efb Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 10 Aug 2019 19:03:25 +0200 Subject: [PATCH 196/221] Fix #1630: Do not convert while to for statement, if any iterator variables are to be declared in the loop body. This causes problems with ref-typed variables. --- .../TestCases/Pretty/RefLocalsAndReturns.cs | 19 +++++++++++++++++ .../CSharp/Transforms/DeclareVariables.cs | 2 +- .../Transforms/PatternStatementTransform.cs | 21 +++++++++++++++++-- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs index 385e0932c..63712439a 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/RefLocalsAndReturns.cs @@ -26,6 +26,25 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty internal class RefLocalsAndReturns { + public struct Issue1630 + { + private object data; + + private int next; + + public static void Test() + { + Issue1630[] array = new Issue1630[1]; + int num = 0; + while (num >= 0) { + ref Issue1630 reference = ref array[num]; + Console.WriteLine(reference.data); + num = reference.next; + } + } + } + + public delegate ref T RefFunc(); public delegate ref readonly T ReadOnlyRefFunc(); public delegate ref TReturn RefFunc(T1 param1); diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs index f9cb5633e..c9310107a 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs @@ -291,7 +291,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } } - bool VariableNeedsDeclaration(VariableKind kind) + internal static bool VariableNeedsDeclaration(VariableKind kind) { switch (kind) { case VariableKind.PinnedLocal: diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs index df4d62112..c96419502 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs @@ -153,7 +153,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms Statements = { new Repeat(new AnyNode("statement")), new NamedNode( - "increment", + "iterator", new ExpressionStatement( new AssignmentExpression { Left = new Backreference("ident"), @@ -180,6 +180,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms if (variable != m3.Get("ident").Single().GetILVariable()) return null; WhileStatement loop = (WhileStatement)next; + // Cannot convert to for loop, if any variable that is used in the "iterator" part of the pattern, + // will be declared in the body of the while-loop. + var iteratorStatement = m3.Get("iterator").Single(); + if (IteratorVariablesDeclaredInsideLoopBody(iteratorStatement)) + return null; // Cannot convert to for loop, because that would change the semantics of the program. // continue in while jumps to the condition block. // Whereas continue in for jumps to the increment block. @@ -193,7 +198,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms forStatement.CopyAnnotationsFrom(loop); forStatement.Initializers.Add(node); forStatement.Condition = loop.Condition.Detach(); - forStatement.Iterators.Add(m3.Get("increment").Single().Detach()); + forStatement.Iterators.Add(iteratorStatement.Detach()); forStatement.EmbeddedStatement = newBody; loop.ReplaceWith(forStatement); return forStatement; @@ -216,6 +221,18 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms return true; return false; } + + bool IteratorVariablesDeclaredInsideLoopBody(Statement iteratorStatement) + { + foreach (var id in iteratorStatement.DescendantsAndSelf.OfType()) { + var v = id.GetILVariable(); + if (v == null || !DeclareVariables.VariableNeedsDeclaration(v.Kind)) + continue; + if (declareVariables.GetDeclarationPoint(v).Parent == iteratorStatement.Parent) + return true; + } + return false; + } #endregion #region foreach From 587f61be19c7415452b374b1945e67c5f2d8a9cc Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 10 Aug 2019 23:34:16 +0200 Subject: [PATCH 197/221] Fix #1280: Transform RuntimeHelpersInitializeArray calls without receiving assignments. --- .../Transforms/TransformArrayInitializers.cs | 67 ++++++++++++++----- 1 file changed, 49 insertions(+), 18 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs index 8d4ff3e14..6a9d149c1 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformArrayInitializers.cs @@ -45,6 +45,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms return; if (context.Settings.StackAllocInitializers && DoTransformStackAllocInitializer(block, pos)) return; + if (DoTransformInlineRuntimeHelpersInitializeArray(block, pos)) + return; } finally { this.context = null; } @@ -56,7 +58,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; ILInstruction inst = body.Instructions[pos]; if (inst.MatchStLoc(out var v, out var newarrExpr) && MatchNewArr(newarrExpr, out var elementType, out var arrayLength)) { - if (HandleRuntimeHelperInitializeArray(body, pos + 1, v, elementType, arrayLength, out var values, out var initArrayPos)) { + if (HandleRuntimeHelpersInitializeArray(body, pos + 1, v, elementType, arrayLength, out var values, out var initArrayPos)) { context.Step("HandleRuntimeHelperInitializeArray: single-dim", inst); var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, v.Type); var block = BlockFromInitializer(tempStore, elementType, arrayLength, values); @@ -146,8 +148,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; ILInstruction inst = body.Instructions[pos]; if (inst.MatchStLoc(out var v, out var newarrExpr) && MatchNewArr(newarrExpr, out var elementType, out var length)) { - if (HandleRuntimeHelperInitializeArray(body, pos + 1, v, elementType, length, out var values, out var initArrayPos)) { - context.Step("HandleRuntimeHelperInitializeArray: multi-dim", inst); + if (HandleRuntimeHelpersInitializeArray(body, pos + 1, v, elementType, length, out var values, out var initArrayPos)) { + context.Step("HandleRuntimeHelpersInitializeArray: multi-dim", inst); var block = BlockFromInitializer(v, elementType, length, values); body.Instructions[pos].ReplaceWith(new StLoc(v, block)); body.Instructions.RemoveAt(initArrayPos); @@ -534,7 +536,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - bool MatchInitializeArrayCall(ILInstruction instruction, out ILVariable array, out FieldDefinition field) + bool MatchInitializeArrayCall(ILInstruction instruction, out ILInstruction array, out FieldDefinition field) { array = null; field = default; @@ -549,8 +551,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms { return false; } - if (!call.Arguments[0].MatchLdLoc(out array)) - return false; + array = call.Arguments[0]; if (!call.Arguments[1].MatchLdMemberToken(out var member)) return false; if (member.MetadataToken.IsNil) @@ -559,9 +560,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms return true; } - bool HandleRuntimeHelperInitializeArray(Block body, int pos, ILVariable array, IType arrayType, int[] arrayLength, out ILInstruction[] values, out int foundPos) + bool HandleRuntimeHelpersInitializeArray(Block body, int pos, ILVariable array, IType arrayType, int[] arrayLength, out ILInstruction[] values, out int foundPos) { - if (MatchInitializeArrayCall(body.Instructions[pos], out var v2, out var field) && array == v2) { + if (MatchInitializeArrayCall(body.Instructions[pos], out var arrayInst, out var field) && arrayInst.MatchLdLoc(array)) { if (field.HasFlag(System.Reflection.FieldAttributes.HasFieldRVA)) { var valuesList = new List(); var initialValue = field.GetInitialValue(context.PEFile.Reader, context.TypeSystem); @@ -577,30 +578,60 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } + /// + /// call InitializeArray(newarr T(size), ldmembertoken fieldToken) + /// => + /// Block (ArrayInitializer) { + /// stloc i(newarr T(size)) + /// stobj T(ldelema T(... indices ...), value) + /// final: ldloc i + /// } + /// + bool DoTransformInlineRuntimeHelpersInitializeArray(Block body, int pos) + { + var inst = body.Instructions[pos]; + if (!MatchInitializeArrayCall(inst, out var arrayInst, out var field)) + return false; + if (!MatchNewArr(arrayInst, out var elementType, out var arrayLength)) + return false; + if (!field.HasFlag(System.Reflection.FieldAttributes.HasFieldRVA)) + return false; + var valuesList = new List(); + var initialValue = field.GetInitialValue(context.PEFile.Reader, context.TypeSystem); + if (!DecodeArrayInitializer(elementType, initialValue, arrayLength, valuesList)) + return false; + context.Step("InlineRuntimeHelpersInitializeArray: single-dim", inst); + var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, new ArrayType(context.TypeSystem, elementType, arrayLength.Length)); + var block = BlockFromInitializer(tempStore, elementType, arrayLength, valuesList.ToArray()); + body.Instructions[pos] = block; + ILInlining.InlineIfPossible(body, pos, context); + return true; + } + static bool DecodeArrayInitializer(IType type, BlobReader initialValue, int[] arrayLength, List output) { TypeCode typeCode = ReflectionHelper.GetTypeCode(type); switch (typeCode) { case TypeCode.Boolean: case TypeCode.Byte: - return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, type, (ref BlobReader r) => new LdcI4(r.ReadByte())); + return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, (ref BlobReader r) => new LdcI4(r.ReadByte())); case TypeCode.SByte: - return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, type, (ref BlobReader r) => new LdcI4(r.ReadSByte())); + return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, (ref BlobReader r) => new LdcI4(r.ReadSByte())); case TypeCode.Int16: - return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, type, (ref BlobReader r) => new LdcI4(r.ReadInt16())); + return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, (ref BlobReader r) => new LdcI4(r.ReadInt16())); case TypeCode.Char: case TypeCode.UInt16: - return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, type, (ref BlobReader r) => new LdcI4(r.ReadUInt16())); + return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, (ref BlobReader r) => new LdcI4(r.ReadUInt16())); case TypeCode.Int32: case TypeCode.UInt32: - return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, type, (ref BlobReader r) => new LdcI4(r.ReadInt32())); + return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, (ref BlobReader r) => new LdcI4(r.ReadInt32())); case TypeCode.Int64: case TypeCode.UInt64: - return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, type, (ref BlobReader r) => new LdcI8(r.ReadInt64())); + return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, (ref BlobReader r) => new LdcI8(r.ReadInt64())); case TypeCode.Single: - return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, type, (ref BlobReader r) => new LdcF4(r.ReadSingle())); + return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, (ref BlobReader r) => new LdcF4(r.ReadSingle())); case TypeCode.Double: - return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, type, (ref BlobReader r) => new LdcF8(r.ReadDouble())); + return DecodeArrayInitializer(initialValue, arrayLength, output, typeCode, (ref BlobReader r) => new LdcF8(r.ReadDouble())); case TypeCode.Object: case TypeCode.Empty: var typeDef = type.GetDefinition(); @@ -615,7 +646,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms delegate ILInstruction ValueDecoder(ref BlobReader reader); static bool DecodeArrayInitializer(BlobReader initialValue, int[] arrayLength, - List output, TypeCode elementType, IType type, ValueDecoder decoder) + List output, TypeCode elementType, ValueDecoder decoder) { int elementSize = ElementSizeOf(elementType); var totalLength = arrayLength.Aggregate(1, (t, l) => t * l); @@ -627,7 +658,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms int next = i; for (int j = arrayLength.Length - 1; j >= 0; j--) { output.Add(new LdcI4(next % arrayLength[j])); - next = next / arrayLength[j]; + next /= arrayLength[j]; } } From 84b9f1c4f47aa58a89300bdd376f5c90a3f8dcd2 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 11 Aug 2019 12:37:55 +0200 Subject: [PATCH 198/221] Fix #1514: Allow StackType.I8 and StackType.I4 in HandleManagedPointerArithmetic (ref [+-] int). --- ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 575eb9d02..239d84976 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -1085,7 +1085,7 @@ namespace ICSharpCode.Decompiler.CSharp // ref - ref => i return CallUnsafeIntrinsic("ByteOffset", new[] { left.Expression, right.Expression }, compilation.FindType(KnownTypeCode.IntPtr), inst); } - if (inst.LeftInputType == StackType.Ref && inst.RightInputType == StackType.I + if (inst.LeftInputType == StackType.Ref && inst.RightInputType.IsIntegerType() && left.Type is ByReferenceType brt) { // ref [+-] int string name = (inst.Operator == BinaryNumericOperator.Sub ? "Subtract" : "Add"); From e5d603ced203456ea291076f65678557fe7003b2 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 11 Aug 2019 13:40:59 +0200 Subject: [PATCH 199/221] Fix #1632: await pattern might use negated IsCompleted check. --- .../IL/ControlFlow/AsyncAwaitDecompiler.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs index c122cabdd..fd4e3d4dd 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs @@ -730,15 +730,21 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow // if (call get_IsCompleted(ldloca awaiterVar)) br completedBlock if (!block.Instructions[block.Instructions.Count - 2].MatchIfInstruction(out var condition, out var trueInst)) return; - if (!MatchCall(condition, "get_IsCompleted", out var isCompletedArgs) || isCompletedArgs.Count != 1) - return; - if (!isCompletedArgs[0].MatchLdLocRef(awaiterVar)) - return; if (!trueInst.MatchBranch(out var completedBlock)) return; // br awaitBlock if (!block.Instructions.Last().MatchBranch(out var awaitBlock)) return; + // condition might be inverted, swap branches: + if (condition.MatchLogicNot(out var negatedCondition)) { + condition = negatedCondition; + ExtensionMethods.Swap(ref completedBlock, ref awaitBlock); + } + // continue matching call get_IsCompleted(ldloca awaiterVar) + if (!MatchCall(condition, "get_IsCompleted", out var isCompletedArgs) || isCompletedArgs.Count != 1) + return; + if (!isCompletedArgs[0].MatchLdLocRef(awaiterVar)) + return; // Check awaitBlock and resumeBlock: if (!awaitBlocks.TryGetValue(awaitBlock, out var awaitBlockData)) return; From 7afa86d90c6662039353cde8e1a3be3d8437ddf6 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 10 Aug 2019 15:08:48 +0200 Subject: [PATCH 200/221] Add support for "where T : notnull" constraint. --- .../TestCases/Pretty/NullableRefTypes.cs | 4 ++++ .../CSharp/Syntax/TypeSystemAstBuilder.cs | 4 +++- .../TypeSystem/Implementation/MetadataTypeParameter.cs | 8 +++++++- ILSpy/Languages/CSharpHighlightingTokenWriter.cs | 5 ++++- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs index 1bc660bd4..de00697f1 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs @@ -48,6 +48,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty ByRef(ref a).ToString(); ByRef(ref b)!.ToString(); } + + public void Constraints() where C : class where CN : class? where NN : notnull where S : struct where D : IDisposable where DN : IDisposable? where NND : notnull, IDisposable + { + } } public class T02_EverythingIsNullableInHere diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index 2e3009fa3..53836a230 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -1785,7 +1785,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax Constraint ConvertTypeParameterConstraint(ITypeParameter tp) { - if (!tp.HasDefaultConstructorConstraint && !tp.HasReferenceTypeConstraint && !tp.HasValueTypeConstraint && tp.DirectBaseTypes.All(IsObjectOrValueType)) { + if (!tp.HasDefaultConstructorConstraint && !tp.HasReferenceTypeConstraint && !tp.HasValueTypeConstraint && tp.NullabilityConstraint != Nullability.NotNullable && tp.DirectBaseTypes.All(IsObjectOrValueType)) { return null; } Constraint c = new Constraint(); @@ -1802,6 +1802,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } else { c.BaseTypes.Add(new PrimitiveType("struct")); } + } else if (tp.NullabilityConstraint == Nullability.NotNullable) { + c.BaseTypes.Add(new PrimitiveType("notnull")); } foreach (IType t in tp.DirectBaseTypes) { if (!IsObjectOrValueType(t)) diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs index 862fc4482..b7549e6ac 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs @@ -161,7 +161,13 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } } } - return Nullability.Oblivious; + if (Owner is MetadataMethod method) { + return method.NullableContext; + } else if (Owner is ITypeDefinition td) { + return td.NullableContext; + } else { + return Nullability.Oblivious; + } } public override IEnumerable DirectBaseTypes { diff --git a/ILSpy/Languages/CSharpHighlightingTokenWriter.cs b/ILSpy/Languages/CSharpHighlightingTokenWriter.cs index cf0464067..ddd989831 100644 --- a/ILSpy/Languages/CSharpHighlightingTokenWriter.cs +++ b/ILSpy/Languages/CSharpHighlightingTokenWriter.cs @@ -272,7 +272,9 @@ namespace ICSharpCode.ILSpy HighlightingColor color = null; switch (type) { case "new": - color = typeKeywordsColor; + case "notnull": + // Not sure if reference type or value type + color = referenceTypeKeywordsColor; break; case "bool": case "byte": @@ -289,6 +291,7 @@ namespace ICSharpCode.ILSpy case "uint": case "ushort": case "ulong": + case "unmanaged": color = valueTypeKeywordsColor; break; case "class": From 152aa2a21bfae6bd333f5be9333cb9f72932eece Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 10 Aug 2019 15:30:29 +0200 Subject: [PATCH 201/221] IL Disassembler: show attributes on constraints dotnet/coreclr#25488 --- .../Disassembler/ReflectionDisassembler.cs | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs index ba1f1f985..2f9712599 100644 --- a/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs +++ b/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs @@ -310,7 +310,7 @@ namespace ICSharpCode.Decompiler.Disassembler } foreach (var p in methodDefinition.GetGenericParameters()) { - WriteGenericParameterAttributes(module, p); + WriteGenericParameterAttributes(module, genericContext, p); } foreach (var p in methodDefinition.GetParameters()) { WriteParameterAttributes(module, p); @@ -1005,15 +1005,28 @@ namespace ICSharpCode.Decompiler.Disassembler output.WriteLine(); } - void WriteGenericParameterAttributes(PEFile module, GenericParameterHandle handle) + void WriteGenericParameterAttributes(PEFile module, GenericContext context, GenericParameterHandle handle) { var metadata = module.Metadata; var p = metadata.GetGenericParameter(handle); - if (p.GetCustomAttributes().Count == 0) - return; - output.Write(".param type {0}", metadata.GetString(p.Name)); - output.WriteLine(); - WriteAttributes(module, p.GetCustomAttributes()); + if (p.GetCustomAttributes().Count > 0) { + output.Write(".param type {0}", metadata.GetString(p.Name)); + output.WriteLine(); + output.Indent(); + WriteAttributes(module, p.GetCustomAttributes()); + output.Unindent(); + } + foreach (var constraintHandle in p.GetConstraints()) { + var constraint = metadata.GetGenericParameterConstraint(constraintHandle); + if (constraint.GetCustomAttributes().Count > 0) { + output.Write(".param constraint {0}, ", metadata.GetString(p.Name)); + constraint.Type.WriteTo(module, output, context, ILNameSyntax.TypeName); + output.WriteLine(); + output.Indent(); + WriteAttributes(module, constraint.GetCustomAttributes()); + output.Unindent(); + } + } } void WriteParameterAttributes(PEFile module, ParameterHandle handle) @@ -1028,7 +1041,9 @@ namespace ICSharpCode.Decompiler.Disassembler WriteConstant(metadata, metadata.GetConstant(p.GetDefaultValue())); } output.WriteLine(); + output.Indent(); WriteAttributes(module, p.GetCustomAttributes()); + output.Unindent(); } void WriteConstant(MetadataReader metadata, Constant constant) @@ -1378,6 +1393,9 @@ namespace ICSharpCode.Decompiler.Disassembler isInType = true; WriteAttributes(module, typeDefinition.GetCustomAttributes()); WriteSecurityDeclarations(module, typeDefinition.GetDeclarativeSecurityAttributes()); + foreach (var tp in typeDefinition.GetGenericParameters()) { + WriteGenericParameterAttributes(module, genericContext, tp); + } var layout = typeDefinition.GetLayout(); if (!layout.IsDefault) { output.WriteLine(".pack {0}", layout.PackingSize); From c1510027dfc523f77c0bcb14401c0728e180377e Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 10 Aug 2019 16:54:52 +0200 Subject: [PATCH 202/221] Show attributes on type parameter constraints in C# decompilation. --- .../OutputVisitor/CSharpOutputVisitor.cs | 18 ++++++++--- .../CSharp/Syntax/ComposedType.cs | 4 +++ .../CSharp/Syntax/GeneralScope/Constraint.cs | 4 +-- .../Syntax/TypeMembers/EntityDeclaration.cs | 1 - .../CSharp/Syntax/TypeSystemAstBuilder.cs | 16 ++++++++-- ICSharpCode.Decompiler/TypeSystem/ISymbol.cs | 4 +++ .../TypeSystem/ITypeParameter.cs | 17 ++++++++++ .../Implementation/AbstractTypeParameter.cs | 6 +++- .../Implementation/DefaultTypeParameter.cs | 28 ++++++++++------- .../Implementation/DummyTypeParameter.cs | 2 ++ .../Implementation/MetadataTypeParameter.cs | 31 ++++++++++++------- .../NullabilityAnnotatedType.cs | 1 + .../Implementation/SpecializedMethod.cs | 11 +++++-- 13 files changed, 107 insertions(+), 36 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs index da33b215e..618f0d5af 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -1313,10 +1313,15 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } WriteCommaSeparatedList(attributeSection.Attributes); WriteToken(Roles.RBracket); - if (attributeSection.Parent is ParameterDeclaration || attributeSection.Parent is TypeParameterDeclaration) { - Space(); - } else { - NewLine(); + switch (attributeSection.Parent) { + case ParameterDeclaration _: + case TypeParameterDeclaration _: + case ComposedType _: + Space(); + break; + default: + NewLine(); + break; } EndNode(attributeSection); } @@ -2349,6 +2354,11 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor public virtual void VisitComposedType(ComposedType composedType) { StartNode(composedType); + if (composedType.Attributes.Any()) { + foreach (var attr in composedType.Attributes) { + attr.AcceptVisitor(this); + } + } if (composedType.HasRefSpecifier) { WriteKeyword(ComposedType.RefRole); } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/ComposedType.cs b/ICSharpCode.Decompiler/CSharp/Syntax/ComposedType.cs index e05857a11..79363dc99 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/ComposedType.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/ComposedType.cs @@ -35,11 +35,15 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { public class ComposedType : AstType { + public static readonly Role AttributeRole = EntityDeclaration.AttributeRole; public static readonly TokenRole RefRole = new TokenRole("ref"); public static readonly TokenRole ReadonlyRole = new TokenRole("readonly"); public static readonly TokenRole NullableRole = new TokenRole("?"); public static readonly TokenRole PointerRole = new TokenRole("*"); public static readonly Role ArraySpecifierRole = new Role("ArraySpecifier"); + public AstNodeCollection Attributes { + get { return base.GetChildrenByRole(AttributeRole); } + } /// /// Gets/sets whether this type has a 'ref' specifier. diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Constraint.cs b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Constraint.cs index 1bd6b59a2..ec1d707a1 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Constraint.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/GeneralScope/Constraint.cs @@ -36,9 +36,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public class Constraint : AstNode { public override NodeType NodeType { - get { - return NodeType.Unknown; - } + get { return NodeType.Unknown; } } public CSharpTokenNode WhereKeyword { diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EntityDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EntityDeclaration.cs index 2bca3eb50..a8e254c1d 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EntityDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/EntityDeclaration.cs @@ -25,7 +25,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public abstract class EntityDeclaration : AstNode { public static readonly Role AttributeRole = new Role("Attribute"); - public static readonly Role UnattachedAttributeRole = new Role("UnattachedAttribute"); public static readonly Role ModifierRole = new Role("Modifier"); public static readonly Role PrivateImplementationTypeRole = new Role("PrivateImplementationType", AstType.Null); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index 53836a230..02416b7e0 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -1805,9 +1805,19 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } else if (tp.NullabilityConstraint == Nullability.NotNullable) { c.BaseTypes.Add(new PrimitiveType("notnull")); } - foreach (IType t in tp.DirectBaseTypes) { - if (!IsObjectOrValueType(t)) - c.BaseTypes.Add(ConvertType(t)); + foreach (TypeConstraint t in tp.TypeConstraints) { + if (!IsObjectOrValueType(t.Type) || t.Attributes.Count > 0) { + AstType astType = ConvertType(t.Type); + if (t.Attributes.Count > 0) { + var attrSection = new AttributeSection(); + attrSection.Attributes.AddRange(t.Attributes.Select(ConvertAttribute)); + astType = new ComposedType { + Attributes = { attrSection }, + BaseType = astType + }; + } + c.BaseTypes.Add(astType); + } } if (tp.HasDefaultConstructorConstraint && !tp.HasValueTypeConstraint) { c.BaseTypes.Add(new PrimitiveType("new")); diff --git a/ICSharpCode.Decompiler/TypeSystem/ISymbol.cs b/ICSharpCode.Decompiler/TypeSystem/ISymbol.cs index 89c1b7984..b1b59c404 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ISymbol.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ISymbol.cs @@ -69,6 +69,10 @@ namespace ICSharpCode.Decompiler.TypeSystem Parameter, /// TypeParameter, + /// + /// Constraint on a type parameter. + /// + Constraint, } /// diff --git a/ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs index 0db477fb8..8ff935184 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ITypeParameter.cs @@ -16,7 +16,9 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System; using System.Collections.Generic; +using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.TypeSystem { @@ -99,6 +101,21 @@ namespace ICSharpCode.Decompiler.TypeSystem /// E.g. "T? GetNull<T>() where T : class => null;" /// Nullability NullabilityConstraint { get; } + + IReadOnlyList TypeConstraints { get; } + } + + public readonly struct TypeConstraint + { + public SymbolKind SymbolKind => SymbolKind.Constraint; + public IType Type { get; } + public IReadOnlyList Attributes { get; } + + public TypeConstraint(IType type, IReadOnlyList attributes = null) + { + this.Type = type ?? throw new ArgumentNullException(nameof(type)); + this.Attributes = attributes ?? EmptyList.Instance; + } } /// diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs index 7ad3d137c..12de07edc 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs @@ -220,7 +220,11 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation get { return EmptyList.Instance; } } - public abstract IEnumerable DirectBaseTypes { get; } + public IEnumerable DirectBaseTypes { + get { return TypeConstraints.Select(t => t.Type); } + } + + public abstract IReadOnlyList TypeConstraints { get; } public string Name { get { return name; } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultTypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultTypeParameter.cs index e99e17af7..8ff30441a 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultTypeParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/DefaultTypeParameter.cs @@ -16,6 +16,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System; using System.Collections.Generic; using ICSharpCode.Decompiler.Util; @@ -43,10 +44,10 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation this.hasReferenceTypeConstraint = hasReferenceTypeConstraint; this.hasDefaultConstructorConstraint = hasDefaultConstructorConstraint; this.nullabilityConstraint = nullabilityConstraint; - this.constraints = constraints ?? EmptyList.Instance; + this.TypeConstraints = MakeConstraints(constraints); this.attributes = attributes ?? EmptyList.Instance; } - + public DefaultTypeParameter( ICompilation compilation, SymbolKind ownerType, int index, string name = null, @@ -60,7 +61,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation this.hasReferenceTypeConstraint = hasReferenceTypeConstraint; this.hasDefaultConstructorConstraint = hasDefaultConstructorConstraint; this.nullabilityConstraint = nullabilityConstraint; - this.constraints = constraints ?? EmptyList.Instance; + this.TypeConstraints = MakeConstraints(constraints); this.attributes = attributes ?? EmptyList.Instance; } @@ -72,19 +73,24 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public override bool HasUnmanagedConstraint => false; public override Nullability NullabilityConstraint => nullabilityConstraint; - public override IEnumerable DirectBaseTypes { - get { - bool hasNonInterfaceConstraint = false; + public override IReadOnlyList TypeConstraints { get; } + + IReadOnlyList MakeConstraints(IReadOnlyList constraints) + { + var result = new List(); + bool hasNonInterfaceConstraint = false; + if (constraints != null) { foreach (IType c in constraints) { - yield return c; + result.Add(new TypeConstraint(c)); if (c.Kind != TypeKind.Interface) hasNonInterfaceConstraint = true; } - // Do not add the 'System.Object' constraint if there is another constraint with a base class. - if (this.HasValueTypeConstraint || !hasNonInterfaceConstraint) { - yield return this.Compilation.FindType(this.HasValueTypeConstraint ? KnownTypeCode.ValueType : KnownTypeCode.Object); - } } + // Do not add the 'System.Object' constraint if there is another constraint with a base class. + if (this.HasValueTypeConstraint || !hasNonInterfaceConstraint) { + result.Add(new TypeConstraint(this.Compilation.FindType(this.HasValueTypeConstraint ? KnownTypeCode.ValueType : KnownTypeCode.Object))); + } + return result; } } } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/DummyTypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/DummyTypeParameter.cs index 6cf394b2e..b92b6697f 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/DummyTypeParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/DummyTypeParameter.cs @@ -169,6 +169,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation bool ITypeParameter.HasUnmanagedConstraint => false; Nullability ITypeParameter.NullabilityConstraint => Nullability.Oblivious; + IReadOnlyList ITypeParameter.TypeConstraints => EmptyList.Instance; + public override IType ChangeNullability(Nullability nullability) { if (nullability == Nullability.Oblivious) { diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs index b7549e6ac..75597198c 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeParameter.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Reflection; using System.Reflection.Metadata; using System.Reflection.Metadata.Ecma335; @@ -34,7 +35,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation readonly GenericParameterAttributes attr; // lazy-loaded: - IReadOnlyList constraints; + IReadOnlyList constraints; byte unmanagedConstraint = ThreeState.Unknown; const byte nullabilityNotYetLoaded = 255; byte nullabilityConstraint = nullabilityNotYetLoaded; @@ -170,16 +171,17 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } } - public override IEnumerable DirectBaseTypes { + public override IReadOnlyList TypeConstraints { get { var constraints = LazyInit.VolatileRead(ref this.constraints); - if (constraints != null) - return constraints; - return LazyInit.GetOrSet(ref this.constraints, DecodeConstraints()); + if (constraints == null) { + constraints = LazyInit.GetOrSet(ref this.constraints, DecodeConstraints()); + } + return constraints; } } - private IReadOnlyList DecodeConstraints() + private IReadOnlyList DecodeConstraints() { var metadata = module.metadata; var gp = metadata.GetGenericParameter(handle); @@ -193,18 +195,25 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } var constraintHandleCollection = gp.GetConstraints(); - List result = new List(constraintHandleCollection.Count + 1); + var result = new List(constraintHandleCollection.Count + 1); bool hasNonInterfaceConstraint = false; foreach (var constraintHandle in constraintHandleCollection) { var constraint = metadata.GetGenericParameterConstraint(constraintHandle); - var ty = module.ResolveType(constraint.Type, new GenericContext(Owner), constraint.GetCustomAttributes(), nullableContext); - result.Add(ty); + var attrs = constraint.GetCustomAttributes(); + var ty = module.ResolveType(constraint.Type, new GenericContext(Owner), attrs, nullableContext); + if (attrs.Count == 0) { + result.Add(new TypeConstraint(ty)); + } else { + AttributeListBuilder b = new AttributeListBuilder(module); + b.Add(attrs, SymbolKind.Constraint); + result.Add(new TypeConstraint(ty, b.Build())); + } hasNonInterfaceConstraint |= (ty.Kind != TypeKind.Interface); } if (this.HasValueTypeConstraint) { - result.Add(Compilation.FindType(KnownTypeCode.ValueType)); + result.Add(new TypeConstraint(Compilation.FindType(KnownTypeCode.ValueType))); } else if (!hasNonInterfaceConstraint) { - result.Add(Compilation.FindType(KnownTypeCode.Object)); + result.Add(new TypeConstraint(Compilation.FindType(KnownTypeCode.Object))); } return result; } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs index b13bede86..63e7b0885 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/NullabilityAnnotatedType.cs @@ -107,6 +107,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation bool ITypeParameter.HasValueTypeConstraint => baseType.HasValueTypeConstraint; bool ITypeParameter.HasUnmanagedConstraint => baseType.HasUnmanagedConstraint; Nullability ITypeParameter.NullabilityConstraint => baseType.NullabilityConstraint; + IReadOnlyList ITypeParameter.TypeConstraints => baseType.TypeConstraints; SymbolKind ISymbol.SymbolKind => SymbolKind.TypeParameter; IEnumerable ITypeParameter.GetAttributes() => baseType.GetAttributes(); } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs index 3ffe94f96..311395fc4 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/SpecializedMethod.cs @@ -257,9 +257,16 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation public override Nullability NullabilityConstraint => baseTp.NullabilityConstraint; - public override IEnumerable DirectBaseTypes { + IReadOnlyList typeConstraints; + + public override IReadOnlyList TypeConstraints { get { - return baseTp.DirectBaseTypes.Select(t => t.AcceptVisitor(substitution)); + var typeConstraints = LazyInit.VolatileRead(ref this.typeConstraints); + if (typeConstraints == null) { + typeConstraints = baseTp.TypeConstraints.SelectReadOnlyArray(c => new TypeConstraint(c.Type.AcceptVisitor(substitution), c.Attributes)); + typeConstraints = LazyInit.GetOrSet(ref this.typeConstraints, typeConstraints); + } + return typeConstraints; } } } From cdc7a3641fcc9752878dbd1399a086400262589f Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 10 Aug 2019 17:29:35 +0200 Subject: [PATCH 203/221] Fix references to nested types in generic classes. --- .../TestCases/Pretty/NullableRefTypes.cs | 13 +++++++++++++ .../Implementation/AbstractTypeParameter.cs | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs index de00697f1..baf5e6184 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/NullableRefTypes.cs @@ -83,4 +83,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } public event EventHandler Event; } + + public class T04_Dictionary where TKey : notnull + { + private struct Entry + { + public TKey key; + public TValue value; + } + + private int[]? _buckets; + private Entry[]? _entries; + private IEqualityComparer? _comparer; + } } diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs index 12de07edc..373ea7433 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/AbstractTypeParameter.cs @@ -194,11 +194,11 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation } bool IType.IsByRefLike => false; - Nullability IType.Nullability => NullabilityConstraint; + Nullability IType.Nullability => Nullability.Oblivious; public IType ChangeNullability(Nullability nullability) { - if (nullability == NullabilityConstraint) + if (nullability == Nullability.Oblivious) return this; else return new NullabilityAnnotatedTypeParameter(this, nullability); From b4e432195960df4ac83d205ec18c476d7e7adb41 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Mon, 12 Aug 2019 09:09:16 +0200 Subject: [PATCH 204/221] Frontends for P4 --- DecompilerNuGetDemos.workbook | 2 +- .../ICSharpCode.Decompiler.Console.csproj | 4 ++-- .../ICSharpCode.Decompiler.PowerShell.csproj | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DecompilerNuGetDemos.workbook b/DecompilerNuGetDemos.workbook index b07fe4f3f..e0629fbc7 100644 --- a/DecompilerNuGetDemos.workbook +++ b/DecompilerNuGetDemos.workbook @@ -6,7 +6,7 @@ platforms: - DotNetCore packages: - id: ICSharpCode.Decompiler - version: 5.0.0.4861-preview3 + version: 5.0.0.5066-preview4 --- Setup: load the references required to work with the decompiler diff --git a/ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj b/ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj index 09f8a362e..f207dfe1f 100644 --- a/ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj +++ b/ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj @@ -7,7 +7,7 @@ true ilspycmd ilspycmd - 5.0.0-preview3 + 5.0.0-preview4 Command-line decompiler using the ILSpy decompilation engine Copyright 2011-2019 AlphaSierraPapa https://github.com/icsharpcode/ILSpy/ @@ -28,7 +28,7 @@ - + diff --git a/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj b/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj index c60026221..3477b5693 100644 --- a/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj +++ b/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj @@ -8,7 +8,7 @@ - + From 8d2e8cc267f4b708a2e2e97be78ef92134003d31 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 12 Aug 2019 09:14:32 +0200 Subject: [PATCH 205/221] Fix #1628: Preserve order of child nodes in PatternStatementTransform.VisitBinaryOperatorExpression. --- .../Transforms/PatternStatementTransform.cs | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs index c96419502..faa6a3163 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs @@ -1002,24 +1002,26 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms /// /// Use associativity of logic operators to avoid parentheses. /// - public override AstNode VisitBinaryOperatorExpression(BinaryOperatorExpression boe1) + public override AstNode VisitBinaryOperatorExpression(BinaryOperatorExpression expr) { - switch (boe1.Operator) { + switch (expr.Operator) { case BinaryOperatorType.ConditionalAnd: case BinaryOperatorType.ConditionalOr: // a && (b && c) ==> (a && b) && c - var boe2 = boe1.Right as BinaryOperatorExpression; - if (boe2 != null && boe2.Operator == boe1.Operator) { - // make boe2 the parent and boe1 the child - var b = boe2.Left.Detach(); - boe1.ReplaceWith(boe2.Detach()); - boe2.Left = boe1; - boe1.Right = b; - return base.VisitBinaryOperatorExpression(boe2); + var bAndC = expr.Right as BinaryOperatorExpression; + if (bAndC != null && bAndC.Operator == expr.Operator) { + // make bAndC the parent and expr the child + var b = bAndC.Left.Detach(); + var c = bAndC.Right.Detach(); + expr.ReplaceWith(bAndC.Detach()); + bAndC.Left = expr; + bAndC.Right = c; + expr.Right = b; + return base.VisitBinaryOperatorExpression(bAndC); } break; } - return base.VisitBinaryOperatorExpression(boe1); + return base.VisitBinaryOperatorExpression(expr); } #endregion } From d13dfebe948789efe0faae0f6a8f473013d7297d Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 12 Aug 2019 13:16:53 +0200 Subject: [PATCH 206/221] Do not include "app.config" in output. It is automatically copied as "ILSpy.exe.config" --- ILSpy/ILSpy.csproj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index ea4846842..7e166b5b8 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -267,9 +267,7 @@ - - Always - + From 85d1d21b3523cbe2812f5d79bd02517bc9a8473f Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 13 Aug 2019 12:27:08 +0200 Subject: [PATCH 207/221] Fix possible NRE in DynamicCallSiteTransform.ExtractArgumentInfo. --- .../IL/Transforms/DynamicCallSiteTransform.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/DynamicCallSiteTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/DynamicCallSiteTransform.cs index 8d4a8bf0f..8d77cf103 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DynamicCallSiteTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DynamicCallSiteTransform.cs @@ -500,7 +500,10 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; int i = 0; callSiteInfo.ArgumentInfos = new CSharpArgumentInfo[numberOfArguments]; - var compileTimeTypes = callSiteInfo.DelegateType.GetDelegateInvokeMethod().Parameters.SelectReadOnlyArray(p => p.Type); + IMethod invokeMethod = callSiteInfo.DelegateType.GetDelegateInvokeMethod(); + if (invokeMethod == null) + return false; + var compileTimeTypes = invokeMethod.Parameters.SelectReadOnlyArray(p => p.Type); foreach (var (_, arg) in arguments) { if (!(arg is Call createCall)) return false; From 44c044aa337d89f4d4db610e7880401bfec7f678 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Tue, 13 Aug 2019 12:31:27 +0200 Subject: [PATCH 208/221] Fix #1635: DynamicInvokeConstructorInstruction Did report the wrong StackType for value types and unknown types. --- .../TestCases/Pretty/DynamicTests.cs | 5 +++++ .../IL/Instructions/DynamicInstructions.cs | 8 ++++++-- .../IL/Transforms/DynamicCallSiteTransform.cs | 13 ++++++++++++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs index 2e299b63e..3f6ab5112 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs @@ -415,6 +415,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty return true.Equals(a); } + private static IntPtr NewIntPtr(dynamic a) + { + return new IntPtr(a); + } + private static dynamic GetDynamic(int i) { return null; diff --git a/ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs b/ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs index 1eb663f63..5a3b64b54 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs @@ -349,14 +349,17 @@ namespace ICSharpCode.Decompiler.IL partial class DynamicInvokeConstructorInstruction { + readonly IType resultType; + public IReadOnlyList ArgumentInfo { get; } - public DynamicInvokeConstructorInstruction(CSharpBinderFlags binderFlags, IType context, CSharpArgumentInfo[] argumentInfo, ILInstruction[] arguments) + public DynamicInvokeConstructorInstruction(CSharpBinderFlags binderFlags, IType type, IType context, CSharpArgumentInfo[] argumentInfo, ILInstruction[] arguments) : base(OpCode.DynamicInvokeConstructorInstruction, binderFlags, context) { ArgumentInfo = argumentInfo; Arguments = new InstructionCollection(this, 0); Arguments.AddRange(arguments); + this.resultType = type; } public override void WriteTo(ITextOutput output, ILAstWritingOptions options) @@ -365,11 +368,12 @@ namespace ICSharpCode.Decompiler.IL output.Write(OpCode); WriteBinderFlags(output, options); output.Write(' '); + resultType?.WriteTo(output); output.Write(".ctor"); WriteArgumentList(output, options, Arguments.Zip(ArgumentInfo)); } - public override StackType ResultType => StackType.O; + public override StackType ResultType => resultType?.GetStackType() ?? StackType.Unknown; public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) { diff --git a/ICSharpCode.Decompiler/IL/Transforms/DynamicCallSiteTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/DynamicCallSiteTransform.cs index 8d77cf103..3b9537daf 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/DynamicCallSiteTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/DynamicCallSiteTransform.cs @@ -183,12 +183,23 @@ namespace ICSharpCode.Decompiler.IL.Transforms arguments: targetInvokeCall.Arguments.Skip(2).ToArray() ); case BinderMethodKind.InvokeConstructor: + var arguments = targetInvokeCall.Arguments.Skip(2).ToArray(); + // Extract type information from targetInvokeCall: + // Must either be an inlined type or + // a reference to a variable that is initialized with a type. + if (!TransformExpressionTrees.MatchGetTypeFromHandle(arguments[0], out var type)) { + if (!(arguments[0].MatchLdLoc(out var temp) && temp.IsSingleDefinition && temp.StoreInstructions.FirstOrDefault() is StLoc initStore)) + return null; + if (!TransformExpressionTrees.MatchGetTypeFromHandle(initStore.Value, out type)) + return null; + } deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); return new DynamicInvokeConstructorInstruction( binderFlags: callsite.Flags, + type: type ?? SpecialType.UnknownType, context: callsite.Context, argumentInfo: callsite.ArgumentInfos, - arguments: targetInvokeCall.Arguments.Skip(2).ToArray() + arguments: arguments ); case BinderMethodKind.InvokeMember: deadArguments.AddRange(targetInvokeCall.Arguments.Take(2)); From 37fcde56458087a663041a9fc4727c9936b0a566 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Wed, 14 Aug 2019 07:53:00 +0200 Subject: [PATCH 209/221] Fix tooltip for "Reload all assemblies" toolbar button --- ILSpy/Properties/Resources.Designer.cs | 2 +- ILSpy/Properties/Resources.resx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index 9cb673c88..d4f71c88f 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -1432,7 +1432,7 @@ namespace ICSharpCode.ILSpy.Properties { } /// - /// Looks up a localized string similar to ReloadAssemblies. + /// Looks up a localized string similar to Reload all assemblies. /// public static string RefreshCommand_ReloadAssemblies { get { diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index 08bb57a15..389c58dbf 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -172,7 +172,7 @@ Generate portable PDB - ReloadAssemblies + Reload all assemblies _Reload From e857fb4f906e9053fdd8cd64f0ad5713bcde4985 Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Wed, 14 Aug 2019 11:09:25 +0200 Subject: [PATCH 210/221] Add tooltips to both comboboxes in toolbar --- ILSpy/MainWindow.xaml | 4 ++-- ILSpy/Properties/Resources.Designer.cs | 18 ++++++++++++++++++ ILSpy/Properties/Resources.resx | 6 ++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/ILSpy/MainWindow.xaml b/ILSpy/MainWindow.xaml index ed8cc1b1b..a83b6d799 100644 --- a/ILSpy/MainWindow.xaml +++ b/ILSpy/MainWindow.xaml @@ -93,9 +93,9 @@ - diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index d4f71c88f..845687b34 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -1566,6 +1566,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Select language to decompile to. + /// + public static string SelectLanguageDropdownTooltip { + get { + return ResourceManager.GetString("SelectLanguageDropdownTooltip", resourceCulture); + } + } + /// /// Looks up a localized string similar to Select a list:. /// @@ -1575,6 +1584,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Select version of language to output. + /// + public static string SelectVersionDropdownTooltip { + get { + return ResourceManager.GetString("SelectVersionDropdownTooltip", resourceCulture); + } + } + /// /// Looks up a localized string similar to Shell. /// diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index 389c58dbf..e38101b12 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -745,4 +745,10 @@ Are you sure you want to continue? Highlight matching braces + + Select language to decompile to + + + Select version of language to output + \ No newline at end of file From a9bb17aba92c2038585e774dd021d8011c626a6c Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 15 Aug 2019 11:04:08 +0200 Subject: [PATCH 211/221] Make it possible to search by full name without including generic type parameters. --- .../Metadata/MetadataExtensions.cs | 16 +++++++------ ILSpy/Languages/CSharpLanguage.cs | 18 +++++++------- ILSpy/Languages/Language.cs | 24 +++++++++---------- ILSpy/Search/AbstractSearchStrategy.cs | 3 +++ ILSpy/Search/MemberSearchStrategy.cs | 10 ++++---- 5 files changed, 38 insertions(+), 33 deletions(-) diff --git a/ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs b/ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs index 7d8f06f34..561c25704 100644 --- a/ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs +++ b/ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs @@ -101,24 +101,26 @@ namespace ICSharpCode.Decompiler.Metadata } } - public static string ToILNameString(this FullTypeName typeName) + public static string ToILNameString(this FullTypeName typeName, bool omitGenerics = false) { string name; if (typeName.IsNested) { name = typeName.Name; - int localTypeParameterCount = typeName.GetNestedTypeAdditionalTypeParameterCount(typeName.NestingLevel - 1); - if (localTypeParameterCount > 0) - name += "`" + localTypeParameterCount; + if (!omitGenerics) { + int localTypeParameterCount = typeName.GetNestedTypeAdditionalTypeParameterCount(typeName.NestingLevel - 1); + if (localTypeParameterCount > 0) + name += "`" + localTypeParameterCount; + } name = Disassembler.DisassemblerHelpers.Escape(name); - return $"{typeName.GetDeclaringType().ToILNameString()}/{name}"; + return $"{typeName.GetDeclaringType().ToILNameString(omitGenerics)}/{name}"; } if (!string.IsNullOrEmpty(typeName.TopLevelTypeName.Namespace)) { name = $"{typeName.TopLevelTypeName.Namespace}.{typeName.Name}"; - if (typeName.TypeParameterCount > 0) + if (!omitGenerics && typeName.TypeParameterCount > 0) name += "`" + typeName.TypeParameterCount; } else { name = typeName.Name; - if (typeName.TypeParameterCount > 0) + if (!omitGenerics && typeName.TypeParameterCount > 0) name += "`" + typeName.TypeParameterCount; } return Disassembler.DisassemblerHelpers.Escape(name); diff --git a/ILSpy/Languages/CSharpLanguage.cs b/ILSpy/Languages/CSharpLanguage.cs index e952647e1..087aaec61 100644 --- a/ILSpy/Languages/CSharpLanguage.cs +++ b/ILSpy/Languages/CSharpLanguage.cs @@ -519,7 +519,7 @@ namespace ICSharpCode.ILSpy return EntityToString(@event, includeDeclaringTypeName, includeNamespace, includeNamespaceOfDeclaringTypeName); } - string ToCSharpString(MetadataReader metadata, TypeDefinitionHandle handle, bool fullName) + string ToCSharpString(MetadataReader metadata, TypeDefinitionHandle handle, bool fullName, bool omitGenerics) { StringBuilder builder = new StringBuilder(); var currentTypeDefHandle = handle; @@ -531,7 +531,7 @@ namespace ICSharpCode.ILSpy typeDef = metadata.GetTypeDefinition(currentTypeDefHandle); var part = ReflectionHelper.SplitTypeParameterCountFromReflectionName(metadata.GetString(typeDef.Name), out int typeParamCount); var genericParams = typeDef.GetGenericParameters(); - if (genericParams.Count > 0) { + if (!omitGenerics && genericParams.Count > 0) { builder.Insert(0, '>'); int firstIndex = genericParams.Count - typeParamCount; for (int i = genericParams.Count - 1; i >= genericParams.Count - typeParamCount; i--) { @@ -552,17 +552,17 @@ namespace ICSharpCode.ILSpy return builder.ToString(); } - public override string GetEntityName(PEFile module, EntityHandle handle, bool fullName) + public override string GetEntityName(PEFile module, EntityHandle handle, bool fullName, bool omitGenerics) { MetadataReader metadata = module.Metadata; switch (handle.Kind) { case HandleKind.TypeDefinition: - return ToCSharpString(metadata, (TypeDefinitionHandle)handle, fullName); + return ToCSharpString(metadata, (TypeDefinitionHandle)handle, fullName, omitGenerics); case HandleKind.FieldDefinition: var fd = metadata.GetFieldDefinition((FieldDefinitionHandle)handle); var declaringType = fd.GetDeclaringType(); if (fullName) - return ToCSharpString(metadata, declaringType, fullName) + "." + metadata.GetString(fd.Name); + return ToCSharpString(metadata, declaringType, fullName, omitGenerics) + "." + metadata.GetString(fd.Name); return metadata.GetString(fd.Name); case HandleKind.MethodDefinition: var md = metadata.GetMethodDefinition((MethodDefinitionHandle)handle); @@ -586,7 +586,7 @@ namespace ICSharpCode.ILSpy break; default: var genericParams = md.GetGenericParameters(); - if (genericParams.Count > 0) { + if (!omitGenerics && genericParams.Count > 0) { methodName += "<"; int i = 0; foreach (var h in genericParams) { @@ -600,19 +600,19 @@ namespace ICSharpCode.ILSpy break; } if (fullName) - return ToCSharpString(metadata, declaringType, fullName) + "." + methodName; + return ToCSharpString(metadata, declaringType, fullName, omitGenerics) + "." + methodName; return methodName; case HandleKind.EventDefinition: var ed = metadata.GetEventDefinition((EventDefinitionHandle)handle); declaringType = metadata.GetMethodDefinition(ed.GetAccessors().GetAny()).GetDeclaringType(); if (fullName) - return ToCSharpString(metadata, declaringType, fullName) + "." + metadata.GetString(ed.Name); + return ToCSharpString(metadata, declaringType, fullName, omitGenerics) + "." + metadata.GetString(ed.Name); return metadata.GetString(ed.Name); case HandleKind.PropertyDefinition: var pd = metadata.GetPropertyDefinition((PropertyDefinitionHandle)handle); declaringType = metadata.GetMethodDefinition(pd.GetAccessors().GetAny()).GetDeclaringType(); if (fullName) - return ToCSharpString(metadata, declaringType, fullName) + "." + metadata.GetString(pd.Name); + return ToCSharpString(metadata, declaringType, fullName, omitGenerics) + "." + metadata.GetString(pd.Name); return metadata.GetString(pd.Name); default: return null; diff --git a/ILSpy/Languages/Language.cs b/ILSpy/Languages/Language.cs index 701399c70..ec419b3af 100644 --- a/ILSpy/Languages/Language.cs +++ b/ILSpy/Languages/Language.cs @@ -448,42 +448,42 @@ namespace ICSharpCode.ILSpy /// /// This should produce a string representation of the entity for search to match search strings against. /// - public virtual string GetEntityName(PEFile module, EntityHandle handle, bool fullName) + public virtual string GetEntityName(PEFile module, EntityHandle handle, bool fullName, bool omitGenerics) { MetadataReader metadata = module.Metadata; switch (handle.Kind) { case HandleKind.TypeDefinition: if (fullName) - return EscapeName(((TypeDefinitionHandle)handle).GetFullTypeName(metadata).ToILNameString()); + return EscapeName(((TypeDefinitionHandle)handle).GetFullTypeName(metadata).ToILNameString(omitGenerics)); var td = metadata.GetTypeDefinition((TypeDefinitionHandle)handle); return EscapeName(metadata.GetString(td.Name)); case HandleKind.FieldDefinition: var fd = metadata.GetFieldDefinition((FieldDefinitionHandle)handle); - var declaringType = fd.GetDeclaringType(); if (fullName) - return EscapeName(fd.GetDeclaringType().GetFullTypeName(metadata).ToILNameString() + "." + metadata.GetString(fd.Name)); + return EscapeName(fd.GetDeclaringType().GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + metadata.GetString(fd.Name)); return EscapeName(metadata.GetString(fd.Name)); case HandleKind.MethodDefinition: var md = metadata.GetMethodDefinition((MethodDefinitionHandle)handle); - declaringType = md.GetDeclaringType(); string methodName = metadata.GetString(md.Name); - int genericParamCount = md.GetGenericParameters().Count; - if (genericParamCount > 0) - methodName += "``" + genericParamCount; + if (!omitGenerics) { + int genericParamCount = md.GetGenericParameters().Count; + if (genericParamCount > 0) + methodName += "``" + genericParamCount; + } if (fullName) - return EscapeName(md.GetDeclaringType().GetFullTypeName(metadata).ToILNameString() + "." + methodName); + return EscapeName(md.GetDeclaringType().GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + methodName); return EscapeName(methodName); case HandleKind.EventDefinition: var ed = metadata.GetEventDefinition((EventDefinitionHandle)handle); - declaringType = metadata.GetMethodDefinition(ed.GetAccessors().GetAny()).GetDeclaringType(); + var declaringType = metadata.GetMethodDefinition(ed.GetAccessors().GetAny()).GetDeclaringType(); if (fullName) - return EscapeName(declaringType.GetFullTypeName(metadata).ToILNameString() + "." + metadata.GetString(ed.Name)); + return EscapeName(declaringType.GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + metadata.GetString(ed.Name)); return EscapeName(metadata.GetString(ed.Name)); case HandleKind.PropertyDefinition: var pd = metadata.GetPropertyDefinition((PropertyDefinitionHandle)handle); declaringType = metadata.GetMethodDefinition(pd.GetAccessors().GetAny()).GetDeclaringType(); if (fullName) - return EscapeName(declaringType.GetFullTypeName(metadata).ToILNameString() + "." + metadata.GetString(pd.Name)); + return EscapeName(declaringType.GetFullTypeName(metadata).ToILNameString(omitGenerics) + "." + metadata.GetString(pd.Name)); return EscapeName(metadata.GetString(pd.Name)); default: return null; diff --git a/ILSpy/Search/AbstractSearchStrategy.cs b/ILSpy/Search/AbstractSearchStrategy.cs index ae787621f..1a25f1698 100644 --- a/ILSpy/Search/AbstractSearchStrategy.cs +++ b/ILSpy/Search/AbstractSearchStrategy.cs @@ -14,6 +14,7 @@ namespace ICSharpCode.ILSpy.Search protected readonly string[] searchTerm; protected readonly Regex regex; protected readonly bool fullNameSearch; + protected readonly bool omitGenerics; protected readonly Language language; protected readonly ApiVisibility apiVisibility; private readonly IProducerConsumerCollection resultQueue; @@ -29,11 +30,13 @@ namespace ICSharpCode.ILSpy.Search if (search.StartsWith("/", StringComparison.Ordinal) && search.Length > 4) { var regexString = search.Substring(1, search.Length - 1); fullNameSearch = search.Contains("\\."); + omitGenerics = !search.Contains("<"); if (regexString.EndsWith("/", StringComparison.Ordinal)) regexString = regexString.Substring(0, regexString.Length - 1); regex = SafeNewRegex(regexString); } else { fullNameSearch = search.Contains("."); + omitGenerics = !search.Contains("<"); } } searchTerm = terms; diff --git a/ILSpy/Search/MemberSearchStrategy.cs b/ILSpy/Search/MemberSearchStrategy.cs index ef68010fb..4a631986b 100644 --- a/ILSpy/Search/MemberSearchStrategy.cs +++ b/ILSpy/Search/MemberSearchStrategy.cs @@ -31,7 +31,7 @@ namespace ICSharpCode.ILSpy.Search if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Type) { foreach (var handle in metadata.TypeDefinitions) { cancellationToken.ThrowIfCancellationRequested(); - string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch); + string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch, omitGenerics); if (languageSpecificName != null && !IsMatch(languageSpecificName)) continue; var type = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); @@ -43,7 +43,7 @@ namespace ICSharpCode.ILSpy.Search if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Member || searchKind == MemberSearchKind.Method) { foreach (var handle in metadata.MethodDefinitions) { cancellationToken.ThrowIfCancellationRequested(); - string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch); + string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch, omitGenerics); if (languageSpecificName != null && !IsMatch(languageSpecificName)) continue; var method = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); @@ -55,7 +55,7 @@ namespace ICSharpCode.ILSpy.Search if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Member || searchKind == MemberSearchKind.Field) { foreach (var handle in metadata.FieldDefinitions) { cancellationToken.ThrowIfCancellationRequested(); - string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch); + string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch, omitGenerics); if (languageSpecificName != null && !IsMatch(languageSpecificName)) continue; var field = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); @@ -67,7 +67,7 @@ namespace ICSharpCode.ILSpy.Search if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Member || searchKind == MemberSearchKind.Property) { foreach (var handle in metadata.PropertyDefinitions) { cancellationToken.ThrowIfCancellationRequested(); - string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch); + string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch, omitGenerics); if (languageSpecificName != null && !IsMatch(languageSpecificName)) continue; var property = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); @@ -79,7 +79,7 @@ namespace ICSharpCode.ILSpy.Search if (searchKind == MemberSearchKind.All || searchKind == MemberSearchKind.Member || searchKind == MemberSearchKind.Event) { foreach (var handle in metadata.EventDefinitions) { cancellationToken.ThrowIfCancellationRequested(); - string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch); + string languageSpecificName = language.GetEntityName(module, handle, fullNameSearch, omitGenerics); if (!IsMatch(languageSpecificName)) continue; var @event = ((MetadataModule)typeSystem.MainModule).GetDefinition(handle); From 7502e521c150f1a314fea01865a2f8950f0d636d Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Thu, 15 Aug 2019 11:31:36 +0200 Subject: [PATCH 212/221] Improve documentation of ExpressionTransforms.TransformCatchVariable --- .../IL/Transforms/ExpressionTransforms.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 91229cffa..8578de0bd 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -683,7 +683,18 @@ namespace ICSharpCode.Decompiler.IL.Transforms } /// - /// Transform local exception variable. + /// catch ex : TException when (...) BlockContainer { + /// Block entryPoint (incoming: 1) { + /// stloc v(ldloc ex) + /// ... + /// } + /// } + /// => + /// catch v : TException when (...) BlockContainer { + /// Block entryPoint (incoming: 1) { + /// ... + /// } + /// } /// void TransformCatchVariable(TryCatchHandler handler, Block entryPoint) { @@ -693,6 +704,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms return; if (!exceptionSlotLoad.MatchLdLoc(handler.Variable) || !handler.Variable.IsSingleDefinition || handler.Variable.LoadCount != 1) return; + context.Step("TransformCatchVariable", entryPoint.Instructions[0]); handler.Variable = exceptionVar; exceptionVar.Kind = VariableKind.ExceptionLocal; entryPoint.Instructions.RemoveAt(0); @@ -705,6 +717,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms { TransformCatchVariable(handler, entryPoint); if (entryPoint.Instructions.Count == 1 && entryPoint.Instructions[0].MatchLeave(out _, out var condition)) { + context.Step("TransformCatchWhen", entryPoint.Instructions[0]); handler.Filter = condition; } } From 9c06d5c9242b41973cdc3058ee9b69a1d57ab9f2 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 17 Aug 2019 19:59:20 +0200 Subject: [PATCH 213/221] Fix #1642: Allow StackTypes I, I4, I8 and Unknown as switch value, convert Unknown and I to I8. --- .../IL/ControlFlow/SwitchDetection.cs | 8 ++++---- .../IL/Transforms/ExpressionTransforms.cs | 11 +++++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs index a1fca3d89..72a741b28 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs @@ -158,13 +158,13 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow void ProcessBlock(Block block, ref bool blockContainerNeedsCleanup) { bool analysisSuccess = analysis.AnalyzeBlock(block); - KeyValuePair defaultSection; - if (analysisSuccess && UseCSharpSwitch(out defaultSection)) { + if (analysisSuccess && UseCSharpSwitch(out _)) { // complex multi-block switch that can be combined into a single SwitchInstruction ILInstruction switchValue = new LdLoc(analysis.SwitchVariable); - if (switchValue.ResultType == StackType.Unknown) { + Debug.Assert(switchValue.ResultType.IsIntegerType() || switchValue.ResultType == StackType.Unknown); + if (!(switchValue.ResultType == StackType.I4 || switchValue.ResultType == StackType.I8)) { // switchValue must have a result type of either I4 or I8 - switchValue = new Conv(switchValue, PrimitiveType.I8, false, TypeSystem.Sign.Signed); + switchValue = new Conv(switchValue, PrimitiveType.I8, false, Sign.Signed); } var sw = new SwitchInstruction(switchValue); foreach (var section in analysis.Sections) { diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 8578de0bd..9ff31840e 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -62,6 +62,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms } } + protected internal override void VisitBlockContainer(BlockContainer container) + { + if (container.Kind == ContainerKind.Switch) { + // Special case for switch: Only visit the switch condition block. + var switchInst = (SwitchInstruction)container.EntryPoint.Instructions[0]; + switchInst.Value.AcceptVisitor(this); + return; + } + base.VisitBlockContainer(container); + } + protected internal override void VisitBlock(Block block) { if (block.Kind == BlockKind.ControlFlow) { From 81e702f840af210b66ab22c27893a3770084694a Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 17 Aug 2019 21:32:13 +0200 Subject: [PATCH 214/221] #1638: Make TransformDisplayClassUsage.IsPotentialClosure less aggressive --- .../Transforms/TransformDisplayClassUsage.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs index b9c28fdac..d892b9c48 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs @@ -173,7 +173,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // Special case for Mono-compiled yield state machines ITypeDefinition closureType = thisVariable.Type.GetDefinition(); if (!(closureType != decompilationContext.CurrentTypeDefinition - && IsPotentialClosure(decompilationContext.CurrentTypeDefinition, closureType))) + && IsPotentialClosure(decompilationContext.CurrentTypeDefinition, closureType, allowTypeImplementingInterfaces: true))) return false; var displayClass = new DisplayClass { @@ -236,10 +236,25 @@ namespace ICSharpCode.Decompiler.IL.Transforms return IsPotentialClosure(decompilationContext.CurrentTypeDefinition, potentialDisplayClass); } - internal static bool IsPotentialClosure(ITypeDefinition decompiledTypeDefinition, ITypeDefinition potentialDisplayClass) + internal static bool IsPotentialClosure(ITypeDefinition decompiledTypeDefinition, ITypeDefinition potentialDisplayClass, bool allowTypeImplementingInterfaces = false) { if (potentialDisplayClass == null || !potentialDisplayClass.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) return false; + switch (potentialDisplayClass.Kind) { + case TypeKind.Struct: + break; + case TypeKind.Class: + if (!potentialDisplayClass.IsSealed) + return false; + if (!allowTypeImplementingInterfaces) { + if (!potentialDisplayClass.DirectBaseTypes.All(t => t.IsKnownType(KnownTypeCode.Object))) + return false; + } + break; + default: + return false; + } + while (potentialDisplayClass != decompiledTypeDefinition) { potentialDisplayClass = potentialDisplayClass.DeclaringTypeDefinition; if (potentialDisplayClass == null) From 03151bf37bd6d0d8c057470b8b777e721316ee1c Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 17 Aug 2019 23:05:13 +0200 Subject: [PATCH 215/221] Remove redundant code in ExpressionTransforms.VisitBlockContainer --- ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index 9ff31840e..e16c7d093 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -68,9 +68,8 @@ namespace ICSharpCode.Decompiler.IL.Transforms // Special case for switch: Only visit the switch condition block. var switchInst = (SwitchInstruction)container.EntryPoint.Instructions[0]; switchInst.Value.AcceptVisitor(this); - return; } - base.VisitBlockContainer(container); + // No need to call base.VisitBlockContainer, see comment in VisitBlock. } protected internal override void VisitBlock(Block block) From f1021cb3004d85d497f0577ef36493d186f16648 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 17 Aug 2019 23:54:29 +0200 Subject: [PATCH 216/221] Fix #1643: Handle unbox.any in TransformCatchVariable. --- .../TestCases/Pretty/ExceptionHandling.cs | 74 ++++++++++++++++++- ICSharpCode.Decompiler/IL/ILVariable.cs | 3 +- .../IL/Transforms/ExpressionTransforms.cs | 37 ++++++++-- 3 files changed, 104 insertions(+), 10 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.cs index 06b071f85..1472eda8f 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExceptionHandling.cs @@ -243,7 +243,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine("0"); } - + Console.WriteLine("End Try"); } catch { @@ -259,7 +259,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty Console.WriteLine("Catch2"); } return B(10) && B(11); - } catch { + } catch { Console.WriteLine("Catch"); } finally { Console.WriteLine("Finally"); @@ -281,5 +281,75 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } } + + public void ReassignExceptionVar() + { + try { + Console.WriteLine("ReassignExceptionVar"); + } catch (Exception innerException) { + if (innerException.InnerException != null) { + innerException = innerException.InnerException; + } + Console.WriteLine(innerException); + } + } + + public int UseExceptionVarOutsideCatch() + { + Exception ex2; + try { + return 1; + } catch (Exception ex) { + ex2 = ex; + } + Console.WriteLine(ex2 != null); + return 2; + } + + public void GenericException(int input) where TException : Exception + { + try { + Console.WriteLine(input); + } catch (TException val) { + Console.WriteLine(val.Message); + throw; + } + } + + public void GenericException2() where T : Exception + { + try { + Console.WriteLine("CatchT"); +#if ROSLYN + } catch (T val) { + Console.WriteLine("{0} {1}", val, val.ToString()); + } +#else + } catch (T arg) { + Console.WriteLine("{0} {1}", arg, arg.ToString()); + } +#endif + } + +#if CS60 + public void GenericExceptionWithCondition(int input) where TException : Exception + { + try { + Console.WriteLine(input); + } catch (TException val) when (val.Message.Contains("Test")) { + Console.WriteLine(val.Message); + throw; + } + } + + public void GenericException2WithCondition(int input) where TException : Exception + { + try { + Console.WriteLine(input); + } catch (TException val) when (val.Message.Contains("Test")) { + Console.WriteLine("{0} {1}", val, val.ToString()); + } + } +#endif } } \ No newline at end of file diff --git a/ICSharpCode.Decompiler/IL/ILVariable.cs b/ICSharpCode.Decompiler/IL/ILVariable.cs index 9b77c5ecc..4d3fca31d 100644 --- a/ICSharpCode.Decompiler/IL/ILVariable.cs +++ b/ICSharpCode.Decompiler/IL/ILVariable.cs @@ -20,6 +20,7 @@ using System; using System.Collections.Generic; using ICSharpCode.Decompiler.TypeSystem; using System.Diagnostics; +using System.Linq; namespace ICSharpCode.Decompiler.IL { @@ -216,7 +217,7 @@ namespace ICSharpCode.Decompiler.IL /// This list is automatically updated when adding/removing ldloc instructions from the ILAst. /// public IReadOnlyList LoadInstructions => loadInstructions; - + /// /// Number of store instructions referencing this variable, /// plus 1 if HasInitialValue. diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index e16c7d093..b2b9b4c13 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -17,13 +17,10 @@ // DEALINGS IN THE SOFTWARE. using System; -using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Linq.Expressions; -using System.Reflection.Metadata; using ICSharpCode.Decompiler.TypeSystem; -using ICSharpCode.Decompiler.TypeSystem.Implementation; namespace ICSharpCode.Decompiler.IL.Transforms { @@ -708,15 +705,41 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// void TransformCatchVariable(TryCatchHandler handler, Block entryPoint) { - if (!entryPoint.Instructions[0].MatchStLoc(out var exceptionVar, out var exceptionSlotLoad)) + if (!handler.Variable.IsSingleDefinition || handler.Variable.LoadCount != 1) + return; // handle.Variable already has non-trivial uses + if (!entryPoint.Instructions[0].MatchStLoc(out var exceptionVar, out var exceptionSlotLoad)) { + // Not the pattern with a second exceptionVar. + // However, it is still possible that we need to remove a pointless UnboxAny: + if (handler.Variable.LoadInstructions.Single().Parent is UnboxAny inlinedUnboxAny) { + if (inlinedUnboxAny.Type.Equals(handler.Variable.Type)) { + context.Step("TransformCatchVariable - remove inlined UnboxAny", inlinedUnboxAny); + inlinedUnboxAny.ReplaceWith(inlinedUnboxAny.Argument); + } + } return; - if (!exceptionVar.IsSingleDefinition || exceptionVar.Kind != VariableKind.Local) + } + if (exceptionVar.Kind != VariableKind.Local && exceptionVar.Kind != VariableKind.StackSlot) return; - if (!exceptionSlotLoad.MatchLdLoc(handler.Variable) || !handler.Variable.IsSingleDefinition || handler.Variable.LoadCount != 1) + if (exceptionSlotLoad is UnboxAny unboxAny) { + // When catching a type parameter, csc emits an unbox.any instruction + if (!unboxAny.Type.Equals(handler.Variable.Type)) + return; + exceptionSlotLoad = unboxAny.Argument; + } + if (!exceptionSlotLoad.MatchLdLoc(handler.Variable)) return; + // Check that exceptionVar is only used within the catch block: + var allUses = exceptionVar.LoadInstructions + .Concat(exceptionVar.StoreInstructions.Cast()) + .Concat(exceptionVar.AddressInstructions); + foreach (var inst in allUses) { + if (!inst.IsDescendantOf(handler)) + return; + } context.Step("TransformCatchVariable", entryPoint.Instructions[0]); - handler.Variable = exceptionVar; exceptionVar.Kind = VariableKind.ExceptionLocal; + exceptionVar.Type = handler.Variable.Type; + handler.Variable = exceptionVar; entryPoint.Instructions.RemoveAt(0); } From 047f119e9996c86a1360b4be61040eb295eec750 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 18 Aug 2019 00:25:26 +0200 Subject: [PATCH 217/221] Add RemoveDeadStores option --- ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs | 8 ++++---- ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs | 6 +++--- ICSharpCode.Decompiler/DecompilerSettings.cs | 14 ++++++++++++++ .../IL/ControlFlow/ControlFlowSimplification.cs | 2 +- .../IL/Transforms/RemoveDeadVariableInit.cs | 2 +- ILSpy/Properties/Resources.Designer.cs | 9 +++++++++ ILSpy/Properties/Resources.resx | 3 +++ 7 files changed, 35 insertions(+), 9 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs index 5d3d575e8..72e16db69 100644 --- a/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/ILPrettyTestRunner.cs @@ -97,13 +97,13 @@ namespace ICSharpCode.Decompiler.Tests [Test] public void FSharpUsing_Debug() { - Run(settings: new DecompilerSettings { RemoveDeadCode = true }); + Run(settings: new DecompilerSettings { RemoveDeadStores = true }); } [Test] public void FSharpUsing_Release() { - Run(settings: new DecompilerSettings { RemoveDeadCode = true }); + Run(settings: new DecompilerSettings { RemoveDeadStores = true }); } [Test] @@ -182,14 +182,14 @@ namespace ICSharpCode.Decompiler.Tests public void FSharpLoops_Debug() { CopyFSharpCoreDll(); - Run(settings: new DecompilerSettings { RemoveDeadCode = true }); + Run(settings: new DecompilerSettings { RemoveDeadStores = true }); } [Test] public void FSharpLoops_Release() { CopyFSharpCoreDll(); - Run(settings: new DecompilerSettings { RemoveDeadCode = true }); + Run(settings: new DecompilerSettings { RemoveDeadStores = true }); } void Run([CallerMemberName] string testName = null, DecompilerSettings settings = null) diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs index cfee6cb8b..401c23221 100644 --- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs +++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs @@ -114,7 +114,7 @@ namespace ICSharpCode.Decompiler.Tests RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings { NullPropagation = false, // legacy csc generates a dead store in debug builds - RemoveDeadCode = (cscOptions == CompilerOptions.None) + RemoveDeadStores = (cscOptions == CompilerOptions.None) }); } @@ -123,7 +123,7 @@ namespace ICSharpCode.Decompiler.Tests { RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings { // legacy csc generates a dead store in debug builds - RemoveDeadCode = (cscOptions == CompilerOptions.None) + RemoveDeadStores = (cscOptions == CompilerOptions.None) }); } @@ -180,7 +180,7 @@ namespace ICSharpCode.Decompiler.Tests { RunForLibrary(cscOptions: cscOptions, decompilerSettings: new DecompilerSettings { // legacy csc generates a dead store in debug builds - RemoveDeadCode = (cscOptions == CompilerOptions.None) + RemoveDeadStores = (cscOptions == CompilerOptions.None) }); } diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index da888d71d..f62d22969 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -1095,6 +1095,20 @@ namespace ICSharpCode.Decompiler } } } + + bool removeDeadStores = false; + + [Category("DecompilerSettings.FSpecificOptions")] + [Description("DecompilerSettings.RemoveDeadStores")] + public bool RemoveDeadStores { + get { return removeDeadStores; } + set { + if (removeDeadStores != value) { + removeDeadStores = value; + OnPropertyChanged(); + } + } + } #endregion #region Assembly Load and Resolve options diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs b/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs index 8bde22e66..9fd519461 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/ControlFlowSimplification.cs @@ -46,7 +46,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow context.CancellationToken.ThrowIfCancellationRequested(); RemoveNopInstructions(block); - RemoveDeadStackStores(block, aggressive: context.Settings.RemoveDeadCode); + RemoveDeadStackStores(block, aggressive: context.Settings.RemoveDeadStores); InlineVariableInReturnBlock(block, context); // 1st pass SimplifySwitchInstruction before SimplifyBranchChains() diff --git a/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs b/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs index 9bdb3af18..43cb2e577 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/RemoveDeadVariableInit.cs @@ -41,7 +41,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms // This is necessary to remove useless stores generated by some compilers, e.g., the F# compiler. // In yield return + async, the C# compiler tends to store null/default(T) to variables // when the variable goes out of scope. - if (function.IsAsync || function.IsIterator || context.Settings.RemoveDeadCode) { + if (function.IsAsync || function.IsIterator || context.Settings.RemoveDeadStores) { var variableQueue = new Queue(function.Variables); while (variableQueue.Count > 0) { var v = variableQueue.Dequeue(); diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs index 845687b34..b555a63a4 100644 --- a/ILSpy/Properties/Resources.Designer.cs +++ b/ILSpy/Properties/Resources.Designer.cs @@ -801,6 +801,15 @@ namespace ICSharpCode.ILSpy.Properties { } } + /// + /// Looks up a localized string similar to Remove dead stores (use with caution!). + /// + public static string DecompilerSettings_RemoveDeadStores { + get { + return ResourceManager.GetString("DecompilerSettings.RemoveDeadStores", resourceCulture); + } + } + /// /// Looks up a localized string similar to Remove optional arguments, if possible. /// diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx index e38101b12..d98c8cdcc 100644 --- a/ILSpy/Properties/Resources.resx +++ b/ILSpy/Properties/Resources.resx @@ -751,4 +751,7 @@ Are you sure you want to continue? Select version of language to output + + Remove dead stores (use with caution!) + \ No newline at end of file From def681ccdfa95f50f12dc4e4326206676b8a7d0d Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 18 Aug 2019 00:29:27 +0200 Subject: [PATCH 218/221] Fix #1621: Add AllowUnreachableCases in switch analysis. --- .../TestCases/Pretty/Switch.cs | 23 +++++++++++++++++++ .../IL/ControlFlow/SwitchAnalysis.cs | 12 ++++++---- .../IL/ControlFlow/SwitchDetection.cs | 2 ++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs index aa5d29df2..a6f551686 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs @@ -1145,5 +1145,28 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } Console.WriteLine(); } + + public static string Issue1621(int x) + { + if (x == 5) { + return "5"; + } + switch (x) { + case 1: + return "1"; + case 2: + case 6: + case 7: + return "2-6-7"; + case 3: + return "3"; + case 4: + return "4"; + case 5: + return "unreachable"; + default: + throw new Exception(); + } + } } } \ No newline at end of file diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs index cb50ec78f..bc9b39b77 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchAnalysis.cs @@ -28,10 +28,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow /// /// The variable to be used as the argument of the switch instruction. /// - public ILVariable SwitchVariable - { - get { return switchVar; } - } + public ILVariable SwitchVariable => switchVar; /// /// Whether at least one the analyzed blocks contained an IL switch constructors. @@ -62,6 +59,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow public Block RootBlock { get; private set; } + /// + /// Gets/sets whether to allow unreachable cases in switch instructions. + /// + public bool AllowUnreachableCases { get; set; } + /// /// Analyze the last two statements in the block and see if they can be turned into a /// switch instruction. @@ -180,6 +182,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow } foreach (var section in inst.Sections) { var matchValues = section.Labels.AddOffset(offset).IntersectWith(inputValues); + if (!AllowUnreachableCases && matchValues.IsEmpty) + return false; if (matchValues.Count() > 1 && section.Body.MatchBranch(out var targetBlock) && AnalyzeBlock(targetBlock, matchValues)) { InnerBlocks.Add(targetBlock); } else { diff --git a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs index 72a741b28..08a1f185f 100644 --- a/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs +++ b/ICSharpCode.Decompiler/IL/ControlFlow/SwitchDetection.cs @@ -132,6 +132,8 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow { this.context = context; + analysis.AllowUnreachableCases = context.Settings.RemoveDeadCode; + foreach (var container in function.Descendants.OfType()) { currentContainer = container; controlFlowGraph = null; From bbb40ecb32cdaea8eb7dbe5eb6e05e456114fc9d Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 18 Aug 2019 18:38:55 +0200 Subject: [PATCH 219/221] Fix #1602: Recognize Roslyn empty string case block. --- .../TestCases/Pretty/Switch.cs | 24 ++++++ .../IL/Transforms/SwitchOnStringTransform.cs | 75 ++++++++++++++++++- 2 files changed, 97 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs index a6f551686..874769b9b 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Switch.cs @@ -1168,5 +1168,29 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty throw new Exception(); } } + + public static int Issue1602(string x) + { + switch (x) { + case null: + return 0; + case "": + return -1; + case "A": + return 65; + case "B": + return 66; + case "C": + return 67; + case "D": + return 68; + case "E": + return 69; + case "F": + return 70; + default: + throw new ArgumentOutOfRangeException(); + } + } } } \ No newline at end of file diff --git a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs index f1d1f126c..7c8575c20 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/SwitchOnStringTransform.cs @@ -820,8 +820,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms // extract target block if (!section.Body.MatchBranch(out Block target)) return false; - if (!MatchRoslynCaseBlockHead(target, switchValueLoad.Variable, out ILInstruction targetOrLeave, out Block currentExitBlock, out string stringValue)) + string stringValue; + if (MatchRoslynEmptyStringCaseBlockHead(target, switchValueLoad.Variable, out ILInstruction targetOrLeave, out Block currentExitBlock)) { + stringValue = ""; + } else if (!MatchRoslynCaseBlockHead(target, switchValueLoad.Variable, out targetOrLeave, out currentExitBlock, out stringValue)) { return false; + } + if (exitOrDefaultBlock != null && exitOrDefaultBlock != currentExitBlock) return false; exitOrDefaultBlock = currentExitBlock; @@ -902,7 +907,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// /// Matches (and the negated version): - /// if (call op_Equality(ldloc V_0, ldstr "Fifth case")) br body + /// if (call op_Equality(ldloc switchValueVar, stringValue)) br body /// br exit /// bool MatchRoslynCaseBlockHead(Block target, ILVariable switchValueVar, out ILInstruction bodyOrLeave, out Block defaultOrExitBlock, out string stringValue) @@ -938,6 +943,72 @@ namespace ICSharpCode.Decompiler.IL.Transforms return false; } + /// + /// Block target(incoming: 1) { + /// if (comp.o(ldloc switchValueVar == ldnull)) br exit + /// br lengthCheckBlock + /// } + /// + /// Block lengthCheckBlock(incoming: 1) { + /// if (logic.not(call get_Length(ldloc switchValueVar))) br body + /// br exit + /// } + /// + bool MatchRoslynEmptyStringCaseBlockHead(Block target, ILVariable switchValueVar, out ILInstruction bodyOrLeave, out Block defaultOrExitBlock) + { + bodyOrLeave = null; + defaultOrExitBlock = null; + if (target.Instructions.Count != 2 || target.IncomingEdgeCount != 1) + return false; + if (!target.Instructions[0].MatchIfInstruction(out var nullComparisonCondition, out var exitBranch)) + return false; + if (!nullComparisonCondition.MatchCompEqualsNull(out var arg) || !arg.MatchLdLoc(switchValueVar)) + return false; + if (!target.Instructions[1].MatchBranch(out Block lengthCheckBlock)) + return false; + if (lengthCheckBlock.Instructions.Count != 2 || lengthCheckBlock.IncomingEdgeCount != 1) + return false; + if (!lengthCheckBlock.Instructions[0].MatchIfInstruction(out var lengthCheckCondition, out var exitBranch2)) + return false; + ILInstruction bodyBranch; + if (lengthCheckCondition.MatchLogicNot(out arg)) { + bodyBranch = exitBranch2; + exitBranch2 = lengthCheckBlock.Instructions[1]; + lengthCheckCondition = arg; + } else { + bodyBranch = lengthCheckBlock.Instructions[1]; + } + if (!(exitBranch2.MatchBranch(out defaultOrExitBlock) || exitBranch2.MatchLeave(out _))) + return false; + if (!MatchStringLengthCall(lengthCheckCondition, switchValueVar)) + return false; + if (!(exitBranch.MatchBranch(out defaultOrExitBlock) && exitBranch2.MatchBranch(defaultOrExitBlock))) + return false; + if (bodyBranch.MatchLeave(out _)) { + bodyOrLeave = bodyBranch; + return true; + } + if (bodyBranch.MatchBranch(out var bodyBlock)) { + bodyOrLeave = bodyBlock; + return true; + } + return false; + } + + /// + /// call get_Length(ldloc switchValueVar) + /// + bool MatchStringLengthCall(ILInstruction inst, ILVariable switchValueVar) + { + return inst is Call call + && call.Method.DeclaringType.IsKnownType(KnownTypeCode.String) + && call.Method.IsAccessor + && call.Method.AccessorKind == System.Reflection.MethodSemanticsAttributes.Getter + && call.Method.AccessorOwner.Name == "Length" + && call.Arguments.Count == 1 + && call.Arguments[0].MatchLdLoc(switchValueVar); + } + /// /// Matches 'stloc(targetVar, call ComputeStringHash(ldloc switchValue))' /// From 427a459bfd547ad26951d0ec9f24c128f6575b19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jochen=20K=C3=BChner?= Date: Mon, 19 Aug 2019 08:46:04 +0200 Subject: [PATCH 220/221] fixes #1646 - netcoreapp uses wrong mscorlib (#1649) * fixes #1646 - netcoreapp uses wrong mscorlib * optimize framework detection * refactor decompiler runtime to a enum --- .../Metadata/UniversalAssemblyResolver.cs | 49 +++++++++++-------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs b/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs index 2cbaa95eb..eeafad3f0 100644 --- a/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs +++ b/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs @@ -29,6 +29,18 @@ namespace ICSharpCode.Decompiler.Metadata // This inspired by Mono.Cecil's BaseAssemblyResolver/DefaultAssemblyResolver. public class UniversalAssemblyResolver : IAssemblyResolver { + static UniversalAssemblyResolver() + { + // TODO : test whether this works with Mono on *Windows*, not sure if we'll + // ever need this... + if (Type.GetType("Mono.Runtime") != null) + decompilerRuntime = DecompilerRuntime.Mono; + else if (Environment.OSVersion.Platform == PlatformID.Unix) + decompilerRuntime = DecompilerRuntime.Mono; + else if (typeof(object).Assembly.GetName().Name == "System.Private.CoreLib") + decompilerRuntime = DecompilerRuntime.NETCoreApp; + } + DotNetCorePathFinder dotNetCorePathFinder; readonly bool throwOnError; readonly PEStreamOptions streamOptions; @@ -38,21 +50,7 @@ namespace ICSharpCode.Decompiler.Metadata readonly List directories = new List(); readonly List gac_paths = GetGacPaths(); HashSet targetFrameworkSearchPaths; - - /// - /// Detect whether we're in a Mono environment. - /// - /// This is used whenever we're trying to decompile a plain old .NET framework assembly on Unix. - static bool DetectMono() - { - // TODO : test whether this works with Mono on *Windows*, not sure if we'll - // ever need this... - if (Type.GetType("Mono.Runtime") != null) - return true; - if (Environment.OSVersion.Platform == PlatformID.Unix) - return true; - return false; - } + static readonly DecompilerRuntime decompilerRuntime; public void AddSearchDirectory(string directory) { @@ -77,6 +75,13 @@ namespace ICSharpCode.Decompiler.Metadata Silverlight } + enum DecompilerRuntime + { + NETFramework, + NETCoreApp, + Mono + } + string targetFramework; TargetFrameworkIdentifier targetFrameworkIdentifier; Version targetFrameworkVersion; @@ -291,7 +296,7 @@ namespace ICSharpCode.Decompiler.Metadata return assembly; var framework_dir = Path.GetDirectoryName(typeof(object).Module.FullyQualifiedName); - var framework_dirs = DetectMono() + var framework_dirs = decompilerRuntime == DecompilerRuntime.Mono ? new[] { framework_dir, Path.Combine(framework_dir, "Facades") } : new[] { framework_dir }; @@ -367,11 +372,13 @@ namespace ICSharpCode.Decompiler.Metadata var version = reference.Version; var corlib = typeof(object).Assembly.GetName(); - if (corlib.Version == version || IsSpecialVersionOrRetargetable(reference)) - return typeof(object).Module.FullyQualifiedName; + if (decompilerRuntime != DecompilerRuntime.NETCoreApp) { + if (corlib.Version == version || IsSpecialVersionOrRetargetable(reference)) + return typeof(object).Module.FullyQualifiedName; + } string path; - if (DetectMono()) { + if (decompilerRuntime == DecompilerRuntime.Mono) { path = GetMonoMscorlibBasePath(version); } else { path = GetMscorlibBasePath(version, reference.PublicKeyToken.ToHexString(8)); @@ -460,7 +467,7 @@ namespace ICSharpCode.Decompiler.Metadata static List GetGacPaths() { - if (DetectMono()) + if (decompilerRuntime == DecompilerRuntime.Mono) return GetDefaultMonoGacPaths(); var paths = new List(2); @@ -510,7 +517,7 @@ namespace ICSharpCode.Decompiler.Metadata if (reference.PublicKeyToken == null || reference.PublicKeyToken.Length == 0) return null; - if (DetectMono()) + if (decompilerRuntime == DecompilerRuntime.Mono) return GetAssemblyInMonoGac(reference); return GetAssemblyInNetGac(reference); From 22fa104ed6b64a96986f126c1a886d9152840810 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Mon, 19 Aug 2019 09:14:52 +0200 Subject: [PATCH 221/221] UniversalAssemblyResolver: Unix can be either .NET Core or Mono, hence put the check last. --- ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs b/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs index eeafad3f0..51a1c8cfe 100644 --- a/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs +++ b/ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs @@ -35,10 +35,10 @@ namespace ICSharpCode.Decompiler.Metadata // ever need this... if (Type.GetType("Mono.Runtime") != null) decompilerRuntime = DecompilerRuntime.Mono; - else if (Environment.OSVersion.Platform == PlatformID.Unix) - decompilerRuntime = DecompilerRuntime.Mono; else if (typeof(object).Assembly.GetName().Name == "System.Private.CoreLib") decompilerRuntime = DecompilerRuntime.NETCoreApp; + else if (Environment.OSVersion.Platform == PlatformID.Unix) + decompilerRuntime = DecompilerRuntime.Mono; } DotNetCorePathFinder dotNetCorePathFinder;