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