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