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.ImmutableSet;
020    import com.google.common.collect.Lists;
021    import com.intellij.psi.PsiElement;
022    import com.intellij.psi.tree.IElementType;
023    import com.intellij.psi.util.PsiTreeUtil;
024    import org.jetbrains.annotations.NotNull;
025    import org.jetbrains.annotations.Nullable;
026    import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowInstructionsGenerator;
027    import org.jetbrains.jet.lang.cfg.pseudocode.Pseudocode;
028    import org.jetbrains.jet.lang.cfg.pseudocode.PseudocodeImpl;
029    import org.jetbrains.jet.lang.descriptors.*;
030    import org.jetbrains.jet.lang.psi.*;
031    import org.jetbrains.jet.lang.resolve.BindingContext;
032    import org.jetbrains.jet.lang.resolve.BindingContextUtils;
033    import org.jetbrains.jet.lang.resolve.BindingTrace;
034    import org.jetbrains.jet.lang.resolve.CompileTimeConstantUtils;
035    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
036    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument;
037    import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall;
038    import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
039    import org.jetbrains.jet.lang.resolve.name.Name;
040    import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
041    import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
042    import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiver;
043    import org.jetbrains.jet.lang.resolve.scopes.receivers.TransientReceiver;
044    import org.jetbrains.jet.lang.types.JetType;
045    import org.jetbrains.jet.lang.types.expressions.OperatorConventions;
046    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
047    import org.jetbrains.jet.lexer.JetToken;
048    import org.jetbrains.jet.lexer.JetTokens;
049    
050    import java.util.Iterator;
051    import java.util.LinkedList;
052    import java.util.List;
053    
054    import static org.jetbrains.jet.lang.cfg.JetControlFlowBuilder.PredefinedOperation.*;
055    import static org.jetbrains.jet.lang.cfg.JetControlFlowProcessor.CFPContext.IN_CONDITION;
056    import static org.jetbrains.jet.lang.cfg.JetControlFlowProcessor.CFPContext.NOT_IN_CONDITION;
057    import static org.jetbrains.jet.lang.diagnostics.Errors.*;
058    import static org.jetbrains.jet.lexer.JetTokens.*;
059    
060    public class JetControlFlowProcessor {
061    
062        private final JetControlFlowBuilder builder;
063        private final BindingTrace trace;
064    
065        public JetControlFlowProcessor(BindingTrace trace) {
066            this.builder = new JetControlFlowInstructionsGenerator();
067            this.trace = trace;
068        }
069    
070        @NotNull
071        public Pseudocode generatePseudocode(@NotNull JetElement subroutine) {
072            Pseudocode pseudocode = generate(subroutine);
073            ((PseudocodeImpl) pseudocode).postProcess();
074            return pseudocode;
075        }
076    
077        @NotNull
078        private Pseudocode generate(@NotNull JetElement subroutine) {
079            builder.enterSubroutine(subroutine);
080            CFPVisitor cfpVisitor = new CFPVisitor(builder);
081            if (subroutine instanceof JetDeclarationWithBody) {
082                JetDeclarationWithBody declarationWithBody = (JetDeclarationWithBody) subroutine;
083                List<JetParameter> valueParameters = declarationWithBody.getValueParameters();
084                for (JetParameter valueParameter : valueParameters) {
085                    cfpVisitor.generateInstructions(valueParameter, NOT_IN_CONDITION);
086                }
087                JetExpression bodyExpression = declarationWithBody.getBodyExpression();
088                if (bodyExpression != null) {
089                    cfpVisitor.generateInstructions(bodyExpression, NOT_IN_CONDITION);
090                }
091            } else {
092                cfpVisitor.generateInstructions(subroutine, NOT_IN_CONDITION);
093            }
094            return builder.exitSubroutine(subroutine);
095        }
096    
097        private void processLocalDeclaration(@NotNull JetDeclaration subroutine) {
098            JetElement parent = PsiTreeUtil.getParentOfType(subroutine, JetElement.class);
099            assert parent != null;
100    
101            Label afterDeclaration = builder.createUnboundLabel();
102    
103            builder.nondeterministicJump(afterDeclaration, parent);
104            generate(subroutine);
105            builder.bindLabel(afterDeclaration);
106        }
107    
108        /*package*/ enum CFPContext {
109            IN_CONDITION(true),
110            NOT_IN_CONDITION(false);
111    
112            private final boolean inCondition;
113    
114            private CFPContext(boolean inCondition) {
115                this.inCondition = inCondition;
116            }
117    
118            public boolean inCondition() {
119                return inCondition;
120            }
121        }
122        
123        private class CFPVisitor extends JetVisitorVoidWithParameter<CFPContext> {
124            private final JetControlFlowBuilder builder;
125    
126            private final JetVisitorVoidWithParameter<CFPContext> conditionVisitor = new JetVisitorVoidWithParameter<CFPContext>() {
127    
128                @Override
129                public void visitWhenConditionInRangeVoid(@NotNull JetWhenConditionInRange condition, CFPContext context) {
130                    generateInstructions(condition.getRangeExpression(), context);
131                    generateInstructions(condition.getOperationReference(), context);
132                    // TODO : read the call to contains()...
133                }
134    
135                @Override
136                public void visitWhenConditionIsPatternVoid(@NotNull JetWhenConditionIsPattern condition, CFPContext context) {
137                    // TODO: types in CF?
138                }
139    
140                @Override
141                public void visitWhenConditionWithExpressionVoid(@NotNull JetWhenConditionWithExpression condition, CFPContext context) {
142                    generateInstructions(condition.getExpression(), context);
143                }
144    
145                @Override
146                public void visitJetElementVoid(@NotNull JetElement element, CFPContext context) {
147                    throw new UnsupportedOperationException("[JetControlFlowProcessor] " + element.toString());
148                }
149            };
150    
151            private CFPVisitor(@NotNull JetControlFlowBuilder builder) {
152                this.builder = builder;
153            }
154    
155            private void mark(JetElement element) {
156                builder.mark(element);
157            }
158    
159            public void generateInstructions(@Nullable JetElement element, CFPContext context) {
160                if (element == null) return;
161                element.accept(this, context);
162                checkNothingType(element);
163            }
164    
165            private void checkNothingType(JetElement element) {
166                if (!(element instanceof JetExpression)) return;
167    
168                JetExpression expression = JetPsiUtil.deparenthesize((JetExpression) element);
169                if (expression == null) return;
170    
171                if (expression instanceof JetStatementExpression || expression instanceof JetTryExpression
172                        || expression instanceof JetIfExpression || expression instanceof JetWhenExpression) {
173                    return;
174                }
175    
176                JetType type = trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, expression);
177                if (type != null && KotlinBuiltIns.getInstance().isNothing(type)) {
178                    builder.jumpToError(expression);
179                }
180            }
181    
182            @Override
183            public void visitParenthesizedExpressionVoid(@NotNull JetParenthesizedExpression expression, CFPContext context) {
184                mark(expression);
185                JetExpression innerExpression = expression.getExpression();
186                if (innerExpression != null) {
187                    generateInstructions(innerExpression, context);
188                }
189            }
190    
191            @Override
192            public void visitAnnotatedExpressionVoid(@NotNull JetAnnotatedExpression expression, CFPContext context) {
193                JetExpression baseExpression = expression.getBaseExpression();
194                if (baseExpression != null) {
195                    generateInstructions(baseExpression, context);
196                }
197            }
198    
199            @Override
200            public void visitThisExpressionVoid(@NotNull JetThisExpression expression, CFPContext context) {
201                ResolvedCall<?> resolvedCall = getResolvedCall(expression);
202                if (resolvedCall == null) {
203                    builder.readThis(expression, null);
204                    return;
205                }
206    
207                CallableDescriptor resultingDescriptor = resolvedCall.getResultingDescriptor();
208                if (resultingDescriptor instanceof ReceiverParameterDescriptor) {
209                    builder.readThis(expression, (ReceiverParameterDescriptor) resultingDescriptor);
210                }
211            }
212    
213            @Override
214            public void visitConstantExpressionVoid(@NotNull JetConstantExpression expression, CFPContext context) {
215                CompileTimeConstant<?> constant = trace.get(BindingContext.COMPILE_TIME_VALUE, expression);
216                builder.loadConstant(expression, constant);
217            }
218    
219            @Override
220            public void visitSimpleNameExpressionVoid(@NotNull JetSimpleNameExpression expression, CFPContext context) {
221                ResolvedCall<?> resolvedCall = getResolvedCall(expression);
222                if (resolvedCall instanceof VariableAsFunctionResolvedCall) {
223                    VariableAsFunctionResolvedCall variableAsFunctionResolvedCall = (VariableAsFunctionResolvedCall) resolvedCall;
224                    generateCall(expression, variableAsFunctionResolvedCall.getVariableCall());
225                }
226                else {
227                    generateCall(expression);
228                }
229            }
230    
231            @Override
232            public void visitLabelQualifiedExpressionVoid(@NotNull JetLabelQualifiedExpression expression, CFPContext context) {
233                String labelName = expression.getLabelName();
234                JetExpression labeledExpression = expression.getLabeledExpression();
235                if (labelName != null && labeledExpression != null) {
236                    visitLabeledExpression(labelName, labeledExpression, context);
237                }
238            }
239    
240            private void visitLabeledExpression(@NotNull String labelName, @NotNull JetExpression labeledExpression, CFPContext context) {
241                JetExpression deparenthesized = JetPsiUtil.deparenthesize(labeledExpression);
242                if (deparenthesized != null) {
243                    generateInstructions(labeledExpression, context);
244                }
245            }
246    
247            @SuppressWarnings("SuspiciousMethodCalls") @Override
248            public void visitBinaryExpressionVoid(@NotNull JetBinaryExpression expression, CFPContext context) {
249                JetSimpleNameExpression operationReference = expression.getOperationReference();
250                IElementType operationType = operationReference.getReferencedNameElementType();
251                if (!ImmutableSet.of(ANDAND, OROR, EQ, ELVIS).contains(operationType)) {
252                    mark(expression);
253                }
254                JetExpression right = expression.getRight();
255                if (operationType == ANDAND) {
256                    generateInstructions(expression.getLeft(), IN_CONDITION);
257                    Label resultLabel = builder.createUnboundLabel();
258                    builder.jumpOnFalse(resultLabel, expression);
259                    if (right != null) {
260                        generateInstructions(right, IN_CONDITION);
261                    }
262                    builder.bindLabel(resultLabel);
263                    if (!context.inCondition()) {
264                        builder.predefinedOperation(expression, AND);
265                    }
266                }
267                else if (operationType == OROR) {
268                    generateInstructions(expression.getLeft(), IN_CONDITION);
269                    Label resultLabel = builder.createUnboundLabel();
270                    builder.jumpOnTrue(resultLabel, expression);
271                    if (right != null) {
272                        generateInstructions(right, IN_CONDITION);
273                    }
274                    builder.bindLabel(resultLabel);
275                    if (!context.inCondition()) {
276                        builder.predefinedOperation(expression, OR);
277                    }
278                }
279                else if (operationType == EQ) {
280                    visitAssignment(expression.getLeft(), right, expression);
281                }
282                else if (OperatorConventions.ASSIGNMENT_OPERATIONS.containsKey(operationType)) {
283                    if (generateCall(operationReference)) {
284                        ResolvedCall<?> resolvedCall = getResolvedCall(operationReference);
285                        assert resolvedCall != null : "Generation succeeded, but no call is found: " + expression.getText();
286                        CallableDescriptor descriptor = resolvedCall.getResultingDescriptor();
287                        Name assignMethodName = OperatorConventions.getNameForOperationSymbol((JetToken) expression.getOperationToken());
288                        if (!descriptor.getName().equals(assignMethodName)) {
289                            // plus() called, assignment needed
290                            visitAssignment(expression.getLeft(), null, expression);
291                        }
292                    }
293                    else {
294                        generateBothArguments(expression);
295                    }
296                }
297                else if (operationType == ELVIS) {
298                    generateInstructions(expression.getLeft(), NOT_IN_CONDITION);
299                    Label afterElvis = builder.createUnboundLabel();
300                    builder.jumpOnTrue(afterElvis, expression);
301                    if (right != null) {
302                        generateInstructions(right, NOT_IN_CONDITION);
303                    }
304                    builder.bindLabel(afterElvis);
305                }
306                else {
307                    if (!generateCall(operationReference)) {
308                        generateBothArguments(expression);
309                    }
310                }
311            }
312    
313            private void generateBothArguments(JetBinaryExpression expression) {
314                JetExpression left = JetPsiUtil.deparenthesize(expression.getLeft());
315                if (left != null) {
316                    generateInstructions(left, NOT_IN_CONDITION);
317                }
318                JetExpression right = expression.getRight();
319                if (right != null) {
320                    generateInstructions(right, NOT_IN_CONDITION);
321                }
322            }
323    
324            private void visitAssignment(JetExpression lhs, @Nullable JetExpression rhs, JetExpression parentExpression) {
325                JetExpression left = JetPsiUtil.deparenthesize(lhs);
326                if (left == null) {
327                    builder.compilationError(lhs, "No lValue in assignment");
328                    return;
329                }
330    
331                if (left instanceof JetArrayAccessExpression) {
332                    ResolvedCall<FunctionDescriptor> setResolvedCall = trace.get(BindingContext.INDEXED_LVALUE_SET, left);
333                    generateArrayAccess((JetArrayAccessExpression) left, setResolvedCall);
334                    recordWrite(left, parentExpression);
335                    return;
336                }
337    
338                generateInstructions(rhs, NOT_IN_CONDITION);
339                if (left instanceof JetSimpleNameExpression || left instanceof JetProperty) {
340                    // Do nothing, just record write below
341                }
342                else if (left instanceof JetQualifiedExpression) {
343                    generateInstructions(((JetQualifiedExpression) left).getReceiverExpression(), NOT_IN_CONDITION);
344                }
345                else {
346                    builder.unsupported(parentExpression); // TODO
347                }
348    
349                recordWrite(left, parentExpression);
350            }
351    
352            private void recordWrite(JetExpression left, JetExpression parentExpression) {
353                VariableDescriptor descriptor = BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), left, false);
354                if (descriptor != null) {
355                    builder.write(parentExpression, left);
356                }
357            }
358    
359            private void generateArrayAccess(JetArrayAccessExpression arrayAccessExpression, @Nullable ResolvedCall<?> resolvedCall) {
360                mark(arrayAccessExpression);
361                if (!checkAndGenerateCall(arrayAccessExpression, resolvedCall)) {
362                    for (JetExpression index : arrayAccessExpression.getIndexExpressions()) {
363                        generateInstructions(index, NOT_IN_CONDITION);
364                    }
365    
366                    generateInstructions(arrayAccessExpression.getArrayExpression(), NOT_IN_CONDITION);
367                }
368            }
369    
370            @Override
371            public void visitUnaryExpressionVoid(@NotNull JetUnaryExpression expression, CFPContext context) {
372                mark(expression);
373                JetSimpleNameExpression operationSign = expression.getOperationReference();
374                IElementType operationType = operationSign.getReferencedNameElementType();
375                JetExpression baseExpression = expression.getBaseExpression();
376                if (baseExpression == null) return;
377                if (JetTokens.LABELS.contains(operationType)) {
378                    String referencedName = operationSign.getReferencedName();
379                    visitLabeledExpression(referencedName.substring(1), baseExpression, context);
380                }
381                else if (JetTokens.EXCLEXCL == operationType) {
382                    generateInstructions(baseExpression, NOT_IN_CONDITION);
383                    builder.predefinedOperation(expression, NOT_NULL_ASSERTION);
384                }
385                else {
386                    if (!generateCall(expression.getOperationReference())) {
387                        generateInstructions(baseExpression, NOT_IN_CONDITION);
388                    }
389    
390                    boolean incrementOrDecrement = isIncrementOrDecrement(operationType);
391                    if (incrementOrDecrement) {
392                        // We skip dup's and other subtleties here
393                        visitAssignment(baseExpression, null, expression);
394                    }
395                }
396            }
397    
398            private boolean isIncrementOrDecrement(IElementType operationType) {
399                return operationType == JetTokens.PLUSPLUS || operationType == JetTokens.MINUSMINUS;
400            }
401    
402    
403            @Override
404            public void visitIfExpressionVoid(@NotNull JetIfExpression expression, CFPContext context) {
405                mark(expression);
406                JetExpression condition = expression.getCondition();
407                if (condition != null) {
408                    generateInstructions(condition, IN_CONDITION);
409                }
410                Label elseLabel = builder.createUnboundLabel();
411                builder.jumpOnFalse(elseLabel, expression);
412                JetExpression thenBranch = expression.getThen();
413                if (thenBranch != null) {
414                    generateInstructions(thenBranch, context);
415                }
416                else {
417                    builder.loadUnit(expression);
418                }
419                Label resultLabel = builder.createUnboundLabel();
420                builder.jump(resultLabel, expression);
421                builder.bindLabel(elseLabel);
422                JetExpression elseBranch = expression.getElse();
423                if (elseBranch != null) {
424                    generateInstructions(elseBranch, context);
425                }
426                else {
427                    builder.loadUnit(expression);
428                }
429                builder.bindLabel(resultLabel);
430            }
431    
432            private class FinallyBlockGenerator {
433                private final JetFinallySection finallyBlock;
434                private final CFPContext context;
435                private Label startFinally = null;
436                private Label finishFinally = null;
437    
438                private FinallyBlockGenerator(JetFinallySection block, CFPContext context) {
439                    finallyBlock = block;
440                    this.context = context;
441                }
442    
443                public void generate() {
444                    JetBlockExpression finalExpression = finallyBlock.getFinalExpression();
445                    if (finalExpression == null) return;
446                    if (startFinally != null) {
447                        assert finishFinally != null;
448                        builder.repeatPseudocode(startFinally, finishFinally);
449                        return;
450                    }
451                    startFinally = builder.createUnboundLabel("start finally");
452                    builder.bindLabel(startFinally);
453                    generateInstructions(finalExpression, context);
454                    finishFinally = builder.createUnboundLabel("finish finally");
455                    builder.bindLabel(finishFinally);
456                }
457            }
458           
459    
460            @Override
461            public void visitTryExpressionVoid(@NotNull JetTryExpression expression, CFPContext context) {
462                mark(expression);
463                JetFinallySection finallyBlock = expression.getFinallyBlock();
464                final FinallyBlockGenerator finallyBlockGenerator = new FinallyBlockGenerator(finallyBlock, context);
465                if (finallyBlock != null) {
466                    builder.enterTryFinally(new GenerationTrigger() {
467                        private boolean working = false;
468    
469                        @Override
470                        public void generate() {
471                            // This checks are needed for the case of having e.g. return inside finally: 'try {return} finally{return}'
472                            if (working) return;
473                            working = true;
474                            finallyBlockGenerator.generate();
475                            working = false;
476                        }
477                    });
478                }
479    
480                List<JetCatchClause> catchClauses = expression.getCatchClauses();
481                boolean hasCatches = !catchClauses.isEmpty();
482                Label onException = null;
483                if (hasCatches) {
484                    onException = builder.createUnboundLabel("onException");
485                    builder.nondeterministicJump(onException, expression);
486                }
487                Label onExceptionToFinallyBlock = null;
488                if (finallyBlock != null) {
489                    onExceptionToFinallyBlock = builder.createUnboundLabel("onExceptionToFinallyBlock");
490                    builder.nondeterministicJump(onExceptionToFinallyBlock, expression);
491                }
492                generateInstructions(expression.getTryBlock(), context);
493    
494                if (hasCatches) {
495                    Label afterCatches = builder.createUnboundLabel("afterCatches");
496                    builder.jump(afterCatches, expression);
497    
498                    builder.bindLabel(onException);
499                    LinkedList<Label> catchLabels = Lists.newLinkedList();
500                    int catchClausesSize = catchClauses.size();
501                    for (int i = 0; i < catchClausesSize - 1; i++) {
502                        catchLabels.add(builder.createUnboundLabel("catch " + i));
503                    }
504                    if (!catchLabels.isEmpty()) {
505                        builder.nondeterministicJump(catchLabels, expression);
506                    }
507                    boolean isFirst = true;
508                    for (JetCatchClause catchClause : catchClauses) {
509                        builder.enterLexicalScope(catchClause);
510                        if (!isFirst) {
511                            builder.bindLabel(catchLabels.remove());
512                        }
513                        else {
514                            isFirst = false;
515                        }
516                        JetParameter catchParameter = catchClause.getCatchParameter();
517                        if (catchParameter != null) {
518                            builder.declareParameter(catchParameter);
519                            builder.write(catchParameter, catchParameter);
520                        }
521                        JetExpression catchBody = catchClause.getCatchBody();
522                        if (catchBody != null) {
523                            generateInstructions(catchBody, NOT_IN_CONDITION);
524                        }
525                        builder.jump(afterCatches, expression);
526                        builder.exitLexicalScope(catchClause);
527                    }
528    
529                    builder.bindLabel(afterCatches);
530                }
531    
532                if (finallyBlock != null) {
533                    builder.exitTryFinally();
534    
535                    Label skipFinallyToErrorBlock = builder.createUnboundLabel("skipFinallyToErrorBlock");
536                    builder.jump(skipFinallyToErrorBlock, expression);
537                    builder.bindLabel(onExceptionToFinallyBlock);
538                    finallyBlockGenerator.generate();
539                    builder.jumpToError(expression);
540                    builder.bindLabel(skipFinallyToErrorBlock);
541    
542                    finallyBlockGenerator.generate();
543                }
544            }
545    
546            @Override
547            public void visitWhileExpressionVoid(@NotNull JetWhileExpression expression, CFPContext context) {
548                mark(expression);
549                LoopInfo loopInfo = builder.enterLoop(expression, null, null);
550    
551                builder.bindLabel(loopInfo.getConditionEntryPoint());
552                JetExpression condition = expression.getCondition();
553                if (condition != null) {
554                    generateInstructions(condition, IN_CONDITION);
555                }
556                boolean conditionIsTrueConstant = CompileTimeConstantUtils.canBeReducedToBooleanConstant(condition, trace, true);
557                if (!conditionIsTrueConstant) {
558                    builder.jumpOnFalse(loopInfo.getExitPoint(), expression);
559                }
560    
561                builder.bindLabel(loopInfo.getBodyEntryPoint());
562                JetExpression body = expression.getBody();
563                if (body != null) {
564                    generateInstructions(body, NOT_IN_CONDITION);
565                }
566                builder.jump(loopInfo.getEntryPoint(), expression);
567                builder.exitLoop(expression);
568                builder.loadUnit(expression);
569            }
570    
571            @Override
572            public void visitDoWhileExpressionVoid(@NotNull JetDoWhileExpression expression, CFPContext context) {
573                builder.enterLexicalScope(expression);
574                mark(expression);
575                LoopInfo loopInfo = builder.enterLoop(expression, null, null);
576    
577                builder.bindLabel(loopInfo.getBodyEntryPoint());
578                JetExpression body = expression.getBody();
579                if (body != null) {
580                    generateInstructions(body, NOT_IN_CONDITION);
581                }
582                builder.bindLabel(loopInfo.getConditionEntryPoint());
583                JetExpression condition = expression.getCondition();
584                if (condition != null) {
585                    generateInstructions(condition, IN_CONDITION);
586                }
587                builder.jumpOnTrue(loopInfo.getEntryPoint(), expression);
588                builder.exitLoop(expression);
589                builder.loadUnit(expression);
590                builder.exitLexicalScope(expression);
591            }
592    
593            @Override
594            public void visitForExpressionVoid(@NotNull JetForExpression expression, CFPContext context) {
595                builder.enterLexicalScope(expression);
596                mark(expression);
597                JetExpression loopRange = expression.getLoopRange();
598                if (loopRange != null) {
599                    generateInstructions(loopRange, NOT_IN_CONDITION);
600                }
601                declareLoopParameter(expression);
602    
603                // TODO : primitive cases
604                Label loopExitPoint = builder.createUnboundLabel();
605                Label conditionEntryPoint = builder.createUnboundLabel();
606    
607                builder.bindLabel(conditionEntryPoint);
608                builder.nondeterministicJump(loopExitPoint, expression);
609    
610                LoopInfo loopInfo = builder.enterLoop(expression, loopExitPoint, conditionEntryPoint);
611    
612                builder.bindLabel(loopInfo.getBodyEntryPoint());
613                writeLoopParameterAssignment(expression);
614    
615                JetExpression body = expression.getBody();
616                if (body != null) {
617                    generateInstructions(body, NOT_IN_CONDITION);
618                }
619    
620                builder.nondeterministicJump(loopInfo.getEntryPoint(), expression);
621                builder.exitLoop(expression);
622                builder.loadUnit(expression);
623                builder.exitLexicalScope(expression);
624            }
625    
626            private void declareLoopParameter(JetForExpression expression) {
627                JetParameter loopParameter = expression.getLoopParameter();
628                JetMultiDeclaration multiDeclaration = expression.getMultiParameter();
629                if (loopParameter != null) {
630                    builder.declareParameter(loopParameter);
631                }
632                else if (multiDeclaration != null) {
633                    visitMultiDeclaration(multiDeclaration, false);
634                }
635            }
636    
637            private void writeLoopParameterAssignment(JetForExpression expression) {
638                JetParameter loopParameter = expression.getLoopParameter();
639                JetMultiDeclaration multiDeclaration = expression.getMultiParameter();
640                if (loopParameter != null) {
641                    builder.write(loopParameter, loopParameter);
642                }
643                else if (multiDeclaration != null) {
644                    for (JetMultiDeclarationEntry entry : multiDeclaration.getEntries()) {
645                        builder.write(entry, entry);
646                    }
647                }
648            }
649    
650            @Override
651            public void visitBreakExpressionVoid(@NotNull JetBreakExpression expression, CFPContext context) {
652                JetElement loop = getCorrespondingLoop(expression);
653                if (loop != null) {
654                    checkJumpDoesNotCrossFunctionBoundary(expression, loop);
655                    builder.jump(builder.getExitPoint(loop), expression);
656                }
657            }
658    
659            @Override
660            public void visitContinueExpressionVoid(@NotNull JetContinueExpression expression, CFPContext context) {
661                JetElement loop = getCorrespondingLoop(expression);
662                if (loop != null) {
663                    checkJumpDoesNotCrossFunctionBoundary(expression, loop);
664                    builder.jump(builder.getEntryPoint(loop), expression);
665                }
666            }
667    
668            private JetElement getCorrespondingLoop(JetLabelQualifiedExpression expression) {
669                String labelName = expression.getLabelName();
670                JetElement loop;
671                if (labelName != null) {
672                    JetSimpleNameExpression targetLabel = expression.getTargetLabel();
673                    assert targetLabel != null;
674                    PsiElement labeledElement = trace.get(BindingContext.LABEL_TARGET, targetLabel);
675                    if (labeledElement instanceof JetLoopExpression) {
676                        loop = (JetLoopExpression) labeledElement;
677                    }
678                    else {
679                        trace.report(NOT_A_LOOP_LABEL.on(expression, targetLabel.getText()));
680                        loop = null;
681                    }
682                }
683                else {
684                    loop = builder.getCurrentLoop();
685                    if (loop == null) {
686                        trace.report(BREAK_OR_CONTINUE_OUTSIDE_A_LOOP.on(expression));
687                    }
688                }
689                return loop;
690            }
691    
692            private void checkJumpDoesNotCrossFunctionBoundary(@NotNull JetLabelQualifiedExpression jumpExpression, @NotNull JetElement jumpTarget) {
693                BindingContext bindingContext = trace.getBindingContext();
694    
695                FunctionDescriptor labelExprEnclosingFunc = BindingContextUtils.getEnclosingFunctionDescriptor(bindingContext, jumpExpression);
696                FunctionDescriptor labelTargetEnclosingFunc = BindingContextUtils.getEnclosingFunctionDescriptor(bindingContext, jumpTarget);
697                if (labelExprEnclosingFunc != labelTargetEnclosingFunc) {
698                    trace.report(BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY.on(jumpExpression));
699                }
700            }
701    
702            @Override
703            public void visitReturnExpressionVoid(@NotNull JetReturnExpression expression, CFPContext context) {
704                JetExpression returnedExpression = expression.getReturnedExpression();
705                if (returnedExpression != null) {
706                    generateInstructions(returnedExpression, NOT_IN_CONDITION);
707                }
708                JetSimpleNameExpression labelElement = expression.getTargetLabel();
709                JetElement subroutine;
710                String labelName = expression.getLabelName();
711                if (labelElement != null) {
712                    assert labelName != null;
713                    PsiElement labeledElement = trace.get(BindingContext.LABEL_TARGET, labelElement);
714                    if (labeledElement != null) {
715                        assert labeledElement instanceof JetElement;
716                        subroutine = (JetElement) labeledElement;
717                    }
718                    else {
719                        subroutine = null;
720                    }
721                }
722                else {
723                    subroutine = builder.getReturnSubroutine();
724                    // TODO : a context check
725                }
726    
727                if (subroutine instanceof JetFunction || subroutine instanceof JetPropertyAccessor) {
728                    if (returnedExpression == null) {
729                        builder.returnNoValue(expression, subroutine);
730                    }
731                    else {
732                        builder.returnValue(expression, subroutine);
733                    }
734                }
735            }
736    
737            @Override
738            public void visitParameterVoid(@NotNull JetParameter parameter, CFPContext context) {
739                builder.declareParameter(parameter);
740                JetExpression defaultValue = parameter.getDefaultValue();
741                if (defaultValue != null) {
742                    generateInstructions(defaultValue, context);
743                }
744                builder.write(parameter, parameter);
745            }
746    
747            @Override
748            public void visitBlockExpressionVoid(@NotNull JetBlockExpression expression, CFPContext context) {
749                boolean declareLexicalScope = !isBlockInDoWhile(expression);
750                if (declareLexicalScope) {
751                    builder.enterLexicalScope(expression);
752                }
753                mark(expression);
754                List<JetElement> statements = expression.getStatements();
755                for (JetElement statement : statements) {
756                    generateInstructions(statement, NOT_IN_CONDITION);
757                }
758                if (statements.isEmpty()) {
759                    builder.loadUnit(expression);
760                }
761                if (declareLexicalScope) {
762                    builder.exitLexicalScope(expression);
763                }
764            }
765    
766            private boolean isBlockInDoWhile(@NotNull JetBlockExpression expression) {
767                PsiElement parent = expression.getParent();
768                if (parent == null) return false;
769                return parent.getParent() instanceof JetDoWhileExpression;
770            }
771    
772            @Override
773            public void visitNamedFunctionVoid(@NotNull JetNamedFunction function, CFPContext context) {
774                processLocalDeclaration(function);
775            }
776    
777            @Override
778            public void visitFunctionLiteralExpressionVoid(@NotNull JetFunctionLiteralExpression expression, CFPContext context) {
779                mark(expression);
780                JetFunctionLiteral functionLiteral = expression.getFunctionLiteral();
781                processLocalDeclaration(functionLiteral);
782                builder.createFunctionLiteral(expression);
783            }
784    
785            @Override
786            public void visitQualifiedExpressionVoid(@NotNull JetQualifiedExpression expression, CFPContext context) {
787                mark(expression);
788                JetExpression selectorExpression = expression.getSelectorExpression();
789    
790                if (selectorExpression == null) {
791                    generateInstructions(expression.getReceiverExpression(), NOT_IN_CONDITION);
792                    return;
793                }
794    
795                generateInstructions(selectorExpression, NOT_IN_CONDITION);
796    
797                // receiver was generated for resolvedCall
798                JetExpression calleeExpression = JetPsiUtil.getCalleeExpressionIfAny(selectorExpression);
799                if (calleeExpression == null || getResolvedCall(calleeExpression) == null) {
800                    generateInstructions(expression.getReceiverExpression(), NOT_IN_CONDITION);
801                }
802            }
803    
804            @Override
805            public void visitCallExpressionVoid(@NotNull JetCallExpression expression, CFPContext context) {
806                mark(expression);
807                if (!generateCall(expression.getCalleeExpression())) {
808                    for (ValueArgument argument : expression.getValueArguments()) {
809                        JetExpression argumentExpression = argument.getArgumentExpression();
810                        if (argumentExpression != null) {
811                            generateInstructions(argumentExpression, NOT_IN_CONDITION);
812                        }
813                    }
814    
815                    for (JetExpression functionLiteral : expression.getFunctionLiteralArguments()) {
816                        generateInstructions(functionLiteral, NOT_IN_CONDITION);
817                    }
818    
819                    generateInstructions(expression.getCalleeExpression(), NOT_IN_CONDITION);
820                }
821            }
822    
823            @Override
824            public void visitPropertyVoid(@NotNull JetProperty property, CFPContext context) {
825                builder.declareVariable(property);
826                JetExpression initializer = property.getInitializer();
827                if (initializer != null) {
828                    generateInstructions(initializer, NOT_IN_CONDITION);
829                    visitAssignment(property, null, property);
830                }
831                JetExpression delegate = property.getDelegateExpression();
832                if (delegate != null) {
833                    generateInstructions(delegate, NOT_IN_CONDITION);
834                }
835                if (JetPsiUtil.isLocal(property)) {
836                    for (JetPropertyAccessor accessor : property.getAccessors()) {
837                        generateInstructions(accessor, NOT_IN_CONDITION);
838                    }
839                }
840            }
841    
842            @Override
843            public void visitMultiDeclarationVoid(@NotNull JetMultiDeclaration declaration, CFPContext context) {
844                visitMultiDeclaration(declaration, true);
845            }
846    
847            private void visitMultiDeclaration(@NotNull JetMultiDeclaration declaration, boolean generateWriteForEntries) {
848                generateInstructions(declaration.getInitializer(), NOT_IN_CONDITION);
849    
850                for (JetMultiDeclarationEntry entry : declaration.getEntries()) {
851                    builder.declareVariable(entry);
852                    ResolvedCall<FunctionDescriptor> resolvedCall = trace.get(BindingContext.COMPONENT_RESOLVED_CALL, entry);
853                    if (resolvedCall != null) {
854                        builder.call(entry, resolvedCall);
855                    }
856                    if (generateWriteForEntries) {
857                        builder.write(entry, entry);
858                    }
859                }
860            }
861    
862            @Override
863            public void visitPropertyAccessorVoid(@NotNull JetPropertyAccessor accessor, CFPContext context) {
864                processLocalDeclaration(accessor);
865            }
866    
867            @Override
868            public void visitBinaryWithTypeRHSExpressionVoid(@NotNull JetBinaryExpressionWithTypeRHS expression, CFPContext context) {
869                mark(expression);
870                IElementType operationType = expression.getOperationReference().getReferencedNameElementType();
871                if (operationType == JetTokens.COLON || operationType == JetTokens.AS_KEYWORD || operationType == JetTokens.AS_SAFE) {
872                    generateInstructions(expression.getLeft(), NOT_IN_CONDITION);
873                }
874                else {
875                    visitJetElementVoid(expression, context);
876                }
877            }
878    
879            @Override
880            public void visitThrowExpressionVoid(@NotNull JetThrowExpression expression, CFPContext context) {
881                mark(expression);
882                JetExpression thrownExpression = expression.getThrownExpression();
883                if (thrownExpression != null) {
884                    generateInstructions(thrownExpression, NOT_IN_CONDITION);
885                }
886                builder.throwException(expression);
887            }
888    
889            @Override
890            public void visitArrayAccessExpressionVoid(@NotNull JetArrayAccessExpression expression, CFPContext context) {
891                mark(expression);
892                ResolvedCall<FunctionDescriptor> getMethodResolvedCall = trace.get(BindingContext.INDEXED_LVALUE_GET, expression);
893                if (!checkAndGenerateCall(expression, getMethodResolvedCall)) {
894                    generateArrayAccess(expression, getMethodResolvedCall);
895                }
896            }
897    
898            @Override
899            public void visitIsExpressionVoid(@NotNull JetIsExpression expression, CFPContext context) {
900                mark(expression);
901                generateInstructions(expression.getLeftHandSide(), context);
902            }
903    
904            @Override
905            public void visitWhenExpressionVoid(@NotNull JetWhenExpression expression, CFPContext context) {
906                mark(expression);
907                JetExpression subjectExpression = expression.getSubjectExpression();
908                if (subjectExpression != null) {
909                    generateInstructions(subjectExpression, context);
910                }
911                boolean hasElse = false;
912    
913                Label doneLabel = builder.createUnboundLabel();
914    
915                Label nextLabel = null;
916                for (Iterator<JetWhenEntry> iterator = expression.getEntries().iterator(); iterator.hasNext(); ) {
917                    JetWhenEntry whenEntry = iterator.next();
918                    mark(whenEntry);
919    
920                    boolean isElse = whenEntry.isElse();
921                    if (isElse) {
922                        hasElse = true;
923                        if (iterator.hasNext()) {
924                            trace.report(ELSE_MISPLACED_IN_WHEN.on(whenEntry));
925                        }
926                    }
927                    Label bodyLabel = builder.createUnboundLabel();
928    
929                    JetWhenCondition[] conditions = whenEntry.getConditions();
930                    for (int i = 0; i < conditions.length; i++) {
931                        JetWhenCondition condition = conditions[i];
932                        condition.accept(conditionVisitor, context);
933                        if (i + 1 < conditions.length) {
934                            builder.nondeterministicJump(bodyLabel, expression);
935                        }
936                    }
937    
938                    if (!isElse) {
939                        nextLabel = builder.createUnboundLabel();
940                        builder.nondeterministicJump(nextLabel, expression);
941                    }
942    
943                    builder.bindLabel(bodyLabel);
944                    generateInstructions(whenEntry.getExpression(), context);
945                    builder.jump(doneLabel, expression);
946    
947                    if (!isElse) {
948                        builder.bindLabel(nextLabel);
949                    }
950                }
951                builder.bindLabel(doneLabel);
952                if (!hasElse && WhenChecker.mustHaveElse(expression, trace)) {
953                    trace.report(NO_ELSE_IN_WHEN.on(expression));
954                }
955            }
956    
957            @Override
958            public void visitObjectLiteralExpressionVoid(@NotNull JetObjectLiteralExpression expression, CFPContext context) {
959                mark(expression);
960                JetObjectDeclaration declaration = expression.getObjectDeclaration();
961                generateInstructions(declaration, context);
962    
963                builder.createAnonymousObject(expression);
964            }
965    
966            @Override
967            public void visitObjectDeclarationVoid(@NotNull JetObjectDeclaration objectDeclaration, CFPContext context) {
968                visitClassOrObject(objectDeclaration, context);
969            }
970    
971            @Override
972            public void visitStringTemplateExpressionVoid(@NotNull JetStringTemplateExpression expression, CFPContext context) {
973                mark(expression);
974                for (JetStringTemplateEntry entry : expression.getEntries()) {
975                    if (entry instanceof JetStringTemplateEntryWithExpression) {
976                        JetStringTemplateEntryWithExpression entryWithExpression = (JetStringTemplateEntryWithExpression) entry;
977                        generateInstructions(entryWithExpression.getExpression(), NOT_IN_CONDITION);
978                    }
979                }
980                builder.loadStringTemplate(expression);
981            }
982    
983            @Override
984            public void visitTypeProjectionVoid(@NotNull JetTypeProjection typeProjection, CFPContext context) {
985                // TODO : Support Type Arguments. Class object may be initialized at this point");
986            }
987    
988            @Override
989            public void visitAnonymousInitializerVoid(@NotNull JetClassInitializer classInitializer, CFPContext context) {
990                generateInstructions(classInitializer.getBody(), context);
991            }
992    
993            private void visitClassOrObject(JetClassOrObject classOrObject, CFPContext context) {
994                for (JetDelegationSpecifier specifier : classOrObject.getDelegationSpecifiers()) {
995                    generateInstructions(specifier, context);
996                }
997                List<JetDeclaration> declarations = classOrObject.getDeclarations();
998                if (JetPsiUtil.isLocal(classOrObject)) {
999                    for (JetDeclaration declaration : declarations) {
1000                        generateInstructions(declaration, context);
1001                    }
1002                    return;
1003                }
1004                //For top-level and inner classes and objects functions are collected and checked separately.
1005                for (JetDeclaration declaration : declarations) {
1006                    if (declaration instanceof JetProperty || declaration instanceof JetClassInitializer) {
1007                        generateInstructions(declaration, context);
1008                    }
1009                }
1010            }
1011    
1012            @Override
1013            public void visitClassVoid(@NotNull JetClass klass, CFPContext context) {
1014                List<JetParameter> parameters = klass.getPrimaryConstructorParameters();
1015                for (JetParameter parameter : parameters) {
1016                    generateInstructions(parameter, context);
1017                }
1018                visitClassOrObject(klass, context);
1019            }
1020    
1021            @Override
1022            public void visitDelegationToSuperCallSpecifierVoid(@NotNull JetDelegatorToSuperCall call, CFPContext context) {
1023                List<? extends ValueArgument> valueArguments = call.getValueArguments();
1024                for (ValueArgument valueArgument : valueArguments) {
1025                    generateInstructions(valueArgument.getArgumentExpression(), context);
1026                }
1027            }
1028    
1029            @Override
1030            public void visitDelegationByExpressionSpecifierVoid(@NotNull JetDelegatorByExpressionSpecifier specifier, CFPContext context) {
1031                generateInstructions(specifier.getDelegateExpression(), context);
1032            }
1033    
1034            @Override
1035            public void visitJetFileVoid(@NotNull JetFile file, CFPContext context) {
1036                for (JetDeclaration declaration : file.getDeclarations()) {
1037                    if (declaration instanceof JetProperty) {
1038                        generateInstructions(declaration, context);
1039                    }
1040                }
1041            }
1042    
1043            @Override
1044            public void visitJetElementVoid(@NotNull JetElement element, CFPContext context) {
1045                builder.unsupported(element);
1046            }
1047    
1048            @Nullable
1049            private ResolvedCall<?> getResolvedCall(@NotNull JetElement expression) {
1050                return trace.get(BindingContext.RESOLVED_CALL, expression);
1051            }
1052    
1053            private boolean generateCall(@Nullable JetExpression calleeExpression) {
1054                if (calleeExpression == null) return false;
1055                return checkAndGenerateCall(calleeExpression, getResolvedCall(calleeExpression));
1056            }
1057    
1058            private boolean checkAndGenerateCall(JetExpression calleeExpression, @Nullable ResolvedCall<?> resolvedCall) {
1059                if (resolvedCall == null) {
1060                    builder.compilationError(calleeExpression, "No resolved call");
1061                    return false;
1062                }
1063                generateCall(calleeExpression, resolvedCall);
1064                return true;
1065            }
1066    
1067            private void generateCall(JetExpression calleeExpression, ResolvedCall<?> resolvedCall) {
1068                if (resolvedCall instanceof VariableAsFunctionResolvedCall) {
1069                    VariableAsFunctionResolvedCall variableAsFunctionResolvedCall = (VariableAsFunctionResolvedCall) resolvedCall;
1070                    generateCall(calleeExpression, variableAsFunctionResolvedCall.getFunctionCall());
1071                    return;
1072                }
1073    
1074                CallableDescriptor resultingDescriptor = resolvedCall.getResultingDescriptor();
1075    
1076                generateReceiver(resolvedCall.getThisObject());
1077                generateReceiver(resolvedCall.getReceiverArgument());
1078    
1079                for (ValueParameterDescriptor parameterDescriptor : resultingDescriptor.getValueParameters()) {
1080                    ResolvedValueArgument argument = resolvedCall.getValueArguments().get(parameterDescriptor);
1081                    if (argument == null) continue;
1082    
1083                    generateValueArgument(argument);
1084                }
1085    
1086                if (resultingDescriptor instanceof VariableDescriptor) {
1087                    builder.readVariable(calleeExpression, (VariableDescriptor) resultingDescriptor);
1088                }
1089                else {
1090                    builder.call(calleeExpression, resolvedCall);
1091                }
1092            }
1093    
1094            private void generateReceiver(ReceiverValue receiver) {
1095                if (!receiver.exists()) return;
1096                if (receiver instanceof ThisReceiver) {
1097                    // TODO: Receiver is passed implicitly: no expression to tie the read to
1098                }
1099                else if (receiver instanceof ExpressionReceiver) {
1100                    generateInstructions(((ExpressionReceiver) receiver).getExpression(), NOT_IN_CONDITION);
1101                }
1102                else if (receiver instanceof TransientReceiver) {
1103                    // Do nothing
1104                }
1105                else {
1106                    throw new IllegalArgumentException("Unknown receiver kind: " + receiver);
1107                }
1108            }
1109    
1110            private void generateValueArgument(ResolvedValueArgument argument) {
1111                for (ValueArgument valueArgument : argument.getArguments()) {
1112                    JetExpression expression = valueArgument.getArgumentExpression();
1113                    if (expression != null) {
1114                        generateInstructions(expression, NOT_IN_CONDITION);
1115                    }
1116                }
1117            }
1118        }
1119    }