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