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 16 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 @@ -29,7 +29,7 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
this.callTreeNodeParameter = callTreeNodeParameter;
}
string EscapeString(string str)
static string EscapeString(string str)
{
return "'" + str.Replace("'", "''") + "'";
}
@ -37,6 +37,12 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -37,6 +37,12 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
public void Write(Expression expression)
{
switch (expression.NodeType) {
case ExpressionType.MemberAccess:
WriteMemberAccess((MemberExpression)expression);
break;
case ExpressionType.Call:
WriteMethodCall((MethodCallExpression)expression);
break;
case ExpressionType.AndAlso:
case ExpressionType.OrElse:
case ExpressionType.LessThan:
@ -45,17 +51,15 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -45,17 +51,15 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
case ExpressionType.GreaterThanOrEqual:
case ExpressionType.Equal:
case ExpressionType.NotEqual:
{
BinaryExpression binary = (BinaryExpression)expression;
w.Write('(');
Write(binary.Left);
w.Write(' ');
w.Write(GetOperatorSymbol(expression.NodeType));
w.Write(' ');
Write(binary.Right);
w.Write(')');
break;
}
BinaryExpression binary = (BinaryExpression)expression;
w.Write('(');
Write(binary.Left);
w.Write(' ');
w.Write(GetOperatorSymbol(expression.NodeType));
w.Write(' ');
Write(binary.Right);
w.Write(')');
break;
case ExpressionType.Constant:
var ce = (ConstantExpression)expression;
if (ce.Type == typeof(int))
@ -65,71 +69,6 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -65,71 +69,6 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
else
throw new NotSupportedException("constant of type not supported: " + ce.Type.FullName);
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:
w.Write("(NOT ");
UnaryExpression unary = (UnaryExpression)expression;
@ -140,17 +79,7 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -140,17 +79,7 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
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)
{
switch (nodeType) {
@ -174,5 +103,72 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -174,5 +103,72 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
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 @@ -34,11 +34,6 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
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)
{
return (MethodInfo)InfoOf(ex);
@ -61,11 +56,15 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -61,11 +56,15 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
}
#endregion
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters")]
static bool LikeImpl(string input, string pattern)
{
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)
{
throw new NotImplementedException();

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

@ -16,6 +16,9 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -16,6 +16,9 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
/// <summary>
/// Performs query optimizations.
/// 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>
sealed class OptimizeQueryExpressionVisitor : System.Linq.Expressions.ExpressionVisitor
{
@ -81,6 +84,7 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -81,6 +84,7 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
public static bool Test(Expression ex)
{
var visitor = new IsConditionSafeForMoveIntoMergeByName();
visitor.Visit(ex);
return visitor.IsSafe;
}
@ -99,8 +103,10 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -99,8 +103,10 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Object.NodeType == ExpressionType.Parameter && !SafeMembers.Contains(node.Method))
IsSafe = false;
if (node.Object != null) {
if (node.Object.NodeType == ExpressionType.Parameter && !SafeMembers.Contains(node.Method))
IsSafe = false;
}
return base.VisitMethodCall(node);
}
}
@ -146,11 +152,11 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -146,11 +152,11 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
protected override Expression VisitMethodCall(MethodCallExpression node)
{
// 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;
if (list.Count == 0)
return Expression.Constant(false);
else if (list.Count == 1)
// if (list.Count == 0)
// return Expression.Constant(false); // we cannot optimize to 'false' because bool constants are not valid
if (list.Count == 1)
return Expression.Equal(Visit(node.Arguments[0]), Expression.Constant(list[0]));
}
return base.VisitMethodCall(node);

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

@ -8,6 +8,7 @@ @@ -8,6 +8,7 @@
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
@ -77,7 +78,7 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -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
/// even after ORDER BY or LIMIT.
/// </summary>
protected void WrapSqlIntoNestedStatement(StringBuilder b, SqlQueryContext context)
protected static void WrapSqlIntoNestedStatement(StringBuilder b, SqlQueryContext context)
{
CallTreeNodeSqlNameSet oldNames = context.CurrentNameSet;
CallTreeNodeSqlNameSet newNames = new CallTreeNodeSqlNameSet(context, false);
@ -274,10 +275,10 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -274,10 +275,10 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
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);
StringWriter w = new StringWriter();
StringWriter w = new StringWriter(CultureInfo.InvariantCulture);
ExpressionSqlWriter writer = new ExpressionSqlWriter(w, context.CurrentNameSet, condition.Parameters[0]);
writer.Write(condition.Body);
b.Append(w.ToString());

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

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

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

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

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

@ -132,22 +132,6 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -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
{
ProfilingDataSQLiteProvider provider;

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

@ -171,8 +171,8 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -171,8 +171,8 @@ namespace ICSharpCode.Profiler.Controller.Data
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)
return false;

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

@ -21,7 +21,7 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -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.
/// Instance members of this class are not thread-safe.
/// </summary>
public class TempFileDatabase : IDisposable
public sealed class TempFileDatabase : IDisposable
{
FileStream file;
int processorFrequency;

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

@ -83,7 +83,8 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -83,7 +83,8 @@ namespace ICSharpCode.Profiler.Controller.Data
/// <inheritdoc/>
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/>

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

@ -119,9 +119,8 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -119,9 +119,8 @@ namespace ICSharpCode.Profiler.Controller.Data
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;
}

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

@ -57,7 +57,7 @@ namespace ICSharpCode.Profiler.Controller @@ -57,7 +57,7 @@ namespace ICSharpCode.Profiler.Controller
[DllImport("kernel32", SetLastError = true)]
[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]

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

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

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

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

Loading…
Cancel
Save