Browse Source

Merge branch 'master' of git://github.com/icsharpcode/ILSpy into Debugger

pull/191/merge
Eusebiu Marcu 15 years ago
parent
commit
fb8a453f79
  1. 16
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 2
      ICSharpCode.Decompiler/DecompilerSettings.cs
  3. 61
      ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs
  4. 133
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  5. 31
      ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs
  6. 16
      ICSharpCode.Decompiler/Tests/ValueTypes.cs

16
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -152,6 +152,12 @@ namespace ICSharpCode.Decompiler.Ast
} }
if (tryCatchNode.FinallyBlock != null) if (tryCatchNode.FinallyBlock != null)
tryCatchStmt.FinallyBlock = TransformBlock(tryCatchNode.FinallyBlock); tryCatchStmt.FinallyBlock = TransformBlock(tryCatchNode.FinallyBlock);
if (tryCatchNode.FaultBlock != null) {
CatchClause cc = new CatchClause();
cc.Body = TransformBlock(tryCatchNode.FaultBlock);
cc.Body.Add(new ThrowStatement()); // rethrow
tryCatchStmt.CatchClauses.Add(cc);
}
yield return tryCatchStmt; yield return tryCatchStmt;
} else if (node is ILBlock) { } else if (node is ILBlock) {
yield return TransformBlock((ILBlock)node); yield return TransformBlock((ILBlock)node);
@ -534,6 +540,16 @@ namespace ICSharpCode.Decompiler.Ast
} }
} }
if (cecilMethod.Name == ".ctor" && cecilMethod.DeclaringType.IsValueType) {
// On value types, the constructor can be called.
// This is equivalent to 'target = new ValueType(args);'.
ObjectCreateExpression oce = new ObjectCreateExpression();
oce.Type = AstBuilder.ConvertType(cecilMethod.DeclaringType);
AdjustArgumentsForMethodCall(cecilMethod, methodArgs);
oce.Arguments.AddRange(methodArgs);
return new AssignmentExpression(target, oce);
}
if (cecilMethod.Name == "Get" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 1) { if (cecilMethod.Name == "Get" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 1) {
return target.Indexer(methodArgs); return target.Indexer(methodArgs);
} else if (cecilMethod.Name == "Set" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 2) { } else if (cecilMethod.Name == "Set" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 2) {

2
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -41,8 +41,6 @@ namespace ICSharpCode.Decompiler
} }
} }
public event EventHandler YieldReturnChanged;
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) protected virtual void OnPropertyChanged(string propertyName)

61
ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs

@ -208,9 +208,6 @@ namespace ICSharpCode.Decompiler.ILAst
MethodDefinition methodDef; MethodDefinition methodDef;
bool optimize; bool optimize;
Dictionary<Instruction, ByteCode> instrToByteCode = new Dictionary<Instruction, ByteCode>();
Dictionary<ILVariable, bool> allowInline = new Dictionary<ILVariable, bool>();
// Virtual instructions to load exception on stack // Virtual instructions to load exception on stack
Dictionary<ExceptionHandler, ByteCode> ldexceptions = new Dictionary<ExceptionHandler, ILAstBuilder.ByteCode>(); Dictionary<ExceptionHandler, ByteCode> ldexceptions = new Dictionary<ExceptionHandler, ILAstBuilder.ByteCode>();
@ -230,6 +227,8 @@ namespace ICSharpCode.Decompiler.ILAst
List<ByteCode> StackAnalysis(MethodDefinition methodDef) List<ByteCode> StackAnalysis(MethodDefinition methodDef)
{ {
Dictionary<Instruction, ByteCode> instrToByteCode = new Dictionary<Instruction, ByteCode>();
// Create temporary structure for the stack analysis // Create temporary structure for the stack analysis
List<ByteCode> body = new List<ByteCode>(methodDef.Body.Instructions.Count); List<ByteCode> body = new List<ByteCode>(methodDef.Body.Instructions.Count);
List<Instruction> prefixes = null; List<Instruction> prefixes = null;
@ -413,9 +412,6 @@ namespace ICSharpCode.Decompiler.ILAst
} }
pushedBy.StoreTo.Add(tmpVar); pushedBy.StoreTo.Add(tmpVar);
} }
if (byteCode.StackBefore[i].PushedBy.Length == 1) {
allowInline[tmpVar] = true;
}
argIdx++; argIdx++;
} }
} }
@ -507,13 +503,6 @@ namespace ICSharpCode.Decompiler.ILAst
newVars.Add(mergedVar); newVars.Add(mergedVar);
} }
} }
// Permit inlining
foreach(VariableInfo newVar in newVars) {
if (newVar.Stores.Count == 1 && newVar.Loads.Count == 1) {
allowInline[newVar.Variable] = true;
}
}
} }
// Set bytecode operands // Set bytecode operands
@ -668,56 +657,12 @@ namespace ICSharpCode.Decompiler.ILAst
} else { } else {
ILVariable tmpVar = new ILVariable() { Name = "expr_" + byteCode.Offset.ToString("X2"), IsGenerated = true }; ILVariable tmpVar = new ILVariable() { Name = "expr_" + byteCode.Offset.ToString("X2"), IsGenerated = true };
ast.Add(new ILExpression(ILCode.Stloc, tmpVar, expr)); ast.Add(new ILExpression(ILCode.Stloc, tmpVar, expr));
foreach(ILVariable storeTo in byteCode.StoreTo) { foreach(ILVariable storeTo in byteCode.StoreTo.AsEnumerable().Reverse()) {
ast.Add(new ILExpression(ILCode.Stloc, storeTo, new ILExpression(ILCode.Ldloc, tmpVar))); ast.Add(new ILExpression(ILCode.Stloc, storeTo, new ILExpression(ILCode.Ldloc, tmpVar)));
} }
} }
} }
// Try to in-line stloc / ldloc pairs
for(int i = 0; i < ast.Count - 1; i++) {
if (i < 0) continue;
ILExpression currExpr = ast[i] as ILExpression;
ILExpression nextExpr = ast[i + 1] as ILExpression;
if (currExpr != null && nextExpr != null && currExpr.Code == ILCode.Stloc) {
// If the next expression is generated stloc, look inside
if (nextExpr.Code == ILCode.Stloc && ((ILVariable)nextExpr.Operand).IsGenerated) {
nextExpr = nextExpr.Arguments[0];
}
// Find the use of the 'expr'
for(int j = 0; j < nextExpr.Arguments.Count; j++) {
ILExpression arg = nextExpr.Arguments[j];
// We are moving the expression evaluation past the other aguments.
// It is ok to pass ldloc because the expression can not contain stloc and thus the ldcoc will still return the same value
// Do not inline ldloca
if (arg.Code == ILCode.Ldloc) {
if (arg.Operand == currExpr.Operand) {
bool canInline;
allowInline.TryGetValue((ILVariable)arg.Operand, out canInline);
if (canInline) {
// Assigne the ranges for optimized away instrustions somewhere
currExpr.Arguments[0].ILRanges.AddRange(currExpr.ILRanges);
currExpr.Arguments[0].ILRanges.AddRange(nextExpr.Arguments[j].ILRanges);
ast.RemoveAt(i);
nextExpr.Arguments[j] = currExpr.Arguments[0]; // Inline the stloc body
i -= 2; // Try the same index again
break; // Found
}
}
} else {
break; // Side-effects
}
}
}
}
return ast; return ast;
} }
} }

133
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -11,6 +11,8 @@ namespace ICSharpCode.Decompiler.ILAst
{ {
public enum ILAstOptimizationStep public enum ILAstOptimizationStep
{ {
SimpleGotoAndNopRemoval,
InlineVariables,
ReduceBranchInstructionSet, ReduceBranchInstructionSet,
YieldReturn, YieldReturn,
SplitToMovableBlocks, SplitToMovableBlocks,
@ -22,6 +24,7 @@ namespace ICSharpCode.Decompiler.ILAst
DuplicateReturns, DuplicateReturns,
FlattenIfStatements, FlattenIfStatements,
PeepholeTransforms, PeepholeTransforms,
InlineVariables2,
TypeInference, TypeInference,
None None
} }
@ -34,6 +37,13 @@ namespace ICSharpCode.Decompiler.ILAst
public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None) public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None)
{ {
if (abortBeforeStep == ILAstOptimizationStep.SimpleGotoAndNopRemoval) return;
SimpleGotoAndNopRemoval(method);
if (abortBeforeStep == ILAstOptimizationStep.InlineVariables) return;
// Works better after simple goto removal because of the following debug pattern: stloc X; br Next; Next:; ldloc X
InlineVariables(method);
if (abortBeforeStep == ILAstOptimizationStep.ReduceBranchInstructionSet) return; if (abortBeforeStep == ILAstOptimizationStep.ReduceBranchInstructionSet) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) { foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) {
ReduceBranchInstructionSet(block); ReduceBranchInstructionSet(block);
@ -78,6 +88,7 @@ namespace ICSharpCode.Decompiler.ILAst
FlattenBasicBlocks(method); FlattenBasicBlocks(method);
if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval) return; if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval) return;
SimpleGotoAndNopRemoval(method);
new GotoRemoval().RemoveGotos(method); new GotoRemoval().RemoveGotos(method);
if (abortBeforeStep == ILAstOptimizationStep.DuplicateReturns) return; if (abortBeforeStep == ILAstOptimizationStep.DuplicateReturns) return;
@ -89,12 +100,123 @@ namespace ICSharpCode.Decompiler.ILAst
if (abortBeforeStep == ILAstOptimizationStep.PeepholeTransforms) return; if (abortBeforeStep == ILAstOptimizationStep.PeepholeTransforms) return;
PeepholeTransforms.Run(context, method); PeepholeTransforms.Run(context, method);
if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return;
InlineVariables(method);
if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return; if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return;
TypeAnalysis.Run(context, method); TypeAnalysis.Run(context, method);
GotoRemoval.RemoveRedundantCode(method); GotoRemoval.RemoveRedundantCode(method);
} }
void SimpleGotoAndNopRemoval(ILBlock method)
{
Dictionary<ILLabel, int> labelRefCount = new Dictionary<ILLabel, int>();
foreach (ILLabel target in method.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.GetBranchTargets())) {
labelRefCount[target] = labelRefCount.GetOrDefault(target) + 1;
}
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) {
List<ILNode> body = block.Body;
List<ILNode> newBody = new List<ILNode>(body.Count);
for (int i = 0; i < body.Count; i++) {
ILLabel target;
if (body[i].Match(ILCode.Br, out target) && i+1 < body.Count && body[i+1] == target) {
// Ignore the branch TODO: ILRanges
if (labelRefCount[target] == 1)
i++; // Ignore the label as well
} else if (body[i].Match(ILCode.Nop)){
// Ignore nop TODO: ILRanges
} else {
newBody.Add(body[i]);
}
}
block.Body = newBody;
}
}
void InlineVariables(ILBlock method)
{
// Analyse the whole method
Dictionary<ILVariable, int> numStloc = new Dictionary<ILVariable, int>();
Dictionary<ILVariable, int> numLdloc = new Dictionary<ILVariable, int>();
Dictionary<ILVariable, int> numLdloca = new Dictionary<ILVariable, int>();
foreach(ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) {
ILVariable locVar = expr.Operand as ILVariable;
if (locVar != null) {
if (expr.Code == ILCode.Stloc) {
numStloc[locVar] = numStloc.GetOrDefault(locVar) + 1;
} else if (expr.Code == ILCode.Ldloc) {
numLdloc[locVar] = numLdloc.GetOrDefault(locVar) + 1;
} else if (expr.Code == ILCode.Ldloca) {
numLdloca[locVar] = numLdloca.GetOrDefault(locVar) + 1;
} else {
throw new NotSupportedException(expr.Code.ToString());
}
}
}
// Inline all blocks
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
List<ILNode> body = block.Body;
for(int i = 0; i < body.Count - 1;) {
ILExpression nextExpr = body[i + 1] as ILExpression;
ILVariable locVar;
ILExpression expr;
ILExpression ldParent;
int ldPos;
if (body[i].Match(ILCode.Stloc, out locVar, out expr) &&
numStloc.GetOrDefault(locVar) == 1 &&
numLdloc.GetOrDefault(locVar) == 1 &&
numLdloca.GetOrDefault(locVar) == 0 &&
nextExpr != null &&
FindLdloc(nextExpr, locVar, out ldParent, out ldPos) == true &&
ldParent != null)
{
// Assign the ranges of the optimized instrustions
expr.ILRanges.AddRange(((ILExpression)body[i]).ILRanges);
expr.ILRanges.AddRange(ldParent.Arguments[ldPos].ILRanges);
// We are moving the expression evaluation past the other aguments.
// It is ok to pass ldloc because the expression can not contain stloc and thus the ldloc will still return the same value
body.RemoveAt(i);
ldParent.Arguments[ldPos] = expr; // Inline the stloc body
i = Math.Max(0, i - 1); // Go back one step
} else {
i++;
}
}
}
}
/// <summary>
/// Finds the position to inline to.
/// </summary>
/// <returns>true = found; false = cannot continue search; null = not found</returns>
static bool? FindLdloc(ILExpression expr, ILVariable v, out ILExpression parent, out int pos)
{
parent = null;
pos = 0;
for (int i = 0; i < expr.Arguments.Count; i++) {
// Stop when seeing an opcode that does not guarantee that its operands will be evaluated
// Inlining in that case migth result in the inlined expresion not being evaluted
if (i == 1 && (expr.Code == ILCode.LogicAnd || expr.Code == ILCode.LogicOr || expr.Code == ILCode.TernaryOp))
return false;
ILExpression arg = expr.Arguments[i];
if (arg.Code == ILCode.Ldloc && arg.Operand == v) {
parent = expr;
pos = i;
return true;
}
bool? r = FindLdloc(arg, v, out parent, out pos);
if (r != null)
return r;
}
return expr.Code == ILCode.Ldloc ? (bool?)null : false;
}
/// <summary> /// <summary>
/// Reduces the branch codes to just br and brtrue. /// Reduces the branch codes to just br and brtrue.
/// Moves ILRanges to the branch argument /// Moves ILRanges to the branch argument
@ -136,10 +258,6 @@ namespace ICSharpCode.Decompiler.ILAst
/// </summary> /// </summary>
void SplitToBasicBlocks(ILBlock block) void SplitToBasicBlocks(ILBlock block)
{ {
// Remve no-ops
// TODO: Assign the no-op range to someting
block.Body = block.Body.Where(n => !(n is ILExpression && ((ILExpression)n).Code == ILCode.Nop)).ToList();
List<ILNode> basicBlocks = new List<ILNode>(); List<ILNode> basicBlocks = new List<ILNode>();
ILBasicBlock basicBlock = new ILBasicBlock() { ILBasicBlock basicBlock = new ILBasicBlock() {
@ -941,6 +1059,13 @@ namespace ICSharpCode.Decompiler.ILAst
return true; return true;
} }
public static V GetOrDefault<K,V>(this Dictionary<K, V> dict, K key)
{
V ret;
dict.TryGetValue(key, out ret);
return ret;
}
public static void RemoveOrThrow<T>(this ICollection<T> collection, T item) public static void RemoveOrThrow<T>(this ICollection<T> collection, T item)
{ {
if (!collection.Remove(item)) if (!collection.Remove(item))

31
ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs

@ -118,8 +118,6 @@ namespace ICSharpCode.Decompiler.ILAst
// the compiler might skip the above instruction in release builds; in that case, it directly returns stloc.Operand // the compiler might skip the above instruction in release builds; in that case, it directly returns stloc.Operand
var2 = var1; var2 = var1;
} }
if (!SkipDummyBr(method, ref i))
return false;
ILExpression retArg; ILExpression retArg;
if (i < method.Body.Count && method.Body[i].Match(ILCode.Ret, out retArg)) { if (i < method.Body.Count && method.Body[i].Match(ILCode.Ret, out retArg)) {
// ret(ldloc(var_2)) // ret(ldloc(var_2))
@ -146,17 +144,6 @@ namespace ICSharpCode.Decompiler.ILAst
return method as MethodDefinition; return method as MethodDefinition;
} }
bool SkipDummyBr(ILBlock method, ref int i)
{
ILLabel target;
if (i + 1 < method.Body.Count && method.Body[i].Match(ILCode.Br, out target)) {
if (target != method.Body[i + 1])
return false;
i += 2;
}
return true;
}
bool MatchEnumeratorCreationNewObj(ILExpression expr, out MethodDefinition ctor) bool MatchEnumeratorCreationNewObj(ILExpression expr, out MethodDefinition ctor)
{ {
// newobj(CurrentType/...::.ctor, ldc.i4(-2)) // newobj(CurrentType/...::.ctor, ldc.i4(-2))
@ -243,7 +230,7 @@ namespace ICSharpCode.Decompiler.ILAst
StoreToVariable v = new StoreToVariable(new ILExpression(ILCode.Ldfld, ILExpression.AnyOperand, LoadFromArgument.This)); StoreToVariable v = new StoreToVariable(new ILExpression(ILCode.Ldfld, ILExpression.AnyOperand, LoadFromArgument.This));
if (v.Match(method.Body[0])) { if (v.Match(method.Body[0])) {
int i = 1; int i = 1;
if (SkipDummyBr(method, ref i) && i == method.Body.Count - 1) { if (i == method.Body.Count - 1) {
if (new ILExpression(ILCode.Ret, null, new LoadFromVariable(v)).Match(method.Body[i])) { if (new ILExpression(ILCode.Ret, null, new LoadFromVariable(v)).Match(method.Body[i])) {
currentField = GetFieldDefinition(((ILExpression)method.Body[0]).Arguments[0].Operand as FieldReference); currentField = GetFieldDefinition(((ILExpression)method.Body[0]).Arguments[0].Operand as FieldReference);
} }
@ -310,16 +297,14 @@ namespace ICSharpCode.Decompiler.ILAst
foreach (var tryFinally in ilMethod.GetSelfAndChildrenRecursive<ILTryCatchBlock>()) { foreach (var tryFinally in ilMethod.GetSelfAndChildrenRecursive<ILTryCatchBlock>()) {
Interval interval = ranges[tryFinally.TryBlock.Body[0]].ToEnclosingInterval(); Interval interval = ranges[tryFinally.TryBlock.Body[0]].ToEnclosingInterval();
var finallyBody = tryFinally.FinallyBlock.Body; var finallyBody = tryFinally.FinallyBlock.Body;
if (!(finallyBody.Count == 2 || finallyBody.Count == 3)) if (finallyBody.Count != 2)
throw new YieldAnalysisFailedException(); throw new YieldAnalysisFailedException();
ILExpression call = finallyBody[0] as ILExpression; ILExpression call = finallyBody[0] as ILExpression;
if (call == null || call.Code != ILCode.Call || call.Arguments.Count != 1) if (call == null || call.Code != ILCode.Call || call.Arguments.Count != 1)
throw new YieldAnalysisFailedException(); throw new YieldAnalysisFailedException();
if (call.Arguments[0].Code != ILCode.Ldarg || ((ParameterDefinition)call.Arguments[0].Operand).Index >= 0) if (call.Arguments[0].Code != ILCode.Ldarg || ((ParameterDefinition)call.Arguments[0].Operand).Index >= 0)
throw new YieldAnalysisFailedException(); throw new YieldAnalysisFailedException();
if (finallyBody.Count == 3 && !finallyBody[1].Match(ILCode.Nop)) if (!finallyBody[1].Match(ILCode.Endfinally))
throw new YieldAnalysisFailedException();
if (!finallyBody[finallyBody.Count - 1].Match(ILCode.Endfinally))
throw new YieldAnalysisFailedException(); throw new YieldAnalysisFailedException();
MethodDefinition mdef = GetMethodDefinition(call.Operand as MethodReference); MethodDefinition mdef = GetMethodDefinition(call.Operand as MethodReference);
@ -651,8 +636,6 @@ namespace ICSharpCode.Decompiler.ILAst
if (!ilMethod.Body.Last().Match(ILCode.Ret, out lastReturnArg)) if (!ilMethod.Body.Last().Match(ILCode.Ret, out lastReturnArg))
throw new YieldAnalysisFailedException(); throw new YieldAnalysisFailedException();
ilMethod.Body.RemoveAll(n => n.Match(ILCode.Nop)); // remove nops
// There are two possibilities: // There are two possibilities:
if (lastReturnArg.Code == ILCode.Ldloc) { if (lastReturnArg.Code == ILCode.Ldloc) {
// a) the compiler uses a variable for returns (in debug builds, or when there are try-finally blocks) // a) the compiler uses a variable for returns (in debug builds, or when there are try-finally blocks)
@ -682,7 +665,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILBlock faultBlock = tryFaultBlock.FaultBlock; ILBlock faultBlock = tryFaultBlock.FaultBlock;
// Ensure the fault block contains the call to Dispose(). // Ensure the fault block contains the call to Dispose().
if (!(faultBlock.Body.Count == 2 || faultBlock.Body.Count == 3)) if (faultBlock.Body.Count != 2)
throw new YieldAnalysisFailedException(); throw new YieldAnalysisFailedException();
MethodReference disposeMethodRef; MethodReference disposeMethodRef;
ILExpression disposeArg; ILExpression disposeArg;
@ -690,13 +673,10 @@ namespace ICSharpCode.Decompiler.ILAst
throw new YieldAnalysisFailedException(); throw new YieldAnalysisFailedException();
if (GetMethodDefinition(disposeMethodRef) != disposeMethod || !LoadFromArgument.This.Match(disposeArg)) if (GetMethodDefinition(disposeMethodRef) != disposeMethod || !LoadFromArgument.This.Match(disposeArg))
throw new YieldAnalysisFailedException(); throw new YieldAnalysisFailedException();
if (faultBlock.Body.Count == 3 && !faultBlock.Body[1].Match(ILCode.Nop)) if (!faultBlock.Body[1].Match(ILCode.Endfinally))
throw new YieldAnalysisFailedException();
if (!faultBlock.Body[faultBlock.Body.Count - 1].Match(ILCode.Endfinally))
throw new YieldAnalysisFailedException(); throw new YieldAnalysisFailedException();
body = tryFaultBlock.TryBlock.Body; body = tryFaultBlock.TryBlock.Body;
body.RemoveAll(n => n.Match(ILCode.Nop)); // remove nops
bodyLength = body.Count; bodyLength = body.Count;
} else { } else {
// no try-finally blocks // no try-finally blocks
@ -872,7 +852,6 @@ namespace ICSharpCode.Decompiler.ILAst
ILBlock ConvertFinallyBlock(MethodDefinition finallyMethod) ILBlock ConvertFinallyBlock(MethodDefinition finallyMethod)
{ {
ILBlock block = CreateILAst(finallyMethod); ILBlock block = CreateILAst(finallyMethod);
block.Body.RemoveAll(n => n.Match(ILCode.Nop));
// Get rid of assignment to state // Get rid of assignment to state
FieldReference stfld; FieldReference stfld;
List<ILExpression> args; List<ILExpression> args;

16
ICSharpCode.Decompiler/Tests/ValueTypes.cs

@ -9,6 +9,11 @@ public static class ValueTypes
{ {
public int Field; public int Field;
public S(int field)
{
this.Field = field;
}
public void SetField() public void SetField()
{ {
this.Field = 5; this.Field = 5;
@ -46,6 +51,17 @@ public static class ValueTypes
p = default(S); p = default(S);
} }
public static S CallValueTypeCtor1()
{
return new S(10);
}
public static S CallValueTypeCtor2()
{
S s = new S(10);
return s;
}
public static S Copy1(S p) public static S Copy1(S p)
{ {
return p; return p;

Loading…
Cancel
Save