Browse Source

Fixed bugs in OptimizeQueryExpressionVisitor.

Fixed FxCop issues in the new Profiler code.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@5026 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 17 years ago
parent
commit
7403f3c987
  1. 172
      src/AddIns/Misc/Profiler/Controller/Data/Linq/ExpressionSqlWriter.cs
  2. 9
      src/AddIns/Misc/Profiler/Controller/Data/Linq/KnownMembers.cs
  3. 18
      src/AddIns/Misc/Profiler/Controller/Data/Linq/OptimizeQueryExpressionVisitor.cs
  4. 7
      src/AddIns/Misc/Profiler/Controller/Data/Linq/QueryAst.cs
  5. 15
      src/AddIns/Misc/Profiler/Controller/Data/Linq/SQLiteQueryProvider.cs
  6. 3
      src/AddIns/Misc/Profiler/Controller/Data/Linq/SqlQueryContext.cs
  7. 16
      src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteProvider.cs
  8. 4
      src/AddIns/Misc/Profiler/Controller/Data/SQLiteCallTreeNode.cs
  9. 2
      src/AddIns/Misc/Profiler/Controller/Data/TempFileDatabase.cs
  10. 3
      src/AddIns/Misc/Profiler/Controller/Data/UnitTestRootCallTreeNode.cs
  11. 5
      src/AddIns/Misc/Profiler/Controller/Data/UnmanagedCallTreeNode.cs
  12. 2
      src/AddIns/Misc/Profiler/Controller/ExtendedRegistry.cs
  13. 2
      src/AddIns/Misc/Profiler/Controller/Profiler.Controller.csproj
  14. 8
      src/AddIns/Misc/Profiler/Controller/Queries/NodePath.cs

172
src/AddIns/Misc/Profiler/Controller/Data/Linq/ExpressionSqlWriter.cs

