| OLD | NEW |
| (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 } | |
| OLD | NEW |