001    /*
002     * Copyright 2010-2013 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.jet.lang.cfg;
018    
019    import com.google.common.collect.Lists;
020    import com.intellij.psi.PsiElement;
021    import com.intellij.psi.tree.IElementType;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.jet.JetNodeTypes;
025    import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowInstructionsGenerator;
026    import org.jetbrains.jet.lang.cfg.pseudocode.LocalDeclarationInstruction;
027    import org.jetbrains.jet.lang.cfg.pseudocode.Pseudocode;
028    import org.jetbrains.jet.lang.cfg.pseudocode.PseudocodeImpl;
029    import org.jetbrains.jet.lang.psi.*;
030    import org.jetbrains.jet.lang.resolve.BindingContext;
031    import org.jetbrains.jet.lang.resolve.BindingTrace;
032    import org.jetbrains.jet.lang.resolve.constants.BooleanValue;
033    import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstantResolver;
034    import org.jetbrains.jet.lang.types.JetType;
035    import org.jetbrains.jet.lang.types.expressions.OperatorConventions;
036    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
037    import org.jetbrains.jet.lexer.JetTokens;
038    
039    import java.util.Collection;
040    import java.util.Iterator;
041    import java.util.LinkedList;
042    import java.util.List;
043    
044    import static org.jetbrains.jet.lang.diagnostics.Errors.*;
045    
046    public class JetControlFlowProcessor {
047    
048        private final JetControlFlowBuilder builder;
049        private final BindingTrace trace;
050    
051        public JetControlFlowProcessor(BindingTrace trace) {
052            this.builder = new JetControlFlowInstructionsGenerator();
053            this.trace = trace;
054        }
055    
056        public Pseudocode generatePseudocode(@NotNull JetElement subroutine) {
057            Pseudocode pseudocode = generate(subroutine);
058            ((PseudocodeImpl) pseudocode).postProcess();
059            for (LocalDeclarationInstruction localDeclarationInstruction : pseudocode.getLocalDeclarations()) {
060                ((PseudocodeImpl)localDeclarationInstruction.getBody()).postProcess();
061            }
062            return pseudocode;
063        }
064    
065        private Pseudocode generate(@NotNull JetElement subroutine) {
066            builder.enterSubroutine(subroutine);
067            CFPVisitor cfpVisitor = new CFPVisitor(false);
068            if (subroutine instanceof JetDeclarationWithBody) {
069                JetDeclarationWithBody declarationWithBody = (JetDeclarationWithBody) subroutine;
070                List<JetParameter> valueParameters = declarationWithBody.getValueParameters();
071                for (JetParameter valueParameter : valueParameters) {
072                    cfpVisitor.generateInstructions(valueParameter);
073                }
074                JetExpression bodyExpression = declarationWithBody.getBodyExpression();
075                if (bodyExpression != null) {
076                    cfpVisitor.generateInstructions(bodyExpression);
077                }
078            } else {
079                cfpVisitor.generateInstructions(subroutine);
080            }
081            return builder.exitSubroutine(subroutine);
082        }
083    
084        private void processLocalDeclaration(@NotNull JetDeclaration subroutine) {
085            Label afterDeclaration = builder.createUnboundLabel();
086            builder.nondeterministicJump(afterDeclaration);
087            generate(subroutine);
088            builder.bindLabel(afterDeclaration);
089        }
090    
091        
092        private class CFPVisitor extends JetVisitorVoid {
093            private final boolean inCondition;
094            private final JetVisitorVoid conditionVisitor = new JetVisitorVoid() {
095    
096                @Override
097                public void visitWhenConditionInRange(@NotNull JetWhenConditionInRange condition) {
098                    generateInstructions(condition.getRangeExpression(), CFPVisitor.this.inCondition); // TODO : inCondition?
099                    generateInstructions(condition.getOperationReference(), CFPVisitor.this.inCondition); // TODO : inCondition?
100                    // TODO : read the call to contains()...
101                }
102    
103                @Override
104                public void visitWhenConditionIsPattern(@NotNull JetWhenConditionIsPattern condition) {
105                    // TODO: types in CF?
106                }
107    
108                @Override
109                public void visitWhenConditionWithExpression(@NotNull JetWhenConditionWithExpression condition) {
110                    generateInstructions(condition.getExpression(), inCondition);
111                }
112    
113                @Override
114                public void visitJetElement(@NotNull JetElement element) {
115                    throw new UnsupportedOperationException("[JetControlFlowProcessor] " + element.toString());
116                }
117            };
118    
119            private CFPVisitor(boolean inCondition) {
120                this.inCondition = inCondition;
121            }
122    
123            public void generateInstructions(@Nullable JetElement element) {
124                generateInstructions(element, inCondition);
125            }
126    
127            private void generateInstructions(@Nullable JetElement element, boolean inCondition) {
128                if (element == null) return;
129                CFPVisitor visitor;
130                if (this.inCondition == inCondition) {
131                    visitor = this;
132                }
133                else {
134                    visitor = new CFPVisitor(inCondition);
135                }
136                element.accept(visitor);
137                checkNothingType(element);
138            }
139    
140            private void checkNothingType(JetElement element) {
141                if (!(element instanceof JetExpression)) return;
142                JetExpression expression = JetPsiUtil.deparenthesize((JetExpression) element);
143                if (expression instanceof JetStatementExpression || expression instanceof JetTryExpression
144                        || expression instanceof JetIfExpression || expression instanceof JetWhenExpression) {
145                    return;
146                }
147    
148                JetType type = trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, expression);
149                if (type != null && KotlinBuiltIns.getInstance().isNothing(type)) {
150                    builder.jumpToError();
151                }
152            }
153    
154            @Override
155            public void visitParenthesizedExpression(@NotNull JetParenthesizedExpression expression) {
156                builder.read(expression);
157    
158                JetExpression innerExpression = expression.getExpression();
159                if (innerExpression != null) {
160                    generateInstructions(innerExpression, inCondition);
161                }
162            }
163    
164            @Override
165            public void visitAnnotatedExpression(@NotNull JetAnnotatedExpression expression) {
166                builder.read(expression);
167    
168                JetExpression baseExpression = expression.getBaseExpression();
169                if (baseExpression != null) {
170                    generateInstructions(baseExpression, inCondition);
171                }
172            }
173    
174            @Override
175            public void visitThisExpression(@NotNull JetThisExpression expression) {
176                builder.read(expression);
177            }
178    
179            @Override
180            public void visitConstantExpression(@NotNull JetConstantExpression expression) {
181                builder.read(expression);
182            }
183    
184            @Override
185            public void visitSimpleNameExpression(@NotNull JetSimpleNameExpression expression) {
186                builder.read(expression);
187            }
188    
189            @Override
190            public void visitLabelQualifiedExpression(@NotNull JetLabelQualifiedExpression expression) {
191                String labelName = expression.getLabelName();
192                JetExpression labeledExpression = expression.getLabeledExpression();
193                if (labelName != null && labeledExpression != null) {
194                    visitLabeledExpression(labelName, labeledExpression);
195                }
196            }
197    
198            private void visitLabeledExpression(@NotNull String labelName, @NotNull JetExpression labeledExpression) {
199                JetExpression deparenthesized = JetPsiUtil.deparenthesize(labeledExpression);
200                if (deparenthesized != null) {
201                    generateInstructions(labeledExpression, inCondition);
202                }
203            }
204    
205            @SuppressWarnings("SuspiciousMethodCalls") @Override
206            public void visitBinaryExpression(@NotNull JetBinaryExpression expression) {
207                IElementType operationType = expression.getOperationReference().getReferencedNameElementType();
208                JetExpression right = expression.getRight();
209                if (operationType == JetTokens.ANDAND) {
210                    generateInstructions(expression.getLeft(), true);
211                    Label resultLabel = builder.createUnboundLabel();
212                    builder.jumpOnFalse(resultLabel);
213                    if (right != null) {
214                        generateInstructions(right, true);
215                    }
216                    builder.bindLabel(resultLabel);
217                    if (!inCondition) {
218                        builder.read(expression);
219                    }
220                }
221                else if (operationType == JetTokens.OROR) {
222                    generateInstructions(expression.getLeft(), true);
223                    Label resultLabel = builder.createUnboundLabel();
224                    builder.jumpOnTrue(resultLabel);
225                    if (right != null) {
226                        generateInstructions(right, true);
227                    }
228                    builder.bindLabel(resultLabel);
229                    if (!inCondition) {
230                        builder.read(expression);
231                    }
232                }
233                else if (operationType == JetTokens.EQ) {
234                    JetExpression left = JetPsiUtil.deparenthesize(expression.getLeft());
235                    if (right != null) {
236                        generateInstructions(right, false);
237                    }
238                    if (left instanceof JetSimpleNameExpression) {
239                        builder.write(expression, left);
240                    }
241                    else if (left instanceof JetArrayAccessExpression) {
242                        JetArrayAccessExpression arrayAccessExpression = (JetArrayAccessExpression) left;
243                        visitAssignToArrayAccess(expression, arrayAccessExpression);
244                    }
245                    else if (left instanceof JetQualifiedExpression) {
246                        JetQualifiedExpression qualifiedExpression = (JetQualifiedExpression) left;
247                        generateInstructions(qualifiedExpression.getReceiverExpression(), false);
248                        generateInstructions(expression.getOperationReference(), false);
249                        builder.write(expression, left);
250                    }
251                    else {
252                        builder.unsupported(expression); // TODO
253                    }
254                }
255                else if (OperatorConventions.ASSIGNMENT_OPERATIONS.containsKey(operationType)) {
256                    JetExpression left = JetPsiUtil.deparenthesize(expression.getLeft());
257                    if (left != null) {
258                        generateInstructions(left, false);
259                    }
260                    if (right != null) {
261                        generateInstructions(right, false);
262                    }
263                    if (left instanceof JetSimpleNameExpression || left instanceof JetArrayAccessExpression) {
264                        generateInstructions(expression.getOperationReference(), false);
265                        builder.write(expression, left);
266                    }
267                    else if (left != null) {
268                        builder.unsupported(expression); // TODO
269                    }
270                }
271                else if (operationType == JetTokens.ELVIS) {
272                    builder.read(expression);
273                    generateInstructions(expression.getLeft(), false);
274                    generateInstructions(expression.getOperationReference(), false);
275                    Label afterElvis = builder.createUnboundLabel();
276                    builder.jumpOnTrue(afterElvis);
277                    if (right != null) {
278                        generateInstructions(right, false);
279                    }
280                    builder.bindLabel(afterElvis);
281                }
282                else {
283                    generateInstructions(expression.getLeft(), false);
284                    if (right != null) {
285                        generateInstructions(right, false);
286                    }
287                    generateInstructions(expression.getOperationReference(), false);
288                    builder.read(expression);
289                }
290            }
291    
292            private void visitAssignToArrayAccess(JetBinaryExpression expression, JetArrayAccessExpression arrayAccessExpression) {
293                for (JetExpression index : arrayAccessExpression.getIndexExpressions()) {
294                    generateInstructions(index, false);
295                }
296                generateInstructions(arrayAccessExpression.getArrayExpression(), false);
297                generateInstructions(expression.getOperationReference(), false);
298                builder.write(expression, arrayAccessExpression); // TODO : ???
299            }
300    
301            @Override
302            public void visitUnaryExpression(@NotNull JetUnaryExpression expression) {
303                JetSimpleNameExpression operationSign = expression.getOperationReference();
304                IElementType operationType = operationSign.getReferencedNameElementType();
305                JetExpression baseExpression = expression.getBaseExpression();
306                if (baseExpression == null) return;
307                if (JetTokens.LABELS.contains(operationType)) {
308                    String referencedName = operationSign.getReferencedName();
309                    visitLabeledExpression(referencedName.substring(1), baseExpression);
310                }
311                else {
312                    generateInstructions(baseExpression, false);
313                    generateInstructions(operationSign, false);
314    
315                    boolean incrementOrDecrement = isIncrementOrDecrement(operationType);
316                    if (incrementOrDecrement) {
317                        builder.write(expression, baseExpression);
318                    }
319    
320                    builder.read(expression);
321                }
322            }
323    
324            private boolean isIncrementOrDecrement(IElementType operationType) {
325                return operationType == JetTokens.PLUSPLUS || operationType == JetTokens.MINUSMINUS;
326            }
327    
328    
329            @Override
330            public void visitIfExpression(@NotNull JetIfExpression expression) {
331                JetExpression condition = expression.getCondition();
332                if (condition != null) {
333                    generateInstructions(condition, true);
334                }
335                Label elseLabel = builder.createUnboundLabel();
336                builder.jumpOnFalse(elseLabel);
337                JetExpression thenBranch = expression.getThen();
338                if (thenBranch != null) {
339                    generateInstructions(thenBranch, inCondition);
340                }
341                else {
342                    builder.readUnit(expression);
343                }
344                Label resultLabel = builder.createUnboundLabel();
345                builder.jump(resultLabel);
346                builder.bindLabel(elseLabel);
347                JetExpression elseBranch = expression.getElse();
348                if (elseBranch != null) {
349                    generateInstructions(elseBranch, inCondition);
350                }
351                else {
352                    builder.readUnit(expression);
353                }
354                builder.bindLabel(resultLabel);
355            }
356            
357            private class FinallyBlockGenerator {
358                private final JetFinallySection finallyBlock;
359                private Label startFinally = null;
360                private Label finishFinally = null;
361    
362                private FinallyBlockGenerator(JetFinallySection block) {
363                    finallyBlock = block;
364                }
365    
366                public void generate() {
367                    JetBlockExpression finalExpression = finallyBlock.getFinalExpression();
368                    if (finalExpression == null) return;
369                    if (startFinally != null) {
370                        assert finishFinally != null;
371                        builder.repeatPseudocode(startFinally, finishFinally);
372                        return;
373                    }
374                    startFinally = builder.createUnboundLabel("start finally");
375                    builder.bindLabel(startFinally);
376                    generateInstructions(finalExpression, inCondition);
377                    finishFinally = builder.createUnboundLabel("finish finally");
378                    builder.bindLabel(finishFinally);
379                }
380            }
381           
382    
383            @Override
384            public void visitTryExpression(@NotNull JetTryExpression expression) {
385                builder.read(expression);
386                JetFinallySection finallyBlock = expression.getFinallyBlock();
387                final FinallyBlockGenerator finallyBlockGenerator = new FinallyBlockGenerator(finallyBlock);
388                if (finallyBlock != null) {
389                    builder.enterTryFinally(new GenerationTrigger() {
390                        private boolean working = false;
391    
392                        @Override
393                        public void generate() {
394                            // This checks are needed for the case of having e.g. return inside finally: 'try {return} finally{return}'
395                            if (working) return;
396                            working = true;
397                            finallyBlockGenerator.generate();
398                            working = false;
399                        }
400                    });
401                }
402    
403                List<JetCatchClause> catchClauses = expression.getCatchClauses();
404                boolean hasCatches = !catchClauses.isEmpty();
405                Label onException = null;
406                if (hasCatches) {
407                    onException = builder.createUnboundLabel("onException");
408                    builder.nondeterministicJump(onException);
409                }
410                Label onExceptionToFinallyBlock = null;
411                if (finallyBlock != null) {
412                    onExceptionToFinallyBlock = builder.createUnboundLabel("onExceptionToFinallyBlock");
413                    builder.nondeterministicJump(onExceptionToFinallyBlock);
414                }
415                generateInstructions(expression.getTryBlock(), inCondition);
416    
417                Collection<Label> allowDeadLabels = Lists.newArrayList();
418                if (hasCatches) {
419                    Label afterCatches = builder.createUnboundLabel("afterCatches");
420                    builder.jump(afterCatches);
421    
422                    builder.bindLabel(onException);
423                    LinkedList<Label> catchLabels = Lists.newLinkedList();
424                    int catchClausesSize = catchClauses.size();
425                    for (int i = 0; i < catchClausesSize - 1; i++) {
426                        catchLabels.add(builder.createUnboundLabel("catch " + i));
427                    }
428                    if (!catchLabels.isEmpty()) {
429                        builder.nondeterministicJump(catchLabels);
430                    }
431                    boolean isFirst = true;
432                    for (JetCatchClause catchClause : catchClauses) {
433                        if (!isFirst) {
434                            builder.bindLabel(catchLabels.remove());
435                        }
436                        else {
437                            isFirst = false;
438                        }
439                        JetParameter catchParameter = catchClause.getCatchParameter();
440                        if (catchParameter != null) {
441                            builder.declare(catchParameter);
442                            builder.write(catchParameter, catchParameter);
443                        }
444                        JetExpression catchBody = catchClause.getCatchBody();
445                        if (catchBody != null) {
446                            generateInstructions(catchBody, false);
447                        }
448                        builder.jump(afterCatches);
449                    }
450    
451                    builder.bindLabel(afterCatches);
452                }
453    
454                if (finallyBlock != null) {
455                    builder.exitTryFinally();
456    
457                    Label skipFinallyToErrorBlock = builder.createUnboundLabel("skipFinallyToErrorBlock");
458                    builder.jump(skipFinallyToErrorBlock);
459                    builder.bindLabel(onExceptionToFinallyBlock);
460                    finallyBlockGenerator.generate();
461                    builder.jumpToError();
462                    builder.bindLabel(skipFinallyToErrorBlock);
463    
464                    finallyBlockGenerator.generate();
465                }
466            }
467    
468            @Override
469            public void visitWhileExpression(@NotNull JetWhileExpression expression) {
470                builder.read(expression);
471                LoopInfo loopInfo = builder.enterLoop(expression, null, null);
472    
473                builder.bindLabel(loopInfo.getConditionEntryPoint());
474                JetExpression condition = expression.getCondition();
475                if (condition != null) {
476                    generateInstructions(condition, true);
477                }
478                boolean conditionIsTrueConstant = false;
479                if (condition instanceof JetConstantExpression && condition.getNode().getElementType() == JetNodeTypes.BOOLEAN_CONSTANT) {
480                    if (BooleanValue.TRUE == new CompileTimeConstantResolver().getBooleanValue(
481                            (JetConstantExpression) condition, KotlinBuiltIns.getInstance().getBooleanType())) {
482                        conditionIsTrueConstant = true;
483                    }
484                }
485                if (!conditionIsTrueConstant) {
486                    builder.jumpOnFalse(loopInfo.getExitPoint());
487                }
488    
489                builder.bindLabel(loopInfo.getBodyEntryPoint());
490                JetExpression body = expression.getBody();
491                if (body != null) {
492                    generateInstructions(body, false);
493                }
494                builder.jump(loopInfo.getEntryPoint());
495                builder.exitLoop(expression);
496                builder.readUnit(expression);
497            }
498    
499            @Override
500            public void visitDoWhileExpression(@NotNull JetDoWhileExpression expression) {
501                builder.read(expression);
502                LoopInfo loopInfo = builder.enterLoop(expression, null, null);
503    
504                builder.bindLabel(loopInfo.getBodyEntryPoint());
505                JetExpression body = expression.getBody();
506                if (body != null) {
507                    generateInstructions(body, false);
508                }
509                builder.bindLabel(loopInfo.getConditionEntryPoint());
510                JetExpression condition = expression.getCondition();
511                if (condition != null) {
512                    generateInstructions(condition, true);
513                }
514                builder.jumpOnTrue(loopInfo.getEntryPoint());
515                builder.exitLoop(expression);
516                builder.readUnit(expression);
517            }
518    
519            @Override
520            public void visitForExpression(@NotNull JetForExpression expression) {
521                builder.read(expression);
522                JetExpression loopRange = expression.getLoopRange();
523                if (loopRange != null) {
524                    generateInstructions(loopRange, false);
525                }
526                JetParameter loopParameter = expression.getLoopParameter();
527                if (loopParameter != null) {
528                    generateInstructions(loopParameter, inCondition);
529                }
530                else {
531                    JetMultiDeclaration multiParameter = expression.getMultiParameter();
532                    generateInstructions(multiParameter, inCondition);
533                }
534    
535                // TODO : primitive cases
536                Label loopExitPoint = builder.createUnboundLabel();
537                Label conditionEntryPoint = builder.createUnboundLabel();
538    
539                builder.bindLabel(conditionEntryPoint);
540                builder.nondeterministicJump(loopExitPoint);
541    
542                LoopInfo loopInfo = builder.enterLoop(expression, loopExitPoint, conditionEntryPoint);
543    
544                builder.bindLabel(loopInfo.getBodyEntryPoint());
545                JetExpression body = expression.getBody();
546                if (body != null) {
547                    generateInstructions(body, false);
548                }
549    
550                builder.nondeterministicJump(loopInfo.getEntryPoint());
551                builder.exitLoop(expression);
552                builder.readUnit(expression);
553            }
554    
555            @Override
556            public void visitBreakExpression(@NotNull JetBreakExpression expression) {
557                JetElement loop = getCorrespondingLoop(expression);
558                if (loop != null) {
559                    builder.jump(builder.getExitPoint(loop));
560                }
561            }
562    
563            @Override
564            public void visitContinueExpression(@NotNull JetContinueExpression expression) {
565                JetElement loop = getCorrespondingLoop(expression);
566                if (loop != null) {
567                    builder.jump(builder.getEntryPoint(loop));
568                }
569            }
570    
571            private JetElement getCorrespondingLoop(JetLabelQualifiedExpression expression) {
572                String labelName = expression.getLabelName();
573                JetElement loop;
574                if (labelName != null) {
575                    JetSimpleNameExpression targetLabel = expression.getTargetLabel();
576                    assert targetLabel != null;
577                    PsiElement labeledElement = trace.get(BindingContext.LABEL_TARGET, targetLabel);
578                    if (labeledElement instanceof JetLoopExpression) {
579                        loop = (JetLoopExpression) labeledElement;
580                    }
581                    else {
582                        trace.report(NOT_A_LOOP_LABEL.on(expression, targetLabel.getText()));
583                        loop = null;
584                    }
585                }
586                else {
587                    loop = builder.getCurrentLoop();
588                    if (loop == null) {
589                        trace.report(BREAK_OR_CONTINUE_OUTSIDE_A_LOOP.on(expression));
590                    }
591                }
592                return loop;
593            }
594    
595            @Override
596            public void visitReturnExpression(@NotNull JetReturnExpression expression) {
597                JetExpression returnedExpression = expression.getReturnedExpression();
598                if (returnedExpression != null) {
599                    generateInstructions(returnedExpression, false);
600                }
601                JetSimpleNameExpression labelElement = expression.getTargetLabel();
602                JetElement subroutine;
603                String labelName = expression.getLabelName();
604                if (labelElement != null) {
605                    assert labelName != null;
606                    PsiElement labeledElement = trace.get(BindingContext.LABEL_TARGET, labelElement);
607                    if (labeledElement != null) {
608                        assert labeledElement instanceof JetElement;
609                        subroutine = (JetElement) labeledElement;
610                    }
611                    else {
612                        subroutine = null;
613                    }
614                }
615                else {
616                    subroutine = builder.getReturnSubroutine();
617                    // TODO : a context check
618                }
619    
620                if (subroutine instanceof JetFunction || subroutine instanceof JetPropertyAccessor) {
621                    if (returnedExpression == null) {
622                        builder.returnNoValue(expression, subroutine);
623                    }
624                    else {
625                        builder.returnValue(expression, subroutine);
626                    }
627                }
628            }
629    
630            @Override
631            public void visitParameter(@NotNull JetParameter parameter) {
632                builder.declare(parameter);
633                JetExpression defaultValue = parameter.getDefaultValue();
634                if (defaultValue != null) {
635                    generateInstructions(defaultValue, inCondition);
636                }
637                builder.write(parameter, parameter);
638            }
639    
640            @Override
641            public void visitBlockExpression(@NotNull JetBlockExpression expression) {
642                List<JetElement> statements = expression.getStatements();
643                for (JetElement statement : statements) {
644                    generateInstructions(statement, false);
645                }
646                if (statements.isEmpty()) {
647                    builder.readUnit(expression);
648                }
649            }
650    
651            @Override
652            public void visitNamedFunction(@NotNull JetNamedFunction function) {
653                processLocalDeclaration(function);
654            }
655    
656            @Override
657            public void visitFunctionLiteralExpression(@NotNull JetFunctionLiteralExpression expression) {
658                JetFunctionLiteral functionLiteral = expression.getFunctionLiteral();
659                processLocalDeclaration(functionLiteral);
660                builder.read(expression);
661            }
662    
663            @Override
664            public void visitQualifiedExpression(@NotNull JetQualifiedExpression expression) {
665                generateInstructions(expression.getReceiverExpression(), false);
666                JetExpression selectorExpression = expression.getSelectorExpression();
667                if (selectorExpression != null) {
668                    generateInstructions(selectorExpression, false);
669                }
670                builder.read(expression);
671            }
672    
673            private void visitCall(JetCallElement call) {
674                for (ValueArgument argument : call.getValueArguments()) {
675                    JetExpression argumentExpression = argument.getArgumentExpression();
676                    if (argumentExpression != null) {
677                        generateInstructions(argumentExpression, false);
678                    }
679                }
680    
681                for (JetExpression functionLiteral : call.getFunctionLiteralArguments()) {
682                    generateInstructions(functionLiteral, false);
683                }
684            }
685    
686            @Override
687            public void visitCallExpression(@NotNull JetCallExpression expression) {
688                for (JetTypeProjection typeArgument : expression.getTypeArguments()) {
689                    generateInstructions(typeArgument, false);
690                }
691    
692                visitCall(expression);
693    
694                generateInstructions(expression.getCalleeExpression(), false);
695                builder.read(expression);
696            }
697    
698            @Override
699            public void visitProperty(@NotNull JetProperty property) {
700                builder.declare(property);
701                JetExpression initializer = property.getInitializer();
702                if (initializer != null) {
703                    generateInstructions(initializer, false);
704                    builder.write(property, property);
705                }
706                JetExpression delegate = property.getDelegateExpression();
707                if (delegate != null) {
708                    generateInstructions(delegate, false);
709                }
710                for (JetPropertyAccessor accessor : property.getAccessors()) {
711                    generateInstructions(accessor, false);
712                }
713            }
714    
715            @Override
716            public void visitMultiDeclaration(@NotNull JetMultiDeclaration declaration) {
717                JetExpression initializer = declaration.getInitializer();
718                if (initializer != null) {
719                    generateInstructions(initializer, false);
720                }
721                List<JetMultiDeclarationEntry> entries = declaration.getEntries();
722                for (JetMultiDeclarationEntry entry : entries) {
723                    builder.declare(entry);
724                    builder.write(entry, entry);
725                }
726            }
727    
728            @Override
729            public void visitPropertyAccessor(@NotNull JetPropertyAccessor accessor) {
730                processLocalDeclaration(accessor);
731            }
732    
733            @Override
734            public void visitBinaryWithTypeRHSExpression(@NotNull JetBinaryExpressionWithTypeRHS expression) {
735                IElementType operationType = expression.getOperationReference().getReferencedNameElementType();
736                if (operationType == JetTokens.COLON || operationType == JetTokens.AS_KEYWORD || operationType == JetTokens.AS_SAFE) {
737                    generateInstructions(expression.getLeft(), false);
738                    builder.read(expression);
739                }
740                else {
741                    visitJetElement(expression);
742                }
743            }
744    
745            @Override
746            public void visitThrowExpression(@NotNull JetThrowExpression expression) {
747                JetExpression thrownExpression = expression.getThrownExpression();
748                if (thrownExpression != null) {
749                    generateInstructions(thrownExpression, false);
750                }
751                builder.throwException(expression);
752            }
753    
754            @Override
755            public void visitArrayAccessExpression(@NotNull JetArrayAccessExpression expression) {
756                for (JetExpression index : expression.getIndexExpressions()) {
757                    generateInstructions(index, false);
758                }
759                generateInstructions(expression.getArrayExpression(), false);
760                // TODO : read 'get' or 'set' function
761                builder.read(expression);
762            }
763    
764            @Override
765            public void visitIsExpression(@NotNull JetIsExpression expression) {
766                generateInstructions(expression.getLeftHandSide(), inCondition);
767                // no CF for types
768                // TODO : builder.read(expression.getPattern());
769                builder.read(expression);
770            }
771    
772            @Override
773            public void visitWhenExpression(@NotNull JetWhenExpression expression) {
774                JetExpression subjectExpression = expression.getSubjectExpression();
775                if (subjectExpression != null) {
776                    generateInstructions(subjectExpression, inCondition);
777                }
778                boolean hasElse = false;
779    
780                Label doneLabel = builder.createUnboundLabel();
781    
782                Label nextLabel = null;
783                for (Iterator<JetWhenEntry> iterator = expression.getEntries().iterator(); iterator.hasNext(); ) {
784                    JetWhenEntry whenEntry = iterator.next();
785    
786                    builder.read(whenEntry);
787    
788                    boolean isElse = whenEntry.isElse();
789                    if (isElse) {
790                        hasElse = true;
791                        if (iterator.hasNext()) {
792                            trace.report(ELSE_MISPLACED_IN_WHEN.on(whenEntry));
793                        }
794                    }
795                    Label bodyLabel = builder.createUnboundLabel();
796    
797                    JetWhenCondition[] conditions = whenEntry.getConditions();
798                    for (int i = 0; i < conditions.length; i++) {
799                        JetWhenCondition condition = conditions[i];
800                        condition.accept(conditionVisitor);
801                        if (i + 1 < conditions.length) {
802                            builder.nondeterministicJump(bodyLabel);
803                        }
804                    }
805    
806                    if (!isElse) {
807                        nextLabel = builder.createUnboundLabel();
808                        builder.nondeterministicJump(nextLabel);
809                    }
810    
811                    builder.bindLabel(bodyLabel);
812                    generateInstructions(whenEntry.getExpression(), inCondition);
813                    builder.jump(doneLabel);
814    
815                    if (!isElse) {
816                        builder.bindLabel(nextLabel);
817                    }
818                }
819                builder.bindLabel(doneLabel);
820                if (!hasElse && WhenChecker.mustHaveElse(expression, trace)) {
821                    trace.report(NO_ELSE_IN_WHEN.on(expression));
822                }
823            }
824    
825            @Override
826            public void visitObjectLiteralExpression(@NotNull JetObjectLiteralExpression expression) {
827                JetObjectDeclaration declaration = expression.getObjectDeclaration();
828                generateInstructions(declaration, inCondition);
829    
830                List<JetDeclaration> declarations = declaration.getDeclarations();
831                List<JetDeclaration> functions = Lists.newArrayList();
832                for (JetDeclaration localDeclaration : declarations) {
833                    if (!(localDeclaration instanceof JetProperty) && !(localDeclaration instanceof JetClassInitializer)) {
834                        functions.add(localDeclaration);
835                    }
836                }
837                for (JetDeclaration function : functions) {
838                    generateInstructions(function, inCondition);
839                }
840                builder.read(expression);
841            }
842    
843            @Override
844            public void visitObjectDeclaration(@NotNull JetObjectDeclaration objectDeclaration) {
845                visitClassOrObject(objectDeclaration);
846            }
847    
848            @Override
849            public void visitStringTemplateExpression(@NotNull JetStringTemplateExpression expression) {
850                for (JetStringTemplateEntry entry : expression.getEntries()) {
851                    if (entry instanceof JetStringTemplateEntryWithExpression) {
852                        JetStringTemplateEntryWithExpression entryWithExpression = (JetStringTemplateEntryWithExpression) entry;
853                        generateInstructions(entryWithExpression.getExpression(), false);
854                    }
855                }
856                builder.read(expression);
857            }
858    
859            @Override
860            public void visitTypeProjection(@NotNull JetTypeProjection typeProjection) {
861                // TODO : Support Type Arguments. Class object may be initialized at this point");
862            }
863    
864            @Override
865            public void visitAnonymousInitializer(@NotNull JetClassInitializer classInitializer) {
866                generateInstructions(classInitializer.getBody(), inCondition);
867            }
868    
869            private void visitClassOrObject(JetClassOrObject classOrObject) {
870                for (JetDelegationSpecifier specifier : classOrObject.getDelegationSpecifiers()) {
871                    generateInstructions(specifier, inCondition);
872                }
873                List<JetDeclaration> declarations = classOrObject.getDeclarations();
874                for (JetDeclaration declaration : declarations) {
875                    if (declaration instanceof JetProperty || declaration instanceof JetClassInitializer) {
876                        generateInstructions(declaration, inCondition);
877                    }
878                }
879            }
880    
881            @Override
882            public void visitClass(@NotNull JetClass klass) {
883                List<JetParameter> parameters = klass.getPrimaryConstructorParameters();
884                for (JetParameter parameter : parameters) {
885                    generateInstructions(parameter, inCondition);
886                }
887                visitClassOrObject(klass);
888            }
889    
890            @Override
891            public void visitDelegationToSuperCallSpecifier(@NotNull JetDelegatorToSuperCall call) {
892                List<? extends ValueArgument> valueArguments = call.getValueArguments();
893                for (ValueArgument valueArgument : valueArguments) {
894                    generateInstructions(valueArgument.getArgumentExpression(), inCondition);
895                }
896            }
897    
898            @Override
899            public void visitDelegationByExpressionSpecifier(@NotNull JetDelegatorByExpressionSpecifier specifier) {
900                generateInstructions(specifier.getDelegateExpression(), inCondition);
901            }
902    
903            @Override
904            public void visitJetFile(@NotNull JetFile file) {
905                for (JetDeclaration declaration : file.getDeclarations()) {
906                    if (declaration instanceof JetProperty) {
907                        generateInstructions(declaration, inCondition);
908                    }
909                }
910            }
911    
912            @Override
913            public void visitJetElement(@NotNull JetElement element) {
914                builder.unsupported(element);
915            }
916        }
917    }