Browse Source

When decompiling a field, also decompile constructors to check whether there's an initializer on the field.

When decompiling a constructor, display field initializers outside of the constructor.
Closes #3.
pull/172/merge
Daniel Grunwald 14 years ago
parent
commit
e2794252fb
  1. 3
      ICSharpCode.Decompiler/Ast/AstBuilder.cs
  2. 56
      ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs
  3. 102
      ILSpy/CSharpLanguage.cs
  4. 2
      NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs

3
ICSharpCode.Decompiler/Ast/AstBuilder.cs

@ -729,6 +729,9 @@ namespace ICSharpCode.Decompiler.Ast @@ -729,6 +729,9 @@ namespace ICSharpCode.Decompiler.Ast
astMethod.Body = CreateMethodBody(methodDef, astMethod.Parameters);
ConvertAttributes(astMethod, methodDef);
astMethod.WithAnnotation(methodMapping);
if (methodDef.IsStatic && methodDef.DeclaringType.IsBeforeFieldInit && !astMethod.Body.IsNull) {
astMethod.Body.InsertChildAfter(null, new Comment(" Note: this type is marked as 'beforefieldinit'."), AstNode.Roles.Comment);
}
return astMethod;
}

56
ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs

@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
@ -69,9 +70,29 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -69,9 +70,29 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
{
var instanceCtors = typeDeclaration.Members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray();
// Handle initializers on instance fields
HandleInstanceFieldInitializers(typeDeclaration.Members);
// Now convert base constructor calls to initializers:
base.VisitTypeDeclaration(typeDeclaration, data);
// Remove single empty constructor:
RemoveSingleEmptyConstructor(typeDeclaration);
// Handle initializers on static fields:
HandleStaticFieldInitializers(typeDeclaration.Members);
return null;
}
void HandleInstanceFieldInitializers(IEnumerable<AstNode> members)
{
var instanceCtors = members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray();
var instanceCtorsNotChainingWithThis = instanceCtors.Where(ctor => !thisCallPattern.IsMatch(ctor.Body.Statements.FirstOrDefault())).ToArray();
if (instanceCtorsNotChainingWithThis.Length > 0 && typeDeclaration.ClassType == NRefactory.TypeSystem.ClassType.Class) {
if (instanceCtorsNotChainingWithThis.Length > 0) {
MethodDefinition ctorMethodDef = instanceCtorsNotChainingWithThis[0].Annotation<MethodDefinition>();
if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsValueType)
return;
// Recognize field initializers:
// Convert first statement in all ctors (if all ctors have the same statement) into a field initializer.
bool allSame;
@ -83,7 +104,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -83,7 +104,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
FieldDefinition fieldDef = m.Get<AstNode>("fieldAccess").Single().Annotation<FieldReference>().ResolveWithinSameModule();
if (fieldDef == null)
break;
AttributedNode fieldOrEventDecl = typeDeclaration.Members.FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef);
AstNode fieldOrEventDecl = members.FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef);
if (fieldOrEventDecl == null)
break;
@ -99,11 +120,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -99,11 +120,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}
} while (allSame);
}
// Now convert base constructor calls to initializers:
base.VisitTypeDeclaration(typeDeclaration, data);
// Remove single empty constructor:
}
void RemoveSingleEmptyConstructor(TypeDeclaration typeDeclaration)
{
var instanceCtors = typeDeclaration.Members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray();
if (instanceCtors.Length == 1) {
ConstructorDeclaration emptyCtor = new ConstructorDeclaration();
emptyCtor.Modifiers = ((typeDeclaration.Modifiers & Modifiers.Abstract) == Modifiers.Abstract ? Modifiers.Protected : Modifiers.Public);
@ -111,12 +132,15 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -111,12 +132,15 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
if (emptyCtor.IsMatch(instanceCtors[0]))
instanceCtors[0].Remove();
}
}
void HandleStaticFieldInitializers(IEnumerable<AstNode> members)
{
// Convert static constructor into field initializers if the class is BeforeFieldInit
var staticCtor = typeDeclaration.Members.OfType<ConstructorDeclaration>().FirstOrDefault(c => (c.Modifiers & Modifiers.Static) == Modifiers.Static);
var staticCtor = members.OfType<ConstructorDeclaration>().FirstOrDefault(c => (c.Modifiers & Modifiers.Static) == Modifiers.Static);
if (staticCtor != null) {
TypeDefinition typeDef = typeDeclaration.Annotation<TypeDefinition>();
if (typeDef != null && typeDef.IsBeforeFieldInit) {
MethodDefinition ctorMethodDef = staticCtor.Annotation<MethodDefinition>();
if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsBeforeFieldInit) {
while (true) {
ExpressionStatement es = staticCtor.Body.Statements.FirstOrDefault() as ExpressionStatement;
if (es == null)
@ -127,7 +151,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -127,7 +151,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
FieldDefinition fieldDef = assignment.Left.Annotation<FieldReference>().ResolveWithinSameModule();
if (fieldDef == null || !fieldDef.IsStatic)
break;
FieldDeclaration fieldDecl = typeDeclaration.Members.OfType<FieldDeclaration>().FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef);
FieldDeclaration fieldDecl = members.OfType<FieldDeclaration>().FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef);
if (fieldDecl == null)
break;
fieldDecl.Variables.Single().Initializer = assignment.Right.Detach();
@ -137,11 +161,15 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -137,11 +161,15 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
staticCtor.Remove();
}
}
return null;
}
void IAstTransform.Run(AstNode node)
{
// If we're viewing some set of members (fields are direct children of CompilationUnit),
// we also need to handle those:
HandleInstanceFieldInitializers(node.Children);
HandleStaticFieldInitializers(node.Children);
node.AcceptVisitor(this, null);
}
}

