comparison 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
comparison
equal deleted inserted replaced
8:d132d442dc34 9:e6ad9016601c
1 //
2 // Copyright 2011 Kazuki Oikawa
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 using System;
18 using System.Collections.Generic;
19 using System.Reflection;
20 using System.Reflection.Emit;
21 using System.Runtime.Serialization;
22
23 namespace MsgPack.Compiler
24 {
25 static class PackILGenerator
26 {
27 #region Pack IL Generator
28 public static void EmitPackCode (Type type, MethodInfo mi, ILGenerator il,
29 Func<Type,MemberInfo[]> targetMemberSelector,
30 Func<MemberInfo,string> memberNameFormatter,
31 Func<Type, MethodInfo> lookupPackMethod)
32 {
33 if (type.IsPrimitive || type.IsInterface)
34 throw new NotSupportedException ();
35
36 Variable arg_writer = Variable.CreateArg (0);
37 Variable arg_obj = Variable.CreateArg (1);
38 Variable local_i = Variable.CreateLocal (il.DeclareLocal (typeof (int)));
39
40 if (!type.IsValueType) { // null check
41 Label notNullLabel = il.DefineLabel ();
42 il.EmitLd (arg_obj);
43 il.Emit (OpCodes.Brtrue_S, notNullLabel);
44 il.EmitLd (arg_writer);
45 il.Emit (OpCodes.Call, typeof(MsgPackWriter).GetMethod("WriteNil", new Type[0]));
46 il.Emit (OpCodes.Ret);
47 il.MarkLabel (notNullLabel);
48 }
49
50 if (type.IsArray) {
51 EmitPackArrayCode (mi, il, type, arg_writer, arg_obj, local_i, lookupPackMethod);
52 goto FinallyProcess;
53 }
54
55 // MsgPackWriter.WriteMapHeader
56 MemberInfo[] members = targetMemberSelector (type);
57 il.EmitLd (arg_writer);
58 il.EmitLdc (members.Length);
59 il.Emit (OpCodes.Callvirt, typeof (MsgPackWriter).GetMethod("WriteMapHeader", new Type[]{typeof (int)}));
60
61 for (int i = 0; i < members.Length; i ++) {
62 MemberInfo m = members[i];
63 Type mt = m.GetMemberType ();
64
65 // write field-name
66 il.EmitLd (arg_writer);
67 il.EmitLdstr (memberNameFormatter (m));
68 il.EmitLd_True ();
69 il.Emit (OpCodes.Call, typeof (MsgPackWriter).GetMethod("Write", new Type[]{typeof (string), typeof (bool)}));
70
71 // write value
72 EmitPackMemberValueCode (mt, il, arg_writer, arg_obj, m, null, type, mi, lookupPackMethod);
73 }
74
75 FinallyProcess:
76 il.Emit (OpCodes.Ret);
77 }
78
79 static void EmitPackArrayCode (MethodInfo mi, ILGenerator il, Type t, Variable var_writer, Variable var_obj, Variable var_loop, Func<Type, MethodInfo> lookupPackMethod)
80 {
81 Type et = t.GetElementType ();
82 il.EmitLd (var_writer, var_obj);
83 il.Emit (OpCodes.Ldlen);
84 il.Emit (OpCodes.Call, typeof(MsgPackWriter).GetMethod("WriteArrayHeader", new Type[]{ typeof(int) }));
85
86 Label beginLabel = il.DefineLabel ();
87 Label exprLabel = il.DefineLabel ();
88
89 // for-loop: init loop counter
90 il.EmitLdc (0);
91 il.EmitSt (var_loop);
92
93 // jump
94 il.Emit (OpCodes.Br_S, exprLabel);
95
96 // mark begin-label
97 il.MarkLabel (beginLabel);
98
99 // write element
100 EmitPackMemberValueCode (et, il, var_writer, var_obj, null, var_loop, t, mi, lookupPackMethod);
101
102 // increment loop-counter
103 il.EmitLd (var_loop);
104 il.Emit (OpCodes.Ldc_I4_1);
105 il.Emit (OpCodes.Add);
106 il.EmitSt (var_loop);
107
108 // mark expression label
109 il.MarkLabel (exprLabel);
110
111 // expression
112 il.EmitLd (var_loop, var_obj);
113 il.Emit (OpCodes.Ldlen);
114 il.Emit (OpCodes.Blt_S, beginLabel);
115 }
116
117 /// <param name="m">(optional)</param>
118 /// <param name="elementIdx">(optional)</param>
119 static void EmitPackMemberValueCode (Type type, ILGenerator il, Variable var_writer, Variable var_obj,
120 MemberInfo m, Variable elementIdx, Type currentType, MethodInfo currentMethod, Func<Type, MethodInfo> lookupPackMethod)
121 {
122 MethodInfo mi;
123 il.EmitLd (var_writer, var_obj);
124 if (m != null)
125 il.EmitLdMember (m);
126 if (elementIdx != null) {
127 il.EmitLd (elementIdx);
128 il.Emit (OpCodes.Ldelem, type);
129 }
130 if (type.IsPrimitive) {
131 mi = typeof(MsgPackWriter).GetMethod("Write", new Type[]{type});
132 } else {
133 if (currentType == type) {
134 mi = currentMethod;
135 } else {
136 mi = lookupPackMethod (type);
137 }
138 }
139 il.Emit (OpCodes.Call, mi);
140 }
141 #endregion
142
143 #region Unpack IL Generator
144 public static void EmitUnpackCode (Type type, MethodInfo mi, ILGenerator il,
145 Func<Type,MemberInfo[]> targetMemberSelector,
146 Func<MemberInfo,string> memberNameFormatter,
147 Func<Type, MethodInfo> lookupUnpackMethod,
148 Func<Type, IDictionary<string,int>> lookupMemberMapping,
149 MethodInfo lookupMemberMappingMethod)
150 {
151 if (type.IsArray) {
152 EmitUnpackArrayCode (type, mi, il, targetMemberSelector, memberNameFormatter, lookupUnpackMethod);
153 } else {
154 EmitUnpackMapCode (type, mi, il, targetMemberSelector, memberNameFormatter, lookupUnpackMethod, lookupMemberMapping, lookupMemberMappingMethod);
155 }
156 }
157
158 static void EmitUnpackMapCode (Type type, MethodInfo mi, ILGenerator il,
159 Func<Type,MemberInfo[]> targetMemberSelector,
160 Func<MemberInfo,string> memberNameFormatter,
161 Func<Type, MethodInfo> lookupUnpackMethod,
162 Func<Type, IDictionary<string,int>> lookupMemberMapping,
163 MethodInfo lookupMemberMappingMethod)
164 {
165 MethodInfo failedMethod = typeof (PackILGenerator).GetMethod ("UnpackFailed", BindingFlags.Static | BindingFlags.NonPublic);
166 MemberInfo[] members = targetMemberSelector (type);
167 IDictionary<string, int> member_mapping = lookupMemberMapping (type);
168 for (int i = 0; i < members.Length; i ++)
169 member_mapping.Add (memberNameFormatter (members[i]), i);
170
171 Variable msgpackReader = Variable.CreateArg (0);
172 Variable obj = Variable.CreateLocal (il.DeclareLocal (type));
173 Variable num_of_fields = Variable.CreateLocal (il.DeclareLocal (typeof (int)));
174 Variable loop_idx = Variable.CreateLocal (il.DeclareLocal (typeof (int)));
175 Variable mapping = Variable.CreateLocal (il.DeclareLocal (typeof (IDictionary<string, int>)));
176 Variable switch_idx = Variable.CreateLocal (il.DeclareLocal (typeof (int)));
177 Variable var_type = Variable.CreateLocal (il.DeclareLocal (typeof (Type)));
178
179 // if (!MsgPackReader.Read()) UnpackFailed ();
180 // if (MsgPackReader.Type == TypePrefixes.Nil) return null;
181 // if (!MsgPackReader.IsMap ()) UnpackFailed ();
182 EmitUnpackReadAndTypeCheckCode (il, msgpackReader, typeof (MsgPackReader).GetMethod ("IsMap"), failedMethod, true);
183
184 // type = typeof (T)
185 il.Emit (OpCodes.Ldtoken, type);
186 il.Emit (OpCodes.Call, typeof(Type).GetMethod ("GetTypeFromHandle"));
187 il.EmitSt (var_type);
188
189 // mapping = LookupMemberMapping (typeof (T))
190 il.EmitLd (var_type);
191 il.Emit (OpCodes.Call, lookupMemberMappingMethod);
192 il.EmitSt (mapping);
193
194 // object o = FormatterServices.GetUninitializedObject (Type);
195 il.EmitLd (var_type);
196 il.Emit (OpCodes.Call, typeof (FormatterServices).GetMethod ("GetUninitializedObject"));
197 il.Emit (OpCodes.Castclass, type);
198 il.EmitSt (obj);
199
200 // num_of_fields = (int)reader.Length
201 il.EmitLd (msgpackReader);
202 il.Emit (OpCodes.Call, typeof (MsgPackReader).GetProperty ("Length").GetGetMethod ());
203 il.EmitSt (num_of_fields);
204
205 // Loop labels
206 Label lblLoopStart = il.DefineLabel ();
207 Label lblLoopExpr = il.DefineLabel ();
208
209 // i = 0;
210 il.EmitLdc (0);
211 il.EmitSt (loop_idx);
212 il.Emit (OpCodes.Br, lblLoopExpr);
213 il.MarkLabel (lblLoopStart);
214
215 /* process */
216 // if (!MsgPackReader.Read() || !MsgPackReader.IsRaw()) UnpackFailed();
217 EmitUnpackReadAndTypeCheckCode (il, msgpackReader, typeof (MsgPackReader).GetMethod ("IsRaw"), failedMethod, false);
218
219 // MsgPackReader.ReadRawString ()
220 // if (!Dictionary.TryGetValue (,)) UnpackFailed();
221 Label lbl3 = il.DefineLabel ();
222 il.EmitLd (mapping);
223 il.EmitLd (msgpackReader);
224 il.Emit (OpCodes.Call, typeof (MsgPackReader).GetMethod ("ReadRawString", new Type[0]));
225 il.Emit (OpCodes.Ldloca_S, (byte)switch_idx.Index);
226 il.Emit (OpCodes.Callvirt, typeof (IDictionary<string,int>).GetMethod ("TryGetValue"));
227 il.Emit (OpCodes.Brtrue, lbl3);
228 il.Emit (OpCodes.Call, failedMethod);
229 il.MarkLabel (lbl3);
230
231 // switch
232 Label[] switchCases = new Label[members.Length];
233 for (int i = 0; i < switchCases.Length; i ++)
234 switchCases[i] = il.DefineLabel ();
235 Label switchCaseEndLabel = il.DefineLabel ();
236 il.EmitLd (switch_idx);
237 il.Emit (OpCodes.Switch, switchCases);
238 il.Emit (OpCodes.Call, failedMethod);
239
240 for (int i = 0; i < switchCases.Length; i ++) {
241 il.MarkLabel (switchCases[i]);
242 MemberInfo minfo = members[i];
243 Type mt = minfo.GetMemberType ();
244 MethodInfo unpack_method = lookupUnpackMethod (mt);
245 il.EmitLd (obj);
246 il.EmitLd (msgpackReader);
247 il.Emit (OpCodes.Call, unpack_method);
248 il.EmitStMember (minfo);
249 il.Emit (OpCodes.Br, switchCaseEndLabel);
250 }
251 il.MarkLabel (switchCaseEndLabel);
252
253 // i ++
254 il.EmitLd (loop_idx);
255 il.EmitLdc (1);
256 il.Emit (OpCodes.Add);
257 il.EmitSt (loop_idx);
258
259 // i < num_of_fields;
260 il.MarkLabel (lblLoopExpr);
261 il.EmitLd (loop_idx);
262 il.EmitLd (num_of_fields);
263 il.Emit (OpCodes.Blt, lblLoopStart);
264
265 // return
266 il.EmitLd (obj);
267 il.Emit (OpCodes.Ret);
268 }
269
270 static void EmitUnpackArrayCode (Type arrayType, MethodInfo mi, ILGenerator il,
271 Func<Type,MemberInfo[]> targetMemberSelector,
272 Func<MemberInfo,string> memberNameFormatter,
273 Func<Type, MethodInfo> lookupUnpackMethod)
274 {
275 Type elementType = arrayType.GetElementType ();
276 MethodInfo failedMethod = typeof (PackILGenerator).GetMethod ("UnpackFailed", BindingFlags.Static | BindingFlags.NonPublic);
277
278 Variable msgpackReader = Variable.CreateArg (0);
279 Variable obj = Variable.CreateLocal (il.DeclareLocal (arrayType));
280 Variable num_of_elements = Variable.CreateLocal (il.DeclareLocal (typeof (int)));
281 Variable loop_idx = Variable.CreateLocal (il.DeclareLocal (typeof (int)));
282 Variable type = Variable.CreateLocal (il.DeclareLocal (typeof (Type)));
283
284 // if (!MsgPackReader.Read() || !MsgPackReader.IsArray ()) UnpackFailed ();
285 EmitUnpackReadAndTypeCheckCode (il, msgpackReader, typeof (MsgPackReader).GetMethod ("IsArray"), failedMethod, true);
286
287 // type = typeof (T)
288 il.Emit (OpCodes.Ldtoken, elementType);
289 il.Emit (OpCodes.Call, typeof(Type).GetMethod ("GetTypeFromHandle"));
290 il.EmitSt (type);
291
292 // num_of_elements = (int)reader.Length
293 il.EmitLd (msgpackReader);
294 il.Emit (OpCodes.Call, typeof (MsgPackReader).GetProperty ("Length").GetGetMethod ());
295 il.EmitSt (num_of_elements);
296
297 // object o = Array.CreateInstance (Type, Length);
298 il.EmitLd (type);
299 il.EmitLd (num_of_elements);
300 il.Emit (OpCodes.Call, typeof (Array).GetMethod ("CreateInstance", new Type[] {typeof (Type), typeof (int)}));
301 il.Emit (OpCodes.Castclass, arrayType);
302 il.EmitSt (obj);
303
304 // Unpack element method
305 MethodInfo unpack_method = lookupUnpackMethod (elementType);
306
307 // Loop labels
308 Label lblLoopStart = il.DefineLabel ();
309 Label lblLoopExpr = il.DefineLabel ();
310
311 // i = 0;
312 il.EmitLdc (0);
313 il.EmitSt (loop_idx);
314 il.Emit (OpCodes.Br, lblLoopExpr);
315 il.MarkLabel (lblLoopStart);
316
317 /* process */
318 il.EmitLd (obj, loop_idx);
319 il.EmitLd (msgpackReader);
320 il.Emit (OpCodes.Call, unpack_method);
321 il.Emit (OpCodes.Stelem, elementType);
322
323 // i ++
324 il.EmitLd (loop_idx);
325 il.EmitLdc (1);
326 il.Emit (OpCodes.Add);
327 il.EmitSt (loop_idx);
328
329 // i < num_of_fields;
330 il.MarkLabel (lblLoopExpr);
331 il.EmitLd (loop_idx);
332 il.EmitLd (num_of_elements);
333 il.Emit (OpCodes.Blt, lblLoopStart);
334
335 // return
336 il.EmitLd (obj);
337 il.Emit (OpCodes.Ret);
338 }
339
340 static void EmitUnpackReadAndTypeCheckCode (ILGenerator il, Variable msgpackReader, MethodInfo typeCheckMethod, MethodInfo failedMethod, bool nullCheckAndReturn)
341 {
342 Label lblFailed = il.DefineLabel ();
343 Label lblNullReturn = nullCheckAndReturn ? il.DefineLabel () : default(Label);
344 Label lblPassed = il.DefineLabel ();
345 il.EmitLd (msgpackReader);
346 il.Emit (OpCodes.Call, typeof (MsgPackReader).GetMethod ("Read"));
347 il.Emit (OpCodes.Brfalse_S, lblFailed);
348 if (nullCheckAndReturn) {
349 il.EmitLd (msgpackReader);
350 il.Emit (OpCodes.Call, typeof (MsgPackReader).GetProperty ("Type").GetGetMethod ());
351 il.EmitLdc ((int)TypePrefixes.Nil);
352 il.Emit (OpCodes.Beq_S, lblNullReturn);
353 }
354 il.EmitLd (msgpackReader);
355 il.Emit (OpCodes.Call, typeCheckMethod);
356 il.Emit (OpCodes.Brtrue_S, lblPassed);
357 il.Emit (OpCodes.Br, lblFailed);
358 if (nullCheckAndReturn) {
359 il.MarkLabel (lblNullReturn);
360 il.Emit (OpCodes.Ldnull);
361 il.Emit (OpCodes.Ret);
362 }
363 il.MarkLabel (lblFailed);
364 il.Emit (OpCodes.Call, failedMethod);
365 il.MarkLabel (lblPassed);
366 }
367
368 /// <summary>Exception Helper</summary>
369 internal static void UnpackFailed ()
370 {
371 throw new FormatException ();
372 }
373 #endregion
374
375 #region Misc
376 static Type GetMemberType (this MemberInfo mi)
377 {
378 if (mi.MemberType == MemberTypes.Field)
379 return ((FieldInfo)mi).FieldType;
380 if (mi.MemberType == MemberTypes.Property)
381 return ((PropertyInfo)mi).PropertyType;
382 throw new ArgumentException ();
383 }
384 #endregion
385 }
386 }