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 (classOrObject.isLocal()) {
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 }