CreatorMapDelegateCompiler.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  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. using System;
  16. using System.Linq;
  17. using System.Linq.Expressions;
  18. using System.Reflection;
  19. using System.Collections.Generic;
  20. namespace MongoDB.Bson.Serialization
  21. {
  22. /// <summary>
  23. /// A helper class used to create and compile delegates for creator maps.
  24. /// </summary>
  25. public class CreatorMapDelegateCompiler : ExpressionVisitor
  26. {
  27. // private fields
  28. private ParameterExpression _prototypeParameter;
  29. private Dictionary<MemberInfo, ParameterExpression> _parameters;
  30. // public methods
  31. /// <summary>
  32. /// Creates and compiles a delegate that calls a constructor.
  33. /// </summary>
  34. /// <param name="constructorInfo">The constructor.</param>
  35. /// <returns>A delegate that calls the constructor.</returns>
  36. public Delegate CompileConstructorDelegate(ConstructorInfo constructorInfo)
  37. {
  38. // build and compile the following delegate:
  39. // (p1, p2, ...) => new TClass(p1, p2, ...)
  40. var parameters = constructorInfo.GetParameters().Select(p => Expression.Parameter(p.ParameterType, p.Name)).ToArray();
  41. var body = Expression.New(constructorInfo, parameters);
  42. var lambda = Expression.Lambda(body, parameters);
  43. return lambda.Compile();
  44. }
  45. /// <summary>
  46. /// Creates and compiles a delegate from a lambda expression.
  47. /// </summary>
  48. /// <typeparam name="TClass">The type of the class.</typeparam>
  49. /// <param name="creatorLambda">The lambda expression.</param>
  50. /// <param name="arguments">The arguments for the delegate's parameters.</param>
  51. /// <returns>A delegate.</returns>
  52. public Delegate CompileCreatorDelegate<TClass>(Expression<Func<TClass, TClass>> creatorLambda, out IEnumerable<MemberInfo> arguments)
  53. {
  54. // transform c => expression (where c is the prototype parameter)
  55. // to (p1, p2, ...) => expression' where expression' is expression with every c.X replaced by p#
  56. _prototypeParameter = creatorLambda.Parameters[0];
  57. _parameters = new Dictionary<MemberInfo, ParameterExpression>();
  58. var body = Visit(creatorLambda.Body);
  59. var lambda = Expression.Lambda(body, _parameters.Values.ToArray());
  60. var @delegate = lambda.Compile();
  61. arguments = _parameters.Keys.ToArray();
  62. return @delegate;
  63. }
  64. /// <summary>
  65. /// Creates and compiles a delegate that calls a factory method.
  66. /// </summary>
  67. /// <param name="methodInfo">the method.</param>
  68. /// <returns>A delegate that calls the factory method.</returns>
  69. public Delegate CompileFactoryMethodDelegate(MethodInfo methodInfo)
  70. {
  71. // build and compile the following delegate:
  72. // (p1, p2, ...) => factoryMethod(p1, p2, ...)
  73. var parameters = methodInfo.GetParameters().Select(p => Expression.Parameter(p.ParameterType, p.Name)).ToArray();
  74. var body = Expression.Call(methodInfo, parameters);
  75. var lambda = Expression.Lambda(body, parameters);
  76. return lambda.Compile();
  77. }
  78. // protected methods
  79. /// <summary>
  80. /// Visits a MemberExpression.
  81. /// </summary>
  82. /// <param name="node">The MemberExpression.</param>
  83. /// <returns>The MemberExpression (possibly modified).</returns>
  84. protected override Expression VisitMember(MemberExpression node)
  85. {
  86. if (node.Expression == _prototypeParameter)
  87. {
  88. var memberInfo = node.Member;
  89. ParameterExpression parameter;
  90. if (!_parameters.TryGetValue(memberInfo, out parameter))
  91. {
  92. var parameterName = string.Format("_p{0}_", _parameters.Count + 1); // avoid naming conflicts with body
  93. parameter = Expression.Parameter(node.Type, parameterName);
  94. _parameters.Add(memberInfo, parameter);
  95. }
  96. return parameter;
  97. }
  98. return base.VisitMember(node);
  99. }
  100. /// <summary>
  101. /// Visits a ParameterExpression.
  102. /// </summary>
  103. /// <param name="node">The ParameterExpression.</param>
  104. /// <returns>The ParameterExpression (possibly modified).</returns>
  105. protected override Expression VisitParameter(ParameterExpression node)
  106. {
  107. if (node == _prototypeParameter)
  108. {
  109. throw new BsonSerializationException("The only operations allowed on the prototype parameter are accessing a field or property.");
  110. }
  111. return base.VisitParameter(node);
  112. }
  113. }
  114. }