| 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 part of dart2js.ir_builder; | |
| 6 | |
| 7 /** | |
| 8 * This task iterates through all resolved elements and builds [ir.Node]s. The | |
| 9 * nodes are stored in the [nodes] map and accessible through [hasIr] and | |
| 10 * [getIr]. | |
| 11 * | |
| 12 * The functionality of the IrNodes is added gradually, therefore elements might | |
| 13 * have an IR or not, depending on the language features that are used. For | |
| 14 * elements that do have an IR, the tree [ast.Node]s and the [Token]s are not | |
| 15 * used in the rest of the compilation. This is ensured by setting the element's | |
| 16 * cached tree to `null` and also breaking the token stream to crash future | |
| 17 * attempts to parse. | |
| 18 * | |
| 19 * The type inferrer works on either IR nodes or tree nodes. The IR nodes are | |
| 20 * then translated into the SSA form for optimizations and code generation. | |
| 21 * Long-term, once the IR supports the full language, the backend can be | |
| 22 * re-implemented to work directly on the IR. | |
| 23 */ | |
| 24 class IrBuilderTask extends CompilerTask { | |
| 25 final Map<Element, ir.ExecutableDefinition> nodes = | |
| 26 <Element, ir.ExecutableDefinition>{}; | |
| 27 final bool generateSourceMap; | |
| 28 | |
| 29 IrBuilderTask(Compiler compiler, {this.generateSourceMap: true}) | |
| 30 : super(compiler); | |
| 31 | |
| 32 String get name => 'IR builder'; | |
| 33 | |
| 34 bool hasIr(Element element) => nodes.containsKey(element.implementation); | |
| 35 | |
| 36 ir.ExecutableDefinition getIr(ExecutableElement element) { | |
| 37 return nodes[element.implementation]; | |
| 38 } | |
| 39 | |
| 40 ir.ExecutableDefinition buildNode(AstElement element) { | |
| 41 if (!canBuild(element)) return null; | |
| 42 | |
| 43 TreeElements elementsMapping = element.resolvedAst.elements; | |
| 44 element = element.implementation; | |
| 45 return compiler.withCurrentElement(element, () { | |
| 46 SourceInformationBuilder sourceInformationBuilder = generateSourceMap | |
| 47 ? new PositionSourceInformationBuilder(element) | |
| 48 : const SourceInformationBuilder(); | |
| 49 | |
| 50 IrBuilderVisitor builder = | |
| 51 compiler.backend is JavaScriptBackend | |
| 52 ? new JsIrBuilderVisitor( | |
| 53 elementsMapping, compiler, sourceInformationBuilder) | |
| 54 : new DartIrBuilderVisitor( | |
| 55 elementsMapping, compiler, sourceInformationBuilder); | |
| 56 ir.ExecutableDefinition definition = | |
| 57 builder.buildExecutable(element); | |
| 58 if (definition != null) { | |
| 59 nodes[element] = definition; | |
| 60 } | |
| 61 return definition; | |
| 62 }); | |
| 63 } | |
| 64 | |
| 65 void buildNodes() { | |
| 66 measure(() { | |
| 67 Set<Element> resolved = compiler.enqueuer.resolution.resolvedElements; | |
| 68 resolved.forEach(buildNode); | |
| 69 }); | |
| 70 } | |
| 71 | |
| 72 bool canBuild(Element element) { | |
| 73 if (element is TypedefElement) return false; | |
| 74 if (element is FunctionElement) { | |
| 75 // TODO(sigurdm): Support native functions for dart2js. | |
| 76 assert(invariant(element, !element.isNative)); | |
| 77 | |
| 78 if (element is ConstructorElement) { | |
| 79 if (!element.isGenerativeConstructor) { | |
| 80 // TODO(kmillikin,sigurdm): Support constructors. | |
| 81 return false; | |
| 82 } | |
| 83 if (element.isSynthesized) { | |
| 84 // Do generate CPS for synthetic constructors. | |
| 85 return true; | |
| 86 } | |
| 87 } | |
| 88 } else if (element is! FieldElement) { | |
| 89 compiler.internalError(element, "Unexpected element type $element"); | |
| 90 } | |
| 91 return compiler.backend.shouldOutput(element); | |
| 92 } | |
| 93 | |
| 94 bool get inCheckedMode { | |
| 95 bool result = false; | |
| 96 assert((result = true)); | |
| 97 return result; | |
| 98 } | |
| 99 | |
| 100 } | |
| 101 | |
| 102 class _GetterElements { | |
| 103 ir.Primitive result; | |
| 104 ir.Primitive index; | |
| 105 ir.Primitive receiver; | |
| 106 | |
| 107 _GetterElements({this.result, this.index, this.receiver}) ; | |
| 108 } | |
| 109 | |
| 110 /** | |
| 111 * A tree visitor that builds [IrNodes]. The visit methods add statements using | |
| 112 * to the [builder] and return the last added statement for trees that represent | |
| 113 * an expression. | |
| 114 */ | |
| 115 abstract class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive> | |
| 116 with IrBuilderMixin<ast.Node> { | |
| 117 final Compiler compiler; | |
| 118 final SourceInformationBuilder sourceInformationBuilder; | |
| 119 | |
| 120 // In SSA terms, join-point continuation parameters are the phis and the | |
| 121 // continuation invocation arguments are the corresponding phi inputs. To | |
| 122 // support name introduction and renaming for source level variables, we use | |
| 123 // nested (delimited) visitors for constructing subparts of the IR that will | |
| 124 // need renaming. Each source variable is assigned an index. | |
| 125 // | |
| 126 // Each nested visitor maintains a list of free variable uses in the body. | |
| 127 // These are implemented as a list of parameters, each with their own use | |
| 128 // list of references. When the delimited subexpression is plugged into the | |
| 129 // surrounding context, the free occurrences can be captured or become free | |
| 130 // occurrences in the next outer delimited subexpression. | |
| 131 // | |
| 132 // Each nested visitor maintains a list that maps indexes of variables | |
| 133 // assigned in the delimited subexpression to their reaching definition --- | |
| 134 // that is, the definition in effect at the hole in 'current'. These are | |
| 135 // used to determine if a join-point continuation needs to be passed | |
| 136 // arguments, and what the arguments are. | |
| 137 | |
| 138 /// Construct a top-level visitor. | |
| 139 IrBuilderVisitor(TreeElements elements, | |
| 140 this.compiler, | |
| 141 this.sourceInformationBuilder) | |
| 142 : super(elements); | |
| 143 | |
| 144 /** | |
| 145 * Builds the [ir.ExecutableDefinition] for an executable element. In case the | |
| 146 * function uses features that cannot be expressed in the IR, this element | |
| 147 * returns `null`. | |
| 148 */ | |
| 149 ir.ExecutableDefinition buildExecutable(ExecutableElement element); | |
| 150 | |
| 151 ClosureScope getClosureScopeForNode(ast.Node node); | |
| 152 ClosureEnvironment getClosureEnvironment(); | |
| 153 | |
| 154 /// Normalizes the argument list to a static invocation (i.e. where the target | |
| 155 /// element is known). | |
| 156 /// | |
| 157 /// For the JS backend, inserts default arguments and normalizes order of | |
| 158 /// named arguments. | |
| 159 /// | |
| 160 /// For the Dart backend, returns [arguments]. | |
| 161 List<ir.Primitive> normalizeStaticArguments( | |
| 162 Selector selector, | |
| 163 FunctionElement target, | |
| 164 List<ir.Primitive> arguments); | |
| 165 | |
| 166 /// Normalizes the argument list of a dynamic invocation (i.e. where the | |
| 167 /// target element is unknown). | |
| 168 /// | |
| 169 /// For the JS backend, normalizes order of named arguments. | |
| 170 /// | |
| 171 /// For the Dart backend, returns [arguments]. | |
| 172 List<ir.Primitive> normalizeDynamicArguments( | |
| 173 Selector selector, | |
| 174 List<ir.Primitive> arguments); | |
| 175 | |
| 176 ir.FunctionDefinition _makeFunctionBody(FunctionElement element, | |
| 177 ast.FunctionExpression node) { | |
| 178 FunctionSignature signature = element.functionSignature; | |
| 179 List<ParameterElement> parameters = []; | |
| 180 signature.orderedForEachParameter(parameters.add); | |
| 181 | |
| 182 irBuilder.buildFunctionHeader(parameters, | |
| 183 closureScope: getClosureScopeForNode(node), | |
| 184 env: getClosureEnvironment()); | |
| 185 | |
| 186 List<ConstantExpression> defaults = new List<ConstantExpression>(); | |
| 187 signature.orderedOptionalParameters.forEach((ParameterElement element) { | |
| 188 defaults.add(getConstantForVariable(element)); | |
| 189 }); | |
| 190 | |
| 191 List<ir.Initializer> initializers; | |
| 192 if (element.isSynthesized) { | |
| 193 assert(element is ConstructorElement); | |
| 194 return irBuilder.makeConstructorDefinition(const <ConstantExpression>[], | |
| 195 const <ir.Initializer>[]); | |
| 196 } else if (element.isGenerativeConstructor) { | |
| 197 if (element.isExternal) { | |
| 198 return irBuilder.makeAbstractConstructorDefinition(defaults); | |
| 199 } else { | |
| 200 initializers = buildConstructorInitializers(node, element); | |
| 201 visit(node.body); | |
| 202 return irBuilder.makeConstructorDefinition(defaults, initializers); | |
| 203 } | |
| 204 } else { | |
| 205 visit(node.body); | |
| 206 return irBuilder.makeFunctionDefinition(defaults); | |
| 207 } | |
| 208 } | |
| 209 | |
| 210 List<ir.Initializer> buildConstructorInitializers( | |
| 211 ast.FunctionExpression function, ConstructorElement element) { | |
| 212 List<ir.Initializer> result = <ir.Initializer>[]; | |
| 213 FunctionSignature signature = element.functionSignature; | |
| 214 | |
| 215 void tryAddInitializingFormal(ParameterElement parameterElement) { | |
| 216 if (parameterElement.isInitializingFormal) { | |
| 217 InitializingFormalElement initializingFormal = parameterElement; | |
| 218 withBuilder(irBuilder.makeInitializerBuilder(), () { | |
| 219 ir.Primitive value = irBuilder.buildLocalGet(parameterElement); | |
| 220 result.add(irBuilder.makeFieldInitializer( | |
| 221 initializingFormal.fieldElement, | |
| 222 irBuilder.makeRunnableBody(value))); | |
| 223 }); | |
| 224 } | |
| 225 } | |
| 226 | |
| 227 // TODO(sigurdm): Preserve initializing formals as initializing formals. | |
| 228 signature.orderedForEachParameter(tryAddInitializingFormal); | |
| 229 | |
| 230 if (function.initializers == null) return result; | |
| 231 bool explicitSuperInitializer = false; | |
| 232 for(ast.Node initializer in function.initializers) { | |
| 233 if (initializer is ast.SendSet) { | |
| 234 // Field initializer. | |
| 235 FieldElement field = elements[initializer]; | |
| 236 withBuilder(irBuilder.makeInitializerBuilder(), () { | |
| 237 ir.Primitive value = visit(initializer.arguments.head); | |
| 238 ir.RunnableBody body = irBuilder.makeRunnableBody(value); | |
| 239 result.add(irBuilder.makeFieldInitializer(field, body)); | |
| 240 }); | |
| 241 } else if (initializer is ast.Send) { | |
| 242 // Super or this initializer. | |
| 243 if (ast.Initializers.isConstructorRedirect(initializer)) { | |
| 244 giveup(initializer, "constructor redirect (this) initializer"); | |
| 245 } | |
| 246 ConstructorElement constructor = elements[initializer].implementation; | |
| 247 Selector selector = elements.getSelector(initializer); | |
| 248 List<ir.RunnableBody> arguments = | |
| 249 initializer.arguments.mapToList((ast.Node argument) { | |
| 250 return withBuilder(irBuilder.makeInitializerBuilder(), () { | |
| 251 ir.Primitive value = visit(argument); | |
| 252 return irBuilder.makeRunnableBody(value); | |
| 253 }); | |
| 254 }); | |
| 255 result.add(irBuilder.makeSuperInitializer(constructor, | |
| 256 arguments, | |
| 257 selector)); | |
| 258 explicitSuperInitializer = true; | |
| 259 } else { | |
| 260 compiler.internalError(initializer, | |
| 261 "Unexpected initializer type $initializer"); | |
| 262 } | |
| 263 | |
| 264 } | |
| 265 if (!explicitSuperInitializer) { | |
| 266 // No super initializer found. Try to find the default constructor if | |
| 267 // the class is not Object. | |
| 268 ClassElement enclosingClass = element.enclosingClass; | |
| 269 if (!enclosingClass.isObject) { | |
| 270 ClassElement superClass = enclosingClass.superclass; | |
| 271 FunctionElement target = superClass.lookupDefaultConstructor(); | |
| 272 if (target == null) { | |
| 273 compiler.internalError(superClass, | |
| 274 "No default constructor available."); | |
| 275 } | |
| 276 Selector selector = new Selector.callDefaultConstructor(); | |
| 277 result.add(irBuilder.makeSuperInitializer(target, | |
| 278 <ir.RunnableBody>[], | |
| 279 selector)); | |
| 280 } | |
| 281 } | |
| 282 return result; | |
| 283 } | |
| 284 | |
| 285 ir.Primitive visit(ast.Node node) => node.accept(this); | |
| 286 | |
| 287 // ==== Statements ==== | |
| 288 visitBlock(ast.Block node) { | |
| 289 irBuilder.buildBlock(node.statements.nodes, build); | |
| 290 } | |
| 291 | |
| 292 ir.Primitive visitBreakStatement(ast.BreakStatement node) { | |
| 293 if (!irBuilder.buildBreak(elements.getTargetOf(node))) { | |
| 294 compiler.internalError(node, "'break' target not found"); | |
| 295 } | |
| 296 return null; | |
| 297 } | |
| 298 | |
| 299 ir.Primitive visitContinueStatement(ast.ContinueStatement node) { | |
| 300 if (!irBuilder.buildContinue(elements.getTargetOf(node))) { | |
| 301 compiler.internalError(node, "'continue' target not found"); | |
| 302 } | |
| 303 return null; | |
| 304 } | |
| 305 | |
| 306 // Build(EmptyStatement, C) = C | |
| 307 ir.Primitive visitEmptyStatement(ast.EmptyStatement node) { | |
| 308 assert(irBuilder.isOpen); | |
| 309 return null; | |
| 310 } | |
| 311 | |
| 312 // Build(ExpressionStatement(e), C) = C' | |
| 313 // where (C', _) = Build(e, C) | |
| 314 ir.Primitive visitExpressionStatement(ast.ExpressionStatement node) { | |
| 315 assert(irBuilder.isOpen); | |
| 316 visit(node.expression); | |
| 317 return null; | |
| 318 } | |
| 319 | |
| 320 visitFor(ast.For node) { | |
| 321 List<LocalElement> loopVariables = <LocalElement>[]; | |
| 322 if (node.initializer is ast.VariableDefinitions) { | |
| 323 ast.VariableDefinitions definitions = node.initializer; | |
| 324 for (ast.Node node in definitions.definitions.nodes) { | |
| 325 LocalElement loopVariable = elements[node]; | |
| 326 loopVariables.add(loopVariable); | |
| 327 } | |
| 328 } | |
| 329 | |
| 330 JumpTarget target = elements.getTargetDefinition(node); | |
| 331 irBuilder.buildFor( | |
| 332 buildInitializer: subbuild(node.initializer), | |
| 333 buildCondition: subbuild(node.condition), | |
| 334 buildBody: subbuild(node.body), | |
| 335 buildUpdate: subbuildSequence(node.update), | |
| 336 closureScope: getClosureScopeForNode(node), | |
| 337 loopVariables: loopVariables, | |
| 338 target: target); | |
| 339 } | |
| 340 | |
| 341 visitIf(ast.If node) { | |
| 342 irBuilder.buildIf( | |
| 343 build(node.condition), | |
| 344 subbuild(node.thenPart), | |
| 345 subbuild(node.elsePart)); | |
| 346 } | |
| 347 | |
| 348 ir.Primitive visitLabeledStatement(ast.LabeledStatement node) { | |
| 349 ast.Statement body = node.statement; | |
| 350 if (body is ast.Loop) return visit(body); | |
| 351 JumpTarget target = elements.getTargetDefinition(body); | |
| 352 JumpCollector jumps = new JumpCollector(target); | |
| 353 irBuilder.state.breakCollectors.add(jumps); | |
| 354 IrBuilder innerBuilder = irBuilder.makeDelimitedBuilder(); | |
| 355 withBuilder(innerBuilder, () { | |
| 356 visit(body); | |
| 357 }); | |
| 358 irBuilder.state.breakCollectors.removeLast(); | |
| 359 bool hasBreaks = !jumps.isEmpty; | |
| 360 ir.Continuation joinContinuation; | |
| 361 if (hasBreaks) { | |
| 362 if (innerBuilder.isOpen) { | |
| 363 jumps.addJump(innerBuilder); | |
| 364 } | |
| 365 | |
| 366 // All jumps to the break continuation must be in the scope of the | |
| 367 // continuation's binding. The continuation is bound just outside the | |
| 368 // body to satisfy this property without extra analysis. | |
| 369 // As a consequence, the break continuation needs parameters for all | |
| 370 // local variables in scope at the exit from the body. | |
| 371 List<ir.Parameter> parameters = | |
| 372 new List<ir.Parameter>.generate(irBuilder.environment.length, (i) { | |
| 373 return new ir.Parameter(irBuilder.environment.index2variable[i]); | |
| 374 }); | |
| 375 joinContinuation = new ir.Continuation(parameters); | |
| 376 irBuilder.invokeFullJoin(joinContinuation, jumps, recursive: false); | |
| 377 irBuilder.add(new ir.LetCont(joinContinuation, | |
| 378 innerBuilder._root)); | |
| 379 for (int i = 0; i < irBuilder.environment.length; ++i) { | |
| 380 irBuilder.environment.index2value[i] = parameters[i]; | |
| 381 } | |
| 382 } else { | |
| 383 if (innerBuilder._root != null) { | |
| 384 irBuilder.add(innerBuilder._root); | |
| 385 irBuilder._current = innerBuilder._current; | |
| 386 irBuilder.environment = innerBuilder.environment; | |
| 387 } | |
| 388 } | |
| 389 return null; | |
| 390 } | |
| 391 | |
| 392 visitWhile(ast.While node) { | |
| 393 irBuilder.buildWhile( | |
| 394 buildCondition: subbuild(node.condition), | |
| 395 buildBody: subbuild(node.body), | |
| 396 target: elements.getTargetDefinition(node), | |
| 397 closureScope: getClosureScopeForNode(node)); | |
| 398 } | |
| 399 | |
| 400 visitForIn(ast.ForIn node) { | |
| 401 // [node.declaredIdentifier] can be either an [ast.VariableDefinitions] | |
| 402 // (defining a new local variable) or a send designating some existing | |
| 403 // variable. | |
| 404 ast.Node identifier = node.declaredIdentifier; | |
| 405 ast.VariableDefinitions variableDeclaration = | |
| 406 identifier.asVariableDefinitions(); | |
| 407 Element variableElement = elements.getForInVariable(node); | |
| 408 Selector selector = elements.getSelector(identifier); | |
| 409 | |
| 410 irBuilder.buildForIn( | |
| 411 buildExpression: subbuild(node.expression), | |
| 412 buildVariableDeclaration: subbuild(variableDeclaration), | |
| 413 variableElement: variableElement, | |
| 414 variableSelector: selector, | |
| 415 buildBody: subbuild(node.body), | |
| 416 target: elements.getTargetDefinition(node), | |
| 417 closureScope: getClosureScopeForNode(node)); | |
| 418 } | |
| 419 | |
| 420 ir.Primitive visitVariableDefinitions(ast.VariableDefinitions node) { | |
| 421 assert(irBuilder.isOpen); | |
| 422 if (node.modifiers.isConst) { | |
| 423 for (ast.SendSet definition in node.definitions.nodes) { | |
| 424 assert(!definition.arguments.isEmpty); | |
| 425 assert(definition.arguments.tail.isEmpty); | |
| 426 VariableElement element = elements[definition]; | |
| 427 ConstantExpression value = getConstantForVariable(element); | |
| 428 irBuilder.declareLocalConstant(element, value); | |
| 429 } | |
| 430 } else { | |
| 431 for (ast.Node definition in node.definitions.nodes) { | |
| 432 Element element = elements[definition]; | |
| 433 ir.Primitive initialValue; | |
| 434 // Definitions are either SendSets if there is an initializer, or | |
| 435 // Identifiers if there is no initializer. | |
| 436 if (definition is ast.SendSet) { | |
| 437 assert(!definition.arguments.isEmpty); | |
| 438 assert(definition.arguments.tail.isEmpty); | |
| 439 initialValue = visit(definition.arguments.head); | |
| 440 } else { | |
| 441 assert(definition is ast.Identifier); | |
| 442 } | |
| 443 irBuilder.declareLocalVariable(element, initialValue: initialValue); | |
| 444 } | |
| 445 } | |
| 446 return null; | |
| 447 } | |
| 448 | |
| 449 // Build(Return(e), C) = C'[InvokeContinuation(return, x)] | |
| 450 // where (C', x) = Build(e, C) | |
| 451 // | |
| 452 // Return without a subexpression is translated as if it were return null. | |
| 453 ir.Primitive visitReturn(ast.Return node) { | |
| 454 assert(irBuilder.isOpen); | |
| 455 assert(invariant(node, node.beginToken.value != 'native')); | |
| 456 irBuilder.buildReturn(build(node.expression)); | |
| 457 return null; | |
| 458 } | |
| 459 | |
| 460 visitTryStatement(ast.TryStatement node) { | |
| 461 // Try/catch is not yet implemented in the JS backend. | |
| 462 if (this.irBuilder.tryStatements == null) { | |
| 463 return giveup(node, 'try/catch in the JS backend'); | |
| 464 } | |
| 465 // Multiple catch blocks are not yet implemented. | |
| 466 if (node.catchBlocks.isEmpty || | |
| 467 node.catchBlocks.nodes.tail == null) { | |
| 468 return giveup(node, 'not exactly one catch block'); | |
| 469 } | |
| 470 // 'on T' catch blocks are not yet implemented. | |
| 471 if ((node.catchBlocks.nodes.head as ast.CatchBlock).onKeyword != null) { | |
| 472 return giveup(node, '"on T" catch block'); | |
| 473 } | |
| 474 // Finally blocks are not yet implemented. | |
| 475 if (node.finallyBlock != null) { | |
| 476 return giveup(node, 'try/finally'); | |
| 477 } | |
| 478 | |
| 479 List<CatchClauseInfo> catchClauseInfos = <CatchClauseInfo>[]; | |
| 480 for (ast.CatchBlock catchClause in node.catchBlocks.nodes) { | |
| 481 assert(catchClause.exception != null); | |
| 482 LocalVariableElement exceptionVariable = elements[catchClause.exception]; | |
| 483 LocalVariableElement stackTraceVariable; | |
| 484 if (catchClause.trace != null) { | |
| 485 stackTraceVariable = elements[catchClause.trace]; | |
| 486 } | |
| 487 catchClauseInfos.add(new CatchClauseInfo( | |
| 488 exceptionVariable: exceptionVariable, | |
| 489 stackTraceVariable: stackTraceVariable, | |
| 490 buildCatchBlock: subbuild(catchClause.block))); | |
| 491 } | |
| 492 | |
| 493 irBuilder.buildTry( | |
| 494 tryStatementInfo: irBuilder.tryStatements[node], | |
| 495 buildTryBlock: subbuild(node.tryBlock), | |
| 496 catchClauseInfos: catchClauseInfos); | |
| 497 } | |
| 498 | |
| 499 // ==== Expressions ==== | |
| 500 ir.Primitive visitConditional(ast.Conditional node) { | |
| 501 return irBuilder.buildConditional( | |
| 502 build(node.condition), | |
| 503 subbuild(node.thenExpression), | |
| 504 subbuild(node.elseExpression)); | |
| 505 } | |
| 506 | |
| 507 // For all simple literals: | |
| 508 // Build(Literal(c), C) = C[let val x = Constant(c) in [], x] | |
| 509 ir.Primitive visitLiteralBool(ast.LiteralBool node) { | |
| 510 assert(irBuilder.isOpen); | |
| 511 return translateConstant(node); | |
| 512 } | |
| 513 | |
| 514 ir.Primitive visitLiteralDouble(ast.LiteralDouble node) { | |
| 515 assert(irBuilder.isOpen); | |
| 516 return translateConstant(node); | |
| 517 } | |
| 518 | |
| 519 ir.Primitive visitLiteralInt(ast.LiteralInt node) { | |
| 520 assert(irBuilder.isOpen); | |
| 521 return translateConstant(node); | |
| 522 } | |
| 523 | |
| 524 ir.Primitive visitLiteralNull(ast.LiteralNull node) { | |
| 525 assert(irBuilder.isOpen); | |
| 526 return translateConstant(node); | |
| 527 } | |
| 528 | |
| 529 ir.Primitive visitLiteralString(ast.LiteralString node) { | |
| 530 assert(irBuilder.isOpen); | |
| 531 return translateConstant(node); | |
| 532 } | |
| 533 | |
| 534 ConstantExpression getConstantForNode(ast.Node node) { | |
| 535 ConstantExpression constant = | |
| 536 compiler.backend.constantCompilerTask.compileNode(node, elements); | |
| 537 assert(invariant(node, constant != null, | |
| 538 message: 'No constant computed for $node')); | |
| 539 return constant; | |
| 540 } | |
| 541 | |
| 542 ConstantExpression getConstantForVariable(VariableElement element) { | |
| 543 ConstantExpression constant = | |
| 544 compiler.backend.constants.getConstantForVariable(element); | |
| 545 assert(invariant(element, constant != null, | |
| 546 message: 'No constant computed for $element')); | |
| 547 return constant; | |
| 548 } | |
| 549 | |
| 550 ir.Primitive visitLiteralList(ast.LiteralList node) { | |
| 551 if (node.isConst) { | |
| 552 return translateConstant(node); | |
| 553 } | |
| 554 List<ir.Primitive> values = node.elements.nodes.mapToList(visit); | |
| 555 InterfaceType type = elements.getType(node); | |
| 556 return irBuilder.buildListLiteral(type, values); | |
| 557 } | |
| 558 | |
| 559 ir.Primitive visitLiteralMap(ast.LiteralMap node) { | |
| 560 if (node.isConst) { | |
| 561 return translateConstant(node); | |
| 562 } | |
| 563 InterfaceType type = elements.getType(node); | |
| 564 return irBuilder.buildMapLiteral( | |
| 565 type, | |
| 566 node.entries.nodes.map((e) => e.key), | |
| 567 node.entries.nodes.map((e) => e.value), | |
| 568 build); | |
| 569 } | |
| 570 | |
| 571 ir.Primitive visitLiteralSymbol(ast.LiteralSymbol node) { | |
| 572 assert(irBuilder.isOpen); | |
| 573 return translateConstant(node); | |
| 574 } | |
| 575 | |
| 576 ir.Primitive visitIdentifier(ast.Identifier node) { | |
| 577 // "this" is the only identifier that should be met by the visitor. | |
| 578 assert(node.isThis()); | |
| 579 return irBuilder.buildThis(); | |
| 580 } | |
| 581 | |
| 582 ir.Primitive visitParenthesizedExpression( | |
| 583 ast.ParenthesizedExpression node) { | |
| 584 assert(irBuilder.isOpen); | |
| 585 return visit(node.expression); | |
| 586 } | |
| 587 | |
| 588 // Stores the result of visiting a CascadeReceiver, so we can return it from | |
| 589 // its enclosing Cascade. | |
| 590 ir.Primitive _currentCascadeReceiver; | |
| 591 | |
| 592 ir.Primitive visitCascadeReceiver(ast.CascadeReceiver node) { | |
| 593 assert(irBuilder.isOpen); | |
| 594 return _currentCascadeReceiver = visit(node.expression); | |
| 595 } | |
| 596 | |
| 597 ir.Primitive visitCascade(ast.Cascade node) { | |
| 598 assert(irBuilder.isOpen); | |
| 599 var oldCascadeReceiver = _currentCascadeReceiver; | |
| 600 // Throw away the result of visiting the expression. | |
| 601 // Instead we return the result of visiting the CascadeReceiver. | |
| 602 this.visit(node.expression); | |
| 603 ir.Primitive receiver = _currentCascadeReceiver; | |
| 604 _currentCascadeReceiver = oldCascadeReceiver; | |
| 605 return receiver; | |
| 606 } | |
| 607 | |
| 608 // ==== Sends ==== | |
| 609 ir.Primitive visitAssert(ast.Send node) { | |
| 610 assert(irBuilder.isOpen); | |
| 611 return giveup(node, 'Assert'); | |
| 612 } | |
| 613 | |
| 614 ir.Primitive visitNamedArgument(ast.NamedArgument node) { | |
| 615 assert(irBuilder.isOpen); | |
| 616 return visit(node.expression); | |
| 617 } | |
| 618 | |
| 619 ir.Primitive visitClosureSend(ast.Send node) { | |
| 620 assert(irBuilder.isOpen); | |
| 621 Element element = elements[node]; | |
| 622 Selector selector = elements.getSelector(node); | |
| 623 ir.Primitive receiver = (element == null) | |
| 624 ? visit(node.selector) | |
| 625 : irBuilder.buildLocalGet(element); | |
| 626 List<ir.Primitive> arguments = node.arguments.mapToList(visit); | |
| 627 arguments = normalizeDynamicArguments(selector, arguments); | |
| 628 return irBuilder.buildCallInvocation(receiver, selector, arguments); | |
| 629 } | |
| 630 | |
| 631 /// If [node] is null, returns this. | |
| 632 /// If [node] is super, returns null (for special handling) | |
| 633 /// Otherwise visits [node] and returns the result. | |
| 634 ir.Primitive visitReceiver(ast.Expression node) { | |
| 635 if (node == null) return irBuilder.buildThis(); | |
| 636 if (node.isSuper()) return null; | |
| 637 return visit(node); | |
| 638 } | |
| 639 | |
| 640 /// Returns `true` if [node] is a super call. | |
| 641 // TODO(johnniwinther): Remove the need for this. | |
| 642 bool isSuperCall(ast.Send node) { | |
| 643 return node != null && node.receiver != null && node.receiver.isSuper(); | |
| 644 } | |
| 645 | |
| 646 ir.Primitive visitDynamicSend(ast.Send node) { | |
| 647 assert(irBuilder.isOpen); | |
| 648 Selector selector = elements.getSelector(node); | |
| 649 ir.Primitive receiver = visitReceiver(node.receiver); | |
| 650 List<ir.Primitive> arguments = node.arguments.mapToList(visit); | |
| 651 arguments = normalizeDynamicArguments(selector, arguments); | |
| 652 return irBuilder.buildDynamicInvocation(receiver, selector, arguments); | |
| 653 } | |
| 654 | |
| 655 _GetterElements translateGetter(ast.Send node, Selector selector) { | |
| 656 Element element = elements[node]; | |
| 657 ir.Primitive result; | |
| 658 ir.Primitive receiver; | |
| 659 ir.Primitive index; | |
| 660 | |
| 661 if (element != null && element.isConst) { | |
| 662 // Reference to constant local, top-level or static field | |
| 663 result = translateConstant(node); | |
| 664 } else if (Elements.isLocal(element)) { | |
| 665 // Reference to local variable | |
| 666 result = irBuilder.buildLocalGet(element); | |
| 667 } else if (element == null || | |
| 668 Elements.isInstanceField(element) || | |
| 669 Elements.isInstanceMethod(element) || | |
| 670 selector.isIndex || | |
| 671 // TODO(johnniwinther): clean up semantics of resolution. | |
| 672 node.isSuperCall) { | |
| 673 // Dynamic dispatch to a getter. Sometimes resolution will suggest a | |
| 674 // target element, but in these cases we must still emit a dynamic | |
| 675 // dispatch. The target element may be an instance method in case we are | |
| 676 // converting a method to a function object. | |
| 677 | |
| 678 receiver = visitReceiver(node.receiver); | |
| 679 List<ir.Primitive> arguments = new List<ir.Primitive>(); | |
| 680 if (selector.isIndex) { | |
| 681 index = visit(node.arguments.head); | |
| 682 arguments.add(index); | |
| 683 } | |
| 684 | |
| 685 assert(selector.kind == SelectorKind.GETTER || | |
| 686 selector.kind == SelectorKind.INDEX); | |
| 687 if (isSuperCall(node)) { | |
| 688 result = irBuilder.buildSuperInvocation(element, selector, arguments); | |
| 689 } else { | |
| 690 result = | |
| 691 irBuilder.buildDynamicInvocation(receiver, selector, arguments); | |
| 692 } | |
| 693 } else if (element.isField || element.isGetter || element.isErroneous || | |
| 694 element.isSetter) { | |
| 695 // TODO(johnniwinther): Change handling of setter selectors. | |
| 696 // Access to a static field or getter (non-static case handled above). | |
| 697 // Even if there is only a setter, we compile as if it was a getter, | |
| 698 // so the vm can fail at runtime. | |
| 699 assert(selector.kind == SelectorKind.GETTER || | |
| 700 selector.kind == SelectorKind.SETTER); | |
| 701 result = irBuilder.buildStaticGet(element, selector, | |
| 702 sourceInformation: sourceInformationBuilder.buildGet(node)); | |
| 703 } else if (Elements.isStaticOrTopLevelFunction(element)) { | |
| 704 // Convert a top-level or static function to a function object. | |
| 705 result = translateConstant(node); | |
| 706 } else { | |
| 707 throw "Unexpected SendSet getter: $node, $element"; | |
| 708 } | |
| 709 return new _GetterElements( | |
| 710 result: result,index: index, receiver: receiver); | |
| 711 } | |
| 712 | |
| 713 ir.Primitive visitGetterSend(ast.Send node) { | |
| 714 assert(irBuilder.isOpen); | |
| 715 return translateGetter(node, elements.getSelector(node)).result; | |
| 716 | |
| 717 } | |
| 718 | |
| 719 ir.Primitive translateLogicalOperator(ast.Operator op, | |
| 720 ast.Expression left, | |
| 721 ast.Expression right) { | |
| 722 ir.Primitive leftValue = visit(left); | |
| 723 | |
| 724 ir.Primitive buildRightValue(IrBuilder rightBuilder) { | |
| 725 return withBuilder(rightBuilder, () => visit(right)); | |
| 726 } | |
| 727 | |
| 728 return irBuilder.buildLogicalOperator( | |
| 729 leftValue, buildRightValue, isLazyOr: op.source == '||'); | |
| 730 } | |
| 731 | |
| 732 ir.Primitive visitOperatorSend(ast.Send node) { | |
| 733 assert(irBuilder.isOpen); | |
| 734 ast.Operator op = node.selector; | |
| 735 if (isUserDefinableOperator(op.source)) { | |
| 736 return visitDynamicSend(node); | |
| 737 } | |
| 738 if (op.source == '&&' || op.source == '||') { | |
| 739 assert(node.receiver != null); | |
| 740 assert(!node.arguments.isEmpty); | |
| 741 assert(node.arguments.tail.isEmpty); | |
| 742 return translateLogicalOperator(op, node.receiver, node.arguments.head); | |
| 743 } | |
| 744 if (op.source == "!") { | |
| 745 assert(node.receiver != null); | |
| 746 assert(node.arguments.isEmpty); | |
| 747 return irBuilder.buildNegation(visit(node.receiver)); | |
| 748 } | |
| 749 if (op.source == "!=") { | |
| 750 assert(node.receiver != null); | |
| 751 assert(!node.arguments.isEmpty); | |
| 752 assert(node.arguments.tail.isEmpty); | |
| 753 return irBuilder.buildNegation(visitDynamicSend(node)); | |
| 754 } | |
| 755 assert(invariant(node, op.source == "is" || op.source == "as", | |
| 756 message: "unexpected operator $op")); | |
| 757 DartType type = elements.getType(node.typeAnnotationFromIsCheckOrCast); | |
| 758 ir.Primitive receiver = visit(node.receiver); | |
| 759 return irBuilder.buildTypeOperator( | |
| 760 receiver, type, | |
| 761 isTypeTest: op.source == "is", | |
| 762 isNotCheck: node.isIsNotCheck); | |
| 763 } | |
| 764 | |
| 765 // Build(StaticSend(f, arguments), C) = C[C'[InvokeStatic(f, xs)]] | |
| 766 // where (C', xs) = arguments.fold(Build, C) | |
| 767 ir.Primitive visitStaticSend(ast.Send node) { | |
| 768 assert(irBuilder.isOpen); | |
| 769 Element element = elements[node]; | |
| 770 assert(!element.isConstructor); | |
| 771 // TODO(lry): support foreign functions. | |
| 772 if (element.isForeign(compiler.backend)) { | |
| 773 return giveup(node, 'StaticSend: foreign'); | |
| 774 } | |
| 775 | |
| 776 Selector selector = elements.getSelector(node); | |
| 777 | |
| 778 if (selector.isCall && (element.isGetter || element.isField)) { | |
| 779 // We are invoking a static field or getter as if it was a method, e.g: | |
| 780 // | |
| 781 // get foo => {..} | |
| 782 // main() { foo(1, 2, 3); } | |
| 783 // | |
| 784 // We invoke the getter of 'foo' and then invoke the 'call' method | |
| 785 // on the result, using the given arguments. | |
| 786 Selector getter = new Selector.getterFrom(selector); | |
| 787 Selector call = new Selector.callClosureFrom(selector); | |
| 788 ir.Primitive receiver = irBuilder.buildStaticGet(element, getter); | |
| 789 List<ir.Primitive> arguments = node.arguments.mapToList(visit); | |
| 790 arguments = normalizeDynamicArguments(selector, arguments); | |
| 791 return irBuilder.buildCallInvocation(receiver, call, arguments); | |
| 792 } else if (selector.isGetter) { | |
| 793 // We are reading a static field or invoking a static getter. | |
| 794 return irBuilder.buildStaticGet(element, selector); | |
| 795 } else { | |
| 796 // We are invoking a static method. | |
| 797 assert(selector.isCall); | |
| 798 assert(element is FunctionElement); | |
| 799 List<ir.Primitive> arguments = node.arguments.mapToList(visit); | |
| 800 arguments = normalizeStaticArguments(selector, element, arguments); | |
| 801 return irBuilder.buildStaticInvocation(element, selector, arguments, | |
| 802 sourceInformation: sourceInformationBuilder.buildCall(node)); | |
| 803 } | |
| 804 } | |
| 805 | |
| 806 ir.Primitive visitSuperSend(ast.Send node) { | |
| 807 assert(irBuilder.isOpen); | |
| 808 Selector selector = elements.getSelector(node); | |
| 809 Element target = elements[node]; | |
| 810 | |
| 811 if (selector.isCall && (target.isGetter || target.isField)) { | |
| 812 // We are invoking a field or getter as if it was a method, e.g: | |
| 813 // | |
| 814 // class A { get foo => {..} } | |
| 815 // class B extends A { | |
| 816 // m() { | |
| 817 // super.foo(1, 2, 3); } | |
| 818 // } | |
| 819 // } | |
| 820 // | |
| 821 // We invoke the getter of 'foo' and then invoke the 'call' method on | |
| 822 // the result, using the given arguments. | |
| 823 Selector getter = new Selector.getterFrom(selector); | |
| 824 Selector call = new Selector.callClosureFrom(selector); | |
| 825 ir.Primitive receiver = | |
| 826 irBuilder.buildSuperInvocation(target, getter, []); | |
| 827 List<ir.Primitive> arguments = node.arguments.mapToList(visit); | |
| 828 arguments = normalizeDynamicArguments(selector, arguments); | |
| 829 return irBuilder.buildCallInvocation(receiver, call, arguments); | |
| 830 } else if (selector.isCall) { | |
| 831 // We are invoking a method. | |
| 832 assert(target is FunctionElement); | |
| 833 List<ir.Primitive> arguments = node.arguments.mapToList(visit); | |
| 834 arguments = normalizeStaticArguments(selector, target, arguments); | |
| 835 return irBuilder.buildSuperInvocation(target, selector, arguments); | |
| 836 } else { | |
| 837 // We are invoking a getter, operator, indexer, etc. | |
| 838 List<ir.Primitive> arguments = node.argumentsNode == null | |
| 839 ? <ir.Primitive>[] | |
| 840 : node.arguments.mapToList(visit); | |
| 841 return irBuilder.buildSuperInvocation(target, selector, arguments); | |
| 842 } | |
| 843 } | |
| 844 | |
| 845 visitTypePrefixSend(ast.Send node) { | |
| 846 compiler.internalError(node, "visitTypePrefixSend should not be called."); | |
| 847 } | |
| 848 | |
| 849 ir.Primitive visitTypeLiteralSend(ast.Send node) { | |
| 850 assert(irBuilder.isOpen); | |
| 851 // If the user is trying to invoke the type literal or variable, | |
| 852 // it must be treated as a function call. | |
| 853 if (node.argumentsNode != null) { | |
| 854 // TODO(sigurdm): Handle this to match proposed semantics of issue #19725. | |
| 855 return giveup(node, 'Type literal invoked as function'); | |
| 856 } | |
| 857 | |
| 858 DartType type = elements.getTypeLiteralType(node); | |
| 859 if (type is TypeVariableType) { | |
| 860 ir.Primitive prim = new ir.ReifyTypeVar(type.element); | |
| 861 irBuilder.add(new ir.LetPrim(prim)); | |
| 862 return prim; | |
| 863 } else { | |
| 864 return translateConstant(node); | |
| 865 } | |
| 866 } | |
| 867 | |
| 868 ir.Primitive visitSendSet(ast.SendSet node) { | |
| 869 assert(irBuilder.isOpen); | |
| 870 Element element = elements[node]; | |
| 871 ast.Operator op = node.assignmentOperator; | |
| 872 // For complex operators, this is the result of getting (before assigning) | |
| 873 ir.Primitive originalValue; | |
| 874 // For []+= style operators, this saves the index. | |
| 875 ir.Primitive index; | |
| 876 ir.Primitive receiver; | |
| 877 // This is what gets assigned. | |
| 878 ir.Primitive valueToStore; | |
| 879 Selector selector = elements.getSelector(node); | |
| 880 Selector operatorSelector = | |
| 881 elements.getOperatorSelectorInComplexSendSet(node); | |
| 882 Selector getterSelector = | |
| 883 elements.getGetterSelectorInComplexSendSet(node); | |
| 884 assert( | |
| 885 // Indexing send-sets have an argument for the index. | |
| 886 (selector.isIndexSet ? 1 : 0) + | |
| 887 // Non-increment send-sets have one more argument. | |
| 888 (ast.Operator.INCREMENT_OPERATORS.contains(op.source) ? 0 : 1) | |
| 889 == node.argumentCount()); | |
| 890 | |
| 891 ast.Node getAssignArgument() { | |
| 892 assert(invariant(node, !node.arguments.isEmpty, | |
| 893 message: "argument expected")); | |
| 894 return selector.isIndexSet | |
| 895 ? node.arguments.tail.head | |
| 896 : node.arguments.head; | |
| 897 } | |
| 898 | |
| 899 // Get the value into valueToStore | |
| 900 if (op.source == "=") { | |
| 901 if (selector.isIndexSet) { | |
| 902 receiver = visitReceiver(node.receiver); | |
| 903 index = visit(node.arguments.head); | |
| 904 } else if (element == null || Elements.isInstanceField(element)) { | |
| 905 receiver = visitReceiver(node.receiver); | |
| 906 } | |
| 907 valueToStore = visit(getAssignArgument()); | |
| 908 } else { | |
| 909 // Get the original value into getter | |
| 910 assert(ast.Operator.COMPLEX_OPERATORS.contains(op.source)); | |
| 911 | |
| 912 _GetterElements getterResult = translateGetter(node, getterSelector); | |
| 913 index = getterResult.index; | |
| 914 receiver = getterResult.receiver; | |
| 915 originalValue = getterResult.result; | |
| 916 | |
| 917 // Do the modification of the value in getter. | |
| 918 ir.Primitive arg; | |
| 919 if (ast.Operator.INCREMENT_OPERATORS.contains(op.source)) { | |
| 920 arg = irBuilder.buildIntegerLiteral(1); | |
| 921 } else { | |
| 922 arg = visit(getAssignArgument()); | |
| 923 } | |
| 924 valueToStore = new ir.Parameter(null); | |
| 925 ir.Continuation k = new ir.Continuation([valueToStore]); | |
| 926 ir.Expression invoke = | |
| 927 new ir.InvokeMethod(originalValue, operatorSelector, k, [arg]); | |
| 928 irBuilder.add(new ir.LetCont(k, invoke)); | |
| 929 } | |
| 930 | |
| 931 if (Elements.isLocal(element)) { | |
| 932 irBuilder.buildLocalSet(element, valueToStore); | |
| 933 } else if ((!node.isSuperCall && Elements.isErroneous(element)) || | |
| 934 Elements.isStaticOrTopLevel(element)) { | |
| 935 irBuilder.buildStaticSet( | |
| 936 element, elements.getSelector(node), valueToStore); | |
| 937 } else { | |
| 938 // Setter or index-setter invocation | |
| 939 Selector selector = elements.getSelector(node); | |
| 940 assert(selector.kind == SelectorKind.SETTER || | |
| 941 selector.kind == SelectorKind.INDEX); | |
| 942 if (selector.isIndexSet) { | |
| 943 if (isSuperCall(node)) { | |
| 944 irBuilder.buildSuperIndexSet(element, index, valueToStore); | |
| 945 } else { | |
| 946 irBuilder.buildDynamicIndexSet(receiver, index, valueToStore); | |
| 947 } | |
| 948 } else { | |
| 949 if (isSuperCall(node)) { | |
| 950 irBuilder.buildSuperSet(element, selector, valueToStore); | |
| 951 } else { | |
| 952 irBuilder.buildDynamicSet(receiver, selector, valueToStore); | |
| 953 } | |
| 954 } | |
| 955 } | |
| 956 | |
| 957 if (node.isPostfix) { | |
| 958 assert(originalValue != null); | |
| 959 return originalValue; | |
| 960 } else { | |
| 961 return valueToStore; | |
| 962 } | |
| 963 } | |
| 964 | |
| 965 ir.Primitive visitNewExpression(ast.NewExpression node) { | |
| 966 if (node.isConst) { | |
| 967 return translateConstant(node); | |
| 968 } | |
| 969 FunctionElement element = elements[node.send]; | |
| 970 Selector selector = elements.getSelector(node.send); | |
| 971 DartType type = elements.getType(node); | |
| 972 ast.Node selectorNode = node.send.selector; | |
| 973 List<ir.Primitive> arguments = | |
| 974 node.send.arguments.mapToList(visit, growable:false); | |
| 975 arguments = normalizeStaticArguments(selector, element, arguments); | |
| 976 return irBuilder.buildConstructorInvocation( | |
| 977 element, selector, type, arguments); | |
| 978 } | |
| 979 | |
| 980 ir.Primitive visitStringJuxtaposition(ast.StringJuxtaposition node) { | |
| 981 assert(irBuilder.isOpen); | |
| 982 ir.Primitive first = visit(node.first); | |
| 983 ir.Primitive second = visit(node.second); | |
| 984 return irBuilder.buildStringConcatenation([first, second]); | |
| 985 } | |
| 986 | |
| 987 ir.Primitive visitStringInterpolation(ast.StringInterpolation node) { | |
| 988 assert(irBuilder.isOpen); | |
| 989 List<ir.Primitive> arguments = []; | |
| 990 arguments.add(visitLiteralString(node.string)); | |
| 991 var it = node.parts.iterator; | |
| 992 while (it.moveNext()) { | |
| 993 ast.StringInterpolationPart part = it.current; | |
| 994 arguments.add(visit(part.expression)); | |
| 995 arguments.add(visitLiteralString(part.string)); | |
| 996 } | |
| 997 return irBuilder.buildStringConcatenation(arguments); | |
| 998 } | |
| 999 | |
| 1000 ir.Primitive translateConstant(ast.Node node) { | |
| 1001 assert(irBuilder.isOpen); | |
| 1002 return irBuilder.buildConstantLiteral(getConstantForNode(node)); | |
| 1003 } | |
| 1004 | |
| 1005 ir.ExecutableDefinition nullIfGiveup(ir.ExecutableDefinition action()) { | |
| 1006 try { | |
| 1007 return action(); | |
| 1008 } catch(e, tr) { | |
| 1009 if (e == ABORT_IRNODE_BUILDER) { | |
| 1010 return null; | |
| 1011 } | |
| 1012 rethrow; | |
| 1013 } | |
| 1014 } | |
| 1015 | |
| 1016 void internalError(String reason, {ast.Node node}) { | |
| 1017 giveup(node); | |
| 1018 } | |
| 1019 } | |
| 1020 | |
| 1021 final String ABORT_IRNODE_BUILDER = "IrNode builder aborted"; | |
| 1022 | |
| 1023 dynamic giveup(ast.Node node, [String reason]) { | |
| 1024 throw ABORT_IRNODE_BUILDER; | |
| 1025 } | |
| 1026 | |
| 1027 /// Classifies local variables and local functions as captured, if they | |
| 1028 /// are accessed from within a nested function. | |
| 1029 /// | |
| 1030 /// This class is specific to the [DartIrBuilder], in that it gives up if it | |
| 1031 /// sees a feature that is currently unsupport by that builder. In particular, | |
| 1032 /// loop variables captured in a for-loop initializer, condition, or update | |
| 1033 /// expression are unsupported. | |
| 1034 class DartCapturedVariables extends ast.Visitor { | |
| 1035 final TreeElements elements; | |
| 1036 DartCapturedVariables(this.elements); | |
| 1037 | |
| 1038 FunctionElement currentFunction; | |
| 1039 bool insideInitializer = false; | |
| 1040 Set<Local> capturedVariables = new Set<Local>(); | |
| 1041 | |
| 1042 Map<ast.TryStatement, TryStatementInfo> tryStatements = | |
| 1043 <ast.TryStatement, TryStatementInfo>{}; | |
| 1044 | |
| 1045 List<TryStatementInfo> tryNestingStack = <TryStatementInfo>[]; | |
| 1046 bool get inTryStatement => tryNestingStack.isNotEmpty; | |
| 1047 | |
| 1048 void markAsCaptured(Local local) { | |
| 1049 capturedVariables.add(local); | |
| 1050 } | |
| 1051 | |
| 1052 visit(ast.Node node) => node.accept(this); | |
| 1053 | |
| 1054 visitNode(ast.Node node) { | |
| 1055 node.visitChildren(this); | |
| 1056 } | |
| 1057 | |
| 1058 visitFor(ast.For node) { | |
| 1059 if (node.initializer != null) visit(node.initializer); | |
| 1060 if (node.condition != null) visit(node.condition); | |
| 1061 if (node.update != null) visit(node.update); | |
| 1062 | |
| 1063 // Give up if a variable was captured outside of the loop body. | |
| 1064 if (node.initializer is ast.VariableDefinitions) { | |
| 1065 ast.VariableDefinitions definitions = node.initializer; | |
| 1066 for (ast.Node node in definitions.definitions.nodes) { | |
| 1067 LocalElement loopVariable = elements[node]; | |
| 1068 if (capturedVariables.contains(loopVariable)) { | |
| 1069 return giveup(node, 'For-loop variable captured in loop header'); | |
| 1070 } | |
| 1071 } | |
| 1072 } | |
| 1073 | |
| 1074 if (node.body != null) visit(node.body); | |
| 1075 } | |
| 1076 | |
| 1077 void handleSend(ast.Send node) { | |
| 1078 Element element = elements[node]; | |
| 1079 if (Elements.isLocal(element) && | |
| 1080 !element.isConst && | |
| 1081 element.enclosingElement != currentFunction) { | |
| 1082 LocalElement local = element; | |
| 1083 markAsCaptured(local); | |
| 1084 } | |
| 1085 } | |
| 1086 | |
| 1087 visitSend(ast.Send node) { | |
| 1088 handleSend(node); | |
| 1089 node.visitChildren(this); | |
| 1090 } | |
| 1091 | |
| 1092 visitSendSet(ast.SendSet node) { | |
| 1093 handleSend(node); | |
| 1094 Element element = elements[node]; | |
| 1095 if (Elements.isLocal(element)) { | |
| 1096 LocalElement local = element; | |
| 1097 if (insideInitializer) { | |
| 1098 assert(local.isParameter); | |
| 1099 // Initializers in an initializer-list can communicate via parameters. | |
| 1100 // If a parameter is stored in an initializer list we box it. | |
| 1101 // TODO(sigurdm): Fix this. | |
| 1102 // Though these variables do not outlive the activation of the | |
| 1103 // function, they still need to be boxed. As a simplification, we | |
| 1104 // treat them as if they are captured by a closure (i.e., they do | |
| 1105 // outlive the activation of the function). | |
| 1106 markAsCaptured(local); | |
| 1107 } else if (inTryStatement) { | |
| 1108 assert(local.isParameter || local.isVariable); | |
| 1109 // Search for the position of the try block containing the variable | |
| 1110 // declaration, or -1 if it is declared outside the outermost try. | |
| 1111 int i = tryNestingStack.length - 1; | |
| 1112 while (i >= 0 && !tryNestingStack[i].declared.contains(local)) { | |
| 1113 --i; | |
| 1114 } | |
| 1115 // If there is a next inner try, then the variable should be boxed on | |
| 1116 // entry to it. | |
| 1117 if (i + 1 < tryNestingStack.length) { | |
| 1118 tryNestingStack[i + 1].boxedOnEntry.add(local); | |
| 1119 } | |
| 1120 } | |
| 1121 } | |
| 1122 node.visitChildren(this); | |
| 1123 } | |
| 1124 | |
| 1125 visitFunctionExpression(ast.FunctionExpression node) { | |
| 1126 FunctionElement oldFunction = currentFunction; | |
| 1127 currentFunction = elements[node]; | |
| 1128 if (currentFunction.asyncMarker != AsyncMarker.SYNC) { | |
| 1129 giveup(node, "cannot handle async/sync*/async* functions"); | |
| 1130 } | |
| 1131 if (node.initializers != null) { | |
| 1132 insideInitializer = true; | |
| 1133 visit(node.initializers); | |
| 1134 insideInitializer = false; | |
| 1135 } | |
| 1136 visit(node.body); | |
| 1137 currentFunction = oldFunction; | |
| 1138 } | |
| 1139 | |
| 1140 visitTryStatement(ast.TryStatement node) { | |
| 1141 TryStatementInfo info = new TryStatementInfo(); | |
| 1142 tryStatements[node] = info; | |
| 1143 tryNestingStack.add(info); | |
| 1144 visit(node.tryBlock); | |
| 1145 assert(tryNestingStack.last == info); | |
| 1146 tryNestingStack.removeLast(); | |
| 1147 | |
| 1148 visit(node.catchBlocks); | |
| 1149 if (node.finallyBlock != null) visit(node.finallyBlock); | |
| 1150 } | |
| 1151 | |
| 1152 visitVariableDefinitions(ast.VariableDefinitions node) { | |
| 1153 if (inTryStatement) { | |
| 1154 for (ast.Node definition in node.definitions.nodes) { | |
| 1155 LocalVariableElement local = elements[definition]; | |
| 1156 assert(local != null); | |
| 1157 // In the closure conversion pass we check for isInitializingFormal, | |
| 1158 // but I'm not sure it can arise. | |
| 1159 assert(!local.isInitializingFormal); | |
| 1160 tryNestingStack.last.declared.add(local); | |
| 1161 } | |
| 1162 } | |
| 1163 node.visitChildren(this); | |
| 1164 } | |
| 1165 } | |
| 1166 | |
| 1167 /// IR builder specific to the Dart backend, coupled to the [DartIrBuilder]. | |
| 1168 class DartIrBuilderVisitor extends IrBuilderVisitor { | |
| 1169 /// Promote the type of [irBuilder] to [DartIrBuilder]. | |
| 1170 DartIrBuilder get irBuilder => super.irBuilder; | |
| 1171 | |
| 1172 DartIrBuilderVisitor(TreeElements elements, | |
| 1173 Compiler compiler, | |
| 1174 SourceInformationBuilder sourceInformationBuilder) | |
| 1175 : super(elements, compiler, sourceInformationBuilder); | |
| 1176 | |
| 1177 DartIrBuilder makeIRBuilder(ast.Node node, ExecutableElement element) { | |
| 1178 DartCapturedVariables closures = new DartCapturedVariables(elements); | |
| 1179 if (!element.isSynthesized) { | |
| 1180 closures.visit(node); | |
| 1181 } | |
| 1182 return new DartIrBuilder(compiler.backend.constantSystem, | |
| 1183 element, | |
| 1184 closures); | |
| 1185 } | |
| 1186 | |
| 1187 /// Recursively builds the IR for the given nested function. | |
| 1188 ir.FunctionDefinition makeSubFunction(ast.FunctionExpression node) { | |
| 1189 FunctionElement element = elements[node]; | |
| 1190 assert(invariant(element, element.isImplementation)); | |
| 1191 | |
| 1192 IrBuilder builder = irBuilder.makeInnerFunctionBuilder(element); | |
| 1193 | |
| 1194 return withBuilder(builder, () => _makeFunctionBody(element, node)); | |
| 1195 } | |
| 1196 | |
| 1197 ir.Primitive visitFunctionExpression(ast.FunctionExpression node) { | |
| 1198 return irBuilder.buildFunctionExpression(makeSubFunction(node)); | |
| 1199 } | |
| 1200 | |
| 1201 visitFunctionDeclaration(ast.FunctionDeclaration node) { | |
| 1202 LocalFunctionElement element = elements[node.function]; | |
| 1203 Object inner = makeSubFunction(node.function); | |
| 1204 irBuilder.declareLocalFunction(element, inner); | |
| 1205 } | |
| 1206 | |
| 1207 ClosureScope getClosureScopeForNode(ast.Node node) => null; | |
| 1208 ClosureEnvironment getClosureEnvironment() => null; | |
| 1209 | |
| 1210 ir.ExecutableDefinition buildExecutable(ExecutableElement element) { | |
| 1211 return nullIfGiveup(() { | |
| 1212 if (element is FieldElement) { | |
| 1213 return buildField(element); | |
| 1214 } else if (element is FunctionElement) { | |
| 1215 return buildFunction(element); | |
| 1216 } else { | |
| 1217 compiler.internalError(element, "Unexpected element type $element"); | |
| 1218 } | |
| 1219 }); | |
| 1220 } | |
| 1221 | |
| 1222 /// Returns a [ir.FieldDefinition] describing the initializer of [element]. | |
| 1223 ir.FieldDefinition buildField(FieldElement element) { | |
| 1224 assert(invariant(element, element.isImplementation)); | |
| 1225 ast.VariableDefinitions definitions = element.node; | |
| 1226 ast.Node fieldDefinition = definitions.definitions.nodes.first; | |
| 1227 if (definitions.modifiers.isConst) { | |
| 1228 // TODO(sigurdm): Just return const value. | |
| 1229 } | |
| 1230 assert(fieldDefinition != null); | |
| 1231 assert(elements[fieldDefinition] != null); | |
| 1232 | |
| 1233 IrBuilder builder = makeIRBuilder(fieldDefinition, element); | |
| 1234 | |
| 1235 return withBuilder(builder, () { | |
| 1236 builder.buildFieldInitializerHeader( | |
| 1237 closureScope: getClosureScopeForNode(fieldDefinition)); | |
| 1238 ir.Primitive initializer; | |
| 1239 if (fieldDefinition is ast.SendSet) { | |
| 1240 ast.SendSet sendSet = fieldDefinition; | |
| 1241 initializer = visit(sendSet.arguments.first); | |
| 1242 } | |
| 1243 return builder.makeFieldDefinition(initializer); | |
| 1244 }); | |
| 1245 } | |
| 1246 | |
| 1247 ir.FunctionDefinition buildFunction(FunctionElement element) { | |
| 1248 assert(invariant(element, element.isImplementation)); | |
| 1249 ast.FunctionExpression node = element.node; | |
| 1250 if (element.asyncMarker != AsyncMarker.SYNC) { | |
| 1251 giveup(null, 'cannot handle async-await'); | |
| 1252 } | |
| 1253 | |
| 1254 if (!element.isSynthesized) { | |
| 1255 assert(node != null); | |
| 1256 assert(elements[node] != null); | |
| 1257 } else { | |
| 1258 SynthesizedConstructorElementX constructor = element; | |
| 1259 if (!constructor.isDefaultConstructor) { | |
| 1260 giveup(null, 'cannot handle synthetic forwarding constructors'); | |
| 1261 } | |
| 1262 } | |
| 1263 | |
| 1264 IrBuilder builder = makeIRBuilder(node, element); | |
| 1265 | |
| 1266 return withBuilder(builder, () => _makeFunctionBody(element, node)); | |
| 1267 } | |
| 1268 | |
| 1269 List<ir.Primitive> normalizeStaticArguments( | |
| 1270 Selector selector, | |
| 1271 FunctionElement target, | |
| 1272 List<ir.Primitive> arguments) { | |
| 1273 return arguments; | |
| 1274 } | |
| 1275 | |
| 1276 List<ir.Primitive> normalizeDynamicArguments( | |
| 1277 Selector selector, | |
| 1278 List<ir.Primitive> arguments) { | |
| 1279 return arguments; | |
| 1280 } | |
| 1281 } | |
| 1282 | |
| 1283 /// IR builder specific to the JavaScript backend, coupled to the [JsIrBuilder]. | |
| 1284 class JsIrBuilderVisitor extends IrBuilderVisitor { | |
| 1285 /// Promote the type of [irBuilder] to [JsIrBuilder]. | |
| 1286 JsIrBuilder get irBuilder => super.irBuilder; | |
| 1287 | |
| 1288 /// Result of closure conversion for the current body of code. | |
| 1289 /// | |
| 1290 /// Will be initialized upon entering the body of a function. | |
| 1291 /// It is computed by the [ClosureTranslator]. | |
| 1292 ClosureClassMap closureMap; | |
| 1293 | |
| 1294 /// During construction of a constructor factory, [fieldValues] maps fields | |
| 1295 /// to the primitive containing their initial value. | |
| 1296 Map<FieldElement, ir.Primitive> fieldValues = <FieldElement, ir.Primitive>{}; | |
| 1297 | |
| 1298 JsIrBuilderVisitor(TreeElements elements, | |
| 1299 Compiler compiler, | |
| 1300 SourceInformationBuilder sourceInformationBuilder) | |
| 1301 : super(elements, compiler, sourceInformationBuilder); | |
| 1302 | |
| 1303 /// Builds the IR for creating an instance of the closure class corresponding | |
| 1304 /// to the given nested function. | |
| 1305 ClosureClassElement makeSubFunction(ast.FunctionExpression node) { | |
| 1306 ClosureClassMap innerMap = | |
| 1307 compiler.closureToClassMapper.getMappingForNestedFunction(node); | |
| 1308 ClosureClassElement closureClass = innerMap.closureClassElement; | |
| 1309 return closureClass; | |
| 1310 } | |
| 1311 | |
| 1312 ir.Primitive visitFunctionExpression(ast.FunctionExpression node) { | |
| 1313 return irBuilder.buildFunctionExpression(makeSubFunction(node)); | |
| 1314 } | |
| 1315 | |
| 1316 visitFunctionDeclaration(ast.FunctionDeclaration node) { | |
| 1317 LocalFunctionElement element = elements[node.function]; | |
| 1318 Object inner = makeSubFunction(node.function); | |
| 1319 irBuilder.declareLocalFunction(element, inner); | |
| 1320 } | |
| 1321 | |
| 1322 Map mapValues(Map map, dynamic fn(dynamic)) { | |
| 1323 Map result = {}; | |
| 1324 map.forEach((key, value) { | |
| 1325 result[key] = fn(value); | |
| 1326 }); | |
| 1327 return result; | |
| 1328 } | |
| 1329 | |
| 1330 /// Converts closure.dart's CapturedVariable into a ClosureLocation. | |
| 1331 /// There is a 1:1 corresponce between these; we do this because the | |
| 1332 /// IR builder should not depend on synthetic elements. | |
| 1333 ClosureLocation getLocation(CapturedVariable v) { | |
| 1334 if (v is BoxFieldElement) { | |
| 1335 return new ClosureLocation(v.box, v); | |
| 1336 } else { | |
| 1337 ClosureFieldElement field = v; | |
| 1338 return new ClosureLocation(null, field); | |
| 1339 } | |
| 1340 } | |
| 1341 | |
| 1342 /// If the current function is a nested function with free variables (or a | |
| 1343 /// captured reference to `this`), returns a [ClosureEnvironment] | |
| 1344 /// indicating how to access these. | |
| 1345 ClosureEnvironment getClosureEnvironment() { | |
| 1346 if (closureMap.closureElement == null) return null; | |
| 1347 return new ClosureEnvironment( | |
| 1348 closureMap.closureElement, | |
| 1349 closureMap.thisLocal, | |
| 1350 mapValues(closureMap.freeVariableMap, getLocation)); | |
| 1351 } | |
| 1352 | |
| 1353 /// If [node] has declarations for variables that should be boxed, | |
| 1354 /// returns a [ClosureScope] naming a box to create, and enumerating the | |
| 1355 /// variables that should be stored in the box. | |
| 1356 /// | |
| 1357 /// Also see [ClosureScope]. | |
| 1358 ClosureScope getClosureScopeForNode(ast.Node node) { | |
| 1359 closurelib.ClosureScope scope = closureMap.capturingScopes[node]; | |
| 1360 if (scope == null) return null; | |
| 1361 // We translate a ClosureScope from closure.dart into IR builder's variant | |
| 1362 // because the IR builder should not depend on the synthetic elements | |
| 1363 // created in closure.dart. | |
| 1364 return new ClosureScope(scope.boxElement, | |
| 1365 mapValues(scope.capturedVariables, getLocation), | |
| 1366 scope.boxedLoopVariables); | |
| 1367 } | |
| 1368 | |
| 1369 /// Returns the [ClosureScope] for any function, possibly different from the | |
| 1370 /// one currently being built. | |
| 1371 ClosureScope getClosureScopeForFunction(FunctionElement function) { | |
| 1372 ClosureClassMap map = | |
| 1373 compiler.closureToClassMapper.computeClosureToClassMapping( | |
| 1374 function, | |
| 1375 function.node, | |
| 1376 elements); | |
| 1377 closurelib.ClosureScope scope = map.capturingScopes[function.node]; | |
| 1378 if (scope == null) return null; | |
| 1379 return new ClosureScope(scope.boxElement, | |
| 1380 mapValues(scope.capturedVariables, getLocation), | |
| 1381 scope.boxedLoopVariables); | |
| 1382 } | |
| 1383 | |
| 1384 ir.ExecutableDefinition buildExecutable(ExecutableElement element) { | |
| 1385 return nullIfGiveup(() { | |
| 1386 switch (element.kind) { | |
| 1387 case ElementKind.GENERATIVE_CONSTRUCTOR: | |
| 1388 return buildConstructor(element); | |
| 1389 | |
| 1390 case ElementKind.GENERATIVE_CONSTRUCTOR_BODY: | |
| 1391 return buildConstructorBody(element); | |
| 1392 | |
| 1393 case ElementKind.FUNCTION: | |
| 1394 case ElementKind.GETTER: | |
| 1395 case ElementKind.SETTER: | |
| 1396 return buildFunction(element); | |
| 1397 | |
| 1398 default: | |
| 1399 compiler.internalError(element, "Unexpected element type $element"); | |
| 1400 } | |
| 1401 }); | |
| 1402 } | |
| 1403 | |
| 1404 /// Builds the IR for an [expression] taken from a different [context]. | |
| 1405 /// | |
| 1406 /// Such expressions need to be compiled with a different [sourceFile] and | |
| 1407 /// [elements] mapping. | |
| 1408 ir.Primitive inlineExpression(AstElement context, ast.Expression expression) { | |
| 1409 JsIrBuilderVisitor visitor = new JsIrBuilderVisitor( | |
| 1410 context.resolvedAst.elements, | |
| 1411 compiler, | |
| 1412 sourceInformationBuilder.forContext(context)); | |
| 1413 return visitor.withBuilder(irBuilder, () => visitor.visit(expression)); | |
| 1414 } | |
| 1415 | |
| 1416 /// Builds the IR for a constant taken from a different [context]. | |
| 1417 /// | |
| 1418 /// Such constants need to be compiled with a different [sourceFile] and | |
| 1419 /// [elements] mapping. | |
| 1420 ir.Primitive inlineConstant(AstElement context, ast.Expression exp) { | |
| 1421 JsIrBuilderVisitor visitor = new JsIrBuilderVisitor( | |
| 1422 context.resolvedAst.elements, | |
| 1423 compiler, | |
| 1424 sourceInformationBuilder.forContext(context)); | |
| 1425 return visitor.withBuilder(irBuilder, () => visitor.translateConstant(exp)); | |
| 1426 } | |
| 1427 | |
| 1428 /// Builds the IR for a given constructor. | |
| 1429 /// | |
| 1430 /// 1. Evaluates all own or inherited field initializers. | |
| 1431 /// 2. Creates the object and assigns its fields. | |
| 1432 /// 3. Calls constructor body and super constructor bodies. | |
| 1433 /// 4. Returns the created object. | |
| 1434 ir.FunctionDefinition buildConstructor(ConstructorElement constructor) { | |
| 1435 constructor = constructor.implementation; | |
| 1436 ClassElement classElement = constructor.enclosingClass.implementation; | |
| 1437 | |
| 1438 JsIrBuilder builder = | |
| 1439 new JsIrBuilder(compiler.backend.constantSystem, constructor); | |
| 1440 | |
| 1441 return withBuilder(builder, () { | |
| 1442 // Setup parameters and create a box if anything is captured. | |
| 1443 List<ParameterElement> parameters = []; | |
| 1444 constructor.functionSignature.orderedForEachParameter(parameters.add); | |
| 1445 builder.buildFunctionHeader(parameters, | |
| 1446 closureScope: getClosureScopeForFunction(constructor)); | |
| 1447 | |
| 1448 // -- Step 1: evaluate field initializers --- | |
| 1449 // Evaluate field initializers in constructor and super constructors. | |
| 1450 List<ConstructorElement> constructorList = <ConstructorElement>[]; | |
| 1451 evaluateConstructorFieldInitializers(constructor, constructorList); | |
| 1452 | |
| 1453 // All parameters in all constructors are now bound in the environment. | |
| 1454 // BoxLocals for captured parameters are also in the environment. | |
| 1455 // The initial value of all fields are now bound in [fieldValues]. | |
| 1456 | |
| 1457 // --- Step 2: create the object --- | |
| 1458 // Get the initial field values in the canonical order. | |
| 1459 List<ir.Primitive> instanceArguments = <ir.Primitive>[]; | |
| 1460 classElement.forEachInstanceField((ClassElement c, FieldElement field) { | |
| 1461 ir.Primitive value = fieldValues[field]; | |
| 1462 if (value != null) { | |
| 1463 instanceArguments.add(fieldValues[field]); | |
| 1464 } else { | |
| 1465 assert(Elements.isNativeOrExtendsNative(c)); | |
| 1466 // Native fields are initialized elsewhere. | |
| 1467 } | |
| 1468 }, includeSuperAndInjectedMembers: true); | |
| 1469 ir.Primitive instance = | |
| 1470 new ir.CreateInstance(classElement, instanceArguments); | |
| 1471 irBuilder.add(new ir.LetPrim(instance)); | |
| 1472 | |
| 1473 // --- Step 3: call constructor bodies --- | |
| 1474 for (ConstructorElement target in constructorList) { | |
| 1475 ConstructorBodyElement bodyElement = getConstructorBody(target); | |
| 1476 if (bodyElement == null) continue; // Skip if constructor has no body. | |
| 1477 List<ir.Primitive> bodyArguments = <ir.Primitive>[]; | |
| 1478 for (Local param in getConstructorBodyParameters(bodyElement)) { | |
| 1479 bodyArguments.add(irBuilder.environment.lookup(param)); | |
| 1480 } | |
| 1481 irBuilder.buildInvokeDirectly(bodyElement, instance, bodyArguments); | |
| 1482 } | |
| 1483 | |
| 1484 // --- step 4: return the created object ---- | |
| 1485 irBuilder.buildReturn(instance); | |
| 1486 | |
| 1487 return irBuilder.makeFunctionDefinition([]); | |
| 1488 }); | |
| 1489 } | |
| 1490 | |
| 1491 /// Evaluates all field initializers on [constructor] and all constructors | |
| 1492 /// invoked through `this()` or `super()` ("superconstructors"). | |
| 1493 /// | |
| 1494 /// The resulting field values will be available in [fieldValues]. The values | |
| 1495 /// are not stored in any fields. | |
| 1496 /// | |
| 1497 /// This procedure assumes that the parameters to [constructor] are available | |
| 1498 /// in the IR builder's environment. | |
| 1499 /// | |
| 1500 /// The parameters to superconstructors are, however, assumed *not* to be in | |
| 1501 /// the environment, but will be put there by this procedure. | |
| 1502 /// | |
| 1503 /// All constructors will be added to [supers], with superconstructors first. | |
| 1504 void evaluateConstructorFieldInitializers(ConstructorElement constructor, | |
| 1505 List<ConstructorElement> supers) { | |
| 1506 // Evaluate declaration-site field initializers. | |
| 1507 ClassElement enclosingClass = constructor.enclosingClass.implementation; | |
| 1508 enclosingClass.forEachInstanceField((ClassElement c, FieldElement field) { | |
| 1509 if (field.initializer != null) { | |
| 1510 fieldValues[field] = inlineExpression(field, field.initializer); | |
| 1511 } else { | |
| 1512 if (Elements.isNativeOrExtendsNative(c)) { | |
| 1513 // Native field is initialized elsewhere. | |
| 1514 } else { | |
| 1515 // Fields without an initializer default to null. | |
| 1516 // This value will be overwritten below if an initializer is found. | |
| 1517 fieldValues[field] = irBuilder.buildNullLiteral(); | |
| 1518 } | |
| 1519 } | |
| 1520 }); | |
| 1521 // Evaluate initializing parameters, e.g. `Foo(this.x)`. | |
| 1522 constructor.functionSignature.orderedForEachParameter( | |
| 1523 (ParameterElement parameter) { | |
| 1524 if (parameter.isInitializingFormal) { | |
| 1525 InitializingFormalElement fieldParameter = parameter; | |
| 1526 fieldValues[fieldParameter.fieldElement] = | |
| 1527 irBuilder.buildLocalGet(parameter); | |
| 1528 } | |
| 1529 }); | |
| 1530 // Evaluate constructor initializers, e.g. `Foo() : x = 50`. | |
| 1531 ast.FunctionExpression node = constructor.node; | |
| 1532 bool hasConstructorCall = false; // Has this() or super() initializer? | |
| 1533 if (node != null && node.initializers != null) { | |
| 1534 for(ast.Node initializer in node.initializers) { | |
| 1535 if (initializer is ast.SendSet) { | |
| 1536 // Field initializer. | |
| 1537 FieldElement field = elements[initializer]; | |
| 1538 fieldValues[field] = | |
| 1539 inlineExpression(constructor, initializer.arguments.head); | |
| 1540 } else if (initializer is ast.Send) { | |
| 1541 // Super or this initializer. | |
| 1542 ConstructorElement target = elements[initializer].implementation; | |
| 1543 Selector selector = elements.getSelector(initializer); | |
| 1544 List<ir.Primitive> arguments = initializer.arguments.mapToList(visit); | |
| 1545 loadArguments(target, selector, arguments); | |
| 1546 evaluateConstructorFieldInitializers(target, supers); | |
| 1547 hasConstructorCall = true; | |
| 1548 } else { | |
| 1549 compiler.internalError(initializer, | |
| 1550 "Unexpected initializer type $initializer"); | |
| 1551 } | |
| 1552 } | |
| 1553 } | |
| 1554 // If no super() or this() was found, also call default superconstructor. | |
| 1555 if (!hasConstructorCall && !enclosingClass.isObject) { | |
| 1556 ClassElement superClass = enclosingClass.superclass; | |
| 1557 FunctionElement target = superClass.lookupDefaultConstructor(); | |
| 1558 if (target == null) { | |
| 1559 compiler.internalError(superClass, "No default constructor available."); | |
| 1560 } | |
| 1561 evaluateConstructorFieldInitializers(target, supers); | |
| 1562 } | |
| 1563 // Add this constructor after the superconstructors. | |
| 1564 supers.add(constructor); | |
| 1565 } | |
| 1566 | |
| 1567 /// In preparation of inlining (part of) [target], the [arguments] are moved | |
| 1568 /// into the environment bindings for the corresponding parameters. | |
| 1569 /// | |
| 1570 /// Defaults for optional arguments are evaluated in order to ensure | |
| 1571 /// all parameters are available in the environment. | |
| 1572 void loadArguments(FunctionElement target, | |
| 1573 Selector selector, | |
| 1574 List<ir.Primitive> arguments) { | |
| 1575 target = target.implementation; | |
| 1576 FunctionSignature signature = target.functionSignature; | |
| 1577 | |
| 1578 // Establish a scope in case parameters are captured. | |
| 1579 ClosureScope scope = getClosureScopeForFunction(target); | |
| 1580 irBuilder._enterScope(scope); | |
| 1581 | |
| 1582 // Load required parameters | |
| 1583 int index = 0; | |
| 1584 signature.forEachRequiredParameter((ParameterElement param) { | |
| 1585 irBuilder.declareLocalVariable(param, initialValue: arguments[index]); | |
| 1586 index++; | |
| 1587 }); | |
| 1588 | |
| 1589 // Load optional parameters, evaluating default values for omitted ones. | |
| 1590 signature.forEachOptionalParameter((ParameterElement param) { | |
| 1591 ir.Primitive value; | |
| 1592 // Load argument if provided. | |
| 1593 if (signature.optionalParametersAreNamed) { | |
| 1594 int nameIndex = selector.namedArguments.indexOf(param.name); | |
| 1595 if (nameIndex != -1) { | |
| 1596 int translatedIndex = selector.positionalArgumentCount + nameIndex; | |
| 1597 value = arguments[translatedIndex]; | |
| 1598 } | |
| 1599 } else if (index < arguments.length) { | |
| 1600 value = arguments[index]; | |
| 1601 } | |
| 1602 // Load default if argument was not provided. | |
| 1603 if (value == null) { | |
| 1604 if (param.initializer != null) { | |
| 1605 value = inlineExpression(target, param.initializer); | |
| 1606 } else { | |
| 1607 value = irBuilder.buildNullLiteral(); | |
| 1608 } | |
| 1609 } | |
| 1610 irBuilder.declareLocalVariable(param, initialValue: value); | |
| 1611 index++; | |
| 1612 }); | |
| 1613 } | |
| 1614 | |
| 1615 /** | |
| 1616 * Returns the constructor body associated with the given constructor or | |
| 1617 * creates a new constructor body, if none can be found. | |
| 1618 * | |
| 1619 * Returns `null` if the constructor does not have a body. | |
| 1620 */ | |
| 1621 ConstructorBodyElement getConstructorBody(FunctionElement constructor) { | |
| 1622 // TODO(asgerf): This is largely inherited from the SSA builder. | |
| 1623 // The ConstructorBodyElement has an invalid function signature, but we | |
| 1624 // cannot add a BoxLocal as parameter, because BoxLocal is not an element. | |
| 1625 // Instead of forging ParameterElements to forge a FunctionSignature, we | |
| 1626 // need a way to create backend methods without creating more fake elements. | |
| 1627 | |
| 1628 assert(constructor.isGenerativeConstructor); | |
| 1629 assert(invariant(constructor, constructor.isImplementation)); | |
| 1630 if (constructor.isSynthesized) return null; | |
| 1631 ast.FunctionExpression node = constructor.node; | |
| 1632 // If we know the body doesn't have any code, we don't generate it. | |
| 1633 if (!node.hasBody()) return null; | |
| 1634 if (node.hasEmptyBody()) return null; | |
| 1635 ClassElement classElement = constructor.enclosingClass; | |
| 1636 ConstructorBodyElement bodyElement; | |
| 1637 classElement.forEachBackendMember((Element backendMember) { | |
| 1638 if (backendMember.isGenerativeConstructorBody) { | |
| 1639 ConstructorBodyElement body = backendMember; | |
| 1640 if (body.constructor == constructor) { | |
| 1641 bodyElement = backendMember; | |
| 1642 } | |
| 1643 } | |
| 1644 }); | |
| 1645 if (bodyElement == null) { | |
| 1646 bodyElement = new ConstructorBodyElementX(constructor); | |
| 1647 classElement.addBackendMember(bodyElement); | |
| 1648 | |
| 1649 if (constructor.isPatch) { | |
| 1650 // Create origin body element for patched constructors. | |
| 1651 ConstructorBodyElementX patch = bodyElement; | |
| 1652 ConstructorBodyElementX origin = | |
| 1653 new ConstructorBodyElementX(constructor.origin); | |
| 1654 origin.applyPatch(patch); | |
| 1655 classElement.origin.addBackendMember(bodyElement.origin); | |
| 1656 } | |
| 1657 } | |
| 1658 assert(bodyElement.isGenerativeConstructorBody); | |
| 1659 return bodyElement; | |
| 1660 } | |
| 1661 | |
| 1662 /// The list of parameters to send from the generative constructor | |
| 1663 /// to the generative constructor body. | |
| 1664 /// | |
| 1665 /// Boxed parameters are not in the list, instead, a [BoxLocal] is passed | |
| 1666 /// containing the boxed parameters. | |
| 1667 /// | |
| 1668 /// For example, given the following constructor, | |
| 1669 /// | |
| 1670 /// Foo(x, y) : field = (() => ++x) { print(x + y) } | |
| 1671 /// | |
| 1672 /// the argument `x` would be replaced by a [BoxLocal]: | |
| 1673 /// | |
| 1674 /// Foo_body(box0, y) { print(box0.x + y) } | |
| 1675 /// | |
| 1676 List<Local> getConstructorBodyParameters(ConstructorBodyElement body) { | |
| 1677 List<Local> parameters = <Local>[]; | |
| 1678 ClosureScope scope = getClosureScopeForFunction(body.constructor); | |
| 1679 if (scope != null) { | |
| 1680 parameters.add(scope.box); | |
| 1681 } | |
| 1682 body.functionSignature.orderedForEachParameter((ParameterElement param) { | |
| 1683 if (scope != null && scope.capturedVariables.containsKey(param)) { | |
| 1684 // Do not pass this parameter; the box will carry its value. | |
| 1685 } else { | |
| 1686 parameters.add(param); | |
| 1687 } | |
| 1688 }); | |
| 1689 return parameters; | |
| 1690 } | |
| 1691 | |
| 1692 /// Builds the IR for the body of a constructor. | |
| 1693 /// | |
| 1694 /// This function is invoked from one or more "factory" constructors built by | |
| 1695 /// [buildConstructor]. | |
| 1696 ir.FunctionDefinition buildConstructorBody(ConstructorBodyElement body) { | |
| 1697 ConstructorElement constructor = body.constructor; | |
| 1698 ast.FunctionExpression node = constructor.node; | |
| 1699 closureMap = compiler.closureToClassMapper.computeClosureToClassMapping( | |
| 1700 constructor, | |
| 1701 node, | |
| 1702 elements); | |
| 1703 | |
| 1704 JsIrBuilder builder = | |
| 1705 new JsIrBuilder(compiler.backend.constantSystem, body); | |
| 1706 | |
| 1707 return withBuilder(builder, () { | |
| 1708 irBuilder.buildConstructorBodyHeader(getConstructorBodyParameters(body), | |
| 1709 getClosureScopeForNode(node)); | |
| 1710 visit(node.body); | |
| 1711 return irBuilder.makeFunctionDefinition([]); | |
| 1712 }); | |
| 1713 } | |
| 1714 | |
| 1715 ir.FunctionDefinition buildFunction(FunctionElement element) { | |
| 1716 assert(invariant(element, element.isImplementation)); | |
| 1717 ast.FunctionExpression node = element.node; | |
| 1718 | |
| 1719 assert(!element.isSynthesized); | |
| 1720 assert(node != null); | |
| 1721 assert(elements[node] != null); | |
| 1722 | |
| 1723 closureMap = compiler.closureToClassMapper.computeClosureToClassMapping( | |
| 1724 element, | |
| 1725 node, | |
| 1726 elements); | |
| 1727 IrBuilder builder = | |
| 1728 new JsIrBuilder(compiler.backend.constantSystem, element); | |
| 1729 return withBuilder(builder, () => _makeFunctionBody(element, node)); | |
| 1730 } | |
| 1731 | |
| 1732 /// Creates a primitive for the default value of [parameter]. | |
| 1733 ir.Primitive translateDefaultValue(ParameterElement parameter) { | |
| 1734 if (parameter.initializer == null) { | |
| 1735 return irBuilder.buildNullLiteral(); | |
| 1736 } else { | |
| 1737 return inlineConstant(parameter.executableContext, parameter.initializer); | |
| 1738 } | |
| 1739 } | |
| 1740 | |
| 1741 /// Inserts default arguments and normalizes order of named arguments. | |
| 1742 List<ir.Primitive> normalizeStaticArguments( | |
| 1743 Selector selector, | |
| 1744 FunctionElement target, | |
| 1745 List<ir.Primitive> arguments) { | |
| 1746 target = target.implementation; | |
| 1747 FunctionSignature signature = target.functionSignature; | |
| 1748 if (!signature.optionalParametersAreNamed && | |
| 1749 signature.parameterCount == arguments.length) { | |
| 1750 // Optimization: don't copy the argument list for trivial cases. | |
| 1751 return arguments; | |
| 1752 } | |
| 1753 | |
| 1754 List<ir.Primitive> result = <ir.Primitive>[]; | |
| 1755 int i = 0; | |
| 1756 signature.forEachRequiredParameter((ParameterElement element) { | |
| 1757 result.add(arguments[i]); | |
| 1758 ++i; | |
| 1759 }); | |
| 1760 | |
| 1761 if (!signature.optionalParametersAreNamed) { | |
| 1762 signature.forEachOptionalParameter((ParameterElement element) { | |
| 1763 if (i < arguments.length) { | |
| 1764 result.add(arguments[i]); | |
| 1765 ++i; | |
| 1766 } else { | |
| 1767 result.add(translateDefaultValue(element)); | |
| 1768 } | |
| 1769 }); | |
| 1770 } else { | |
| 1771 int offset = i; | |
| 1772 // Iterate over the optional parameters of the signature, and try to | |
| 1773 // find them in [compiledNamedArguments]. If found, we use the | |
| 1774 // value in the temporary list, otherwise the default value. | |
| 1775 signature.orderedOptionalParameters.forEach((ParameterElement element) { | |
| 1776 int nameIndex = selector.namedArguments.indexOf(element.name); | |
| 1777 if (nameIndex != -1) { | |
| 1778 int translatedIndex = offset + nameIndex; | |
| 1779 result.add(arguments[translatedIndex]); | |
| 1780 } else { | |
| 1781 result.add(translateDefaultValue(element)); | |
| 1782 } | |
| 1783 }); | |
| 1784 } | |
| 1785 return result; | |
| 1786 } | |
| 1787 | |
| 1788 /// Normalizes order of named arguments. | |
| 1789 List<ir.Primitive> normalizeDynamicArguments( | |
| 1790 Selector selector, | |
| 1791 List<ir.Primitive> arguments) { | |
| 1792 assert(arguments.length == selector.argumentCount); | |
| 1793 // Optimization: don't copy the argument list for trivial cases. | |
| 1794 if (selector.namedArguments.isEmpty) return arguments; | |
| 1795 List<ir.Primitive> result = <ir.Primitive>[]; | |
| 1796 for (int i=0; i < selector.positionalArgumentCount; i++) { | |
| 1797 result.add(arguments[i]); | |
| 1798 } | |
| 1799 for (String argName in selector.getOrderedNamedArguments()) { | |
| 1800 int nameIndex = selector.namedArguments.indexOf(argName); | |
| 1801 int translatedIndex = selector.positionalArgumentCount + nameIndex; | |
| 1802 result.add(arguments[translatedIndex]); | |
| 1803 } | |
| 1804 return result; | |
| 1805 } | |
| 1806 } | |
| 1807 | |
| 1808 /// Interface for generating [SourceInformation] for the CPS. | |
| 1809 class SourceInformationBuilder { | |
| 1810 const SourceInformationBuilder(); | |
| 1811 | |
| 1812 /// Create a [SourceInformationBuilder] for [element]. | |
| 1813 SourceInformationBuilder forContext(AstElement element) => this; | |
| 1814 | |
| 1815 /// Generate [SourceInformation] for the read access in [node]. | |
| 1816 SourceInformation buildGet(ast.Node node) => null; | |
| 1817 | |
| 1818 /// Generate [SourceInformation] for the invocation in [node]. | |
| 1819 SourceInformation buildCall(ast.Node node) => null; | |
| 1820 } | |
| 1821 | |
| 1822 /// [SourceInformationBuilder] that generates [PositionSourceInformation]. | |
| 1823 class PositionSourceInformationBuilder implements SourceInformationBuilder { | |
| 1824 final SourceFile sourceFile; | |
| 1825 final String name; | |
| 1826 | |
| 1827 PositionSourceInformationBuilder(AstElement element) | |
| 1828 : sourceFile = element.compilationUnit.script.file, | |
| 1829 name = element.name; | |
| 1830 | |
| 1831 @override | |
| 1832 SourceInformation buildGet(ast.Node node) { | |
| 1833 return new PositionSourceInformation( | |
| 1834 new TokenSourceLocation(sourceFile, node.getBeginToken(), name)); | |
| 1835 } | |
| 1836 | |
| 1837 @override | |
| 1838 SourceInformation buildCall(ast.Node node) { | |
| 1839 return new PositionSourceInformation( | |
| 1840 new TokenSourceLocation(sourceFile, node.getBeginToken(), name)); | |
| 1841 } | |
| 1842 | |
| 1843 @override | |
| 1844 SourceInformationBuilder forContext(AstElement element) { | |
| 1845 return new PositionSourceInformationBuilder(element); | |
| 1846 } | |
| 1847 } | |
| OLD | NEW |