forked from IronLanguages/ironpython3
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathYieldExpression.cs
More file actions
106 lines (92 loc) · 4.63 KB
/
Copy pathYieldExpression.cs
File metadata and controls
106 lines (92 loc) · 4.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.
#nullable enable
using MSAst = System.Linq.Expressions;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Diagnostics;
using Microsoft.Scripting;
using Microsoft.Scripting.Runtime;
using AstUtils = Microsoft.Scripting.Ast.Utils;
namespace IronPython.Compiler.Ast {
using Ast = MSAst.Expression;
// New in Pep342 for Python 2.5. Yield is an expression with a return value.
// x = yield z
// The return value (x) is provided by calling Generator.Send()
public class YieldExpression : Expression {
#if FEATURE_NET_ASYNC
private static readonly System.Reflection.MethodInfo s_captureMethod
= typeof(ExceptionDispatchInfo).GetMethod(nameof(ExceptionDispatchInfo.Capture))!;
private static readonly System.Reflection.MethodInfo s_throwMethod
= typeof(ExceptionDispatchInfo).GetMethod(nameof(ExceptionDispatchInfo.Throw), Type.EmptyTypes)!;
#endif
public YieldExpression(Expression? expression) {
Expression = expression;
}
public Expression? Expression { get; }
// Generate AST statement to call $gen.CheckThrowable() on the Python Generator.
// This needs to be injected at any yield suspension points, mainly:
// - at the start of the generator body
// - after each yield statement.
internal static MSAst.Expression CreateCheckThrowExpression(SourceSpan span) {
MSAst.Expression instance = GeneratorRewriter._generatorParam;
Debug.Assert(instance.Type == typeof(IronPython.Runtime.PythonGenerator));
MSAst.Expression s2 = LightExceptions.CheckAndThrow(
Expression.Call(
AstMethods.GeneratorCheckThrowableAndReturnSendValue,
instance
)
);
return s2;
}
public override MSAst.Expression Reduce() {
MSAst.Expression yieldValue = Expression == null ? AstUtils.Constant(null) : AstUtils.Convert(Expression, typeof(object));
#if FEATURE_NET_ASYNC
// An async generator (`async def` with `yield`) is lowered via AsyncEnumerableExpression
// and has no backing PythonGenerator, so there is no `$generator` to call CheckThrowable() on.
// Instead the resume reads two per-generator cells that PythonAsyncGenerator writes before advancing:
// AsyncThrowSlot — if set (athrow/aclose), rethrow it here (preserving stack);
// cleared first so a body that catches it and yields again doesn't re-throw on the next resume.
// AsyncSendSlot — the value of the yield expression: the asend(v) value, or None.
if (Parent is FunctionDefinition { IsAsync: true } fd) {
MSAst.ParameterExpression sendSlot = fd.AsyncSendSlot;
MSAst.ParameterExpression throwSlot = fd.AsyncThrowSlot;
MSAst.ParameterExpression pending = Ast.Variable(typeof(Exception), "$athrow");
return Ast.Block(
typeof(object),
[pending],
AstUtils.YieldReturn(GeneratorLabel, yieldValue),
Ast.Assign(pending, Ast.Field(throwSlot, nameof(StrongBox<Exception>.Value))),
Ast.Assign(Ast.Field(throwSlot, nameof(StrongBox<Exception>.Value)), Ast.Constant(null, typeof(Exception))),
Ast.IfThen(
Ast.ReferenceNotEqual(pending, Ast.Constant(null, typeof(Exception))),
Ast.Call(Ast.Call(s_captureMethod, pending), s_throwMethod)),
Ast.Field(sendSlot, nameof(StrongBox<object>.Value))
);
}
#endif
// (yield z) becomes:
// .comma (1) {
// .void ( .yield_statement (_expression) ),
// $gen.CheckThrowable() // <-- has return result from send
// }
return Ast.Block(
AstUtils.YieldReturn(GeneratorLabel, yieldValue),
CreateCheckThrowExpression(Span) // emits ($gen.CheckThrowable())
);
}
public override void Walk(PythonWalker walker) {
if (walker.Walk(this)) {
Expression?.Walk(walker);
}
walker.PostWalk(this);
}
public override string NodeName {
get {
return "yield expression";
}
}
}
}