Browse Source

Implement type analysis for a lot more opcodes.

pull/37/head
Daniel Grunwald 15 years ago
parent
commit
8e69601184
  1. 3
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 4
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  3. 196
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
  4. 5
      ILSpy/ILAstLanguage.cs

3
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -74,7 +74,7 @@ namespace Decompiler @@ -74,7 +74,7 @@ namespace Decompiler
context.CancellationToken.ThrowIfCancellationRequested();
ILAstOptimizer bodyGraph = new ILAstOptimizer();
bodyGraph.Optimize(methodDef, ilMethod);
bodyGraph.Optimize(context, ilMethod);
context.CancellationToken.ThrowIfCancellationRequested();
List<string> intNames = new List<string>(new string[] {"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t"});
@ -410,7 +410,6 @@ namespace Decompiler @@ -410,7 +410,6 @@ namespace Decompiler
}
case Code.Ldlen:
return arg1.Member("Length");
case Code.Ldelem_I:
case Code.Ldelem_I1:
case Code.Ldelem_I2:

4
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -24,7 +24,7 @@ namespace Decompiler.ControlFlow @@ -24,7 +24,7 @@ namespace Decompiler.ControlFlow
{
Dictionary<ILLabel, ControlFlowNode> labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>();
public void Optimize(MethodDefinition cecilMethod, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None)
public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None)
{
if (abortBeforeStep == ILAstOptimizationStep.SplitToMovableBlocks) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) {
@ -57,7 +57,7 @@ namespace Decompiler.ControlFlow @@ -57,7 +57,7 @@ namespace Decompiler.ControlFlow
if (abortBeforeStep == ILAstOptimizationStep.RemoveDeadLabels) return;
RemoveDeadLabels(method);
if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return;
TypeAnalysis.Run(cecilMethod.Module.TypeSystem, method);
TypeAnalysis.Run(context, method);
}
class ILMoveableBlock: ILBlock

