Merge pull request #900 from Blewzman/FixAggregateExceptionGetBaseException
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Dynamic / Interpreter / InterpretedFrame.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.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;
30
31 namespace Microsoft.Scripting.Interpreter {
32 #if CLR45
33     using InterpretedFrameThreadLocal = ThreadLocal<InterpretedFrame>;
34 #else
35     using InterpretedFrameThreadLocal = Microsoft.Scripting.Utils.ThreadLocal<InterpretedFrame>;
36 #endif
37
38     public sealed class InterpretedFrame {
39         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
40         public static readonly InterpretedFrameThreadLocal CurrentFrame = new InterpretedFrameThreadLocal();
41
42         internal readonly Interpreter Interpreter;
43         internal InterpretedFrame _parent;
44
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;
50
51         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")]
52         public readonly object[] Data;
53
54         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")]
55         public readonly StrongBox<object>[] Closure;
56
57         public int StackIndex;
58         public int InstructionIndex;
59
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;
63
64         internal InterpretedFrame(Interpreter interpreter, StrongBox<object>[] closure) {
65             Interpreter = interpreter;
66             StackIndex = interpreter.LocalCount;
67             Data = new object[StackIndex + interpreter.Instructions.MaxStackDepth];
68
69             int c = interpreter.Instructions.MaxContinuationDepth;
70             if (c > 0) {
71                 _continuations = new int[c];
72             }
73
74             Closure = closure;
75         }
76
77         public DebugInfo GetDebugInfo(int instructionIndex) {
78             return DebugInfo.GetMatchingDebugInfo(Interpreter._debugInfos, instructionIndex);
79         }
80
81         public string Name {
82             get { return Interpreter._name; }
83         }
84
85         #region Data Stack Operations
86
87         public void Push(object value) {
88             Data[StackIndex++] = value;
89         }
90
91         public void Push(bool value) {
92             Data[StackIndex++] = value ? ScriptingRuntimeHelpers.True : ScriptingRuntimeHelpers.False;
93         }
94
95         public void Push(int value) {
96             Data[StackIndex++] = ScriptingRuntimeHelpers.Int32ToObject(value);
97         }
98
99         public object Pop() {
100             return Data[--StackIndex];
101         }
102
103         public object Pop(int n) {
104             int si = StackIndex - n;
105             StackIndex = si;
106             return Data[si];
107         }
108
109         internal void SetStackDepth(int depth) {
110             StackIndex = Interpreter.LocalCount + depth;
111         }
112
113         public object Peek() {
114             return Data[StackIndex - 1];
115         }
116
117         public void Dup() {
118             int i = StackIndex;
119             Data[i] = Data[i - 1];
120             StackIndex = i + 1;
121         }
122
123         #endregion
124
125         #region Stack Trace
126
127         public InterpretedFrame Parent {
128             get { return _parent; }
129         }
130
131         public static bool IsInterpretedFrame(MethodBase method) {
132             ContractUtils.RequiresNotNull(method, "method");
133             return method.DeclaringType == typeof(Interpreter) && method.Name == "Run";
134         }
135
136 #if FEATURE_STACK_TRACE
137         /// <summary>
138         /// A single interpreted frame might be represented by multiple subsequent Interpreter.Run CLR frames.
139         /// This method filters out the duplicate CLR frames.
140         /// </summary>
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) {
146                         continue;
147                     }
148                     inInterpretedFrame = true;
149                 } else {
150                     inInterpretedFrame = false;
151                 }
152                 yield return frame;
153             }
154         }
155 #endif
156
157         public IEnumerable<InterpretedFrameInfo> GetStackTraceDebugInfo() {
158             var frame = this;
159             do {
160                 yield return new InterpretedFrameInfo(frame.Name, frame.GetDebugInfo(frame.InstructionIndex));
161                 frame = frame.Parent;
162             } while (frame != null);
163         }
164
165         internal void SaveTraceToException(Exception exception) {
166             if (exception.GetData(typeof(InterpretedFrameInfo)) == null) {
167                 exception.SetData(typeof(InterpretedFrameInfo), new List<InterpretedFrameInfo>(GetStackTraceDebugInfo()).ToArray());
168             }
169         }
170
171         public static InterpretedFrameInfo[] GetExceptionStackTrace(Exception exception) {
172             return exception.GetData(typeof(InterpretedFrameInfo)) as InterpretedFrameInfo[];
173         }
174
175 #if DEBUG
176         internal string[] Trace {
177             get {
178                 var trace = new List<string>();
179                 var frame = this;
180                 do {
181                     trace.Add(frame.Name);
182                     frame = frame.Parent;
183                 } while (frame != null);
184                 return trace.ToArray();
185             }
186         }
187 #endif
188
189 #if CLR45
190         internal InterpretedFrameThreadLocal Enter() {
191             var currentFrame = InterpretedFrame.CurrentFrame;
192             _parent = currentFrame.Value;
193             currentFrame.Value = this;
194             return currentFrame;
195         }
196
197         internal void Leave(InterpretedFrameThreadLocal currentFrame) {
198             currentFrame.Value = _parent;
199         }
200 #else
201         internal InterpretedFrameThreadLocal.StorageInfo Enter() {
202             var currentFrame = InterpretedFrame.CurrentFrame.GetStorageInfo();
203             _parent = currentFrame.Value;
204             currentFrame.Value = this;
205             return currentFrame;
206         }
207
208         internal void Leave(InterpretedFrameThreadLocal.StorageInfo currentFrame) {
209             currentFrame.Value = _parent;
210         }
211 #endif
212         #endregion
213
214         #region Continuations
215
216         public void RemoveContinuation() {
217             _continuationIndex--;
218         }
219
220         public void PushContinuation(int continuation) {
221             _continuations[_continuationIndex++] = continuation;
222         }
223
224         public int YieldToCurrentContinuation() {
225             var target = Interpreter._labels[_continuations[_continuationIndex - 1]];
226             SetStackDepth(target.StackDepth);
227             return target.Index - InstructionIndex;
228         }
229
230         public int YieldToPendingContinuation() {
231             Debug.Assert(_pendingContinuation >= 0);
232
233             RuntimeLabel pendingTarget = Interpreter._labels[_pendingContinuation];
234
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;
240             }
241
242             SetStackDepth(pendingTarget.StackDepth);
243             if (_pendingValue != Interpreter.NoValue) {
244                 Data[StackIndex - 1] = _pendingValue;
245             }
246             return pendingTarget.Index - InstructionIndex;
247         }
248
249         internal void PushPendingContinuation() {
250             Push(_pendingContinuation);
251             Push(_pendingValue);
252 #if DEBUG
253             _pendingContinuation = -1;
254 #endif
255         }
256
257         internal void PopPendingContinuation() {
258             _pendingValue = Pop();
259             _pendingContinuation = (int)Pop();
260         }
261
262         private static MethodInfo _Goto;
263         private static MethodInfo _VoidGoto;
264
265         internal static MethodInfo GotoMethod {
266             get { return _Goto ?? (_Goto = typeof(InterpretedFrame).GetMethod("Goto")); }
267         }
268
269         internal static MethodInfo VoidGotoMethod {
270             get { return _VoidGoto ?? (_VoidGoto = typeof(InterpretedFrame).GetMethod("VoidGoto")); }
271         }
272
273         public int VoidGoto(int labelIndex) {
274             return Goto(labelIndex, Interpreter.NoValue);
275         }
276
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;
284                 }
285                 return target.Index - InstructionIndex;
286             }
287
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();
292         }
293
294         #endregion
295
296     }
297 }