Skip to content

Commit

Permalink
Merge pull request AutoMapper#2282 from lbargaoanu/ExtensionMethodNull
Browse files Browse the repository at this point in the history
Null check static methods too
  • Loading branch information
jbogard authored Aug 25, 2017
2 parents ed01e44 + a212fe0 commit b7e411f
Show file tree
Hide file tree
Showing 14 changed files with 275 additions and 317 deletions.
72 changes: 22 additions & 50 deletions src/AutoMapper/Execution/ExpressionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ public static Expression MapExpression(IConfigurationProvider configurationProvi
}
var objectMapperExpression = ObjectMapperExpression(configurationProvider, profileMap, typePair,
sourceParameter, contextParameter, propertyMap, destinationParameter);
return NullCheckSource(profileMap, sourceParameter, destinationParameter, objectMapperExpression,
propertyMap);
var nullCheckSource = NullCheckSource(profileMap, sourceParameter, destinationParameter, objectMapperExpression, propertyMap);
return ExpressionFactory.ToType(nullCheckSource, typePair.DestinationType);
}

public static Expression NullCheckSource(ProfileMap profileMap,
Expand All @@ -54,66 +54,40 @@ public static Expression NullCheckSource(ProfileMap profileMap,
Expression objectMapperExpression,
PropertyMap propertyMap = null)
{
var destinationType = destinationParameter.Type;
var defaultDestination = propertyMap == null
? destinationParameter.IfNullElse(DefaultDestination(destinationType, profileMap), destinationParameter)
: (propertyMap.UseDestinationValue
? destinationParameter
: DefaultDestination(destinationType, profileMap));
var ifSourceNull = destinationType.IsCollectionType() ? ClearDestinationCollection() : defaultDestination;
var declaredDestinationType = destinationParameter.Type;
var destinationType = objectMapperExpression.Type;
var defaultDestination = DefaultDestination(destinationType, declaredDestinationType, profileMap);
var destination = propertyMap == null
? destinationParameter.IfNullElse(defaultDestination, destinationParameter)
: (propertyMap.UseDestinationValue ? destinationParameter : defaultDestination);
var ifSourceNull = destinationParameter.Type.IsCollectionType() ? ClearDestinationCollection() : destination;
return sourceParameter.IfNullElse(ifSourceNull, objectMapperExpression);

Expression ClearDestinationCollection()
{
var destinationElementType = ElementTypeHelper.GetElementType(destinationParameter.Type);
var destinationCollectionType = typeof(ICollection<>).MakeGenericType(destinationElementType);
var destinationVariable = Variable(destinationCollectionType, "collectionDestination");
var clearMethod = destinationCollectionType.GetDeclaredMethod("Clear");
var clear = Condition(Property(destinationVariable, "IsReadOnly"),
Empty(), Call(destinationVariable, clearMethod));
var clear = Call(destinationVariable, destinationCollectionType.GetDeclaredMethod("Clear"));
var isReadOnly = Property(destinationVariable, "IsReadOnly");
return Block(new[] {destinationVariable},
Assign(destinationVariable,
ExpressionFactory.ToType(destinationParameter, destinationCollectionType)),
destinationVariable.IfNullElse(Empty(), clear),
defaultDestination);
Assign(destinationVariable, ExpressionFactory.ToType(destinationParameter, destinationCollectionType)),
Condition(OrElse(Equal(destinationVariable, Constant(null)), isReadOnly), Empty(), clear),
destination);
}
}

private static Expression DefaultDestination(Type destinationType, ProfileMap profileMap)
private static Expression DefaultDestination(Type destinationType, Type declaredDestinationType, ProfileMap profileMap)
{
var defaultValue = Default(destinationType);
if (profileMap.AllowNullCollections || destinationType == typeof(string) ||
!destinationType.IsEnumerableType())
return defaultValue;
if (destinationType.IsArray)
if(profileMap.AllowNullCollections || destinationType == typeof(string) || !destinationType.IsEnumerableType())
{
var destinationElementType = destinationType.GetElementType();
return NewArrayBounds(destinationElementType,
Enumerable.Repeat(Constant(0), destinationType.GetArrayRank()));
return Default(declaredDestinationType);
}
if (destinationType.IsDictionaryType())
return CreateCollection(typeof(Dictionary<,>));
if (destinationType.IsSetType())
return CreateCollection(typeof(HashSet<>));
return CreateCollection(typeof(List<>));

Expression CreateCollection(Type collectionType)
if(destinationType.IsArray)
{
Type concreteDestinationType;
if (destinationType.IsInterface())
{
var genericArguments = destinationType.GetGenericArguments();
if (genericArguments.Length == 0)
genericArguments = new[] {typeof(object)};
concreteDestinationType = collectionType.MakeGenericType(genericArguments);
}
else
{
concreteDestinationType = destinationType;
}
var constructor = DelegateFactory.GenerateNonNullConstructorExpression(concreteDestinationType);
return ExpressionFactory.ToType(constructor, destinationType);
var destinationElementType = destinationType.GetElementType();
return NewArrayBounds(destinationElementType, Enumerable.Repeat(Constant(0), destinationType.GetArrayRank()));
}
return DelegateFactory.GenerateNonNullConstructorExpression(destinationType);
}

