Browse Source

Refactored handling of large and multi-dimensional arrays

newNRvisualizers
David Srbecký 13 years ago
parent
commit
232985182a
  1. 7
      src/AddIns/Debugger/Debugger.AddIn/NRefactory/ExpressionEvaluationVisitor.cs
  2. 121
      src/AddIns/Debugger/Debugger.AddIn/TreeModel/ValueNode.cs
  3. 70
      src/AddIns/Debugger/Debugger.Core/ArrayDimension.cs
  4. 109
      src/AddIns/Debugger/Debugger.Core/ArrayDimensions.cs
  5. 2
      src/AddIns/Debugger/Debugger.Core/Debugger.Core.csproj
  6. 73
      src/AddIns/Debugger/Debugger.Core/Value.cs
  7. 8
      src/AddIns/Debugger/Debugger.Tests/Tests/Value_Tests.cs

7
src/AddIns/Debugger/Debugger.AddIn/NRefactory/ExpressionEvaluationVisitor.cs

@ -308,7 +308,7 @@ namespace Debugger.AddIn
Value Visit(ArrayAccessResolveResult result) Value Visit(ArrayAccessResolveResult result)
{ {
var val = Convert(result.Array).GetPermanentReference(evalThread); var val = Convert(result.Array).GetPermanentReference(evalThread);
return val.GetArrayElement(result.Indexes.Select(rr => (int)Convert(rr).PrimitiveValue).ToArray()); return val.GetArrayElement(result.Indexes.Select(rr => (uint)(int)Convert(rr).PrimitiveValue).ToArray());
} }
Value Visit(ArrayCreateResolveResult result) Value Visit(ArrayCreateResolveResult result)
@ -390,10 +390,11 @@ namespace Debugger.AddIn
sb.Append(val.Type.Name); sb.Append(val.Type.Name);
sb.Append(" {"); sb.Append(" {");
bool first = true; bool first = true;
foreach(Value item in val.GetArrayElements()) { int size = val.ArrayLength;
for (int i = 0; i < size; i++) {
if (!first) sb.Append(", "); if (!first) sb.Append(", ");
first = false; first = false;
sb.Append(FormatValue(evalThread, item)); sb.Append(FormatValue(evalThread, val.GetElementAtPosition(i)));
} }
sb.Append("}"); sb.Append("}");
return sb.ToString(); return sb.ToString();

121
src/AddIns/Debugger/Debugger.AddIn/TreeModel/ValueNode.cs

