/DLR_Main/Languages/IronPython/IronPythonTest/LightExceptionTests.cs
C# | 318 lines | 262 code | 41 blank | 15 comment | 22 complexity | ae8d9a49a9995a02f2d592b88b39e404 MD5 | raw file
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 !CLR2
17using System.Linq.Expressions;
18using Microsoft.Scripting.Ast;
19#else
20using Microsoft.Scripting.Ast;
21#endif
22
23using System;
24using System.Collections.Generic;
25using System.Dynamic;
26using System.Runtime.CompilerServices;
27
28using Microsoft.Scripting.Generation;
29using Microsoft.Scripting.Utils;
30
31using IronPython.Runtime;
32using System.Reflection;
33using Microsoft.Scripting.Runtime;
34using Microsoft.Scripting.Actions;
35
36namespace IronPythonTest {
37 class LightExceptionTests {
38 private Func<LightExceptionTests, IEnumerable<Expression>>[] StatementBuilders = new[] {
39 new Func<LightExceptionTests, IEnumerable<Expression>>(TryCatchBuilder),
40 new Func<LightExceptionTests, IEnumerable<Expression>>(TryFinallyBuilder),
41 new Func<LightExceptionTests, IEnumerable<Expression>>(ThrowBuilder),
42 new Func<LightExceptionTests, IEnumerable<Expression>>(CallBuilder),
43 new Func<LightExceptionTests, IEnumerable<Expression>>(ReturnBuilder),
44 };
45 private static ParameterExpression _input = Expression.Parameter(typeof(List<string>), "log");
46 private static MethodInfo _addLog = typeof(List<string>).GetMethod("Add");
47 private static LabelTarget _ret = Expression.Label(typeof(object), "return");
48 private static Expression[] _catchExtras = new[] { (Expression)Expression.Default(typeof(void)), Expression.Rethrow() };
49
50 public static void RunTests() {
51 var param = Expression.Parameter(typeof(Exception), "foo");
52 var lambdax = Expression.Lambda<Func<object>>(
53 Expression.Block(
54 LightExceptions.RewriteExternal(
55 Expression.TryCatch(
56 Expression.Default(typeof(object)),
57 Expression.Catch(param, Expression.Default(typeof(object)))
58 )
59 ),
60 LightExceptions.RewriteExternal(
61 Expression.TryCatch(
62 Expression.Default(typeof(object)),
63 Expression.Catch(param, Expression.Default(typeof(object)))
64 )
65 )
66 )
67 );
68
69 lambdax.Compile()();
70 CompilerHelpers.LightCompile(lambdax)();
71
72 var builder = new LightExceptionTests();
73 List<string> record = new List<string>();
74 List<string> rewriteRecord = new List<string>();
75 int testCount = 0;
76 try {
77 foreach (var lambda in builder.MakeLambda()) {
78 // run each test in normal and lightweight exception modes, make sure they have the same result
79 try {
80
81 object res = lambda.Compile()(record);
82 if (res != null) {
83 record.Add(res.ToString());
84 }
85 } catch (Exception e) {
86 record.Add(String.Format("EXCEPTION {0}", e.GetType()));
87 }
88
89 try {
90 object res = ((Expression<Func<List<string>, object>>)LightExceptions.Rewrite(lambda)).Compile()(rewriteRecord);
91 Exception e = LightExceptions.GetLightException(res);
92 if (e != null) {
93 rewriteRecord.Add(String.Format("EXCEPTION {0}", e.GetType()));
94 } else if (res != null) {
95 rewriteRecord.Add(res.ToString());
96 }
97 } catch (Exception e) {
98 rewriteRecord.Add(String.Format("EXCEPTION {0}", e.GetType()));
99 }
100
101 if (record.Count != rewriteRecord.Count) {
102 PrintLambda(lambda, record, rewriteRecord);
103 throw new Exception("Records differ in length");
104 }
105 for (int i = 0; i < record.Count; i++) {
106 if (record[i] != rewriteRecord[i]) {
107 PrintLambda(lambda, record, rewriteRecord);
108 throw new Exception("Records differ");
109 }
110 }
111
112 record.Clear();
113 rewriteRecord.Clear();
114 testCount++;
115 }
116 } finally {
117 Console.Write("Ran {0} tests", testCount);
118 }
119 }
120
121 private static void PrintLambda(Expression<Func<List<string>, object>> lambda, List<string> record, List<string> rewriteRecord) {
122 for (int i = 0; i < Math.Min(record.Count, rewriteRecord.Count); i++) {
123 Console.WriteLine(record[i]);
124 Console.WriteLine(rewriteRecord[i]);
125 Console.WriteLine();
126 }
127#if CLR2
128 Console.WriteLine("Before: " + Environment.NewLine + lambda.DebugView);
129 Console.WriteLine("After: " + Environment.NewLine + LightExceptions.Rewrite(lambda).DebugView);
130#endif
131 }
132
133 private int _depth = 0;
134
135 private IEnumerable<Expression<Func<List<string>, object>>> MakeLambda() {
136 foreach (var expr in GetStatements()) {
137 var res = expr;
138 if (res.Type != typeof(object)) {
139 res = Expression.Block(expr, Expression.Default(typeof(object)));
140 }
141 yield return Expression.Lambda<Func<List<string>, object>>(
142 Expression.Label(_ret, res),
143 _input
144 );
145 }
146 }
147
148 public IEnumerable<Expression> GetStatements() {
149 if (_depth < 2) {
150 foreach (var func in StatementBuilders) {
151 foreach (var expr in func(this)) { yield return expr; }
152 }
153 }
154 }
155
156 private static Expression AddLogging(Expression expr, string log) {
157 return Expression.Block(
158 Expression.Call(
159 _input,
160 _addLog,
161 Expression.Constant(log)
162 ),
163 expr
164 );
165 }
166
167 private static IEnumerable<Expression> ReturnBuilder(LightExceptionTests self) {
168 yield return Expression.Return(_ret, Expression.Default(typeof(object)));
169 }
170
171 private static IEnumerable<Expression> TryCatchBuilder(LightExceptionTests self) {
172 using (new DepthHolder(self)) {
173 foreach (var body in self.GetStatements()) {
174 foreach (var handler in self.GetStatements()) {
175 for (int i = 0; i < _catchExtras.Length; i++) {
176 var extra = _catchExtras[i];
177
178 yield return AddLogging(
179 Expression.TryCatch(
180 Expression.Block(typeof(void), body),
181 Expression.Catch(
182 typeof(Exception),
183 AddLogging(Expression.Block(typeof(void), handler, extra), "catch " + i)
184 )
185 ),
186 "try"
187 );
188
189 yield return AddLogging(
190 Expression.TryCatch(
191 Expression.Block(typeof(void), body),
192 Expression.Catch(
193 typeof(InvalidOperationException),
194 AddLogging(Expression.Block(typeof(void), handler, extra), "invalidEx catch 1 " + i)
195 )
196 ),
197 "try"
198 );
199
200 yield return AddLogging(
201 Expression.TryCatch(
202 Expression.Block(typeof(void), body),
203 Expression.Catch(
204 typeof(InvalidOperationException),
205 AddLogging(Expression.Block(typeof(void), handler, extra), "invalidEx catch 2 " + i)
206 ),
207 Expression.Catch(
208 typeof(InvalidOperationException),
209 AddLogging(Expression.Block(typeof(void), handler, extra), "catch " + i)
210 )
211 ),
212 "try"
213 );
214 }
215
216 }
217 }
218 }
219 }
220
221 private static IEnumerable<Expression> TryFinallyBuilder(LightExceptionTests self) {
222 using (new DepthHolder(self)) {
223 foreach (var body in self.GetStatements()) {
224 foreach (var handler in self.GetStatements()) {
225 yield return Expression.TryFinally(AddLogging(body, "try finally"), AddLogging(handler, "finally"));
226 }
227 }
228 }
229 }
230
231 private static IEnumerable<Expression> ThrowBuilder(LightExceptionTests self) {
232 yield return AddLogging(Expression.Throw(Expression.New(typeof(Exception))), "throw ex");
233 yield return AddLogging(Expression.Throw(Expression.New(typeof(InvalidOperationException))), "throw invalidEx");
234 }
235
236 private static IEnumerable<Expression> CallBuilder(LightExceptionTests self) {
237 yield return AddLogging(LightExceptions.CheckAndThrow(Expression.Call(typeof(LightExceptionTests).GetMethod("SomeCall"))), "call");
238 yield return AddLogging(Expression.Call(typeof(LightExceptionTests).GetMethod("ThrowingCall")), "call throw");
239 yield return AddLogging(Expression.Call(typeof(LightExceptionTests).GetMethod("ThrowingCallInvalidOp")), "call throw invalidop");
240 yield return AddLogging(LightExceptions.CheckAndThrow(Expression.Call(typeof(LightExceptionTests).GetMethod("LightThrowingCall"))), "call throw");
241 yield return AddLogging(LightExceptions.CheckAndThrow(Expression.Call(typeof(LightExceptionTests).GetMethod("LightThrowingCallInvalidOp"))), "call throw invalidop");
242 yield return AddLogging(Expression.Dynamic(new LightExBinder("test", false), typeof(object), Expression.Constant(42)), "dynamic throw");
243 yield return AddLogging(
244 Expression.Dynamic(new LightExBinder("test", false), typeof(object),
245 Expression.Dynamic(new LightExBinder("foo", false), typeof(object), Expression.Constant(42))), "dynamic nothrow");
246 }
247
248 public static object SomeCall() {
249 return null;
250 }
251
252 public static object ThrowingCall() {
253 throw new Exception();
254 }
255
256 public static object ThrowingCallInvalidOp() {
257 throw new InvalidOperationException();
258 }
259
260 public static object LightThrowingCall() {
261 return LightExceptions.Throw(new Exception());
262 }
263
264 public static object LightThrowingCallInvalidOp() {
265 return LightExceptions.Throw(new InvalidOperationException());
266 }
267
268 struct DepthHolder : IDisposable {
269 private LightExceptionTests Builder;
270 public DepthHolder(LightExceptionTests builder) {
271 Builder = builder;
272 builder._depth++;
273 }
274
275 public void Dispose() {
276 Builder._depth--;
277 }
278 }
279
280 class LightExBinder : GetMemberBinder, ILightExceptionBinder {
281 private bool _supportsLightEx;
282 public LightExBinder(string name, bool supportsLightEx)
283 : base(name, false) {
284 _supportsLightEx = supportsLightEx;
285 }
286
287 public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion) {
288 var ex = Expression.New(typeof(MissingMemberException));
289 Expression body;
290 if (Name == "foo") {
291 body = target.Expression;
292 } else if (_supportsLightEx) {
293 body = LightExceptions.Throw(ex, typeof(object));
294 } else {
295 body = Expression.Throw(ex, typeof(object));
296 }
297
298 return new DynamicMetaObject(
299 body,
300 BindingRestrictions.GetTypeRestriction(target.Expression, target.Value.GetType())
301 );
302 }
303
304 #region ILightExceptionBinder Members
305
306 public bool SupportsLightThrow {
307 get { return _supportsLightEx; }
308 }
309
310 public CallSiteBinder GetLightExceptionBinder() {
311 return new LightExBinder(Name, true);
312 }
313
314 #endregion
315 }
316
317 }
318}