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.Collections.Generic;
24 using System.Diagnostics;
25 using System.Reflection;
26 using System.Runtime.CompilerServices;
27 using System.Threading;
28 using Microsoft.Scripting.Utils;
29 using Microsoft.Scripting.Runtime;
31 namespace Microsoft.Scripting.Interpreter {
33 using InterpretedFrameThreadLocal = ThreadLocal<InterpretedFrame>;
35 using InterpretedFrameThreadLocal = Microsoft.Scripting.Utils.ThreadLocal<InterpretedFrame>;
38 public sealed class InterpretedFrame {
39 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
40 public static readonly InterpretedFrameThreadLocal CurrentFrame = new InterpretedFrameThreadLocal();
42 internal readonly Interpreter Interpreter;
43 internal InterpretedFrame _parent;
45 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")]
46 private int[] _continuations;
47 private int _continuationIndex;
48 private int _pendingContinuation;
49 private object _pendingValue;
51 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")]
52 public readonly object[] Data;
54 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")]
55 public readonly StrongBox<object>[] Closure;
57 public int StackIndex;
58 public int InstructionIndex;
60 // When a ThreadAbortException is raised from interpreted code this is the first frame that caught it.
61 // No handlers within this handler re-abort the current thread when left.
62 public ExceptionHandler CurrentAbortHandler;
64 internal InterpretedFrame(Interpreter interpreter, StrongBox<object>[] closure) {
65 Interpreter = interpreter;
66 StackIndex = interpreter.LocalCount;
67 Data = new object[StackIndex + interpreter.Instructions.MaxStackDepth];
69 int c = interpreter.Instructions.MaxContinuationDepth;
71 _continuations = new int[c];
77 public DebugInfo GetDebugInfo(int instructionIndex) {
78 return DebugInfo.GetMatchingDebugInfo(Interpreter._debugInfos, instructionIndex);
82 get { return Interpreter._name; }
85 #region Data Stack Operations
87 public void Push(object value) {
88 Data[StackIndex++] = value;
91 public void Push(bool value) {
92 Data[StackIndex++] = value ? ScriptingRuntimeHelpers.True : ScriptingRuntimeHelpers.False;
95 public void Push(int value) {
96 Data[StackIndex++] = ScriptingRuntimeHelpers.Int32ToObject(value);
100 return Data[--StackIndex];
103 public object Pop(int n) {
104 int si = StackIndex - n;
109 internal void SetStackDepth(int depth) {
110 StackIndex = Interpreter.LocalCount + depth;
113 public object Peek() {
114 return Data[StackIndex - 1];
119 Data[i] = Data[i - 1];
127 public InterpretedFrame Parent {
128 get { return _parent; }
131 public static bool IsInterpretedFrame(MethodBase method) {
132 ContractUtils.RequiresNotNull(method, "method");
133 return method.DeclaringType == typeof(Interpreter) && method.Name == "Run";
136 #if FEATURE_STACK_TRACE
138 /// A single interpreted frame might be represented by multiple subsequent Interpreter.Run CLR frames.
139 /// This method filters out the duplicate CLR frames.
141 public static IEnumerable<StackFrame> GroupStackFrames(IEnumerable<StackFrame> stackTrace) {
142 bool inInterpretedFrame = false;
143 foreach (StackFrame frame in stackTrace) {
144 if (InterpretedFrame.IsInterpretedFrame(frame.GetMethod())) {
145 if (inInterpretedFrame) {
148 inInterpretedFrame = true;
150 inInterpretedFrame = false;
157 public IEnumerable<InterpretedFrameInfo> GetStackTraceDebugInfo() {
160 yield return new InterpretedFrameInfo(frame.Name, frame.GetDebugInfo(frame.InstructionIndex));
161 frame = frame.Parent;
162 } while (frame != null);
165 internal void SaveTraceToException(Exception exception) {
166 if (exception.GetData(typeof(InterpretedFrameInfo)) == null) {
167 exception.SetData(typeof(InterpretedFrameInfo), new List<InterpretedFrameInfo>(GetStackTraceDebugInfo()).ToArray());
171 public static InterpretedFrameInfo[] GetExceptionStackTrace(Exception exception) {
172 return exception.GetData(typeof(InterpretedFrameInfo)) as InterpretedFrameInfo[];
176 internal string[] Trace {
178 var trace = new List<string>();
181 trace.Add(frame.Name);
182 frame = frame.Parent;
183 } while (frame != null);
184 return trace.ToArray();
190 internal InterpretedFrameThreadLocal Enter() {
191 var currentFrame = InterpretedFrame.CurrentFrame;
192 _parent = currentFrame.Value;
193 currentFrame.Value = this;
197 internal void Leave(InterpretedFrameThreadLocal currentFrame) {
198 currentFrame.Value = _parent;
201 internal InterpretedFrameThreadLocal.StorageInfo Enter() {
202 var currentFrame = InterpretedFrame.CurrentFrame.GetStorageInfo();
203 _parent = currentFrame.Value;
204 currentFrame.Value = this;
208 internal void Leave(InterpretedFrameThreadLocal.StorageInfo currentFrame) {
209 currentFrame.Value = _parent;
214 #region Continuations
216 public void RemoveContinuation() {
217 _continuationIndex--;
220 public void PushContinuation(int continuation) {
221 _continuations[_continuationIndex++] = continuation;
224 public int YieldToCurrentContinuation() {
225 var target = Interpreter._labels[_continuations[_continuationIndex - 1]];
226 SetStackDepth(target.StackDepth);
227 return target.Index - InstructionIndex;
230 public int YieldToPendingContinuation() {
231 Debug.Assert(_pendingContinuation >= 0);
233 RuntimeLabel pendingTarget = Interpreter._labels[_pendingContinuation];
235 // the current continuation might have higher priority (continuationIndex is the depth of the current continuation):
236 if (pendingTarget.ContinuationStackDepth < _continuationIndex) {
237 RuntimeLabel currentTarget = Interpreter._labels[_continuations[_continuationIndex - 1]];
238 SetStackDepth(currentTarget.StackDepth);
239 return currentTarget.Index - InstructionIndex;
242 SetStackDepth(pendingTarget.StackDepth);
243 if (_pendingValue != Interpreter.NoValue) {
244 Data[StackIndex - 1] = _pendingValue;
246 return pendingTarget.Index - InstructionIndex;
249 internal void PushPendingContinuation() {
250 Push(_pendingContinuation);
253 _pendingContinuation = -1;
257 internal void PopPendingContinuation() {
258 _pendingValue = Pop();
259 _pendingContinuation = (int)Pop();
262 private static MethodInfo _Goto;
263 private static MethodInfo _VoidGoto;
265 internal static MethodInfo GotoMethod {
266 get { return _Goto ?? (_Goto = typeof(InterpretedFrame).GetMethod("Goto")); }
269 internal static MethodInfo VoidGotoMethod {
270 get { return _VoidGoto ?? (_VoidGoto = typeof(InterpretedFrame).GetMethod("VoidGoto")); }
273 public int VoidGoto(int labelIndex) {
274 return Goto(labelIndex, Interpreter.NoValue);
277 public int Goto(int labelIndex, object value) {
278 // TODO: we know this at compile time (except for compiled loop):
279 RuntimeLabel target = Interpreter._labels[labelIndex];
280 if (_continuationIndex == target.ContinuationStackDepth) {
281 SetStackDepth(target.StackDepth);
282 if (value != Interpreter.NoValue) {
283 Data[StackIndex - 1] = value;
285 return target.Index - InstructionIndex;
288 // if we are in the middle of executing jump we forget the previous target and replace it by a new one:
289 _pendingContinuation = labelIndex;
290 _pendingValue = value;
291 return YieldToCurrentContinuation();