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                    checkJumpDoesNotCrossFunctionBoundary(expression, loop);
640                    builder.jump(builder.getExitPoint(loop));
641                }
642            }
643    
644            @Override
645            public void visitContinueExpressionVoid(@NotNull JetContinueExpression expression, CFPContext context) {
646                JetElement loop = getCorrespondingLoop(expression);
647                if (loop != null) {
648                    checkJumpDoesNotCrossFunctionBoundary(expression, loop);
649                    builder.jump(builder.getEntryPoint(loop));
650                }
651            }
652    
653            private JetElement getCorrespondingLoop(JetLabelQualifiedExpression expression) {
654                String labelName = expression.getLabelName();
655                JetElement loop;
656                if (labelName != null) {
657                    JetSimpleNameExpression targetLabel = expression.getTargetLabel();
658                    assert targetLabel != null;
659                    PsiElement labeledElement = trace.get(BindingContext.LABEL_TARGET, targetLabel);
660                    if (labeledElement instanceof JetLoopExpression) {
661                        loop = (JetLoopExpression) labeledElement;
662                    }
663                    else {
664                        trace.report(NOT_A_LOOP_LABEL.on(expression, targetLabel.getText()));
665                        loop = null;
666                    }
667                }
668                else {
669                    loop = builder.getCurrentLoop();
670                    if (loop == null) {
671                        trace.report(BREAK_OR_CONTINUE_OUTSIDE_A_LOOP.on(expression));
672                    }
673                }
674                return loop;
675            }
676    
677            private void checkJumpDoesNotCrossFunctionBoundary(@NotNull JetLabelQualifiedExpression jumpExpression, @NotNull JetElement jumpTarget) {
678                BindingContext bindingContext = trace.getBindingContext();
679    
680                FunctionDescriptor labelExprEnclosingFunc = BindingContextUtils.getEnclosingFunctionDescriptor(bindingContext, jumpExpression);
681                FunctionDescriptor labelTargetEnclosingFunc = BindingContextUtils.getEnclosingFunctionDescriptor(bindingContext, jumpTarget);
682                if (labelExprEnclosingFunc != labelTargetEnclosingFunc) {
683                    trace.report(BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY.on(jumpExpression));
684                }
685            }
686    
687            @Override
688            public void visitReturnExpressionVoid(@NotNull JetReturnExpression expression, CFPContext context) {
689                JetExpression returnedExpression = expression.getReturnedExpression();
690                if (returnedExpression != null) {
691                    generateInstructions(returnedExpression, NOT_IN_CONDITION);
692                }
693                JetSimpleNameExpression labelElement = expression.getTargetLabel();
694                JetElement subroutine;
695                String labelName = expression.getLabelName();
696                if (labelElement != null) {
697                    assert labelName != null;
698                    PsiElement labeledElement = trace.get(BindingContext.LABEL_TARGET, labelElement);
699                    if (labeledElement != null) {
700                        assert labeledElement instanceof JetElement;
701                        subroutine = (JetElement) labeledElement;
702                    }
703                    else {
704                        subroutine = null;
705                    }
706                }
707                else {
708                    subroutine = builder.getReturnSubroutine();
709                    // TODO : a context check
710                }
711    
712                if (subroutine instanceof JetFunction || subroutine instanceof JetPropertyAccessor) {
713                    if (returnedExpression == null) {
714                        builder.returnNoValue(expression, subroutine);
715                    }
716                    else {
717                        builder.returnValue(expression, subroutine);
718                    }
719                }
720            }
721    
722            @Override
723            public void visitParameterVoid(@NotNull JetParameter parameter, CFPContext context) {
724                builder.declareParameter(parameter);
725                JetExpression defaultValue = parameter.getDefaultValue();
726                if (defaultValue != null) {
727                    generateInstructions(defaultValue, context);
728                }
729                builder.write(parameter, parameter);
730            }
731    
732            @Override
733            public void visitBlockExpressionVoid(@NotNull JetBlockExpression expression, CFPContext context) {
734                mark(expression);
735                List<JetElement> statements = expression.getStatements();
736                for (JetElement statement : statements) {
737                    generateInstructions(statement, NOT_IN_CONDITION);
738                }
739                if (statements.isEmpty()) {
740                    builder.loadUnit(expression);
741                }
742            }
743    
744            @Override
745            public void visitNamedFunctionVoid(@NotNull JetNamedFunction function, CFPContext context) {
746                processLocalDeclaration(function);
747            }
748    
749            @Override
750            public void visitFunctionLiteralExpressionVoid(@NotNull JetFunctionLiteralExpression expression, CFPContext context) {
751                mark(expression);
752                JetFunctionLiteral functionLiteral = expression.getFunctionLiteral();
753                processLocalDeclaration(functionLiteral);
754                builder.createFunctionLiteral(expression);
755            }
756    
757            @Override
758            public void visitQualifiedExpressionVoid(@NotNull JetQualifiedExpression expression, CFPContext context) {
759                mark(expression);
760                JetExpression selectorExpression = expression.getSelectorExpression();
761    
762                if (selectorExpression == null) {
763                    generateInstructions(expression.getReceiverExpression(), NOT_IN_CONDITION);
764                    return;
765                }
766    
767                generateInstructions(selectorExpression, NOT_IN_CONDITION);
768    
769                // receiver was generated for resolvedCall
770                JetExpression calleeExpression = JetPsiUtil.getCalleeExpressionIfAny(selectorExpression);
771                if (calleeExpression == null || getResolvedCall(calleeExpression) == null) {
772                    generateInstructions(expression.getReceiverExpression(), NOT_IN_CONDITION);
773                }
774            }
775    
776            @Override
777            public void visitCallExpressionVoid(@NotNull JetCallExpression expression, CFPContext context) {
778                mark(expression);
779                if (!generateCall(expression.getCalleeExpression())) {
780                    for (ValueArgument argument : expression.getValueArguments()) {
781                        JetExpression argumentExpression = argument.getArgumentExpression();
782                        if (argumentExpression != null) {
783                            generateInstructions(argumentExpression, NOT_IN_CONDITION);
784                        }
785                    }
786    
787                    for (JetExpression functionLiteral : expression.getFunctionLiteralArguments()) {
788                        generateInstructions(functionLiteral, NOT_IN_CONDITION);
789                    }
790    
791                    generateInstructions(expression.getCalleeExpression(), NOT_IN_CONDITION);
792                }
793            }
794    
795            @Override
796            public void visitPropertyVoid(@NotNull JetProperty property, CFPContext context) {
797                builder.declareVariable(property);
798                JetExpression initializer = property.getInitializer();
799                if (initializer != null) {
800                    generateInstructions(initializer, NOT_IN_CONDITION);
801                    visitAssignment(property, null, property);
802                }
803                JetExpression delegate = property.getDelegateExpression();
804                if (delegate != null) {
805                    generateInstructions(delegate, NOT_IN_CONDITION);
806                }
807                if (JetPsiUtil.isLocal(property)) {
808                    for (JetPropertyAccessor accessor : property.getAccessors()) {
809                        generateInstructions(accessor, NOT_IN_CONDITION);
810                    }
811                }
812            }
813    
814            @Override
815            public void visitMultiDeclarationVoid(@NotNull JetMultiDeclaration declaration, CFPContext context) {
816                JetExpression initializer = declaration.getInitializer();
817                if (initializer != null) {
818                    generateInstructions(initializer, NOT_IN_CONDITION);
819                }
820                List<JetMultiDeclarationEntry> entries = declaration.getEntries();
821                for (JetMultiDeclarationEntry entry : entries) {
822                    builder.declareVariable(entry);
823                    ResolvedCall<FunctionDescriptor> resolvedCall = trace.get(BindingContext.COMPONENT_RESOLVED_CALL, entry);
824                    if (resolvedCall != null) {
825                        builder.call(entry, resolvedCall);
826                    }
827                    builder.write(entry, entry);
828                }
829            }
830    
831            @Override
832            public void visitPropertyAccessorVoid(@NotNull JetPropertyAccessor accessor, CFPContext context) {
833                processLocalDeclaration(accessor);
834            }
835    
836            @Override
837            public void visitBinaryWithTypeRHSExpressionVoid(@NotNull JetBinaryExpressionWithTypeRHS expression, CFPContext context) {
838                mark(expression);
839                IElementType operationType = expression.getOperationReference().getReferencedNameElementType();
840                if (operationType == JetTokens.COLON || operationType == JetTokens.AS_KEYWORD || operationType == JetTokens.AS_SAFE) {
841                    generateInstructions(expression.getLeft(), NOT_IN_CONDITION);
842                }
843                else {
844                    visitJetElementVoid(expression, context);
845                }
846            }
847    
848            @Override
849            public void visitThrowExpressionVoid(@NotNull JetThrowExpression expression, CFPContext context) {
850                mark(expression);
851                JetExpression thrownExpression = expression.getThrownExpression();
852                if (thrownExpression != null) {
853                    generateInstructions(thrownExpression, NOT_IN_CONDITION);
854                }
855                builder.throwException(expression);
856            }
857    
858            @Override
859            public void visitArrayAccessExpressionVoid(@NotNull JetArrayAccessExpression expression, CFPContext context) {
860                mark(expression);
861                ResolvedCall<FunctionDescriptor> getMethodResolvedCall = trace.get(BindingContext.INDEXED_LVALUE_GET, expression);
862                if (!checkAndGenerateCall(expression, getMethodResolvedCall)) {
863                    generateArrayAccess(expression, getMethodResolvedCall);
864                }
865            }
866    
867            @Override
868            public void visitIsExpressionVoid(@NotNull JetIsExpression expression, CFPContext context) {
869                mark(expression);
870                generateInstructions(expression.getLeftHandSide(), context);
871            }
872    
873            @Override
874            public void visitWhenExpressionVoid(@NotNull JetWhenExpression expression, CFPContext context) {
875                mark(expression);
876                JetExpression subjectExpression = expression.getSubjectExpression();
877                if (subjectExpression != null) {
878                    generateInstructions(subjectExpression, context);
879                }
880                boolean hasElse = false;
881    
882                Label doneLabel = builder.createUnboundLabel();
883    
884                Label nextLabel = null;
885                for (Iterator<JetWhenEntry> iterator = expression.getEntries().iterator(); iterator.hasNext(); ) {
886                    JetWhenEntry whenEntry = iterator.next();
887                    mark(whenEntry);
888    
889                    boolean isElse = whenEntry.isElse();
890                    if (isElse) {
891                        hasElse = true;
892                        if (iterator.hasNext()) {
893                            trace.report(ELSE_MISPLACED_IN_WHEN.on(whenEntry));
894                        }
895                    }
896                    Label bodyLabel = builder.createUnboundLabel();
897    
898                    JetWhenCondition[] conditions = whenEntry.getConditions();
899                    for (int i = 0; i < conditions.length; i++) {
900                        JetWhenCondition condition = conditions[i];
901                        condition.accept(conditionVisitor, context);
902                        if (i + 1 < conditions.length) {
903                            builder.nondeterministicJump(bodyLabel);
904                        }
905                    }
906    
907                    if (!isElse) {
908                        nextLabel = builder.createUnboundLabel();
909                        builder.nondeterministicJump(nextLabel);
910                    }
911    
912                    builder.bindLabel(bodyLabel);
913                    generateInstructions(whenEntry.getExpression(), context);
914                    builder.jump(doneLabel);
915    
916                    if (!isElse) {
917                        builder.bindLabel(nextLabel);
918                    }
919                }
920                builder.bindLabel(doneLabel);
921                if (!hasElse && WhenChecker.mustHaveElse(expression, trace)) {
922                    trace.report(NO_ELSE_IN_WHEN.on(expression));
923                }
924            }
925    
926            @Override
927            public void visitObjectLiteralExpressionVoid(@NotNull JetObjectLiteralExpression expression, CFPContext context) {
928                mark(expression);
929                JetObjectDeclaration declaration = expression.getObjectDeclaration();
930                generateInstructions(declaration, context);
931    
932                builder.createAnonymousObject(expression);
933            }
934    
935            @Override
936            public void visitObjectDeclarationVoid(@NotNull JetObjectDeclaration objectDeclaration, CFPContext context) {
937                visitClassOrObject(objectDeclaration, context);
938            }
939    
940            @Override
941            public void visitStringTemplateExpressionVoid(@NotNull JetStringTemplateExpression expression, CFPContext context) {
942                mark(expression);
943                for (JetStringTemplateEntry entry : expression.getEntries()) {
944                    if (entry instanceof JetStringTemplateEntryWithExpression) {
945                        JetStringTemplateEntryWithExpression entryWithExpression = (JetStringTemplateEntryWithExpression) entry;
946                        generateInstructions(entryWithExpression.getExpression(), NOT_IN_CONDITION);
947                    }
948                }
949                builder.loadStringTemplate(expression);
950            }
951    
952            @Override
953            public void visitTypeProjectionVoid(@NotNull JetTypeProjection typeProjection, CFPContext context) {
954                // TODO : Support Type Arguments. Class object may be initialized at this point");
955            }
956    
957            @Override
958            public void visitAnonymousInitializerVoid(@NotNull JetClassInitializer classInitializer, CFPContext context) {
959                generateInstructions(classInitializer.getBody(), context);
960            }
961    
962            private void visitClassOrObject(JetClassOrObject classOrObject, CFPContext context) {
963                for (JetDelegationSpecifier specifier : classOrObject.getDelegationSpecifiers()) {
964                    generateInstructions(specifier, context);
965                }
966                List<JetDeclaration> declarations = classOrObject.getDeclarations();
967                if (JetPsiUtil.isLocal(classOrObject)) {
968                    for (JetDeclaration declaration : declarations) {
969                        generateInstructions(declaration, context);
970                    }
971                    return;
972                }
973                //For top-level and inner classes and objects functions are collected and checked separately.
974                for (JetDeclaration declaration : declarations) {
975                    if (declaration instanceof JetProperty || declaration instanceof JetClassInitializer) {
976                        generateInstructions(declaration, context);
977                    }
978                }
979            }
980    
981            @Override
982            public void visitClassVoid(@NotNull JetClass klass, CFPContext context) {
983                List<JetParameter> parameters = klass.getPrimaryConstructorParameters();
984                for (JetParameter parameter : parameters) {
985                    generateInstructions(parameter, context);
986                }
987                visitClassOrObject(klass, context);
988            }
989    
990            @Override
991            public void visitDelegationToSuperCallSpecifierVoid(@NotNull JetDelegatorToSuperCall call, CFPContext context) {
992                List<? extends ValueArgument> valueArguments = call.getValueArguments();
993                for (ValueArgument valueArgument : valueArguments) {
994                    generateInstructions(valueArgument.getArgumentExpression(), context);
995                }
996            }
997    
998            @Override
999            public void visitDelegationByExpressionSpecifierVoid(@NotNull JetDelegatorByExpressionSpecifier specifier, CFPContext context) {
1000                generateInstructions(specifier.getDelegateExpression(), context);
1001            }
1002    
1003            @Override
1004            public void visitJetFileVoid(@NotNull JetFile file, CFPContext context) {
1005                for (JetDeclaration declaration : file.getDeclarations()) {
1006                    if (declaration instanceof JetProperty) {
1007                        generateInstructions(declaration, context);
1008                    }
1009                }
1010            }
1011    
1012            @Override
1013            public void visitJetElementVoid(@NotNull JetElement element, CFPContext context) {
1014                builder.unsupported(element);
1015            }
1016    
1017            @Nullable
1018            private ResolvedCall<?> getResolvedCall(@NotNull JetElement expression) {
1019                return trace.get(BindingContext.RESOLVED_CALL, expression);
1020            }
1021    
1022            private boolean generateCall(@Nullable JetExpression calleeExpression) {
1023                if (calleeExpression == null) return false;
1024                return checkAndGenerateCall(calleeExpression, getResolvedCall(calleeExpression));
1025            }
1026    
1027            private boolean checkAndGenerateCall(JetExpression calleeExpression, @Nullable ResolvedCall<?> resolvedCall) {
1028                if (resolvedCall == null) {
1029                    builder.compilationError(calleeExpression, "No resolved call");
1030                    return false;
1031                }
1032                generateCall(calleeExpression, resolvedCall);
1033                return true;
1034            }
1035    
1036            private void generateCall(JetExpression calleeExpression, ResolvedCall<?> resolvedCall) {
1037                if (resolvedCall instanceof VariableAsFunctionResolvedCall) {
1038                    VariableAsFunctionResolvedCall variableAsFunctionResolvedCall = (VariableAsFunctionResolvedCall) resolvedCall;
1039                    generateCall(calleeExpression, variableAsFunctionResolvedCall.getFunctionCall());
1040                    return;
1041                }
1042    
1043                CallableDescriptor resultingDescriptor = resolvedCall.getResultingDescriptor();
1044                if (resultingDescriptor instanceof ExpressionAsFunctionDescriptor) {
1045                    generateInstructions(((ExpressionAsFunctionDescriptor) resultingDescriptor).getExpression(), NOT_IN_CONDITION);
1046                }
1047    
1048                generateReceiver(resolvedCall.getThisObject());
1049                generateReceiver(resolvedCall.getReceiverArgument());
1050    
1051                for (ValueParameterDescriptor parameterDescriptor : resultingDescriptor.getValueParameters()) {
1052                    ResolvedValueArgument argument = resolvedCall.getValueArguments().get(parameterDescriptor);
1053                    if (argument == null) continue;
1054    
1055                    generateValueArgument(argument);
1056                }
1057    
1058                if (resultingDescriptor instanceof VariableDescriptor) {
1059                    builder.readVariable(calleeExpression, (VariableDescriptor) resultingDescriptor);
1060                }
1061                else {
1062                    builder.call(calleeExpression, resolvedCall);
1063                }
1064            }
1065    
1066            private void generateReceiver(ReceiverValue receiver) {
1067                if (!receiver.exists()) return;
1068                if (receiver instanceof ThisReceiver) {
1069                    // TODO: Receiver is passed implicitly: no expression to tie the read to
1070                }
1071                else if (receiver instanceof ExpressionReceiver) {
1072                    generateInstructions(((ExpressionReceiver) receiver).getExpression(), NOT_IN_CONDITION);
1073                }
1074                else if (receiver instanceof TransientReceiver) {
1075                    // Do nothing
1076                }
1077                else {
1078                    throw new IllegalArgumentException("Unknown receiver kind: " + receiver);
1079                }
1080            }
1081    
1082            private void generateValueArgument(ResolvedValueArgument argument) {
1083                for (ValueArgument valueArgument : argument.getArguments()) {
1084                    JetExpression expression = valueArgument.getArgumentExpression();
1085                    if (expression != null) {
1086                        generateInstructions(expression, NOT_IN_CONDITION);
1087                    }
1088                }
1089            }
1090        }
1091    }