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 * ***************************************************************************/
16 using System
.Diagnostics
;
17 using System
.Dynamic
.Utils
;
20 namespace Microsoft
.Scripting
.Ast
.Compiler
{
22 namespace System
.Linq
.Expressions
.Compiler
{
25 // The part of the LambdaCompiler dealing with low level control flow
26 // break, contiue, return, exceptions, etc
27 partial class LambdaCompiler
{
29 private LabelInfo
EnsureLabel(LabelTarget node
) {
31 if (!_labelInfo
.TryGetValue(node
, out result
)) {
32 _labelInfo
.Add(node
, result
= new LabelInfo(_ilg
, node
, false));
37 private LabelInfo
ReferenceLabel(LabelTarget node
) {
38 LabelInfo result
= EnsureLabel(node
);
39 result
.Reference(_labelBlock
);
43 private LabelInfo
DefineLabel(LabelTarget node
) {
45 return new LabelInfo(_ilg
, null, false);
47 LabelInfo result
= EnsureLabel(node
);
48 result
.Define(_labelBlock
);
52 private void PushLabelBlock(LabelScopeKind type
) {
53 _labelBlock
= new LabelScopeInfo(_labelBlock
, type
);
56 [System
.Diagnostics
.CodeAnalysis
.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId
= "kind")]
57 private void PopLabelBlock(LabelScopeKind kind
) {
58 Debug
.Assert(_labelBlock
!= null && _labelBlock
.Kind
== kind
);
59 _labelBlock
= _labelBlock
.Parent
;
62 private void EmitLabelExpression(Expression expr
, CompilationFlags flags
) {
63 var node
= (LabelExpression
)expr
;
64 Debug
.Assert(node
.Target
!= null);
66 // If we're an immediate child of a block, our label will already
67 // be defined. If not, we need to define our own block so this
68 // label isn't exposed except to its own child expression.
69 LabelInfo label
= null;
71 if (_labelBlock
.Kind
== LabelScopeKind
.Block
) {
72 _labelBlock
.TryGetLabelInfo(node
.Target
, out label
);
74 // We're in a block but didn't find our label, try switch
75 if (label
== null && _labelBlock
.Parent
.Kind
== LabelScopeKind
.Switch
) {
76 _labelBlock
.Parent
.TryGetLabelInfo(node
.Target
, out label
);
79 // if we're in a switch or block, we should've found the label
80 Debug
.Assert(label
!= null);
84 label
= DefineLabel(node
.Target
);
87 if (node
.DefaultValue
!= null) {
88 if (node
.Target
.Type
== typeof(void)) {
89 EmitExpressionAsVoid(node
.DefaultValue
, flags
);
91 flags
= UpdateEmitExpressionStartFlag(flags
, CompilationFlags
.EmitExpressionStart
);
92 EmitExpression(node
.DefaultValue
, flags
);
99 private void EmitGotoExpression(Expression expr
, CompilationFlags flags
) {
100 var node
= (GotoExpression
)expr
;
101 var labelInfo
= ReferenceLabel(node
.Target
);
103 var tailCall
= flags
& CompilationFlags
.EmitAsTailCallMask
;
104 if (tailCall
!= CompilationFlags
.EmitAsNoTail
) {
105 // Since tail call flags are not passed into EmitTryExpression, CanReturn
106 // means the goto will be emitted as Ret. Therefore we can emit the goto's
107 // default value with tail call. This can be improved by detecting if the
108 // target label is equivalent to the return label.
109 tailCall
= labelInfo
.CanReturn
? CompilationFlags
.EmitAsTail
: CompilationFlags
.EmitAsNoTail
;
110 flags
= UpdateEmitAsTailCallFlag(flags
, tailCall
);
113 if (node
.Value
!= null) {
114 if (node
.Target
.Type
== typeof(void)) {
115 EmitExpressionAsVoid(node
.Value
, flags
);
117 flags
= UpdateEmitExpressionStartFlag(flags
, CompilationFlags
.EmitExpressionStart
);
118 EmitExpression(node
.Value
, flags
);
122 labelInfo
.EmitJump();
124 EmitUnreachable(node
, flags
);
127 // We need to push default(T), unless we're emitting ourselves as
128 // void. Even though the code is unreachable, we still have to
129 // generate correct IL. We can get rid of this once we have better
130 // reachability analysis.
131 private void EmitUnreachable(Expression node
, CompilationFlags flags
) {
132 if (node
.Type
!= typeof(void) && (flags
& CompilationFlags
.EmitAsVoidType
) == 0) {
133 _ilg
.EmitDefault(node
.Type
);
137 private bool TryPushLabelBlock(Expression node
) {
138 // Anything that is "statement-like" -- e.g. has no associated
139 // stack state can be jumped into, with the exception of try-blocks
140 // We indicate this by a "Block"
142 // Otherwise, we push an "Expression" to indicate that it can't be
144 switch (node
.NodeType
) {
146 if (_labelBlock
.Kind
!= LabelScopeKind
.Expression
) {
147 PushLabelBlock(LabelScopeKind
.Expression
);
151 case ExpressionType
.Label
:
152 // LabelExpression is a bit special, if it's directly in a
153 // block it becomes associate with the block's scope. Same
154 // thing if it's in a switch case body.
155 if (_labelBlock
.Kind
== LabelScopeKind
.Block
) {
156 var label
= ((LabelExpression
)node
).Target
;
157 if (_labelBlock
.ContainsTarget(label
)) {
160 if (_labelBlock
.Parent
.Kind
== LabelScopeKind
.Switch
&&
161 _labelBlock
.Parent
.ContainsTarget(label
)) {
165 PushLabelBlock(LabelScopeKind
.Statement
);
167 case ExpressionType
.Block
:
168 if (node
is SpilledExpressionBlock
) {
169 // treat it as an expression
173 PushLabelBlock(LabelScopeKind
.Block
);
174 // Labels defined immediately in the block are valid for
176 if (_labelBlock
.Parent
.Kind
!= LabelScopeKind
.Switch
) {
177 DefineBlockLabels(node
);
180 case ExpressionType
.Switch
:
181 PushLabelBlock(LabelScopeKind
.Switch
);
182 // Define labels inside of the switch cases so theyare in
183 // scope for the whole switch. This allows "goto case" and
184 // "goto default" to be considered as local jumps.
185 var @switch = (SwitchExpression
)node
;
186 foreach (SwitchCase c
in @switch.Cases
) {
187 DefineBlockLabels(c
.Body
);
189 DefineBlockLabels(@switch.DefaultBody
);
192 // Remove this when Convert(Void) goes away.
193 case ExpressionType
.Convert
:
194 if (node
.Type
!= typeof(void)) {
195 // treat it as an expression
198 PushLabelBlock(LabelScopeKind
.Statement
);
201 case ExpressionType
.Conditional
:
202 case ExpressionType
.Loop
:
203 case ExpressionType
.Goto
:
204 PushLabelBlock(LabelScopeKind
.Statement
);
209 private void DefineBlockLabels(Expression node
) {
210 var block
= node
as BlockExpression
;
211 if (block
== null || block
is SpilledExpressionBlock
) {
214 for (int i
= 0, n
= block
.ExpressionCount
; i
< n
; i
++) {
215 Expression e
= block
.GetExpression(i
);
217 var label
= e
as LabelExpression
;
219 DefineLabel(label
.Target
);
224 // See if this lambda has a return label
225 // If so, we'll create it now and mark it as allowing the "ret" opcode
226 // This allows us to generate better IL
227 private void AddReturnLabel(LambdaExpression lambda
) {
228 var expression
= lambda
.Body
;
231 switch (expression
.NodeType
) {
233 // Didn't find return label
235 case ExpressionType
.Label
:
236 // Found the label. We can directly return from this place
237 // only if the label type is reference assignable to the lambda return type.
238 var label
= ((LabelExpression
)expression
).Target
;
239 _labelInfo
.Add(label
, new LabelInfo(_ilg
, label
, TypeUtils
.AreReferenceAssignable(lambda
.ReturnType
, label
.Type
)));
241 case ExpressionType
.Block
:
242 // Look in the last significant expression of a block
243 var body
= (BlockExpression
)expression
;
244 // omit empty and debuginfo at the end of the block since they
245 // are not going to emit any IL
246 for (int i
= body
.ExpressionCount
- 1; i
>= 0; i
--) {
247 expression
= body
.GetExpression(i
);
248 if (Significant(expression
)) {