4 using System.Collections.Generic;
6 using System.Linq.Expressions;
7 using System.Reflection;
8 using SiliconStudio.Shaders.Utility;
11 namespace SiliconStudio.Shaders.Ast
23 internal Dictionary<DeepCloner.CacheKey, Delegate> Cache;
25 internal bool IsSelfClone;
52 return base.ContainsKey(key) || (Parent != null && Parent.ContainsKey(key));
57 return base.TryGetValue(key, out value) || (Parent != null && Parent.TryGetValue(key, out value));
60 public new object this[
object key]
64 return base[key] ?? (Parent != null ? Parent[key] : null);
79 #region Constants and Fields
82 private static Dictionary<CacheKey, Delegate> deepcache;
85 private static Dictionary<CacheKey, Delegate> deepCacheSelf;
87 private static readonly MethodInfo DeepcloneArray = MethodReflector(() => DeepClone<object>(
new object[0], out tempObjects, null));
89 private static readonly MethodInfo DeepcloneObject = MethodReflector(() => DeepClone(ref tempObject, out tempObject, null));
91 private static readonly MethodInfo MethodAdd = typeof(Dictionary<object, object>).GetMethod(
"Add");
93 private static readonly MethodInfo MethodTryGetValue = typeof(
CloneContext).GetMethod(
"TryGetValue");
95 private static object tempObject =
new object();
97 private static object[] tempObjects =
new object[0];
103 private delegate
void CloneDelegate<T>(ref T toClone, out T output,
CloneContext context);
107 #region Public Methods
109 private static Dictionary<CacheKey, Delegate> GetCache(
CloneContext context)
111 if (context.IsSelfClone)
113 return deepCacheSelf ?? (deepCacheSelf =
new Dictionary<CacheKey, Delegate>());
116 return deepcache ?? (deepcache =
new Dictionary<CacheKey, Delegate>());
131 if (ReferenceEquals(obj, null))
138 context.Cache = GetCache(context);
140 DeepClone(ref obj, out result, context);
154 throw new ArgumentNullException(
"context");
157 context.IsSelfClone =
true;
158 DeepClone(obj, context);
159 context.IsSelfClone =
false;
166 private static void DeepClone<T>(ref T obj, out T
dest,
CloneContext context)
168 if (ReferenceEquals(obj, null))
172 var t = obj.GetType();
173 if (IsPrimitive(t) || obj is Type)
177 else if (typeof(T) == t && t.IsValueType)
179 GetObjectCloner<T>(typeof(T), context)(ref obj, out
dest, context);
183 object localDest = null;
186 GetObjectCloner<T>(t, context)(ref obj, out
dest, context);
196 private static void DeepClone<T>(
T[] obj, out
T[]
dest, CloneContext context)
198 if (ReferenceEquals(obj, null))
204 dest = (T[])arrayObj;
208 if (IsPrimitive(t) || t == typeof(Type))
210 dest = context.IsSelfClone ? obj : (
T[])obj.Clone();
214 dest = context.IsSelfClone ? obj :
new T[obj.Length];
215 context.Add(obj,
dest);
216 for (var i = 0; i < dest.Length; i++)
223 private static List<FieldInfo> GetFields(Type type)
225 var fields =
new List<FieldInfo>();
229 fields.AddRange(t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic));
236 private static CloneDelegate<T> GetObjectCloner<T>(
Type type, CloneContext context)
239 var cache = context.Cache;
241 var key =
new CacheKey(typeof(T), type);
242 if (!cache.TryGetValue(key, out result))
244 var statements =
new List<LinqExpression>();
246 var param = LinqExpression.Parameter(typeof(T).MakeByRefType(),
"input");
247 var output = LinqExpression.Parameter(typeof(T).MakeByRefType(),
"output");
248 var contextParam = LinqExpression.Parameter(typeof(CloneContext),
"context");
250 ParameterExpression localCast;
251 ParameterExpression localOutput;
252 ParameterExpression localObj;
253 bool isStruct = type.IsValueType && typeof(T) == type;
258 localOutput = output;
263 localObj = LinqExpression.Variable(typeof(
object),
"localObj");
264 localCast = LinqExpression.Variable(type,
"localCast");
265 localOutput = LinqExpression.Variable(type,
"localOut");
267 statements.Add(LinqExpression.Assign(localCast, LinqExpression.Convert(param, type)));
269 if (context.IsSelfClone)
271 statements.Add(LinqExpression.Call(contextParam, MethodAdd, param, param));
272 statements.Add(LinqExpression.Assign(output, localCast));
278 statements.Add(LinqExpression.Assign(localOutput, LinqExpression.New(type)));
281 statements.Add(LinqExpression.Assign(output, localOutput));
282 statements.Add(LinqExpression.Call(contextParam, MethodAdd, param, localOutput));
286 var fields = GetFields(type);
289 if (isStruct && fields.All(field => IsPrimitive(field.FieldType)))
291 statements.Add(LinqExpression.Assign(localOutput, param));
295 foreach (var field
in fields)
297 if (field.IsInitOnly)
298 throw new InvalidOperationException(
String.Format(
"Field [{0}] in [{1}] is readonly, which is not supported in DeepClone", field.Name, type));
300 var t = field.FieldType;
301 if (t.IsSubclassOf(typeof(Delegate)))
304 var value = LinqExpression.Field(localCast, field);
309 if (!context.IsSelfClone)
311 cloneField = LinqExpression.Assign(LinqExpression.Field(localOutput, field), value);
316 MethodInfo genericCloneMethod;
317 if (field.FieldType.IsArray)
319 genericCloneMethod = DeepcloneArray.GetGenericMethodDefinition();
320 genericCloneMethod = genericCloneMethod.MakeGenericMethod(
new[] {field.FieldType.GetElementType()});
324 genericCloneMethod = DeepcloneObject.GetGenericMethodDefinition();
325 genericCloneMethod = genericCloneMethod.MakeGenericMethod(
new[] {field.FieldType});
328 cloneField = LinqExpression.Call(genericCloneMethod, value, context.IsSelfClone ? value : LinqExpression.Field(localOutput, field), contextParam);
331 if (cloneField != null)
333 statements.Add(cloneField);
341 if (statements.Count == 0)
343 body = LinqExpression.Empty();
347 body = LinqExpression.Block(statements);
352 var innerBody = LinqExpression.Block(
new[] { localCast, localOutput }, statements);
354 body = LinqExpression.Block(
356 LinqExpression.IfThenElse(
357 LinqExpression.Call(contextParam, MethodTryGetValue, param, localObj),
LinqExpression.Assign(output,
LinqExpression.Convert(localObj, typeof(T))), innerBody));
360 var tempLambda = LinqExpression.Lambda<CloneDelegate<T>>(body, param, output, contextParam);
361 result = tempLambda.Compile();
362 cache.Add(key, result);
365 return (CloneDelegate<T>)result;
368 private static bool IsPrimitive(Type type)
370 return type.IsPrimitive || type.IsEnum || type == typeof(
string);
373 private static MethodInfo MethodReflector(Expression<Action> access)
375 return ((MethodCallExpression)access.Body).Method;
378 internal struct CacheKey : IEquatable<CacheKey>
380 private readonly
Type type;
381 private readonly
Type realType;
383 public CacheKey(Type type, Type realType)
387 this.realType = realType;
390 public bool Equals(CacheKey other)
392 return type == other.type && realType == other.realType;
395 public override bool Equals(
object obj)
397 if (ReferenceEquals(null, obj))
return false;
398 return obj is CacheKey && Equals((CacheKey)obj);
401 public override int GetHashCode()
403 return (type.GetHashCode() * 397) ^ realType.GetHashCode();
new bool TryGetValue(object key, out object value)
CloneContext()
Initializes a new instance of the CloneContext class.
new bool ContainsKey(object key)
CloneContext(CloneContext parent)
Initializes a new instance of the CloneContext class.
The type of the serialized type will be passed as a generic arguments of the serializer. Example: serializer of A becomes instantiated as Serializer{A}.
Provides a dictionary of cloned values, where the [key] is the original object and [value] the new ob...
System.Linq.Expressions.Expression LinqExpression