@ -109,9 +109,10 @@ namespace Debugger.AddIn.TreeModel
} else if (val.Type.IsPrimitiveType() || val.Type.IsKnownType(KnownTypeCode.String)) { // Must be before IsClass } else if (val.Type.IsPrimitiveType() || val.Type.IsKnownType(KnownTypeCode.String)) { // Must be before IsClass
this.GetChildren = null; this.GetChildren = null;
} else if (val.Type.Kind == TypeKind.Array) { // Must be before IsClass } else if (val.Type.Kind == TypeKind.Array) { // Must be before IsClass
var dims = val.ArrayDimensions; // Eval now var dimBase = val.ArrayBaseIndicies; // Eval now
if (dims.TotalElementCount > 0) { var dimSize = val.ArrayDimensions; // Eval now
this.GetChildren = () => GetArrayChildren(dims, dims); if (val.ArrayLength > 0) {
this.GetChildren = () => GetArrayChildren(dimBase, dimSize);
} }
} else if (val.Type.Kind == TypeKind.Class || val.Type.Kind == TypeKind.Struct) { } else if (val.Type.Kind == TypeKind.Class || val.Type.Kind == TypeKind.Struct) {
if (val.Type.IsKnownType(typeof(List<>))) { if (val.Type.IsKnownType(typeof(List<>))) {
@ -373,83 +374,87 @@ namespace Debugger.AddIn.TreeModel
} }
} }
TreeNode GetArraySubsetNode(ArrayDimensions bounds, ArrayDimensions originalBounds) IEnumerable<TreeNode> GetArrayChildren(uint[] dimBase, uint[] dimSize)
{
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) {
name.Append("-");
} 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 new TreeNode(name.ToString(), () => GetArrayChildren(bounds, originalBounds));
}
IEnumerable<TreeNode> GetArrayChildren(ArrayDimensions bounds, ArrayDimensions originalBounds)
{ {
const int MaxElementCount = 100; const int MaxElementCount = 100;
if (bounds.TotalElementCount == 0) int rank = dimSize.Length;
uint totalSize = dimSize.Aggregate((uint)1, (acc, s) => acc * s);
if (totalSize == 0)
{ {
yield return new TreeNode("(empty)", null); yield return new TreeNode("(empty)", null);
yield break; yield break;
} }
// The whole array is small - just add all elements as childs // The array is small - just add all elements as children
if (bounds.TotalElementCount <= MaxElementCount * 2) { StringBuilder sb = new StringBuilder();
foreach(int[] indices in bounds.Indices) { if (totalSize <= MaxElementCount) {
StringBuilder sb = new StringBuilder(indices.Length * 4); uint[] indices = (uint[])dimBase.Clone();
sb.Append("["); while(indices[0] < dimBase[0] + dimSize[0]) {
// Make element name
sb.Clear();
sb.Append('[');
bool isFirst = true; bool isFirst = true;
foreach(int index in indices) { foreach(int index in indices) {
if (!isFirst) sb.Append(", "); if (!isFirst) sb.Append(", ");
sb.Append(index.ToString()); sb.Append(index);
isFirst = false; isFirst = false;
} }
sb.Append("]"); sb.Append(']');
int[] indicesCopy = indices;
// The getValue delegate might be called later or several times
uint[] indicesCopy = (uint[])indices.Clone();
yield return new ValueNode(ClassBrowserIconService.Field, sb.ToString(), () => GetValue().GetArrayElement(indicesCopy)); yield return new ValueNode(ClassBrowserIconService.Field, sb.ToString(), () => GetValue().GetArrayElement(indicesCopy));
// Get next combination
indices[rank - 1]++;
for (int i = rank - 1; i > 0; i--) {
if (indices[i] >= dimBase[i] + dimSize[i]) {
indices[i] = dimBase[i];
indices[i - 1]++;
}
}
} }
yield break; yield break;
} }
// Find a dimension of size at least 2 // Split the array into smaller subsets
int splitDimensionIndex = bounds.Count - 1; int splitIndex = Array.FindIndex(dimSize, s => (s > 1));
for(int i = 0; i < bounds.Count; i++) { uint groupSize = 1;
if (bounds[i].Count > 1) { while (dimSize[splitIndex] > groupSize * MaxElementCount) {
splitDimensionIndex = i; groupSize *= MaxElementCount;
break;
} }
} for(uint i = dimBase[splitIndex]; i < dimBase[splitIndex] + dimSize[splitIndex]; i += groupSize) {
ArrayDimension splitDim = bounds[splitDimensionIndex]; // Get the base&size for the subset
uint[] newDimBase = (uint[])dimBase.Clone();
uint[] newDimSize = (uint[])dimSize.Clone();
newDimBase[splitIndex] = i;
newDimSize[splitIndex] = Math.Min(groupSize, dimBase[splitIndex] + dimSize[splitIndex] - i);
// Split the dimension // Make the subset name
int elementsPerSegment = 1; sb.Clear();
while (splitDim.Count > elementsPerSegment * MaxElementCount) { sb.Append('[');
elementsPerSegment *= MaxElementCount; bool isFirst = true;
for (int j = 0; j < rank; j++) {
if (!isFirst) sb.Append(", ");
if (j < splitIndex) {
sb.Append(newDimBase[j]);
} else if (j == splitIndex) {
sb.Append(i);
if (newDimSize[splitIndex] > 1) {
sb.Append("..");
sb.Append(i + newDimSize[splitIndex] - 1);
} }
for(int i = splitDim.LowerBound; i <= splitDim.UpperBound; i += elementsPerSegment) { } else if (j > splitIndex) {
List<ArrayDimension> newDims = new List<ArrayDimension>(bounds); sb.Append('*');
newDims[splitDimensionIndex] = new ArrayDimension(i, Math.Min(i + elementsPerSegment - 1, splitDim.UpperBound)); }
yield return GetArraySubsetNode(new ArrayDimensions(newDims), originalBounds); isFirst = false;
}
sb.Append(']');
yield return new TreeNode(sb.ToString(), () => GetArrayChildren(newDimBase, newDimSize));
} }
yield break;
} }
} }
} }

