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
+	}
+}