- Notifications
You must be signed in to change notification settings - Fork 1.3k
/
Copy pathCreatorMapDelegateCompiler.cs
129 lines (114 loc) · 5.23 KB
/
CreatorMapDelegateCompiler.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/* Copyright 2010-present MongoDB Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
usingSystem;
usingSystem.Linq;
usingSystem.Linq.Expressions;
usingSystem.Reflection;
usingSystem.Collections.Generic;
namespaceMongoDB.Bson.Serialization
{
/// <summary>
/// A helper class used to create and compile delegates for creator maps.
/// </summary>
publicclassCreatorMapDelegateCompiler:ExpressionVisitor
{
// private fields
privateParameterExpression_prototypeParameter;
privateDictionary<MemberInfo,ParameterExpression>_parameters;
// public methods
/// <summary>
/// Creates and compiles a delegate that calls a constructor.
/// </summary>
/// <param name="constructorInfo">The constructor.</param>
/// <returns>A delegate that calls the constructor.</returns>
publicDelegateCompileConstructorDelegate(ConstructorInfoconstructorInfo)
{
// build and compile the following delegate:
// (p1, p2, ...) => new TClass(p1, p2, ...)
varparameters=constructorInfo.GetParameters().Select(p =>Expression.Parameter(p.ParameterType,p.Name)).ToArray();
varbody=Expression.New(constructorInfo,parameters);
varlambda=Expression.Lambda(body,parameters);
returnlambda.Compile();
}
/// <summary>
/// Creates and compiles a delegate from a lambda expression.
/// </summary>
/// <typeparam name="TClass">The type of the class.</typeparam>
/// <param name="creatorLambda">The lambda expression.</param>
/// <param name="arguments">The arguments for the delegate's parameters.</param>
/// <returns>A delegate.</returns>
publicDelegateCompileCreatorDelegate<TClass>(Expression<Func<TClass,TClass>>creatorLambda,outIEnumerable<MemberInfo>arguments)
{
// transform c => expression (where c is the prototype parameter)
// to (p1, p2, ...) => expression' where expression' is expression with every c.X replaced by p#
_prototypeParameter=creatorLambda.Parameters[0];
_parameters=newDictionary<MemberInfo,ParameterExpression>();
varbody=Visit(creatorLambda.Body);
varlambda=Expression.Lambda(body,_parameters.Values.ToArray());
var@delegate=lambda.Compile();
arguments=_parameters.Keys.ToArray();
return@delegate;
}
/// <summary>
/// Creates and compiles a delegate that calls a factory method.
/// </summary>
/// <param name="methodInfo">the method.</param>
/// <returns>A delegate that calls the factory method.</returns>
publicDelegateCompileFactoryMethodDelegate(MethodInfomethodInfo)
{
// build and compile the following delegate:
// (p1, p2, ...) => factoryMethod(p1, p2, ...)
varparameters=methodInfo.GetParameters().Select(p =>Expression.Parameter(p.ParameterType,p.Name)).ToArray();
varbody=Expression.Call(methodInfo,parameters);
varlambda=Expression.Lambda(body,parameters);
returnlambda.Compile();
}
// protected methods
/// <summary>
/// Visits a MemberExpression.
/// </summary>
/// <param name="node">The MemberExpression.</param>
/// <returns>The MemberExpression (possibly modified).</returns>
protectedoverrideExpressionVisitMember(MemberExpressionnode)
{
if(node.Expression==_prototypeParameter)
{
varmemberInfo=node.Member;
ParameterExpressionparameter;
if(!_parameters.TryGetValue(memberInfo,outparameter))
{
varparameterName=string.Format("_p{0}_",_parameters.Count+1);// avoid naming conflicts with body
parameter=Expression.Parameter(node.Type,parameterName);
_parameters.Add(memberInfo,parameter);
}
returnparameter;
}
returnbase.VisitMember(node);
}
/// <summary>
/// Visits a ParameterExpression.
/// </summary>
/// <param name="node">The ParameterExpression.</param>
/// <returns>The ParameterExpression (possibly modified).</returns>
protectedoverrideExpressionVisitParameter(ParameterExpressionnode)
{
if(node==_prototypeParameter)
{
thrownewBsonSerializationException("The only operations allowed on the prototype parameter are accessing a field or property.");
}
returnbase.VisitParameter(node);
}
}
}