5
// Jb Evain (jbevain@gmail.com)
7
// (C) 2005 - 2007 Jb Evain
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:
17
// The above copyright notice and this permission notice shall be
18
// included in all copies or substantial portions of the Software.
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.
29
namespace Mono.Cecil.Cil {
32
using System.Collections;
35
using Mono.Cecil.Binary;
36
using Mono.Cecil.Metadata;
37
using Mono.Cecil.Signatures;
39
class CodeWriter : BaseCodeVisitor {
41
ReflectionWriter m_reflectWriter;
42
MemoryBinaryWriter m_binaryWriter;
43
MemoryBinaryWriter m_codeWriter;
45
IDictionary m_localSigCache;
46
IDictionary m_standaloneSigCache;
48
public CodeWriter (ReflectionWriter reflectWriter, MemoryBinaryWriter writer)
50
m_reflectWriter = reflectWriter;
51
m_binaryWriter = writer;
52
m_codeWriter = new MemoryBinaryWriter ();
54
m_localSigCache = new Hashtable ();
55
m_standaloneSigCache = new Hashtable ();
58
public RVA WriteMethodBody (MethodDefinition meth)
60
if (meth.Body == null)
63
RVA ret = m_reflectWriter.MetadataWriter.GetDataCursor ();
64
meth.Body.Accept (this);
68
public override void VisitMethodBody (MethodBody body)
70
m_codeWriter.Empty ();
73
void WriteToken (MetadataToken token)
76
m_codeWriter.Write (0);
78
m_codeWriter.Write (token.ToUInt ());
81
static int GetParameterIndex (MethodBody body, ParameterDefinition p)
83
int idx = body.Method.Parameters.IndexOf (p);
84
if (idx == -1 && p == body.Method.This)
86
if (body.Method.HasThis)
92
public override void VisitInstructionCollection (InstructionCollection instructions)
94
MethodBody body = instructions.Container;
95
long start = m_codeWriter.BaseStream.Position;
97
ComputeMaxStack (instructions);
99
foreach (Instruction instr in instructions) {
101
instr.Offset = (int) (m_codeWriter.BaseStream.Position - start);
103
if (instr.OpCode.Size == 1)
104
m_codeWriter.Write (instr.OpCode.Op2);
106
m_codeWriter.Write (instr.OpCode.Op1);
107
m_codeWriter.Write (instr.OpCode.Op2);
110
if (instr.OpCode.OperandType != OperandType.InlineNone &&
111
instr.Operand == null)
112
throw new ReflectionException ("OpCode {0} have null operand", instr.OpCode.Name);
114
switch (instr.OpCode.OperandType) {
115
case OperandType.InlineNone :
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);
122
case OperandType.ShortInlineBrTarget :
123
m_codeWriter.Write ((byte) 0);
125
case OperandType.InlineBrTarget :
126
m_codeWriter.Write (0);
128
case OperandType.ShortInlineI :
129
if (instr.OpCode == OpCodes.Ldc_I4_S)
130
m_codeWriter.Write ((sbyte) instr.Operand);
132
m_codeWriter.Write ((byte) instr.Operand);
134
case OperandType.ShortInlineVar :
135
m_codeWriter.Write ((byte) body.Variables.IndexOf (
136
(VariableDefinition) instr.Operand));
138
case OperandType.ShortInlineParam :
139
m_codeWriter.Write ((byte) GetParameterIndex (body, (ParameterDefinition) instr.Operand));
141
case OperandType.InlineSig :
142
WriteToken (GetCallSiteToken ((CallSite) instr.Operand));
144
case OperandType.InlineI :
145
m_codeWriter.Write ((int) instr.Operand);
147
case OperandType.InlineVar :
148
m_codeWriter.Write ((short) body.Variables.IndexOf (
149
(VariableDefinition) instr.Operand));
151
case OperandType.InlineParam :
152
m_codeWriter.Write ((short) GetParameterIndex (
153
body, (ParameterDefinition) instr.Operand));
155
case OperandType.InlineI8 :
156
m_codeWriter.Write ((long) instr.Operand);
158
case OperandType.ShortInlineR :
159
m_codeWriter.Write ((float) instr.Operand);
161
case OperandType.InlineR :
162
m_codeWriter.Write ((double) instr.Operand);
164
case OperandType.InlineString :
165
WriteToken (new MetadataToken (TokenType.String,
166
m_reflectWriter.MetadataWriter.AddUserString (instr.Operand as string)));
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);
182
throw new ReflectionException (
183
string.Format ("Wrong operand for {0} OpCode: {1}",
184
instr.OpCode.OperandType,
185
instr.Operand.GetType ().FullName));
191
long pos = m_codeWriter.BaseStream.Position;
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)))));
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)));
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));
216
m_codeWriter.BaseStream.Position = pos;
219
MetadataToken GetCallSiteToken (CallSite cs)
222
int sentinel = cs.GetSentinel ();
224
sig = m_reflectWriter.SignatureWriter.AddMethodDefSig (
225
m_reflectWriter.GetMethodDefSig (cs));
227
sig = m_reflectWriter.SignatureWriter.AddMethodRefSig (
228
m_reflectWriter.GetMethodRefSig (cs));
230
if (m_standaloneSigCache.Contains (sig))
231
return (MetadataToken) m_standaloneSigCache [sig];
233
StandAloneSigTable sasTable = m_reflectWriter.MetadataTableWriter.GetStandAloneSigTable ();
234
StandAloneSigRow sasRow = m_reflectWriter.MetadataRowWriter.CreateStandAloneSigRow (sig);
236
sasTable.Rows.Add(sasRow);
238
MetadataToken token = new MetadataToken (TokenType.Signature, (uint) sasTable.Rows.Count);
239
m_standaloneSigCache [sig] = token;
243
static int GetLength (Instruction start, Instruction end, InstructionCollection instructions)
245
Instruction last = instructions [instructions.Count - 1];
246
return (end == instructions.Outside ? last.Offset + last.GetSize () : end.Offset) - start.Offset;
249
static bool IsRangeFat (Instruction start, Instruction end, InstructionCollection instructions)
251
return GetLength (start, end, instructions) >= 256 ||
252
start.Offset >= 65536;
255
static bool IsFat (ExceptionHandlerCollection seh)
257
for (int i = 0; i < seh.Count; i++) {
258
ExceptionHandler eh = seh [i];
259
if (IsRangeFat (eh.TryStart, eh.TryEnd, seh.Container.Instructions))
262
if (IsRangeFat (eh.HandlerStart, eh.HandlerEnd, seh.Container.Instructions))
266
case ExceptionHandlerType.Filter :
267
if (IsRangeFat (eh.FilterStart, eh.FilterEnd, seh.Container.Instructions))
276
void WriteExceptionHandlerCollection (ExceptionHandlerCollection seh)
278
m_codeWriter.QuadAlign ();
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);
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);
306
void WriteFatBlockSize (ExceptionHandlerCollection seh)
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));
314
void WriteHandlerSpecific (ExceptionHandler eh)
317
case ExceptionHandlerType.Catch :
318
WriteToken (eh.CatchType.MetadataToken);
320
case ExceptionHandlerType.Filter :
321
m_codeWriter.Write ((uint) eh.FilterStart.Offset);
324
m_codeWriter.Write (0);
329
public override void VisitVariableDefinitionCollection (VariableDefinitionCollection variables)
331
MethodBody body = variables.Container as MethodBody;
335
uint sig = m_reflectWriter.SignatureWriter.AddLocalVarSig (
336
GetLocalVarSig (variables));
338
if (m_localSigCache.Contains (sig)) {
339
body.LocalVarToken = (int) m_localSigCache [sig];
343
StandAloneSigTable sasTable = m_reflectWriter.MetadataTableWriter.GetStandAloneSigTable ();
344
StandAloneSigRow sasRow = m_reflectWriter.MetadataRowWriter.CreateStandAloneSigRow (
347
sasTable.Rows.Add (sasRow);
348
body.LocalVarToken = sasTable.Rows.Count;
349
m_localSigCache [sig] = body.LocalVarToken;
352
public override void TerminateMethodBody (MethodBody body)
354
long pos = m_binaryWriter.BaseStream.Position;
356
if (body.Variables.Count > 0 || body.ExceptionHandlers.Count > 0
357
|| m_codeWriter.BaseStream.Length >= 64 || body.MaxStack > 8) {
359
MethodHeader header = MethodHeader.FatFormat;
361
header |= MethodHeader.InitLocals;
362
if (body.ExceptionHandlers.Count > 0)
363
header |= MethodHeader.MoreSects;
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));
371
WriteExceptionHandlerCollection (body.ExceptionHandlers);
373
m_binaryWriter.Write ((byte) ((byte) MethodHeader.TinyFormat |
374
m_codeWriter.BaseStream.Length << 2));
376
m_binaryWriter.Write (m_codeWriter);
377
m_binaryWriter.QuadAlign ();
379
m_reflectWriter.MetadataWriter.AddData (
380
(int) (m_binaryWriter.BaseStream.Position - pos));
383
public LocalVarSig.LocalVariable GetLocalVariableSig (VariableDefinition var)
385
LocalVarSig.LocalVariable lv = new LocalVarSig.LocalVariable ();
386
TypeReference type = var.VariableType;
388
lv.CustomMods = m_reflectWriter.GetCustomMods (type);
390
if (type is PinnedType) {
391
lv.Constraint |= Constraint.Pinned;
392
type = (type as PinnedType).ElementType;
395
if (type is ReferenceType) {
397
type = (type as ReferenceType).ElementType;
400
lv.Type = m_reflectWriter.GetSigType (type);
405
public LocalVarSig GetLocalVarSig (VariableDefinitionCollection vars)
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]);
418
static void ComputeMaxStack (InstructionCollection instructions)
420
InstructionCollection ehs = new InstructionCollection (null);
421
foreach (ExceptionHandler eh in instructions.Container.ExceptionHandlers) {
423
case ExceptionHandlerType.Catch :
424
ehs.Add (eh.HandlerStart);
426
case ExceptionHandlerType.Filter :
427
ehs.Add (eh.FilterStart);
433
foreach (Instruction instr in instructions) {
434
if (ehs.Contains (instr))
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:
447
case StackBehaviour.Push1_push1:
452
if (instr.OpCode.OperandType == OperandType.InlineMethod) {
453
IMethodSignature signature = instr.Operand as IMethodSignature;
454
if (signature != null && signature.ReturnType.ReturnType.FullName != Constants.Void)
459
instructions.Container.MaxStack = max;