Initial Commit

This commit is contained in:
Sebastian Cabrera 2021-08-02 05:44:37 -04:00
parent 53eb92e9af
commit 270ab7d11f
15341 changed files with 700234 additions and 0 deletions

View file

@ -0,0 +1,44 @@
using System;
namespace Unity.VisualScripting
{
public abstract class Cloner<T> : ICloner
{
protected Cloner() { }
public abstract bool Handles(Type type);
void ICloner.BeforeClone(Type type, object original)
{
BeforeClone(type, (T)original);
}
public virtual void BeforeClone(Type type, T original) { }
object ICloner.ConstructClone(Type type, object original)
{
return ConstructClone(type, (T)original);
}
public virtual T ConstructClone(Type type, T original)
{
return (T)Activator.CreateInstance(type, true);
}
void ICloner.FillClone(Type type, ref object clone, object original, CloningContext context)
{
var _instance = (T)clone;
FillClone(type, ref _instance, (T)original, context);
clone = _instance;
}
public virtual void FillClone(Type type, ref T clone, T original, CloningContext context) { }
void ICloner.AfterClone(Type type, object clone)
{
AfterClone(type, (T)clone);
}
public virtual void AfterClone(Type type, T clone) { }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 50985218b7f8c4670b61869830f5ff7f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c562f70f440ba462ba456f970152ebb1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,31 @@
using System;
using UnityEngine;
namespace Unity.VisualScripting
{
public sealed class AnimationCurveCloner : Cloner<AnimationCurve>
{
public override bool Handles(Type type)
{
return type == typeof(AnimationCurve);
}
public override AnimationCurve ConstructClone(Type type, AnimationCurve original)
{
return new AnimationCurve();
}
public override void FillClone(Type type, ref AnimationCurve clone, AnimationCurve original, CloningContext context)
{
for (int i = 0; i < clone.length; i++)
{
clone.RemoveKey(i);
}
foreach (var key in original.keys)
{
clone.AddKey(key);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f94de5bcee76742e2adc9ac35bfbcede
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,29 @@
using System;
namespace Unity.VisualScripting
{
public sealed class ArrayCloner : Cloner<Array>
{
public override bool Handles(Type type)
{
return type.IsArray;
}
public override Array ConstructClone(Type type, Array original)
{
return Array.CreateInstance(type.GetElementType(), 0);
}
public override void FillClone(Type type, ref Array clone, Array original, CloningContext context)
{
var length = original.GetLength(0);
clone = Array.CreateInstance(type.GetElementType(), length);
for (int i = 0; i < length; i++)
{
clone.SetValue(Cloning.Clone(context, original.GetValue(i)), i);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0a7f87d6d28e64ed9b331c4d12c46937
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,31 @@
using System;
using System.Collections;
namespace Unity.VisualScripting
{
public sealed class DictionaryCloner : Cloner<IDictionary>
{
public override bool Handles(Type type)
{
return typeof(IDictionary).IsAssignableFrom(type);
}
public override void FillClone(Type type, ref IDictionary clone, IDictionary original, CloningContext context)
{
// No support for instance preservation here, but none in FS either, so it shouldn't matter
var originalEnumerator = original.GetEnumerator();
while (originalEnumerator.MoveNext())
{
var originalKey = originalEnumerator.Key;
var originalValue = originalEnumerator.Value;
var cloneKey = Cloning.Clone(context, originalKey);
var cloneValue = Cloning.Clone(context, originalValue);
clone.Add(cloneKey, cloneValue);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bb2273f662a444114949392f30936c92
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,50 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting.FullSerializer;
using Unity.VisualScripting.FullSerializer.Internal;
namespace Unity.VisualScripting
{
public sealed class EnumerableCloner : Cloner<IEnumerable>
{
public override bool Handles(Type type)
{
return typeof(IEnumerable).IsAssignableFrom(type) && !typeof(IList).IsAssignableFrom(type) && GetAddMethod(type) != null;
}
public override void FillClone(Type type, ref IEnumerable clone, IEnumerable original, CloningContext context)
{
var addMethod = GetAddMethod(type);
if (addMethod == null)
{
throw new InvalidOperationException($"Cannot instantiate enumerable type '{type}' because it does not provide an add method.");
}
// No way to preserve instances here
foreach (var item in original)
{
addMethod.Invoke(item, Cloning.Clone(context, item));
}
}
private readonly Dictionary<Type, IOptimizedInvoker> addMethods = new Dictionary<Type, IOptimizedInvoker>();
private IOptimizedInvoker GetAddMethod(Type type)
{
if (!addMethods.ContainsKey(type))
{
var addMethod = fsReflectionUtility.GetInterface(type, typeof(ICollection<>))?.GetDeclaredMethod("Add") ??
type.GetFlattenedMethod("Add") ??
type.GetFlattenedMethod("Push") ??
type.GetFlattenedMethod("Enqueue");
addMethods.Add(type, addMethod?.Prewarm());
}
return addMethods[type];
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b6d1033b3313d4b48a5985fa78ec5621
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Unity.VisualScripting.FullSerializer;
using UnityEngine;
namespace Unity.VisualScripting
{
public sealed class FakeSerializationCloner : ReflectedCloner
{
public fsConfig config { get; set; } = new fsConfig();
public override void BeforeClone(Type type, object original)
{
(original as ISerializationCallbackReceiver)?.OnBeforeSerialize();
}
public override void AfterClone(Type type, object clone)
{
(clone as ISerializationCallbackReceiver)?.OnAfterDeserialize();
}
protected override IEnumerable<MemberInfo> GetMembers(Type type)
{
return fsMetaType.Get(config, type).Properties.Select(p => p._memberInfo);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d112c25d2373f447a965ce5f734e2a67
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,17 @@
using System.Reflection;
namespace Unity.VisualScripting
{
public sealed class FieldsCloner : ReflectedCloner
{
protected override bool IncludeField(FieldInfo field)
{
return true;
}
protected override bool IncludeProperty(PropertyInfo property)
{
return false;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f3a95ea3668ca49bba7f93586f8e08ce
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,48 @@
using System;
using System.Collections;
namespace Unity.VisualScripting
{
public sealed class ListCloner : Cloner<IList>
{
public override bool Handles(Type type)
{
return typeof(IList).IsAssignableFrom(type);
}
public override void FillClone(Type type, ref IList clone, IList original, CloningContext context)
{
if (context.tryPreserveInstances)
{
for (int i = 0; i < original.Count; i++)
{
var originalItem = original[i];
if (i < clone.Count)
{
// The clone has at least as many items, we can try to preserve its instances.
var cloneItem = clone[i];
Cloning.CloneInto(context, ref cloneItem, originalItem);
clone[i] = cloneItem;
}
else
{
// The clone has less items than the original, we have to add a new item.
clone.Add(Cloning.Clone(context, originalItem));
}
}
}
else
{
// Avoiding foreach to avoid enumerator allocation
for (int i = 0; i < original.Count; i++)
{
var originalItem = original[i];
clone.Add(Cloning.Clone(context, originalItem));
}
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cfddb429b323a4b0eb5bbfb7c5ef6ac0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,130 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Unity.VisualScripting
{
public abstract class ReflectedCloner : Cloner<object>
{
public override bool Handles(Type type)
{
return false; // Should only be used as a fallback cloner
}
public override void FillClone(Type type, ref object clone, object original, CloningContext context)
{
if (PlatformUtility.supportsJit)
{
foreach (var accessor in GetOptimizedAccessors(type))
{
if (context.tryPreserveInstances)
{
var cloneProperty = accessor.GetValue(clone);
Cloning.CloneInto(context, ref cloneProperty, accessor.GetValue(original));
accessor.SetValue(clone, cloneProperty);
}
else
{
accessor.SetValue(clone, Cloning.Clone(context, accessor.GetValue(original)));
}
}
}
else
{
foreach (var accessor in GetAccessors(type))
{
if (accessor is FieldInfo)
{
var field = (FieldInfo)accessor;
if (context.tryPreserveInstances)
{
var cloneProperty = field.GetValue(clone);
Cloning.CloneInto(context, ref cloneProperty, field.GetValue(original));
field.SetValue(clone, cloneProperty);
}
else
{
field.SetValue(clone, Cloning.Clone(context, field.GetValue(original)));
}
}
else if (accessor is PropertyInfo)
{
var property = (PropertyInfo)accessor;
if (context.tryPreserveInstances)
{
var cloneProperty = property.GetValue(clone, null);
Cloning.CloneInto(context, ref cloneProperty, property.GetValue(original, null));
property.SetValue(clone, cloneProperty, null);
}
else
{
property.SetValue(clone, Cloning.Clone(context, property.GetValue(original, null)), null);
}
}
}
}
}
private readonly Dictionary<Type, MemberInfo[]> accessors = new Dictionary<Type, MemberInfo[]>();
private MemberInfo[] GetAccessors(Type type)
{
if (!accessors.ContainsKey(type))
{
accessors.Add(type, GetMembers(type).ToArray());
}
return accessors[type];
}
private readonly Dictionary<Type, IOptimizedAccessor[]> optimizedAccessors = new Dictionary<Type, IOptimizedAccessor[]>();
private IOptimizedAccessor[] GetOptimizedAccessors(Type type)
{
if (!optimizedAccessors.ContainsKey(type))
{
var list = new List<IOptimizedAccessor>();
foreach (var member in GetMembers(type))
{
if (member is FieldInfo)
{
list.Add(((FieldInfo)member).Prewarm());
}
else if (member is PropertyInfo)
{
list.Add(((PropertyInfo)member).Prewarm());
}
}
optimizedAccessors.Add(type, list.ToArray());
}
return optimizedAccessors[type];
}
protected virtual IEnumerable<MemberInfo> GetMembers(Type type)
{
var bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
return LinqUtility.Concat<MemberInfo>
(
type.GetFields(bindingFlags).Where(IncludeField),
type.GetProperties(bindingFlags).Where(IncludeProperty)
);
}
protected virtual bool IncludeField(FieldInfo field)
{
return false;
}
protected virtual bool IncludeProperty(PropertyInfo property)
{
return false;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9171b20d783a745a78171a47606f8843
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using UnityObject = UnityEngine.Object;
namespace Unity.VisualScripting
{
public static class Cloning
{
static Cloning()
{
cloners.Add(arrayCloner);
cloners.Add(dictionaryCloner);
cloners.Add(enumerableCloner);
cloners.Add(listCloner);
cloners.Add(animationCurveCloner);
}
// Cloning has to be really fast, and skippable takes a while.
private static readonly Dictionary<Type, bool> skippable = new Dictionary<Type, bool>();
public static HashSet<ICloner> cloners { get; } = new HashSet<ICloner>();
public static ArrayCloner arrayCloner { get; } = new ArrayCloner();
public static DictionaryCloner dictionaryCloner { get; } = new DictionaryCloner();
public static EnumerableCloner enumerableCloner { get; } = new EnumerableCloner();
public static ListCloner listCloner { get; } = new ListCloner();
public static AnimationCurveCloner animationCurveCloner { get; } = new AnimationCurveCloner();
public static FieldsCloner fieldsCloner { get; } = new FieldsCloner();
public static FakeSerializationCloner fakeSerializationCloner { get; } = new FakeSerializationCloner();
public static object Clone(this object original, ICloner fallbackCloner, bool tryPreserveInstances)
{
using (var context = CloningContext.New(fallbackCloner, tryPreserveInstances))
{
return Clone(context, original);
}
}
public static T Clone<T>(this T original, ICloner fallbackCloner, bool tryPreserveInstances)
{
return (T)Clone((object)original, fallbackCloner, tryPreserveInstances);
}
public static object CloneViaFakeSerialization(this object original)
{
return original.Clone(fakeSerializationCloner, true);
}
public static T CloneViaFakeSerialization<T>(this T original)
{
return (T)CloneViaFakeSerialization((object)original);
}
internal static object Clone(CloningContext context, object original)
{
object clone = null;
CloneInto(context, ref clone, original);
return clone;
}
internal static void CloneInto(CloningContext context, ref object clone, object original)
{
if (original == null)
{
clone = null;
return;
}
var type = original.GetType();
if (Skippable(type))
{
clone = original;
return;
}
if (context.clonings.ContainsKey(original))
{
clone = context.clonings[original];
return;
}
var cloner = GetCloner(original, type, context.fallbackCloner);
if (clone == null)
{
clone = cloner.ConstructClone(type, original);
}
context.clonings.Add(original, clone);
cloner.BeforeClone(type, original);
cloner.FillClone(type, ref clone, original, context);
cloner.AfterClone(type, clone);
context.clonings[original] = clone; // In case the reference changed, for example in arrays
}
[CanBeNull]
public static ICloner GetCloner(object original, Type type)
{
if (original is ISpecifiesCloner cloneableVia)
{
return cloneableVia.cloner;
}
return cloners.FirstOrDefault(cloner => cloner.Handles(type));
}
private static ICloner GetCloner(object original, Type type, ICloner fallbackCloner)
{
var cloner = GetCloner(original, type);
if (cloner != null)
return cloner;
Ensure.That(nameof(fallbackCloner)).IsNotNull(fallbackCloner);
return fallbackCloner;
}
private static bool Skippable(Type type)
{
bool result;
if (!skippable.TryGetValue(type, out result))
{
result = type.IsValueType || // Value types are copied on assignment, so no cloning is necessary
type == typeof(string) || // Strings have copy on write semantics as well, but aren't value types
typeof(Type).IsAssignableFrom(type) || // Types are guaranteed to be singletons. Using inheritance because MonoType/RuntimeType extend Type
typeof(UnityObject).IsAssignableFrom(type); // Unity objects act as pure references
skippable.Add(type, result);
}
return result;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: caccb2b4912fd42848baed54c72fcaec
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
namespace Unity.VisualScripting
{
public sealed class CloningContext : IPoolable, IDisposable
{
public Dictionary<object, object> clonings { get; } = new Dictionary<object, object>(ReferenceEqualityComparer.Instance);
public ICloner fallbackCloner { get; private set; }
public bool tryPreserveInstances { get; private set; }
private bool disposed;
void IPoolable.New()
{
disposed = false;
}
void IPoolable.Free()
{
disposed = true;
clonings.Clear();
}
public void Dispose()
{
if (disposed)
{
throw new ObjectDisposedException(ToString());
}
GenericPool<CloningContext>.Free(this);
}
public static CloningContext New(ICloner fallbackCloner, bool tryPreserveInstances)
{
var context = GenericPool<CloningContext>.New(() => new CloningContext());
context.fallbackCloner = fallbackCloner;
context.tryPreserveInstances = tryPreserveInstances;
return context;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 006f225b9646c461aa078fc0b336581e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,13 @@
using System;
namespace Unity.VisualScripting
{
public interface ICloner
{
bool Handles(Type type);
object ConstructClone(Type type, object original);
void BeforeClone(Type type, object original);
void FillClone(Type type, ref object clone, object original, CloningContext context);
void AfterClone(Type type, object clone);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b9b7b894041594292acd8c04daf20f83
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,7 @@
namespace Unity.VisualScripting
{
public interface ISpecifiesCloner
{
ICloner cloner { get; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c30372978c88446b390054f403f3e0ad
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: