~do-win/do/test-paths

« back to all changes in this revision

Viewing changes to Mono.Addins.CecilReflector/Mono.Cecil/Mono.Cecil.Cil/CodeWriter.cs

  • Committer: Chris S.
  • Date: 2009-06-21 03:37:34 UTC
  • Revision ID: chris@szikszoy.com-20090621033734-ud2jdcd5pq9r3ue9
initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//
 
2
// CodeWriter.cs
 
3
//
 
4
// Author:
 
5
//   Jb Evain (jbevain@gmail.com)
 
6
//
 
7
// (C) 2005 - 2007 Jb Evain
 
8
//
 
9
// Permission is hereby granted, free of charge, to any person obtaining
 
10
// a copy of this software and associated documentation files (the
 
11
// "Software"), to deal in the Software without restriction, including
 
12
// without limitation the rights to use, copy, modify, merge, publish,
 
13
// distribute, sublicense, and/or sell copies of the Software, and to
 
14
// permit persons to whom the Software is furnished to do so, subject to
 
15
// the following conditions:
 
16
//
 
17
// The above copyright notice and this permission notice shall be
 
18
// included in all copies or substantial portions of the Software.
 
19
//
 
20
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 
21
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 
22
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 
23
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 
24
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 
25
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 
26
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
27
//
 