@ -29,7 +29,7 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
this.callTreeNodeParameter = callTreeNodeParameter; this.callTreeNodeParameter = callTreeNodeParameter;
} }
string EscapeString(string str) static string EscapeString(string str)
{ {
return "'" + str.Replace("'", "''") + "'"; return "'" + str.Replace("'", "''") + "'";
} }
@ -37,6 +37,12 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
public void Write(Expression expression) public void Write(Expression expression)
{ {
switch (expression.NodeType) { switch (expression.NodeType) {
case ExpressionType.MemberAccess:
WriteMemberAccess((MemberExpression)expression);
break;
case ExpressionType.Call:
WriteMethodCall((MethodCallExpression)expression);
break;
case ExpressionType.AndAlso: case ExpressionType.AndAlso:
case ExpressionType.OrElse: case ExpressionType.OrElse:
case ExpressionType.LessThan: case ExpressionType.LessThan:
@ -45,17 +51,15 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
case ExpressionType.GreaterThanOrEqual: case ExpressionType.GreaterThanOrEqual:
case ExpressionType.Equal: case ExpressionType.Equal:
case ExpressionType.NotEqual: case ExpressionType.NotEqual:
{ BinaryExpression binary = (BinaryExpression)expression;
BinaryExpression binary = (BinaryExpression)expression; w.Write('(');
w.Write('('); Write(binary.Left);
Write(binary.Left); w.Write(' ');
w.Write(' '); w.Write(GetOperatorSymbol(expression.NodeType));
w.Write(GetOperatorSymbol(expression.NodeType)); w.Write(' ');
w.Write(' '); Write(binary.Right);
Write(binary.Right); w.Write(')');
w.Write(')'); break;
break;
}
case ExpressionType.Constant: case ExpressionType.Constant:
var ce = (ConstantExpression)expression; var ce = (ConstantExpression)expression;
if (ce.Type == typeof(int)) if (ce.Type == typeof(int))
@ -65,71 +69,6 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
else else
throw new NotSupportedException("constant of type not supported: " + ce.Type.FullName); throw new NotSupportedException("constant of type not supported: " + ce.Type.FullName);
break; break;
case ExpressionType.MemberAccess:
{
MemberExpression me = (MemberExpression)expression;
if (me.Expression == callTreeNodeParameter) {
if (!nameSet.IsCalls)
throw new InvalidOperationException("SingleCall references are invalid here");
if (me.Member == SingleCall.IDField) {
w.Write("id");
break;
} else if (me.Member == SingleCall.DataSetIdField) {
w.Write("datasetid");
break;
} else if (me.Member == SingleCall.ParentIDField) {
w.Write("parentid");
break;
} else {
throw new NotSupportedException(me.Member.ToString());
}
} else if (IsNameMappingOnParameter(me.Expression)) {
if (me.Member == KnownMembers.NameMapping_ID) {
w.Write(nameSet.NameID);
break;
} else if (me.Member == KnownMembers.NameMapping_Name) {
w.Write("(SELECT name FROM namemapping WHERE namemapping.id = " + nameSet.NameID + ")");
break;
} else {
throw new NotSupportedException(me.Member.ToString());
}
} else {
throw new NotSupportedException(me.Member.ToString());
}
}
case ExpressionType.Call:
{
MethodCallExpression mc = (MethodCallExpression)expression;
if (mc.Method == KnownMembers.ListOfInt_Contains) {
List<int> list = (List<int>)((ConstantExpression)mc.Object).Value;
w.Write('(');
Write(mc.Arguments[0]);
w.Write(" IN (");
for (int i = 0; i < list.Count; i++) {
if (i > 0)
w.Write(',');
w.Write(list[i]);
}
w.Write("))");
break;
} else if (mc.Method == KnownMembers.Like) {
w.Write("( ");
Write(mc.Arguments[0]);
w.Write(" LIKE ");
Write(mc.Arguments[1]);
w.Write(" ESCAPE '\' )");
break;
} else if (mc.Method == KnownMembers.Glob) {
w.Write("( ");
Write(mc.Arguments[0]);
w.Write(" GLOB ");
Write(mc.Arguments[1]);
w.Write(" )");
break;
} else {
throw new NotSupportedException(mc.Method.ToString());
}
}
case ExpressionType.Not: case ExpressionType.Not:
w.Write("(NOT "); w.Write("(NOT ");
UnaryExpression unary = (UnaryExpression)expression; UnaryExpression unary = (UnaryExpression)expression;
@ -140,17 +79,7 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
throw new NotSupportedException(expression.NodeType.ToString()); throw new NotSupportedException(expression.NodeType.ToString());
} }
} }
bool IsNameMappingOnParameter(Expression expr)
{
if (expr.NodeType == ExpressionType.MemberAccess) {
var me = (MemberExpression)expr;
return me.Expression == callTreeNodeParameter && me.Member == KnownMembers.CallTreeNode_NameMapping;
} else {
return false;
}
}
static string GetOperatorSymbol(ExpressionType nodeType) static string GetOperatorSymbol(ExpressionType nodeType)
{ {
switch (nodeType) { switch (nodeType) {
@ -174,5 +103,72 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
throw new NotSupportedException(nodeType.ToString()); throw new NotSupportedException(nodeType.ToString());
} }
} }
void WriteMemberAccess(MemberExpression me)
{
if (me.Expression == callTreeNodeParameter) {
if (!nameSet.IsCalls)
throw new InvalidOperationException("SingleCall references are invalid here");
if (me.Member == SingleCall.IDField) {
w.Write("id");
} else if (me.Member == SingleCall.DataSetIdField) {
w.Write("datasetid");
} else if (me.Member == SingleCall.ParentIDField) {
w.Write("parentid");
} else {
throw new NotSupportedException(me.Member.ToString());
}
} else if (IsNameMappingOnParameter(me.Expression)) {
if (me.Member == KnownMembers.NameMapping_ID) {
w.Write(nameSet.NameID);
} else if (me.Member == KnownMembers.NameMapping_Name) {
w.Write("(SELECT name FROM namemapping WHERE namemapping.id = " + nameSet.NameID + ")");
} else {
throw new NotSupportedException(me.Member.ToString());
}
} else {
throw new NotSupportedException(me.Member.ToString());
}
}
bool IsNameMappingOnParameter(Expression expr)
{
if (expr.NodeType == ExpressionType.MemberAccess) {
var me = (MemberExpression)expr;
return me.Expression == callTreeNodeParameter && me.Member == KnownMembers.CallTreeNode_NameMapping;
} else {
return false;
}
}
void WriteMethodCall(MethodCallExpression mc)
{
if (mc.Method == KnownMembers.ListOfInt_Contains) {
List<int> list = (List<int>)((ConstantExpression)mc.Object).Value;
w.Write('(');
Write(mc.Arguments[0]);
w.Write(" IN (");
for (int i = 0; i < list.Count; i++) {
if (i > 0)
w.Write(',');
w.Write(list[i]);
}
w.Write("))");
} else if (mc.Method == KnownMembers.Like) {
w.Write("( ");
Write(mc.Arguments[0]);
w.Write(" LIKE ");
Write(mc.Arguments[1]);
w.Write(" ESCAPE '' )");
} else if (mc.Method == KnownMembers.Glob) {
w.Write("( ");
Write(mc.Arguments[0]);
w.Write(" GLOB ");
Write(mc.Arguments[1]);
w.Write(" )");
} else {
throw new NotSupportedException(mc.Method.ToString());
}
}
} }
} }

