Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
26 changed files
with
2,242 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
120 changes: 120 additions & 0 deletions
120
Debugger/ILSpy.Debugger/Models/TreeModel/ArrayRangeNode.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) | ||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Text; | ||
|
||
using Debugger; | ||
using ICSharpCode.NRefactory.Ast; | ||
|
||
namespace ILSpy.Debugger.Models.TreeModel | ||
{ | ||
public partial class Utils | ||
{ | ||
public static IEnumerable<TreeNode> LazyGetChildNodesOfArray(Expression expression, ArrayDimensions dimensions) | ||
{ | ||
if (dimensions.TotalElementCount == 0) | ||
return new TreeNode[] { new TreeNode(null, "(empty)", null, null, null) }; | ||
|
||
return new ArrayRangeNode(expression, dimensions, dimensions).ChildNodes; | ||
} | ||
} | ||
|
||
/// <summary> This is a partent node for all elements within a given bounds </summary> | ||
public class ArrayRangeNode: TreeNode | ||
{ | ||
const int MaxElementCount = 100; | ||
|
||
Expression arrayTarget; | ||
ArrayDimensions bounds; | ||
ArrayDimensions originalBounds; | ||
|
||
public ArrayRangeNode(Expression arrayTarget, ArrayDimensions bounds, ArrayDimensions originalBounds) | ||
{ | ||
this.arrayTarget = arrayTarget; | ||
this.bounds = bounds; | ||
this.originalBounds = originalBounds; | ||
|
||
this.Name = GetName(); | ||
this.ChildNodes = LazyGetChildren(); | ||
} | ||
|
||
string GetName() | ||
{ | ||
StringBuilder name = new StringBuilder(); | ||
bool isFirst = true; | ||
name.Append("["); | ||
for(int i = 0; i < bounds.Count; i++) { | ||
if (!isFirst) name.Append(", "); | ||
isFirst = false; | ||
ArrayDimension dim = bounds[i]; | ||
ArrayDimension originalDim = originalBounds[i]; | ||
|
||
if (dim.Count == 0) { | ||
throw new DebuggerException("Empty dimension"); | ||
} else if (dim.Count == 1) { | ||
name.Append(dim.LowerBound.ToString()); | ||
} else if (dim.Equals(originalDim)) { | ||
name.Append("*"); | ||
} else { | ||
name.Append(dim.LowerBound); | ||
name.Append(".."); | ||
name.Append(dim.UpperBound); | ||
} | ||
} | ||
name.Append("]"); | ||
return name.ToString(); | ||
} | ||
|
||
static string GetName(int[] indices) | ||
{ | ||
StringBuilder sb = new StringBuilder(indices.Length * 4); | ||
sb.Append("["); | ||
bool isFirst = true; | ||
foreach(int index in indices) { | ||
if (!isFirst) sb.Append(", "); | ||
sb.Append(index.ToString()); | ||
isFirst = false; | ||
} | ||
sb.Append("]"); | ||
return sb.ToString(); | ||
} | ||
|
||
IEnumerable<TreeNode> LazyGetChildren() | ||
{ | ||
// The whole array is small - just add all elements as childs | ||
if (bounds.TotalElementCount <= MaxElementCount) { | ||
foreach(int[] indices in bounds.Indices) { | ||
string imageName; | ||
var image = ExpressionNode.GetImageForArrayIndexer(out imageName); | ||
var expression = new ExpressionNode(image, GetName(indices), arrayTarget.AppendIndexer(indices)); | ||
expression.ImageName = imageName; | ||
yield return expression; | ||
} | ||
yield break; | ||
} | ||
|
||
// Find a dimension of size at least 2 | ||
int splitDimensionIndex = bounds.Count - 1; | ||
for(int i = 0; i < bounds.Count; i++) { | ||
if (bounds[i].Count > 1) { | ||
splitDimensionIndex = i; | ||
break; | ||
} | ||
} | ||
ArrayDimension splitDim = bounds[splitDimensionIndex]; | ||
|
||
// Split the dimension | ||
int elementsPerSegment = 1; | ||
while (splitDim.Count > elementsPerSegment * MaxElementCount) { | ||
elementsPerSegment *= MaxElementCount; | ||
} | ||
for(int i = splitDim.LowerBound; i <= splitDim.UpperBound; i += elementsPerSegment) { | ||
List<ArrayDimension> newDims = new List<ArrayDimension>(bounds); | ||
newDims[splitDimensionIndex] = new ArrayDimension(i, Math.Min(i + elementsPerSegment - 1, splitDim.UpperBound)); | ||
yield return new ArrayRangeNode(arrayTarget, new ArrayDimensions(newDims), originalBounds); | ||
} | ||
yield break; | ||
} | ||
} | ||
} |
153 changes: 153 additions & 0 deletions
153
Debugger/ILSpy.Debugger/Models/TreeModel/ChildNodesOfObject.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) | ||
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
using System.Reflection; | ||
|
||
using Debugger; | ||
using Debugger.MetaData; | ||
using ICSharpCode.Core; | ||
using ICSharpCode.NRefactory.Ast; | ||
using ILSpy.Debugger.Services; | ||
using ILSpy.Debugger.Services.Debugger; | ||
|
||
namespace ILSpy.Debugger.Models.TreeModel | ||
{ | ||
public partial class Utils | ||
{ | ||
public static IEnumerable<TreeNode> LazyGetChildNodesOfObject(Expression targetObject, DebugType shownType) | ||
{ | ||
MemberInfo[] publicStatic = shownType.GetFieldsAndNonIndexedProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly); | ||
MemberInfo[] publicInstance = shownType.GetFieldsAndNonIndexedProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); | ||
MemberInfo[] nonPublicStatic = shownType.GetFieldsAndNonIndexedProperties(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly); | ||
MemberInfo[] nonPublicInstance = shownType.GetFieldsAndNonIndexedProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly); | ||
|
||
DebugType baseType = (DebugType)shownType.BaseType; | ||
if (baseType != null) { | ||
yield return new TreeNode( | ||
ImageService.GetImage("Icons.16x16.Class"), | ||
"BaseClass", | ||
baseType.Name, | ||
baseType.FullName, | ||
baseType.FullName == "System.Object" ? null : Utils.LazyGetChildNodesOfObject(targetObject, baseType) | ||
); | ||
} | ||
|
||
if (nonPublicInstance.Length > 0) { | ||
yield return new TreeNode( | ||
null, | ||
"NonPublicMembers", | ||
string.Empty, | ||
string.Empty, | ||
Utils.LazyGetMembersOfObject(targetObject, nonPublicInstance) | ||
); | ||
} | ||
|
||
if (publicStatic.Length > 0 || nonPublicStatic.Length > 0) { | ||
IEnumerable<TreeNode> childs = Utils.LazyGetMembersOfObject(targetObject, publicStatic); | ||
if (nonPublicStatic.Length > 0) { | ||
TreeNode nonPublicStaticNode = new TreeNode( | ||
null, | ||
"NonPublicStaticMembers", | ||
string.Empty, | ||
string.Empty, | ||
Utils.LazyGetMembersOfObject(targetObject, nonPublicStatic) | ||
); | ||
childs = Utils.PrependNode(nonPublicStaticNode, childs); | ||
} | ||
yield return new TreeNode( | ||
null, | ||
"StaticMembers", | ||
string.Empty, | ||
string.Empty, | ||
childs | ||
); | ||
} | ||
|
||
DebugType iListType = (DebugType)shownType.GetInterface(typeof(IList).FullName); | ||
if (iListType != null) { | ||
yield return new IListNode(targetObject); | ||
} else { | ||
DebugType iEnumerableType, itemType; | ||
if (shownType.ResolveIEnumerableImplementation(out iEnumerableType, out itemType)) { | ||
yield return new IEnumerableNode(targetObject, itemType); | ||
} | ||
} | ||
|
||
foreach(TreeNode node in LazyGetMembersOfObject(targetObject, publicInstance)) { | ||
yield return node; | ||
} | ||
} | ||
|
||
public static IEnumerable<TreeNode> LazyGetMembersOfObject(Expression expression, MemberInfo[] members) | ||
{ | ||
List<TreeNode> nodes = new List<TreeNode>(); | ||
foreach(MemberInfo memberInfo in members) { | ||
string imageName; | ||
var image = ExpressionNode.GetImageForMember((IDebugMemberInfo)memberInfo, out imageName); | ||
var exp = new ExpressionNode(image, memberInfo.Name, expression.AppendMemberReference((IDebugMemberInfo)memberInfo)); | ||
exp.ImageName = imageName; | ||
nodes.Add(exp); | ||
} | ||
nodes.Sort(); | ||
return nodes; | ||
} | ||
|
||
|
||
public static IEnumerable<TreeNode> LazyGetItemsOfIList(Expression targetObject) | ||
{ | ||
// This is needed for expanding IEnumerable<T> | ||
targetObject = new CastExpression( | ||
new TypeReference(typeof(IList).FullName), | ||
targetObject, | ||
CastType.Cast | ||
); | ||
int count = 0; | ||
GetValueException error = null; | ||
try { | ||
count = GetIListCount(targetObject); | ||
} catch (GetValueException e) { | ||
// Cannot yield a value in the body of a catch clause (CS1631) | ||
error = e; | ||
} | ||
if (error != null) { | ||
yield return new TreeNode(null, "(error)", error.Message, null, null); | ||
} else if (count == 0) { | ||
yield return new TreeNode(null, "(empty)", null, null, null); | ||
} else { | ||
for(int i = 0; i < count; i++) { | ||
string imageName; | ||
var image = ExpressionNode.GetImageForArrayIndexer(out imageName); | ||
var expression = new ExpressionNode(image, "[" + i + "]", targetObject.AppendIndexer(i)); | ||
expression.ImageName = imageName; | ||
yield return expression; | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Evaluates System.Collections.ICollection.Count property on given object. | ||
/// </summary> | ||
/// <exception cref="GetValueException">Evaluating System.Collections.ICollection.Count on targetObject failed.</exception> | ||
public static int GetIListCount(Expression targetObject) | ||
{ | ||
Value list = targetObject.Evaluate(WindowsDebugger.CurrentProcess); | ||
var iCollectionInterface = list.Type.GetInterface(typeof(ICollection).FullName); | ||
if (iCollectionInterface == null) | ||
throw new GetValueException(targetObject, targetObject.PrettyPrint() + " does not implement System.Collections.ICollection"); | ||
PropertyInfo countProperty = iCollectionInterface.GetProperty("Count"); | ||
// Do not get string representation since it can be printed in hex | ||
return (int)list.GetPropertyValue(countProperty).PrimitiveValue; | ||
} | ||
|
||
public static IEnumerable<TreeNode> PrependNode(TreeNode node, IEnumerable<TreeNode> rest) | ||
{ | ||
yield return node; | ||
if (rest != null) { | ||
foreach(TreeNode absNode in rest) { | ||
yield return absNode; | ||
} | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.