Skip to content

Commit c56bd0c

Browse files
committed
Zero tests failing! But more refactoring to do.
1 parent cadbca5 commit c56bd0c

File tree

7 files changed

+243
-37
lines changed

7 files changed

+243
-37
lines changed

src/MongoDB.Driver/Linq/Linq3Implementation/KnownSerializerFinders/KnownSerializerFinderHelperMethods.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,26 @@ private bool AnyAreNotKnown(IEnumerable<Expression> nodes)
5454
return nodes.Any(IsNotKnown);
5555
}
5656

57+
private bool AnyIsKnown(IEnumerable<Expression> nodes, out IBsonSerializer knownSerializer)
58+
{
59+
foreach (var node in nodes)
60+
{
61+
if (IsKnown(node, out var nodeSerializer))
62+
{
63+
knownSerializer = nodeSerializer;
64+
return true;
65+
}
66+
}
67+
68+
knownSerializer = null;
69+
return false;
70+
}
71+
72+
private bool AnyIsNotKnown(IEnumerable<Expression> nodes)
73+
{
74+
return nodes.Any(IsNotKnown);
75+
}
76+
5777
private bool CanDeduceSerializer(Expression node1, Expression node2, out Expression unknownNode, out IBsonSerializer knownSerializer)
5878
{
5979
if (IsKnown(node1, out var node1Serializer) &&

src/MongoDB.Driver/Linq/Linq3Implementation/KnownSerializerFinders/KnownSerializerFinderVisitBinary.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,16 @@ protected override Expression VisitBinary(BinaryExpression node)
5454
if (IsNotKnown(node) &&
5555
IsKnown(leftExpression, out var leftSerializer))
5656
{
57-
if (leftSerializer is not IBsonSerializer arraySerializer)
57+
IBsonSerializer itemSerializer;
58+
if (leftSerializer is IFixedSizeArraySerializer fixedSizeArraySerializer)
5859
{
59-
throw new ExpressionNotSupportedException(node, because: $"serializer type {leftSerializer.GetType()} does not implement IBsonArraySerializer");
60+
var index = rightExpression.GetConstantValue<int>(node);
61+
itemSerializer = fixedSizeArraySerializer.GetItemSerializer(index);
62+
}
63+
else
64+
{
65+
itemSerializer = leftSerializer.GetItemSerializer();
6066
}
61-
62-
var itemSerializer = ArraySerializerHelper.GetItemSerializer(arraySerializer);
6367

6468
// expr[index] => node: itemSerializer
6569
AddKnownSerializer(node, itemSerializer);

src/MongoDB.Driver/Linq/Linq3Implementation/KnownSerializerFinders/KnownSerializerFinderVisitNewArray.cs

Lines changed: 72 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515

1616
using System;
17+
using System.Collections.Generic;
1718
using System.Linq;
1819
using System.Linq.Expressions;
1920
using MongoDB.Bson.Serialization;
@@ -54,35 +55,89 @@ void DeduceNewArrayBoundsSerializers()
5455

5556
void DeduceNewArrayInitSerializers()
5657
{
57-
if (node.Expressions.Any(IsNotKnown) && IsKnown(node, out var arraySerializer))
58-
{
59-
var itemSerializer = arraySerializer.GetItemSerializer();
58+
var itemExpressions = node.Expressions;
6059

61-
foreach (var valueExpression in node.Expressions)
60+
if (AnyIsNotKnown(itemExpressions) && IsKnown(node, out var arraySerializer))
61+
{
62+
if (arraySerializer is IFixedSizeArraySerializer fixedSizeArraySerializer)
63+
{
64+
for (var i = 0; i < itemExpressions.Count; i++)
65+
{
66+
var itemExpression = itemExpressions[i];
67+
if (IsNotKnown(itemExpression))
68+
{
69+
var itemSerializer = fixedSizeArraySerializer.GetItemSerializer(i);
70+
AddKnownSerializer(itemExpression, itemSerializer);
71+
}
72+
}
73+
}
74+
else
6275
{
63-
DeduceSerializer(valueExpression, itemSerializer);
76+
var itemSerializer = arraySerializer.GetItemSerializer();
77+
foreach (var itemExpression in itemExpressions)
78+
{
79+
if (IsNotKnown(itemExpression))
80+
{
81+
AddKnownSerializer(itemExpression, itemSerializer);
82+
}
83+
}
6484
}
6585
}
6686

67-
if (IsNotKnown(node))
87+
if (AnyIsNotKnown(itemExpressions) && AnyIsKnown(itemExpressions, out var knownItemSerializer))
6888
{
69-
var itemType = node.Type.GetElementType();
70-
IBsonSerializer itemSerializer = null;
71-
72-
if (node.Expressions.Count == 0)
89+
var firstItemType = itemExpressions.First().Type;
90+
if (itemExpressions.All(e => e.Type == firstItemType))
7391
{
74-
itemSerializer = BsonSerializer.LookupSerializer(itemType); // TODO: don't use static registry
92+
foreach (var itemExpression in itemExpressions)
93+
{
94+
if (IsNotKnown(itemExpression))
95+
{
96+
AddKnownSerializer(itemExpression, knownItemSerializer);
97+
}
98+
}
7599
}
76-
else if (node.Expressions.Any(e => IsKnown(e, out itemSerializer)))
100+
}
101+
102+
if (IsNotKnown(node))
103+
{
104+
if (AllAreKnown(itemExpressions, out var itemSerializers))
77105
{
78-
// itemSerializer has been assigned a value by IsKnown
106+
if (AllItemSerializersAreEqual(itemSerializers, out var itemSerializer))
107+
{
108+
arraySerializer = ArraySerializer.Create(itemSerializer);
109+
}
110+
else
111+
{
112+
var itemType = node.Type.GetElementType();
113+
arraySerializer = FixedSizeArraySerializer.Create(itemType, itemSerializers);
114+
}
115+
AddKnownSerializer(node, arraySerializer);
79116
}
117+
}
80118

81-
if (itemSerializer != null)
119+
static bool AllItemSerializersAreEqual(IReadOnlyList<IBsonSerializer> itemSerializers, out IBsonSerializer itemSerializer)
120+
{
121+
switch (itemSerializers.Count)
82122
{
83-
var arraySerializerType = typeof(ArraySerializer<>).MakeGenericType(itemType);
84-
arraySerializer = (IBsonSerializer)Activator.CreateInstance(arraySerializerType, itemSerializer);
85-
AddKnownSerializer(node, arraySerializer);
123+
case 0:
124+
itemSerializer = null;
125+
return false;
126+
case 1:
127+
itemSerializer = itemSerializers[0];
128+
return true;
129+
default:
130+
var firstItemSerializer = itemSerializers[0];
131+
if (itemSerializers.Skip(1).All(s => s.Equals(firstItemSerializer)))
132+
{
133+
itemSerializer = firstItemSerializer;
134+
return true;
135+
}
136+
else
137+
{
138+
itemSerializer = null;
139+
return false;
140+
}
86141
}
87142
}
88143
}

src/MongoDB.Driver/Linq/Linq3Implementation/Misc/IBsonSerializerExtensions.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@
1313
* limitations under the License.
1414
*/
1515

16+
using System.Linq.Expressions;
1617
using MongoDB.Bson;
1718
using MongoDB.Bson.Serialization;
19+
using MongoDB.Driver.Linq.Linq3Implementation.ExtensionMethods;
20+
using MongoDB.Driver.Linq.Linq3Implementation.Serializers;
1821

1922
namespace MongoDB.Driver.Linq.Linq3Implementation.Misc;
2023

@@ -46,6 +49,31 @@ public static bool CanBeAssignedTo(this IBsonSerializer sourceSerializer, IBsonS
4649
public static IBsonSerializer GetItemSerializer(this IBsonSerializer serializer)
4750
=> ArraySerializerHelper.GetItemSerializer(serializer);
4851

52+
public static IBsonSerializer GetItemSerializer(this IBsonSerializer serializer, int index)
53+
{
54+
if (serializer is IFixedSizeArraySerializer fixedSizeArraySerializer)
55+
{
56+
return fixedSizeArraySerializer.GetItemSerializer(index);
57+
}
58+
else
59+
{
60+
return serializer.GetItemSerializer();
61+
}
62+
}
63+
64+
public static IBsonSerializer GetItemSerializer(this IBsonSerializer serializer, Expression indexExpression, Expression containingExpression)
65+
{
66+
if (serializer is IFixedSizeArraySerializer fixedSizeArraySerializer)
67+
{
68+
var index = indexExpression.GetConstantValue<int>(containingExpression);
69+
return fixedSizeArraySerializer.GetItemSerializer(index);
70+
}
71+
else
72+
{
73+
return serializer.GetItemSerializer();
74+
}
75+
}
76+
4977
public static bool HasNumericRepresentation(this IBsonSerializer serializer)
5078
{
5179
return
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/* Copyright 2010-present MongoDB 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 MongoDB.Bson;
20+
using MongoDB.Bson.Serialization;
21+
using MongoDB.Bson.Serialization.Serializers;
22+
23+
namespace MongoDB.Driver.Linq.Linq3Implementation.Serializers;
24+
25+
internal static class FixedSizeArraySerializer
26+
{
27+
public static IBsonSerializer Create(Type itemType, IEnumerable<IBsonSerializer> itemSerializers)
28+
{
29+
var serializerType = typeof(FixedSizeArraySerializer<>).MakeGenericType(itemType);
30+
return (IBsonSerializer)Activator.CreateInstance(serializerType, itemSerializers);
31+
}
32+
}
33+
34+
internal interface IFixedSizeArraySerializer
35+
{
36+
IBsonSerializer GetItemSerializer(int index);
37+
}
38+
39+
internal sealed class FixedSizeArraySerializer<TItem> : SerializerBase<TItem[]>, IFixedSizeArraySerializer
40+
{
41+
private readonly IReadOnlyList<IBsonSerializer> _itemSerializers;
42+
43+
public FixedSizeArraySerializer(IEnumerable<IBsonSerializer> itemSerializers)
44+
{
45+
var itemSerializersArray = itemSerializers.ToArray();
46+
foreach (var itemSerializer in itemSerializersArray)
47+
{
48+
if (!typeof(TItem).IsAssignableFrom(itemSerializer.ValueType))
49+
{
50+
throw new ArgumentException($"Serializer class {itemSerializer.ValueType} value type is not assignable to item type {typeof(TItem).Name}");
51+
}
52+
}
53+
54+
_itemSerializers = itemSerializersArray;
55+
}
56+
57+
public override TItem[] Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
58+
{
59+
var reader = context.Reader;
60+
var array = new TItem[_itemSerializers.Count];
61+
62+
reader.ReadStartArray();
63+
var i = 0;
64+
while (reader.ReadBsonType() != BsonType.EndOfDocument)
65+
{
66+
if (i < array.Length)
67+
{
68+
array[i] = (TItem)_itemSerializers[i].Deserialize(context);
69+
i++;
70+
}
71+
}
72+
if (i != array.Length)
73+
{
74+
throw new BsonSerializationException($"Expected {array.Length} array items but found {i}.");
75+
}
76+
reader.ReadEndArray();
77+
78+
return array;
79+
}
80+
81+
IBsonSerializer IFixedSizeArraySerializer.GetItemSerializer(int index) => _itemSerializers[index];
82+
83+
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TItem[] value)
84+
{
85+
if (value.Length != _itemSerializers.Count)
86+
{
87+
throw new BsonSerializationException($"Expected array value to have {_itemSerializers.Count} items but found {value.Length}.");
88+
}
89+
90+
var writer = context.Writer;
91+
writer.WriteStartArray();
92+
for (var i = 0; i < value.Length; i++)
93+
{
94+
_itemSerializers[i].Serialize(context, args, value[i]);
95+
}
96+
writer.WriteEndArray();
97+
}
98+
}

src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/ArrayIndexExpressionToAggregationExpressionTranslator.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@
1414
*/
1515

1616
using System.Linq.Expressions;
17+
using MongoDB.Bson.Serialization;
1718
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions;
19+
using MongoDB.Driver.Linq.Linq3Implementation.ExtensionMethods;
1820
using MongoDB.Driver.Linq.Linq3Implementation.Misc;
21+
using MongoDB.Driver.Linq.Linq3Implementation.Serializers;
1922

2023
namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators
2124
{
@@ -30,7 +33,17 @@ public static TranslatedExpression Translate(TranslationContext context, BinaryE
3033
var indexExpression = expression.Right;
3134
var indexTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, indexExpression);
3235
var ast = AstExpression.ArrayElemAt(arrayTranslation.Ast, indexTranslation.Ast);
33-
var itemSerializer = ArraySerializerHelper.GetItemSerializer(arrayTranslation.Serializer);
36+
var arraySerializer = arrayTranslation.Serializer;
37+
IBsonSerializer itemSerializer;
38+
if (arraySerializer is IFixedSizeArraySerializer fixedSizeArraySerializer)
39+
{
40+
var index = indexExpression.GetConstantValue<int>(expression);
41+
itemSerializer = fixedSizeArraySerializer.GetItemSerializer(index);
42+
}
43+
else
44+
{
45+
itemSerializer = arraySerializer.GetItemSerializer();
46+
}
3447
return new TranslatedExpression(expression, ast, itemSerializer);
3548
}
3649

src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/NewArrayInitExpressionToAggregationExpressionTranslator.cs

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
using MongoDB.Bson.Serialization;
2020
using MongoDB.Bson.Serialization.Serializers;
2121
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions;
22+
using MongoDB.Driver.Linq.Linq3Implementation.Misc;
23+
using MongoDB.Driver.Linq.Linq3Implementation.Serializers;
2224

2325
namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators
2426
{
@@ -27,28 +29,14 @@ internal static class NewArrayInitExpressionToAggregationExpressionTranslator
2729
public static TranslatedExpression Translate(TranslationContext context, NewArrayExpression expression)
2830
{
2931
var items = new List<AstExpression>();
30-
IBsonSerializer itemSerializer = null;
3132
foreach (var itemExpression in expression.Expressions)
3233
{
3334
var itemTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, itemExpression);
3435
items.Add(itemTranslation.Ast);
35-
itemSerializer ??= itemTranslation.Serializer;
36-
37-
// make sure all items are serialized using the same serializer
38-
if (!itemTranslation.Serializer.Equals(itemSerializer))
39-
{
40-
throw new ExpressionNotSupportedException(expression, because: "all items in the array must be serialized using the same serializer");
41-
}
4236
}
43-
4437
var ast = AstExpression.ComputedArray(items);
4538

46-
var arrayType = expression.Type;
47-
var itemType = arrayType.GetElementType();
48-
itemSerializer ??= BsonSerializer.LookupSerializer(itemType); // if the array is empty itemSerializer will be null
49-
var arraySerializerType = typeof(ArraySerializer<>).MakeGenericType(itemType);
50-
var arraySerializer = (IBsonSerializer)Activator.CreateInstance(arraySerializerType, itemSerializer);
51-
39+
var arraySerializer = context.KnownSerializers.GetSerializer(expression);
5240
return new TranslatedExpression(expression, ast, arraySerializer);
5341
}
5442
}

0 commit comments

Comments
 (0)