Skip to content

Commit

Permalink
Fixed enum boxing decompilation bug. Enum in attributes better printing.
Browse files Browse the repository at this point in the history
  • Loading branch information
arturek committed Feb 23, 2011
1 parent 7e5e945 commit 679d525
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 29 deletions.
71 changes: 70 additions & 1 deletion ICSharpCode.Decompiler/Ast/AstBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
using Decompiler.Transforms;
using ICSharpCode.Decompiler;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.Utils;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Ast = ICSharpCode.NRefactory.CSharp;
using ClassType = ICSharpCode.NRefactory.TypeSystem.ClassType;
using VarianceModifier = ICSharpCode.NRefactory.TypeSystem.VarianceModifier;

Expand Down Expand Up @@ -583,13 +585,80 @@ static void ConvertCustomAtributes(AttributedNode attributedNode, ICustomAttribu

foreach (var parameter in customAttribute.ConstructorArguments)
{
attribute.Arguments.Add(new PrimitiveExpression(parameter.Value));
var isEnum = parameter.Type.IsValueType && !parameter.Type.IsPrimitive;
Expression parameterValue;
if (isEnum)
{
parameterValue = MakePrimitive(Convert.ToInt64(parameter.Value), parameter.Type);
}
else
{
parameterValue = new PrimitiveExpression(parameter.Value);
}
attribute.Arguments.Add(parameterValue);
}

}

attributedNode.Attributes.Add(section);
}
}


internal static Expression MakePrimitive(long val, TypeReference type)
{
if (TypeAnalysis.IsBoolean(type) && val == 0)
return new Ast.PrimitiveExpression(false);
else if (TypeAnalysis.IsBoolean(type) && val == 1)
return new Ast.PrimitiveExpression(true);
if (type != null)
{ // cannot rely on type.IsValueType, it's not set for typerefs (but is set for typespecs)
TypeDefinition enumDefinition = type.Resolve();
if (enumDefinition != null && enumDefinition.IsEnum)
{
foreach (FieldDefinition field in enumDefinition.Fields)
{
if (field.IsStatic && object.Equals(CSharpPrimitiveCast.Cast(TypeCode.Int64, field.Constant, false), val))
return AstBuilder.ConvertType(enumDefinition).Member(field.Name).WithAnnotation(field);
else if (!field.IsStatic && field.IsRuntimeSpecialName)
type = field.FieldType; // use primitive type of the enum
}
if (IsFlagsEnum(enumDefinition))
{
Expression expr = null;
foreach (FieldDefinition field in enumDefinition.Fields.Where(fld => fld.IsStatic))
{
long fieldValue = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, field.Constant, false);
if (fieldValue == 0)
continue; // skip None enum value

if ((fieldValue & val) == fieldValue)
{
var fieldExpression = AstBuilder.ConvertType(enumDefinition).Member(field.Name).WithAnnotation(field);
if (expr == null)
expr = fieldExpression;
else
expr = new BinaryOperatorExpression(expr, BinaryOperatorType.BitwiseOr, fieldExpression);
}
}
if (expr != null)
return expr;
}
}
}
TypeCode code = TypeAnalysis.GetTypeCode(type);
if (code == TypeCode.Object)
return new Ast.PrimitiveExpression((int)val);
else
return new Ast.PrimitiveExpression(CSharpPrimitiveCast.Cast(code, val, false));
}