70
src/AddIns/Debugger/Debugger.Core/ArrayDimension.cs

@ -1,70 +0,0 @@
// 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;
namespace Debugger
{
/// <summary>
/// Specifies the range of valid indicies for an array dimension
/// </summary>
public class ArrayDimension
{
int lowerBound;
int upperBound;
/// <summary> The smallest valid index in this dimension </summary>
public int LowerBound {
get { return lowerBound; }
}
/// <summary> The largest valid index in this dimension.
/// Returns LowerBound - 1 if the array is empty. </summary>
public int UpperBound {
get { return upperBound; }
}
/// <summary> The number of valid indicies of this dimension </summary>
public int Count {
get { return upperBound - lowerBound + 1; }
}
/// <summary> Determines whether the given index is a valid index for this dimension </summary>
public bool IsIndexValid(int index)
{
return (this.LowerBound <= index && index <= this.UpperBound);
}
public ArrayDimension(int lowerBound, int upperBound)
{
this.lowerBound = lowerBound;
this.upperBound = upperBound;
}
public override string ToString()
{
if (this.LowerBound == 0) {
return this.Count.ToString();
} else {
return this.LowerBound + ".." + this.UpperBound;
}
}
public override int GetHashCode()
{
int hashCode = 0;
unchecked {
hashCode += 1000000007 * lowerBound.GetHashCode();
hashCode += 1000000009 * upperBound.GetHashCode();
}
return hashCode;
}
public override bool Equals(object obj)
{
ArrayDimension other = obj as ArrayDimension;
if (other == null) return false;
return this.lowerBound == other.lowerBound && this.upperBound == other.upperBound;
}
}
}

109
src/AddIns/Debugger/Debugger.Core/ArrayDimensions.cs

@ -1,109 +0,0 @@
// 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;
using System.Collections.Generic;
namespace Debugger
{
/// <summary>
/// Specifies the range of valid indicies for all array dimensions
/// </summary>
public class ArrayDimensions: IEnumerable<ArrayDimension>
{
List<ArrayDimension> dimensions = new List<ArrayDimension>();
public IEnumerator<ArrayDimension> GetEnumerator()
{
return dimensions.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)dimensions).GetEnumerator();
}
/// <summary> Gets a given dimension </summary>
public ArrayDimension this[int index] {
get {
return dimensions[index];
}
}
/// <summary> Get the number of dimensions of the array </summary>
public int Count {
get {
return dimensions.Count;
}
}
/// <summary> Get the total number of elements within the bounds
/// of an array specified by these dimensions. </summary>
public int TotalElementCount {
get {
int totalCount = 1;
foreach(ArrayDimension dim in this) {
totalCount *= dim.Count;
}
return totalCount;
}
}
/// <summary> Enumerate all vaild indicies in the array </summary>
public IEnumerable<int[]> Indices {
get {
foreach(ArrayDimension dim in this) {
if (dim.Count == 0) yield break;
}
int rank = this.Count;
int[] indices = new int[rank];
for(int i = 0; i < rank; i++) {
indices[i] = this[i].LowerBound;
}
while(true) { // Go thought all combinations
for (int i = rank - 1; i >= 1; i--) {
if (indices[i] > this[i].UpperBound) {
indices[i] = this[i].LowerBound;
indices[i - 1]++;
}
}
if (indices[0] > this[0].UpperBound) yield break; // We are done
yield return (int[])indices.Clone();
indices[rank - 1]++;
}
}
}
/// <summary> Determines whether the given index is a valid index for the array </summary>
public bool IsIndexValid(int[] indices)
{
for (int i = 0; i < this.Count; i++) {
if (!this[i].IsIndexValid(indices[i])) return false;
}
return true;
}
public ArrayDimensions(List<ArrayDimension> dimensions)
{
this.dimensions = dimensions;
}
public override string ToString()
{
string result = "[";
bool isFirst = true;
foreach(ArrayDimension dim in this) {
if (!isFirst) result += ", ";
result += dim.ToString();
isFirst = false;
}
result += "]";
return result;
}
}
}