9
src/AddIns/Misc/Profiler/Controller/Data/Linq/KnownMembers.cs

@ -34,11 +34,6 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
return (MethodInfo)InfoOf(ex); return (MethodInfo)InfoOf(ex);
} }
static MethodInfo MethodOf<T1, T2, R>(Expression<Func<T1, T2, R>> ex)
{
return (MethodInfo)InfoOf(ex);
}
static MethodInfo MethodOf<R>(Expression<Func<R>> ex) static MethodInfo MethodOf<R>(Expression<Func<R>> ex)
{ {
return (MethodInfo)InfoOf(ex); return (MethodInfo)InfoOf(ex);
@ -61,11 +56,15 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
} }
#endregion #endregion
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters")]
static bool LikeImpl(string input, string pattern) static bool LikeImpl(string input, string pattern)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters")]
static bool GlobImpl(string input, string pattern) static bool GlobImpl(string input, string pattern)
{ {
throw new NotImplementedException(); throw new NotImplementedException();

18
src/AddIns/Misc/Profiler/Controller/Data/Linq/OptimizeQueryExpressionVisitor.cs

@ -16,6 +16,9 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
/// <summary> /// <summary>
/// Performs query optimizations. /// Performs query optimizations.
/// See the documentation on SQLiteQueryProvider for the list of optimizations being performed. /// See the documentation on SQLiteQueryProvider for the list of optimizations being performed.
///
/// Nodes returned from 'Visit' can be assumed to be fully optimized (they won't contain any of the patterns
/// described in the SQLiteQueryProvider optimization documentation).
/// </summary> /// </summary>
sealed class OptimizeQueryExpressionVisitor : System.Linq.Expressions.ExpressionVisitor sealed class OptimizeQueryExpressionVisitor : System.Linq.Expressions.ExpressionVisitor
{ {
@ -81,6 +84,7 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
public static bool Test(Expression ex) public static bool Test(Expression ex)
{ {
var visitor = new IsConditionSafeForMoveIntoMergeByName(); var visitor = new IsConditionSafeForMoveIntoMergeByName();
visitor.Visit(ex);
return visitor.IsSafe; return visitor.IsSafe;
} }
@ -99,8 +103,10 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
protected override Expression VisitMethodCall(MethodCallExpression node) protected override Expression VisitMethodCall(MethodCallExpression node)
{ {
if (node.Object.NodeType == ExpressionType.Parameter && !SafeMembers.Contains(node.Method)) if (node.Object != null) {
IsSafe = false; if (node.Object.NodeType == ExpressionType.Parameter && !SafeMembers.Contains(node.Method))
IsSafe = false;
}
return base.VisitMethodCall(node); return base.VisitMethodCall(node);
} }
} }
@ -146,11 +152,11 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
protected override Expression VisitMethodCall(MethodCallExpression node) protected override Expression VisitMethodCall(MethodCallExpression node)
{ {
// Optimize List<int>.Contains when the list has 0 or 1 elements // Optimize List<int>.Contains when the list has 0 or 1 elements
if (node.Method == KnownMembers.ListOfInt_Contains && node.Object.NodeType == ExpressionType.Constant && node.Arguments[0].Type == typeof(int)) { if (node.Method == KnownMembers.ListOfInt_Contains && node.Object.NodeType == ExpressionType.Constant) {
List<int> list = (List<int>)((ConstantExpression)node.Object).Value; List<int> list = (List<int>)((ConstantExpression)node.Object).Value;
if (list.Count == 0) // if (list.Count == 0)
return Expression.Constant(false); // return Expression.Constant(false); // we cannot optimize to 'false' because bool constants are not valid
else if (list.Count == 1) if (list.Count == 1)
return Expression.Equal(Visit(node.Arguments[0]), Expression.Constant(list[0])); return Expression.Equal(Visit(node.Arguments[0]), Expression.Constant(list[0]));
} }
return base.VisitMethodCall(node); return base.VisitMethodCall(node);

7
src/AddIns/Misc/Profiler/Controller/Data/Linq/QueryAst.cs

@ -8,6 +8,7 @@
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
@ -77,7 +78,7 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
/// Wraps the current SQL statement into an inner select, allowing to continue with "WHERE" queries /// Wraps the current SQL statement into an inner select, allowing to continue with "WHERE" queries
/// even after ORDER BY or LIMIT. /// even after ORDER BY or LIMIT.
/// </summary> /// </summary>
protected void WrapSqlIntoNestedStatement(StringBuilder b, SqlQueryContext context) protected static void WrapSqlIntoNestedStatement(StringBuilder b, SqlQueryContext context)
{ {
CallTreeNodeSqlNameSet oldNames = context.CurrentNameSet; CallTreeNodeSqlNameSet oldNames = context.CurrentNameSet;
CallTreeNodeSqlNameSet newNames = new CallTreeNodeSqlNameSet(context, false); CallTreeNodeSqlNameSet newNames = new CallTreeNodeSqlNameSet(context, false);
@ -274,10 +275,10 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
return resultKind; return resultKind;
} }
void BuildSqlForCondition(StringBuilder b, SqlQueryContext context, LambdaExpression condition) static void BuildSqlForCondition(StringBuilder b, SqlQueryContext context, LambdaExpression condition)
{ {
Debug.Assert(condition.Parameters.Count == 1); Debug.Assert(condition.Parameters.Count == 1);
StringWriter w = new StringWriter(); StringWriter w = new StringWriter(CultureInfo.InvariantCulture);
ExpressionSqlWriter writer = new ExpressionSqlWriter(w, context.CurrentNameSet, condition.Parameters[0]); ExpressionSqlWriter writer = new ExpressionSqlWriter(w, context.CurrentNameSet, condition.Parameters[0]);
writer.Write(condition.Body); writer.Write(condition.Body);
b.Append(w.ToString()); b.Append(w.ToString());

15
src/AddIns/Misc/Profiler/Controller/Data/Linq/SQLiteQueryProvider.cs

@ -84,8 +84,8 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
Any valid expressions (as defined in 'valid expressions in QueryAst nodes') are copied over directly. Any valid expressions (as defined in 'valid expressions in QueryAst nodes') are copied over directly.
Moreover, these expressions are be converted into valid expressions: Moreover, these expressions are be converted into valid expressions:
c.IsUserCode -> c.NameMapping.ID > 0 c.IsUserCode -> c.NameMapping.ID > 0
c.NameMapping.Name.StartsWith(constantString, StringComparison.Ordinal) -> Glob(c.NameMapping.Name, constantString); s.StartsWith(constantString, StringComparison.Ordinal) -> Glob(s, constantString + "*");
c.NameMapping.Name.StartsWith(constantString, StringComparison.OrdinalIgnoreCase) -> Like(c.NameMapping.Name, constantString); s.StartsWith(constantString, StringComparison.OrdinalIgnoreCase) -> Like(s, constantString + "%");
Optimization of QueryAst: Optimization of QueryAst:
The OptimizeQueryExpressionVisitor is performing these optimizations: The OptimizeQueryExpressionVisitor is performing these optimizations:
@ -268,7 +268,7 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
} }
} }
object GetConstantValue(Expression expr) static object GetConstantValue(Expression expr)
{ {
return ((ConstantExpression)expr).Value; return ((ConstantExpression)expr).Value;
} }
@ -363,20 +363,13 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
} }
} }
string EscapeLikeExpr(string expression, string escape) static string EscapeLikeExpr(string expression, string escape)
{ {
return expression.Replace(escape, escape + escape) return expression.Replace(escape, escape + escape)
.Replace("%", escape + "%") .Replace("%", escape + "%")
.Replace("_", escape + "_"); .Replace("_", escape + "_");
} }
string EscapeGlobExpr(string expression, string escape)
{
return expression.Replace(escape, escape + escape)
.Replace("*", escape + "*")
.Replace("?", escape + "?");
}
/// <summary> /// <summary>
/// Tests if expr is 'c.NameMapping'. /// Tests if expr is 'c.NameMapping'.
/// </summary> /// </summary>

