IKVM11  11
Java SE 11 Virtual Machine for .NET
Loading...
Searching...
No Matches
ILGenerator.cs
Go to the documentation of this file.
1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3
4// This file was forked from the .NET RuntimeILGenerator implementation as of 1/24/2024, and updated to emit debug
5// symbols as it used to on .NET Framework.
6
7#nullable enable
8
9using System;
10using System.Buffers.Binary;
11using System.Diagnostics;
12using System.Diagnostics.Contracts;
13using System.Diagnostics.SymbolStore;
15using System.Runtime.InteropServices;
16
18{
19 internal class ILGenerator
20 {
21
22 #region Const Members
23 private const int DefaultSize = 16;
24 private const int DefaultFixupArraySize = 8;
25 private const int DefaultLabelArraySize = 4;
26 private const int DefaultExceptionArraySize = 2;
27 #endregion
28
29 #region Internal Statics
30 internal static T[] EnlargeArray<T>(T[] incoming)
31 {
32 return EnlargeArray(incoming, incoming.Length * 2);
33 }
34
35 internal static T[] EnlargeArray<T>(T[] incoming, int requiredSize)
36 {
37 Debug.Assert(incoming != null);
38
39 T[] temp = new T[requiredSize];
40 Array.Copy(incoming, temp, incoming.Length);
41 return temp;
42 }
43
44 #endregion
45
46 #region Internal Data Members
47 private int m_length;
48 private byte[] m_ILStream;
49
50 private __LabelInfo[]? m_labelList;
51 private int m_labelCount;
52
53 private __FixupData[]? m_fixupData;
54
55 private int m_fixupCount;
56
57 private int[]? m_RelocFixupList;
58 private int m_RelocFixupCount;
59
60 private int m_exceptionCount;
61 private int m_currExcStackCount;
62 private __ExceptionInfo[]? m_exceptions; // This is the list of all of the exceptions in this ILStream.
63 private __ExceptionInfo[]? m_currExcStack; // This is the stack of exceptions which we're currently in.
64
65 internal ScopeTree m_ScopeTree; // this variable tracks all debugging scope information
66 internal LineNumberInfo m_LineNumberInfo; // this variable tracks all line number information
67
68 internal MethodBuilder m_methodBuilder;
69 internal int m_localCount;
70 internal SignatureHelper m_localSignature;
71
72 private int m_curDepth; // Current stack depth, with -1 meaning unknown.
73 private int m_targetDepth; // Stack depth at a target of the previous instruction (when it is branching).
74 private int m_maxDepth; // Running max of the stack depth.
75
76 // Adjustment to add to m_maxDepth for incorrect/invalid IL. For example, when branch instructions
77 // with different stack depths target the same label.
78 private long m_depthAdjustment;
79
80 internal int CurrExcStackCount => m_currExcStackCount;
81
82 internal __ExceptionInfo[]? CurrExcStack => m_currExcStack;
83
84 #endregion
85
86 #region Constructor
87 // package private constructor. This code path is used when client create
88 // ILGenerator through MethodBuilder.
89 internal ILGenerator(MethodBuilder methodBuilder) : this(methodBuilder, 64)
90 {
91 }
92
93 internal ILGenerator(MethodBuilder methodBuilder, int size)
94 {
95 Debug.Assert(methodBuilder != null);
96 Debug.Assert(methodBuilder is MethodBuilder);
97
98 m_ILStream = new byte[Math.Max(size, DefaultSize)];
99
100 // initialize the scope tree
101 m_ScopeTree = new ScopeTree();
102 m_LineNumberInfo = new LineNumberInfo();
103 m_methodBuilder = methodBuilder;
104
105 // initialize local signature
106 m_localSignature = SignatureHelper.GetLocalVarSigHelper(methodBuilder.ModuleBuilder);
107 }
108
109 #endregion
110
111 #region Internal Members
112 internal virtual void RecordTokenFixup()
113 {
114 if (m_RelocFixupList == null)
115 {
116 m_RelocFixupList = new int[DefaultFixupArraySize];
117 }
118 else if (m_RelocFixupList.Length <= m_RelocFixupCount)
119 {
120 m_RelocFixupList = EnlargeArray(m_RelocFixupList);
121 }
122
123 m_RelocFixupList[m_RelocFixupCount++] = m_length;
124 }
125
126 internal void InternalEmit(OpCode opcode)
127 {
128 short opcodeValue = opcode.Value;
129 if (opcode.Size != 1)
130 {
131 BinaryPrimitives.WriteInt16BigEndian(m_ILStream.AsSpan(m_length), opcodeValue);
132 m_length += 2;
133 }
134 else
135 {
136 m_ILStream[m_length++] = (byte)opcodeValue;
137 }
138
139 UpdateStackSize(opcode, opcode.StackChange());
140 }
141
142 [MethodImpl(MethodImplOptions.AggressiveInlining)]
143 internal void UpdateStackSize(OpCode opcode, int stackchange)
144 {
145 // Updates internal variables for keeping track of the stack size
146 // requirements for the function. stackchange specifies the amount
147 // by which the stacksize needs to be updated.
148
149 if (m_curDepth < 0)
150 {
151 // Current depth is "unknown". We get here when:
152 // * this is unreachable code.
153 // * the client uses explicit numeric offsets rather than Labels.
154 m_curDepth = 0;
155 }
156
157 m_curDepth += stackchange;
158 if (m_curDepth < 0)
159 {
160 // Stack underflow. Assume our previous depth computation was flawed.
161 m_depthAdjustment -= m_curDepth;
162 m_curDepth = 0;
163 }
164 else if (m_maxDepth < m_curDepth)
165 m_maxDepth = m_curDepth;
166 Debug.Assert(m_depthAdjustment >= 0);
167 Debug.Assert(m_curDepth >= 0);
168
169 // Record the stack depth at a "target" of this instruction.
170 m_targetDepth = m_curDepth;
171
172 // If the current instruction can't fall through, set the depth to unknown.
173 if (opcode.EndsUncondJmpBlk())
174 m_curDepth = -1;
175 }
176
177 private int GetMethodToken(MethodBase method, Type[]? optionalParameterTypes, bool useMethodDef)
178 {
179 var mi = method is ConstructorInfo ctor ? ctor.GetMethodInfo() : (MethodInfo)method;
180 if (optionalParameterTypes == null || optionalParameterTypes.Length == 0)
181 return m_methodBuilder.ModuleBuilder.GetMethodTokenForIL(mi).Token;
182 else
183 return m_methodBuilder.ModuleBuilder.__GetMethodToken(mi, optionalParameterTypes, null).Token;
184 }
185
186 internal SignatureHelper GetMemberRefSignature(
187 CallingConventions call,
188 Type? returnType,
189 Type[]? parameterTypes,
190 Type[]? optionalParameterTypes)
191 {
192 var sig = SignatureHelper.GetMethodSigHelper(m_methodBuilder.Module, call, returnType);
193 sig.AddArguments(parameterTypes, null, null);
194 if (optionalParameterTypes != null && optionalParameterTypes.Length != 0)
195 {
196 sig.AddSentinel();
197 sig.AddArguments(optionalParameterTypes, null, null);
198 }
199
200 return sig;
201 }
202
203 internal byte[]? BakeByteArray()
204 {
205 // BakeByteArray is an internal function designed to be called by MethodBuilder to do
206 // all of the fixups and return a new byte array representing the byte stream with labels resolved, etc.
207
208 if (m_currExcStackCount != 0)
209 {
210 throw new ArgumentException("The IL Generator cannot be used while there are unclosed exceptions.");
211 }
212
213 if (m_length == 0)
214 return null;
215
216 // Allocate space for the new array.
217 byte[] newBytes = new byte[m_length];
218
219 // Copy the data from the old array
220 Array.Copy(m_ILStream, newBytes, m_length);
221
222 // Do the fixups.
223 // This involves iterating over all of the labels and
224 // replacing them with their proper values.
225 for (int i = 0; i < m_fixupCount; i++)
226 {
227 __FixupData fixupData = m_fixupData![i];
228 int updateAddr = GetLabelPos(fixupData.m_fixupLabel) - (fixupData.m_fixupPos + fixupData.m_fixupInstSize);
229
230 // Handle single byte instructions
231 // Throw an exception if they're trying to store a jump in a single byte instruction that doesn't fit.
232 if (fixupData.m_fixupInstSize == 1)
233 {
234 // Verify that our one-byte arg will fit into a Signed Byte.
235 if (updateAddr < sbyte.MinValue || updateAddr > sbyte.MaxValue)
236 {
237 throw new NotSupportedException(string.Format("Illegal one-byte branch at position: {0}. Requested branch was: {1}.", fixupData.m_fixupPos, updateAddr));
238 }
239
240 // Place the one-byte arg
241 newBytes[fixupData.m_fixupPos] = (byte)updateAddr;
242 }
243 else
244 {
245 // Place the four-byte arg
246 BinaryPrimitives.WriteInt32LittleEndian(newBytes.AsSpan(fixupData.m_fixupPos), updateAddr);
247 }
248 }
249 return newBytes;
250 }
251
252 internal __ExceptionInfo[]? GetExceptions()
253 {
254 if (m_currExcStackCount != 0)
255 {
256 throw new NotSupportedException("The IL Generator cannot be used while there are unclosed exceptions.");
257 }
258
259 if (m_exceptionCount == 0)
260 {
261 return null;
262 }
263
264 var temp = new __ExceptionInfo[m_exceptionCount];
265 Array.Copy(m_exceptions!, temp, m_exceptionCount);
266 SortExceptions(temp);
267 return temp;
268 }
269
270 internal void EnsureCapacity(int size)
271 {
272 // Guarantees an array capable of holding at least size elements.
273 if (m_length + size >= m_ILStream.Length)
274 {
275 IncreaseCapacity(size);
276 }
277 }
278
279 private void IncreaseCapacity(int size)
280 {
281 byte[] temp = new byte[Math.Max(m_ILStream.Length * 2, m_length + size)];
282 Array.Copy(m_ILStream, temp, m_ILStream.Length);
283 m_ILStream = temp;
284 }
285
286 [MethodImpl(MethodImplOptions.AggressiveInlining)]
287 internal void PutInteger4(int value)
288 {
289 BinaryPrimitives.WriteInt32LittleEndian(m_ILStream.AsSpan(m_length), value);
290 m_length += 4;
291 }
292
293 private int GetLabelPos(Label lbl)
294 {
295 // Gets the position in the stream of a particular label.
296 // Verifies that the label exists and that it has been given a value.
297
298 int index = lbl.Id;
299
300 if (index < 0 || index >= m_labelCount || m_labelList is null)
301 throw new ArgumentException("Bad label in ILGenerator.");
302
303 int pos = m_labelList[index].m_pos;
304 if (pos < 0)
305 throw new ArgumentException("Bad label content in ILGenerator.");
306
307 return pos;
308 }
309
310 private void AddFixup(Label lbl, int pos, int instSize)
311 {
312 // Notes the label, position, and instruction size of a new fixup. Expands
313 // all of the fixup arrays as appropriate.
314
315 if (m_fixupData == null)
316 {
317 m_fixupData = new __FixupData[DefaultFixupArraySize];
318 }
319 else if (m_fixupData.Length <= m_fixupCount)
320 {
321 m_fixupData = EnlargeArray(m_fixupData);
322 }
323
324 m_fixupData[m_fixupCount++] = new __FixupData
325 {
326 m_fixupPos = pos,
327 m_fixupLabel = lbl,
328 m_fixupInstSize = instSize
329 };
330
331 int labelIndex = lbl.Id;
332 if (labelIndex < 0 || labelIndex >= m_labelCount || m_labelList is null)
333 throw new ArgumentException("Bad label in ILGenerator.");
334
335 int depth = m_labelList[labelIndex].m_depth;
336 int targetDepth = m_targetDepth;
337 Debug.Assert(depth >= -1);
338 Debug.Assert(targetDepth >= -1);
339 if (depth < targetDepth)
340 {
341 // Either unknown depth for this label or this branch location has a larger depth than previously recorded.
342 // In the latter case, the IL is (likely) invalid, but we just compensate for it.
343 if (depth >= 0)
344 m_depthAdjustment += targetDepth - depth;
345 m_labelList[labelIndex].m_depth = targetDepth;
346 }
347 }
348
349 internal int GetMaxStackSize()
350 {
351 // Limit the computed max stack to 2^16 - 1, since the value is mod`ed by 2^16 by other code.
352 Debug.Assert(m_depthAdjustment >= 0);
353 return (int)Math.Min(ushort.MaxValue, m_maxDepth + m_depthAdjustment);
354 }
355
356 private static void SortExceptions(__ExceptionInfo[] exceptions)
357 {
358 // In order to call exceptions properly we have to sort them in ascending order by their end position.
359 // Just a cheap insertion sort. We don't expect many exceptions (<10), where InsertionSort beats QuickSort.
360 // If we have more exceptions than this in real life, we should consider moving to a QuickSort.
361
362 for (int i = 0; i < exceptions.Length; i++)
363 {
364 int least = i;
365 for (int j = i + 1; j < exceptions.Length; j++)
366 {
367 if (exceptions[least].IsInner(exceptions[j]))
368 {
369 least = j;
370 }
371 }
372 __ExceptionInfo temp = exceptions[i];
373 exceptions[i] = exceptions[least];
374 exceptions[least] = temp;
375 }
376 }
377
378 internal int[]? GetTokenFixups()
379 {
380 if (m_RelocFixupCount == 0)
381 {
382 Debug.Assert(m_RelocFixupList == null);
383 return null;
384 }
385
386 int[] narrowTokens = new int[m_RelocFixupCount];
387 Array.Copy(m_RelocFixupList!, narrowTokens, m_RelocFixupCount);
388 return narrowTokens;
389 }
390 #endregion
391
392 #region Public Members
393
394 #region Emit
395 public void Emit(OpCode opcode)
396 {
397 EnsureCapacity(3);
398 InternalEmit(opcode);
399 }
400
401 public void Emit(OpCode opcode, byte arg)
402 {
403 EnsureCapacity(4);
404 InternalEmit(opcode);
405 m_ILStream[m_length++] = arg;
406 }
407
408 public void Emit(OpCode opcode, short arg)
409 {
410 // Puts opcode onto the stream of instructions followed by arg
411 EnsureCapacity(5);
412 InternalEmit(opcode);
413 BinaryPrimitives.WriteInt16LittleEndian(m_ILStream.AsSpan(m_length), arg);
414 m_length += 2;
415 }
416
417 public void Emit(OpCode opcode, int arg)
418 {
419 // Special-case several opcodes that have shorter variants for common values.
420 if (opcode.Equals(OpCodes.Ldc_I4))
421 {
422 if (arg >= -1 && arg <= 8)
423 {
424 opcode = arg switch
425 {
426 -1 => OpCodes.Ldc_I4_M1,
427 0 => OpCodes.Ldc_I4_0,
428 1 => OpCodes.Ldc_I4_1,
429 2 => OpCodes.Ldc_I4_2,
430 3 => OpCodes.Ldc_I4_3,
431 4 => OpCodes.Ldc_I4_4,
432 5 => OpCodes.Ldc_I4_5,
433 6 => OpCodes.Ldc_I4_6,
434 7 => OpCodes.Ldc_I4_7,
435 _ => OpCodes.Ldc_I4_8,
436 };
437 Emit(opcode);
438 return;
439 }
440
441 if (arg >= -128 && arg <= 127)
442 {
443 Emit(OpCodes.Ldc_I4_S, (sbyte)arg);
444 return;
445 }
446 }
447 else if (opcode.Equals(OpCodes.Ldarg))
448 {
449 if ((uint)arg <= 3)
450 {
451 Emit(arg switch
452 {
453 0 => OpCodes.Ldarg_0,
454 1 => OpCodes.Ldarg_1,
455 2 => OpCodes.Ldarg_2,
456 _ => OpCodes.Ldarg_3,
457 });
458 return;
459 }
460
461 if ((uint)arg <= byte.MaxValue)
462 {
463 Emit(OpCodes.Ldarg_S, (byte)arg);
464 return;
465 }
466
467 if ((uint)arg <= ushort.MaxValue) // this will be true except on misuse of the opcode
468 {
469 Emit(OpCodes.Ldarg, (short)arg);
470 return;
471 }
472 }
473 else if (opcode.Equals(OpCodes.Ldarga))
474 {
475 if ((uint)arg <= byte.MaxValue)
476 {
477 Emit(OpCodes.Ldarga_S, (byte)arg);
478 return;
479 }
480
481 if ((uint)arg <= ushort.MaxValue) // this will be true except on misuse of the opcode
482 {
483 Emit(OpCodes.Ldarga, (short)arg);
484 return;
485 }
486 }
487 else if (opcode.Equals(OpCodes.Starg))
488 {
489 if ((uint)arg <= byte.MaxValue)
490 {
491 Emit(OpCodes.Starg_S, (byte)arg);
492 return;
493 }
494
495 if ((uint)arg <= ushort.MaxValue) // this will be true except on misuse of the opcode
496 {
497 Emit(OpCodes.Starg, (short)arg);
498 return;
499 }
500 }
501
502 // For everything else, put the opcode followed by the arg onto the stream of instructions.
503 EnsureCapacity(7);
504 InternalEmit(opcode);
505 PutInteger4(arg);
506 }
507
508 public void Emit(OpCode opcode, MethodInfo meth)
509 {
510 if (meth is null)
511 throw new ArgumentNullException(nameof(meth));
512
513 if (opcode.Equals(OpCodes.Call) || opcode.Equals(OpCodes.Callvirt) || opcode.Equals(OpCodes.Newobj))
514 {
515 EmitCall(opcode, meth, null);
516 }
517 else
518 {
519 // Reflection doesn't distinguish between these two concepts:
520 // 1. A generic method definition: Foo`1
521 // 2. A generic method definition instantiated over its own generic arguments: Foo`1<!!0>
522 // In RefEmit, we always want 1 for Ld* opcodes and 2 for Call* and Newobj.
523 bool useMethodDef = opcode.Equals(OpCodes.Ldtoken) || opcode.Equals(OpCodes.Ldftn) || opcode.Equals(OpCodes.Ldvirtftn);
524 int tk = GetMethodToken(meth, null, useMethodDef);
525
526 EnsureCapacity(7);
527 InternalEmit(opcode);
528
529 UpdateStackSize(opcode, 0);
530 RecordTokenFixup();
531 PutInteger4(tk);
532 }
533 }
534
535 public void EmitCalli(OpCode opcode, CallingConventions callingConvention,
536 Type? returnType, Type[]? parameterTypes, Type[]? optionalParameterTypes)
537 {
538 int stackchange = 0;
539 if (optionalParameterTypes != null)
540 {
541 if ((callingConvention & CallingConventions.VarArgs) == 0)
542 {
543 // Client should not supply optional parameter in default calling convention
544 throw new InvalidOperationException("Calling convention must be VarArgs.");
545 }
546 }
547
548 ModuleBuilder modBuilder = (ModuleBuilder)m_methodBuilder.ModuleBuilder;
549 SignatureHelper sig = GetMemberRefSignature(callingConvention,
550 returnType,
551 parameterTypes,
552 optionalParameterTypes);
553
554 EnsureCapacity(7);
555 Emit(OpCodes.Calli);
556
557 // If there is a non-void return type, push one.
558 if (returnType != modBuilder.Universe.System_Void)
559 stackchange++;
560 // Pop off arguments if any.
561 if (parameterTypes != null)
562 stackchange -= parameterTypes.Length;
563 // Pop off vararg arguments.
564 if (optionalParameterTypes != null)
565 stackchange -= optionalParameterTypes.Length;
566 // Pop the this parameter if the method has a this parameter.
567 if ((callingConvention & CallingConventions.HasThis) == CallingConventions.HasThis)
568 stackchange--;
569 // Pop the native function pointer.
570 stackchange--;
571 UpdateStackSize(OpCodes.Calli, stackchange);
572
573 RecordTokenFixup();
574 PutInteger4(modBuilder.GetSignatureToken(sig).Token);
575 }
576
577 public void EmitCalli(OpCode opcode, CallingConvention unmanagedCallConv, Type? returnType, Type[]? parameterTypes)
578 {
579 int stackchange = 0;
580 int cParams = 0;
581
582 ModuleBuilder modBuilder = (ModuleBuilder)m_methodBuilder.Module;
583
584 if (parameterTypes != null)
585 {
586 cParams = parameterTypes.Length;
587 }
588
589 SignatureHelper sig = SignatureHelper.GetMethodSigHelper(
590 modBuilder,
591 unmanagedCallConv,
592 returnType);
593
594 if (parameterTypes != null)
595 {
596 for (int i = 0; i < cParams; i++)
597 {
598 sig.AddArgument(parameterTypes[i]);
599 }
600 }
601
602 // If there is a non-void return type, push one.
603 if (returnType != modBuilder.Universe.System_Void)
604 stackchange++;
605
606 // Pop off arguments if any.
607 if (parameterTypes != null)
608 stackchange -= cParams;
609
610 // Pop the native function pointer.
611 stackchange--;
612 UpdateStackSize(OpCodes.Calli, stackchange);
613
614 EnsureCapacity(7);
615 Emit(OpCodes.Calli);
616 RecordTokenFixup();
617 PutInteger4(modBuilder.GetSignatureToken(sig).Token);
618 }
619
620 public void EmitCall(OpCode opcode, MethodInfo methodInfo, Type[]? optionalParameterTypes)
621 {
622 if (methodInfo is null)
623 throw new ArgumentNullException(nameof(methodInfo));
624
625 if (!(opcode.Equals(OpCodes.Call) || opcode.Equals(OpCodes.Callvirt) || opcode.Equals(OpCodes.Newobj)))
626 throw new ArgumentException("The specified opcode cannot be passed to EmitCall.", nameof(opcode));
627
628 int stackchange = 0;
629 int tk = GetMethodToken(methodInfo, optionalParameterTypes, false);
630
631 EnsureCapacity(7);
632 InternalEmit(opcode);
633
634 // Push the return value if there is one.
635 if (methodInfo.ReturnType != m_methodBuilder.ModuleBuilder.Universe.System_Void)
636 stackchange++;
637 // Pop the parameters.
638 Type[] parameters = methodInfo.GetParameterTypes();
639 if (parameters != null)
640 stackchange -= parameters.Length;
641
642 // Pop the this parameter if the method is non-static and the
643 // instruction is not newobj.
644 if (!methodInfo.IsStatic && !opcode.Equals(OpCodes.Newobj))
645 stackchange--;
646 // Pop the optional parameters off the stack.
647 if (optionalParameterTypes != null)
648 stackchange -= optionalParameterTypes.Length;
649 UpdateStackSize(opcode, stackchange);
650
651 RecordTokenFixup();
652 PutInteger4(tk);
653 }
654
655 public void Emit(OpCode opcode, SignatureHelper signature)
656 {
657 if (signature is null)
658 throw new ArgumentNullException(nameof(signature));
659
660 int stackchange = 0;
661 ModuleBuilder modBuilder = (ModuleBuilder)m_methodBuilder.Module;
662 int sig = modBuilder.GetSignatureToken(signature).Token;
663
664 int tempVal = sig;
665
666 EnsureCapacity(7);
667 InternalEmit(opcode);
668
669 // The only IL instruction that has VarPop behaviour, that takes a
670 // Signature token as a parameter is calli. Pop the parameters and
671 // the native function pointer. To be conservative, do not pop the
672 // this pointer since this information is not easily derived from
673 // SignatureHelper.
674 if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
675 {
676 Debug.Assert(opcode.Equals(OpCodes.Calli),
677 "Unexpected opcode encountered for StackBehaviour VarPop.");
678 // Pop the arguments..
679 stackchange -= signature.ArgumentCount;
680 // Pop native function pointer off the stack.
681 stackchange--;
682 UpdateStackSize(opcode, stackchange);
683 }
684
685 RecordTokenFixup();
686 PutInteger4(tempVal);
687 }
688
689 public void Emit(OpCode opcode, ConstructorInfo con)
690 {
691 if (con is null)
692 throw new ArgumentNullException(nameof(con));
693
694 int stackchange = 0;
695
696 // Constructors cannot be generic so the value of UseMethodDef doesn't matter.
697 int tk = GetMethodToken(con, null, true);
698
699 EnsureCapacity(7);
700 InternalEmit(opcode);
701
702 // Make a conservative estimate by assuming a return type and no
703 // this parameter.
704 if (opcode.StackBehaviourPush == StackBehaviour.Varpush)
705 {
706 // Instruction must be one of call or callvirt.
707 Debug.Assert(opcode.Equals(OpCodes.Call) ||
708 opcode.Equals(OpCodes.Callvirt),
709 "Unexpected opcode encountered for StackBehaviour of VarPush.");
710 stackchange++;
711 }
712 if (opcode.StackBehaviourPop == StackBehaviour.Varpop)
713 {
714 // Instruction must be one of call, callvirt or newobj.
715 Debug.Assert(opcode.Equals(OpCodes.Call) ||
716 opcode.Equals(OpCodes.Callvirt) ||
717 opcode.Equals(OpCodes.Newobj),
718 "Unexpected opcode encountered for StackBehaviour of VarPop.");
719
720 Type[] parameters = con.GetParameterTypes();
721 if (parameters != null)
722 stackchange -= parameters.Length;
723 }
724 UpdateStackSize(opcode, stackchange);
725
726 RecordTokenFixup();
727 PutInteger4(tk);
728 }
729
730 public void Emit(OpCode opcode, Type cls)
731 {
732 // Puts opcode onto the stream and then the metadata token represented
733 // by cls. The location of cls is recorded so that the token can be
734 // patched if necessary when persisting the module to a PE.
735
736 ModuleBuilder modBuilder = (ModuleBuilder)m_methodBuilder.Module;
737 bool getGenericDefinition = (opcode == OpCodes.Ldtoken && cls != null && cls.IsGenericTypeDefinition);
738 int tempVal = getGenericDefinition ? modBuilder.GetTypeToken(cls).Token : modBuilder.GetTypeTokenForMemberRef(cls);
739
740 EnsureCapacity(7);
741 InternalEmit(opcode);
742 RecordTokenFixup();
743 PutInteger4(tempVal);
744 }
745
746 public void Emit(OpCode opcode, long arg)
747 {
748 EnsureCapacity(11);
749 InternalEmit(opcode);
750 BinaryPrimitives.WriteInt64LittleEndian(m_ILStream.AsSpan(m_length), arg);
751 m_length += 8;
752 }
753
754 public void Emit(OpCode opcode, float arg)
755 {
756 EnsureCapacity(7);
757 InternalEmit(opcode);
758 BinaryPrimitives.WriteInt32LittleEndian(m_ILStream.AsSpan(m_length), SingleToInt32Bits(arg));
759 m_length += 4;
760 }
761
767 [MethodImpl(MethodImplOptions.AggressiveInlining)]
768 static unsafe int SingleToInt32Bits(float value)
769 {
770#if NETFRAMEWORK
771 return *((int*)&value);
772#else
773 return BitConverter.SingleToInt32Bits(value);
774#endif
775 }
776
777 public void Emit(OpCode opcode, double arg)
778 {
779 EnsureCapacity(11);
780 InternalEmit(opcode);
781 BinaryPrimitives.WriteInt64LittleEndian(m_ILStream.AsSpan(m_length), BitConverter.DoubleToInt64Bits(arg));
782 m_length += 8;
783 }
784
785 public void Emit(OpCode opcode, Label label)
786 {
787 // Puts opcode onto the stream and leaves space to include label
788 // when fixups are done. Labels are created using ILGenerator.DefineLabel and
789 // their location within the stream is fixed by using ILGenerator.MarkLabel.
790 // If a single-byte instruction (designated by the _S suffix in OpCodes.cs) is used,
791 // the label can represent a jump of at most 127 bytes along the stream.
792 //
793 // opcode must represent a branch instruction (although we don't explicitly
794 // verify this). Since branches are relative instructions, label will be replaced with the
795 // correct offset to branch during the fixup process.
796
797 EnsureCapacity(7);
798
799 InternalEmit(opcode);
800 if (OpCodes.TakesSingleByteArgument(opcode))
801 {
802 AddFixup(label, m_length++, 1);
803 }
804 else
805 {
806 AddFixup(label, m_length, 4);
807 m_length += 4;
808 }
809 }
810
811 public void Emit(OpCode opcode, Label[] labels)
812 {
813 if (labels is null)
814 throw new ArgumentNullException(nameof(labels));
815
816 // Emitting a switch table
817
818 int i;
819 int remaining; // number of bytes remaining for this switch instruction to be subtracted
820 // for computing the offset
821
822 int count = labels.Length;
823
824 EnsureCapacity(count * 4 + 7);
825 InternalEmit(opcode);
826 PutInteger4(count);
827 for (remaining = count * 4, i = 0; remaining > 0; remaining -= 4, i++)
828 {
829 AddFixup(labels[i], m_length, remaining);
830 m_length += 4;
831 }
832 }
833
834 public void Emit(OpCode opcode, FieldInfo field)
835 {
836 ModuleBuilder modBuilder = (ModuleBuilder)m_methodBuilder.Module;
837 int tempVal = modBuilder.GetFieldToken(field).Token;
838 EnsureCapacity(7);
839 InternalEmit(opcode);
840 RecordTokenFixup();
841 PutInteger4(tempVal);
842 }
843
844 public void Emit(OpCode opcode, string str)
845 {
846 // Puts the opcode onto the IL stream followed by the metadata token
847 // represented by str. The location of str is recorded for future
848 // fixups if the module is persisted to a PE.
849
850 ModuleBuilder modBuilder = (ModuleBuilder)m_methodBuilder.Module;
851 int tempVal = modBuilder.GetStringConstant(str).Token;
852 EnsureCapacity(7);
853 InternalEmit(opcode);
854 PutInteger4(tempVal);
855 }
856
857 public void Emit(OpCode opcode, LocalBuilder local)
858 {
859 if (local is null)
860 throw new ArgumentNullException(nameof(local));
861
862 // Puts the opcode onto the IL stream followed by the information for local variable local.
863 int tempVal = local.LocalIndex;
864 if (local.Method != m_methodBuilder)
865 {
866 throw new ArgumentException("Local passed in does not belong to this ILGenerator.", nameof(local));
867 }
868 // If the instruction is a ldloc, ldloca a stloc, morph it to the optimal form.
869 if (opcode.Equals(OpCodes.Ldloc))
870 {
871 switch (tempVal)
872 {
873 case 0:
874 opcode = OpCodes.Ldloc_0;
875 break;
876 case 1:
877 opcode = OpCodes.Ldloc_1;
878 break;
879 case 2:
880 opcode = OpCodes.Ldloc_2;
881 break;
882 case 3:
883 opcode = OpCodes.Ldloc_3;
884 break;
885 default:
886 if (tempVal <= 255)
887 opcode = OpCodes.Ldloc_S;
888 break;
889 }
890 }
891 else if (opcode.Equals(OpCodes.Stloc))
892 {
893 switch (tempVal)
894 {
895 case 0:
896 opcode = OpCodes.Stloc_0;
897 break;
898 case 1:
899 opcode = OpCodes.Stloc_1;
900 break;
901 case 2:
902 opcode = OpCodes.Stloc_2;
903 break;
904 case 3:
905 opcode = OpCodes.Stloc_3;
906 break;
907 default:
908 if (tempVal <= 255)
909 opcode = OpCodes.Stloc_S;
910 break;
911 }
912 }
913 else if (opcode.Equals(OpCodes.Ldloca))
914 {
915 if (tempVal <= 255)
916 opcode = OpCodes.Ldloca_S;
917 }
918
919 EnsureCapacity(7);
920 InternalEmit(opcode);
921
922 if (opcode.OperandType == OperandType.InlineNone)
923 return;
924
925 if (!OpCodes.TakesSingleByteArgument(opcode))
926 {
927 BinaryPrimitives.WriteInt16LittleEndian(m_ILStream.AsSpan(m_length), (short)tempVal);
928 m_length += 2;
929 }
930 else
931 {
932 // Handle stloc_1, ldloc_1
933 if (tempVal > byte.MaxValue)
934 {
935 throw new InvalidOperationException("Opcodes using a short-form index cannot address a local position over 255.");
936 }
937 m_ILStream[m_length++] = (byte)tempVal;
938 }
939 }
940 #endregion
941
942 #region Exceptions
943
944 public virtual void ThrowException(Type excType)
945 {
946 if (excType is null)
947 throw new ArgumentNullException(nameof(excType));
948
949 // TODO figure out how to load type here
950 //if (!excType.IsSubclassOf( typeof(Exception)) && excType != typeof(Exception))
951 //throw new ArgumentException(nameof(excType));
952
953 var con = excType.GetConstructor(Type.EmptyTypes);
954 if (con == null)
955 throw new ArgumentException(nameof(excType));
956
957 Emit(OpCodes.Newobj, con);
958 Emit(OpCodes.Throw);
959 }
960
961 public Label BeginExceptionBlock()
962 {
963 // Begin an Exception block. Creating an Exception block records some information,
964 // but does not actually emit any IL onto the stream. Exceptions should be created and
965 // marked in the following form:
966 //
967 // Emit Some IL
968 // BeginExceptionBlock
969 // Emit the IL which should appear within the "try" block
970 // BeginCatchBlock
971 // Emit the IL which should appear within the "catch" block
972 // Optional: BeginCatchBlock (this can be repeated an arbitrary number of times
973 // EndExceptionBlock
974
975 // Delay init
976 m_exceptions ??= new __ExceptionInfo[DefaultExceptionArraySize];
977 m_currExcStack ??= new __ExceptionInfo[DefaultExceptionArraySize];
978
979 if (m_exceptionCount >= m_exceptions.Length)
980 {
981 m_exceptions = EnlargeArray(m_exceptions);
982 }
983
984 if (m_currExcStackCount >= m_currExcStack.Length)
985 {
986 m_currExcStack = EnlargeArray(m_currExcStack);
987 }
988
989 Label endLabel = DefineLabel(0);
990 __ExceptionInfo exceptionInfo = new __ExceptionInfo(m_length, endLabel);
991
992 // add the exception to the tracking list
993 m_exceptions[m_exceptionCount++] = exceptionInfo;
994
995 // Make this exception the current active exception
996 m_currExcStack[m_currExcStackCount++] = exceptionInfo;
997
998 // Stack depth for "try" starts at zero.
999 m_curDepth = 0;
1000
1001 return endLabel;
1002 }
1003
1004 public void EndExceptionBlock()
1005 {
1006 if (m_currExcStackCount == 0)
1007 {
1008 throw new NotSupportedException("Not currently in an exception block.");
1009 }
1010
1011 // Pop the current exception block
1012 __ExceptionInfo current = m_currExcStack![m_currExcStackCount - 1];
1013 m_currExcStack[--m_currExcStackCount] = null!;
1014
1015 Label endLabel = current.GetEndLabel();
1016 int state = current.GetCurrentState();
1017
1018 if (state == __ExceptionInfo.State_Filter ||
1019 state == __ExceptionInfo.State_Try)
1020 {
1021 throw new InvalidOperationException("Incorrect code generation for exception block.");
1022 }
1023
1024 if (state == __ExceptionInfo.State_Catch)
1025 {
1026 Emit(OpCodes.Leave, endLabel);
1027 }
1028 else if (state == __ExceptionInfo.State_Finally || state == __ExceptionInfo.State_Fault)
1029 {
1030 Emit(OpCodes.Endfinally);
1031 }
1032
1033 // Check if we've already set this label.
1034 // The only reason why we might have set this is if we have a finally block.
1035
1036 Label label = m_labelList![endLabel.GetLabelValue()].m_pos != -1
1037 ? current.m_finallyEndLabel
1038 : endLabel;
1039
1040 MarkLabel(label);
1041
1042 current.Done(m_length);
1043 }
1044
1045 public void BeginExceptFilterBlock()
1046 {
1047 // Begins an exception filter block. Emits a branch instruction to the end of the current exception block.
1048
1049 if (m_currExcStackCount == 0)
1050 throw new NotSupportedException("Not currently in an exception block.");
1051
1052 __ExceptionInfo current = m_currExcStack![m_currExcStackCount - 1];
1053
1054 Emit(OpCodes.Leave, current.GetEndLabel());
1055
1056 current.MarkFilterAddr(m_length);
1057
1058 // Stack depth for "filter" starts at one.
1059 m_curDepth = 1;
1060 }
1061
1062 public void BeginCatchBlock(Type? exceptionType)
1063 {
1064 Debug.Assert(ModuleBuilder.IsPseudoToken(m_methodBuilder.ModuleBuilder.GetTypeTokenForMemberRef(exceptionType)) == false);
1065
1066 // Begins a catch block. Emits a branch instruction to the end of the current exception block.
1067
1068 if (m_currExcStackCount == 0)
1069 {
1070 throw new NotSupportedException("Not currently in an exception block.");
1071 }
1072 __ExceptionInfo current = m_currExcStack![m_currExcStackCount - 1];
1073
1074 if (current.GetCurrentState() == __ExceptionInfo.State_Filter)
1075 {
1076 if (exceptionType != null)
1077 {
1078 throw new ArgumentException("Should not specify exception type for catch clause for filter block.");
1079 }
1080
1081 Emit(OpCodes.Endfilter);
1082 }
1083 else
1084 {
1085 // execute this branch if previous clause is Catch or Fault
1086 if (exceptionType is null)
1087 throw new ArgumentNullException(nameof(exceptionType));
1088
1089 Emit(OpCodes.Leave, current.GetEndLabel());
1090 }
1091
1092 current.MarkCatchAddr(m_length, exceptionType);
1093
1094 // Stack depth for "catch" starts at one.
1095 m_curDepth = 1;
1096 }
1097
1098 public void BeginFaultBlock()
1099 {
1100 if (m_currExcStackCount == 0)
1101 {
1102 throw new NotSupportedException("Not currently in an exception block.");
1103 }
1104 __ExceptionInfo current = m_currExcStack![m_currExcStackCount - 1];
1105
1106 // emit the leave for the clause before this one.
1107 Emit(OpCodes.Leave, current.GetEndLabel());
1108
1109 current.MarkFaultAddr(m_length);
1110
1111 // Stack depth for "fault" starts at zero.
1112 m_curDepth = 0;
1113 }
1114
1115 public void BeginFinallyBlock()
1116 {
1117 if (m_currExcStackCount == 0)
1118 {
1119 throw new NotSupportedException("Not currently in an exception block.");
1120 }
1121 __ExceptionInfo current = m_currExcStack![m_currExcStackCount - 1];
1122 int state = current.GetCurrentState();
1123 Label endLabel = current.GetEndLabel();
1124 int catchEndAddr = 0;
1125 if (state != __ExceptionInfo.State_Try)
1126 {
1127 // generate leave for any preceding catch clause
1128 Emit(OpCodes.Leave, endLabel);
1129 catchEndAddr = m_length;
1130 }
1131
1132 MarkLabel(endLabel);
1133
1134 Label finallyEndLabel = DefineLabel(0);
1135 current.SetFinallyEndLabel(finallyEndLabel);
1136
1137 // generate leave for try clause
1138 Emit(OpCodes.Leave, finallyEndLabel);
1139 if (catchEndAddr == 0)
1140 catchEndAddr = m_length;
1141 current.MarkFinallyAddr(m_length, catchEndAddr);
1142
1143 // Stack depth for "finally" starts at zero.
1144 m_curDepth = 0;
1145 }
1146
1147 #endregion
1148
1149 #region Labels
1150 public Label DefineLabel()
1151 {
1152 // We don't know the stack depth at the label yet, so set it to -1.
1153 return DefineLabel(-1);
1154 }
1155
1156 private Label DefineLabel(int depth)
1157 {
1158 // Declares a new Label. This is just a token and does not yet represent any particular location
1159 // within the stream. In order to set the position of the label within the stream, you must call
1160 // Mark Label.
1161 Debug.Assert(depth >= -1);
1162
1163 // Delay init the label array in case we dont use it
1164 m_labelList ??= new __LabelInfo[DefaultLabelArraySize];
1165
1166 if (m_labelCount >= m_labelList.Length)
1167 {
1168 m_labelList = EnlargeArray(m_labelList);
1169 }
1170 m_labelList[m_labelCount].m_pos = -1;
1171 m_labelList[m_labelCount].m_depth = depth;
1172 return new Label(m_labelCount++);
1173 }
1174
1175 public void MarkLabel(Label loc)
1176 {
1177 // Defines a label by setting the position where that label is found within the stream.
1178 // Does not allow a label to be defined more than once.
1179
1180 int labelIndex = loc.Id;
1181
1182 // This should only happen if a label from another generator is used with this one.
1183 if (m_labelList is null || labelIndex < 0 || labelIndex >= m_labelList.Length)
1184 {
1185 throw new ArgumentException("Invalid Label.");
1186 }
1187
1188 if (m_labelList[labelIndex].m_pos != -1)
1189 {
1190 throw new ArgumentException("Label defined multiple times.");
1191 }
1192
1193 m_labelList[labelIndex].m_pos = m_length;
1194
1195 int depth = m_labelList[labelIndex].m_depth;
1196 if (depth < 0)
1197 {
1198 // Unknown depth for this label, indicating that it hasn't been used yet.
1199 // If m_curDepth is unknown, we're in the Backward branch constraint case. See ECMA-335 III.1.7.5.
1200 // The m_depthAdjustment field will compensate for violations of this constraint, as we
1201 // discover them. That is, here we assume a depth of zero. If a (later) branch to this label
1202 // has a positive stack depth, we'll record that as the new depth and add the delta into
1203 // m_depthAdjustment.
1204 if (m_curDepth < 0)
1205 m_curDepth = 0;
1206 m_labelList[labelIndex].m_depth = m_curDepth;
1207 }
1208 else if (depth < m_curDepth)
1209 {
1210 // A branch location with smaller stack targets this label. In this case, the IL is
1211 // invalid, but we just compensate for it.
1212 m_depthAdjustment += m_curDepth - depth;
1213 m_labelList[labelIndex].m_depth = m_curDepth;
1214 }
1215 else if (depth > m_curDepth)
1216 {
1217 // Either the current depth is unknown, or a branch location with larger stack targets
1218 // this label, so the IL is invalid. In either case, just adjust the current depth.
1219 m_curDepth = depth;
1220 }
1221 }
1222
1223 #endregion
1224
1225 #region Debug API
1226
1227 public LocalBuilder DeclareLocal(Type localType)
1228 {
1229 return DeclareLocal(localType, false);
1230 }
1231
1232 public LocalBuilder DeclareLocal(Type localType, bool pinned)
1233 {
1234 // Declare a local of type "local". The current active lexical scope
1235 // will be the scope that local will live.
1236
1237 if (m_methodBuilder is not MethodBuilder methodBuilder)
1238 throw new NotSupportedException();
1239
1240 if (methodBuilder.IsTypeCreated())
1241 {
1242 // cannot change method after its containing type has been created
1243 throw new InvalidOperationException("Unable to change after type has been created.");
1244 }
1245
1246 if (localType is null)
1247 throw new ArgumentNullException(nameof(localType));
1248
1249 if (methodBuilder.IsBaked)
1250 {
1251 throw new InvalidOperationException("Type definition of the method is complete.");
1252 }
1253
1254 // add the localType to local signature
1255 m_localSignature.AddArgument(localType, pinned);
1256
1257 return new LocalBuilder(m_methodBuilder, localType, m_localCount++, pinned);
1258 }
1259
1260 public void UsingNamespace(string usingNamespace)
1261 {
1262 // Specifying the namespace to be used in evaluating locals and watches
1263 // for the current active lexical scope.
1264
1265 if (string.IsNullOrEmpty(usingNamespace))
1266 throw new ArgumentException(nameof(usingNamespace));
1267
1268 if (m_methodBuilder is not MethodBuilder methodBuilder)
1269 throw new NotSupportedException();
1270
1271 int index = ((ILGenerator)methodBuilder.GetILGenerator()).m_ScopeTree.GetCurrentActiveScopeIndex();
1272 if (index == -1)
1273 {
1274 methodBuilder.m_localSymInfo ??= new();
1275 methodBuilder.m_localSymInfo!.AddUsingNamespace(usingNamespace);
1276 }
1277 else
1278 {
1279 m_ScopeTree.AddUsingNamespaceToCurrentScope(usingNamespace);
1280 }
1281 }
1282
1283 public void BeginScope()
1284 {
1285 m_ScopeTree.AddScopeInfo(ScopeAction.Open, m_length);
1286 }
1287
1288 public void EndScope()
1289 {
1290 m_ScopeTree.AddScopeInfo(ScopeAction.Close, m_length);
1291 }
1292
1293 public virtual void MarkSequencePoint(
1294 ISymbolDocumentWriter document,
1295 int startLine, // line number is 1 based
1296 int startColumn, // column is 0 based
1297 int endLine, // line number is 1 based
1298 int endColumn) // column is 0 based
1299 {
1300 if (startLine == 0 || startLine < 0 || endLine == 0 || endLine < 0)
1301 {
1302 throw new ArgumentOutOfRangeException("startLine");
1303 }
1304 Contract.EndContractBlock();
1305 m_LineNumberInfo.AddLineNumberInfo(document, m_length, startLine, startColumn, endLine, endColumn);
1306 }
1307
1308 public int ILOffset => m_length;
1309
1310 public void Emit(OpCode opcode, sbyte arg) => Emit(opcode, (byte)arg);
1311
1312 #endregion
1313
1314 #endregion
1315 }
1316
1317 internal struct __LabelInfo
1318 {
1319 internal int m_pos; // Position in the il stream, with -1 meaning unknown.
1320 internal int m_depth; // Stack depth, with -1 meaning unknown.
1321 }
1322
1323 internal struct __FixupData
1324 {
1325 internal Label m_fixupLabel;
1326 internal int m_fixupPos;
1327
1328 internal int m_fixupInstSize;
1329 }
1330
1331 internal sealed class __ExceptionInfo
1332 {
1333 internal const int None = 0x0000; // COR_ILEXCEPTION_CLAUSE_NONE
1334 internal const int Filter = 0x0001; // COR_ILEXCEPTION_CLAUSE_FILTER
1335 internal const int Finally = 0x0002; // COR_ILEXCEPTION_CLAUSE_FINALLY
1336 internal const int Fault = 0x0004; // COR_ILEXCEPTION_CLAUSE_FAULT
1337 internal const int PreserveStack = 0x0004; // COR_ILEXCEPTION_CLAUSE_PRESERVESTACK
1338
1339 internal const int State_Try = 0;
1340 internal const int State_Filter = 1;
1341 internal const int State_Catch = 2;
1342 internal const int State_Finally = 3;
1343 internal const int State_Fault = 4;
1344 internal const int State_Done = 5;
1345
1346 internal int m_startAddr;
1347 internal int[] m_filterAddr;
1348 internal int[] m_catchAddr;
1349 internal int[] m_catchEndAddr;
1350 internal int[] m_type;
1351 internal Type[] m_catchClass;
1352 internal Label m_endLabel;
1353 internal Label m_finallyEndLabel;
1354 internal int m_endAddr;
1355 internal int m_endFinally;
1356 internal int m_currentCatch;
1357
1358 private int m_currentState;
1359
1360 internal __ExceptionInfo(int startAddr, Label endLabel)
1361 {
1362 m_startAddr = startAddr;
1363 m_endAddr = -1;
1364 m_filterAddr = new int[4];
1365 m_catchAddr = new int[4];
1366 m_catchEndAddr = new int[4];
1367 m_catchClass = new Type[4];
1368 m_currentCatch = 0;
1369 m_endLabel = endLabel;
1370 m_type = new int[4];
1371 m_endFinally = -1;
1372 m_currentState = State_Try;
1373 }
1374
1375 private void MarkHelper(
1376 int catchorfilterAddr, // the starting address of a clause
1377 int catchEndAddr, // the end address of a previous catch clause. Only use when finally is following a catch
1378 Type? catchClass, // catch exception type
1379 int type) // kind of clause
1380 {
1381 int currentCatch = m_currentCatch;
1382 if (currentCatch >= m_catchAddr.Length)
1383 {
1384 m_filterAddr = ILGenerator.EnlargeArray(m_filterAddr);
1385 m_catchAddr = ILGenerator.EnlargeArray(m_catchAddr);
1386 m_catchEndAddr = ILGenerator.EnlargeArray(m_catchEndAddr);
1387 m_catchClass = ILGenerator.EnlargeArray(m_catchClass);
1388 m_type = ILGenerator.EnlargeArray(m_type);
1389 }
1390 if (type == Filter)
1391 {
1392 m_type[currentCatch] = type;
1393 m_filterAddr[currentCatch] = catchorfilterAddr;
1394 m_catchAddr[currentCatch] = -1;
1395 if (currentCatch > 0)
1396 {
1397 Debug.Assert(m_catchEndAddr[currentCatch - 1] == -1, "m_catchEndAddr[m_currentCatch-1] == -1");
1398 m_catchEndAddr[currentCatch - 1] = catchorfilterAddr;
1399 }
1400 }
1401 else
1402 {
1403 // catch or Fault clause
1404 m_catchClass[currentCatch] = catchClass!;
1405 if (m_type[currentCatch] != Filter)
1406 {
1407 m_type[currentCatch] = type;
1408 }
1409 m_catchAddr[currentCatch] = catchorfilterAddr;
1410 if (currentCatch > 0)
1411 {
1412 if (m_type[currentCatch] != Filter)
1413 {
1414 Debug.Assert(m_catchEndAddr[currentCatch - 1] == -1, "m_catchEndAddr[m_currentCatch-1] == -1");
1415 m_catchEndAddr[currentCatch - 1] = catchEndAddr;
1416 }
1417 }
1418 m_catchEndAddr[currentCatch] = -1;
1419 m_currentCatch++;
1420 }
1421
1422 if (m_endAddr == -1)
1423 {
1424 m_endAddr = catchorfilterAddr;
1425 }
1426 }
1427
1428 internal void MarkFilterAddr(int filterAddr)
1429 {
1430 m_currentState = State_Filter;
1431 MarkHelper(filterAddr, filterAddr, null, Filter);
1432 }
1433
1434 internal void MarkFaultAddr(int faultAddr)
1435 {
1436 m_currentState = State_Fault;
1437 MarkHelper(faultAddr, faultAddr, null, Fault);
1438 }
1439
1440 internal void MarkCatchAddr(int catchAddr, Type? catchException)
1441 {
1442 m_currentState = State_Catch;
1443 MarkHelper(catchAddr, catchAddr, catchException, None);
1444 }
1445
1446 internal void MarkFinallyAddr(int finallyAddr, int endCatchAddr)
1447 {
1448 if (m_endFinally != -1)
1449 {
1450 throw new ArgumentException("Exception blocks may have at most one finally clause.");
1451 }
1452
1453 m_currentState = State_Finally;
1454 m_endFinally = finallyAddr;
1455 MarkHelper(finallyAddr, endCatchAddr, null, Finally);
1456 }
1457
1458 internal void Done(int endAddr)
1459 {
1460 Debug.Assert(m_currentCatch > 0, "m_currentCatch > 0");
1461 Debug.Assert(m_catchAddr[m_currentCatch - 1] > 0, "m_catchAddr[m_currentCatch-1] > 0");
1462 Debug.Assert(m_catchEndAddr[m_currentCatch - 1] == -1, "m_catchEndAddr[m_currentCatch-1] == -1");
1463 m_catchEndAddr[m_currentCatch - 1] = endAddr;
1464 m_currentState = State_Done;
1465 }
1466
1467 internal int GetStartAddress()
1468 {
1469 return m_startAddr;
1470 }
1471
1472 internal int GetEndAddress()
1473 {
1474 return m_endAddr;
1475 }
1476
1477 internal int GetFinallyEndAddress()
1478 {
1479 return m_endFinally;
1480 }
1481
1482 internal Label GetEndLabel()
1483 {
1484 return m_endLabel;
1485 }
1486
1487 internal int[] GetFilterAddresses()
1488 {
1489 return m_filterAddr;
1490 }
1491
1492 internal int[] GetCatchAddresses()
1493 {
1494 return m_catchAddr;
1495 }
1496
1497 internal int[] GetCatchEndAddresses()
1498 {
1499 return m_catchEndAddr;
1500 }
1501
1502 internal Type[] GetCatchClass()
1503 {
1504 return m_catchClass;
1505 }
1506
1507 internal int GetNumberOfCatches()
1508 {
1509 return m_currentCatch;
1510 }
1511
1512 internal int[] GetExceptionTypes()
1513 {
1514 return m_type;
1515 }
1516
1517 internal void SetFinallyEndLabel(Label lbl)
1518 {
1519 m_finallyEndLabel = lbl;
1520 }
1521
1522 internal Label GetFinallyEndLabel()
1523 {
1524 return m_finallyEndLabel;
1525 }
1526
1527 // Specifies whether exc is an inner exception for "this". The way
1528 // its determined is by comparing the end address for the last catch
1529 // clause for both exceptions. If they're the same, the start address
1530 // for the exception is compared.
1531 // WARNING: This is not a generic function to determine the innerness
1532 // of an exception. This is somewhat of a mis-nomer. This gives a
1533 // random result for cases where the two exceptions being compared do
1534 // not having a nesting relation.
1535 internal bool IsInner(__ExceptionInfo exc)
1536 {
1537 Debug.Assert(exc != null);
1538 Debug.Assert(m_currentCatch > 0, "m_currentCatch > 0");
1539 Debug.Assert(exc.m_currentCatch > 0, "exc.m_currentCatch > 0");
1540
1541 int exclast = exc.m_currentCatch - 1;
1542 int last = m_currentCatch - 1;
1543
1544 if (exc.m_catchEndAddr[exclast] < m_catchEndAddr[last])
1545 return true;
1546
1547 if (exc.m_catchEndAddr[exclast] != m_catchEndAddr[last])
1548 return false;
1549 Debug.Assert(exc.GetEndAddress() != GetEndAddress(),
1550 "exc.GetEndAddress() != GetEndAddress()");
1551
1552 return exc.GetEndAddress() > GetEndAddress();
1553 }
1554
1555 // 0 indicates in a try block
1556 // 1 indicates in a filter block
1557 // 2 indicates in a catch block
1558 // 3 indicates in a finally block
1559 // 4 indicates Done
1560 internal int GetCurrentState()
1561 {
1562 return m_currentState;
1563 }
1564 }
1565
1572 internal enum ScopeAction : sbyte
1573 {
1574 Open = -0x1,
1575 Close = 0x1
1576 }
1577
1578 internal sealed class ScopeTree
1579 {
1580 internal ScopeTree()
1581 {
1582 // initialize data variables
1583 m_iOpenScopeCount = 0;
1584 m_iCount = 0;
1585 }
1586
1592 internal int GetCurrentActiveScopeIndex()
1593 {
1594 if (m_iCount == 0)
1595 {
1596 return -1;
1597 }
1598
1599 int i = m_iCount - 1;
1600
1601 for (int cClose = 0; cClose > 0 || m_ScopeActions[i] == ScopeAction.Close; i--)
1602 {
1603 cClose += (sbyte)m_ScopeActions[i];
1604 }
1605
1606 return i;
1607 }
1608
1609 internal void AddLocalSymInfoToCurrentScope(
1610 string strName,
1611 byte[] signature,
1612 int slot,
1613 int startOffset,
1614 int endOffset)
1615 {
1616 int i = GetCurrentActiveScopeIndex();
1617 m_localSymInfos[i] ??= new LocalSymInfo();
1618 m_localSymInfos[i]!.AddLocalSymInfo(strName, signature, slot, startOffset, endOffset);
1619 }
1620
1621 internal void AddUsingNamespaceToCurrentScope(string strNamespace)
1622 {
1623 int i = GetCurrentActiveScopeIndex();
1624 m_localSymInfos[i] ??= new LocalSymInfo();
1625 m_localSymInfos[i]!.AddUsingNamespace(strNamespace);
1626 }
1627
1628 internal void AddScopeInfo(ScopeAction sa, int iOffset)
1629 {
1630 if (sa == ScopeAction.Close && m_iOpenScopeCount <= 0)
1631 {
1632 throw new ArgumentException("Non-matching symbol scope.");
1633 }
1634
1635 // make sure that arrays are large enough to hold addition info
1636 EnsureCapacity();
1637
1638 m_ScopeActions[m_iCount] = sa;
1639 m_iOffsets[m_iCount] = iOffset;
1640 m_localSymInfos[m_iCount] = null;
1641 checked { m_iCount++; }
1642
1643 m_iOpenScopeCount += -(sbyte)sa;
1644 }
1645
1649 internal void EnsureCapacity()
1650 {
1651 if (m_iCount == 0)
1652 {
1653 // First time. Allocate the arrays.
1654 m_iOffsets = new int[InitialSize];
1655 m_ScopeActions = new ScopeAction[InitialSize];
1656 m_localSymInfos = new LocalSymInfo[InitialSize];
1657 }
1658 else if (m_iCount == m_iOffsets.Length)
1659 {
1660 // the arrays are full. Enlarge the arrays
1661 // It would probably be simpler to just use Lists here.
1662 int newSize = checked(m_iCount * 2);
1663 int[] temp = new int[newSize];
1664 Array.Copy(m_iOffsets, temp, m_iCount);
1665 m_iOffsets = temp;
1666
1667 ScopeAction[] tempSA = new ScopeAction[newSize];
1668 Array.Copy(m_ScopeActions, tempSA, m_iCount);
1669 m_ScopeActions = tempSA;
1670
1671 LocalSymInfo[] tempLSI = new LocalSymInfo[newSize];
1672 Array.Copy(m_localSymInfos, tempLSI, m_iCount);
1673 m_localSymInfos = tempLSI;
1674 }
1675 }
1676 internal void EmitScopeTree(ISymbolWriter symWriter)
1677 {
1678 int i;
1679 for (i = 0; i < m_iCount; i++)
1680 {
1681 if (m_ScopeActions != null && m_ScopeActions[i] == ScopeAction.Open)
1682 {
1683 symWriter.OpenScope(m_iOffsets[i]);
1684 }
1685 else
1686 {
1687 symWriter.CloseScope(m_iOffsets[i]);
1688 }
1689 if (m_localSymInfos != null && m_localSymInfos[i] != null)
1690 {
1691 m_localSymInfos[i]!.EmitLocalSymInfo(symWriter);
1692 }
1693 }
1694 }
1695
1696 internal int[] m_iOffsets = null!; // array of offsets
1697 internal ScopeAction[] m_ScopeActions = null!; // array of scope actions
1698 internal int m_iCount; // how many entries in the arrays are occupied
1699 internal int m_iOpenScopeCount; // keep track how many scopes are open
1700 internal const int InitialSize = 16;
1701 internal LocalSymInfo?[] m_localSymInfos = null!; // keep track debugging local information
1702 }
1703
1704
1705 /***************************
1706 *
1707 * This class tracks the line number info
1708 *
1709 ***************************/
1710 internal sealed class LineNumberInfo
1711 {
1712 internal LineNumberInfo()
1713 {
1714 // initialize data variables
1715 m_DocumentCount = 0;
1716 m_iLastFound = 0;
1717 }
1718
1719 internal void AddLineNumberInfo(
1720 ISymbolDocumentWriter document,
1721 int iOffset,
1722 int iStartLine,
1723 int iStartColumn,
1724 int iEndLine,
1725 int iEndColumn)
1726 {
1727 int i;
1728
1729 // make sure that arrays are large enough to hold addition info
1730 i = FindDocument(document);
1731
1732 Contract.Assert(i < m_DocumentCount, "Bad document look up!");
1733 m_Documents[i].AddLineNumberInfo(document, iOffset, iStartLine, iStartColumn, iEndLine, iEndColumn);
1734 }
1735
1736 // Find a REDocument representing document. If we cannot find one, we will add a new entry into
1737 // the REDocument array.
1738 private int FindDocument(ISymbolDocumentWriter document)
1739 {
1740 int i;
1741
1742 // This is an optimization. The chance that the previous line is coming from the same
1743 // document is very high.
1744 if (m_iLastFound < m_DocumentCount && m_Documents[m_iLastFound].m_document == document)
1745 return m_iLastFound;
1746
1747 for (i = 0; i < m_DocumentCount; i++)
1748 {
1749 if (m_Documents[i].m_document == document)
1750 {
1751 m_iLastFound = i;
1752 return m_iLastFound;
1753 }
1754 }
1755
1756 // cannot find an existing document so add one to the array
1757 EnsureCapacity();
1758 m_iLastFound = m_DocumentCount;
1759 m_Documents[m_iLastFound] = new REDocument(document);
1760 checked { m_DocumentCount++; }
1761 return m_iLastFound;
1762 }
1763
1764 /**************************
1765 *
1766 * Helper to ensure arrays are large enough
1767 *
1768 **************************/
1769 private void EnsureCapacity()
1770 {
1771 if (m_DocumentCount == 0)
1772 {
1773 // First time. Allocate the arrays.
1774 m_Documents = new REDocument[InitialSize];
1775 }
1776 else if (m_DocumentCount == m_Documents.Length)
1777 {
1778 // the arrays are full. Enlarge the arrays
1779 REDocument[] temp = new REDocument[m_DocumentCount * 2];
1780 Array.Copy(m_Documents, temp, m_DocumentCount);
1781 m_Documents = temp;
1782 }
1783 }
1784
1785 internal void EmitLineNumberInfo(ISymbolWriter symWriter)
1786 {
1787 for (int i = 0; i < m_DocumentCount; i++)
1788 m_Documents[i].EmitLineNumberInfo(symWriter);
1789 }
1790
1791 private int m_DocumentCount; // how many documents that we have right now
1792 private REDocument[] m_Documents; // array of documents
1793 private const int InitialSize = 16;
1794 private int m_iLastFound;
1795 }
1796
1797
1798 /***************************
1799 *
1800 * This class tracks the line number info
1801 *
1802 ***************************/
1803 internal sealed class REDocument
1804 {
1805 internal REDocument(ISymbolDocumentWriter document)
1806 {
1807 // initialize data variables
1808 m_iLineNumberCount = 0;
1809 m_document = document;
1810 }
1811
1812 internal void AddLineNumberInfo(
1813 ISymbolDocumentWriter document,
1814 int iOffset,
1815 int iStartLine,
1816 int iStartColumn,
1817 int iEndLine,
1818 int iEndColumn)
1819 {
1820 Contract.Assert(document == m_document, "Bad document look up!");
1821
1822 // make sure that arrays are large enough to hold addition info
1823 EnsureCapacity();
1824
1825 m_iOffsets[m_iLineNumberCount] = iOffset;
1826 m_iLines[m_iLineNumberCount] = iStartLine;
1827 m_iColumns[m_iLineNumberCount] = iStartColumn;
1828 m_iEndLines[m_iLineNumberCount] = iEndLine;
1829 m_iEndColumns[m_iLineNumberCount] = iEndColumn;
1830 checked { m_iLineNumberCount++; }
1831 }
1832
1833 /**************************
1834 *
1835 * Helper to ensure arrays are large enough
1836 *
1837 **************************/
1838 private void EnsureCapacity()
1839 {
1840 if (m_iLineNumberCount == 0)
1841 {
1842 // First time. Allocate the arrays.
1843 m_iOffsets = new int[InitialSize];
1844 m_iLines = new int[InitialSize];
1845 m_iColumns = new int[InitialSize];
1846 m_iEndLines = new int[InitialSize];
1847 m_iEndColumns = new int[InitialSize];
1848 }
1849 else if (m_iLineNumberCount == m_iOffsets.Length)
1850 {
1851 // the arrays are full. Enlarge the arrays
1852 // It would probably be simpler to just use Lists here
1853 int newSize = checked(m_iLineNumberCount * 2);
1854 int[] temp = new int[newSize];
1855 Array.Copy(m_iOffsets, temp, m_iLineNumberCount);
1856 m_iOffsets = temp;
1857
1858 temp = new int[newSize];
1859 Array.Copy(m_iLines, temp, m_iLineNumberCount);
1860 m_iLines = temp;
1861
1862 temp = new int[newSize];
1863 Array.Copy(m_iColumns, temp, m_iLineNumberCount);
1864 m_iColumns = temp;
1865
1866 temp = new int[newSize];
1867 Array.Copy(m_iEndLines, temp, m_iLineNumberCount);
1868 m_iEndLines = temp;
1869
1870 temp = new int[newSize];
1871 Array.Copy(m_iEndColumns, temp, m_iLineNumberCount);
1872 m_iEndColumns = temp;
1873 }
1874 }
1875
1876 internal void EmitLineNumberInfo(ISymbolWriter symWriter)
1877 {
1878 int[] iOffsetsTemp;
1879 int[] iLinesTemp;
1880 int[] iColumnsTemp;
1881 int[] iEndLinesTemp;
1882 int[] iEndColumnsTemp;
1883
1884 if (m_iLineNumberCount == 0)
1885 return;
1886 // reduce the array size to be exact
1887 iOffsetsTemp = new int[m_iLineNumberCount];
1888 Array.Copy(m_iOffsets, iOffsetsTemp, m_iLineNumberCount);
1889
1890 iLinesTemp = new int[m_iLineNumberCount];
1891 Array.Copy(m_iLines, iLinesTemp, m_iLineNumberCount);
1892
1893 iColumnsTemp = new int[m_iLineNumberCount];
1894 Array.Copy(m_iColumns, iColumnsTemp, m_iLineNumberCount);
1895
1896 iEndLinesTemp = new int[m_iLineNumberCount];
1897 Array.Copy(m_iEndLines, iEndLinesTemp, m_iLineNumberCount);
1898
1899 iEndColumnsTemp = new int[m_iLineNumberCount];
1900 Array.Copy(m_iEndColumns, iEndColumnsTemp, m_iLineNumberCount);
1901
1902 symWriter.DefineSequencePoints(m_document, iOffsetsTemp, iLinesTemp, iColumnsTemp, iEndLinesTemp, iEndColumnsTemp);
1903 }
1904
1905 private int[] m_iOffsets; // array of offsets
1906 private int[] m_iLines; // array of offsets
1907 private int[] m_iColumns; // array of offsets
1908 private int[] m_iEndLines; // array of offsets
1909 private int[] m_iEndColumns; // array of offsets
1910 internal ISymbolDocumentWriter m_document; // The ISymbolDocumentWriter that this REDocument is tracking.
1911 private int m_iLineNumberCount; // how many entries in the arrays are occupied
1912 private const int InitialSize = 16;
1913 } // end of REDocument
1914
1915}
1916
1917#nullable restore
IKVM.Reflection.Type Type
IKVM.Reflection.ConstructorInfo ConstructorInfo
IKVM.Reflection.FieldInfo FieldInfo
IKVM.Reflection.MethodInfo MethodInfo
IKVM.Reflection.MethodBase MethodBase
System.Runtime.InteropServices.CallingConvention CallingConvention
Definition Signature.cs:35