| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 |
- /* 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.
- */
- using System;
- using System.Linq;
- using System.Linq.Expressions;
- using System.Reflection;
- using System.Collections.Generic;
- namespace MongoDB.Bson.Serialization
- {
- /// <summary>
- /// A helper class used to create and compile delegates for creator maps.
- /// </summary>
- public class CreatorMapDelegateCompiler : ExpressionVisitor
- {
- // private fields
- private ParameterExpression _prototypeParameter;
- private Dictionary<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>
- public Delegate CompileConstructorDelegate(ConstructorInfo constructorInfo)
- {
- // build and compile the following delegate:
- // (p1, p2, ...) => new TClass(p1, p2, ...)
- var parameters = constructorInfo.GetParameters().Select(p => Expression.Parameter(p.ParameterType, p.Name)).ToArray();
- var body = Expression.New(constructorInfo, parameters);
- var lambda = Expression.Lambda(body, parameters);
- return lambda.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>
- public Delegate CompileCreatorDelegate<TClass>(Expression<Func<TClass, TClass>> creatorLambda, out IEnumerable<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 = new Dictionary<MemberInfo, ParameterExpression>();
- var body = Visit(creatorLambda.Body);
- var lambda = 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>
- public Delegate CompileFactoryMethodDelegate(MethodInfo methodInfo)
- {
- // build and compile the following delegate:
- // (p1, p2, ...) => factoryMethod(p1, p2, ...)
- var parameters = methodInfo.GetParameters().Select(p => Expression.Parameter(p.ParameterType, p.Name)).ToArray();
- var body = Expression.Call(methodInfo, parameters);
- var lambda = Expression.Lambda(body, parameters);
- return lambda.Compile();
- }
- // protected methods
- /// <summary>
- /// Visits a MemberExpression.
- /// </summary>
- /// <param name="node">The MemberExpression.</param>
- /// <returns>The MemberExpression (possibly modified).</returns>
- protected override Expression VisitMember(MemberExpression node)
- {
- if (node.Expression == _prototypeParameter)
- {
- var memberInfo = node.Member;
- ParameterExpression parameter;
- if (!_parameters.TryGetValue(memberInfo, out parameter))
- {
- var parameterName = string.Format("_p{0}_", _parameters.Count + 1); // avoid naming conflicts with body
- parameter = Expression.Parameter(node.Type, parameterName);
- _parameters.Add(memberInfo, parameter);
- }
- return parameter;
- }
- return base.VisitMember(node);
- }
- /// <summary>
- /// Visits a ParameterExpression.
- /// </summary>
- /// <param name="node">The ParameterExpression.</param>
- /// <returns>The ParameterExpression (possibly modified).</returns>
- protected override Expression VisitParameter(ParameterExpression node)
- {
- if (node == _prototypeParameter)
- {
- throw new BsonSerializationException("The only operations allowed on the prototype parameter are accessing a field or property.");
- }
- return base.VisitParameter(node);
- }
- }
- }
|