3
src/AddIns/Misc/Profiler/Controller/Data/Linq/SqlQueryContext.cs

@ -6,6 +6,7 @@
// </file> // </file>
using System; using System;
using System.Globalization;
namespace ICSharpCode.Profiler.Controller.Data.Linq namespace ICSharpCode.Profiler.Controller.Data.Linq
{ {
@ -22,7 +23,7 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
public string GenerateUniqueVariableName() public string GenerateUniqueVariableName()
{ {
return "v" + (++uniqueVariableIndex).ToString(); return "v" + (++uniqueVariableIndex).ToString(CultureInfo.InvariantCulture);
} }
public CallTreeNodeSqlNameSet CurrentNameSet; public CallTreeNodeSqlNameSet CurrentNameSet;

16
src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteProvider.cs

@ -132,22 +132,6 @@ namespace ICSharpCode.Profiler.Controller.Data
} }
} }
double GetCpuUsage(int index) {
SQLiteCommand cmd;
using (LockAndCreateCommand(out cmd)) {
cmd.CommandText = @"SELECT cpuusage
FROM DataSets
WHERE id = " + index + ";";
using (SQLiteDataReader reader = cmd.ExecuteReader()) {
while (reader.Read())
return reader.GetDouble(0);
return 0;
}
}
}
class SQLiteDataSet : IProfilingDataSet class SQLiteDataSet : IProfilingDataSet
{ {
ProfilingDataSQLiteProvider provider; ProfilingDataSQLiteProvider provider;

4
src/AddIns/Misc/Profiler/Controller/Data/SQLiteCallTreeNode.cs

@ -171,8 +171,8 @@ namespace ICSharpCode.Profiler.Controller.Data
public override bool Equals(CallTreeNode other) public override bool Equals(CallTreeNode other)
{ {
if (other is SQLiteCallTreeNode) { SQLiteCallTreeNode node = other as SQLiteCallTreeNode;
SQLiteCallTreeNode node = other as SQLiteCallTreeNode; if (node != null) {
if (node.ids.Count != this.ids.Count) if (node.ids.Count != this.ids.Count)
return false; return false;

2
src/AddIns/Misc/Profiler/Controller/Data/TempFileDatabase.cs

@ -21,7 +21,7 @@ namespace ICSharpCode.Profiler.Controller.Data
/// Stores recorded profiling data in a temporary file, which is deleted when the file database is closed. /// Stores recorded profiling data in a temporary file, which is deleted when the file database is closed.
/// Instance members of this class are not thread-safe. /// Instance members of this class are not thread-safe.
/// </summary> /// </summary>
public class TempFileDatabase : IDisposable public sealed class TempFileDatabase : IDisposable
{ {
FileStream file; FileStream file;
int processorFrequency; int processorFrequency;

3
src/AddIns/Misc/Profiler/Controller/Data/UnitTestRootCallTreeNode.cs

@ -83,7 +83,8 @@ namespace ICSharpCode.Profiler.Controller.Data
/// <inheritdoc/> /// <inheritdoc/>
public override bool Equals(CallTreeNode other) public override bool Equals(CallTreeNode other)
{ {
return (other is UnitTestRootCallTreeNode) && (other as UnitTestRootCallTreeNode).unitTests.SequenceEqual(unitTests); UnitTestRootCallTreeNode node = other as UnitTestRootCallTreeNode;
return node != null && node.unitTests.SequenceEqual(unitTests);
} }
/// <inheritdoc/> /// <inheritdoc/>

5
src/AddIns/Misc/Profiler/Controller/Data/UnmanagedCallTreeNode.cs

@ -119,9 +119,8 @@ namespace ICSharpCode.Profiler.Controller.Data
public override bool Equals(CallTreeNode other) public override bool Equals(CallTreeNode other)
{ {
if (other is UnmanagedCallTreeNode32) { UnmanagedCallTreeNode32 node = other as UnmanagedCallTreeNode32;
UnmanagedCallTreeNode32 node = other as UnmanagedCallTreeNode32; if (node != null) {
return node.data == this.data; return node.data == this.data;
} }

2
src/AddIns/Misc/Profiler/Controller/ExtendedRegistry.cs

@ -57,7 +57,7 @@ namespace ICSharpCode.Profiler.Controller
[DllImport("kernel32", SetLastError = true)] [DllImport("kernel32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool IsWow64Process(IntPtr hProcess, out bool wow64Process); internal static extern bool IsWow64Process(IntPtr hProcess, [MarshalAs(UnmanagedType.Bool)] out bool wow64Process);
} }
[Flags] [Flags]

2
src/AddIns/Misc/Profiler/Controller/Profiler.Controller.csproj

@ -55,7 +55,6 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<CodeAnalysisRuleSet>Migrated rules for Profiler.Controller.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>Migrated rules for Profiler.Controller.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRules />
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>PdbOnly</DebugType> <DebugType>PdbOnly</DebugType>
@ -65,7 +64,6 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<DebugSymbols>false</DebugSymbols> <DebugSymbols>false</DebugSymbols>
<CodeAnalysisRuleSet>Migrated rules for Profiler.Controller.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>Migrated rules for Profiler.Controller.ruleset</CodeAnalysisRuleSet>
<CodeAnalysisRules />
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow> <CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>

8
src/AddIns/Misc/Profiler/Controller/Queries/NodePath.cs

@ -14,11 +14,13 @@ namespace ICSharpCode.Profiler.Controller.Queries
/// <summary> /// <summary>
/// Describes an absolute path to an CallTreeNode. /// Describes an absolute path to an CallTreeNode.
/// </summary> /// </summary>
public class NodePath : IEquatable<NodePath>, IEnumerable<int> public sealed class NodePath : IEquatable<NodePath>, IEnumerable<int>
{ {
/// <summary> /// <summary>
/// Describes an empty NodePath. /// Describes an empty NodePath.
/// </summary> /// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes",
Justification = "NodePath is immutable")]
public static readonly NodePath Empty = new NodePath(0, null); public static readonly NodePath Empty = new NodePath(0, null);
int lastId; int lastId;
@ -47,9 +49,9 @@ namespace ICSharpCode.Profiler.Controller.Queries
/// <summary> /// <summary>
/// Creates a new NodePath from this with a new Name Id segment attached. /// Creates a new NodePath from this with a new Name Id segment attached.
/// </summary> /// </summary>
public NodePath Append(int newVal) public NodePath Append(int newValue)
{ {
return new NodePath(newVal, this); return new NodePath(newValue, this);
} }
/// <summary> /// <summary>

Loading…
Cancel
Save