001 /* 002 * Copyright 2010-2015 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.kotlin.cfg.pseudocode; 018 019 import com.intellij.util.containers.Stack; 020 import org.jetbrains.annotations.NotNull; 021 import org.jetbrains.annotations.Nullable; 022 import org.jetbrains.kotlin.builtins.KotlinBuiltIns; 023 import org.jetbrains.kotlin.cfg.*; 024 import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction; 025 import org.jetbrains.kotlin.cfg.pseudocode.instructions.LexicalScope; 026 import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*; 027 import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.*; 028 import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.*; 029 import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor; 030 import org.jetbrains.kotlin.psi.*; 031 import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; 032 import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant; 033 import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; 034 import org.jetbrains.kotlin.types.JetType; 035 036 import java.util.*; 037 038 public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAdapter { 039 private JetControlFlowBuilder builder = null; 040 041 private final Stack<LoopInfo> loopInfo = new Stack<LoopInfo>(); 042 private final Stack<LexicalScope> lexicalScopes = new Stack<LexicalScope>(); 043 private final Map<JetElement, BreakableBlockInfo> elementToBlockInfo = new HashMap<JetElement, BreakableBlockInfo>(); 044 private int labelCount = 0; 045 046 private final Stack<JetControlFlowInstructionsGeneratorWorker> builders = new Stack<JetControlFlowInstructionsGeneratorWorker>(); 047 048 private final Stack<BlockInfo> allBlocks = new Stack<BlockInfo>(); 049 050 @NotNull 051 @Override 052 protected JetControlFlowBuilder getDelegateBuilder() { 053 return builder; 054 } 055 056 private void pushBuilder(JetElement scopingElement, JetElement subroutine) { 057 JetControlFlowInstructionsGeneratorWorker worker = new JetControlFlowInstructionsGeneratorWorker(scopingElement, subroutine); 058 builders.push(worker); 059 builder = worker; 060 } 061 062 private JetControlFlowInstructionsGeneratorWorker popBuilder(@NotNull JetElement element) { 063 JetControlFlowInstructionsGeneratorWorker worker = builders.pop(); 064 if (!builders.isEmpty()) { 065 builder = builders.peek(); 066 } 067 else { 068 builder = null; 069 } 070 return worker; 071 } 072 073 @Override 074 public void enterSubroutine(@NotNull JetElement subroutine) { 075 if (builder != null && subroutine instanceof JetFunctionLiteral) { 076 pushBuilder(subroutine, builder.getReturnSubroutine()); 077 } 078 else { 079 pushBuilder(subroutine, subroutine); 080 } 081 assert builder != null; 082 builder.enterLexicalScope(subroutine); 083 builder.enterSubroutine(subroutine); 084 } 085 086 @NotNull 087 @Override 088 public Pseudocode exitSubroutine(@NotNull JetElement subroutine) { 089 super.exitSubroutine(subroutine); 090 builder.exitLexicalScope(subroutine); 091 JetControlFlowInstructionsGeneratorWorker worker = popBuilder(subroutine); 092 if (!builders.empty()) { 093 JetControlFlowInstructionsGeneratorWorker builder = builders.peek(); 094 builder.declareFunction(subroutine, worker.getPseudocode()); 095 } 096 return worker.getPseudocode(); 097 } 098 099 private class JetControlFlowInstructionsGeneratorWorker implements JetControlFlowBuilder { 100 101 private final PseudocodeImpl pseudocode; 102 private final Label error; 103 private final Label sink; 104 private final JetElement returnSubroutine; 105 106 private final PseudoValueFactory valueFactory = new PseudoValueFactoryImpl() { 107 @NotNull 108 @Override 109 public PseudoValue newValue(@Nullable JetElement element, @Nullable InstructionWithValue instruction) { 110 PseudoValue value = super.newValue(element, instruction); 111 if (element != null) { 112 bindValue(value, element); 113 } 114 return value; 115 } 116 }; 117 118 private JetControlFlowInstructionsGeneratorWorker(@NotNull JetElement scopingElement, @NotNull JetElement returnSubroutine) { 119 this.pseudocode = new PseudocodeImpl(scopingElement); 120 this.error = pseudocode.createLabel("error", null); 121 this.sink = pseudocode.createLabel("sink", null); 122 this.returnSubroutine = returnSubroutine; 123 } 124 125 public PseudocodeImpl getPseudocode() { 126 return pseudocode; 127 } 128 129 private void add(@NotNull Instruction instruction) { 130 pseudocode.addInstruction(instruction); 131 } 132 133 @NotNull 134 @Override 135 public final Label createUnboundLabel() { 136 return pseudocode.createLabel("L" + labelCount++, null); 137 } 138 139 @NotNull 140 @Override 141 public Label createUnboundLabel(@NotNull String name) { 142 return pseudocode.createLabel("L" + labelCount++, name); 143 } 144 145 @NotNull 146 @Override 147 public final LoopInfo enterLoop(@NotNull JetLoopExpression expression) { 148 LoopInfo info = new LoopInfo( 149 expression, 150 createUnboundLabel("loop entry point"), 151 createUnboundLabel("loop exit point"), 152 createUnboundLabel("body entry point"), 153 createUnboundLabel("body exit point"), 154 createUnboundLabel("condition entry point")); 155 bindLabel(info.getEntryPoint()); 156 elementToBlockInfo.put(expression, info); 157 return info; 158 } 159 160 @Override 161 public void enterLoopBody(@NotNull JetLoopExpression expression) { 162 LoopInfo info = (LoopInfo) elementToBlockInfo.get(expression); 163 bindLabel(info.getBodyEntryPoint()); 164 loopInfo.push(info); 165 allBlocks.push(info); 166 } 167 168 @Override 169 public final void exitLoopBody(@NotNull JetLoopExpression expression) { 170 LoopInfo info = loopInfo.pop(); 171 elementToBlockInfo.remove(expression); 172 allBlocks.pop(); 173 bindLabel(info.getBodyExitPoint()); 174 } 175 176 @Override 177 public JetLoopExpression getCurrentLoop() { 178 return loopInfo.empty() ? null : loopInfo.peek().getElement(); 179 } 180 181 @Override 182 public void enterSubroutine(@NotNull JetElement subroutine) { 183 BreakableBlockInfo blockInfo = new BreakableBlockInfo( 184 subroutine, 185 /* entry point */ createUnboundLabel(), 186 /* exit point */ createUnboundLabel()); 187 elementToBlockInfo.put(subroutine, blockInfo); 188 allBlocks.push(blockInfo); 189 bindLabel(blockInfo.getEntryPoint()); 190 add(new SubroutineEnterInstruction(subroutine, getCurrentScope())); 191 } 192 193 @NotNull 194 @Override 195 public JetElement getCurrentSubroutine() { 196 return pseudocode.getCorrespondingElement(); 197 } 198 199 @Override 200 public JetElement getReturnSubroutine() { 201 return returnSubroutine;// subroutineInfo.empty() ? null : subroutineInfo.peek().getElement(); 202 } 203 204 @NotNull 205 @Override 206 public Label getEntryPoint(@NotNull JetElement labelElement) { 207 return elementToBlockInfo.get(labelElement).getEntryPoint(); 208 } 209 210 @NotNull 211 @Override 212 public Label getConditionEntryPoint(@NotNull JetElement labelElement) { 213 BreakableBlockInfo blockInfo = elementToBlockInfo.get(labelElement); 214 assert blockInfo instanceof LoopInfo : "expected LoopInfo for " + labelElement.getText() ; 215 return ((LoopInfo)blockInfo).getConditionEntryPoint(); 216 } 217 218 @NotNull 219 @Override 220 public Label getExitPoint(@NotNull JetElement labelElement) { 221 BreakableBlockInfo blockInfo = elementToBlockInfo.get(labelElement); 222 assert blockInfo != null : labelElement.getText(); 223 return blockInfo.getExitPoint(); 224 } 225 226 @NotNull 227 private LexicalScope getCurrentScope() { 228 return lexicalScopes.peek(); 229 } 230 231 @Override 232 public void enterLexicalScope(@NotNull JetElement element) { 233 LexicalScope current = lexicalScopes.isEmpty() ? null : getCurrentScope(); 234 LexicalScope scope = new LexicalScope(current, element); 235 lexicalScopes.push(scope); 236 } 237 238 @Override 239 public void exitLexicalScope(@NotNull JetElement element) { 240 LexicalScope currentScope = getCurrentScope(); 241 assert currentScope.getElement() == element : "Exit from not the current lexical scope.\n" + 242 "Current scope is for: " + currentScope.getElement() + ".\n" + 243 "Exit from the scope for: " + element.getText(); 244 lexicalScopes.pop(); 245 } 246 247 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 248 249 private void handleJumpInsideTryFinally(Label jumpTarget) { 250 List<TryFinallyBlockInfo> finallyBlocks = new ArrayList<TryFinallyBlockInfo>(); 251 252 for (int i = allBlocks.size() - 1; i >= 0; i--) { 253 BlockInfo blockInfo = allBlocks.get(i); 254 if (blockInfo instanceof BreakableBlockInfo) { 255 BreakableBlockInfo breakableBlockInfo = (BreakableBlockInfo) blockInfo; 256 if (breakableBlockInfo.getReferablePoints().contains(jumpTarget) || jumpTarget == error) { 257 for (int j = finallyBlocks.size() - 1; j >= 0; j--) { 258 finallyBlocks.get(j).generateFinallyBlock(); 259 } 260 break; 261 } 262 } 263 else if (blockInfo instanceof TryFinallyBlockInfo) { 264 TryFinallyBlockInfo tryFinallyBlockInfo = (TryFinallyBlockInfo) blockInfo; 265 finallyBlocks.add(tryFinallyBlockInfo); 266 } 267 } 268 } 269 270 @NotNull 271 @Override 272 public Pseudocode exitSubroutine(@NotNull JetElement subroutine) { 273 bindLabel(getExitPoint(subroutine)); 274 pseudocode.addExitInstruction(new SubroutineExitInstruction(subroutine, getCurrentScope(), false)); 275 bindLabel(error); 276 pseudocode.addErrorInstruction(new SubroutineExitInstruction(subroutine, getCurrentScope(), true)); 277 bindLabel(sink); 278 pseudocode.addSinkInstruction(new SubroutineSinkInstruction(subroutine, getCurrentScope(), "<SINK>")); 279 elementToBlockInfo.remove(subroutine); 280 allBlocks.pop(); 281 return pseudocode; 282 } 283 284 @Override 285 public void mark(@NotNull JetElement element) { 286 add(new MarkInstruction(element, getCurrentScope())); 287 } 288 289 @Nullable 290 @Override 291 public PseudoValue getBoundValue(@Nullable JetElement element) { 292 return pseudocode.getElementValue(element); 293 } 294 295 @Override 296 public void bindValue(@NotNull PseudoValue value, @NotNull JetElement element) { 297 pseudocode.bindElementToValue(element, value); 298 } 299 300 @NotNull 301 @Override 302 public PseudoValue newValue(@Nullable JetElement element) { 303 return valueFactory.newValue(element, null); 304 } 305 306 @Override 307 public void returnValue(@NotNull JetExpression returnExpression, @NotNull PseudoValue returnValue, @NotNull JetElement subroutine) { 308 Label exitPoint = getExitPoint(subroutine); 309 handleJumpInsideTryFinally(exitPoint); 310 add(new ReturnValueInstruction(returnExpression, getCurrentScope(), exitPoint, returnValue)); 311 } 312 313 @Override 314 public void returnNoValue(@NotNull JetReturnExpression returnExpression, @NotNull JetElement subroutine) { 315 Label exitPoint = getExitPoint(subroutine); 316 handleJumpInsideTryFinally(exitPoint); 317 add(new ReturnNoValueInstruction(returnExpression, getCurrentScope(), exitPoint)); 318 } 319 320 @Override 321 public void write( 322 @NotNull JetElement assignment, 323 @NotNull JetElement lValue, 324 @NotNull PseudoValue rValue, 325 @NotNull AccessTarget target, 326 @NotNull Map<PseudoValue, ReceiverValue> receiverValues) { 327 add(new WriteValueInstruction(assignment, getCurrentScope(), target, receiverValues, lValue, rValue)); 328 } 329 330 @Override 331 public void declareParameter(@NotNull JetParameter parameter) { 332 add(new VariableDeclarationInstruction(parameter, getCurrentScope())); 333 } 334 335 @Override 336 public void declareVariable(@NotNull JetVariableDeclaration property) { 337 add(new VariableDeclarationInstruction(property, getCurrentScope())); 338 } 339 340 @Override 341 public void declareFunction(@NotNull JetElement subroutine, @NotNull Pseudocode pseudocode) { 342 add(new LocalFunctionDeclarationInstruction(subroutine, pseudocode, getCurrentScope())); 343 } 344 345 @Override 346 public void loadUnit(@NotNull JetExpression expression) { 347 add(new LoadUnitValueInstruction(expression, getCurrentScope())); 348 } 349 350 @Override 351 public void jump(@NotNull Label label, @NotNull JetElement element) { 352 handleJumpInsideTryFinally(label); 353 add(new UnconditionalJumpInstruction(element, label, getCurrentScope())); 354 } 355 356 @Override 357 public void jumpOnFalse(@NotNull Label label, @NotNull JetElement element, @Nullable PseudoValue conditionValue) { 358 handleJumpInsideTryFinally(label); 359 add(new ConditionalJumpInstruction(element, false, getCurrentScope(), label, conditionValue)); 360 } 361 362 @Override 363 public void jumpOnTrue(@NotNull Label label, @NotNull JetElement element, @Nullable PseudoValue conditionValue) { 364 handleJumpInsideTryFinally(label); 365 add(new ConditionalJumpInstruction(element, true, getCurrentScope(), label, conditionValue)); 366 } 367 368 @Override 369 public void bindLabel(@NotNull Label label) { 370 pseudocode.bindLabel(label); 371 } 372 373 @Override 374 public void nondeterministicJump(@NotNull Label label, @NotNull JetElement element, @Nullable PseudoValue inputValue) { 375 handleJumpInsideTryFinally(label); 376 add(new NondeterministicJumpInstruction(element, Collections.singletonList(label), getCurrentScope(), inputValue)); 377 } 378 379 @Override 380 public void nondeterministicJump(@NotNull List<Label> labels, @NotNull JetElement element) { 381 //todo 382 //handleJumpInsideTryFinally(label); 383 add(new NondeterministicJumpInstruction(element, labels, getCurrentScope(), null)); 384 } 385 386 @Override 387 public void jumpToError(@NotNull JetElement element) { 388 handleJumpInsideTryFinally(error); 389 add(new UnconditionalJumpInstruction(element, error, getCurrentScope())); 390 } 391 392 @Override 393 public void enterTryFinally(@NotNull GenerationTrigger generationTrigger) { 394 allBlocks.push(new TryFinallyBlockInfo(generationTrigger)); 395 } 396 397 @Override 398 public void throwException(@NotNull JetThrowExpression expression, @NotNull PseudoValue thrownValue) { 399 handleJumpInsideTryFinally(error); 400 add(new ThrowExceptionInstruction(expression, getCurrentScope(), error, thrownValue)); 401 } 402 403 @Override 404 public void exitTryFinally() { 405 BlockInfo pop = allBlocks.pop(); 406 assert pop instanceof TryFinallyBlockInfo; 407 } 408 409 @Override 410 public void repeatPseudocode(@NotNull Label startLabel, @NotNull Label finishLabel) { 411 labelCount = pseudocode.repeatPart(startLabel, finishLabel, labelCount); 412 } 413 414 @NotNull 415 @Override 416 public InstructionWithValue loadConstant(@NotNull JetExpression expression, @Nullable CompileTimeConstant<?> constant) { 417 return read(expression); 418 } 419 420 @NotNull 421 @Override 422 public InstructionWithValue createAnonymousObject(@NotNull JetObjectLiteralExpression expression) { 423 return read(expression); 424 } 425 426 @NotNull 427 @Override 428 public InstructionWithValue createLambda(@NotNull JetFunction expression) { 429 return read(expression instanceof JetFunctionLiteral ? (JetFunctionLiteralExpression) expression.getParent() : expression); 430 } 431 432 @NotNull 433 @Override 434 public InstructionWithValue loadStringTemplate(@NotNull JetStringTemplateExpression expression, @NotNull List<PseudoValue> inputValues) { 435 if (inputValues.isEmpty()) return read(expression); 436 Map<PseudoValue, TypePredicate> predicate = PseudocodePackage.expectedTypeFor(AllTypes.INSTANCE$, inputValues); 437 return magic(expression, expression, inputValues, predicate, MagicKind.STRING_TEMPLATE); 438 } 439 440 @NotNull 441 @Override 442 public MagicInstruction magic( 443 @NotNull JetElement instructionElement, 444 @Nullable JetElement valueElement, 445 @NotNull List<PseudoValue> inputValues, 446 @NotNull Map<PseudoValue, TypePredicate> expectedTypes, 447 @NotNull MagicKind kind 448 ) { 449 MagicInstruction instruction = MagicInstruction.Factory.create( 450 instructionElement, valueElement, getCurrentScope(), inputValues, expectedTypes, kind, valueFactory 451 ); 452 add(instruction); 453 return instruction; 454 } 455 456 @NotNull 457 @Override 458 public MergeInstruction merge(@NotNull JetExpression expression, @NotNull List<PseudoValue> inputValues) { 459 MergeInstruction instruction = MergeInstruction.Factory.create(expression, getCurrentScope(), inputValues, valueFactory); 460 add(instruction); 461 return instruction; 462 } 463 464 @NotNull 465 @Override 466 public ReadValueInstruction readVariable( 467 @NotNull JetExpression expression, 468 @NotNull ResolvedCall<?> resolvedCall, 469 @NotNull Map<PseudoValue, ReceiverValue> receiverValues 470 ) { 471 return read(expression, resolvedCall, receiverValues); 472 } 473 474 @NotNull 475 @Override 476 public CallInstruction call( 477 @NotNull JetElement valueElement, 478 @NotNull ResolvedCall<?> resolvedCall, 479 @NotNull Map<PseudoValue, ReceiverValue> receiverValues, 480 @NotNull Map<PseudoValue, ValueParameterDescriptor> arguments 481 ) { 482 JetType returnType = resolvedCall.getResultingDescriptor().getReturnType(); 483 CallInstruction instruction = CallInstruction.Factory.create( 484 valueElement, 485 getCurrentScope(), 486 resolvedCall, 487 receiverValues, 488 arguments, 489 returnType != null && KotlinBuiltIns.isNothing(returnType) ? null : valueFactory 490 ); 491 add(instruction); 492 return instruction; 493 } 494 495 @NotNull 496 @Override 497 public OperationInstruction predefinedOperation( 498 @NotNull JetExpression expression, 499 @NotNull PredefinedOperation operation, 500 @NotNull List<PseudoValue> inputValues 501 ) { 502 Map<PseudoValue, TypePredicate> expectedTypes; 503 switch(operation) { 504 case AND: 505 case OR: 506 SingleType onlyBoolean = new SingleType(KotlinBuiltIns.getInstance().getBooleanType()); 507 expectedTypes = PseudocodePackage.expectedTypeFor(onlyBoolean, inputValues); 508 break; 509 case NOT_NULL_ASSERTION: 510 expectedTypes = PseudocodePackage.expectedTypeFor(AllTypes.INSTANCE$, inputValues); 511 break; 512 default: 513 throw new IllegalArgumentException("Invalid operation: " + operation); 514 } 515 516 return magic(expression, expression, inputValues, expectedTypes, getMagicKind(operation)); 517 } 518 519 @NotNull 520 private MagicKind getMagicKind(@NotNull PredefinedOperation operation) { 521 switch(operation) { 522 case AND: 523 return MagicKind.AND; 524 case OR: 525 return MagicKind.OR; 526 case NOT_NULL_ASSERTION: 527 return MagicKind.NOT_NULL_ASSERTION; 528 default: 529 throw new IllegalArgumentException("Invalid operation: " + operation); 530 } 531 } 532 533 @NotNull 534 private ReadValueInstruction read( 535 @NotNull JetExpression expression, 536 @Nullable ResolvedCall<?> resolvedCall, 537 @NotNull Map<PseudoValue, ReceiverValue> receiverValues 538 ) { 539 AccessTarget accessTarget = resolvedCall != null ? new AccessTarget.Call(resolvedCall) : AccessTarget.BlackBox.INSTANCE$; 540 ReadValueInstruction instruction = ReadValueInstruction.Factory.create( 541 expression, getCurrentScope(), accessTarget, receiverValues, valueFactory 542 ); 543 add(instruction); 544 return instruction; 545 } 546 547 @NotNull 548 private ReadValueInstruction read(@NotNull JetExpression expression) { 549 return read(expression, null, Collections.<PseudoValue, ReceiverValue>emptyMap()); 550 } 551 } 552 553 public static class TryFinallyBlockInfo extends BlockInfo { 554 private final GenerationTrigger finallyBlock; 555 556 private TryFinallyBlockInfo(GenerationTrigger finallyBlock) { 557 this.finallyBlock = finallyBlock; 558 } 559 560 public void generateFinallyBlock() { 561 finallyBlock.generate(); 562 } 563 } 564 565 }