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 + GetSize (last) : end.Offset) - start.Offset;
249 static int GetSize (Instruction i)
251 int size = i.OpCode.Size;
253 switch (i.OpCode.OperandType) {
254 case OperandType.InlineSwitch:
255 size += ((Instruction []) i.Operand).Length * 4;
257 case OperandType.InlineI8:
258 case OperandType.InlineR:
261 case OperandType.InlineBrTarget:
262 case OperandType.InlineField:
263 case OperandType.InlineI:
264 case OperandType.InlineMethod:
265 case OperandType.InlineString:
266 case OperandType.InlineTok:
267 case OperandType.InlineType:
268 case OperandType.ShortInlineR:
271 case OperandType.InlineParam:
272 case OperandType.InlineVar:
275 case OperandType.ShortInlineBrTarget:
276 case OperandType.ShortInlineI:
277 case OperandType.ShortInlineParam:
278 case OperandType.ShortInlineVar:
286 static bool IsRangeFat (Instruction start, Instruction end, InstructionCollection instructions)
288 return GetLength (start, end, instructions) >= 256 ||
289 start.Offset >= 65536;
292 static bool IsFat (ExceptionHandlerCollection seh)
294 for (int i = 0; i < seh.Count; i++) {
295 ExceptionHandler eh = seh [i];
296 if (IsRangeFat (eh.TryStart, eh.TryEnd, seh.Container.Instructions))
300 case ExceptionHandlerType.Catch :
301 case ExceptionHandlerType.Fault :
302 case ExceptionHandlerType.Finally :
303 if (IsRangeFat (eh.HandlerStart, eh.HandlerEnd, seh.Container.Instructions))
306 case ExceptionHandlerType.Filter :
307 if (IsRangeFat (eh.FilterStart, eh.FilterEnd, seh.Container.Instructions))
316 void WriteExceptionHandlerCollection (ExceptionHandlerCollection seh)
318 m_codeWriter.QuadAlign ();
320 if (seh.Count < 0x15 && !IsFat (seh)) {
321 m_codeWriter.Write ((byte) MethodDataSection.EHTable);
322 m_codeWriter.Write ((byte) (seh.Count * 12 + 4));
323 m_codeWriter.Write (new byte [2]);
324 foreach (ExceptionHandler eh in seh) {
325 m_codeWriter.Write ((ushort) eh.Type);
326 m_codeWriter.Write ((ushort) eh.TryStart.Offset);
327 m_codeWriter.Write ((byte) (eh.TryEnd.Offset - eh.TryStart.Offset));
328 m_codeWriter.Write ((ushort) eh.HandlerStart.Offset);
329 m_codeWriter.Write ((byte) GetLength (eh.HandlerStart, eh.HandlerEnd, seh.Container.Instructions));
330 WriteHandlerSpecific (eh);
333 m_codeWriter.Write ((byte) (MethodDataSection.FatFormat | MethodDataSection.EHTable));
334 WriteFatBlockSize (seh);
335 foreach (ExceptionHandler eh in seh) {
336 m_codeWriter.Write ((uint) eh.Type);
337 m_codeWriter.Write ((uint) eh.TryStart.Offset);
338 m_codeWriter.Write ((uint) (eh.TryEnd.Offset - eh.TryStart.Offset));
339 m_codeWriter.Write ((uint) eh.HandlerStart.Offset);
340 m_codeWriter.Write ((uint) GetLength (eh.HandlerStart, eh.HandlerEnd, seh.Container.Instructions));
341 WriteHandlerSpecific (eh);
346 void WriteFatBlockSize (ExceptionHandlerCollection seh)
348 int size = seh.Count * 24 + 4;
349 m_codeWriter.Write ((byte) (size & 0xff));
350 m_codeWriter.Write ((byte) ((size >> 8) & 0xff));
351 m_codeWriter.Write ((byte) ((size >> 16) & 0xff));
354 void WriteHandlerSpecific (ExceptionHandler eh)
357 case ExceptionHandlerType.Catch :
358 WriteToken (eh.CatchType.MetadataToken);
360 case ExceptionHandlerType.Filter :
361 m_codeWriter.Write ((uint) eh.FilterStart.Offset);
364 m_codeWriter.Write (0);
369 public override void VisitVariableDefinitionCollection (VariableDefinitionCollection variables)
371 MethodBody body = variables.Container as MethodBody;
375 uint sig = m_reflectWriter.SignatureWriter.AddLocalVarSig (
376 GetLocalVarSig (variables));
378 if (m_localSigCache.Contains (sig)) {
379 body.LocalVarToken = (int) m_localSigCache [sig];
383 StandAloneSigTable sasTable = m_reflectWriter.MetadataTableWriter.GetStandAloneSigTable ();
384 StandAloneSigRow sasRow = m_reflectWriter.MetadataRowWriter.CreateStandAloneSigRow (
387 sasTable.Rows.Add (sasRow);
388 body.LocalVarToken = sasTable.Rows.Count;
389 m_localSigCache [sig] = body.LocalVarToken;
392 public override void TerminateMethodBody (MethodBody body)
394 long pos = m_binaryWriter.BaseStream.Position;
396 if (body.Variables.Count > 0 || body.ExceptionHandlers.Count > 0
397 || m_codeWriter.BaseStream.Length >= 64 || body.MaxStack > 8) {
399 MethodHeader header = MethodHeader.FatFormat;
401 header |= MethodHeader.InitLocals;
402 if (body.ExceptionHandlers.Count > 0)
403 header |= MethodHeader.MoreSects;
405 m_binaryWriter.Write ((byte) header);
406 m_binaryWriter.Write ((byte) 0x30); // (header size / 4) << 4
407 m_binaryWriter.Write ((short) body.MaxStack);
408 m_binaryWriter.Write ((int) m_codeWriter.BaseStream.Length);
409 m_binaryWriter.Write (((int) TokenType.Signature | body.LocalVarToken));
411 WriteExceptionHandlerCollection (body.ExceptionHandlers);
413 m_binaryWriter.Write ((byte) ((byte) MethodHeader.TinyFormat |
414 m_codeWriter.BaseStream.Length << 2));
416 m_binaryWriter.Write (m_codeWriter);
417 m_binaryWriter.QuadAlign ();
419 m_reflectWriter.MetadataWriter.AddData (
420 (int) (m_binaryWriter.BaseStream.Position - pos));
423 LocalVarSig GetLocalVarSig (VariableDefinitionCollection vars)
425 LocalVarSig lvs = new LocalVarSig ();
426 lvs.CallingConvention |= 0x7;
427 lvs.Count = vars.Count;
428 lvs.LocalVariables = new LocalVarSig.LocalVariable [lvs.Count];
429 for (int i = 0; i < lvs.Count; i++) {
430 LocalVarSig.LocalVariable lv = new LocalVarSig.LocalVariable ();
431 TypeReference type = vars [i].VariableType;
433 lv.CustomMods = m_reflectWriter.GetCustomMods (type);
435 if (type is PinnedType) {
436 lv.Constraint |= Constraint.Pinned;
437 type = (type as PinnedType).ElementType;
440 if (type is ReferenceType) {
442 type = (type as ReferenceType).ElementType;
445 lv.Type = m_reflectWriter.GetSigType (type);
447 lvs.LocalVariables [i] = lv;
452 static void ComputeMaxStack (InstructionCollection instructions)
454 InstructionCollection ehs = new InstructionCollection (null);
455 foreach (ExceptionHandler eh in instructions.Container.ExceptionHandlers) {
457 case ExceptionHandlerType.Catch :
458 ehs.Add (eh.HandlerStart);
460 case ExceptionHandlerType.Filter :
461 ehs.Add (eh.FilterStart);
466 int max = 0, current = 0;
467 foreach (Instruction instr in instructions) {
469 if (ehs.Contains (instr))
472 switch (instr.OpCode.StackBehaviourPush) {
473 case StackBehaviour.Push1:
474 case StackBehaviour.Pushi:
475 case StackBehaviour.Pushi8:
476 case StackBehaviour.Pushr4:
477 case StackBehaviour.Pushr8:
478 case StackBehaviour.Pushref:
479 case StackBehaviour.Varpush:
482 case StackBehaviour.Push1_push1:
490 switch (instr.OpCode.StackBehaviourPop) {
491 case StackBehaviour.Varpop:
493 case StackBehaviour.Pop1:
494 case StackBehaviour.Popi:
495 case StackBehaviour.Popref:
498 case StackBehaviour.Pop1_pop1:
499 case StackBehaviour.Popi_pop1:
500 case StackBehaviour.Popi_popi:
501 case StackBehaviour.Popi_popi8:
502 case StackBehaviour.Popi_popr4:
503 case StackBehaviour.Popi_popr8:
504 case StackBehaviour.Popref_pop1:
505 case StackBehaviour.Popref_popi:
508 case StackBehaviour.Popi_popi_popi:
509 case StackBehaviour.Popref_popi_popi:
510 case StackBehaviour.Popref_popi_popi8:
511 case StackBehaviour.Popref_popi_popr4:
512 case StackBehaviour.Popref_popi_popr8:
513 case StackBehaviour.Popref_popi_popref:
522 instructions.Container.MaxStack = max;