Browse Source

Fix "yield return" decompilation in generic classes.

pull/70/head
Daniel Grunwald 15 years ago
parent
commit
cdc56cb98a
  1. 54
      ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs

54
ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs

@ -102,7 +102,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -102,7 +102,8 @@ namespace ICSharpCode.Decompiler.ILAst
break;
if (ldloc.Code != ILCode.Ldloc || ldarg.Code != ILCode.Ldarg)
return false;
if (ldloc.Operand != var1 || !(storedField is FieldDefinition))
storedField = GetFieldDefinition(storedField);
if (ldloc.Operand != var1 || storedField == null)
return false;
fieldToParameterMap[(FieldDefinition)storedField] = (ParameterDefinition)ldarg.Operand;
}
@ -129,6 +130,22 @@ namespace ICSharpCode.Decompiler.ILAst @@ -129,6 +130,22 @@ namespace ICSharpCode.Decompiler.ILAst
return false;
}
static FieldDefinition GetFieldDefinition(FieldReference field)
{
if (field != null && field.DeclaringType.IsGenericInstance)
return field.Resolve();
else
return field as FieldDefinition;
}
static MethodDefinition GetMethodDefinition(MethodReference method)
{
if (method != null && method.DeclaringType.IsGenericInstance)
return method.Resolve();
else
return method as MethodDefinition;
}
bool SkipDummyBr(ILBlock method, ref int i)
{
ILLabel target;
@ -143,7 +160,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -143,7 +160,7 @@ namespace ICSharpCode.Decompiler.ILAst
bool MatchEnumeratorCreationNewObj(ILExpression expr, out MethodDefinition ctor)
{
// newobj(CurrentType/...::.ctor, ldc.i4(-2))
ctor = expr.Operand as MethodDefinition;
ctor = null;
if (expr.Code != ILCode.Newobj || expr.Arguments.Count != 1)
return false;
if (expr.Arguments[0].Code != ILCode.Ldc_I4)
@ -151,6 +168,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -151,6 +168,7 @@ namespace ICSharpCode.Decompiler.ILAst
int initialState = (int)expr.Arguments[0].Operand;
if (!(initialState == -2 || initialState == 0))
return false;
ctor = GetMethodDefinition(expr.Operand as MethodReference);
if (ctor == null || ctor.DeclaringType.DeclaringType != context.CurrentType)
return false;
return IsCompilerGeneratorEnumerator(ctor.DeclaringType);
@ -180,7 +198,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -180,7 +198,7 @@ namespace ICSharpCode.Decompiler.ILAst
foreach (ILNode node in method.Body) {
if (stfldPattern.Match(node)) {
stateField = ((ILExpression)node).Operand as FieldDefinition;
stateField = GetFieldDefinition(((ILExpression)node).Operand as FieldReference);
}
}
if (stateField == null)
@ -219,7 +237,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -219,7 +237,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (method.Body.Count == 1) {
// release builds directly return the current field
if (returnFieldFromThisPattern.Match(method.Body[0])) {
currentField = ((ILExpression)method.Body[0]).Arguments[0].Operand as FieldDefinition;
currentField = GetFieldDefinition(((ILExpression)method.Body[0]).Arguments[0].Operand as FieldReference);
}
} else {
StoreToVariable v = new StoreToVariable(new ILExpression(ILCode.Ldfld, ILExpression.AnyOperand, LoadFromArgument.This));
@ -227,7 +245,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -227,7 +245,7 @@ namespace ICSharpCode.Decompiler.ILAst
int i = 1;
if (SkipDummyBr(method, ref i) && i == method.Body.Count - 1) {
if (new ILExpression(ILCode.Ret, null, new LoadFromVariable(v)).Match(method.Body[i])) {
currentField = ((ILExpression)method.Body[0]).Arguments[0].Operand as FieldDefinition;
currentField = GetFieldDefinition(((ILExpression)method.Body[0]).Arguments[0].Operand as FieldReference);
}
}
}
@ -254,8 +272,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -254,8 +272,8 @@ namespace ICSharpCode.Decompiler.ILAst
foreach (ILNode node in method.Body) {
if (mappingPattern.Match(node)) {
ILExpression stfld = (ILExpression)node;
FieldDefinition storedField = stfld.Operand as FieldDefinition;
FieldDefinition loadedField = stfld.Arguments[1].Operand as FieldDefinition;
FieldDefinition storedField = GetFieldDefinition(stfld.Operand as FieldReference);
FieldDefinition loadedField = GetFieldDefinition(stfld.Arguments[1].Operand as FieldReference);
if (storedField != null && loadedField != null) {
ParameterDefinition mappedParameter;
if (fieldToParameterMap.TryGetValue(loadedField, out mappedParameter))
@ -304,7 +322,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -304,7 +322,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (!finallyBody[finallyBody.Count - 1].Match(ILCode.Endfinally))
throw new YieldAnalysisFailedException();
MethodDefinition mdef = call.Operand as MethodDefinition;
MethodDefinition mdef = GetMethodDefinition(call.Operand as MethodReference);
if (mdef == null || finallyMethodToStateInterval.ContainsKey(mdef))
throw new YieldAnalysisFailedException();
finallyMethodToStateInterval.Add(mdef, interval);
@ -506,7 +524,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -506,7 +524,7 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Call:
// in some cases (e.g. foreach over array) the C# compiler produces a finally method outside of try-finally blocks
if (forDispose) {
MethodDefinition mdef = expr.Operand as MethodDefinition;
MethodDefinition mdef = GetMethodDefinition(expr.Operand as MethodReference);
if (mdef == null || finallyMethodToStateInterval.ContainsKey(mdef))
throw new YieldAnalysisFailedException();
finallyMethodToStateInterval.Add(mdef, nodeRange.ToEnclosingInterval());
@ -580,7 +598,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -580,7 +598,7 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Ldfld:
if (Eval(expr.Arguments[0]).Type != SymbolicValueType.This)
throw new YieldAnalysisFailedException();
if (expr.Operand != stateField)
if (GetFieldDefinition(expr.Operand as FieldReference) != stateField)
throw new YieldAnalysisFailedException();
return new SymbolicValue(SymbolicValueType.State);
case ILCode.Ldloc:
@ -666,7 +684,11 @@ namespace ICSharpCode.Decompiler.ILAst @@ -666,7 +684,11 @@ namespace ICSharpCode.Decompiler.ILAst
// Ensure the fault block contains the call to Dispose().
if (!(faultBlock.Body.Count == 2 || faultBlock.Body.Count == 3))
throw new YieldAnalysisFailedException();
if (!new ILExpression(ILCode.Call, disposeMethod, LoadFromArgument.This).Match(faultBlock.Body[0]))
MethodReference disposeMethodRef;
ILExpression disposeArg;
if (!faultBlock.Body[0].Match(ILCode.Call, out disposeMethodRef, out disposeArg))
throw new YieldAnalysisFailedException();
if (GetMethodDefinition(disposeMethodRef) != disposeMethod || !LoadFromArgument.This.Match(disposeArg))
throw new YieldAnalysisFailedException();
if (faultBlock.Body.Count == 3 && !faultBlock.Body[1].Match(ILCode.Nop))
throw new YieldAnalysisFailedException();
@ -756,12 +778,12 @@ namespace ICSharpCode.Decompiler.ILAst @@ -756,12 +778,12 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression expr = body[pos] as ILExpression;
if (expr != null && expr.Code == ILCode.Stfld && LoadFromArgument.This.Match(expr.Arguments[0])) {
// Handle stores to 'state' or 'current'
if (expr.Operand == stateField) {
if (GetFieldDefinition(expr.Operand as FieldReference) == stateField) {
if (expr.Arguments[1].Code != ILCode.Ldc_I4)
throw new YieldAnalysisFailedException();
currentState = (int)expr.Arguments[1].Operand;
stateChanges.Add(new SetState(newBody.Count, currentState));
} else if (expr.Operand == currentField) {
} else if (GetFieldDefinition(expr.Operand as FieldReference) == currentField) {
newBody.Add(new ILExpression(ILCode.YieldReturn, null, expr.Arguments[1]));
} else {
newBody.Add(body[pos]);
@ -792,7 +814,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -792,7 +814,7 @@ namespace ICSharpCode.Decompiler.ILAst
throw new YieldAnalysisFailedException();
}
} else if (expr != null && expr.Code == ILCode.Call && expr.Arguments.Count == 1 && LoadFromArgument.This.Match(expr.Arguments[0])) {
MethodDefinition method = expr.Operand as MethodDefinition;
MethodDefinition method = GetMethodDefinition(expr.Operand as MethodReference);
if (method == null)
throw new YieldAnalysisFailedException();
Interval interval;
@ -855,7 +877,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -855,7 +877,7 @@ namespace ICSharpCode.Decompiler.ILAst
FieldReference stfld;
List<ILExpression> args;
if (block.Body.Count > 0 && block.Body[0].Match(ILCode.Stfld, out stfld, out args)) {
if (stfld == stateField && LoadFromArgument.This.Match(args[0]))
if (GetFieldDefinition(stfld) == stateField && LoadFromArgument.This.Match(args[0]))
block.Body.RemoveAt(0);
}
// Convert ret to endfinally
@ -873,7 +895,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -873,7 +895,7 @@ namespace ICSharpCode.Decompiler.ILAst
var fieldToLocalMap = new DefaultDictionary<FieldDefinition, ILVariable>(f => new ILVariable { Name = f.Name, Type = f.FieldType });
foreach (ILNode node in newBody) {
foreach (ILExpression expr in node.GetSelfAndChildrenRecursive<ILExpression>()) {
FieldDefinition field = expr.Operand as FieldDefinition;
FieldDefinition field = GetFieldDefinition(expr.Operand as FieldReference);
if (field != null) {
switch (expr.Code) {
case ILCode.Ldfld:

Loading…
Cancel
Save