2
src/AddIns/Debugger/Debugger.Core/Debugger.Core.csproj

@ -64,8 +64,6 @@
<Compile Include="TypeSystemExtensions.cs" /> <Compile Include="TypeSystemExtensions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="AppDomain.cs" /> <Compile Include="AppDomain.cs" />
<Compile Include="ArrayDimension.cs" />
<Compile Include="ArrayDimensions.cs" />
<Compile Include="Breakpoint.cs" /> <Compile Include="Breakpoint.cs" />
<Compile Include="DebuggerException.cs" /> <Compile Include="DebuggerException.cs" />
<Compile Include="Eval.cs" /> <Compile Include="Eval.cs" />

73
src/AddIns/Debugger/Debugger.Core/Value.cs

@ -297,10 +297,7 @@ namespace Debugger
#region Array #region Array
/// <summary> /// <summary> Gets the number of elements in the array. (eg new object[4,5] returns 20) </summary>
/// Gets the number of elements in the array.
/// eg new object[4,5] returns 20
/// </summary>
/// <returns> 0 for non-arrays </returns> /// <returns> 0 for non-arrays </returns>
public int ArrayLength { public int ArrayLength {
get { get {
@ -309,10 +306,7 @@ namespace Debugger
} }
} }
/// <summary> /// <summary> Gets the number of dimensions of the array. (eg new object[4,5] returns 2) </summary>
/// Gets the number of dimensions of the array.
/// eg new object[4,5] returns 2
/// </summary>
/// <returns> 0 for non-arrays </returns> /// <returns> 0 for non-arrays </returns>
public int ArrayRank { public int ArrayRank {
get { get {
@ -321,73 +315,58 @@ namespace Debugger
} }
} }
/// <summary> Gets the dimensions of the array </summary> /// <summary> Returns the lowest allowed index for each dimension. Generally zero. </summary>
/// <returns> null for non-arrays </returns> public uint[] ArrayBaseIndicies {
public ArrayDimensions ArrayDimensions {
get { get {
if (this.Type.Kind != TypeKind.Array) return null; if (this.Type.Kind != TypeKind.Array) return null;
int rank = this.ArrayRank;
uint[] baseIndicies;
if (CorArrayValue.HasBaseIndicies() == 1) { if (CorArrayValue.HasBaseIndicies() == 1) {
baseIndicies = CorArrayValue.GetBaseIndicies(); return CorArrayValue.GetBaseIndicies();
} else { } else {
baseIndicies = new uint[this.ArrayRank]; return new uint[this.ArrayRank];
}
} }
uint[] dimensionCounts = CorArrayValue.GetDimensions();
List<ArrayDimension> dimensions = new List<ArrayDimension>();
for(int i = 0; i < rank; i++) {
dimensions.Add(new ArrayDimension((int)baseIndicies[i], (int)baseIndicies[i] + (int)dimensionCounts[i] - 1));
} }
return new ArrayDimensions(dimensions); /// <summary> Returns the number of elements in each dimension </summary>
public uint[] ArrayDimensions {
get {
if (this.Type.Kind != TypeKind.Array) return null;
return CorArrayValue.GetDimensions();
} }
} }
/// <summary> Returns an element of a single-dimensional array </summary> /// <summary> Returns an element of a single-dimensional array </summary>
public Value GetArrayElement(int index) public Value GetArrayElement(int index)
{ {
return GetArrayElement(new int[] {index}); return GetArrayElement(new uint[] { (uint)index });
} }
/// <summary> Returns an element of an array </summary> /// <summary> Returns an element of an array </summary>
public Value GetArrayElement(int[] elementIndices) public Value GetArrayElement(uint[] indices)
{ {
int[] indices = (int[])elementIndices.Clone(); try {
return new Value(this.AppDomain, CorArrayValue.GetElement(indices));
return new Value(this.AppDomain, GetCorValueOfArrayElement(indices)); } catch (ArgumentException) {
throw new GetValueException("Invalid array index");
}
} }
// May be called later /// <summary> Returns an element of an array (treats the array as zero-based and single dimensional) </summary>
ICorDebugValue GetCorValueOfArrayElement(int[] indices) public Value GetElementAtPosition(int index)
{ {
if (indices.Length != ArrayRank) { try {
throw new GetValueException("Given indicies do not have the same dimension as array."); return new Value(this.AppDomain, CorArrayValue.GetElementAtPosition((uint)index));
} catch (ArgumentException) {
throw new GetValueException("Invalid array index");
} }
if (!this.ArrayDimensions.IsIndexValid(indices)) {
throw new GetValueException("Given indices are out of range of the array");
} }
return CorArrayValue.GetElement(indices); public void SetArrayElement(Thread evalThread, uint[] elementIndices, Value newVal)
}
public void SetArrayElement(Thread evalThread, int[] elementIndices, Value newVal)
{ {
Value elem = GetArrayElement(elementIndices); Value elem = GetArrayElement(elementIndices);
elem.SetValue(evalThread, newVal); elem.SetValue(evalThread, newVal);
} }
/// <summary> Returns all elements in the array </summary>
public Value[] GetArrayElements()
{
if (this.Type.Kind != TypeKind.Array) return null;
List<Value> values = new List<Value>();
foreach(int[] indices in this.ArrayDimensions.Indices) {
values.Add(GetArrayElement(indices));
}
return values.ToArray();
}
#endregion #endregion
#region Object #region Object

