Browse Source

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

pull/191/merge
Eusebiu Marcu 15 years ago
parent
commit
9b26b98339
  1. 3
      ICSharpCode.Decompiler/Ast/AstBuilder.cs
  2. 4
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  3. 279
      ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs
  4. 2
      ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs
  5. 78
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  6. 9
      ICSharpCode.Decompiler/ILAst/ILCodes.cs
  7. 136
      ICSharpCode.Decompiler/ILAst/ILInlining.cs
  8. 33
      ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs
  9. 4
      ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
  10. 10
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
  11. 25
      ICSharpCode.Decompiler/Tests/Generics.cs
  12. 18
      ICSharpCode.Decompiler/Tests/ValueTypes.cs
  13. 250
      ILSpy/BamlDecompiler.cs
  14. 2
      ILSpy/TreeNodes/Analyzer/AnalyzedPropertyOverridesTreeNode.cs
  15. 2
      ILSpy/TreeNodes/Analyzer/AnalyzerMethodOverridesTreeNode.cs

3
ICSharpCode.Decompiler/Ast/AstBuilder.cs

@ -633,7 +633,8 @@ namespace ICSharpCode.Decompiler.Ast @@ -633,7 +633,8 @@ namespace ICSharpCode.Decompiler.Ast
astMethod.Name = CleanName(methodDef.Name);
astMethod.TypeParameters.AddRange(MakeTypeParameters(methodDef.GenericParameters));
astMethod.Parameters.AddRange(MakeParameters(methodDef));
astMethod.Constraints.AddRange(MakeConstraints(methodDef.GenericParameters));
// constraints for override and explicit interface implementation methods are inherited from the base method, so they cannot be specified directly
if (!methodDef.IsVirtual || (methodDef.IsNewSlot && !methodDef.IsPrivate)) astMethod.Constraints.AddRange(MakeConstraints(methodDef.GenericParameters));
if (!methodDef.DeclaringType.IsInterface) {
if (!methodDef.HasOverrides) {
astMethod.Modifiers = ConvertModifiers(methodDef);

4
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -743,6 +743,8 @@ namespace ICSharpCode.Decompiler.Ast @@ -743,6 +743,8 @@ namespace ICSharpCode.Decompiler.Ast
}
case ILCode.InitializedObject:
return new InitializedObjectExpression();
case ILCode.AddressOf:
return MakeRef(arg1);
default:
throw new Exception("Unknown OpCode: " + byteCode.Code);
}
@ -884,8 +886,6 @@ namespace ICSharpCode.Decompiler.Ast @@ -884,8 +886,6 @@ namespace ICSharpCode.Decompiler.Ast
if (cecilMethod.Name == "Get" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 1) {
return target.Indexer(methodArgs);
} else if (cecilMethod.Name == "Address" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 1) {
return MakeRef(target.Indexer(methodArgs));
} else if (cecilMethod.Name == "Set" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 2) {
return new AssignmentExpression(target.Indexer(methodArgs.GetRange(0, methodArgs.Count - 1)), methodArgs.Last());
}

279
ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs

@ -12,14 +12,13 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -12,14 +12,13 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
/// <summary>
/// Introduces using declarations.
/// </summary>
public class IntroduceUsingDeclarations : DepthFirstAstVisitor<object, object>, IAstTransform
public class IntroduceUsingDeclarations : IAstTransform
{
DecompilerContext context;
public IntroduceUsingDeclarations(DecompilerContext context)
{
this.context = context;
currentNamespace = context.CurrentType != null ? context.CurrentType.Namespace : string.Empty;
}
public void Run(AstNode compilationUnit)
@ -28,7 +27,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -28,7 +27,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
return;
// First determine all the namespaces that need to be imported:
compilationUnit.AcceptVisitor(this, null);
compilationUnit.AcceptVisitor(new FindRequiredImports(this), null);
importedNamespaces.Add("System"); // always import System, even when not necessary
@ -55,48 +54,60 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -55,48 +54,60 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}
// verify that the SimpleTypes refer to the correct type (no ambiguities)
FullyQualifyAmbiguousTypeNames(compilationUnit);
compilationUnit.AcceptVisitor(new FullyQualifyAmbiguousTypeNamesVisitor(this), null);
}
readonly HashSet<string> declaredNamespaces = new HashSet<string>() { string.Empty };
readonly HashSet<string> importedNamespaces = new HashSet<string>();
// Note that we store type names with `n suffix, so we automatically disambiguate based on number of type parameters.
readonly HashSet<string> availableTypeNames = new HashSet<string>();
readonly HashSet<string> ambiguousTypeNames = new HashSet<string>();
string currentNamespace;
bool IsParentOfCurrentNamespace(string ns)
sealed class FindRequiredImports : DepthFirstAstVisitor<object, object>
{
if (ns.Length == 0)
return true;
if (currentNamespace.StartsWith(ns, StringComparison.Ordinal)) {
if (currentNamespace.Length == ns.Length)
return true;
if (currentNamespace[ns.Length] == '.')
readonly IntroduceUsingDeclarations transform;
string currentNamespace;
public FindRequiredImports(IntroduceUsingDeclarations transform)
{
this.transform = transform;
this.currentNamespace = transform.context.CurrentType != null ? transform.context.CurrentType.Namespace : string.Empty;
}
bool IsParentOfCurrentNamespace(string ns)
{
if (ns.Length == 0)
return true;
if (currentNamespace.StartsWith(ns, StringComparison.Ordinal)) {
if (currentNamespace.Length == ns.Length)
return true;
if (currentNamespace[ns.Length] == '.')
return true;
}
return false;
}
return false;
}
public override object VisitSimpleType(SimpleType simpleType, object data)
{
TypeReference tr = simpleType.Annotation<TypeReference>();
if (tr != null && !IsParentOfCurrentNamespace(tr.Namespace)) {
importedNamespaces.Add(tr.Namespace);
public override object VisitSimpleType(SimpleType simpleType, object data)
{
TypeReference tr = simpleType.Annotation<TypeReference>();
if (tr != null && !IsParentOfCurrentNamespace(tr.Namespace)) {
transform.importedNamespaces.Add(tr.Namespace);
}
return base.VisitSimpleType(simpleType, data); // also visit type arguments
}
public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data)
{
string oldNamespace = currentNamespace;
foreach (Identifier ident in namespaceDeclaration.Identifiers) {
currentNamespace = NamespaceDeclaration.BuildQualifiedName(currentNamespace, ident.Name);
transform.declaredNamespaces.Add(currentNamespace);
}
base.VisitNamespaceDeclaration(namespaceDeclaration, data);
currentNamespace = oldNamespace;
return null;
}
return base.VisitSimpleType(simpleType, data); // also visit type arguments
}
public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data)
{
string oldNamespace = currentNamespace;
foreach (Identifier ident in namespaceDeclaration.Identifiers) {
currentNamespace = NamespaceDeclaration.BuildQualifiedName(currentNamespace, ident.Name);
declaredNamespaces.Add(currentNamespace);
}
base.VisitNamespaceDeclaration(namespaceDeclaration, data);
currentNamespace = oldNamespace;
return null;
}
void FindAmbiguousTypeNames(ModuleDefinition module, bool internalsVisible)
@ -111,17 +122,176 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -111,17 +122,176 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}
}
void FullyQualifyAmbiguousTypeNames(AstNode compilationUnit)
sealed class FullyQualifyAmbiguousTypeNamesVisitor : DepthFirstAstVisitor<object, object>
{
foreach (SimpleType simpleType in compilationUnit.Descendants.OfType<SimpleType>()) {
readonly IntroduceUsingDeclarations transform;
string currentNamespace;
HashSet<string> currentMemberTypes;
Dictionary<string, MemberReference> currentMembers;
bool isWithinTypeReferenceExpression;
public FullyQualifyAmbiguousTypeNamesVisitor(IntroduceUsingDeclarations transform)
{
this.transform = transform;
this.currentNamespace = transform.context.CurrentType != null ? transform.context.CurrentType.Namespace : string.Empty;
}
public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data)
{
string oldNamespace = currentNamespace;
foreach (Identifier ident in namespaceDeclaration.Identifiers) {
currentNamespace = NamespaceDeclaration.BuildQualifiedName(currentNamespace, ident.Name);
}
base.VisitNamespaceDeclaration(namespaceDeclaration, data);
currentNamespace = oldNamespace;
return null;
}
public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
{
HashSet<string> oldMemberTypes = currentMemberTypes;
currentMemberTypes = currentMemberTypes != null ? new HashSet<string>(currentMemberTypes) : new HashSet<string>();
Dictionary<string, MemberReference> oldMembers = currentMembers;
currentMembers = new Dictionary<string, MemberReference>();
TypeDefinition typeDef = typeDeclaration.Annotation<TypeDefinition>();
bool privateMembersVisible = true;
ModuleDefinition internalMembersVisibleInModule = typeDef.Module;
while (typeDef != null) {
foreach (GenericParameter gp in typeDef.GenericParameters) {
currentMemberTypes.Add(gp.Name);
}
foreach (TypeDefinition t in typeDef.NestedTypes) {
if (privateMembersVisible || IsVisible(t, internalMembersVisibleInModule))
currentMemberTypes.Add(t.Name.Substring(t.Name.LastIndexOf('+') + 1));
}
foreach (MethodDefinition method in typeDef.Methods) {
if (privateMembersVisible || IsVisible(method, internalMembersVisibleInModule))
AddCurrentMember(method);
}
foreach (PropertyDefinition property in typeDef.Properties) {
if (privateMembersVisible || IsVisible(property.GetMethod, internalMembersVisibleInModule) || IsVisible(property.SetMethod, internalMembersVisibleInModule))
AddCurrentMember(property);
}
foreach (EventDefinition ev in typeDef.Events) {
if (privateMembersVisible || IsVisible(ev.AddMethod, internalMembersVisibleInModule) || IsVisible(ev.RemoveMethod, internalMembersVisibleInModule))
AddCurrentMember(ev);
}
foreach (FieldDefinition f in typeDef.Fields) {
if (privateMembersVisible || IsVisible(f, internalMembersVisibleInModule))
AddCurrentMember(f);
}
// repeat with base class:
if (typeDef.BaseType != null)
typeDef = typeDef.BaseType.Resolve();
else
typeDef = null;
privateMembersVisible = false;
}
// Now add current members from outer classes:
if (oldMembers != null) {
foreach (var pair in oldMembers) {
// add members from outer classes only if the inner class doesn't define the member
if (!currentMembers.ContainsKey(pair.Key))
currentMembers.Add(pair.Key, pair.Value);
}
}
base.VisitTypeDeclaration(typeDeclaration, data);
currentMembers = oldMembers;
return null;
}
void AddCurrentMember(MemberReference m)
{
MemberReference existingMember;
if (currentMembers.TryGetValue(m.Name, out existingMember)) {
// We keep the existing member assignment if it was from another class (=from a derived class),
// because members in derived classes have precedence over members in base classes.
if (existingMember != null && existingMember.DeclaringType == m.DeclaringType) {
// Use null as value to signalize multiple members with the same name
currentMembers[m.Name] = null;
}
} else {
currentMembers.Add(m.Name, m);
}
}
bool IsVisible(MethodDefinition m, ModuleDefinition internalMembersVisibleInModule)
{
if (m == null)
return false;
switch (m.Attributes & MethodAttributes.MemberAccessMask) {
case MethodAttributes.FamANDAssem:
case MethodAttributes.Assembly:
return m.Module == internalMembersVisibleInModule;
case MethodAttributes.Family:
case MethodAttributes.FamORAssem:
case MethodAttributes.Public:
return true;
default:
return false;
}
}
bool IsVisible(FieldDefinition f, ModuleDefinition internalMembersVisibleInModule)
{
if (f == null)
return false;
switch (f.Attributes & FieldAttributes.FieldAccessMask) {
case FieldAttributes.FamANDAssem:
case FieldAttributes.Assembly:
return f.Module == internalMembersVisibleInModule;
case FieldAttributes.Family:
case FieldAttributes.FamORAssem:
case FieldAttributes.Public:
return true;
default:
return false;
}
}
bool IsVisible(TypeDefinition t, ModuleDefinition internalMembersVisibleInModule)
{
if (t == null)
return false;
switch (t.Attributes & TypeAttributes.VisibilityMask) {
case TypeAttributes.NotPublic:
case TypeAttributes.NestedAssembly:
case TypeAttributes.NestedFamANDAssem:
return t.Module == internalMembersVisibleInModule;
case TypeAttributes.NestedFamily:
case TypeAttributes.NestedFamORAssem:
case TypeAttributes.NestedPublic:
case TypeAttributes.Public:
return true;
default:
return false;
}
}
public override object VisitSimpleType(SimpleType simpleType, object data)
{
// Handle type arguments first, so that the fixed-up type arguments get moved over to the MemberType,
// if we're also creating one here.
base.VisitSimpleType(simpleType, data);
TypeReference tr = simpleType.Annotation<TypeReference>();
if (tr != null && ambiguousTypeNames.Contains(tr.Name)) {
// Fully qualify any ambiguous type names.
if (tr != null && IsAmbiguous(tr.Namespace, tr.Name)) {
AstType ns;
if (string.IsNullOrEmpty(tr.Namespace)) {
ns = new SimpleType("global");
} else {
string[] parts = tr.Namespace.Split('.');
ns = new SimpleType(parts[0]);
if (IsAmbiguous(string.Empty, parts[0])) {
// conflict between namespace and type name/member name
ns = new MemberType { Target = new SimpleType("global"), IsDoubleColon = true, MemberName = parts[0] };
} else {
ns = new SimpleType(parts[0]);
}
for (int i = 1; i < parts.Length; i++) {
ns = new MemberType { Target = ns, MemberName = parts[i] };
}
@ -134,6 +304,41 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -134,6 +304,41 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
simpleType.TypeArguments.MoveTo(mt.TypeArguments);
simpleType.ReplaceWith(mt);
}
return null;
}
public override object VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, object data)
{
isWithinTypeReferenceExpression = true;
base.VisitTypeReferenceExpression(typeReferenceExpression, data);
isWithinTypeReferenceExpression = false;
return null;
}
bool IsAmbiguous(string ns, string name)
{
// If the type name conflicts with an inner class/type parameter, we need to fully-qualify it:
if (currentMemberTypes != null && currentMemberTypes.Contains(name))
return true;
// If the type name conflicts with a field/property etc. on the current class, we need to fully-qualify it,
// if we're inside an expression.
if (isWithinTypeReferenceExpression && currentMembers != null) {
MemberReference mr;
if (currentMembers.TryGetValue(name, out mr)) {
// However, in the special case where the member is a field or property with the same type
// as is requested, then we can use the short name (if it's not otherwise ambiguous)
PropertyDefinition prop = mr as PropertyDefinition;
FieldDefinition field = mr as FieldDefinition;
if (!(prop != null && prop.PropertyType.Namespace == ns && prop.PropertyType.Name == name)
&& !(field != null && field.FieldType.Namespace == ns && field.FieldType.Name == name))
return true;
}
}
// If the type is defined in the current namespace,
// then we can use the short name even if we imported type with same name from another namespace.
if (ns == currentNamespace && !string.IsNullOrEmpty(ns))
return false;
return transform.ambiguousTypeNames.Contains(name);
}
}
}