196
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -20,14 +20,18 @@ namespace Decompiler @@ -20,14 +20,18 @@ namespace Decompiler
/// </remarks>
public class TypeAnalysis
{
public static void Run(TypeSystem typeSystem, ILNode node)
public static void Run(DecompilerContext context, ILNode node)
{
TypeAnalysis ta = new TypeAnalysis();
ta.typeSystem = typeSystem;
ta.context = context;
ta.module = context.CurrentMethod.Module;
ta.typeSystem = ta.module.TypeSystem;
ta.InferTypes(node);
}
DecompilerContext context;
TypeSystem typeSystem;
ModuleDefinition module;
List<ILExpression> storedToGeneratedVariables = new List<ILExpression>();
void InferTypes(ILNode node)
@ -67,14 +71,25 @@ namespace Decompiler @@ -67,14 +71,25 @@ namespace Decompiler
TypeReference DoInferTypeForExpression(ILExpression expr, TypeReference expectedType, bool forceInferChildren = false)
{
switch (expr.OpCode.Code) {
#region Variable load/store
case Code.Stloc:
if (forceInferChildren)
InferTypeForExpression(expr.Arguments.Single(), ((ILVariable)expr.Operand).Type);
return null;
case Code.Ldloc:
return ((ILVariable)expr.Operand).Type;
case Code.Starg:
if (forceInferChildren)
InferTypeForExpression(expr.Arguments.Single(), ((ParameterReference)expr.Operand).ParameterType);
return null;
case Code.Ldarg:
return ((ParameterDefinition)expr.Operand).ParameterType;
return ((ParameterReference)expr.Operand).ParameterType;
case Code.Ldloca:
return new ByReferenceType(((VariableDefinition)expr.Operand).VariableType);
case Code.Ldarga:
return new ByReferenceType(((ParameterReference)expr.Operand).ParameterType);
#endregion
#region Call / NewObj
case Code.Call:
case Code.Callvirt:
{
@ -99,42 +114,201 @@ namespace Decompiler @@ -99,42 +114,201 @@ namespace Decompiler
}
return ctor.DeclaringType;
}
#endregion
#region Load/Store Fields
case Code.Ldfld:
return UnpackModifiers(((FieldReference)expr.Operand).FieldType);
case Code.Ldsfld:
return UnpackModifiers(((FieldReference)expr.Operand).FieldType);
case Code.Ldflda:
return new ByReferenceType(UnpackModifiers(((FieldReference)expr.Operand).FieldType));
case Code.Stfld:
if (forceInferChildren)
InferTypeForExpression(expr.Arguments[1], ((FieldReference)expr.Operand).FieldType);
return null;
case Code.Stsfld:
if (forceInferChildren)
InferTypeForExpression(expr.Arguments[0], ((FieldReference)expr.Operand).FieldType);
return null;
#endregion
#region Arithmetic instructions
case Code.Add:
case Code.Sub:
case Code.Mul:
case Code.Or:
return InferArgumentsInBinaryOperator(expr);
case Code.And:
return InferArgumentsInBinaryOperator(expr, null);
case Code.Add_Ovf:
case Code.Sub_Ovf:
case Code.Mul_Ovf:
case Code.Div:
return InferArgumentsInBinaryOperator(expr, true);
case Code.Add_Ovf_Un:
case Code.Sub_Ovf_Un:
case Code.Mul_Ovf_Un:
case Code.Div_Un:
return InferArgumentsInBinaryOperator(expr, false);
case Code.Shl:
case Code.Shr:
if (forceInferChildren)
InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
return InferTypeForExpression(expr.Arguments[0], expectedType);
#endregion
#region Constant loading instructions
case Code.Ldnull:
return typeSystem.Object;
case Code.Ldstr:
return typeSystem.String;
case Code.Ldftn:
return typeSystem.IntPtr;
case Code.Ldc_I4:
return (IsIntegerOrEnum(expectedType) || expectedType == typeSystem.Boolean) ? expectedType : typeSystem.Int32;
case Code.Ldc_I8:
return (IsIntegerOrEnum(expectedType)) ? expectedType : typeSystem.Int64;
case Code.Ldc_R8:
return typeSystem.Double;
case Code.Ldtoken:
if (expr.Operand is TypeReference)
return new TypeReference("System", "RuntimeTypeHandle", module, module, true);
else if (expr.Operand is FieldReference)
return new TypeReference("System", "RuntimeFieldHandle", module, module, true);
else
return new TypeReference("System", "RuntimeMethodHandle", module, module, true);
#endregion
#region Array instructions
case Code.Newarr:
if (forceInferChildren)
InferTypeForExpression(expr.Arguments.Single(), typeSystem.Int32);
return new ArrayType((TypeReference)expr.Operand);
case Code.Ldlen:
return typeSystem.Int32;
case Code.Ldelem_U1:
case Code.Ldelem_U2:
case Code.Ldelem_U4:
case Code.Ldelem_I1:
case Code.Ldelem_I2:
case Code.Ldelem_I4:
case Code.Ldelem_I8:
case Code.Ldelem_I:
case Code.Ldelem_Ref:
{
ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType;
if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], new ArrayType(typeSystem.Byte));
InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
}
return arrayType != null ? arrayType.ElementType : null;
}
case Code.Ldelema:
{
ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType;
if (forceInferChildren)
InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
return arrayType != null ? new ByReferenceType(arrayType.ElementType) : null;
}
case Code.Stelem_I:
case Code.Stelem_I1:
case Code.Stelem_I2:
case Code.Stelem_I4:
case Code.Stelem_I8:
case Code.Stelem_R4:
case Code.Stelem_R8:
case Code.Stelem_Ref:
case Code.Stelem_Any:
if (forceInferChildren) {
ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType;
InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
if (arrayType != null) {
InferTypeForExpression(expr.Arguments[2], arrayType.ElementType);
}
}
return null;
#endregion
#region Conversion instructions
case Code.Conv_I1:
return (GetInformationAmount(expectedType) == 8 && IsSigned(expectedType) == true) ? expectedType : typeSystem.SByte;
case Code.Conv_I2:
return (GetInformationAmount(expectedType) == 16 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int16;
case Code.Conv_I4:
return (GetInformationAmount(expectedType) == 32 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int32;
case Code.Conv_I8:
return (GetInformationAmount(expectedType) == 64 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int64;
case Code.Dup:
return InferTypeForExpression(expr.Arguments.Single(), expectedType);
case Code.Conv_U1:
return (GetInformationAmount(expectedType) == 8 && IsSigned(expectedType) == false) ? expectedType : typeSystem.Byte;
case Code.Conv_U2:
return (GetInformationAmount(expectedType) == 16 && IsSigned(expectedType) == false) ? expectedType : typeSystem.UInt16;
case Code.Conv_U4:
return (GetInformationAmount(expectedType) == 32 && IsSigned(expectedType) == false) ? expectedType : typeSystem.UInt32;
case Code.Conv_U8:
return (GetInformationAmount(expectedType) == 64 && IsSigned(expectedType) == false) ? expectedType : typeSystem.UInt64;
case Code.Castclass:
case Code.Isinst:
case Code.Unbox_Any:
return (TypeReference)expr.Operand;
case Code.Box:
if (forceInferChildren)
InferTypeForExpression(expr.Arguments.Single(), (TypeReference)expr.Operand);
return (TypeReference)expr.Operand;
#endregion
#region Comparison instructions
case Code.Ceq:
if (forceInferChildren)
InferArgumentsInBinaryOperator(expr, null);
return typeSystem.Boolean;
case Code.Clt:
case Code.Cgt:
if (forceInferChildren)
InferArgumentsInBinaryOperator(expr);
InferArgumentsInBinaryOperator(expr, true);
return typeSystem.Boolean;
case Code.Clt_Un:
case Code.Cgt_Un:
if (forceInferChildren)
InferArgumentsInBinaryOperator(expr, false);
return typeSystem.Boolean;
#endregion
#region Branch instructions
case Code.Beq:
case Code.Blt:
case Code.Bne_Un:
if (forceInferChildren)
InferArgumentsInBinaryOperator(expr);
InferArgumentsInBinaryOperator(expr, null);
return null;
case Code.Brtrue:
case Code.Brfalse:
if (forceInferChildren)
InferTypeForExpression(expr.Arguments.Single(), typeSystem.Boolean);
return null;
case Code.Blt:
case Code.Ble:
case Code.Bgt:
case Code.Bge:
if (forceInferChildren)
InferArgumentsInBinaryOperator(expr, true);
return null;
case Code.Blt_Un:
case Code.Ble_Un:
case Code.Bgt_Un:
case Code.Bge_Un:
if (forceInferChildren)
InferArgumentsInBinaryOperator(expr, false);
return null;
case Code.Br:
case Code.Leave:
case Code.Endfinally:
case Code.Switch:
case Code.Throw:
case Code.Rethrow:
return null;
case Code.Ret:
if (forceInferChildren && expr.Arguments.Count == 1)
InferTypeForExpression(expr.Arguments[0], context.CurrentMethod.ReturnType);
return null;
#endregion
case Code.Pop:
return null;
case Code.Dup:
return InferTypeForExpression(expr.Arguments.Single(), expectedType);
default:
//throw new NotImplementedException("Can't handle " + expr.OpCode.Name);
Debug.WriteLine("Type Inference: Can't handle " + expr.OpCode.Name);
return null;
}
}
@ -146,7 +320,7 @@ namespace Decompiler @@ -146,7 +320,7 @@ namespace Decompiler
return type;
}
TypeReference InferArgumentsInBinaryOperator(ILExpression expr)
TypeReference InferArgumentsInBinaryOperator(ILExpression expr, bool? isSigned)
{
ILExpression left = expr.Arguments[0];
ILExpression right = expr.Arguments[1];

5
ILSpy/ILAstLanguage.cs

@ -52,13 +52,14 @@ namespace ICSharpCode.ILSpy @@ -52,13 +52,14 @@ namespace ICSharpCode.ILSpy
ilMethod.Body = astBuilder.Build(method, inlineVariables);
if (abortBeforeStep != null) {
new ILAstOptimizer().Optimize(method, ilMethod, abortBeforeStep.Value);
DecompilerContext context = new DecompilerContext { CurrentType = method.DeclaringType, CurrentMethod = method };
new ILAstOptimizer().Optimize(context, ilMethod, abortBeforeStep.Value);
}
var allVariables = astBuilder.Variables
.Concat(ilMethod.GetSelfAndChildrenRecursive<ILExpression>().Select(e => e.Operand as ILVariable).Where(v => v != null)).Distinct();
foreach (ILVariable v in allVariables) {
output.Write(v.Name);
output.WriteDefinition(v.Name, v);
if (v.Type != null) {
output.Write(" : ");
v.Type.WriteTo(output, true, true);

Loading…
Cancel
Save