diff --git a/ICSharpCode.Decompiler/IL/ILVariable.cs b/ICSharpCode.Decompiler/IL/ILVariable.cs index 8f6ec5e08..c89821c5c 100644 --- a/ICSharpCode.Decompiler/IL/ILVariable.cs +++ b/ICSharpCode.Decompiler/IL/ILVariable.cs @@ -335,8 +335,23 @@ namespace ICSharpCode.Decompiler.IL { output.WriteReference(this.Name, this, isLocal: true); } + + /// + /// Gets whether this variable occurs within the specified instruction. + /// + internal bool IsUsedWithin(ILInstruction inst) + { + if (inst is IInstructionWithVariableOperand iwvo && iwvo.Variable == this) { + return true; + } + foreach (var child in inst.Children) { + if (IsUsedWithin(child)) + return true; + } + return false; + } } - + public interface IInstructionWithVariableOperand { ILVariable Variable { get; set; } diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index c5d4786c5..809aabafe 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -93,7 +93,8 @@ namespace ICSharpCode.Decompiler.IL LdLoc, /// Loads the address of a local variable. (ldarga/ldloca) LdLoca, - /// Stores a value into a local variable. (starg/stloc) + /// Stores a value into a local variable. (IL: starg/stloc) + /// Evaluates to the value that was stored (for byte/short variables: evaluates to the truncated value) StLoc, /// Stores the value into an anonymous temporary variable, and returns the address of that variable. AddressOf, @@ -137,7 +138,8 @@ namespace ICSharpCode.Decompiler.IL IsInst, /// Indirect load (ref/pointer dereference). LdObj, - /// Indirect store (store to ref/pointer). + /// Indirect store (store to ref/pointer). + /// Evaluates to the value that was stored (when using type byte/short: evaluates to the truncated value) StObj, /// Boxes a value. Box, @@ -2217,7 +2219,8 @@ namespace ICSharpCode.Decompiler.IL } namespace ICSharpCode.Decompiler.IL { - /// Stores a value into a local variable. (starg/stloc) + /// Stores a value into a local variable. (IL: starg/stloc) + /// Evaluates to the value that was stored (for byte/short variables: evaluates to the truncated value) public sealed partial class StLoc : ILInstruction, IStoreInstruction { public StLoc(ILVariable variable, ILInstruction value) : base(OpCode.StLoc) @@ -3540,7 +3543,8 @@ namespace ICSharpCode.Decompiler.IL } namespace ICSharpCode.Decompiler.IL { - /// Indirect store (store to ref/pointer). + /// Indirect store (store to ref/pointer). + /// Evaluates to the value that was stored (when using type byte/short: evaluates to the truncated value) public sealed partial class StObj : ILInstruction, ISupportsVolatilePrefix, ISupportsUnalignedPrefix { public StObj(ILInstruction target, ILInstruction value, IType type) : base(OpCode.StObj) diff --git a/ICSharpCode.Decompiler/IL/Instructions.tt b/ICSharpCode.Decompiler/IL/Instructions.tt index cab30f507..126b9c795 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.tt +++ b/ICSharpCode.Decompiler/IL/Instructions.tt @@ -150,7 +150,8 @@ CustomClassName("LdLoc"), NoArguments, HasVariableOperand("Load"), ResultType("variable.StackType")), new OpCode("ldloca", "Loads the address of a local variable. (ldarga/ldloca)", CustomClassName("LdLoca"), NoArguments, ResultType("Ref"), HasVariableOperand("Address")), - new OpCode("stloc", "Stores a value into a local variable. (starg/stloc)", + new OpCode("stloc", "Stores a value into a local variable. (IL: starg/stloc)" + Environment.NewLine + + "Evaluates to the value that was stored (for byte/short variables: evaluates to the truncated value)", CustomClassName("StLoc"), HasVariableOperand("Store"), CustomArguments("value"), ResultType("variable.StackType")), new OpCode("addressof", "Stores the value into an anonymous temporary variable, and returns the address of that variable.", @@ -204,10 +205,11 @@ new OpCode("ldobj", "Indirect load (ref/pointer dereference).", CustomClassName("LdObj"), CustomArguments("target"), HasTypeOperand, MemoryAccess, CustomWriteToButKeepOriginal, SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("type.GetStackType()")), - new OpCode("stobj", "Indirect store (store to ref/pointer).", + new OpCode("stobj", "Indirect store (store to ref/pointer)." + Environment.NewLine + + "Evaluates to the value that was stored (when using type byte/short: evaluates to the truncated value)", CustomClassName("StObj"), CustomArguments("target", "value"), HasTypeOperand, MemoryAccess, CustomWriteToButKeepOriginal, SupportsVolatilePrefix, SupportsUnalignedPrefix, MayThrow, ResultType("type.GetStackType()")), - + new OpCode("box", "Boxes a value.", Unary, HasTypeOperand, MemoryAccess, MayThrow, ResultType("O")), new OpCode("unbox", "Compute address inside box.", diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs index a1de1bf3e..c18d1ed96 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs @@ -41,7 +41,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms block.Instructions.RemoveAt(i); continue; } - if (TransformInlineAssignmentStObj(block, i)) + if (TransformInlineAssignmentStObj(block, i) || TransformInlineAssignmentLocal(block, i)) continue; if (TransformInlineCompoundAssignmentCall(block, i)) continue; @@ -59,13 +59,17 @@ namespace ICSharpCode.Decompiler.IL.Transforms /// --> /// stloc l(stobj (..., value)) /// + /// e.g. used for inline assignment to instance field + /// /// -or- + /// /// /// stloc s(value) /// stobj (..., ldloc s) /// --> /// stloc s(stobj (..., value)) /// + /// e.g. used for inline assignment to static field bool TransformInlineAssignmentStObj(Block block, int i) { var inst = block.Instructions[i] as StLoc; @@ -76,27 +80,28 @@ namespace ICSharpCode.Decompiler.IL.Transforms ILInstruction replacement; StObj fieldStore; ILVariable local; - if (nextInst is StLoc) { // instance fields - var localStore = (StLoc)nextInst; - if (localStore.Variable.Kind == VariableKind.StackSlot || !localStore.Value.MatchLdLoc(inst.Variable)) + if (nextInst is StLoc localStore) { // with extra local + if (localStore.Variable.Kind != VariableKind.Local || !localStore.Value.MatchLdLoc(inst.Variable)) + return false; + if (!(inst.Variable.IsSingleDefinition && inst.Variable.LoadCount == 2)) return false; var memberStore = block.Instructions.ElementAtOrDefault(i + 2); if (memberStore is StObj) { fieldStore = memberStore as StObj; - if (!fieldStore.Value.MatchLdLoc(inst.Variable)) + if (!fieldStore.Value.MatchLdLoc(inst.Variable) || localStore.Variable.IsUsedWithin(fieldStore.Target)) return false; replacement = new StObj(fieldStore.Target, inst.Value, fieldStore.Type); - } else { // otherwise it must be local - return TransformInlineAssignmentLocal(block, i); + } else { + return false; } - context.Step("Inline assignment to instance field", fieldStore); + context.Step("Inline assignment stobj (with extra local)", fieldStore); local = localStore.Variable; block.Instructions.RemoveAt(i + 1); - } else if (nextInst is StObj) { // static fields + } else if (nextInst is StObj) { // without extra local fieldStore = (StObj)nextInst; - if (!fieldStore.Value.MatchLdLoc(inst.Variable) || (fieldStore.Target.MatchLdFlda(out var target, out _) && target.MatchLdLoc(inst.Variable))) + if (!fieldStore.Value.MatchLdLoc(inst.Variable) || inst.Variable.IsUsedWithin(fieldStore.Target)) return false; - context.Step("Inline assignment to static field", fieldStore); + context.Step("Inline assignment stobj", fieldStore); local = inst.Variable; replacement = new StObj(fieldStore.Target, inst.Value, fieldStore.Type); } else { @@ -246,7 +251,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms var nextInst = block.Instructions.ElementAtOrDefault(i + 1) as StLoc; if (inst == null || nextInst == null) return false; - if (nextInst.Variable.Kind == VariableKind.StackSlot || !nextInst.Value.MatchLdLoc(inst.Variable)) + if (inst.Variable.Kind != VariableKind.StackSlot) + return false; + if (nextInst.Variable.Kind != VariableKind.Local || !nextInst.Value.MatchLdLoc(inst.Variable)) return false; context.Step("Inline assignment to local variable", inst); var value = inst.Value;