Mercurial > hg > Database > jungle-sharp
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 } |