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 (ExpectedType.IsValueType) { 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); } } }
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); } } }
protected override void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { ctx.LoadValue(valueFrom); ctx.LoadValue(typeof(Uri).GetProperty("AbsoluteUri")); Tail.EmitWrite(ctx, null); }
private static void EmitReadAndAddItem(Compiler.CompilerContext ctx, Compiler.Local list, IProtoSerializer tail, MethodInfo add, bool castListForAdd) { ctx.LoadValue(list); if (castListForAdd) { ctx.Cast(add.DeclaringType); } Type itemType = tail.ExpectedType; bool tailReturnsValue = tail.ReturnsValue; if (tail.RequiresOldValue) { if (itemType.IsValueType || !tailReturnsValue) { // going to need a variable using (Compiler.Local item = new Compiler.Local(ctx, itemType)) { if (itemType.IsValueType) { // initialise the struct ctx.LoadAddress(item, itemType); ctx.EmitCtor(itemType); } else { // assign null ctx.LoadNullRef(); ctx.StoreValue(item); } tail.EmitRead(ctx, item); if (!tailReturnsValue) { ctx.LoadValue(item); } } } else { // no variable; pass the null on the stack and take the value *off* the stack ctx.LoadNullRef(); tail.EmitRead(ctx, null); } } else { if (tailReturnsValue) { // out only (on the stack); just emit it tail.EmitRead(ctx, null); } else { // doesn't take anything in nor return anything! WTF? throw new InvalidOperationException(); } } // our "Add" is chosen either to take the correct type, or to take "object"; // we may need to box the value Type addParamType = add.GetParameters()[0].ParameterType; if (addParamType != itemType) { if (addParamType == ctx.MapType(typeof(object))) { ctx.CastToObject(itemType); } #if !NO_GENERICS else if (Helpers.GetUnderlyingType(addParamType) == itemType) { // list is nullable ConstructorInfo ctor = Helpers.GetConstructor(addParamType, new Type[] { itemType }, false); ctx.EmitCtor(ctor); // the itemType on the stack is now a Nullable<ItemType> } #endif else { throw new InvalidOperationException("Conflicting item/add type"); } } ctx.EmitCall(add); if (add.ReturnType != ctx.MapType(typeof(void))) { ctx.DiscardValue(); } }
public void EmitRead(Compiler.CompilerContext ctx, Compiler.Local incoming) { using (Compiler.Local objValue = ctx.GetLocalWithValue(ExpectedType, incoming)) { Compiler.Local[] locals = new Compiler.Local[members.Length]; try { for (int i = 0; i < locals.Length; i++) { Type type = GetMemberType(i); bool store = true; locals[i] = new Compiler.Local(ctx, type); if (!ExpectedType.IsValueType) { // value-types always read the old value if (type.IsValueType) { switch (Helpers.GetTypeCode(type)) { case ProtoTypeCode.Boolean: case ProtoTypeCode.Byte: case ProtoTypeCode.Int16: case ProtoTypeCode.Int32: case ProtoTypeCode.SByte: case ProtoTypeCode.UInt16: case ProtoTypeCode.UInt32: ctx.LoadValue(0); break; case ProtoTypeCode.Int64: case ProtoTypeCode.UInt64: ctx.LoadValue(0L); break; case ProtoTypeCode.Single: ctx.LoadValue(0.0F); break; case ProtoTypeCode.Double: ctx.LoadValue(0.0D); break; case ProtoTypeCode.Decimal: ctx.LoadValue(0M); break; case ProtoTypeCode.Guid: ctx.LoadValue(Guid.Empty); break; default: ctx.LoadAddress(locals[i], type); ctx.EmitCtor(type); store = false; break; } } else { ctx.LoadNullRef(); } if (store) { ctx.StoreValue(locals[i]); } } } Compiler.CodeLabel skipOld = ExpectedType.IsValueType ? new Compiler.CodeLabel() : ctx.DefineLabel(); if (!ExpectedType.IsValueType) { ctx.LoadAddress(objValue, ExpectedType); ctx.BranchIfFalse(skipOld, false); } for (int i = 0; i < members.Length; i++) { ctx.LoadAddress(objValue, ExpectedType); switch (members[i].MemberType) { case MemberTypes.Field: ctx.LoadValue((FieldInfo)members[i]); break; case MemberTypes.Property: ctx.LoadValue((PropertyInfo)members[i]); break; } ctx.StoreValue(locals[i]); } if (!ExpectedType.IsValueType) { ctx.MarkLabel(skipOld); } using (Compiler.Local fieldNumber = new Compiler.Local(ctx, typeof(int))) { Compiler.CodeLabel @continue = ctx.DefineLabel(), processField = ctx.DefineLabel(), notRecognised = ctx.DefineLabel(); ctx.Branch(@continue, false); Compiler.CodeLabel[] handlers = new Compiler.CodeLabel[members.Length]; for (int i = 0; i < members.Length; i++) { handlers[i] = ctx.DefineLabel(); } ctx.MarkLabel(processField); ctx.LoadValue(fieldNumber); ctx.LoadValue(1); ctx.Subtract(); // jump-table is zero-based ctx.Switch(handlers); // and the default: ctx.Branch(notRecognised, false); for (int i = 0; i < handlers.Length; i++) { ctx.MarkLabel(handlers[i]); IProtoSerializer tail = tails[i]; Compiler.Local oldValIfNeeded = tail.RequiresOldValue ? locals[i] : null; ctx.ReadNullCheckedTail(locals[i].Type, tail, oldValIfNeeded); if (tail.ReturnsValue) { if (locals[i].Type.IsValueType) { ctx.StoreValue(locals[i]); } else { Compiler.CodeLabel hasValue = ctx.DefineLabel(), allDone = ctx.DefineLabel(); ctx.CopyValue(); ctx.BranchIfTrue(hasValue, true); // interpret null as "don't assign" ctx.DiscardValue(); ctx.Branch(allDone, true); ctx.MarkLabel(hasValue); ctx.StoreValue(locals[i]); ctx.MarkLabel(allDone); } } ctx.Branch(@continue, false); } ctx.MarkLabel(notRecognised); ctx.LoadReaderWriter(); ctx.EmitCall(typeof(ProtoReader).GetMethod("SkipField")); ctx.MarkLabel(@continue); ctx.EmitBasicRead("ReadFieldHeader", typeof(int)); ctx.CopyValue(); ctx.StoreValue(fieldNumber); ctx.LoadValue(0); ctx.BranchIfGreater(processField, false); } for (int i = 0; i < locals.Length; i++) { ctx.LoadValue(locals[i]); } ctx.EmitCtor(ctor); ctx.StoreValue(objValue); } finally { for (int i = 0; i < locals.Length; i++) { if (locals[i] != null) { locals[i].Dispose(); // release for re-use } } } } }
private void EmitBranchIfDefaultValue(Compiler.CompilerContext ctx, Compiler.CodeLabel label) { Type expected = ExpectedType; switch (Helpers.GetTypeCode(expected)) { case ProtoTypeCode.Boolean: if ((bool)defaultValue) { ctx.BranchIfTrue(label, false); } else { ctx.BranchIfFalse(label, false); } break; case ProtoTypeCode.Byte: if ((byte)defaultValue == (byte)0) { ctx.BranchIfFalse(label, false); } else { ctx.LoadValue((int)(byte)defaultValue); EmitBeq(ctx, label, expected); } break; case ProtoTypeCode.SByte: if ((sbyte)defaultValue == (sbyte)0) { ctx.BranchIfFalse(label, false); } else { ctx.LoadValue((int)(sbyte)defaultValue); EmitBeq(ctx, label, expected); } break; case ProtoTypeCode.Int16: if ((short)defaultValue == (short)0) { ctx.BranchIfFalse(label, false); } else { ctx.LoadValue((int)(short)defaultValue); EmitBeq(ctx, label, expected); } break; case ProtoTypeCode.UInt16: if ((ushort)defaultValue == (ushort)0) { ctx.BranchIfFalse(label, false); } else { ctx.LoadValue((int)(ushort)defaultValue); EmitBeq(ctx, label, expected); } break; case ProtoTypeCode.Int32: if ((int)defaultValue == (int)0) { ctx.BranchIfFalse(label, false); } else { ctx.LoadValue((int)defaultValue); EmitBeq(ctx, label, expected); } break; case ProtoTypeCode.UInt32: if ((uint)defaultValue == (uint)0) { ctx.BranchIfFalse(label, false); } else { ctx.LoadValue((int)(uint)defaultValue); EmitBeq(ctx, label, expected); } break; case ProtoTypeCode.Char: if ((char)defaultValue == (char)0) { ctx.BranchIfFalse(label, false); } else { ctx.LoadValue((int)(char)defaultValue); EmitBeq(ctx, label, expected); } break; case ProtoTypeCode.Int64: ctx.LoadValue((long)defaultValue); EmitBeq(ctx, label, expected); break; case ProtoTypeCode.UInt64: ctx.LoadValue((long)(ulong)defaultValue); EmitBeq(ctx, label, expected); break; case ProtoTypeCode.Double: ctx.LoadValue((double)defaultValue); EmitBeq(ctx, label, expected); break; case ProtoTypeCode.Single: ctx.LoadValue((float)defaultValue); EmitBeq(ctx, label, expected); break; case ProtoTypeCode.String: ctx.LoadValue((string)defaultValue); EmitBeq(ctx, label, expected); break; case ProtoTypeCode.Decimal: { decimal d = (decimal)defaultValue; ctx.LoadValue(d); EmitBeq(ctx, label, expected); } break; case ProtoTypeCode.TimeSpan: { TimeSpan ts = (TimeSpan)defaultValue; if (ts == TimeSpan.Zero) { ctx.LoadValue(typeof(TimeSpan).GetField("Zero")); } else { ctx.LoadValue(ts.Ticks); ctx.EmitCall(typeof(TimeSpan).GetMethod("FromTicks")); } EmitBeq(ctx, label, expected); break; } case ProtoTypeCode.Guid: { ctx.LoadValue((Guid)defaultValue); EmitBeq(ctx, label, expected); break; } case ProtoTypeCode.DateTime: { ctx.LoadValue(((DateTime)defaultValue).ToBinary()); ctx.EmitCall(typeof(DateTime).GetMethod("FromBinary")); EmitBeq(ctx, label, expected); break; } default: throw new NotSupportedException("Type cannot be represented as a default value: " + expected.FullName); } }
private void WriteSerializers(CompilerOptions options, string assemblyName, TypeBuilder type, bool root, SerializerPair[] basicMethodPairs, out int index, out bool hasInheritance, out SerializerPair[] methodPairs, out Compiler.CompilerContext.ILVersion ilVersion) { Compiler.CompilerContext ctx; index = 0; hasInheritance = false; methodPairs = new SerializerPair[_types.Count]; foreach (MetaType metaType in _types) { string writeName = "Write"; if (root) { writeName += "Root"; } #if DEBUG writeName += metaType.Type.Name; #endif MethodContext writeGenCtx; MethodBuilder writeMethod = EmitDefineMethod( type, writeName, MethodAttributes.Private | MethodAttributes.Static, CallingConventions.Standard, MapType(typeof(void)), new[] { new MethodContext.ParameterGenInfo(metaType.Type, "obj", 1), new MethodContext.ParameterGenInfo(MapType(typeof(ProtoWriter)), "dest", 2), }, false, out writeGenCtx); string readName = "Read"; if (root) { readName += "Root"; } #if DEBUG readName += metaType.Type.Name; #endif MethodContext readGenCtx; MethodBuilder readMethod = EmitDefineMethod( type, readName, MethodAttributes.Private | MethodAttributes.Static, CallingConventions.Standard, metaType.Type, new[] { new MethodContext.ParameterGenInfo(metaType.Type, "obj", 1), new MethodContext.ParameterGenInfo(MapType(typeof(ProtoReader)), "source", 2), }, false, out readGenCtx); SerializerPair pair = new SerializerPair( GetKey(metaType.Type, true, false), GetKey(metaType.Type, true, true), metaType, writeMethod, readMethod, writeGenCtx, readGenCtx); methodPairs[index++] = pair; if (pair.MetaKey != pair.BaseKey) { hasInheritance = true; } } if (hasInheritance) { Array.Sort(methodPairs); } ilVersion = Compiler.CompilerContext.ILVersion.Net2; if (options.MetaDataVersion == 0x10000) { ilVersion = Compiler.CompilerContext.ILVersion.Net1; // old-school! } for (index = 0; index < methodPairs.Length; index++) { SerializerPair pair = methodPairs[index]; var ser = root ? pair.Type.RootSerializer : pair.Type.Serializer; ctx = new Compiler.CompilerContext(pair.SerializeBody, true, true, basicMethodPairs ?? methodPairs, this, ilVersion, assemblyName, pair.Type.Type); ctx.CheckAccessibility(pair.Deserialize.ReturnType); ser.EmitWrite(ctx, ctx.InputValue); ctx.Return(); ctx = new Compiler.CompilerContext(pair.DeserializeBody, true, false, basicMethodPairs ?? methodPairs, this, ilVersion, assemblyName, pair.Type.Type); ser.EmitRead(ctx, ctx.InputValue); if (!ser.EmitReadReturnsValue) { ctx.LoadValue(ctx.InputValue); } ctx.Return(); } }
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" }
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using (Compiler.Local oldValue = ctx.GetLocalWithValue(ExpectedType, valueFrom)) using (Compiler.Local token = new Compiler.Local(ctx, typeof(SubItemToken))) using (Compiler.Local field = new Compiler.Local(ctx, typeof(int))) { ctx.LoadReader(true); ctx.EmitCall(typeof(ProtoReader).GetMethod("StartSubItem", Compiler.ReaderUtil.ReaderStateTypeArray)); ctx.StoreValue(token); Compiler.CodeLabel next = ctx.DefineLabel(), processField = ctx.DefineLabel(), end = ctx.DefineLabel(); ctx.MarkLabel(next); ctx.EmitBasicRead("ReadFieldHeader", typeof(int)); ctx.CopyValue(); ctx.StoreValue(field); ctx.LoadValue(Tag); // = 1 - process ctx.BranchIfEqual(processField, true); ctx.LoadValue(field); ctx.LoadValue(1); // < 1 - exit ctx.BranchIfLess(end, false); // default: skip ctx.LoadReader(true); ctx.EmitCall(typeof(ProtoReader).GetMethod("SkipField", Compiler.ReaderUtil.StateTypeArray)); ctx.Branch(next, true); // process ctx.MarkLabel(processField); if (Tail.RequiresOldValue) { if (Helpers.IsValueType(ExpectedType)) { ctx.LoadAddress(oldValue, ExpectedType); ctx.EmitCall(ExpectedType.GetMethod("GetValueOrDefault", Helpers.EmptyTypes)); } else { ctx.LoadValue(oldValue); } } Tail.EmitRead(ctx, null); // note we demanded always returns a value if (Helpers.IsValueType(ExpectedType)) { ctx.EmitCtor(ExpectedType, Tail.ExpectedType); // re-nullable<T> it } ctx.StoreValue(oldValue); ctx.Branch(next, false); // outro ctx.MarkLabel(end); ctx.LoadValue(token); ctx.LoadReader(true); ctx.EmitCall(typeof(ProtoReader).GetMethod("EndSubItem", new[] { typeof(SubItemToken), typeof(ProtoReader), Compiler.ReaderUtil.ByRefStateType })); ctx.LoadValue(oldValue); // load the old value } }
private bool EmitDedicatedMethod(Compiler.CompilerContext ctx, Compiler.Local valueFrom, bool read) { MethodBuilder method = ctx.GetDedicatedMethod(key, read); if (method == null) { return(false); } using (Compiler.Local val = ctx.GetLocalWithValue(type, valueFrom)) using (Compiler.Local token = new ProtoBuf.Compiler.Local(ctx, typeof(SubItemToken))) { Type rwType = read ? typeof(ProtoReader) : typeof(ProtoWriter); if (read) { ctx.LoadReader(true); } else { // write requires the object for StartSubItem; read doesn't // (if recursion-check is disabled [subtypes] then null is fine too) if (Helpers.IsValueType(type) || !recursionCheck) { ctx.LoadNullRef(); } else { ctx.LoadValue(val); } ctx.LoadWriter(true); } ctx.EmitCall(Helpers.GetStaticMethod(rwType, "StartSubItem", read ? ProtoReader.State.ReaderStateTypeArray : new Type[] { typeof(object), rwType, ProtoWriter.ByRefStateType })); ctx.StoreValue(token); if (read) { ctx.LoadReader(true); ctx.LoadValue(val); } else { ctx.LoadWriter(true); ctx.LoadValue(val); } ctx.EmitCall(method); // handle inheritance (we will be calling the *base* version of things, // but we expect Read to return the "type" type) if (read && type != method.ReturnType) { ctx.Cast(type); } ctx.LoadValue(token); if (read) { ctx.LoadReader(true); ctx.EmitCall(Helpers.GetStaticMethod(rwType, "EndSubItem", new Type[] { typeof(SubItemToken), rwType, ProtoReader.State.ByRefStateType })); } else { ctx.LoadWriter(true); ctx.EmitCall(ProtoWriter.GetStaticMethod("EndSubItem")); } } return(true); }
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { bool writeValue; SanityCheck(ctx.Model, property, Tail, out writeValue, ctx.NonPublic, ctx.AllowInternal(property)); if (Helpers.IsValueType(ExpectedType) && valueFrom == null) { throw new InvalidOperationException("Attempt to mutate struct on the head of the stack; changes would be lost"); } using (Compiler.Local loc = ctx.GetLocalWithValue(ExpectedType, valueFrom)) { if (Tail.RequiresOldValue) { ctx.LoadAddress(loc, ExpectedType); // stack is: old-addr ctx.LoadValue(property); // stack is: old-value } Type propertyType = property.PropertyType; ctx.ReadNullCheckedTail(propertyType, Tail, null, overrideType); // stack is [new-value] if (writeValue) { var newType = overrideType != null ? overrideType : property.PropertyType; using (Compiler.Local newVal = new Compiler.Local(ctx, newType)) { ctx.StoreValue(newVal); // stack is empty Compiler.CodeLabel allDone = new Compiler.CodeLabel(); // <=== default structs if (!Helpers.IsValueType(propertyType)) { // if the tail returns a null, intepret that as *no assign* allDone = ctx.DefineLabel(); ctx.LoadValue(newVal); // stack is: new-value ctx.BranchIfFalse(@allDone, true); // stack is empty } // assign the value ctx.LoadAddress(loc, ExpectedType); // parent-addr ctx.LoadValue(newVal); // parent-obj|new-value if (overrideType != null) { ctx.Cast(property.PropertyType); } if (shadowSetter == null) { ctx.StoreValue(property); // empty } else { ctx.EmitCall(shadowSetter); // empty } if (!Helpers.IsValueType(propertyType)) { ctx.MarkLabel(allDone); } } } else { // don't want return value; drop it if anything there // stack is [new-value] if (Tail.ReturnsValue) { ctx.DiscardValue(); } } } }
protected override void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { ctx.LoadAddress(valueFrom, ExpectedType); ctx.LoadValue(property); ctx.WriteNullCheckedTail(property.PropertyType, Tail, null, overrideType); }
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 (constructType.IsClass && 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); } } }
/// <summary> /// Fully compiles the current model into a static-compiled serialization dll /// (the serialization dll still requires protobuf-net for support services). /// </summary> /// <remarks>A full compilation is restricted to accessing public types / members</remarks> /// <param name="name">The name of the TypeModel class to create</param> /// <param name="path">The path for the new dll</param> /// <returns>An instance of the newly created compiled type-model</returns> public TypeModel Compile(string name, string path) { BuildAllSerializers(); Freeze(); bool save = !Helpers.IsNullOrEmpty(path); if (Helpers.IsNullOrEmpty(name)) { if (save) throw new ArgumentNullException("name"); name = Guid.NewGuid().ToString(); } AssemblyName an = new AssemblyName(); an.Name = name; AssemblyBuilder asm = AppDomain.CurrentDomain.DefineDynamicAssembly(an, (save ? AssemblyBuilderAccess.RunAndSave : AssemblyBuilderAccess.Run) ); ModuleBuilder module = save ? asm.DefineDynamicModule(name, path) : asm.DefineDynamicModule(name); Type baseType = typeof(TypeModel); TypeBuilder type = module.DefineType(name, (baseType.Attributes & ~TypeAttributes.Abstract) | TypeAttributes.Sealed, baseType); Compiler.CompilerContext ctx; int index = 0; bool hasInheritance = false; SerializerPair[] methodPairs = new SerializerPair[types.Count]; foreach (MetaType metaType in types) { MethodBuilder writeMethod = type.DefineMethod("Write" #if DEBUG + metaType.Type.Name #endif , MethodAttributes.Private | MethodAttributes.Static, CallingConventions.Standard, typeof(void), new Type[] { metaType.Type, typeof(ProtoWriter) }); MethodBuilder readMethod = type.DefineMethod("Read" #if DEBUG + metaType.Type.Name #endif , MethodAttributes.Private | MethodAttributes.Static, CallingConventions.Standard, metaType.Type, new Type[] { metaType.Type, typeof(ProtoReader) }); SerializerPair pair = new SerializerPair( GetKey(metaType.Type, true, false), GetKey(metaType.Type, true, true), metaType, writeMethod, readMethod, writeMethod.GetILGenerator(), readMethod.GetILGenerator()); methodPairs[index++] = pair; if (pair.MetaKey != pair.BaseKey) hasInheritance = true; } if (hasInheritance) { Array.Sort(methodPairs); } for(index = 0; index < methodPairs.Length ; index++) { SerializerPair pair = methodPairs[index]; ctx = new Compiler.CompilerContext(pair.SerializeBody, true, true, methodPairs); pair.Type.Serializer.EmitWrite(ctx, Compiler.Local.InputValue); ctx.Return(); ctx = new Compiler.CompilerContext(pair.DeserializeBody, true, false, methodPairs); pair.Type.Serializer.EmitRead(ctx, Compiler.Local.InputValue); if (!pair.Type.Serializer.ReturnsValue) { ctx.LoadValue(Compiler.Local.InputValue); } ctx.Return(); } FieldBuilder knownTypes = type.DefineField("knownTypes", typeof(Type[]), FieldAttributes.Private | FieldAttributes.InitOnly); ILGenerator il = Override(type, "GetKeyImpl"); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, knownTypes); il.Emit(OpCodes.Ldarg_1); // note that Array.IndexOf is not supported under CF il.EmitCall(OpCodes.Callvirt,typeof(IList).GetMethod( "IndexOf", new Type[] { typeof(object) }), null); if (hasInheritance) { il.DeclareLocal(typeof(int)); // loc-0 il.Emit(OpCodes.Dup); il.Emit(OpCodes.Stloc_0); BasicList getKeyLabels = new BasicList(); int lastKey = -1; for (int i = 0; i < methodPairs.Length; i++) { if (methodPairs[i].MetaKey == methodPairs[i].BaseKey) break; if (lastKey == methodPairs[i].BaseKey) { // add the last label again getKeyLabels.Add(getKeyLabels[getKeyLabels.Count - 1]); } else { // add a new unique label getKeyLabels.Add(il.DefineLabel()); lastKey = methodPairs[i].BaseKey; } } Label[] subtypeLabels = new Label[getKeyLabels.Count]; getKeyLabels.CopyTo(subtypeLabels, 0); il.Emit(OpCodes.Switch, subtypeLabels); il.Emit(OpCodes.Ldloc_0); // not a sub-type; use the original value il.Emit(OpCodes.Ret); lastKey = -1; // now output the different branches per sub-type (not derived type) for (int i = subtypeLabels.Length - 1; i >= 0; i--) { if (lastKey != methodPairs[i].BaseKey) { lastKey = methodPairs[i].BaseKey; // find the actual base-index for this base-key (i.e. the index of // the base-type) int keyIndex = -1; for (int j = subtypeLabels.Length; j < methodPairs.Length; j++) { if (methodPairs[j].BaseKey == lastKey && methodPairs[j].MetaKey == lastKey) { keyIndex = j; break; } } il.MarkLabel(subtypeLabels[i]); Compiler.CompilerContext.LoadValue(il, keyIndex); il.Emit(OpCodes.Ret); } } } else { il.Emit(OpCodes.Ret); } il = Override(type, "Serialize"); ctx = new Compiler.CompilerContext(il, false, true, methodPairs); // arg0 = this, arg1 = key, arg2=obj, arg3=dest Label[] jumpTable = new Label[types.Count]; for (int i = 0; i < jumpTable.Length; i++) { jumpTable[i] = il.DefineLabel(); } il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Switch, jumpTable); ctx.Return(); for (int i = 0; i < jumpTable.Length; i++) { SerializerPair pair = methodPairs[i]; il.MarkLabel(jumpTable[i]); il.Emit(OpCodes.Ldarg_2); ctx.CastFromObject(pair.Type.Type); il.Emit(OpCodes.Ldarg_3); il.EmitCall(OpCodes.Call, pair.Serialize, null); ctx.Return(); } il = Override(type, "Deserialize"); ctx = new Compiler.CompilerContext(il, false, false, methodPairs); // arg0 = this, arg1 = key, arg2=obj, arg3=source for (int i = 0; i < jumpTable.Length; i++) { jumpTable[i] = il.DefineLabel(); } il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Switch, jumpTable); ctx.LoadNullRef(); ctx.Return(); for (int i = 0; i < jumpTable.Length; i++) { SerializerPair pair = methodPairs[i]; il.MarkLabel(jumpTable[i]); Type keyType = pair.Type.Type; if (keyType.IsValueType) { il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Ldarg_3); il.EmitCall(OpCodes.Call, EmitBoxedSerializer(type, i, keyType, methodPairs), null); ctx.Return(); } else { il.Emit(OpCodes.Ldarg_2); ctx.CastFromObject(keyType); il.Emit(OpCodes.Ldarg_3); il.EmitCall(OpCodes.Call, pair.Deserialize, null); ctx.Return(); } } ConstructorBuilder ctor = type.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, Helpers.EmptyTypes); il = ctor.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Call, Helpers.GetConstructor(baseType, Helpers.EmptyTypes, true)); il.Emit(OpCodes.Ldarg_0); Compiler.CompilerContext.LoadValue(il, types.Count); il.Emit(OpCodes.Newarr, typeof(Type)); index = 0; foreach(SerializerPair pair in methodPairs) { il.Emit(OpCodes.Dup); Compiler.CompilerContext.LoadValue(il, index); il.Emit(OpCodes.Ldtoken, pair.Type.Type); il.EmitCall(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null); il.Emit(OpCodes.Stelem_Ref); index++; } il.Emit(OpCodes.Stfld, knownTypes); il.Emit(OpCodes.Ret); Type finalType = type.CreateType(); if(!Helpers.IsNullOrEmpty(path)) { asm.Save(path); Helpers.DebugWriteLine("Wrote dll:" + path); } return (TypeModel)Activator.CreateInstance(finalType); }
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using (ctx.StartDebugBlockAuto(this)) { using (Compiler.Local oldValue = ctx.GetLocalWithValueForEmitRead(this, valueFrom)) { Compiler.Local tempLocal = null; Compiler.Local valueForTail; Debug.Assert(Tail.RequiresOldValue || Tail.EmitReadReturnsValue); if (Tail.RequiresOldValue) { if (_expectedType.IsValueType) { tempLocal = ctx.Local(Tail.ExpectedType); ctx.LoadAddress(oldValue, _expectedType); ctx.EmitCall(_expectedType.GetMethod("GetValueOrDefault", Helpers.EmptyTypes)); ctx.StoreValue(tempLocal); valueForTail = tempLocal; } else { valueForTail = oldValue; } } else { valueForTail = null; } // valueForTail contains: // null: when not required old value // oldValue local: when reference type // tempLocal: when nullable value type Tail.EmitRead(ctx, valueForTail); if (_expectedType.IsValueType) { if (!Tail.EmitReadReturnsValue) { ctx.LoadValue(tempLocal); } // note we demanded always returns a value ctx.EmitCtor(_expectedType, Tail.ExpectedType); // re-nullable<T> it } if (Tail.EmitReadReturnsValue || _expectedType.IsValueType) { ctx.StoreValue(oldValue); } if (EmitReadReturnsValue) { ctx.LoadValue(oldValue); } if (!tempLocal.IsNullRef()) { tempLocal.Dispose(); } } } }
private void WriteSerializers(CompilerOptions options, string assemblyName, TypeBuilder type, out int index, out bool hasInheritance, out SerializerPair[] methodPairs, out Compiler.CompilerContext.ILVersion ilVersion) { Compiler.CompilerContext ctx; index = 0; hasInheritance = false; methodPairs = new SerializerPair[types.Count]; foreach (MetaType metaType in types) { MethodBuilder writeMethod = type.DefineMethod("Write" #if DEBUG + metaType.Type.Name #endif , MethodAttributes.Private | MethodAttributes.Static, CallingConventions.Standard, MapType(typeof(void)), new Type[] { metaType.Type, MapType(typeof(ProtoWriter)) }); MethodBuilder readMethod = type.DefineMethod("Read" #if DEBUG + metaType.Type.Name #endif , MethodAttributes.Private | MethodAttributes.Static, CallingConventions.Standard, metaType.Type, new Type[] { metaType.Type, MapType(typeof(ProtoReader)) }); SerializerPair pair = new SerializerPair( GetKey(metaType.Type, true, false), GetKey(metaType.Type, true, true), metaType, writeMethod, readMethod, writeMethod.GetILGenerator(), readMethod.GetILGenerator()); methodPairs[index++] = pair; if (pair.MetaKey != pair.BaseKey) hasInheritance = true; } if (hasInheritance) { Array.Sort(methodPairs); } ilVersion = Compiler.CompilerContext.ILVersion.Net2; if (options.MetaDataVersion == 0x10000) { ilVersion = Compiler.CompilerContext.ILVersion.Net1; // old-school! } for (index = 0; index < methodPairs.Length; index++) { SerializerPair pair = methodPairs[index]; ctx = new Compiler.CompilerContext(pair.SerializeBody, true, true, methodPairs, this, ilVersion, assemblyName, pair.Type.Type); ctx.CheckAccessibility(pair.Deserialize.ReturnType); pair.Type.Serializer.EmitWrite(ctx, ctx.InputValue); ctx.Return(); ctx = new Compiler.CompilerContext(pair.DeserializeBody, true, false, methodPairs, this, ilVersion, assemblyName, pair.Type.Type); pair.Type.Serializer.EmitRead(ctx, ctx.InputValue); if (!pair.Type.Serializer.ReturnsValue) { ctx.LoadValue(ctx.InputValue); } ctx.Return(); } }
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using (Compiler.Local loc = ctx.GetLocalWithValue(ExpectedType, valueFrom)) { if (Tail.RequiresOldValue) { ctx.LoadAddress(loc, ExpectedType); ctx.LoadValue(field); } // value is either now on the stack or not needed ctx.ReadNullCheckedTail(field.FieldType, Tail, null); if (Tail.ReturnsValue) { using (Compiler.Local newVal = new Compiler.Local(ctx, field.FieldType)) { ctx.StoreValue(newVal); if (field.FieldType.IsValueType) { ctx.LoadAddress(loc, ExpectedType); ctx.LoadValue(newVal); ctx.StoreValue(field); } else { Compiler.CodeLabel allDone = ctx.DefineLabel(); ctx.LoadValue(newVal); ctx.BranchIfFalse(allDone, true); // interpret null as "don't assign" ctx.LoadAddress(loc, ExpectedType); ctx.LoadValue(newVal); ctx.StoreValue(field); ctx.MarkLabel(allDone); } } } } }
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { TypeCode typeCode = GetTypeCode(); if (map == null) { ctx.EmitBasicRead("ReadInt32", typeof(int)); ctx.ConvertFromInt32(typeCode); } else { int[] wireValues = new int[map.Length]; object[] values = new object[map.Length]; for (int i = 0; i < map.Length; i++) { wireValues[i] = map[i].WireValue; values[i] = map[i].Value; } using (Compiler.Local result = new Compiler.Local(ctx, ExpectedType)) using (Compiler.Local wireValue = new Compiler.Local(ctx, typeof(int))) { ctx.EmitBasicRead("ReadInt32", typeof(int)); ctx.StoreValue(wireValue); Compiler.CodeLabel @continue = ctx.DefineLabel(); foreach (BasicList.Group group in BasicList.GetContiguousGroups(wireValues, values)) { Compiler.CodeLabel tryNextGroup = ctx.DefineLabel(); int groupItemCount = group.Items.Count; if (groupItemCount == 1) { // discreet group; use an equality test ctx.LoadValue(wireValue); ctx.LoadValue(group.First); Compiler.CodeLabel processThisValue = ctx.DefineLabel(); ctx.BranchIfEqual(processThisValue, true); ctx.Branch(tryNextGroup, false); WriteEnumValue(ctx, typeCode, processThisValue, @continue, group.Items[0], @result); } else { // implement as a jump-table-based switch ctx.LoadValue(wireValue); 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(tryNextGroup, false); for (int i = 0; i < groupItemCount; i++) { WriteEnumValue(ctx, typeCode, jmp[i], @continue, group.Items[i], @result); } } ctx.MarkLabel(tryNextGroup); } // throw source.CreateEnumException(ExpectedType, wireValue); ctx.LoadReaderWriter(); ctx.LoadValue(ExpectedType); ctx.LoadValue(wireValue); ctx.EmitCall(typeof(ProtoReader).GetMethod("ThrowEnumException")); ctx.MarkLabel(@continue); ctx.LoadValue(result); } } }
private static void EmitReadAndAddItem(Compiler.CompilerContext ctx, Compiler.Local list, IProtoSerializer tail, MethodInfo add) { ctx.LoadValue(list); Type itemType = tail.ExpectedType; if (tail.RequiresOldValue) { if (itemType.IsValueType || !tail.ReturnsValue) { // going to need a variable using (Compiler.Local item = new Compiler.Local(ctx, itemType)) { if (itemType.IsValueType) { // initialise the struct ctx.LoadAddress(item, itemType); ctx.EmitCtor(itemType); } else { // assign null ctx.LoadNullRef(); ctx.StoreValue(item); } tail.EmitRead(ctx, item); if (!tail.ReturnsValue) { ctx.LoadValue(item); } } } else { // no variable; pass the null on the stack and take the value *off* the stack ctx.LoadNullRef(); tail.EmitRead(ctx, null); } } else { if (tail.ReturnsValue) { // out only (on the stack); just emit it tail.EmitRead(ctx, null); } else { // doesn't take anything in nor return anything! WTF? throw new InvalidOperationException(); } } // our "Add" is chosen either to take the correct type, or to take "object"; // we may need to box the value Type addParamType = add.GetParameters()[0].ParameterType; if (addParamType != itemType) { if (addParamType == typeof(object)) { ctx.CastToObject(itemType); } else { throw new InvalidOperationException("Conflicting item/add type"); } } ctx.EmitCall(add); if (add.ReturnType != typeof(void)) { ctx.DiscardValue(); } }
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using (Compiler.Local oldList = AppendToCollection ? ctx.GetLocalWithValue(ExpectedType, valueFrom) : null) using (Compiler.Local builder = new Compiler.Local(ctx, builderFactory.ReturnType)) { ctx.EmitCall(builderFactory); ctx.StoreValue(builder); if (AppendToCollection) { Compiler.CodeLabel done = ctx.DefineLabel(); if (!Helpers.IsValueType(ExpectedType)) { ctx.LoadValue(oldList); ctx.BranchIfFalse(done, false); // old value null; nothing to add } #if COREFX TypeInfo typeInfo = ExpectedType.GetTypeInfo(); #else Type typeInfo = ExpectedType; #endif PropertyInfo prop = Helpers.GetProperty(typeInfo, "Length", false); if (prop == null) { prop = Helpers.GetProperty(typeInfo, "Count", false); } #if !NO_GENERICS if (prop == null) { prop = Helpers.GetProperty(ResolveIReadOnlyCollection(ExpectedType, Tail.ExpectedType), "Count", false); } #endif ctx.LoadAddress(oldList, oldList.Type); ctx.EmitCall(Helpers.GetGetMethod(prop, false, false)); ctx.BranchIfFalse(done, false); // old list is empty; nothing to add Type voidType = ctx.MapType(typeof(void)); if (addRange != null) { ctx.LoadValue(builder); ctx.LoadValue(oldList); ctx.EmitCall(addRange); if (addRange.ReturnType != null && add.ReturnType != voidType) { ctx.DiscardValue(); } } else { // loop and call Add repeatedly MethodInfo moveNext, current, getEnumerator = GetEnumeratorInfo(ctx.Model, out moveNext, out current); Helpers.DebugAssert(moveNext != null); Helpers.DebugAssert(current != null); Helpers.DebugAssert(getEnumerator != null); Type enumeratorType = getEnumerator.ReturnType; using (Compiler.Local iter = new Compiler.Local(ctx, enumeratorType)) { ctx.LoadAddress(oldList, ExpectedType); ctx.EmitCall(getEnumerator); ctx.StoreValue(iter); using (ctx.Using(iter)) { Compiler.CodeLabel body = ctx.DefineLabel(), next = ctx.DefineLabel(); ctx.Branch(next, false); ctx.MarkLabel(body); ctx.LoadAddress(builder, builder.Type); ctx.LoadAddress(iter, enumeratorType); ctx.EmitCall(current); ctx.EmitCall(add); if (add.ReturnType != null && add.ReturnType != voidType) { ctx.DiscardValue(); } ctx.MarkLabel(@next); ctx.LoadAddress(iter, enumeratorType); ctx.EmitCall(moveNext); ctx.BranchIfTrue(body, false); } } } ctx.MarkLabel(done); } EmitReadList(ctx, builder, Tail, add, packedWireType, false); ctx.LoadAddress(builder, builder.Type); ctx.EmitCall(finish); if (ExpectedType != finish.ReturnType) { ctx.Cast(ExpectedType); } } }
/// <summary> /// Fully compiles the current model into a static-compiled serialization dll /// (the serialization dll still requires protobuf-net for support services). /// </summary> /// <remarks>A full compilation is restricted to accessing public types / members</remarks> /// <param name="name">The name of the TypeModel class to create</param> /// <param name="path">The path for the new dll</param> /// <returns>An instance of the newly created compiled type-model</returns> public TypeModel Compile(string name, string path) { BuildAllSerializers(); Freeze(); bool save = !Helpers.IsNullOrEmpty(path); if (Helpers.IsNullOrEmpty(name)) { if (save) { throw new ArgumentNullException("name"); } name = Guid.NewGuid().ToString(); } AssemblyName an = new AssemblyName(); an.Name = name; AssemblyBuilder asm = AppDomain.CurrentDomain.DefineDynamicAssembly(an, (save ? AssemblyBuilderAccess.RunAndSave : AssemblyBuilderAccess.Run) ); ModuleBuilder module = save ? asm.DefineDynamicModule(name, path) : asm.DefineDynamicModule(name); Type baseType = typeof(TypeModel); TypeBuilder type = module.DefineType(name, (baseType.Attributes & ~TypeAttributes.Abstract) | TypeAttributes.Sealed, baseType); Compiler.CompilerContext ctx; int index = 0; bool hasInheritance = false; SerializerPair[] methodPairs = new SerializerPair[types.Count]; foreach (MetaType metaType in types) { MethodBuilder writeMethod = type.DefineMethod("Write", MethodAttributes.Private | MethodAttributes.Static, CallingConventions.Standard, typeof(void), new Type[] { metaType.Type, typeof(ProtoWriter) }); MethodBuilder readMethod = type.DefineMethod("Read", MethodAttributes.Private | MethodAttributes.Static, CallingConventions.Standard, metaType.Type, new Type[] { metaType.Type, typeof(ProtoReader) }); SerializerPair pair = new SerializerPair( GetKey(metaType.Type, true, false), GetKey(metaType.Type, true, true), metaType, writeMethod, readMethod, writeMethod.GetILGenerator(), readMethod.GetILGenerator()); methodPairs[index++] = pair; if (pair.MetaKey != pair.BaseKey) { hasInheritance = true; } } if (hasInheritance) { Array.Sort(methodPairs); } for (index = 0; index < methodPairs.Length; index++) { SerializerPair pair = methodPairs[index]; ctx = new Compiler.CompilerContext(pair.SerializeBody, true, methodPairs); pair.Type.Serializer.EmitWrite(ctx, Compiler.Local.InputValue); ctx.Return(); ctx = new Compiler.CompilerContext(pair.DeserializeBody, true, methodPairs); pair.Type.Serializer.EmitRead(ctx, Compiler.Local.InputValue); ctx.LoadValue(Compiler.Local.InputValue); ctx.Return(); } FieldBuilder knownTypes = type.DefineField("knownTypes", typeof(Type[]), FieldAttributes.Private | FieldAttributes.InitOnly); ILGenerator il = Override(type, "GetKeyImpl"); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, knownTypes); il.Emit(OpCodes.Ldarg_1); // note that Array.IndexOf is not supported under CF il.EmitCall(OpCodes.Callvirt, typeof(IList).GetMethod( "IndexOf", new Type[] { typeof(object) }), null); if (hasInheritance) { il.DeclareLocal(typeof(int)); // loc-0 il.Emit(OpCodes.Dup); il.Emit(OpCodes.Stloc_0); BasicList getKeyLabels = new BasicList(); int lastKey = -1; for (int i = 0; i < methodPairs.Length; i++) { if (methodPairs[i].MetaKey == methodPairs[i].BaseKey) { break; } if (lastKey == methodPairs[i].BaseKey) { // add the last label again getKeyLabels.Add(getKeyLabels[getKeyLabels.Count - 1]); } else { // add a new unique label getKeyLabels.Add(il.DefineLabel()); lastKey = methodPairs[i].BaseKey; } } Label[] subtypeLabels = new Label[getKeyLabels.Count]; getKeyLabels.CopyTo(subtypeLabels, 0); il.Emit(OpCodes.Switch, subtypeLabels); il.Emit(OpCodes.Ldloc_0); // not a sub-type; use the original value il.Emit(OpCodes.Ret); lastKey = -1; // now output the different branches per sub-type (not derived type) for (int i = subtypeLabels.Length - 1; i >= 0; i--) { if (lastKey != methodPairs[i].BaseKey) { lastKey = methodPairs[i].BaseKey; // find the actual base-index for this base-key (i.e. the index of // the base-type) int keyIndex = -1; for (int j = subtypeLabels.Length; j < methodPairs.Length; j++) { if (methodPairs[j].BaseKey == lastKey && methodPairs[j].MetaKey == lastKey) { keyIndex = j; break; } } il.MarkLabel(subtypeLabels[i]); Compiler.CompilerContext.LoadValue(il, keyIndex); il.Emit(OpCodes.Ret); } } } else { il.Emit(OpCodes.Ret); } il = Override(type, "Serialize"); ctx = new Compiler.CompilerContext(il, false, methodPairs); // arg0 = this, arg1 = key, arg2=obj, arg3=dest Label[] jumpTable = new Label[types.Count]; for (int i = 0; i < jumpTable.Length; i++) { jumpTable[i] = il.DefineLabel(); } il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Switch, jumpTable); ctx.Return(); for (int i = 0; i < jumpTable.Length; i++) { SerializerPair pair = methodPairs[i]; il.MarkLabel(jumpTable[i]); il.Emit(OpCodes.Ldarg_2); ctx.CastFromObject(pair.Type.Type); il.Emit(OpCodes.Ldarg_3); il.EmitCall(OpCodes.Call, pair.Serialize, null); ctx.Return(); } il = Override(type, "Deserialize"); ctx = new Compiler.CompilerContext(il, false, methodPairs); // arg0 = this, arg1 = key, arg2=obj, arg3=source for (int i = 0; i < jumpTable.Length; i++) { jumpTable[i] = il.DefineLabel(); } il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Switch, jumpTable); ctx.LoadNullRef(); ctx.Return(); for (int i = 0; i < jumpTable.Length; i++) { SerializerPair pair = methodPairs[i]; il.MarkLabel(jumpTable[i]); Type keyType = pair.Type.Type; if (keyType.IsValueType) { il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Ldarg_3); il.EmitCall(OpCodes.Call, EmitBoxedSerializer(type, i, keyType, methodPairs), null); ctx.Return(); } else { il.Emit(OpCodes.Ldarg_2); ctx.CastFromObject(keyType); il.Emit(OpCodes.Ldarg_3); il.EmitCall(OpCodes.Call, pair.Deserialize, null); ctx.Return(); } } ConstructorBuilder ctor = type.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, Helpers.EmptyTypes); il = ctor.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Call, baseType.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance)[0]); il.Emit(OpCodes.Ldarg_0); Compiler.CompilerContext.LoadValue(il, types.Count); il.Emit(OpCodes.Newarr, typeof(Type)); index = 0; foreach (SerializerPair pair in methodPairs) { il.Emit(OpCodes.Dup); Compiler.CompilerContext.LoadValue(il, index); il.Emit(OpCodes.Ldtoken, pair.Type.Type); il.EmitCall(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle"), null); il.Emit(OpCodes.Stelem_Ref); index++; } il.Emit(OpCodes.Stfld, knownTypes); il.Emit(OpCodes.Ret); Type finalType = type.CreateType(); if (!Helpers.IsNullOrEmpty(path)) { asm.Save(path); Helpers.DebugWriteLine("Wrote dll:" + path); } return((TypeModel)Activator.CreateInstance(finalType)); }
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using (ctx.StartDebugBlockAuto(this, _property.Name)) { var g = ctx.G; bool canSet; SanityCheck(ctx.Model, _property, Tail, out canSet, ctx.NonPublic, ctx.AllowInternal(_property), true); using (Compiler.Local loc = ctx.GetLocalWithValueForEmitRead(this, valueFrom)) using (Compiler.Local oldVal = canSet ? new Compiler.Local(ctx, _property.PropertyType) : null) using (Compiler.Local newVal = new Compiler.Local(ctx, _property.PropertyType)) { Compiler.Local valueForTail = null; if (Tail.RequiresOldValue) { ctx.LoadAddress(loc, ExpectedType); ctx.LoadValue(_property); if (!oldVal.IsNullRef()) { if (Tail.RequiresOldValue) // we need it later { ctx.CopyValue(); } ctx.StoreValue(oldVal); } } if (Tail.RequiresOldValue) // !here! <-- we need value again { if (!Tail.EmitReadReturnsValue) { ctx.StoreValue(newVal); valueForTail = newVal; } // otherwise valueForTail = null (leave value on stack) } Tail.EmitRead(ctx, valueForTail); // otherwise newVal was passed to EmitRead so already has necessary data if (Tail.EmitReadReturnsValue) { ctx.StoreValue(newVal); } // check-condition: //(!Tail.RequiresOldValue // always set where can't check oldVal // // and if it's value type or nullable with changed null/not null or ref // || (Helpers.IsValueType(property.PropertyType) && oldVal != null && newVal != null) // || !ReferenceEquals(oldVal, newVal) // )) if (canSet) { bool check = Tail.RequiresOldValue; if (check) { var condition = !g.StaticFactory.InvokeReferenceEquals(oldVal, newVal); if (Helpers.IsValueType(_property.PropertyType)) { condition = (oldVal.AsOperand != null && newVal.AsOperand != null) || condition; } g.If(condition); } ctx.LoadAddress(loc, ExpectedType); ctx.LoadValue(newVal); if (_shadowSetter == null) { ctx.StoreValue(_property); } else { ctx.EmitCall(_shadowSetter); } if (check) { g.End(); } } if (EmitReadReturnsValue) { ctx.LoadValue(loc); } } } }
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 == ExpectedType) { 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.LoadReader(false); ctx.LoadValue(loc); ((IProtoTypeSerializer)serializer).EmitCreateInstance(ctx); ctx.EmitCall(typeof(ProtoReader).GetMethod("Merge", new[] { typeof(ProtoReader), typeof(object), typeof(object) })); ctx.Cast(expected); ctx.StoreValue(loc); // Merge always returns a value // nothing needs doing ctx.MarkLabel(allDone); } if (Helpers.IsValueType(serType)) { Compiler.CodeLabel initValue = ctx.DefineLabel(); Compiler.CodeLabel hasValue = ctx.DefineLabel(); using (Compiler.Local emptyValue = new Compiler.Local(ctx, serType)) { ctx.LoadValue(loc); ctx.BranchIfFalse(initValue, false); ctx.LoadValue(loc); ctx.CastFromObject(serType); ctx.Branch(hasValue, false); ctx.MarkLabel(initValue); ctx.InitLocal(serType, emptyValue); ctx.LoadValue(emptyValue); ctx.MarkLabel(hasValue); } } else { ctx.LoadValue(loc); ctx.Cast(serType); } serializer.EmitRead(ctx, null); } if (serializer.ReturnsValue) { // update the variable if (Helpers.IsValueType(serType)) { // but box it first in case of value type ctx.CastToObject(serType); } ctx.StoreValue(loc); } ctx.Branch(@continue, false); // "continue" }
void IProtoSerializer.EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { ctx.LoadValue(valueFrom); ctx.LoadReaderWriter(); ctx.EmitCall(typeof(ProtoReader).GetMethod("AppendBytes")); }
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using Compiler.Local loc = ctx.GetLocalWithValue(ExpectedType, valueFrom); if (Tail.RequiresOldValue) { ctx.LoadAddress(loc, ExpectedType); ctx.LoadValue(field); } // value is either now on the stack or not needed ctx.ReadNullCheckedTail(field.FieldType, Tail, null); // the field could be a backing field that needs to be raised back to // the property if we're doing a full compile MemberInfo member = field; ctx.CheckAccessibility(ref member); bool writeValue = member is FieldInfo; if (writeValue) { if (Tail.ReturnsValue) { var localType = PropertyDecorator.ChooseReadLocalType(field.FieldType, Tail.ExpectedType); using Compiler.Local newVal = new Compiler.Local(ctx, localType); ctx.StoreValue(newVal); if (field.FieldType.IsValueType) { ctx.LoadAddress(loc, ExpectedType); ctx.LoadValue(newVal); ctx.StoreValue(field); } else { Compiler.CodeLabel allDone = ctx.DefineLabel(); ctx.LoadValue(newVal); ctx.BranchIfFalse(allDone, true); // interpret null as "don't assign" ctx.LoadAddress(loc, ExpectedType); ctx.LoadValue(newVal); // cast if needed (this is mostly for ReadMap/ReadRepeated) if (!field.FieldType.IsValueType && !localType.IsValueType && !field.FieldType.IsAssignableFrom(localType)) { ctx.Cast(field.FieldType); } ctx.StoreValue(field); ctx.MarkLabel(allDone); } } } else { // can't use result if (Tail.ReturnsValue) { ctx.DiscardValue(); } } }
protected override void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { ctx.LoadAddress(valueFrom, ExpectedType); ctx.LoadValue(field); ctx.WriteNullCheckedTail(field.FieldType, Tail, null); }
protected override void EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { ctx.LoadValue(valueFrom); ctx.LoadValue(absoluteUriProperty); Tail.EmitWrite(ctx, null); }
} // 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); } }
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { using (ctx.StartDebugBlockAuto(this)) { Type voidType = ctx.MapType(typeof(void)); using (Compiler.Local value = ctx.GetLocalWithValueForEmitRead(this, valueFrom)) using (Compiler.Local builderInstance = new Compiler.Local(ctx, _builderFactory.ReturnType)) using (Compiler.Local trappedKey = new Compiler.Local(ctx, typeof(int))) { ctx.G.Assign(trappedKey, ctx.G.ReaderFunc.ReserveNoteObject_int()); ctx.EmitCall(_builderFactory); ctx.StoreValue(builderInstance); if (AppendToCollection) { Compiler.CodeLabel done = ctx.DefineLabel(); if (!ExpectedType.IsValueType) { ctx.LoadValue(value); ctx.BranchIfFalse(done, false); // old value null; nothing to add } PropertyInfo prop = Helpers.GetProperty(ExpectedType, "Length", false) ?? Helpers.GetProperty(ExpectedType, "Count", false); #if !NO_GENERICS if (prop == null) { prop = Helpers.GetProperty(ResolveIReadOnlyCollection(ExpectedType, Tail.ExpectedType), "Count", false); } #endif ctx.LoadAddress(value, value.Type); ctx.EmitCall(Helpers.GetGetMethod(prop, false, false)); ctx.BranchIfFalse(done, false); // old list is empty; nothing to add if (_addRange != null) { ctx.LoadValue(builderInstance); ctx.LoadValue(value); ctx.EmitCall(_addRange); if (_addRange.ReturnType != null && _add.ReturnType != voidType) { ctx.DiscardValue(); } } else { // loop and call Add repeatedly MethodInfo moveNext, current, getEnumerator = GetEnumeratorInfo(ctx.Model, out moveNext, out current); Helpers.DebugAssert(moveNext != null); Helpers.DebugAssert(current != null); Helpers.DebugAssert(getEnumerator != null); Type enumeratorType = getEnumerator.ReturnType; using (Compiler.Local iter = new Compiler.Local(ctx, enumeratorType)) { ctx.LoadAddress(value, ExpectedType); ctx.EmitCall(getEnumerator); ctx.StoreValue(iter); using (ctx.Using(iter)) { Compiler.CodeLabel body = ctx.DefineLabel(), next = ctx.DefineLabel(); ctx.Branch(next, false); ctx.MarkLabel(body); ctx.LoadAddress(builderInstance, builderInstance.Type); ctx.LoadAddress(iter, enumeratorType); ctx.EmitCall(current); ctx.EmitCall(_add); if (_add.ReturnType != null && _add.ReturnType != voidType) { ctx.DiscardValue(); } ctx.MarkLabel(@next); ctx.LoadAddress(iter, enumeratorType); ctx.EmitCall(moveNext); ctx.BranchIfTrue(body, false); } } } ctx.MarkLabel(done); } ListHelpers.EmitRead( ctx.G, null, null, o => { using (ctx.StartDebugBlockAuto(this, "add")) { ctx.LoadAddress(builderInstance, builderInstance.Type); ctx.LoadValue(o); ctx.EmitCall(_add); if (_add.ReturnType != null && _add.ReturnType != voidType) { ctx.DiscardValue(); } } }); ctx.LoadAddress(builderInstance, builderInstance.Type); ctx.EmitCall(_finish); if (ExpectedType != _finish.ReturnType) { ctx.Cast(ExpectedType); } ctx.StoreValue(value); ctx.G.Reader.NoteReservedTrappedObject(trappedKey, value); if (EmitReadReturnsValue) { ctx.LoadValue(value); } } } }
/// <summary> /// Fully compiles the current model into a static-compiled serialization dll /// (the serialization dll still requires protobuf-net for support services). /// </summary> /// <remarks>A full compilation is restricted to accessing public types / members</remarks> /// <returns>An instance of the newly created compiled type-model</returns> public TypeModel Compile(CompilerOptions options) { if (options == null) throw new ArgumentNullException("options"); string typeName = options.TypeName; string path = options.OutputPath; BuildAllSerializers(); Freeze(); bool save = !Helpers.IsNullOrEmpty(path); if (Helpers.IsNullOrEmpty(typeName)) { if (save) throw new ArgumentNullException("typeName"); typeName = Guid.NewGuid().ToString(); } string assemblyName, moduleName; if(path == null) { assemblyName = typeName; moduleName = assemblyName + ".dll"; } else { assemblyName = new System.IO.FileInfo(System.IO.Path.GetFileNameWithoutExtension(path)).Name; moduleName = assemblyName + System.IO.Path.GetExtension(path); } #if FEAT_IKVM IKVM.Reflection.AssemblyName an = new IKVM.Reflection.AssemblyName(); an.Name = assemblyName; AssemblyBuilder asm = universe.DefineDynamicAssembly(an, AssemblyBuilderAccess.Save); if(!Helpers.IsNullOrEmpty(options.ImageRuntimeVersion) && options.MetaDataVersion != 0) { asm.__SetImageRuntimeVersion(options.ImageRuntimeVersion, options.MetaDataVersion); } ModuleBuilder module = asm.DefineDynamicModule(moduleName, path); #else AssemblyName an = new AssemblyName(); an.Name = assemblyName; AssemblyBuilder asm = AppDomain.CurrentDomain.DefineDynamicAssembly(an, (save ? AssemblyBuilderAccess.RunAndSave : AssemblyBuilderAccess.Run) ); ModuleBuilder module = save ? asm.DefineDynamicModule(moduleName, path) : asm.DefineDynamicModule(moduleName); #endif if (!Helpers.IsNullOrEmpty(options.TargetFrameworkName)) { // get [TargetFramework] from mscorlib/equivalent and burn into the new assembly Type versionAttribType = null; try { // this is best-endeavours only versionAttribType = GetType("System.Runtime.Versioning.TargetFrameworkAttribute", MapType(typeof(string)).Assembly); } catch { /* don't stress */ } if (versionAttribType != null) { PropertyInfo[] props; object[] propValues; if (Helpers.IsNullOrEmpty(options.TargetFrameworkDisplayName)) { props = new PropertyInfo[0]; propValues = new object[0]; } else { props = new PropertyInfo[1] { versionAttribType.GetProperty("FrameworkDisplayName") }; propValues = new object[1] { options.TargetFrameworkDisplayName }; } CustomAttributeBuilder builder = new CustomAttributeBuilder( versionAttribType.GetConstructor(new Type[] { MapType(typeof(string)) }), new object[] { options.TargetFrameworkName }, props, propValues); asm.SetCustomAttribute(builder); } } // copy assembly:InternalsVisibleTo Type internalsVisibleToAttribType = null; #if !FX11 try { internalsVisibleToAttribType = MapType(typeof(System.Runtime.CompilerServices.InternalsVisibleToAttribute)); } catch { /* best endeavors only */ } #endif if (internalsVisibleToAttribType != null) { BasicList internalAssemblies = new BasicList(), consideredAssemblies = new BasicList(); foreach (MetaType metaType in types) { Assembly assembly = metaType.Type.Assembly; if (consideredAssemblies.IndexOfReference(assembly) >= 0) continue; consideredAssemblies.Add(assembly); AttributeMap[] assemblyAttribsMap = AttributeMap.Create(this, assembly); for (int i = 0; i < assemblyAttribsMap.Length; i++) { if (assemblyAttribsMap[i].AttributeType != internalsVisibleToAttribType) continue; object privelegedAssemblyObj; assemblyAttribsMap[i].TryGet("AssemblyName", out privelegedAssemblyObj); string privelegedAssemblyName = privelegedAssemblyObj as string; if (privelegedAssemblyName == assemblyName || Helpers.IsNullOrEmpty(privelegedAssemblyName)) continue; // ignore if (internalAssemblies.IndexOf(new StringFinder(privelegedAssemblyName)) >= 0) continue; // seen it before internalAssemblies.Add(privelegedAssemblyName); CustomAttributeBuilder builder = new CustomAttributeBuilder( internalsVisibleToAttribType.GetConstructor(new Type[] { MapType(typeof(string)) }), new object[] { privelegedAssemblyName }); asm.SetCustomAttribute(builder); } } } Type baseType = MapType(typeof(TypeModel)); TypeAttributes typeAttributes = (baseType.Attributes & ~TypeAttributes.Abstract) | TypeAttributes.Sealed; if(options.Accessibility == Accessibility.Internal) { typeAttributes &= ~TypeAttributes.Public; } TypeBuilder type = module.DefineType(typeName, typeAttributes, baseType); Compiler.CompilerContext ctx; int index = 0; bool hasInheritance = false; SerializerPair[] methodPairs = new SerializerPair[types.Count]; foreach (MetaType metaType in types) { MethodBuilder writeMethod = type.DefineMethod("Write" #if DEBUG + metaType.Type.Name #endif , MethodAttributes.Private | MethodAttributes.Static, CallingConventions.Standard, MapType(typeof(void)), new Type[] { metaType.Type, MapType(typeof(ProtoWriter)) }); MethodBuilder readMethod = type.DefineMethod("Read" #if DEBUG + metaType.Type.Name #endif , MethodAttributes.Private | MethodAttributes.Static, CallingConventions.Standard, metaType.Type, new Type[] { metaType.Type, MapType(typeof(ProtoReader)) }); SerializerPair pair = new SerializerPair( GetKey(metaType.Type, true, false), GetKey(metaType.Type, true, true), metaType, writeMethod, readMethod, writeMethod.GetILGenerator(), readMethod.GetILGenerator()); methodPairs[index++] = pair; if (pair.MetaKey != pair.BaseKey) hasInheritance = true; } if (hasInheritance) { Array.Sort(methodPairs); } Compiler.CompilerContext.ILVersion ilVersion = Compiler.CompilerContext.ILVersion.Net2; if (options.MetaDataVersion == 0x10000) { ilVersion = Compiler.CompilerContext.ILVersion.Net1; // old-school! } for(index = 0; index < methodPairs.Length ; index++) { SerializerPair pair = methodPairs[index]; ctx = new Compiler.CompilerContext(pair.SerializeBody, true, true, methodPairs, this, ilVersion, assemblyName); ctx.CheckAccessibility(pair.Deserialize.ReturnType); pair.Type.Serializer.EmitWrite(ctx, Compiler.Local.InputValue); ctx.Return(); ctx = new Compiler.CompilerContext(pair.DeserializeBody, true, false, methodPairs, this, ilVersion, assemblyName); pair.Type.Serializer.EmitRead(ctx, Compiler.Local.InputValue); if (!pair.Type.Serializer.ReturnsValue) { ctx.LoadValue(Compiler.Local.InputValue); } ctx.Return(); } ILGenerator il = Override(type, "GetKeyImpl"); int knownTypesCategory; FieldBuilder knownTypes; Type knownTypesLookupType; const int KnownTypes_Array = 1, KnownTypes_Dictionary = 2, KnownTypes_Hashtable = 3, KnownTypes_ArrayCutoff = 20; if (types.Count <= KnownTypes_ArrayCutoff) { knownTypesCategory = KnownTypes_Array; knownTypesLookupType = MapType(typeof(System.Type[]), true); } else { #if NO_GENERICS knownTypesLookupType = null; #else knownTypesLookupType = MapType(typeof(System.Collections.Generic.Dictionary<System.Type, int>), false); #endif if (knownTypesLookupType == null) { knownTypesLookupType = MapType(typeof(Hashtable), true); knownTypesCategory = KnownTypes_Hashtable; } else { knownTypesCategory = KnownTypes_Dictionary; } } knownTypes = type.DefineField("knownTypes", knownTypesLookupType, FieldAttributes.Private | FieldAttributes.InitOnly | FieldAttributes.Static); switch(knownTypesCategory) { case KnownTypes_Array: { il.Emit(OpCodes.Ldsfld, knownTypes); il.Emit(OpCodes.Ldarg_1); // note that Array.IndexOf is not supported under CF il.EmitCall(OpCodes.Callvirt, MapType(typeof(IList)).GetMethod( "IndexOf", new Type[] { MapType(typeof(object)) }), null); if (hasInheritance) { il.DeclareLocal(MapType(typeof(int))); // loc-0 il.Emit(OpCodes.Dup); il.Emit(OpCodes.Stloc_0); BasicList getKeyLabels = new BasicList(); int lastKey = -1; for (int i = 0; i < methodPairs.Length; i++) { if (methodPairs[i].MetaKey == methodPairs[i].BaseKey) break; if (lastKey == methodPairs[i].BaseKey) { // add the last label again getKeyLabels.Add(getKeyLabels[getKeyLabels.Count - 1]); } else { // add a new unique label getKeyLabels.Add(il.DefineLabel()); lastKey = methodPairs[i].BaseKey; } } Label[] subtypeLabels = new Label[getKeyLabels.Count]; getKeyLabels.CopyTo(subtypeLabels, 0); il.Emit(OpCodes.Switch, subtypeLabels); il.Emit(OpCodes.Ldloc_0); // not a sub-type; use the original value il.Emit(OpCodes.Ret); lastKey = -1; // now output the different branches per sub-type (not derived type) for (int i = subtypeLabels.Length - 1; i >= 0; i--) { if (lastKey != methodPairs[i].BaseKey) { lastKey = methodPairs[i].BaseKey; // find the actual base-index for this base-key (i.e. the index of // the base-type) int keyIndex = -1; for (int j = subtypeLabels.Length; j < methodPairs.Length; j++) { if (methodPairs[j].BaseKey == lastKey && methodPairs[j].MetaKey == lastKey) { keyIndex = j; break; } } il.MarkLabel(subtypeLabels[i]); Compiler.CompilerContext.LoadValue(il, keyIndex); il.Emit(OpCodes.Ret); } } } else { il.Emit(OpCodes.Ret); } } break; case KnownTypes_Dictionary: { LocalBuilder result = il.DeclareLocal(MapType(typeof(int))); Label otherwise = il.DefineLabel(); il.Emit(OpCodes.Ldsfld, knownTypes); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldloca_S, result); il.EmitCall(OpCodes.Callvirt, knownTypesLookupType.GetMethod("TryGetValue", BindingFlags.Instance | BindingFlags.Public), null); il.Emit(OpCodes.Brfalse_S, otherwise); il.Emit(OpCodes.Ldloc_S, result); il.Emit(OpCodes.Ret); il.MarkLabel(otherwise); il.Emit(OpCodes.Ldc_I4_M1); il.Emit(OpCodes.Ret); } break; case KnownTypes_Hashtable: { Label otherwise = il.DefineLabel(); il.Emit(OpCodes.Ldsfld, knownTypes); il.Emit(OpCodes.Ldarg_1); il.EmitCall(OpCodes.Callvirt, knownTypesLookupType.GetProperty("Item").GetGetMethod(), null); il.Emit(OpCodes.Dup); il.Emit(OpCodes.Brfalse_S, otherwise); #if FX11 il.Emit(OpCodes.Unbox, MapType(typeof(int))); il.Emit(OpCodes.Ldobj, MapType(typeof(int))); #else if (ilVersion == Compiler.CompilerContext.ILVersion.Net1) { il.Emit(OpCodes.Unbox, MapType(typeof(int))); il.Emit(OpCodes.Ldobj, MapType(typeof(int))); } else { il.Emit(OpCodes.Unbox_Any, MapType(typeof(int))); } #endif il.Emit(OpCodes.Ret); il.MarkLabel(otherwise); il.Emit(OpCodes.Pop); il.Emit(OpCodes.Ldc_I4_M1); il.Emit(OpCodes.Ret); } break; default: throw new InvalidOperationException(); } il = Override(type, "Serialize"); ctx = new Compiler.CompilerContext(il, false, true, methodPairs, this, ilVersion, assemblyName); // arg0 = this, arg1 = key, arg2=obj, arg3=dest Label[] jumpTable = new Label[types.Count]; for (int i = 0; i < jumpTable.Length; i++) { jumpTable[i] = il.DefineLabel(); } il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Switch, jumpTable); ctx.Return(); for (int i = 0; i < jumpTable.Length; i++) { SerializerPair pair = methodPairs[i]; il.MarkLabel(jumpTable[i]); il.Emit(OpCodes.Ldarg_2); ctx.CastFromObject(pair.Type.Type); il.Emit(OpCodes.Ldarg_3); il.EmitCall(OpCodes.Call, pair.Serialize, null); ctx.Return(); } il = Override(type, "Deserialize"); ctx = new Compiler.CompilerContext(il, false, false, methodPairs, this, ilVersion, assemblyName); // arg0 = this, arg1 = key, arg2=obj, arg3=source for (int i = 0; i < jumpTable.Length; i++) { jumpTable[i] = il.DefineLabel(); } il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Switch, jumpTable); ctx.LoadNullRef(); ctx.Return(); for (int i = 0; i < jumpTable.Length; i++) { SerializerPair pair = methodPairs[i]; il.MarkLabel(jumpTable[i]); Type keyType = pair.Type.Type; if (keyType.IsValueType) { il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Ldarg_3); il.EmitCall(OpCodes.Call, EmitBoxedSerializer(type, i, keyType, methodPairs, this, ilVersion, assemblyName), null); ctx.Return(); } else { il.Emit(OpCodes.Ldarg_2); ctx.CastFromObject(keyType); il.Emit(OpCodes.Ldarg_3); il.EmitCall(OpCodes.Call, pair.Deserialize, null); ctx.Return(); } } type.DefineDefaultConstructor(MethodAttributes.Public); il = type.DefineTypeInitializer().GetILGenerator(); switch (knownTypesCategory) { case KnownTypes_Array: { Compiler.CompilerContext.LoadValue(il, types.Count); il.Emit(OpCodes.Newarr, ctx.MapType(typeof(System.Type))); index = 0; foreach (SerializerPair pair in methodPairs) { il.Emit(OpCodes.Dup); Compiler.CompilerContext.LoadValue(il, index); il.Emit(OpCodes.Ldtoken, pair.Type.Type); il.EmitCall(OpCodes.Call, ctx.MapType(typeof(System.Type)).GetMethod("GetTypeFromHandle"), null); il.Emit(OpCodes.Stelem_Ref); index++; } il.Emit(OpCodes.Stsfld, knownTypes); il.Emit(OpCodes.Ret); } break; case KnownTypes_Dictionary: { Compiler.CompilerContext.LoadValue(il, types.Count); LocalBuilder loc = il.DeclareLocal(knownTypesLookupType); il.Emit(OpCodes.Newobj, knownTypesLookupType.GetConstructor(new Type[] { MapType(typeof(int)) })); il.Emit(OpCodes.Stsfld, knownTypes); int typeIndex = 0; foreach (SerializerPair pair in methodPairs) { il.Emit(OpCodes.Ldsfld, knownTypes); il.Emit(OpCodes.Ldtoken, pair.Type.Type); il.EmitCall(OpCodes.Call, ctx.MapType(typeof(System.Type)).GetMethod("GetTypeFromHandle"), null); int keyIndex = typeIndex++, lastKey = pair.BaseKey; if (lastKey != pair.MetaKey) // not a base-type; need to give the index of the base-type { keyIndex = -1; // assume epic fail for (int j = 0; j < methodPairs.Length; j++) { if (methodPairs[j].BaseKey == lastKey && methodPairs[j].MetaKey == lastKey) { keyIndex = j; break; } } } Compiler.CompilerContext.LoadValue(il, keyIndex); il.EmitCall(OpCodes.Callvirt, knownTypesLookupType.GetMethod("Add", new Type[] { MapType(typeof(System.Type)), MapType(typeof(int)) }), null); } il.Emit(OpCodes.Ret); } break; case KnownTypes_Hashtable: { Compiler.CompilerContext.LoadValue(il, types.Count); il.Emit(OpCodes.Newobj, knownTypesLookupType.GetConstructor(new Type[] { MapType(typeof(int)) })); il.Emit(OpCodes.Stsfld, knownTypes); int typeIndex = 0; foreach (SerializerPair pair in methodPairs) { il.Emit(OpCodes.Ldsfld, knownTypes); il.Emit(OpCodes.Ldtoken, pair.Type.Type); il.EmitCall(OpCodes.Call, ctx.MapType(typeof(System.Type)).GetMethod("GetTypeFromHandle"), null); int keyIndex = typeIndex++, lastKey = pair.BaseKey; if (lastKey != pair.MetaKey) // not a base-type; need to give the index of the base-type { keyIndex = -1; // assume epic fail for (int j = 0; j < methodPairs.Length; j++) { if (methodPairs[j].BaseKey == lastKey && methodPairs[j].MetaKey == lastKey) { keyIndex = j; break; } } } Compiler.CompilerContext.LoadValue(il, keyIndex); il.Emit(OpCodes.Box, MapType(typeof(int))); il.EmitCall(OpCodes.Callvirt, knownTypesLookupType.GetMethod("Add", new Type[] { MapType(typeof(object)), MapType(typeof(object)) }), null); } il.Emit(OpCodes.Ret); } break; default: throw new InvalidOperationException(); } Type finalType = type.CreateType(); if(!Helpers.IsNullOrEmpty(path)) { asm.Save(path); Helpers.DebugWriteLine("Wrote dll:" + path); } #if FEAT_IKVM return null; #else return (TypeModel)Activator.CreateInstance(finalType); #endif }
void IProtoSerializer.EmitWrite(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { ctx.LoadValue(valueFrom); ctx.EmitCall(toTail); rootTail.EmitWrite(ctx, null); }
private static MethodBuilder EmitBoxedSerializer(TypeBuilder type, int i, Type valueType, SerializerPair[] methodPairs) { MethodInfo dedicated = methodPairs[i].Deserialize; MethodBuilder boxedSerializer = type.DefineMethod("_" + i, MethodAttributes.Static, CallingConventions.Standard, typeof(object), new Type[] { typeof(object), typeof(ProtoReader) }); Compiler.CompilerContext ctx = new Compiler.CompilerContext(boxedSerializer.GetILGenerator(), true, false, methodPairs); ctx.LoadValue(Compiler.Local.InputValue); Compiler.CodeLabel @null = ctx.DefineLabel(); ctx.BranchIfFalse(@null, true); ctx.LoadValue(Compiler.Local.InputValue); ctx.CastFromObject(valueType); ctx.LoadReaderWriter(); ctx.EmitCall(dedicated); ctx.CastToObject(valueType); ctx.Return(); ctx.MarkLabel(@null); using(Compiler.Local typedVal = new Compiler.Local(ctx, valueType)) { // create a new valueType ctx.LoadAddress(typedVal, valueType); ctx.EmitCtor(valueType); ctx.LoadValue(typedVal); ctx.LoadReaderWriter(); ctx.EmitCall(dedicated); ctx.CastToObject(valueType); ctx.Return(); } return boxedSerializer; }
protected override void EmitRead(Compiler.CompilerContext ctx, Compiler.Local valueFrom) { bool writeValue; SanityCheck(ctx.Model, property, Tail, out writeValue, ctx.NonPublic, ctx.AllowInternal(property)); if (ExpectedType.IsValueType && valueFrom == null) { throw new InvalidOperationException("Attempt to mutate struct on the head of the stack; changes would be lost"); } ctx.LoadAddress(valueFrom, ExpectedType); // stack is: old-addr if (writeValue && Tail.RequiresOldValue) { // need to read and write ctx.CopyValue(); } // stack is: [old-addr]|old-addr if (Tail.RequiresOldValue) { ctx.LoadValue(property); // stack is: [old-addr]|old-value } Type propertyType = property.PropertyType; ctx.ReadNullCheckedTail(propertyType, Tail, null); // stack is [old-addr]|[new-value] if (writeValue) { // stack is old-addr|new-value Compiler.CodeLabel @skip = new Compiler.CodeLabel(), allDone = new Compiler.CodeLabel(); // <=== default structs if (!propertyType.IsValueType) { // if the tail returns a null, intepret that as *no assign* ctx.CopyValue(); // old-addr|new-value|new-value @skip = ctx.DefineLabel(); allDone = ctx.DefineLabel(); ctx.BranchIfFalse(@skip, true); // old-addr|new-value } if (shadowSetter == null) { ctx.StoreValue(property); } else { ctx.EmitCall(shadowSetter); } if (!propertyType.IsValueType) { ctx.Branch(allDone, true); ctx.MarkLabel(@skip); // old-addr|new-value ctx.DiscardValue(); ctx.DiscardValue(); ctx.MarkLabel(allDone); } } else { // don't want return value; drop it if anything there // stack is [new-value] if (Tail.ReturnsValue) { ctx.DiscardValue(); } } }
private static MethodBuilder EmitBoxedSerializer(TypeBuilder type, int i, Type valueType, SerializerPair[] methodPairs, TypeModel model, Compiler.CompilerContext.ILVersion ilVersion, string assemblyName) { MethodInfo dedicated = methodPairs[i].Deserialize; MethodBuilder boxedSerializer = type.DefineMethod("_" + i.ToString(), MethodAttributes.Static, CallingConventions.Standard, model.MapType(typeof(object)), new Type[] { model.MapType(typeof(object)), model.MapType(typeof(ProtoReader)) }); Compiler.CompilerContext ctx = new Compiler.CompilerContext(boxedSerializer.GetILGenerator(), true, false, methodPairs, model, ilVersion, assemblyName, model.MapType(typeof(object))); ctx.LoadValue(ctx.InputValue); Compiler.CodeLabel @null = ctx.DefineLabel(); ctx.BranchIfFalse(@null, true); Type mappedValueType = valueType; ctx.LoadValue(ctx.InputValue); ctx.CastFromObject(mappedValueType); ctx.LoadReaderWriter(); ctx.EmitCall(dedicated); ctx.CastToObject(mappedValueType); ctx.Return(); ctx.MarkLabel(@null); using (Compiler.Local typedVal = new Compiler.Local(ctx, mappedValueType)) { // create a new valueType ctx.LoadAddress(typedVal, mappedValueType); ctx.EmitCtor(mappedValueType); ctx.LoadValue(typedVal); ctx.LoadReaderWriter(); ctx.EmitCall(dedicated); ctx.CastToObject(mappedValueType); ctx.Return(); } return boxedSerializer; }
bool EmitDedicatedMethod(Compiler.CompilerContext ctx, Compiler.Local valueFrom, bool read) { #if SILVERLIGHT return(false); #else MethodBuilder method = ctx.GetDedicatedMethod(key, read); if (method == null) { return(false); } using (Compiler.Local token = new ProtoBuf.Compiler.Local(ctx, ctx.MapType(typeof(SubItemToken)))) { Type rwType = ctx.MapType(read ? typeof(ProtoReader) : typeof(ProtoWriter)); ctx.LoadValue(valueFrom); if (!read) // write requires the object for StartSubItem; read doesn't { // (if recursion-check is disabled [subtypes] then null is fine too) if (type.IsValueType || !recursionCheck) { ctx.LoadNullRef(); } else { ctx.CopyValue(); } } ctx.LoadReaderWriter(); ctx.EmitCall(rwType.GetMethod("StartSubItem")); ctx.StoreValue(token); // note: value already on the stack ctx.LoadReaderWriter(); ctx.EmitCall(method); // handle inheritance (we will be calling the *base* version of things, // but we expect Read to return the "type" type) if (read && type != method.ReturnType) { ctx.Cast(this.type); } ctx.LoadValue(token); ctx.LoadReaderWriter(); ctx.EmitCall(rwType.GetMethod("EndSubItem")); } return(true); #endif }