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;