Skip to content

Commit 78869e1

Browse files
committed
ported csharp linq tests to vb and made some changes to accomodate vb idiosyncracies in expression tree generation.
1 parent 3fac6fe commit 78869e1

19 files changed

+7667
-24
lines changed

CSharpDriver-2010.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Release Notes", "Release No
3939
Release Notes\Release Notes v1.4.md = Release Notes\Release Notes v1.4.md
4040
EndProjectSection
4141
EndProject
42+
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "DriverUnitTestsVB", "DriverUnitTestsVB\DriverUnitTestsVB.vbproj", "{19ED9AD5-A2CA-4F1B-9BE4-96450382F404}"
43+
EndProject
4244
Global
4345
GlobalSection(SolutionConfigurationPlatforms) = preSolution
4446
Debug|Any CPU = Debug|Any CPU
@@ -61,6 +63,10 @@ Global
6163
{FBBF0D71-107F-49C4-8858-B3A4DC24AA33}.Debug|Any CPU.Build.0 = Debug|Any CPU
6264
{FBBF0D71-107F-49C4-8858-B3A4DC24AA33}.Release|Any CPU.ActiveCfg = Release|Any CPU
6365
{FBBF0D71-107F-49C4-8858-B3A4DC24AA33}.Release|Any CPU.Build.0 = Release|Any CPU
66+
{19ED9AD5-A2CA-4F1B-9BE4-96450382F404}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
67+
{19ED9AD5-A2CA-4F1B-9BE4-96450382F404}.Debug|Any CPU.Build.0 = Debug|Any CPU
68+
{19ED9AD5-A2CA-4F1B-9BE4-96450382F404}.Release|Any CPU.ActiveCfg = Release|Any CPU
69+
{19ED9AD5-A2CA-4F1B-9BE4-96450382F404}.Release|Any CPU.Build.0 = Release|Any CPU
6470
EndGlobalSection
6571
GlobalSection(SolutionProperties) = preSolution
6672
HideSolutionNode = FALSE

