private static Delegate CreateWriterMethod(TypeImpl type) { var methodName = $"{ type }_writer"; var dynamicMethod = new DynamicMethod(methodName, typeof(void), new[] { typeof(Stream), type.Type }, typeof(SerializerBuilder), true); var il = dynamicMethod.GetILGenerator(); if (!type.TypeInfo.IsArray) { GenerateObjectWriter(il, type); } else { GenerateArrayWriter(il, type); } return(dynamicMethod.CreateDelegate(typeof(Writer <>).MakeGenericType(type.Type))); }
public static Reader <TTo> CastReader <TTo>(Delegate reader, TypeImpl from) { // Check if equals var to = new TypeImpl(typeof(TTo)); if (to == from) { return((Reader <TTo>)reader); } // Value types not supported covariance/contravariance if (!from.TypeInfo.IsValueType && to.TypeInfo.IsAssignableFrom(from.Type)) { return((Reader <TTo>)reader); } // Check cache var key = new TypePair(from, to); if (CachedReaders.TryGetValue(key, out Delegate castedReader)) { return((Reader <TTo>)castedReader); } // Create var closedAdapter = typeof(ReaderMethodAdapter <,>).MakeGenericType(from.Type, to.Type); var write = closedAdapter.GetTypeInfo().GetMethod(nameof(ReaderMethodAdapter <object, object> .Read)); var adapter = Activator.CreateInstance(closedAdapter, reader); castedReader = write.CreateDelegate(typeof(Reader <TTo>), adapter); // Cache CachedReaders.TryAdd(key, castedReader); // Result return((Reader <TTo>)castedReader); }
public TypePair(TypeImpl from, TypeImpl to) { _from = from; _to = to; }
private static Dictionary <string, BinField> GetFieldsMap(TypeImpl type) => BinField.Get(type.Type).ToDictionary(GetFieldId, GetField);
private static void GenerateArrayReader(ILGenerator il, TypeImpl type) { var elementType = type.TypeInfo.GetElementType(); var addRef = typeof(RefReaderWatcher) .GetTypeInfo() .GetMethod(nameof(RefReaderWatcher.AddRef), BindingFlags.Public | BindingFlags.Static) .MakeGenericMethod(type.Type); var tryGetRef = typeof(RefReaderWatcher) .GetTypeInfo() .GetMethod(nameof(RefReaderWatcher.TryGetRef), BindingFlags.Public | BindingFlags.Static) .MakeGenericMethod(type.Type); var deserialize = typeof(BinSerializer <>) .MakeGenericType(elementType) .GetTypeInfo() .GetMethod(nameof(BinSerializer <object> .Deserialize), BindingFlags.Static | BindingFlags.Public); // array type id already was readed il.DeclareLocal(typeof(int)); // Ref id il.DeclareLocal(typeof(int)); // Array length il.DeclareLocal(typeof(int)); // Current index il.DeclareLocal(type.Type); // Result array var loopLabel = il.DefineLabel(); var resultLabel = il.DefineLabel(); var tryLoadReferenceLabel = il.DefineLabel(); // Read reference id il.Emit(OpCodes.Ldarg_0); // Load stream il.Emit(OpCodes.Call, SerializerTypes.TryGetStreamReader(typeof(int))); // Read ref id il.Emit(OpCodes.Dup); // Duplicate ref id il.Emit(OpCodes.Stloc_0); // Set ref id to local // Check if result null il.Emit(OpCodes.Brtrue, tryLoadReferenceLabel); // Check if null was written // Null was written (return null) il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Stloc_3); // Set result to null il.Emit(OpCodes.Br, resultLabel); // Jump to result // Try get readed reference il.MarkLabel(tryLoadReferenceLabel); il.Emit(OpCodes.Ldloc_0); // Load reference id il.Emit(OpCodes.Ldloca_S, (byte)3); // Load address of result il.Emit(OpCodes.Call, tryGetRef); il.Emit(OpCodes.Brtrue, resultLabel); // Jump to result if reference already exist // Read array length il.Emit(OpCodes.Ldarg_0); // Load stream il.Emit(OpCodes.Call, SerializerTypes.TryGetStreamReader(typeof(int))); // Read length il.Emit(OpCodes.Stloc_1); // Set array length // Set current index il.Emit(OpCodes.Ldc_I4_0); // Load zero il.Emit(OpCodes.Stloc_2); // Set current index // Create array il.Emit(OpCodes.Ldloc_1); // Load array length il.Emit(OpCodes.Newarr, elementType); // Create array il.Emit(OpCodes.Stloc_3); // Set array to local // Set ref id il.Emit(OpCodes.Ldloc_0); // Load reference id il.Emit(OpCodes.Ldloc_3); // Load result object reference il.Emit(OpCodes.Call, addRef); // Loop il.MarkLabel(loopLabel); // Check array end il.Emit(OpCodes.Ldloc_1); // Load length il.Emit(OpCodes.Ldloc_2); // Load index il.Emit(OpCodes.Beq, resultLabel); // Prepare set element il.Emit(OpCodes.Ldloc_3); // Load array il.Emit(OpCodes.Ldloc_2); // Load index // Read value il.Emit(OpCodes.Ldarg_0); // Load stream il.Emit(OpCodes.Call, deserialize); // Deserialize element // Set element il.Emit(OpCodes.Stelem, elementType); // Inrement index il.Emit(OpCodes.Ldloc_2); // Load index il.Emit(OpCodes.Ldc_I4_1); // Load one il.Emit(OpCodes.Add); // Sum il.Emit(OpCodes.Stloc_2); // Set index // Go to loop start il.Emit(OpCodes.Br, loopLabel); il.MarkLabel(resultLabel); il.Emit(OpCodes.Ldloc_3); // Load result array il.Emit(OpCodes.Ret); }
private static void GenerateObjectWriter(ILGenerator il, TypeImpl type) { var getRefId = typeof(RefWriterWatcher) .GetTypeInfo() .GetMethod(nameof(RefWriterWatcher.GetRefId), BindingFlags.Public | BindingFlags.Static); var onSerializing = typeof(IBinSerializable).GetMethod(nameof(IBinSerializable.OnSerializing)); il.DeclareLocal(typeof(bool)); il.DeclareLocal(typeof(SerializationInfo)); var skipTypeLabel = il.DefineLabel(); BSDebug.TraceStart(il, "Write " + type.TypeInfo.Name); // Invoke deserialzation callback if (typeof(IBinSerializable).IsAssignableFrom(type.Type)) { il.Emit(OpCodes.Ldloca_S, (byte)1); // Load result local address il.Emit(OpCodes.Initobj, typeof(SerializationInfo)); il.Emit(OpCodes.Ldarg_1); // Load serializing object il.Emit(OpCodes.Ldloc_1); // Load created SerializationInfo il.Emit(OpCodes.Callvirt, onSerializing); // Call onSerializing } // Write type il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldstr, SerializerTypes.GetTypeId(type)); il.Emit(OpCodes.Call, SerializerTypes.TryGetStreamWriter(typeof(string))); if (!type.TypeInfo.IsValueType) { // Write refId il.Emit(OpCodes.Ldarg_0); // Load stream il.Emit(OpCodes.Ldarg_1); // Load obj il.Emit(OpCodes.Ldloca_S, (byte)0); // Load address of bool local il.Emit(OpCodes.Call, getRefId); // Obj => RefId il.Emit(OpCodes.Call, SerializerTypes.TryGetStreamWriter(typeof(int))); // Check case when reference type already serialized. // If null be returned then created flag be zero too and serialization will be skipped. il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Brfalse, skipTypeLabel); } // Write type version var version = SerializerTypes.GetVersion(type); il.Emit(OpCodes.Ldarg_0); // Load stream il.Emit(OpCodes.Ldc_I4, version); // Load version il.Emit(OpCodes.Call, SerializerTypes.TryGetStreamWriter(typeof(int))); // If writer exist then just write var writer = SerializerTypes.TryGetStreamWriter(type) ?? SerializerTypes.TryGetTypeWriter(type); if (writer != null) { il.Emit(OpCodes.Ldarg_0); // Load stream il.Emit(OpCodes.Ldarg_1); // Load obj il.Emit(OpCodes.Call, writer); // Write to stream } else { foreach (var binField in BinField.Get(type.Type)) { var skipFieldLabel = il.DefineLabel(); // If field is null then skip it if (!binField.IsValueType) { il.Emit(OpCodes.Ldarg_1); // Load object binField.EmitRead(il); // Read field form object il.Emit(OpCodes.Brfalse, skipFieldLabel); // Skip if field is null } // Write field id il.Emit(OpCodes.Ldarg_0); // Load stream il.Emit(OpCodes.Ldstr, binField.Id); // Load field id il.Emit(OpCodes.Call, SerializerTypes.TryGetStreamWriter(typeof(string))); var serialize = typeof(BinSerializer <>) .MakeGenericType(binField.Type) .GetTypeInfo() .GetMethod(nameof(BinSerializer <object> .Serialize), BindingFlags.Static | BindingFlags.Public); // Write field il.Emit(OpCodes.Ldarg_0); // Load stream il.Emit(OpCodes.Ldarg_1); // Load object binField.EmitRead(il); // Read field from object il.Emit(OpCodes.Call, serialize); // Write field value // Skip field label il.MarkLabel(skipFieldLabel); } // Write end id il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldstr, SerializerTypes.TypeEndToken); il.Emit(OpCodes.Call, SerializerTypes.TryGetStreamWriter(typeof(string))); } // Skip type label il.MarkLabel(skipTypeLabel); BSDebug.TraceEnd(il, "Write " + type.TypeInfo.Name); // End il.Emit(OpCodes.Ret); }
private static void GenerateObjectReader(ILGenerator il, TypeImpl type) { var addRef = typeof(RefReaderWatcher) .GetTypeInfo() .GetMethod(nameof(RefReaderWatcher.AddRef), BindingFlags.Public | BindingFlags.Static) .MakeGenericMethod(type.Type); var tryGetRef = typeof(RefReaderWatcher) .GetTypeInfo() .GetMethod(nameof(RefReaderWatcher.TryGetRef), BindingFlags.Public | BindingFlags.Static) .MakeGenericMethod(type.Type); var getTypeFromHandle = typeof(Type) .GetTypeInfo() .GetMethod(nameof(Type.GetTypeFromHandle), BindingFlags.Public | BindingFlags.Static); var defaultCtor = type.TypeInfo.GetConstructor(Type.EmptyTypes); var getUninitializedObject = typeof(FormatterServices) .GetTypeInfo() .GetMethod(nameof(FormatterServices.GetUninitializedObject), BindingFlags.Public | BindingFlags.Static); var dynamicObjectRead = typeof(DynamicObjectReader <>) .MakeGenericType(type.Type) .GetTypeInfo() .GetMethod(nameof(DynamicObjectReader <object> .Read), BindingFlags.Public | BindingFlags.Static); var stringEquals = typeof(string) .GetTypeInfo() .GetMethod(nameof(string.Equals), new[] { typeof(string), typeof(string), typeof(StringComparison) }); var onDeserialized = typeof(IBinSerializable).GetMethod(nameof(IBinSerializable.OnDeserialized)); il.DeclareLocal(typeof(int)); // ref if il.DeclareLocal(type.Type); // readed object il.DeclareLocal(typeof(string)); // last readed field id il.DeclareLocal(typeof(int)); // readed object version il.DeclareLocal(typeof(DeserializationInfo)); // info var resultLabel = il.DefineLabel(); var readFastLabel = il.DefineLabel(); // Process reference id if (!type.TypeInfo.IsValueType) { var tryLoadReferenceLabel = il.DefineLabel(); // Read reference id il.Emit(OpCodes.Ldarg_0); // Load stream il.Emit(OpCodes.Call, SerializerTypes.TryGetStreamReader(typeof(int))); // Read ref id il.Emit(OpCodes.Dup); // Duplicate ref id il.Emit(OpCodes.Stloc_0); // Set ref id to local // Check if result null il.Emit(OpCodes.Brtrue, tryLoadReferenceLabel); // Check if null was written // Null was written (return null) il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Stloc_1); // Set result to null il.Emit(OpCodes.Br, resultLabel); // Jump to result // Try get readed reference il.MarkLabel(tryLoadReferenceLabel); il.Emit(OpCodes.Ldloc_0); // Load reference id il.Emit(OpCodes.Ldloca_S, (byte)1); // Load address of result il.Emit(OpCodes.Call, tryGetRef); il.Emit(OpCodes.Brtrue, resultLabel); // Jump to result if reference already exist } // Read version il.Emit(OpCodes.Ldarg_0); // Load stream il.Emit(OpCodes.Call, SerializerTypes.TryGetStreamReader(typeof(int))); // Read saved version il.Emit(OpCodes.Stloc_3); // Save version to local var reader = SerializerTypes.TryGetStreamReader(type); if (reader != null) { // Read il.Emit(OpCodes.Ldarg_0); // Load stream il.Emit(OpCodes.Call, reader); il.Emit(OpCodes.Stloc_1); // Set result to local // Add reference to watcher if (!type.TypeInfo.IsValueType) { il.Emit(OpCodes.Ldloc_0); // Load reference id il.Emit(OpCodes.Ldloc_1); // Load result object reference il.Emit(OpCodes.Call, addRef); } } else { // Create if (!type.TypeInfo.IsValueType) { if (defaultCtor != null) { il.Emit(OpCodes.Newobj, defaultCtor); il.Emit(OpCodes.Stloc_1); // Set result to local } else { il.Emit(OpCodes.Ldtoken, type.Type); il.Emit(OpCodes.Call, getTypeFromHandle); il.Emit(OpCodes.Call, getUninitializedObject); il.Emit(OpCodes.Castclass, type.Type); il.Emit(OpCodes.Stloc_1); // Set result to local } } else { il.Emit(OpCodes.Ldloca_S, (byte)1); // Load result local address il.Emit(OpCodes.Initobj, type.Type); } // Add reference to watcher if (!type.TypeInfo.IsValueType) { il.Emit(OpCodes.Ldloc_0); // Load reference id il.Emit(OpCodes.Ldloc_1); // Load result object reference il.Emit(OpCodes.Call, addRef); } // Read code type version var version = SerializerTypes.GetVersion(type); il.Emit(OpCodes.Ldloc_3); // Load version il.Emit(OpCodes.Ldc_I4, version); // Load program type version il.Emit(OpCodes.Beq, readFastLabel); // Call dynamic read if versions not equal il.Emit(OpCodes.Ldarg_0); // Load stream il.Emit(OpCodes.Ldloc_1); // Load obj instance il.Emit(OpCodes.Ldloc_3); // Load version il.Emit(OpCodes.Call, dynamicObjectRead); // Do dynamic read il.Emit(OpCodes.Stloc_1); // Set readed value to result local il.Emit(OpCodes.Br, resultLabel); // Jump to return code part il.MarkLabel(readFastLabel); // Read var typeReader = SerializerTypes.TryGetTypeReader(type); if (typeReader != null) { // Read type il.Emit(OpCodes.Ldarg_0); // Load stream il.Emit(OpCodes.Ldloc_1); // Load obj instance il.Emit(OpCodes.Ldloc_3); // Load version il.Emit(OpCodes.Call, typeReader); il.Emit(OpCodes.Stloc_1); // Set result to local } else { // Read fields Label?nextFieldLabel = null; foreach (var binField in BinField.Get(type.Type)) { var markCurrent = nextFieldLabel != null; if (nextFieldLabel == null) { nextFieldLabel = il.DefineLabel(); } // Read field id il.Emit(OpCodes.Ldarg_0); // Load stream il.Emit(OpCodes.Call, SerializerTypes.TryGetStreamReader(typeof(string))); // Read field id il.Emit(OpCodes.Stloc_2); // Save last readed field id // Mark next field label only after field read if (markCurrent) { il.MarkLabel(nextFieldLabel.Value); nextFieldLabel = il.DefineLabel(); } // Compare field id with endToken il.Emit(OpCodes.Ldloc_2); // Load readed field id il.Emit(OpCodes.Ldstr, SerializerTypes.TypeEndToken); // Load endToken id il.Emit(OpCodes.Ldc_I4, (int)StringComparison.Ordinal); // Load comparsion type il.Emit(OpCodes.Call, stringEquals); // Compare il.Emit(OpCodes.Brtrue, resultLabel); // This is the end // Compare field ids, if it not equals then skip read il.Emit(OpCodes.Ldloc_2); // Load readed field id il.Emit(OpCodes.Ldstr, binField.Id); // Load field id il.Emit(OpCodes.Ldc_I4, (int)StringComparison.Ordinal); // Load comparsion type il.Emit(OpCodes.Call, stringEquals); // Compare il.Emit(OpCodes.Brfalse, nextFieldLabel.Value); // These aren't the field your looking for var deserialize = typeof(BinSerializer <>) .MakeGenericType(binField.Type) .GetTypeInfo() .GetMethod(nameof(BinSerializer <object> .Deserialize), BindingFlags.Static | BindingFlags.Public); // Prepeare stack to field set if (type.TypeInfo.IsValueType) { il.Emit(OpCodes.Ldloca_S, (byte)1); } else { il.Emit(OpCodes.Ldloc_1); } // Read field il.Emit(OpCodes.Ldarg_0); // Load stream il.Emit(OpCodes.Call, deserialize); // Set field binField.EmitWrite(il); } // Mark jump (for last field) if (nextFieldLabel != null) { il.MarkLabel(nextFieldLabel.Value); } // Skip end il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Call, SerializerTypes.TryGetStreamSkiper(typeof(string))); } } // Return result and invoke callback il.MarkLabel(resultLabel); // Invoke deserialzation callback if (typeof(IBinSerializable).IsAssignableFrom(type.Type)) { il.Emit(OpCodes.Ldloca_S, (byte)4); // Load result local address il.Emit(OpCodes.Ldloc_3); // Load version il.Emit(OpCodes.Call, typeof(DeserializationInfo).GetConstructor(new Type[] { typeof(int) })); il.Emit(OpCodes.Ldloc_1); // Load deserialized object il.Emit(OpCodes.Ldloc_S, (byte)4); // Load created DeserialzationInfo il.Emit(OpCodes.Callvirt, onDeserialized); // Call onDeserialized } // Return result il.Emit(OpCodes.Ldloc_1); BSDebug.TraceEnd(il, "Read " + type.TypeInfo.Name); il.Emit(OpCodes.Ret); }
public static Reader <T> CreateReader <T>(TypeImpl type) { var method = Readers.GetOrAdd(type, CreateReaderMethod); return(MethodAdapter.CastReader <T>(method, type)); }
private static void GenerateArrayWriter(ILGenerator il, TypeImpl type) { var elementType = type.TypeInfo.GetElementType(); var getRefId = typeof(RefWriterWatcher) .GetTypeInfo() .GetMethod(nameof(RefWriterWatcher.GetRefId), BindingFlags.Public | BindingFlags.Static); var getLowerBound = typeof(Array) .GetTypeInfo() .GetMethod(nameof(Array.GetLowerBound), BindingFlags.Instance | BindingFlags.Public); var serialize = typeof(BinSerializer <>) .MakeGenericType(elementType) .GetTypeInfo() .GetMethod(nameof(BinSerializer <object> .Serialize), BindingFlags.Static | BindingFlags.Public); il.DeclareLocal(typeof(int)); // Array length il.DeclareLocal(typeof(int)); // Array index il.DeclareLocal(typeof(bool)); // Ref id created flag var skipLabel = il.DefineLabel(); var zeroBased = il.DefineLabel(); var loopLabel = il.DefineLabel(); var exitLoopLabel = il.DefineLabel(); BSDebug.TraceStart(il, "Write " + type.TypeInfo.Name); // Write type il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldstr, SerializerTypes.GetTypeId(type)); il.Emit(OpCodes.Call, SerializerTypes.TryGetStreamWriter(typeof(string))); // Write refId il.Emit(OpCodes.Ldarg_0); // Load stream il.Emit(OpCodes.Ldarg_1); // Load obj il.Emit(OpCodes.Ldloca_S, (byte)2); // Load address of bool local il.Emit(OpCodes.Call, getRefId); // Obj => RefId il.Emit(OpCodes.Call, SerializerTypes.TryGetStreamWriter(typeof(int))); // Check case when reference type already serialized. // If null be returned then created flag be zero too and serialization will be skipped. il.Emit(OpCodes.Ldloc_2); il.Emit(OpCodes.Brfalse, skipLabel); var dim = type.TypeInfo.GetArrayRank(); if (dim == 1) { // Dimension check il.Emit(OpCodes.Ldarg_1); // Load array il.Emit(OpCodes.Ldc_I4_0); // Load zero (0 dim) il.Emit(OpCodes.Callvirt, getLowerBound); // Get lower bound of 0 dim il.Emit(OpCodes.Brfalse, zeroBased); // If result 0 then jump to zeroBased // Throw exception if non zero based il.Emit(OpCodes.Ldstr, "Non zero based arrays not supported."); il.Emit(OpCodes.Newobj, typeof(InvalidOperationException).GetTypeInfo().GetConstructor(new[] { typeof(string) })); il.Emit(OpCodes.Throw); // Write array length il.MarkLabel(zeroBased); il.Emit(OpCodes.Ldarg_0); // Load stream il.Emit(OpCodes.Ldarg_1); // Load array il.Emit(OpCodes.Ldlen); // Load array length il.Emit(OpCodes.Call, SerializerTypes.TryGetStreamWriter(typeof(int))); // Set array length il.Emit(OpCodes.Ldarg_1); // Load array il.Emit(OpCodes.Ldlen); // Load array length il.Emit(OpCodes.Stloc_0); // Set to 0 local // Set array index to 0 il.Emit(OpCodes.Ldc_I4_0); // Load 0 il.Emit(OpCodes.Stloc_1); // Set to 1 local // Loop start il.MarkLabel(loopLabel); // Loop check il.Emit(OpCodes.Ldloc_0); // Load array length il.Emit(OpCodes.Ldloc_1); // Load current index il.Emit(OpCodes.Beq, exitLoopLabel); // If equals then exit // Write element to stream il.Emit(OpCodes.Ldarg_0); // Load stream il.Emit(OpCodes.Ldarg_1); // Load array il.Emit(OpCodes.Ldloc_1); // Load current index il.Emit(OpCodes.Ldelem, elementType); // Load element il.Emit(OpCodes.Call, serialize); // Inrement il.Emit(OpCodes.Ldloc_1); // Load current index il.Emit(OpCodes.Ldc_I4_1); // Load 1 il.Emit(OpCodes.Add); // Add il.Emit(OpCodes.Stloc_1); // Set current index // Jump to loop start il.Emit(OpCodes.Br, loopLabel); } else { throw new NotSupportedException("Arrays with non single dimension not supported."); } // Skipped il.MarkLabel(skipLabel); il.MarkLabel(exitLoopLabel); BSDebug.TraceEnd(il, "Write " + type.TypeInfo.FullName); // End il.Emit(OpCodes.Ret); }