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.FunctionDefinition> nodes = | |
26 <Element, ir.FunctionDefinition>{}; | |
27 | |
28 IrBuilderTask(Compiler compiler) : super(compiler); | |
29 | |
30 String get name => 'IR builder'; | |
31 | |
32 bool hasIr(Element element) => nodes.containsKey(element.implementation); | |
33 | |
34 ir.FunctionDefinition getIr(Element element) => nodes[element.implementation]; | |
35 | |
36 void buildNodes({bool useNewBackend: false}) { | |
37 if (!irEnabled(useNewBackend: useNewBackend)) return; | |
38 measure(() { | |
39 Set<Element> resolved = compiler.enqueuer.resolution.resolvedElements; | |
40 resolved.forEach((AstElement element) { | |
41 if (canBuild(element)) { | |
42 TreeElements elementsMapping = element.resolvedAst.elements; | |
43 element = element.implementation; | |
44 compiler.withCurrentElement(element, () { | |
45 SourceFile sourceFile = elementSourceFile(element); | |
46 IrBuilderVisitor builder = | |
47 new IrBuilderVisitor(elementsMapping, compiler, sourceFile); | |
48 ir.FunctionDefinition function; | |
49 function = builder.buildFunction(element); | |
50 | |
51 if (function != null) { | |
52 nodes[element] = function; | |
53 compiler.tracer.traceCompilation(element.name, null); | |
54 compiler.tracer.traceGraph("IR Builder", function); | |
55 } | |
56 }); | |
57 } | |
58 }); | |
59 }); | |
60 } | |
61 | |
62 bool irEnabled({bool useNewBackend: false}) { | |
63 // TODO(sigurdm,kmillikin): Support checked-mode checks. | |
64 return (useNewBackend || const bool.fromEnvironment('USE_NEW_BACKEND')) && | |
65 compiler.backend is DartBackend && | |
66 !compiler.enableTypeAssertions && | |
67 !compiler.enableConcreteTypeInference; | |
68 } | |
69 | |
70 bool canBuild(Element element) { | |
71 FunctionElement function = element.asFunctionElement(); | |
72 // TODO(kmillikin,sigurdm): support lazy field initializers. | |
73 if (function == null) return false; | |
74 | |
75 if (!compiler.backend.shouldOutput(function)) return false; | |
76 | |
77 assert(invariant(element, !function.isNative)); | |
78 | |
79 // TODO(kmillikin,sigurdm): Support constructors. | |
80 if (function is ConstructorElement) return false; | |
81 | |
82 return true; | |
83 } | |
84 | |
85 bool get inCheckedMode { | |
86 bool result = false; | |
87 assert((result = true)); | |
88 return result; | |
89 } | |
90 | |
91 SourceFile elementSourceFile(Element element) { | |
92 if (element is FunctionElement) { | |
93 FunctionElement functionElement = element; | |
94 if (functionElement.patch != null) element = functionElement.patch; | |
95 } | |
96 return element.compilationUnit.script.file; | |
97 } | |
98 } | |
99 | |
100 class _GetterElements { | |
101 ir.Primitive result; | |
102 ir.Primitive index; | |
103 ir.Primitive receiver; | |
104 | |
105 _GetterElements({this.result, this.index, this.receiver}) ; | |
106 } | |
107 | |
108 /** | |
109 * A tree visitor that builds [IrNodes]. The visit methods add statements using | |
110 * to the [builder] and return the last added statement for trees that represent | |
111 * an expression. | |
112 */ | |
113 class IrBuilderVisitor extends ResolvedVisitor<ir.Primitive> | |
114 with IrBuilderMixin<ast.Node> { | |
115 final Compiler compiler; | |
116 final SourceFile sourceFile; | |
117 | |
118 // In SSA terms, join-point continuation parameters are the phis and the | |
119 // continuation invocation arguments are the corresponding phi inputs. To | |
120 // support name introduction and renaming for source level variables, we use | |
121 // nested (delimited) visitors for constructing subparts of the IR that will | |
122 // need renaming. Each source variable is assigned an index. | |
123 // | |
124 // Each nested visitor maintains a list of free variable uses in the body. | |
125 // These are implemented as a list of parameters, each with their own use | |
126 // list of references. When the delimited subexpression is plugged into the | |
127 // surrounding context, the free occurrences can be captured or become free | |
128 // occurrences in the next outer delimited subexpression. | |
129 // | |
130 // Each nested visitor maintains a list that maps indexes of variables | |
131 // assigned in the delimited subexpression to their reaching definition --- | |
132 // that is, the definition in effect at the hole in 'current'. These are | |
133 // used to determine if a join-point continuation needs to be passed | |
134 // arguments, and what the arguments are. | |
135 | |
136 /// Construct a top-level visitor. | |
137 IrBuilderVisitor(TreeElements elements, this.compiler, this.sourceFile) | |
138 : super(elements); | |
139 | |
140 /** | |
141 * Builds the [ir.FunctionDefinition] for a function element. In case the | |
142 * function uses features that cannot be expressed in the IR, this function | |
143 * returns `null`. | |
144 */ | |
145 ir.FunctionDefinition buildFunction(FunctionElement functionElement) { | |
146 return nullIfGiveup(() => buildFunctionInternal(functionElement)); | |
147 } | |
148 | |
149 ir.FunctionDefinition buildFunctionInternal(FunctionElement element) { | |
150 assert(invariant(element, element.isImplementation)); | |
151 ast.FunctionExpression function = element.node; | |
152 assert(function != null); | |
153 assert(!function.modifiers.isExternal); | |
154 assert(elements[function] != null); | |
155 | |
156 DetectClosureVariables closureLocals = new DetectClosureVariables(elements); | |
157 closureLocals.visit(function); | |
158 | |
159 return withBuilder( | |
160 new IrBuilder(compiler.backend.constantSystem, | |
161 element, closureLocals.usedFromClosure), | |
162 () { | |
163 FunctionSignature signature = element.functionSignature; | |
164 signature.orderedForEachParameter((ParameterElement parameterElement) { | |
165 irBuilder.createParameter(parameterElement); | |
166 }); | |
167 | |
168 List<ConstantExpression> defaults = new List<ConstantExpression>(); | |
169 signature.orderedOptionalParameters.forEach((ParameterElement element) { | |
170 defaults.add(getConstantForVariable(element)); | |
171 }); | |
172 | |
173 visit(function.body); | |
174 return irBuilder.buildFunctionDefinition(element, defaults); | |
175 }); | |
176 } | |
177 | |
178 ir.Primitive visit(ast.Node node) => node.accept(this); | |
179 | |
180 // ==== Statements ==== | |
181 visitBlock(ast.Block node) { | |
182 irBuilder.buildBlock(node.statements.nodes, build); | |
183 } | |
184 | |
185 ir.Primitive visitBreakStatement(ast.BreakStatement node) { | |
186 if (!irBuilder.buildBreak(elements.getTargetOf(node))) { | |
187 compiler.internalError(node, "'break' target not found"); | |
188 } | |
189 return null; | |
190 } | |
191 | |
192 ir.Primitive visitContinueStatement(ast.ContinueStatement node) { | |
193 if (!irBuilder.buildContinue(elements.getTargetOf(node))) { | |
194 compiler.internalError(node, "'continue' target not found"); | |
195 } | |
196 return null; | |
197 } | |
198 | |
199 // Build(EmptyStatement, C) = C | |
200 ir.Primitive visitEmptyStatement(ast.EmptyStatement node) { | |
201 assert(irBuilder.isOpen); | |
202 return null; | |
203 } | |
204 | |
205 // Build(ExpressionStatement(e), C) = C' | |
206 // where (C', _) = Build(e, C) | |
207 ir.Primitive visitExpressionStatement(ast.ExpressionStatement node) { | |
208 assert(irBuilder.isOpen); | |
209 visit(node.expression); | |
210 return null; | |
211 } | |
212 | |
213 visitFor(ast.For node) { | |
214 // TODO(kmillikin,sigurdm): Handle closure variables declared in a for-loop. | |
215 if (node.initializer is ast.VariableDefinitions) { | |
216 ast.VariableDefinitions definitions = node.initializer; | |
217 for (ast.Node definition in definitions.definitions.nodes) { | |
218 Element element = elements[definition]; | |
219 if (irBuilder.isClosureVariable(element)) { | |
220 return giveup(definition, 'Closure variable in for loop initializer'); | |
221 } | |
222 } | |
223 } | |
224 | |
225 JumpTarget target = elements.getTargetDefinition(node); | |
226 irBuilder.buildFor( | |
227 buildInitializer: subbuild(node.initializer), | |
228 buildCondition: subbuild(node.condition), | |
229 buildBody: subbuild(node.body), | |
230 buildUpdate: subbuildSequence(node.update), | |
231 target: target); | |
232 } | |
233 | |
234 visitIf(ast.If node) { | |
235 irBuilder.buildIf( | |
236 build(node.condition), | |
237 subbuild(node.thenPart), | |
238 subbuild(node.elsePart)); | |
239 } | |
240 | |
241 ir.Primitive visitLabeledStatement(ast.LabeledStatement node) { | |
242 ast.Statement body = node.statement; | |
243 return body is ast.Loop | |
244 ? visit(body) | |
245 : giveup(node, 'labeled statement'); | |
246 } | |
247 | |
248 visitWhile(ast.While node) { | |
249 irBuilder.buildWhile( | |
250 buildCondition: subbuild(node.condition), | |
251 buildBody: subbuild(node.body), | |
252 target: elements.getTargetDefinition(node)); | |
253 } | |
254 | |
255 visitForIn(ast.ForIn node) { | |
256 // [node.declaredIdentifier] can be either an [ast.VariableDefinitions] | |
257 // (defining a new local variable) or a send designating some existing | |
258 // variable. | |
259 ast.Node identifier = node.declaredIdentifier; | |
260 ast.VariableDefinitions variableDeclaration = | |
261 identifier.asVariableDefinitions(); | |
262 Element variableElement = elements.getForInVariable(node); | |
263 Selector selector = elements.getSelector(identifier); | |
264 | |
265 irBuilder.buildForIn( | |
266 buildExpression: subbuild(node.expression), | |
267 buildVariableDeclaration: subbuild(variableDeclaration), | |
268 variableElement: variableElement, | |
269 variableSelector: selector, | |
270 buildBody: subbuild(node.body), | |
271 target: elements.getTargetDefinition(node)); | |
272 } | |
273 | |
274 ir.Primitive visitVariableDefinitions(ast.VariableDefinitions node) { | |
275 assert(irBuilder.isOpen); | |
276 if (node.modifiers.isConst) { | |
277 for (ast.SendSet definition in node.definitions.nodes) { | |
278 assert(!definition.arguments.isEmpty); | |
279 assert(definition.arguments.tail.isEmpty); | |
280 VariableElement element = elements[definition]; | |
281 ConstantExpression value = getConstantForVariable(element); | |
282 irBuilder.declareLocalConstant(element, value); | |
283 } | |
284 } else { | |
285 for (ast.Node definition in node.definitions.nodes) { | |
286 Element element = elements[definition]; | |
287 ir.Primitive initialValue; | |
288 // Definitions are either SendSets if there is an initializer, or | |
289 // Identifiers if there is no initializer. | |
290 if (definition is ast.SendSet) { | |
291 assert(!definition.arguments.isEmpty); | |
292 assert(definition.arguments.tail.isEmpty); | |
293 initialValue = visit(definition.arguments.head); | |
294 } else { | |
295 assert(definition is ast.Identifier); | |
296 } | |
297 irBuilder.declareLocalVariable(element, initialValue: initialValue); | |
298 } | |
299 } | |
300 return null; | |
301 } | |
302 | |
303 // Build(Return(e), C) = C'[InvokeContinuation(return, x)] | |
304 // where (C', x) = Build(e, C) | |
305 // | |
306 // Return without a subexpression is translated as if it were return null. | |
307 ir.Primitive visitReturn(ast.Return node) { | |
308 assert(irBuilder.isOpen); | |
309 assert(invariant(node, node.beginToken.value != 'native')); | |
310 irBuilder.buildReturn(build(node.expression)); | |
311 return null; | |
312 } | |
313 | |
314 // ==== Expressions ==== | |
315 ir.Primitive visitConditional(ast.Conditional node) { | |
316 return irBuilder.buildConditional( | |
317 build(node.condition), | |
318 subbuild(node.thenExpression), | |
319 subbuild(node.elseExpression)); | |
320 } | |
321 | |
322 // For all simple literals: | |
323 // Build(Literal(c), C) = C[let val x = Constant(c) in [], x] | |
324 ir.Primitive visitLiteralBool(ast.LiteralBool node) { | |
325 assert(irBuilder.isOpen); | |
326 return translateConstant(node); | |
327 } | |
328 | |
329 ir.Primitive visitLiteralDouble(ast.LiteralDouble node) { | |
330 assert(irBuilder.isOpen); | |
331 return translateConstant(node); | |
332 } | |
333 | |
334 ir.Primitive visitLiteralInt(ast.LiteralInt node) { | |
335 assert(irBuilder.isOpen); | |
336 return translateConstant(node); | |
337 } | |
338 | |
339 ir.Primitive visitLiteralNull(ast.LiteralNull node) { | |
340 assert(irBuilder.isOpen); | |
341 return translateConstant(node); | |
342 } | |
343 | |
344 ir.Primitive visitLiteralString(ast.LiteralString node) { | |
345 assert(irBuilder.isOpen); | |
346 return translateConstant(node); | |
347 } | |
348 | |
349 ConstantExpression getConstantForNode(ast.Node node) { | |
350 ConstantExpression constant = | |
351 compiler.backend.constantCompilerTask.compileNode(node, elements); | |
352 assert(invariant(node, constant != null, | |
353 message: 'No constant computed for $node')); | |
354 return constant; | |
355 } | |
356 | |
357 ConstantExpression getConstantForVariable(VariableElement element) { | |
358 ConstantExpression constant = | |
359 compiler.backend.constants.getConstantForVariable(element); | |
360 assert(invariant(element, constant != null, | |
361 message: 'No constant computed for $element')); | |
362 return constant; | |
363 } | |
364 | |
365 ir.Primitive visitLiteralList(ast.LiteralList node) { | |
366 if (node.isConst) { | |
367 return translateConstant(node); | |
368 } | |
369 List<ir.Primitive> values = node.elements.nodes.mapToList(visit); | |
370 InterfaceType type = elements.getType(node); | |
371 return irBuilder.buildListLiteral(type, values); | |
372 } | |
373 | |
374 ir.Primitive visitLiteralMap(ast.LiteralMap node) { | |
375 if (node.isConst) { | |
376 return translateConstant(node); | |
377 } | |
378 InterfaceType type = elements.getType(node); | |
379 return irBuilder.buildMapLiteral( | |
380 type, | |
381 node.entries.nodes.map((e) => e.key), | |
382 node.entries.nodes.map((e) => e.value), | |
383 build); | |
384 } | |
385 | |
386 ir.Primitive visitLiteralSymbol(ast.LiteralSymbol node) { | |
387 assert(irBuilder.isOpen); | |
388 return translateConstant(node); | |
389 } | |
390 | |
391 ir.Primitive visitIdentifier(ast.Identifier node) { | |
392 // "this" is the only identifier that should be met by the visitor. | |
393 assert(node.isThis()); | |
394 return irBuilder.buildThis(); | |
395 } | |
396 | |
397 ir.Primitive visitParenthesizedExpression( | |
398 ast.ParenthesizedExpression node) { | |
399 assert(irBuilder.isOpen); | |
400 return visit(node.expression); | |
401 } | |
402 | |
403 // Stores the result of visiting a CascadeReceiver, so we can return it from | |
404 // its enclosing Cascade. | |
405 ir.Primitive _currentCascadeReceiver; | |
406 | |
407 ir.Primitive visitCascadeReceiver(ast.CascadeReceiver node) { | |
408 assert(irBuilder.isOpen); | |
409 return _currentCascadeReceiver = visit(node.expression); | |
410 } | |
411 | |
412 ir.Primitive visitCascade(ast.Cascade node) { | |
413 assert(irBuilder.isOpen); | |
414 var oldCascadeReceiver = _currentCascadeReceiver; | |
415 // Throw away the result of visiting the expression. | |
416 // Instead we return the result of visiting the CascadeReceiver. | |
417 this.visit(node.expression); | |
418 ir.Primitive receiver = _currentCascadeReceiver; | |
419 _currentCascadeReceiver = oldCascadeReceiver; | |
420 return receiver; | |
421 } | |
422 | |
423 // ==== Sends ==== | |
424 ir.Primitive visitAssert(ast.Send node) { | |
425 assert(irBuilder.isOpen); | |
426 return giveup(node, 'Assert'); | |
427 } | |
428 | |
429 ir.Primitive visitNamedArgument(ast.NamedArgument node) { | |
430 assert(irBuilder.isOpen); | |
431 return visit(node.expression); | |
432 } | |
433 | |
434 ir.Primitive translateClosureCall(ir.Primitive receiver, | |
435 Selector closureSelector, | |
436 ast.NodeList arguments) { | |
437 Selector namedCallSelector = new Selector(closureSelector.kind, | |
438 "call", | |
439 closureSelector.library, | |
440 closureSelector.argumentCount, | |
441 closureSelector.namedArguments); | |
442 List<ir.Primitive> args = arguments.nodes.mapToList(visit, growable:false); | |
443 return irBuilder.buildDynamicInvocation(receiver, namedCallSelector, args); | |
444 } | |
445 | |
446 ir.Primitive visitClosureSend(ast.Send node) { | |
447 assert(irBuilder.isOpen); | |
448 Element element = elements[node]; | |
449 ir.Primitive closureTarget; | |
450 if (element == null) { | |
451 closureTarget = visit(node.selector); | |
452 } else if (irBuilder.isClosureVariable(element)) { | |
453 LocalElement local = element; | |
454 closureTarget = new ir.GetClosureVariable(local); | |
455 irBuilder.add(new ir.LetPrim(closureTarget)); | |
456 } else { | |
457 assert(Elements.isLocal(element)); | |
458 closureTarget = irBuilder.environment.lookup(element); | |
459 } | |
460 Selector closureSelector = elements.getSelector(node); | |
461 return translateClosureCall(closureTarget, closureSelector, | |
462 node.argumentsNode); | |
463 } | |
464 | |
465 /// If [node] is null, returns this. | |
466 /// If [node] is super, returns null (for special handling) | |
467 /// Otherwise visits [node] and returns the result. | |
468 ir.Primitive visitReceiver(ast.Expression node) { | |
469 if (node == null) return irBuilder.buildThis(); | |
470 if (node.isSuper()) return null; | |
471 return visit(node); | |
472 } | |
473 | |
474 /// Returns `true` if [node] is a super call. | |
475 // TODO(johnniwinther): Remove the need for this. | |
476 bool isSuperCall(ast.Send node) { | |
477 return node != null && node.receiver != null && node.receiver.isSuper(); | |
478 } | |
479 | |
480 ir.Primitive visitDynamicSend(ast.Send node) { | |
481 assert(irBuilder.isOpen); | |
482 Selector selector = elements.getSelector(node); | |
483 ir.Primitive receiver = visitReceiver(node.receiver); | |
484 List<ir.Primitive> arguments = new List<ir.Primitive>(); | |
485 for (ast.Node n in node.arguments) { | |
486 arguments.add(visit(n)); | |
487 } | |
488 return irBuilder.buildDynamicInvocation(receiver, selector, arguments); | |
489 } | |
490 | |
491 _GetterElements translateGetter(ast.Send node, Selector selector) { | |
492 Element element = elements[node]; | |
493 ir.Primitive result; | |
494 ir.Primitive receiver; | |
495 ir.Primitive index; | |
496 | |
497 if (element != null && element.isConst) { | |
498 // Reference to constant local, top-level or static field | |
499 result = translateConstant(node); | |
500 } else if (Elements.isLocal(element)) { | |
501 // Reference to local variable | |
502 result = irBuilder.buildLocalGet(element); | |
503 } else if (element == null || | |
504 Elements.isInstanceField(element) || | |
505 Elements.isInstanceMethod(element) || | |
506 selector.isIndex || | |
507 // TODO(johnniwinther): clean up semantics of resolution. | |
508 node.isSuperCall) { | |
509 // Dynamic dispatch to a getter. Sometimes resolution will suggest a | |
510 // target element, but in these cases we must still emit a dynamic | |
511 // dispatch. The target element may be an instance method in case we are | |
512 // converting a method to a function object. | |
513 | |
514 receiver = visitReceiver(node.receiver); | |
515 List<ir.Primitive> arguments = new List<ir.Primitive>(); | |
516 if (selector.isIndex) { | |
517 index = visit(node.arguments.head); | |
518 arguments.add(index); | |
519 } | |
520 | |
521 assert(selector.kind == SelectorKind.GETTER || | |
522 selector.kind == SelectorKind.INDEX); | |
523 if (isSuperCall(node)) { | |
524 result = irBuilder.buildSuperInvocation(selector, arguments); | |
525 } else { | |
526 result = | |
527 irBuilder.buildDynamicInvocation(receiver, selector, arguments); | |
528 } | |
529 } else if (element.isField || element.isGetter || element.isErroneous || | |
530 element.isSetter) { | |
531 // TODO(johnniwinther): Change handling of setter selectors. | |
532 // Access to a static field or getter (non-static case handled above). | |
533 // Even if there is only a setter, we compile as if it was a getter, | |
534 // so the vm can fail at runtime. | |
535 assert(selector.kind == SelectorKind.GETTER || | |
536 selector.kind == SelectorKind.SETTER); | |
537 result = irBuilder.buildStaticGet(element, selector); | |
538 } else if (Elements.isStaticOrTopLevelFunction(element)) { | |
539 // Convert a top-level or static function to a function object. | |
540 result = translateConstant(node); | |
541 } else { | |
542 throw "Unexpected SendSet getter: $node, $element"; | |
543 } | |
544 return new _GetterElements( | |
545 result: result,index: index, receiver: receiver); | |
546 } | |
547 | |
548 ir.Primitive visitGetterSend(ast.Send node) { | |
549 assert(irBuilder.isOpen); | |
550 return translateGetter(node, elements.getSelector(node)).result; | |
551 | |
552 } | |
553 | |
554 ir.Primitive translateLogicalOperator(ast.Operator op, | |
555 ast.Expression left, | |
556 ast.Expression right) { | |
557 ir.Primitive leftValue = visit(left); | |
558 | |
559 ir.Primitive buildRightValue(IrBuilder rightBuilder) { | |
560 return withBuilder(rightBuilder, () => visit(right)); | |
561 } | |
562 | |
563 return irBuilder.buildLogicalOperator( | |
564 leftValue, buildRightValue, isLazyOr: op.source == '||'); | |
565 } | |
566 | |
567 ir.Primitive visitOperatorSend(ast.Send node) { | |
568 assert(irBuilder.isOpen); | |
569 ast.Operator op = node.selector; | |
570 if (isUserDefinableOperator(op.source)) { | |
571 return visitDynamicSend(node); | |
572 } | |
573 if (op.source == '&&' || op.source == '||') { | |
574 assert(node.receiver != null); | |
575 assert(!node.arguments.isEmpty); | |
576 assert(node.arguments.tail.isEmpty); | |
577 return translateLogicalOperator(op, node.receiver, node.arguments.head); | |
578 } | |
579 if (op.source == "!") { | |
580 assert(node.receiver != null); | |
581 assert(node.arguments.isEmpty); | |
582 return irBuilder.buildNegation(visit(node.receiver)); | |
583 } | |
584 if (op.source == "!=") { | |
585 assert(node.receiver != null); | |
586 assert(!node.arguments.isEmpty); | |
587 assert(node.arguments.tail.isEmpty); | |
588 return irBuilder.buildNegation(visitDynamicSend(node)); | |
589 } | |
590 assert(invariant(node, op.source == "is" || op.source == "as", | |
591 message: "unexpected operator $op")); | |
592 DartType type = elements.getType(node.typeAnnotationFromIsCheckOrCast); | |
593 ir.Primitive receiver = visit(node.receiver); | |
594 return irBuilder.buildTypeOperator( | |
595 receiver, type, | |
596 isTypeTest: op.source == "is", | |
597 isNotCheck: node.isIsNotCheck); | |
598 } | |
599 | |
600 // Build(StaticSend(f, arguments), C) = C[C'[InvokeStatic(f, xs)]] | |
601 // where (C', xs) = arguments.fold(Build, C) | |
602 ir.Primitive visitStaticSend(ast.Send node) { | |
603 assert(irBuilder.isOpen); | |
604 Element element = elements[node]; | |
605 assert(!element.isConstructor); | |
606 // TODO(lry): support foreign functions. | |
607 if (element.isForeign(compiler.backend)) { | |
608 return giveup(node, 'StaticSend: foreign'); | |
609 } | |
610 | |
611 Selector selector = elements.getSelector(node); | |
612 | |
613 // TODO(lry): support default arguments, need support for locals. | |
614 List<ir.Definition> arguments = node.arguments.mapToList(visit, | |
615 growable:false); | |
616 return irBuilder.buildStaticInvocation(element, selector, arguments); | |
617 } | |
618 | |
619 ir.Primitive visitSuperSend(ast.Send node) { | |
620 assert(irBuilder.isOpen); | |
621 if (node.isPropertyAccess) { | |
622 return visitGetterSend(node); | |
623 } else { | |
624 Selector selector = elements.getSelector(node); | |
625 List<ir.Primitive> arguments = new List<ir.Primitive>(); | |
626 for (ast.Node n in node.arguments) { | |
627 arguments.add(visit(n)); | |
628 } | |
629 return irBuilder.buildSuperInvocation(selector, arguments); | |
630 } | |
631 } | |
632 | |
633 visitTypePrefixSend(ast.Send node) { | |
634 compiler.internalError(node, "visitTypePrefixSend should not be called."); | |
635 } | |
636 | |
637 ir.Primitive visitTypeLiteralSend(ast.Send node) { | |
638 assert(irBuilder.isOpen); | |
639 // If the user is trying to invoke the type literal or variable, | |
640 // it must be treated as a function call. | |
641 if (node.argumentsNode != null) { | |
642 // TODO(sigurdm): Handle this to match proposed semantics of issue #19725. | |
643 return giveup(node, 'Type literal invoked as function'); | |
644 } | |
645 | |
646 DartType type = elements.getTypeLiteralType(node); | |
647 if (type is TypeVariableType) { | |
648 ir.Primitive prim = new ir.ReifyTypeVar(type.element); | |
649 irBuilder.add(new ir.LetPrim(prim)); | |
650 return prim; | |
651 } else { | |
652 return translateConstant(node); | |
653 } | |
654 } | |
655 | |
656 ir.Primitive visitSendSet(ast.SendSet node) { | |
657 assert(irBuilder.isOpen); | |
658 Element element = elements[node]; | |
659 ast.Operator op = node.assignmentOperator; | |
660 // For complex operators, this is the result of getting (before assigning) | |
661 ir.Primitive originalValue; | |
662 // For []+= style operators, this saves the index. | |
663 ir.Primitive index; | |
664 ir.Primitive receiver; | |
665 // This is what gets assigned. | |
666 ir.Primitive valueToStore; | |
667 Selector selector = elements.getSelector(node); | |
668 Selector operatorSelector = | |
669 elements.getOperatorSelectorInComplexSendSet(node); | |
670 Selector getterSelector = | |
671 elements.getGetterSelectorInComplexSendSet(node); | |
672 assert( | |
673 // Indexing send-sets have an argument for the index. | |
674 (selector.isIndexSet ? 1 : 0) + | |
675 // Non-increment send-sets have one more argument. | |
676 (ast.Operator.INCREMENT_OPERATORS.contains(op.source) ? 0 : 1) | |
677 == node.argumentCount()); | |
678 | |
679 ast.Node getAssignArgument() { | |
680 assert(invariant(node, !node.arguments.isEmpty, | |
681 message: "argument expected")); | |
682 return selector.isIndexSet | |
683 ? node.arguments.tail.head | |
684 : node.arguments.head; | |
685 } | |
686 | |
687 // Get the value into valueToStore | |
688 if (op.source == "=") { | |
689 if (selector.isIndexSet) { | |
690 receiver = visitReceiver(node.receiver); | |
691 index = visit(node.arguments.head); | |
692 } else if (element == null || Elements.isInstanceField(element)) { | |
693 receiver = visitReceiver(node.receiver); | |
694 } | |
695 valueToStore = visit(getAssignArgument()); | |
696 } else { | |
697 // Get the original value into getter | |
698 assert(ast.Operator.COMPLEX_OPERATORS.contains(op.source)); | |
699 | |
700 _GetterElements getterResult = translateGetter(node, getterSelector); | |
701 index = getterResult.index; | |
702 receiver = getterResult.receiver; | |
703 originalValue = getterResult.result; | |
704 | |
705 // Do the modification of the value in getter. | |
706 ir.Primitive arg; | |
707 if (ast.Operator.INCREMENT_OPERATORS.contains(op.source)) { | |
708 arg = irBuilder.buildIntegerLiteral(1); | |
709 } else { | |
710 arg = visit(getAssignArgument()); | |
711 } | |
712 valueToStore = new ir.Parameter(null); | |
713 ir.Continuation k = new ir.Continuation([valueToStore]); | |
714 ir.Expression invoke = | |
715 new ir.InvokeMethod(originalValue, operatorSelector, k, [arg]); | |
716 irBuilder.add(new ir.LetCont(k, invoke)); | |
717 } | |
718 | |
719 if (Elements.isLocal(element)) { | |
720 irBuilder.buildLocalSet(element, valueToStore); | |
721 } else if ((!node.isSuperCall && Elements.isErroneousElement(element)) || | |
722 Elements.isStaticOrTopLevel(element)) { | |
723 irBuilder.buildStaticSet( | |
724 element, elements.getSelector(node), valueToStore); | |
725 } else { | |
726 // Setter or index-setter invocation | |
727 Selector selector = elements.getSelector(node); | |
728 assert(selector.kind == SelectorKind.SETTER || | |
729 selector.kind == SelectorKind.INDEX); | |
730 if (selector.isIndexSet) { | |
731 if (isSuperCall(node)) { | |
732 irBuilder.buildSuperIndexSet(index, valueToStore); | |
733 } else { | |
734 irBuilder.buildDynamicIndexSet(receiver, index, valueToStore); | |
735 } | |
736 } else { | |
737 if (isSuperCall(node)) { | |
738 irBuilder.buildSuperSet(selector, valueToStore); | |
739 } else { | |
740 irBuilder.buildDynamicSet(receiver, selector, valueToStore); | |
741 } | |
742 } | |
743 } | |
744 | |
745 if (node.isPostfix) { | |
746 assert(originalValue != null); | |
747 return originalValue; | |
748 } else { | |
749 return valueToStore; | |
750 } | |
751 } | |
752 | |
753 ir.Primitive visitNewExpression(ast.NewExpression node) { | |
754 if (node.isConst) { | |
755 return translateConstant(node); | |
756 } | |
757 FunctionElement element = elements[node.send]; | |
758 Selector selector = elements.getSelector(node.send); | |
759 DartType type = elements.getType(node); | |
760 ast.Node selectorNode = node.send.selector; | |
761 List<ir.Definition> arguments = | |
762 node.send.arguments.mapToList(visit, growable:false); | |
763 return irBuilder.buildConstructorInvocation( | |
764 element, selector, type, arguments); | |
765 } | |
766 | |
767 ir.Primitive visitStringJuxtaposition(ast.StringJuxtaposition node) { | |
768 assert(irBuilder.isOpen); | |
769 ir.Primitive first = visit(node.first); | |
770 ir.Primitive second = visit(node.second); | |
771 return irBuilder.buildStringConcatenation([first, second]); | |
772 } | |
773 | |
774 ir.Primitive visitStringInterpolation(ast.StringInterpolation node) { | |
775 assert(irBuilder.isOpen); | |
776 List<ir.Primitive> arguments = []; | |
777 arguments.add(visitLiteralString(node.string)); | |
778 var it = node.parts.iterator; | |
779 while (it.moveNext()) { | |
780 ast.StringInterpolationPart part = it.current; | |
781 arguments.add(visit(part.expression)); | |
782 arguments.add(visitLiteralString(part.string)); | |
783 } | |
784 return irBuilder.buildStringConcatenation(arguments); | |
785 } | |
786 | |
787 ir.Primitive translateConstant(ast.Node node, [ConstantExpression constant]) { | |
788 assert(irBuilder.isOpen); | |
789 if (constant == null) { | |
790 constant = getConstantForNode(node); | |
791 } | |
792 return irBuilder.buildConstantLiteral(constant); | |
793 } | |
794 | |
795 ir.FunctionDefinition makeSubFunction(ast.FunctionExpression node) { | |
796 // TODO(johnniwinther): Share the visitor. | |
797 return new IrBuilderVisitor(elements, compiler, sourceFile) | |
798 .buildFunctionInternal(elements[node]); | |
799 } | |
800 | |
801 ir.Primitive visitFunctionExpression(ast.FunctionExpression node) { | |
802 FunctionElement element = elements[node]; | |
803 ir.FunctionDefinition inner = makeSubFunction(node); | |
804 ir.CreateFunction prim = new ir.CreateFunction(inner); | |
805 irBuilder.add(new ir.LetPrim(prim)); | |
806 return prim; | |
807 } | |
808 | |
809 ir.Primitive visitFunctionDeclaration(ast.FunctionDeclaration node) { | |
810 LocalFunctionElement element = elements[node.function]; | |
811 ir.FunctionDefinition inner = makeSubFunction(node.function); | |
812 if (irBuilder.isClosureVariable(element)) { | |
813 irBuilder.add(new ir.DeclareFunction(element, inner)); | |
814 } else { | |
815 ir.CreateFunction prim = new ir.CreateFunction(inner); | |
816 irBuilder.add(new ir.LetPrim(prim)); | |
817 irBuilder.environment.extend(element, prim); | |
818 prim.useElementAsHint(element); | |
819 } | |
820 return null; | |
821 } | |
822 | |
823 static final String ABORT_IRNODE_BUILDER = "IrNode builder aborted"; | |
824 | |
825 dynamic giveup(ast.Node node, [String reason]) { | |
826 throw ABORT_IRNODE_BUILDER; | |
827 } | |
828 | |
829 ir.FunctionDefinition nullIfGiveup(ir.FunctionDefinition action()) { | |
830 try { | |
831 return action(); | |
832 } catch(e, tr) { | |
833 if (e == ABORT_IRNODE_BUILDER) { | |
834 return null; | |
835 } | |
836 rethrow; | |
837 } | |
838 } | |
839 | |
840 void internalError(String reason, {ast.Node node}) { | |
841 giveup(node); | |
842 } | |
843 } | |
844 | |
845 /// Classifies local variables and local functions as 'closure variables'. | |
846 /// A closure variable is one that is accessed from an inner function nested | |
847 /// one or more levels inside the one that declares it. | |
848 class DetectClosureVariables extends ast.Visitor { | |
849 final TreeElements elements; | |
850 DetectClosureVariables(this.elements); | |
851 | |
852 FunctionElement currentFunction; | |
853 Set<Local> usedFromClosure = new Set<Local>(); | |
854 Set<FunctionElement> recursiveFunctions = new Set<FunctionElement>(); | |
855 | |
856 bool isClosureVariable(Entity entity) => usedFromClosure.contains(entity); | |
857 | |
858 void markAsClosureVariable(Local local) { | |
859 usedFromClosure.add(local); | |
860 } | |
861 | |
862 visit(ast.Node node) => node.accept(this); | |
863 | |
864 visitNode(ast.Node node) { | |
865 node.visitChildren(this); | |
866 } | |
867 | |
868 visitSend(ast.Send node) { | |
869 Element element = elements[node]; | |
870 if (Elements.isLocal(element) && | |
871 !element.isConst && | |
872 element.enclosingElement != currentFunction) { | |
873 LocalElement local = element; | |
874 markAsClosureVariable(local); | |
875 } | |
876 node.visitChildren(this); | |
877 } | |
878 | |
879 visitFunctionExpression(ast.FunctionExpression node) { | |
880 FunctionElement oldFunction = currentFunction; | |
881 currentFunction = elements[node]; | |
882 visit(node.body); | |
883 currentFunction = oldFunction; | |
884 } | |
885 } | |
OLD | NEW |