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