28
 
 
29
namespace Mono.Cecil.Cil {
 
30
 
 
31
        using System;
 
32
        using System.Collections;
 
33
 
 
34
        using Mono.Cecil;
 
35
        using Mono.Cecil.Binary;
 
36
        using Mono.Cecil.Metadata;
 
37
        using Mono.Cecil.Signatures;
 
38
 
 
39
        class CodeWriter : BaseCodeVisitor {
 
40
 
 
41
                ReflectionWriter m_reflectWriter;
 
42
                MemoryBinaryWriter m_binaryWriter;
 
43
                MemoryBinaryWriter m_codeWriter;
 
44
 
 
45
                IDictionary m_localSigCache;
 
46
                IDictionary m_standaloneSigCache;
 
47
 
 
48
                public CodeWriter (ReflectionWriter reflectWriter, MemoryBinaryWriter writer)
 
49
                {
 
50
                        m_reflectWriter = reflectWriter;
 
51
                        m_binaryWriter = writer;
 
52
                        m_codeWriter = new MemoryBinaryWriter ();
 
53
 
 
54
                        m_localSigCache = new Hashtable ();
 
55
                        m_standaloneSigCache = new Hashtable ();
 
56
                }
 
57
 
 
58
                public RVA WriteMethodBody (MethodDefinition meth)
 
59
                {
 
60
                        if (meth.Body == null)
 
61
                                return RVA.Zero;
 
62
 
 
63
                        RVA ret = m_reflectWriter.MetadataWriter.GetDataCursor ();
 
64
                        meth.Body.Accept (this);
 
65
                        return ret;
 
66
                }
 
67
 
 
68
                public override void VisitMethodBody (MethodBody body)
 
69
                {
 
70
                        m_codeWriter.Empty ();
 
71
                }
 
72
 
 
73
                void WriteToken (MetadataToken token)
 
74
                {
 
75
                        if (token.RID == 0)
 
76
                                m_codeWriter.Write (0);
 
77
                        else
 
78
                                m_codeWriter.Write (token.ToUInt ());
 
79
                }
 
80
 
 
81
                static int GetParameterIndex (MethodBody body, ParameterDefinition p)
 
82
                {
 
83
                        int idx = body.Method.Parameters.IndexOf (p);
 
84
                        if (idx == -1 && p == body.Method.This)
 
85
                                return 0;
 
86
                        if (body.Method.HasThis)
 
87
                                idx++;
 
88
 
 
89
                        return idx;
 
90
                }
 
91
 
 
92
                public override void VisitInstructionCollection (InstructionCollection instructions)
 
93
                {
 
94
                        MethodBody body = instructions.Container;
 
95
                        long start = m_codeWriter.BaseStream.Position;
 
96
 
 
97
                        ComputeMaxStack (instructions);
 
98
 
 
99
                        foreach (Instruction instr in instructions) {
 
100
 
 
101
                                instr.Offset = (int) (m_codeWriter.BaseStream.Position - start);
 
102
 
 
103
                                if (instr.OpCode.Size == 1)
 
104
                                        m_codeWriter.Write (instr.OpCode.Op2);
 
105
                                else {
 
106
                                        m_codeWriter.Write (instr.OpCode.Op1);
 
107
                                        m_codeWriter.Write (instr.OpCode.Op2);
 
108
                                }
 
109
 
 
110
                                if (instr.OpCode.OperandType != OperandType.InlineNone &&
 
111
                                        instr.Operand == null)
 
112
                                        throw new ReflectionException ("OpCode {0} have null operand", instr.OpCode.Name);
 
113
 
 
114
                                switch (instr.OpCode.OperandType) {
 
115
                                case OperandType.InlineNone :
 
116
                                        break;
 
117
                                case OperandType.InlineSwitch :
 
118
                                        Instruction [] targets = (Instruction []) instr.Operand;
 
119
                                        for (int i = 0; i < targets.Length + 1; i++)
 
120
                                                m_codeWriter.Write ((uint) 0);
 
121
                                        break;
 
122
                                case OperandType.ShortInlineBrTarget :
 
123
                                        m_codeWriter.Write ((byte) 0);
 
124
                                        break;
 
125
                                case OperandType.InlineBrTarget :
 
126
                                        m_codeWriter.Write (0);
 
127
                                        break;
 
128
                                case OperandType.ShortInlineI :
 
129
                                        if (instr.OpCode == OpCodes.Ldc_I4_S)
 
130
                                                m_codeWriter.Write ((sbyte) instr.Operand);
 
131
                                        else
 
132
                                                m_codeWriter.Write ((byte) instr.Operand);
 
133
                                        break;
 
134
                                case OperandType.ShortInlineVar :
 
135
                                        m_codeWriter.Write ((byte) body.Variables.IndexOf (
 
136
                                                (VariableDefinition) instr.Operand));
 
137
                                        break;
 
138
                                case OperandType.ShortInlineParam :
 
139
                                        m_codeWriter.Write ((byte) GetParameterIndex (body, (ParameterDefinition) instr.Operand));
 
140
                                        break;
 
141
                                case OperandType.InlineSig :
 
142
                                        WriteToken (GetCallSiteToken ((CallSite) instr.Operand));
 
143
                                        break;
 
144
                                case OperandType.InlineI :
 
145
                                        m_codeWriter.Write ((int) instr.Operand);
 
146
                                        break;
 
147
                                case OperandType.InlineVar :
 
148
                                        m_codeWriter.Write ((short) body.Variables.IndexOf (
 
149
                                                (VariableDefinition) instr.Operand));
 
150
                                        break;
 
151
                                case OperandType.InlineParam :
 
152
                                        m_codeWriter.Write ((short) GetParameterIndex (
 
153
                                                        body, (ParameterDefinition) instr.Operand));
 
154
                                        break;
 
155
                                case OperandType.InlineI8 :
 
156
                                        m_codeWriter.Write ((long) instr.Operand);
 
157
                                        break;
 
158
                                case OperandType.ShortInlineR :
 
159
                                        m_codeWriter.Write ((float) instr.Operand);
 
160
                                        break;
 
161
                                case OperandType.InlineR :
 
162
                                        m_codeWriter.Write ((double) instr.Operand);
 
163
                                        break;
 
164
                                case OperandType.InlineString :
 
165
                                        WriteToken (new MetadataToken (TokenType.String,
 
166
                                                        m_reflectWriter.MetadataWriter.AddUserString (instr.Operand as string)));
 
167
                                        break;
 
168
                                case OperandType.InlineField :
 
169
                                case OperandType.InlineMethod :
 
170
                                case OperandType.InlineType :
 
171
                                case OperandType.InlineTok :
 
172
                                        if (instr.Operand is TypeReference)
 
173
                                                WriteToken (m_reflectWriter.GetTypeDefOrRefToken (
 
174
                                                                instr.Operand as TypeReference));
 
175
                                        else if (instr.Operand is GenericInstanceMethod)
 
176
                                                WriteToken (m_reflectWriter.GetMethodSpecToken (instr.Operand as GenericInstanceMethod));
 
177
                                        else if (instr.Operand is MemberReference)
 
178
                                                WriteToken (m_reflectWriter.GetMemberRefToken ((MemberReference) instr.Operand));
 
179
                                        else if (instr.Operand is IMetadataTokenProvider)
 
180
                                                WriteToken (((IMetadataTokenProvider) instr.Operand).MetadataToken);
 
181
                                        else
 
182
                                                throw new ReflectionException (
 
183
                                                        string.Format ("Wrong operand for {0} OpCode: {1}",
 
184
                                                                instr.OpCode.OperandType,
 
185
                                                                instr.Operand.GetType ().FullName));
 
186
                                        break;
 
187
                                }
 
188
                        }
 
189
 
 
190
                        // patch branches
 
191
                        long pos = m_codeWriter.BaseStream.Position;
 
192
 
 
193
                        foreach (Instruction instr in instructions) {
 
194
                                switch (instr.OpCode.OperandType) {
 
195
                                case OperandType.InlineSwitch :
 
196
                                        m_codeWriter.BaseStream.Position = instr.Offset + instr.OpCode.Size;
 
197
                                        Instruction [] targets = (Instruction []) instr.Operand;
 
198
                                        m_codeWriter.Write ((uint) targets.Length);
 
199
                                        foreach (Instruction tgt in targets)
 
200
                                                m_codeWriter.Write ((tgt.Offset - (instr.Offset +
 
201
                                                        instr.OpCode.Size + (4 * (targets.Length + 1)))));
 
202
                                        break;
 
203
                                case OperandType.ShortInlineBrTarget :
 
204
                                        m_codeWriter.BaseStream.Position = instr.Offset + instr.OpCode.Size;
 
205
                                        m_codeWriter.Write ((byte) (((Instruction) instr.Operand).Offset -
 
206
                                                (instr.Offset + instr.OpCode.Size + 1)));
 
207
                                        break;
 
208
                                case OperandType.InlineBrTarget :
 
209
                                        m_codeWriter.BaseStream.Position = instr.Offset + instr.OpCode.Size;
 
210
                                        m_codeWriter.Write(((Instruction) instr.Operand).Offset -
 
211
                                                (instr.Offset + instr.OpCode.Size + 4));
 
212
                                        break;
 
213
                                }
 
214
                        }
 
215
 
 
216
                        m_codeWriter.BaseStream.Position = pos;
 
217
                }
 
218
 
 
219
                MetadataToken GetCallSiteToken (CallSite cs)
 
220
                {
 
221
                        uint sig;
 
222
                        int sentinel = cs.GetSentinel ();
 
223
                        if (sentinel > 0)
 
224
                                sig = m_reflectWriter.SignatureWriter.AddMethodDefSig (
 
225
                                        m_reflectWriter.GetMethodDefSig (cs));
 
226
                        else
 
227
                                sig = m_reflectWriter.SignatureWriter.AddMethodRefSig (
 
228
                                        m_reflectWriter.GetMethodRefSig (cs));
 
229
 
 
230
                        if (m_standaloneSigCache.Contains (sig))
 
231
                                return (MetadataToken) m_standaloneSigCache [sig];
 
232
 
 
233
                        StandAloneSigTable sasTable = m_reflectWriter.MetadataTableWriter.GetStandAloneSigTable ();
 
234
                        StandAloneSigRow sasRow = m_reflectWriter.MetadataRowWriter.CreateStandAloneSigRow (sig);
 
235
 
 
236
                        sasTable.Rows.Add(sasRow);
 
237
 
 
238
                        MetadataToken token = new MetadataToken (TokenType.Signature, (uint) sasTable.Rows.Count);
 
239
                        m_standaloneSigCache [sig] = token;
 
240
                        return token;
 
241
                }
 
242
 
 
243
                static int GetLength (Instruction start, Instruction end, InstructionCollection instructions)
 
244
                {
 
245
                        Instruction last = instructions [instructions.Count - 1];
 
246
                        return (end == instructions.Outside ? last.Offset + last.GetSize () : end.Offset) - start.Offset;
 
247
                }
 
248
 
 
249
                static bool IsRangeFat (Instruction start, Instruction end, InstructionCollection instructions)
 
250
                {
 
251
                        return GetLength (start, end, instructions) >= 256 ||
 
252
                                start.Offset >= 65536;
 
253
                }
 
254
 
 
255
                static bool IsFat (ExceptionHandlerCollection seh)
 
256
                {
 
257
                        for (int i = 0; i < seh.Count; i++) {
 
258
                                ExceptionHandler eh = seh [i];
 
259
                                if (IsRangeFat (eh.TryStart, eh.TryEnd, seh.Container.Instructions))
 
260
                                        return true;
 
261
 
 
262
                                if (IsRangeFat (eh.HandlerStart, eh.HandlerEnd, seh.Container.Instructions))
 
263
                                        return true;
 
264
 
 
265
                                switch (eh.Type) {
 
266
                                case ExceptionHandlerType.Filter :
 
267
                                        if (IsRangeFat (eh.FilterStart, eh.FilterEnd, seh.Container.Instructions))
 
268
                                                return true;
 
269
                                        break;
 
270
                                }
 
271
                        }
 
272
 
 
273
                        return false;
 
274
                }
 
275
 
 
276
                void WriteExceptionHandlerCollection (ExceptionHandlerCollection seh)
 
277
                {
 
278
                        m_codeWriter.QuadAlign ();
 
279
 
 
280
                        if (seh.Count < 0x15 && !IsFat (seh)) {
 
281
                                m_codeWriter.Write ((byte) MethodDataSection.EHTable);
 
282
                                m_codeWriter.Write ((byte) (seh.Count * 12 + 4));
 
283
                                m_codeWriter.Write (new byte [2]);
 
284
                                foreach (ExceptionHandler eh in seh) {
 
285
                                        m_codeWriter.Write ((ushort) eh.Type);
 
286
                                        m_codeWriter.Write ((ushort) eh.TryStart.Offset);
 
287
                                        m_codeWriter.Write ((byte) (eh.TryEnd.Offset - eh.TryStart.Offset));
 
288
                                        m_codeWriter.Write ((ushort) eh.HandlerStart.Offset);
 
289
                                        m_codeWriter.Write ((byte) GetLength (eh.HandlerStart, eh.HandlerEnd, seh.Container.Instructions));
 
290
                                        WriteHandlerSpecific (eh);
 
291
                                }
 
292
                        } else {
 
293
                                m_codeWriter.Write ((byte) (MethodDataSection.FatFormat | MethodDataSection.EHTable));
 
294
                                WriteFatBlockSize (seh);
 
295
                                foreach (ExceptionHandler eh in seh) {
 
296
                                        m_codeWriter.Write ((uint) eh.Type);
 
297
                                        m_codeWriter.Write ((uint) eh.TryStart.Offset);
 
298
                                        m_codeWriter.Write ((uint) (eh.TryEnd.Offset - eh.TryStart.Offset));
 
299
                                        m_codeWriter.Write ((uint) eh.HandlerStart.Offset);
 
300
                                        m_codeWriter.Write ((uint) GetLength (eh.HandlerStart, eh.HandlerEnd, seh.Container.Instructions));
 
301
                                        WriteHandlerSpecific (eh);
 
302
                                }
 
303
                        }
 
304
                }
 
305
 
 
306
                void WriteFatBlockSize (ExceptionHandlerCollection seh)
 
307
                {
 
308
                        int size = seh.Count * 24 + 4;
 
309
                        m_codeWriter.Write ((byte) (size & 0xff));
 
310
                        m_codeWriter.Write ((byte) ((size >> 8) & 0xff));
 
311
                        m_codeWriter.Write ((byte) ((size >> 16) & 0xff));
 
312
                }
 
313
 
 
314
                void WriteHandlerSpecific (ExceptionHandler eh)
 
315
                {
 
316
                        switch (eh.Type) {
 
317
                        case ExceptionHandlerType.Catch :
 
318
                                WriteToken (eh.CatchType.MetadataToken);
 
319
                                break;
 
320
                        case ExceptionHandlerType.Filter :
 
321
                                m_codeWriter.Write ((uint) eh.FilterStart.Offset);
 
322
                                break;
 
323
                        default :
 
324
                                m_codeWriter.Write (0);
 
325
                                break;
 
326
                        }
 
327
                }
 
328
 
 
329
                public override void VisitVariableDefinitionCollection (VariableDefinitionCollection variables)
 
330
                {
 
331
                        MethodBody body = variables.Container as MethodBody;
 
332
                        if (body == null)
 
333
                                return;
 
334
 
 
335
                        uint sig = m_reflectWriter.SignatureWriter.AddLocalVarSig (
 
336
                                        GetLocalVarSig (variables));
 
337
 
 
338
                        if (m_localSigCache.Contains (sig)) {
 
339
                                body.LocalVarToken = (int) m_localSigCache [sig];
 
340
                                return;
 
341
                        }
 
342
 
 
343
                        StandAloneSigTable sasTable = m_reflectWriter.MetadataTableWriter.GetStandAloneSigTable ();
 
344
                        StandAloneSigRow sasRow = m_reflectWriter.MetadataRowWriter.CreateStandAloneSigRow (
 
345
                                sig);
 
346
 
 
347
                        sasTable.Rows.Add (sasRow);
 
348
                        body.LocalVarToken = sasTable.Rows.Count;
 
349
                        m_localSigCache [sig] = body.LocalVarToken;
 
350
                }
 
351
 
 
352
                public override void TerminateMethodBody (MethodBody body)
 
353
                {
 
354
                        long pos = m_binaryWriter.BaseStream.Position;
 
355
 
 
356
                        if (body.Variables.Count > 0 || body.ExceptionHandlers.Count > 0
 
357
                                || m_codeWriter.BaseStream.Length >= 64 || body.MaxStack > 8) {
 
358
 
 
359
                                MethodHeader header = MethodHeader.FatFormat;
 
360
                                if (body.InitLocals)
 
361
                                        header |= MethodHeader.InitLocals;
 
362
                                if (body.ExceptionHandlers.Count > 0)
 
363
                                        header |= MethodHeader.MoreSects;
 
364
 
 
365
                                m_binaryWriter.Write ((byte) header);
 
366
                                m_binaryWriter.Write ((byte) 0x30); // (header size / 4) << 4
 
367
                                m_binaryWriter.Write ((short) body.MaxStack);
 
368
                                m_binaryWriter.Write ((int) m_codeWriter.BaseStream.Length);
 
369
                                m_binaryWriter.Write (((int) TokenType.Signature | body.LocalVarToken));
 
370
 
 
371
                                WriteExceptionHandlerCollection (body.ExceptionHandlers);
 
372
                        } else
 
373
                                m_binaryWriter.Write ((byte) ((byte) MethodHeader.TinyFormat |
 
374
                                        m_codeWriter.BaseStream.Length << 2));
 
375
 
 
376
                        m_binaryWriter.Write (m_codeWriter);
 
377
                        m_binaryWriter.QuadAlign ();
 
378
 
 
379
                        m_reflectWriter.MetadataWriter.AddData (
 
380
                                (int) (m_binaryWriter.BaseStream.Position - pos));
 
381
                }
 
382
 
 
383
                public LocalVarSig.LocalVariable GetLocalVariableSig (VariableDefinition var)
 
384
                {
 
385
                        LocalVarSig.LocalVariable lv = new LocalVarSig.LocalVariable ();
 
386
                        TypeReference type = var.VariableType;
 
387
 
 
388
                        lv.CustomMods = m_reflectWriter.GetCustomMods (type);
 
389
 
 
390
                        if (type is PinnedType) {
 
391
                                lv.Constraint |= Constraint.Pinned;
 
392
                                type = (type as PinnedType).ElementType;
 
393
                        }
 
394
 
 
395
                        if (type is ReferenceType) {
 
396
                                lv.ByRef = true;
 
397
                                type = (type as ReferenceType).ElementType;
 
398
                        }
 
399
 
 
400
                        lv.Type = m_reflectWriter.GetSigType (type);
 
401
 
 
402
                        return lv;
 
403
                }
 
404
 
 
405
                public LocalVarSig GetLocalVarSig (VariableDefinitionCollection vars)
 
406
                {
 
407
                        LocalVarSig lvs = new LocalVarSig ();
 
408
                        lvs.CallingConvention |= 0x7;
 
409
                        lvs.Count = vars.Count;
 
410
                        lvs.LocalVariables = new LocalVarSig.LocalVariable [lvs.Count];
 
411
                        for (int i = 0; i < lvs.Count; i++) {
 
412
                                lvs.LocalVariables [i] = GetLocalVariableSig (vars [i]);
 
413
                        }
 
414
 
 
415
                        return lvs;
 
416
                }
 
417
 
 
418
                static void ComputeMaxStack (InstructionCollection instructions)
 
419
                {
 
420
                        InstructionCollection ehs = new InstructionCollection (null);
 
421
                        foreach (ExceptionHandler eh in instructions.Container.ExceptionHandlers) {
 
422
                                switch (eh.Type) {
 
423
                                case ExceptionHandlerType.Catch :
 
424
                                        ehs.Add (eh.HandlerStart);
 
425
                                        break;
 
426
                                case ExceptionHandlerType.Filter :
 
427
                                        ehs.Add (eh.FilterStart);
 
428
                                        break;
 
429
                                }
 
430
                        }
 
431
 
 
432
                        int max = 0;
 
433
                        foreach (Instruction instr in instructions) {
 
434
                                if (ehs.Contains (instr))
 
435
                                        max++;
 
436
 
 
437
                                switch (instr.OpCode.StackBehaviourPush) {
 
438
                                case StackBehaviour.Push1:
 
439
                                case StackBehaviour.Pushi:
 
440
                                case StackBehaviour.Pushi8:
 
441
                                case StackBehaviour.Pushr4:
 
442
                                case StackBehaviour.Pushr8:
 
443
                                case StackBehaviour.Pushref:
 
444
                                case StackBehaviour.Varpush:
 
445
                                        max++;
 
446
                                        break;
 
447
                                case StackBehaviour.Push1_push1:
 
448
                                        max += 2;
 
449
                                        break;
 
450
                                }
 
451
 
 
452
                                if (instr.OpCode.OperandType == OperandType.InlineMethod) {
 
453
                                        IMethodSignature signature = instr.Operand as IMethodSignature;
 
454
                                        if (signature != null && signature.ReturnType.ReturnType.FullName != Constants.Void)
 
455
                                                max++;
 
456
                                }
 
457
                        }
 
458
 
 
459
                        instructions.Container.MaxStack = max;
 
460
                }
 
461
        }
 
462
}