Mercurial > hg > Database > jungle-sharp
diff src/main/csharp/jp.ac.u-ryukyu.ie.cr/jungle/persistent/msgpack/src/Compiler/PackILGenerator.cs @ 9:e6ad9016601c
Add Msgpack for Unity.
author | Kazuma |
---|---|
date | Sun, 23 Oct 2016 04:37:16 +0900 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/csharp/jp.ac.u-ryukyu.ie.cr/jungle/persistent/msgpack/src/Compiler/PackILGenerator.cs Sun Oct 23 04:37:16 2016 +0900 @@ -0,0 +1,386 @@ +// +// Copyright 2011 Kazuki Oikawa +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +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 + } +}