mirror of
https://github.com/msgpack/msgpack-c.git
synced 2025-04-16 23:12:57 +02:00
csharp: Refactoring IL code generator & add two compiled packer implementation (dynamic-method, method-builder)
This commit is contained in:
parent
94e5b0d78f
commit
33498d3673
@ -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.ActiveCfg = Debug|Any CPU
|
||||||
{CE24167B-8F0A-4670-BD1E-3C283311E86B}.Debug|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
|
||||||
{CE24167B-8F0A-4670-BD1E-3C283311E86B}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -9,19 +9,44 @@ namespace msgpack.tests
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class CompiledPackerTests
|
public class CompiledPackerTests
|
||||||
{
|
{
|
||||||
|
CompiledPacker _mbImpl = new CompiledPacker (false);
|
||||||
|
CompiledPacker _dynImpl = new CompiledPacker (true);
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestA ()
|
public void TestA_MethodBuilder ()
|
||||||
{
|
{
|
||||||
TestA_Class obj0 = new TestA_Class ();
|
TestA (_mbImpl);
|
||||||
TestA_Class obj1 = CompiledPacker.Unpack<TestA_Class> (CompiledPacker.Pack<TestA_Class> (obj0));
|
|
||||||
obj0.Check (obj1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[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<TestA_Class> (packer.Pack<TestA_Class> (obj0));
|
||||||
|
obj0.Check (obj1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestB (CompiledPacker packer)
|
||||||
{
|
{
|
||||||
TestB_Class obj0 = TestB_Class.Create ();
|
TestB_Class obj0 = TestB_Class.Create ();
|
||||||
TestB_Class obj1 = CompiledPacker.Unpack<TestB_Class> (CompiledPacker.Pack<TestB_Class> (obj0));
|
TestB_Class obj1 = packer.Unpack<TestB_Class> (packer.Pack<TestB_Class> (obj0));
|
||||||
obj0.Check (obj1);
|
obj0.Check (obj1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,3 +34,4 @@ using System.Runtime.InteropServices;
|
|||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion ("1.0.0.0")]
|
[assembly: AssemblyVersion ("1.0.0.0")]
|
||||||
[assembly: AssemblyFileVersion ("1.0.0.0")]
|
[assembly: AssemblyFileVersion ("1.0.0.0")]
|
||||||
|
[assembly: InternalsVisibleTo (msgpack.CompiledPacker.MethodBuilderPacker.AssemblyName)]
|
||||||
|
@ -1,28 +1,38 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Reflection.Emit;
|
||||||
|
using System.Threading;
|
||||||
using msgpack.Compiler;
|
using msgpack.Compiler;
|
||||||
|
|
||||||
namespace msgpack
|
namespace msgpack
|
||||||
{
|
{
|
||||||
public class CompiledPacker
|
public class CompiledPacker
|
||||||
{
|
{
|
||||||
public static byte[] Pack (object o)
|
static PackerBase _publicFieldPacker, _allFieldPacker;
|
||||||
|
PackerBase _packer;
|
||||||
|
|
||||||
|
static CompiledPacker ()
|
||||||
{
|
{
|
||||||
using (MemoryStream ms = new MemoryStream ()) {
|
_publicFieldPacker = new MethodBuilderPacker ();
|
||||||
Pack (ms, o);
|
_allFieldPacker = new DynamicMethodPacker ();
|
||||||
return ms.ToArray ();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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> (T o)
|
public void Prepare<T> ()
|
||||||
|
{
|
||||||
|
_packer.CreatePacker<T> ();
|
||||||
|
_packer.CreateUnpacker<T> ();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Generics Pack/Unpack Methods
|
||||||
|
public byte[] Pack<T> (T o)
|
||||||
{
|
{
|
||||||
using (MemoryStream ms = new MemoryStream ()) {
|
using (MemoryStream ms = new MemoryStream ()) {
|
||||||
Pack<T> (ms, o);
|
Pack<T> (ms, o);
|
||||||
@ -30,43 +40,494 @@ namespace msgpack
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Pack<T> (Stream strm, T o)
|
public void Pack<T> (Stream strm, T o)
|
||||||
{
|
{
|
||||||
PackerCompiler.GetPackMethod<T> () (new MsgPackWriter (strm), o);
|
_packer.CreatePacker<T> () (new MsgPackWriter (strm), o);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T Unpack<T> (byte[] buf)
|
public T Unpack<T> (byte[] buf)
|
||||||
{
|
{
|
||||||
return Unpack<T> (buf, 0, buf.Length);
|
return Unpack<T> (buf, 0, buf.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T Unpack<T> (byte[] buf, int offset, int size)
|
public T Unpack<T> (byte[] buf, int offset, int size)
|
||||||
{
|
{
|
||||||
using (MemoryStream ms = new MemoryStream (buf, offset, size)) {
|
using (MemoryStream ms = new MemoryStream (buf, offset, size)) {
|
||||||
return Unpack<T> (ms);
|
return Unpack<T> (ms);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T Unpack<T> (Stream strm)
|
public T Unpack<T> (Stream strm)
|
||||||
{
|
{
|
||||||
return PackerCompiler.GetUnpackMethod<T> () (new MsgPackReader (strm));
|
return _packer.CreateUnpacker<T> () (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);
|
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)) {
|
using (MemoryStream ms = new MemoryStream (buf, offset, size)) {
|
||||||
return Unpack (t, ms);
|
return Unpack (t, ms);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static object Unpack (Type t, Stream strm)
|
public object Unpack (Type t, Stream strm)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException ();
|
throw new NotImplementedException ();
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Compiled Packer Implementations
|
||||||
|
public abstract class PackerBase
|
||||||
|
{
|
||||||
|
Dictionary<Type, Delegate> _packers = new Dictionary<Type, Delegate> ();
|
||||||
|
Dictionary<Type, Delegate> _unpackers = new Dictionary<Type, Delegate> ();
|
||||||
|
|
||||||
|
protected Dictionary<Type, MethodInfo> _packMethods = new Dictionary<Type, MethodInfo> ();
|
||||||
|
protected Dictionary<Type, MethodInfo> _unpackMethods = new Dictionary<Type, MethodInfo> ();
|
||||||
|
|
||||||
|
protected PackerBase ()
|
||||||
|
{
|
||||||
|
DefaultPackMethods.Register (_packMethods, _unpackMethods);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Action<MsgPackWriter, T> CreatePacker<T> ()
|
||||||
|
{
|
||||||
|
Delegate d;
|
||||||
|
lock (_packers) {
|
||||||
|
if (!_packers.TryGetValue (typeof (T), out d)) {
|
||||||
|
d = CreatePacker_Internal<T> ();
|
||||||
|
_packers.Add (typeof (T), d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (Action<MsgPackWriter, T>)d;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Func<MsgPackReader, T> CreateUnpacker<T> ()
|
||||||
|
{
|
||||||
|
Delegate d;
|
||||||
|
lock (_unpackers) {
|
||||||
|
if (!_unpackers.TryGetValue (typeof (T), out d)) {
|
||||||
|
d = CreateUnpacker_Internal<T> ();
|
||||||
|
_unpackers.Add (typeof (T), d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (Func<MsgPackReader, T>)d;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract Action<MsgPackWriter, T> CreatePacker_Internal<T> ();
|
||||||
|
protected abstract Func<MsgPackReader, T> CreateUnpacker_Internal<T> ();
|
||||||
|
}
|
||||||
|
public sealed class DynamicMethodPacker : PackerBase
|
||||||
|
{
|
||||||
|
protected static MethodInfo LookupMemberMappingMethod;
|
||||||
|
static Dictionary<Type, IDictionary<string, int>> UnpackMemberMappings;
|
||||||
|
|
||||||
|
static DynamicMethodPacker ()
|
||||||
|
{
|
||||||
|
UnpackMemberMappings = new Dictionary<Type, IDictionary<string, int>> ();
|
||||||
|
LookupMemberMappingMethod = typeof (DynamicMethodPacker).GetMethod ("LookupMemberMapping", BindingFlags.Static | BindingFlags.NonPublic);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DynamicMethodPacker () : base ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Action<MsgPackWriter, T> CreatePacker_Internal<T> ()
|
||||||
|
{
|
||||||
|
DynamicMethod dm = CreatePacker (typeof (T), CreatePackDynamicMethod (typeof (T)));
|
||||||
|
return (Action<MsgPackWriter, T>)dm.CreateDelegate (typeof (Action<MsgPackWriter, T>));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Func<MsgPackReader, T> CreateUnpacker_Internal<T> ()
|
||||||
|
{
|
||||||
|
DynamicMethod dm = CreateUnpacker (typeof (T), CreateUnpackDynamicMethod (typeof (T)));
|
||||||
|
return (Func<MsgPackReader, T>)dm.CreateDelegate (typeof (Func<MsgPackReader, T>));
|
||||||
|
}
|
||||||
|
|
||||||
|
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<MemberInfo> list = new List<MemberInfo> ();
|
||||||
|
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 (\<.+\>) <ab>
|
||||||
|
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<string,int> LookupMemberMapping (Type t)
|
||||||
|
{
|
||||||
|
IDictionary<string, int> mapping;
|
||||||
|
lock (UnpackMemberMappings) {
|
||||||
|
if (!UnpackMemberMappings.TryGetValue (t, out mapping)) {
|
||||||
|
mapping = new Dictionary<string, int> ();
|
||||||
|
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<Type, IDictionary<string, int>> UnpackMemberMappings;
|
||||||
|
|
||||||
|
static MethodBuilderPacker ()
|
||||||
|
{
|
||||||
|
UnpackMemberMappings = new Dictionary<Type, IDictionary<string, int>> ();
|
||||||
|
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<MsgPackWriter, T> CreatePacker_Internal<T> ()
|
||||||
|
{
|
||||||
|
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<MsgPackWriter, T>)Delegate.CreateDelegate (typeof (Action<MsgPackWriter, T>), mi);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Func<MsgPackReader, T> CreateUnpacker_Internal<T> ()
|
||||||
|
{
|
||||||
|
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<MsgPackReader, T>)Delegate.CreateDelegate (typeof (Func<MsgPackReader, T>), 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<MemberInfo> list = new List<MemberInfo> ();
|
||||||
|
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<string,int> LookupMemberMapping (Type t)
|
||||||
|
{
|
||||||
|
IDictionary<string, int> mapping;
|
||||||
|
lock (UnpackMemberMappings) {
|
||||||
|
if (!UnpackMemberMappings.TryGetValue (t, out mapping)) {
|
||||||
|
mapping = new Dictionary<string, int> ();
|
||||||
|
UnpackMemberMappings.Add (t, mapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region default pack/unpack methods
|
||||||
|
internal static class DefaultPackMethods
|
||||||
|
{
|
||||||
|
public static void Register (Dictionary<Type, MethodInfo> packMethods, Dictionary<Type, MethodInfo> unpackMethods)
|
||||||
|
{
|
||||||
|
RegisterPackMethods (packMethods);
|
||||||
|
RegisterUnpackMethods (unpackMethods);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Pack
|
||||||
|
static void RegisterPackMethods (Dictionary<Type, MethodInfo> 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<Type, MethodInfo> 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
370
csharp/msgpack/Compiler/PackILGenerator.cs
Normal file
370
csharp/msgpack/Compiler/PackILGenerator.cs
Normal file
@ -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<Type,MemberInfo[]> targetMemberSelector,
|
||||||
|
Func<MemberInfo,string> memberNameFormatter,
|
||||||
|
Func<Type, MethodInfo> 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<Type, MethodInfo> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <param name="m">(optional)</param>
|
||||||
|
/// <param name="elementIdx">(optional)</param>
|
||||||
|
static void EmitPackMemberValueCode (Type type, ILGenerator il, Variable var_writer, Variable var_obj,
|
||||||
|
MemberInfo m, Variable elementIdx, Type currentType, MethodInfo currentMethod, Func<Type, MethodInfo> 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<Type,MemberInfo[]> targetMemberSelector,
|
||||||
|
Func<MemberInfo,string> memberNameFormatter,
|
||||||
|
Func<Type, MethodInfo> lookupUnpackMethod,
|
||||||
|
Func<Type, IDictionary<string,int>> 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<Type,MemberInfo[]> targetMemberSelector,
|
||||||
|
Func<MemberInfo,string> memberNameFormatter,
|
||||||
|
Func<Type, MethodInfo> lookupUnpackMethod,
|
||||||
|
Func<Type, IDictionary<string,int>> lookupMemberMapping,
|
||||||
|
MethodInfo lookupMemberMappingMethod)
|
||||||
|
{
|
||||||
|
MethodInfo failedMethod = typeof (PackILGenerator).GetMethod ("UnpackFailed", BindingFlags.Static | BindingFlags.NonPublic);
|
||||||
|
MemberInfo[] members = targetMemberSelector (type);
|
||||||
|
IDictionary<string, int> 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<string, int>)));
|
||||||
|
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<string,int>).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<Type,MemberInfo[]> targetMemberSelector,
|
||||||
|
Func<MemberInfo,string> memberNameFormatter,
|
||||||
|
Func<Type, MethodInfo> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Exception Helper</summary>
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -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<Type, DynamicMethod> CompiledPackMethods;
|
|
||||||
static Dictionary<Type, Delegate> PackDelegates;
|
|
||||||
static Dictionary<Type, Action<MsgPackWriter, object>> PackWrapperDelegates;
|
|
||||||
|
|
||||||
static Dictionary<Type, MethodInfo> CompiledUnpackMethods;
|
|
||||||
static Dictionary<Type, Delegate> UnpackDelegates;
|
|
||||||
static Dictionary<Type, Dictionary<string, int>> UnpackFieldMapping;
|
|
||||||
|
|
||||||
static Dictionary<Type, MethodInfo> UserDefinedPackMethods;
|
|
||||||
|
|
||||||
static PackerCompiler ()
|
|
||||||
{
|
|
||||||
CompiledPackMethods = new Dictionary<Type, DynamicMethod> ();
|
|
||||||
PackDelegates = new Dictionary<Type, Delegate> ();
|
|
||||||
PackWrapperDelegates = new Dictionary<Type, Action<MsgPackWriter, object>> ();
|
|
||||||
UserDefinedPackMethods = new Dictionary<Type, MethodInfo> ();
|
|
||||||
|
|
||||||
CompiledUnpackMethods = new Dictionary<Type, MethodInfo> ();
|
|
||||||
UnpackDelegates = new Dictionary<Type, Delegate> ();
|
|
||||||
UnpackFieldMapping = new Dictionary<Type, Dictionary<string, int>> ();
|
|
||||||
|
|
||||||
RegisterDefaultUnpackMethods ();
|
|
||||||
RegisterUserDefinedMethods ();
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Pack Frontend / Compiler
|
|
||||||
public static Action<MsgPackWriter,T> GetPackMethod<T> ()
|
|
||||||
{
|
|
||||||
Delegate d;
|
|
||||||
lock (CompiledPackMethods) {
|
|
||||||
PackDelegates.TryGetValue (typeof (T), out d);
|
|
||||||
}
|
|
||||||
if (d == null)
|
|
||||||
CreatePackMethod (typeof (T));
|
|
||||||
lock (CompiledPackMethods) {
|
|
||||||
d = PackDelegates[typeof (T)];
|
|
||||||
}
|
|
||||||
return (Action<MsgPackWriter,T>)d;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Action<MsgPackWriter, object> GetPackMethod (Type t)
|
|
||||||
{
|
|
||||||
Action<MsgPackWriter, object> 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<MsgPackWriter, object> 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <param name="m">(optional)</param>
|
|
||||||
/// <param name="elementIdx">(optional)</param>
|
|
||||||
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<MsgPackWriter, object> 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<MsgPackWriter, object>)dm.CreateDelegate (typeof (Action<MsgPackWriter,object>));
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Unpack Frontend / Compiler
|
|
||||||
public static Func<MsgPackReader,T> GetUnpackMethod<T> ()
|
|
||||||
{
|
|
||||||
Delegate d;
|
|
||||||
lock (CompiledUnpackMethods) {
|
|
||||||
UnpackDelegates.TryGetValue (typeof (T), out d);
|
|
||||||
}
|
|
||||||
if (d == null)
|
|
||||||
CreateUnpackMethod (typeof (T));
|
|
||||||
lock (CompiledUnpackMethods) {
|
|
||||||
d = UnpackDelegates[typeof (T)];
|
|
||||||
}
|
|
||||||
return (Func<MsgPackReader,T>)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<string,int> ());
|
|
||||||
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<string, int> 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<string, int>)));
|
|
||||||
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<string,int>).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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Exception Helper</summary>
|
|
||||||
static void UnpackFailed ()
|
|
||||||
{
|
|
||||||
throw new FormatException ();
|
|
||||||
}
|
|
||||||
|
|
||||||
static Dictionary<string, int> 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<MemberInfo> list = new List<MemberInfo> ();
|
|
||||||
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 (\<.+\>) <ab>
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
@ -42,7 +42,7 @@
|
|||||||
<Compile Include="BoxingPacker.cs" />
|
<Compile Include="BoxingPacker.cs" />
|
||||||
<Compile Include="CompiledPacker.cs" />
|
<Compile Include="CompiledPacker.cs" />
|
||||||
<Compile Include="Compiler\EmitExtensions.cs" />
|
<Compile Include="Compiler\EmitExtensions.cs" />
|
||||||
<Compile Include="Compiler\PackerCompiler.cs" />
|
<Compile Include="Compiler\PackILGenerator.cs" />
|
||||||
<Compile Include="Compiler\Variable.cs" />
|
<Compile Include="Compiler\Variable.cs" />
|
||||||
<Compile Include="Compiler\VariableType.cs" />
|
<Compile Include="Compiler\VariableType.cs" />
|
||||||
<Compile Include="MsgPackReader.cs" />
|
<Compile Include="MsgPackReader.cs" />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user