apple 2 savestates: custom Type handling to be more resilient to assembly changes
This commit is contained in:
parent
704ace5eb7
commit
894cae0c52
|
@ -156,6 +156,85 @@ namespace Jellyfish.Virtu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class TypeTypeConverter : JsonConverter
|
||||||
|
{
|
||||||
|
// serialize and deserialize types, ignoring assembly entirely and only using namespace+typename
|
||||||
|
// all types, including generic type arguments to supplied types, must be in one of the declared assemblies (only checked on read!)
|
||||||
|
// the main goal here is to have something with a slight chance of working across versions
|
||||||
|
|
||||||
|
public TypeTypeConverter(IEnumerable<Assembly> ass)
|
||||||
|
{
|
||||||
|
assemblies = ass.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Assembly> assemblies;
|
||||||
|
private Dictionary<string, Type> readlookup = new Dictionary<string, Type>();
|
||||||
|
|
||||||
|
public override bool CanConvert(Type objectType)
|
||||||
|
{
|
||||||
|
return typeof(Type).IsAssignableFrom(objectType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanRead { get { return true; } }
|
||||||
|
public override bool CanWrite { get { return true; } }
|
||||||
|
|
||||||
|
private Type GetType(string name)
|
||||||
|
{
|
||||||
|
Type ret;
|
||||||
|
if (!readlookup.TryGetValue(name, out ret))
|
||||||
|
{
|
||||||
|
ret = assemblies.Select(ass => ass.GetType(name, false)).Where(t => t != null).Single();
|
||||||
|
readlookup.Add(name, ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetName(Type type)
|
||||||
|
{
|
||||||
|
return string.Format("{0}.{1}", type.Namespace, type.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonToken.Null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else if (reader.TokenType == JsonToken.String)
|
||||||
|
{
|
||||||
|
return GetType(reader.Value.ToString());
|
||||||
|
}
|
||||||
|
else if (reader.TokenType == JsonToken.StartArray) // full generic
|
||||||
|
{
|
||||||
|
List<string> vals = serializer.Deserialize<List<string>>(reader);
|
||||||
|
return GetType(vals[0]).MakeGenericType(vals.Skip(1).Select(GetType).ToArray());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
var type = (Type)value;
|
||||||
|
if (type.IsGenericType && !type.IsGenericTypeDefinition)
|
||||||
|
{
|
||||||
|
writer.WriteStartArray();
|
||||||
|
writer.WriteValue(GetName(type));
|
||||||
|
foreach (var t in type.GetGenericArguments())
|
||||||
|
{
|
||||||
|
writer.WriteValue(GetName(t));
|
||||||
|
}
|
||||||
|
writer.WriteEndArray();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.WriteValue(GetName(type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class DelegateConverter : JsonConverter
|
public class DelegateConverter : JsonConverter
|
||||||
{
|
{
|
||||||
// caveats: if used on anonymous delegates and/or closures, brittle to name changes in the generated classes and methods
|
// caveats: if used on anonymous delegates and/or closures, brittle to name changes in the generated classes and methods
|
||||||
|
|
|
@ -123,6 +123,8 @@ namespace Jellyfish.Virtu
|
||||||
|
|
||||||
private static JsonSerializer CreateSerializer()
|
private static JsonSerializer CreateSerializer()
|
||||||
{
|
{
|
||||||
|
// TODO: converters could be cached for speedup
|
||||||
|
|
||||||
var ser = new JsonSerializer
|
var ser = new JsonSerializer
|
||||||
{
|
{
|
||||||
TypeNameHandling = TypeNameHandling.Auto,
|
TypeNameHandling = TypeNameHandling.Auto,
|
||||||
|
@ -130,8 +132,15 @@ namespace Jellyfish.Virtu
|
||||||
ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
|
ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ser.Converters.Add(new TypeTypeConverter(new[]
|
||||||
|
{
|
||||||
|
// all expected Types to convert are either in this assembly or mscorlib
|
||||||
|
typeof(Machine).Assembly,
|
||||||
|
typeof(object).Assembly
|
||||||
|
}));
|
||||||
ser.Converters.Add(new DelegateConverter());
|
ser.Converters.Add(new DelegateConverter());
|
||||||
ser.Converters.Add(new ArrayConverter());
|
ser.Converters.Add(new ArrayConverter());
|
||||||
|
|
||||||
var cr = new DefaultContractResolver();
|
var cr = new DefaultContractResolver();
|
||||||
cr.DefaultMembersSearchFlags |= System.Reflection.BindingFlags.NonPublic;
|
cr.DefaultMembersSearchFlags |= System.Reflection.BindingFlags.NonPublic;
|
||||||
ser.ContractResolver = cr;
|
ser.ContractResolver = cr;
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue