Merge pull request #819 from brendanzagaeski/patch-1
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Dynamic / Interpreter / Interpreter.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
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.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15
16 #if FEATURE_CORE_DLR
17 using System.Linq.Expressions;
18 #else
19 using Microsoft.Scripting.Ast;
20 #endif
21
22 using System;
23 using System.Reflection;
24 using System.Runtime.CompilerServices;
25 using System.Threading;
26
27 using Microsoft.Scripting.Runtime;
28 using Microsoft.Scripting.Utils;
29 using System.Diagnostics;
30 using System.Collections.Generic;
31
32 namespace Microsoft.Scripting.Interpreter {
33     /// <summary>
34     /// A simple forth-style stack machine for executing Expression trees
35     /// without the need to compile to IL and then invoke the JIT.  This trades
36     /// off much faster compilation time for a slower execution performance.
37     /// For code that is only run a small number of times this can be a 
38     /// sweet spot.
39     /// 
40     /// The core loop in the interpreter is the RunInstructions method.
41     /// </summary>
42     internal sealed class Interpreter {
43         internal static readonly object NoValue = new object();
44         internal const int RethrowOnReturn = Int32.MaxValue;
45
46         // zero: sync compilation
47         // negative: default
48         internal readonly int _compilationThreshold;
49
50         private readonly int _localCount;
51         private readonly HybridReferenceDictionary<LabelTarget, BranchLabel> _labelMapping;
52         private readonly Dictionary<ParameterExpression, LocalVariable> _closureVariables;
53
54         private readonly InstructionArray _instructions;
55         internal readonly object[] _objects;
56         internal readonly RuntimeLabel[] _labels;
57
58         internal readonly string _name;
59         private readonly ExceptionHandler[] _handlers;
60         internal readonly DebugInfo[] _debugInfos;
61
62         internal Interpreter(string name, LocalVariables locals, HybridReferenceDictionary<LabelTarget, BranchLabel> labelMapping,
63             InstructionArray instructions, ExceptionHandler[] handlers, DebugInfo[] debugInfos, int compilationThreshold) {
64
65             _name = name;
66             _localCount = locals.LocalCount;
67             _closureVariables = locals.ClosureVariables;
68
69             _instructions = instructions;
70             _objects = instructions.Objects;
71             _labels = instructions.Labels;
72             _labelMapping = labelMapping;
73
74             _handlers = handlers;
75             _debugInfos = debugInfos;
76             _compilationThreshold = compilationThreshold;
77         }
78
79         internal int ClosureSize {
80             get {
81                 if (_closureVariables == null) {
82                     return 0;
83                 }
84                 return _closureVariables.Count;
85             }
86         }
87
88         internal int LocalCount {
89             get {
90                 return _localCount;
91             }
92         }
93
94         internal bool CompileSynchronously {
95             get { return _compilationThreshold <= 1; }
96         }
97
98         internal InstructionArray Instructions {
99             get { return _instructions; }
100         }
101
102         internal Dictionary<ParameterExpression, LocalVariable> ClosureVariables {
103             get { return _closureVariables; } 
104         }
105
106         internal HybridReferenceDictionary<LabelTarget, BranchLabel> LabelMapping {
107             get { return _labelMapping; }
108         }
109
110         /// <summary>
111         /// Runs instructions within the given frame.
112         /// </summary>
113         /// <remarks>
114         /// Interpreted stack frames are linked via Parent reference so that each CLR frame of this method corresponds 
115         /// to an interpreted stack frame in the chain. It is therefore possible to combine CLR stack traces with 
116         /// interpreted stack traces by aligning interpreted frames to the frames of this method.
117         /// Each group of subsequent frames of Run method corresponds to a single interpreted frame.
118         /// </remarks>
119         [SpecialName, MethodImpl(MethodImplOptions.NoInlining)]
120         public void Run(InterpretedFrame frame) {
121             while (true) {
122                 try {
123                     var instructions = _instructions.Instructions;
124                     int index = frame.InstructionIndex;
125                     while (index < instructions.Length) {
126                         index += instructions[index].Run(frame);
127                         frame.InstructionIndex = index;
128                     }
129                     return;
130                 } catch (Exception exception) {
131                     switch (HandleException(frame, exception)) {
132                         case ExceptionHandlingResult.Rethrow: throw;
133                         case ExceptionHandlingResult.Continue: continue;
134                         case ExceptionHandlingResult.Return: return;
135                     }
136                 }
137             }
138         }
139
140         private ExceptionHandlingResult HandleException(InterpretedFrame frame, Exception exception) {
141             frame.SaveTraceToException(exception);
142             ExceptionHandler handler;
143             frame.InstructionIndex += GotoHandler(frame, exception, out handler);
144
145             if (handler == null || handler.IsFault) {
146                 // run finally/fault blocks:
147                 Run(frame);
148
149                 // a finally block can throw an exception caught by a handler, which cancels the previous exception:
150                 if (frame.InstructionIndex == RethrowOnReturn) {
151                     return ExceptionHandlingResult.Rethrow;
152                 }
153                 return ExceptionHandlingResult.Return;
154             }
155             
156 #if FEATURE_THREAD
157             // stay in the current catch so that ThreadAbortException is not rethrown by CLR:
158             var abort = exception as ThreadAbortException;
159             if (abort != null) {
160                 _anyAbortException = abort;
161                 frame.CurrentAbortHandler = handler;
162             }
163 #endif
164             while (true) {
165                 try {
166                     var instructions = _instructions.Instructions;
167                     int index = frame.InstructionIndex;
168
169                     while (index < instructions.Length) {
170                         var curInstr = instructions[index];                        
171
172                         index += curInstr.Run(frame);
173                         frame.InstructionIndex = index;
174                         
175                         if (curInstr is LeaveExceptionHandlerInstruction) {
176                             // we've completed handling of this exception
177                             return ExceptionHandlingResult.Continue;
178                         }
179                     }
180
181                     if (frame.InstructionIndex == RethrowOnReturn) {
182                         return ExceptionHandlingResult.Rethrow;
183                     }
184
185                     return ExceptionHandlingResult.Return;
186                 } catch (Exception nestedException) {                    
187                     switch (HandleException(frame, nestedException)) {
188                         case ExceptionHandlingResult.Rethrow: throw;
189                         case ExceptionHandlingResult.Continue: continue;
190                         case ExceptionHandlingResult.Return: return ExceptionHandlingResult.Return;
191                         default: throw Assert.Unreachable;
192                     }
193                 }
194             }
195         }
196
197         enum ExceptionHandlingResult {
198             Rethrow,
199             Continue,
200             Return
201         }
202
203 #if FEATURE_THREAD
204         // To get to the current AbortReason object on Thread.CurrentThread 
205         // we need to use ExceptionState property of any ThreadAbortException instance.
206         [ThreadStatic]
207         private static ThreadAbortException _anyAbortException = null;
208
209         internal static void AbortThreadIfRequested(InterpretedFrame frame, int targetLabelIndex) {
210             var abortHandler = frame.CurrentAbortHandler;
211             if (abortHandler != null && !abortHandler.IsInside(frame.Interpreter._labels[targetLabelIndex].Index)) {
212                 frame.CurrentAbortHandler = null;
213
214                 var currentThread = Thread.CurrentThread;
215                 if ((currentThread.ThreadState & System.Threading.ThreadState.AbortRequested) != 0) {
216                     Debug.Assert(_anyAbortException != null);
217
218 #if FEATURE_EXCEPTION_STATE
219                     // The current abort reason needs to be preserved.
220                     currentThread.Abort(_anyAbortException.ExceptionState);
221 #else
222                     currentThread.Abort();
223 #endif
224                 }
225             }
226         }
227 #else
228         internal static void AbortThreadIfRequested(InterpretedFrame frame, int targetLabelIndex) {
229             // nop
230         }
231 #endif
232
233         internal ExceptionHandler GetBestHandler(int instructionIndex, Type exceptionType) {
234             ExceptionHandler best = null;
235             foreach (var handler in _handlers) {
236                 if (handler.Matches(exceptionType, instructionIndex)) {
237                     if (handler.IsBetterThan(best)) {
238                         best = handler;
239                     }
240                 }
241             }
242             return best;
243         }
244
245         internal int ReturnAndRethrowLabelIndex {
246             get {
247                 // the last label is "return and rethrow" label:
248                 Debug.Assert(_labels[_labels.Length - 1].Index == RethrowOnReturn);
249                 return _labels.Length - 1;
250             }
251         }
252
253         internal int GotoHandler(InterpretedFrame frame, object exception, out ExceptionHandler handler) {
254             handler = GetBestHandler(frame.InstructionIndex, exception.GetType());
255             if (handler == null) {
256                 return frame.Goto(ReturnAndRethrowLabelIndex, Interpreter.NoValue);
257             } else {
258                 return frame.Goto(handler.LabelIndex, exception);
259             }
260         }
261     }
262 }