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