OLD | NEW |
| (Empty) |
1 // Copyright (c) 2015, the Dartino 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.md file. | |
4 | |
5 library fletchc.codegen_visitor; | |
6 | |
7 import 'package:compiler/src/resolution/semantic_visitor.dart'; | |
8 | |
9 import 'package:compiler/src/resolution/operators.dart' show | |
10 AssignmentOperator, | |
11 BinaryOperator, | |
12 IncDecOperator, | |
13 UnaryOperator; | |
14 | |
15 import 'package:compiler/src/constants/expressions.dart' show | |
16 BoolFromEnvironmentConstantExpression, | |
17 IntFromEnvironmentConstantExpression, | |
18 StringFromEnvironmentConstantExpression, | |
19 ConstantExpression, | |
20 ConstructedConstantExpression, | |
21 TypeConstantExpression; | |
22 | |
23 import 'package:compiler/src/resolution/tree_elements.dart' show | |
24 TreeElements; | |
25 | |
26 import 'package:compiler/src/util/util.dart' show | |
27 Link; | |
28 | |
29 import 'package:compiler/src/common/names.dart' show | |
30 Names, | |
31 Selectors; | |
32 | |
33 import 'package:compiler/src/universe/use.dart' show DynamicUse, StaticUse; | |
34 | |
35 import 'package:compiler/src/elements/elements.dart'; | |
36 import 'package:compiler/src/tree/tree.dart'; | |
37 import 'package:compiler/src/universe/call_structure.dart' show | |
38 CallStructure; | |
39 import 'package:compiler/src/universe/selector.dart' show | |
40 Selector; | |
41 import 'package:compiler/src/diagnostics/spannable.dart' show | |
42 Spannable; | |
43 import 'package:compiler/src/dart_types.dart'; | |
44 | |
45 import 'fletch_context.dart'; | |
46 | |
47 import 'fletch_backend.dart'; | |
48 | |
49 import 'fletch_constants.dart' show | |
50 FletchClassConstant, | |
51 FletchClassInstanceConstant; | |
52 | |
53 import 'fletch_function_builder.dart' show | |
54 FletchFunctionBuilder; | |
55 | |
56 import 'fletch_class_builder.dart' show | |
57 FletchClassBuilder; | |
58 | |
59 import 'fletch_selector.dart'; | |
60 | |
61 import '../fletch_system.dart'; | |
62 | |
63 import 'closure_environment.dart'; | |
64 | |
65 import '../incremental/fletchc_incremental.dart' show | |
66 IncrementalCompilationFailed; // TODO(ahe): Remove this import. | |
67 | |
68 import 'fletch_registry.dart' show | |
69 ClosureKind, | |
70 FletchRegistry; | |
71 | |
72 import 'package:compiler/src/diagnostics/diagnostic_listener.dart' show | |
73 DiagnosticMessage; | |
74 | |
75 import 'package:compiler/src/diagnostics/messages.dart' show | |
76 MessageKind; | |
77 | |
78 import 'package:compiler/src/constants/values.dart' show | |
79 ConstantValue; | |
80 | |
81 enum VisitState { | |
82 Value, | |
83 Effect, | |
84 Test, | |
85 } | |
86 | |
87 /** | |
88 * A reference to a local value, including how it should be used | |
89 * (loaded/stored). | |
90 */ | |
91 abstract class LocalValue { | |
92 final int slot; | |
93 final Element element; | |
94 LocalValue(this.slot, this.element); | |
95 | |
96 void initialize(BytecodeAssembler assembler); | |
97 | |
98 void load(BytecodeAssembler assembler); | |
99 | |
100 void loadRaw(BytecodeAssembler assembler) { | |
101 load(assembler); | |
102 } | |
103 | |
104 void store(BytecodeAssembler assembler); | |
105 } | |
106 | |
107 /** | |
108 * A reference to a local value that is boxed. | |
109 */ | |
110 class BoxedLocalValue extends LocalValue { | |
111 BoxedLocalValue(int slot, Element element) : super(slot, element); | |
112 | |
113 void initialize(BytecodeAssembler assembler) { | |
114 assembler.allocateBoxed(); | |
115 } | |
116 | |
117 void load(BytecodeAssembler assembler) { | |
118 assembler.loadBoxedSlot(slot); | |
119 } | |
120 | |
121 void loadRaw(BytecodeAssembler assembler) { | |
122 assembler.loadSlot(slot); | |
123 } | |
124 | |
125 void store(BytecodeAssembler assembler) { | |
126 assembler.storeBoxedSlot(slot); | |
127 } | |
128 | |
129 String toString() => "Boxed($element, $slot)"; | |
130 } | |
131 | |
132 /** | |
133 * A reference to a local value that is boxed. | |
134 */ | |
135 class UnboxedLocalValue extends LocalValue { | |
136 UnboxedLocalValue(int slot, Element element) : super(slot, element); | |
137 | |
138 void initialize(BytecodeAssembler assembler) {} | |
139 | |
140 void load(BytecodeAssembler assembler) { | |
141 assembler.loadSlot(slot); | |
142 } | |
143 | |
144 void store(BytecodeAssembler assembler) { | |
145 assembler.storeSlot(slot); | |
146 } | |
147 | |
148 String toString() => "Local($element, $slot)"; | |
149 } | |
150 | |
151 /** | |
152 * A reference to a local value that is boxed. | |
153 */ | |
154 class BoxedParameterValue extends LocalValue { | |
155 BoxedParameterValue( | |
156 int parameter, | |
157 Element element, | |
158 BytecodeAssembler assembler) | |
159 : super(assembler.computeParameterSlot(parameter), element); | |
160 | |
161 void initialize(BytecodeAssembler assembler) { | |
162 assembler.allocateBoxed(); | |
163 } | |
164 | |
165 void load(BytecodeAssembler assembler) { | |
166 assembler.loadBoxedParameterSlot(slot); | |
167 } | |
168 | |
169 void loadRaw(BytecodeAssembler assembler) { | |
170 assembler.loadParameterSlot(slot); | |
171 } | |
172 | |
173 void store(BytecodeAssembler assembler) { | |
174 assembler.storeBoxedParameterSlot(slot); | |
175 } | |
176 | |
177 String toString() => "BoxedParameter($element, $slot)"; | |
178 } | |
179 | |
180 /** | |
181 * A reference to a local value that is boxed. | |
182 */ | |
183 class UnboxedParameterValue extends LocalValue { | |
184 UnboxedParameterValue( | |
185 int parameter, | |
186 Element element, | |
187 BytecodeAssembler assembler) | |
188 : super(assembler.computeParameterSlot(parameter), element); | |
189 | |
190 void initialize(BytecodeAssembler assembler) {} | |
191 | |
192 void load(BytecodeAssembler assembler) { | |
193 assembler.loadParameterSlot(slot); | |
194 } | |
195 | |
196 void store(BytecodeAssembler assembler) { | |
197 assembler.storeParameterSlot(slot); | |
198 } | |
199 | |
200 String toString() => "Parameter($element, $slot)"; | |
201 } | |
202 | |
203 class JumpInfo { | |
204 final int stackSize; | |
205 final BytecodeLabel continueLabel; | |
206 final BytecodeLabel breakLabel; | |
207 JumpInfo(this.stackSize, this.continueLabel, this.breakLabel); | |
208 } | |
209 | |
210 class TryBlock { | |
211 final int stackSize; | |
212 final BytecodeLabel finallyLabel; | |
213 final BytecodeLabel finallyReturnLabel; | |
214 TryBlock(this.stackSize, this.finallyLabel, this.finallyReturnLabel); | |
215 } | |
216 | |
217 abstract class CodegenVisitor | |
218 extends SemanticVisitor | |
219 with GetBulkMixin, | |
220 SetBulkMixin, | |
221 ErrorBulkMixin, | |
222 InvokeBulkMixin, | |
223 IndexSetBulkMixin, | |
224 CompoundBulkMixin, | |
225 UnaryBulkMixin, | |
226 BaseBulkMixin, | |
227 BinaryBulkMixin, | |
228 PrefixBulkMixin, | |
229 PostfixBulkMixin, | |
230 NewBulkMixin, | |
231 VariableBulkMixin, | |
232 ParameterBulkMixin, | |
233 FunctionBulkMixin, | |
234 ConstructorBulkMixin, | |
235 InitializerBulkMixin, | |
236 BaseImplementationOfStaticsMixin, | |
237 BaseImplementationOfLocalsMixin, | |
238 SetIfNullBulkMixin | |
239 implements SemanticSendVisitor, SemanticDeclarationVisitor { | |
240 // A literal int can have up to 31 bits of information (32 minus sign). | |
241 static const int LITERAL_INT_MAX = 0x3FFFFFFF; | |
242 static const int MAX_INT64 = (1 << 63) - 1; | |
243 static const int MIN_INT64 = -(1 << 63); | |
244 | |
245 final FletchContext context; | |
246 | |
247 final ClosureEnvironment closureEnvironment; | |
248 | |
249 final ExecutableElement element; | |
250 | |
251 final FletchFunctionBuilder functionBuilder; | |
252 | |
253 final Map<Element, LocalValue> scope = <Element, LocalValue>{}; | |
254 | |
255 final Map<Node, JumpInfo> jumpInfo = <Node, JumpInfo>{}; | |
256 | |
257 // Stack of try blocks (inner-most first), in the lexical scope. | |
258 Link<TryBlock> tryBlockStack = const Link<TryBlock>(); | |
259 | |
260 VisitState visitState; | |
261 BytecodeLabel trueLabel; | |
262 BytecodeLabel falseLabel; | |
263 | |
264 // TODO(ajohnsen): Merge computation into constructor. | |
265 // The slot at which 'this' is stored. In closures, this is overwritten. | |
266 LocalValue thisValue; | |
267 | |
268 TreeElements initializerElements; | |
269 | |
270 List<Element> blockLocals = <Element>[]; | |
271 | |
272 /// A FunctionExpression in this set is a named local function declaration. | |
273 /// Many calls to such functions are statically bound. So if `f` is a named | |
274 /// local function declaration, `f()` doesn't need to be registered as a | |
275 /// dynamic send. | |
276 // TODO(ahe): Get rid of this by refactoring initializeLocal. See TODO there. | |
277 final Set<FunctionExpression> functionDeclarations = | |
278 new Set<FunctionExpression>(); | |
279 | |
280 CodegenVisitor(this.functionBuilder, | |
281 this.context, | |
282 TreeElements elements, | |
283 this.closureEnvironment, | |
284 this.element) | |
285 : super(elements) { | |
286 if (functionBuilder.isInstanceMember) { | |
287 thisValue = new UnboxedParameterValue(0, null, assembler); | |
288 } | |
289 } | |
290 | |
291 TreeElements get elements { | |
292 if (initializerElements != null) return initializerElements; | |
293 return super.elements; | |
294 } | |
295 | |
296 BytecodeAssembler get assembler => functionBuilder.assembler; | |
297 | |
298 SemanticSendVisitor get sendVisitor => this; | |
299 SemanticDeclarationVisitor get declVisitor => this; | |
300 | |
301 void compile(); | |
302 | |
303 ConstantExpression compileConstant( | |
304 Node node, | |
305 {TreeElements elements, | |
306 bool isConst}) { | |
307 if (elements == null) elements = this.elements; | |
308 return context.compileConstant(node, elements, isConst: isConst); | |
309 } | |
310 | |
311 ConstantExpression inspectConstant( | |
312 Node node, | |
313 {TreeElements elements, | |
314 bool isConst}) { | |
315 if (elements == null) elements = this.elements; | |
316 return context.inspectConstant(node, elements, isConst: isConst); | |
317 } | |
318 | |
319 bool isConstNull(Node node) { | |
320 ConstantExpression expression = inspectConstant(node, isConst: false); | |
321 if (expression == null) return false; | |
322 return context.getConstantValue(expression).isNull; | |
323 } | |
324 | |
325 int allocateConstantFromNode(Node node, {TreeElements elements}) { | |
326 ConstantExpression expression = compileConstant( | |
327 node, | |
328 elements: elements, | |
329 isConst: false); | |
330 return functionBuilder.allocateConstant( | |
331 context.getConstantValue(expression)); | |
332 } | |
333 | |
334 int allocateConstantClassInstance(int classId) { | |
335 var constant = new FletchClassInstanceConstant(classId); | |
336 context.markConstantUsed(constant); | |
337 return functionBuilder.allocateConstant(constant); | |
338 } | |
339 | |
340 int allocateStringConstant(String string) { | |
341 return functionBuilder.allocateConstant( | |
342 context.backend.constantSystem.createString( | |
343 new DartString.literal(string))); | |
344 } | |
345 | |
346 ClosureInfo get closureInfo => closureEnvironment.closures[element]; | |
347 | |
348 LocalValue createLocalValueFor( | |
349 LocalElement element, | |
350 {int slot, | |
351 bool isCapturedValueBoxed: true}) { | |
352 if (slot == null) slot = assembler.stackSize; | |
353 if (closureEnvironment.shouldBeBoxed(element)) { | |
354 if (isCapturedValueBoxed) { | |
355 return new BoxedLocalValue(slot, element); | |
356 } | |
357 LocalValue value = new BoxedLocalValue(assembler.stackSize, element); | |
358 assembler.loadSlot(slot); | |
359 value.initialize(assembler); | |
360 return value; | |
361 } | |
362 | |
363 return new UnboxedLocalValue(slot, element); | |
364 } | |
365 | |
366 LocalValue createLocalValueForParameter( | |
367 ParameterElement parameter, | |
368 int index, | |
369 {bool isCapturedValueBoxed: true}) { | |
370 // TODO(kasperl): Use [ParameterElement.constant] instead when | |
371 // [ConstantValue] can be computed on-the-fly from a [ConstantExpression]. | |
372 Expression initializer = parameter.initializer; | |
373 if (initializer != null) { | |
374 // If the parameter has an initializer expression, we ask the context | |
375 // to compile it right away to make sure we enqueue all dependent | |
376 // elements correctly before we start assembling the program. | |
377 context.compileConstant( | |
378 initializer, | |
379 parameter.memberContext.resolvedAst.elements, | |
380 isConst: true); | |
381 } | |
382 | |
383 if (closureEnvironment.shouldBeBoxed(parameter)) { | |
384 if (isCapturedValueBoxed) { | |
385 return new BoxedParameterValue(index, parameter, assembler); | |
386 } | |
387 LocalValue value = new BoxedLocalValue(assembler.stackSize, parameter); | |
388 assembler.loadParameter(index); | |
389 value.initialize(assembler); | |
390 return value; | |
391 } | |
392 return new UnboxedParameterValue(index, parameter, assembler); | |
393 } | |
394 | |
395 void pushVariableDeclaration(LocalValue value) { | |
396 scope[value.element] = value; | |
397 } | |
398 | |
399 void popVariableDeclaration(Element local) { | |
400 scope.remove(local); | |
401 } | |
402 | |
403 void registerDynamicUse(Selector selector); | |
404 | |
405 void registerStaticUse(StaticUse use); | |
406 | |
407 void registerInstantiatedClass(ClassElement klass); | |
408 | |
409 void registerIsCheck(DartType type); | |
410 | |
411 void registerLocalInvoke(LocalElement element, Selector selector); | |
412 | |
413 /// Register that [element] is a closure. This can happen for a tear-off, or | |
414 /// for local functions. See [ClosureKind] for more information about the | |
415 /// various kinds of implicit or explicit closurizations that can occur. | |
416 void registerClosurization(FunctionElement element, ClosureKind kind); | |
417 | |
418 int compileLazyFieldInitializer(FieldElement field); | |
419 | |
420 void invokeMethod(Node node, Selector selector) { | |
421 registerDynamicUse(selector); | |
422 String symbol = context.getSymbolFromSelector(selector); | |
423 int id = context.getSymbolId(symbol); | |
424 int arity = selector.argumentCount; | |
425 int fletchSelector = FletchSelector.encodeMethod(id, arity); | |
426 assembler.invokeMethod(fletchSelector, arity, selector.name); | |
427 } | |
428 | |
429 void invokeGetter(Node node, Name name) { | |
430 registerDynamicUse(new Selector.getter(name)); | |
431 String symbol = context.mangleName(name); | |
432 int id = context.getSymbolId(symbol); | |
433 int fletchSelector = FletchSelector.encodeGetter(id); | |
434 assembler.invokeMethod(fletchSelector, 0); | |
435 } | |
436 | |
437 void invokeSetter(Node node, Name name) { | |
438 registerDynamicUse(new Selector.setter(name)); | |
439 String symbol = context.mangleName(name); | |
440 int id = context.getSymbolId(symbol); | |
441 int fletchSelector = FletchSelector.encodeSetter(id); | |
442 assembler.invokeMethod(fletchSelector, 1); | |
443 } | |
444 | |
445 void invokeFactory(Node node, int constId, int arity) { | |
446 assembler.invokeFactory(constId, arity); | |
447 } | |
448 | |
449 void invokeStatic(Node node, int constId, int arity) { | |
450 assembler.invokeStatic(constId, arity); | |
451 } | |
452 | |
453 void generateIdentical(Node node) { | |
454 assembler.identical(); | |
455 } | |
456 | |
457 void generateIdenticalNonNumeric(Node node) { | |
458 assembler.identicalNonNumeric(); | |
459 } | |
460 | |
461 void generateReturn(Node node) { | |
462 assembler.ret(); | |
463 } | |
464 | |
465 void generateReturnNull(Node node) { | |
466 assembler.returnNull(); | |
467 } | |
468 | |
469 void generateThrow(Node node) { | |
470 assembler.emitThrow(); | |
471 } | |
472 | |
473 void generateSwitchCaseMatch(CaseMatch caseMatch, BytecodeLabel ifTrue) { | |
474 assembler.dup(); | |
475 int constId = allocateConstantFromNode(caseMatch.expression); | |
476 assembler.loadConst(constId); | |
477 // For debugging, ignore the equality checks in connection | |
478 // with case matches by not associating the calls with | |
479 // any node. | |
480 invokeMethod(null, new Selector.binaryOperator('==')); | |
481 assembler.branchIfTrue(ifTrue); | |
482 } | |
483 | |
484 FletchFunctionBase requireFunction(FunctionElement element) { | |
485 // TODO(johnniwinther): More precise use. | |
486 registerStaticUse(new StaticUse.foreignUse(element)); | |
487 return context.backend.getFunctionForElement(element); | |
488 } | |
489 | |
490 FletchFunctionBase requireConstructorInitializer( | |
491 ConstructorElement constructor) { | |
492 assert(constructor.isGenerativeConstructor); | |
493 registerInstantiatedClass(constructor.enclosingClass); | |
494 registerStaticUse(new StaticUse.foreignUse(constructor)); | |
495 return context.backend.getConstructorInitializerFunction(constructor); | |
496 } | |
497 | |
498 void doStaticFunctionInvoke( | |
499 Node node, | |
500 FletchFunctionBase function, | |
501 NodeList arguments, | |
502 CallStructure callStructure, | |
503 {bool factoryInvoke: false}) { | |
504 if (function.isInstanceMember) loadThis(); | |
505 FunctionSignature signature = function.signature; | |
506 int functionId; | |
507 int arity; | |
508 if (signature.hasOptionalParameters && | |
509 signature.optionalParametersAreNamed) { | |
510 if (FletchBackend.isExactParameterMatch(signature, callStructure)) { | |
511 functionId = function.functionId; | |
512 } else if (callStructure.signatureApplies(signature)) { | |
513 // TODO(ajohnsen): Inline parameter stub? | |
514 FletchFunctionBase stub = context.backend.createParameterStub( | |
515 function, | |
516 callStructure.callSelector); | |
517 functionId = stub.functionId; | |
518 } else { | |
519 doUnresolved(function.name); | |
520 return; | |
521 } | |
522 for (Node argument in arguments) { | |
523 visitForValue(argument); | |
524 } | |
525 arity = callStructure.argumentCount; | |
526 } else if (callStructure != null && | |
527 callStructure.namedArguments.isNotEmpty) { | |
528 doUnresolved(function.name); | |
529 return; | |
530 } else { | |
531 functionId = function.functionId; | |
532 arity = loadPositionalArguments(arguments, signature, function.name); | |
533 } | |
534 if (function.isInstanceMember) arity++; | |
535 int constId = functionBuilder.allocateConstantFromFunction(functionId); | |
536 if (factoryInvoke) { | |
537 invokeFactory(node, constId, arity); | |
538 } else { | |
539 invokeStatic(node, constId, arity); | |
540 } | |
541 } | |
542 | |
543 void loadThis() { | |
544 thisValue.load(assembler); | |
545 } | |
546 | |
547 /** | |
548 * Load the [arguments] for caling [function], with potential positional | |
549 * arguments. | |
550 * | |
551 * Return the number of arguments pushed onto the stack. | |
552 */ | |
553 int loadPositionalArguments( | |
554 NodeList arguments, | |
555 FunctionSignature signature, | |
556 String name) { | |
557 int argumentCount = 0; | |
558 Iterator<Node> it = arguments.iterator; | |
559 signature.orderedForEachParameter((ParameterElement parameter) { | |
560 if (it.moveNext()) { | |
561 visitForValue(it.current); | |
562 } else { | |
563 if (parameter.isOptional) { | |
564 doParameterInitializer(parameter); | |
565 } else { | |
566 doUnresolved(name); | |
567 } | |
568 } | |
569 argumentCount++; | |
570 }); | |
571 if (it.moveNext()) doUnresolved(name); | |
572 return argumentCount; | |
573 } | |
574 | |
575 void doParameterInitializer(ParameterElement parameter) { | |
576 Expression initializer = parameter.initializer; | |
577 if (initializer == null) { | |
578 assembler.loadLiteralNull(); | |
579 } else { | |
580 var previousElements = initializerElements; | |
581 initializerElements = parameter.resolvedAst.elements; | |
582 visitForValue(initializer); | |
583 initializerElements = previousElements; | |
584 } | |
585 } | |
586 | |
587 void doVisitForValue(Node node) { | |
588 VisitState oldState = visitState; | |
589 visitState = VisitState.Value; | |
590 node.accept(this); | |
591 visitState = oldState; | |
592 } | |
593 | |
594 // Visit the expression [node] and push the result on top of the stack. | |
595 void visitForValue(Node node) { | |
596 doVisitForValue(node); | |
597 } | |
598 | |
599 // Visit the expression [node] and push the result on top of the stack. | |
600 // This method bypasses debug information collection and using this | |
601 // method will not generate breakpoints for the expression evaluation. | |
602 // This is useful when dealing with internal details that the programmer | |
603 // shouldn't care about such as the string concatenation aspects of | |
604 // of string interpolation. | |
605 void visitForValueNoDebugInfo(Node node) { | |
606 doVisitForValue(node); | |
607 } | |
608 | |
609 // Visit the expression [node] without pushing the result on top of the stack. | |
610 void visitForEffect(Node node) { | |
611 VisitState oldState = visitState; | |
612 visitState = VisitState.Effect; | |
613 node.accept(this); | |
614 visitState = oldState; | |
615 } | |
616 | |
617 // Visit the expression [node] with the result being a branch to either | |
618 // [trueLabel] or [falseLabel]. | |
619 void visitForTest( | |
620 Node node, | |
621 BytecodeLabel trueLabel, | |
622 BytecodeLabel falseLabel) { | |
623 VisitState oldState = visitState; | |
624 visitState = VisitState.Test; | |
625 BytecodeLabel oldTrueLabel = this.trueLabel; | |
626 this.trueLabel = trueLabel; | |
627 BytecodeLabel oldFalseLabel = this.falseLabel; | |
628 this.falseLabel = falseLabel; | |
629 | |
630 assert(trueLabel != null || falseLabel != null); | |
631 node.accept(this); | |
632 | |
633 visitState = oldState; | |
634 this.trueLabel = oldTrueLabel; | |
635 this.falseLabel = oldFalseLabel; | |
636 } | |
637 | |
638 void negateTest() { | |
639 assert(visitState == VisitState.Test); | |
640 BytecodeLabel temporary = trueLabel; | |
641 trueLabel = falseLabel; | |
642 falseLabel = temporary; | |
643 } | |
644 | |
645 void applyVisitState() { | |
646 if (visitState == VisitState.Effect) { | |
647 assembler.pop(); | |
648 } else if (visitState == VisitState.Test) { | |
649 if (trueLabel == null) { | |
650 assembler.branchIfFalse(falseLabel); | |
651 } else if (falseLabel == null) { | |
652 assembler.branchIfTrue(trueLabel); | |
653 } else { | |
654 assembler.branchIfTrue(trueLabel); | |
655 assembler.branch(falseLabel); | |
656 } | |
657 } | |
658 } | |
659 | |
660 void visitNamedArgument(NamedArgument node) { | |
661 Expression expression = node.expression; | |
662 if (expression != null) { | |
663 visitForValue(expression); | |
664 } else { | |
665 assembler.loadLiteralNull(); | |
666 } | |
667 applyVisitState(); | |
668 } | |
669 | |
670 void doLocalVariableCompound( | |
671 Node node, | |
672 LocalVariableElement variable, | |
673 AssignmentOperator operator, | |
674 Node rhs) { | |
675 LocalValue value = scope[variable]; | |
676 value.load(assembler); | |
677 visitForValue(rhs); | |
678 String operatorName = operator.binaryOperator.name; | |
679 invokeMethod(node, new Selector.binaryOperator(operatorName)); | |
680 value.store(assembler); | |
681 } | |
682 | |
683 void visitLocalVariableCompound( | |
684 Send node, | |
685 LocalVariableElement variable, | |
686 AssignmentOperator operator, | |
687 Node rhs, | |
688 _) { | |
689 doLocalVariableCompound(node, variable, operator, rhs); | |
690 applyVisitState(); | |
691 } | |
692 | |
693 void visitParameterCompound( | |
694 Send node, | |
695 LocalParameterElement parameter, | |
696 AssignmentOperator operator, | |
697 Node rhs, | |
698 _) { | |
699 doLocalVariableCompound(node, parameter, operator, rhs); | |
700 applyVisitState(); | |
701 } | |
702 | |
703 void doStaticFieldCompound( | |
704 Node node, | |
705 FieldElement field, | |
706 AssignmentOperator operator, | |
707 Node rhs) { | |
708 doStaticFieldGet(field); | |
709 visitForValue(rhs); | |
710 Selector selector = new Selector.binaryOperator( | |
711 operator.binaryOperator.name); | |
712 invokeMethod(node, selector); | |
713 doStaticFieldSet(field); | |
714 } | |
715 | |
716 void visitTopLevelFieldCompound( | |
717 Send node, | |
718 FieldElement field, | |
719 AssignmentOperator operator, | |
720 Node rhs, | |
721 _) { | |
722 doStaticFieldCompound(node, field, operator, rhs); | |
723 applyVisitState(); | |
724 } | |
725 | |
726 void visitStaticFieldCompound( | |
727 Send node, | |
728 FieldElement field, | |
729 AssignmentOperator operator, | |
730 Node rhs, | |
731 _) { | |
732 doStaticFieldCompound(node, field, operator, rhs); | |
733 applyVisitState(); | |
734 } | |
735 | |
736 void doBinaryOperator( | |
737 Node node, | |
738 Node left, | |
739 Node right, | |
740 BinaryOperator operator) { | |
741 visitForValue(left); | |
742 visitForValue(right); | |
743 // For '==', if either side is a null literal, use identicalNonNumeric. | |
744 if (operator == BinaryOperator.EQ && | |
745 (isConstNull(left) || isConstNull(right))) { | |
746 generateIdenticalNonNumeric(node); | |
747 return; | |
748 } | |
749 | |
750 Selector selector = new Selector.binaryOperator(operator.name); | |
751 invokeMethod(node, selector); | |
752 } | |
753 | |
754 void visitEquals( | |
755 Send node, | |
756 Node left, | |
757 Node right, | |
758 _) { | |
759 // TODO(ajohnsen): Inject null check (in callee). | |
760 doBinaryOperator(node, left, right, BinaryOperator.EQ); | |
761 applyVisitState(); | |
762 } | |
763 | |
764 void visitNotEquals( | |
765 Send node, | |
766 Node left, | |
767 Node right, | |
768 _) { | |
769 doBinaryOperator(node, left, right, BinaryOperator.EQ); | |
770 if (visitState == VisitState.Test) { | |
771 negateTest(); | |
772 } else { | |
773 assembler.negate(); | |
774 } | |
775 applyVisitState(); | |
776 } | |
777 | |
778 void visitBinary( | |
779 Send node, | |
780 Node left, | |
781 BinaryOperator operator, | |
782 Node right, | |
783 _) { | |
784 doBinaryOperator(node, left, right, operator); | |
785 applyVisitState(); | |
786 } | |
787 | |
788 void visitUnary( | |
789 Send node, | |
790 UnaryOperator operator, | |
791 Node value, | |
792 _) { | |
793 visitForValue(value); | |
794 Selector selector = new Selector.unaryOperator(operator.name); | |
795 invokeMethod(node, selector); | |
796 applyVisitState(); | |
797 } | |
798 | |
799 void visitNot( | |
800 Send node, | |
801 Node value, | |
802 _) { | |
803 if (visitState == VisitState.Test) { | |
804 visitForTest(value, falseLabel, trueLabel); | |
805 } else { | |
806 visitForValue(value); | |
807 assembler.negate(); | |
808 applyVisitState(); | |
809 } | |
810 } | |
811 | |
812 void visitIndex( | |
813 Send node, | |
814 Node receiver, | |
815 Node index, | |
816 _) { | |
817 visitForValue(receiver); | |
818 visitForValue(index); | |
819 Selector selector = new Selector.index(); | |
820 invokeMethod(node, selector); | |
821 applyVisitState(); | |
822 } | |
823 | |
824 void visitIndexSet( | |
825 SendSet node, | |
826 Node receiver, | |
827 Node index, | |
828 Node value, | |
829 _) { | |
830 visitForValue(receiver); | |
831 visitForValue(index); | |
832 visitForValue(value); | |
833 Selector selector = new Selector.indexSet(); | |
834 invokeMethod(node, selector); | |
835 applyVisitState(); | |
836 } | |
837 | |
838 void visitLogicalAnd( | |
839 Send node, | |
840 Node left, | |
841 Node right, | |
842 _) { | |
843 if (visitState == VisitState.Test) { | |
844 if (falseLabel == null) { | |
845 BytecodeLabel ifFalse = new BytecodeLabel(); | |
846 visitForTest(left, null, ifFalse); | |
847 visitForTest(right, trueLabel, null); | |
848 assembler.bind(ifFalse); | |
849 } else { | |
850 visitForTest(left, null, falseLabel); | |
851 visitForTest(right, trueLabel, falseLabel); | |
852 } | |
853 return; | |
854 } | |
855 | |
856 BytecodeLabel isFalse = new BytecodeLabel(); | |
857 assembler.loadLiteralFalse(); | |
858 | |
859 visitForTest(left, null, isFalse); | |
860 visitForTest(right, null, isFalse); | |
861 assembler.pop(); | |
862 assembler.loadLiteralTrue(); | |
863 | |
864 assembler.bind(isFalse); | |
865 applyVisitState(); | |
866 } | |
867 | |
868 void visitLogicalOr( | |
869 Send node, | |
870 Node left, | |
871 Node right, | |
872 _) { | |
873 if (visitState == VisitState.Test) { | |
874 if (trueLabel == null) { | |
875 BytecodeLabel ifTrue = new BytecodeLabel(); | |
876 visitForTest(left, ifTrue, null); | |
877 visitForTest(right, null, falseLabel); | |
878 assembler.bind(ifTrue); | |
879 } else { | |
880 visitForTest(left, trueLabel, null); | |
881 visitForTest(right, trueLabel, falseLabel); | |
882 } | |
883 return; | |
884 } | |
885 | |
886 BytecodeLabel isTrue = new BytecodeLabel(); | |
887 assembler.loadLiteralTrue(); | |
888 | |
889 visitForTest(left, isTrue, null); | |
890 visitForTest(right, isTrue, null); | |
891 assembler.pop(); | |
892 assembler.loadLiteralFalse(); | |
893 | |
894 assembler.bind(isTrue); | |
895 applyVisitState(); | |
896 } | |
897 | |
898 void visitConditional(Conditional node) { | |
899 BytecodeLabel isFalse = new BytecodeLabel(); | |
900 BytecodeLabel done = new BytecodeLabel(); | |
901 | |
902 assembler.loadLiteralNull(); | |
903 | |
904 visitForTest(node.condition, null, isFalse); | |
905 | |
906 assembler.pop(); | |
907 visitForValue(node.thenExpression); | |
908 assembler.branch(done); | |
909 | |
910 assembler.bind(isFalse); | |
911 assembler.pop(); | |
912 visitForValue(node.elseExpression); | |
913 | |
914 assembler.bind(done); | |
915 | |
916 applyVisitState(); | |
917 } | |
918 | |
919 void callIsSelector( | |
920 Node node, | |
921 DartType type, | |
922 // TODO(ahe): Remove [diagnosticLocation] when malformed types are | |
923 // handled. | |
924 Spannable diagnosticLocation) { | |
925 if (type == null || type.isMalformed) { | |
926 assembler.pop(); | |
927 generateUnimplementedError( | |
928 diagnosticLocation, "Unhandled type test for malformed $type."); | |
929 return; | |
930 } | |
931 | |
932 registerIsCheck(type); | |
933 | |
934 if (type.isDynamic) { | |
935 assembler.pop(); | |
936 assembler.loadLiteralTrue(); | |
937 return; | |
938 } | |
939 | |
940 if (type.isTypedef) { | |
941 // TODO(ajohnsen): This only matches with the number of arguments, not | |
942 // the actual argument types. | |
943 TypedefType typedefType = type; | |
944 int arity = typedefType.element.functionSignature.parameterCount; | |
945 int fletchSelector = context.toFletchIsSelector( | |
946 context.backend.compiler.coreClasses.functionClass, arity); | |
947 assembler.invokeTest(fletchSelector, 0); | |
948 return; | |
949 } | |
950 | |
951 if (!type.isInterfaceType) { | |
952 assembler.pop(); | |
953 generateUnimplementedError( | |
954 diagnosticLocation, "Unhandled type test for $type."); | |
955 return; | |
956 } | |
957 | |
958 Element element = type.element; | |
959 int fletchSelector = context.toFletchIsSelector(element); | |
960 assembler.invokeTest(fletchSelector, 0); | |
961 } | |
962 | |
963 void doIs( | |
964 Node node, | |
965 Node expression, | |
966 DartType type, | |
967 // TODO(ahe): Remove [diagnosticLocation] when callIsSelector does not | |
968 // require it. | |
969 Spannable diagnosticLocation) { | |
970 visitForValue(expression); | |
971 callIsSelector(node, type, diagnosticLocation); | |
972 } | |
973 | |
974 void visitIs( | |
975 Send node, | |
976 Node expression, | |
977 DartType type, | |
978 _) { | |
979 doIs(node, expression, type, node.arguments.first); | |
980 applyVisitState(); | |
981 } | |
982 | |
983 void visitIsNot( | |
984 Send node, | |
985 Node expression, | |
986 DartType type, | |
987 _) { | |
988 doIs(node, expression, type, node.arguments.first); | |
989 if (visitState == VisitState.Test) { | |
990 negateTest(); | |
991 } else { | |
992 assembler.negate(); | |
993 } | |
994 applyVisitState(); | |
995 } | |
996 | |
997 void visitAs( | |
998 Send node, | |
999 Node expression, | |
1000 DartType type, | |
1001 _) { | |
1002 // TODO(ajohnsen): To actual type check. | |
1003 visitForValue(expression); | |
1004 applyVisitState(); | |
1005 } | |
1006 | |
1007 void visitThisGet( | |
1008 Node node, | |
1009 _) { | |
1010 loadThis(); | |
1011 applyVisitState(); | |
1012 } | |
1013 | |
1014 void doIdenticalCall(Node node, NodeList arguments) { | |
1015 assert(arguments.slowLength() == 2); | |
1016 for (Node argument in arguments) { | |
1017 visitForValue(argument); | |
1018 } | |
1019 generateIdentical(node); | |
1020 } | |
1021 | |
1022 void handleStaticFunctionGet( | |
1023 Send node, | |
1024 MethodElement function, | |
1025 _) { | |
1026 registerClosurization(function, ClosureKind.tearOff); | |
1027 FletchFunctionBase target = requireFunction(function); | |
1028 FletchClassBuilder classBuilder = | |
1029 context.backend.createTearoffClass(target); | |
1030 assert(classBuilder.fields == 0); | |
1031 int constId = allocateConstantClassInstance(classBuilder.classId); | |
1032 assembler.loadConst(constId); | |
1033 applyVisitState(); | |
1034 } | |
1035 | |
1036 void doMainCall(Send node, NodeList arguments) { | |
1037 FunctionElement function = context.compiler.mainFunction; | |
1038 if (function.isMalformed) { | |
1039 DiagnosticMessage message = | |
1040 context.compiler.elementsWithCompileTimeErrors[function]; | |
1041 if (message == null) { | |
1042 // TODO(johnniwinther): The error should always be associated with the | |
1043 // element. | |
1044 // Example triggering this: | |
1045 // ``` | |
1046 // [ | |
1047 // main() {} | |
1048 // ``` | |
1049 message = context.compiler.reporter.createMessage( | |
1050 function, MessageKind.GENERIC, {'text': 'main is malformed.'}); | |
1051 } | |
1052 doCompileError(message); | |
1053 return; | |
1054 } | |
1055 if (context.compiler.libraryLoader.libraries.any(checkCompileError)) return; | |
1056 | |
1057 // Load up to 'parameterCount' arguments, padding with nulls. | |
1058 int parameterCount = function.functionSignature.parameterCount; | |
1059 int argumentCount = 0; | |
1060 for (Node argument in arguments) { | |
1061 if (argumentCount == parameterCount) break; | |
1062 visitForValue(argument); | |
1063 argumentCount++; | |
1064 } | |
1065 for (int i = argumentCount; i < parameterCount; i++) { | |
1066 assembler.loadLiteralNull(); | |
1067 } | |
1068 | |
1069 FletchFunctionBase base = requireFunction(function); | |
1070 int constId = functionBuilder.allocateConstantFromFunction(base.functionId); | |
1071 invokeStatic(node, constId, parameterCount); | |
1072 } | |
1073 | |
1074 void doStaticallyBoundInvoke( | |
1075 Send node, | |
1076 MethodElement element, | |
1077 NodeList arguments, | |
1078 CallStructure callStructure) { | |
1079 if (checkCompileError(element)) return; | |
1080 if (element.declaration == context.compiler.identicalFunction) { | |
1081 doIdenticalCall(node, arguments); | |
1082 return; | |
1083 } | |
1084 if (element.isExternal) { | |
1085 // Patch known functions directly. | |
1086 if (element == context.backend.fletchExternalInvokeMain) { | |
1087 doMainCall(node, arguments); | |
1088 return; | |
1089 } else if (element == context.backend.fletchExternalCoroutineChange) { | |
1090 for (Node argument in arguments) { | |
1091 visitForValue(argument); | |
1092 } | |
1093 assembler.coroutineChange(); | |
1094 return; | |
1095 } | |
1096 // TODO(ajohnsen): Define a known set of external functions we allow | |
1097 // calls to? | |
1098 } | |
1099 FletchFunctionBase target = requireFunction(element); | |
1100 doStaticFunctionInvoke(node, target, arguments, callStructure); | |
1101 } | |
1102 | |
1103 void handleStaticFunctionInvoke( | |
1104 Send node, | |
1105 MethodElement element, | |
1106 NodeList arguments, | |
1107 CallStructure callStructure, | |
1108 _) { | |
1109 doStaticallyBoundInvoke( | |
1110 node, element.declaration, arguments, callStructure); | |
1111 applyVisitState(); | |
1112 } | |
1113 | |
1114 void visitSuperMethodInvoke( | |
1115 Send node, | |
1116 MethodElement element, | |
1117 NodeList arguments, | |
1118 CallStructure callStructure, | |
1119 _) { | |
1120 doStaticallyBoundInvoke(node, element, arguments, callStructure); | |
1121 applyVisitState(); | |
1122 } | |
1123 | |
1124 void doSuperCall(Node node, FunctionElement function) { | |
1125 registerStaticUse(new StaticUse.foreignUse(function)); | |
1126 int arity = function.functionSignature.parameterCount + 1; | |
1127 FletchFunctionBase base = requireFunction(function); | |
1128 int constId = functionBuilder.allocateConstantFromFunction(base.functionId); | |
1129 invokeStatic(node, constId, arity); | |
1130 } | |
1131 | |
1132 void visitSuperGetterGet( | |
1133 Send node, | |
1134 FunctionElement getter, | |
1135 _) { | |
1136 loadThis(); | |
1137 doSuperCall(node, getter); | |
1138 applyVisitState(); | |
1139 } | |
1140 | |
1141 void visitSuperMethodGet( | |
1142 Send node, | |
1143 MethodElement method, | |
1144 _) { | |
1145 registerClosurization(method, ClosureKind.superTearOff); | |
1146 loadThis(); | |
1147 FletchFunctionBase target = requireFunction(method); | |
1148 FletchClassBuilder classBuilder = | |
1149 context.backend.createTearoffClass(target); | |
1150 assert(classBuilder.fields == 1); | |
1151 int constId = functionBuilder.allocateConstantFromClass( | |
1152 classBuilder.classId); | |
1153 assembler.allocate(constId, classBuilder.fields); | |
1154 applyVisitState(); | |
1155 } | |
1156 | |
1157 void visitSuperSetterSet( | |
1158 SendSet node, | |
1159 FunctionElement setter, | |
1160 Node rhs, | |
1161 _) { | |
1162 loadThis(); | |
1163 visitForValue(rhs); | |
1164 doSuperCall(node, setter); | |
1165 applyVisitState(); | |
1166 } | |
1167 | |
1168 void visitSuperIndex( | |
1169 Send node, | |
1170 FunctionElement function, | |
1171 Node index, | |
1172 _) { | |
1173 loadThis(); | |
1174 visitForValue(index); | |
1175 doSuperCall(node, function); | |
1176 applyVisitState(); | |
1177 } | |
1178 | |
1179 void visitSuperIndexSet( | |
1180 Send node, | |
1181 FunctionElement function, | |
1182 Node index, | |
1183 Node rhs, | |
1184 _) { | |
1185 loadThis(); | |
1186 visitForValue(index); | |
1187 visitForValue(rhs); | |
1188 doSuperCall(node, function); | |
1189 applyVisitState(); | |
1190 } | |
1191 | |
1192 void visitSuperCompoundIndexSet( | |
1193 Send node, | |
1194 FunctionElement getter, | |
1195 FunctionElement setter, | |
1196 Node index, | |
1197 AssignmentOperator operator, | |
1198 Node rhs, | |
1199 _) { | |
1200 visitForValue(index); | |
1201 loadThis(); | |
1202 assembler.loadLocal(1); | |
1203 doSuperCall(node, getter); | |
1204 loadThis(); | |
1205 // Load index | |
1206 assembler.loadLocal(2); | |
1207 // Load value from index call and call operator. | |
1208 assembler.loadLocal(2); | |
1209 visitForValue(rhs); | |
1210 invokeMethod(node, getAssignmentSelector(operator)); | |
1211 doSuperCall(node, setter); | |
1212 // Override 'index' with result value, and pop everything else. | |
1213 assembler.storeLocal(2); | |
1214 assembler.popMany(2); | |
1215 applyVisitState(); | |
1216 } | |
1217 | |
1218 void visitSuperIndexPostfix( | |
1219 SendSet node, | |
1220 FunctionElement getter, | |
1221 FunctionElement setter, | |
1222 Node index, | |
1223 IncDecOperator operator, | |
1224 _) { | |
1225 // TODO(ajohnsen): Fast-case when for effect. | |
1226 visitForValue(index); | |
1227 loadThis(); | |
1228 assembler.loadLocal(1); | |
1229 doSuperCall(node, getter); | |
1230 loadThis(); | |
1231 // Load index | |
1232 assembler.loadLocal(2); | |
1233 // Load value from index call and inc/dec. | |
1234 assembler.loadLocal(2); | |
1235 assembler.loadLiteral(1); | |
1236 invokeMethod(node, getIncDecSelector(operator)); | |
1237 // We can now call []= with 'this', 'index' and 'value'. | |
1238 doSuperCall(node, setter); | |
1239 assembler.pop(); | |
1240 // Pop result, override 'index' with initial indexed value, and pop again. | |
1241 assembler.storeLocal(1); | |
1242 assembler.pop(); | |
1243 applyVisitState(); | |
1244 } | |
1245 | |
1246 void visitSuperBinary( | |
1247 Send node, | |
1248 FunctionElement function, | |
1249 BinaryOperator operator, | |
1250 Node argument, | |
1251 _) { | |
1252 loadThis(); | |
1253 visitForValue(argument); | |
1254 doSuperCall(node, function); | |
1255 applyVisitState(); | |
1256 } | |
1257 | |
1258 void visitSuperEquals( | |
1259 Send node, | |
1260 FunctionElement function, | |
1261 Node argument, | |
1262 _) { | |
1263 loadThis(); | |
1264 visitForValue(argument); | |
1265 doSuperCall(node, function); | |
1266 applyVisitState(); | |
1267 } | |
1268 | |
1269 void visitSuperUnary( | |
1270 Send node, | |
1271 UnaryOperator operator, | |
1272 FunctionElement function, | |
1273 _) { | |
1274 loadThis(); | |
1275 doSuperCall(node, function); | |
1276 applyVisitState(); | |
1277 } | |
1278 | |
1279 int computeFieldIndex(FieldElement field) { | |
1280 ClassElement classElement = element.enclosingClass; | |
1281 int fieldIndex; | |
1282 FletchClassBuilder classBuilder; | |
1283 do { | |
1284 // We need to find the mixin application of the class, where the field | |
1285 // is stored. Iterate until it's found. | |
1286 classBuilder = context.backend.registerClassElement(classElement); | |
1287 classElement = classElement.implementation; | |
1288 int i = 0; | |
1289 classElement.forEachInstanceField((_, FieldElement member) { | |
1290 if (member == field) { | |
1291 assert(fieldIndex == null); | |
1292 fieldIndex = i; | |
1293 } | |
1294 i++; | |
1295 }); | |
1296 classElement = classElement.superclass; | |
1297 } while (fieldIndex == null); | |
1298 fieldIndex += classBuilder.superclassFields; | |
1299 return fieldIndex; | |
1300 } | |
1301 | |
1302 void visitSuperFieldGet( | |
1303 Send node, | |
1304 FieldElement field, | |
1305 _) { | |
1306 loadThis(); | |
1307 assembler.loadField(computeFieldIndex(field)); | |
1308 applyVisitState(); | |
1309 } | |
1310 | |
1311 void visitSuperFieldSet( | |
1312 SendSet node, | |
1313 FieldElement field, | |
1314 Node rhs, | |
1315 _) { | |
1316 loadThis(); | |
1317 visitForValue(rhs); | |
1318 assembler.storeField(computeFieldIndex(field)); | |
1319 applyVisitState(); | |
1320 } | |
1321 | |
1322 void handleStaticFieldInvoke( | |
1323 Node node, | |
1324 FieldElement field, | |
1325 NodeList arguments, | |
1326 CallStructure callStructure, | |
1327 _) { | |
1328 doStaticFieldGet(field); | |
1329 for (Node argument in arguments) { | |
1330 visitForValue(argument); | |
1331 } | |
1332 invokeMethod(node, callStructure.callSelector); | |
1333 applyVisitState(); | |
1334 } | |
1335 | |
1336 void visitDynamicPropertyInvoke( | |
1337 Send node, | |
1338 Node receiver, | |
1339 NodeList arguments, | |
1340 Selector selector, | |
1341 _) { | |
1342 visitForValue(receiver); | |
1343 for (Node argument in arguments) { | |
1344 visitForValue(argument); | |
1345 } | |
1346 invokeMethod(node, selector); | |
1347 applyVisitState(); | |
1348 } | |
1349 | |
1350 void visitIfNull( | |
1351 Send node, | |
1352 Node left, | |
1353 Node right, | |
1354 _) { | |
1355 BytecodeLabel end = new BytecodeLabel(); | |
1356 visitForValue(left); | |
1357 assembler.dup(); | |
1358 assembler.loadLiteralNull(); | |
1359 assembler.identicalNonNumeric(); | |
1360 assembler.branchIfFalse(end); | |
1361 assembler.pop(); | |
1362 visitForValue(right); | |
1363 assembler.bind(end); | |
1364 applyVisitState(); | |
1365 } | |
1366 | |
1367 void doIfNotNull(Node receiver, void ifNotNull()) { | |
1368 BytecodeLabel end = new BytecodeLabel(); | |
1369 visitForValue(receiver); | |
1370 assembler.dup(); | |
1371 assembler.loadLiteralNull(); | |
1372 assembler.identicalNonNumeric(); | |
1373 assembler.branchIfTrue(end); | |
1374 ifNotNull(); | |
1375 assembler.bind(end); | |
1376 } | |
1377 | |
1378 void visitIfNotNullDynamicPropertyInvoke( | |
1379 Send node, | |
1380 Node receiver, | |
1381 NodeList arguments, | |
1382 Selector selector, | |
1383 _) { | |
1384 doIfNotNull(receiver, () { | |
1385 for (Node argument in arguments) { | |
1386 visitForValue(argument); | |
1387 } | |
1388 invokeMethod(node, selector); | |
1389 }); | |
1390 applyVisitState(); | |
1391 } | |
1392 | |
1393 void visitExpressionInvoke( | |
1394 Send node, | |
1395 Expression receiver, | |
1396 NodeList arguments, | |
1397 CallStructure callStructure, | |
1398 _) { | |
1399 visitForValue(receiver); | |
1400 for (Node argument in arguments) { | |
1401 visitForValue(argument); | |
1402 } | |
1403 invokeMethod(node, new Selector.call(Names.call, callStructure)); | |
1404 applyVisitState(); | |
1405 } | |
1406 | |
1407 void visitThisPropertyInvoke( | |
1408 Send node, | |
1409 NodeList arguments, | |
1410 Selector selector, | |
1411 _) { | |
1412 loadThis(); | |
1413 | |
1414 // If the property is statically known to be a field, instead invoke the | |
1415 // getter and then invoke 'call(...)' on the value. | |
1416 // TODO(ajohnsen): This is a fix that only works when the field is | |
1417 // statically known - that is not always the case. Implement VM support? | |
1418 Element target = elements[node]; | |
1419 if (target != null && target.isField) { | |
1420 invokeGetter(node, new Name(target.name, element.library)); | |
1421 selector = new Selector.callClosureFrom(selector); | |
1422 } | |
1423 for (Node argument in arguments) { | |
1424 visitForValue(argument); | |
1425 } | |
1426 invokeMethod(node, selector); | |
1427 applyVisitState(); | |
1428 } | |
1429 | |
1430 void visitThisInvoke( | |
1431 Send node, | |
1432 NodeList arguments, | |
1433 CallStructure callStructure, | |
1434 _) { | |
1435 loadThis(); | |
1436 for (Node argument in arguments) { | |
1437 visitForValue(argument); | |
1438 } | |
1439 invokeMethod(node, callStructure.callSelector); | |
1440 applyVisitState(); | |
1441 } | |
1442 | |
1443 void visitClassTypeLiteralGet( | |
1444 Send node, | |
1445 ConstantExpression constant, | |
1446 _) { | |
1447 generateUnimplementedError( | |
1448 node, "[visitClassTypeLiteralGet] isn't implemented."); | |
1449 applyVisitState(); | |
1450 } | |
1451 | |
1452 void visitDynamicPropertyGet( | |
1453 Send node, | |
1454 Node receiver, | |
1455 Name name, | |
1456 _) { | |
1457 if (name.text == "runtimeType") { | |
1458 // TODO(ahe): Implement runtimeType. | |
1459 generateUnimplementedError( | |
1460 node, | |
1461 "'runtimeType' isn't supported in Fletch. See https://goo.gl/ELH6Zc"); | |
1462 applyVisitState(); | |
1463 return; | |
1464 } | |
1465 visitForValue(receiver); | |
1466 invokeGetter(node, name); | |
1467 applyVisitState(); | |
1468 } | |
1469 | |
1470 void visitIfNotNullDynamicPropertyGet( | |
1471 Send node, | |
1472 Node receiver, | |
1473 Name name, | |
1474 _) { | |
1475 doIfNotNull(receiver, () { | |
1476 invokeGetter(node, name); | |
1477 }); | |
1478 applyVisitState(); | |
1479 } | |
1480 | |
1481 void visitThisPropertyGet( | |
1482 Send node, | |
1483 Name name, | |
1484 _) { | |
1485 loadThis(); | |
1486 invokeGetter(node, name); | |
1487 applyVisitState(); | |
1488 } | |
1489 | |
1490 void visitThisPropertySet( | |
1491 Send node, | |
1492 Name name, | |
1493 Node rhs, | |
1494 _) { | |
1495 loadThis(); | |
1496 visitForValue(rhs); | |
1497 invokeSetter(node, name); | |
1498 applyVisitState(); | |
1499 } | |
1500 | |
1501 void doStaticFieldGet(FieldElement field) { | |
1502 if (checkCompileError(field)) return; | |
1503 if (field.isConst) { | |
1504 int constId = allocateConstantFromNode( | |
1505 field.initializer, | |
1506 elements: field.resolvedAst.elements); | |
1507 assembler.loadConst(constId); | |
1508 } else { | |
1509 int index = compileLazyFieldInitializer(field); | |
1510 if (field.initializer != null) { | |
1511 assembler.loadStaticInit(index); | |
1512 } else { | |
1513 assembler.loadStatic(index); | |
1514 } | |
1515 } | |
1516 } | |
1517 | |
1518 void handleStaticFieldGet( | |
1519 Send node, | |
1520 FieldElement field, | |
1521 _) { | |
1522 doStaticFieldGet(field); | |
1523 applyVisitState(); | |
1524 } | |
1525 | |
1526 void visitAssert(Assert node) { | |
1527 // TODO(ajohnsen): Emit assert in checked mode. | |
1528 } | |
1529 | |
1530 void visitDynamicPropertySet( | |
1531 Send node, | |
1532 Node receiver, | |
1533 Name name, | |
1534 Node rhs, | |
1535 _) { | |
1536 visitForValue(receiver); | |
1537 visitForValue(rhs); | |
1538 invokeSetter(node, name); | |
1539 applyVisitState(); | |
1540 } | |
1541 | |
1542 void visitIfNotNullDynamicPropertySet( | |
1543 SendSet node, | |
1544 Node receiver, | |
1545 Name name, | |
1546 Node rhs, | |
1547 _) { | |
1548 doIfNotNull(receiver, () { | |
1549 visitForValue(rhs); | |
1550 invokeSetter(node, name); | |
1551 }); | |
1552 applyVisitState(); | |
1553 } | |
1554 | |
1555 void doStaticFieldSet( | |
1556 FieldElement field) { | |
1557 int index = context.getStaticFieldIndex(field, element); | |
1558 assembler.storeStatic(index); | |
1559 } | |
1560 | |
1561 void handleStaticFieldSet( | |
1562 SendSet node, | |
1563 FieldElement field, | |
1564 Node rhs, | |
1565 _) { | |
1566 visitForValue(rhs); | |
1567 doStaticFieldSet(field); | |
1568 applyVisitState(); | |
1569 } | |
1570 | |
1571 void visitStringJuxtaposition(StringJuxtaposition node) { | |
1572 // TODO(ajohnsen): This could probably be optimized to string constants in | |
1573 // some cases. | |
1574 visitForValue(node.first); | |
1575 visitForValue(node.second); | |
1576 // TODO(ajohnsen): Cache these in context/backend. | |
1577 Selector concat = new Selector.binaryOperator('+'); | |
1578 invokeMethod(node, concat); | |
1579 applyVisitState(); | |
1580 } | |
1581 | |
1582 void visitStringInterpolation(StringInterpolation node) { | |
1583 // TODO(ajohnsen): Cache this in context/backend. | |
1584 Selector concat = new Selector.binaryOperator('+'); | |
1585 visitForValueNoDebugInfo(node.string); | |
1586 for (StringInterpolationPart part in node.parts) { | |
1587 visitForValue(part.expression); | |
1588 invokeMethod(part.expression, Selectors.toString_); | |
1589 LiteralString string = part.string; | |
1590 if (string.dartString.isNotEmpty) { | |
1591 visitForValueNoDebugInfo(string); | |
1592 invokeMethod(null, concat); | |
1593 } | |
1594 invokeMethod(null, concat); | |
1595 } | |
1596 applyVisitState(); | |
1597 } | |
1598 | |
1599 void visitLiteralNull(LiteralNull node) { | |
1600 if (visitState == VisitState.Value) { | |
1601 assembler.loadLiteralNull(); | |
1602 } else if (visitState == VisitState.Test) { | |
1603 if (falseLabel != null) assembler.branch(falseLabel); | |
1604 } | |
1605 } | |
1606 | |
1607 void visitLiteralSymbol(LiteralSymbol node) { | |
1608 int constId = allocateConstantFromNode(node); | |
1609 assembler.loadConst(constId); | |
1610 applyVisitState(); | |
1611 } | |
1612 | |
1613 void visitLiteralBool(LiteralBool node) { | |
1614 var expression = compileConstant(node, isConst: false); | |
1615 bool isTrue = expression != null && | |
1616 context.getConstantValue(expression).isTrue; | |
1617 | |
1618 if (visitState == VisitState.Value) { | |
1619 if (isTrue) { | |
1620 assembler.loadLiteralTrue(); | |
1621 } else { | |
1622 assembler.loadLiteralFalse(); | |
1623 } | |
1624 } else if (visitState == VisitState.Test) { | |
1625 if (isTrue) { | |
1626 if (trueLabel != null) assembler.branch(trueLabel); | |
1627 } else { | |
1628 if (falseLabel != null) assembler.branch(falseLabel); | |
1629 } | |
1630 } | |
1631 } | |
1632 | |
1633 void visitLiteralInt(LiteralInt node) { | |
1634 if (visitState == VisitState.Value) { | |
1635 int value = node.value; | |
1636 assert(value >= 0); | |
1637 if (value > LITERAL_INT_MAX) { | |
1638 if ((value < MIN_INT64 || value > MAX_INT64) && !context.enableBigint) { | |
1639 generateUnimplementedError( | |
1640 node, | |
1641 'Program compiled without support for big integers'); | |
1642 } else { | |
1643 int constId = allocateConstantFromNode(node); | |
1644 assembler.loadConst(constId); | |
1645 } | |
1646 } else { | |
1647 assembler.loadLiteral(value); | |
1648 } | |
1649 } else if (visitState == VisitState.Test) { | |
1650 if (falseLabel != null) assembler.branch(falseLabel); | |
1651 } | |
1652 } | |
1653 | |
1654 void visitLiteral(Literal node) { | |
1655 if (visitState == VisitState.Value) { | |
1656 assembler.loadConst(allocateConstantFromNode(node)); | |
1657 } else if (visitState == VisitState.Test) { | |
1658 if (falseLabel != null) assembler.branch(falseLabel); | |
1659 } | |
1660 } | |
1661 | |
1662 void visitLiteralList(LiteralList node) { | |
1663 if (node.isConst) { | |
1664 int constId = allocateConstantFromNode(node); | |
1665 assembler.loadConst(constId); | |
1666 applyVisitState(); | |
1667 return; | |
1668 } | |
1669 ClassElement literalClass = context.backend.growableListClass; | |
1670 ConstructorElement constructor = literalClass.lookupDefaultConstructor(); | |
1671 if (constructor == null) { | |
1672 internalError(node, "Failed to lookup default list constructor"); | |
1673 } | |
1674 // Call with empty arguments, as we call the default constructor. | |
1675 callConstructor( | |
1676 node, constructor, new NodeList.empty(), CallStructure.NO_ARGS); | |
1677 Selector add = new Selector.call(new Name('add', null), | |
1678 CallStructure.ONE_ARG); | |
1679 for (Node element in node.elements) { | |
1680 assembler.dup(); | |
1681 visitForValue(element); | |
1682 invokeMethod(node, add); | |
1683 assembler.pop(); | |
1684 } | |
1685 applyVisitState(); | |
1686 } | |
1687 | |
1688 void visitLiteralMap(LiteralMap node) { | |
1689 if (node.isConst) { | |
1690 int constId = allocateConstantFromNode(node); | |
1691 assembler.loadConst(constId); | |
1692 applyVisitState(); | |
1693 return; | |
1694 } | |
1695 ClassElement literalClass = | |
1696 context.backend.mapImplementation.implementation; | |
1697 ConstructorElement constructor = literalClass.lookupConstructor(""); | |
1698 if (constructor == null) { | |
1699 internalError(literalClass, "Failed to lookup default map constructor"); | |
1700 return; | |
1701 } | |
1702 // The default constructor is a redirecting factory constructor. Follow it. | |
1703 constructor = constructor.effectiveTarget; | |
1704 FletchFunctionBase function = requireFunction(constructor.declaration); | |
1705 doStaticFunctionInvoke( | |
1706 node, | |
1707 function, | |
1708 new NodeList.empty(), | |
1709 CallStructure.NO_ARGS, | |
1710 factoryInvoke: true); | |
1711 | |
1712 Selector selector = new Selector.indexSet(); | |
1713 for (Node element in node.entries) { | |
1714 assembler.dup(); | |
1715 visitForValue(element); | |
1716 invokeMethod(node, selector); | |
1717 assembler.pop(); | |
1718 } | |
1719 applyVisitState(); | |
1720 } | |
1721 | |
1722 void visitLiteralMapEntry(LiteralMapEntry node) { | |
1723 assert(visitState == VisitState.Value); | |
1724 visitForValue(node.key); | |
1725 visitForValue(node.value); | |
1726 } | |
1727 | |
1728 void visitLiteralString(LiteralString node) { | |
1729 if (visitState == VisitState.Value) { | |
1730 assembler.loadConst(allocateConstantFromNode(node)); | |
1731 registerInstantiatedClass( | |
1732 context.compiler.backend.stringImplementation); | |
1733 } else if (visitState == VisitState.Test) { | |
1734 if (falseLabel != null) assembler.branch(falseLabel); | |
1735 } | |
1736 } | |
1737 | |
1738 void visitCascadeReceiver(CascadeReceiver node) { | |
1739 visitForValue(node.expression); | |
1740 assembler.dup(); | |
1741 assert(visitState == VisitState.Value); | |
1742 } | |
1743 | |
1744 void visitCascade(Cascade node) { | |
1745 visitForEffect(node.expression); | |
1746 applyVisitState(); | |
1747 } | |
1748 | |
1749 void visitParenthesizedExpression(ParenthesizedExpression node) { | |
1750 // Visit expression in the same VisitState. | |
1751 node.expression.accept(this); | |
1752 } | |
1753 | |
1754 void visitLocalFunctionGet(Send node, LocalFunctionElement function, _) { | |
1755 registerClosurization(function, ClosureKind.localFunction); | |
1756 handleLocalGet(node, function, _); | |
1757 } | |
1758 | |
1759 void handleLocalGet( | |
1760 Send node, | |
1761 LocalElement element, | |
1762 _) { | |
1763 scope[element].load(assembler); | |
1764 applyVisitState(); | |
1765 } | |
1766 | |
1767 void handleLocalSet( | |
1768 SendSet node, | |
1769 LocalElement element, | |
1770 Node rhs, | |
1771 _) { | |
1772 visitForValue(rhs); | |
1773 scope[element].store(assembler); | |
1774 applyVisitState(); | |
1775 } | |
1776 | |
1777 void visitLocalFunctionInvoke( | |
1778 Send node, | |
1779 LocalFunctionElement function, | |
1780 NodeList arguments, | |
1781 CallStructure callStructure, | |
1782 _) { | |
1783 // TODO(ahe): We could use loadPositionalArguments if [element] is a local | |
1784 // function to avoid generating additional stubs and to avoid registering | |
1785 // this as a dynamic call. | |
1786 registerLocalInvoke(function, callStructure.callSelector); | |
1787 handleLocalInvoke(node, function, arguments, callStructure, _); | |
1788 } | |
1789 | |
1790 void handleLocalInvoke( | |
1791 Node node, | |
1792 LocalElement element, | |
1793 NodeList arguments, | |
1794 CallStructure callStructure, | |
1795 _) { | |
1796 scope[element].load(assembler); | |
1797 for (Node argument in arguments) { | |
1798 visitForValue(argument); | |
1799 } | |
1800 invokeMethod(node, callStructure.callSelector); | |
1801 applyVisitState(); | |
1802 } | |
1803 | |
1804 static Selector getIncDecSelector(IncDecOperator operator) { | |
1805 String name = operator == IncDecOperator.INC ? '+' : '-'; | |
1806 return new Selector.binaryOperator(name); | |
1807 } | |
1808 | |
1809 static Selector getAssignmentSelector(AssignmentOperator operator) { | |
1810 String name = operator.binaryOperator.name; | |
1811 return new Selector.binaryOperator(name); | |
1812 } | |
1813 | |
1814 void doLocalVariableIncrement( | |
1815 Node node, | |
1816 LocalVariableElement element, | |
1817 IncDecOperator operator, | |
1818 bool prefix) { | |
1819 // TODO(ajohnsen): Candidate for bytecode: Inc/Dec local with non-Smi | |
1820 // bailout. | |
1821 LocalValue value = scope[element]; | |
1822 value.load(assembler); | |
1823 // For postfix, keep local, unmodified version, to 'return' after store. | |
1824 if (!prefix) assembler.dup(); | |
1825 assembler.loadLiteral(1); | |
1826 invokeMethod(node, getIncDecSelector(operator)); | |
1827 value.store(assembler); | |
1828 if (!prefix) assembler.pop(); | |
1829 } | |
1830 | |
1831 void visitLocalVariablePrefix( | |
1832 SendSet node, | |
1833 LocalVariableElement element, | |
1834 IncDecOperator operator, | |
1835 _) { | |
1836 doLocalVariableIncrement(node, element, operator, true); | |
1837 applyVisitState(); | |
1838 } | |
1839 | |
1840 void visitParameterPrefix( | |
1841 Send node, | |
1842 LocalParameterElement parameter, | |
1843 IncDecOperator operator, | |
1844 _) { | |
1845 doLocalVariableIncrement(node, parameter, operator, true); | |
1846 applyVisitState(); | |
1847 } | |
1848 | |
1849 void visitLocalVariablePostfix( | |
1850 SendSet node, | |
1851 LocalVariableElement element, | |
1852 IncDecOperator operator, | |
1853 _) { | |
1854 // If visitState is for effect, we can ignore the return value, thus always | |
1855 // generate code for the simpler 'prefix' case. | |
1856 bool prefix = (visitState == VisitState.Effect); | |
1857 doLocalVariableIncrement(node, element, operator, prefix); | |
1858 applyVisitState(); | |
1859 } | |
1860 | |
1861 void visitParameterPostfix( | |
1862 SendSet node, | |
1863 LocalParameterElement parameter, | |
1864 IncDecOperator operator, | |
1865 _) { | |
1866 // If visitState is for effect, we can ignore the return value, thus always | |
1867 // generate code for the simpler 'prefix' case. | |
1868 bool prefix = (visitState == VisitState.Effect); | |
1869 doLocalVariableIncrement(node, parameter, operator, prefix); | |
1870 applyVisitState(); | |
1871 } | |
1872 | |
1873 void doStaticFieldPrefix( | |
1874 Node node, | |
1875 FieldElement field, | |
1876 IncDecOperator operator) { | |
1877 doStaticFieldGet(field); | |
1878 assembler.loadLiteral(1); | |
1879 invokeMethod(node, getIncDecSelector(operator)); | |
1880 doStaticFieldSet(field); | |
1881 } | |
1882 | |
1883 void doStaticFieldPostfix( | |
1884 Node node, | |
1885 FieldElement field, | |
1886 IncDecOperator operator) { | |
1887 doStaticFieldGet(field); | |
1888 // For postfix, keep local, unmodified version, to 'return' after store. | |
1889 assembler.dup(); | |
1890 assembler.loadLiteral(1); | |
1891 invokeMethod(node, getIncDecSelector(operator)); | |
1892 doStaticFieldSet(field); | |
1893 assembler.pop(); | |
1894 } | |
1895 | |
1896 void visitStaticFieldPostfix( | |
1897 Send node, | |
1898 FieldElement field, | |
1899 IncDecOperator operator, | |
1900 _) { | |
1901 if (visitState == VisitState.Effect) { | |
1902 doStaticFieldPrefix(node, field, operator); | |
1903 } else { | |
1904 doStaticFieldPostfix(node, field, operator); | |
1905 } | |
1906 applyVisitState(); | |
1907 } | |
1908 | |
1909 void visitStaticFieldPrefix( | |
1910 Send node, | |
1911 FieldElement field, | |
1912 IncDecOperator operator, | |
1913 _) { | |
1914 doStaticFieldPrefix(node, field, operator); | |
1915 applyVisitState(); | |
1916 } | |
1917 | |
1918 void visitTopLevelFieldPostfix( | |
1919 Send node, | |
1920 FieldElement field, | |
1921 IncDecOperator operator, | |
1922 _) { | |
1923 if (visitState == VisitState.Effect) { | |
1924 doStaticFieldPrefix(node, field, operator); | |
1925 } else { | |
1926 doStaticFieldPostfix(node, field, operator); | |
1927 } | |
1928 applyVisitState(); | |
1929 } | |
1930 | |
1931 void visitTopLevelFieldPrefix( | |
1932 Send node, | |
1933 FieldElement field, | |
1934 IncDecOperator operator, | |
1935 _) { | |
1936 doStaticFieldPrefix(node, field, operator); | |
1937 applyVisitState(); | |
1938 } | |
1939 | |
1940 void doDynamicPropertyCompound( | |
1941 Node node, | |
1942 Name name, | |
1943 AssignmentOperator operator, | |
1944 Node rhs) { | |
1945 // Dup receiver for setter. | |
1946 assembler.dup(); | |
1947 invokeGetter(node, name); | |
1948 visitForValue(rhs); | |
1949 invokeMethod(node, getAssignmentSelector(operator)); | |
1950 invokeSetter(node, name); | |
1951 } | |
1952 | |
1953 void visitDynamicPropertyCompound( | |
1954 Send node, | |
1955 Node receiver, | |
1956 Name name, | |
1957 AssignmentOperator operator, | |
1958 Node rhs, | |
1959 _) { | |
1960 visitForValue(receiver); | |
1961 doDynamicPropertyCompound( | |
1962 node, | |
1963 name, | |
1964 operator, | |
1965 rhs); | |
1966 applyVisitState(); | |
1967 } | |
1968 | |
1969 void visitIfNotNullDynamicPropertyCompound( | |
1970 Send node, | |
1971 Node receiver, | |
1972 Name name, | |
1973 AssignmentOperator operator, | |
1974 Node rhs, | |
1975 _) { | |
1976 doIfNotNull(receiver, () { | |
1977 doDynamicPropertyCompound( | |
1978 node, | |
1979 name, | |
1980 operator, | |
1981 rhs); | |
1982 }); | |
1983 applyVisitState(); | |
1984 } | |
1985 | |
1986 void visitThisPropertyCompound( | |
1987 Send node, | |
1988 Name name, | |
1989 AssignmentOperator operator, | |
1990 Node rhs, | |
1991 _) { | |
1992 loadThis(); | |
1993 doDynamicPropertyCompound( | |
1994 node, | |
1995 name, | |
1996 operator, | |
1997 rhs); | |
1998 applyVisitState(); | |
1999 } | |
2000 | |
2001 void doDynamicPrefix( | |
2002 Node node, | |
2003 Name name, | |
2004 IncDecOperator operator) { | |
2005 assembler.dup(); | |
2006 invokeGetter(node, name); | |
2007 assembler.loadLiteral(1); | |
2008 invokeMethod(node, getIncDecSelector(operator)); | |
2009 invokeSetter(node, name); | |
2010 } | |
2011 | |
2012 void doIndexPrefix( | |
2013 SendSet node, | |
2014 Node receiver, | |
2015 Node index, | |
2016 IncDecOperator operator) { | |
2017 visitForValue(receiver); | |
2018 visitForValue(index); | |
2019 // Load already evaluated receiver and index for '[]' call. | |
2020 assembler.loadLocal(1); | |
2021 assembler.loadLocal(1); | |
2022 invokeMethod(node, new Selector.index()); | |
2023 assembler.loadLiteral(1); | |
2024 invokeMethod(node, getIncDecSelector(operator)); | |
2025 // Use existing evaluated receiver and index for '[]=' call. | |
2026 invokeMethod(node, new Selector.indexSet()); | |
2027 } | |
2028 | |
2029 void visitIndexPrefix( | |
2030 SendSet node, | |
2031 Node receiver, | |
2032 Node index, | |
2033 IncDecOperator operator, | |
2034 _) { | |
2035 doIndexPrefix(node, receiver, index, operator); | |
2036 applyVisitState(); | |
2037 } | |
2038 | |
2039 void visitIndexPostfix( | |
2040 Send node, | |
2041 Node receiver, | |
2042 Node index, | |
2043 IncDecOperator operator, | |
2044 _) { | |
2045 if (visitState == VisitState.Effect) { | |
2046 doIndexPrefix(node, receiver, index, operator); | |
2047 applyVisitState(); | |
2048 return; | |
2049 } | |
2050 | |
2051 // Reserve slot for result. | |
2052 assembler.loadLiteralNull(); | |
2053 visitForValue(receiver); | |
2054 visitForValue(index); | |
2055 // Load already evaluated receiver and index for '[]' call. | |
2056 assembler.loadLocal(1); | |
2057 assembler.loadLocal(1); | |
2058 invokeMethod(node, new Selector.index()); | |
2059 assembler.storeLocal(3); | |
2060 assembler.loadLiteral(1); | |
2061 invokeMethod(node, getIncDecSelector(operator)); | |
2062 // Use existing evaluated receiver and index for '[]=' call. | |
2063 invokeMethod(node, new Selector.indexSet()); | |
2064 assembler.pop(); | |
2065 applyVisitState(); | |
2066 } | |
2067 | |
2068 void visitCompoundIndexSet( | |
2069 Send node, | |
2070 Node receiver, | |
2071 Node index, | |
2072 AssignmentOperator operator, | |
2073 Node rhs, | |
2074 _) { | |
2075 visitForValue(receiver); | |
2076 visitForValue(index); | |
2077 // Load already evaluated receiver and index for '[]' call. | |
2078 assembler.loadLocal(1); | |
2079 assembler.loadLocal(1); | |
2080 invokeMethod(node, new Selector.index()); | |
2081 visitForValue(rhs); | |
2082 invokeMethod(node, getAssignmentSelector(operator)); | |
2083 // Use existing evaluated receiver and index for '[]=' call. | |
2084 invokeMethod(node, new Selector.indexSet()); | |
2085 applyVisitState(); | |
2086 } | |
2087 | |
2088 void visitThisPropertyPrefix( | |
2089 Send node, | |
2090 Name name, | |
2091 IncDecOperator operator, | |
2092 _) { | |
2093 loadThis(); | |
2094 doDynamicPrefix(node, name, operator); | |
2095 applyVisitState(); | |
2096 } | |
2097 | |
2098 void visitThisPropertyPostfix( | |
2099 Send node, | |
2100 Name name, | |
2101 IncDecOperator operator, | |
2102 _) { | |
2103 // If visitState is for effect, we can ignore the return value, thus always | |
2104 // generate code for the simpler 'prefix' case. | |
2105 if (visitState == VisitState.Effect) { | |
2106 loadThis(); | |
2107 doDynamicPrefix(node, name, operator); | |
2108 applyVisitState(); | |
2109 return; | |
2110 } | |
2111 | |
2112 loadThis(); | |
2113 invokeGetter(node, name); | |
2114 // For postfix, keep local, unmodified version, to 'return' after store. | |
2115 assembler.dup(); | |
2116 assembler.loadLiteral(1); | |
2117 invokeMethod(node, getIncDecSelector(operator)); | |
2118 loadThis(); | |
2119 assembler.loadLocal(1); | |
2120 invokeSetter(node, name); | |
2121 assembler.popMany(2); | |
2122 applyVisitState(); | |
2123 } | |
2124 | |
2125 void visitDynamicPropertyPrefix( | |
2126 Send node, | |
2127 Node receiver, | |
2128 Name name, | |
2129 IncDecOperator operator, | |
2130 _) { | |
2131 visitForValue(receiver); | |
2132 doDynamicPrefix(node, name, operator); | |
2133 applyVisitState(); | |
2134 } | |
2135 | |
2136 void visitIfNotNullDynamicPropertyPrefix( | |
2137 Send node, | |
2138 Node receiver, | |
2139 Name name, | |
2140 IncDecOperator operator, | |
2141 _) { | |
2142 doIfNotNull(receiver, () { | |
2143 doDynamicPrefix(node, name, operator); | |
2144 }); | |
2145 applyVisitState(); | |
2146 } | |
2147 | |
2148 void doDynamicPostfix( | |
2149 Send node, | |
2150 Node receiver, | |
2151 Name name, | |
2152 IncDecOperator operator) { | |
2153 int receiverSlot = assembler.stackSize - 1; | |
2154 assembler.loadSlot(receiverSlot); | |
2155 invokeGetter(node, name); | |
2156 // For postfix, keep local, unmodified version, to 'return' after store. | |
2157 assembler.dup(); | |
2158 assembler.loadLiteral(1); | |
2159 invokeMethod(node, getIncDecSelector(operator)); | |
2160 assembler.loadSlot(receiverSlot); | |
2161 assembler.loadLocal(1); | |
2162 invokeSetter(node, name); | |
2163 assembler.popMany(2); | |
2164 assembler.storeLocal(1); | |
2165 // Pop receiver. | |
2166 assembler.pop(); | |
2167 } | |
2168 | |
2169 void visitDynamicPropertyPostfix( | |
2170 Send node, | |
2171 Node receiver, | |
2172 Name name, | |
2173 IncDecOperator operator, | |
2174 _) { | |
2175 // If visitState is for effect, we can ignore the return value, thus always | |
2176 // generate code for the simpler 'prefix' case. | |
2177 if (visitState == VisitState.Effect) { | |
2178 visitForValue(receiver); | |
2179 doDynamicPrefix(node, name, operator); | |
2180 applyVisitState(); | |
2181 return; | |
2182 } | |
2183 | |
2184 visitForValue(receiver); | |
2185 doDynamicPostfix(node, receiver, name, operator); | |
2186 applyVisitState(); | |
2187 } | |
2188 | |
2189 void visitIfNotNullDynamicPropertyPostfix( | |
2190 Send node, | |
2191 Node receiver, | |
2192 Name name, | |
2193 IncDecOperator operator, | |
2194 _) { | |
2195 doIfNotNull(receiver, () { | |
2196 doDynamicPostfix( | |
2197 node, receiver, name, operator); | |
2198 }); | |
2199 applyVisitState(); | |
2200 } | |
2201 | |
2202 void visitThrow(Throw node) { | |
2203 visitForValue(node.expression); | |
2204 generateThrow(node); | |
2205 // TODO(ahe): It seems suboptimal that each throw is followed by a pop. | |
2206 applyVisitState(); | |
2207 } | |
2208 | |
2209 void visitRethrow(Rethrow node) { | |
2210 if (tryBlockStack.isEmpty) { | |
2211 doCompileError(context.compiler.reporter.createMessage( | |
2212 node, MessageKind.GENERIC, {"text": "Rethrow outside try"})); | |
2213 } else { | |
2214 TryBlock block = tryBlockStack.head; | |
2215 assembler.loadSlot(block.stackSize - 1); | |
2216 // TODO(ahe): It seems suboptimal that each throw is followed by a pop. | |
2217 generateThrow(node); | |
2218 } | |
2219 assembler.pop(); | |
2220 } | |
2221 | |
2222 void callConstructor(Node node, | |
2223 ConstructorElement constructor, | |
2224 NodeList arguments, | |
2225 CallStructure callStructure) { | |
2226 FletchFunctionBase function = requireConstructorInitializer(constructor); | |
2227 doStaticFunctionInvoke(node, function, arguments, callStructure); | |
2228 } | |
2229 | |
2230 void doConstConstructorInvoke(ConstantExpression constant) { | |
2231 var value = context.getConstantValue(constant); | |
2232 context.markConstantUsed(value); | |
2233 int constId = functionBuilder.allocateConstant(value); | |
2234 assembler.loadConst(constId); | |
2235 } | |
2236 | |
2237 void visitConstConstructorInvoke( | |
2238 NewExpression node, | |
2239 ConstructedConstantExpression constant, | |
2240 _) { | |
2241 // TODO(johnniwinther): We should not end up here with an bad constructor. | |
2242 if (!checkCompileError(elements[node.send])) { | |
2243 doConstConstructorInvoke(constant); | |
2244 } | |
2245 applyVisitState(); | |
2246 } | |
2247 | |
2248 void visitBoolFromEnvironmentConstructorInvoke( | |
2249 NewExpression node, | |
2250 BoolFromEnvironmentConstantExpression constant, | |
2251 _) { | |
2252 doConstConstructorInvoke(constant); | |
2253 applyVisitState(); | |
2254 } | |
2255 | |
2256 void visitIntFromEnvironmentConstructorInvoke( | |
2257 NewExpression node, | |
2258 IntFromEnvironmentConstantExpression constant, | |
2259 _) { | |
2260 doConstConstructorInvoke(constant); | |
2261 applyVisitState(); | |
2262 } | |
2263 | |
2264 void visitStringFromEnvironmentConstructorInvoke( | |
2265 NewExpression node, | |
2266 StringFromEnvironmentConstantExpression constant, | |
2267 _) { | |
2268 doConstConstructorInvoke(constant); | |
2269 applyVisitState(); | |
2270 } | |
2271 | |
2272 void visitGenerativeConstructorInvoke( | |
2273 NewExpression node, | |
2274 ConstructorElement constructor, | |
2275 InterfaceType type, | |
2276 NodeList arguments, | |
2277 CallStructure callStructure, | |
2278 _) { | |
2279 if (!checkCompileError(constructor)) { | |
2280 callConstructor(node, constructor.declaration, arguments, callStructure); | |
2281 } | |
2282 applyVisitState(); | |
2283 } | |
2284 | |
2285 void visitFactoryConstructorInvoke( | |
2286 NewExpression node, | |
2287 ConstructorElement constructor, | |
2288 InterfaceType type, | |
2289 NodeList arguments, | |
2290 CallStructure callStructure, | |
2291 _) { | |
2292 // If the constructor has an implementation, the implementation is the | |
2293 // factory we want to invoke. Redirect to | |
2294 // visitRedirectingFactoryConstructorInvoke, so we handle both cases of | |
2295 // either a factory or a redirecting factory. | |
2296 if (constructor.implementation != constructor) { | |
2297 ConstructorElement implementation = constructor.implementation; | |
2298 visitRedirectingFactoryConstructorInvoke( | |
2299 node, | |
2300 constructor, | |
2301 type, | |
2302 implementation.effectiveTarget, | |
2303 null, | |
2304 arguments, | |
2305 callStructure, | |
2306 null); | |
2307 return; | |
2308 } | |
2309 // TODO(ahe): Remove ".declaration" when issue 23135 is fixed. | |
2310 FletchFunctionBase function = requireFunction(constructor.declaration); | |
2311 doStaticFunctionInvoke( | |
2312 node, function, arguments, callStructure, factoryInvoke: true); | |
2313 applyVisitState(); | |
2314 } | |
2315 | |
2316 void visitConstructorIncompatibleInvoke( | |
2317 NewExpression node, | |
2318 ConstructorElement constructor, | |
2319 InterfaceType type, | |
2320 NodeList arguments, | |
2321 CallStructure callStructure, | |
2322 _) { | |
2323 if (!checkCompileError(constructor)) { | |
2324 doUnresolved(constructor.name); | |
2325 } | |
2326 applyVisitState(); | |
2327 } | |
2328 | |
2329 void visitRedirectingGenerativeConstructorInvoke( | |
2330 NewExpression node, | |
2331 ConstructorElement constructor, | |
2332 InterfaceType type, | |
2333 NodeList arguments, | |
2334 CallStructure callStructure, | |
2335 _) { | |
2336 visitGenerativeConstructorInvoke( | |
2337 node, | |
2338 constructor, | |
2339 type, | |
2340 arguments, | |
2341 callStructure, | |
2342 null); | |
2343 } | |
2344 | |
2345 void visitRedirectingFactoryConstructorInvoke( | |
2346 NewExpression node, | |
2347 ConstructorElement constructor, | |
2348 InterfaceType type, | |
2349 ConstructorElement effectiveTarget, | |
2350 InterfaceType effectiveTargetType, | |
2351 NodeList arguments, | |
2352 CallStructure callStructure, | |
2353 _) { | |
2354 if (effectiveTarget.isGenerativeConstructor) { | |
2355 visitGenerativeConstructorInvoke( | |
2356 node, | |
2357 effectiveTarget, | |
2358 effectiveTargetType, | |
2359 arguments, | |
2360 callStructure, | |
2361 null); | |
2362 } else { | |
2363 visitFactoryConstructorInvoke( | |
2364 node, | |
2365 effectiveTarget, | |
2366 effectiveTargetType, | |
2367 arguments, | |
2368 callStructure, | |
2369 null); | |
2370 } | |
2371 } | |
2372 | |
2373 void visitUnresolvedConstructorInvoke( | |
2374 NewExpression node, | |
2375 Element constructor, | |
2376 DartType type, | |
2377 NodeList arguments, | |
2378 Selector selector, | |
2379 _) { | |
2380 if (!checkCompileError(constructor.enclosingClass)) { | |
2381 doUnresolved(node.send.toString()); | |
2382 } | |
2383 applyVisitState(); | |
2384 } | |
2385 | |
2386 void visitUnresolvedClassConstructorInvoke( | |
2387 NewExpression node, | |
2388 Element element, | |
2389 DartType type, | |
2390 NodeList arguments, | |
2391 Selector selector, | |
2392 _) { | |
2393 doUnresolved(node.send.toString()); | |
2394 applyVisitState(); | |
2395 } | |
2396 | |
2397 void visitAbstractClassConstructorInvoke( | |
2398 NewExpression node, | |
2399 ConstructorElement element, | |
2400 InterfaceType type, | |
2401 NodeList arguments, | |
2402 CallStructure callStructure, | |
2403 _) { | |
2404 generateUnimplementedError(node, "Cannot allocate abstract class"); | |
2405 applyVisitState(); | |
2406 } | |
2407 | |
2408 void visitUnresolvedRedirectingFactoryConstructorInvoke( | |
2409 NewExpression node, | |
2410 ConstructorElement constructor, | |
2411 InterfaceType type, | |
2412 NodeList arguments, | |
2413 CallStructure callStructure, | |
2414 _) { | |
2415 doUnresolved(node.send.toString()); | |
2416 applyVisitState(); | |
2417 } | |
2418 | |
2419 void doStaticGetterGet(Send node, FunctionElement getter) { | |
2420 if (getter == context.backend.fletchExternalNativeError) { | |
2421 assembler.loadSlot(0); | |
2422 return; | |
2423 } | |
2424 | |
2425 if (getter.isDeferredLoaderGetter) { | |
2426 generateUnimplementedError(node, "Deferred loading is not supported."); | |
2427 return; | |
2428 } | |
2429 | |
2430 FletchFunctionBase base = requireFunction(getter); | |
2431 int constId = functionBuilder.allocateConstantFromFunction(base.functionId); | |
2432 invokeStatic(node, constId, 0); | |
2433 } | |
2434 | |
2435 void handleStaticGetterGet(Send node, FunctionElement getter, _) { | |
2436 doStaticGetterGet(node, getter); | |
2437 applyVisitState(); | |
2438 } | |
2439 | |
2440 void handleStaticGetterInvoke( | |
2441 Send node, | |
2442 FunctionElement getter, | |
2443 NodeList arguments, | |
2444 CallStructure callStructure, | |
2445 _) { | |
2446 doStaticGetterGet(node, getter); | |
2447 for (Node argument in arguments) { | |
2448 visitForValue(argument); | |
2449 } | |
2450 invokeMethod(node, callStructure.callSelector); | |
2451 applyVisitState(); | |
2452 } | |
2453 | |
2454 void handleStaticSetterSet( | |
2455 Send node, | |
2456 FunctionElement setter, | |
2457 Node rhs, | |
2458 _) { | |
2459 visitForValue(rhs); | |
2460 FletchFunctionBase base = requireFunction(setter); | |
2461 int constId = functionBuilder.allocateConstantFromFunction(base.functionId); | |
2462 invokeStatic(node, constId, 1); | |
2463 applyVisitState(); | |
2464 } | |
2465 | |
2466 /** | |
2467 * Load the captured variables of [function], expressed in [info]. | |
2468 * | |
2469 * If [function] captures itself, its field index is returned. | |
2470 */ | |
2471 int pushCapturedVariables(FunctionElement function) { | |
2472 ClosureInfo info = closureEnvironment.closures[function]; | |
2473 if (info == null) { | |
2474 // TODO(ahe): Do not throw here, instead fix bug in incremental compiler | |
2475 // (see test closure_capture). | |
2476 throw new IncrementalCompilationFailed( | |
2477 "Internal error: no closure info for $function"); | |
2478 } | |
2479 int index = 0; | |
2480 if (info.isThisFree) { | |
2481 loadThis(); | |
2482 index++; | |
2483 } | |
2484 int thisClosureIndex = -1; | |
2485 for (LocalElement element in info.free) { | |
2486 if (element == function) { | |
2487 // If we capture ourself, remember index and assign into closure after | |
2488 // allocation. | |
2489 assembler.loadLiteralNull(); | |
2490 assert(thisClosureIndex == -1); | |
2491 thisClosureIndex = index; | |
2492 } else { | |
2493 // Load the raw value (the 'Box' when by reference). | |
2494 scope[element].loadRaw(assembler); | |
2495 } | |
2496 index++; | |
2497 } | |
2498 return thisClosureIndex; | |
2499 } | |
2500 | |
2501 void visitFunctionExpression(FunctionExpression node) { | |
2502 FunctionElement function = elements[node]; | |
2503 | |
2504 // If the closure captures itself, thisClosureIndex is the field-index in | |
2505 // the closure. | |
2506 int thisClosureIndex = pushCapturedVariables(function); | |
2507 bool needToStoreThisReference = thisClosureIndex >= 0; | |
2508 | |
2509 FletchClassBuilder classBuilder = context.backend.createClosureClass( | |
2510 function, | |
2511 closureEnvironment); | |
2512 int classConstant = functionBuilder.allocateConstantFromClass( | |
2513 classBuilder.classId); | |
2514 | |
2515 // NOTE: Currently we emit a storeField instruction in case a closure | |
2516 // captures itself. Changing fields makes it a mutable object. | |
2517 // We can therefore not allocate the object with `immutable = true`. | |
2518 // TODO(fletchc-team): Could we restrict this limitation. | |
2519 bool immutable = !closureEnvironment.closures[function].free.any( | |
2520 closureEnvironment.shouldBeBoxed) && !needToStoreThisReference; | |
2521 | |
2522 assembler.allocate( | |
2523 classConstant, classBuilder.fields, immutable: immutable); | |
2524 | |
2525 if (needToStoreThisReference) { | |
2526 assert(!immutable); | |
2527 assembler.dup(); | |
2528 assembler.storeField(thisClosureIndex); | |
2529 } | |
2530 | |
2531 if (!functionDeclarations.contains(node)) { | |
2532 registerClosurization(function, ClosureKind.localFunction); | |
2533 } | |
2534 applyVisitState(); | |
2535 } | |
2536 | |
2537 void visitExpression(Expression node) { | |
2538 generateUnimplementedError( | |
2539 node, "Missing visit of expression: ${node.runtimeType}"); | |
2540 applyVisitState(); | |
2541 } | |
2542 | |
2543 void visitStatement(Node node) { | |
2544 generateUnimplementedError( | |
2545 node, "Missing visit of statement: ${node.runtimeType}"); | |
2546 assembler.pop(); | |
2547 } | |
2548 | |
2549 void doStatements(NodeList statements) { | |
2550 List<Element> oldBlockLocals = blockLocals; | |
2551 blockLocals = <Element>[]; | |
2552 int stackSize = assembler.stackSize; | |
2553 | |
2554 for (Node statement in statements) { | |
2555 statement.accept(this); | |
2556 } | |
2557 | |
2558 int stackSizeDifference = assembler.stackSize - stackSize; | |
2559 if (stackSizeDifference != blockLocals.length) { | |
2560 internalError( | |
2561 statements, | |
2562 "Unbalanced number of block locals and stack slots used by block."); | |
2563 } | |
2564 | |
2565 if (blockLocals.length > 0) assembler.popMany(blockLocals.length); | |
2566 | |
2567 for (int i = blockLocals.length - 1; i >= 0; --i) { | |
2568 popVariableDeclaration(blockLocals[i]); | |
2569 } | |
2570 | |
2571 blockLocals = oldBlockLocals; | |
2572 } | |
2573 | |
2574 void visitBlock(Block node) { | |
2575 var breakLabel = new BytecodeLabel(); | |
2576 jumpInfo[node] = new JumpInfo(assembler.stackSize, null, breakLabel); | |
2577 doStatements(node.statements); | |
2578 assembler.bind(breakLabel); | |
2579 } | |
2580 | |
2581 void visitEmptyStatement(EmptyStatement node) { | |
2582 } | |
2583 | |
2584 void visitExpressionStatement(ExpressionStatement node) { | |
2585 visitForEffect(node.expression); | |
2586 } | |
2587 | |
2588 // Called before 'return', as an option to replace the already evaluated | |
2589 // return value. One example is setters. | |
2590 bool get hasAssignmentSemantics => false; | |
2591 void optionalReplaceResultValue() { } | |
2592 | |
2593 void visitReturn(Return node) { | |
2594 Expression expression = node.expression; | |
2595 bool returnNull = true; | |
2596 if (expression != null && !isConstNull(expression)) { | |
2597 visitForValue(expression); | |
2598 returnNull = false; | |
2599 } | |
2600 | |
2601 // Avoid using the return-null bytecode if we have assignment semantics. | |
2602 if (returnNull && hasAssignmentSemantics) { | |
2603 assembler.loadLiteralNull(); | |
2604 returnNull = false; | |
2605 } | |
2606 | |
2607 if (returnNull) { | |
2608 callFinallyBlocks(0, false); | |
2609 generateReturnNull(node); | |
2610 } else { | |
2611 callFinallyBlocks(0, true); | |
2612 optionalReplaceResultValue(); | |
2613 generateReturn(node); | |
2614 } | |
2615 } | |
2616 | |
2617 // Find the JumpInfo matching the target of [node]. | |
2618 JumpInfo getJumpTargetInfo(GotoStatement node) { | |
2619 JumpTarget target = elements.getTargetOf(node); | |
2620 if (target == null) { | |
2621 generateUnimplementedError(node, "'$node' not in loop"); | |
2622 assembler.pop(); | |
2623 return null; | |
2624 } | |
2625 Node statement = target.statement; | |
2626 JumpInfo info = jumpInfo[statement]; | |
2627 if (info == null) { | |
2628 generateUnimplementedError(node, "'$node' has no target"); | |
2629 assembler.pop(); | |
2630 } | |
2631 return info; | |
2632 } | |
2633 | |
2634 void callFinallyBlocks(int targetStackSize, bool preserveTop) { | |
2635 int popCount = 0; | |
2636 for (var block in tryBlockStack) { | |
2637 // Break once all exited finally blocks are processed. Finally blocks | |
2638 // are ordered by stack size which coincides with scoping. Blocks with | |
2639 // stack sizes at least equal to target size are being exited. | |
2640 if (block.stackSize < targetStackSize) break; | |
2641 if (block.finallyLabel == null) continue; | |
2642 if (preserveTop) { | |
2643 // We reuse the exception slot as a temporary buffer for the top | |
2644 // element, which is located -1 relative to the block's stack size. | |
2645 assembler.storeSlot(block.stackSize - 1); | |
2646 } | |
2647 // TODO(ajohnsen): Don't pop, but let subroutineCall take a 'pop count' | |
2648 // argument, just like popAndBranch. | |
2649 if (assembler.stackSize > block.stackSize) { | |
2650 int sizeDifference = assembler.stackSize - block.stackSize; | |
2651 popCount += sizeDifference; | |
2652 assembler.popMany(sizeDifference); | |
2653 } | |
2654 assembler.subroutineCall(block.finallyLabel, block.finallyReturnLabel); | |
2655 if (preserveTop) { | |
2656 assembler.loadSlot(block.stackSize - 1); | |
2657 popCount--; | |
2658 } | |
2659 } | |
2660 // Reallign stack (should be removed, according to above TODO). | |
2661 for (int i = 0; i < popCount; i++) { | |
2662 // Note we dup, to make sure the top element is the return value. | |
2663 assembler.dup(); | |
2664 } | |
2665 } | |
2666 | |
2667 void unbalancedBranch(GotoStatement node, bool isBreak) { | |
2668 JumpInfo info = getJumpTargetInfo(node); | |
2669 if (info == null) return; | |
2670 callFinallyBlocks(info.stackSize, false); | |
2671 BytecodeLabel label = isBreak ? info.breakLabel : info.continueLabel; | |
2672 int diff = assembler.stackSize - info.stackSize; | |
2673 assembler.popAndBranch(diff, label); | |
2674 } | |
2675 | |
2676 void visitBreakStatement(BreakStatement node) { | |
2677 var breakLabel = new BytecodeLabel(); | |
2678 jumpInfo[node] = new JumpInfo(assembler.stackSize, null, breakLabel); | |
2679 unbalancedBranch(node, true); | |
2680 assembler.bind(breakLabel); | |
2681 } | |
2682 | |
2683 void visitContinueStatement(ContinueStatement node) { | |
2684 unbalancedBranch(node, false); | |
2685 } | |
2686 | |
2687 void visitIf(If node) { | |
2688 ConstantExpression conditionConstant = | |
2689 inspectConstant(node.condition, isConst: false); | |
2690 | |
2691 if (conditionConstant != null) { | |
2692 BytecodeLabel end = new BytecodeLabel(); | |
2693 jumpInfo[node] = new JumpInfo(assembler.stackSize, null, end); | |
2694 if (context.getConstantValue(conditionConstant).isTrue) { | |
2695 doScopedStatement(node.thenPart); | |
2696 } else if (node.hasElsePart) { | |
2697 doScopedStatement(node.elsePart); | |
2698 } | |
2699 assembler.bind(end); | |
2700 return; | |
2701 } | |
2702 | |
2703 BytecodeLabel ifFalse = new BytecodeLabel(); | |
2704 | |
2705 visitForTest(node.condition, null, ifFalse); | |
2706 if (node.hasElsePart) { | |
2707 BytecodeLabel end = new BytecodeLabel(); | |
2708 jumpInfo[node] = new JumpInfo(assembler.stackSize, null, end); | |
2709 doScopedStatement(node.thenPart); | |
2710 assembler.branch(end); | |
2711 assembler.bind(ifFalse); | |
2712 doScopedStatement(node.elsePart); | |
2713 assembler.bind(end); | |
2714 } else { | |
2715 jumpInfo[node] = new JumpInfo(assembler.stackSize, null, ifFalse); | |
2716 doScopedStatement(node.thenPart); | |
2717 assembler.bind(ifFalse); | |
2718 } | |
2719 } | |
2720 | |
2721 void visitFor(For node) { | |
2722 List<Element> oldBlockLocals = blockLocals; | |
2723 blockLocals = <Element>[]; | |
2724 | |
2725 BytecodeLabel start = new BytecodeLabel(); | |
2726 BytecodeLabel end = new BytecodeLabel(); | |
2727 BytecodeLabel afterBody = new BytecodeLabel(); | |
2728 | |
2729 Node initializer = node.initializer; | |
2730 if (initializer != null) visitForEffect(initializer); | |
2731 | |
2732 jumpInfo[node] = new JumpInfo(assembler.stackSize, afterBody, end); | |
2733 | |
2734 assembler.bind(start); | |
2735 | |
2736 Expression condition = node.condition; | |
2737 if (condition != null) { | |
2738 visitForTest(condition, null, end); | |
2739 } | |
2740 | |
2741 doScopedStatement(node.body); | |
2742 | |
2743 assembler.bind(afterBody); | |
2744 | |
2745 for (int i = blockLocals.length - 1; i >= 0; --i) { | |
2746 LocalElement local = blockLocals[i]; | |
2747 // If the locals are captured by reference, load the current value and | |
2748 // store it in a new boxed. | |
2749 if (closureEnvironment.shouldBeBoxed(local)) { | |
2750 LocalValue value = scope[local]; | |
2751 value.load(assembler); | |
2752 value.initialize(assembler); | |
2753 assembler.storeSlot(value.slot); | |
2754 assembler.pop(); | |
2755 } | |
2756 } | |
2757 | |
2758 for (Node update in node.update) { | |
2759 visitForEffect(update); | |
2760 } | |
2761 assembler.branch(start); | |
2762 | |
2763 assembler.bind(end); | |
2764 | |
2765 for (int i = blockLocals.length - 1; i >= 0; --i) { | |
2766 assembler.pop(); | |
2767 popVariableDeclaration(blockLocals[i]); | |
2768 } | |
2769 | |
2770 blockLocals = oldBlockLocals; | |
2771 } | |
2772 | |
2773 void visitSyncForIn(SyncForIn node) { | |
2774 visitForIn(node); | |
2775 } | |
2776 | |
2777 void visitForIn(ForIn node) { | |
2778 BytecodeLabel start = new BytecodeLabel(); | |
2779 BytecodeLabel end = new BytecodeLabel(); | |
2780 | |
2781 // Evalutate expression and iterator. | |
2782 visitForValue(node.expression); | |
2783 invokeGetter(node.expression, Names.iterator); | |
2784 | |
2785 jumpInfo[node] = new JumpInfo(assembler.stackSize, start, end); | |
2786 | |
2787 assembler.bind(start); | |
2788 | |
2789 assembler.dup(); | |
2790 invokeMethod(node, Selectors.moveNext); | |
2791 assembler.branchIfFalse(end); | |
2792 | |
2793 bool isVariableDeclaration = node.declaredIdentifier.asSend() == null; | |
2794 Element element = elements[node]; | |
2795 if (isVariableDeclaration) { | |
2796 // Create local value and load the current element to it. | |
2797 LocalValue value = createLocalValueFor(element); | |
2798 assembler.dup(); | |
2799 invokeGetter(node, Names.current); | |
2800 value.initialize(assembler); | |
2801 pushVariableDeclaration(value); | |
2802 } else { | |
2803 if (element == null || element.isInstanceMember) { | |
2804 loadThis(); | |
2805 assembler.loadLocal(1); | |
2806 invokeGetter(node, Names.current); | |
2807 Selector selector = elements.getSelector(node.declaredIdentifier); | |
2808 invokeSetter(node, selector.memberName); | |
2809 } else { | |
2810 assembler.dup(); | |
2811 invokeGetter(node, Names.current); | |
2812 if (element.isLocal) { | |
2813 scope[element].store(assembler); | |
2814 } else if (element.isField) { | |
2815 doStaticFieldSet(element); | |
2816 } else if (element.isMalformed) { | |
2817 doUnresolved(element.name); | |
2818 assembler.pop(); | |
2819 } else { | |
2820 internalError(node, "Unhandled store in for-in"); | |
2821 } | |
2822 } | |
2823 assembler.pop(); | |
2824 } | |
2825 | |
2826 doScopedStatement(node.body); | |
2827 | |
2828 if (isVariableDeclaration) { | |
2829 // Pop the local again. | |
2830 assembler.pop(); | |
2831 popVariableDeclaration(element); | |
2832 } | |
2833 | |
2834 assembler.branch(start); | |
2835 | |
2836 assembler.bind(end); | |
2837 | |
2838 // Pop iterator. | |
2839 assembler.pop(); | |
2840 } | |
2841 | |
2842 void visitLabeledStatement(LabeledStatement node) { | |
2843 node.statement.accept(this); | |
2844 } | |
2845 | |
2846 // Visit the statement in a scope, where locals are popped when left. | |
2847 void doScopedStatement(Node statement) { | |
2848 Block block = statement.asBlock(); | |
2849 if (block != null) { | |
2850 doStatements(block.statements); | |
2851 } else { | |
2852 doStatements(new NodeList.singleton(statement)); | |
2853 } | |
2854 } | |
2855 | |
2856 void visitWhile(While node) { | |
2857 BytecodeLabel start = new BytecodeLabel(); | |
2858 BytecodeLabel end = new BytecodeLabel(); | |
2859 jumpInfo[node] = new JumpInfo(assembler.stackSize, start, end); | |
2860 assembler.bind(start); | |
2861 visitForTest(node.condition, null, end); | |
2862 doScopedStatement(node.body); | |
2863 assembler.branch(start); | |
2864 assembler.bind(end); | |
2865 } | |
2866 | |
2867 void visitDoWhile(DoWhile node) { | |
2868 BytecodeLabel start = new BytecodeLabel(); | |
2869 BytecodeLabel end = new BytecodeLabel(); | |
2870 BytecodeLabel skipBody = new BytecodeLabel(); | |
2871 jumpInfo[node] = new JumpInfo(assembler.stackSize, skipBody, end); | |
2872 assembler.bind(start); | |
2873 doScopedStatement(node.body); | |
2874 assembler.bind(skipBody); | |
2875 visitForTest(node.condition, start, null); | |
2876 assembler.bind(end); | |
2877 } | |
2878 | |
2879 LocalValue initializeLocal(LocalElement element, Expression initializer) { | |
2880 int slot = assembler.stackSize; | |
2881 if (initializer != null) { | |
2882 // TODO(ahe): If we can move this to the caller, then we don't need | |
2883 // functionDeclarations. | |
2884 visitForValue(initializer); | |
2885 } else { | |
2886 generateEmptyInitializer(element.node); | |
2887 } | |
2888 LocalValue value = createLocalValueFor(element, slot: slot); | |
2889 value.initialize(assembler); | |
2890 pushVariableDeclaration(value); | |
2891 blockLocals.add(element); | |
2892 return value; | |
2893 } | |
2894 | |
2895 void generateEmptyInitializer(Node node) { | |
2896 assembler.loadLiteralNull(); | |
2897 } | |
2898 | |
2899 void visitVariableDefinitions(VariableDefinitions node) { | |
2900 for (Node definition in node.definitions) { | |
2901 LocalVariableElement element = elements[definition]; | |
2902 initializeLocal(element, element.initializer); | |
2903 } | |
2904 } | |
2905 | |
2906 void visitFunctionDeclaration(FunctionDeclaration node) { | |
2907 FunctionExpression function = node.function; | |
2908 functionDeclarations.add(function); | |
2909 initializeLocal(elements[function], function); | |
2910 } | |
2911 | |
2912 void visitSwitchStatement(SwitchStatement node) { | |
2913 BytecodeLabel end = new BytecodeLabel(); | |
2914 | |
2915 visitForValue(node.expression); | |
2916 | |
2917 jumpInfo[node] = new JumpInfo(assembler.stackSize, null, end); | |
2918 | |
2919 // Install cross-case jump targets. | |
2920 for (SwitchCase switchCase in node.cases) { | |
2921 BytecodeLabel continueLabel = new BytecodeLabel(); | |
2922 jumpInfo[switchCase] = new JumpInfo( | |
2923 assembler.stackSize, | |
2924 continueLabel, | |
2925 null); | |
2926 } | |
2927 | |
2928 for (SwitchCase switchCase in node.cases) { | |
2929 BytecodeLabel ifTrue = jumpInfo[switchCase].continueLabel; | |
2930 BytecodeLabel next = new BytecodeLabel(); | |
2931 if (!switchCase.isDefaultCase) { | |
2932 for (Node labelOrCaseMatch in switchCase.labelsAndCases) { | |
2933 CaseMatch caseMatch = labelOrCaseMatch.asCaseMatch(); | |
2934 if (caseMatch == null) continue; | |
2935 generateSwitchCaseMatch(caseMatch, ifTrue); | |
2936 } | |
2937 assembler.branch(next); | |
2938 } | |
2939 assembler.bind(ifTrue); | |
2940 doStatements(switchCase.statements); | |
2941 assembler.branch(end); | |
2942 assembler.bind(next); | |
2943 } | |
2944 | |
2945 assembler.bind(end); | |
2946 assembler.pop(); | |
2947 } | |
2948 | |
2949 void doCatchBlock(CatchBlock node, int exceptionSlot, BytecodeLabel end) { | |
2950 BytecodeLabel wrongType = new BytecodeLabel(); | |
2951 | |
2952 TypeAnnotation type = node.type; | |
2953 if (type != null) { | |
2954 assembler.loadSlot(exceptionSlot); | |
2955 callIsSelector(type, elements.getType(type), type); | |
2956 assembler.branchIfFalse(wrongType); | |
2957 } | |
2958 | |
2959 List<Element> locals = <Element>[]; | |
2960 Node exception = node.exception; | |
2961 if (exception != null) { | |
2962 LocalVariableElement element = elements[exception]; | |
2963 LocalValue value = createLocalValueFor(element); | |
2964 assembler.loadSlot(exceptionSlot); | |
2965 value.initialize(assembler); | |
2966 pushVariableDeclaration(value); | |
2967 locals.add(element); | |
2968 | |
2969 Node trace = node.trace; | |
2970 if (trace != null) { | |
2971 LocalVariableElement element = elements[trace]; | |
2972 LocalValue value = createLocalValueFor(element); | |
2973 assembler.loadLiteralNull(); | |
2974 value.initialize(assembler); | |
2975 pushVariableDeclaration(value); | |
2976 // TODO(ajohnsen): Set trace. | |
2977 locals.add(element); | |
2978 } | |
2979 } | |
2980 | |
2981 node.block.accept(this); | |
2982 | |
2983 assembler.popMany(locals.length); | |
2984 for (Element e in locals) { | |
2985 popVariableDeclaration(e); | |
2986 } | |
2987 | |
2988 assembler.branch(end); | |
2989 | |
2990 assembler.bind(wrongType); | |
2991 } | |
2992 | |
2993 void visitTryStatement(TryStatement node) { | |
2994 BytecodeLabel end = new BytecodeLabel(); | |
2995 BytecodeLabel finallyLabel = new BytecodeLabel(); | |
2996 BytecodeLabel finallyReturnLabel = new BytecodeLabel(); | |
2997 | |
2998 Block finallyBlock = node.finallyBlock; | |
2999 bool hasFinally = finallyBlock != null; | |
3000 | |
3001 // Reserve slot for exception. | |
3002 int exceptionSlot = assembler.stackSize; | |
3003 assembler.loadLiteralNull(); | |
3004 | |
3005 jumpInfo[node] = new JumpInfo(assembler.stackSize, null, end); | |
3006 | |
3007 int startBytecodeSize = assembler.byteSize; | |
3008 | |
3009 tryBlockStack = tryBlockStack.prepend( | |
3010 new TryBlock( | |
3011 assembler.stackSize, | |
3012 hasFinally ? finallyLabel : null, | |
3013 hasFinally ? finallyReturnLabel: null)); | |
3014 | |
3015 node.tryBlock.accept(this); | |
3016 | |
3017 // Go to end if no exceptions was thrown. | |
3018 assembler.branch(end); | |
3019 int endBytecodeSize = assembler.byteSize; | |
3020 | |
3021 // Add catch-frame to the assembler. | |
3022 assembler.addCatchFrameRange(startBytecodeSize, endBytecodeSize); | |
3023 | |
3024 for (Node catchBlock in node.catchBlocks) { | |
3025 doCatchBlock(catchBlock, exceptionSlot, end); | |
3026 } | |
3027 | |
3028 tryBlockStack = tryBlockStack.tail; | |
3029 | |
3030 if (hasFinally) { | |
3031 if (!node.catchBlocks.isEmpty) { | |
3032 assembler.addCatchFrameRange(endBytecodeSize, assembler.byteSize); | |
3033 } | |
3034 // Catch exception from catch blocks. | |
3035 assembler.subroutineCall(finallyLabel, finallyReturnLabel); | |
3036 } | |
3037 | |
3038 // The exception was not caught. Rethrow. | |
3039 generateThrow(node); | |
3040 | |
3041 assembler.bind(end); | |
3042 | |
3043 if (hasFinally) { | |
3044 BytecodeLabel done = new BytecodeLabel(); | |
3045 assembler.subroutineCall(finallyLabel, finallyReturnLabel); | |
3046 assembler.branch(done); | |
3047 | |
3048 assembler.bind(finallyLabel); | |
3049 assembler.applyStackSizeFix(1); | |
3050 finallyBlock.accept(this); | |
3051 assembler.subroutineReturn(finallyReturnLabel); | |
3052 | |
3053 assembler.bind(done); | |
3054 } | |
3055 | |
3056 // Pop exception slot. | |
3057 assembler.pop(); | |
3058 } | |
3059 | |
3060 void doUnresolved(String name) { | |
3061 var constString = context.backend.constantSystem.createString( | |
3062 new DartString.literal(name)); | |
3063 context.markConstantUsed(constString); | |
3064 assembler.loadConst(functionBuilder.allocateConstant(constString)); | |
3065 FunctionElement function = context.backend.fletchUnresolved; | |
3066 FletchFunctionBase base = requireFunction(function); | |
3067 int constId = functionBuilder.allocateConstantFromFunction(base.functionId); | |
3068 assembler.invokeStatic(constId, 1); | |
3069 } | |
3070 | |
3071 bool checkCompileError(Element element) { | |
3072 DiagnosticMessage message = | |
3073 context.compiler.elementsWithCompileTimeErrors[element]; | |
3074 if (message != null) { | |
3075 doCompileError(message); | |
3076 return true; | |
3077 } | |
3078 return false; | |
3079 } | |
3080 | |
3081 String formatError(DiagnosticMessage diagnosticMessage) { | |
3082 return diagnosticMessage.message.computeMessage(); | |
3083 } | |
3084 | |
3085 | |
3086 void doCompileError(DiagnosticMessage errorMessage) { | |
3087 FunctionElement function = context.backend.fletchCompileError; | |
3088 FletchFunctionBase base = requireFunction(function); | |
3089 int constId = functionBuilder.allocateConstantFromFunction(base.functionId); | |
3090 String errorString = formatError(errorMessage); | |
3091 ConstantValue stringConstant = | |
3092 context.backend.constantSystem.createString( | |
3093 new DartString.literal(errorString)); | |
3094 int messageConstId = functionBuilder.allocateConstant(stringConstant); | |
3095 context.markConstantUsed(stringConstant); | |
3096 assembler.loadConst(messageConstId); | |
3097 registerInstantiatedClass(context.backend.stringImplementation); | |
3098 assembler.invokeStatic(constId, 1); | |
3099 } | |
3100 | |
3101 void visitUnresolvedInvoke( | |
3102 Send node, | |
3103 Element element, | |
3104 Node arguments, | |
3105 Selector selector, | |
3106 _) { | |
3107 if (!checkCompileError(element)) { | |
3108 doUnresolved(node.selector.toString()); | |
3109 } | |
3110 applyVisitState(); | |
3111 } | |
3112 | |
3113 void visitUnresolvedGet( | |
3114 Send node, | |
3115 Element element, | |
3116 _) { | |
3117 doUnresolved(node.selector.toString()); | |
3118 applyVisitState(); | |
3119 } | |
3120 | |
3121 void visitUnresolvedSet( | |
3122 Send node, | |
3123 Element element, | |
3124 Node rhs, | |
3125 _) { | |
3126 doUnresolved(node.selector.toString()); | |
3127 applyVisitState(); | |
3128 } | |
3129 | |
3130 void handleStaticFunctionIncompatibleInvoke( | |
3131 Send node, | |
3132 MethodElement function, | |
3133 NodeList arguments, | |
3134 CallStructure callStructure, | |
3135 _) { | |
3136 if (!checkCompileError(function)) { | |
3137 doUnresolved(function.name); | |
3138 } | |
3139 applyVisitState(); | |
3140 } | |
3141 | |
3142 void internalError(Spannable spannable, String reason) { | |
3143 context.compiler.reporter.internalError(spannable, reason); | |
3144 } | |
3145 | |
3146 void generateUnimplementedError(Spannable spannable, String reason) { | |
3147 context.backend.generateUnimplementedError( | |
3148 spannable, | |
3149 reason, | |
3150 functionBuilder); | |
3151 } | |
3152 | |
3153 String toString() => "FunctionCompiler(${element.name})"; | |
3154 | |
3155 void handleFinalStaticFieldSet( | |
3156 SendSet node, | |
3157 FieldElement field, | |
3158 Node rhs, | |
3159 _) { | |
3160 generateUnimplementedError( | |
3161 node, "[handleFinalStaticFieldSet] isn't implemented."); | |
3162 applyVisitState(); | |
3163 } | |
3164 | |
3165 void handleImmutableLocalSet( | |
3166 SendSet node, | |
3167 LocalElement element, | |
3168 Node rhs, | |
3169 _) { | |
3170 generateUnimplementedError( | |
3171 node, "[handleImmutableLocalSet] isn't implemented."); | |
3172 applyVisitState(); | |
3173 } | |
3174 | |
3175 void handleStaticSetterGet( | |
3176 Send node, | |
3177 FunctionElement setter, | |
3178 _) { | |
3179 generateUnimplementedError( | |
3180 node, "[handleStaticSetterGet] isn't implemented."); | |
3181 applyVisitState(); | |
3182 } | |
3183 | |
3184 void handleStaticSetterInvoke( | |
3185 Send node, | |
3186 FunctionElement setter, | |
3187 NodeList arguments, | |
3188 CallStructure callStructure, | |
3189 _) { | |
3190 generateUnimplementedError( | |
3191 node, "[handleStaticSetterInvoke] isn't implemented."); | |
3192 applyVisitState(); | |
3193 } | |
3194 | |
3195 void handleStaticGetterSet( | |
3196 Send node, | |
3197 FunctionElement getter, | |
3198 Node rhs, | |
3199 _) { | |
3200 generateUnimplementedError( | |
3201 node, "[handleStaticGetterSet] isn't implemented."); | |
3202 applyVisitState(); | |
3203 } | |
3204 | |
3205 void handleStaticFunctionSet( | |
3206 SendSet node, | |
3207 MethodElement function, | |
3208 Node rhs, | |
3209 _) { | |
3210 generateUnimplementedError( | |
3211 node, "[handleStaticFunctionSet] isn't implemented."); | |
3212 applyVisitState(); | |
3213 } | |
3214 | |
3215 @override | |
3216 void bulkHandleSetIfNull(Node node, _) { | |
3217 generateUnimplementedError( | |
3218 node, "[bulkHandleSetIfNull] isn't implemented."); | |
3219 applyVisitState(); | |
3220 } | |
3221 | |
3222 void previsitDeferredAccess(Send node, PrefixElement prefix, _) { | |
3223 // We don't support deferred access, so nothing to do for now. | |
3224 } | |
3225 | |
3226 void bulkHandleNode(Node node, String msg, _) { | |
3227 generateUnimplementedError(node, msg.replaceAll('#', node.toString())); | |
3228 applyVisitState(); | |
3229 } | |
3230 | |
3231 void visitNode(Node node) { | |
3232 internalError(node, "[visitNode] isn't implemented."); | |
3233 } | |
3234 | |
3235 void apply(Node node, _) { | |
3236 internalError(node, "[apply] isn't implemented."); | |
3237 } | |
3238 | |
3239 void applyInitializers(FunctionExpression initializers, _) { | |
3240 internalError(initializers, "[applyInitializers] isn't implemented."); | |
3241 } | |
3242 | |
3243 void applyParameters(NodeList parameters, _) { | |
3244 internalError(parameters, "[applyParameters] isn't implemented."); | |
3245 } | |
3246 } | |
3247 | |
3248 abstract class FletchRegistryMixin { | |
3249 FletchRegistry get registry; | |
3250 FletchContext get context; | |
3251 | |
3252 void registerDynamicUse(Selector selector) { | |
3253 registry.registerDynamicUse(selector); | |
3254 } | |
3255 | |
3256 void registerStaticUse(StaticUse staticUse) { | |
3257 registry.registerStaticUse(staticUse); | |
3258 } | |
3259 | |
3260 void registerInstantiatedClass(ClassElement klass) { | |
3261 registry.registerInstantiatedClass(klass); | |
3262 } | |
3263 | |
3264 void registerIsCheck(DartType type) { | |
3265 registry.registerIsCheck(type); | |
3266 } | |
3267 | |
3268 void registerLocalInvoke(LocalElement element, Selector selector) { | |
3269 registry.registerLocalInvoke(element, selector); | |
3270 } | |
3271 | |
3272 void registerClosurization(FunctionElement element, ClosureKind kind) { | |
3273 if (kind == ClosureKind.localFunction) { | |
3274 // TODO(ahe): Get rid of the call to [registerStaticUse]. It is | |
3275 // currently needed to ensure that local function expression closures are | |
3276 // compiled correctly. For example, `[() {}].last()`, notice that `last` | |
3277 // is a getter. This happens for both named and unnamed. | |
3278 registerStaticUse(new StaticUse.foreignUse(element)); | |
3279 } | |
3280 registry.registerClosurization(element, kind); | |
3281 } | |
3282 | |
3283 int compileLazyFieldInitializer(FieldElement field) { | |
3284 return context.backend.compileLazyFieldInitializer(field, registry); | |
3285 } | |
3286 } | |
OLD | NEW |