5
// Jb Evain (jbevain@gmail.com)
7
// Copyright (c) 2008 - 2010 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.
30
using System.Collections.Generic;
32
using Mono.Collections.Generic;
34
using Mono.Cecil.Metadata;
37
using RVA = System.UInt32;
41
namespace Mono.Cecil.Cil {
43
sealed class CodeWriter : ByteBuffer {
45
readonly RVA code_base;
46
internal readonly MetadataBuilder metadata;
47
readonly Dictionary<uint, MetadataToken> standalone_signatures;
52
public CodeWriter (MetadataBuilder metadata)
55
this.code_base = metadata.text_map.GetNextRVA (TextSegment.CLIHeader);
56
this.current = code_base;
57
this.metadata = metadata;
58
this.standalone_signatures = new Dictionary<uint, MetadataToken> ();
61
public RVA WriteMethodBody (MethodDefinition method)
63
var rva = BeginMethod ();
65
if (IsUnresolved (method)) {
69
WriteUnresolvedMethodBody (method);
71
if (IsEmptyMethodBody (method.Body))
74
WriteResolvedMethodBody (method);
83
static bool IsEmptyMethodBody (MethodBody body)
85
return body.instructions.IsNullOrEmpty ()
86
&& body.variables.IsNullOrEmpty ();
89
static bool IsUnresolved (MethodDefinition method)
91
return method.HasBody && method.HasImage && method.body == null;
94
void WriteUnresolvedMethodBody (MethodDefinition method)
96
var code_reader = metadata.module.Read (method, (_, reader) => reader.code);
98
MethodSymbols symbols;
99
var buffer = code_reader.PatchRawMethodBody (method, this, out symbols);
103
if (symbols.instructions.IsNullOrEmpty ())
106
symbols.method_token = method.token;
107
symbols.local_var_token = GetLocalVarToken (buffer, symbols);
109
var symbol_writer = metadata.symbol_writer;
110
if (symbol_writer != null)
111
symbol_writer.Write (symbols);
114
static MetadataToken GetLocalVarToken (ByteBuffer buffer, MethodSymbols symbols)
116
if (symbols.variables.IsNullOrEmpty ())
117
return MetadataToken.Zero;
120
return new MetadataToken (buffer.ReadUInt32 ());
123
void WriteResolvedMethodBody (MethodDefinition method)
127
if (RequiresFatHeader ())
130
WriteByte ((byte) (0x2 | (body.CodeSize << 2))); // tiny
132
WriteInstructions ();
134
if (body.HasExceptionHandlers)
135
WriteExceptionHandlers ();
137
var symbol_writer = metadata.symbol_writer;
138
if (symbol_writer != null)
139
symbol_writer.Write (body);
142
void WriteFatHeader ()
144
var body = this.body;
145
byte flags = 0x3; // fat
147
flags |= 0x10; // init locals
148
if (body.HasExceptionHandlers)
149
flags |= 0x8; // more sections
153
WriteInt16 ((short) body.max_stack_size);
154
WriteInt32 (body.code_size);
155
body.local_var_token = body.HasVariables
156
? GetStandAloneSignature (body.Variables)
157
: MetadataToken.Zero;
158
WriteMetadataToken (body.local_var_token);
161
void WriteInstructions ()
163
var instructions = body.Instructions;
164
var items = instructions.items;
165
var size = instructions.size;
167
for (int i = 0; i < size; i++) {
168
var instruction = items [i];
169
WriteOpCode (instruction.opcode);
170
WriteOperand (instruction);
174
void WriteOpCode (OpCode opcode)
176
if (opcode.Size == 1) {
177
WriteByte (opcode.Op2);
179
WriteByte (opcode.Op1);
180
WriteByte (opcode.Op2);
184
void WriteOperand (Instruction instruction)
186
var opcode = instruction.opcode;
187
var operand_type = opcode.OperandType;
188
if (operand_type == OperandType.InlineNone)
191
var operand = instruction.operand;
193
throw new ArgumentException ();
195
switch (operand_type) {
196
case OperandType.InlineSwitch: {
197
var targets = (Instruction []) operand;
198
WriteInt32 (targets.Length);
199
var diff = instruction.Offset + opcode.Size + (4 * (targets.Length + 1));
200
for (int i = 0; i < targets.Length; i++)
201
WriteInt32 (GetTargetOffset (targets [i]) - diff);
204
case OperandType.ShortInlineBrTarget: {
205
var target = (Instruction) operand;
206
WriteSByte ((sbyte) (GetTargetOffset (target) - (instruction.Offset + opcode.Size + 1)));
209
case OperandType.InlineBrTarget: {
210
var target = (Instruction) operand;
211
WriteInt32 (GetTargetOffset (target) - (instruction.Offset + opcode.Size + 4));
214
case OperandType.ShortInlineVar:
215
WriteByte ((byte) GetVariableIndex ((VariableDefinition) operand));
217
case OperandType.ShortInlineArg:
218
WriteByte ((byte) GetParameterIndex ((ParameterDefinition) operand));
220
case OperandType.InlineVar:
221
WriteInt16 ((short) GetVariableIndex ((VariableDefinition) operand));
223
case OperandType.InlineArg:
224
WriteInt16 ((short) GetParameterIndex ((ParameterDefinition) operand));
226
case OperandType.InlineSig:
227
WriteMetadataToken (GetStandAloneSignature ((CallSite) operand));
229
case OperandType.ShortInlineI:
230
if (opcode == OpCodes.Ldc_I4_S)
231
WriteSByte ((sbyte) operand);
233
WriteByte ((byte) operand);
235
case OperandType.InlineI:
236
WriteInt32 ((int) operand);
238
case OperandType.InlineI8:
239
WriteInt64 ((long) operand);
241
case OperandType.ShortInlineR:
242
WriteSingle ((float) operand);
244
case OperandType.InlineR:
245
WriteDouble ((double) operand);
247
case OperandType.InlineString:
251
GetUserStringIndex ((string) operand)));
253
case OperandType.InlineType:
254
case OperandType.InlineField:
255
case OperandType.InlineMethod:
256
case OperandType.InlineTok:
257
WriteMetadataToken (metadata.LookupToken ((IMetadataTokenProvider) operand));
260
throw new ArgumentException ();
264
int GetTargetOffset (Instruction instruction)
266
if (instruction == null) {
267
var last = body.instructions [body.instructions.size - 1];
268
return last.offset + last.GetSize ();
271
return instruction.offset;
274
uint GetUserStringIndex (string @string)
279
return metadata.user_string_heap.GetStringIndex (@string);
282
static int GetVariableIndex (VariableDefinition variable)
284
return variable.Index;
287
int GetParameterIndex (ParameterDefinition parameter)
289
if (body.method.HasThis) {
290
if (parameter == body.this_parameter)
293
return parameter.Index + 1;
296
return parameter.Index;
299
bool RequiresFatHeader ()
301
var body = this.body;
302
return body.CodeSize >= 64
305
|| body.HasExceptionHandlers
306
|| body.MaxStackSize > 8;
309
void ComputeHeader ()
312
var instructions = body.instructions;
313
var items = instructions.items;
314
var count = instructions.size;
317
Dictionary<Instruction, int> stack_sizes = null;
319
if (body.HasExceptionHandlers)
320
ComputeExceptionHandlerStackSize (ref stack_sizes);
322
for (int i = 0; i < count; i++) {
323
var instruction = items [i];
324
instruction.offset = offset;
325
offset += instruction.GetSize ();
327
ComputeStackSize (instruction, ref stack_sizes, ref stack_size, ref max_stack);
330
body.code_size = offset;
331
body.max_stack_size = max_stack;
334
void ComputeExceptionHandlerStackSize (ref Dictionary<Instruction, int> stack_sizes)
336
var exception_handlers = body.ExceptionHandlers;
338
for (int i = 0; i < exception_handlers.Count; i++) {
339
var exception_handler = exception_handlers [i];
341
switch (exception_handler.HandlerType) {
342
case ExceptionHandlerType.Catch:
343
AddExceptionStackSize (exception_handler.HandlerStart, ref stack_sizes);
345
case ExceptionHandlerType.Filter:
346
AddExceptionStackSize (exception_handler.FilterStart, ref stack_sizes);
347
AddExceptionStackSize (exception_handler.HandlerStart, ref stack_sizes);
353
static void AddExceptionStackSize (Instruction handler_start, ref Dictionary<Instruction, int> stack_sizes)
355
if (handler_start == null)
358
if (stack_sizes == null)
359
stack_sizes = new Dictionary<Instruction, int> ();
361
stack_sizes [handler_start] = 1;
364
static void ComputeStackSize (Instruction instruction, ref Dictionary<Instruction, int> stack_sizes, ref int stack_size, ref int max_stack)
367
if (stack_sizes != null && stack_sizes.TryGetValue (instruction, out computed_size))
368
stack_size = computed_size;
370
max_stack = System.Math.Max (max_stack, stack_size);
371
ComputeStackDelta (instruction, ref stack_size);
372
max_stack = System.Math.Max (max_stack, stack_size);
374
CopyBranchStackSize (instruction, ref stack_sizes, stack_size);
375
ComputeStackSize (instruction, ref stack_size);
378
static void CopyBranchStackSize (Instruction instruction, ref Dictionary<Instruction, int> stack_sizes, int stack_size)
383
switch (instruction.opcode.OperandType) {
384
case OperandType.ShortInlineBrTarget:
385
case OperandType.InlineBrTarget:
386
CopyBranchStackSize (ref stack_sizes, (Instruction) instruction.operand, stack_size);
388
case OperandType.InlineSwitch:
389
var targets = (Instruction[]) instruction.operand;
390
for (int i = 0; i < targets.Length; i++)
391
CopyBranchStackSize (ref stack_sizes, targets [i], stack_size);
396
static void CopyBranchStackSize (ref Dictionary<Instruction, int> stack_sizes, Instruction target, int stack_size)
398
if (stack_sizes == null)
399
stack_sizes = new Dictionary<Instruction, int> ();
401
int branch_stack_size = stack_size;
404
if (stack_sizes.TryGetValue (target, out computed_size))
405
branch_stack_size = System.Math.Max (branch_stack_size, computed_size);
407
stack_sizes [target] = branch_stack_size;
410
static void ComputeStackSize (Instruction instruction, ref int stack_size)
412
switch (instruction.opcode.FlowControl) {
413
case FlowControl.Branch:
414
case FlowControl.Break:
415
case FlowControl.Throw:
416
case FlowControl.Return:
422
static void ComputeStackDelta (Instruction instruction, ref int stack_size)
424
switch (instruction.opcode.FlowControl) {
425
case FlowControl.Call: {
426
var method = (IMethodSignature) instruction.operand;
427
stack_size -= (method.HasParameters ? method.Parameters.Count : 0)
428
+ (method.HasThis && instruction.opcode.Code != Code.Newobj ? 1 : 0);
429
stack_size += (method.ReturnType.etype == ElementType.Void ? 0 : 1)
430
+ (method.HasThis && instruction.opcode.Code == Code.Newobj ? 1 : 0);
434
ComputePopDelta (instruction.opcode.StackBehaviourPop, ref stack_size);
435
ComputePushDelta (instruction.opcode.StackBehaviourPush, ref stack_size);
440
static void ComputePopDelta (StackBehaviour pop_behavior, ref int stack_size)
442
switch (pop_behavior) {
443
case StackBehaviour.Popi:
444
case StackBehaviour.Popref:
445
case StackBehaviour.Pop1:
448
case StackBehaviour.Pop1_pop1:
449
case StackBehaviour.Popi_pop1:
450
case StackBehaviour.Popi_popi:
451
case StackBehaviour.Popi_popi8:
452
case StackBehaviour.Popi_popr4:
453
case StackBehaviour.Popi_popr8:
454
case StackBehaviour.Popref_pop1:
455
case StackBehaviour.Popref_popi:
458
case StackBehaviour.Popi_popi_popi:
459
case StackBehaviour.Popref_popi_popi:
460
case StackBehaviour.Popref_popi_popi8:
461
case StackBehaviour.Popref_popi_popr4:
462
case StackBehaviour.Popref_popi_popr8:
463
case StackBehaviour.Popref_popi_popref:
466
case StackBehaviour.PopAll:
472
static void ComputePushDelta (StackBehaviour push_behaviour, ref int stack_size)
474
switch (push_behaviour) {
475
case StackBehaviour.Push1:
476
case StackBehaviour.Pushi:
477
case StackBehaviour.Pushi8:
478
case StackBehaviour.Pushr4:
479
case StackBehaviour.Pushr8:
480
case StackBehaviour.Pushref:
483
case StackBehaviour.Push1_push1:
489
void WriteExceptionHandlers ()
493
var handlers = body.ExceptionHandlers;
495
if (handlers.Count < 0x15 && !RequiresFatSection (handlers))
496
WriteSmallSection (handlers);
498
WriteFatSection (handlers);
501
static bool RequiresFatSection (Collection<ExceptionHandler> handlers)
503
for (int i = 0; i < handlers.Count; i++) {
504
var handler = handlers [i];
506
if (IsFatRange (handler.TryStart, handler.TryEnd))
509
if (IsFatRange (handler.HandlerStart, handler.HandlerEnd))
512
if (handler.HandlerType == ExceptionHandlerType.Filter
513
&& IsFatRange (handler.FilterStart, handler.FilterEnd))
520
static bool IsFatRange (Instruction start, Instruction end)
525
return end.Offset - start.Offset > 255 || start.Offset > 65535;
528
void WriteSmallSection (Collection<ExceptionHandler> handlers)
530
const byte eh_table = 0x1;
532
WriteByte (eh_table);
533
WriteByte ((byte) (handlers.Count * 12 + 4));
536
WriteExceptionHandlers (
538
i => WriteUInt16 ((ushort) i),
539
i => WriteByte ((byte) i));
542
void WriteFatSection (Collection<ExceptionHandler> handlers)
544
const byte eh_table = 0x1;
545
const byte fat_format = 0x40;
547
WriteByte (eh_table | fat_format);
549
int size = handlers.Count * 24 + 4;
550
WriteByte ((byte) (size & 0xff));
551
WriteByte ((byte) ((size >> 8) & 0xff));
552
WriteByte ((byte) ((size >> 16) & 0xff));
554
WriteExceptionHandlers (handlers, WriteInt32, WriteInt32);
557
void WriteExceptionHandlers (Collection<ExceptionHandler> handlers, Action<int> write_entry, Action<int> write_length)
559
for (int i = 0; i < handlers.Count; i++) {
560
var handler = handlers [i];
562
write_entry ((int) handler.HandlerType);
564
write_entry (handler.TryStart.Offset);
565
write_length (GetTargetOffset (handler.TryEnd) - handler.TryStart.Offset);
567
write_entry (handler.HandlerStart.Offset);
568
write_length (GetTargetOffset (handler.HandlerEnd) - handler.HandlerStart.Offset);
570
WriteExceptionHandlerSpecific (handler);
574
void WriteExceptionHandlerSpecific (ExceptionHandler handler)
576
switch (handler.HandlerType) {
577
case ExceptionHandlerType.Catch:
578
WriteMetadataToken (metadata.LookupToken (handler.CatchType));
580
case ExceptionHandlerType.Filter:
581
WriteInt32 (handler.FilterStart.Offset);
589
public MetadataToken GetStandAloneSignature (Collection<VariableDefinition> variables)
591
var signature = metadata.GetLocalVariableBlobIndex (variables);
593
return GetStandAloneSignatureToken (signature);
596
public MetadataToken GetStandAloneSignature (CallSite call_site)
598
var signature = metadata.GetCallSiteBlobIndex (call_site);
599
var token = GetStandAloneSignatureToken (signature);
600
call_site.MetadataToken = token;
604
MetadataToken GetStandAloneSignatureToken (uint signature)
607
if (standalone_signatures.TryGetValue (signature, out token))
610
token = new MetadataToken (TokenType.Signature, metadata.AddStandAloneSignature (signature));
611
standalone_signatures.Add (signature, token);
620
void WriteMetadataToken (MetadataToken token)
622
WriteUInt32 (token.ToUInt32 ());
625
void Align (int align)
628
WriteBytes (((position + align) & ~align) - position);
633
current = (RVA) (code_base + position);