diff --git a/csharp/msgpack.sln b/csharp/msgpack.sln index 351c7b8a..2aad4419 100644 --- a/csharp/msgpack.sln +++ b/csharp/msgpack.sln @@ -18,7 +18,6 @@ Global {CE24167B-8F0A-4670-BD1E-3C283311E86B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CE24167B-8F0A-4670-BD1E-3C283311E86B}.Debug|Any CPU.Build.0 = Debug|Any CPU {CE24167B-8F0A-4670-BD1E-3C283311E86B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CE24167B-8F0A-4670-BD1E-3C283311E86B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/csharp/msgpack.tests/CompiledPackerTests.cs b/csharp/msgpack.tests/CompiledPackerTests.cs index 8e5a5161..583409e8 100644 --- a/csharp/msgpack.tests/CompiledPackerTests.cs +++ b/csharp/msgpack.tests/CompiledPackerTests.cs @@ -9,19 +9,44 @@ namespace msgpack.tests [TestFixture] public class CompiledPackerTests { + CompiledPacker _mbImpl = new CompiledPacker (false); + CompiledPacker _dynImpl = new CompiledPacker (true); + [Test] - public void TestA () + public void TestA_MethodBuilder () { - TestA_Class obj0 = new TestA_Class (); - TestA_Class obj1 = CompiledPacker.Unpack (CompiledPacker.Pack (obj0)); - obj0.Check (obj1); + TestA (_mbImpl); } [Test] - public void TestB () + public void TestA_DynamicMethod () + { + TestA (_dynImpl); + } + + [Test] + public void TestB_MethodBuilder () + { + TestB (_mbImpl); + } + + [Test] + public void TestB_DynamicMethod () + { + TestB (_dynImpl); + } + + void TestA (CompiledPacker packer) + { + TestA_Class obj0 = new TestA_Class (); + TestA_Class obj1 = packer.Unpack (packer.Pack (obj0)); + obj0.Check (obj1); + } + + void TestB (CompiledPacker packer) { TestB_Class obj0 = TestB_Class.Create (); - TestB_Class obj1 = CompiledPacker.Unpack (CompiledPacker.Pack (obj0)); + TestB_Class obj1 = packer.Unpack (packer.Pack (obj0)); obj0.Check (obj1); } } diff --git a/csharp/msgpack/AssemblyInfo.cs b/csharp/msgpack/AssemblyInfo.cs index 0ef2ef9b..323742f6 100644 --- a/csharp/msgpack/AssemblyInfo.cs +++ b/csharp/msgpack/AssemblyInfo.cs @@ -34,3 +34,4 @@ using System.Runtime.InteropServices; // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion ("1.0.0.0")] [assembly: AssemblyFileVersion ("1.0.0.0")] +[assembly: InternalsVisibleTo (msgpack.CompiledPacker.MethodBuilderPacker.AssemblyName)] diff --git a/csharp/msgpack/CompiledPacker.cs b/csharp/msgpack/CompiledPacker.cs index 7bb6b622..7245d1b4 100644 --- a/csharp/msgpack/CompiledPacker.cs +++ b/csharp/msgpack/CompiledPacker.cs @@ -1,28 +1,38 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.IO; +using System.Reflection; +using System.Reflection.Emit; +using System.Threading; using msgpack.Compiler; namespace msgpack { public class CompiledPacker { - public static byte[] Pack (object o) + static PackerBase _publicFieldPacker, _allFieldPacker; + PackerBase _packer; + + static CompiledPacker () { - using (MemoryStream ms = new MemoryStream ()) { - Pack (ms, o); - return ms.ToArray (); - } + _publicFieldPacker = new MethodBuilderPacker (); + _allFieldPacker = new DynamicMethodPacker (); } - public static void Pack (Stream strm, object o) + public CompiledPacker () : this (false) {} + public CompiledPacker (bool packPrivateField) { - PackerCompiler.GetPackMethod (o.GetType ()) (new MsgPackWriter (strm), o); + _packer = (packPrivateField ? _allFieldPacker : _publicFieldPacker); } - public static byte[] Pack (T o) + public void Prepare () + { + _packer.CreatePacker (); + _packer.CreateUnpacker (); + } + + #region Generics Pack/Unpack Methods + public byte[] Pack (T o) { using (MemoryStream ms = new MemoryStream ()) { Pack (ms, o); @@ -30,43 +40,494 @@ namespace msgpack } } - public static void Pack (Stream strm, T o) + public void Pack (Stream strm, T o) { - PackerCompiler.GetPackMethod () (new MsgPackWriter (strm), o); + _packer.CreatePacker () (new MsgPackWriter (strm), o); } - public static T Unpack (byte[] buf) + public T Unpack (byte[] buf) { return Unpack (buf, 0, buf.Length); } - public static T Unpack (byte[] buf, int offset, int size) + public T Unpack (byte[] buf, int offset, int size) { using (MemoryStream ms = new MemoryStream (buf, offset, size)) { return Unpack (ms); } } - public static T Unpack (Stream strm) + public T Unpack (Stream strm) { - return PackerCompiler.GetUnpackMethod () (new MsgPackReader (strm)); + return _packer.CreateUnpacker () (new MsgPackReader (strm)); + } + #endregion + + #region Non-generics Pack/Unpack Methods + public byte[] Pack (object o) + { + using (MemoryStream ms = new MemoryStream ()) { + Pack (ms, o); + return ms.ToArray (); + } } - public static object Unpack (Type t, byte[] buf) + public void Pack (Stream strm, object o) + { + throw new NotImplementedException (); + } + + public object Unpack (Type t, byte[] buf) { return Unpack (t, buf, 0, buf.Length); } - public static object Unpack (Type t, byte[] buf, int offset, int size) + public object Unpack (Type t, byte[] buf, int offset, int size) { using (MemoryStream ms = new MemoryStream (buf, offset, size)) { return Unpack (t, ms); } } - public static object Unpack (Type t, Stream strm) + public object Unpack (Type t, Stream strm) { throw new NotImplementedException (); } + #endregion + + #region Compiled Packer Implementations + public abstract class PackerBase + { + Dictionary _packers = new Dictionary (); + Dictionary _unpackers = new Dictionary (); + + protected Dictionary _packMethods = new Dictionary (); + protected Dictionary _unpackMethods = new Dictionary (); + + protected PackerBase () + { + DefaultPackMethods.Register (_packMethods, _unpackMethods); + } + + public Action CreatePacker () + { + Delegate d; + lock (_packers) { + if (!_packers.TryGetValue (typeof (T), out d)) { + d = CreatePacker_Internal (); + _packers.Add (typeof (T), d); + } + } + return (Action)d; + } + + public Func CreateUnpacker () + { + Delegate d; + lock (_unpackers) { + if (!_unpackers.TryGetValue (typeof (T), out d)) { + d = CreateUnpacker_Internal (); + _unpackers.Add (typeof (T), d); + } + } + return (Func)d; + } + + protected abstract Action CreatePacker_Internal (); + protected abstract Func CreateUnpacker_Internal (); + } + public sealed class DynamicMethodPacker : PackerBase + { + protected static MethodInfo LookupMemberMappingMethod; + static Dictionary> UnpackMemberMappings; + + static DynamicMethodPacker () + { + UnpackMemberMappings = new Dictionary> (); + LookupMemberMappingMethod = typeof (DynamicMethodPacker).GetMethod ("LookupMemberMapping", BindingFlags.Static | BindingFlags.NonPublic); + } + + public DynamicMethodPacker () : base () + { + } + + protected override Action CreatePacker_Internal () + { + DynamicMethod dm = CreatePacker (typeof (T), CreatePackDynamicMethod (typeof (T))); + return (Action)dm.CreateDelegate (typeof (Action)); + } + + protected override Func CreateUnpacker_Internal () + { + DynamicMethod dm = CreateUnpacker (typeof (T), CreateUnpackDynamicMethod (typeof (T))); + return (Func)dm.CreateDelegate (typeof (Func)); + } + + DynamicMethod CreatePacker (Type t, DynamicMethod dm) + { + ILGenerator il = dm.GetILGenerator (); + _packMethods.Add (t, dm); + PackILGenerator.EmitPackCode (t, dm, il, LookupMembers, FormatMemberName, LookupPackMethod); + return dm; + } + + DynamicMethod CreateUnpacker (Type t, DynamicMethod dm) + { + ILGenerator il = dm.GetILGenerator (); + _unpackMethods.Add (t, dm); + PackILGenerator.EmitUnpackCode (t, dm, il, LookupMembers, FormatMemberName, LookupUnpackMethod, + LookupMemberMapping, LookupMemberMappingMethod); + return dm; + } + + static DynamicMethod CreatePackDynamicMethod (Type t) + { + return CreateDynamicMethod (typeof (void), new Type[] {typeof (MsgPackWriter), t}); + } + + static DynamicMethod CreateUnpackDynamicMethod (Type t) + { + return CreateDynamicMethod (t, new Type[] {typeof (MsgPackReader)}); + } + + static MemberInfo[] LookupMembers (Type t) + { + BindingFlags baseFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; + List list = new List (); + list.AddRange (t.GetFields (baseFlags)); + // TODO: Add NonSerialized Attribute Filter ? + return list.ToArray (); + } + + MethodInfo LookupPackMethod (Type t) + { + MethodInfo mi; + DynamicMethod dm; + if (_packMethods.TryGetValue (t, out mi)) + return mi; + dm = CreatePackDynamicMethod (t); + return CreatePacker (t, dm); + } + + MethodInfo LookupUnpackMethod (Type t) + { + MethodInfo mi; + if (_unpackMethods.TryGetValue (t, out mi)) + return mi; + DynamicMethod dm = CreateUnpackDynamicMethod (t); + return CreateUnpacker (t, dm); + } + + static string FormatMemberName (MemberInfo m) + { + if (m.MemberType != MemberTypes.Field) + return m.Name; + + int pos; + string name = m.Name; + if (name[0] == '<' && (pos = name.IndexOf ('>')) > 1) + name = name.Substring (1, pos - 1); // Auto-Property (\<.+\>) + return name; + } + + static int _dynamicMethodIdx = 0; + static DynamicMethod CreateDynamicMethod (Type returnType, Type[] parameterTypes) + { + string name = "_" + Interlocked.Increment (ref _dynamicMethodIdx).ToString (); + return new DynamicMethod (name, returnType, parameterTypes, true); + } + + internal static IDictionary LookupMemberMapping (Type t) + { + IDictionary mapping; + lock (UnpackMemberMappings) { + if (!UnpackMemberMappings.TryGetValue (t, out mapping)) { + mapping = new Dictionary (); + UnpackMemberMappings.Add (t, mapping); + } + } + return mapping; + } + } + public sealed class MethodBuilderPacker : PackerBase + { + public const string AssemblyName = "MessagePackInternalAssembly"; + static AssemblyName DynamicAsmName; + static AssemblyBuilder DynamicAsmBuilder; + static ModuleBuilder DynamicModuleBuilder; + + protected static MethodInfo LookupMemberMappingMethod; + static Dictionary> UnpackMemberMappings; + + static MethodBuilderPacker () + { + UnpackMemberMappings = new Dictionary> (); + LookupMemberMappingMethod = typeof (MethodBuilderPacker).GetMethod ("LookupMemberMapping", BindingFlags.Static | BindingFlags.NonPublic); + + DynamicAsmName = new AssemblyName (AssemblyName); + DynamicAsmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly (DynamicAsmName, AssemblyBuilderAccess.Run); + DynamicModuleBuilder = DynamicAsmBuilder.DefineDynamicModule (DynamicAsmName.Name); + } + + public MethodBuilderPacker () : base () + { + } + + protected override Action CreatePacker_Internal () + { + TypeBuilder tb; + MethodBuilder mb; + CreatePackMethodBuilder (typeof (T), out tb, out mb); + _packMethods.Add (typeof (T), mb); + CreatePacker (typeof (T), mb); + MethodInfo mi = ToCallableMethodInfo (typeof (T), tb, true); + return (Action)Delegate.CreateDelegate (typeof (Action), mi); + } + + protected override Func CreateUnpacker_Internal () + { + TypeBuilder tb; + MethodBuilder mb; + CreateUnpackMethodBuilder (typeof (T), out tb, out mb); + _unpackMethods.Add (typeof (T), mb); + CreateUnpacker (typeof (T), mb); + MethodInfo mi = ToCallableMethodInfo (typeof (T), tb, false); + return (Func)Delegate.CreateDelegate (typeof (Func), mi); + } + + void CreatePacker (Type t, MethodBuilder mb) + { + ILGenerator il = mb.GetILGenerator (); + PackILGenerator.EmitPackCode (t, mb, il, LookupMembers, FormatMemberName, LookupPackMethod); + } + + void CreateUnpacker (Type t, MethodBuilder mb) + { + ILGenerator il = mb.GetILGenerator (); + PackILGenerator.EmitUnpackCode (t, mb, il, LookupMembers, FormatMemberName, LookupUnpackMethod, + LookupMemberMapping, LookupMemberMappingMethod); + } + + MethodInfo ToCallableMethodInfo (Type t, TypeBuilder tb, bool isPacker) + { + Type type = tb.CreateType (); + MethodInfo mi = type.GetMethod (isPacker ? "Pack" : "Unpack", BindingFlags.Static | BindingFlags.Public); + if (isPacker) { + _packMethods[t] = mi; + } else { + _unpackMethods[t] = mi; + } + return mi; + } + + MethodInfo LookupPackMethod (Type t) + { + MethodInfo mi; + TypeBuilder tb; + MethodBuilder mb; + if (_packMethods.TryGetValue (t, out mi)) + return mi; + CreatePackMethodBuilder (t, out tb, out mb); + _packMethods.Add (t, mb); + CreatePacker (t, mb); + return ToCallableMethodInfo (t, tb, true); + } + + MethodInfo LookupUnpackMethod (Type t) + { + MethodInfo mi; + TypeBuilder tb; + MethodBuilder mb; + if (_unpackMethods.TryGetValue (t, out mi)) + return mi; + CreateUnpackMethodBuilder (t, out tb, out mb); + _unpackMethods.Add (t, mb); + CreateUnpacker (t, mb); + return ToCallableMethodInfo (t, tb, false); + } + + static string FormatMemberName (MemberInfo m) + { + return m.Name; + } + + static MemberInfo[] LookupMembers (Type t) + { + BindingFlags baseFlags = BindingFlags.Instance | BindingFlags.Public; + List list = new List (); + list.AddRange (t.GetFields (baseFlags)); + // TODO: Add NonSerialized Attribute Filter ? + return list.ToArray (); + } + + static void CreatePackMethodBuilder (Type t, out TypeBuilder tb, out MethodBuilder mb) + { + tb = DynamicModuleBuilder.DefineType (t.Name + "PackerType", TypeAttributes.Public); + mb = tb.DefineMethod ("Pack", MethodAttributes.Static | MethodAttributes.Public, typeof (void), new Type[] {typeof (MsgPackWriter), t}); + } + + static void CreateUnpackMethodBuilder (Type t, out TypeBuilder tb, out MethodBuilder mb) + { + tb = DynamicModuleBuilder.DefineType (t.Name + "UnpackerType", TypeAttributes.Public); + mb = tb.DefineMethod ("Unpack", MethodAttributes.Static | MethodAttributes.Public, t, new Type[] {typeof (MsgPackReader)}); + } + + internal static IDictionary LookupMemberMapping (Type t) + { + IDictionary mapping; + lock (UnpackMemberMappings) { + if (!UnpackMemberMappings.TryGetValue (t, out mapping)) { + mapping = new Dictionary (); + UnpackMemberMappings.Add (t, mapping); + } + } + return mapping; + } + } + #endregion + + #region default pack/unpack methods + internal static class DefaultPackMethods + { + public static void Register (Dictionary packMethods, Dictionary unpackMethods) + { + RegisterPackMethods (packMethods); + RegisterUnpackMethods (unpackMethods); + } + + #region Pack + static void RegisterPackMethods (Dictionary packMethods) + { + Type type = typeof (DefaultPackMethods); + MethodInfo[] methods = type.GetMethods (BindingFlags.Static | BindingFlags.NonPublic); + string methodName = "Pack"; + for (int i = 0; i < methods.Length; i ++) { + if (!methodName.Equals (methods[i].Name)) + continue; + ParameterInfo[] parameters = methods[i].GetParameters (); + if (parameters.Length != 2 || parameters[0].ParameterType != typeof (MsgPackWriter)) + continue; + packMethods.Add (parameters[1].ParameterType, methods[i]); + } + } + + internal static void Pack (MsgPackWriter writer, string x) + { + if (x == null) { + writer.WriteNil (); + } else { + writer.Write (x, false); + } + } + #endregion + + #region Unpack + static void RegisterUnpackMethods (Dictionary unpackMethods) + { + BindingFlags flags = BindingFlags.Static | BindingFlags.NonPublic; + Type type = typeof (DefaultPackMethods); + MethodInfo mi = type.GetMethod ("Unpack_Signed", flags); + unpackMethods.Add (typeof (sbyte), mi); + unpackMethods.Add (typeof (short), mi); + unpackMethods.Add (typeof (int), mi); + + mi = type.GetMethod ("Unpack_Signed64", flags); + unpackMethods.Add (typeof (long), mi); + + mi = type.GetMethod ("Unpack_Unsigned", flags); + unpackMethods.Add (typeof (byte), mi); + unpackMethods.Add (typeof (ushort), mi); + unpackMethods.Add (typeof (char), mi); + unpackMethods.Add (typeof (uint), mi); + + mi = type.GetMethod ("Unpack_Unsigned64", flags); + unpackMethods.Add (typeof (ulong), mi); + + mi = type.GetMethod ("Unpack_Boolean", flags); + unpackMethods.Add (typeof (bool), mi); + + mi = type.GetMethod ("Unpack_Float", flags); + unpackMethods.Add (typeof (float), mi); + + mi = type.GetMethod ("Unpack_Double", flags); + unpackMethods.Add (typeof (double), mi); + + mi = type.GetMethod ("Unpack_String", flags); + unpackMethods.Add (typeof (string), mi); + } + + internal static int Unpack_Signed (MsgPackReader reader) + { + if (!reader.Read () || !reader.IsSigned ()) + UnpackFailed (); + return reader.ValueSigned; + } + + internal static long Unpack_Signed64 (MsgPackReader reader) + { + if (!reader.Read ()) + UnpackFailed (); + if (reader.IsSigned ()) + return reader.ValueSigned; + if (reader.IsSigned64 ()) + return reader.ValueSigned64; + UnpackFailed (); + return 0; // unused + } + + internal static uint Unpack_Unsigned (MsgPackReader reader) + { + if (!reader.Read () || !reader.IsUnsigned ()) + UnpackFailed (); + return reader.ValueUnsigned; + } + + internal static ulong Unpack_Unsigned64 (MsgPackReader reader) + { + if (!reader.Read ()) + UnpackFailed (); + if (reader.IsUnsigned ()) + return reader.ValueUnsigned; + if (reader.IsUnsigned64 ()) + return reader.ValueUnsigned64; + UnpackFailed (); + return 0; // unused + } + + internal static bool Unpack_Boolean (MsgPackReader reader) + { + if (!reader.Read () || !reader.IsBoolean ()) + UnpackFailed (); + return reader.ValueBoolean; + } + + internal static float Unpack_Float (MsgPackReader reader) + { + if (!reader.Read () || reader.Type != TypePrefixes.Float) + UnpackFailed (); + return reader.ValueFloat; + } + + internal static double Unpack_Double (MsgPackReader reader) + { + if (!reader.Read () || reader.Type != TypePrefixes.Double) + UnpackFailed (); + return reader.ValueDouble; + } + + internal static string Unpack_String (MsgPackReader reader) + { + if (!reader.Read () || !reader.IsRaw ()) + UnpackFailed (); + return reader.ReadRawString (); + } + + internal static void UnpackFailed () + { + throw new FormatException (); + } + #endregion + } + #endregion } } diff --git a/csharp/msgpack/Compiler/PackILGenerator.cs b/csharp/msgpack/Compiler/PackILGenerator.cs new file mode 100644 index 00000000..d413acbe --- /dev/null +++ b/csharp/msgpack/Compiler/PackILGenerator.cs @@ -0,0 +1,370 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.Serialization; + +namespace msgpack.Compiler +{ + static class PackILGenerator + { + #region Pack IL Generator + public static void EmitPackCode (Type type, MethodInfo mi, ILGenerator il, + Func targetMemberSelector, + Func memberNameFormatter, + Func lookupPackMethod) + { + if (type.IsPrimitive || type.IsInterface) + throw new NotSupportedException (); + + Variable arg_writer = Variable.CreateArg (0); + Variable arg_obj = Variable.CreateArg (1); + Variable local_i = Variable.CreateLocal (il.DeclareLocal (typeof (int))); + + if (!type.IsValueType) { // null check + Label notNullLabel = il.DefineLabel (); + il.EmitLd (arg_obj); + il.Emit (OpCodes.Brtrue_S, notNullLabel); + il.EmitLd (arg_writer); + il.Emit (OpCodes.Call, typeof(MsgPackWriter).GetMethod("WriteNil", new Type[0])); + il.Emit (OpCodes.Ret); + il.MarkLabel (notNullLabel); + } + + if (type.IsArray) { + EmitPackArrayCode (mi, il, type, arg_writer, arg_obj, local_i, lookupPackMethod); + goto FinallyProcess; + } + + // MsgPackWriter.WriteMapHeader + MemberInfo[] members = targetMemberSelector (type); + il.EmitLd (arg_writer); + il.EmitLdc (members.Length); + il.Emit (OpCodes.Callvirt, typeof (MsgPackWriter).GetMethod("WriteMapHeader", new Type[]{typeof (int)})); + + for (int i = 0; i < members.Length; i ++) { + MemberInfo m = members[i]; + Type mt = m.GetMemberType (); + + // write field-name + il.EmitLd (arg_writer); + il.EmitLdstr (memberNameFormatter (m)); + il.EmitLd_True (); + il.Emit (OpCodes.Call, typeof (MsgPackWriter).GetMethod("Write", new Type[]{typeof (string), typeof (bool)})); + + // write value + EmitPackMemberValueCode (mt, il, arg_writer, arg_obj, m, null, type, mi, lookupPackMethod); + } + +FinallyProcess: + il.Emit (OpCodes.Ret); + } + + static void EmitPackArrayCode (MethodInfo mi, ILGenerator il, Type t, Variable var_writer, Variable var_obj, Variable var_loop, Func lookupPackMethod) + { + Type et = t.GetElementType (); + il.EmitLd (var_writer, var_obj); + il.Emit (OpCodes.Ldlen); + il.Emit (OpCodes.Call, typeof(MsgPackWriter).GetMethod("WriteArrayHeader", new Type[]{ typeof(int) })); + + Label beginLabel = il.DefineLabel (); + Label exprLabel = il.DefineLabel (); + + // for-loop: init loop counter + il.EmitLdc (0); + il.EmitSt (var_loop); + + // jump + il.Emit (OpCodes.Br_S, exprLabel); + + // mark begin-label + il.MarkLabel (beginLabel); + + // write element + EmitPackMemberValueCode (et, il, var_writer, var_obj, null, var_loop, t, mi, lookupPackMethod); + + // increment loop-counter + il.EmitLd (var_loop); + il.Emit (OpCodes.Ldc_I4_1); + il.Emit (OpCodes.Add); + il.EmitSt (var_loop); + + // mark expression label + il.MarkLabel (exprLabel); + + // expression + il.EmitLd (var_loop, var_obj); + il.Emit (OpCodes.Ldlen); + il.Emit (OpCodes.Blt_S, beginLabel); + } + + /// (optional) + /// (optional) + static void EmitPackMemberValueCode (Type type, ILGenerator il, Variable var_writer, Variable var_obj, + MemberInfo m, Variable elementIdx, Type currentType, MethodInfo currentMethod, Func lookupPackMethod) + { + MethodInfo mi; + il.EmitLd (var_writer, var_obj); + if (m != null) + il.EmitLdMember (m); + if (elementIdx != null) { + il.EmitLd (elementIdx); + il.Emit (OpCodes.Ldelem, type); + } + if (type.IsPrimitive) { + mi = typeof(MsgPackWriter).GetMethod("Write", new Type[]{type}); + } else { + if (currentType == type) { + mi = currentMethod; + } else { + mi = lookupPackMethod (type); + } + } + il.Emit (OpCodes.Call, mi); + } + #endregion + + #region Unpack IL Generator + public static void EmitUnpackCode (Type type, MethodInfo mi, ILGenerator il, + Func targetMemberSelector, + Func memberNameFormatter, + Func lookupUnpackMethod, + Func> lookupMemberMapping, + MethodInfo lookupMemberMappingMethod) + { + if (type.IsArray) { + EmitUnpackArrayCode (type, mi, il, targetMemberSelector, memberNameFormatter, lookupUnpackMethod); + } else { + EmitUnpackMapCode (type, mi, il, targetMemberSelector, memberNameFormatter, lookupUnpackMethod, lookupMemberMapping, lookupMemberMappingMethod); + } + } + + static void EmitUnpackMapCode (Type type, MethodInfo mi, ILGenerator il, + Func targetMemberSelector, + Func memberNameFormatter, + Func lookupUnpackMethod, + Func> lookupMemberMapping, + MethodInfo lookupMemberMappingMethod) + { + MethodInfo failedMethod = typeof (PackILGenerator).GetMethod ("UnpackFailed", BindingFlags.Static | BindingFlags.NonPublic); + MemberInfo[] members = targetMemberSelector (type); + IDictionary member_mapping = lookupMemberMapping (type); + for (int i = 0; i < members.Length; i ++) + member_mapping.Add (memberNameFormatter (members[i]), i); + + Variable msgpackReader = Variable.CreateArg (0); + Variable obj = Variable.CreateLocal (il.DeclareLocal (type)); + Variable num_of_fields = Variable.CreateLocal (il.DeclareLocal (typeof (int))); + Variable loop_idx = Variable.CreateLocal (il.DeclareLocal (typeof (int))); + Variable mapping = Variable.CreateLocal (il.DeclareLocal (typeof (IDictionary))); + Variable switch_idx = Variable.CreateLocal (il.DeclareLocal (typeof (int))); + Variable var_type = Variable.CreateLocal (il.DeclareLocal (typeof (Type))); + + // if (!MsgPackReader.Read()) UnpackFailed (); + // if (MsgPackReader.Type == TypePrefixes.Nil) return null; + // if (!MsgPackReader.IsMap ()) UnpackFailed (); + EmitUnpackReadAndTypeCheckCode (il, msgpackReader, typeof (MsgPackReader).GetMethod ("IsMap"), failedMethod, true); + + // type = typeof (T) + il.Emit (OpCodes.Ldtoken, type); + il.Emit (OpCodes.Call, typeof(Type).GetMethod ("GetTypeFromHandle")); + il.EmitSt (var_type); + + // mapping = LookupMemberMapping (typeof (T)) + il.EmitLd (var_type); + il.Emit (OpCodes.Call, lookupMemberMappingMethod); + il.EmitSt (mapping); + + // object o = FormatterServices.GetUninitializedObject (Type); + il.EmitLd (var_type); + il.Emit (OpCodes.Call, typeof (FormatterServices).GetMethod ("GetUninitializedObject")); + il.Emit (OpCodes.Castclass, type); + il.EmitSt (obj); + + // num_of_fields = (int)reader.Length + il.EmitLd (msgpackReader); + il.Emit (OpCodes.Call, typeof (MsgPackReader).GetProperty ("Length").GetGetMethod ()); + il.EmitSt (num_of_fields); + + // Loop labels + Label lblLoopStart = il.DefineLabel (); + Label lblLoopExpr = il.DefineLabel (); + + // i = 0; + il.EmitLdc (0); + il.EmitSt (loop_idx); + il.Emit (OpCodes.Br, lblLoopExpr); + il.MarkLabel (lblLoopStart); + + /* process */ + // if (!MsgPackReader.Read() || !MsgPackReader.IsRaw()) UnpackFailed(); + EmitUnpackReadAndTypeCheckCode (il, msgpackReader, typeof (MsgPackReader).GetMethod ("IsRaw"), failedMethod, false); + + // MsgPackReader.ReadRawString () + // if (!Dictionary.TryGetValue (,)) UnpackFailed(); + Label lbl3 = il.DefineLabel (); + il.EmitLd (mapping); + il.EmitLd (msgpackReader); + il.Emit (OpCodes.Call, typeof (MsgPackReader).GetMethod ("ReadRawString", new Type[0])); + il.Emit (OpCodes.Ldloca_S, (byte)switch_idx.Index); + il.Emit (OpCodes.Callvirt, typeof (IDictionary).GetMethod ("TryGetValue")); + il.Emit (OpCodes.Brtrue, lbl3); + il.Emit (OpCodes.Call, failedMethod); + il.MarkLabel (lbl3); + + // switch + Label[] switchCases = new Label[members.Length]; + for (int i = 0; i < switchCases.Length; i ++) + switchCases[i] = il.DefineLabel (); + Label switchCaseEndLabel = il.DefineLabel (); + il.EmitLd (switch_idx); + il.Emit (OpCodes.Switch, switchCases); + il.Emit (OpCodes.Call, failedMethod); + + for (int i = 0; i < switchCases.Length; i ++) { + il.MarkLabel (switchCases[i]); + MemberInfo minfo = members[i]; + Type mt = minfo.GetMemberType (); + MethodInfo unpack_method = lookupUnpackMethod (mt); + il.EmitLd (obj); + il.EmitLd (msgpackReader); + il.Emit (OpCodes.Call, unpack_method); + il.EmitStMember (minfo); + il.Emit (OpCodes.Br, switchCaseEndLabel); + } + il.MarkLabel (switchCaseEndLabel); + + // i ++ + il.EmitLd (loop_idx); + il.EmitLdc (1); + il.Emit (OpCodes.Add); + il.EmitSt (loop_idx); + + // i < num_of_fields; + il.MarkLabel (lblLoopExpr); + il.EmitLd (loop_idx); + il.EmitLd (num_of_fields); + il.Emit (OpCodes.Blt, lblLoopStart); + + // return + il.EmitLd (obj); + il.Emit (OpCodes.Ret); + } + + static void EmitUnpackArrayCode (Type arrayType, MethodInfo mi, ILGenerator il, + Func targetMemberSelector, + Func memberNameFormatter, + Func lookupUnpackMethod) + { + Type elementType = arrayType.GetElementType (); + MethodInfo failedMethod = typeof (PackILGenerator).GetMethod ("UnpackFailed", BindingFlags.Static | BindingFlags.NonPublic); + + Variable msgpackReader = Variable.CreateArg (0); + Variable obj = Variable.CreateLocal (il.DeclareLocal (arrayType)); + Variable num_of_elements = Variable.CreateLocal (il.DeclareLocal (typeof (int))); + Variable loop_idx = Variable.CreateLocal (il.DeclareLocal (typeof (int))); + Variable type = Variable.CreateLocal (il.DeclareLocal (typeof (Type))); + + // if (!MsgPackReader.Read() || !MsgPackReader.IsArray ()) UnpackFailed (); + EmitUnpackReadAndTypeCheckCode (il, msgpackReader, typeof (MsgPackReader).GetMethod ("IsArray"), failedMethod, true); + + // type = typeof (T) + il.Emit (OpCodes.Ldtoken, elementType); + il.Emit (OpCodes.Call, typeof(Type).GetMethod ("GetTypeFromHandle")); + il.EmitSt (type); + + // num_of_elements = (int)reader.Length + il.EmitLd (msgpackReader); + il.Emit (OpCodes.Call, typeof (MsgPackReader).GetProperty ("Length").GetGetMethod ()); + il.EmitSt (num_of_elements); + + // object o = Array.CreateInstance (Type, Length); + il.EmitLd (type); + il.EmitLd (num_of_elements); + il.Emit (OpCodes.Call, typeof (Array).GetMethod ("CreateInstance", new Type[] {typeof (Type), typeof (int)})); + il.Emit (OpCodes.Castclass, arrayType); + il.EmitSt (obj); + + // Unpack element method + MethodInfo unpack_method = lookupUnpackMethod (elementType); + + // Loop labels + Label lblLoopStart = il.DefineLabel (); + Label lblLoopExpr = il.DefineLabel (); + + // i = 0; + il.EmitLdc (0); + il.EmitSt (loop_idx); + il.Emit (OpCodes.Br, lblLoopExpr); + il.MarkLabel (lblLoopStart); + + /* process */ + il.EmitLd (obj, loop_idx); + il.EmitLd (msgpackReader); + il.Emit (OpCodes.Call, unpack_method); + il.Emit (OpCodes.Stelem, elementType); + + // i ++ + il.EmitLd (loop_idx); + il.EmitLdc (1); + il.Emit (OpCodes.Add); + il.EmitSt (loop_idx); + + // i < num_of_fields; + il.MarkLabel (lblLoopExpr); + il.EmitLd (loop_idx); + il.EmitLd (num_of_elements); + il.Emit (OpCodes.Blt, lblLoopStart); + + // return + il.EmitLd (obj); + il.Emit (OpCodes.Ret); + } + + static void EmitUnpackReadAndTypeCheckCode (ILGenerator il, Variable msgpackReader, MethodInfo typeCheckMethod, MethodInfo failedMethod, bool nullCheckAndReturn) + { + Label lblFailed = il.DefineLabel (); + Label lblNullReturn = nullCheckAndReturn ? il.DefineLabel () : default(Label); + Label lblPassed = il.DefineLabel (); + il.EmitLd (msgpackReader); + il.Emit (OpCodes.Call, typeof (MsgPackReader).GetMethod ("Read")); + il.Emit (OpCodes.Brfalse_S, lblFailed); + if (nullCheckAndReturn) { + il.EmitLd (msgpackReader); + il.Emit (OpCodes.Call, typeof (MsgPackReader).GetProperty ("Type").GetGetMethod ()); + il.EmitLdc ((int)TypePrefixes.Nil); + il.Emit (OpCodes.Beq_S, lblNullReturn); + } + il.EmitLd (msgpackReader); + il.Emit (OpCodes.Call, typeCheckMethod); + il.Emit (OpCodes.Brtrue_S, lblPassed); + il.Emit (OpCodes.Br, lblFailed); + if (nullCheckAndReturn) { + il.MarkLabel (lblNullReturn); + il.Emit (OpCodes.Ldnull); + il.Emit (OpCodes.Ret); + } + il.MarkLabel (lblFailed); + il.Emit (OpCodes.Call, failedMethod); + il.MarkLabel (lblPassed); + } + + /// Exception Helper + internal static void UnpackFailed () + { + throw new FormatException (); + } + #endregion + + #region Misc + static Type GetMemberType (this MemberInfo mi) + { + if (mi.MemberType == MemberTypes.Field) + return ((FieldInfo)mi).FieldType; + if (mi.MemberType == MemberTypes.Property) + return ((PropertyInfo)mi).PropertyType; + throw new ArgumentException (); + } + #endregion + } +} diff --git a/csharp/msgpack/Compiler/PackerCompiler.cs b/csharp/msgpack/Compiler/PackerCompiler.cs deleted file mode 100644 index 07767177..00000000 --- a/csharp/msgpack/Compiler/PackerCompiler.cs +++ /dev/null @@ -1,643 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Reflection; -using System.Reflection.Emit; -using System.Threading; -using System.Runtime.Serialization; - -namespace msgpack.Compiler -{ - public static class PackerCompiler - { - static Dictionary CompiledPackMethods; - static Dictionary PackDelegates; - static Dictionary> PackWrapperDelegates; - - static Dictionary CompiledUnpackMethods; - static Dictionary UnpackDelegates; - static Dictionary> UnpackFieldMapping; - - static Dictionary UserDefinedPackMethods; - - static PackerCompiler () - { - CompiledPackMethods = new Dictionary (); - PackDelegates = new Dictionary (); - PackWrapperDelegates = new Dictionary> (); - UserDefinedPackMethods = new Dictionary (); - - CompiledUnpackMethods = new Dictionary (); - UnpackDelegates = new Dictionary (); - UnpackFieldMapping = new Dictionary> (); - - RegisterDefaultUnpackMethods (); - RegisterUserDefinedMethods (); - } - - #region Pack Frontend / Compiler - public static Action GetPackMethod () - { - Delegate d; - lock (CompiledPackMethods) { - PackDelegates.TryGetValue (typeof (T), out d); - } - if (d == null) - CreatePackMethod (typeof (T)); - lock (CompiledPackMethods) { - d = PackDelegates[typeof (T)]; - } - return (Action)d; - } - - public static Action GetPackMethod (Type t) - { - Action d; - lock (CompiledPackMethods) { - PackWrapperDelegates.TryGetValue (t, out d); - } - if (d == null) - CreatePackMethod (t); - lock (CompiledPackMethods) { - d = PackWrapperDelegates[t]; - } - return d; - } - - static DynamicMethod CreatePackMethod (Type t) - { - DynamicMethod methodBuilder; - lock (CompiledPackMethods) { - if (CompiledPackMethods.TryGetValue (t, out methodBuilder)) { - return CompiledPackMethods[t]; - } else { - methodBuilder = CreateDynamicMethod (typeof (void), new Type[]{typeof (MsgPackWriter), t}); - CompiledPackMethods.Add (t, methodBuilder); - CreatePackMethod (t, methodBuilder); - PackDelegates.Add (t, methodBuilder.CreateDelegate (typeof (Action<,>).MakeGenericType (typeof (MsgPackWriter), t))); - Action wrapper = CreatePackWrapper (t, methodBuilder); - PackWrapperDelegates.Add (t, wrapper); - } - } - return methodBuilder; - } - - static void CreatePackMethod (Type t, DynamicMethod methodBuilder) - { - if (t == null || methodBuilder == null) - throw new ArgumentNullException (); - if (t.IsPrimitive || t.IsInterface) - throw new NotSupportedException (); - - ILGenerator il = methodBuilder.GetILGenerator(); - - Variable arg_writer = Variable.CreateArg (0); - Variable arg_obj = Variable.CreateArg (1); - Variable local_i = Variable.CreateLocal (il.DeclareLocal (typeof (int))); - - if (!t.IsValueType) { // null check - Label notNullLabel = il.DefineLabel (); - il.EmitLd (arg_obj); - il.Emit (OpCodes.Brtrue_S, notNullLabel); - il.EmitLd (arg_writer); - il.Emit (OpCodes.Call, typeof(MsgPackWriter).GetMethod("WriteNil", new Type[0])); - il.Emit (OpCodes.Ret); - il.MarkLabel (notNullLabel); - } - - MethodInfo udm; - lock (UserDefinedPackMethods) { - UserDefinedPackMethods.TryGetValue (t, out udm); - } - if (udm != null) { - il.EmitLd (arg_writer, arg_obj); - il.Emit (OpCodes.Call, udm); - goto FinallyProcess; - } - - if (t.IsArray) { - EmitArrayWriteProcess (il, t, arg_writer, arg_obj, local_i); - goto FinallyProcess; - } - - // MsgPackWriter.WriteMapHeader - MemberInfo[] members = LookupMembers (t); - il.EmitLd (arg_writer); - il.EmitLdc (members.Length); - il.Emit (OpCodes.Callvirt, typeof (MsgPackWriter).GetMethod("WriteMapHeader", new Type[]{typeof (int)})); - - for (int i = 0; i < members.Length; i ++) { - MemberInfo m = members[i]; - Type mt = m.GetMemberType (); - - // write field-name - il.EmitLd (arg_writer); - il.EmitLdstr (FormatMemberName (m)); - il.EmitLd_True (); - il.Emit (OpCodes.Call, typeof (MsgPackWriter).GetMethod("Write", new Type[]{typeof (string), typeof (bool)})); - - // write value - CreatePackMethod_EmitMemberValue (il, arg_writer, arg_obj, m, null, mt, t, methodBuilder); - } - -FinallyProcess: - il.Emit (OpCodes.Ret); - } - - static void EmitArrayWriteProcess (ILGenerator il, Type t, Variable var_writer, Variable var_obj, Variable var_loop) - { - Type et = t.GetElementType (); - il.EmitLd (var_writer, var_obj); - il.Emit (OpCodes.Ldlen); - il.Emit (OpCodes.Call, typeof(MsgPackWriter).GetMethod("WriteArrayHeader", new Type[]{ typeof(int) })); - - Label beginLabel = il.DefineLabel (); - Label exprLabel = il.DefineLabel (); - - // for-loop: init loop counter - il.EmitLdc (0); - il.EmitSt (var_loop); - - // jump - il.Emit (OpCodes.Br_S, exprLabel); - - // mark begin-label - il.MarkLabel (beginLabel); - - // write element - CreatePackMethod_EmitMemberValue (il, var_writer, var_obj, null, var_loop, et, null, null); - - // increment loop-counter - il.EmitLd (var_loop); - il.Emit (OpCodes.Ldc_I4_1); - il.Emit (OpCodes.Add); - il.EmitSt (var_loop); - - // mark expression label - il.MarkLabel (exprLabel); - - // expression - il.EmitLd (var_loop, var_obj); - il.Emit (OpCodes.Ldlen); - il.Emit (OpCodes.Blt_S, beginLabel); - } - - /// (optional) - /// (optional) - static void CreatePackMethod_EmitMemberValue (ILGenerator il, Variable var_writer, Variable var_obj, MemberInfo m, Variable elementIdx, Type type, Type currentType, DynamicMethod currentMethod) - { - MethodInfo mi; - il.EmitLd (var_writer, var_obj); - if (m != null) - il.EmitLdMember (m); - if (elementIdx != null) { - il.EmitLd (elementIdx); - il.Emit (OpCodes.Ldelem, type); - } - if (type.IsPrimitive) { - mi = typeof(MsgPackWriter).GetMethod("Write", new Type[]{type}); - } else { - if (currentType == type) { - mi = currentMethod; - } else { - lock (UserDefinedPackMethods) { - UserDefinedPackMethods.TryGetValue (type, out mi); - } - if (mi == null) - mi = CreatePackMethod (type); - } - } - il.Emit (OpCodes.Call, mi); - } - - static Action CreatePackWrapper (Type t, DynamicMethod packMethod) - { - DynamicMethod dm = CreateDynamicMethod (typeof (void), new Type[] {typeof (MsgPackWriter), typeof (object)}); - ILGenerator il = dm.GetILGenerator(); - il.EmitLd (Variable.CreateArg (0), Variable.CreateArg (1)); - il.Emit (OpCodes.Castclass, t); - il.Emit (OpCodes.Call, packMethod); - il.Emit (OpCodes.Ret); - return (Action)dm.CreateDelegate (typeof (Action)); - } - #endregion - - #region Unpack Frontend / Compiler - public static Func GetUnpackMethod () - { - Delegate d; - lock (CompiledUnpackMethods) { - UnpackDelegates.TryGetValue (typeof (T), out d); - } - if (d == null) - CreateUnpackMethod (typeof (T)); - lock (CompiledUnpackMethods) { - d = UnpackDelegates[typeof (T)]; - } - return (Func)d; - } - - static MethodInfo CreateUnpackMethod (Type t) - { - MethodInfo methodBuilder; - lock (CompiledUnpackMethods) { - if (CompiledUnpackMethods.TryGetValue (t, out methodBuilder)) { - return methodBuilder; - } else { - methodBuilder = CreateDynamicMethod (t, new Type[]{typeof (MsgPackReader)}); - CompiledUnpackMethods.Add (t, methodBuilder); - UnpackFieldMapping.Add (t, new Dictionary ()); - if (t.IsArray) { - CreateUnpackArrayMethod (t, t.GetElementType (), (DynamicMethod)methodBuilder); - } else { - CreateUnpackMethod (t, (DynamicMethod)methodBuilder); - } - UnpackDelegates.Add (t, ((DynamicMethod)methodBuilder).CreateDelegate (typeof (Func<,>).MakeGenericType (typeof (MsgPackReader), t))); - } - } - return methodBuilder; - } - - static void CreateUnpackMethod (Type t, DynamicMethod methodBuilder) - { - ILGenerator il = methodBuilder.GetILGenerator (); - MethodInfo failedMethod = typeof (PackerCompiler).GetMethod ("UnpackFailed", BindingFlags.Static | BindingFlags.NonPublic); - MemberInfo[] members = LookupMembers (t); - Dictionary member_mapping = UnpackFieldMapping[t]; - for (int i = 0; i < members.Length; i ++) - member_mapping.Add (FormatMemberName (members[i]), i); - - Variable msgpackReader = Variable.CreateArg (0); - Variable obj = Variable.CreateLocal (il.DeclareLocal (t)); - Variable num_of_fields = Variable.CreateLocal (il.DeclareLocal (typeof (int))); - Variable loop_idx = Variable.CreateLocal (il.DeclareLocal (typeof (int))); - Variable mapping = Variable.CreateLocal (il.DeclareLocal (typeof (Dictionary))); - Variable switch_idx = Variable.CreateLocal (il.DeclareLocal (typeof (int))); - Variable type = Variable.CreateLocal (il.DeclareLocal (typeof (Type))); - - // if (!MsgPackReader.Read()) UnpackFailed (); - // if (MsgPackReader.Type == TypePrefixes.Nil) return null; - // if (!MsgPackReader.IsMap ()) UnpackFailed (); - EmitReadAndTypeCheck (il, msgpackReader, typeof (MsgPackReader).GetMethod ("IsMap"), failedMethod, true); - - // type = typeof (T) - il.Emit (OpCodes.Ldtoken, t); - il.Emit (OpCodes.Call, typeof(Type).GetMethod ("GetTypeFromHandle")); - il.EmitSt (type); - - // mapping = LookupMemberMapping (typeof (T)) - il.EmitLd (type); - il.Emit (OpCodes.Call, typeof (PackerCompiler).GetMethod ("LookupMemberMapping", BindingFlags.Static | BindingFlags.NonPublic)); - il.EmitSt (mapping); - - // object o = FormatterServices.GetUninitializedObject (Type); - il.EmitLd (type); - il.Emit (OpCodes.Call, typeof (FormatterServices).GetMethod ("GetUninitializedObject")); - il.Emit (OpCodes.Castclass, t); - il.EmitSt (obj); - - // num_of_fields = (int)reader.Length - il.EmitLd (msgpackReader); - il.Emit (OpCodes.Call, typeof (MsgPackReader).GetProperty ("Length").GetGetMethod ()); - il.EmitSt (num_of_fields); - - // Loop labels - Label lblLoopStart = il.DefineLabel (); - Label lblLoopExpr = il.DefineLabel (); - - // i = 0; - il.EmitLdc (0); - il.EmitSt (loop_idx); - il.Emit (OpCodes.Br, lblLoopExpr); - il.MarkLabel (lblLoopStart); - - /* process */ - // if (!MsgPackReader.Read() || !MsgPackReader.IsRaw()) UnpackFailed(); - EmitReadAndTypeCheck (il, msgpackReader, typeof (MsgPackReader).GetMethod ("IsRaw"), failedMethod, false); - - // MsgPackReader.ReadRawString () - // if (!Dictionary.TryGetValue (,)) UnpackFailed(); - Label lbl3 = il.DefineLabel (); - il.EmitLd (mapping); - il.EmitLd (msgpackReader); - il.Emit (OpCodes.Call, typeof (MsgPackReader).GetMethod ("ReadRawString", new Type[0])); - il.Emit (OpCodes.Ldloca_S, (short)switch_idx.Index); - il.Emit (OpCodes.Call, typeof (Dictionary).GetMethod ("TryGetValue")); - il.Emit (OpCodes.Brtrue, lbl3); - il.Emit (OpCodes.Call, failedMethod); - il.MarkLabel (lbl3); - - // switch - Label[] switchCases = new Label[members.Length]; - for (int i = 0; i < switchCases.Length; i ++) - switchCases[i] = il.DefineLabel (); - Label switchCaseEndLabel = il.DefineLabel (); - il.EmitLd (switch_idx); - il.Emit (OpCodes.Switch, switchCases); - il.Emit (OpCodes.Call, failedMethod); - - for (int i = 0; i < switchCases.Length; i ++) { - il.MarkLabel (switchCases[i]); - MemberInfo mi = members[i]; - Type mt = mi.GetMemberType (); - MethodInfo unpack_method; - - il.EmitLd (obj); - if (!CompiledUnpackMethods.TryGetValue (mt, out unpack_method)) { - unpack_method = CreateUnpackMethod (mt); - } - il.EmitLd (msgpackReader); - il.Emit (OpCodes.Call, unpack_method); - il.EmitStMember (mi); - il.Emit (OpCodes.Br, switchCaseEndLabel); - } - il.MarkLabel (switchCaseEndLabel); - - // i ++ - il.EmitLd (loop_idx); - il.EmitLdc (1); - il.Emit (OpCodes.Add); - il.EmitSt (loop_idx); - - // i < num_of_fields; - il.MarkLabel (lblLoopExpr); - il.EmitLd (loop_idx); - il.EmitLd (num_of_fields); - il.Emit (OpCodes.Blt, lblLoopStart); - - // return - il.EmitLd (obj); - il.Emit (OpCodes.Ret); - } - - static void CreateUnpackArrayMethod (Type arrayType, Type elementType, DynamicMethod methodBuilder) - { - ILGenerator il = methodBuilder.GetILGenerator (); - MethodInfo failedMethod = typeof (PackerCompiler).GetMethod ("UnpackFailed", BindingFlags.Static | BindingFlags.NonPublic); - - Variable msgpackReader = Variable.CreateArg (0); - Variable obj = Variable.CreateLocal (il.DeclareLocal (arrayType)); - Variable num_of_elements = Variable.CreateLocal (il.DeclareLocal (typeof (int))); - Variable loop_idx = Variable.CreateLocal (il.DeclareLocal (typeof (int))); - Variable type = Variable.CreateLocal (il.DeclareLocal (typeof (Type))); - - // if (!MsgPackReader.Read() || !MsgPackReader.IsArray ()) UnpackFailed (); - EmitReadAndTypeCheck (il, msgpackReader, typeof (MsgPackReader).GetMethod ("IsArray"), failedMethod, true); - - // type = typeof (T) - il.Emit (OpCodes.Ldtoken, elementType); - il.Emit (OpCodes.Call, typeof(Type).GetMethod ("GetTypeFromHandle")); - il.EmitSt (type); - - // num_of_elements = (int)reader.Length - il.EmitLd (msgpackReader); - il.Emit (OpCodes.Call, typeof (MsgPackReader).GetProperty ("Length").GetGetMethod ()); - il.EmitSt (num_of_elements); - - // object o = Array.CreateInstance (Type, Length); - il.EmitLd (type); - il.EmitLd (num_of_elements); - il.Emit (OpCodes.Call, typeof (Array).GetMethod ("CreateInstance", new Type[] {typeof (Type), typeof (int)})); - il.Emit (OpCodes.Castclass, arrayType); - il.EmitSt (obj); - - // Unpack element method - MethodInfo unpack_method; - lock (CompiledUnpackMethods) { - if (!CompiledUnpackMethods.TryGetValue (elementType, out unpack_method)) { - unpack_method = CreateUnpackMethod (elementType); - } - } - - // Loop labels - Label lblLoopStart = il.DefineLabel (); - Label lblLoopExpr = il.DefineLabel (); - - // i = 0; - il.EmitLdc (0); - il.EmitSt (loop_idx); - il.Emit (OpCodes.Br, lblLoopExpr); - il.MarkLabel (lblLoopStart); - - /* process */ - il.EmitLd (obj, loop_idx); - il.EmitLd (msgpackReader); - il.Emit (OpCodes.Call, unpack_method); - il.Emit (OpCodes.Stelem, elementType); - - // i ++ - il.EmitLd (loop_idx); - il.EmitLdc (1); - il.Emit (OpCodes.Add); - il.EmitSt (loop_idx); - - // i < num_of_fields; - il.MarkLabel (lblLoopExpr); - il.EmitLd (loop_idx); - il.EmitLd (num_of_elements); - il.Emit (OpCodes.Blt, lblLoopStart); - - // return - il.EmitLd (obj); - il.Emit (OpCodes.Ret); - } - - static void EmitReadAndTypeCheck (ILGenerator il, Variable msgpackReader, MethodInfo typeCheckMethod, MethodInfo failedMethod, bool nullCheckAndReturn) - { - Label lblFailed = il.DefineLabel (); - Label lblNullReturn = il.DefineLabel (); - Label lblPassed = il.DefineLabel (); - il.EmitLd (msgpackReader); - il.Emit (OpCodes.Call, typeof (MsgPackReader).GetMethod ("Read")); - il.Emit (OpCodes.Brfalse_S, lblFailed); - if (nullCheckAndReturn) { - il.EmitLd (msgpackReader); - il.Emit (OpCodes.Call, typeof (MsgPackReader).GetProperty ("Type").GetGetMethod ()); - il.EmitLdc ((int)TypePrefixes.Nil); - il.Emit (OpCodes.Beq_S, lblNullReturn); - } - il.EmitLd (msgpackReader); - il.Emit (OpCodes.Call, typeCheckMethod); - il.Emit (OpCodes.Brtrue_S, lblPassed); - if (nullCheckAndReturn) { - il.MarkLabel (lblNullReturn); - il.Emit (OpCodes.Ldnull); - il.Emit (OpCodes.Ret); - } - il.MarkLabel (lblFailed); - il.Emit (OpCodes.Call, failedMethod); - il.MarkLabel (lblPassed); - } - - /// Exception Helper - static void UnpackFailed () - { - throw new FormatException (); - } - - static Dictionary LookupMemberMapping (Type t) - { - lock (CompiledUnpackMethods) { - return UnpackFieldMapping[t]; - } - } - - static void RegisterDefaultUnpackMethods () - { - BindingFlags flags = BindingFlags.Static | BindingFlags.NonPublic; - MethodInfo mi = typeof (PackerCompiler).GetMethod ("Unpack_Signed", flags); - CompiledUnpackMethods.Add (typeof (sbyte), mi); - CompiledUnpackMethods.Add (typeof (short), mi); - CompiledUnpackMethods.Add (typeof (int), mi); - - mi = typeof (PackerCompiler).GetMethod ("Unpack_Signed64", flags); - CompiledUnpackMethods.Add (typeof (long), mi); - - mi = typeof (PackerCompiler).GetMethod ("Unpack_Unsigned", flags); - CompiledUnpackMethods.Add (typeof (byte), mi); - CompiledUnpackMethods.Add (typeof (ushort), mi); - CompiledUnpackMethods.Add (typeof (char), mi); - CompiledUnpackMethods.Add (typeof (uint), mi); - - mi = typeof (PackerCompiler).GetMethod ("Unpack_Unsigned64", flags); - CompiledUnpackMethods.Add (typeof (ulong), mi); - - mi = typeof (PackerCompiler).GetMethod ("Unpack_Boolean", flags); - CompiledUnpackMethods.Add (typeof (bool), mi); - - mi = typeof (PackerCompiler).GetMethod ("Unpack_Float", flags); - CompiledUnpackMethods.Add (typeof (float), mi); - - mi = typeof (PackerCompiler).GetMethod ("Unpack_Double", flags); - CompiledUnpackMethods.Add (typeof (double), mi); - - mi = typeof (PackerCompiler).GetMethod ("Unpack_String", flags); - CompiledUnpackMethods.Add (typeof (string), mi); - } - - static int Unpack_Signed (MsgPackReader reader) - { - if (!reader.Read () || !reader.IsSigned ()) - UnpackFailed (); - return reader.ValueSigned; - } - - static long Unpack_Signed64 (MsgPackReader reader) - { - if (!reader.Read ()) - UnpackFailed (); - if (reader.IsSigned ()) - return reader.ValueSigned; - if (reader.IsSigned64 ()) - return reader.ValueSigned64; - UnpackFailed (); - return 0; // unused - } - - static uint Unpack_Unsigned (MsgPackReader reader) - { - if (!reader.Read () || !reader.IsUnsigned ()) - UnpackFailed (); - return reader.ValueUnsigned; - } - - static ulong Unpack_Unsigned64 (MsgPackReader reader) - { - if (!reader.Read ()) - UnpackFailed (); - if (reader.IsUnsigned ()) - return reader.ValueUnsigned; - if (reader.IsUnsigned64 ()) - return reader.ValueUnsigned64; - UnpackFailed (); - return 0; // unused - } - - static bool Unpack_Boolean (MsgPackReader reader) - { - if (!reader.Read () || !reader.IsBoolean ()) - UnpackFailed (); - return reader.ValueBoolean; - } - - static float Unpack_Float (MsgPackReader reader) - { - if (!reader.Read () || reader.Type != TypePrefixes.Float) - UnpackFailed (); - return reader.ValueFloat; - } - - static double Unpack_Double (MsgPackReader reader) - { - if (!reader.Read () || reader.Type != TypePrefixes.Double) - UnpackFailed (); - return reader.ValueDouble; - } - - static string Unpack_String (MsgPackReader reader) - { - if (!reader.Read () || !reader.IsRaw ()) - UnpackFailed (); - return reader.ReadRawString (); - } - #endregion - - #region User Defined Packer/Unpackers - static void RegisterUserDefinedMethods () - { - BindingFlags flags = BindingFlags.Static | BindingFlags.NonPublic; - Type type = typeof (PackerCompiler); - object[][] list = new object[][] { - new object[] {typeof (string), "PackString"} - }; - for (int i = 0; i < list.Length; i ++) - UserDefinedPackMethods.Add ((Type)list[i][0], type.GetMethod ((string)list[i][1], flags)); - } - - static void PackString (MsgPackWriter writer, string x) - { - writer.Write (x); - } - #endregion - - #region Misc - static int _dynamicMethodIdx = 0; - static DynamicMethod CreateDynamicMethod (Type returnType, Type[] parameterTypes) - { - string name = "_" + Interlocked.Increment (ref _dynamicMethodIdx).ToString (); - return new DynamicMethod (name, returnType, parameterTypes, true); - } - - static MemberInfo[] LookupMembers (Type t) - { - BindingFlags baseFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; - List list = new List (); - list.AddRange (t.GetFields (baseFlags)); - //list.AddRange (t.GetProperties (baseFlags)); // TODO: - return list.ToArray (); - } - - static string FormatMemberName (MemberInfo m) - { - if (m.MemberType != MemberTypes.Field) - return m.Name; - - int pos; - string name = m.Name; - if (name[0] == '<' && (pos = name.IndexOf ('>')) > 1) - name = name.Substring (1, pos - 1); // Auto-Property (\<.+\>) - return name; - } - - static Type GetMemberType (this MemberInfo mi) - { - if (mi.MemberType == MemberTypes.Field) - return ((FieldInfo)mi).FieldType; - if (mi.MemberType == MemberTypes.Property) - return ((PropertyInfo)mi).PropertyType; - throw new ArgumentException (); - } - #endregion - } -} diff --git a/csharp/msgpack/msgpack.csproj b/csharp/msgpack/msgpack.csproj index 2ecda117..086b729b 100644 --- a/csharp/msgpack/msgpack.csproj +++ b/csharp/msgpack/msgpack.csproj @@ -42,7 +42,7 @@ - +