private static Expression ObjectMapperExpression(IConfigurationProvider configurationProvider,
Expand All @@ -125,10 +99,8 @@ private static Expression ObjectMapperExpression(IConfigurationProvider configur
{
var mapperExpression = match.MapExpression(configurationProvider, profileMap, propertyMap,
sourceParameter, destinationParameter, contextParameter);

return ExpressionFactory.ToType(mapperExpression, typePair.DestinationType);
return mapperExpression;
}

return ContextMap(typePair, sourceParameter, contextParameter, destinationParameter);
}

Expand Down
37 changes: 15 additions & 22 deletions src/AutoMapper/Execution/TypeMapPlanBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,8 @@ private Expression CreateMapperFunc(Expression assignmentFunc)
mapperFunc,
Default(_typeMap.DestinationTypeToUse));

if (_typeMap.Profile.AllowNullDestinationValues && !_typeMap.SourceType.IsValueType())
mapperFunc =
Condition(Equal(Source, Default(_typeMap.SourceType)),
Default(_typeMap.DestinationTypeToUse), mapperFunc.RemoveIfNotNull(Source));
if (_typeMap.Profile.AllowNullDestinationValues)
mapperFunc = Source.IfNullElse(Default(_typeMap.DestinationTypeToUse), mapperFunc);

if (_typeMap.PreserveReferences)
{
Expand Down Expand Up @@ -325,21 +323,15 @@ private Expression ResolveSource(ConstructorParameterMap ctorParamMap)
{
if (ctorParamMap.CustomExpression != null)
return ctorParamMap.CustomExpression.ConvertReplaceParameters(Source)
.IfNotNull(ctorParamMap.DestinationType);
.NullCheck(ctorParamMap.DestinationType);
if (ctorParamMap.CustomValueResolver != null)
return ctorParamMap.CustomValueResolver.ConvertReplaceParameters(Source, Context);
if (ctorParamMap.Parameter.IsOptional)
{
ctorParamMap.DefaultValue = true;
return Constant(ctorParamMap.Parameter.GetDefaultValue(), ctorParamMap.Parameter.ParameterType);
}
return ctorParamMap.SourceMembers.Aggregate(
(Expression) Source,
(inner, getter) => getter is MethodInfo
? Call(getter.IsStatic() ? null : inner, (MethodInfo) getter)
: (Expression) MakeMemberAccess(getter.IsStatic() ? null : inner, getter)
)
.IfNotNull(ctorParamMap.DestinationType);
return Chain(ctorParamMap.SourceMembers, ctorParamMap.DestinationType);
}

private Expression TryPropertyMap(PropertyMap propertyMap)
Expand Down Expand Up @@ -468,7 +460,7 @@ private Expression BuildValueResolverFunc(PropertyMap propertyMap, Expression de
else if (propertyMap.CustomExpression != null)
{
var nullCheckedExpression = propertyMap.CustomExpression.ReplaceParameters(Source)
.IfNotNull(destinationPropertyType);
.NullCheck(destinationPropertyType);
var destinationNullable = destinationPropertyType.IsNullableType();
var returnType = destinationNullable && destinationPropertyType.GetTypeOfNullable() ==
nullCheckedExpression.Type
Expand All @@ -492,15 +484,7 @@ private Expression BuildValueResolverFunc(PropertyMap propertyMap, Expression de
}
else
{
valueResolverFunc = propertyMap.SourceMembers.Aggregate(
(Expression) Source,
(inner, getter) => getter is MethodInfo
? getter.IsStatic()
? Call(null, (MethodInfo) getter, inner)
: (Expression) Call(inner, (MethodInfo) getter)
: MakeMemberAccess(getter.IsStatic() ? null : inner, getter)
);
valueResolverFunc = valueResolverFunc.IfNotNull(destinationPropertyType);
valueResolverFunc = Chain(propertyMap.SourceMembers, destinationPropertyType);
}
}
else if (propertyMap.SourceMember != null)
Expand Down Expand Up @@ -529,6 +513,15 @@ private Expression BuildValueResolverFunc(PropertyMap propertyMap, Expression de

return valueResolverFunc;
}

private Expression Chain(IEnumerable<MemberInfo> members, Type destinationType) =>
members
.Aggregate(
(Expression) Source,
(inner, getter) => getter is MethodInfo method ?
(getter.IsStatic() ? Call(null, method, inner) : (Expression) Call(inner, method)) :
MakeMemberAccess(getter.IsStatic() ? null : inner, getter))
.NullCheck(destinationType);

private Expression CreateInstance(Type type)
=> Call(Property(Context, nameof(ResolutionContext.Options)),
Expand Down
7 changes: 2 additions & 5 deletions src/AutoMapper/ExpressionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,8 @@ public static Expression Replace(this Expression exp, Expression old, Expression
public static LambdaExpression Concat(this LambdaExpression expr, LambdaExpression concat)
=> ExpressionFactory.Concat(expr, concat);

public static Expression IfNotNull(this Expression expression, Type destinationType)
=> ExpressionFactory.IfNotNull(expression, destinationType);

public static Expression RemoveIfNotNull(this Expression expression, params Expression[] expressions)
=> ExpressionFactory.RemoveIfNotNull(expression, expressions);
public static Expression NullCheck(this Expression expression, Type destinationType)
=> ExpressionFactory.NullCheck(expression, destinationType);

public static Expression IfNullElse(this Expression expression, Expression then, Expression @else = null)
=> ExpressionFactory.IfNullElse(expression, then, @else);
Expand Down
Loading

0 comments on commit b7e411f

Please sign in to comment.