Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
chaowlert committed Feb 19, 2019
1 parent fc6d5e8 commit 3f02b6d
Show file tree
Hide file tree
Showing 14 changed files with 292 additions and 225 deletions.
12 changes: 6 additions & 6 deletions src/Benchmark/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -223,16 +223,16 @@ static void TestCustomerNative(Customer item, int iterations)

static void TestCodeGen(Customer item, int iterations, ref double counter)
{
//var time = Loop(item, get => CustomerMapper.Map(get), iterations);
//Console.WriteLine("Codegen:\t\t" + time);
//counter += time;
var time = Loop(item, get => CustomerMapper.Map(get), iterations);
Console.WriteLine("Codegen:\t\t" + time);
counter += time;
}

static void TestCodeGen(Foo item, int iterations, ref double counter)
{
//var time = Loop(item, get => FooMapper.Map(get), iterations);
//Console.WriteLine("Codegen:\t\t" + time);
//counter += time;
var time = Loop(item, get => FooMapper.Map(get), iterations);
Console.WriteLine("Codegen:\t\t" + time);
counter += time;
}

static long Loop<T>(T item, Action<T> action, int iterations = 1000000)
Expand Down
69 changes: 35 additions & 34 deletions src/Mapster/Adapters/BaseAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,18 +63,22 @@ protected virtual bool CanInline(Expression source, Expression destination, Comp
return false;
if (arg.Settings.AfterMappingFactories.Count > 0)
return false;
if (arg.Settings.BeforeMappingFactories.Count > 0)
return false;
if (arg.Settings.Includes.Count > 0)
return false;
return true;
}

