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 return inputValues.isEmpty() ? read(expression) : magic(expression, expression, inputValues, MagicKind.STRING_TEMPLATE); 436 } 437 438 @NotNull 439 @Override 440 public MagicInstruction magic( 441 @NotNull JetElement instructionElement, 442 @Nullable JetElement valueElement, 443 @NotNull List<PseudoValue> inputValues, 444 @NotNull MagicKind kind 445 ) { 446 MagicInstruction instruction = new MagicInstruction( 447 instructionElement, valueElement, getCurrentScope(), inputValues, kind, valueFactory 448 ); 449 add(instruction); 450 return instruction; 451 } 452 453 @NotNull 454 @Override 455 public MergeInstruction merge(@NotNull JetExpression expression, @NotNull List<PseudoValue> inputValues) { 456 MergeInstruction instruction = new MergeInstruction(expression, getCurrentScope(), inputValues, valueFactory); 457 add(instruction); 458 return instruction; 459 } 460 461 @NotNull 462 @Override 463 public ReadValueInstruction readVariable( 464 @NotNull JetExpression expression, 465 @NotNull ResolvedCall<?> resolvedCall, 466 @NotNull Map<PseudoValue, ReceiverValue> receiverValues 467 ) { 468 return read(expression, resolvedCall, receiverValues); 469 } 470 471 @NotNull 472 @Override 473 public CallInstruction call( 474 @NotNull JetElement valueElement, 475 @NotNull ResolvedCall<?> resolvedCall, 476 @NotNull Map<PseudoValue, ReceiverValue> receiverValues, 477 @NotNull Map<PseudoValue, ValueParameterDescriptor> arguments 478 ) { 479 JetType returnType = resolvedCall.getResultingDescriptor().getReturnType(); 480 CallInstruction instruction = new CallInstruction( 481 valueElement, 482 getCurrentScope(), 483 resolvedCall, 484 receiverValues, 485 arguments, 486 returnType != null && KotlinBuiltIns.isNothing(returnType) ? null : valueFactory 487 ); 488 add(instruction); 489 return instruction; 490 } 491 492 @NotNull 493 @Override 494 public OperationInstruction predefinedOperation( 495 @NotNull JetExpression expression, 496 @NotNull PredefinedOperation operation, 497 @NotNull List<PseudoValue> inputValues 498 ) { 499 return magic(expression, expression, inputValues, getMagicKind(operation)); 500 } 501 502 @NotNull 503 private MagicKind getMagicKind(@NotNull PredefinedOperation operation) { 504 switch(operation) { 505 case AND: 506 return MagicKind.AND; 507 case OR: 508 return MagicKind.OR; 509 case NOT_NULL_ASSERTION: 510 return MagicKind.NOT_NULL_ASSERTION; 511 default: 512 throw new IllegalArgumentException("Invalid operation: " + operation); 513 } 514 } 515 516 @NotNull 517 private ReadValueInstruction read( 518 @NotNull JetExpression expression, 519 @Nullable ResolvedCall<?> resolvedCall, 520 @NotNull Map<PseudoValue, ReceiverValue> receiverValues 521 ) { 522 AccessTarget accessTarget = resolvedCall != null ? new AccessTarget.Call(resolvedCall) : AccessTarget.BlackBox.INSTANCE$; 523 ReadValueInstruction instruction = new ReadValueInstruction( 524 expression, getCurrentScope(), accessTarget, receiverValues, valueFactory 525 ); 526 add(instruction); 527 return instruction; 528 } 529 530 @NotNull 531 private ReadValueInstruction read(@NotNull JetExpression expression) { 532 return read(expression, null, Collections.<PseudoValue, ReceiverValue>emptyMap()); 533 } 534 } 535 536 public static class TryFinallyBlockInfo extends BlockInfo { 537 private final GenerationTrigger finallyBlock; 538 539 private TryFinallyBlockInfo(GenerationTrigger finallyBlock) { 540 this.finallyBlock = finallyBlock; 541 } 542 543 public void generateFinallyBlock() { 544 finallyBlock.generate(); 545 } 546 } 547 548 }