static bool IsFlagsEnum(TypeDefinition type)
{
if (!type.HasCustomAttributes)
return false;

return type.CustomAttributes.Any(attr => attr.AttributeType.FullName == "System.FlagsAttribute");
}
}
}
38 changes: 10 additions & 28 deletions ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ AstNode TransformByteCode(ILExpression byteCode, List<Ast.Expression> args)
return MakeRef(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand));
}
case Code.Ldc_I4:
return MakePrimitive((int)operand, byteCode.InferredType);
return AstBuilder.MakePrimitive((int)operand, byteCode.InferredType);
case Code.Ldc_I8:
case Code.Ldc_R4:
case Code.Ldc_R8:
Expand Down Expand Up @@ -763,47 +763,29 @@ Ast.Expression Convert(Ast.Expression expr, Cecil.TypeReference actualType, Ceci
if (TypeAnalysis.IsBoolean(actualType))
return expr;
if (actualIsIntegerOrEnum) {
return new BinaryOperatorExpression(expr, BinaryOperatorType.InEquality, MakePrimitive(0, actualType));
return new BinaryOperatorExpression(expr, BinaryOperatorType.InEquality, AstBuilder.MakePrimitive(0, actualType));
} else {
return new BinaryOperatorExpression(expr, BinaryOperatorType.InEquality, new NullReferenceExpression());
}
}
if (TypeAnalysis.IsBoolean(actualType) && requiredIsIntegerOrEnum) {
return new ConditionalExpression {
Condition = expr,
TrueExpression = MakePrimitive(1, reqType),
FalseExpression = MakePrimitive(0, reqType)
TrueExpression = AstBuilder.MakePrimitive(1, reqType),
FalseExpression = AstBuilder.MakePrimitive(0, reqType)
};
}

if (expr is PrimitiveExpression && !requiredIsIntegerOrEnum && TypeAnalysis.IsEnum(actualType))
{
return expr.CastTo(AstBuilder.ConvertType(actualType));
}

if (actualIsIntegerOrEnum && requiredIsIntegerOrEnum) {
return expr.CastTo(AstBuilder.ConvertType(reqType));
}
return expr;
}
}

Expression MakePrimitive(long val, TypeReference type)
{
if (TypeAnalysis.IsBoolean(type) && val == 0)
return new Ast.PrimitiveExpression(false);
else if (TypeAnalysis.IsBoolean(type) && val == 1)
return new Ast.PrimitiveExpression(true);
if (type != null) { // cannot rely on type.IsValueType, it's not set for typerefs (but is set for typespecs)
TypeDefinition enumDefinition = type.Resolve();
if (enumDefinition != null && enumDefinition.IsEnum) {
foreach (FieldDefinition field in enumDefinition.Fields) {
if (field.IsStatic && object.Equals(CSharpPrimitiveCast.Cast(TypeCode.Int64, field.Constant, false), val))
return AstBuilder.ConvertType(enumDefinition).Member(field.Name).WithAnnotation(field);
else if (!field.IsStatic && field.IsRuntimeSpecialName)
type = field.FieldType; // use primitive type of the enum
}
}
}
TypeCode code = TypeAnalysis.GetTypeCode(type);
if (code == TypeCode.Object)
return new Ast.PrimitiveExpression((int)val);
else
return new Ast.PrimitiveExpression(CSharpPrimitiveCast.Cast(code, val, false));
}
}
}
9 changes: 9 additions & 0 deletions ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,15 @@ public static bool IsIntegerOrEnum(TypeReference type)
{
return IsSigned(type) != null;
}

public static bool IsEnum(TypeReference type)
{
if (type == null)
return false;
// unfortunately we cannot rely on type.IsValueType here - it's not set when the instruction operand is a typeref (as opposed to a typespec)
TypeDefinition typeDef = type.Resolve() as TypeDefinition;
return typeDef != null && typeDef.IsEnum;
}

static bool? IsSigned(TypeReference type)
{
Expand Down
8 changes: 8 additions & 0 deletions ICSharpCode.Decompiler/Tests/CustomAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,16 @@ public enum EnumWithFlag
// Item1,
Item2
}
[AttributeUsage(AttributeTargets.All)]
public class MyAttribute : Attribute
{
}
[Obsolete("some message")]
public static void ObsoletedMethod()
{
//Console.WriteLine("{0} $$$ {1}", AttributeTargets.Interface, (AttributeTargets)(AttributeTargets.Property | AttributeTargets.Field));
Console.WriteLine("{0} $$$ {1}", AttributeTargets.Interface, AttributeTargets.Property | AttributeTargets.Field);
AttributeTargets attributeTargets = AttributeTargets.Property | AttributeTargets.Field;
Console.WriteLine("{0} $$$ {1}", AttributeTargets.Interface, attributeTargets);
}
}

0 comments on commit 679d525

Please sign in to comment.