8
src/AddIns/Debugger/Debugger.Tests/Tests/Value_Tests.cs

@ -46,7 +46,7 @@ namespace Debugger.Tests {
Value lbArray = this.CurrentStackFrame.GetLocalVariableValue("lbArray").GetPermanentReference(this.EvalThread); Value lbArray = this.CurrentStackFrame.GetLocalVariableValue("lbArray").GetPermanentReference(this.EvalThread);
ObjectDump("lbArray", lbArray); ObjectDump("lbArray", lbArray);
ObjectDump("lbArray-10-20", lbArray.GetArrayElement(new int[] {10, 20})); ObjectDump("lbArray-10-20", lbArray.GetArrayElement(new uint[] {10, 20}));
EndTest(); EndTest();
} }
@ -108,20 +108,20 @@ namespace Debugger.Tests {
</array.Length> </array.Length>
<array> <array>
<Value <Value
ArrayBaseIndicies="{0, 0}"
ArrayDimensions="{2, 2}" ArrayDimensions="{2, 2}"
ArrayLength="4" ArrayLength="4"
ArrayRank="2" ArrayRank="2"
GetArrayElements="{0, 1, 2, 3}"
IsReference="True" IsReference="True"
PrimitiveValue="{Exception: Value is not a primitive type}" PrimitiveValue="{Exception: Value is not a primitive type}"
Type="System.Int32[,]" /> Type="System.Int32[,]" />
</array> </array>
<lbArray> <lbArray>
<Value <Value
ArrayDimensions="{10..11, 20..21}" ArrayBaseIndicies="{10, 20}"
ArrayDimensions="{2, 2}"
ArrayLength="4" ArrayLength="4"
ArrayRank="2" ArrayRank="2"
GetArrayElements="{a, b, c, d}"
IsReference="True" IsReference="True"
PrimitiveValue="{Exception: Value is not a primitive type}" PrimitiveValue="{Exception: Value is not a primitive type}"
Type="System.Char[,]" /> Type="System.Char[,]" />

Loading…
Cancel
Save