1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
5 * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
6 * copy of the license can be found in the License.html file at the root of this distribution. If
7 * you cannot locate the Apache License, Version 2.0, please send an email to
8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
9 * by the terms of the Apache License, Version 2.0.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
17 using System.Collections.Generic;
18 using System.Diagnostics;
19 using System.Reflection;
20 using System.Reflection.Emit;
21 using System.Diagnostics.SymbolStore;
23 // Not needed in CLR 4 builds because we have the
24 // ILGenerator.ILOffset property.
26 #if CLR2 || SILVERLIGHT
29 namespace Microsoft.Scripting.Ast.Compiler {
31 namespace System.Linq.Expressions.Compiler {
34 /// Wraps ILGenerator with code that tracks the current IL offset as instructions are emitted into the IL stream.
36 internal sealed class OffsetTrackingILGenerator {
37 private readonly ILGenerator _ilg;
40 internal int ILOffset { get { return _offset; } }
42 internal OffsetTrackingILGenerator(ILGenerator ilg) {
43 Debug.Assert(ilg != null);
47 private void AdvanceOffset(OpCode opcode) {
48 _offset += opcode.Size;
51 private void AdvanceOffsetWithLabel(OpCode opcode) {
52 AdvanceOffset(opcode);
53 if (OpCodes.TakesSingleByteArgument(opcode)) {
60 #region Simple Instructions
62 internal void Emit(OpCode opcode) {
64 AdvanceOffset(opcode);
65 AssertOffsetMatches();
68 internal void Emit(OpCode opcode, byte arg) {
69 _ilg.Emit(opcode, arg);
70 AdvanceOffset(opcode);
72 AssertOffsetMatches();
75 internal void Emit(OpCode opcode, sbyte arg) {
76 _ilg.Emit(opcode, arg);
77 AdvanceOffset(opcode);
79 AssertOffsetMatches();
82 internal void Emit(OpCode opcode, int arg) {
83 _ilg.Emit(opcode, arg);
84 AdvanceOffset(opcode);
86 AssertOffsetMatches();
89 internal void Emit(OpCode opcode, MethodInfo meth) {
90 _ilg.Emit(opcode, meth);
91 AdvanceOffset(opcode);
93 AssertOffsetMatches();
96 internal void EmitCall(OpCode opcode, MethodInfo methodInfo, Type[] optionalParameterTypes) {
97 _ilg.EmitCall(opcode, methodInfo, optionalParameterTypes);
98 AdvanceOffset(opcode);
100 AssertOffsetMatches();
103 internal void Emit(OpCode opcode, ConstructorInfo con) {
104 _ilg.Emit(opcode, con);
105 AdvanceOffset(opcode);
107 AssertOffsetMatches();
110 internal void Emit(OpCode opcode, Type cls) {
111 _ilg.Emit(opcode, cls);
112 AdvanceOffset(opcode);
114 AssertOffsetMatches();
117 internal void Emit(OpCode opcode, long arg) {
118 _ilg.Emit(opcode, arg);
119 AdvanceOffset(opcode);
121 AssertOffsetMatches();
124 internal void Emit(OpCode opcode, float arg) {
125 _ilg.Emit(opcode, arg);
126 AdvanceOffset(opcode);
128 AssertOffsetMatches();
131 internal void Emit(OpCode opcode, double arg) {
132 _ilg.Emit(opcode, arg);
133 AdvanceOffset(opcode);
135 AssertOffsetMatches();
138 internal void Emit(OpCode opcode, Label label) {
139 _ilg.Emit(opcode, label);
140 AdvanceOffsetWithLabel(opcode);
141 AssertOffsetMatches();
144 internal void Emit(OpCode opcode, Label[] labels) {
145 _ilg.Emit(opcode, labels);
146 AdvanceOffset(opcode);
148 for (int remaining = labels.Length * 4, i = 0; remaining > 0; remaining -= 4, i++) {
151 AssertOffsetMatches();
154 internal void Emit(OpCode opcode, FieldInfo field) {
155 _ilg.Emit(opcode, field);
156 AdvanceOffset(opcode);
158 AssertOffsetMatches();
161 internal void Emit(OpCode opcode, String str) {
162 _ilg.Emit(opcode, str);
163 AdvanceOffset(opcode);
165 AssertOffsetMatches();
168 internal void Emit(OpCode opcode, LocalBuilder local) {
169 _ilg.Emit(opcode, local);
170 int tempVal = local.LocalIndex;
171 if (opcode.Equals(OpCodes.Ldloc)) {
174 opcode = OpCodes.Ldloc_0;
177 opcode = OpCodes.Ldloc_1;
180 opcode = OpCodes.Ldloc_2;
183 opcode = OpCodes.Ldloc_3;
187 opcode = OpCodes.Ldloc_S;
190 } else if (opcode.Equals(OpCodes.Stloc)) {
193 opcode = OpCodes.Stloc_0;
196 opcode = OpCodes.Stloc_1;
199 opcode = OpCodes.Stloc_2;
202 opcode = OpCodes.Stloc_3;
206 opcode = OpCodes.Stloc_S;
209 } else if (opcode.Equals(OpCodes.Ldloca)) {
211 opcode = OpCodes.Ldloca_S;
214 AdvanceOffset(opcode);
216 if (opcode.OperandType == OperandType.InlineNone)
218 else if (!OpCodes.TakesSingleByteArgument(opcode)) {
223 AssertOffsetMatches();
228 #region Exception Handling
230 private enum ExceptionState {
238 private Stack<ExceptionState> _exceptionState = new Stack<ExceptionState>();
240 internal void BeginExceptionBlock() {
241 _ilg.BeginExceptionBlock();
242 _exceptionState.Push(ExceptionState.Try);
243 AssertOffsetMatches();
246 internal void EndExceptionBlock() {
247 _ilg.EndExceptionBlock();
249 ExceptionState state = _exceptionState.Pop();
250 if (state == ExceptionState.Catch) {
251 AdvanceOffsetWithLabel(OpCodes.Leave);
252 } else if (state == ExceptionState.Finally || state == ExceptionState.Fault) {
253 AdvanceOffset(OpCodes.Endfinally);
256 AssertOffsetMatches();
259 internal void BeginExceptFilterBlock() {
260 _ilg.BeginExceptFilterBlock();
262 _exceptionState.Pop();
263 _exceptionState.Push(ExceptionState.Filter);
265 AssertOffsetMatches();
268 internal void BeginCatchBlock(Type exceptionType) {
269 _ilg.BeginCatchBlock(exceptionType);
271 ExceptionState state = _exceptionState.Pop();
272 if (state == ExceptionState.Filter) {
273 AdvanceOffset(OpCodes.Endfilter);
275 AdvanceOffsetWithLabel(OpCodes.Leave);
278 _exceptionState.Push(ExceptionState.Catch);
280 AssertOffsetMatches();
283 internal void BeginFaultBlock() {
284 _ilg.BeginFaultBlock();
286 AdvanceOffsetWithLabel(OpCodes.Leave);
287 _exceptionState.Pop();
288 _exceptionState.Push(ExceptionState.Fault);
290 AssertOffsetMatches();
293 internal void BeginFinallyBlock() {
294 _ilg.BeginFinallyBlock();
296 ExceptionState state = _exceptionState.Pop();
297 if (state != ExceptionState.Try) {
298 // leave for any preceeding catch clause
299 AdvanceOffsetWithLabel(OpCodes.Leave);
302 // leave for try clause
303 AdvanceOffsetWithLabel(OpCodes.Leave);
304 _exceptionState.Push(ExceptionState.Finally);
306 AssertOffsetMatches();
311 #region Labels and Locals
313 internal Label DefineLabel() {
314 return _ilg.DefineLabel();
317 internal void MarkLabel(Label loc) {
321 internal LocalBuilder DeclareLocal(Type localType) {
322 return _ilg.DeclareLocal(localType);
325 internal void MarkSequencePoint(ISymbolDocumentWriter document, int startLine, int startColumn, int endLine, int endColumn) {
326 _ilg.MarkSequencePoint(document, startLine, startColumn, endLine, endColumn);
334 private FieldInfo _ilgOffsetField;
335 private bool _checkOffset = true;
338 [Conditional("STRESS_DEBUG")]
339 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
340 private void AssertOffsetMatches() {
348 if (_ilgOffsetField == null) {
349 _ilgOffsetField = typeof(ILGenerator).GetField("m_length", BindingFlags.NonPublic | BindingFlags.Instance);
351 m_length = (int)_ilgOffsetField.GetValue(_ilg);
352 } catch (Exception) {
353 _checkOffset = false;
357 Debug.Assert(m_length == _offset);