protected virtual Expression CreateExpressionBody(Expression source, Expression destination, CompileArgument arg)
{
if (this.CheckExplicitMapping
&& arg.Context.Config.RequireExplicitMapping
&& !arg.ExplicitMapping)
if (this.CheckExplicitMapping)
{
throw new InvalidOperationException("Implicit mapping is not allowed (check GlobalSettings.RequireExplicitMapping) and no configuration exists");
if (arg.Settings.MaxDepth <= 0)
return arg.DestinationType.CreateDefault();

if (arg.Context.Config.RequireExplicitMapping && !arg.ExplicitMapping)
throw new InvalidOperationException("Implicit mapping is not allowed (check GlobalSettings.RequireExplicitMapping) and no configuration exists");
}

if (CanInline(source, destination, arg) && arg.Settings.AvoidInlineMapping != true)
Expand All @@ -96,34 +100,22 @@ protected Expression CreateBlockExpressionBody(Expression source, Expression des

if (set.NodeType != ExpressionType.Throw)
{
Expression assign = Expression.Assign(result, set);
set = CreateBlockExpression(source, result, arg);
var actions = new List<Expression>
{
Expression.Assign(result, set)
};

//before(source, result);
var beforeMappings = arg.Settings.BeforeMappingFactories.Select(it => InvokeMapping(it, source, result, arg));
actions.AddRange(beforeMappings);

//result.prop = adapt(source.prop);
//action(source, result);
if (arg.Settings.AfterMappingFactories.Count > 0)
{
var actions = new List<Expression> { set };

foreach (var afterMappingFactory in arg.Settings.AfterMappingFactories)
{
var afterMapping = afterMappingFactory(arg);
var args = afterMapping.Parameters;
Expression invoke;
if (args[0].Type.IsReferenceAssignableFrom(source.Type) && args[1].Type.IsReferenceAssignableFrom(result.Type))
{
var replacer = new ParameterExpressionReplacer(args, source, result);
invoke = replacer.Visit(afterMapping.Body);
}
else
{
invoke = Expression.Invoke(afterMapping, source.To(args[0].Type), result.To(args[1].Type));
}
actions.Add(invoke);
}
set = CreateBlockExpression(source, result, arg);
actions.Add(set);

set = Expression.Block(actions);
}
//after(source, result);
var afterMappings = arg.Settings.AfterMappingFactories.Select(it => InvokeMapping(it, source, result, arg));
actions.AddRange(afterMappings);

//using (var scope = new MapContextScope()) {
// var references = scope.Context.Reference;
Expand Down Expand Up @@ -153,7 +145,8 @@ protected Expression CreateBlockExpressionBody(Expression source, Expression des
var refAssign = Expression.Assign(
Expression.Property(dict, indexer, Expression.Convert(source, typeof(object))),
Expression.Convert(result, typeof(object)));
var setResultAndCache = Expression.Block(assign, refAssign, set);
actions.Add(refAssign);
var setResultAndCache = Expression.Block(actions);

var cache = Expression.Variable(typeof(object), "cache");
var tryGetMethod = typeof(Dictionary<object, object>).GetMethod("TryGetValue", new[] { typeof(object), typeof(object).MakeByRefType() });
Expand All @@ -169,7 +162,7 @@ protected Expression CreateBlockExpressionBody(Expression source, Expression des
}
else
{
set = Expression.Block(assign, set);
set = Expression.Block(actions);
}

//TDestination result;
Expand Down Expand Up @@ -247,6 +240,16 @@ protected Expression CreateBlockExpressionBody(Expression source, Expression des
return Expression.Block(new[] { result }, set, result);
}

private Expression InvokeMapping(Func<CompileArgument, LambdaExpression> mappingFactory, Expression source, Expression result, CompileArgument arg)
{
var afterMapping = mappingFactory(arg);
var args = afterMapping.Parameters;
var invoke = afterMapping.Apply(source.To(args[0].Type), result.To(args[1].Type));
if (invoke.Type != typeof(void))
invoke = ExpressionEx.Assign(result, invoke);
return invoke;
}

protected Expression CreateInlineExpressionBody(Expression source, CompileArgument arg)
{
//source == null ? default(TDestination) : adapt(source)
Expand Down Expand Up @@ -301,9 +304,7 @@ protected virtual Expression CreateInstantiationExpression(Expression source, Ex
throw new InvalidOperationException($"No default constructor for type '{arg.DestinationType.Name}', please use 'ConstructUsing'");

//dest ?? new TDest();
return destination == null
? newObj
: Expression.Coalesce(destination, newObj);
return newObj;
}

protected Expression CreateAdaptExpression(Expression source, Type destinationType, CompileArgument arg)
Expand Down
68 changes: 58 additions & 10 deletions src/Mapster/Adapters/BaseClassAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,25 @@
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Mapster.Models;
using Mapster.Utils;

namespace Mapster.Adapters
{
internal abstract class BaseClassAdapter : BaseAdapter
{
protected abstract ClassModel GetClassModel(Type destinationType, CompileArgument arg);

#region Build the Adapter Model

protected ClassMapping CreateClassConverter(Expression source, Expression destination, CompileArgument arg)
protected ClassMapping CreateClassConverter(Expression source, ClassModel classModel, CompileArgument arg)
{
var classModel = GetClassModel(arg.DestinationType, arg);
var destinationMembers = classModel.Members;

var unmappedDestinationMembers = new List<string>();

var properties = new List<MemberMapping>();

foreach (var destinationMember in destinationMembers)
{
if (ProcessIgnores(arg.Settings, destinationMember, out var setterCondition))
if (ProcessIgnores(arg, destinationMember, out var setterCondition))
continue;

var member = destinationMember;
Expand All @@ -46,6 +43,9 @@ protected ClassMapping CreateClassConverter(Expression source, Expression destin
}
else if (classModel.ConstructorInfo != null)
{
var info = (ParameterInfo)member.Info;
if (!info.IsOptional)
return null;
var propertyModel = new MemberMapping
{
Getter = null,
Expand Down Expand Up @@ -73,18 +73,66 @@ protected ClassMapping CreateClassConverter(Expression source, Expression destin
}

protected static bool ProcessIgnores(
TypeAdapterSettings config,
CompileArgument arg,
IMemberModel destinationMember,
out LambdaExpression condition)
{
condition = null;
if (!destinationMember.ShouldMapMember(config.ShouldMapMember, MemberSide.Destination))
if (!destinationMember.ShouldMapMember(arg, MemberSide.Destination))
return true;

return config.IgnoreIfs.TryGetValue(destinationMember.Name, out condition)
return arg.Settings.IgnoreIfs.TryGetValue(destinationMember.Name, out condition)
&& condition == null;
}


protected Expression CreateInstantiationExpression(Expression source, ClassMapping classConverter, CompileArgument arg)
{
var members = classConverter.Members;

var arguments = new List<Expression>();
foreach (var member in members)
{
var parameterInfo = (ParameterInfo)member.DestinationMember.Info;
var defaultConst = parameterInfo.IsOptional
? Expression.Constant(parameterInfo.DefaultValue, member.DestinationMember.Type)
: parameterInfo.ParameterType.CreateDefault();

Expression getter;
if (member.Getter == null)
{
getter = defaultConst;
}
else
{
getter = CreateAdaptExpression(member.Getter, member.DestinationMember.Type, arg);

if (arg.Settings.IgnoreNullValues == true && member.Getter.Type.CanBeNull())
{
var condition = Expression.NotEqual(member.Getter, Expression.Constant(null, member.Getter.Type));
getter = Expression.Condition(condition, getter, defaultConst);
}
if (member.SetterCondition != null)
{
var condition = ExpressionEx.Not(member.SetterCondition.Apply(source, arg.DestinationType.CreateDefault()));
getter = Expression.Condition(condition, getter, defaultConst);
}
}
arguments.Add(getter);
}

return Expression.New(classConverter.ConstructorInfo, arguments);
}

protected ClassModel GetClassModel(ConstructorInfo ctor)
{
return new ClassModel
{
ConstructorInfo = ctor,
Members = ctor.GetParameters().Select(ReflectionUtils.CreateModel)
};
}

#endregion
}
}
37 changes: 26 additions & 11 deletions src/Mapster/Adapters/ClassAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,7 @@ internal class ClassAdapter : BaseClassAdapter

protected override bool CanMap(PreCompileArgument arg)
{
var bindingFlags = arg.ExplicitMapping
? BindingFlags.Public | BindingFlags.NonPublic
: BindingFlags.Public;
if (!arg.DestinationType.IsPoco(bindingFlags))
return false;

return true;
return arg.ExplicitMapping || arg.DestinationType.IsPoco();
}

protected override bool CanInline(Expression source, Expression destination, CompileArgument arg)
Expand Down Expand Up @@ -56,6 +50,25 @@ protected override bool CanInline(Expression source, Expression destination, Com
return true;
}

protected override Expression CreateInstantiationExpression(Expression source, Expression destination, CompileArgument arg)
{
//new TDestination(src.Prop1, src.Prop2)

if (arg.Settings.ConstructUsingFactory != null || arg.Settings.MapToConstructor != true)
return base.CreateInstantiationExpression(source, destination, arg);

var classConverter = arg.DestinationType.GetConstructors()
.OrderByDescending(it => it.GetParameters().Length)
.Select(GetClassModel)
.Select(it => CreateClassConverter(source, it, arg))
.FirstOrDefault(it => it != null);

if (classConverter == null)
throw new Exception();

return CreateInstantiationExpression(source, classConverter, arg);
}

protected override Expression CreateBlockExpression(Expression source, Expression destination, CompileArgument arg)
{
//### !IgnoreNullValues
Expand All @@ -68,7 +81,8 @@ protected override Expression CreateBlockExpression(Expression source, Expressio
//if (src.Prop2 != null)
// dest.Prop2 = convert(src.Prop2);

var classConverter = CreateClassConverter(source, destination, arg);
var classModel = GetClassModel(arg.DestinationType);
var classConverter = CreateClassConverter(source, classModel, arg);
var members = classConverter.Members;

var lines = new List<Expression>();
Expand Down Expand Up @@ -108,7 +122,7 @@ protected override Expression CreateBlockExpression(Expression source, Expressio
foreach (var kvp in conditions)
{
var condition = Expression.IfThen(
Expression.Not(kvp.Key.Apply(source, destination)),
ExpressionEx.Not(kvp.Key.Apply(source, destination)),
Expression.Block(kvp.Value));
lines.Add(condition);
}
Expand All @@ -128,7 +142,8 @@ protected override Expression CreateInlineExpression(Expression source, CompileA
var memberInit = exp as MemberInitExpression;
var newInstance = memberInit?.NewExpression ?? (NewExpression)exp;

var classConverter = CreateClassConverter(source, newInstance, arg);
var classModel = GetClassModel(arg.DestinationType);
var classConverter = CreateClassConverter(source, classModel, arg);
var members = classConverter.Members;

var lines = new List<MemberBinding>();
Expand Down Expand Up @@ -161,7 +176,7 @@ protected override Expression CreateInlineExpression(Expression source, CompileA
return Expression.MemberInit(newInstance, lines);
}

protected override ClassModel GetClassModel(Type destinationType, CompileArgument arg)
private ClassModel GetClassModel(Type destinationType)
{
return new ClassModel
{
Expand Down
20 changes: 8 additions & 12 deletions src/Mapster/Adapters/DictionaryAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ protected override Expression CreateBlockExpression(Expression source, Expressio
else
{
var setWithCondition = Expression.IfThen(
Expression.Not(ignoreIf.Value.Apply(source, destination)),
ExpressionEx.Not(ignoreIf.Value.Apply(source, destination)),
set);
dict.Add(ignoreIf.Key, setWithCondition);
}
Expand Down Expand Up @@ -141,7 +141,8 @@ protected override Expression CreateInlineExpression(Expression source, CompileA
var listInit = exp as ListInitExpression;
var newInstance = listInit?.NewExpression ?? (NewExpression)exp;

var classConverter = CreateClassConverter(source, newInstance, arg);
var classModel = GetClassModel(arg);
var classConverter = CreateClassConverter(source, classModel, arg);
var members = classConverter.Members;

var dictType = arg.DestinationType.GetDictionaryType();
Expand All @@ -164,27 +165,22 @@ protected override Expression CreateInlineExpression(Expression source, CompileA
return Expression.ListInit(newInstance, lines);
}

protected override ClassModel GetClassModel(Type destinationType, CompileArgument arg)
private ClassModel GetClassModel(CompileArgument arg)
{
//get member name from map
var destNames = arg.Settings.Resolvers.Select(r => r.DestinationMemberName);
var destNames = arg.GetDestinationNames().AsEnumerable();

//get member name from properties
if (arg.SourceType.GetDictionaryType() == null)
{
var srcNames = arg.Settings.Resolvers.Select(r => r.SourceMemberName)
.Where(name => name != null)
.ToHashSet();
var propNames = arg.SourceType.GetFieldsAndProperties(accessorFlags: BindingFlags.NonPublic | BindingFlags.Public)
.Where(model => model.ShouldMapMember(arg.Settings.ShouldMapMember, MemberSide.Source))
.Select(model => model.Name)
.Where(name => !srcNames.Contains(name))
.Select(name => arg.Settings.NameMatchingStrategy.SourceMemberNameConverter(name));
.Where(model => model.ShouldMapMember(arg, MemberSide.Source))
.Select(model => arg.Settings.NameMatchingStrategy.SourceMemberNameConverter(model.Name));
destNames = destNames.Union(propNames);
}

//create model
var dictType = destinationType.GetDictionaryType();
var dictType = arg.DestinationType.GetDictionaryType();
var valueType = dictType.GetGenericArguments()[1];

var getFn = GetFunction(arg, dictType);
Expand Down
Loading

0 comments on commit 3f02b6d

Please sign in to comment.