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. 123
      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. 75
      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 @@ -308,7 +308,7 @@ namespace Debugger.AddIn
Value Visit(ArrayAccessResolveResult result)
{
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)
@ -390,10 +390,11 @@ namespace Debugger.AddIn @@ -390,10 +390,11 @@ namespace Debugger.AddIn
sb.Append(val.Type.Name);
sb.Append(" {");
bool first = true;
foreach(Value item in val.GetArrayElements()) {
int size = val.ArrayLength;
for (int i = 0; i < size; i++) {
if (!first) sb.Append(", ");
first = false;
sb.Append(FormatValue(evalThread, item));
sb.Append(FormatValue(evalThread, val.GetElementAtPosition(i)));
}
sb.Append("}");
return sb.ToString();

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

@ -109,9 +109,10 @@ namespace Debugger.AddIn.TreeModel @@ -109,9 +109,10 @@ namespace Debugger.AddIn.TreeModel
} else if (val.Type.IsPrimitiveType() || val.Type.IsKnownType(KnownTypeCode.String)) { // Must be before IsClass
this.GetChildren = null;
} else if (val.Type.Kind == TypeKind.Array) { // Must be before IsClass
var dims = val.ArrayDimensions; // Eval now
if (dims.TotalElementCount > 0) {
this.GetChildren = () => GetArrayChildren(dims, dims);
var dimBase = val.ArrayBaseIndicies; // Eval now
var dimSize = val.ArrayDimensions; // Eval now
if (val.ArrayLength > 0) {
this.GetChildren = () => GetArrayChildren(dimBase, dimSize);
}
} else if (val.Type.Kind == TypeKind.Class || val.Type.Kind == TypeKind.Struct) {
if (val.Type.IsKnownType(typeof(List<>))) {
@ -373,83 +374,87 @@ namespace Debugger.AddIn.TreeModel @@ -373,83 +374,87 @@ namespace Debugger.AddIn.TreeModel
}
}
TreeNode GetArraySubsetNode(ArrayDimensions bounds, ArrayDimensions originalBounds)
{
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)
IEnumerable<TreeNode> GetArrayChildren(uint[] dimBase, uint[] dimSize)
{
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 break;
}
// The whole array is small - just add all elements as childs
if (bounds.TotalElementCount <= MaxElementCount * 2) {
foreach(int[] indices in bounds.Indices) {
StringBuilder sb = new StringBuilder(indices.Length * 4);
sb.Append("[");
// The array is small - just add all elements as children
StringBuilder sb = new StringBuilder();
if (totalSize <= MaxElementCount) {
uint[] indices = (uint[])dimBase.Clone();
while(indices[0] < dimBase[0] + dimSize[0]) {
// Make element name
sb.Clear();
sb.Append('[');
bool isFirst = true;
foreach(int index in indices) {
if (!isFirst) sb.Append(", ");
sb.Append(index.ToString());
sb.Append(index);
isFirst = false;
}
sb.Append("]");
int[] indicesCopy = indices;
sb.Append(']');
// 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));
// 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;
}
// 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;
}
// Split the array into smaller subsets
int splitIndex = Array.FindIndex(dimSize, s => (s > 1));
uint groupSize = 1;
while (dimSize[splitIndex] > groupSize * MaxElementCount) {
groupSize *= MaxElementCount;
}
ArrayDimension splitDim = bounds[splitDimensionIndex];
for(uint i = dimBase[splitIndex]; i < dimBase[splitIndex] + dimSize[splitIndex]; i += groupSize) {
// 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);
// Make the subset name
sb.Clear();
sb.Append('[');
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);
}
} else if (j > splitIndex) {
sb.Append('*');
}
isFirst = false;
}
sb.Append(']');
// 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 GetArraySubsetNode(new ArrayDimensions(newDims), originalBounds);
yield return new TreeNode(sb.ToString(), () => GetArrayChildren(newDimBase, newDimSize));
}
yield break;
}
}
}

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

@ -1,70 +0,0 @@ @@ -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 @@ @@ -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 @@ @@ -64,8 +64,6 @@
<Compile Include="TypeSystemExtensions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="AppDomain.cs" />
<Compile Include="ArrayDimension.cs" />
<Compile Include="ArrayDimensions.cs" />
<Compile Include="Breakpoint.cs" />
<Compile Include="DebuggerException.cs" />
<Compile Include="Eval.cs" />

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

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

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

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

Loading…
Cancel
Save