2 Copyright (C) 2008-2010 Jeroen Frijters
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely, subject to the following restrictions:
12 1. The origin of this software must not be misrepresented; you must not
13 claim that you wrote the original software. If you use this software
14 in a product, an acknowledgment in the product documentation would be
15 appreciated but is not required.
16 2. Altered source versions must be plainly marked as such, and must not be
17 misrepresented as being the original software.
18 3. This notice may not be removed or altered from any source distribution.
25 using System.Runtime.InteropServices;
26 using System.Collections.Generic;
27 using System.Diagnostics.SymbolStore;
28 using System.Diagnostics;
29 using IKVM.Reflection.Metadata;
30 using IKVM.Reflection.Writer;
32 namespace IKVM.Reflection.Emit
36 // 1-based here, to make sure that an uninitialized Label isn't valid
37 private readonly int index1;
39 internal Label(int index)
41 this.index1 = index + 1;
46 get { return index1 - 1; }
49 public bool Equals(Label other)
51 return other.index1 == index1;
54 public override bool Equals(object obj)
56 return this == obj as Label?;
59 public override int GetHashCode()
64 public static bool operator ==(Label arg1, Label arg2)
66 return arg1.index1 == arg2.index1;
69 public static bool operator !=(Label arg1, Label arg2)
71 return !(arg1 == arg2);
75 public sealed class LocalBuilder
77 private readonly Type localType;
78 private readonly int index;
79 private readonly bool pinned;
81 internal int startOffset;
82 internal int endOffset;
84 internal LocalBuilder(Type localType, int index, bool pinned)
86 this.localType = localType;
91 public void SetLocalSymInfo(string name)
96 public void SetLocalSymInfo(string name, int startOffset, int endOffset)
99 this.startOffset = startOffset;
100 this.endOffset = endOffset;
103 public Type LocalType
105 get { return localType; }
108 public int LocalIndex
110 get { return index; }
115 get { return pinned; }
119 public sealed class ILGenerator
121 private static readonly Type FAULT = new BakedType(null); // the type we use here doesn't matter, as long as it can never be used as a real exception type
122 private readonly ModuleBuilder moduleBuilder;
123 private readonly ByteBuffer code;
124 private readonly List<LocalBuilder> locals = new List<LocalBuilder>();
125 private readonly List<int> tokenFixups = new List<int>();
126 private readonly List<int> labels = new List<int>();
127 private readonly List<int> labelStackHeight = new List<int>();
128 private readonly List<LabelFixup> labelFixups = new List<LabelFixup>();
129 private readonly List<SequencePoint> sequencePoints = new List<SequencePoint>();
130 private readonly List<ExceptionBlock> exceptions = new List<ExceptionBlock>();
131 private readonly Stack<ExceptionBlock> exceptionStack = new Stack<ExceptionBlock>();
132 private ushort maxStack;
133 private int stackHeight;
135 private byte exceptionBlockAssistanceMode = EBAM_COMPAT;
136 private const byte EBAM_COMPAT = 0;
137 private const byte EBAM_DISABLE = 1;
138 private const byte EBAM_CLEVER = 2;
140 private struct LabelFixup
146 private sealed class ExceptionBlock : IComparer<ExceptionBlock>
148 internal readonly int ordinal;
149 internal Label labelEnd;
150 internal int tryOffset;
151 internal int tryLength;
152 internal int handlerOffset;
153 internal int handlerLength;
154 internal Type exceptionType; // null = finally block or handler with filter, FAULT = fault block
155 internal int filterOffset;
157 internal ExceptionBlock(int ordinal)
159 this.ordinal = ordinal;
162 int IComparer<ExceptionBlock>.Compare(ExceptionBlock x, ExceptionBlock y)
164 // Mono's sort insists on doing unnecessary comparisons
169 if (x.tryOffset >= y.handlerOffset && x.tryOffset + x.tryLength <= y.handlerOffset + y.handlerLength)
173 if (y.tryOffset >= x.handlerOffset && y.tryOffset + y.tryLength <= x.handlerOffset + x.handlerLength)
177 if (x.tryOffset == y.tryOffset && x.tryLength == y.tryLength)
179 return x.ordinal < y.ordinal ? -1 : 1;
181 if (x.tryOffset + x.tryLength <= y.tryOffset)
185 if (y.tryOffset + y.tryLength <= x.tryOffset)
189 if (x.tryOffset > y.tryOffset || (x.tryOffset == y.tryOffset && x.tryLength < y.tryLength))
200 private struct SequencePoint
202 internal ISymbolDocumentWriter document;
204 internal int startLine;
205 internal int startColumn;
206 internal int endLine;
207 internal int endColumn;
210 private sealed class Scope
212 internal readonly Scope parent;
213 internal readonly List<Scope> children = new List<Scope>();
214 internal readonly List<LocalBuilder> locals = new List<LocalBuilder>();
215 internal int startOffset;
216 internal int endOffset;
218 internal Scope(Scope parent)
220 this.parent = parent;
224 internal ILGenerator(ModuleBuilder moduleBuilder, int initialCapacity)
226 this.code = new ByteBuffer(initialCapacity);
227 this.moduleBuilder = moduleBuilder;
228 if (moduleBuilder.symbolWriter != null)
230 scope = new Scope(null);
234 private bool IsLabelReachable(Label label)
236 return labelStackHeight[label.Index] != -1;
240 public void __DisableExceptionBlockAssistance()
242 exceptionBlockAssistanceMode = EBAM_DISABLE;
246 public void __CleverExceptionBlockAssistance()
248 exceptionBlockAssistanceMode = EBAM_CLEVER;
254 get { return code.Position; }
257 public void BeginCatchBlock(Type exceptionType)
259 ExceptionBlock block = exceptionStack.Peek();
260 if (exceptionBlockAssistanceMode == EBAM_COMPAT || (exceptionBlockAssistanceMode == EBAM_CLEVER && stackHeight != -1))
262 if (exceptionType == null)
264 Emit(OpCodes.Endfilter);
268 Emit(OpCodes.Leave, block.labelEnd);
273 if (block.tryLength == 0)
275 block.tryLength = code.Position - block.tryOffset;
277 else if (exceptionType != null)
279 block.handlerLength = code.Position - block.handlerOffset;
280 exceptionStack.Pop();
281 ExceptionBlock newBlock = new ExceptionBlock(exceptions.Count);
282 newBlock.labelEnd = block.labelEnd;
283 newBlock.tryOffset = block.tryOffset;
284 newBlock.tryLength = block.tryLength;
286 exceptions.Add(block);
287 exceptionStack.Push(block);
289 block.handlerOffset = code.Position;
290 block.exceptionType = exceptionType;
293 public Label BeginExceptionBlock()
295 ExceptionBlock block = new ExceptionBlock(exceptions.Count);
296 block.labelEnd = DefineLabel();
297 block.tryOffset = code.Position;
298 exceptionStack.Push(block);
299 exceptions.Add(block);
301 return block.labelEnd;
304 public void BeginExceptFilterBlock()
306 ExceptionBlock block = BeginFinallyFilterFaultBlock();
307 block.filterOffset = code.Position;
311 public void BeginFaultBlock()
313 ExceptionBlock block = BeginFinallyFilterFaultBlock();
314 block.handlerOffset = code.Position;
315 block.exceptionType = FAULT;
318 public void BeginFinallyBlock()
320 ExceptionBlock block = BeginFinallyFilterFaultBlock();
321 block.handlerOffset = code.Position;
324 private ExceptionBlock BeginFinallyFilterFaultBlock()
326 ExceptionBlock block = exceptionStack.Peek();
327 if (exceptionBlockAssistanceMode == EBAM_COMPAT || (exceptionBlockAssistanceMode == EBAM_CLEVER && stackHeight != -1))
329 Emit(OpCodes.Leave, block.labelEnd);
331 if (block.handlerOffset == 0)
333 block.tryLength = code.Position - block.tryOffset;
337 block.handlerLength = code.Position - block.handlerOffset;
339 if (exceptionBlockAssistanceMode != EBAM_COMPAT)
341 labelEnd = block.labelEnd;
345 MarkLabel(block.labelEnd);
346 labelEnd = DefineLabel();
347 Emit(OpCodes.Leave, labelEnd);
349 exceptionStack.Pop();
350 ExceptionBlock newBlock = new ExceptionBlock(exceptions.Count);
351 newBlock.labelEnd = labelEnd;
352 newBlock.tryOffset = block.tryOffset;
353 newBlock.tryLength = code.Position - block.tryOffset;
355 exceptions.Add(block);
356 exceptionStack.Push(block);
362 public void EndExceptionBlock()
364 ExceptionBlock block = exceptionStack.Pop();
365 if (exceptionBlockAssistanceMode == EBAM_COMPAT || (exceptionBlockAssistanceMode == EBAM_CLEVER && stackHeight != -1))
367 if (block.filterOffset != 0 || (block.exceptionType != null && block.exceptionType != FAULT))
369 Emit(OpCodes.Leave, block.labelEnd);
373 Emit(OpCodes.Endfinally);
376 MarkLabel(block.labelEnd);
377 block.handlerLength = code.Position - block.handlerOffset;
380 public void BeginScope()
382 Scope newScope = new Scope(scope);
383 scope.children.Add(newScope);
385 scope.startOffset = code.Position;
388 public void UsingNamespace(string usingNamespace)
390 if (moduleBuilder.symbolWriter != null)
392 moduleBuilder.symbolWriter.UsingNamespace(usingNamespace);
396 public LocalBuilder DeclareLocal(Type localType)
398 return DeclareLocal(localType, false);
401 public LocalBuilder DeclareLocal(Type localType, bool pinned)
403 LocalBuilder local = new LocalBuilder(localType, locals.Count, pinned);
407 scope.locals.Add(local);
412 public Label DefineLabel()
414 Label label = new Label(labels.Count);
416 labelStackHeight.Add(-1);
420 public void Emit(OpCode opc)
422 Debug.Assert(opc != OpCodes.Ret || (opc == OpCodes.Ret && stackHeight <= 1));
425 code.Write((byte)(opc.Value >> 8));
427 code.Write((byte)opc.Value);
428 switch (opc.FlowControl)
430 case FlowControl.Branch:
431 case FlowControl.Break:
432 case FlowControl.Return:
433 case FlowControl.Throw:
437 UpdateStack(opc.StackDiff);
442 private void UpdateStack(int stackdiff)
444 if (stackHeight == -1)
446 // we're about to emit code that is either unreachable or reachable only via a backward branch
449 Debug.Assert(stackHeight >= 0 && stackHeight <= ushort.MaxValue);
450 stackHeight += stackdiff;
451 Debug.Assert(stackHeight >= 0 && stackHeight <= ushort.MaxValue);
452 maxStack = Math.Max(maxStack, (ushort)stackHeight);
455 public void Emit(OpCode opc, byte arg)
461 public void Emit(OpCode opc, double arg)
467 public void Emit(OpCode opc, FieldInfo field)
470 WriteToken(moduleBuilder.GetFieldToken(field));
473 public void Emit(OpCode opc, short arg)
479 public void Emit(OpCode opc, int arg)
485 public void Emit(OpCode opc, long arg)
491 public void Emit(OpCode opc, Label label)
493 // We need special stackHeight handling for unconditional branches,
494 // because the branch and next flows have differing stack heights.
495 // Note that this assumes that unconditional branches do not push/pop.
496 int flowStackHeight = this.stackHeight;
498 if (opc == OpCodes.Leave || opc == OpCodes.Leave_S)
502 else if (opc.FlowControl != FlowControl.Branch)
504 flowStackHeight = this.stackHeight;
506 // if the label has already been marked, we can emit the branch offset directly
507 if (labels[label.Index] != -1)
509 if (labelStackHeight[label.Index] != flowStackHeight && (labelStackHeight[label.Index] != 0 || flowStackHeight != -1))
511 // the "backward branch constraint" prohibits this, so we don't need to support it
512 throw new NotSupportedException("'Backward branch constraints' violated");
514 if (opc.OperandType == OperandType.ShortInlineBrTarget)
516 WriteByteBranchOffset(labels[label.Index] - (code.Position + 1));
520 code.Write(labels[label.Index] - (code.Position + 4));
525 Debug.Assert(labelStackHeight[label.Index] == -1 || labelStackHeight[label.Index] == flowStackHeight || (flowStackHeight == -1 && labelStackHeight[label.Index] == 0));
526 labelStackHeight[label.Index] = flowStackHeight;
527 LabelFixup fix = new LabelFixup();
528 fix.label = label.Index;
529 fix.offset = code.Position;
530 labelFixups.Add(fix);
531 if (opc.OperandType == OperandType.ShortInlineBrTarget)
542 private void WriteByteBranchOffset(int offset)
544 if (offset < -128 || offset > 127)
546 throw new NotSupportedException("Branch offset of " + offset + " does not fit in one-byte branch target at position " + code.Position);
548 code.Write((byte)offset);
551 public void Emit(OpCode opc, Label[] labels)
554 LabelFixup fix = new LabelFixup();
556 fix.offset = code.Position;
557 labelFixups.Add(fix);
558 code.Write(labels.Length);
559 foreach (Label label in labels)
561 code.Write(label.Index);
562 if (this.labels[label.Index] != -1)
564 if (labelStackHeight[label.Index] != stackHeight)
566 // the "backward branch constraint" prohibits this, so we don't need to support it
567 throw new NotSupportedException();
572 Debug.Assert(labelStackHeight[label.Index] == -1 || labelStackHeight[label.Index] == stackHeight);
573 labelStackHeight[label.Index] = stackHeight;
578 public void Emit(OpCode opc, LocalBuilder local)
580 if ((opc == OpCodes.Ldloc || opc == OpCodes.Ldloca || opc == OpCodes.Stloc) && local.LocalIndex < 256)
582 if (opc == OpCodes.Ldloc)
584 switch (local.LocalIndex)
587 Emit(OpCodes.Ldloc_0);
590 Emit(OpCodes.Ldloc_1);
593 Emit(OpCodes.Ldloc_2);
596 Emit(OpCodes.Ldloc_3);
599 Emit(OpCodes.Ldloc_S);
600 code.Write((byte)local.LocalIndex);
604 else if (opc == OpCodes.Ldloca)
606 Emit(OpCodes.Ldloca_S);
607 code.Write((byte)local.LocalIndex);
609 else if (opc == OpCodes.Stloc)
611 switch (local.LocalIndex)
614 Emit(OpCodes.Stloc_0);
617 Emit(OpCodes.Stloc_1);
620 Emit(OpCodes.Stloc_2);
623 Emit(OpCodes.Stloc_3);
626 Emit(OpCodes.Stloc_S);
627 code.Write((byte)local.LocalIndex);
635 switch (opc.OperandType)
637 case OperandType.InlineVar:
638 code.Write((ushort)local.LocalIndex);
640 case OperandType.ShortInlineVar:
641 code.Write((byte)local.LocalIndex);
647 private void WriteToken(FieldToken token)
649 if (token.IsPseudoToken)
651 tokenFixups.Add(code.Position);
653 code.Write(token.Token);
656 private void WriteToken(MethodToken token)
658 if (token.IsPseudoToken)
660 tokenFixups.Add(code.Position);
662 code.Write(token.Token);
665 private void UpdateStack(OpCode opc, bool hasthis, Type returnType, int parameterCount)
667 if (opc == OpCodes.Jmp)
671 else if (opc.FlowControl == FlowControl.Call)
674 if ((hasthis && opc != OpCodes.Newobj) || opc == OpCodes.Calli)
680 stackdiff -= parameterCount;
681 if (returnType != moduleBuilder.universe.System_Void)
686 UpdateStack(stackdiff);
690 public void Emit(OpCode opc, MethodInfo method)
693 WriteToken(moduleBuilder.GetMethodTokenForIL(method));
694 UpdateStack(opc, method.HasThis, method.ReturnType, method.ParameterCount);
697 public void Emit(OpCode opc, ConstructorInfo constructor)
699 Emit(opc, constructor.GetMethodInfo());
702 public void Emit(OpCode opc, sbyte arg)
708 public void Emit(OpCode opc, float arg)
714 public void Emit(OpCode opc, string str)
717 code.Write(0x70000000 | moduleBuilder.UserStrings.Add(str));
720 public void Emit(OpCode opc, Type type)
723 if (opc == OpCodes.Ldtoken)
725 code.Write(moduleBuilder.GetTypeToken(type).Token);
729 code.Write(moduleBuilder.GetTypeTokenForMemberRef(type));
733 public void Emit(OpCode opcode, SignatureHelper signature)
736 UpdateStack(opcode, signature.HasThis, signature.ReturnType, signature.ParameterCount);
737 code.Write(0x11000000 | moduleBuilder.StandAloneSig.FindOrAddRecord(moduleBuilder.Blobs.Add(signature.GetSignature(moduleBuilder))));
740 public void EmitCall(OpCode opc, MethodInfo method, Type[] optionalParameterTypes)
742 if (optionalParameterTypes == null || optionalParameterTypes.Length == 0)
749 UpdateStack(opc, method.HasThis, method.ReturnType, method.ParameterCount + optionalParameterTypes.Length);
750 ByteBuffer sig = new ByteBuffer(16);
751 method.MethodSignature.WriteMethodRefSig(moduleBuilder, sig, optionalParameterTypes);
752 MemberRefTable.Record record = new MemberRefTable.Record();
753 if (method.Module == moduleBuilder)
755 record.Class = method.MetadataToken;
759 record.Class = moduleBuilder.GetTypeTokenForMemberRef(method.DeclaringType ?? method.Module.GetModuleType());
761 record.Name = moduleBuilder.Strings.Add(method.Name);
762 record.Signature = moduleBuilder.Blobs.Add(sig);
763 code.Write(0x0A000000 | moduleBuilder.MemberRef.FindOrAddRecord(record));
767 public void __EmitCall(OpCode opc, ConstructorInfo constructor, Type[] optionalParameterTypes)
769 EmitCall(opc, constructor.GetMethodInfo(), optionalParameterTypes);
772 public void EmitCalli(OpCode opc, CallingConvention callingConvention, Type returnType, Type[] parameterTypes)
774 returnType = returnType ?? moduleBuilder.universe.System_Void;
776 UpdateStack(opc, false, returnType, parameterTypes.Length);
777 ByteBuffer sig = new ByteBuffer(16);
778 Signature.WriteStandAloneMethodSig(moduleBuilder, sig, callingConvention, returnType, parameterTypes);
779 code.Write(0x11000000 | moduleBuilder.StandAloneSig.FindOrAddRecord(moduleBuilder.Blobs.Add(sig)));
782 public void EmitCalli(OpCode opc, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, Type[] optionalParameterTypes)
784 returnType = returnType ?? moduleBuilder.universe.System_Void;
785 optionalParameterTypes = optionalParameterTypes ?? Type.EmptyTypes;
787 UpdateStack(opc, (callingConvention & CallingConventions.HasThis | CallingConventions.ExplicitThis) == CallingConventions.HasThis, returnType, parameterTypes.Length + optionalParameterTypes.Length);
788 ByteBuffer sig = new ByteBuffer(16);
789 Signature.WriteStandAloneMethodSig(moduleBuilder, sig, callingConvention, returnType, parameterTypes, optionalParameterTypes);
790 code.Write(0x11000000 | moduleBuilder.StandAloneSig.FindOrAddRecord(moduleBuilder.Blobs.Add(sig)));
793 public void EmitWriteLine(string text)
795 Universe u = moduleBuilder.universe;
796 Emit(OpCodes.Ldstr, text);
797 Emit(OpCodes.Call, u.Import(typeof(Console)).GetMethod("WriteLine", new Type[] { u.System_String }));
800 public void EmitWriteLine(FieldInfo field)
802 Universe u = moduleBuilder.universe;
803 Emit(OpCodes.Call, u.Import(typeof(Console)).GetMethod("get_Out"));
806 Emit(OpCodes.Ldsfld, field);
810 Emit(OpCodes.Ldarg_0);
811 Emit(OpCodes.Ldfld, field);
813 Emit(OpCodes.Callvirt, u.Import(typeof(System.IO.TextWriter)).GetMethod("WriteLine", new Type[] { field.FieldType }));
816 public void EmitWriteLine(LocalBuilder local)
818 Universe u = moduleBuilder.universe;
819 Emit(OpCodes.Call, u.Import(typeof(Console)).GetMethod("get_Out"));
820 Emit(OpCodes.Ldloc, local);
821 Emit(OpCodes.Callvirt, u.Import(typeof(System.IO.TextWriter)).GetMethod("WriteLine", new Type[] { local.LocalType }));
824 public void EndScope()
826 scope.endOffset = code.Position;
827 scope = scope.parent;
830 public void MarkLabel(Label loc)
832 Debug.Assert(stackHeight == -1 || labelStackHeight[loc.Index] == -1 || stackHeight == labelStackHeight[loc.Index]);
833 labels[loc.Index] = code.Position;
834 if (labelStackHeight[loc.Index] == -1)
836 if (stackHeight == -1)
838 // We're at a location that can only be reached by a backward branch,
839 // so according to the "backward branch constraint" that must mean the stack is empty,
840 // but note that this may be an unused label followed by another label that is used and
841 // that does have a non-zero stack height, so we don't yet set stackHeight here.
842 labelStackHeight[loc.Index] = 0;
846 labelStackHeight[loc.Index] = stackHeight;
851 Debug.Assert(stackHeight == -1 || stackHeight == labelStackHeight[loc.Index]);
852 stackHeight = labelStackHeight[loc.Index];
856 public void MarkSequencePoint(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn)
858 SequencePoint sp = new SequencePoint();
859 sp.document = document;
860 sp.offset = code.Position;
861 sp.startLine = startLine;
862 sp.startColumn = startColumn;
863 sp.endLine = endLine;
864 sp.endColumn = endColumn;
865 sequencePoints.Add(sp);
868 public void ThrowException(Type excType)
870 Emit(OpCodes.Newobj, excType.GetConstructor(Type.EmptyTypes));
874 internal int WriteBody(bool initLocals)
876 if (moduleBuilder.symbolWriter != null)
878 Debug.Assert(scope != null && scope.parent == null);
879 scope.endOffset = code.Position;
884 ByteBuffer bb = moduleBuilder.methodBodies;
886 int localVarSigTok = 0;
889 if (locals.Count == 0 && exceptions.Count == 0 && maxStack <= 8 && code.Length < 64)
891 rva = WriteTinyHeaderAndCode(bb);
895 rva = WriteFatHeaderAndCode(bb, ref localVarSigTok, initLocals);
898 if (moduleBuilder.symbolWriter != null)
900 if (sequencePoints.Count != 0)
902 ISymbolDocumentWriter document = sequencePoints[0].document;
903 int[] offsets = new int[sequencePoints.Count];
904 int[] lines = new int[sequencePoints.Count];
905 int[] columns = new int[sequencePoints.Count];
906 int[] endLines = new int[sequencePoints.Count];
907 int[] endColumns = new int[sequencePoints.Count];
908 for (int i = 0; i < sequencePoints.Count; i++)
910 if (sequencePoints[i].document != document)
912 throw new NotImplementedException();
914 offsets[i] = sequencePoints[i].offset;
915 lines[i] = sequencePoints[i].startLine;
916 columns[i] = sequencePoints[i].startColumn;
917 endLines[i] = sequencePoints[i].endLine;
918 endColumns[i] = sequencePoints[i].endColumn;
920 moduleBuilder.symbolWriter.DefineSequencePoints(document, offsets, lines, columns, endLines, endColumns);
923 WriteScope(scope, localVarSigTok);
928 private void ResolveBranches()
930 foreach (LabelFixup fixup in labelFixups)
933 if (fixup.label == -1)
935 code.Position = fixup.offset;
936 int count = code.GetInt32AtCurrentPosition();
937 int offset = fixup.offset + 4 + 4 * count;
939 for (int i = 0; i < count; i++)
941 int index = code.GetInt32AtCurrentPosition();
942 code.Write(labels[index] - offset);
947 code.Position = fixup.offset;
948 byte size = code.GetByteAtCurrentPosition();
949 int branchOffset = labels[fixup.label] - (code.Position + size);
952 WriteByteBranchOffset(branchOffset);
956 code.Write(branchOffset);
962 private int WriteTinyHeaderAndCode(ByteBuffer bb)
964 int rva = bb.Position;
965 const byte CorILMethod_TinyFormat = 0x2;
966 bb.Write((byte)(CorILMethod_TinyFormat | (code.Length << 2)));
971 private int WriteFatHeaderAndCode(ByteBuffer bb, ref int localVarSigTok, bool initLocals)
973 // fat headers require 4-byte alignment
975 int rva = bb.Position;
977 if (locals.Count != 0)
979 ByteBuffer localVarSig = new ByteBuffer(locals.Count + 2);
980 Signature.WriteLocalVarSig(moduleBuilder, localVarSig, locals);
981 localVarSigTok = 0x11000000 | moduleBuilder.StandAloneSig.FindOrAddRecord(moduleBuilder.Blobs.Add(localVarSig));
984 const byte CorILMethod_FatFormat = 0x03;
985 const byte CorILMethod_MoreSects = 0x08;
986 const byte CorILMethod_InitLocals = 0x10;
988 short flagsAndSize = (short)(CorILMethod_FatFormat | (3 << 12));
991 flagsAndSize |= CorILMethod_InitLocals;
994 if (exceptions.Count > 0)
996 flagsAndSize |= CorILMethod_MoreSects;
999 bb.Write(flagsAndSize);
1001 bb.Write(code.Length);
1002 bb.Write(localVarSigTok);
1006 if (exceptions.Count > 0)
1011 foreach (ExceptionBlock block in exceptions)
1013 if (block.tryOffset > 65535 || block.tryLength > 255 || block.handlerOffset > 65535 || block.handlerLength > 255)
1019 exceptions.Sort(exceptions[0]);
1020 if (exceptions.Count * 12 + 4 > 255)
1024 const byte CorILMethod_Sect_EHTable = 0x1;
1025 const byte CorILMethod_Sect_FatFormat = 0x40;
1026 const short COR_ILEXCEPTION_CLAUSE_EXCEPTION = 0x0000;
1027 const short COR_ILEXCEPTION_CLAUSE_FILTER = 0x0001;
1028 const short COR_ILEXCEPTION_CLAUSE_FINALLY = 0x0002;
1029 const short COR_ILEXCEPTION_CLAUSE_FAULT = 0x0004;
1033 bb.Write((byte)(CorILMethod_Sect_EHTable | CorILMethod_Sect_FatFormat));
1034 int dataSize = exceptions.Count * 24 + 4;
1035 bb.Write((byte)dataSize);
1036 bb.Write((short)(dataSize >> 8));
1037 foreach (ExceptionBlock block in exceptions)
1039 if (block.exceptionType == FAULT)
1041 bb.Write((int)COR_ILEXCEPTION_CLAUSE_FAULT);
1043 else if (block.filterOffset != 0)
1045 bb.Write((int)COR_ILEXCEPTION_CLAUSE_FILTER);
1047 else if (block.exceptionType != null)
1049 bb.Write((int)COR_ILEXCEPTION_CLAUSE_EXCEPTION);
1053 bb.Write((int)COR_ILEXCEPTION_CLAUSE_FINALLY);
1055 bb.Write(block.tryOffset);
1056 bb.Write(block.tryLength);
1057 bb.Write(block.handlerOffset);
1058 bb.Write(block.handlerLength);
1059 if (block.exceptionType != null && block.exceptionType != FAULT)
1061 bb.Write(moduleBuilder.GetTypeTokenForMemberRef(block.exceptionType));
1065 bb.Write(block.filterOffset);
1071 bb.Write(CorILMethod_Sect_EHTable);
1072 bb.Write((byte)(exceptions.Count * 12 + 4));
1074 foreach (ExceptionBlock block in exceptions)
1076 if (block.exceptionType == FAULT)
1078 bb.Write(COR_ILEXCEPTION_CLAUSE_FAULT);
1080 else if (block.filterOffset != 0)
1082 bb.Write(COR_ILEXCEPTION_CLAUSE_FILTER);
1084 else if (block.exceptionType != null)
1086 bb.Write(COR_ILEXCEPTION_CLAUSE_EXCEPTION);
1090 bb.Write(COR_ILEXCEPTION_CLAUSE_FINALLY);
1092 bb.Write((short)block.tryOffset);
1093 bb.Write((byte)block.tryLength);
1094 bb.Write((short)block.handlerOffset);
1095 bb.Write((byte)block.handlerLength);
1096 if (block.exceptionType != null && block.exceptionType != FAULT)
1098 bb.Write(moduleBuilder.GetTypeTokenForMemberRef(block.exceptionType));
1102 bb.Write(block.filterOffset);
1110 private void WriteCode(ByteBuffer bb)
1112 int codeOffset = bb.Position;
1113 foreach (int fixup in this.tokenFixups)
1115 moduleBuilder.tokenFixupOffsets.Add(fixup + codeOffset);
1120 private void WriteScope(Scope scope, int localVarSigTok)
1122 moduleBuilder.symbolWriter.OpenScope(scope.startOffset);
1123 foreach (LocalBuilder local in scope.locals)
1125 if (local.name != null)
1127 int startOffset = local.startOffset;
1128 int endOffset = local.endOffset;
1129 if (startOffset == 0 && endOffset == 0)
1131 startOffset = scope.startOffset;
1132 endOffset = scope.endOffset;
1134 moduleBuilder.symbolWriter.DefineLocalVariable2(local.name, 0, localVarSigTok, SymAddressKind.ILOffset, local.LocalIndex, 0, 0, startOffset, endOffset);
1137 foreach (Scope child in scope.children)
1139 WriteScope(child, localVarSigTok);
1141 moduleBuilder.symbolWriter.CloseScope(scope.endOffset);