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.Linq.Expressions;
19 using Microsoft.Scripting.Ast;
23 using System.Reflection;
24 using System.Runtime.CompilerServices;
25 using System.Threading;
27 using Microsoft.Scripting.Runtime;
28 using Microsoft.Scripting.Utils;
29 using System.Diagnostics;
30 using System.Collections.Generic;
32 namespace Microsoft.Scripting.Interpreter {
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
40 /// The core loop in the interpreter is the RunInstructions method.
42 internal sealed class Interpreter {
43 internal static readonly object NoValue = new object();
44 internal const int RethrowOnReturn = Int32.MaxValue;
46 // zero: sync compilation
48 internal readonly int _compilationThreshold;
50 private readonly int _localCount;
51 private readonly HybridReferenceDictionary<LabelTarget, BranchLabel> _labelMapping;
52 private readonly Dictionary<ParameterExpression, LocalVariable> _closureVariables;
54 private readonly InstructionArray _instructions;
55 internal readonly object[] _objects;
56 internal readonly RuntimeLabel[] _labels;
58 internal readonly string _name;
59 private readonly ExceptionHandler[] _handlers;
60 internal readonly DebugInfo[] _debugInfos;
62 internal Interpreter(string name, LocalVariables locals, HybridReferenceDictionary<LabelTarget, BranchLabel> labelMapping,
63 InstructionArray instructions, ExceptionHandler[] handlers, DebugInfo[] debugInfos, int compilationThreshold) {
66 _localCount = locals.LocalCount;
67 _closureVariables = locals.ClosureVariables;
69 _instructions = instructions;
70 _objects = instructions.Objects;
71 _labels = instructions.Labels;
72 _labelMapping = labelMapping;
75 _debugInfos = debugInfos;
76 _compilationThreshold = compilationThreshold;
79 internal int ClosureSize {
81 if (_closureVariables == null) {
84 return _closureVariables.Count;
88 internal int LocalCount {
94 internal bool CompileSynchronously {
95 get { return _compilationThreshold <= 1; }
98 internal InstructionArray Instructions {
99 get { return _instructions; }
102 internal Dictionary<ParameterExpression, LocalVariable> ClosureVariables {
103 get { return _closureVariables; }
106 internal HybridReferenceDictionary<LabelTarget, BranchLabel> LabelMapping {
107 get { return _labelMapping; }
111 /// Runs instructions within the given frame.
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.
119 [SpecialName, MethodImpl(MethodImplOptions.NoInlining)]
120 public void Run(InterpretedFrame frame) {
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;
130 } catch (Exception exception) {
131 switch (HandleException(frame, exception)) {
132 case ExceptionHandlingResult.Rethrow: throw;
133 case ExceptionHandlingResult.Continue: continue;
134 case ExceptionHandlingResult.Return: return;
140 private ExceptionHandlingResult HandleException(InterpretedFrame frame, Exception exception) {
141 frame.SaveTraceToException(exception);
142 ExceptionHandler handler;
143 frame.InstructionIndex += GotoHandler(frame, exception, out handler);
145 if (handler == null || handler.IsFault) {
146 // run finally/fault blocks:
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;
153 return ExceptionHandlingResult.Return;
157 // stay in the current catch so that ThreadAbortException is not rethrown by CLR:
158 var abort = exception as ThreadAbortException;
160 _anyAbortException = abort;
161 frame.CurrentAbortHandler = handler;
166 var instructions = _instructions.Instructions;
167 int index = frame.InstructionIndex;
169 while (index < instructions.Length) {
170 var curInstr = instructions[index];
172 index += curInstr.Run(frame);
173 frame.InstructionIndex = index;
175 if (curInstr is LeaveExceptionHandlerInstruction) {
176 // we've completed handling of this exception
177 return ExceptionHandlingResult.Continue;
181 if (frame.InstructionIndex == RethrowOnReturn) {
182 return ExceptionHandlingResult.Rethrow;
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;
197 enum ExceptionHandlingResult {
204 // To get to the current AbortReason object on Thread.CurrentThread
205 // we need to use ExceptionState property of any ThreadAbortException instance.
207 private static ThreadAbortException _anyAbortException = null;
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;
214 var currentThread = Thread.CurrentThread;
215 if ((currentThread.ThreadState & System.Threading.ThreadState.AbortRequested) != 0) {
216 Debug.Assert(_anyAbortException != null);
218 #if FEATURE_EXCEPTION_STATE
219 // The current abort reason needs to be preserved.
220 currentThread.Abort(_anyAbortException.ExceptionState);
222 currentThread.Abort();
228 internal static void AbortThreadIfRequested(InterpretedFrame frame, int targetLabelIndex) {
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)) {
245 internal int ReturnAndRethrowLabelIndex {
247 // the last label is "return and rethrow" label:
248 Debug.Assert(_labels[_labels.Length - 1].Index == RethrowOnReturn);
249 return _labels.Length - 1;
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);
258 return frame.Goto(handler.LabelIndex, exception);