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