Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(25)

Side by Side Diff: pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart

Issue 2246623002: Delete CPS IR (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 library dart2js.ir_builder;
6
7 import '../closure.dart' as closure;
8 import '../common.dart';
9 import '../common/names.dart' show Selectors;
10 import '../compile_time_constants.dart' show BackendConstantEnvironment;
11 import '../constants/constant_system.dart';
12 import '../constants/values.dart' show ConstantValue;
13 import '../dart_types.dart';
14 import '../elements/elements.dart';
15 import '../io/source_information.dart';
16 import '../js/js.dart' as js
17 show
18 js,
19 objectLiteral,
20 Expression,
21 LiteralStatement,
22 Template,
23 InterpolatedExpression,
24 isIdentityTemplate;
25 import '../native/native.dart' show NativeBehavior;
26 import '../tree/tree.dart' as ast;
27 import '../types/types.dart' show TypeMask;
28 import '../universe/call_structure.dart' show CallStructure;
29 import '../universe/selector.dart' show Selector, SelectorKind;
30 import 'cps_ir_builder_task.dart' show GlobalProgramInformation;
31 import 'cps_ir_nodes.dart' as ir;
32
33 /// A mapping from variable elements to their compile-time values.
34 ///
35 /// Map elements denoted by parameters and local variables to the
36 /// [ir.Primitive] that is their value. Parameters and locals are
37 /// assigned indexes which can be used to refer to them.
38 class Environment {
39 /// A map from locals to their environment index.
40 final Map<Local, int> variable2index;
41
42 /// A reverse map from environment indexes to the variable.
43 final List<Local> index2variable;
44
45 /// A map from environment indexes to their value.
46 final List<ir.Primitive> index2value;
47
48 Environment.empty()
49 : variable2index = <Local, int>{},
50 index2variable = <Local>[],
51 index2value = <ir.Primitive>[];
52
53 /// Construct an environment that is a copy of another one.
54 ///
55 /// The mapping from elements to indexes is shared, not copied.
56 Environment.from(Environment other)
57 : variable2index = other.variable2index,
58 index2variable = new List<Local>.from(other.index2variable),
59 index2value = new List<ir.Primitive>.from(other.index2value);
60
61 /// Construct an environment that is shaped like another one but with a
62 /// fresh parameter for each variable.
63 ///
64 /// The mapping from elements to indexes is shared, not copied.
65 Environment.fresh(Environment other)
66 : variable2index = other.variable2index,
67 index2variable = new List<Local>.from(other.index2variable),
68 index2value = other.index2variable.map((Local local) {
69 return new ir.Parameter(local);
70 }).toList();
71
72 get length => index2variable.length;
73
74 ir.Primitive operator [](int index) => index2value[index];
75
76 void extend(Local element, ir.Primitive value) {
77 // Assert that the name is not already in the environment. `null` is used
78 // as the name of anonymous variables.
79 assert(!variable2index.containsKey(element));
80 if (element != null) variable2index[element] = index2variable.length;
81 index2variable.add(element);
82 index2value.add(value);
83 }
84
85 /// Drop [count] values from the environment.
86 ///
87 /// Return the previous last value in the environment for convenience.
88 ir.Primitive discard(int count) {
89 assert(count > 0);
90 assert(count <= index2variable.length);
91 ir.Primitive value = index2value.last;
92 // The map from variables to their index are shared, so we cannot remove
93 // the mapping in `variable2index`.
94 index2variable.length -= count;
95 index2value.length -= count;
96 return value;
97 }
98
99 ir.Primitive lookup(Local element) {
100 assert(invariant(element, variable2index.containsKey(element),
101 message: "Unknown variable: $element."));
102 return index2value[variable2index[element]];
103 }
104
105 void update(Local element, ir.Primitive value) {
106 index2value[variable2index[element]] = value;
107 }
108
109 /// Verify that the variable2index and index2variable maps agree up to the
110 /// index [length] exclusive.
111 bool sameDomain(int length, Environment other) {
112 assert(this.length >= length);
113 assert(other.length >= length);
114 for (int i = 0; i < length; ++i) {
115 // An index maps to the same variable in both environments.
116 Local variable = index2variable[i];
117 if (variable != other.index2variable[i]) return false;
118
119 // A named variable maps to the same index in both environments.
120 if (variable != null) {
121 int index = variable2index[variable];
122 if (index == null || index != other.variable2index[variable]) {
123 return false;
124 }
125 }
126 }
127 return true;
128 }
129
130 bool contains(Local local) => variable2index.containsKey(local);
131 }
132
133 /// The abstract base class of objects that emit jumps to a continuation and
134 /// give a handle to the continuation and its environment.
135 abstract class JumpCollector {
136 final JumpTarget target;
137
138 ir.Continuation _continuation = null;
139 final Environment _continuationEnvironment;
140
141 final List<Iterable<LocalVariableElement>> _boxedTryVariables =
142 <Iterable<LocalVariableElement>>[];
143
144 /// Construct a collector for a given environment and optionally a target.
145 ///
146 /// The environment is the one in effect at the point where the jump's
147 /// continuation will be bound. Continuations can take an extra argument
148 /// (see [addJump]).
149 JumpCollector(
150 this._continuationEnvironment, this.target, bool hasExtraArgument) {
151 if (hasExtraArgument) _continuationEnvironment.extend(null, null);
152 }
153
154 /// Construct a collector for collecting only return jumps.
155 ///
156 /// There is no jump target, it is implicitly the exit from the function.
157 /// There is no environment at the destination.
158 JumpCollector.retrn(this._continuation)
159 : _continuationEnvironment = null,
160 target = null;
161
162 /// Construct a collector for collecting goto jumps.
163 ///
164 /// There is no continuation or environment at the destination.
165 JumpCollector.goto(this.target) : _continuationEnvironment = null;
166
167 /// True if the collector has not recorded any jumps to its continuation.
168 bool get isEmpty;
169
170 /// The continuation encapsulated by this collector.
171 ir.Continuation get continuation;
172
173 /// The compile-time environment to be used for translating code in the body
174 /// of the continuation.
175 Environment get environment;
176
177 /// Emit a jump to the continuation for a given [IrBuilder].
178 ///
179 /// Jumps can take a single extra argument. This is used to pass return
180 /// values to finally blocks for returns inside try/finally and to pass
181 /// values of expressions that have internal control flow to their join-point
182 /// continuations.
183 void addJump(IrBuilder builder,
184 [ir.Primitive value, SourceInformation sourceInformation]);
185
186 /// Add a set of variables that were boxed on entry to a try block.
187 ///
188 /// All jumps from a try block to targets outside have to unbox the
189 /// variables that were boxed on entry before invoking the target
190 /// continuation. Call this function before translating a try block and
191 /// call [leaveTry] after translating it.
192 void enterTry(Iterable<LocalVariableElement> boxedOnEntry) {
193 // The boxed variables are maintained as a stack to make leaving easy.
194 _boxedTryVariables.add(boxedOnEntry);
195 }
196
197 /// Remove the most recently added set of variables boxed on entry to a try
198 /// block.
199 ///
200 /// Call [enterTry] before translating a try block and call this function
201 /// after translating it.
202 void leaveTry() {
203 _boxedTryVariables.removeLast();
204 }
205
206 void _buildTryExit(IrBuilder builder) {
207 for (Iterable<LocalVariableElement> boxedOnEntry in _boxedTryVariables) {
208 for (LocalVariableElement variable in boxedOnEntry) {
209 assert(builder.isInMutableVariable(variable));
210 ir.Primitive value = builder.buildLocalGet(variable);
211 builder.environment.update(variable, value);
212 }
213 }
214 }
215
216 /// True if a jump inserted now will escape from a try block.
217 ///
218 /// Concretely, this is true when [enterTry] has been called without
219 /// its corresponding [leaveTry] call.
220 bool get isEscapingTry => _boxedTryVariables.isNotEmpty;
221 }
222
223 /// A class to collect 'forward' jumps.
224 ///
225 /// A forward jump to a continuation in the sense of the CPS translation is
226 /// a jump where the jump is emitted before any code in the body of the
227 /// continuation is translated. They have the property that continuation
228 /// parameters and the environment for the translation of the body can be
229 /// determined based on the invocations, before translating the body. A
230 /// [ForwardJumpCollector] can encapsulate a continuation where all the
231 /// jumps are forward ones.
232 ///
233 /// Examples of forward jumps in the translation are join points of
234 /// if-then-else and breaks from loops.
235 ///
236 /// The implementation strategy is that the collector collects invocation
237 /// sites and the environments at those sites. Then it constructs a
238 /// continuation 'on demand' after all the jumps are seen. It determines
239 /// continuation parameters, the environment for the translation of code in
240 /// the continuation body, and the arguments at the invocation site only
241 /// after all the jumps to the continuation are seen.
242 class ForwardJumpCollector extends JumpCollector {
243 final List<ir.InvokeContinuation> _invocations = <ir.InvokeContinuation>[];
244 final List<Environment> _invocationEnvironments = <Environment>[];
245
246 /// Construct a collector with a given base environment.
247 ///
248 /// The base environment is the one in scope at the site that the
249 /// continuation represented by this collector will be bound. The
250 /// environment is copied by the collector. Subsequent mutation of the
251 /// original environment will not affect the collector.
252 ForwardJumpCollector(Environment environment,
253 {JumpTarget target, bool hasExtraArgument: false})
254 : super(new Environment.from(environment), target, hasExtraArgument);
255
256 bool get isEmpty => _invocations.isEmpty;
257
258 ir.Continuation get continuation {
259 if (_continuation == null) _setContinuation();
260 return _continuation;
261 }
262
263 Environment get environment {
264 if (_continuation == null) _setContinuation();
265 return _continuationEnvironment;
266 }
267
268 void addJump(IrBuilder builder,
269 [ir.Primitive value, SourceInformation sourceInformation]) {
270 assert(_continuation == null);
271 _buildTryExit(builder);
272 ir.InvokeContinuation invoke =
273 new ir.InvokeContinuation.uninitialized(isEscapingTry: isEscapingTry);
274 builder.add(invoke);
275 _invocations.add(invoke);
276 // Truncate the environment at the invocation site so it only includes
277 // values that will be continuation arguments. If an extra value is passed
278 // it will already be included in the continuation environment, but it is
279 // not present in the invocation environment.
280 int delta = builder.environment.length - _continuationEnvironment.length;
281 if (value != null) ++delta;
282 if (delta > 0) builder.environment.discard(delta);
283 if (value != null) builder.environment.extend(null, value);
284 _invocationEnvironments.add(builder.environment);
285 builder._current = null;
286 // TODO(kmillikin): Can we set builder.environment to null to make it
287 // less likely to mutate it?
288 }
289
290 void _setContinuation() {
291 assert(_continuation == null);
292 // We have seen all invocations of this continuation, and recorded the
293 // environment in effect at each invocation site.
294
295 // Compute the union of the assigned variables reaching the continuation.
296 //
297 // There is a continuation parameter for each environment variable
298 // that has a different value (from the environment in scope at the
299 // continuation binding) on some path. `_environment` is initially a copy
300 // of the environment in scope at the continuation binding. Compute the
301 // continuation parameters and add them to `_environment` so it will become
302 // the one in scope for the continuation body.
303 List<ir.Parameter> parameters = <ir.Parameter>[];
304 if (_invocationEnvironments.isNotEmpty) {
305 int length = _continuationEnvironment.length;
306 for (int varIndex = 0; varIndex < length; ++varIndex) {
307 for (Environment invocationEnvironment in _invocationEnvironments) {
308 assert(invocationEnvironment.sameDomain(
309 length, _continuationEnvironment));
310 if (invocationEnvironment[varIndex] !=
311 _continuationEnvironment[varIndex]) {
312 ir.Parameter parameter = new ir.Parameter(
313 _continuationEnvironment.index2variable[varIndex]);
314 _continuationEnvironment.index2value[varIndex] = parameter;
315 parameters.add(parameter);
316 break;
317 }
318 }
319 }
320 }
321 _continuation = new ir.Continuation(parameters);
322
323 // Compute the intersection of the parameters with the environments at
324 // each continuation invocation. Initialize the invocations.
325 for (int jumpIndex = 0; jumpIndex < _invocations.length; ++jumpIndex) {
326 Environment invocationEnvironment = _invocationEnvironments[jumpIndex];
327 List<ir.Reference> arguments = <ir.Reference>[];
328 int varIndex = 0;
329 for (ir.Parameter parameter in parameters) {
330 varIndex =
331 _continuationEnvironment.index2value.indexOf(parameter, varIndex);
332 arguments.add(new ir.Reference(invocationEnvironment[varIndex]));
333 }
334 ir.InvokeContinuation invocation = _invocations[jumpIndex];
335 invocation.continuationRef = new ir.Reference(_continuation);
336 invocation.argumentRefs = arguments;
337 }
338 }
339 }
340
341 /// A class to collect 'backward' jumps.
342 ///
343 /// A backward jump to a continuation in the sense of the CPS translation is
344 /// a jump where some code in the body of the continuation is translated
345 /// before the jump is emitted. They have the property that the
346 /// continuation parameters and the environment for the translation of the
347 /// body must be determined before emitting all the invocations. A
348 /// [BackwardJumpCollector] can ecapsulate a continuation where some jumps
349 /// are backward ones.
350 ///
351 /// Examples of backward jumps in the translation are the recursive
352 /// invocations of loop continuations.
353 ///
354 /// The implementation strategy is that the collector inserts a continuation
355 /// parameter for each variable in scope at the entry to the continuation,
356 /// before emitting any jump to the continuation. When a jump is added, it
357 /// is given an argument for each continuation parameter.
358 class BackwardJumpCollector extends JumpCollector {
359 /// Construct a collector with a given base environment.
360 ///
361 /// The base environment is the one in scope at the site that the
362 /// continuation represented by this collector will be bound. The
363 /// translation of the continuation body will use an environment with the
364 /// same shape, but with fresh continuation parameters for each variable.
365 BackwardJumpCollector(Environment environment,
366 {JumpTarget target, bool hasExtraArgument: false})
367 : super(new Environment.fresh(environment), target, hasExtraArgument) {
368 List<ir.Parameter> parameters =
369 new List<ir.Parameter>.from(_continuationEnvironment.index2value);
370 _continuation = new ir.Continuation(parameters, isRecursive: true);
371 }
372
373 bool isEmpty = true;
374
375 ir.Continuation get continuation => _continuation;
376 Environment get environment => _continuationEnvironment;
377
378 void addJump(IrBuilder builder,
379 [ir.Primitive value, SourceInformation sourceInformation]) {
380 assert(_continuation.parameters.length <= builder.environment.length);
381 isEmpty = false;
382 _buildTryExit(builder);
383 // Truncate the environment at the invocation site so it only includes
384 // values that will be continuation arguments. If an extra value is passed
385 // it will already be included in the continuation environment, but it is
386 // not present in the invocation environment.
387 int delta = builder.environment.length - _continuationEnvironment.length;
388 if (value != null) ++delta;
389 if (delta > 0) builder.environment.discard(delta);
390 if (value != null) builder.environment.extend(null, value);
391 builder.add(new ir.InvokeContinuation(
392 _continuation, builder.environment.index2value,
393 isRecursive: true, isEscapingTry: isEscapingTry));
394 builder._current = null;
395 }
396 }
397
398 /// Collect 'return' jumps.
399 ///
400 /// A return jump is one that targets the return continuation of a function.
401 /// Thus, returns from inside try/finally are not return jumps because they are
402 /// intercepted by a block that contains the finally handler code.
403 class ReturnJumpCollector extends JumpCollector {
404 bool isEmpty = true;
405 ir.Continuation get continuation => _continuation;
406 Environment environment = null;
407
408 /// Construct a return jump collector for a given return continuation.
409 ReturnJumpCollector(ir.Continuation continuation) : super.retrn(continuation);
410
411 void addJump(IrBuilder builder,
412 [ir.Primitive value, SourceInformation sourceInformation]) {
413 isEmpty = false;
414 builder.add(new ir.InvokeContinuation(continuation, <ir.Primitive>[value],
415 isEscapingTry: isEscapingTry, sourceInformation: sourceInformation));
416 builder._current = null;
417 }
418 }
419
420 /// Collect 'goto' jumps, continue to a labeled case from within a switch.
421 ///
422 /// These jumps are unrestricted within the switch. They can be forward or
423 /// backward. They are implemented by assigning to a state variable.
424 class GotoJumpCollector extends JumpCollector {
425 bool isEmpty = true;
426 final ir.Continuation continuation = null;
427 final Environment environment = null;
428
429 int _stateVariableIndex;
430 int _stateValue;
431 JumpCollector _breakJoin;
432
433 GotoJumpCollector(JumpTarget target, this._stateVariableIndex,
434 this._stateValue, this._breakJoin)
435 : super.goto(target);
436
437 void addJump(IrBuilder builder,
438 [ir.Primitive value, SourceInformation sourceInformation]) {
439 isEmpty = false;
440 ir.Primitive constant = builder.buildIntegerConstant(_stateValue);
441 builder.environment.index2value[_stateVariableIndex] = constant;
442 builder.jumpTo(_breakJoin);
443 }
444 }
445
446 /// Function for building a node in the context of the current builder.
447 typedef ir.Node BuildFunction(node);
448
449 /// Function for building nodes in the context of the provided [builder].
450 typedef ir.Node SubbuildFunction(IrBuilder builder);
451
452 /// Mixin that provides encapsulated access to nested builders.
453 abstract class IrBuilderMixin<N> {
454 IrBuilder _irBuilder;
455
456 /// Execute [f] with [builder] as the current builder.
457 withBuilder(IrBuilder builder, f()) {
458 assert(builder != null);
459 IrBuilder prev = _irBuilder;
460 _irBuilder = builder;
461 var result = f();
462 _irBuilder = prev;
463 return result;
464 }
465
466 /// The current builder.
467 IrBuilder get irBuilder {
468 assert(_irBuilder != null);
469 return _irBuilder;
470 }
471
472 /// Visits the [node].
473 ir.Primitive visit(N node);
474
475 /// Builds and returns the [ir.Node] for [node] or returns `null` if
476 /// [node] is `null`.
477 ir.Node build(N node) => node != null ? visit(node) : null;
478
479 /// Returns a closure that takes an [IrBuilder] and builds [node] in its
480 /// context using [build].
481 SubbuildFunction subbuild(N node) {
482 return (IrBuilder builder) => withBuilder(builder, () => build(node));
483 }
484
485 /// Returns a closure that takes an [IrBuilder] and runs [f] in its context.
486 SubbuildFunction nested(f()) {
487 return (IrBuilder builder) => withBuilder(builder, f);
488 }
489
490 /// Returns a closure that takes an [IrBuilder] and builds the sequence of
491 /// [nodes] in its context using [build].
492 // TODO(johnniwinther): Type [nodes] as `Iterable<N>` when `NodeList` uses
493 // `List` instead of `Link`.
494 SubbuildFunction subbuildSequence(/*Iterable<N>*/ nodes) {
495 return (IrBuilder builder) {
496 return withBuilder(builder, () => builder.buildSequence(nodes, build));
497 };
498 }
499 }
500
501 /// Shared state between delimited IrBuilders within the same function.
502 class IrBuilderSharedState {
503 final GlobalProgramInformation program;
504
505 final BackendConstantEnvironment constants;
506
507 ConstantSystem get constantSystem => constants.constantSystem;
508
509 /// A stack of collectors for breaks.
510 List<JumpCollector> breakCollectors = <JumpCollector>[];
511
512 /// A stack of collectors for continues.
513 List<JumpCollector> continueCollectors = <JumpCollector>[];
514
515 final ExecutableElement currentElement;
516
517 final ir.Continuation returnContinuation = new ir.Continuation.retrn();
518
519 /// The target of a return from the function.
520 ///
521 /// A null value indicates that the target is the function's return
522 /// continuation. Otherwise, when inside the try block of try/finally
523 /// a return is intercepted to give a place to generate the finally code.
524 JumpCollector returnCollector;
525
526 /// Parameter holding the internal value of 'this' passed to the function.
527 ///
528 /// For nested functions, this is *not* captured receiver, but the function
529 /// object itself.
530 ir.Parameter thisParameter;
531
532 /// If non-null, this refers to the receiver (`this`) in the enclosing method.
533 ir.Primitive enclosingThis;
534
535 final List<ir.Parameter> functionParameters = <ir.Parameter>[];
536
537 /// Maps boxed locals to their location. These locals are not part of
538 /// the environment.
539 final Map<Local, ClosureLocation> boxedVariables = {};
540
541 IrBuilderSharedState(this.program, this.constants, this.currentElement) {
542 returnCollector = new ReturnJumpCollector(returnContinuation);
543 }
544 }
545
546 class ThisParameterLocal implements Local {
547 final ExecutableElement executableContext;
548 ThisParameterLocal(this.executableContext);
549 String get name => 'this';
550 toString() => 'ThisParameterLocal($executableContext)';
551 }
552
553 /// The IR builder maintains an environment and an IR fragment.
554 ///
555 /// The IR fragment is an expression with a hole in it. The hole represents
556 /// the focus where new expressions can be added. The fragment is implemented
557 /// by [root] which is the root of the expression and [_current] which is the
558 /// expression that immediately contains the hole. Not all expressions have a
559 /// hole (e.g., invocations, which always occur in tail position, do not have a
560 /// hole). Expressions with a hole have a plug method.
561 ///
562 /// The environment maintains the reaching definition of each local variable,
563 /// including some synthetic locals such as [TypeVariableLocal].
564 ///
565 /// Internally, IR builders also maintains a [JumpCollector] stack and tracks
566 /// which variables are currently boxed or held in a mutable local variable.
567 class IrBuilder {
568 final List<ir.Parameter> _parameters = <ir.Parameter>[];
569
570 final IrBuilderSharedState state;
571
572 /// A map from variable indexes to their values.
573 ///
574 /// [BoxLocal]s map to their box. [LocalElement]s that are boxed are not
575 /// in the map; look up their [BoxLocal] instead.
576 Environment environment;
577
578 /// A map from mutable local variables to their [ir.MutableVariable]s.
579 ///
580 /// Mutable variables are treated as boxed. Writes to them are observable
581 /// side effects.
582 Map<Local, ir.MutableVariable> mutableVariables;
583
584 ir.Expression root = null;
585 ir.Expression _current = null;
586
587 GlobalProgramInformation get program => state.program;
588
589 IrBuilder(GlobalProgramInformation program,
590 BackendConstantEnvironment constants, ExecutableElement currentElement)
591 : state = new IrBuilderSharedState(program, constants, currentElement),
592 environment = new Environment.empty(),
593 mutableVariables = <Local, ir.MutableVariable>{};
594
595 IrBuilder._internal(this.state, this.environment, this.mutableVariables);
596
597 /// Construct a delimited visitor for visiting a subtree.
598 ///
599 /// Build a subterm that is not (yet) connected to the CPS term. The
600 /// delimited visitor has its own has its own context for building an IR
601 /// expression, so the built expression is not plugged into the parent's
602 /// context. It has its own compile-time environment mapping local
603 /// variables to their values. If an optional environment argument is
604 /// supplied, it is used as the builder's initial environment. Otherwise
605 /// the environment is initially a copy of the parent builder's environment.
606 IrBuilder makeDelimitedBuilder([Environment env = null]) {
607 return new IrBuilder._internal(
608 state,
609 env != null ? env : new Environment.from(environment),
610 mutableVariables);
611 }
612
613 /// True if [local] should currently be accessed from a [ir.MutableVariable].
614 bool isInMutableVariable(Local local) {
615 return mutableVariables.containsKey(local);
616 }
617
618 /// Creates a [ir.MutableVariable] for the given local.
619 void makeMutableVariable(Local local) {
620 mutableVariables[local] = new ir.MutableVariable(local);
621 }
622
623 /// Remove an [ir.MutableVariable] for a local.
624 ///
625 /// Subsequent access to the local will be direct rather than through the
626 /// mutable variable.
627 void removeMutableVariable(Local local) {
628 mutableVariables.remove(local);
629 }
630
631 /// Gets the [MutableVariable] containing the value of [local].
632 ir.MutableVariable getMutableVariable(Local local) {
633 return mutableVariables[local];
634 }
635
636 bool get isOpen => root == null || _current != null;
637
638 List<ir.Primitive> buildFunctionHeader(Iterable<Local> parameters,
639 {ClosureScope closureScope, ClosureEnvironment env}) {
640 _createThisParameter();
641 _enterClosureEnvironment(env);
642 _enterScope(closureScope);
643 parameters.forEach(_createFunctionParameter);
644 return _parameters;
645 }
646
647 /// Creates a parameter for [local] and adds it to the current environment.
648 ir.Parameter _createLocalParameter(Local local) {
649 ir.Parameter parameter = new ir.Parameter(local);
650 _parameters.add(parameter);
651 environment.extend(local, parameter);
652 return parameter;
653 }
654
655 /// Plug an expression into the 'hole' in the context being accumulated. The
656 /// empty context (just a hole) is represented by root (and current) being
657 /// null. Since the hole in the current context is filled by this function,
658 /// the new hole must be in the newly added expression---which becomes the
659 /// new value of current.
660 void add(ir.Expression expr) {
661 assert(isOpen);
662 if (root == null) {
663 root = _current = expr;
664 } else {
665 _current = _current.plug(expr);
666 }
667 }
668
669 /// Create and add a new [LetPrim] for [primitive].
670 ir.Primitive addPrimitive(ir.Primitive primitive) {
671 add(new ir.LetPrim(primitive));
672 return primitive;
673 }
674
675 ir.Primitive buildInvokeStatic(Element element, Selector selector,
676 List<ir.Primitive> arguments, SourceInformation sourceInformation) {
677 assert(!element.isLocal);
678 assert(!element.isInstanceMember);
679 assert(isOpen);
680 if (program.isJsInterop(element)) {
681 return buildInvokeJsInteropMember(element, arguments, sourceInformation);
682 }
683 return addPrimitive(
684 new ir.InvokeStatic(element, selector, arguments, sourceInformation));
685 }
686
687 ir.Primitive _buildInvokeSuper(Element target, Selector selector,
688 List<ir.Primitive> arguments, SourceInformation sourceInformation) {
689 assert(target.isInstanceMember);
690 assert(isOpen);
691 return addPrimitive(new ir.InvokeMethodDirectly(
692 buildThis(), target, selector, arguments, sourceInformation));
693 }
694
695 ir.Primitive _buildInvokeDynamic(
696 ir.Primitive receiver,
697 Selector selector,
698 TypeMask mask,
699 List<ir.Primitive> arguments,
700 SourceInformation sourceInformation) {
701 assert(isOpen);
702 return addPrimitive(new ir.InvokeMethod(receiver, selector, mask, arguments,
703 sourceInformation: sourceInformation));
704 }
705
706 ir.Primitive _buildInvokeCall(
707 ir.Primitive target,
708 CallStructure callStructure,
709 TypeMask mask,
710 List<ir.Definition> arguments,
711 SourceInformation sourceInformation) {
712 Selector selector = callStructure.callSelector;
713 return _buildInvokeDynamic(
714 target, selector, mask, arguments, sourceInformation);
715 }
716
717 ir.Primitive buildStaticNoSuchMethod(Selector selector,
718 List<ir.Primitive> arguments, SourceInformation sourceInformation) {
719 ir.Primitive receiver = buildStringConstant('');
720 ir.Primitive name = buildStringConstant(selector.name);
721 ir.Primitive argumentList = buildListLiteral(null, arguments);
722 ir.Primitive expectedArgumentNames = buildNullConstant();
723 return buildStaticFunctionInvocation(
724 program.throwNoSuchMethod,
725 <ir.Primitive>[receiver, name, argumentList, expectedArgumentNames],
726 sourceInformation);
727 }
728
729 /// Create a [ir.Constant] from [value] and add it to the CPS term.
730 ir.Constant buildConstant(ConstantValue value,
731 {SourceInformation sourceInformation}) {
732 assert(isOpen);
733 return addPrimitive(
734 new ir.Constant(value, sourceInformation: sourceInformation));
735 }
736
737 /// Create an integer constant and add it to the CPS term.
738 ir.Constant buildIntegerConstant(int value) {
739 return buildConstant(state.constantSystem.createInt(value));
740 }
741
742 /// Create a double constant and add it to the CPS term.
743 ir.Constant buildDoubleConstant(double value) {
744 return buildConstant(state.constantSystem.createDouble(value));
745 }
746
747 /// Create a Boolean constant and add it to the CPS term.
748 ir.Constant buildBooleanConstant(bool value) {
749 return buildConstant(state.constantSystem.createBool(value));
750 }
751
752 /// Create a null constant and add it to the CPS term.
753 ir.Constant buildNullConstant() {
754 return buildConstant(state.constantSystem.createNull());
755 }
756
757 /// Create a string constant and add it to the CPS term.
758 ir.Constant buildStringConstant(String value) {
759 return buildConstant(
760 state.constantSystem.createString(new ast.DartString.literal(value)));
761 }
762
763 /// Create a string constant and add it to the CPS term.
764 ir.Constant buildDartStringConstant(ast.DartString value) {
765 return buildConstant(state.constantSystem.createString(value));
766 }
767
768 /// Creates a non-constant list literal of the provided [type] and with the
769 /// provided [values].
770 ir.Primitive buildListLiteral(
771 InterfaceType type, Iterable<ir.Primitive> values,
772 {TypeMask allocationSiteType}) {
773 assert(isOpen);
774 return addPrimitive(new ir.LiteralList(type, values.toList(),
775 allocationSiteType: allocationSiteType));
776 }
777
778 /// Creates a conditional expression with the provided [condition] where the
779 /// then and else expression are created through the [buildThenExpression]
780 /// and [buildElseExpression] functions, respectively.
781 ir.Primitive buildConditional(
782 ir.Primitive condition,
783 ir.Primitive buildThenExpression(IrBuilder builder),
784 ir.Primitive buildElseExpression(IrBuilder builder),
785 SourceInformation sourceInformation) {
786 assert(isOpen);
787
788 // The then and else expressions are delimited.
789 IrBuilder thenBuilder = makeDelimitedBuilder();
790 IrBuilder elseBuilder = makeDelimitedBuilder();
791 ir.Primitive thenValue = buildThenExpression(thenBuilder);
792 ir.Primitive elseValue = buildElseExpression(elseBuilder);
793
794 // Treat the values of the subexpressions as named values in the
795 // environment, so they will be treated as arguments to the join-point
796 // continuation. We know the environments are the right size because
797 // expressions cannot introduce variable bindings.
798 assert(environment.length == thenBuilder.environment.length);
799 assert(environment.length == elseBuilder.environment.length);
800 JumpCollector join =
801 new ForwardJumpCollector(environment, hasExtraArgument: true);
802 thenBuilder.jumpTo(join, thenValue);
803 elseBuilder.jumpTo(join, elseValue);
804
805 // Build the term
806 // let cont join(x, ..., result) = [] in
807 // let cont then() = [[thenPart]]; join(v, ...)
808 // and else() = [[elsePart]]; join(v, ...)
809 // in
810 // if condition (then, else)
811 ir.Continuation thenContinuation = new ir.Continuation([]);
812 ir.Continuation elseContinuation = new ir.Continuation([]);
813 thenContinuation.body = thenBuilder.root;
814 elseContinuation.body = elseBuilder.root;
815 add(new ir.LetCont(
816 join.continuation,
817 new ir.LetCont.two(
818 thenContinuation,
819 elseContinuation,
820 new ir.Branch.strict(condition, thenContinuation, elseContinuation,
821 sourceInformation))));
822 environment = join.environment;
823 return environment.discard(1);
824 }
825
826 /**
827 * Add an explicit `return null` for functions that don't have a return
828 * statement on each branch. This includes functions with an empty body,
829 * such as `foo(){ }`.
830 */
831 void _ensureReturn() {
832 if (!isOpen) return;
833 ir.Constant constant = buildNullConstant();
834 add(new ir.InvokeContinuation(state.returnContinuation, [constant]));
835 _current = null;
836 }
837
838 /// Create a [ir.FunctionDefinition] using [root] as the body.
839 ///
840 /// The protocol for building a function is:
841 /// 1. Call [buildFunctionHeader].
842 /// 2. Call `buildXXX` methods to build the body.
843 /// 3. Call [makeFunctionDefinition] to finish.
844 ir.FunctionDefinition makeFunctionDefinition(
845 SourceInformation sourceInformation) {
846 _ensureReturn();
847 return new ir.FunctionDefinition(state.currentElement, state.thisParameter,
848 state.functionParameters, state.returnContinuation, root,
849 sourceInformation: sourceInformation);
850 }
851
852 /// Create a invocation of the [method] on the super class where the call
853 /// structure is defined [callStructure] and the argument values are defined
854 /// by [arguments].
855 ir.Primitive buildSuperMethodInvocation(
856 MethodElement method,
857 CallStructure callStructure,
858 List<ir.Primitive> arguments,
859 SourceInformation sourceInformation) {
860 // TODO(johnniwinther): This shouldn't be necessary.
861 SelectorKind kind = Elements.isOperatorName(method.name)
862 ? SelectorKind.OPERATOR
863 : SelectorKind.CALL;
864 Selector selector = new Selector(kind, method.memberName, callStructure);
865 return _buildInvokeSuper(method, selector, arguments, sourceInformation);
866 }
867
868 /// Create a read access of the [method] on the super class, i.e. a
869 /// closurization of [method].
870 ir.Primitive buildSuperMethodGet(
871 MethodElement method, SourceInformation sourceInformation) {
872 // TODO(johnniwinther): This should have its own ir node.
873 return _buildInvokeSuper(method, new Selector.getter(method.memberName),
874 const <ir.Primitive>[], sourceInformation);
875 }
876
877 /// Create a getter invocation of the [getter] on the super class.
878 ir.Primitive buildSuperGetterGet(
879 MethodElement getter, SourceInformation sourceInformation) {
880 // TODO(johnniwinther): This should have its own ir node.
881 return _buildInvokeSuper(getter, new Selector.getter(getter.memberName),
882 const <ir.Primitive>[], sourceInformation);
883 }
884
885 /// Create an setter invocation of the [setter] on the super class with
886 /// [value].
887 ir.Primitive buildSuperSetterSet(MethodElement setter, ir.Primitive value,
888 SourceInformation sourceInformation) {
889 // TODO(johnniwinther): This should have its own ir node.
890 _buildInvokeSuper(setter, new Selector.setter(setter.memberName),
891 <ir.Primitive>[value], sourceInformation);
892 return value;
893 }
894
895 /// Create an invocation of the index [method] on the super class with
896 /// the provided [index].
897 ir.Primitive buildSuperIndex(MethodElement method, ir.Primitive index,
898 SourceInformation sourceInformation) {
899 return _buildInvokeSuper(
900 method, new Selector.index(), <ir.Primitive>[index], sourceInformation);
901 }
902
903 /// Create an invocation of the index set [method] on the super class with
904 /// the provided [index] and [value].
905 ir.Primitive buildSuperIndexSet(MethodElement method, ir.Primitive index,
906 ir.Primitive value, SourceInformation sourceInformation) {
907 _buildInvokeSuper(method, new Selector.indexSet(),
908 <ir.Primitive>[index, value], sourceInformation);
909 return value;
910 }
911
912 /// Create a dynamic invocation on [receiver] where the method name and
913 /// argument structure are defined by [selector] and the argument values are
914 /// defined by [arguments].
915 ir.Primitive buildDynamicInvocation(
916 ir.Primitive receiver,
917 Selector selector,
918 TypeMask mask,
919 List<ir.Primitive> arguments,
920 SourceInformation sourceInformation) {
921 return _buildInvokeDynamic(
922 receiver, selector, mask, arguments, sourceInformation);
923 }
924
925 /// Create a dynamic getter invocation on [receiver] where the getter name is
926 /// defined by [selector].
927 ir.Primitive buildDynamicGet(ir.Primitive receiver, Selector selector,
928 TypeMask mask, SourceInformation sourceInformation) {
929 assert(selector.isGetter);
930 FieldElement field = program.locateSingleField(selector, mask);
931 if (field != null) {
932 // If the world says this resolves to a unique field, then it MUST be
933 // treated as a field access, since the getter might not be emitted.
934 return buildFieldGet(receiver, field, sourceInformation);
935 } else {
936 return _buildInvokeDynamic(
937 receiver, selector, mask, const <ir.Primitive>[], sourceInformation);
938 }
939 }
940
941 /// Create a dynamic setter invocation on [receiver] where the setter name and
942 /// argument are defined by [selector] and [value], respectively.
943 ir.Primitive buildDynamicSet(ir.Primitive receiver, Selector selector,
944 TypeMask mask, ir.Primitive value, SourceInformation sourceInformation) {
945 assert(selector.isSetter);
946 FieldElement field = program.locateSingleField(selector, mask);
947 if (field != null) {
948 // If the world says this resolves to a unique field, then it MUST be
949 // treated as a field access, since the setter might not be emitted.
950 buildFieldSet(receiver, field, value, sourceInformation);
951 } else {
952 _buildInvokeDynamic(
953 receiver, selector, mask, <ir.Primitive>[value], sourceInformation);
954 }
955 return value;
956 }
957
958 /// Create a dynamic index set invocation on [receiver] with the provided
959 /// [index] and [value].
960 ir.Primitive buildDynamicIndexSet(
961 ir.Primitive receiver,
962 TypeMask mask,
963 ir.Primitive index,
964 ir.Primitive value,
965 SourceInformation sourceInformation) {
966 _buildInvokeDynamic(receiver, new Selector.indexSet(), mask,
967 <ir.Primitive>[index, value], sourceInformation);
968 return value;
969 }
970
971 /// Create an invocation of the local [function] where argument structure is
972 /// defined by [callStructure] and the argument values are defined by
973 /// [arguments].
974 ir.Primitive buildLocalFunctionInvocation(
975 LocalFunctionElement function,
976 CallStructure callStructure,
977 List<ir.Primitive> arguments,
978 SourceInformation sourceInformation) {
979 // TODO(johnniwinther): Maybe this should have its own ir node.
980 return buildCallInvocation(
981 buildLocalGet(function), callStructure, arguments, sourceInformation);
982 }
983
984 /// Create a static invocation of [function].
985 ///
986 /// The arguments are not named and their values are defined by [arguments].
987 ir.Primitive buildStaticFunctionInvocation(MethodElement function,
988 List<ir.Primitive> arguments, SourceInformation sourceInformation) {
989 Selector selector = new Selector.call(
990 function.memberName, new CallStructure(arguments.length));
991 return buildInvokeStatic(function, selector, arguments, sourceInformation);
992 }
993
994 /// Create a getter invocation of the static [getter].
995 ir.Primitive buildStaticGetterGet(
996 MethodElement getter, SourceInformation sourceInformation) {
997 Selector selector = new Selector.getter(getter.memberName);
998 return buildInvokeStatic(
999 getter, selector, const <ir.Primitive>[], sourceInformation);
1000 }
1001
1002 /// Create a write access to the static [field] with the [value].
1003 ir.Primitive buildStaticFieldSet(FieldElement field, ir.Primitive value,
1004 SourceInformation sourceInformation) {
1005 addPrimitive(new ir.SetStatic(field, value, sourceInformation));
1006 return value;
1007 }
1008
1009 /// Create a setter invocation of the static [setter] with the [value].
1010 ir.Primitive buildStaticSetterSet(MethodElement setter, ir.Primitive value,
1011 SourceInformation sourceInformation) {
1012 Selector selector = new Selector.setter(setter.memberName);
1013 buildInvokeStatic(
1014 setter, selector, <ir.Primitive>[value], sourceInformation);
1015 return value;
1016 }
1017
1018 /// Create an erroneous invocation where argument structure is defined by
1019 /// [selector] and the argument values are defined by [arguments].
1020 // TODO(johnniwinther): Make this more fine-grained.
1021 ir.Primitive buildErroneousInvocation(Element element, Selector selector,
1022 List<ir.Primitive> arguments, SourceInformation sourceInformation) {
1023 // TODO(johnniwinther): This should have its own ir node.
1024 return buildInvokeStatic(element, selector, arguments, sourceInformation);
1025 }
1026
1027 /// Concatenate string values. The arguments must be strings.
1028 ir.Primitive buildStringConcatenation(
1029 List<ir.Primitive> arguments, SourceInformation sourceInformation) {
1030 assert(isOpen);
1031 return addPrimitive(new ir.ApplyBuiltinOperator(
1032 ir.BuiltinOperator.StringConcatenate, arguments, sourceInformation));
1033 }
1034
1035 /// Create an invocation of the `call` method of [functionExpression], where
1036 /// the structure of arguments are given by [callStructure].
1037 // TODO(johnniwinther): This should take a [TypeMask].
1038 ir.Primitive buildCallInvocation(
1039 ir.Primitive functionExpression,
1040 CallStructure callStructure,
1041 List<ir.Definition> arguments,
1042 SourceInformation sourceInformation) {
1043 return _buildInvokeCall(
1044 functionExpression, callStructure, null, arguments, sourceInformation);
1045 }
1046
1047 /// Creates an if-then-else statement with the provided [condition] where the
1048 /// then and else branches are created through the [buildThenPart] and
1049 /// [buildElsePart] functions, respectively.
1050 ///
1051 /// An if-then statement is created if [buildElsePart] is a no-op.
1052 // TODO(johnniwinther): Unify implementation with [buildConditional] and
1053 // [_buildLogicalOperator].
1054 void buildIf(
1055 ir.Primitive condition,
1056 void buildThenPart(IrBuilder builder),
1057 void buildElsePart(IrBuilder builder),
1058 SourceInformation sourceInformation) {
1059 assert(isOpen);
1060
1061 // The then and else parts are delimited.
1062 IrBuilder thenBuilder = makeDelimitedBuilder();
1063 IrBuilder elseBuilder = makeDelimitedBuilder();
1064 buildThenPart(thenBuilder);
1065 buildElsePart(elseBuilder);
1066
1067 // Build the term
1068 // (Result =) let cont then() = [[thenPart]]
1069 // and else() = [[elsePart]]
1070 // in
1071 // if condition (then, else)
1072 ir.Continuation thenContinuation = new ir.Continuation([]);
1073 ir.Continuation elseContinuation = new ir.Continuation([]);
1074 // If exactly one of the then and else continuation bodies is open (i.e.,
1075 // the other one has an exit on all paths), then Continuation.plug expects
1076 // that continuation to be listed first. Arbitrarily use [then, else]
1077 // order otherwise.
1078 List<ir.Continuation> arms = !thenBuilder.isOpen && elseBuilder.isOpen
1079 ? <ir.Continuation>[elseContinuation, thenContinuation]
1080 : <ir.Continuation>[thenContinuation, elseContinuation];
1081
1082 ir.Expression result = new ir.LetCont.many(
1083 arms,
1084 new ir.Branch.strict(
1085 condition, thenContinuation, elseContinuation, sourceInformation));
1086
1087 JumpCollector join; // Null if there is no join.
1088 if (thenBuilder.isOpen && elseBuilder.isOpen) {
1089 // There is a join-point continuation. Build the term
1090 // 'let cont join(x, ...) = [] in Result' and plug invocations of the
1091 // join-point continuation into the then and else continuations.
1092 join = new ForwardJumpCollector(environment);
1093 thenBuilder.jumpTo(join);
1094 elseBuilder.jumpTo(join);
1095 result = new ir.LetCont(join.continuation, result);
1096 }
1097
1098 // The then or else term root could be null, but not both. If there is
1099 // a join then an InvokeContinuation was just added to both of them. If
1100 // there is no join, then at least one of them is closed and thus has a
1101 // non-null root by the definition of the predicate isClosed. In the
1102 // case that one of them is null, it must be the only one that is open
1103 // and thus contains the new hole in the context. This case is handled
1104 // after the branch is plugged into the current hole.
1105 thenContinuation.body = thenBuilder.root;
1106 elseContinuation.body = elseBuilder.root;
1107
1108 add(result);
1109 if (join == null) {
1110 // At least one subexpression is closed.
1111 if (thenBuilder.isOpen) {
1112 if (thenBuilder.root != null) _current = thenBuilder._current;
1113 environment = thenBuilder.environment;
1114 } else if (elseBuilder.isOpen) {
1115 if (elseBuilder.root != null) _current = elseBuilder._current;
1116 environment = elseBuilder.environment;
1117 } else {
1118 _current = null;
1119 }
1120 } else {
1121 environment = join.environment;
1122 }
1123 }
1124
1125 void jumpTo(JumpCollector collector,
1126 [ir.Primitive value, SourceInformation sourceInformation]) {
1127 collector.addJump(this, value, sourceInformation);
1128 }
1129
1130 void addRecursiveContinuation(BackwardJumpCollector collector) {
1131 assert(environment.length == collector.environment.length);
1132 add(new ir.LetCont(
1133 collector.continuation,
1134 new ir.InvokeContinuation(
1135 collector.continuation, environment.index2value)));
1136 environment = collector.environment;
1137 }
1138
1139 /// Creates a for loop in which the initializer, condition, body, update are
1140 /// created by [buildInitializer], [buildCondition], [buildBody] and
1141 /// [buildUpdate], respectively.
1142 ///
1143 /// The jump [target] is used to identify which `break` and `continue`
1144 /// statements that have this `for` statement as their target.
1145 ///
1146 /// The [closureScope] identifies variables that should be boxed in this loop.
1147 /// This includes variables declared inside the body of the loop as well as
1148 /// in the for-loop initializer.
1149 ///
1150 /// [loopVariables] is the list of variables declared in the for-loop
1151 /// initializer.
1152 void buildFor(
1153 {SubbuildFunction buildInitializer,
1154 SubbuildFunction buildCondition,
1155 SourceInformation conditionSourceInformation,
1156 SubbuildFunction buildBody,
1157 SubbuildFunction buildUpdate,
1158 JumpTarget target,
1159 ClosureScope closureScope,
1160 List<LocalElement> loopVariables}) {
1161 assert(isOpen);
1162
1163 // For loops use four named continuations: the entry to the condition,
1164 // the entry to the body, the loop exit, and the loop successor (break).
1165 // The CPS translation of
1166 // [[for (initializer; condition; update) body; successor]] is:
1167 //
1168 // _enterForLoopInitializer();
1169 // [[initializer]];
1170 // let cont loop(x, ...) =
1171 // let prim cond = [[condition]] in
1172 // let cont break(x, ...) = [[successor]] in
1173 // let cont exit() = break(v, ...) in
1174 // let cont body() =
1175 // _enterForLoopBody();
1176 // let cont continue(x, ...) =
1177 // _enterForLoopUpdate();
1178 // [[update]];
1179 // loop(v, ...) in
1180 // [[body]];
1181 // continue(v, ...) in
1182 // branch cond (body, exit) in
1183 // loop(v, ...)
1184 //
1185 // If there are no breaks in the body, the break continuation is inlined
1186 // in the exit continuation (i.e., the translation of the successor
1187 // statement occurs in the exit continuation). If there is only one
1188 // invocation of the continue continuation (i.e., no continues in the
1189 // body), the continue continuation is inlined in the body.
1190 _enterForLoopInitializer(closureScope, loopVariables);
1191 buildInitializer(this);
1192
1193 JumpCollector loop = new BackwardJumpCollector(environment);
1194 addRecursiveContinuation(loop);
1195
1196 ir.Primitive condition = buildCondition(this);
1197 if (condition == null) {
1198 // If the condition is empty then the body is entered unconditionally.
1199 condition = buildBooleanConstant(true);
1200 }
1201 JumpCollector breakCollector =
1202 new ForwardJumpCollector(environment, target: target);
1203
1204 // Use a pair of builders for the body, one for the entry code if any
1205 // and one for the body itself. We only decide whether to insert a
1206 // continue continuation until after translating the body and there is no
1207 // way to insert such a continuation between the entry code and the body
1208 // if they are translated together.
1209 IrBuilder outerBodyBuilder = makeDelimitedBuilder();
1210 outerBodyBuilder._enterForLoopBody(closureScope, loopVariables);
1211 JumpCollector continueCollector =
1212 new ForwardJumpCollector(outerBodyBuilder.environment, target: target);
1213
1214 IrBuilder innerBodyBuilder = outerBodyBuilder.makeDelimitedBuilder();
1215 state.breakCollectors.add(breakCollector);
1216 state.continueCollectors.add(continueCollector);
1217 buildBody(innerBodyBuilder);
1218 assert(state.breakCollectors.last == breakCollector);
1219 assert(state.continueCollectors.last == continueCollector);
1220 state.breakCollectors.removeLast();
1221 state.continueCollectors.removeLast();
1222
1223 // The binding of the continue continuation should occur as late as
1224 // possible, that is, at the nearest common ancestor of all the continue
1225 // sites in the body. However, that is difficult to compute here, so it
1226 // is instead placed just outside the translation of the loop body. In
1227 // the case where there are no continues in the body, the updates are
1228 // translated immediately after the body.
1229 bool hasContinues = !continueCollector.isEmpty;
1230 IrBuilder updateBuilder;
1231 if (hasContinues) {
1232 if (innerBodyBuilder.isOpen) innerBodyBuilder.jumpTo(continueCollector);
1233 updateBuilder = makeDelimitedBuilder(continueCollector.environment);
1234 } else {
1235 updateBuilder = innerBodyBuilder;
1236 }
1237 updateBuilder._enterForLoopUpdate(closureScope, loopVariables);
1238 buildUpdate(updateBuilder);
1239 if (updateBuilder.isOpen) updateBuilder.jumpTo(loop);
1240 // Connect the inner and outer body builders. This is done only after
1241 // it is guaranteed that the updateBuilder has a non-empty term.
1242 if (hasContinues) {
1243 outerBodyBuilder.add(new ir.LetCont(
1244 continueCollector.continuation, innerBodyBuilder.root));
1245 continueCollector.continuation.body = updateBuilder.root;
1246 } else {
1247 outerBodyBuilder.add(innerBodyBuilder.root);
1248 }
1249
1250 // Create loop exit and body entry continuations and a branch to them.
1251 ir.Continuation exitContinuation = new ir.Continuation([]);
1252 ir.Continuation bodyContinuation = new ir.Continuation([]);
1253 bodyContinuation.body = outerBodyBuilder.root;
1254 // Note the order of continuations: the first one is the one that will
1255 // be filled by LetCont.plug.
1256 ir.LetCont branch = new ir.LetCont.two(
1257 exitContinuation,
1258 bodyContinuation,
1259 new ir.Branch.strict(condition, bodyContinuation, exitContinuation,
1260 conditionSourceInformation));
1261 // If there are breaks in the body, then there must be a join-point
1262 // continuation for the normal exit and the breaks. Otherwise, the
1263 // successor is translated in the hole in the exit continuation.
1264 bool hasBreaks = !breakCollector.isEmpty;
1265 ir.LetCont letBreak;
1266 if (hasBreaks) {
1267 IrBuilder exitBuilder = makeDelimitedBuilder();
1268 exitBuilder.jumpTo(breakCollector);
1269 exitContinuation.body = exitBuilder.root;
1270 letBreak = new ir.LetCont(breakCollector.continuation, branch);
1271 add(letBreak);
1272 environment = breakCollector.environment;
1273 } else {
1274 add(branch);
1275 }
1276 }
1277
1278 /// Creates a for-in loop, `for (v in e) b`.
1279 ///
1280 /// [buildExpression] creates the expression, `e`. The variable, `v`, can
1281 /// take one of three forms:
1282 /// 1) `v` can be declared within the for-in statement, like in
1283 /// `for (var v in e)`, in which case, [buildVariableDeclaration]
1284 /// creates its declaration and [variableElement] is the element for
1285 /// the declared variable,
1286 /// 2) `v` is predeclared statically known variable, that is top-level,
1287 /// static, or local variable, in which case [variableElement] is the
1288 /// variable element, and [variableSelector] defines its write access,
1289 /// 3) `v` is an instance variable in which case [variableSelector]
1290 /// defines its write access.
1291 /// [buildBody] creates the body, `b`, of the loop. The jump [target] is used
1292 /// to identify which `break` and `continue` statements that have this for-in
1293 /// statement as their target.
1294 void buildForIn(
1295 {SubbuildFunction buildExpression,
1296 SubbuildFunction buildVariableDeclaration,
1297 Element variableElement,
1298 Selector variableSelector,
1299 TypeMask variableMask,
1300 SourceInformation variableSetSourceInformation,
1301 TypeMask currentMask,
1302 SourceInformation currentSourceInformation,
1303 TypeMask iteratorMask,
1304 SourceInformation iteratorSourceInformation,
1305 TypeMask moveNextMask,
1306 SourceInformation moveNextSourceInformation,
1307 SubbuildFunction buildBody,
1308 JumpTarget target,
1309 ClosureScope closureScope,
1310 SourceInformation conditionSourceInformation}) {
1311 // The for-in loop
1312 //
1313 // for (a in e) s;
1314 //
1315 // Is compiled analogously to:
1316 //
1317 // it = e.iterator;
1318 // while (it.moveNext()) {
1319 // var a = it.current;
1320 // s;
1321 // }
1322
1323 // Fill the current hole with:
1324 // let prim expressionReceiver = [[e]] in
1325 // let cont iteratorInvoked(iterator) =
1326 // [ ]
1327 // in expressionReceiver.iterator () iteratorInvoked
1328 ir.Primitive expressionReceiver = buildExpression(this);
1329 List<ir.Primitive> emptyArguments = <ir.Primitive>[];
1330 ir.Primitive iterator = addPrimitive(new ir.InvokeMethod(
1331 expressionReceiver, Selectors.iterator, iteratorMask, emptyArguments));
1332
1333 // Fill with:
1334 // let cont loop(x, ...) =
1335 // let cont moveNextInvoked(condition) =
1336 // [ ]
1337 // in iterator.moveNext () moveNextInvoked
1338 // in loop(v, ...)
1339 JumpCollector loop = new BackwardJumpCollector(environment, target: target);
1340 addRecursiveContinuation(loop);
1341 ir.Primitive condition = addPrimitive(new ir.InvokeMethod(
1342 iterator, Selectors.moveNext, moveNextMask, emptyArguments));
1343
1344 // As a delimited term, build:
1345 // <<BODY>> =
1346 // _enterScope();
1347 // [[variableDeclaration]]
1348 // let cont currentInvoked(currentValue) =
1349 // [[a = currentValue]];
1350 // [ ]
1351 // in iterator.current () currentInvoked
1352 IrBuilder bodyBuilder = makeDelimitedBuilder();
1353 bodyBuilder._enterScope(closureScope);
1354 if (buildVariableDeclaration != null) {
1355 buildVariableDeclaration(bodyBuilder);
1356 }
1357 ir.Primitive currentValue = bodyBuilder.addPrimitive(new ir.InvokeMethod(
1358 iterator, Selectors.current, currentMask, emptyArguments,
1359 sourceInformation: currentSourceInformation));
1360 // TODO(johnniwinther): Extract this as a provided strategy.
1361 if (Elements.isLocal(variableElement)) {
1362 bodyBuilder.buildLocalVariableSet(
1363 variableElement, currentValue, variableSetSourceInformation);
1364 } else if (Elements.isError(variableElement) ||
1365 Elements.isMalformed(variableElement)) {
1366 Selector selector = new Selector.setter(
1367 new Name(variableElement.name, variableElement.library));
1368 List<ir.Primitive> value = <ir.Primitive>[currentValue];
1369 // Note the comparison below. It can be the case that an element isError
1370 // and isMalformed.
1371 if (Elements.isError(variableElement)) {
1372 bodyBuilder.buildStaticNoSuchMethod(
1373 selector, value, variableSetSourceInformation);
1374 } else {
1375 bodyBuilder.buildErroneousInvocation(
1376 variableElement, selector, value, variableSetSourceInformation);
1377 }
1378 } else if (Elements.isStaticOrTopLevel(variableElement)) {
1379 if (variableElement.isField) {
1380 bodyBuilder.addPrimitive(new ir.SetStatic(
1381 variableElement, currentValue, variableSetSourceInformation));
1382 } else {
1383 bodyBuilder.buildStaticSetterSet(
1384 variableElement, currentValue, variableSetSourceInformation);
1385 }
1386 } else {
1387 ir.Primitive receiver = bodyBuilder.buildThis();
1388 assert(receiver != null);
1389 bodyBuilder.buildDynamicSet(receiver, variableSelector, variableMask,
1390 currentValue, variableSetSourceInformation);
1391 }
1392
1393 // Translate the body in the hole in the delimited term above, and add
1394 // a jump to the loop if control flow is live after the body.
1395 JumpCollector breakCollector =
1396 new ForwardJumpCollector(environment, target: target);
1397 state.breakCollectors.add(breakCollector);
1398 state.continueCollectors.add(loop);
1399 buildBody(bodyBuilder);
1400 assert(state.breakCollectors.last == breakCollector);
1401 assert(state.continueCollectors.last == loop);
1402 state.breakCollectors.removeLast();
1403 state.continueCollectors.removeLast();
1404 if (bodyBuilder.isOpen) bodyBuilder.jumpTo(loop);
1405
1406 // Create body entry and loop exit continuations and a branch to them.
1407 //
1408 // let cont exit() = [ ]
1409 // and body() = <<BODY>>
1410 // in branch condition (body, exit)
1411 ir.Continuation exitContinuation = new ir.Continuation([]);
1412 ir.Continuation bodyContinuation = new ir.Continuation([]);
1413 bodyContinuation.body = bodyBuilder.root;
1414 // Note the order of continuations: the first one is the one that will
1415 // be filled by LetCont.plug.
1416 ir.LetCont branch = new ir.LetCont.two(
1417 exitContinuation,
1418 bodyContinuation,
1419 new ir.Branch.strict(condition, bodyContinuation, exitContinuation,
1420 conditionSourceInformation));
1421 // If there are breaks in the body, then there must be a join-point
1422 // continuation for the normal exit and the breaks. Otherwise, the
1423 // successor is translated in the hole in the exit continuation.
1424 bool hasBreaks = !breakCollector.isEmpty;
1425 ir.LetCont letBreak;
1426 if (hasBreaks) {
1427 IrBuilder exitBuilder = makeDelimitedBuilder();
1428 exitBuilder.jumpTo(breakCollector);
1429 exitContinuation.body = exitBuilder.root;
1430 letBreak = new ir.LetCont(breakCollector.continuation, branch);
1431 add(letBreak);
1432 environment = breakCollector.environment;
1433 } else {
1434 add(branch);
1435 }
1436 }
1437
1438 /// Creates a while loop in which the condition and body are created by
1439 /// [buildCondition] and [buildBody], respectively.
1440 ///
1441 /// The jump [target] is used to identify which `break` and `continue`
1442 /// statements that have this `while` statement as their target.
1443 void buildWhile(
1444 {SubbuildFunction buildCondition,
1445 SubbuildFunction buildBody,
1446 JumpTarget target,
1447 ClosureScope closureScope,
1448 SourceInformation sourceInformation}) {
1449 assert(isOpen);
1450 // While loops use four named continuations: the entry to the body, the
1451 // loop exit, the loop back edge (continue), and the loop exit (break).
1452 // The CPS translation of [[while (condition) body; successor]] is:
1453 //
1454 // let cont continue(x, ...) =
1455 // let prim cond = [[condition]] in
1456 // let cont break(x, ...) = [[successor]] in
1457 // let cont exit() = break(v, ...)
1458 // and body() =
1459 // _enterScope();
1460 // [[body]];
1461 // continue(v, ...)
1462 // in branch cond (body, exit)
1463 // in continue(v, ...)
1464 //
1465 // If there are no breaks in the body, the break continuation is inlined
1466 // in the exit continuation (i.e., the translation of the successor
1467 // statement occurs in the exit continuation).
1468 JumpCollector loop = new BackwardJumpCollector(environment, target: target);
1469 addRecursiveContinuation(loop);
1470
1471 ir.Primitive condition = buildCondition(this);
1472
1473 JumpCollector breakCollector =
1474 new ForwardJumpCollector(environment, target: target);
1475
1476 IrBuilder bodyBuilder = makeDelimitedBuilder();
1477 bodyBuilder._enterScope(closureScope);
1478 state.breakCollectors.add(breakCollector);
1479 state.continueCollectors.add(loop);
1480 buildBody(bodyBuilder);
1481 assert(state.breakCollectors.last == breakCollector);
1482 assert(state.continueCollectors.last == loop);
1483 state.breakCollectors.removeLast();
1484 state.continueCollectors.removeLast();
1485 if (bodyBuilder.isOpen) bodyBuilder.jumpTo(loop);
1486
1487 // Create body entry and loop exit continuations and a branch to them.
1488 ir.Continuation exitContinuation = new ir.Continuation([]);
1489 ir.Continuation bodyContinuation = new ir.Continuation([]);
1490 bodyContinuation.body = bodyBuilder.root;
1491 // Note the order of continuations: the first one is the one that will
1492 // be filled by LetCont.plug.
1493 ir.LetCont branch = new ir.LetCont.two(
1494 exitContinuation,
1495 bodyContinuation,
1496 new ir.Branch.strict(
1497 condition, bodyContinuation, exitContinuation, sourceInformation));
1498 // If there are breaks in the body, then there must be a join-point
1499 // continuation for the normal exit and the breaks. Otherwise, the
1500 // successor is translated in the hole in the exit continuation.
1501 bool hasBreaks = !breakCollector.isEmpty;
1502 ir.LetCont letBreak;
1503 if (hasBreaks) {
1504 IrBuilder exitBuilder = makeDelimitedBuilder();
1505 exitBuilder.jumpTo(breakCollector);
1506 exitContinuation.body = exitBuilder.root;
1507 letBreak = new ir.LetCont(breakCollector.continuation, branch);
1508 add(letBreak);
1509 environment = breakCollector.environment;
1510 } else {
1511 add(branch);
1512 }
1513 }
1514
1515 /// Creates a do-while loop.
1516 ///
1517 /// The body and condition are created by [buildBody] and [buildCondition].
1518 /// The jump target [target] is the target of `break` and `continue`
1519 /// statements in the body that have the loop as their target.
1520 /// [closureScope] contains all the variables declared in the loop (but not
1521 /// declared in some inner closure scope).
1522 void buildDoWhile(
1523 {SubbuildFunction buildBody,
1524 SubbuildFunction buildCondition,
1525 JumpTarget target,
1526 ClosureScope closureScope,
1527 SourceInformation sourceInformation}) {
1528 assert(isOpen);
1529 // The CPS translation of [[do body; while (condition); successor]] is:
1530 //
1531 // let cont break(x, ...) = [[successor]] in
1532 // let cont rec loop(x, ...) =
1533 // let cont continue(x, ...) =
1534 // let prim cond = [[condition]] in
1535 // let cont exit() = break(v, ...)
1536 // and repeat() = loop(v, ...)
1537 // in branch cond (repeat, exit)
1538 // in [[body]]; continue(v, ...)
1539 // in loop(v, ...)
1540 IrBuilder loopBuilder = makeDelimitedBuilder();
1541 JumpCollector loop =
1542 new BackwardJumpCollector(loopBuilder.environment, target: target);
1543 loopBuilder.addRecursiveContinuation(loop);
1544
1545 // Translate the body.
1546 JumpCollector breakCollector =
1547 new ForwardJumpCollector(environment, target: target);
1548 JumpCollector continueCollector =
1549 new ForwardJumpCollector(loopBuilder.environment, target: target);
1550 IrBuilder bodyBuilder = loopBuilder.makeDelimitedBuilder();
1551 bodyBuilder._enterScope(closureScope);
1552 state.breakCollectors.add(breakCollector);
1553 state.continueCollectors.add(continueCollector);
1554 buildBody(bodyBuilder);
1555 assert(state.breakCollectors.last == breakCollector);
1556 assert(state.continueCollectors.last == continueCollector);
1557 state.breakCollectors.removeLast();
1558 state.continueCollectors.removeLast();
1559 if (bodyBuilder.isOpen) bodyBuilder.jumpTo(continueCollector);
1560
1561 // Construct the body of the continue continuation (i.e., the condition).
1562 // <Continue> =
1563 // let prim cond = [[condition]] in
1564 // let cont exit() = break(v, ...)
1565 // and repeat() = loop(v, ...)
1566 // in branch cond (repeat, exit)
1567 IrBuilder continueBuilder = loopBuilder.makeDelimitedBuilder();
1568 continueBuilder.environment = continueCollector.environment;
1569 ir.Primitive condition = buildCondition(continueBuilder);
1570
1571 ir.Continuation exitContinuation = new ir.Continuation([]);
1572 IrBuilder exitBuilder = continueBuilder.makeDelimitedBuilder();
1573 exitBuilder.jumpTo(breakCollector);
1574 exitContinuation.body = exitBuilder.root;
1575 ir.Continuation repeatContinuation = new ir.Continuation([]);
1576 IrBuilder repeatBuilder = continueBuilder.makeDelimitedBuilder();
1577 repeatBuilder.jumpTo(loop);
1578 repeatContinuation.body = repeatBuilder.root;
1579
1580 continueBuilder.add(new ir.LetCont.two(
1581 exitContinuation,
1582 repeatContinuation,
1583 new ir.Branch.strict(condition, repeatContinuation, exitContinuation,
1584 sourceInformation)));
1585 continueCollector.continuation.body = continueBuilder.root;
1586
1587 // Construct the loop continuation (i.e., the body and condition).
1588 // <Loop> =
1589 // let cont continue(x, ...) =
1590 // <Continue>
1591 // in [[body]]; continue(v, ...)
1592 loopBuilder
1593 .add(new ir.LetCont(continueCollector.continuation, bodyBuilder.root));
1594
1595 // And tie it all together.
1596 add(new ir.LetCont(breakCollector.continuation, loopBuilder.root));
1597 environment = breakCollector.environment;
1598 }
1599
1600 void buildSimpleSwitch(JumpCollector join, List<SwitchCaseInfo> cases,
1601 SubbuildFunction buildDefaultBody) {
1602 IrBuilder casesBuilder = makeDelimitedBuilder();
1603 for (SwitchCaseInfo caseInfo in cases) {
1604 ir.Primitive condition = caseInfo.buildCondition(casesBuilder);
1605 IrBuilder thenBuilder = makeDelimitedBuilder();
1606 caseInfo.buildBody(thenBuilder);
1607 ir.Continuation thenContinuation = new ir.Continuation([]);
1608 thenContinuation.body = thenBuilder.root;
1609 ir.Continuation elseContinuation = new ir.Continuation([]);
1610 // A LetCont.two term has a hole as the body of the first listed
1611 // continuation, to be plugged by the translation. Therefore put the
1612 // else continuation first.
1613 casesBuilder.add(new ir.LetCont.two(
1614 elseContinuation,
1615 thenContinuation,
1616 new ir.Branch.strict(condition, thenContinuation, elseContinuation,
1617 caseInfo.sourceInformation)));
1618 }
1619
1620 if (buildDefaultBody == null) {
1621 casesBuilder.jumpTo(join);
1622 } else {
1623 buildDefaultBody(casesBuilder);
1624 }
1625
1626 if (!join.isEmpty) {
1627 add(new ir.LetCont(join.continuation, casesBuilder.root));
1628 environment = join.environment;
1629 } else if (casesBuilder.root != null) {
1630 add(casesBuilder.root);
1631 _current = casesBuilder._current;
1632 environment = casesBuilder.environment;
1633 } else {
1634 // The translation of the cases did not emit any code.
1635 }
1636 }
1637
1638 /// Utility function to translate try/catch into the IR.
1639 ///
1640 /// The translation treats try/finally and try/catch/finally as if they
1641 /// were macro-expanded into try/catch. This utility function generates
1642 /// that try/catch. The function is parameterized over a list of variables
1643 /// that should be boxed on entry to the try, and over functions to emit
1644 /// code for entering the try, building the try body, leaving the try body,
1645 /// building the catch body, and leaving the entire try/catch.
1646 ///
1647 /// Please see the function's implementation for where these functions are
1648 /// called.
1649 void _helpBuildTryCatch(
1650 TryStatementInfo variables,
1651 void enterTry(IrBuilder builder),
1652 SubbuildFunction buildTryBlock,
1653 void leaveTry(IrBuilder builder),
1654 List<ir.Parameter> buildCatch(IrBuilder builder, JumpCollector join),
1655 void leaveTryCatch(
1656 IrBuilder builder, JumpCollector join, ir.Expression body)) {
1657 JumpCollector join = new ForwardJumpCollector(environment);
1658 IrBuilder tryCatchBuilder = makeDelimitedBuilder();
1659
1660 // Variables treated as mutable in a try are not mutable outside of it.
1661 // Work with a copy of the outer builder's mutable variables.
1662 tryCatchBuilder.mutableVariables =
1663 new Map<Local, ir.MutableVariable>.from(mutableVariables);
1664 for (LocalVariableElement variable in variables.boxedOnEntry) {
1665 assert(!tryCatchBuilder.isInMutableVariable(variable));
1666 ir.Primitive value = tryCatchBuilder.buildLocalGet(variable);
1667 tryCatchBuilder.makeMutableVariable(variable);
1668 tryCatchBuilder.declareLocalVariable(variable, initialValue: value);
1669 }
1670
1671 IrBuilder tryBuilder = tryCatchBuilder.makeDelimitedBuilder();
1672 enterTry(tryBuilder);
1673 buildTryBlock(tryBuilder);
1674 if (tryBuilder.isOpen) {
1675 join.enterTry(variables.boxedOnEntry);
1676 tryBuilder.jumpTo(join);
1677 join.leaveTry();
1678 }
1679 leaveTry(tryBuilder);
1680
1681 IrBuilder catchBuilder = tryCatchBuilder.makeDelimitedBuilder();
1682 for (LocalVariableElement variable in variables.boxedOnEntry) {
1683 assert(catchBuilder.isInMutableVariable(variable));
1684 ir.Primitive value = catchBuilder.buildLocalGet(variable);
1685 // After this point, the variables that were boxed on entry to the try
1686 // are no longer treated as mutable.
1687 catchBuilder.removeMutableVariable(variable);
1688 catchBuilder.environment.update(variable, value);
1689 }
1690
1691 List<ir.Parameter> catchParameters = buildCatch(catchBuilder, join);
1692 ir.Continuation catchContinuation = new ir.Continuation(catchParameters);
1693 catchContinuation.body = catchBuilder.root;
1694 tryCatchBuilder.add(new ir.LetHandler(catchContinuation, tryBuilder.root));
1695
1696 leaveTryCatch(this, join, tryCatchBuilder.root);
1697 }
1698
1699 /// Translates a try/catch.
1700 ///
1701 /// [variables] provides information on local variables declared and boxed
1702 /// within the try body.
1703 /// [buildTryBlock] builds the try block.
1704 /// [catchClauseInfos] provides access to the catch type, exception variable,
1705 /// and stack trace variable, and a function for building the catch block.
1706 void buildTryCatch(TryStatementInfo variables, SubbuildFunction buildTryBlock,
1707 List<CatchClauseInfo> catchClauseInfos) {
1708 assert(isOpen);
1709 // Catch handlers are in scope for their body. The CPS translation of
1710 // [[try tryBlock catch (ex, st) catchBlock; successor]] is:
1711 //
1712 // let cont join(v0, v1, ...) = [[successor]] in
1713 // let mutable m0 = x0 in
1714 // let mutable m1 = x1 in
1715 // ...
1716 // let handler catch_(ex, st) =
1717 // let prim p0 = GetMutable(m0) in
1718 // let prim p1 = GetMutable(m1) in
1719 // ...
1720 // [[catchBlock]]
1721 // join(p0, p1, ...)
1722 // in
1723 // [[tryBlock]]
1724 // let prim p0' = GetMutable(m0) in
1725 // let prim p1' = GetMutable(m1) in
1726 // ...
1727 // join(p0', p1', ...)
1728 //
1729 // In other words, both the try and catch block are in the scope of the
1730 // join-point continuation, and they are both in the scope of a sequence
1731 // of mutable bindings for the variables assigned in the try. The join-
1732 // point continuation is not in the scope of these mutable bindings.
1733 // The tryBlock is in the scope of a binding for the catch handler. Each
1734 // instruction (specifically, each call) in the tryBlock is in the dynamic
1735 // scope of the handler. The mutable bindings are dereferenced at the end
1736 // of the try block and at the beginning of the catch block, so the
1737 // variables are unboxed in the catch block and at the join point.
1738
1739 void enterTry(IrBuilder builder) {
1740 // On entry to try of try/catch, update the builder's state to reflect the
1741 // variables that have been boxed.
1742 void interceptJump(JumpCollector collector) {
1743 collector.enterTry(variables.boxedOnEntry);
1744 }
1745
1746 builder.state.breakCollectors.forEach(interceptJump);
1747 builder.state.continueCollectors.forEach(interceptJump);
1748 interceptJump(builder.state.returnCollector);
1749 }
1750
1751 void leaveTry(IrBuilder builder) {
1752 // On exit from try of try/catch, update the builder's state to reflect
1753 // the variables that are no longer boxed.
1754 void restoreJump(JumpCollector collector) {
1755 collector.leaveTry();
1756 }
1757
1758 builder.state.breakCollectors.forEach(restoreJump);
1759 builder.state.continueCollectors.forEach(restoreJump);
1760 restoreJump(builder.state.returnCollector);
1761 }
1762
1763 List<ir.Parameter> buildCatch(IrBuilder builder, JumpCollector join) {
1764 // Translate the catch clauses. Multiple clauses are translated as if
1765 // they were explicitly cascaded if/else type tests.
1766
1767 // Handlers are always translated as having both exception and stack trace
1768 // parameters. Multiple clauses do not have to use the same names for
1769 // them. Choose the first of each as the name hint for the respective
1770 // handler parameter.
1771 ir.Parameter exceptionParameter =
1772 new ir.Parameter(catchClauseInfos.first.exceptionVariable);
1773 LocalVariableElement traceVariable;
1774 CatchClauseInfo catchAll;
1775 for (int i = 0; i < catchClauseInfos.length; ++i) {
1776 CatchClauseInfo info = catchClauseInfos[i];
1777 if (info.type == null) {
1778 catchAll = info;
1779 catchClauseInfos.length = i;
1780 break;
1781 }
1782 if (traceVariable == null) {
1783 traceVariable = info.stackTraceVariable;
1784 }
1785 }
1786 ir.Parameter traceParameter = new ir.Parameter(traceVariable);
1787
1788 ir.Expression buildCatchClause(CatchClauseInfo clause) {
1789 IrBuilder clauseBuilder = builder.makeDelimitedBuilder();
1790 if (clause.exceptionVariable != null) {
1791 clauseBuilder.declareLocalVariable(clause.exceptionVariable,
1792 initialValue: exceptionParameter);
1793 }
1794 if (clause.stackTraceVariable != null) {
1795 clauseBuilder.declareLocalVariable(clause.stackTraceVariable,
1796 initialValue: traceParameter);
1797 }
1798 clause.buildCatchBlock(clauseBuilder);
1799 if (clauseBuilder.isOpen) clauseBuilder.jumpTo(join);
1800 return clauseBuilder.root;
1801 }
1802
1803 // Expand multiple catch clauses into an explicit if/then/else. Iterate
1804 // them in reverse so the current block becomes the next else block.
1805 ir.Expression catchBody =
1806 (catchAll == null) ? new ir.Rethrow() : buildCatchClause(catchAll);
1807 for (CatchClauseInfo clause in catchClauseInfos.reversed) {
1808 ir.Continuation thenContinuation = new ir.Continuation([]);
1809 ir.Continuation elseContinuation = new ir.Continuation([]);
1810 thenContinuation.body = buildCatchClause(clause);
1811 elseContinuation.body = catchBody;
1812
1813 // Build the type test guarding this clause. We can share the
1814 // environment with the nested builder because this part cannot mutate
1815 // it.
1816 IrBuilder checkBuilder = builder.makeDelimitedBuilder(environment);
1817 ir.Primitive typeMatches = checkBuilder.buildTypeOperator(
1818 exceptionParameter, clause.type, clause.sourceInformation,
1819 isTypeTest: true);
1820 checkBuilder.add(new ir.LetCont.two(
1821 thenContinuation,
1822 elseContinuation,
1823 new ir.Branch.strict(typeMatches, thenContinuation,
1824 elseContinuation, clause.sourceInformation)));
1825 catchBody = checkBuilder.root;
1826 }
1827 builder.add(catchBody);
1828
1829 return <ir.Parameter>[exceptionParameter, traceParameter];
1830 }
1831
1832 void leaveTryCatch(
1833 IrBuilder builder, JumpCollector join, ir.Expression body) {
1834 // Add the binding for the join-point continuation and continue the
1835 // translation in its body.
1836 builder.add(new ir.LetCont(join.continuation, body));
1837 builder.environment = join.environment;
1838 }
1839
1840 _helpBuildTryCatch(variables, enterTry, buildTryBlock, leaveTry, buildCatch,
1841 leaveTryCatch);
1842 }
1843
1844 /// Translates a try/finally.
1845 ///
1846 /// [variables] provides information on local variables declared and boxed
1847 /// within the try body.
1848 /// [buildTryBlock] builds the try block.
1849 /// [buildFinallyBlock] builds the finally block.
1850 void buildTryFinally(TryStatementInfo variables,
1851 SubbuildFunction buildTryBlock, SubbuildFunction buildFinallyBlock) {
1852 assert(isOpen);
1853 // Try/finally is implemented in terms of try/catch and by duplicating the
1854 // code for finally at all exits. The encoding is:
1855 //
1856 // try tryBlock finally finallyBlock
1857 // ==>
1858 // try tryBlock catch (ex, st) { finallyBlock; rethrow } finallyBlock
1859 //
1860 // Where in tryBlock, all of the break, continue, and return exits are
1861 // translated as jumps to continuations (bound outside the catch handler)
1862 // that include the finally code followed by a break, continue, or
1863 // return respectively.
1864
1865 List<JumpCollector> savedBreaks, newBreaks, savedContinues, newContinues;
1866 JumpCollector savedReturn, newReturn;
1867 void enterTry(IrBuilder builder) {
1868 // On entry to the try of try/finally, update the builder's state to
1869 // relfect the variables that have been boxed. Then intercept all break,
1870 // continue, and return jumps out of the try so that they can go to
1871 // continuations that include the finally code.
1872 JumpCollector interceptJump(JumpCollector collector) {
1873 JumpCollector result =
1874 new ForwardJumpCollector(environment, target: collector.target);
1875 result.enterTry(variables.boxedOnEntry);
1876 return result;
1877 }
1878
1879 savedBreaks = builder.state.breakCollectors;
1880 savedContinues = builder.state.continueCollectors;
1881 savedReturn = builder.state.returnCollector;
1882
1883 builder.state.breakCollectors =
1884 newBreaks = savedBreaks.map(interceptJump).toList();
1885 builder.state.continueCollectors =
1886 newContinues = savedContinues.map(interceptJump).toList();
1887 builder.state.returnCollector = newReturn =
1888 new ForwardJumpCollector(environment, hasExtraArgument: true)
1889 ..enterTry(variables.boxedOnEntry);
1890 }
1891
1892 void leaveTry(IrBuilder builder) {
1893 // On exit from the try of try/finally, update the builder's state to
1894 // reflect the variables that are no longer boxed and restore the
1895 // original, unintercepted break, continue, and return targets.
1896 void restoreJump(JumpCollector collector) {
1897 collector.leaveTry();
1898 }
1899
1900 newBreaks.forEach(restoreJump);
1901 newContinues.forEach(restoreJump);
1902 newReturn.leaveTry();
1903 builder.state.breakCollectors = savedBreaks;
1904 builder.state.continueCollectors = savedContinues;
1905 builder.state.returnCollector = savedReturn;
1906 }
1907
1908 List<ir.Parameter> buildCatch(IrBuilder builder, JumpCollector join) {
1909 // The catch block of the try/catch used for try/finally is the finally
1910 // code followed by a rethrow.
1911 buildFinallyBlock(builder);
1912 if (builder.isOpen) {
1913 builder.add(new ir.Rethrow());
1914 builder._current = null;
1915 }
1916 return <ir.Parameter>[new ir.Parameter(null), new ir.Parameter(null)];
1917 }
1918
1919 void leaveTryCatch(
1920 IrBuilder builder, JumpCollector join, ir.Expression body) {
1921 // Build a list of continuations for jumps from the try block and
1922 // duplicate the finally code before jumping to the actual target.
1923 List<ir.Continuation> exits = <ir.Continuation>[join.continuation];
1924 void addJump(
1925 JumpCollector newCollector, JumpCollector originalCollector) {
1926 if (newCollector.isEmpty) return;
1927 IrBuilder builder = makeDelimitedBuilder(newCollector.environment);
1928 buildFinallyBlock(builder);
1929 if (builder.isOpen) builder.jumpTo(originalCollector);
1930 newCollector.continuation.body = builder.root;
1931 exits.add(newCollector.continuation);
1932 }
1933
1934 for (int i = 0; i < newBreaks.length; ++i) {
1935 addJump(newBreaks[i], savedBreaks[i]);
1936 }
1937 for (int i = 0; i < newContinues.length; ++i) {
1938 addJump(newContinues[i], savedContinues[i]);
1939 }
1940 if (!newReturn.isEmpty) {
1941 IrBuilder builder = makeDelimitedBuilder(newReturn.environment);
1942 ir.Primitive value = builder.environment.discard(1);
1943 buildFinallyBlock(builder);
1944 if (builder.isOpen) builder.buildReturn(value: value);
1945 newReturn.continuation.body = builder.root;
1946 exits.add(newReturn.continuation);
1947 }
1948 builder.add(new ir.LetCont.many(exits, body));
1949 builder.environment = join.environment;
1950 buildFinallyBlock(builder);
1951 }
1952
1953 _helpBuildTryCatch(variables, enterTry, buildTryBlock, leaveTry, buildCatch,
1954 leaveTryCatch);
1955 }
1956
1957 /// Create a return statement `return value;` or `return;` if [value] is
1958 /// null.
1959 void buildReturn({ir.Primitive value, SourceInformation sourceInformation}) {
1960 // Build(Return(e), C) = C'[InvokeContinuation(return, x)]
1961 // where (C', x) = Build(e, C)
1962 //
1963 // Return without a subexpression is translated as if it were return null.
1964 assert(isOpen);
1965 if (value == null) {
1966 value = buildNullConstant();
1967 }
1968 jumpTo(state.returnCollector, value, sourceInformation);
1969 }
1970
1971 /// Generate the body for a native function [function] that is annotated with
1972 /// an implementation in JavaScript (provided as string in [javaScriptCode]).
1973 void buildNativeFunctionBody(FunctionElement function, String javaScriptCode,
1974 SourceInformation sourceInformation) {
1975 NativeBehavior behavior = new NativeBehavior();
1976 behavior.sideEffects.setAllSideEffects();
1977 // Generate a [ForeignCode] statement from the given native code.
1978 buildForeignCode(
1979 js.js
1980 .statementTemplateYielding(new js.LiteralStatement(javaScriptCode)),
1981 <ir.Primitive>[],
1982 behavior,
1983 sourceInformation);
1984 }
1985
1986 /// Generate the body for a native function that redirects to a native
1987 /// JavaScript function, getter, or setter.
1988 ///
1989 /// Generates a call to the real target, which is given by [functions]'s
1990 /// `fixedBackendName`, passing all parameters as arguments. The target can
1991 /// be the JavaScript implementation of a function, getter, or setter.
1992 void buildRedirectingNativeFunctionBody(FunctionElement function, String name,
1993 SourceInformation sourceInformation) {
1994 List<ir.Primitive> arguments = <ir.Primitive>[];
1995 NativeBehavior behavior = new NativeBehavior();
1996 behavior.sideEffects.setAllSideEffects();
1997 program.addNativeMethod(function);
1998 // Construct the access of the target element.
1999 String code = function.isInstanceMember ? '#.$name' : name;
2000 if (function.isInstanceMember) {
2001 arguments.add(state.thisParameter);
2002 }
2003 // Collect all parameters of the function and templates for them to be
2004 // inserted into the JavaScript code.
2005 List<String> argumentTemplates = <String>[];
2006 function.functionSignature.forEachParameter((ParameterElement parameter) {
2007 ir.Primitive input = environment.lookup(parameter);
2008 DartType type = program.unaliasType(parameter.type);
2009 if (type is FunctionType) {
2010 // The parameter type is a function type either directly or through
2011 // typedef(s).
2012 ir.Constant arity = buildIntegerConstant(type.computeArity());
2013 input = buildStaticFunctionInvocation(program.closureConverter,
2014 <ir.Primitive>[input, arity], sourceInformation);
2015 }
2016 arguments.add(input);
2017 argumentTemplates.add('#');
2018 });
2019 // Construct the application of parameters for functions and setters.
2020 if (function.kind == ElementKind.FUNCTION) {
2021 code = "$code(${argumentTemplates.join(', ')})";
2022 } else if (function.kind == ElementKind.SETTER) {
2023 code = "$code = ${argumentTemplates.single}";
2024 } else {
2025 assert(argumentTemplates.isEmpty);
2026 assert(function.kind == ElementKind.GETTER);
2027 }
2028 // Generate the [ForeignCode] expression and a return statement to return
2029 // its value.
2030 ir.Primitive value = buildForeignCode(
2031 js.js.uncachedExpressionTemplate(code),
2032 arguments,
2033 behavior,
2034 sourceInformation,
2035 type: program.getTypeMaskForNativeFunction(function));
2036 buildReturn(value: value, sourceInformation: sourceInformation);
2037 }
2038
2039 static _isNotNull(ir.Primitive value) =>
2040 !(value is ir.Constant && value.value.isNull);
2041
2042 /// Builds a call to a resolved js-interop element.
2043 ir.Primitive buildInvokeJsInteropMember(FunctionElement element,
2044 List<ir.Primitive> arguments, SourceInformation sourceInformation) {
2045 program.addNativeMethod(element);
2046 String target = program.getJsInteropTargetPath(element);
2047 // Strip off trailing arguments that were not specified.
2048 // TODO(jacobr,sigmund): assert that the trailing arguments are all null.
2049 // TODO(jacobr): rewrite named arguments to an object literal matching
2050 // the factory constructor case.
2051 var inputs = arguments.where(_isNotNull).toList();
2052
2053 var behavior = new NativeBehavior()..sideEffects.setAllSideEffects();
2054 DartType type = element.isConstructor
2055 ? element.enclosingClass.thisType
2056 : element.type.returnType;
2057 // Native behavior effects here are similar to native/behavior.dart.
2058 // The return type is dynamic if we don't trust js-interop type
2059 // declarations.
2060 behavior.typesReturned.add(
2061 program.trustJSInteropTypeAnnotations ? type : const DynamicType());
2062
2063 // The allocation effects include the declared type if it is native (which
2064 // includes js interop types).
2065 if (type.element != null && program.isNative(type.element)) {
2066 behavior.typesInstantiated.add(type);
2067 }
2068
2069 // It also includes any other JS interop type if we don't trust the
2070 // annotation or if is declared too broad.
2071 if (!program.trustJSInteropTypeAnnotations ||
2072 type.isObject ||
2073 type.isDynamic) {
2074 behavior.typesInstantiated.add(program.jsJavascriptObjectType);
2075 }
2076
2077 String code;
2078 if (element.isGetter) {
2079 code = target;
2080 } else if (element.isSetter) {
2081 code = "$target = #";
2082 } else {
2083 var args = new List.filled(inputs.length, '#').join(',');
2084 code = element.isConstructor ? "new $target($args)" : "$target($args)";
2085 }
2086 return buildForeignCode(
2087 js.js.parseForeignJS(code), inputs, behavior, sourceInformation);
2088 // TODO(sigmund): should we record the source-information here?
2089 }
2090
2091 /// Builds an object literal that results from invoking a factory constructor
2092 /// of a js-interop anonymous type.
2093 ir.Primitive buildJsInteropObjectLiteral(ConstructorElement constructor,
2094 List<ir.Primitive> arguments, SourceInformation sourceInformation) {
2095 assert(program.isJsInteropAnonymous(constructor));
2096 program.addNativeMethod(constructor);
2097 FunctionSignature params = constructor.functionSignature;
2098 int i = 0;
2099 var filteredArguments = <ir.Primitive>[];
2100 var entries = new Map<String, js.Expression>();
2101 params.orderedForEachParameter((ParameterElement parameter) {
2102 // TODO(jacobr): throw if parameter names do not match names of property
2103 // names in the class.
2104 assert(parameter.isNamed);
2105 ir.Primitive argument = arguments[i++];
2106 if (_isNotNull(argument)) {
2107 filteredArguments.add(argument);
2108 entries[parameter.name] =
2109 new js.InterpolatedExpression(filteredArguments.length - 1);
2110 }
2111 });
2112 var code = new js.Template(null, js.objectLiteral(entries));
2113 var behavior = new NativeBehavior();
2114 if (program.trustJSInteropTypeAnnotations) {
2115 behavior.typesReturned.add(constructor.enclosingClass.thisType);
2116 }
2117
2118 return buildForeignCode(
2119 code, filteredArguments, behavior, sourceInformation);
2120 }
2121
2122 /// Create a blocks of [statements] by applying [build] to all reachable
2123 /// statements. The first statement is assumed to be reachable.
2124 // TODO(johnniwinther): Type [statements] as `Iterable` when `NodeList` uses
2125 // `List` instead of `Link`.
2126 void buildBlock(var statements, BuildFunction build) {
2127 // Build(Block(stamements), C) = C'
2128 // where C' = statements.fold(Build, C)
2129 assert(isOpen);
2130 return buildSequence(statements, build);
2131 }
2132
2133 /// Creates a sequence of [nodes] by applying [build] to all reachable nodes.
2134 ///
2135 /// The first node in the sequence does not need to be reachable.
2136 // TODO(johnniwinther): Type [nodes] as `Iterable` when `NodeList` uses
2137 // `List` instead of `Link`.
2138 void buildSequence(var nodes, BuildFunction build) {
2139 for (var node in nodes) {
2140 if (!isOpen) return;
2141 build(node);
2142 }
2143 }
2144
2145 /// Creates a labeled statement
2146 void buildLabeledStatement({SubbuildFunction buildBody, JumpTarget target}) {
2147 JumpCollector join = new ForwardJumpCollector(environment, target: target);
2148 IrBuilder innerBuilder = makeDelimitedBuilder();
2149 innerBuilder.state.breakCollectors.add(join);
2150 buildBody(innerBuilder);
2151 innerBuilder.state.breakCollectors.removeLast();
2152 bool hasBreaks = !join.isEmpty;
2153 if (hasBreaks) {
2154 if (innerBuilder.isOpen) innerBuilder.jumpTo(join);
2155 add(new ir.LetCont(join.continuation, innerBuilder.root));
2156 environment = join.environment;
2157 } else if (innerBuilder.root != null) {
2158 add(innerBuilder.root);
2159 _current = innerBuilder._current;
2160 environment = innerBuilder.environment;
2161 } else {
2162 // The translation of the body did not emit any CPS term.
2163 }
2164 }
2165
2166 // Build(BreakStatement L, C) = C[InvokeContinuation(...)]
2167 //
2168 // The continuation and arguments are filled in later after translating
2169 // the body containing the break.
2170 bool buildBreak(JumpTarget target) {
2171 return buildJumpInternal(target, state.breakCollectors);
2172 }
2173
2174 // Build(ContinueStatement L, C) = C[InvokeContinuation(...)]
2175 //
2176 // The continuation and arguments are filled in later after translating
2177 // the body containing the continue.
2178 bool buildContinue(JumpTarget target) {
2179 return buildJumpInternal(target, state.continueCollectors);
2180 }
2181
2182 bool buildJumpInternal(
2183 JumpTarget target, Iterable<JumpCollector> collectors) {
2184 assert(isOpen);
2185 for (JumpCollector collector in collectors) {
2186 if (target == collector.target) {
2187 jumpTo(collector);
2188 return true;
2189 }
2190 }
2191 return false;
2192 }
2193
2194 void buildThrow(ir.Primitive value) {
2195 assert(isOpen);
2196 add(new ir.Throw(value));
2197 _current = null;
2198 }
2199
2200 ir.Primitive buildNonTailThrow(ir.Primitive value) {
2201 assert(isOpen);
2202 ir.Parameter param = new ir.Parameter(null);
2203 ir.Continuation cont = new ir.Continuation(<ir.Parameter>[param]);
2204 add(new ir.LetCont(cont, new ir.Throw(value)));
2205 return param;
2206 }
2207
2208 void buildRethrow() {
2209 assert(isOpen);
2210 add(new ir.Rethrow());
2211 _current = null;
2212 }
2213
2214 /// Create a negation of [condition].
2215 ir.Primitive buildNegation(
2216 ir.Primitive condition, SourceInformation sourceInformation) {
2217 // ! e is translated as e ? false : true
2218
2219 // Add a continuation parameter for the result of the expression.
2220 ir.Parameter resultParameter = new ir.Parameter(null);
2221
2222 ir.Continuation joinContinuation = new ir.Continuation([resultParameter]);
2223 ir.Continuation thenContinuation = new ir.Continuation([]);
2224 ir.Continuation elseContinuation = new ir.Continuation([]);
2225
2226 ir.Constant makeBoolConstant(bool value) {
2227 return new ir.Constant(state.constantSystem.createBool(value));
2228 }
2229
2230 ir.Constant trueConstant = makeBoolConstant(true);
2231 ir.Constant falseConstant = makeBoolConstant(false);
2232
2233 thenContinuation.body = new ir.LetPrim(falseConstant)
2234 ..plug(new ir.InvokeContinuation(joinContinuation, [falseConstant]));
2235 elseContinuation.body = new ir.LetPrim(trueConstant)
2236 ..plug(new ir.InvokeContinuation(joinContinuation, [trueConstant]));
2237
2238 add(new ir.LetCont(
2239 joinContinuation,
2240 new ir.LetCont.two(
2241 thenContinuation,
2242 elseContinuation,
2243 new ir.Branch.strict(condition, thenContinuation, elseContinuation,
2244 sourceInformation))));
2245 return resultParameter;
2246 }
2247
2248 /// Create a lazy and/or expression. [leftValue] is the value of the left
2249 /// operand and [buildRightValue] is called to process the value of the right
2250 /// operand in the context of its own [IrBuilder].
2251 ir.Primitive buildLogicalOperator(
2252 ir.Primitive leftValue,
2253 ir.Primitive buildRightValue(IrBuilder builder),
2254 SourceInformation sourceInformation,
2255 {bool isLazyOr: false}) {
2256 // e0 && e1 is translated as if e0 ? (e1 == true) : false.
2257 // e0 || e1 is translated as if e0 ? true : (e1 == true).
2258 // The translation must convert both e0 and e1 to booleans and handle
2259 // local variable assignments in e1.
2260 IrBuilder rightBuilder = makeDelimitedBuilder();
2261 ir.Primitive rightValue = buildRightValue(rightBuilder);
2262 // A dummy empty target for the branch on the left subexpression branch.
2263 // This enables using the same infrastructure for join-point continuations
2264 // as in visitIf and visitConditional. It will hold a definition of the
2265 // appropriate constant and an invocation of the join-point continuation.
2266 IrBuilder emptyBuilder = makeDelimitedBuilder();
2267 // Dummy empty targets for right true and right false. They hold
2268 // definitions of the appropriate constant and an invocation of the
2269 // join-point continuation.
2270 IrBuilder rightTrueBuilder = rightBuilder.makeDelimitedBuilder();
2271 IrBuilder rightFalseBuilder = rightBuilder.makeDelimitedBuilder();
2272
2273 // If we don't evaluate the right subexpression, the value of the whole
2274 // expression is this constant.
2275 ir.Constant leftBool = emptyBuilder.buildBooleanConstant(isLazyOr);
2276 // If we do evaluate the right subexpression, the value of the expression
2277 // is a true or false constant.
2278 ir.Constant rightTrue = rightTrueBuilder.buildBooleanConstant(true);
2279 ir.Constant rightFalse = rightFalseBuilder.buildBooleanConstant(false);
2280
2281 // Result values are passed as continuation arguments, which are
2282 // constructed based on environments. These assertions are a sanity check.
2283 assert(environment.length == emptyBuilder.environment.length);
2284 assert(environment.length == rightTrueBuilder.environment.length);
2285 assert(environment.length == rightFalseBuilder.environment.length);
2286
2287 // Wire up two continuations for the left subexpression, two continuations
2288 // for the right subexpression, and a three-way join continuation.
2289 JumpCollector join =
2290 new ForwardJumpCollector(environment, hasExtraArgument: true);
2291 emptyBuilder.jumpTo(join, leftBool);
2292 rightTrueBuilder.jumpTo(join, rightTrue);
2293 rightFalseBuilder.jumpTo(join, rightFalse);
2294 ir.Continuation leftTrueContinuation = new ir.Continuation([]);
2295 ir.Continuation leftFalseContinuation = new ir.Continuation([]);
2296 ir.Continuation rightTrueContinuation = new ir.Continuation([]);
2297 ir.Continuation rightFalseContinuation = new ir.Continuation([]);
2298 rightTrueContinuation.body = rightTrueBuilder.root;
2299 rightFalseContinuation.body = rightFalseBuilder.root;
2300 // The right subexpression has two continuations.
2301 rightBuilder.add(new ir.LetCont.two(
2302 rightTrueContinuation,
2303 rightFalseContinuation,
2304 new ir.Branch.strict(rightValue, rightTrueContinuation,
2305 rightFalseContinuation, sourceInformation)));
2306 // Depending on the operator, the left subexpression's continuations are
2307 // either the right subexpression or an invocation of the join-point
2308 // continuation.
2309 if (isLazyOr) {
2310 leftTrueContinuation.body = emptyBuilder.root;
2311 leftFalseContinuation.body = rightBuilder.root;
2312 } else {
2313 leftTrueContinuation.body = rightBuilder.root;
2314 leftFalseContinuation.body = emptyBuilder.root;
2315 }
2316
2317 add(new ir.LetCont(
2318 join.continuation,
2319 new ir.LetCont.two(
2320 leftTrueContinuation,
2321 leftFalseContinuation,
2322 new ir.Branch.strict(leftValue, leftTrueContinuation,
2323 leftFalseContinuation, sourceInformation))));
2324 environment = join.environment;
2325 return environment.discard(1);
2326 }
2327
2328 ir.Primitive buildIdentical(ir.Primitive x, ir.Primitive y,
2329 {SourceInformation sourceInformation}) {
2330 return addPrimitive(new ir.ApplyBuiltinOperator(
2331 ir.BuiltinOperator.Identical, <ir.Primitive>[x, y], sourceInformation));
2332 }
2333
2334 /// Called when entering a nested function with free variables.
2335 ///
2336 /// The free variables must subsequently be accessible using [buildLocalGet]
2337 /// and [buildLocalSet].
2338 void _enterClosureEnvironment(ClosureEnvironment env) {
2339 if (env == null) return;
2340
2341 // Obtain a reference to the function object (this).
2342 ir.Parameter thisPrim = state.thisParameter;
2343
2344 // Obtain access to the free variables.
2345 env.freeVariables.forEach((Local local, ClosureLocation location) {
2346 if (location.isBox) {
2347 // Boxed variables are loaded from their box on-demand.
2348 state.boxedVariables[local] = location;
2349 } else {
2350 // Unboxed variables are loaded from the function object immediately.
2351 // This includes BoxLocals which are themselves unboxed variables.
2352 environment.extend(
2353 local, addPrimitive(new ir.GetField(thisPrim, location.field)));
2354 }
2355 });
2356
2357 // If the function captures a reference to the receiver from the
2358 // enclosing method, remember which primitive refers to the receiver object.
2359 if (env.thisLocal != null && env.freeVariables.containsKey(env.thisLocal)) {
2360 state.enclosingThis = environment.lookup(env.thisLocal);
2361 }
2362
2363 // If the function has a self-reference, use the value of `this`.
2364 if (env.selfReference != null) {
2365 environment.extend(env.selfReference, thisPrim);
2366 }
2367 }
2368
2369 /// Creates a box for [scope.box] and binds the captured variables to
2370 /// that box.
2371 ///
2372 /// The captured variables can subsequently be manipulated with
2373 /// [declareLocalVariable], [buildLocalGet], and [buildLocalSet].
2374 void enterScope(ClosureScope scope) => _enterScope(scope);
2375
2376 /// Called when entering a function body or loop body.
2377 ///
2378 /// This is not called for for-loops, which instead use the methods
2379 /// [_enterForLoopInitializer], [_enterForLoopBody], and [_enterForLoopUpdate]
2380 /// due to their special scoping rules.
2381 ///
2382 /// The boxed variables declared in this scope must subsequently be available
2383 /// using [buildLocalGet], [buildLocalSet], etc.
2384 void _enterScope(ClosureScope scope) {
2385 if (scope == null) return;
2386 ir.CreateBox boxPrim = addPrimitive(new ir.CreateBox());
2387 environment.extend(scope.box, boxPrim);
2388 boxPrim.useElementAsHint(scope.box);
2389 scope.capturedVariables.forEach((Local local, ClosureLocation location) {
2390 assert(!state.boxedVariables.containsKey(local));
2391 if (location.isBox) {
2392 state.boxedVariables[local] = location;
2393 }
2394 });
2395 }
2396
2397 /// Add the given function parameter to the IR, and bind it in the environment
2398 /// or put it in its box, if necessary.
2399 void _createFunctionParameter(Local parameterElement) {
2400 ir.Parameter parameter = new ir.Parameter(parameterElement);
2401 _parameters.add(parameter);
2402 state.functionParameters.add(parameter);
2403 ClosureLocation location = state.boxedVariables[parameterElement];
2404 if (location != null) {
2405 addPrimitive(new ir.SetField(
2406 environment.lookup(location.box), location.field, parameter));
2407 } else {
2408 environment.extend(parameterElement, parameter);
2409 }
2410 }
2411
2412 void _createThisParameter() {
2413 assert(state.thisParameter == null);
2414 if (Elements.isStaticOrTopLevel(state.currentElement)) return;
2415 if (state.currentElement.isLocal) return;
2416 state.thisParameter =
2417 new ir.Parameter(new ThisParameterLocal(state.currentElement));
2418 }
2419
2420 void declareLocalVariable(LocalElement variableElement,
2421 {ir.Primitive initialValue}) {
2422 assert(isOpen);
2423 if (initialValue == null) {
2424 initialValue = buildNullConstant();
2425 }
2426 ClosureLocation location = state.boxedVariables[variableElement];
2427 if (location != null) {
2428 addPrimitive(new ir.SetField(
2429 environment.lookup(location.box), location.field, initialValue));
2430 } else if (isInMutableVariable(variableElement)) {
2431 add(new ir.LetMutable(getMutableVariable(variableElement), initialValue));
2432 } else {
2433 initialValue.useElementAsHint(variableElement);
2434 environment.extend(variableElement, initialValue);
2435 }
2436 }
2437
2438 /// Add [functionElement] to the environment with provided [definition].
2439 void declareLocalFunction(
2440 LocalFunctionElement functionElement,
2441 closure.ClosureClassElement classElement,
2442 SourceInformation sourceInformation) {
2443 ir.Primitive closure =
2444 buildFunctionExpression(classElement, sourceInformation);
2445 declareLocalVariable(functionElement, initialValue: closure);
2446 }
2447
2448 ir.Primitive buildFunctionExpression(closure.ClosureClassElement classElement,
2449 SourceInformation sourceInformation) {
2450 List<ir.Primitive> arguments = <ir.Primitive>[];
2451 for (closure.ClosureFieldElement field in classElement.closureFields) {
2452 // Captured 'this' and type variables are not always available as locals
2453 // in the environment, so treat those specially.
2454 ir.Primitive value;
2455 if (field.local is closure.ThisLocal) {
2456 value = buildThis();
2457 } else if (field.local is closure.TypeVariableLocal) {
2458 closure.TypeVariableLocal variable = field.local;
2459 value = buildTypeVariableAccess(variable.typeVariable);
2460 } else {
2461 value = environment.lookup(field.local);
2462 }
2463 arguments.add(value);
2464 }
2465 return addPrimitive(new ir.CreateInstance(
2466 classElement, arguments, null, sourceInformation));
2467 }
2468
2469 /// Create a read access of [local] function, variable, or parameter.
2470 // TODO(johnniwinther): Make [sourceInformation] mandatory.
2471 ir.Primitive buildLocalGet(LocalElement local,
2472 {SourceInformation sourceInformation}) {
2473 assert(isOpen);
2474 ClosureLocation location = state.boxedVariables[local];
2475 if (location != null) {
2476 ir.Primitive result = new ir.GetField(
2477 environment.lookup(location.box), location.field,
2478 sourceInformation: sourceInformation);
2479 result.useElementAsHint(local);
2480 return addPrimitive(result);
2481 } else if (isInMutableVariable(local)) {
2482 return addPrimitive(new ir.GetMutable(getMutableVariable(local),
2483 sourceInformation: sourceInformation));
2484 } else {
2485 return environment.lookup(local);
2486 }
2487 }
2488
2489 /// Create a write access to [local] variable or parameter with the provided
2490 /// [value].
2491 ir.Primitive buildLocalVariableSet(LocalElement local, ir.Primitive value,
2492 SourceInformation sourceInformation) {
2493 assert(isOpen);
2494 ClosureLocation location = state.boxedVariables[local];
2495 if (location != null) {
2496 addPrimitive(new ir.SetField(
2497 environment.lookup(location.box), location.field, value,
2498 sourceInformation: sourceInformation));
2499 } else if (isInMutableVariable(local)) {
2500 addPrimitive(new ir.SetMutable(getMutableVariable(local), value,
2501 sourceInformation: sourceInformation));
2502 } else {
2503 value.useElementAsHint(local);
2504 environment.update(local, value);
2505 }
2506 return value;
2507 }
2508
2509 /// Called before building the initializer of a for-loop.
2510 ///
2511 /// The loop variables will subsequently be declared using
2512 /// [declareLocalVariable].
2513 void _enterForLoopInitializer(
2514 ClosureScope scope, List<LocalElement> loopVariables) {
2515 if (scope == null) return;
2516 // If there are no boxed loop variables, don't create the box here, let
2517 // it be created inside the body instead.
2518 if (scope.boxedLoopVariables.isEmpty) return;
2519 _enterScope(scope);
2520 }
2521
2522 /// Called before building the body of a for-loop.
2523 void _enterForLoopBody(ClosureScope scope, List<LocalElement> loopVariables) {
2524 if (scope == null) return;
2525 // If there are boxed loop variables, the box has already been created
2526 // at the initializer.
2527 if (!scope.boxedLoopVariables.isEmpty) return;
2528 _enterScope(scope);
2529 }
2530
2531 /// Called before building the update of a for-loop.
2532 void _enterForLoopUpdate(
2533 ClosureScope scope, List<LocalElement> loopVariables) {
2534 if (scope == null) return;
2535 // If there are no boxed loop variables, then the box is created inside the
2536 // body, so there is no need to explicitly renew it.
2537 if (scope.boxedLoopVariables.isEmpty) return;
2538 ir.Primitive box = environment.lookup(scope.box);
2539 ir.Primitive newBox = addPrimitive(new ir.CreateBox());
2540 newBox.useElementAsHint(scope.box);
2541 for (VariableElement loopVar in scope.boxedLoopVariables) {
2542 ClosureLocation location = scope.capturedVariables[loopVar];
2543 ir.Primitive value = addPrimitive(new ir.GetField(box, location.field));
2544 addPrimitive(new ir.SetField(newBox, location.field, value));
2545 }
2546 environment.update(scope.box, newBox);
2547 }
2548
2549 /// Creates an access to the receiver from the current (or enclosing) method.
2550 ///
2551 /// If inside a closure class, [buildThis] will redirect access through
2552 /// closure fields in order to access the receiver from the enclosing method.
2553 ir.Primitive buildThis() {
2554 if (state.enclosingThis != null) return state.enclosingThis;
2555 assert(state.thisParameter != null);
2556 return state.thisParameter;
2557 }
2558
2559 ir.Primitive buildFieldGet(ir.Primitive receiver, FieldElement target,
2560 SourceInformation sourceInformation) {
2561 return addPrimitive(new ir.GetField(receiver, target,
2562 sourceInformation: sourceInformation,
2563 isFinal: program.fieldNeverChanges(target)));
2564 }
2565
2566 void buildFieldSet(ir.Primitive receiver, FieldElement target,
2567 ir.Primitive value, SourceInformation sourceInformation) {
2568 addPrimitive(new ir.SetField(receiver, target, value,
2569 sourceInformation: sourceInformation));
2570 }
2571
2572 ir.Primitive buildSuperFieldGet(
2573 FieldElement target, SourceInformation sourceInformation) {
2574 return addPrimitive(new ir.GetField(buildThis(), target,
2575 sourceInformation: sourceInformation));
2576 }
2577
2578 ir.Primitive buildSuperFieldSet(FieldElement target, ir.Primitive value,
2579 SourceInformation sourceInformation) {
2580 addPrimitive(new ir.SetField(buildThis(), target, value,
2581 sourceInformation: sourceInformation));
2582 return value;
2583 }
2584
2585 /// Loads parameters to a constructor body into the environment.
2586 ///
2587 /// The header for a constructor body differs from other functions in that
2588 /// some parameters are already boxed, and the box is passed as an argument
2589 /// instead of being created in the header.
2590 void buildConstructorBodyHeader(
2591 Iterable<Local> parameters, ClosureScope closureScope) {
2592 _createThisParameter();
2593 for (Local param in parameters) {
2594 ir.Parameter parameter = _createLocalParameter(param);
2595 state.functionParameters.add(parameter);
2596 }
2597 if (closureScope != null) {
2598 state.boxedVariables.addAll(closureScope.capturedVariables);
2599 }
2600 }
2601
2602 /// Create a constructor invocation of [element] on [type] where the
2603 /// constructor name and argument structure are defined by [callStructure] and
2604 /// the argument values are defined by [arguments].
2605 ir.Primitive buildConstructorInvocation(
2606 ConstructorElement element,
2607 CallStructure callStructure,
2608 DartType type,
2609 List<ir.Primitive> arguments,
2610 SourceInformation sourceInformation,
2611 {TypeMask allocationSiteType}) {
2612 assert(isOpen);
2613 Selector selector =
2614 new Selector(SelectorKind.CALL, element.memberName, callStructure);
2615 ClassElement cls = element.enclosingClass;
2616 if (program.isJsInterop(element)) {
2617 if (program.isJsInteropAnonymous(element)) {
2618 return buildJsInteropObjectLiteral(
2619 element, arguments, sourceInformation);
2620 }
2621 return buildInvokeJsInteropMember(element, arguments, sourceInformation);
2622 }
2623 if (program.requiresRuntimeTypesFor(cls)) {
2624 InterfaceType interface = type;
2625 Iterable<ir.Primitive> typeArguments =
2626 interface.typeArguments.map((DartType argument) {
2627 return type.treatAsRaw
2628 ? buildNullConstant()
2629 : buildTypeExpression(argument);
2630 });
2631 arguments = new List<ir.Primitive>.from(arguments)..addAll(typeArguments);
2632 }
2633 return addPrimitive(new ir.InvokeConstructor(
2634 type, element, selector, arguments, sourceInformation,
2635 allocationSiteType: allocationSiteType));
2636 }
2637
2638 ir.Primitive buildTypeExpression(DartType type) {
2639 type = program.unaliasType(type);
2640 if (type is TypeVariableType) {
2641 return buildTypeVariableAccess(type);
2642 } else if (type is InterfaceType || type is FunctionType) {
2643 List<ir.Primitive> arguments = <ir.Primitive>[];
2644 type.forEachTypeVariable((TypeVariableType variable) {
2645 ir.Primitive value = buildTypeVariableAccess(variable);
2646 arguments.add(value);
2647 });
2648 return addPrimitive(new ir.TypeExpression(
2649 ir.TypeExpressionKind.COMPLETE, type, arguments));
2650 } else if (type.treatAsDynamic) {
2651 return buildNullConstant();
2652 } else {
2653 // TypedefType can reach here, and possibly other things.
2654 throw 'unimplemented translation of type expression $type (${type.kind})';
2655 }
2656 }
2657
2658 /// Obtains the internal type representation of the type held in [variable].
2659 ///
2660 /// The value of [variable] is taken from the current receiver object, or
2661 /// if we are currently building a constructor field initializer, from the
2662 /// corresponding type argument (field initializers are evaluated before the
2663 /// receiver object is created).
2664 ir.Primitive buildTypeVariableAccess(TypeVariableType variable,
2665 {SourceInformation sourceInformation}) {
2666 // If the local exists in the environment, use that.
2667 // This is put here when we are inside a constructor or field initializer,
2668 // (or possibly a closure inside one of these).
2669 Local local = new closure.TypeVariableLocal(variable, state.currentElement);
2670 if (environment.contains(local)) {
2671 return environment.lookup(local);
2672 }
2673
2674 // If the type variable is not in a local, read its value from the
2675 // receiver object.
2676 ir.Primitive target = buildThis();
2677 return addPrimitive(
2678 new ir.ReadTypeVariable(variable, target, sourceInformation));
2679 }
2680
2681 /// Make the given type variable accessible through the local environment
2682 /// with the value of [binding].
2683 void declareTypeVariable(TypeVariableType variable, DartType binding) {
2684 environment.extend(
2685 new closure.TypeVariableLocal(variable, state.currentElement),
2686 buildTypeExpression(binding));
2687 }
2688
2689 /// Reifies the value of [variable] on the current receiver object.
2690 ir.Primitive buildReifyTypeVariable(
2691 TypeVariableType variable, SourceInformation sourceInformation) {
2692 ir.Primitive typeArgument =
2693 buildTypeVariableAccess(variable, sourceInformation: sourceInformation);
2694 return addPrimitive(
2695 new ir.ReifyRuntimeType(typeArgument, sourceInformation));
2696 }
2697
2698 ir.Primitive buildInvocationMirror(
2699 Selector selector, List<ir.Primitive> arguments) {
2700 return addPrimitive(new ir.CreateInvocationMirror(selector, arguments));
2701 }
2702
2703 ir.Primitive buildForeignCode(
2704 js.Template codeTemplate,
2705 List<ir.Primitive> arguments,
2706 NativeBehavior behavior,
2707 SourceInformation sourceInformation,
2708 {Element dependency,
2709 TypeMask type}) {
2710 assert(behavior != null);
2711 if (type == null) {
2712 type = program.getTypeMaskForForeign(behavior);
2713 }
2714 if (js.isIdentityTemplate(codeTemplate) && !program.isArrayType(type)) {
2715 // JS expression is just a refinement.
2716 // Do not do this for arrays - those are special because array types can
2717 // change after creation. The input and output must therefore be modeled
2718 // as distinct values.
2719 return addPrimitive(new ir.Refinement(arguments.single, type));
2720 }
2721 ir.Primitive result = addPrimitive(new ir.ForeignCode(
2722 codeTemplate, type, arguments, behavior, sourceInformation,
2723 dependency: dependency));
2724 if (!codeTemplate.isExpression) {
2725 // Close the term if this is a "throw" expression or native body.
2726 add(new ir.Unreachable());
2727 _current = null;
2728 }
2729 return result;
2730 }
2731
2732 /// Creates a type test or type cast of [value] against [type].
2733 ir.Primitive buildTypeOperator(
2734 ir.Primitive value, DartType type, SourceInformation sourceInformation,
2735 {bool isTypeTest}) {
2736 assert(isOpen);
2737 assert(isTypeTest != null);
2738
2739 type = program.unaliasType(type);
2740
2741 if (type.isMalformed) {
2742 String message;
2743 if (type is MalformedType) {
2744 ErroneousElement element = type.element;
2745 message = element.message;
2746 } else {
2747 assert(type is MethodTypeVariableType);
2748 message = "Method type variables are not reified, "
2749 "so they cannot be tested dynamically";
2750 }
2751 ir.Primitive irMessage = buildStringConstant(message);
2752 return buildStaticFunctionInvocation(program.throwTypeErrorHelper,
2753 <ir.Primitive>[irMessage], sourceInformation);
2754 }
2755
2756 List<ir.Primitive> typeArguments = const <ir.Primitive>[];
2757 if (type is GenericType && type.typeArguments.isNotEmpty) {
2758 typeArguments = type.typeArguments.map(buildTypeExpression).toList();
2759 } else if (type is TypeVariableType) {
2760 typeArguments = <ir.Primitive>[buildTypeVariableAccess(type)];
2761 } else if (type is FunctionType) {
2762 typeArguments = <ir.Primitive>[buildTypeExpression(type)];
2763 }
2764
2765 if (isTypeTest) {
2766 // For type tests, we must treat specially the rare cases where `null`
2767 // satisfies the test (which otherwise never satisfies a type test).
2768 // This is not an optimization: the TypeOperator assumes that `null`
2769 // cannot satisfy the type test unless the type is a type variable.
2770 if (type.isObject || type.isDynamic) {
2771 // `x is Object` and `x is dynamic` are always true, even if x is null.
2772 return buildBooleanConstant(true);
2773 }
2774 if (type is InterfaceType && type.element == program.nullClass) {
2775 // `x is Null` is true if and only if x is null.
2776 return _buildCheckNull(value, sourceInformation);
2777 }
2778 return addPrimitive(new ir.TypeTest(value, type, typeArguments));
2779 } else {
2780 if (type.isObject || type.isDynamic) {
2781 // `x as Object` and `x as dynamic` are the same as `x`.
2782 return value;
2783 }
2784 return addPrimitive(new ir.TypeCast(value, type, typeArguments));
2785 }
2786 }
2787
2788 /// Create an if-null expression. This is equivalent to a conditional
2789 /// expression whose result is either [value] if [value] is not null, or
2790 /// `right` if [value] is null. Only when [value] is null, [buildRight] is
2791 /// evaluated to produce the `right` value.
2792 ir.Primitive buildIfNull(
2793 ir.Primitive value,
2794 ir.Primitive buildRight(IrBuilder builder),
2795 SourceInformation sourceInformation) {
2796 ir.Primitive condition = _buildCheckNull(value, sourceInformation);
2797 return buildConditional(
2798 condition, buildRight, (_) => value, sourceInformation);
2799 }
2800
2801 /// Create a conditional send. This is equivalent to a conditional expression
2802 /// that checks if [receiver] is null, if so, it returns null, otherwise it
2803 /// evaluates the [buildSend] expression.
2804 ir.Primitive buildIfNotNullSend(
2805 ir.Primitive receiver,
2806 ir.Primitive buildSend(IrBuilder builder),
2807 SourceInformation sourceInformation) {
2808 ir.Primitive condition = _buildCheckNull(receiver, sourceInformation);
2809 return buildConditional(
2810 condition, (_) => receiver, buildSend, sourceInformation);
2811 }
2812
2813 /// Creates a type test checking whether [value] is null.
2814 ir.Primitive _buildCheckNull(
2815 ir.Primitive value, SourceInformation sourceInformation) {
2816 assert(isOpen);
2817 return buildIdentical(value, buildNullConstant(),
2818 sourceInformation: sourceInformation);
2819 }
2820 }
2821
2822 /// Location of a variable relative to a given closure.
2823 class ClosureLocation {
2824 /// If not `null`, this location is [box].[field].
2825 /// The location of [box] can be obtained separately from an
2826 /// enclosing [ClosureEnvironment] or [ClosureScope].
2827 /// If `null`, then the location is [field] on the enclosing function object.
2828 final closure.BoxLocal box;
2829
2830 /// The field in which the variable is stored.
2831 final Entity field;
2832
2833 bool get isBox => box != null;
2834
2835 ClosureLocation(this.box, this.field);
2836
2837 /// Converts a map containing closure.dart's [CapturedVariable]s into one
2838 /// containing [ClosureLocation]s.
2839 ///
2840 /// There is a 1:1 corresponce between these; we do this because the
2841 /// IR builder should not depend on synthetic elements.
2842 static Map<Local, ClosureLocation> mapFrom(
2843 Map<Local, closure.CapturedVariable> map) {
2844 Map result = {};
2845 map.forEach((Local k, closure.CapturedVariable v) {
2846 closure.BoxLocal box = v is closure.BoxFieldElement ? v.box : null;
2847 result[k] = new ClosureLocation(box, v);
2848 });
2849 return result;
2850 }
2851 }
2852
2853 /// Introduces a new box and binds local variables to this box.
2854 ///
2855 /// A [ClosureScope] may exist for each function and for each loop.
2856 /// Generally, one may pass `null` to the [IrBuilder] instead of a
2857 /// [ClosureScope] when a given scope has no boxed variables.
2858 class ClosureScope {
2859 /// This box is now in scope and [capturedVariables] may use it.
2860 final closure.BoxLocal box;
2861
2862 /// Maps [LocalElement]s to their location.
2863 final Map<Local, ClosureLocation> capturedVariables;
2864
2865 /// If this is the scope of a for-loop, [boxedLoopVariables] is the list
2866 /// of boxed variables that are declared in the initializer.
2867 final List<VariableElement> boxedLoopVariables;
2868
2869 factory ClosureScope(closure.ClosureScope scope) {
2870 return scope == null ? null : new ClosureScope._internal(scope);
2871 }
2872
2873 ClosureScope._internal(closure.ClosureScope scope)
2874 : box = scope.boxElement,
2875 capturedVariables = ClosureLocation.mapFrom(scope.capturedVariables),
2876 boxedLoopVariables = scope.boxedLoopVariables;
2877 }
2878
2879 /// Environment passed when building a nested function, describing how
2880 /// to access variables from the enclosing scope.
2881 class ClosureEnvironment {
2882 /// References to this local should be treated as recursive self-reference.
2883 /// (This is *not* in [freeVariables]).
2884 final LocalFunctionElement selfReference;
2885
2886 /// If non-null, [thisLocal] has an entry in [freeVariables] describing where
2887 /// to find the captured value of `this`.
2888 final closure.ThisLocal thisLocal;
2889
2890 /// Maps [LocalElement]s, [BoxLocal]s and [ThisLocal] to their location.
2891 final Map<Local, ClosureLocation> freeVariables;
2892
2893 factory ClosureEnvironment(closure.ClosureClassMap closureClassMap) {
2894 if (closureClassMap.closureElement == null) return null;
2895 return new ClosureEnvironment._internal(closureClassMap);
2896 }
2897
2898 ClosureEnvironment._internal(closure.ClosureClassMap closureClassMap)
2899 : selfReference = closureClassMap.closureElement,
2900 thisLocal = closureClassMap.thisLocal,
2901 freeVariables =
2902 ClosureLocation.mapFrom(closureClassMap.freeVariableMap);
2903 }
2904
2905 class TryStatementInfo {
2906 final Set<LocalVariableElement> declared = new Set<LocalVariableElement>();
2907 final Set<LocalVariableElement> boxedOnEntry =
2908 new Set<LocalVariableElement>();
2909 }
2910
2911 class CatchClauseInfo {
2912 final DartType type;
2913 final LocalVariableElement exceptionVariable;
2914 final LocalVariableElement stackTraceVariable;
2915 final SubbuildFunction buildCatchBlock;
2916 final SourceInformation sourceInformation;
2917
2918 CatchClauseInfo(
2919 {this.type,
2920 this.exceptionVariable,
2921 this.stackTraceVariable,
2922 this.buildCatchBlock,
2923 this.sourceInformation});
2924 }
2925
2926 class SwitchCaseInfo {
2927 final SubbuildFunction buildCondition;
2928 final SubbuildFunction buildBody;
2929 final SourceInformation sourceInformation;
2930
2931 SwitchCaseInfo(this.buildCondition, this.buildBody, this.sourceInformation);
2932 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/cps_ir/cps_fragment.dart ('k') | pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698