Driver/Driver.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@
182182
<Compile Include="Internal\MongoReplyMessage.cs" />
183183
<Compile Include="Internal\MongoUpdateMessage.cs" />
184184
<Compile Include="Internal\ReplicaSetConnector.cs" />
185+
<Compile Include="Linq\Expressions\ExpressionNormalizer.cs" />
185186
<Compile Include="Linq\Expressions\ExpressionFormatter.cs" />
186187
<Compile Include="Linq\Expressions\ExpressionParameterFinder.cs" />
187188
<Compile Include="Linq\Expressions\ExpressionParameterReplacer.cs" />
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/* Copyright 2010-2012 10gen Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System;
17+
using System.Collections.Generic;
18+
using System.Linq;
19+
using System.Linq.Expressions;
20+
using System.Text;
21+
22+
namespace MongoDB.Driver.Linq
23+
{
24+
/// <summary>
25+
/// A class that normalizes C# and VB expression trees.
26+
/// </summary>
27+
public class ExpressionNormalizer : ExpressionVisitor
28+
{
29+
// private fields
30+
private Expression _expression;
31+
32+
// constructors
33+
/// <summary>
34+
/// Initializes a new instance of the ExpressionNormalizer class.
35+
/// </summary>
36+
/// <param name="expression">The expression to be evaluated.</param>
37+
private ExpressionNormalizer(Expression expression)
38+
{
39+
_expression = expression;
40+
}
41+
42+
// public methods
43+
/// <summary>
44+
/// Normalizes C# and VB expression trees.
45+
/// </summary>
46+
/// <param name="node">The expression to normalize.</param>
47+
/// <returns>The normalized expression.</returns>
48+
public static Expression Normalize(Expression node)
49+
{
50+
var normalizer = new ExpressionNormalizer(node);
51+
return normalizer.Visit(node);
52+
}
53+
54+
protected override Expression VisitBinary(BinaryExpression node)
55+
{
56+
node = FlipBinaryExpression(node);
57+
58+
Expression result = null;
59+
if (node.Left.NodeType == ExpressionType.Call && node.Right.NodeType == ExpressionType.Constant)
60+
{
61+
var mex = (MethodCallExpression)node.Left;
62+
var constant = (ConstantExpression)node.Right;
63+
if (mex.Method.DeclaringType.FullName == "Microsoft.VisualBasic.CompilerServices.Operators")
64+
{
65+
//VB creates expression trees with "special" operators
66+
result = VisitVBCompilerServicesOperators(mex, node.NodeType, constant);
67+
}
68+
else if (mex.Method.DeclaringType == typeof(string) && mex.Method.Name == "get_Chars" && constant.Type == typeof(char))
69+
{
70+
//VB creates string index expressions using character comparison whereas C# uses ascii value comparison
71+
//So, we make VB's string index comparison look like C#.
72+
result = Expression.MakeBinary(
73+
node.NodeType,
74+
Expression.Convert(mex, typeof(int)),
75+
Expression.Constant(Convert.ToInt32((char)constant.Value)));
76+
}
77+
}
78+
79+
if (result != null)
80+
{
81+
return result;
82+
}
83+
84+
return base.VisitBinary(node);
85+
}
86+
87+
protected override Expression VisitUnary(UnaryExpression node)
88+
{
89+
if (node.NodeType == ExpressionType.Convert)
90+
{
91+
if (node.Type.IsAssignableFrom(node.Operand.Type))
92+
{
93+
return Visit(node.Operand);
94+
}
95+
}
96+
97+
return base.VisitUnary(node);
98+
}
99+
100+
private BinaryExpression FlipBinaryExpression(BinaryExpression node)
101+
{
102+
var left = node.Left;
103+
var right = node.Right;
104+
var operatorType = node.NodeType;
105+
if (left.NodeType == ExpressionType.Constant)
106+
{
107+
right = node.Left as ConstantExpression;
108+
left = node.Right;
109+
// if the constant was on the left some operators need to be flipped
110+
switch (operatorType)
111+
{
112+
case ExpressionType.LessThan: operatorType = ExpressionType.GreaterThan; break;
113+
case ExpressionType.LessThanOrEqual: operatorType = ExpressionType.GreaterThanOrEqual; break;
114+
case ExpressionType.GreaterThan: operatorType = ExpressionType.LessThan; break;
115+
case ExpressionType.GreaterThanOrEqual: operatorType = ExpressionType.LessThanOrEqual; break;
116+
}
117+
}
118+
119+
if (left != node.Left || right != node.Right || operatorType != node.NodeType)
120+
{
121+
return Expression.MakeBinary(operatorType, left, right);
122+
}
123+
124+
return node;
125+
}
126+
127+
private Expression VisitVBCompilerServicesOperators(MethodCallExpression mex, ExpressionType expressionType, ConstantExpression constant)
128+
{
129+
if (mex.Method.Name == "CompareString" && constant.Type == typeof(Int32))
130+
{
131+
return VisitVBCompilerServicesOperatorsCompareString(mex, expressionType, (int)constant.Value);
132+
}
133+
134+
return null;
135+
}
136+
137+
private Expression VisitVBCompilerServicesOperatorsCompareString(MethodCallExpression mex, ExpressionType expressionType, int comparisonValue)
138+
{
139+
if (comparisonValue == 0)
140+
{
141+
return Expression.MakeBinary(
142+
expressionType,
143+
mex.Arguments[0],
144+
mex.Arguments[1]);
145+
}
146+
147+
return null;
148+
}
149+
}
150+
}

Driver/Linq/Translators/MongoQueryTranslator.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public static TranslatedQuery Translate(IQueryable query)
4646
public static TranslatedQuery Translate(MongoQueryProvider provider, Expression expression)
4747
{
4848
expression = PartialEvaluator.Evaluate(expression, provider.CanBeEvaluatedLocally);
49+
expression = ExpressionNormalizer.Normalize(expression);
4950
// assume for now it's a SelectQuery
5051
var documentType = GetDocumentType(expression);
5152
var selectQuery = new SelectQuery(provider.Collection, documentType);

Driver/Linq/Translators/SelectQuery.cs

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -344,23 +344,9 @@ private IMongoQuery BuildBooleanQuery(Expression expression)
344344

345345
private IMongoQuery BuildComparisonQuery(BinaryExpression binaryExpression)
346346
{
347-
// the constant could be on either side
348347
var variableExpression = binaryExpression.Left;
349348
var constantExpression = binaryExpression.Right as ConstantExpression;
350349
var operatorType = binaryExpression.NodeType;
351-
if (constantExpression == null)
352-
{
353-
constantExpression = binaryExpression.Left as ConstantExpression;
354-
variableExpression = binaryExpression.Right;
355-
// if the constant was on the left some operators need to be flipped
356-
switch (operatorType)
357-
{
358-
case ExpressionType.LessThan: operatorType = ExpressionType.GreaterThan; break;
359-
case ExpressionType.LessThanOrEqual: operatorType = ExpressionType.GreaterThanOrEqual; break;
360-
case ExpressionType.GreaterThan: operatorType = ExpressionType.LessThan; break;
361-
case ExpressionType.GreaterThanOrEqual: operatorType = ExpressionType.LessThanOrEqual; break;
362-
}
363-
}
364350

365351
if (constantExpression == null)
366352
{
@@ -412,7 +398,7 @@ private IMongoQuery BuildComparisonQuery(Expression variableExpression, Expressi
412398
var value = constantExpression.Value;
413399

414400
var unaryExpression = variableExpression as UnaryExpression;
415-
if (unaryExpression != null && unaryExpression.NodeType == ExpressionType.Convert && unaryExpression.Operand.Type.IsEnum)
401+
if (unaryExpression != null && (unaryExpression.NodeType == ExpressionType.Convert || unaryExpression.NodeType == ExpressionType.ConvertChecked) && unaryExpression.Operand.Type.IsEnum)
416402
{
417403
var enumType = unaryExpression.Operand.Type;
418404
if (unaryExpression.Type == Enum.GetUnderlyingType(enumType))

DriverUnitTests/Linq/SelectQueryTests.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2264,7 +2264,7 @@ public void TestWhereALengthEquals3Reversed()
22642264
Assert.AreSame(typeof(C), translatedQuery.DocumentType);
22652265

22662266
var selectQuery = (SelectQuery)translatedQuery;
2267-
Assert.AreEqual("(C c) => (3 == c.A.Length)", ExpressionFormatter.ToString(selectQuery.Where));
2267+
Assert.AreEqual("(C c) => (c.A.Length == 3)", ExpressionFormatter.ToString(selectQuery.Where));
22682268
Assert.IsNull(selectQuery.OrderBy);
22692269
Assert.IsNull(selectQuery.Projection);
22702270
Assert.IsNull(selectQuery.Skip);
@@ -3322,7 +3322,7 @@ public void TestWhereEEqualsAReversed()
33223322
Assert.AreSame(typeof(C), translatedQuery.DocumentType);
33233323

33243324
var selectQuery = (SelectQuery)translatedQuery;
3325-
Assert.AreEqual("(C c) => (1 == (Int32)c.E)", ExpressionFormatter.ToString(selectQuery.Where));
3325+
Assert.AreEqual("(C c) => ((Int32)c.E == 1)", ExpressionFormatter.ToString(selectQuery.Where));
33263326
Assert.IsNull(selectQuery.OrderBy);
33273327
Assert.IsNull(selectQuery.Projection);
33283328
Assert.IsNull(selectQuery.Skip);
@@ -3690,7 +3690,7 @@ public void TestWhereLCountMethodEquals3Reversed()
36903690
Assert.AreSame(typeof(C), translatedQuery.DocumentType);
36913691

36923692
var selectQuery = (SelectQuery)translatedQuery;
3693-
Assert.AreEqual("(C c) => (3 == Enumerable.Count<Int32>(c.L))", ExpressionFormatter.ToString(selectQuery.Where));
3693+
Assert.AreEqual("(C c) => (Enumerable.Count<Int32>(c.L) == 3)", ExpressionFormatter.ToString(selectQuery.Where));
36943694
Assert.IsNull(selectQuery.OrderBy);
36953695
Assert.IsNull(selectQuery.Projection);
36963696
Assert.IsNull(selectQuery.Skip);
@@ -3759,7 +3759,7 @@ public void TestWhereLCountPropertyEquals3Reversed()
37593759
Assert.AreSame(typeof(C), translatedQuery.DocumentType);
37603760

37613761
var selectQuery = (SelectQuery)translatedQuery;
3762-
Assert.AreEqual("(C c) => (3 == c.L.Count)", ExpressionFormatter.ToString(selectQuery.Where));
3762+
Assert.AreEqual("(C c) => (c.L.Count == 3)", ExpressionFormatter.ToString(selectQuery.Where));
37633763
Assert.IsNull(selectQuery.OrderBy);
37643764
Assert.IsNull(selectQuery.Projection);
37653765
Assert.IsNull(selectQuery.Skip);
@@ -6006,7 +6006,7 @@ where 1 < c.X
60066006
Assert.AreSame(typeof(C), translatedQuery.DocumentType);
60076007

60086008
var selectQuery = (SelectQuery)translatedQuery;
6009-
Assert.AreEqual("(C c) => (1 < c.X)", ExpressionFormatter.ToString(selectQuery.Where));
6009+
Assert.AreEqual("(C c) => (c.X > 1)", ExpressionFormatter.ToString(selectQuery.Where));
60106010
Assert.IsNull(selectQuery.OrderBy);
60116011
Assert.IsNull(selectQuery.Projection);
60126012
Assert.IsNull(selectQuery.Skip);
@@ -6075,7 +6075,7 @@ public void TestWhereXGreaterThanOrEquals1Reversed()
60756075
Assert.AreSame(typeof(C), translatedQuery.DocumentType);
60766076

60776077
var selectQuery = (SelectQuery)translatedQuery;
6078-
Assert.AreEqual("(C c) => (1 <= c.X)", ExpressionFormatter.ToString(selectQuery.Where));
6078+
Assert.AreEqual("(C c) => (c.X >= 1)", ExpressionFormatter.ToString(selectQuery.Where));
60796079
Assert.IsNull(selectQuery.OrderBy);
60806080
Assert.IsNull(selectQuery.Projection);
60816081
Assert.IsNull(selectQuery.Skip);
@@ -6236,7 +6236,7 @@ where 1 > c.X
62366236
Assert.AreSame(typeof(C), translatedQuery.DocumentType);
62376237

62386238
var selectQuery = (SelectQuery)translatedQuery;
6239-
Assert.AreEqual("(C c) => (1 > c.X)", ExpressionFormatter.ToString(selectQuery.Where));
6239+
Assert.AreEqual("(C c) => (c.X < 1)", ExpressionFormatter.ToString(selectQuery.Where));
62406240
Assert.IsNull(selectQuery.OrderBy);
62416241
Assert.IsNull(selectQuery.Projection);
62426242
Assert.IsNull(selectQuery.Skip);
@@ -6305,7 +6305,7 @@ public void TestWhereXLessThanOrEquals1Reversed()
63056305
Assert.AreSame(typeof(C), translatedQuery.DocumentType);
63066306

63076307
var selectQuery = (SelectQuery)translatedQuery;
6308-
Assert.AreEqual("(C c) => (1 >= c.X)", ExpressionFormatter.ToString(selectQuery.Where));
6308+
Assert.AreEqual("(C c) => (c.X <= 1)", ExpressionFormatter.ToString(selectQuery.Where));
63096309
Assert.IsNull(selectQuery.OrderBy);
63106310
Assert.IsNull(selectQuery.Projection);
63116311
Assert.IsNull(selectQuery.Skip);
@@ -6452,7 +6452,7 @@ public void TestWhereXModTwoEquals1Reversed()
64526452
Assert.AreSame(typeof(C), translatedQuery.DocumentType);
64536453

64546454
var selectQuery = (SelectQuery)translatedQuery;
6455-
Assert.AreEqual("(C c) => (1 == (c.X % 2))", ExpressionFormatter.ToString(selectQuery.Where));
6455+
Assert.AreEqual("(C c) => ((c.X % 2) == 1)", ExpressionFormatter.ToString(selectQuery.Where));
64566456
Assert.IsNull(selectQuery.OrderBy);
64576457
Assert.IsNull(selectQuery.Projection);
64586458
Assert.IsNull(selectQuery.Skip);

0 commit comments

Comments
 (0)