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