2
ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs

@ -256,7 +256,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -256,7 +256,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
static bool IsWithoutSideEffects(Expression left)
{
return left is ThisReferenceExpression || left is IdentifierExpression || left is TypeReferenceExpression;
return left is ThisReferenceExpression || left is IdentifierExpression || left is TypeReferenceExpression || left is BaseReferenceExpression;
}
void IAstTransform.Run(AstNode node)

78
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -291,32 +291,60 @@ namespace ICSharpCode.Decompiler.ILAst @@ -291,32 +291,60 @@ namespace ICSharpCode.Decompiler.ILAst
///
/// CallGetter/CallSetter is used to allow the ILAst to represent "while ((SomeProperty = value) != null)".
/// </summary>
void IntroducePropertyAccessInstructions(ILBlock method)
void IntroducePropertyAccessInstructions(ILNode node)
{
foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) {
if (expr.Code == ILCode.Call || expr.Code == ILCode.Callvirt) {
MethodReference cecilMethod = (MethodReference)expr.Operand;
if (cecilMethod.DeclaringType is ArrayType) {
switch (cecilMethod.Name) {
case "Get":
expr.Code = ILCode.CallGetter;
break;
case "Set":
expr.Code = ILCode.CallSetter;
break;
case "Address":
expr.Code = ILCode.CallGetter;
expr.AddPrefix(new ILExpressionPrefix(ILCode.PropertyAddress));
break;
}
} else {
MethodDefinition cecilMethodDef = cecilMethod.Resolve();
if (cecilMethodDef != null) {
if (cecilMethodDef.IsGetter)
expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallGetter : ILCode.CallvirtGetter;
else if (cecilMethodDef.IsSetter)
expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallSetter : ILCode.CallvirtSetter;
}
ILExpression parentExpr = node as ILExpression;
if (parentExpr != null) {
for (int i = 0; i < parentExpr.Arguments.Count; i++) {
ILExpression expr = parentExpr.Arguments[i];
IntroducePropertyAccessInstructions(expr);
IntroducePropertyAccessInstructions(expr, parentExpr, i);
}
} else {
foreach (ILNode child in node.GetChildren()) {
IntroducePropertyAccessInstructions(child);
ILExpression expr = child as ILExpression;
if (expr != null) {
IntroducePropertyAccessInstructions(expr, null, -1);
}
}
}
}
void IntroducePropertyAccessInstructions(ILExpression expr, ILExpression parentExpr, int posInParent)
{
if (expr.Code == ILCode.Call || expr.Code == ILCode.Callvirt) {
MethodReference cecilMethod = (MethodReference)expr.Operand;
if (cecilMethod.DeclaringType is ArrayType) {
switch (cecilMethod.Name) {
case "Get":
expr.Code = ILCode.CallGetter;
break;
case "Set":
expr.Code = ILCode.CallSetter;
break;
case "Address":
ByReferenceType brt = cecilMethod.ReturnType as ByReferenceType;
if (brt != null) {
MethodReference getMethod = new MethodReference("Get", brt.ElementType, cecilMethod.DeclaringType);
foreach (var p in cecilMethod.Parameters)
getMethod.Parameters.Add(p);
getMethod.HasThis = cecilMethod.HasThis;
expr.Operand = getMethod;
}
expr.Code = ILCode.CallGetter;
if (parentExpr != null) {
parentExpr.Arguments[posInParent] = new ILExpression(ILCode.AddressOf, null, expr);
}
break;
}
} else {
MethodDefinition cecilMethodDef = cecilMethod.Resolve();
if (cecilMethodDef != null) {
if (cecilMethodDef.IsGetter)
expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallGetter : ILCode.CallvirtGetter;
else if (cecilMethodDef.IsSetter)
expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallSetter : ILCode.CallvirtSetter;
}
}
}

9
ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -286,9 +286,12 @@ namespace ICSharpCode.Decompiler.ILAst @@ -286,9 +286,12 @@ namespace ICSharpCode.Decompiler.ILAst
CallSetter,
/// <summary>Calls the setter of a instance property (or indexer)</summary>
CallvirtSetter,
/// <summary>Simulates getting the address of a property. Used as prefix on CallGetter or CallvirtGetter.</summary>
/// <remarks>Used for postincrement for properties, and to represent the Address() method on multi-dimensional arrays</remarks>
PropertyAddress
/// <summary>Simulates getting the address of the argument instruction.</summary>
/// <remarks>
/// Used for postincrement for properties, and to represent the Address() method on multi-dimensional arrays.
/// Also used when inlining a method call on a value type: "stloc(v, ...); call(M, ldloca(v));" becomes "call(M, AddressOf(...))"
/// </remarks>
AddressOf
}
public static class ILCodeUtil

136
ICSharpCode.Decompiler/ILAst/ILInlining.cs

@ -73,7 +73,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -73,7 +73,7 @@ namespace ICSharpCode.Decompiler.ILAst
}
foreach(ILBasicBlock bb in body.OfType<ILBasicBlock>()) {
modified |= InlineAllInBasicBlock(bb);
}
}
return modified;
}
@ -169,7 +169,10 @@ namespace ICSharpCode.Decompiler.ILAst @@ -169,7 +169,10 @@ namespace ICSharpCode.Decompiler.ILAst
bool InlineIfPossible(ILVariable v, ILExpression inlinedExpression, ILNode next, bool aggressive)
{
// ensure the variable is accessed only a single time
if (!(numStloc.GetOrDefault(v) == 1 && numLdloc.GetOrDefault(v) == 1 && numLdloca.GetOrDefault(v) == 0))
if (numStloc.GetOrDefault(v) != 1)
return false;
int ldloc = numLdloc.GetOrDefault(v);
if (ldloc > 1 || ldloc + numLdloca.GetOrDefault(v) != 1)
return false;
if (next is ILCondition)
@ -180,36 +183,135 @@ namespace ICSharpCode.Decompiler.ILAst @@ -180,36 +183,135 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression parent;
int pos;
if (FindLoadInNext(next as ILExpression, v, inlinedExpression, out parent, out pos) == true) {
if (!aggressive && !v.IsGenerated && !NonAggressiveInlineInto((ILExpression)next, parent, inlinedExpression))
return false;
if (ldloc == 0) {
if (!IsGeneratedValueTypeTemporary((ILExpression)next, parent, pos, v, inlinedExpression))
return false;
} else {
if (!aggressive && !v.IsGenerated && !NonAggressiveInlineInto((ILExpression)next, parent, inlinedExpression))
return false;
}
// Assign the ranges of the ldloc instruction:
inlinedExpression.ILRanges.AddRange(parent.Arguments[pos].ILRanges);
parent.Arguments[pos] = inlinedExpression;
if (ldloc == 0) {
// it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof' so that the types
// comes out correctly
parent.Arguments[pos] = new ILExpression(ILCode.AddressOf, null, inlinedExpression);
} else {
parent.Arguments[pos] = inlinedExpression;
}
return true;
}
return false;
}
/// <summary>
/// Is this a temporary variable generated by the C# compiler for instance method calls on value type values
/// </summary>
/// <param name="next">The next top-level expression</param>
/// <param name="parent">The direct parent of the load within 'next'</param>
/// <param name="pos">Index of the load within 'parent'</param>
/// <param name="v">The variable being inlined.</param>
/// <param name="inlinedExpression">The expression being inlined</param>
bool IsGeneratedValueTypeTemporary(ILExpression next, ILExpression parent, int pos, ILVariable v, ILExpression inlinedExpression)
{
if (pos == 0 && v.Type != null && v.Type.IsValueType) {
// Inlining a value type variable is allowed only if the resulting code will maintain the semantics
// that the method is operating on a copy.
// Thus, we have to disallow inlining of other locals, fields, array elements, dereferenced pointers
switch (inlinedExpression.Code) {
case ILCode.Ldloc:
case ILCode.Stloc:
case ILCode.CompoundAssignment:
case ILCode.Ldelem_Any:
case ILCode.Ldelem_I:
case ILCode.Ldelem_I1:
case ILCode.Ldelem_I2:
case ILCode.Ldelem_I4:
case ILCode.Ldelem_I8:
case ILCode.Ldelem_R4:
case ILCode.Ldelem_R8:
case ILCode.Ldelem_Ref:
case ILCode.Ldelem_U1:
case ILCode.Ldelem_U2:
case ILCode.Ldelem_U4:
case ILCode.Ldobj:
case ILCode.Ldind_Ref:
return false;
case ILCode.Ldfld:
case ILCode.Stfld:
case ILCode.Ldsfld:
case ILCode.Stsfld:
// allow inlining field access only if it's a readonly field
FieldDefinition f = ((FieldReference)inlinedExpression.Operand).Resolve();
if (!(f != null && f.IsInitOnly))
return false;
break;
case ILCode.Call:
case ILCode.CallGetter:
// inlining runs both before and after IntroducePropertyAccessInstructions,
// so we have to handle both 'call' and 'callgetter'
MethodReference mr = (MethodReference)inlinedExpression.Operand;
// ensure that it's not an multi-dimensional array getter
if (mr.DeclaringType is ArrayType)
return false;
goto case ILCode.Callvirt;
case ILCode.Callvirt:
case ILCode.CallvirtGetter:
// don't inline foreach loop variables:
mr = (MethodReference)inlinedExpression.Operand;
if (mr.Name == "get_Current" && mr.HasThis)
return false;
break;
case ILCode.Castclass:
case ILCode.Unbox_Any:
// These are valid, but might occur as part of a foreach loop variable.
ILExpression arg = inlinedExpression.Arguments[0];
if (arg.Code == ILCode.CallGetter || arg.Code == ILCode.CallvirtGetter || arg.Code == ILCode.Call || arg.Code == ILCode.Callvirt) {
mr = (MethodReference)arg.Operand;
if (mr.Name == "get_Current" && mr.HasThis)
return false; // looks like a foreach loop variable, so don't inline it
}
break;
}
// inline the compiler-generated variable that are used when accessing a member on a value type:
switch (parent.Code) {
case ILCode.Call:
case ILCode.CallGetter:
case ILCode.CallSetter:
case ILCode.Callvirt:
case ILCode.CallvirtGetter:
case ILCode.CallvirtSetter:
MethodReference mr = (MethodReference)parent.Operand;
return mr.HasThis;
case ILCode.Stfld:
case ILCode.Ldfld:
case ILCode.Ldflda:
return true;
}
}
return false;
}
/// <summary>
/// Determines whether a variable should be inlined in non-aggressive mode, even though it is not a generated variable.
/// </summary>
/// <param name="next">The next top-level expression</param>
/// <param name="parent">The direct parent of the load within 'next'</param>
/// <param name="inlinedExpression">The expression being inlined</param>
bool NonAggressiveInlineInto(ILExpression next, ILExpression parent, ILExpression inlinedExpression)
{
switch (inlinedExpression.Code) {
case ILCode.InitArray:
case ILCode.InitObject:
case ILCode.InitCollection:
case ILCode.DefaultValue:
return true;
}
if (inlinedExpression.Code == ILCode.DefaultValue)
return true;
switch (next.Code) {
case ILCode.Ret:
return parent.Code == ILCode.Ret;
case ILCode.Brtrue:
return parent.Code == ILCode.Brtrue;
return parent == next;
case ILCode.Switch:
return parent.Code == ILCode.Switch || parent.Code == ILCode.Sub;
return parent == next || (parent.Code == ILCode.Sub && parent == next.Arguments[0]);
default:
return false;
}
@ -243,7 +345,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -243,7 +345,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression arg = expr.Arguments[i];
if (arg.Code == ILCode.Ldloc && arg.Operand == v) {
if ((arg.Code == ILCode.Ldloc || arg.Code == ILCode.Ldloca) && arg.Operand == v) {
parent = expr;
pos = i;
return true;

33
ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs

@ -43,11 +43,14 @@ namespace ICSharpCode.Decompiler.ILAst @@ -43,11 +43,14 @@ namespace ICSharpCode.Decompiler.ILAst
if (DecodeArrayInitializer(TypeAnalysis.GetTypeCode(arrayType), field.InitialValue, newArr)) {
body[pos] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr));
body.RemoveAt(pos + 1);
new ILInlining(method).InlineIfPossible(body, ref pos);
return true;
}
}
const int maxConsecutiveDefaultValueExpressions = 10;
// Put in a limit so that we don't consume too much memory if the code allocates a huge array
// and populates it extremely sparsly. However, 255 "null" elements in a row actually occur in the Mono C# compiler!
const int maxConsecutiveDefaultValueExpressions = 300;
List<ILExpression> operands = new List<ILExpression>();
int numberOfInstructionsToRemove = 0;
for (int j = pos + 1; j < body.Count; j++) {
@ -72,6 +75,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -72,6 +75,8 @@ namespace ICSharpCode.Decompiler.ILAst
if (operands.Count == arrayLength) {
expr.Arguments[0] = new ILExpression(ILCode.InitArray, arrayType, operands);
body.RemoveRange(pos + 1, numberOfInstructionsToRemove);
new ILInlining(method).InlineIfPossible(body, ref pos);
return true;
}
}
@ -206,7 +211,12 @@ namespace ICSharpCode.Decompiler.ILAst @@ -206,7 +211,12 @@ namespace ICSharpCode.Decompiler.ILAst
if (tr == null)
return false;
TypeDefinition td = tr.Resolve();
return td != null && td.Interfaces.Any(intf => intf.Name == "IEnumerable" && intf.Namespace == "System.Collections");
while (td != null) {
if (td.Interfaces.Any(intf => intf.Name == "IEnumerable" && intf.Namespace == "System.Collections"))
return true;
td = td.BaseType != null ? td.BaseType.Resolve() : null;
}
return false;
}
/// <summary>
@ -261,12 +271,16 @@ namespace ICSharpCode.Decompiler.ILAst @@ -261,12 +271,16 @@ namespace ICSharpCode.Decompiler.ILAst
while (++pos < body.Count) {
ILExpression nextExpr = body[pos] as ILExpression;
if (IsSetterInObjectInitializer(nextExpr)) {
if (!AdjustInitializerStack(initializerStack, nextExpr.Arguments[0], v, false))
if (!AdjustInitializerStack(initializerStack, nextExpr.Arguments[0], v, false)) {
CleanupInitializerStackAfterFailedAdjustment(initializerStack);
break;
}
initializerStack[initializerStack.Count - 1].Arguments.Add(nextExpr);
} else if (IsAddMethodCall(nextExpr)) {
if (!AdjustInitializerStack(initializerStack, nextExpr.Arguments[0], v, true))
if (!AdjustInitializerStack(initializerStack, nextExpr.Arguments[0], v, true)) {
CleanupInitializerStackAfterFailedAdjustment(initializerStack);
break;
}
initializerStack[initializerStack.Count - 1].Arguments.Add(nextExpr);
} else {
// can't match any more initializers: end of object initializer
@ -347,6 +361,17 @@ namespace ICSharpCode.Decompiler.ILAst @@ -347,6 +361,17 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
static void CleanupInitializerStackAfterFailedAdjustment(List<ILExpression> initializerStack)
{
// There might be empty nested initializers left over; so we'll remove those:
while (initializerStack.Count > 1 && initializerStack[initializerStack.Count - 1].Arguments.Count == 1) {
ILExpression parent = initializerStack[initializerStack.Count - 2];
Debug.Assert(parent.Arguments.Last() == initializerStack[initializerStack.Count - 1]);
parent.Arguments.RemoveAt(parent.Arguments.Count - 1);
initializerStack.RemoveAt(initializerStack.Count - 1);
}
}
static void ChangeFirstArgumentToInitializedObject(ILExpression initializer)
{
// Go through all elements in the initializer (so skip the newobj-instr. at the start)

4
ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs

@ -473,7 +473,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -473,7 +473,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (exprInit.Code == ILCode.Ldloc)
exprInit.Code = ILCode.Ldloca;
else if (exprInit.Code == ILCode.CallGetter)
exprInit.AddPrefix(new ILExpressionPrefix(ILCode.PropertyAddress));
exprInit = new ILExpression(ILCode.AddressOf, null, exprInit);
else
exprInit.Code = ILCode.Ldsflda;
expr.Arguments[0] = new ILExpression(incrementCode, incrementAmount, exprInit);
@ -567,8 +567,8 @@ namespace ICSharpCode.Decompiler.ILAst @@ -567,8 +567,8 @@ namespace ICSharpCode.Decompiler.ILAst
if (expr.Code == ILCode.Stobj) {
stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue.Arguments[0]);
} else if (expr.Code == ILCode.CallSetter || expr.Code == ILCode.CallvirtSetter) {
initialValue = new ILExpression(ILCode.AddressOf, null, initialValue);
stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue);
initialValue.AddPrefix(new ILExpressionPrefix(ILCode.PropertyAddress));
} else {
stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue);
initialValue.Code = (expr.Code == ILCode.Stfld ? ILCode.Ldflda : ILCode.Ldelema);

10
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -319,10 +319,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -319,10 +319,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (expr.Code == ILCode.CallSetter || expr.Code == ILCode.CallvirtSetter) {
return SubstituteTypeArgs(method.Parameters.Last().ParameterType, method);
} else {
TypeReference type = SubstituteTypeArgs(method.ReturnType, method);
if (expr.GetPrefix(ILCode.PropertyAddress) != null && !(type is ByReferenceType))
type = new ByReferenceType(type);
return type;
return SubstituteTypeArgs(method.ReturnType, method);
}
}
case ILCode.Newobj:
@ -463,6 +460,11 @@ namespace ICSharpCode.Decompiler.ILAst @@ -463,6 +460,11 @@ namespace ICSharpCode.Decompiler.ILAst
InferTypeForExpression(expr.Arguments[0], typeSystem.TypedReference);
}
return new ByReferenceType((TypeReference)expr.Operand);
case ILCode.AddressOf:
{
TypeReference t = InferTypeForExpression(expr.Arguments[0], UnpackPointer(expectedType));
return t != null ? new ByReferenceType(t) : null;
}
#endregion
#region Arithmetic instructions
case ILCode.Not: // bitwise complement