102
ILSpy/CSharpLanguage.cs

@ -52,7 +52,7 @@ namespace ICSharpCode.ILSpy @@ -52,7 +52,7 @@ namespace ICSharpCode.ILSpy
{
}
#if DEBUG
#if DEBUG
internal static IEnumerable<CSharpLanguage> GetDebugLanguages()
{
DecompilerContext context = new DecompilerContext(ModuleDefinition.CreateModule("dummy", ModuleKind.Dll));
@ -71,7 +71,7 @@ namespace ICSharpCode.ILSpy @@ -71,7 +71,7 @@ namespace ICSharpCode.ILSpy
showAllMembers = true
};
}
#endif
#endif
public override string Name
{
@ -92,8 +92,50 @@ namespace ICSharpCode.ILSpy @@ -92,8 +92,50 @@ namespace ICSharpCode.ILSpy
{
WriteCommentLine(output, TypeToString(method.DeclaringType, includeNamespace: true));
AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: method.DeclaringType, isSingleMember: true);
codeDomBuilder.AddMethod(method);
RunTransformsAndGenerateCode(codeDomBuilder, output, options);
if (method.IsConstructor && !method.IsStatic && !method.DeclaringType.IsValueType) {
// also fields and other ctors so that the field initializers can be shown as such
AddFieldsAndCtors(codeDomBuilder, method.DeclaringType, method.IsStatic);
RunTransformsAndGenerateCode(codeDomBuilder, output, options, new SelectCtorTransform(method));
} else {
codeDomBuilder.AddMethod(method);
RunTransformsAndGenerateCode(codeDomBuilder, output, options);
}
}
class SelectCtorTransform : IAstTransform
{
readonly MethodDefinition ctorDef;
public SelectCtorTransform(MethodDefinition ctorDef)
{
this.ctorDef = ctorDef;
}
public void Run(AstNode compilationUnit)
{
ConstructorDeclaration ctorDecl = null;
foreach (var node in compilationUnit.Children) {
ConstructorDeclaration ctor = node as ConstructorDeclaration;
if (ctor != null) {
if (ctor.Annotation<MethodDefinition>() == ctorDef) {
ctorDecl = ctor;
} else {
// remove other ctors
ctor.Remove();
}
}
// Remove any fields without initializers
FieldDeclaration fd = node as FieldDeclaration;
if (fd != null && fd.Variables.All(v => v.Initializer.IsNull))
fd.Remove();
}
if (ctorDecl.Initializer.ConstructorInitializerType == ConstructorInitializerType.This) {
// remove all fields
foreach (var node in compilationUnit.Children)
if (node is FieldDeclaration)
node.Remove();
}
}
}
public override void DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options)
@ -108,8 +150,48 @@ namespace ICSharpCode.ILSpy @@ -108,8 +150,48 @@ namespace ICSharpCode.ILSpy
{
WriteCommentLine(output, TypeToString(field.DeclaringType, includeNamespace: true));
AstBuilder codeDomBuilder = CreateAstBuilder(options, currentType: field.DeclaringType, isSingleMember: true);
codeDomBuilder.AddField(field);
RunTransformsAndGenerateCode(codeDomBuilder, output, options);
if (field.IsLiteral) {
codeDomBuilder.AddField(field);
} else {
// also decompile ctors so that the field initializer can be shown
AddFieldsAndCtors(codeDomBuilder, field.DeclaringType, field.IsStatic);
}
RunTransformsAndGenerateCode(codeDomBuilder, output, options, new SelectFieldTransform(field));
}
/// <summary>
/// Removes all top-level members except for the specified fields.
/// </summary>
sealed class SelectFieldTransform : IAstTransform
{
readonly FieldDefinition field;
public SelectFieldTransform(FieldDefinition field)
{
this.field = field;
}
public void Run(AstNode compilationUnit)
{
foreach (var child in compilationUnit.Children) {
if (child is AttributedNode) {
if (child.Annotation<FieldDefinition>() != field)
child.Remove();
}
}
}
}
void AddFieldsAndCtors(AstBuilder codeDomBuilder, TypeDefinition declaringType, bool isStatic)
{
foreach (var field in declaringType.Fields) {
if (field.IsStatic == isStatic)
codeDomBuilder.AddField(field);
}
foreach (var ctor in declaringType.Methods) {
if (ctor.IsConstructor && ctor.IsStatic == isStatic)
codeDomBuilder.AddMethod(ctor);
}
}
public override void DecompileEvent(EventDefinition ev, ITextOutput output, DecompilationOptions options)
@ -127,11 +209,15 @@ namespace ICSharpCode.ILSpy @@ -127,11 +209,15 @@ namespace ICSharpCode.ILSpy
RunTransformsAndGenerateCode(codeDomBuilder, output, options);
}
void RunTransformsAndGenerateCode(AstBuilder astBuilder, ITextOutput output, DecompilationOptions options)
void RunTransformsAndGenerateCode(AstBuilder astBuilder, ITextOutput output, DecompilationOptions options, IAstTransform additionalTransform = null)
{
astBuilder.RunTransformations(transformAbortCondition);
if (options.DecompilerSettings.ShowXmlDocumentation)
if (additionalTransform != null) {
additionalTransform.Run(astBuilder.CompilationUnit);
}
if (options.DecompilerSettings.ShowXmlDocumentation) {
AddXmlDocTransform.Run(astBuilder.CompilationUnit);
}
astBuilder.GenerateCode(output);
}

2
NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs

@ -2147,7 +2147,9 @@ namespace ICSharpCode.NRefactory.CSharp @@ -2147,7 +2147,9 @@ namespace ICSharpCode.NRefactory.CSharp
// "1.0 / /*comment*/a", then we need to insert a space in front of the comment.
formatter.Space();
}
formatter.StartNode(comment);
formatter.WriteComment(comment.CommentType, comment.Content);
formatter.EndNode(comment);
lastWritten = LastWritten.Whitespace;
return null;
}

Loading…
Cancel
Save