123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746 |
- #if !NO_RUNTIME
- using System;
- using ProtoBuf.Meta;
- #if FEAT_COMPILER
- #endif
- #if FEAT_IKVM
- using Type = IKVM.Reflection.Type;
- using IKVM.Reflection;
- #else
- using System.Reflection;
- #endif
- namespace ProtoBuf.Serializers
- {
- sealed class TypeSerializer : IProtoTypeSerializer
- {
- public bool HasCallbacks(TypeModel.CallbackType callbackType) {
- if(callbacks != null && callbacks[callbackType] != null) return true;
- for (int i = 0; i < serializers.Length; i++)
- {
- if (serializers[i].ExpectedType != forType && ((IProtoTypeSerializer)serializers[i]).HasCallbacks(callbackType)) return true;
- }
- return false;
- }
- private readonly Type forType, constructType;
- #if WINRT || COREFX
- private readonly TypeInfo typeInfo;
- #endif
- public Type ExpectedType { get { return forType; } }
- private readonly IProtoSerializer[] serializers;
- private readonly int[] fieldNumbers;
- private readonly bool isRootType, useConstructor, isExtensible, hasConstructor;
- private readonly CallbackSet callbacks;
- private readonly MethodInfo[] baseCtorCallbacks;
- private readonly MethodInfo factory;
- public TypeSerializer(TypeModel model, Type forType, int[] fieldNumbers, IProtoSerializer[] serializers, MethodInfo[] baseCtorCallbacks, bool isRootType, bool useConstructor, CallbackSet callbacks, Type constructType, MethodInfo factory)
- {
- Helpers.DebugAssert(forType != null);
- Helpers.DebugAssert(fieldNumbers != null);
- Helpers.DebugAssert(serializers != null);
- Helpers.DebugAssert(fieldNumbers.Length == serializers.Length);
- Helpers.Sort(fieldNumbers, serializers);
- bool hasSubTypes = false;
- for (int i = 1; i < fieldNumbers.Length; i++)
- {
- if (fieldNumbers[i] == fieldNumbers[i - 1]) throw new InvalidOperationException("Duplicate field-number detected; " +
- fieldNumbers[i].ToString() + " on: " + forType.FullName);
- if(!hasSubTypes && serializers[i].ExpectedType != forType)
- {
- hasSubTypes = true;
- }
- }
- this.forType = forType;
- this.factory = factory;
- #if WINRT || COREFX
- this.typeInfo = forType.GetTypeInfo();
- #endif
- if (constructType == null)
- {
- constructType = forType;
- }
- else
- {
- #if WINRT || COREFX
- if (!typeInfo.IsAssignableFrom(constructType.GetTypeInfo()))
- #else
- if (!forType.IsAssignableFrom(constructType))
- #endif
- {
- throw new InvalidOperationException(forType.FullName + " cannot be assigned from "+ constructType.FullName);
- }
- }
- this.constructType = constructType;
- this.serializers = serializers;
- this.fieldNumbers = fieldNumbers;
- this.callbacks = callbacks;
- this.isRootType = isRootType;
- this.useConstructor = useConstructor;
-
- if (baseCtorCallbacks != null && baseCtorCallbacks.Length == 0) baseCtorCallbacks = null;
- this.baseCtorCallbacks = baseCtorCallbacks;
- #if !NO_GENERICS
- if (Helpers.GetUnderlyingType(forType) != null)
- {
- throw new ArgumentException("Cannot create a TypeSerializer for nullable types", "forType");
- }
- #endif
- #if WINRT || COREFX
- if (iextensible.IsAssignableFrom(typeInfo))
- {
- if (typeInfo.IsValueType || !isRootType || hasSubTypes)
- #else
- if (model.MapType(iextensible).IsAssignableFrom(forType))
- {
- if (forType.IsValueType || !isRootType || hasSubTypes)
- #endif
- {
- throw new NotSupportedException("IExtensible is not supported in structs or classes with inheritance");
- }
- isExtensible = true;
- }
- #if WINRT || COREFX
- TypeInfo constructTypeInfo = constructType.GetTypeInfo();
- hasConstructor = !constructTypeInfo.IsAbstract && Helpers.GetConstructor(constructTypeInfo, Helpers.EmptyTypes, true) != null;
- #else
- hasConstructor = !constructType.IsAbstract && Helpers.GetConstructor(constructType, Helpers.EmptyTypes, true) != null;
- #endif
- if (constructType != forType && useConstructor && !hasConstructor)
- {
- throw new ArgumentException("The supplied default implementation cannot be created: " + constructType.FullName, "constructType");
- }
- }
- #if WINRT || COREFX
- private static readonly TypeInfo iextensible = typeof(IExtensible).GetTypeInfo();
- #else
- private static readonly System.Type iextensible = typeof(IExtensible);
- #endif
- private bool CanHaveInheritance
- {
- get {
- #if WINRT || COREFX
- return (typeInfo.IsClass || typeInfo.IsInterface) && !typeInfo.IsSealed;
- #else
- return (forType.IsClass || forType.IsInterface) && !forType.IsSealed;
- #endif
- }
- }
- bool IProtoTypeSerializer.CanCreateInstance() { return true; }
- #if !FEAT_IKVM
- object IProtoTypeSerializer.CreateInstance(ProtoReader source)
- {
- return CreateInstance(source, false);
- }
- public void Callback(object value, TypeModel.CallbackType callbackType, SerializationContext context)
- {
- if (callbacks != null) InvokeCallback(callbacks[callbackType], value, context);
- IProtoTypeSerializer ser = (IProtoTypeSerializer)GetMoreSpecificSerializer(value);
- if (ser != null) ser.Callback(value, callbackType, context);
- }
- private IProtoSerializer GetMoreSpecificSerializer(object value)
- {
- if (!CanHaveInheritance) return null;
- Type actualType = value.GetType();
- if (actualType == forType) return null;
-
- for (int i = 0; i < serializers.Length; i++)
- {
- IProtoSerializer ser = serializers[i];
- if (ser.ExpectedType != forType && Helpers.IsAssignableFrom(ser.ExpectedType, actualType))
- {
- return ser;
- }
- }
- if (actualType == constructType) return null; // needs to be last in case the default concrete type is also a known sub-type
- TypeModel.ThrowUnexpectedSubtype(forType, actualType); // might throw (if not a proxy)
- return null;
- }
- public void Write(object value, ProtoWriter dest)
- {
- if (isRootType) Callback(value, TypeModel.CallbackType.BeforeSerialize, dest.Context);
- // write inheritance first
- IProtoSerializer next = GetMoreSpecificSerializer(value);
- if (next != null) next.Write(value, dest);
- // write all actual fields
- //Helpers.DebugWriteLine(">> Writing fields for " + forType.FullName);
- for (int i = 0; i < serializers.Length; i++)
- {
- IProtoSerializer ser = serializers[i];
- if (ser.ExpectedType == forType)
- {
- //Helpers.DebugWriteLine(": " + ser.ToString());
- ser.Write(value, dest);
- }
- }
- //Helpers.DebugWriteLine("<< Writing fields for " + forType.FullName);
- if (isExtensible) ProtoWriter.AppendExtensionData((IExtensible)value, dest);
- if (isRootType) Callback(value, TypeModel.CallbackType.AfterSerialize, dest.Context);
- }
- public object Read(object value, ProtoReader source)
- {
- if (isRootType && value != null) { Callback(value, TypeModel.CallbackType.BeforeDeserialize, source.Context); }
- int fieldNumber, lastFieldNumber = 0, lastFieldIndex = 0;
- bool fieldHandled;
- //Helpers.DebugWriteLine(">> Reading fields for " + forType.FullName);
- while ((fieldNumber = source.ReadFieldHeader()) > 0)
- {
- fieldHandled = false;
- if (fieldNumber < lastFieldNumber)
- {
- lastFieldNumber = lastFieldIndex = 0;
- }
- for (int i = lastFieldIndex; i < fieldNumbers.Length; i++)
- {
- if (fieldNumbers[i] == fieldNumber)
- {
- IProtoSerializer ser = serializers[i];
- //Helpers.DebugWriteLine(": " + ser.ToString());
- Type serType = ser.ExpectedType;
- if (value == null)
- {
- if (serType == forType) value = CreateInstance(source, true);
- }
- else
- {
- if (serType != forType && ((IProtoTypeSerializer)ser).CanCreateInstance()
- && serType
- #if WINRT || COREFX
- .GetTypeInfo()
- #endif
- .IsSubclassOf(value.GetType()))
- {
- value = ProtoReader.Merge(source, value, ((IProtoTypeSerializer)ser).CreateInstance(source));
- }
- }
- if (ser.ReturnsValue) {
- value = ser.Read(value, source);
- } else { // pop
- ser.Read(value, source);
- }
-
- lastFieldIndex = i;
- lastFieldNumber = fieldNumber;
- fieldHandled = true;
- break;
- }
- }
- if (!fieldHandled)
- {
- //Helpers.DebugWriteLine(": [" + fieldNumber + "] (unknown)");
- if (value == null) value = CreateInstance(source, true);
- if (isExtensible) {
- source.AppendExtensionData((IExtensible)value);
- } else {
- source.SkipField();
- }
- }
- }
- //Helpers.DebugWriteLine("<< Reading fields for " + forType.FullName);
- if(value == null) value = CreateInstance(source, true);
- if (isRootType) { Callback(value, TypeModel.CallbackType.AfterDeserialize, source.Context); }
- return value;
- }
- private object InvokeCallback(MethodInfo method, object obj, SerializationContext context)
- {
- object result = null;
- object[] args;
- if (method != null)
- { // pass in a streaming context if one is needed, else null
- bool handled;
- ParameterInfo[] parameters = method.GetParameters();
- switch (parameters.Length)
- {
- case 0:
- args = null;
- handled = true;
- break;
- default:
- args = new object[parameters.Length];
- handled = true;
- for (int i = 0; i < args.Length; i++)
- {
- object val;
- Type paramType = parameters[i].ParameterType;
- if (paramType == typeof(SerializationContext)) val = context;
- else if (paramType == typeof(System.Type)) val = constructType;
- #if PLAT_BINARYFORMATTER || (SILVERLIGHT && NET_4_0)
- else if (paramType == typeof(System.Runtime.Serialization.StreamingContext)) val = (System.Runtime.Serialization.StreamingContext)context;
- #endif
- else
- {
- val = null;
- handled = false;
- }
- args[i] = val;
- }
- break;
- }
- if(handled)
- {
- result = method.Invoke(obj, args);
- }
- else
- {
- throw Meta.CallbackSet.CreateInvalidCallbackSignature(method);
- }
- }
- return result;
- }
- object CreateInstance(ProtoReader source, bool includeLocalCallback)
- {
- //Helpers.DebugWriteLine("* creating : " + forType.FullName);
- object obj;
- if (factory != null)
- {
- obj = InvokeCallback(factory, null, source.Context);
- }
- else if (useConstructor)
- {
- if (!hasConstructor) TypeModel.ThrowCannotCreateInstance(constructType);
- obj = Activator.CreateInstance(constructType
- #if !(CF || SILVERLIGHT || WINRT || PORTABLE || NETSTANDARD1_3 || NETSTANDARD1_4)
- , nonPublic: true
- #endif
- );
- }
- else
- {
- obj = BclHelpers.GetUninitializedObject(constructType);
- }
- ProtoReader.NoteObject(obj, source);
- if (baseCtorCallbacks != null) {
- for (int i = 0; i < baseCtorCallbacks.Length; i++) {
- InvokeCallback(baseCtorCallbacks[i], obj, source.Context);
- }
- }
- if (includeLocalCallback && callbacks != null) InvokeCallback(callbacks.BeforeDeserialize, obj, source.Context);
- return obj;
- }
- #endif
- bool IProtoSerializer.RequiresOldValue { get { return true; } }
- bool IProtoSerializer.ReturnsValue { get { return false; } } // updates field directly
- #if FEAT_COMPILER
- void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
- {
- Type expected = ExpectedType;
- using (Compiler.Local loc = ctx.GetLocalWithValue(expected, valueFrom))
- {
- // pre-callbacks
- EmitCallbackIfNeeded(ctx, loc, TypeModel.CallbackType.BeforeSerialize);
- Compiler.CodeLabel startFields = ctx.DefineLabel();
- // inheritance
- if (CanHaveInheritance)
- {
- for (int i = 0; i < serializers.Length; i++)
- {
- IProtoSerializer ser = serializers[i];
- Type serType = ser.ExpectedType;
- if (serType != forType)
- {
- Compiler.CodeLabel ifMatch = ctx.DefineLabel(), nextTest = ctx.DefineLabel();
- ctx.LoadValue(loc);
- ctx.TryCast(serType);
- ctx.CopyValue();
- ctx.BranchIfTrue(ifMatch, true);
- ctx.DiscardValue();
- ctx.Branch(nextTest, true);
- ctx.MarkLabel(ifMatch);
- ser.EmitWrite(ctx, null);
- ctx.Branch(startFields, false);
- ctx.MarkLabel(nextTest);
- }
- }
- if (constructType != null && constructType != forType)
- {
- using(Compiler.Local actualType = new Compiler.Local(ctx, ctx.MapType(typeof(System.Type))))
- {
- // would have jumped to "fields" if an expected sub-type, so two options:
- // a: *exactly* that type, b: an *unexpected* type
- ctx.LoadValue(loc);
- ctx.EmitCall(ctx.MapType(typeof(object)).GetMethod("GetType"));
- ctx.CopyValue();
- ctx.StoreValue(actualType);
- ctx.LoadValue(forType);
- ctx.BranchIfEqual(startFields, true);
- ctx.LoadValue(actualType);
- ctx.LoadValue(constructType);
- ctx.BranchIfEqual(startFields, true);
- }
- }
- else
- {
- // would have jumped to "fields" if an expected sub-type, so two options:
- // a: *exactly* that type, b: an *unexpected* type
- ctx.LoadValue(loc);
- ctx.EmitCall(ctx.MapType(typeof(object)).GetMethod("GetType"));
- ctx.LoadValue(forType);
- ctx.BranchIfEqual(startFields, true);
- }
- // unexpected, then... note that this *might* be a proxy, which
- // is handled by ThrowUnexpectedSubtype
- ctx.LoadValue(forType);
- ctx.LoadValue(loc);
- ctx.EmitCall(ctx.MapType(typeof(object)).GetMethod("GetType"));
- ctx.EmitCall(ctx.MapType(typeof(TypeModel)).GetMethod("ThrowUnexpectedSubtype",
- BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static));
- }
- // fields
-
- ctx.MarkLabel(startFields);
- for (int i = 0; i < serializers.Length; i++)
- {
- IProtoSerializer ser = serializers[i];
- if (ser.ExpectedType == forType) ser.EmitWrite(ctx, loc);
- }
- // extension data
- if (isExtensible)
- {
- ctx.LoadValue(loc);
- ctx.LoadReaderWriter();
- ctx.EmitCall(ctx.MapType(typeof(ProtoWriter)).GetMethod("AppendExtensionData"));
- }
- // post-callbacks
- EmitCallbackIfNeeded(ctx, loc, TypeModel.CallbackType.AfterSerialize);
- }
- }
- static void EmitInvokeCallback(Compiler.CompilerContext ctx, MethodInfo method, bool copyValue, Type constructType, Type type)
- {
- if (method != null)
- {
- if(copyValue) ctx.CopyValue(); // assumes the target is on the stack, and that we want to *retain* it on the stack
- ParameterInfo[] parameters = method.GetParameters();
- bool handled = true;
- for (int i = 0; i < parameters.Length; i++)
- {
- Type parameterType = parameters[0].ParameterType;
- if (parameterType == ctx.MapType(typeof(SerializationContext)))
- {
- ctx.LoadSerializationContext();
- }
- else if (parameterType == ctx.MapType(typeof(System.Type)))
- {
- Type tmp = constructType;
- if (tmp == null) tmp = type; // no ?? in some C# profiles
- ctx.LoadValue(tmp);
- }
- #if PLAT_BINARYFORMATTER
- else if (parameterType == ctx.MapType(typeof(System.Runtime.Serialization.StreamingContext)))
- {
- ctx.LoadSerializationContext();
- MethodInfo op = ctx.MapType(typeof(SerializationContext)).GetMethod("op_Implicit", new Type[] { ctx.MapType(typeof(SerializationContext)) });
- if (op != null)
- { // it isn't always! (framework versions, etc)
- ctx.EmitCall(op);
- handled = true;
- }
- }
- #endif
- else
- {
- handled = false;
- }
- }
- if (handled)
- {
- ctx.EmitCall(method);
- if (constructType != null)
- {
- if (method.ReturnType == ctx.MapType(typeof(object)))
- {
- ctx.CastFromObject(type);
- }
- }
- }
- else
- {
- throw Meta.CallbackSet.CreateInvalidCallbackSignature(method);
- }
- }
- }
- private void EmitCallbackIfNeeded(Compiler.CompilerContext ctx, Compiler.Local valueFrom, TypeModel.CallbackType callbackType) {
- Helpers.DebugAssert(valueFrom != null);
- if (isRootType && ((IProtoTypeSerializer)this).HasCallbacks(callbackType))
- {
- ((IProtoTypeSerializer)this).EmitCallback(ctx, valueFrom, callbackType);
- }
- }
- void IProtoTypeSerializer.EmitCallback(Compiler.CompilerContext ctx, Compiler.Local valueFrom, TypeModel.CallbackType callbackType)
- {
- bool actuallyHasInheritance = false;
- if (CanHaveInheritance)
- {
- for (int i = 0; i < serializers.Length; i++)
- {
- IProtoSerializer ser = serializers[i];
- if (ser.ExpectedType != forType && ((IProtoTypeSerializer)ser).HasCallbacks(callbackType))
- {
- actuallyHasInheritance = true;
- }
- }
- }
- Helpers.DebugAssert(((IProtoTypeSerializer)this).HasCallbacks(callbackType), "Shouldn't be calling this if there is nothing to do");
- MethodInfo method = callbacks == null ? null : callbacks[callbackType];
- if(method == null && !actuallyHasInheritance)
- {
- return;
- }
- ctx.LoadAddress(valueFrom, ExpectedType);
- EmitInvokeCallback(ctx, method, actuallyHasInheritance, null, forType);
- if (actuallyHasInheritance)
- {
- Compiler.CodeLabel @break = ctx.DefineLabel();
- for (int i = 0; i < serializers.Length; i++)
- {
- IProtoSerializer ser = serializers[i];
- IProtoTypeSerializer typeser;
- Type serType = ser.ExpectedType;
- if (serType != forType &&
- (typeser = (IProtoTypeSerializer) ser).HasCallbacks(callbackType))
- {
- Compiler.CodeLabel ifMatch = ctx.DefineLabel(), nextTest = ctx.DefineLabel();
- ctx.CopyValue();
- ctx.TryCast(serType);
- ctx.CopyValue();
- ctx.BranchIfTrue(ifMatch, true);
- ctx.DiscardValue();
- ctx.Branch(nextTest, false);
- ctx.MarkLabel(ifMatch);
- typeser.EmitCallback(ctx, null, callbackType);
- ctx.Branch(@break, false);
- ctx.MarkLabel(nextTest);
- }
- }
- ctx.MarkLabel(@break);
- ctx.DiscardValue();
- }
- }
- void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom)
- {
- Type expected = ExpectedType;
- Helpers.DebugAssert(valueFrom != null);
- using (Compiler.Local loc = ctx.GetLocalWithValue(expected, valueFrom))
- using (Compiler.Local fieldNumber = new Compiler.Local(ctx, ctx.MapType(typeof(int))))
- {
- // pre-callbacks
- if (HasCallbacks(TypeModel.CallbackType.BeforeDeserialize))
- {
- if(Helpers.IsValueType(ExpectedType))
- {
- EmitCallbackIfNeeded(ctx, loc, TypeModel.CallbackType.BeforeDeserialize);
- }
- else
- { // could be null
- Compiler.CodeLabel callbacksDone = ctx.DefineLabel();
- ctx.LoadValue(loc);
- ctx.BranchIfFalse(callbacksDone, false);
- EmitCallbackIfNeeded(ctx, loc, TypeModel.CallbackType.BeforeDeserialize);
- ctx.MarkLabel(callbacksDone);
- }
- }
- Compiler.CodeLabel @continue = ctx.DefineLabel(), processField = ctx.DefineLabel();
- ctx.Branch(@continue, false);
- ctx.MarkLabel(processField);
- foreach (BasicList.Group group in BasicList.GetContiguousGroups(fieldNumbers, serializers))
- {
- Compiler.CodeLabel tryNextField = ctx.DefineLabel();
- int groupItemCount = group.Items.Count;
- if (groupItemCount == 1)
- {
- // discreet group; use an equality test
- ctx.LoadValue(fieldNumber);
- ctx.LoadValue(group.First);
- Compiler.CodeLabel processThisField = ctx.DefineLabel();
- ctx.BranchIfEqual(processThisField, true);
- ctx.Branch(tryNextField, false);
- WriteFieldHandler(ctx, expected, loc, processThisField, @continue, (IProtoSerializer)group.Items[0]);
- }
- else
- { // implement as a jump-table-based switch
- ctx.LoadValue(fieldNumber);
- ctx.LoadValue(group.First);
- ctx.Subtract(); // jump-tables are zero-based
- Compiler.CodeLabel[] jmp = new Compiler.CodeLabel[groupItemCount];
- for (int i = 0; i < groupItemCount; i++) {
- jmp[i] = ctx.DefineLabel();
- }
- ctx.Switch(jmp);
- // write the default...
- ctx.Branch(tryNextField, false);
- for (int i = 0; i < groupItemCount; i++)
- {
- WriteFieldHandler(ctx, expected, loc, jmp[i], @continue, (IProtoSerializer)group.Items[i]);
- }
- }
- ctx.MarkLabel(tryNextField);
- }
- EmitCreateIfNull(ctx, loc);
- ctx.LoadReaderWriter();
- if (isExtensible)
- {
- ctx.LoadValue(loc);
- ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("AppendExtensionData"));
- }
- else
- {
- ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("SkipField"));
- }
-
- ctx.MarkLabel(@continue);
- ctx.EmitBasicRead("ReadFieldHeader", ctx.MapType(typeof(int)));
- ctx.CopyValue();
- ctx.StoreValue(fieldNumber);
- ctx.LoadValue(0);
- ctx.BranchIfGreater(processField, false);
- EmitCreateIfNull(ctx, loc);
- // post-callbacks
- EmitCallbackIfNeeded(ctx, loc, TypeModel.CallbackType.AfterDeserialize);
- if (valueFrom != null && !loc.IsSame(valueFrom))
- {
- ctx.LoadValue(loc);
- ctx.Cast(valueFrom.Type);
- ctx.StoreValue(valueFrom);
- }
- }
- }
- private void WriteFieldHandler(
- Compiler.CompilerContext ctx, Type expected, Compiler.Local loc,
- Compiler.CodeLabel handler, Compiler.CodeLabel @continue, IProtoSerializer serializer)
- {
- ctx.MarkLabel(handler);
- Type serType = serializer.ExpectedType;
- if (serType == forType) {
- EmitCreateIfNull(ctx, loc);
- serializer.EmitRead(ctx, loc);
- }
- else {
- //RuntimeTypeModel rtm = (RuntimeTypeModel)ctx.Model;
- if (((IProtoTypeSerializer)serializer).CanCreateInstance())
- {
- Compiler.CodeLabel allDone = ctx.DefineLabel();
- ctx.LoadValue(loc);
- ctx.BranchIfFalse(allDone, false); // null is always ok
- ctx.LoadValue(loc);
- ctx.TryCast(serType);
- ctx.BranchIfTrue(allDone, false); // not null, but of the correct type
- // otherwise, need to convert it
- ctx.LoadReaderWriter();
- ctx.LoadValue(loc);
- ((IProtoTypeSerializer)serializer).EmitCreateInstance(ctx);
- ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("Merge"));
- ctx.Cast(expected);
- ctx.StoreValue(loc); // Merge always returns a value
- // nothing needs doing
- ctx.MarkLabel(allDone);
- }
- ctx.LoadValue(loc);
- ctx.Cast(serType);
- serializer.EmitRead(ctx, null);
-
- }
-
- if (serializer.ReturnsValue)
- { // update the variable
- ctx.StoreValue(loc);
- }
- ctx.Branch(@continue, false); // "continue"
- }
- void IProtoTypeSerializer.EmitCreateInstance(Compiler.CompilerContext ctx)
- {
- // different ways of creating a new instance
- bool callNoteObject = true;
- if (factory != null)
- {
- EmitInvokeCallback(ctx, factory, false, constructType, forType);
- }
- else if (!useConstructor)
- { // DataContractSerializer style
- ctx.LoadValue(constructType);
- ctx.EmitCall(ctx.MapType(typeof(BclHelpers)).GetMethod("GetUninitializedObject"));
- ctx.Cast(forType);
- }
- else if (Helpers.IsClass(constructType) && hasConstructor)
- { // XmlSerializer style
- ctx.EmitCtor(constructType);
- }
- else
- {
- ctx.LoadValue(ExpectedType);
- ctx.EmitCall(ctx.MapType(typeof(TypeModel)).GetMethod("ThrowCannotCreateInstance",
- BindingFlags.Static | BindingFlags.Public));
- ctx.LoadNullRef();
- callNoteObject = false;
- }
- if (callNoteObject)
- {
- // track root object creation
- ctx.CopyValue();
- ctx.LoadReaderWriter();
- ctx.EmitCall(ctx.MapType(typeof(ProtoReader)).GetMethod("NoteObject",
- BindingFlags.Static | BindingFlags.Public));
- }
- if (baseCtorCallbacks != null)
- {
- for (int i = 0; i < baseCtorCallbacks.Length; i++)
- {
- EmitInvokeCallback(ctx, baseCtorCallbacks[i], true, null, forType);
- }
- }
- }
- private void EmitCreateIfNull(Compiler.CompilerContext ctx, Compiler.Local storage)
- {
- Helpers.DebugAssert(storage != null);
- if (!Helpers.IsValueType(ExpectedType))
- {
- Compiler.CodeLabel afterNullCheck = ctx.DefineLabel();
- ctx.LoadValue(storage);
- ctx.BranchIfTrue(afterNullCheck, false);
- ((IProtoTypeSerializer)this).EmitCreateInstance(ctx);
-
- if (callbacks != null) EmitInvokeCallback(ctx, callbacks.BeforeDeserialize, true, null, forType);
- ctx.StoreValue(storage);
- ctx.MarkLabel(afterNullCheck);
- }
- }
- #endif
- }
- }
- #endif
|