25
ICSharpCode.Decompiler/Tests/Generics.cs

@ -35,6 +35,31 @@ public static class Generics @@ -35,6 +35,31 @@ public static class Generics
}
}
public interface IInterface
{
void Method1<T>() where T : class;
void Method2<T>() where T : class;
}
public abstract class Base : Generics.IInterface
{
// constraints must be repeated on implicit interface implementation
public abstract void Method1<T>() where T : class;
// constraints must not be specified on explicit interface implementation
void Generics.IInterface.Method2<T>()
{
}
}
public class Derived : Generics.Base
{
// constraints are inherited automatically and must not be specified
public override void Method1<T>()
{
}
}
public static void MethodWithConstraint<T, S>() where T : class, S where S : ICloneable, new()
{
}

18
ICSharpCode.Decompiler/Tests/ValueTypes.cs

@ -35,6 +35,17 @@ public static class ValueTypes @@ -35,6 +35,17 @@ public static class ValueTypes
}
}
private static readonly ValueTypes.S ReadOnlyS = default(ValueTypes.S);
private static ValueTypes.S MutableS = default(ValueTypes.S);
public static void CallMethodViaField()
{
ValueTypes.ReadOnlyS.SetField();
ValueTypes.MutableS.SetField();
ValueTypes.S mutableS = ValueTypes.MutableS;
mutableS.SetField();
}
public static ValueTypes.S InitObj1()
{
ValueTypes.S result = default(ValueTypes.S);
@ -126,4 +137,11 @@ public static class ValueTypes @@ -126,4 +137,11 @@ public static class ValueTypes
{
return obj as ValueTypes.S?;
}
public static ValueTypes.S OnlyChangeTheCopy(ValueTypes.S p)
{
ValueTypes.S s = p;
s.SetField();
return p;
}
}

250
ILSpy/BamlDecompiler.cs

@ -14,6 +14,9 @@ using System.Xml.Linq; @@ -14,6 +14,9 @@ using System.Xml.Linq;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.TreeNodes;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
namespace ICSharpCode.ILSpy.Baml
{
@ -24,47 +27,239 @@ namespace ICSharpCode.ILSpy.Baml @@ -24,47 +27,239 @@ namespace ICSharpCode.ILSpy.Baml
{
}
public string DecompileBaml(MemoryStream bamlCode, string containingAssemblyFile)
abstract class XamlNode
{
bamlCode.Position = 0;
TextWriter w = new StringWriter();
public readonly List<XamlNode> Children = new List<XamlNode>();
Assembly assembly = Assembly.LoadFile(containingAssemblyFile);
public abstract void WriteTo(XamlWriter writer);
}
[Conditional("DEBUG")]
static void Log(string format, params object[] args)
{
//Debug.WriteLine(format, args);
}
sealed class XamlObjectNode : XamlNode
{
public readonly XamlType Type;
Baml2006Reader reader = new Baml2006Reader(bamlCode, new XamlReaderSettings() { ValuesMustBeString = true, LocalAssembly = assembly });
XDocument doc = new XDocument();
XamlXmlWriter writer = new XamlXmlWriter(doc.CreateWriter(), reader.SchemaContext);
public XamlObjectNode(XamlType type)
{
this.Type = type;
}
public override void WriteTo(XamlWriter writer)
{
Log("StartObject {0}", this.Type);
writer.WriteStartObject(this.Type);
Debug.Indent();
foreach (XamlNode node in this.Children)
node.WriteTo(writer);
Debug.Unindent();
Log("EndObject");
writer.WriteEndObject();
}
}
sealed class XamlGetObjectNode : XamlNode
{
public override void WriteTo(XamlWriter writer)
{
Log("GetObject");
writer.WriteGetObject();
Debug.Indent();
foreach (XamlNode node in this.Children)
node.WriteTo(writer);
Debug.Unindent();
Log("EndObject");
writer.WriteEndObject();
}
}
sealed class XamlMemberNode : XamlNode
{
public XamlMember Member;
public XamlMemberNode(XamlMember member)
{
this.Member = member;
}
public override void WriteTo(XamlWriter writer)
{
Log("StartMember {0}", this.Member);
writer.WriteStartMember(this.Member);
Debug.Indent();
foreach (XamlNode node in this.Children)
node.WriteTo(writer);
Debug.Unindent();
Log("EndMember");
writer.WriteEndMember();
}
}
sealed class XamlValueNode : XamlNode
{
public readonly object Value;
public XamlValueNode(object value)
{
this.Value = value;
}
public override void WriteTo(XamlWriter writer)
{
Log("Value {0}", this.Value);
Debug.Assert(this.Children.Count == 0);
// requires XamlReaderSettings.ValuesMustBeString = true to work properly
writer.WriteValue(this.Value);
}
}
sealed class XamlNamespaceDeclarationNode : XamlNode
{
public readonly NamespaceDeclaration Namespace;
public XamlNamespaceDeclarationNode(NamespaceDeclaration @namespace)
{
this.Namespace = @namespace;
}
public override void WriteTo(XamlWriter writer)
{
Log("NamespaceDeclaration {0}", this.Namespace);
Debug.Assert(this.Children.Count == 0);
writer.WriteNamespace(this.Namespace);
}
}
static List<XamlNode> Parse(XamlReader reader)
{
List<XamlNode> currentList = new List<XamlNode>();
Stack<List<XamlNode>> stack = new Stack<List<XamlNode>>();
while (reader.Read()) {
switch (reader.NodeType) {
case XamlNodeType.None:
break;
case XamlNodeType.StartObject:
writer.WriteStartObject(reader.Type);
XamlObjectNode obj = new XamlObjectNode(reader.Type);
currentList.Add(obj);
stack.Push(currentList);
currentList = obj.Children;
break;
case XamlNodeType.GetObject:
writer.WriteGetObject();
break;
case XamlNodeType.EndObject:
writer.WriteEndObject();
XamlGetObjectNode getObject = new XamlGetObjectNode();
currentList.Add(getObject);
stack.Push(currentList);
currentList = getObject.Children;
break;
case XamlNodeType.StartMember:
writer.WriteStartMember(reader.Member);
break;
case XamlNodeType.EndMember:
writer.WriteEndMember();
XamlMemberNode member = new XamlMemberNode(reader.Member);
currentList.Add(member);
stack.Push(currentList);
currentList = member.Children;
break;
case XamlNodeType.Value:
// requires XamlReaderSettings.ValuesMustBeString = true to work properly
writer.WriteValue(reader.Value);
currentList.Add(new XamlValueNode(reader.Value));
break;
case XamlNodeType.NamespaceDeclaration:
writer.WriteNamespace(reader.Namespace);
currentList.Add(new XamlNamespaceDeclarationNode(reader.Namespace));
break;
case XamlNodeType.EndObject:
case XamlNodeType.EndMember:
currentList = stack.Pop();
break;
default:
throw new Exception("Invalid value for XamlNodeType");
throw new InvalidOperationException("Invalid value for XamlNodeType");
}
}
if (stack.Count != 0)
throw new InvalidOperationException("Imbalanced stack");
return currentList;
}
void AvoidContentProperties(XamlNode node)
{
foreach (XamlNode child in node.Children)
AvoidContentProperties(child);
XamlObjectNode obj = node as XamlObjectNode;
if (obj != null) {
// Visit all except for the last child:
for (int i = 0; i < obj.Children.Count - 1; i++) {
// Avoids using content property syntax for simple string values, if the content property is not the last member.
// Without this, we cannot decompile &lt;GridViewColumn Header="Culture" DisplayMemberBinding="{Binding Culture}" /&gt;,
// because the Header property is the content property, but there is no way to represent the Binding as an element.
XamlMemberNode memberNode = obj.Children[i] as XamlMemberNode;
if (memberNode != null && memberNode.Member == obj.Type.ContentProperty) {
if (memberNode.Children.Count == 1 && memberNode.Children[0] is XamlValueNode) {
// By creating a clone of the XamlMember, we prevent WPF from knowing that it's the content property.
XamlMember member = memberNode.Member;
memberNode.Member = new XamlMember(member.Name, member.DeclaringType, member.IsAttachable);
}
}
}
// We also need to avoid using content properties that have a markup extension as value, as the XamlXmlWriter would always expand those:
for (int i = 0; i < obj.Children.Count; i++) {
XamlMemberNode memberNode = obj.Children[i] as XamlMemberNode;
if (memberNode != null && memberNode.Member == obj.Type.ContentProperty && memberNode.Children.Count == 1) {
XamlObjectNode me = memberNode.Children[0] as XamlObjectNode;
if (me != null && me.Type.IsMarkupExtension) {
// By creating a clone of the XamlMember, we prevent WPF from knowing that it's the content property.
XamlMember member = memberNode.Member;
memberNode.Member = new XamlMember(member.Name, member.DeclaringType, member.IsAttachable);
}
}
}
}
}
/// <summary>
/// It seems like BamlReader will always output 'x:Key' as last property. However, it must be specified as attribute in valid .xaml, so we move it to the front
/// of the attribute list.
/// </summary>
void MoveXKeyToFront(XamlNode node)
{
foreach (XamlNode child in node.Children)
MoveXKeyToFront(child);
XamlObjectNode obj = node as XamlObjectNode;
if (obj != null && obj.Children.Count > 0) {
XamlMemberNode memberNode = obj.Children[obj.Children.Count - 1] as XamlMemberNode;
if (memberNode != null && memberNode.Member == XamlLanguage.Key) {
// move memberNode in front of the first member node:
for (int i = 0; i < obj.Children.Count; i++) {
if (obj.Children[i] is XamlMemberNode) {
obj.Children.Insert(i, memberNode);
obj.Children.RemoveAt(obj.Children.Count - 1);
break;
}
}
}
}
}
public string DecompileBaml(MemoryStream bamlCode, string containingAssemblyFile)
{
bamlCode.Position = 0;
TextWriter w = new StringWriter();
Assembly assembly = Assembly.LoadFile(containingAssemblyFile);
Baml2006Reader reader = new Baml2006Reader(bamlCode, new XamlReaderSettings() { ValuesMustBeString = true, LocalAssembly = assembly });
var xamlDocument = Parse(reader);
foreach (var xamlNode in xamlDocument) {
AvoidContentProperties(xamlNode);
MoveXKeyToFront(xamlNode);
}
XDocument doc = new XDocument();
XamlXmlWriter writer = new XamlXmlWriter(doc.CreateWriter(), reader.SchemaContext, new XamlXmlWriterSettings { AssumeValidInput = true });
foreach (var xamlNode in xamlDocument)
xamlNode.WriteTo(writer);
writer.Close();
// Fix namespace references
@ -78,18 +273,7 @@ namespace ICSharpCode.ILSpy.Baml @@ -78,18 +273,7 @@ namespace ICSharpCode.ILSpy.Baml
}
}
}
// Convert x:Key into an attribute where possible
XName xKey = XName.Get("Key", "http://schemas.microsoft.com/winfx/2006/xaml");
foreach (XElement e in doc.Descendants(xKey).ToList()) {
if (e.Nodes().Count() != 1)
continue;
XText text = e.Nodes().Single() as XText;
if (text != null) {
e.Parent.SetAttributeValue(xKey, text.Value);
e.Remove();
}
}
return doc.ToString();
}

2
ILSpy/TreeNodes/Analyzer/AnalyzedPropertyOverridesTreeNode.cs

@ -28,7 +28,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -28,7 +28,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
public override object Text
{
get { return "Overriden By"; }
get { return "Overridden By"; }
}
public override object Icon

2
ILSpy/TreeNodes/Analyzer/AnalyzerMethodOverridesTreeNode.cs

@ -31,7 +31,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -31,7 +31,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
public override object Text
{
get { return "Overriden By"; }
get { return "Overridden By"; }
}
public override object Icon

Loading…
Cancel
Save