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