Initial Commit
This commit is contained in:
parent
53eb92e9af
commit
270ab7d11f
15341 changed files with 700234 additions and 0 deletions
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d9533b5df35dc4eb1ae391f2b0f7fede
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,213 @@
|
|||
// Copyright Christophe Bertrand.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Unity.VisualScripting.AssemblyQualifiedNameParser
|
||||
{
|
||||
public class ParsedAssemblyQualifiedName
|
||||
{
|
||||
public string AssemblyDescriptionString { get; }
|
||||
|
||||
public string TypeName { get; private set; }
|
||||
|
||||
public string ShortAssemblyName { get; }
|
||||
|
||||
public string Version { get; }
|
||||
|
||||
public string Culture { get; }
|
||||
|
||||
public string PublicKeyToken { get; }
|
||||
|
||||
public List<ParsedAssemblyQualifiedName> GenericParameters { get; } = new List<ParsedAssemblyQualifiedName>();
|
||||
|
||||
public int GenericParameterCount { get; }
|
||||
|
||||
public ParsedAssemblyQualifiedName(string AssemblyQualifiedName)
|
||||
{
|
||||
var typeNameLength = AssemblyQualifiedName.Length;
|
||||
var hasAssemblyDescription = false;
|
||||
|
||||
var rootBlock = new Block();
|
||||
{
|
||||
var depth = 0;
|
||||
var currentBlock = rootBlock;
|
||||
|
||||
for (var index = 0; index < AssemblyQualifiedName.Length; ++index)
|
||||
{
|
||||
var c = AssemblyQualifiedName[index];
|
||||
|
||||
if (c == '[')
|
||||
{
|
||||
if (AssemblyQualifiedName[index + 1] == ']') // Array type // TODO (LAZLO): This won't detect multidimensional array, but FS can't handle them anyway
|
||||
{
|
||||
index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (depth == 0)
|
||||
{
|
||||
typeNameLength = index;
|
||||
}
|
||||
|
||||
++depth;
|
||||
|
||||
var innerBlock = new Block
|
||||
{
|
||||
startIndex = index + 1,
|
||||
level = depth,
|
||||
parentBlock = currentBlock
|
||||
};
|
||||
|
||||
currentBlock.innerBlocks.Add(innerBlock);
|
||||
|
||||
currentBlock = innerBlock;
|
||||
}
|
||||
}
|
||||
else if (c == ']')
|
||||
{
|
||||
currentBlock.endIndex = index - 1;
|
||||
|
||||
if (AssemblyQualifiedName[currentBlock.startIndex] != '[')
|
||||
{
|
||||
currentBlock.parsedAssemblyQualifiedName = new ParsedAssemblyQualifiedName(AssemblyQualifiedName.Substring(currentBlock.startIndex, index - currentBlock.startIndex));
|
||||
|
||||
if (depth == 2)
|
||||
{
|
||||
GenericParameters.Add(currentBlock.parsedAssemblyQualifiedName);
|
||||
}
|
||||
}
|
||||
|
||||
currentBlock = currentBlock.parentBlock;
|
||||
--depth;
|
||||
}
|
||||
else if (depth == 0 && c == ',')
|
||||
{
|
||||
typeNameLength = index;
|
||||
hasAssemblyDescription = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TypeName = AssemblyQualifiedName.Substring(0, typeNameLength);
|
||||
|
||||
var tickIndex = TypeName.IndexOf('`');
|
||||
|
||||
if (tickIndex >= 0)
|
||||
{
|
||||
GenericParameterCount = int.Parse(TypeName.Substring(tickIndex + 1));
|
||||
TypeName = TypeName.Substring(0, tickIndex);
|
||||
}
|
||||
|
||||
if (hasAssemblyDescription)
|
||||
{
|
||||
AssemblyDescriptionString = AssemblyQualifiedName.Substring(typeNameLength + 2);
|
||||
|
||||
var parts = AssemblyDescriptionString.Split(',')
|
||||
.Select(x => x.Trim())
|
||||
.ToList();
|
||||
|
||||
Version = LookForPairThenRemove(parts, "Version");
|
||||
Culture = LookForPairThenRemove(parts, "Culture");
|
||||
PublicKeyToken = LookForPairThenRemove(parts, "PublicKeyToken");
|
||||
|
||||
if (parts.Count > 0)
|
||||
{
|
||||
ShortAssemblyName = parts[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Block
|
||||
{
|
||||
internal int startIndex;
|
||||
|
||||
internal int endIndex;
|
||||
|
||||
internal int level;
|
||||
|
||||
internal Block parentBlock;
|
||||
|
||||
internal readonly List<Block> innerBlocks = new List<Block>();
|
||||
|
||||
internal ParsedAssemblyQualifiedName parsedAssemblyQualifiedName;
|
||||
}
|
||||
|
||||
private static string LookForPairThenRemove(List<string> strings, string Name)
|
||||
{
|
||||
for (var istr = 0; istr < strings.Count; istr++)
|
||||
{
|
||||
var s = strings[istr];
|
||||
var i = s.IndexOf(Name);
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
var i2 = s.IndexOf('=');
|
||||
|
||||
if (i2 > 0)
|
||||
{
|
||||
var ret = s.Substring(i2 + 1);
|
||||
strings.RemoveAt(istr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void Replace(string oldTypeName, string newTypeName)
|
||||
{
|
||||
if (TypeName == oldTypeName)
|
||||
{
|
||||
TypeName = newTypeName;
|
||||
}
|
||||
|
||||
foreach (var genericParameter in GenericParameters)
|
||||
{
|
||||
genericParameter.Replace(oldTypeName, newTypeName);
|
||||
}
|
||||
}
|
||||
|
||||
private string ToString(bool includeAssemblyDescription)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.Append(TypeName);
|
||||
|
||||
if (GenericParameters.Count > 0)
|
||||
{
|
||||
sb.Append("`");
|
||||
|
||||
sb.Append(GenericParameterCount);
|
||||
|
||||
sb.Append("[[");
|
||||
|
||||
foreach (var genericParameter in GenericParameters)
|
||||
{
|
||||
sb.Append(genericParameter.ToString(true));
|
||||
}
|
||||
|
||||
sb.Append("]]");
|
||||
}
|
||||
|
||||
if (includeAssemblyDescription)
|
||||
{
|
||||
sb.Append(", ");
|
||||
|
||||
sb.Append(AssemblyDescriptionString);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return ToString(false);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5d92a04ad12d04b89acd2055df01a8b2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: febf30edd777246c5afbf74d55f387d7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d987e9d47d9a848d181e7806e6c9013f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 50b35552dbca348bc84a9fcbd885547c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,51 @@
|
|||
#if !NO_UNITY
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
partial class fsConverterRegistrar
|
||||
{
|
||||
public static AnimationCurve_DirectConverter Register_AnimationCurve_DirectConverter;
|
||||
}
|
||||
|
||||
public class AnimationCurve_DirectConverter : fsDirectConverter<AnimationCurve>
|
||||
{
|
||||
protected override fsResult DoSerialize(AnimationCurve model, Dictionary<string, fsData> serialized)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
result += SerializeMember(serialized, null, "keys", model.keys);
|
||||
result += SerializeMember(serialized, null, "preWrapMode", model.preWrapMode);
|
||||
result += SerializeMember(serialized, null, "postWrapMode", model.postWrapMode);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override fsResult DoDeserialize(Dictionary<string, fsData> data, ref AnimationCurve model)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
var t0 = model.keys;
|
||||
result += DeserializeMember(data, null, "keys", out t0);
|
||||
model.keys = t0;
|
||||
|
||||
var t1 = model.preWrapMode;
|
||||
result += DeserializeMember(data, null, "preWrapMode", out t1);
|
||||
model.preWrapMode = t1;
|
||||
|
||||
var t2 = model.postWrapMode;
|
||||
result += DeserializeMember(data, null, "postWrapMode", out t2);
|
||||
model.postWrapMode = t2;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override object CreateInstance(fsData data, Type storageType)
|
||||
{
|
||||
return new AnimationCurve();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4f71cb511b7b0451c9b23f8b4399ea27
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,47 @@
|
|||
#if !NO_UNITY
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
partial class fsConverterRegistrar
|
||||
{
|
||||
public static Bounds_DirectConverter Register_Bounds_DirectConverter;
|
||||
}
|
||||
|
||||
public class Bounds_DirectConverter : fsDirectConverter<Bounds>
|
||||
{
|
||||
protected override fsResult DoSerialize(Bounds model, Dictionary<string, fsData> serialized)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
result += SerializeMember(serialized, null, "center", model.center);
|
||||
result += SerializeMember(serialized, null, "size", model.size);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override fsResult DoDeserialize(Dictionary<string, fsData> data, ref Bounds model)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
var t0 = model.center;
|
||||
result += DeserializeMember(data, null, "center", out t0);
|
||||
model.center = t0;
|
||||
|
||||
var t1 = model.size;
|
||||
result += DeserializeMember(data, null, "size", out t1);
|
||||
model.size = t1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override object CreateInstance(fsData data, Type storageType)
|
||||
{
|
||||
return new Bounds();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6c8baf67a14784077be24b77996cc717
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,46 @@
|
|||
#if !NO_UNITY
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
partial class fsConverterRegistrar
|
||||
{
|
||||
public static GUIStyleState_DirectConverter Register_GUIStyleState_DirectConverter;
|
||||
}
|
||||
|
||||
public class GUIStyleState_DirectConverter : fsDirectConverter<GUIStyleState>
|
||||
{
|
||||
protected override fsResult DoSerialize(GUIStyleState model, Dictionary<string, fsData> serialized)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
result += SerializeMember(serialized, null, "background", model.background);
|
||||
result += SerializeMember(serialized, null, "textColor", model.textColor);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override fsResult DoDeserialize(Dictionary<string, fsData> data, ref GUIStyleState model)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
var t0 = model.background;
|
||||
result += DeserializeMember(data, null, "background", out t0);
|
||||
model.background = t0;
|
||||
|
||||
var t2 = model.textColor;
|
||||
result += DeserializeMember(data, null, "textColor", out t2);
|
||||
model.textColor = t2;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override object CreateInstance(fsData data, Type storageType)
|
||||
{
|
||||
return new GUIStyleState();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f0ec7b8aa104e452a82dedb597e2d987
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,166 @@
|
|||
#if !NO_UNITY
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
partial class fsConverterRegistrar
|
||||
{
|
||||
public static GUIStyle_DirectConverter Register_GUIStyle_DirectConverter;
|
||||
}
|
||||
|
||||
public class GUIStyle_DirectConverter : fsDirectConverter<GUIStyle>
|
||||
{
|
||||
protected override fsResult DoSerialize(GUIStyle model, Dictionary<string, fsData> serialized)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
result += SerializeMember(serialized, null, "active", model.active);
|
||||
result += SerializeMember(serialized, null, "alignment", model.alignment);
|
||||
result += SerializeMember(serialized, null, "border", model.border);
|
||||
result += SerializeMember(serialized, null, "clipping", model.clipping);
|
||||
result += SerializeMember(serialized, null, "contentOffset", model.contentOffset);
|
||||
result += SerializeMember(serialized, null, "fixedHeight", model.fixedHeight);
|
||||
result += SerializeMember(serialized, null, "fixedWidth", model.fixedWidth);
|
||||
result += SerializeMember(serialized, null, "focused", model.focused);
|
||||
result += SerializeMember(serialized, null, "font", model.font);
|
||||
result += SerializeMember(serialized, null, "fontSize", model.fontSize);
|
||||
result += SerializeMember(serialized, null, "fontStyle", model.fontStyle);
|
||||
result += SerializeMember(serialized, null, "hover", model.hover);
|
||||
result += SerializeMember(serialized, null, "imagePosition", model.imagePosition);
|
||||
result += SerializeMember(serialized, null, "margin", model.margin);
|
||||
result += SerializeMember(serialized, null, "name", model.name);
|
||||
result += SerializeMember(serialized, null, "normal", model.normal);
|
||||
result += SerializeMember(serialized, null, "onActive", model.onActive);
|
||||
result += SerializeMember(serialized, null, "onFocused", model.onFocused);
|
||||
result += SerializeMember(serialized, null, "onHover", model.onHover);
|
||||
result += SerializeMember(serialized, null, "onNormal", model.onNormal);
|
||||
result += SerializeMember(serialized, null, "overflow", model.overflow);
|
||||
result += SerializeMember(serialized, null, "padding", model.padding);
|
||||
result += SerializeMember(serialized, null, "richText", model.richText);
|
||||
result += SerializeMember(serialized, null, "stretchHeight", model.stretchHeight);
|
||||
result += SerializeMember(serialized, null, "stretchWidth", model.stretchWidth);
|
||||
result += SerializeMember(serialized, null, "wordWrap", model.wordWrap);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override fsResult DoDeserialize(Dictionary<string, fsData> data, ref GUIStyle model)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
var t0 = model.active;
|
||||
result += DeserializeMember(data, null, "active", out t0);
|
||||
model.active = t0;
|
||||
|
||||
var t2 = model.alignment;
|
||||
result += DeserializeMember(data, null, "alignment", out t2);
|
||||
model.alignment = t2;
|
||||
|
||||
var t3 = model.border;
|
||||
result += DeserializeMember(data, null, "border", out t3);
|
||||
model.border = t3;
|
||||
|
||||
var t4 = model.clipping;
|
||||
result += DeserializeMember(data, null, "clipping", out t4);
|
||||
model.clipping = t4;
|
||||
|
||||
var t5 = model.contentOffset;
|
||||
result += DeserializeMember(data, null, "contentOffset", out t5);
|
||||
model.contentOffset = t5;
|
||||
|
||||
var t6 = model.fixedHeight;
|
||||
result += DeserializeMember(data, null, "fixedHeight", out t6);
|
||||
model.fixedHeight = t6;
|
||||
|
||||
var t7 = model.fixedWidth;
|
||||
result += DeserializeMember(data, null, "fixedWidth", out t7);
|
||||
model.fixedWidth = t7;
|
||||
|
||||
var t8 = model.focused;
|
||||
result += DeserializeMember(data, null, "focused", out t8);
|
||||
model.focused = t8;
|
||||
|
||||
var t9 = model.font;
|
||||
result += DeserializeMember(data, null, "font", out t9);
|
||||
model.font = t9;
|
||||
|
||||
var t10 = model.fontSize;
|
||||
result += DeserializeMember(data, null, "fontSize", out t10);
|
||||
model.fontSize = t10;
|
||||
|
||||
var t11 = model.fontStyle;
|
||||
result += DeserializeMember(data, null, "fontStyle", out t11);
|
||||
model.fontStyle = t11;
|
||||
|
||||
var t12 = model.hover;
|
||||
result += DeserializeMember(data, null, "hover", out t12);
|
||||
model.hover = t12;
|
||||
|
||||
var t13 = model.imagePosition;
|
||||
result += DeserializeMember(data, null, "imagePosition", out t13);
|
||||
model.imagePosition = t13;
|
||||
|
||||
var t16 = model.margin;
|
||||
result += DeserializeMember(data, null, "margin", out t16);
|
||||
model.margin = t16;
|
||||
|
||||
var t17 = model.name;
|
||||
result += DeserializeMember(data, null, "name", out t17);
|
||||
model.name = t17;
|
||||
|
||||
var t18 = model.normal;
|
||||
result += DeserializeMember(data, null, "normal", out t18);
|
||||
model.normal = t18;
|
||||
|
||||
var t19 = model.onActive;
|
||||
result += DeserializeMember(data, null, "onActive", out t19);
|
||||
model.onActive = t19;
|
||||
|
||||
var t20 = model.onFocused;
|
||||
result += DeserializeMember(data, null, "onFocused", out t20);
|
||||
model.onFocused = t20;
|
||||
|
||||
var t21 = model.onHover;
|
||||
result += DeserializeMember(data, null, "onHover", out t21);
|
||||
model.onHover = t21;
|
||||
|
||||
var t22 = model.onNormal;
|
||||
result += DeserializeMember(data, null, "onNormal", out t22);
|
||||
model.onNormal = t22;
|
||||
|
||||
var t23 = model.overflow;
|
||||
result += DeserializeMember(data, null, "overflow", out t23);
|
||||
model.overflow = t23;
|
||||
|
||||
var t24 = model.padding;
|
||||
result += DeserializeMember(data, null, "padding", out t24);
|
||||
model.padding = t24;
|
||||
|
||||
var t25 = model.richText;
|
||||
result += DeserializeMember(data, null, "richText", out t25);
|
||||
model.richText = t25;
|
||||
|
||||
var t26 = model.stretchHeight;
|
||||
result += DeserializeMember(data, null, "stretchHeight", out t26);
|
||||
model.stretchHeight = t26;
|
||||
|
||||
var t27 = model.stretchWidth;
|
||||
result += DeserializeMember(data, null, "stretchWidth", out t27);
|
||||
model.stretchWidth = t27;
|
||||
|
||||
var t28 = model.wordWrap;
|
||||
result += DeserializeMember(data, null, "wordWrap", out t28);
|
||||
model.wordWrap = t28;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override object CreateInstance(fsData data, Type storageType)
|
||||
{
|
||||
return new GUIStyle();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8baf6181321244e55ae38cb2427d1f31
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,46 @@
|
|||
#if !NO_UNITY
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
partial class fsConverterRegistrar
|
||||
{
|
||||
public static Gradient_DirectConverter Register_Gradient_DirectConverter;
|
||||
}
|
||||
|
||||
public class Gradient_DirectConverter : fsDirectConverter<Gradient>
|
||||
{
|
||||
protected override fsResult DoSerialize(Gradient model, Dictionary<string, fsData> serialized)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
result += SerializeMember(serialized, null, "alphaKeys", model.alphaKeys);
|
||||
result += SerializeMember(serialized, null, "colorKeys", model.colorKeys);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override fsResult DoDeserialize(Dictionary<string, fsData> data, ref Gradient model)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
var t0 = model.alphaKeys;
|
||||
result += DeserializeMember(data, null, "alphaKeys", out t0);
|
||||
model.alphaKeys = t0;
|
||||
|
||||
var t1 = model.colorKeys;
|
||||
result += DeserializeMember(data, null, "colorKeys", out t1);
|
||||
model.colorKeys = t1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override object CreateInstance(fsData data, Type storageType)
|
||||
{
|
||||
return new Gradient();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c375abf85cf9e42a0b84b8a8449cdeae
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,69 @@
|
|||
#if !NO_UNITY
|
||||
#if PACKAGE_INPUT_SYSTEM_EXISTS
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using JetBrains.Annotations;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
partial class fsConverterRegistrar
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public static InputAction_DirectConverter Register_InputAction_DirectConverter;
|
||||
}
|
||||
|
||||
[UsedImplicitly]
|
||||
public class InputAction_DirectConverter : fsDirectConverter<InputAction>
|
||||
{
|
||||
protected override fsResult DoSerialize(InputAction model, Dictionary<string, fsData> serialized)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
result += SerializeMember(serialized, null, "id", model.id.ToString());
|
||||
result += SerializeMember(serialized, null, "name", model.name.ToString());
|
||||
result += SerializeMember(serialized, null, "expectedControlType", model.expectedControlType);
|
||||
result += SerializeMember(serialized, null, "type", model.type);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override fsResult DoDeserialize(Dictionary<string, fsData> data, ref InputAction model)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
result += DeserializeMember(data, null, "id", out string actionId);
|
||||
result += DeserializeMember(data, null, "name", out string actionName);
|
||||
result += DeserializeMember(data, null, "expectedControlType", out string expectedControlType);
|
||||
result += DeserializeMember(data, null, "type", out InputActionType type);
|
||||
|
||||
model = MakeInputActionWithId(actionId, actionName, expectedControlType, type);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a fake InputAction. Ports with an editor MUST serialize a value of the port's type, even if a GUID
|
||||
/// would suffice in that case
|
||||
/// </summary>
|
||||
public static InputAction MakeInputActionWithId(string actionId, string actionName, string expectedControlType, InputActionType type)
|
||||
{
|
||||
var model = new InputAction();
|
||||
typeof(InputAction).GetField("m_Id", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(model, actionId);
|
||||
typeof(InputAction).GetField("m_Name", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(model, actionName);
|
||||
typeof(InputAction).GetField("m_Type", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(model, type);
|
||||
model.expectedControlType = expectedControlType;
|
||||
return model;
|
||||
}
|
||||
|
||||
public override object CreateInstance(fsData data, Type storageType)
|
||||
{
|
||||
return new InputAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 87d4a5472eeb90c42adb81dd8a4f18dd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,62 @@
|
|||
#if !NO_UNITY
|
||||
#pragma warning disable 618
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
partial class fsConverterRegistrar
|
||||
{
|
||||
public static Keyframe_DirectConverter Register_Keyframe_DirectConverter;
|
||||
}
|
||||
|
||||
public class Keyframe_DirectConverter : fsDirectConverter<Keyframe>
|
||||
{
|
||||
protected override fsResult DoSerialize(Keyframe model, Dictionary<string, fsData> serialized)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
result += SerializeMember(serialized, null, "time", model.time);
|
||||
result += SerializeMember(serialized, null, "value", model.value);
|
||||
result += SerializeMember(serialized, null, "tangentMode", model.tangentMode);
|
||||
result += SerializeMember(serialized, null, "inTangent", model.inTangent);
|
||||
result += SerializeMember(serialized, null, "outTangent", model.outTangent);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override fsResult DoDeserialize(Dictionary<string, fsData> data, ref Keyframe model)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
var t0 = model.time;
|
||||
result += DeserializeMember(data, null, "time", out t0);
|
||||
model.time = t0;
|
||||
|
||||
var t1 = model.value;
|
||||
result += DeserializeMember(data, null, "value", out t1);
|
||||
model.value = t1;
|
||||
|
||||
var t2 = model.tangentMode;
|
||||
result += DeserializeMember(data, null, "tangentMode", out t2);
|
||||
model.tangentMode = t2;
|
||||
|
||||
var t3 = model.inTangent;
|
||||
result += DeserializeMember(data, null, "inTangent", out t3);
|
||||
model.inTangent = t3;
|
||||
|
||||
var t4 = model.outTangent;
|
||||
result += DeserializeMember(data, null, "outTangent", out t4);
|
||||
model.outTangent = t4;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override object CreateInstance(fsData data, Type storageType)
|
||||
{
|
||||
return new Keyframe();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d3b511699db3543da9f36148351d5ed0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,41 @@
|
|||
#if !NO_UNITY
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
partial class fsConverterRegistrar
|
||||
{
|
||||
public static LayerMask_DirectConverter Register_LayerMask_DirectConverter;
|
||||
}
|
||||
|
||||
public class LayerMask_DirectConverter : fsDirectConverter<LayerMask>
|
||||
{
|
||||
protected override fsResult DoSerialize(LayerMask model, Dictionary<string, fsData> serialized)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
result += SerializeMember(serialized, null, "value", model.value);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override fsResult DoDeserialize(Dictionary<string, fsData> data, ref LayerMask model)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
var t0 = model.value;
|
||||
result += DeserializeMember(data, null, "value", out t0);
|
||||
model.value = t0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override object CreateInstance(fsData data, Type storageType)
|
||||
{
|
||||
return new LayerMask();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e751b86c173aa4faeb9b50cd8051ccfd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,56 @@
|
|||
#if !NO_UNITY
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
partial class fsConverterRegistrar
|
||||
{
|
||||
public static RectOffset_DirectConverter Register_RectOffset_DirectConverter;
|
||||
}
|
||||
|
||||
public class RectOffset_DirectConverter : fsDirectConverter<RectOffset>
|
||||
{
|
||||
protected override fsResult DoSerialize(RectOffset model, Dictionary<string, fsData> serialized)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
result += SerializeMember(serialized, null, "bottom", model.bottom);
|
||||
result += SerializeMember(serialized, null, "left", model.left);
|
||||
result += SerializeMember(serialized, null, "right", model.right);
|
||||
result += SerializeMember(serialized, null, "top", model.top);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override fsResult DoDeserialize(Dictionary<string, fsData> data, ref RectOffset model)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
var t0 = model.bottom;
|
||||
result += DeserializeMember(data, null, "bottom", out t0);
|
||||
model.bottom = t0;
|
||||
|
||||
var t2 = model.left;
|
||||
result += DeserializeMember(data, null, "left", out t2);
|
||||
model.left = t2;
|
||||
|
||||
var t3 = model.right;
|
||||
result += DeserializeMember(data, null, "right", out t3);
|
||||
model.right = t3;
|
||||
|
||||
var t4 = model.top;
|
||||
result += DeserializeMember(data, null, "top", out t4);
|
||||
model.top = t4;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override object CreateInstance(fsData data, Type storageType)
|
||||
{
|
||||
return new RectOffset();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c5f6a2c1ca71a441b9bfcfa0403794b4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,56 @@
|
|||
#if !NO_UNITY
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
partial class fsConverterRegistrar
|
||||
{
|
||||
public static Rect_DirectConverter Register_Rect_DirectConverter;
|
||||
}
|
||||
|
||||
public class Rect_DirectConverter : fsDirectConverter<Rect>
|
||||
{
|
||||
protected override fsResult DoSerialize(Rect model, Dictionary<string, fsData> serialized)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
result += SerializeMember(serialized, null, "xMin", model.xMin);
|
||||
result += SerializeMember(serialized, null, "yMin", model.yMin);
|
||||
result += SerializeMember(serialized, null, "xMax", model.xMax);
|
||||
result += SerializeMember(serialized, null, "yMax", model.yMax);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected override fsResult DoDeserialize(Dictionary<string, fsData> data, ref Rect model)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
var t0 = model.xMin;
|
||||
result += DeserializeMember(data, null, "xMin", out t0);
|
||||
model.xMin = t0;
|
||||
|
||||
var t1 = model.yMin;
|
||||
result += DeserializeMember(data, null, "yMin", out t1);
|
||||
model.yMin = t1;
|
||||
|
||||
var t2 = model.xMax;
|
||||
result += DeserializeMember(data, null, "xMax", out t2);
|
||||
model.xMax = t2;
|
||||
|
||||
var t3 = model.yMax;
|
||||
result += DeserializeMember(data, null, "yMax", out t3);
|
||||
model.yMax = t3;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override object CreateInstance(fsData data, Type storageType)
|
||||
{
|
||||
return new Rect();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7f819e9b71fcc40da8cab6328e655887
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,56 @@
|
|||
#if !NO_UNITY && UNITY_5_3_OR_NEWER
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
partial class fsConverterRegistrar
|
||||
{
|
||||
// Disable the converter for the time being. Unity's JsonUtility API
|
||||
// cannot be called from within a C# ISerializationCallbackReceiver
|
||||
// callback.
|
||||
|
||||
// public static Internal.Converters.UnityEvent_Converter
|
||||
// Register_UnityEvent_Converter;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer.Internal.Converters
|
||||
{
|
||||
// The standard FS reflection converter has started causing Unity to crash
|
||||
// when processing UnityEvent. We can send the serialization through
|
||||
// JsonUtility which appears to work correctly instead.
|
||||
//
|
||||
// We have to support legacy serialization formats so importing works as
|
||||
// expected.
|
||||
public class UnityEvent_Converter : fsConverter
|
||||
{
|
||||
public override bool CanProcess(Type type)
|
||||
{
|
||||
return typeof(UnityEvent).Resolve().IsAssignableFrom(type.Resolve()) && type.Resolve().IsGenericType == false;
|
||||
}
|
||||
|
||||
public override bool RequestCycleSupport(Type storageType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override fsResult TryDeserialize(fsData data, ref object instance, Type storageType)
|
||||
{
|
||||
Type objectType = (Type)instance;
|
||||
|
||||
fsResult result = fsResult.Success;
|
||||
instance = JsonUtility.FromJson(fsJsonPrinter.CompressedJson(data), objectType);
|
||||
return result;
|
||||
}
|
||||
|
||||
public override fsResult TrySerialize(object instance, out fsData serialized, Type storageType)
|
||||
{
|
||||
fsResult result = fsResult.Success;
|
||||
serialized = fsJsonParser.Parse(JsonUtility.ToJson(instance));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b805c9b714cc14a639d8dbb54ebbb81d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,106 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
public class fsArrayConverter : fsConverter
|
||||
{
|
||||
public override bool CanProcess(Type type)
|
||||
{
|
||||
return type.IsArray;
|
||||
}
|
||||
|
||||
public override bool RequestCycleSupport(Type storageType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool RequestInheritanceSupport(Type storageType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override fsResult TrySerialize(object instance, out fsData serialized, Type storageType)
|
||||
{
|
||||
// note: IList[index] is **significantly** faster than Array.Get, so
|
||||
// make sure we use that instead.
|
||||
|
||||
IList arr = (Array)instance;
|
||||
var elementType = storageType.GetElementType();
|
||||
|
||||
var result = fsResult.Success;
|
||||
|
||||
serialized = fsData.CreateList(arr.Count);
|
||||
var serializedList = serialized.AsList;
|
||||
|
||||
for (var i = 0; i < arr.Count; ++i)
|
||||
{
|
||||
var item = arr[i];
|
||||
|
||||
fsData serializedItem;
|
||||
|
||||
var itemResult = Serializer.TrySerialize(elementType, item, out serializedItem);
|
||||
result.AddMessages(itemResult);
|
||||
if (itemResult.Failed)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
serializedList.Add(serializedItem);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override fsResult TryDeserialize(fsData data, ref object instance, Type storageType)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
// Verify that we actually have an List
|
||||
if ((result += CheckType(data, fsDataType.Array)).Failed)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var elementType = storageType.GetElementType();
|
||||
|
||||
var serializedList = data.AsList;
|
||||
var list = new ArrayList(serializedList.Count);
|
||||
var existingCount = list.Count;
|
||||
|
||||
for (var i = 0; i < serializedList.Count; ++i)
|
||||
{
|
||||
var serializedItem = serializedList[i];
|
||||
object deserialized = null;
|
||||
if (i < existingCount)
|
||||
{
|
||||
deserialized = list[i];
|
||||
}
|
||||
|
||||
var itemResult = Serializer.TryDeserialize(serializedItem, elementType, ref deserialized);
|
||||
result.AddMessages(itemResult);
|
||||
if (itemResult.Failed)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i < existingCount)
|
||||
{
|
||||
list[i] = deserialized;
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(deserialized);
|
||||
}
|
||||
}
|
||||
|
||||
instance = list.ToArray(elementType);
|
||||
return result;
|
||||
}
|
||||
|
||||
public override object CreateInstance(fsData data, Type storageType)
|
||||
{
|
||||
return fsMetaType.Get(Serializer.Config, storageType).CreateInstance();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5f4c650178b4b4b79a3b5cabd17c9307
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,113 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Supports serialization for DateTime, DateTimeOffset, and TimeSpan.
|
||||
/// </summary>
|
||||
public class fsDateConverter : fsConverter
|
||||
{
|
||||
private string DateTimeFormatString => Serializer.Config.CustomDateTimeFormatString ?? DefaultDateTimeFormatString;
|
||||
|
||||
public override bool CanProcess(Type type)
|
||||
{
|
||||
return
|
||||
type == typeof(DateTime) ||
|
||||
type == typeof(DateTimeOffset) ||
|
||||
type == typeof(TimeSpan);
|
||||
}
|
||||
|
||||
public override fsResult TrySerialize(object instance, out fsData serialized, Type storageType)
|
||||
{
|
||||
if (instance is DateTime)
|
||||
{
|
||||
var dateTime = (DateTime)instance;
|
||||
serialized = new fsData(dateTime.ToString(DateTimeFormatString));
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
if (instance is DateTimeOffset)
|
||||
{
|
||||
var dateTimeOffset = (DateTimeOffset)instance;
|
||||
serialized = new fsData(dateTimeOffset.ToString(DateTimeOffsetFormatString));
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
if (instance is TimeSpan)
|
||||
{
|
||||
var timeSpan = (TimeSpan)instance;
|
||||
serialized = new fsData(timeSpan.ToString());
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("FullSerializer Internal Error -- Unexpected serialization type");
|
||||
}
|
||||
|
||||
public override fsResult TryDeserialize(fsData data, ref object instance, Type storageType)
|
||||
{
|
||||
if (data.IsString == false)
|
||||
{
|
||||
return fsResult.Fail("Date deserialization requires a string, not " + data.Type);
|
||||
}
|
||||
|
||||
if (storageType == typeof(DateTime))
|
||||
{
|
||||
DateTime result;
|
||||
if (DateTime.TryParse(data.AsString, null, DateTimeStyles.RoundtripKind, out result))
|
||||
{
|
||||
instance = result;
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
// DateTime.TryParse can fail for some valid DateTime instances.
|
||||
// Try to use Convert.ToDateTime.
|
||||
if (fsGlobalConfig.AllowInternalExceptions)
|
||||
{
|
||||
try
|
||||
{
|
||||
instance = Convert.ToDateTime(data.AsString);
|
||||
return fsResult.Success;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return fsResult.Fail("Unable to parse " + data.AsString + " into a DateTime; got exception " + e);
|
||||
}
|
||||
}
|
||||
|
||||
return fsResult.Fail("Unable to parse " + data.AsString + " into a DateTime");
|
||||
}
|
||||
|
||||
if (storageType == typeof(DateTimeOffset))
|
||||
{
|
||||
DateTimeOffset result;
|
||||
if (DateTimeOffset.TryParse(data.AsString, null, DateTimeStyles.RoundtripKind, out result))
|
||||
{
|
||||
instance = result;
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
return fsResult.Fail("Unable to parse " + data.AsString + " into a DateTimeOffset");
|
||||
}
|
||||
|
||||
if (storageType == typeof(TimeSpan))
|
||||
{
|
||||
TimeSpan result;
|
||||
if (TimeSpan.TryParse(data.AsString, out result))
|
||||
{
|
||||
instance = result;
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
return fsResult.Fail("Unable to parse " + data.AsString + " into a TimeSpan");
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("FullSerializer Internal Error -- Unexpected deserialization type");
|
||||
}
|
||||
|
||||
// The format strings that we use when serializing DateTime and
|
||||
// DateTimeOffset types.
|
||||
private const string DefaultDateTimeFormatString = @"o";
|
||||
private const string DateTimeOffsetFormatString = @"o";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b2cf6489d32e14b8ba1bec90f2dcc4bf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,232 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Unity.VisualScripting.FullSerializer.Internal;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
// While the generic IEnumerable converter can handle dictionaries, we
|
||||
// process them separately here because we support a few more advanced
|
||||
// use-cases with dictionaries, such as inline strings. Further, dictionary
|
||||
// processing in general is a bit more advanced because a few of the
|
||||
// collection implementations are buggy.
|
||||
public class fsDictionaryConverter : fsConverter
|
||||
{
|
||||
public override bool CanProcess(Type type)
|
||||
{
|
||||
return typeof(IDictionary).IsAssignableFrom(type);
|
||||
}
|
||||
|
||||
public override object CreateInstance(fsData data, Type storageType)
|
||||
{
|
||||
return fsMetaType.Get(Serializer.Config, storageType).CreateInstance();
|
||||
}
|
||||
|
||||
public override fsResult TryDeserialize(fsData data, ref object instance_, Type storageType)
|
||||
{
|
||||
var instance = (IDictionary)instance_;
|
||||
var result = fsResult.Success;
|
||||
|
||||
Type keyStorageType, valueStorageType;
|
||||
GetKeyValueTypes(instance.GetType(), out keyStorageType, out valueStorageType);
|
||||
|
||||
if (data.IsList)
|
||||
{
|
||||
var list = data.AsList;
|
||||
for (var i = 0; i < list.Count; ++i)
|
||||
{
|
||||
var item = list[i];
|
||||
|
||||
fsData keyData, valueData;
|
||||
if ((result += CheckType(item, fsDataType.Object)).Failed)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if ((result += CheckKey(item, "Key", out keyData)).Failed)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if ((result += CheckKey(item, "Value", out valueData)).Failed)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
object keyInstance = null, valueInstance = null;
|
||||
if ((result += Serializer.TryDeserialize(keyData, keyStorageType, ref keyInstance)).Failed)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if ((result += Serializer.TryDeserialize(valueData, valueStorageType, ref valueInstance)).Failed)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
AddItemToDictionary(instance, keyInstance, valueInstance);
|
||||
}
|
||||
}
|
||||
else if (data.IsDictionary)
|
||||
{
|
||||
foreach (var entry in data.AsDictionary)
|
||||
{
|
||||
if (fsSerializer.IsReservedKeyword(entry.Key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
fsData keyData = new fsData(entry.Key), valueData = entry.Value;
|
||||
object keyInstance = null, valueInstance = null;
|
||||
|
||||
if ((result += Serializer.TryDeserialize(keyData, keyStorageType, ref keyInstance)).Failed)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if ((result += Serializer.TryDeserialize(valueData, valueStorageType, ref valueInstance)).Failed)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
AddItemToDictionary(instance, keyInstance, valueInstance);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return FailExpectedType(data, fsDataType.Array, fsDataType.Object);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override fsResult TrySerialize(object instance_, out fsData serialized, Type storageType)
|
||||
{
|
||||
serialized = fsData.Null;
|
||||
|
||||
var result = fsResult.Success;
|
||||
|
||||
var instance = (IDictionary)instance_;
|
||||
|
||||
Type keyStorageType, valueStorageType;
|
||||
GetKeyValueTypes(instance.GetType(), out keyStorageType, out valueStorageType);
|
||||
|
||||
// No other way to iterate dictionaries and still have access to the
|
||||
// key/value info
|
||||
var enumerator = instance.GetEnumerator();
|
||||
|
||||
var allStringKeys = true;
|
||||
var serializedKeys = new List<fsData>(instance.Count);
|
||||
var serializedValues = new List<fsData>(instance.Count);
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
fsData keyData, valueData;
|
||||
if ((result += Serializer.TrySerialize(keyStorageType, enumerator.Key, out keyData)).Failed)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if ((result += Serializer.TrySerialize(valueStorageType, enumerator.Value, out valueData)).Failed)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
serializedKeys.Add(keyData);
|
||||
serializedValues.Add(valueData);
|
||||
|
||||
allStringKeys &= keyData.IsString;
|
||||
}
|
||||
|
||||
if (allStringKeys)
|
||||
{
|
||||
serialized = fsData.CreateDictionary();
|
||||
var serializedDictionary = serialized.AsDictionary;
|
||||
|
||||
for (var i = 0; i < serializedKeys.Count; ++i)
|
||||
{
|
||||
var key = serializedKeys[i];
|
||||
var value = serializedValues[i];
|
||||
serializedDictionary[key.AsString] = value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
serialized = fsData.CreateList(serializedKeys.Count);
|
||||
var serializedList = serialized.AsList;
|
||||
|
||||
for (var i = 0; i < serializedKeys.Count; ++i)
|
||||
{
|
||||
var key = serializedKeys[i];
|
||||
var value = serializedValues[i];
|
||||
|
||||
var container = new Dictionary<string, fsData>();
|
||||
container["Key"] = key;
|
||||
container["Value"] = value;
|
||||
serializedList.Add(new fsData(container));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private fsResult AddItemToDictionary(IDictionary dictionary, object key, object value)
|
||||
{
|
||||
// Because we're operating through the IDictionary interface by
|
||||
// default (and not the generic one), we normally send items through
|
||||
// IDictionary.Add(object, object). This works fine in the general
|
||||
// case, except that the add method verifies that it's parameter
|
||||
// types are proper types. However, mono is buggy and these type
|
||||
// checks do not consider null a subtype of the parameter types, and
|
||||
// exceptions get thrown. So, we have to special case adding null
|
||||
// items via the generic functions (which do not do the null check),
|
||||
// which is slow and messy.
|
||||
//
|
||||
// An example of a collection that fails deserialization without this
|
||||
// method is `new SortedList<int, string> { { 0, null } }`.
|
||||
// (SortedDictionary is fine because it properly handles null
|
||||
// values).
|
||||
if (key == null || value == null)
|
||||
{
|
||||
// Life would be much easier if we had MakeGenericType available,
|
||||
// but we don't. So we're going to find the correct generic
|
||||
// KeyValuePair type via a bit of trickery. All dictionaries
|
||||
// extend ICollection<KeyValuePair<TKey, TValue>>, so we just
|
||||
// fetch the ICollection<> type with the proper generic
|
||||
// arguments, and then we take the KeyValuePair<> generic
|
||||
// argument, and whola! we have our proper generic type.
|
||||
|
||||
var collectionType = fsReflectionUtility.GetInterface(dictionary.GetType(), typeof(ICollection<>));
|
||||
if (collectionType == null)
|
||||
{
|
||||
return fsResult.Warn(dictionary.GetType() + " does not extend ICollection");
|
||||
}
|
||||
|
||||
var keyValuePairType = collectionType.GetGenericArguments()[0];
|
||||
var keyValueInstance = Activator.CreateInstance(keyValuePairType, key, value);
|
||||
var add = collectionType.GetFlattenedMethod("Add");
|
||||
add.Invoke(dictionary, new[] { keyValueInstance });
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
// We use the inline set methods instead of dictionary.Add;
|
||||
// dictionary.Add will throw an exception if the key already exists.
|
||||
dictionary[key] = value;
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
private static void GetKeyValueTypes(Type dictionaryType, out Type keyStorageType, out Type valueStorageType)
|
||||
{
|
||||
// All dictionaries extend IDictionary<TKey, TValue>, so we just
|
||||
// fetch the generic arguments from it
|
||||
var interfaceType = fsReflectionUtility.GetInterface(dictionaryType, typeof(IDictionary<,>));
|
||||
if (interfaceType != null)
|
||||
{
|
||||
var genericArgs = interfaceType.GetGenericArguments();
|
||||
keyStorageType = genericArgs[0];
|
||||
valueStorageType = genericArgs[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fetching IDictionary<,> failed... we have to encode full type
|
||||
// information :(
|
||||
keyStorageType = typeof(object);
|
||||
valueStorageType = typeof(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2deb045d480ac4959bcb46744de8db3a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,161 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Unity.VisualScripting.FullSerializer.Internal;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Serializes and deserializes enums by their current name.
|
||||
/// </summary>
|
||||
public class fsEnumConverter : fsConverter
|
||||
{
|
||||
public override bool CanProcess(Type type)
|
||||
{
|
||||
return type.Resolve().IsEnum;
|
||||
}
|
||||
|
||||
public override bool RequestCycleSupport(Type storageType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool RequestInheritanceSupport(Type storageType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override object CreateInstance(fsData data, Type storageType)
|
||||
{
|
||||
// In .NET compact, Enum.ToObject(Type, Object) is defined but the
|
||||
// overloads like Enum.ToObject(Type, int) are not -- so we get
|
||||
// around this by boxing the value.
|
||||
return Enum.ToObject(storageType, (object)0);
|
||||
}
|
||||
|
||||
public override fsResult TrySerialize(object instance, out fsData serialized, Type storageType)
|
||||
{
|
||||
if (Serializer.Config.SerializeEnumsAsInteger)
|
||||
{
|
||||
serialized = new fsData(Convert.ToInt64(instance));
|
||||
}
|
||||
else if (fsPortableReflection.GetAttribute<FlagsAttribute>(storageType) != null)
|
||||
{
|
||||
var lValue = Convert.ToInt64(instance);
|
||||
var result = new StringBuilder();
|
||||
|
||||
var first = true;
|
||||
foreach (var flag in Enum.GetValues(storageType))
|
||||
{
|
||||
var lFlag = Convert.ToInt64(flag);
|
||||
|
||||
if (lFlag == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var isSet = (lValue & lFlag) == lFlag;
|
||||
|
||||
if (isSet)
|
||||
{
|
||||
if (first == false)
|
||||
{
|
||||
result.Append(",");
|
||||
}
|
||||
first = false;
|
||||
result.Append(flag.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
serialized = new fsData(result.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
serialized = new fsData(Enum.GetName(storageType, instance));
|
||||
}
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
public override fsResult TryDeserialize(fsData data, ref object instance, Type storageType)
|
||||
{
|
||||
if (data.IsString)
|
||||
{
|
||||
var enumValues = data.AsString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
for (var i = 0; i < enumValues.Length; ++i)
|
||||
{
|
||||
var enumValue = enumValues[i];
|
||||
|
||||
// Verify that the enum name exists; Enum.TryParse is only
|
||||
// available in .NET 4.0 and above :(.
|
||||
if (ArrayContains(Enum.GetNames(storageType), enumValue) == false)
|
||||
{
|
||||
return fsResult.Fail("Cannot find enum name " + enumValue + " on type " + storageType);
|
||||
}
|
||||
}
|
||||
|
||||
var underlyingType = Enum.GetUnderlyingType(storageType);
|
||||
|
||||
if (underlyingType == typeof(ulong))
|
||||
{
|
||||
ulong instanceValue = 0;
|
||||
|
||||
for (var i = 0; i < enumValues.Length; ++i)
|
||||
{
|
||||
var enumValue = enumValues[i];
|
||||
var flagValue = (ulong)Convert.ChangeType(Enum.Parse(storageType, enumValue), typeof(ulong));
|
||||
instanceValue |= flagValue;
|
||||
}
|
||||
|
||||
instance = Enum.ToObject(storageType, (object)instanceValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
long instanceValue = 0;
|
||||
|
||||
for (var i = 0; i < enumValues.Length; ++i)
|
||||
{
|
||||
var enumValue = enumValues[i];
|
||||
var flagValue = (long)Convert.ChangeType(Enum.Parse(storageType, enumValue), typeof(long));
|
||||
instanceValue |= flagValue;
|
||||
}
|
||||
|
||||
instance = Enum.ToObject(storageType, (object)instanceValue);
|
||||
}
|
||||
|
||||
return fsResult.Success;
|
||||
}
|
||||
else if (data.IsInt64)
|
||||
{
|
||||
var enumValue = (int)data.AsInt64;
|
||||
|
||||
// In .NET compact, Enum.ToObject(Type, Object) is defined but
|
||||
// the overloads like Enum.ToObject(Type, int) are not -- so we
|
||||
// get around this by boxing the value.
|
||||
instance = Enum.ToObject(storageType, (object)enumValue);
|
||||
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
return fsResult.Fail($"EnumConverter encountered an unknown JSON data type for {storageType}: {data.Type}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given value is contained within the specified
|
||||
/// array.
|
||||
/// </summary>
|
||||
private static bool ArrayContains<T>(T[] values, T value)
|
||||
{
|
||||
// note: We don't use LINQ because this function will *not* allocate
|
||||
for (var i = 0; i < values.Length; ++i)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(values[i], value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ab94a630dd6e9441ba5d116d7c1bcfd4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,107 @@
|
|||
using System;
|
||||
using Unity.VisualScripting.FullSerializer.Internal;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// This allows you to forward serialization of an object to one of its
|
||||
/// members. For example,
|
||||
/// [fsForward("Values")]
|
||||
/// struct Wrapper {
|
||||
/// public int[] Values;
|
||||
/// }
|
||||
/// Then `Wrapper` will be serialized into a JSON array of integers. It will
|
||||
/// be as if `Wrapper` doesn't exist.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct)]
|
||||
public sealed class fsForwardAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Forward object serialization to an instance member. See class
|
||||
/// comment.
|
||||
/// </summary>
|
||||
/// <param name="memberName">
|
||||
/// The name of the member that we should serialize this object as.
|
||||
/// </param>
|
||||
public fsForwardAttribute(string memberName)
|
||||
{
|
||||
MemberName = memberName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The name of the member we should serialize as.
|
||||
/// </summary>
|
||||
public string MemberName;
|
||||
}
|
||||
|
||||
public class fsForwardConverter : fsConverter
|
||||
{
|
||||
public fsForwardConverter(fsForwardAttribute attribute)
|
||||
{
|
||||
_memberName = attribute.MemberName;
|
||||
}
|
||||
|
||||
private string _memberName;
|
||||
|
||||
public override bool CanProcess(Type type)
|
||||
{
|
||||
throw new NotSupportedException("Please use the [fsForward(...)] attribute.");
|
||||
}
|
||||
|
||||
private fsResult GetProperty(object instance, out fsMetaProperty property)
|
||||
{
|
||||
var properties = fsMetaType.Get(Serializer.Config, instance.GetType()).Properties;
|
||||
for (var i = 0; i < properties.Length; ++i)
|
||||
{
|
||||
if (properties[i].MemberName == _memberName)
|
||||
{
|
||||
property = properties[i];
|
||||
return fsResult.Success;
|
||||
}
|
||||
}
|
||||
|
||||
property = default(fsMetaProperty);
|
||||
return fsResult.Fail("No property named \"" + _memberName + "\" on " + fsTypeExtensions.CSharpName(instance.GetType()));
|
||||
}
|
||||
|
||||
public override fsResult TrySerialize(object instance, out fsData serialized, Type storageType)
|
||||
{
|
||||
serialized = fsData.Null;
|
||||
var result = fsResult.Success;
|
||||
|
||||
fsMetaProperty property;
|
||||
if ((result += GetProperty(instance, out property)).Failed)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var actualInstance = property.Read(instance);
|
||||
return Serializer.TrySerialize(property.StorageType, actualInstance, out serialized);
|
||||
}
|
||||
|
||||
public override fsResult TryDeserialize(fsData data, ref object instance, Type storageType)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
fsMetaProperty property;
|
||||
if ((result += GetProperty(instance, out property)).Failed)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
object actualInstance = null;
|
||||
if ((result += Serializer.TryDeserialize(data, property.StorageType, ref actualInstance)).Failed)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
property.Write(instance, actualInstance);
|
||||
return result;
|
||||
}
|
||||
|
||||
public override object CreateInstance(fsData data, Type storageType)
|
||||
{
|
||||
return fsMetaType.Get(Serializer.Config, storageType).CreateInstance();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f85fe1ea3f9664fb9be7ce694de49076
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Serializes and deserializes guids.
|
||||
/// </summary>
|
||||
public class fsGuidConverter : fsConverter
|
||||
{
|
||||
public override bool CanProcess(Type type)
|
||||
{
|
||||
return type == typeof(Guid);
|
||||
}
|
||||
|
||||
public override bool RequestCycleSupport(Type storageType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool RequestInheritanceSupport(Type storageType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override fsResult TrySerialize(object instance, out fsData serialized, Type storageType)
|
||||
{
|
||||
var guid = (Guid)instance;
|
||||
serialized = new fsData(guid.ToString());
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
public override fsResult TryDeserialize(fsData data, ref object instance, Type storageType)
|
||||
{
|
||||
if (data.IsString)
|
||||
{
|
||||
instance = new Guid(data.AsString);
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
return fsResult.Fail("fsGuidConverter encountered an unknown JSON data type");
|
||||
}
|
||||
|
||||
public override object CreateInstance(fsData data, Type storageType)
|
||||
{
|
||||
return new Guid();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9836aacf159b449c6a57eb3c92fb6809
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,195 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Unity.VisualScripting.FullSerializer.Internal;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides serialization support for anything which extends from
|
||||
/// `IEnumerable` and has an `Add` method.
|
||||
/// </summary>
|
||||
public class fsIEnumerableConverter : fsConverter
|
||||
{
|
||||
public override bool CanProcess(Type type)
|
||||
{
|
||||
if (typeof(IEnumerable).IsAssignableFrom(type) == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return GetAddMethod(type) != null;
|
||||
}
|
||||
|
||||
public override object CreateInstance(fsData data, Type storageType)
|
||||
{
|
||||
return fsMetaType.Get(Serializer.Config, storageType).CreateInstance();
|
||||
}
|
||||
|
||||
public override fsResult TrySerialize(object instance_, out fsData serialized, Type storageType)
|
||||
{
|
||||
var instance = (IEnumerable)instance_;
|
||||
var result = fsResult.Success;
|
||||
|
||||
var elementType = GetElementType(storageType);
|
||||
|
||||
serialized = fsData.CreateList(HintSize(instance));
|
||||
var serializedList = serialized.AsList;
|
||||
|
||||
foreach (var item in instance)
|
||||
{
|
||||
fsData itemData;
|
||||
|
||||
// note: We don't fail the entire deserialization even if the
|
||||
// item failed
|
||||
var itemResult = Serializer.TrySerialize(elementType, item, out itemData);
|
||||
result.AddMessages(itemResult);
|
||||
if (itemResult.Failed)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
serializedList.Add(itemData);
|
||||
}
|
||||
|
||||
// Stacks iterate from back to front, which means when we deserialize
|
||||
// we will deserialize the items in the wrong order, so the stack
|
||||
// will get reversed.
|
||||
if (IsStack(instance.GetType()))
|
||||
{
|
||||
serializedList.Reverse();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private bool IsStack(Type type)
|
||||
{
|
||||
return type.Resolve().IsGenericType &&
|
||||
type.Resolve().GetGenericTypeDefinition() == typeof(Stack<>);
|
||||
}
|
||||
|
||||
public override fsResult TryDeserialize(fsData data, ref object instance_, Type storageType)
|
||||
{
|
||||
var instance = (IEnumerable)instance_;
|
||||
var result = fsResult.Success;
|
||||
|
||||
if ((result += CheckType(data, fsDataType.Array)).Failed)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// LAZLO/LUDIQ: Changes to default behaviour.
|
||||
// - Do not try to serialize into existing element; always clear and add
|
||||
// (more reliable, compatible with custom indexers)
|
||||
// - If the type is a list, add a default element on failure to prevent
|
||||
// messing up the order.
|
||||
// - Commented out that last change.
|
||||
|
||||
var elementStorageType = GetElementType(storageType);
|
||||
var addMethod = GetAddMethod(storageType);
|
||||
TryClear(storageType, instance);
|
||||
|
||||
var serializedList = data.AsList;
|
||||
|
||||
for (var i = 0; i < serializedList.Count; ++i)
|
||||
{
|
||||
var itemData = serializedList[i];
|
||||
object itemInstance = null;
|
||||
|
||||
var itemResult = Serializer.TryDeserialize(itemData, elementStorageType, ref itemInstance);
|
||||
|
||||
result.AddMessages(itemResult);
|
||||
|
||||
if (itemResult.Succeeded)
|
||||
{
|
||||
addMethod.Invoke(instance, new[] { itemInstance });
|
||||
}
|
||||
// else
|
||||
// {
|
||||
// if (typeof(IList).IsAssignableFrom(storageType))
|
||||
// {
|
||||
// addMethod.Invoke(instance, new[] { elementStorageType.Default() });
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int HintSize(IEnumerable collection)
|
||||
{
|
||||
if (collection is ICollection)
|
||||
{
|
||||
return ((ICollection)collection).Count;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches the element type for objects inside of the collection.
|
||||
/// </summary>
|
||||
private static Type GetElementType(Type objectType)
|
||||
{
|
||||
if (objectType.HasElementType)
|
||||
{
|
||||
return objectType.GetElementType();
|
||||
}
|
||||
|
||||
var enumerableList = fsReflectionUtility.GetInterface(objectType, typeof(IEnumerable<>));
|
||||
if (enumerableList != null)
|
||||
{
|
||||
return enumerableList.GetGenericArguments()[0];
|
||||
}
|
||||
|
||||
return typeof(object);
|
||||
}
|
||||
|
||||
private static void TryClear(Type type, object instance)
|
||||
{
|
||||
var clear = type.GetFlattenedMethod("Clear");
|
||||
if (clear != null)
|
||||
{
|
||||
clear.Invoke(instance, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static int TryGetExistingSize(Type type, object instance)
|
||||
{
|
||||
var count = type.GetFlattenedProperty("Count");
|
||||
if (count != null)
|
||||
{
|
||||
return (int)count.GetGetMethod().Invoke(instance, null);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static MethodInfo GetAddMethod(Type type)
|
||||
{
|
||||
// There is a really good chance the type will extend ICollection{},
|
||||
// which will contain the add method we want. Just doing
|
||||
// type.GetFlattenedMethod() may return the incorrect one -- for
|
||||
// example, with dictionaries, it'll return Add(TKey, TValue), and we
|
||||
// want Add(KeyValuePair<TKey, TValue>).
|
||||
var collectionInterface = fsReflectionUtility.GetInterface(type, typeof(ICollection<>));
|
||||
if (collectionInterface != null)
|
||||
{
|
||||
var add = collectionInterface.GetDeclaredMethod("Add");
|
||||
if (add != null)
|
||||
{
|
||||
return add;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise try and look up a general Add method.
|
||||
return
|
||||
type.GetFlattenedMethod("Add") ??
|
||||
type.GetFlattenedMethod("Push") ??
|
||||
type.GetFlattenedMethod("Enqueue");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 83ff07ed5d5f746a18cff1d9fd010e93
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,81 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.VisualScripting.FullSerializer.Internal;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
public class fsKeyValuePairConverter : fsConverter
|
||||
{
|
||||
public override bool CanProcess(Type type)
|
||||
{
|
||||
return
|
||||
type.Resolve().IsGenericType &&
|
||||
type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>);
|
||||
}
|
||||
|
||||
public override bool RequestCycleSupport(Type storageType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool RequestInheritanceSupport(Type storageType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override fsResult TryDeserialize(fsData data, ref object instance, Type storageType)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
fsData keyData, valueData;
|
||||
if ((result += CheckKey(data, "Key", out keyData)).Failed)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if ((result += CheckKey(data, "Value", out valueData)).Failed)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var genericArguments = storageType.GetGenericArguments();
|
||||
Type keyType = genericArguments[0], valueType = genericArguments[1];
|
||||
|
||||
object keyObject = null, valueObject = null;
|
||||
result.AddMessages(Serializer.TryDeserialize(keyData, keyType, ref keyObject));
|
||||
result.AddMessages(Serializer.TryDeserialize(valueData, valueType, ref valueObject));
|
||||
|
||||
instance = Activator.CreateInstance(storageType, keyObject, valueObject);
|
||||
return result;
|
||||
}
|
||||
|
||||
public override fsResult TrySerialize(object instance, out fsData serialized, Type storageType)
|
||||
{
|
||||
var keyProperty = storageType.GetDeclaredProperty("Key");
|
||||
var valueProperty = storageType.GetDeclaredProperty("Value");
|
||||
|
||||
var keyObject = keyProperty.GetValue(instance, null);
|
||||
var valueObject = valueProperty.GetValue(instance, null);
|
||||
|
||||
var genericArguments = storageType.GetGenericArguments();
|
||||
Type keyType = genericArguments[0], valueType = genericArguments[1];
|
||||
|
||||
var result = fsResult.Success;
|
||||
|
||||
fsData keyData, valueData;
|
||||
result.AddMessages(Serializer.TrySerialize(keyType, keyObject, out keyData));
|
||||
result.AddMessages(Serializer.TrySerialize(valueType, valueObject, out valueData));
|
||||
|
||||
serialized = fsData.CreateDictionary();
|
||||
if (keyData != null)
|
||||
{
|
||||
serialized.AsDictionary["Key"] = keyData;
|
||||
}
|
||||
if (valueData != null)
|
||||
{
|
||||
serialized.AsDictionary["Value"] = valueData;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 37bee35b01d89446581ee06eb78ab74e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
using Unity.VisualScripting.FullSerializer.Internal;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// The reflected converter will properly serialize nullable types. However,
|
||||
/// we do it here instead as we can emit less serialization data.
|
||||
/// </summary>
|
||||
public class fsNullableConverter : fsConverter
|
||||
{
|
||||
public override bool CanProcess(Type type)
|
||||
{
|
||||
return
|
||||
type.Resolve().IsGenericType &&
|
||||
type.GetGenericTypeDefinition() == typeof(Nullable<>);
|
||||
}
|
||||
|
||||
public override fsResult TrySerialize(object instance, out fsData serialized, Type storageType)
|
||||
{
|
||||
// null is automatically serialized
|
||||
return Serializer.TrySerialize(Nullable.GetUnderlyingType(storageType), instance, out serialized);
|
||||
}
|
||||
|
||||
public override fsResult TryDeserialize(fsData data, ref object instance, Type storageType)
|
||||
{
|
||||
// null is automatically deserialized
|
||||
return Serializer.TryDeserialize(data, Nullable.GetUnderlyingType(storageType), ref instance);
|
||||
}
|
||||
|
||||
public override object CreateInstance(fsData data, Type storageType)
|
||||
{
|
||||
return storageType;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d600c4c5c0dac41a885a14952da139bd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,173 @@
|
|||
using System;
|
||||
using Unity.VisualScripting.FullSerializer.Internal;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
public class fsPrimitiveConverter : fsConverter
|
||||
{
|
||||
public override bool CanProcess(Type type)
|
||||
{
|
||||
return
|
||||
type.Resolve().IsPrimitive ||
|
||||
type == typeof(string) ||
|
||||
type == typeof(decimal);
|
||||
}
|
||||
|
||||
public override bool RequestCycleSupport(Type storageType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool RequestInheritanceSupport(Type storageType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override fsResult TrySerialize(object instance, out fsData serialized, Type storageType)
|
||||
{
|
||||
var instanceType = instance.GetType();
|
||||
|
||||
if (Serializer.Config.Serialize64BitIntegerAsString && (instanceType == typeof(Int64) || instanceType == typeof(UInt64)))
|
||||
{
|
||||
serialized = new fsData((string)Convert.ChangeType(instance, typeof(string)));
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
if (UseBool(instanceType))
|
||||
{
|
||||
serialized = new fsData((bool)instance);
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
if (UseInt64(instanceType))
|
||||
{
|
||||
serialized = new fsData((Int64)Convert.ChangeType(instance, typeof(Int64)));
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
if (UseDouble(instanceType))
|
||||
{
|
||||
// Casting from float to double introduces floating point jitter,
|
||||
// ie, 0.1 becomes 0.100000001490116. Casting to decimal as an
|
||||
// intermediate step removes the jitter. Not sure why.
|
||||
if (instance.GetType() == typeof(float) &&
|
||||
// Decimal can't store
|
||||
// float.MinValue/float.MaxValue/float.PositiveInfinity/float.NegativeInfinity/float.NaN
|
||||
// - an exception gets thrown in that scenario.
|
||||
(float)instance != float.MinValue &&
|
||||
(float)instance != float.MaxValue &&
|
||||
!float.IsInfinity((float)instance) &&
|
||||
!float.IsNaN((float)instance)
|
||||
)
|
||||
{
|
||||
serialized = new fsData((double)(decimal)(float)instance);
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
serialized = new fsData((double)Convert.ChangeType(instance, typeof(double)));
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
if (UseString(instanceType))
|
||||
{
|
||||
serialized = new fsData((string)Convert.ChangeType(instance, typeof(string)));
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
serialized = null;
|
||||
return fsResult.Fail("Unhandled primitive type " + instance.GetType());
|
||||
}
|
||||
|
||||
public override fsResult TryDeserialize(fsData storage, ref object instance, Type storageType)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
if (UseBool(storageType))
|
||||
{
|
||||
if ((result += CheckType(storage, fsDataType.Boolean)).Succeeded)
|
||||
{
|
||||
instance = storage.AsBool;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (UseDouble(storageType) || UseInt64(storageType))
|
||||
{
|
||||
if (storage.IsDouble)
|
||||
{
|
||||
instance = Convert.ChangeType(storage.AsDouble, storageType);
|
||||
}
|
||||
else if (storage.IsInt64)
|
||||
{
|
||||
instance = Convert.ChangeType(storage.AsInt64, storageType);
|
||||
}
|
||||
else if (Serializer.Config.Serialize64BitIntegerAsString && storage.IsString &&
|
||||
(storageType == typeof(Int64) || storageType == typeof(UInt64)))
|
||||
{
|
||||
instance = Convert.ChangeType(storage.AsString, storageType);
|
||||
}
|
||||
else
|
||||
{
|
||||
return fsResult.Fail(GetType().Name + " expected number but got " + storage.Type + " in " + storage);
|
||||
}
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
if (UseString(storageType))
|
||||
{
|
||||
if ((result += CheckType(storage, fsDataType.String)).Succeeded)
|
||||
{
|
||||
var str = storage.AsString;
|
||||
|
||||
if (storageType == typeof(char))
|
||||
{
|
||||
if (storageType == typeof(char))
|
||||
{
|
||||
if (str.Length == 1)
|
||||
{
|
||||
instance = str[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
instance = default(char);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
instance = str;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return fsResult.Fail(GetType().Name + ": Bad data; expected bool, number, string, but got " + storage);
|
||||
}
|
||||
|
||||
private static bool UseBool(Type type)
|
||||
{
|
||||
return type == typeof(bool);
|
||||
}
|
||||
|
||||
private static bool UseInt64(Type type)
|
||||
{
|
||||
return type == typeof(sbyte) || type == typeof(byte) ||
|
||||
type == typeof(Int16) || type == typeof(UInt16) ||
|
||||
type == typeof(Int32) || type == typeof(UInt32) ||
|
||||
type == typeof(Int64) || type == typeof(UInt64);
|
||||
}
|
||||
|
||||
private static bool UseDouble(Type type)
|
||||
{
|
||||
return type == typeof(float) ||
|
||||
type == typeof(double) ||
|
||||
type == typeof(decimal);
|
||||
}
|
||||
|
||||
private static bool UseString(Type type)
|
||||
{
|
||||
return type == typeof(string) ||
|
||||
type == typeof(char);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 558f1bd3630a14d2a8445d33219e5060
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,118 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using Unity.VisualScripting.FullSerializer.Internal;
|
||||
|
||||
#if !UNITY_EDITOR && UNITY_WSA
|
||||
// For System.Reflection.TypeExtensions
|
||||
using System.Reflection;
|
||||
#endif
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
public class fsReflectedConverter : fsConverter
|
||||
{
|
||||
public override bool CanProcess(Type type)
|
||||
{
|
||||
if (type.Resolve().IsArray ||
|
||||
typeof(ICollection).IsAssignableFrom(type))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override fsResult TrySerialize(object instance, out fsData serialized, Type storageType)
|
||||
{
|
||||
serialized = fsData.CreateDictionary();
|
||||
var result = fsResult.Success;
|
||||
|
||||
var metaType = fsMetaType.Get(Serializer.Config, instance.GetType());
|
||||
metaType.EmitAotData();
|
||||
|
||||
for (var i = 0; i < metaType.Properties.Length; ++i)
|
||||
{
|
||||
var property = metaType.Properties[i];
|
||||
if (property.CanRead == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
fsData serializedData;
|
||||
|
||||
var itemResult = Serializer.TrySerialize(property.StorageType, property.OverrideConverterType,
|
||||
property.Read(instance), out serializedData);
|
||||
result.AddMessages(itemResult);
|
||||
if (itemResult.Failed)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
serialized.AsDictionary[property.JsonName] = serializedData;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override fsResult TryDeserialize(fsData data, ref object instance, Type storageType)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
// Verify that we actually have an Object
|
||||
if ((result += CheckType(data, fsDataType.Object)).Failed)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var metaType = fsMetaType.Get(Serializer.Config, storageType);
|
||||
metaType.EmitAotData();
|
||||
|
||||
for (var i = 0; i < metaType.Properties.Length; ++i)
|
||||
{
|
||||
var property = metaType.Properties[i];
|
||||
if (property.CanWrite == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
fsData propertyData;
|
||||
if (data.AsDictionary.TryGetValue(property.JsonName, out propertyData))
|
||||
{
|
||||
object deserializedValue = null;
|
||||
|
||||
// We have to read in the existing value, since we need to
|
||||
// support partial deserialization. However, this is bad for
|
||||
// perf.
|
||||
// TODO: Find a way to avoid this call when we are not doing
|
||||
// a partial deserialization Maybe through a new
|
||||
// property, ie, Serializer.IsPartialSerialization,
|
||||
// which just gets set when starting a new
|
||||
// serialization? We cannot pipe the information
|
||||
// through CreateInstance unfortunately.
|
||||
if (property.CanRead)
|
||||
{
|
||||
deserializedValue = property.Read(instance);
|
||||
}
|
||||
|
||||
var itemResult = Serializer.TryDeserialize(propertyData, property.StorageType,
|
||||
property.OverrideConverterType, ref deserializedValue);
|
||||
result.AddMessages(itemResult);
|
||||
if (itemResult.Failed)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
property.Write(instance, deserializedValue);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override object CreateInstance(fsData data, Type storageType)
|
||||
{
|
||||
var metaType = fsMetaType.Get(Serializer.Config, storageType);
|
||||
return metaType.CreateInstance();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: aceaeecabc7804af78324f3a4ca624d0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,58 @@
|
|||
using System;
|
||||
|
||||
#if !UNITY_EDITOR && UNITY_WSA
|
||||
// For System.Reflection.TypeExtensions
|
||||
using System.Reflection;
|
||||
#endif
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
public class fsTypeConverter : fsConverter
|
||||
{
|
||||
public override bool CanProcess(Type type)
|
||||
{
|
||||
return typeof(Type).IsAssignableFrom(type);
|
||||
}
|
||||
|
||||
public override bool RequestCycleSupport(Type type)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool RequestInheritanceSupport(Type type)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override fsResult TrySerialize(object instance, out fsData serialized, Type storageType)
|
||||
{
|
||||
var type = (Type)instance;
|
||||
serialized = new fsData(RuntimeCodebase.SerializeType(type));
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
public override fsResult TryDeserialize(fsData data, ref object instance, Type storageType)
|
||||
{
|
||||
if (data.IsString == false)
|
||||
{
|
||||
return fsResult.Fail("Type converter requires a string");
|
||||
}
|
||||
|
||||
if (RuntimeCodebase.TryDeserializeType(data.AsString, out var type))
|
||||
{
|
||||
instance = type;
|
||||
}
|
||||
else
|
||||
{
|
||||
return fsResult.Fail($"Unable to find type: '{data.AsString ?? "(null)"}'.");
|
||||
}
|
||||
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
public override object CreateInstance(fsData data, Type storageType)
|
||||
{
|
||||
return storageType;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1ffb0c77cc5134994ba0d23fcbdd4205
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,83 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Serializes and deserializes WeakReferences.
|
||||
/// </summary>
|
||||
public class fsWeakReferenceConverter : fsConverter
|
||||
{
|
||||
public override bool CanProcess(Type type)
|
||||
{
|
||||
return type == typeof(WeakReference);
|
||||
}
|
||||
|
||||
public override bool RequestCycleSupport(Type storageType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool RequestInheritanceSupport(Type storageType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override fsResult TrySerialize(object instance, out fsData serialized, Type storageType)
|
||||
{
|
||||
var weakRef = (WeakReference)instance;
|
||||
|
||||
var result = fsResult.Success;
|
||||
serialized = fsData.CreateDictionary();
|
||||
|
||||
if (weakRef.IsAlive)
|
||||
{
|
||||
fsData data;
|
||||
if ((result += Serializer.TrySerialize(weakRef.Target, out data)).Failed)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
serialized.AsDictionary["Target"] = data;
|
||||
serialized.AsDictionary["TrackResurrection"] = new fsData(weakRef.TrackResurrection);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override fsResult TryDeserialize(fsData data, ref object instance, Type storageType)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
|
||||
if ((result += CheckType(data, fsDataType.Object)).Failed)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (data.AsDictionary.ContainsKey("Target"))
|
||||
{
|
||||
var targetData = data.AsDictionary["Target"];
|
||||
object targetInstance = null;
|
||||
|
||||
if ((result += Serializer.TryDeserialize(targetData, typeof(object), ref targetInstance)).Failed)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var trackResurrection = false;
|
||||
if (data.AsDictionary.ContainsKey("TrackResurrection") && data.AsDictionary["TrackResurrection"].IsBool)
|
||||
{
|
||||
trackResurrection = data.AsDictionary["TrackResurrection"].AsBool;
|
||||
}
|
||||
|
||||
instance = new WeakReference(targetInstance, trackResurrection);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public override object CreateInstance(fsData data, Type storageType)
|
||||
{
|
||||
return new WeakReference(null);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 57352352fce8a4f57800fb25fb539745
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3ee33d080bf544382a0b3bb52e994713
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,107 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer.Internal
|
||||
{
|
||||
public class fsCyclicReferenceManager
|
||||
{
|
||||
private Dictionary<object, int> _objectIds = new Dictionary<object, int>(ObjectReferenceEqualityComparator.Instance);
|
||||
private int _nextId;
|
||||
|
||||
private Dictionary<int, object> _marked = new Dictionary<int, object>();
|
||||
private int _depth;
|
||||
|
||||
public void Enter()
|
||||
{
|
||||
_depth++;
|
||||
}
|
||||
|
||||
public bool Exit()
|
||||
{
|
||||
_depth--;
|
||||
|
||||
if (_depth == 0)
|
||||
{
|
||||
_objectIds = new Dictionary<object, int>(ObjectReferenceEqualityComparator.Instance);
|
||||
_nextId = 0;
|
||||
_marked = new Dictionary<int, object>();
|
||||
}
|
||||
|
||||
if (_depth < 0)
|
||||
{
|
||||
_depth = 0;
|
||||
throw new InvalidOperationException("Internal Error - Mismatched Enter/Exit. Please report a bug at https://github.com/jacobdufault/fullserializer/issues with the serialization data.");
|
||||
}
|
||||
|
||||
return _depth == 0;
|
||||
}
|
||||
|
||||
public object GetReferenceObject(int id)
|
||||
{
|
||||
if (_marked.ContainsKey(id) == false)
|
||||
{
|
||||
throw new InvalidOperationException("Internal Deserialization Error - Object " +
|
||||
"definition has not been encountered for object with id=" + id +
|
||||
"; have you reordered or modified the serialized data? If this is an issue " +
|
||||
"with an unmodified Full Serializer implementation and unmodified serialization " +
|
||||
"data, please report an issue with an included test case.");
|
||||
}
|
||||
|
||||
return _marked[id];
|
||||
}
|
||||
|
||||
public void AddReferenceWithId(int id, object reference)
|
||||
{
|
||||
_marked[id] = reference;
|
||||
}
|
||||
|
||||
public int GetReferenceId(object item)
|
||||
{
|
||||
int id;
|
||||
if (_objectIds.TryGetValue(item, out id) == false)
|
||||
{
|
||||
id = _nextId++;
|
||||
_objectIds[item] = id;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
public bool IsReference(object item)
|
||||
{
|
||||
return _marked.ContainsKey(GetReferenceId(item));
|
||||
}
|
||||
|
||||
public void MarkSerialized(object item)
|
||||
{
|
||||
var referenceId = GetReferenceId(item);
|
||||
|
||||
if (_marked.ContainsKey(referenceId))
|
||||
{
|
||||
throw new InvalidOperationException("Internal Error - " + item +
|
||||
" has already been marked as serialized");
|
||||
}
|
||||
|
||||
_marked[referenceId] = item;
|
||||
}
|
||||
|
||||
// We use the default ReferenceEquals when comparing two objects because
|
||||
// custom objects may override equals methods. These overriden equals may
|
||||
// treat equals differently; we want to serialize/deserialize the object
|
||||
// graph *identically* to how it currently exists.
|
||||
private class ObjectReferenceEqualityComparator : IEqualityComparer<object>
|
||||
{
|
||||
bool IEqualityComparer<object>.Equals(object x, object y)
|
||||
{
|
||||
return ReferenceEquals(x, y);
|
||||
}
|
||||
|
||||
int IEqualityComparer<object>.GetHashCode(object obj)
|
||||
{
|
||||
return RuntimeHelpers.GetHashCode(obj);
|
||||
}
|
||||
|
||||
public static readonly IEqualityComparer<object> Instance = new ObjectReferenceEqualityComparator();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0304625fd852e4c1a88e3a6367245d13
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple option type. This is akin to nullable types.
|
||||
/// </summary>
|
||||
public struct fsOption<T>
|
||||
{
|
||||
private bool _hasValue;
|
||||
private T _value;
|
||||
|
||||
public bool HasValue => _hasValue;
|
||||
|
||||
public bool IsEmpty => _hasValue == false;
|
||||
|
||||
public T Value
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsEmpty)
|
||||
{
|
||||
throw new InvalidOperationException("fsOption is empty");
|
||||
}
|
||||
return _value;
|
||||
}
|
||||
}
|
||||
|
||||
public fsOption(T value)
|
||||
{
|
||||
_hasValue = true;
|
||||
_value = value;
|
||||
}
|
||||
|
||||
public static fsOption<T> Empty;
|
||||
}
|
||||
|
||||
public static class fsOption
|
||||
{
|
||||
public static fsOption<T> Just<T>(T value)
|
||||
{
|
||||
return new fsOption<T>(value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b2d2628348c58407eb518c9b65d489cb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,507 @@
|
|||
#if !UNITY_EDITOR && UNITY_METRO && !ENABLE_IL2CPP
|
||||
#define USE_TYPEINFO
|
||||
#if !UNITY_WINRT_10_0
|
||||
#define USE_TYPEINFO_EXTENSIONS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
#if USE_TYPEINFO
|
||||
namespace System
|
||||
{
|
||||
public static class AssemblyExtensions
|
||||
{
|
||||
#if USE_TYPEINFO_EXTENSIONS
|
||||
public static Type[] GetTypes(this Assembly assembly)
|
||||
{
|
||||
TypeInfo[] infos = assembly.DefinedTypes.ToArray();
|
||||
Type[] types = new Type[infos.Length];
|
||||
for (int i = 0; i < infos.Length; ++i)
|
||||
{
|
||||
types[i] = infos[i].AsType();
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
public static Type GetType(this Assembly assembly, string name, bool throwOnError)
|
||||
{
|
||||
var types = assembly.GetTypes();
|
||||
for (int i = 0; i < types.Length; ++i)
|
||||
{
|
||||
if (types[i].Name == name)
|
||||
{
|
||||
return types[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (throwOnError) throw new Exception("Type " + name + " was not found");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This wraps reflection types so that it is portable across different Unity
|
||||
/// runtimes.
|
||||
/// </summary>
|
||||
public static class fsPortableReflection
|
||||
{
|
||||
public static Type[] EmptyTypes = { };
|
||||
|
||||
#region Attribute Queries
|
||||
|
||||
#if USE_TYPEINFO
|
||||
public static TAttribute GetAttribute<TAttribute>(Type type)
|
||||
where TAttribute : Attribute
|
||||
{
|
||||
return GetAttribute<TAttribute>(type.GetTypeInfo());
|
||||
}
|
||||
|
||||
public static Attribute GetAttribute(Type type, Type attributeType)
|
||||
{
|
||||
return GetAttribute(type.GetTypeInfo(), attributeType, /*shouldCache:*/ false);
|
||||
}
|
||||
|
||||
public static bool HasAttribute(Type type, Type attributeType)
|
||||
{
|
||||
return GetAttribute(type, attributeType) != null;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given attribute is defined on the given element.
|
||||
/// </summary>
|
||||
public static bool HasAttribute<TAttribute>(MemberInfo element)
|
||||
{
|
||||
return HasAttribute(element, typeof(TAttribute));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given attribute is defined on the given element.
|
||||
/// </summary>
|
||||
public static bool HasAttribute<TAttribute>(MemberInfo element, bool shouldCache)
|
||||
{
|
||||
return HasAttribute(element, typeof(TAttribute), shouldCache);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given attribute is defined on the given element.
|
||||
/// </summary>
|
||||
public static bool HasAttribute(MemberInfo element, Type attributeType)
|
||||
{
|
||||
return HasAttribute(element, attributeType, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given attribute is defined on the given element.
|
||||
/// </summary>
|
||||
public static bool HasAttribute(MemberInfo element, Type attributeType, bool shouldCache)
|
||||
{
|
||||
// LAZLO / LUDIQ FIX
|
||||
// Inheritance on property overrides. MemberInfo.IsDefined ignores the inherited parameter.
|
||||
// https://stackoverflow.com/questions/2520035
|
||||
return Attribute.IsDefined(element, attributeType, true);
|
||||
//return element.IsDefined(attributeType, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches the given attribute from the given MemberInfo. This method
|
||||
/// applies caching and is allocation free (after caching has been
|
||||
/// performed).
|
||||
/// </summary>
|
||||
/// <param name="element">
|
||||
/// The MemberInfo the get the attribute from.
|
||||
/// </param>
|
||||
/// <param name="attributeType">The type of attribute to fetch.</param>
|
||||
/// <returns>The attribute or null.</returns>
|
||||
public static Attribute GetAttribute(MemberInfo element, Type attributeType, bool shouldCache)
|
||||
{
|
||||
var query = new AttributeQuery
|
||||
{
|
||||
MemberInfo = element,
|
||||
AttributeType = attributeType
|
||||
};
|
||||
|
||||
Attribute attribute;
|
||||
if (_cachedAttributeQueries.TryGetValue(query, out attribute) == false)
|
||||
{
|
||||
// LAZLO / LUDIQ FIX
|
||||
// Inheritance on property overrides. MemberInfo.IsDefined ignores the inherited parameter
|
||||
//var attributes = element.GetCustomAttributes(attributeType, /*inherit:*/ true).ToArray();
|
||||
var attributes = Attribute.GetCustomAttributes(element, attributeType, true).ToArray();
|
||||
|
||||
if (attributes.Length > 0)
|
||||
{
|
||||
attribute = (Attribute)attributes[0];
|
||||
}
|
||||
if (shouldCache)
|
||||
{
|
||||
_cachedAttributeQueries[query] = attribute;
|
||||
}
|
||||
}
|
||||
|
||||
return attribute;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches the given attribute from the given MemberInfo.
|
||||
/// </summary>
|
||||
/// <typeparam name="TAttribute">
|
||||
/// The type of attribute to fetch.
|
||||
/// </typeparam>
|
||||
/// <param name="element">
|
||||
/// The MemberInfo to get the attribute from.
|
||||
/// </param>
|
||||
/// <param name="shouldCache">
|
||||
/// Should this computation be cached? If this is the only time it will
|
||||
/// ever be done, don't bother caching.
|
||||
/// </param>
|
||||
/// <returns>The attribute or null.</returns>
|
||||
public static TAttribute GetAttribute<TAttribute>(MemberInfo element, bool shouldCache)
|
||||
where TAttribute : Attribute
|
||||
{
|
||||
return (TAttribute)GetAttribute(element, typeof(TAttribute), shouldCache);
|
||||
}
|
||||
|
||||
public static TAttribute GetAttribute<TAttribute>(MemberInfo element)
|
||||
where TAttribute : Attribute
|
||||
{
|
||||
return GetAttribute<TAttribute>(element, /*shouldCache:*/ true);
|
||||
}
|
||||
|
||||
private struct AttributeQuery
|
||||
{
|
||||
public MemberInfo MemberInfo;
|
||||
public Type AttributeType;
|
||||
}
|
||||
|
||||
private static IDictionary<AttributeQuery, Attribute> _cachedAttributeQueries =
|
||||
new Dictionary<AttributeQuery, Attribute>(new AttributeQueryComparator());
|
||||
|
||||
private class AttributeQueryComparator : IEqualityComparer<AttributeQuery>
|
||||
{
|
||||
public bool Equals(AttributeQuery x, AttributeQuery y)
|
||||
{
|
||||
return
|
||||
x.MemberInfo == y.MemberInfo &&
|
||||
x.AttributeType == y.AttributeType;
|
||||
}
|
||||
|
||||
public int GetHashCode(AttributeQuery obj)
|
||||
{
|
||||
return
|
||||
obj.MemberInfo.GetHashCode() +
|
||||
(17 * obj.AttributeType.GetHashCode());
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Attribute Queries
|
||||
|
||||
#if !USE_TYPEINFO
|
||||
private static BindingFlags DeclaredFlags =
|
||||
BindingFlags.NonPublic |
|
||||
BindingFlags.Public |
|
||||
BindingFlags.Instance |
|
||||
BindingFlags.Static |
|
||||
BindingFlags.DeclaredOnly;
|
||||
#endif
|
||||
|
||||
public static PropertyInfo GetDeclaredProperty(this Type type, string propertyName)
|
||||
{
|
||||
var props = GetDeclaredProperties(type);
|
||||
|
||||
for (var i = 0; i < props.Length; ++i)
|
||||
{
|
||||
if (props[i].Name == propertyName)
|
||||
{
|
||||
return props[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static MethodInfo GetDeclaredMethod(this Type type, string methodName)
|
||||
{
|
||||
var methods = GetDeclaredMethods(type);
|
||||
|
||||
for (var i = 0; i < methods.Length; ++i)
|
||||
{
|
||||
if (methods[i].Name == methodName)
|
||||
{
|
||||
return methods[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ConstructorInfo GetDeclaredConstructor(this Type type, Type[] parameters)
|
||||
{
|
||||
var ctors = GetDeclaredConstructors(type);
|
||||
|
||||
for (var i = 0; i < ctors.Length; ++i)
|
||||
{
|
||||
var ctor = ctors[i];
|
||||
var ctorParams = ctor.GetParameters();
|
||||
|
||||
if (parameters.Length != ctorParams.Length)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (var j = 0; j < ctorParams.Length; ++j)
|
||||
{
|
||||
// require an exact match
|
||||
if (ctorParams[j].ParameterType != parameters[j])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return ctor;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ConstructorInfo[] GetDeclaredConstructors(this Type type)
|
||||
{
|
||||
#if USE_TYPEINFO
|
||||
return type.GetTypeInfo().DeclaredConstructors.ToArray();
|
||||
#else
|
||||
return type.GetConstructors(DeclaredFlags & ~BindingFlags.Static); // LUDIQ: Exclude static constructors
|
||||
#endif
|
||||
}
|
||||
|
||||
public static MemberInfo[] GetFlattenedMember(this Type type, string memberName)
|
||||
{
|
||||
var result = new List<MemberInfo>();
|
||||
|
||||
while (type != null)
|
||||
{
|
||||
var members = GetDeclaredMembers(type);
|
||||
|
||||
for (var i = 0; i < members.Length; ++i)
|
||||
{
|
||||
if (members[i].Name == memberName)
|
||||
{
|
||||
result.Add(members[i]);
|
||||
}
|
||||
}
|
||||
|
||||
type = type.Resolve().BaseType;
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
public static MethodInfo GetFlattenedMethod(this Type type, string methodName)
|
||||
{
|
||||
while (type != null)
|
||||
{
|
||||
var methods = GetDeclaredMethods(type);
|
||||
|
||||
for (var i = 0; i < methods.Length; ++i)
|
||||
{
|
||||
if (methods[i].Name == methodName)
|
||||
{
|
||||
return methods[i];
|
||||
}
|
||||
}
|
||||
|
||||
type = type.Resolve().BaseType;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static IEnumerable<MethodInfo> GetFlattenedMethods(this Type type, string methodName)
|
||||
{
|
||||
while (type != null)
|
||||
{
|
||||
var methods = GetDeclaredMethods(type);
|
||||
|
||||
for (var i = 0; i < methods.Length; ++i)
|
||||
{
|
||||
if (methods[i].Name == methodName)
|
||||
{
|
||||
yield return methods[i];
|
||||
}
|
||||
}
|
||||
|
||||
type = type.Resolve().BaseType;
|
||||
}
|
||||
}
|
||||
|
||||
public static PropertyInfo GetFlattenedProperty(this Type type, string propertyName)
|
||||
{
|
||||
while (type != null)
|
||||
{
|
||||
var properties = GetDeclaredProperties(type);
|
||||
|
||||
for (var i = 0; i < properties.Length; ++i)
|
||||
{
|
||||
if (properties[i].Name == propertyName)
|
||||
{
|
||||
return properties[i];
|
||||
}
|
||||
}
|
||||
|
||||
type = type.Resolve().BaseType;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static MemberInfo GetDeclaredMember(this Type type, string memberName)
|
||||
{
|
||||
var members = GetDeclaredMembers(type);
|
||||
|
||||
for (var i = 0; i < members.Length; ++i)
|
||||
{
|
||||
if (members[i].Name == memberName)
|
||||
{
|
||||
return members[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static MethodInfo[] GetDeclaredMethods(this Type type)
|
||||
{
|
||||
#if USE_TYPEINFO
|
||||
return type.GetTypeInfo().DeclaredMethods.ToArray();
|
||||
#else
|
||||
return type.GetMethods(DeclaredFlags);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static PropertyInfo[] GetDeclaredProperties(this Type type)
|
||||
{
|
||||
#if USE_TYPEINFO
|
||||
return type.GetTypeInfo().DeclaredProperties.ToArray();
|
||||
#else
|
||||
return type.GetProperties(DeclaredFlags);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static FieldInfo[] GetDeclaredFields(this Type type)
|
||||
{
|
||||
#if USE_TYPEINFO
|
||||
return type.GetTypeInfo().DeclaredFields.ToArray();
|
||||
#else
|
||||
return type.GetFields(DeclaredFlags);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static MemberInfo[] GetDeclaredMembers(this Type type)
|
||||
{
|
||||
#if USE_TYPEINFO
|
||||
return type.GetTypeInfo().DeclaredMembers.ToArray();
|
||||
#else
|
||||
return type.GetMembers(DeclaredFlags);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static MemberInfo AsMemberInfo(Type type)
|
||||
{
|
||||
#if USE_TYPEINFO
|
||||
return type.GetTypeInfo();
|
||||
#else
|
||||
return type;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static bool IsType(MemberInfo member)
|
||||
{
|
||||
#if USE_TYPEINFO
|
||||
return member is TypeInfo;
|
||||
#else
|
||||
return member is Type;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static Type AsType(MemberInfo member)
|
||||
{
|
||||
#if USE_TYPEINFO
|
||||
return ((TypeInfo)member).AsType();
|
||||
#else
|
||||
return (Type)member;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if USE_TYPEINFO
|
||||
public static TypeInfo Resolve(this Type type)
|
||||
{
|
||||
return type.GetTypeInfo();
|
||||
}
|
||||
|
||||
#else
|
||||
public static Type Resolve(this Type type)
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#region Extensions
|
||||
|
||||
#if USE_TYPEINFO_EXTENSIONS
|
||||
public static bool IsAssignableFrom(this Type parent, Type child)
|
||||
{
|
||||
return parent.GetTypeInfo().IsAssignableFrom(child.GetTypeInfo());
|
||||
}
|
||||
|
||||
public static Type GetElementType(this Type type)
|
||||
{
|
||||
return type.GetTypeInfo().GetElementType();
|
||||
}
|
||||
|
||||
public static MethodInfo GetSetMethod(this PropertyInfo member, bool nonPublic = false)
|
||||
{
|
||||
// only public requested but the set method is not public
|
||||
if (nonPublic == false && member.SetMethod != null && member.SetMethod.IsPublic == false) return null;
|
||||
|
||||
return member.SetMethod;
|
||||
}
|
||||
|
||||
public static MethodInfo GetGetMethod(this PropertyInfo member, bool nonPublic = false)
|
||||
{
|
||||
// only public requested but the set method is not public
|
||||
if (nonPublic == false && member.GetMethod != null && member.GetMethod.IsPublic == false) return null;
|
||||
|
||||
return member.GetMethod;
|
||||
}
|
||||
|
||||
public static MethodInfo GetBaseDefinition(this MethodInfo method)
|
||||
{
|
||||
return method.GetRuntimeBaseDefinition();
|
||||
}
|
||||
|
||||
public static Type[] GetInterfaces(this Type type)
|
||||
{
|
||||
return type.GetTypeInfo().ImplementedInterfaces.ToArray();
|
||||
}
|
||||
|
||||
public static Type[] GetGenericArguments(this Type type)
|
||||
{
|
||||
return type.GetTypeInfo().GenericTypeArguments.ToArray();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endregion Extensions
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6e8457d68030d4970ae014b8a8aa5e28
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,118 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
#if !UNITY_EDITOR && UNITY_WSA
|
||||
// For System.Reflection.TypeExtensions
|
||||
using System.Reflection;
|
||||
#endif
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer.Internal
|
||||
{
|
||||
public static class fsTypeExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a pretty name for the type in the style of one that you'd see
|
||||
/// in C# without the namespace.
|
||||
/// </summary>
|
||||
public static string CSharpName(this Type type)
|
||||
{
|
||||
return CSharpName(type, /*includeNamespace:*/ false);
|
||||
}
|
||||
|
||||
public static string CSharpName(this Type type, bool includeNamespace, bool ensureSafeDeclarationName)
|
||||
{
|
||||
var name = CSharpName(type, includeNamespace);
|
||||
if (ensureSafeDeclarationName)
|
||||
{
|
||||
name = name.Replace('>', '_').Replace('<', '_').Replace('.', '_');
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a pretty name for the type in the style of one that you'd see
|
||||
/// in C#.
|
||||
/// </summary>
|
||||
/// <parparam name="includeNamespace">
|
||||
/// Should the name include namespaces?
|
||||
/// </parparam>
|
||||
public static string CSharpName(this Type type, bool includeNamespace)
|
||||
{
|
||||
// we special case some of the common type names
|
||||
if (type == typeof(void))
|
||||
{
|
||||
return "void";
|
||||
}
|
||||
if (type == typeof(int))
|
||||
{
|
||||
return "int";
|
||||
}
|
||||
if (type == typeof(float))
|
||||
{
|
||||
return "float";
|
||||
}
|
||||
if (type == typeof(bool))
|
||||
{
|
||||
return "bool";
|
||||
}
|
||||
if (type == typeof(double))
|
||||
{
|
||||
return "double";
|
||||
}
|
||||
if (type == typeof(string))
|
||||
{
|
||||
return "string";
|
||||
}
|
||||
|
||||
// Generic parameter, ie, T in Okay<T> We special-case this logic
|
||||
// otherwise we will recurse on the T
|
||||
if (type.IsGenericParameter)
|
||||
{
|
||||
return type.ToString();
|
||||
}
|
||||
|
||||
var name = "";
|
||||
|
||||
var genericArguments = (IEnumerable<Type>)type.GetGenericArguments();
|
||||
if (type.IsNested)
|
||||
{
|
||||
name += type.DeclaringType.CSharpName() + ".";
|
||||
|
||||
// The declaring type generic parameters are considered part of
|
||||
// the nested types generic parameters so we need to remove them,
|
||||
// otherwise it will get included again.
|
||||
//
|
||||
// Say we have type `class Parent<T> { class Child {} }` If we
|
||||
// did not do the removal, then we would output
|
||||
// Parent<T>.Child<T>, but we really want to output
|
||||
// Parent<T>.Child
|
||||
if (type.DeclaringType.GetGenericArguments().Length > 0)
|
||||
{
|
||||
genericArguments = genericArguments.Skip(type.DeclaringType.GetGenericArguments().Length);
|
||||
}
|
||||
}
|
||||
|
||||
if (genericArguments.Any() == false)
|
||||
{
|
||||
name += type.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
var genericsTic = type.Name.IndexOf('`');
|
||||
if (genericsTic > 0)
|
||||
{
|
||||
name += type.Name.Substring(0, genericsTic);
|
||||
}
|
||||
name += "<" + String.Join(",", genericArguments.Select(t => CSharpName(t, includeNamespace)).ToArray()) + ">";
|
||||
}
|
||||
|
||||
if (includeNamespace && type.Namespace != null)
|
||||
{
|
||||
name = type.Namespace + "." + name;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8e88fda4ffd5d4c97818a2f71560bd9c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,157 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer.Internal
|
||||
{
|
||||
public static class fsVersionManager
|
||||
{
|
||||
private static readonly Dictionary<Type, fsOption<fsVersionedType>> _cache = new Dictionary<Type, fsOption<fsVersionedType>>();
|
||||
|
||||
public static fsResult GetVersionImportPath(string currentVersion, fsVersionedType targetVersion, out List<fsVersionedType> path)
|
||||
{
|
||||
path = new List<fsVersionedType>();
|
||||
|
||||
if (GetVersionImportPathRecursive(path, currentVersion, targetVersion) == false)
|
||||
{
|
||||
return fsResult.Fail("There is no migration path from \"" + currentVersion + "\" to \"" + targetVersion.VersionString + "\"");
|
||||
}
|
||||
|
||||
path.Add(targetVersion);
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
private static bool GetVersionImportPathRecursive(List<fsVersionedType> path, string currentVersion, fsVersionedType current)
|
||||
{
|
||||
for (var i = 0; i < current.Ancestors.Length; ++i)
|
||||
{
|
||||
var ancestor = current.Ancestors[i];
|
||||
|
||||
if (ancestor.VersionString == currentVersion ||
|
||||
GetVersionImportPathRecursive(path, currentVersion, ancestor))
|
||||
{
|
||||
path.Add(ancestor);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static fsOption<fsVersionedType> GetVersionedType(Type type)
|
||||
{
|
||||
fsOption<fsVersionedType> optionalVersionedType;
|
||||
|
||||
if (_cache.TryGetValue(type, out optionalVersionedType) == false)
|
||||
{
|
||||
var attr = fsPortableReflection.GetAttribute<fsObjectAttribute>(type);
|
||||
|
||||
if (attr != null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(attr.VersionString) == false || attr.PreviousModels != null)
|
||||
{
|
||||
// Version string must be provided
|
||||
if (attr.PreviousModels != null && string.IsNullOrEmpty(attr.VersionString))
|
||||
{
|
||||
throw new Exception("fsObject attribute on " + type + " contains a PreviousModels specifier - it must also include a VersionString modifier");
|
||||
}
|
||||
|
||||
// Map the ancestor types into versioned types
|
||||
var ancestors = new fsVersionedType[attr.PreviousModels != null ? attr.PreviousModels.Length : 0];
|
||||
for (var i = 0; i < ancestors.Length; ++i)
|
||||
{
|
||||
var ancestorType = GetVersionedType(attr.PreviousModels[i]);
|
||||
if (ancestorType.IsEmpty)
|
||||
{
|
||||
throw new Exception("Unable to create versioned type for ancestor " + ancestorType + "; please add an [fsObject(VersionString=\"...\")] attribute");
|
||||
}
|
||||
ancestors[i] = ancestorType.Value;
|
||||
}
|
||||
|
||||
// construct the actual versioned type instance
|
||||
var versionedType = new fsVersionedType
|
||||
{
|
||||
Ancestors = ancestors,
|
||||
VersionString = attr.VersionString,
|
||||
ModelType = type
|
||||
};
|
||||
|
||||
// finally, verify that the versioned type passes some
|
||||
// sanity checks
|
||||
VerifyUniqueVersionStrings(versionedType);
|
||||
VerifyConstructors(versionedType);
|
||||
|
||||
optionalVersionedType = fsOption.Just(versionedType);
|
||||
}
|
||||
}
|
||||
|
||||
_cache[type] = optionalVersionedType;
|
||||
}
|
||||
|
||||
return optionalVersionedType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the given type has constructors to migrate from all
|
||||
/// ancestor types.
|
||||
/// </summary>
|
||||
private static void VerifyConstructors(fsVersionedType type)
|
||||
{
|
||||
var publicConstructors = type.ModelType.GetDeclaredConstructors();
|
||||
|
||||
for (var i = 0; i < type.Ancestors.Length; ++i)
|
||||
{
|
||||
var requiredConstructorType = type.Ancestors[i].ModelType;
|
||||
|
||||
var found = false;
|
||||
for (var j = 0; j < publicConstructors.Length; ++j)
|
||||
{
|
||||
var parameters = publicConstructors[j].GetParameters();
|
||||
if (parameters.Length == 1 && parameters[0].ParameterType == requiredConstructorType)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found == false)
|
||||
{
|
||||
throw new fsMissingVersionConstructorException(type.ModelType, requiredConstructorType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the given version graph contains only unique versions.
|
||||
/// </summary>
|
||||
private static void VerifyUniqueVersionStrings(fsVersionedType type)
|
||||
{
|
||||
// simple tree traversal
|
||||
|
||||
var found = new Dictionary<string, Type>();
|
||||
|
||||
var remaining = new Queue<fsVersionedType>();
|
||||
remaining.Enqueue(type);
|
||||
|
||||
while (remaining.Count > 0)
|
||||
{
|
||||
var item = remaining.Dequeue();
|
||||
|
||||
// Verify we do not already have the version string. Take into
|
||||
// account that we're not just comparing the same model twice,
|
||||
// since we can have a valid import graph that has the same model
|
||||
// multiple times.
|
||||
if (found.ContainsKey(item.VersionString) && found[item.VersionString] != item.ModelType)
|
||||
{
|
||||
throw new fsDuplicateVersionNameException(found[item.VersionString], item.ModelType, item.VersionString);
|
||||
}
|
||||
found[item.VersionString] = item.ModelType;
|
||||
|
||||
// scan the ancestors as well
|
||||
foreach (var ancestor in item.Ancestors)
|
||||
{
|
||||
remaining.Enqueue(ancestor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b20bca1887a4a470897d494be7929520
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer.Internal
|
||||
{
|
||||
public struct fsVersionedType
|
||||
{
|
||||
/// <summary>
|
||||
/// The direct ancestors that this type can import.
|
||||
/// </summary>
|
||||
public fsVersionedType[] Ancestors;
|
||||
|
||||
/// <summary>
|
||||
/// The identifying string that is unique among all ancestors.
|
||||
/// </summary>
|
||||
public string VersionString;
|
||||
|
||||
/// <summary>
|
||||
/// The modeling type that this versioned type maps back to.
|
||||
/// </summary>
|
||||
public Type ModelType;
|
||||
|
||||
/// <summary>
|
||||
/// Migrate from an instance of an ancestor.
|
||||
/// </summary>
|
||||
public object Migrate(object ancestorInstance)
|
||||
{
|
||||
return Activator.CreateInstance(ModelType, ancestorInstance);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "fsVersionedType [ModelType=" + ModelType + ", VersionString=" + VersionString + ", Ancestors.Length=" + Ancestors.Length + "]";
|
||||
}
|
||||
|
||||
public static bool operator ==(fsVersionedType a, fsVersionedType b)
|
||||
{
|
||||
return a.ModelType == b.ModelType;
|
||||
}
|
||||
|
||||
public static bool operator !=(fsVersionedType a, fsVersionedType b)
|
||||
{
|
||||
return a.ModelType != b.ModelType;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return
|
||||
obj is fsVersionedType &&
|
||||
ModelType == ((fsVersionedType)obj).ModelType;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return ModelType.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 201c5a374fe1842a591c42128390be5c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9ddd8c837d1c542b5aaf490022c44132
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,168 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using Unity.VisualScripting.FullSerializer.Internal;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// A property or field on a MetaType. This unifies the FieldInfo and
|
||||
/// PropertyInfo classes.
|
||||
/// </summary>
|
||||
public class fsMetaProperty
|
||||
{
|
||||
internal fsMetaProperty(fsConfig config, FieldInfo field)
|
||||
{
|
||||
_memberInfo = field;
|
||||
StorageType = field.FieldType;
|
||||
MemberName = field.Name;
|
||||
IsPublic = field.IsPublic;
|
||||
IsReadOnly = field.IsInitOnly;
|
||||
CanRead = true;
|
||||
CanWrite = true;
|
||||
|
||||
CommonInitialize(config);
|
||||
}
|
||||
|
||||
internal fsMetaProperty(fsConfig config, PropertyInfo property)
|
||||
{
|
||||
_memberInfo = property;
|
||||
StorageType = property.PropertyType;
|
||||
MemberName = property.Name;
|
||||
IsPublic = property.GetGetMethod() != null && property.GetGetMethod().IsPublic && property.GetSetMethod() != null && property.GetSetMethod().IsPublic;
|
||||
IsReadOnly = false;
|
||||
CanRead = property.CanRead;
|
||||
CanWrite = property.CanWrite;
|
||||
|
||||
CommonInitialize(config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal handle to the reflected member.
|
||||
/// </summary>
|
||||
internal MemberInfo _memberInfo;
|
||||
|
||||
/// <summary>
|
||||
/// The type of value that is stored inside of the property. For example,
|
||||
/// for an int field, StorageType will be typeof(int).
|
||||
/// </summary>
|
||||
public Type StorageType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A custom fsBaseConverter instance to use for this field/property, if
|
||||
/// requested. This will be null if the default converter selection
|
||||
/// algorithm should be used. This is specified using the [fsObject]
|
||||
/// annotation with the Converter field.
|
||||
/// </summary>
|
||||
public Type OverrideConverterType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Can this property be read?
|
||||
/// </summary>
|
||||
public bool CanRead { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Can this property be written to?
|
||||
/// </summary>
|
||||
public bool CanWrite { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The serialized name of the property, as it should appear in JSON.
|
||||
/// </summary>
|
||||
public string JsonName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the actual member.
|
||||
/// </summary>
|
||||
public string MemberName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is this member public?
|
||||
/// </summary>
|
||||
public bool IsPublic { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is this type readonly? We can modify readonly properties using
|
||||
/// reflection, but not using generated C#.
|
||||
/// </summary>
|
||||
public bool IsReadOnly { get; private set; }
|
||||
|
||||
private void CommonInitialize(fsConfig config)
|
||||
{
|
||||
var attr = fsPortableReflection.GetAttribute<fsPropertyAttribute>(_memberInfo);
|
||||
if (attr != null)
|
||||
{
|
||||
JsonName = attr.Name;
|
||||
OverrideConverterType = attr.Converter;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(JsonName))
|
||||
{
|
||||
JsonName = config.GetJsonNameFromMemberName(MemberName, _memberInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a value to the property that this MetaProperty represents,
|
||||
/// using given object instance as the context.
|
||||
/// </summary>
|
||||
public void Write(object context, object value)
|
||||
{
|
||||
var field = _memberInfo as FieldInfo;
|
||||
var property = _memberInfo as PropertyInfo;
|
||||
|
||||
if (field != null)
|
||||
{
|
||||
if (PlatformUtility.supportsJit)
|
||||
{
|
||||
field.SetValueOptimized(context, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
field.SetValue(context, value);
|
||||
}
|
||||
}
|
||||
else if (property != null)
|
||||
{
|
||||
if (PlatformUtility.supportsJit)
|
||||
{
|
||||
if (property.CanWrite)
|
||||
{
|
||||
property.SetValueOptimized(context, value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var setMethod = property.GetSetMethod(/*nonPublic:*/ true);
|
||||
if (setMethod != null)
|
||||
{
|
||||
setMethod.Invoke(context, new[] { value });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a value from the property that this MetaProperty represents,
|
||||
/// using the given object instance as the context.
|
||||
/// </summary>
|
||||
public object Read(object context)
|
||||
{
|
||||
if (_memberInfo is PropertyInfo)
|
||||
{
|
||||
#if LUDIQ_OPTIMIZE
|
||||
return ((PropertyInfo)_memberInfo).GetValueOptimized(context);
|
||||
#else
|
||||
return ((PropertyInfo)_memberInfo).GetValue(context, null); // Attempt fix-1588
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#if LUDIQ_OPTIMIZE
|
||||
return ((FieldInfo)_memberInfo).GetValueOptimized(context);
|
||||
#else
|
||||
return ((FieldInfo)_memberInfo).GetValue(context);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 726bd1b4a2da44073b58af642cb2cd67
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,414 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.VisualScripting.FullSerializer.Internal;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// MetaType contains metadata about a type. This is used by the reflection
|
||||
/// serializer.
|
||||
/// </summary>
|
||||
public class fsMetaType
|
||||
{
|
||||
private fsMetaType(fsConfig config, Type reflectedType)
|
||||
{
|
||||
ReflectedType = reflectedType;
|
||||
|
||||
var properties = new List<fsMetaProperty>();
|
||||
CollectProperties(config, properties, reflectedType);
|
||||
Properties = properties.ToArray();
|
||||
}
|
||||
|
||||
public Type ReflectedType;
|
||||
private bool _hasEmittedAotData;
|
||||
private bool? _hasDefaultConstructorCache;
|
||||
private bool _isDefaultConstructorPublic;
|
||||
|
||||
public fsMetaProperty[] Properties { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the type represented by this metadata contains a
|
||||
/// default constructor.
|
||||
/// </summary>
|
||||
public bool HasDefaultConstructor
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_hasDefaultConstructorCache.HasValue == false)
|
||||
{
|
||||
// arrays are considered to have a default constructor
|
||||
if (ReflectedType.Resolve().IsArray)
|
||||
{
|
||||
_hasDefaultConstructorCache = true;
|
||||
_isDefaultConstructorPublic = true;
|
||||
}
|
||||
// value types (ie, structs) always have a default
|
||||
// constructor
|
||||
else if (ReflectedType.Resolve().IsValueType)
|
||||
{
|
||||
_hasDefaultConstructorCache = true;
|
||||
_isDefaultConstructorPublic = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// consider private constructors as well
|
||||
var ctor = ReflectedType.GetDeclaredConstructor(fsPortableReflection.EmptyTypes);
|
||||
_hasDefaultConstructorCache = ctor != null;
|
||||
if (ctor != null)
|
||||
{
|
||||
_isDefaultConstructorPublic = ctor.IsPublic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _hasDefaultConstructorCache.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to emit an AOT compiled direct converter for this type.
|
||||
/// </summary>
|
||||
/// <returns>True if AOT data was emitted, false otherwise.</returns>
|
||||
public bool EmitAotData()
|
||||
{
|
||||
if (_hasEmittedAotData == false)
|
||||
{
|
||||
_hasEmittedAotData = true;
|
||||
|
||||
// NOTE: Even if the type has derived types, we can still
|
||||
// generate a direct converter for it. Direct converters are not
|
||||
// used for inherited types, so the reflected converter or
|
||||
// something similar will be used for the derived type instead of
|
||||
// our AOT compiled one.
|
||||
|
||||
for (var i = 0; i < Properties.Length; ++i)
|
||||
{
|
||||
// Cannot AOT compile since we need to public member access.
|
||||
if (Properties[i].IsPublic == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Cannot AOT compile since readonly members can only be
|
||||
// modified using reflection.
|
||||
if (Properties[i].IsReadOnly)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Cannot AOT compile since we need a default ctor.
|
||||
if (HasDefaultConstructor == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
fsAotCompilationManager.AddAotCompilation(ReflectedType, Properties, _isDefaultConstructorPublic);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the type that this metadata points back to.
|
||||
/// If this type has a default constructor, then Activator.CreateInstance
|
||||
/// will be used to construct the type (or Array.CreateInstance if it an
|
||||
/// array). Otherwise, an uninitialized object created via
|
||||
/// FormatterServices.GetSafeUninitializedObject is used to construct the
|
||||
/// instance.
|
||||
/// </summary>
|
||||
public object CreateInstance()
|
||||
{
|
||||
if (ReflectedType.Resolve().IsInterface || ReflectedType.Resolve().IsAbstract)
|
||||
{
|
||||
throw new Exception("Cannot create an instance of an interface or abstract type for " + ReflectedType);
|
||||
}
|
||||
|
||||
#if !NO_UNITY
|
||||
// Unity requires special construction logic for types that derive
|
||||
// from ScriptableObject.
|
||||
if (typeof(UnityEngine.ScriptableObject).IsAssignableFrom(ReflectedType))
|
||||
{
|
||||
return UnityEngine.ScriptableObject.CreateInstance(ReflectedType);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Strings don't have default constructors but also fail when run
|
||||
// through FormatterSerivces.GetSafeUninitializedObject
|
||||
if (typeof(string) == ReflectedType)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
if (HasDefaultConstructor == false)
|
||||
{
|
||||
#if !UNITY_EDITOR && (UNITY_WEBPLAYER || UNITY_WP8 || UNITY_METRO)
|
||||
throw new InvalidOperationException("The selected Unity platform requires " +
|
||||
ReflectedType.FullName + " to have a default constructor. Please add one.");
|
||||
#else
|
||||
return System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject(ReflectedType);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (ReflectedType.Resolve().IsArray)
|
||||
{
|
||||
// we have to start with a size zero array otherwise it will have
|
||||
// invalid data inside of it
|
||||
return Array.CreateInstance(ReflectedType.GetElementType(), 0);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
#if (!UNITY_EDITOR && (UNITY_METRO))
|
||||
// In WinRT/WinStore builds, Activator.CreateInstance(..., true)
|
||||
// is broken
|
||||
return Activator.CreateInstance(ReflectedType);
|
||||
#else
|
||||
return Activator.CreateInstance(ReflectedType, /*nonPublic:*/ true);
|
||||
#endif
|
||||
}
|
||||
#if (!UNITY_EDITOR && (UNITY_METRO)) == false
|
||||
catch (MissingMethodException e)
|
||||
{
|
||||
throw new InvalidOperationException("Unable to create instance of " + ReflectedType + "; there is no default constructor", e);
|
||||
}
|
||||
#endif
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
throw new InvalidOperationException("Constructor of " + ReflectedType + " threw an exception when creating an instance", e);
|
||||
}
|
||||
catch (MemberAccessException e)
|
||||
{
|
||||
throw new InvalidOperationException("Unable to access constructor of " + ReflectedType, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<fsConfig, Dictionary<Type, fsMetaType>> _configMetaTypes =
|
||||
new Dictionary<fsConfig, Dictionary<Type, fsMetaType>>();
|
||||
|
||||
public static fsMetaType Get(fsConfig config, Type type)
|
||||
{
|
||||
Dictionary<Type, fsMetaType> metaTypes;
|
||||
lock (typeof(fsMetaType))
|
||||
{
|
||||
if (_configMetaTypes.TryGetValue(config, out metaTypes) == false)
|
||||
{
|
||||
metaTypes = _configMetaTypes[config] = new Dictionary<Type, fsMetaType>();
|
||||
}
|
||||
}
|
||||
|
||||
fsMetaType metaType;
|
||||
if (metaTypes.TryGetValue(type, out metaType) == false)
|
||||
{
|
||||
metaType = new fsMetaType(config, type);
|
||||
metaTypes[type] = metaType;
|
||||
}
|
||||
|
||||
return metaType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears out the cached type results. Useful if some prior assumptions
|
||||
/// become invalid, ie, the default member serialization mode.
|
||||
/// </summary>
|
||||
public static void ClearCache()
|
||||
{
|
||||
lock (typeof(fsMetaType))
|
||||
{
|
||||
_configMetaTypes = new Dictionary<fsConfig, Dictionary<Type, fsMetaType>>();
|
||||
}
|
||||
}
|
||||
|
||||
private static void CollectProperties(fsConfig config, List<fsMetaProperty> properties, Type reflectedType)
|
||||
{
|
||||
// do we require a [SerializeField] or [fsProperty] attribute?
|
||||
var requireOptIn = config.DefaultMemberSerialization == fsMemberSerialization.OptIn;
|
||||
var requireOptOut = config.DefaultMemberSerialization == fsMemberSerialization.OptOut;
|
||||
|
||||
var attr = fsPortableReflection.GetAttribute<fsObjectAttribute>(reflectedType);
|
||||
if (attr != null)
|
||||
{
|
||||
requireOptIn = attr.MemberSerialization == fsMemberSerialization.OptIn;
|
||||
requireOptOut = attr.MemberSerialization == fsMemberSerialization.OptOut;
|
||||
}
|
||||
|
||||
var members = reflectedType.GetDeclaredMembers();
|
||||
foreach (var member in members)
|
||||
{
|
||||
// We don't serialize members annotated with any of the ignore
|
||||
// serialize attributes
|
||||
if (config.IgnoreSerializeAttributes.Any(t => fsPortableReflection.HasAttribute(member, t)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var property = member as PropertyInfo;
|
||||
var field = member as FieldInfo;
|
||||
|
||||
// Early out if it's neither a field or a property, since we
|
||||
// don't serialize anything else.
|
||||
if (property == null && field == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip properties if we don't want them, to avoid the cost of
|
||||
// checking attributes.
|
||||
if (property != null && !config.EnablePropertySerialization)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// If an opt-in annotation is required, then skip the property if
|
||||
// it doesn't have one of the serialize attributes
|
||||
if (requireOptIn &&
|
||||
!config.SerializeAttributes.Any(t => fsPortableReflection.HasAttribute(member, t)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// If an opt-out annotation is required, then skip the property
|
||||
// *only if* it has one of the not serialize attributes
|
||||
if (requireOptOut &&
|
||||
config.IgnoreSerializeAttributes.Any(t => fsPortableReflection.HasAttribute(member, t)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (property != null)
|
||||
{
|
||||
if (CanSerializeProperty(config, property, members, requireOptOut))
|
||||
{
|
||||
properties.Add(new fsMetaProperty(config, property));
|
||||
}
|
||||
}
|
||||
else if (field != null)
|
||||
{
|
||||
if (CanSerializeField(config, field, requireOptOut))
|
||||
{
|
||||
properties.Add(new fsMetaProperty(config, field));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reflectedType.Resolve().BaseType != null)
|
||||
{
|
||||
CollectProperties(config, properties, reflectedType.Resolve().BaseType);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsAutoProperty(PropertyInfo property, MemberInfo[] members)
|
||||
{
|
||||
return
|
||||
property.CanWrite && property.CanRead &&
|
||||
fsPortableReflection.HasAttribute(
|
||||
property.GetGetMethod(), typeof(CompilerGeneratedAttribute), /*shouldCache:*/ false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if the given property should be serialized.
|
||||
/// </summary>
|
||||
/// <param name="annotationFreeValue">
|
||||
/// Should a property without any annotations be serialized?
|
||||
/// </param>
|
||||
private static bool CanSerializeProperty(fsConfig config, PropertyInfo property, MemberInfo[] members, bool annotationFreeValue)
|
||||
{
|
||||
// We don't serialize delegates
|
||||
if (typeof(Delegate).IsAssignableFrom(property.PropertyType))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var publicGetMethod = property.GetGetMethod(/*nonPublic:*/ false);
|
||||
var publicSetMethod = property.GetSetMethod(/*nonPublic:*/ false);
|
||||
|
||||
// We do not bother to serialize static fields.
|
||||
if ((publicGetMethod != null && publicGetMethod.IsStatic) ||
|
||||
(publicSetMethod != null && publicSetMethod.IsStatic))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Never serialize indexers. I can't think of a sane way to
|
||||
// serialize/deserialize them, and they're normally wrappers around
|
||||
// other fields anyway...
|
||||
if (property.GetIndexParameters().Length > 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If a property is annotated with one of the serializable
|
||||
// attributes, then it should it should definitely be serialized.
|
||||
//
|
||||
// NOTE: We place this override check *after* the static check,
|
||||
// because we *never* allow statics to be serialized.
|
||||
if (config.SerializeAttributes.Any(t => fsPortableReflection.HasAttribute(property, t)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the property cannot be both read and written to, we are not
|
||||
// going to serialize it regardless of the default serialization mode
|
||||
if (property.CanRead == false || property.CanWrite == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Depending on the configuration options, check whether the property
|
||||
// is automatic and if it has a public setter to determine whether it
|
||||
// should be serialized
|
||||
if ((publicGetMethod != null && (config.SerializeNonPublicSetProperties || publicSetMethod != null)) &&
|
||||
(config.SerializeNonAutoProperties || IsAutoProperty(property, members)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, we don't bother with serialization
|
||||
return annotationFreeValue;
|
||||
}
|
||||
|
||||
private static bool CanSerializeField(fsConfig config, FieldInfo field, bool annotationFreeValue)
|
||||
{
|
||||
// We don't serialize delegates
|
||||
if (typeof(Delegate).IsAssignableFrom(field.FieldType))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// We don't serialize compiler generated fields.
|
||||
// LAZLO / LUDIQ FIX: Attributes inheritance
|
||||
if (Attribute.IsDefined(field, typeof(CompilerGeneratedAttribute), false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// We don't serialize static fields
|
||||
if (field.IsStatic)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// We want to serialize any fields annotated with one of the
|
||||
// serialize attributes.
|
||||
//
|
||||
// NOTE: This occurs *after* the static check, because we *never*
|
||||
// want to serialize static fields.
|
||||
if (config.SerializeAttributes.Any(t => fsPortableReflection.HasAttribute(field, t)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// We use !IsPublic because that also checks for internal, protected,
|
||||
// and private.
|
||||
if (!annotationFreeValue && !field.IsPublic)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8cacd6b711f9c44b2bcaeee005d77210
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,61 @@
|
|||
using System;
|
||||
using Unity.VisualScripting.FullSerializer.Internal;
|
||||
|
||||
#if !UNITY_EDITOR && UNITY_WSA
|
||||
// For System.Reflection.TypeExtensions
|
||||
using System.Reflection;
|
||||
#endif
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
public static class fsReflectionUtility
|
||||
{
|
||||
/// <summary>
|
||||
/// Searches for a particular implementation of the given interface type
|
||||
/// inside of the type. This is particularly useful if the interface type
|
||||
/// is an open type, ie, typeof(IFace{}), because this method will then
|
||||
/// return IFace{} but with appropriate type parameters inserted.
|
||||
/// </summary>
|
||||
/// <param name="type">The base type to search for interface</param>
|
||||
/// <param name="interfaceType">
|
||||
/// The interface type to search for. Can be an open generic type.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The actual interface type that the type contains, or null if there is
|
||||
/// no implementation of the given interfaceType on type.
|
||||
/// </returns>
|
||||
public static Type GetInterface(Type type, Type interfaceType)
|
||||
{
|
||||
if (interfaceType.Resolve().IsGenericType &&
|
||||
interfaceType.Resolve().IsGenericTypeDefinition == false)
|
||||
{
|
||||
throw new ArgumentException("GetInterface requires that if the interface " +
|
||||
"type is generic, then it must be the generic type definition, not a " +
|
||||
"specific generic type instantiation");
|
||||
}
|
||||
;
|
||||
|
||||
while (type != null)
|
||||
{
|
||||
foreach (var iface in type.GetInterfaces())
|
||||
{
|
||||
if (iface.Resolve().IsGenericType)
|
||||
{
|
||||
if (interfaceType == iface.GetGenericTypeDefinition())
|
||||
{
|
||||
return iface;
|
||||
}
|
||||
}
|
||||
else if (interfaceType == iface)
|
||||
{
|
||||
return iface;
|
||||
}
|
||||
}
|
||||
|
||||
type = type.Resolve().BaseType;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 51067c71ac63e4194be5651f0c1a32ad
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,11 @@
|
|||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Caches type name to type lookups. Type lookups occur in all loaded
|
||||
/// assemblies.
|
||||
/// </summary>
|
||||
public static class fsTypeCache
|
||||
{
|
||||
// Moved / converted to RuntimeCodebase
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 75dc9dc2af22844e2a7d47d23b127f49
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,163 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Unity.VisualScripting.FullSerializer.Internal;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// The AOT compilation manager
|
||||
/// </summary>
|
||||
public class fsAotCompilationManager
|
||||
{
|
||||
private static Dictionary<Type, string> _computedAotCompilations = new Dictionary<Type, string>();
|
||||
private static List<AotCompilation> _uncomputedAotCompilations = new List<AotCompilation>();
|
||||
|
||||
/// <summary>
|
||||
/// Ahead of time compilations that are available. The type maps to the
|
||||
/// object type the generated converter will serialize/deserialize, and
|
||||
/// the string is the text content for a converter that will do the
|
||||
/// serialization.
|
||||
/// <para />
|
||||
/// The generated serializer is completely independent and you don't need
|
||||
/// to do anything. Simply add the file to your project and it'll get
|
||||
/// used instead of the reflection based one.
|
||||
/// </summary>
|
||||
public static Dictionary<Type, string> AvailableAotCompilations
|
||||
{
|
||||
get
|
||||
{
|
||||
for (var i = 0; i < _uncomputedAotCompilations.Count; ++i)
|
||||
{
|
||||
var item = _uncomputedAotCompilations[i];
|
||||
_computedAotCompilations[item.Type] = GenerateDirectConverterForTypeInCSharp(item.Type, item.Members, item.IsConstructorPublic);
|
||||
}
|
||||
_uncomputedAotCompilations.Clear();
|
||||
|
||||
return _computedAotCompilations;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is a helper method that makes it simple to run an AOT
|
||||
/// compilation on the given type.
|
||||
/// </summary>
|
||||
/// <param name="config">
|
||||
/// The configuration to use when running AOT compilation.
|
||||
/// </param>
|
||||
/// <param name="type">
|
||||
/// The type to perform the AOT compilation on.
|
||||
/// </param>
|
||||
/// <param name="aotCompiledClassInCSharp">
|
||||
/// The AOT class. Add this C# code to your project.
|
||||
/// </param>
|
||||
/// <returns>True if AOT compilation was successful.</returns>
|
||||
public static bool TryToPerformAotCompilation(fsConfig config, Type type, out string aotCompiledClassInCSharp)
|
||||
{
|
||||
if (fsMetaType.Get(config, type).EmitAotData())
|
||||
{
|
||||
aotCompiledClassInCSharp = AvailableAotCompilations[type];
|
||||
return true;
|
||||
}
|
||||
|
||||
aotCompiledClassInCSharp = default(string);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new AOT compilation unit.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of object we are AOT compiling.</param>
|
||||
/// <param name="members">
|
||||
/// The members on the object which will be serialized/deserialized.
|
||||
/// </param>
|
||||
public static void AddAotCompilation(Type type, fsMetaProperty[] members, bool isConstructorPublic)
|
||||
{
|
||||
_uncomputedAotCompilations.Add(new AotCompilation
|
||||
{
|
||||
Type = type,
|
||||
Members = members,
|
||||
IsConstructorPublic = isConstructorPublic
|
||||
});
|
||||
}
|
||||
|
||||
private static string GetConverterString(fsMetaProperty member)
|
||||
{
|
||||
if (member.OverrideConverterType == null)
|
||||
{
|
||||
return "null";
|
||||
}
|
||||
|
||||
return $"typeof({fsTypeExtensions.CSharpName( /*includeNamespace:*/member.OverrideConverterType, true)})";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AOT compiles the object (in C#).
|
||||
/// </summary>
|
||||
private static string GenerateDirectConverterForTypeInCSharp(Type type, fsMetaProperty[] members, bool isConstructorPublic)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var typeName = fsTypeExtensions.CSharpName( /*includeNamespace:*/
|
||||
type, true);
|
||||
var typeNameSafeDecl = type.CSharpName(true, true);
|
||||
|
||||
sb.AppendLine("using System;");
|
||||
sb.AppendLine("using System.Collections.Generic;");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("namespace Unity.VisualScripting.Dependencies.FullSerializer {");
|
||||
sb.AppendLine(" partial class fsConverterRegistrar {");
|
||||
sb.AppendLine(" public static Speedup." + typeNameSafeDecl + "_DirectConverter " + "Register_" + typeNameSafeDecl + ";");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine("}");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("namespace Unity.VisualScripting.Dependencies.FullSerializer.Speedup {");
|
||||
sb.AppendLine(" public class " + typeNameSafeDecl + "_DirectConverter : fsDirectConverter<" + typeName + "> {");
|
||||
sb.AppendLine(" protected override fsResult DoSerialize(" + typeName + " model, Dictionary<string, fsData> serialized) {");
|
||||
sb.AppendLine(" var result = fsResult.Success;");
|
||||
sb.AppendLine();
|
||||
foreach (var member in members)
|
||||
{
|
||||
sb.AppendLine(" result += SerializeMember(serialized, " + GetConverterString(member) + ", \"" + member.JsonName + "\", model." + member.MemberName + ");");
|
||||
}
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(" return result;");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(" protected override fsResult DoDeserialize(Dictionary<string, fsData> data, ref " + typeName + " model) {");
|
||||
sb.AppendLine(" var result = fsResult.Success;");
|
||||
sb.AppendLine();
|
||||
for (var i = 0; i < members.Length; ++i)
|
||||
{
|
||||
var member = members[i];
|
||||
sb.AppendLine(" var t" + i + " = model." + member.MemberName + ";");
|
||||
sb.AppendLine(" result += DeserializeMember(data, " + GetConverterString(member) + ", \"" + member.JsonName + "\", out t" + i + ");");
|
||||
sb.AppendLine(" model." + member.MemberName + " = t" + i + ";");
|
||||
sb.AppendLine();
|
||||
}
|
||||
sb.AppendLine(" return result;");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine();
|
||||
sb.AppendLine(" public override object CreateInstance(fsData data, Type storageType) {");
|
||||
if (isConstructorPublic)
|
||||
{
|
||||
sb.AppendLine(" return new " + typeName + "();");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine(" return Activator.CreateInstance(typeof(" + typeName + "), /*nonPublic:*/true);");
|
||||
}
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine(" }");
|
||||
sb.AppendLine("}");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private struct AotCompilation
|
||||
{
|
||||
public Type Type;
|
||||
public fsMetaProperty[] Members;
|
||||
public bool IsConstructorPublic;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f658832818572457f80abe66f86cc055
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,161 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Unity.VisualScripting.FullSerializer.Internal;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// The serialization converter allows for customization of the serialization
|
||||
/// process.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You do not want to derive from this class - there is no way to actually
|
||||
/// use it within the serializer.. Instead, derive from either fsConverter or
|
||||
/// fsDirectConverter
|
||||
/// </remarks>
|
||||
public abstract class fsBaseConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// The serializer that was owns this converter.
|
||||
/// </summary>
|
||||
public fsSerializer Serializer;
|
||||
|
||||
/// <summary>
|
||||
/// Construct an object instance that will be passed to TryDeserialize.
|
||||
/// This should **not** deserialize the object.
|
||||
/// </summary>
|
||||
/// <param name="data">The data the object was serialized with.</param>
|
||||
/// <param name="storageType">
|
||||
/// The field/property type that is storing the instance.
|
||||
/// </param>
|
||||
/// <returns>An object instance</returns>
|
||||
public virtual object CreateInstance(fsData data, Type storageType)
|
||||
{
|
||||
if (RequestCycleSupport(storageType))
|
||||
{
|
||||
throw new InvalidOperationException("Please override CreateInstance for " +
|
||||
GetType() + "; the object graph for " + storageType +
|
||||
" can contain potentially contain cycles, so separated instance creation " +
|
||||
"is needed");
|
||||
}
|
||||
|
||||
return storageType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If true, then the serializer will support cyclic references with the
|
||||
/// given converted type.
|
||||
/// </summary>
|
||||
/// <param name="storageType">
|
||||
/// The field/property type that is currently storing the object that is
|
||||
/// being serialized.
|
||||
/// </param>
|
||||
public virtual bool RequestCycleSupport(Type storageType)
|
||||
{
|
||||
if (storageType == typeof(string))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return storageType.Resolve().IsClass || storageType.Resolve().IsInterface;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If true, then the serializer will include inheritance data for the
|
||||
/// given converter.
|
||||
/// </summary>
|
||||
/// <param name="storageType">
|
||||
/// The field/property type that is currently storing the object that is
|
||||
/// being serialized.
|
||||
/// </param>
|
||||
public virtual bool RequestInheritanceSupport(Type storageType)
|
||||
{
|
||||
return storageType.Resolve().IsSealed == false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serialize the actual object into the given data storage.
|
||||
/// </summary>
|
||||
/// <param name="instance">
|
||||
/// The object instance to serialize. This will never be null.
|
||||
/// </param>
|
||||
/// <param name="serialized">The serialized state.</param>
|
||||
/// <param name="storageType">
|
||||
/// The field/property type that is storing this instance.
|
||||
/// </param>
|
||||
/// <returns>If serialization was successful.</returns>
|
||||
public abstract fsResult TrySerialize(object instance, out fsData serialized, Type storageType);
|
||||
|
||||
/// <summary>
|
||||
/// Deserialize data into the object instance.
|
||||
/// </summary>
|
||||
/// <param name="data">Serialization data to deserialize from.</param>
|
||||
/// <param name="instance">
|
||||
/// The object instance to deserialize into.
|
||||
/// </param>
|
||||
/// <param name="storageType">
|
||||
/// The field/property type that is storing the instance.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// True if serialization was successful, false otherwise.
|
||||
/// </returns>
|
||||
public abstract fsResult TryDeserialize(fsData data, ref object instance, Type storageType);
|
||||
|
||||
protected fsResult FailExpectedType(fsData data, params fsDataType[] types)
|
||||
{
|
||||
return fsResult.Fail(GetType().Name + " expected one of " +
|
||||
string.Join(", ", types.Select(t => t.ToString()).ToArray()) +
|
||||
" but got " + data.Type + " in " + data);
|
||||
}
|
||||
|
||||
protected fsResult CheckType(fsData data, fsDataType type)
|
||||
{
|
||||
if (data.Type != type)
|
||||
{
|
||||
return fsResult.Fail(GetType().Name + " expected " + type + " but got " + data.Type + " in " + data);
|
||||
}
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
protected fsResult CheckKey(fsData data, string key, out fsData subitem)
|
||||
{
|
||||
return CheckKey(data.AsDictionary, key, out subitem);
|
||||
}
|
||||
|
||||
protected fsResult CheckKey(Dictionary<string, fsData> data, string key, out fsData subitem)
|
||||
{
|
||||
if (data.TryGetValue(key, out subitem) == false)
|
||||
{
|
||||
return fsResult.Fail(GetType().Name + " requires a <" + key + "> key in the data " + data);
|
||||
}
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
protected fsResult SerializeMember<T>(Dictionary<string, fsData> data, Type overrideConverterType, string name, T value)
|
||||
{
|
||||
fsData memberData;
|
||||
var result = Serializer.TrySerialize(typeof(T), overrideConverterType, value, out memberData);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
data[name] = memberData;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected fsResult DeserializeMember<T>(Dictionary<string, fsData> data, Type overrideConverterType, string name, out T value)
|
||||
{
|
||||
fsData memberData;
|
||||
if (data.TryGetValue(name, out memberData) == false)
|
||||
{
|
||||
value = default(T);
|
||||
return fsResult.Fail("Unable to find member \"" + name + "\"");
|
||||
}
|
||||
|
||||
object storage = null;
|
||||
var result = Serializer.TryDeserialize(memberData, typeof(T), overrideConverterType, ref storage);
|
||||
value = (T)storage;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5c7e2ff8b37f14fb29e741eea37c4af1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,115 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
// Global configuration options.
|
||||
public static class fsGlobalConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Should deserialization be case sensitive? If this is false and the
|
||||
/// JSON has multiple members with the same keys only separated by case,
|
||||
/// then this results in undefined behavior.
|
||||
/// </summary>
|
||||
public static bool IsCaseSensitive = true;
|
||||
|
||||
/// <summary>
|
||||
/// If exceptions are allowed internally, then additional date formats
|
||||
/// can be deserialized. Note that the Full Serializer public API will
|
||||
/// *not* throw exceptions with this enabled; errors will still be
|
||||
/// returned in a fsResult instance.
|
||||
/// </summary>
|
||||
public static bool AllowInternalExceptions = true;
|
||||
|
||||
/// <summary>
|
||||
/// This string will be used to prefix fields used internally by
|
||||
/// FullSerializer.
|
||||
/// </summary>
|
||||
public static string InternalFieldPrefix = "$";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables some top-level customization of Full Serializer.
|
||||
/// </summary>
|
||||
public class fsConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// The attributes that will force a field or property to be serialized.
|
||||
/// </summary>
|
||||
public Type[] SerializeAttributes =
|
||||
{
|
||||
#if !NO_UNITY
|
||||
typeof(UnityEngine.SerializeField),
|
||||
#endif
|
||||
typeof(fsPropertyAttribute),
|
||||
typeof(SerializeAttribute),
|
||||
typeof(SerializeAsAttribute)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The attributes that will force a field or property to *not* be
|
||||
/// serialized.
|
||||
/// </summary>
|
||||
public Type[] IgnoreSerializeAttributes =
|
||||
{
|
||||
typeof(NonSerializedAttribute),
|
||||
typeof(fsIgnoreAttribute),
|
||||
typeof(DoNotSerializeAttribute)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The default member serialization.
|
||||
/// </summary>
|
||||
public fsMemberSerialization DefaultMemberSerialization = fsMemberSerialization.Default;
|
||||
|
||||
/// <summary>
|
||||
/// Convert a C# field/property name into the key used for the JSON
|
||||
/// object. For example, you could force all JSON names to lowercase
|
||||
/// with:
|
||||
/// fsConfig.GetJsonNameFromMemberName = (name, info) =>
|
||||
/// name.ToLower();
|
||||
/// This will only be used when the name is not explicitly specified with
|
||||
/// fsProperty.
|
||||
/// </summary>
|
||||
public Func<string, MemberInfo, string> GetJsonNameFromMemberName = (name, info) => name;
|
||||
|
||||
/// <summary>
|
||||
/// If false, then *all* property serialization support will be disabled
|
||||
/// - even properties explicitly annotated with fsProperty or any other
|
||||
/// opt-in annotation.
|
||||
/// Setting this to false means that SerializeNonAutoProperties and
|
||||
/// SerializeNonPublicSetProperties will be completely ignored.
|
||||
/// </summary>
|
||||
public bool EnablePropertySerialization = true;
|
||||
|
||||
/// <summary>
|
||||
/// Should the default serialization behaviour include non-auto
|
||||
/// properties?
|
||||
/// </summary>
|
||||
public bool SerializeNonAutoProperties = false;
|
||||
|
||||
/// <summary>
|
||||
/// Should the default serialization behaviour include properties with
|
||||
/// non-public setters?
|
||||
/// </summary>
|
||||
public bool SerializeNonPublicSetProperties = true;
|
||||
|
||||
/// <summary>
|
||||
/// If not null, this string format will be used for DateTime instead of
|
||||
/// the default one.
|
||||
/// </summary>
|
||||
public string CustomDateTimeFormatString = null;
|
||||
|
||||
/// <summary>
|
||||
/// Int64 and UInt64 will be serialized and deserialized as string for
|
||||
/// compatibility
|
||||
/// </summary>
|
||||
public bool Serialize64BitIntegerAsString = false;
|
||||
|
||||
/// <summary>
|
||||
/// Enums are serialized using their names by default. Setting this to
|
||||
/// true will serialize them as integers instead.
|
||||
/// </summary>
|
||||
public bool SerializeEnumsAsInteger = false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 98d16b6826cab47639c1c29e2a9ed97d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// fsContext stores global metadata that can be used to customize how
|
||||
/// fsConverters operate during serialization.
|
||||
/// </summary>
|
||||
public sealed class fsContext
|
||||
{
|
||||
/// <summary>
|
||||
/// All of the context objects.
|
||||
/// </summary>
|
||||
private readonly Dictionary<Type, object> _contextObjects = new Dictionary<Type, object>();
|
||||
|
||||
/// <summary>
|
||||
/// Removes all context objects from the context.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
_contextObjects.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the context object for the given type with the given value.
|
||||
/// </summary>
|
||||
public void Set<T>(T obj)
|
||||
{
|
||||
_contextObjects[typeof(T)] = obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if there is a context object for the given type.
|
||||
/// </summary>
|
||||
public bool Has<T>()
|
||||
{
|
||||
return _contextObjects.ContainsKey(typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches the context object for the given type.
|
||||
/// </summary>
|
||||
public T Get<T>()
|
||||
{
|
||||
object val;
|
||||
if (_contextObjects.TryGetValue(typeof(T), out val))
|
||||
{
|
||||
return (T)val;
|
||||
}
|
||||
throw new InvalidOperationException("There is no context object of type " + typeof(T));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2056e0c2ed99946f2aa0b59a7d523406
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// The serialization converter allows for customization of the serialization
|
||||
/// process.
|
||||
/// </summary>
|
||||
public abstract class fsConverter : fsBaseConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Can this converter serialize and deserialize the given object type?
|
||||
/// </summary>
|
||||
/// <param name="type">The given object type.</param>
|
||||
/// <returns>
|
||||
/// True if the converter can serialize it, false otherwise.
|
||||
/// </returns>
|
||||
public abstract bool CanProcess(Type type);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 14260676d94004f02b41b5c7844458fe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,47 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.VisualScripting.FullSerializer.Internal;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// This class allows arbitrary code to easily register global converters. To
|
||||
/// add a converter, simply declare a new field called "Register_*" that
|
||||
/// stores the type of converter you would like to add. Alternatively, you
|
||||
/// can do the same with a method called "Register_*"; just add the converter
|
||||
/// type to the `Converters` list.
|
||||
/// </summary>
|
||||
public partial class fsConverterRegistrar
|
||||
{
|
||||
static fsConverterRegistrar()
|
||||
{
|
||||
Converters = new List<Type>();
|
||||
|
||||
foreach (var field in typeof(fsConverterRegistrar).GetDeclaredFields())
|
||||
{
|
||||
if (field.Name.StartsWith("Register_"))
|
||||
{
|
||||
Converters.Add(field.FieldType);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var method in typeof(fsConverterRegistrar).GetDeclaredMethods())
|
||||
{
|
||||
if (method.Name.StartsWith("Register_"))
|
||||
{
|
||||
method.Invoke(null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Type> Converters;
|
||||
//public static AnimationCurve_DirectConverter Register_AnimationCurve_DirectConverter;
|
||||
|
||||
// Example field registration:
|
||||
|
||||
// Example method registration:
|
||||
//public static void Register_AnimationCurve_DirectConverter() {
|
||||
// Converters.Add(typeof(AnimationCurve_DirectConverter));
|
||||
//}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cbda48de6424242dcbc9f142e5a80c40
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,425 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// The actual type that a JsonData instance can store.
|
||||
/// </summary>
|
||||
public enum fsDataType
|
||||
{
|
||||
Array,
|
||||
Object,
|
||||
Double,
|
||||
Int64,
|
||||
Boolean,
|
||||
String,
|
||||
Null
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A union type that stores a serialized value. The stored type can be one
|
||||
/// of six different
|
||||
/// types: null, boolean, double, Int64, string, Dictionary, or List.
|
||||
/// </summary>
|
||||
public sealed class fsData
|
||||
{
|
||||
/// <summary>
|
||||
/// The raw value that this serialized data stores. It can be one of six
|
||||
/// different types; a boolean, a double, Int64, a string, a Dictionary,
|
||||
/// or a List.
|
||||
/// </summary>
|
||||
private object _value;
|
||||
|
||||
#region ToString Implementation
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return fsJsonPrinter.CompressedJson(this);
|
||||
}
|
||||
|
||||
#endregion ToString Implementation
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Creates a fsData instance that holds null.
|
||||
/// </summary>
|
||||
public fsData()
|
||||
{
|
||||
_value = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a fsData instance that holds a boolean.
|
||||
/// </summary>
|
||||
public fsData(bool boolean)
|
||||
{
|
||||
_value = boolean;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a fsData instance that holds a double.
|
||||
/// </summary>
|
||||
public fsData(double f)
|
||||
{
|
||||
_value = f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new fsData instance that holds an integer.
|
||||
/// </summary>
|
||||
public fsData(Int64 i)
|
||||
{
|
||||
_value = i;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a fsData instance that holds a string.
|
||||
/// </summary>
|
||||
public fsData(string str)
|
||||
{
|
||||
_value = str;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a fsData instance that holds a dictionary of values.
|
||||
/// </summary>
|
||||
public fsData(Dictionary<string, fsData> dict)
|
||||
{
|
||||
_value = dict;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a fsData instance that holds a list of values.
|
||||
/// </summary>
|
||||
public fsData(List<fsData> list)
|
||||
{
|
||||
_value = list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to create a fsData instance that holds a dictionary.
|
||||
/// </summary>
|
||||
public static fsData CreateDictionary()
|
||||
{
|
||||
return new fsData(new Dictionary<string, fsData>(
|
||||
fsGlobalConfig.IsCaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to create a fsData instance that holds a list.
|
||||
/// </summary>
|
||||
public static fsData CreateList()
|
||||
{
|
||||
return new fsData(new List<fsData>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to create a fsData instance that holds a list with the
|
||||
/// initial capacity.
|
||||
/// </summary>
|
||||
public static fsData CreateList(int capacity)
|
||||
{
|
||||
return new fsData(new List<fsData>(capacity));
|
||||
}
|
||||
|
||||
public readonly static fsData True = new fsData(true);
|
||||
public readonly static fsData False = new fsData(false);
|
||||
public readonly static fsData Null = new fsData();
|
||||
|
||||
#endregion Constructors
|
||||
|
||||
#region Internal Helper Methods
|
||||
|
||||
/// <summary>
|
||||
/// Transforms the internal fsData instance into a dictionary.
|
||||
/// </summary>
|
||||
internal void BecomeDictionary()
|
||||
{
|
||||
_value = new Dictionary<string, fsData>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a shallow clone of this data instance.
|
||||
/// </summary>
|
||||
internal fsData Clone()
|
||||
{
|
||||
var clone = new fsData();
|
||||
clone._value = _value;
|
||||
return clone;
|
||||
}
|
||||
|
||||
#endregion Internal Helper Methods
|
||||
|
||||
#region Casting Predicates
|
||||
|
||||
public fsDataType Type
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_value == null)
|
||||
{
|
||||
return fsDataType.Null;
|
||||
}
|
||||
if (_value is double)
|
||||
{
|
||||
return fsDataType.Double;
|
||||
}
|
||||
if (_value is Int64)
|
||||
{
|
||||
return fsDataType.Int64;
|
||||
}
|
||||
if (_value is bool)
|
||||
{
|
||||
return fsDataType.Boolean;
|
||||
}
|
||||
if (_value is string)
|
||||
{
|
||||
return fsDataType.String;
|
||||
}
|
||||
if (_value is Dictionary<string, fsData>)
|
||||
{
|
||||
return fsDataType.Object;
|
||||
}
|
||||
if (_value is List<fsData>)
|
||||
{
|
||||
return fsDataType.Array;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("unknown JSON data type");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this fsData instance maps back to null.
|
||||
/// </summary>
|
||||
public bool IsNull => _value == null;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this fsData instance maps back to a double.
|
||||
/// </summary>
|
||||
public bool IsDouble => _value is double;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this fsData instance maps back to an Int64.
|
||||
/// </summary>
|
||||
public bool IsInt64 => _value is Int64;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this fsData instance maps back to a boolean.
|
||||
/// </summary>
|
||||
public bool IsBool => _value is bool;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this fsData instance maps back to a string.
|
||||
/// </summary>
|
||||
public bool IsString => _value is string;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this fsData instance maps back to a Dictionary.
|
||||
/// </summary>
|
||||
public bool IsDictionary => _value is Dictionary<string, fsData>;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this fsData instance maps back to a List.
|
||||
/// </summary>
|
||||
public bool IsList => _value is List<fsData>;
|
||||
|
||||
#endregion Casting Predicates
|
||||
|
||||
#region Casts
|
||||
|
||||
/// <summary>
|
||||
/// Casts this fsData to a double. Throws an exception if it is not a
|
||||
/// double.
|
||||
/// </summary>
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
||||
public double AsDouble => Cast<double>();
|
||||
|
||||
/// <summary>
|
||||
/// Casts this fsData to an Int64. Throws an exception if it is not an
|
||||
/// Int64.
|
||||
/// </summary>
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
||||
public Int64 AsInt64 => Cast<Int64>();
|
||||
|
||||
/// <summary>
|
||||
/// Casts this fsData to a boolean. Throws an exception if it is not a
|
||||
/// boolean.
|
||||
/// </summary>
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
||||
public bool AsBool => Cast<bool>();
|
||||
|
||||
/// <summary>
|
||||
/// Casts this fsData to a string. Throws an exception if it is not a
|
||||
/// string.
|
||||
/// </summary>
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
||||
public string AsString => Cast<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Casts this fsData to a Dictionary. Throws an exception if it is not a
|
||||
/// Dictionary.
|
||||
/// </summary>
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
||||
public Dictionary<string, fsData> AsDictionary => Cast<Dictionary<string, fsData>>();
|
||||
|
||||
/// <summary>
|
||||
/// Casts this fsData to a List. Throws an exception if it is not a List.
|
||||
/// </summary>
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
||||
public List<fsData> AsList => Cast<List<fsData>>();
|
||||
|
||||
/// <summary>
|
||||
/// Internal helper method to cast the underlying storage to the given
|
||||
/// type or throw a pretty printed exception on failure.
|
||||
/// </summary>
|
||||
private T Cast<T>()
|
||||
{
|
||||
if (_value is T)
|
||||
{
|
||||
return (T)_value;
|
||||
}
|
||||
|
||||
throw new InvalidCastException("Unable to cast <" + this + "> (with type = " +
|
||||
_value.GetType() + ") to type " + typeof(T));
|
||||
}
|
||||
|
||||
#endregion Casts
|
||||
|
||||
#region Equality Comparisons
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified object is equal to the current
|
||||
/// object.
|
||||
/// </summary>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return Equals(obj as fsData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified object is equal to the current
|
||||
/// object.
|
||||
/// </summary>
|
||||
public bool Equals(fsData other)
|
||||
{
|
||||
if (other == null || Type != other.Type)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case fsDataType.Null:
|
||||
return true;
|
||||
|
||||
case fsDataType.Double:
|
||||
return AsDouble == other.AsDouble || Math.Abs(AsDouble - other.AsDouble) < double.Epsilon;
|
||||
|
||||
case fsDataType.Int64:
|
||||
return AsInt64 == other.AsInt64;
|
||||
|
||||
case fsDataType.Boolean:
|
||||
return AsBool == other.AsBool;
|
||||
|
||||
case fsDataType.String:
|
||||
return AsString == other.AsString;
|
||||
|
||||
case fsDataType.Array:
|
||||
var thisList = AsList;
|
||||
var otherList = other.AsList;
|
||||
|
||||
if (thisList.Count != otherList.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < thisList.Count; ++i)
|
||||
{
|
||||
if (thisList[i].Equals(otherList[i]) == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
case fsDataType.Object:
|
||||
var thisDict = AsDictionary;
|
||||
var otherDict = other.AsDictionary;
|
||||
|
||||
if (thisDict.Count != otherDict.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var key in thisDict.Keys)
|
||||
{
|
||||
if (otherDict.ContainsKey(key) == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (thisDict[key].Equals(otherDict[key]) == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new Exception("Unknown data type");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true iff a == b.
|
||||
/// </summary>
|
||||
public static bool operator ==(fsData a, fsData b)
|
||||
{
|
||||
// If both are null, or both are same instance, return true.
|
||||
if (ReferenceEquals(a, b))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If one is null, but not both, return false.
|
||||
if (((object)a == null) || ((object)b == null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (a.IsDouble && b.IsDouble)
|
||||
{
|
||||
return Math.Abs(a.AsDouble - b.AsDouble) < double.Epsilon;
|
||||
}
|
||||
|
||||
return a.Equals(b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true iff a != b.
|
||||
/// </summary>
|
||||
public static bool operator !=(fsData a, fsData b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A hash code for this instance, suitable for use in hashing algorithms
|
||||
/// and data structures like a hash table.
|
||||
/// </returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _value.GetHashCode();
|
||||
}
|
||||
|
||||
#endregion Equality Comparisons
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3520c694a43b048559591ea21c37a8d4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,51 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// The direct converter is similar to a regular converter, except that it
|
||||
/// targets specifically only one type. This means that it can be used
|
||||
/// without performance impact when discovering converters. It is strongly
|
||||
/// recommended that you derive from fsDirectConverter{TModel}.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Due to the way that direct converters operate, inheritance is *not*
|
||||
/// supported. Direct converters will only be used with the exact ModelType
|
||||
/// object.
|
||||
/// </remarks>
|
||||
public abstract class fsDirectConverter : fsBaseConverter
|
||||
{
|
||||
public abstract Type ModelType { get; }
|
||||
}
|
||||
|
||||
public abstract class fsDirectConverter<TModel> : fsDirectConverter
|
||||
{
|
||||
public override Type ModelType => typeof(TModel);
|
||||
|
||||
public sealed override fsResult TrySerialize(object instance, out fsData serialized, Type storageType)
|
||||
{
|
||||
var serializedDictionary = new Dictionary<string, fsData>();
|
||||
var result = DoSerialize((TModel)instance, serializedDictionary);
|
||||
serialized = new fsData(serializedDictionary);
|
||||
return result;
|
||||
}
|
||||
|
||||
public sealed override fsResult TryDeserialize(fsData data, ref object instance, Type storageType)
|
||||
{
|
||||
var result = fsResult.Success;
|
||||
if ((result += CheckType(data, fsDataType.Object)).Failed)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var obj = (TModel)instance;
|
||||
result += DoDeserialize(data.AsDictionary, ref obj);
|
||||
instance = obj;
|
||||
return result;
|
||||
}
|
||||
|
||||
protected abstract fsResult DoSerialize(TModel model, Dictionary<string, fsData> serialized);
|
||||
protected abstract fsResult DoDeserialize(Dictionary<string, fsData> data, ref TModel model);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0a458c80bcb97443e9e611c09e5ca71a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,22 @@
|
|||
// note: This file contains exceptions used by FullSerializer. Exceptions are
|
||||
// never used at runtime in FullSerializer; they are only used when
|
||||
// validating annotations and code-based models.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
public sealed class fsMissingVersionConstructorException : Exception
|
||||
{
|
||||
public fsMissingVersionConstructorException(Type versionedType, Type constructorType) :
|
||||
base(versionedType + " is missing a constructor for previous model type " + constructorType)
|
||||
{ }
|
||||
}
|
||||
|
||||
public sealed class fsDuplicateVersionNameException : Exception
|
||||
{
|
||||
public fsDuplicateVersionNameException(Type typeA, Type typeB, string version) :
|
||||
base(typeA + " and " + typeB + " have the same version string (" + version + "); please change one of them.")
|
||||
{ }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 521bba425c159452e902b0b73f80e68c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,131 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
using UnityObject = UnityEngine.Object;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
#if !UNITY_EDITOR && UNITY_WSA
|
||||
// For System.Reflection.TypeExtensions
|
||||
using System.Reflection;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Extend this interface on your type to receive notifications about
|
||||
/// serialization/deserialization events. If you don't have access to the
|
||||
/// type itself, then you can write an fsObjectProcessor instead.
|
||||
/// </summary>
|
||||
public interface fsISerializationCallbacks
|
||||
{
|
||||
/// <summary>
|
||||
/// Called before serialization.
|
||||
/// </summary>
|
||||
void OnBeforeSerialize(Type storageType);
|
||||
|
||||
/// <summary>
|
||||
/// Called after serialization.
|
||||
/// </summary>
|
||||
/// <param name="storageType">
|
||||
/// The field/property type that is storing the instance.
|
||||
/// </param>
|
||||
/// <param name="data">The data that was serialized.</param>
|
||||
void OnAfterSerialize(Type storageType, ref fsData data);
|
||||
|
||||
/// <summary>
|
||||
/// Called before deserialization.
|
||||
/// </summary>
|
||||
/// <param name="storageType">
|
||||
/// The field/property type that is storing the instance.
|
||||
/// </param>
|
||||
/// <param name="data">
|
||||
/// The data that will be used for deserialization.
|
||||
/// </param>
|
||||
void OnBeforeDeserialize(Type storageType, ref fsData data);
|
||||
|
||||
/// <summary>
|
||||
/// Called after deserialization.
|
||||
/// </summary>
|
||||
/// <param name="storageType">
|
||||
/// The field/property type that is storing the instance.
|
||||
/// </param>
|
||||
/// <param name="instance">The type of the instance.</param>
|
||||
void OnAfterDeserialize(Type storageType);
|
||||
}
|
||||
|
||||
public class fsSerializationCallbackProcessor : fsObjectProcessor
|
||||
{
|
||||
public override bool CanProcess(Type type)
|
||||
{
|
||||
return typeof(fsISerializationCallbacks).IsAssignableFrom(type);
|
||||
}
|
||||
|
||||
public override void OnBeforeSerialize(Type storageType, object instance)
|
||||
{
|
||||
// Don't call the callback on null instances.
|
||||
if (instance == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
((fsISerializationCallbacks)instance).OnBeforeSerialize(storageType);
|
||||
}
|
||||
|
||||
public override void OnAfterSerialize(Type storageType, object instance, ref fsData data)
|
||||
{
|
||||
// Don't call the callback on null instances.
|
||||
if (instance == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
((fsISerializationCallbacks)instance).OnAfterSerialize(storageType, ref data);
|
||||
}
|
||||
|
||||
public override void OnBeforeDeserializeAfterInstanceCreation(Type storageType, object instance, ref fsData data)
|
||||
{
|
||||
if (instance is fsISerializationCallbacks == false)
|
||||
{
|
||||
throw new InvalidCastException("Please ensure the converter for " + storageType + " actually returns an instance of it, not an instance of " + instance.GetType());
|
||||
}
|
||||
|
||||
((fsISerializationCallbacks)instance).OnBeforeDeserialize(storageType, ref data);
|
||||
}
|
||||
|
||||
public override void OnAfterDeserialize(Type storageType, object instance)
|
||||
{
|
||||
// Don't call the callback on null instances.
|
||||
if (instance == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
((fsISerializationCallbacks)instance).OnAfterDeserialize(storageType);
|
||||
}
|
||||
}
|
||||
|
||||
#if !NO_UNITY
|
||||
public class fsSerializationCallbackReceiverProcessor : fsObjectProcessor
|
||||
{
|
||||
public override bool CanProcess(Type type)
|
||||
{
|
||||
return typeof(ISerializationCallbackReceiver).IsAssignableFrom(type);
|
||||
}
|
||||
|
||||
public override void OnBeforeSerialize(Type storageType, object instance)
|
||||
{
|
||||
// Don't call the callback on null instances.
|
||||
if (instance == null || instance is UnityObject)
|
||||
{
|
||||
return;
|
||||
}
|
||||
((ISerializationCallbackReceiver)instance).OnBeforeSerialize();
|
||||
}
|
||||
|
||||
public override void OnAfterDeserialize(Type storageType, object instance)
|
||||
{
|
||||
// Don't call the callback on null instances.
|
||||
if (instance == null || instance is UnityObject)
|
||||
{
|
||||
return;
|
||||
}
|
||||
((ISerializationCallbackReceiver)instance).OnAfterDeserialize();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a9d2948c439334d35b3b0155feda81aa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// The given property or field annotated with [JsonIgnore] will not be
|
||||
/// serialized.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
|
||||
public sealed class fsIgnoreAttribute : Attribute { }
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ec8a76215406a466c86f4003b82f6811
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,628 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace Unity.VisualScripting.FullSerializer
|
||||
{
|
||||
// TODO: properly propagate warnings/etc for fsResult states
|
||||
|
||||
/// <summary>
|
||||
/// A simple recursive descent parser for JSON.
|
||||
/// </summary>
|
||||
public class fsJsonParser
|
||||
{
|
||||
private fsJsonParser(string input)
|
||||
{
|
||||
_input = input;
|
||||
_start = 0;
|
||||
}
|
||||
|
||||
private readonly StringBuilder _cachedStringBuilder = new StringBuilder(256);
|
||||
private int _start;
|
||||
private string _input;
|
||||
|
||||
private fsResult MakeFailure(string message)
|
||||
{
|
||||
var start = Math.Max(0, _start - 20);
|
||||
var length = Math.Min(50, _input.Length - start);
|
||||
|
||||
var error = "Error while parsing: " + message + "; context = <" +
|
||||
_input.Substring(start, length) + ">";
|
||||
return fsResult.Fail(error);
|
||||
}
|
||||
|
||||
private bool TryMoveNext()
|
||||
{
|
||||
if (_start < _input.Length)
|
||||
{
|
||||
++_start;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool HasValue()
|
||||
{
|
||||
return HasValue(0);
|
||||
}
|
||||
|
||||
private bool HasValue(int offset)
|
||||
{
|
||||
return (_start + offset) >= 0 && (_start + offset) < _input.Length;
|
||||
}
|
||||
|
||||
private char Character()
|
||||
{
|
||||
return Character(0);
|
||||
}
|
||||
|
||||
private char Character(int offset)
|
||||
{
|
||||
return _input[_start + offset];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Skips input such that Character() will return a non-whitespace
|
||||
/// character
|
||||
/// </summary>
|
||||
private void SkipSpace()
|
||||
{
|
||||
while (HasValue())
|
||||
{
|
||||
var c = Character();
|
||||
|
||||
// whitespace; fine to skip
|
||||
if (char.IsWhiteSpace(c))
|
||||
{
|
||||
TryMoveNext();
|
||||
continue;
|
||||
}
|
||||
|
||||
// comment?
|
||||
if (HasValue(1) && Character(0) == '/')
|
||||
{
|
||||
if (Character(1) == '/')
|
||||
{
|
||||
// skip the rest of the line
|
||||
while (HasValue() && Environment.NewLine.Contains("" + Character()) == false)
|
||||
{
|
||||
TryMoveNext();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (Character(1) == '*')
|
||||
{
|
||||
// skip to comment close
|
||||
TryMoveNext();
|
||||
TryMoveNext();
|
||||
while (HasValue(1))
|
||||
{
|
||||
if (Character(0) == '*' && Character(1) == '/')
|
||||
{
|
||||
TryMoveNext();
|
||||
TryMoveNext();
|
||||
TryMoveNext();
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
TryMoveNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
// let other checks to check fail
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private fsResult TryParseExact(string content)
|
||||
{
|
||||
for (var i = 0; i < content.Length; ++i)
|
||||
{
|
||||
if (Character() != content[i])
|
||||
{
|
||||
return MakeFailure("Expected " + content[i]);
|
||||
}
|
||||
|
||||
if (TryMoveNext() == false)
|
||||
{
|
||||
return MakeFailure("Unexpected end of content when parsing " + content);
|
||||
}
|
||||
}
|
||||
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
private fsResult TryParseTrue(out fsData data)
|
||||
{
|
||||
var fail = TryParseExact("true");
|
||||
|
||||
if (fail.Succeeded)
|
||||
{
|
||||
data = new fsData(true);
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
data = null;
|
||||
return fail;
|
||||
}
|
||||
|
||||
private fsResult TryParseFalse(out fsData data)
|
||||
{
|
||||
var fail = TryParseExact("false");
|
||||
|
||||
if (fail.Succeeded)
|
||||
{
|
||||
data = new fsData(false);
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
data = null;
|
||||
return fail;
|
||||
}
|
||||
|
||||
private fsResult TryParseNull(out fsData data)
|
||||
{
|
||||
var fail = TryParseExact("null");
|
||||
|
||||
if (fail.Succeeded)
|
||||
{
|
||||
data = new fsData();
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
data = null;
|
||||
return fail;
|
||||
}
|
||||
|
||||
private bool IsSeparator(char c)
|
||||
{
|
||||
return char.IsWhiteSpace(c) || c == ',' || c == '}' || c == ']';
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses numbers that follow the regular expression [-+](\d+|\d*\.\d*)
|
||||
/// </summary>
|
||||
private fsResult TryParseNumber(out fsData data)
|
||||
{
|
||||
var start = _start;
|
||||
|
||||
// read until we get to a separator
|
||||
while (
|
||||
TryMoveNext() &&
|
||||
(HasValue() && IsSeparator(Character()) == false)) { }
|
||||
|
||||
// try to parse the value
|
||||
var numberString = _input.Substring(start, _start - start);
|
||||
|
||||
// double -- includes a .
|
||||
if (numberString.Contains(".") || numberString.Contains("e") || numberString.Contains("E") ||
|
||||
numberString == "Infinity" || numberString == "-Infinity" || numberString == "NaN")
|
||||
{
|
||||
double doubleValue;
|
||||
if (double.TryParse(numberString, NumberStyles.Any, CultureInfo.InvariantCulture, out doubleValue) == false)
|
||||
{
|
||||
data = null;
|
||||
return MakeFailure("Bad double format with " + numberString);
|
||||
}
|
||||
|
||||
data = new fsData(doubleValue);
|
||||
return fsResult.Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
Int64 intValue;
|
||||
if (Int64.TryParse(numberString, NumberStyles.Any, CultureInfo.InvariantCulture, out intValue) == false)
|
||||
{
|
||||
data = null;
|
||||
return MakeFailure("Bad Int64 format with " + numberString);
|
||||
}
|
||||
|
||||
data = new fsData(intValue);
|
||||
return fsResult.Success;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a string
|
||||
/// </summary>
|
||||
private fsResult TryParseString(out string str)
|
||||
{
|
||||
_cachedStringBuilder.Length = 0;
|
||||
|
||||
// skip the first "
|
||||
if (Character() != '"' || TryMoveNext() == false)
|
||||
{
|
||||
str = string.Empty;
|
||||
return MakeFailure("Expected initial \" when parsing a string");
|
||||
}
|
||||
|
||||
// read until the next "
|
||||
while (HasValue() && Character() != '\"')
|
||||
{
|
||||
var c = Character();
|
||||
|
||||
// escape if necessary
|
||||
if (c == '\\')
|
||||
{
|
||||
char unescaped;
|
||||
var fail = TryUnescapeChar(out unescaped);
|
||||
if (fail.Failed)
|
||||
{
|
||||
str = string.Empty;
|
||||
return fail;
|
||||
}
|
||||
|
||||
_cachedStringBuilder.Append(unescaped);
|
||||
}
|
||||
// no escaping necessary
|
||||
else
|
||||
{
|
||||
_cachedStringBuilder.Append(c);
|
||||
|
||||
// get the next character
|
||||
if (TryMoveNext() == false)
|
||||
{
|
||||
str = string.Empty;
|
||||
return MakeFailure("Unexpected end of input when reading a string");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// skip the first "
|
||||
if (HasValue() == false || Character() != '"' || TryMoveNext() == false)
|
||||
{
|
||||
str = string.Empty;
|
||||
return MakeFailure("No closing \" when parsing a string");
|
||||
}
|
||||
|
||||
str = _cachedStringBuilder.ToString();
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses an array
|
||||
/// </summary>
|
||||
private fsResult TryParseArray(out fsData arr)
|
||||
{
|
||||
if (Character() != '[')
|
||||
{
|
||||
arr = null;
|
||||
return MakeFailure("Expected initial [ when parsing an array");
|
||||
}
|
||||
|
||||
// skip '['
|
||||
if (TryMoveNext() == false)
|
||||
{
|
||||
arr = null;
|
||||
return MakeFailure("Unexpected end of input when parsing an array");
|
||||
}
|
||||
SkipSpace();
|
||||
|
||||
var result = new List<fsData>();
|
||||
|
||||
while (HasValue() && Character() != ']')
|
||||
{
|
||||
// parse the element
|
||||
fsData element;
|
||||
var fail = RunParse(out element);
|
||||
if (fail.Failed)
|
||||
{
|
||||
arr = null;
|
||||
return fail;
|
||||
}
|
||||
|
||||
result.Add(element);
|
||||
|
||||
// parse the comma
|
||||
SkipSpace();
|
||||
if (HasValue() && Character() == ',')
|
||||
{
|
||||
if (TryMoveNext() == false)
|
||||
{
|
||||
break;
|
||||
}
|
||||
SkipSpace();
|
||||
}
|
||||
}
|
||||
|
||||
// skip the final ]
|
||||
if (HasValue() == false || Character() != ']' || TryMoveNext() == false)
|
||||
{
|
||||
arr = null;
|
||||
return MakeFailure("No closing ] for array");
|
||||
}
|
||||
|
||||
arr = new fsData(result);
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
private fsResult TryParseObject(out fsData obj)
|
||||
{
|
||||
if (Character() != '{')
|
||||
{
|
||||
obj = null;
|
||||
return MakeFailure("Expected initial { when parsing an object");
|
||||
}
|
||||
|
||||
// skip '{'
|
||||
if (TryMoveNext() == false)
|
||||
{
|
||||
obj = null;
|
||||
return MakeFailure("Unexpected end of input when parsing an object");
|
||||
}
|
||||
SkipSpace();
|
||||
|
||||
var result = new Dictionary<string, fsData>(
|
||||
fsGlobalConfig.IsCaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
while (HasValue() && Character() != '}')
|
||||
{
|
||||
fsResult failure;
|
||||
|
||||
// parse the key
|
||||
SkipSpace();
|
||||
string key;
|
||||
failure = TryParseString(out key);
|
||||
if (failure.Failed)
|
||||
{
|
||||
obj = null;
|
||||
return failure;
|
||||
}
|
||||
SkipSpace();
|
||||
|
||||
// parse the ':' after the key
|
||||
if (HasValue() == false || Character() != ':' || TryMoveNext() == false)
|
||||
{
|
||||
obj = null;
|
||||
return MakeFailure("Expected : after key \"" + key + "\"");
|
||||
}
|
||||
SkipSpace();
|
||||
|
||||
// parse the value
|
||||
fsData value;
|
||||
failure = RunParse(out value);
|
||||
if (failure.Failed)
|
||||
{
|
||||
obj = null;
|
||||
return failure;
|
||||
}
|
||||
|
||||
result.Add(key, value);
|
||||
|
||||
// parse the comma
|
||||
SkipSpace();
|
||||
if (HasValue() && Character() == ',')
|
||||
{
|
||||
if (TryMoveNext() == false)
|
||||
{
|
||||
break;
|
||||
}
|
||||
SkipSpace();
|
||||
}
|
||||
}
|
||||
|
||||
// skip the final }
|
||||
if (HasValue() == false || Character() != '}' || TryMoveNext() == false)
|
||||
{
|
||||
obj = null;
|
||||
return MakeFailure("No closing } for object");
|
||||
}
|
||||
|
||||
obj = new fsData(result);
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
private fsResult RunParse(out fsData data)
|
||||
{
|
||||
SkipSpace();
|
||||
|
||||
if (HasValue() == false)
|
||||
{
|
||||
data = default(fsData);
|
||||
return MakeFailure("Unexpected end of input");
|
||||
}
|
||||
|
||||
switch (Character())
|
||||
{
|
||||
case 'I': // Infinity
|
||||
case 'N': // NaN
|
||||
case '.':
|
||||
case '+':
|
||||
case '-':
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
return TryParseNumber(out data);
|
||||
case '"':
|
||||
{
|
||||
string str;
|
||||
var fail = TryParseString(out str);
|
||||
if (fail.Failed)
|
||||
{
|
||||
data = null;
|
||||
return fail;
|
||||
}
|
||||
data = new fsData(str);
|
||||
return fsResult.Success;
|
||||
}
|
||||
case '[':
|
||||
return TryParseArray(out data);
|
||||
case '{':
|
||||
return TryParseObject(out data);
|
||||
case 't':
|
||||
return TryParseTrue(out data);
|
||||
case 'f':
|
||||
return TryParseFalse(out data);
|
||||
case 'n':
|
||||
return TryParseNull(out data);
|
||||
default:
|
||||
data = null;
|
||||
return MakeFailure("unable to parse; invalid token \"" + Character() + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the specified input. Returns a failure state if parsing
|
||||
/// failed.
|
||||
/// </summary>
|
||||
/// <param name="input">The input to parse.</param>
|
||||
/// <param name="data">
|
||||
/// The parsed data. This is undefined if parsing fails.
|
||||
/// </param>
|
||||
/// <returns>The parsed input.</returns>
|
||||
public static fsResult Parse(string input, out fsData data)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
data = default(fsData);
|
||||
return fsResult.Fail("No input");
|
||||
}
|
||||
|
||||
var context = new fsJsonParser(input);
|
||||
return context.RunParse(out data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method for Parse that does not allow the error information to
|
||||
/// be recovered.
|
||||
/// </summary>
|
||||
public static fsData Parse(string input)
|
||||
{
|
||||
fsData data;
|
||||
Parse(input, out data).AssertSuccess();
|
||||
return data;
|
||||
}
|
||||
|
||||
#region Escaping
|
||||
|
||||
private bool IsHex(char c)
|
||||
{
|
||||
return ((c >= '0' && c <= '9') ||
|
||||
(c >= 'a' && c <= 'f') ||
|
||||
(c >= 'A' && c <= 'F'));
|
||||
}
|
||||
|
||||
private uint ParseSingleChar(char c1, uint multipliyer)
|
||||
{
|
||||
uint p1 = 0;
|
||||
if (c1 >= '0' && c1 <= '9')
|
||||
{
|
||||
p1 = (uint)(c1 - '0') * multipliyer;
|
||||
}
|
||||
else if (c1 >= 'A' && c1 <= 'F')
|
||||
{
|
||||
p1 = (uint)((c1 - 'A') + 10) * multipliyer;
|
||||
}
|
||||
else if (c1 >= 'a' && c1 <= 'f')
|
||||
{
|
||||
p1 = (uint)((c1 - 'a') + 10) * multipliyer;
|
||||
}
|
||||
return p1;
|
||||
}
|
||||
|
||||
private uint ParseUnicode(char c1, char c2, char c3, char c4)
|
||||
{
|
||||
var p1 = ParseSingleChar(c1, 0x1000);
|
||||
var p2 = ParseSingleChar(c2, 0x100);
|
||||
var p3 = ParseSingleChar(c3, 0x10);
|
||||
var p4 = ParseSingleChar(c4, 0x1);
|
||||
|
||||
return p1 + p2 + p3 + p4;
|
||||
}
|
||||
|
||||
private fsResult TryUnescapeChar(out char escaped)
|
||||
{
|
||||
// skip leading backslash '\'
|
||||
TryMoveNext();
|
||||
if (HasValue() == false)
|
||||
{
|
||||
escaped = ' ';
|
||||
return MakeFailure("Unexpected end of input after \\");
|
||||
}
|
||||
|
||||
switch (Character())
|
||||
{
|
||||
case '\\':
|
||||
TryMoveNext();
|
||||
escaped = '\\';
|
||||
return fsResult.Success;
|
||||
case '/':
|
||||
TryMoveNext();
|
||||
escaped = '/';
|
||||
return fsResult.Success;
|
||||
case '"':
|
||||
TryMoveNext();
|
||||
escaped = '\"';
|
||||
return fsResult.Success;
|
||||
case 'a':
|
||||
TryMoveNext();
|
||||
escaped = '\a';
|
||||
return fsResult.Success;
|
||||
case 'b':
|
||||
TryMoveNext();
|
||||
escaped = '\b';
|
||||
return fsResult.Success;
|
||||
case 'f':
|
||||
TryMoveNext();
|
||||
escaped = '\f';
|
||||
return fsResult.Success;
|
||||
case 'n':
|
||||
TryMoveNext();
|
||||
escaped = '\n';
|
||||
return fsResult.Success;
|
||||
case 'r':
|
||||
TryMoveNext();
|
||||
escaped = '\r';
|
||||
return fsResult.Success;
|
||||
case 't':
|
||||
TryMoveNext();
|
||||
escaped = '\t';
|
||||
return fsResult.Success;
|
||||
case '0':
|
||||
TryMoveNext();
|
||||
escaped = '\0';
|
||||
return fsResult.Success;
|
||||
case 'u':
|
||||
TryMoveNext();
|
||||
if (IsHex(Character(0))
|
||||
&& IsHex(Character(1))
|
||||
&& IsHex(Character(2))
|
||||
&& IsHex(Character(3)))
|
||||
{
|
||||
var codePoint = ParseUnicode(Character(0), Character(1), Character(2), Character(3));
|
||||
|
||||
TryMoveNext();
|
||||
TryMoveNext();
|
||||
TryMoveNext();
|
||||
TryMoveNext();
|
||||
|
||||
escaped = (char)codePoint;
|
||||
return fsResult.Success;
|
||||
}
|
||||
|
||||
// invalid escape sequence
|
||||
escaped = (char)0;
|
||||
return MakeFailure(
|
||||
$"invalid escape sequence '\\u{Character(0)}{Character(1)}{Character(2)}{Character(3)}'\n");
|
||||
default:
|
||||
escaped = (char)0;
|
||||
return MakeFailure($"Invalid escape sequence \\{Character()}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Escaping
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b0c8cf2ada4fd4eb6b1a8960abf537f1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue