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.google.common.collect.*; 020 import com.intellij.util.containers.BidirectionalMap; 021 import kotlin.collections.MapsKt; 022 import kotlin.jvm.functions.Function0; 023 import kotlin.jvm.functions.Function1; 024 import org.jetbrains.annotations.NotNull; 025 import org.jetbrains.annotations.Nullable; 026 import org.jetbrains.kotlin.cfg.Label; 027 import org.jetbrains.kotlin.cfg.pseudocode.instructions.*; 028 import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicInstruction; 029 import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicKind; 030 import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MergeInstruction; 031 import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.AbstractJumpInstruction; 032 import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ConditionalJumpInstruction; 033 import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.NondeterministicJumpInstruction; 034 import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction; 035 import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineEnterInstruction; 036 import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineExitInstruction; 037 import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineSinkInstruction; 038 import org.jetbrains.kotlin.cfg.pseudocodeTraverser.PseudocodeTraverserKt; 039 import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraverseInstructionResult; 040 import org.jetbrains.kotlin.psi.KtElement; 041 042 import java.util.*; 043 044 import static org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder.BACKWARD; 045 import static org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder.FORWARD; 046 047 public class PseudocodeImpl implements Pseudocode { 048 049 public class PseudocodeLabel implements Label { 050 private final String name; 051 private final String comment; 052 private Integer targetInstructionIndex; 053 054 055 private PseudocodeLabel(@NotNull String name, @Nullable String comment) { 056 this.name = name; 057 this.comment = comment; 058 } 059 060 @NotNull 061 @Override 062 public String getName() { 063 return name; 064 } 065 066 @Override 067 public String toString() { 068 return comment == null ? name : (name + " [" + comment + "]"); 069 } 070 071 public Integer getTargetInstructionIndex() { 072 return targetInstructionIndex; 073 } 074 075 public void setTargetInstructionIndex(int targetInstructionIndex) { 076 this.targetInstructionIndex = targetInstructionIndex; 077 } 078 079 @Nullable 080 private List<Instruction> resolve() { 081 assert targetInstructionIndex != null; 082 return mutableInstructionList.subList(getTargetInstructionIndex(), mutableInstructionList.size()); 083 } 084 085 public Instruction resolveToInstruction() { 086 assert targetInstructionIndex != null; 087 return mutableInstructionList.get(targetInstructionIndex); 088 } 089 090 public PseudocodeLabel copy(int newLabelIndex) { 091 return new PseudocodeLabel("L" + newLabelIndex, "copy of " + name + ", " + comment); 092 } 093 094 public PseudocodeImpl getPseudocode() { 095 return PseudocodeImpl.this; 096 } 097 } 098 099 private final List<Instruction> mutableInstructionList = new ArrayList<Instruction>(); 100 private final List<Instruction> instructions = new ArrayList<Instruction>(); 101 102 private final BidirectionalMap<KtElement, PseudoValue> elementsToValues = new BidirectionalMap<KtElement, PseudoValue>(); 103 104 private final Map<PseudoValue, List<Instruction>> valueUsages = Maps.newHashMap(); 105 private final Map<PseudoValue, Set<PseudoValue>> mergedValues = Maps.newHashMap(); 106 private final Set<Instruction> sideEffectFree = Sets.newHashSet(); 107 108 private Pseudocode parent = null; 109 private Set<LocalFunctionDeclarationInstruction> localDeclarations = null; 110 //todo getters 111 private final Map<KtElement, Instruction> representativeInstructions = new HashMap<KtElement, Instruction>(); 112 113 private final List<PseudocodeLabel> labels = new ArrayList<PseudocodeLabel>(); 114 115 private final KtElement correspondingElement; 116 private SubroutineExitInstruction exitInstruction; 117 private SubroutineSinkInstruction sinkInstruction; 118 private SubroutineExitInstruction errorInstruction; 119 private boolean postPrecessed = false; 120 121 public PseudocodeImpl(KtElement correspondingElement) { 122 this.correspondingElement = correspondingElement; 123 } 124 125 @NotNull 126 @Override 127 public KtElement getCorrespondingElement() { 128 return correspondingElement; 129 } 130 131 @NotNull 132 @Override 133 public Set<LocalFunctionDeclarationInstruction> getLocalDeclarations() { 134 if (localDeclarations == null) { 135 localDeclarations = getLocalDeclarations(this); 136 } 137 return localDeclarations; 138 } 139 140 @NotNull 141 private static Set<LocalFunctionDeclarationInstruction> getLocalDeclarations(@NotNull Pseudocode pseudocode) { 142 Set<LocalFunctionDeclarationInstruction> localDeclarations = Sets.newLinkedHashSet(); 143 for (Instruction instruction : ((PseudocodeImpl)pseudocode).mutableInstructionList) { 144 if (instruction instanceof LocalFunctionDeclarationInstruction) { 145 localDeclarations.add((LocalFunctionDeclarationInstruction) instruction); 146 localDeclarations.addAll(getLocalDeclarations(((LocalFunctionDeclarationInstruction)instruction).getBody())); 147 } 148 } 149 return localDeclarations; 150 } 151 152 @Override 153 @Nullable 154 public Pseudocode getParent() { 155 return parent; 156 } 157 158 private void setParent(Pseudocode parent) { 159 this.parent = parent; 160 } 161 162 @NotNull 163 public Pseudocode getRootPseudocode() { 164 Pseudocode parent = getParent(); 165 while (parent != null) { 166 if (parent.getParent() == null) return parent; 167 parent = parent.getParent(); 168 } 169 return this; 170 } 171 172 /*package*/ PseudocodeLabel createLabel(@NotNull String name, @Nullable String comment) { 173 PseudocodeLabel label = new PseudocodeLabel(name, comment); 174 labels.add(label); 175 return label; 176 } 177 178 @Override 179 @NotNull 180 public List<Instruction> getInstructions() { 181 return instructions; 182 } 183 184 @NotNull 185 @Override 186 public List<Instruction> getReversedInstructions() { 187 LinkedHashSet<Instruction> traversedInstructions = Sets.newLinkedHashSet(); 188 PseudocodeTraverserKt.traverseFollowingInstructions(sinkInstruction, traversedInstructions, BACKWARD, null); 189 if (traversedInstructions.size() < instructions.size()) { 190 List<Instruction> simplyReversedInstructions = Lists.newArrayList(instructions); 191 Collections.reverse(simplyReversedInstructions); 192 for (Instruction instruction : simplyReversedInstructions) { 193 if (!traversedInstructions.contains(instruction)) { 194 PseudocodeTraverserKt.traverseFollowingInstructions(instruction, traversedInstructions, BACKWARD, null); 195 } 196 } 197 } 198 return Lists.newArrayList(traversedInstructions); 199 } 200 201 @Override 202 @NotNull 203 public List<Instruction> getInstructionsIncludingDeadCode() { 204 return mutableInstructionList; 205 } 206 207 //for tests only 208 @NotNull 209 public List<PseudocodeLabel> getLabels() { 210 return labels; 211 } 212 213 /*package*/ void addExitInstruction(SubroutineExitInstruction exitInstruction) { 214 addInstruction(exitInstruction); 215 assert this.exitInstruction == null; 216 this.exitInstruction = exitInstruction; 217 } 218 219 /*package*/ void addSinkInstruction(SubroutineSinkInstruction sinkInstruction) { 220 addInstruction(sinkInstruction); 221 assert this.sinkInstruction == null; 222 this.sinkInstruction = sinkInstruction; 223 } 224 225 /*package*/ void addErrorInstruction(SubroutineExitInstruction errorInstruction) { 226 addInstruction(errorInstruction); 227 assert this.errorInstruction == null; 228 this.errorInstruction = errorInstruction; 229 } 230 231 /*package*/ void addInstruction(Instruction instruction) { 232 mutableInstructionList.add(instruction); 233 instruction.setOwner(this); 234 235 if (instruction instanceof KtElementInstruction) { 236 KtElementInstruction elementInstruction = (KtElementInstruction) instruction; 237 representativeInstructions.put(elementInstruction.getElement(), instruction); 238 } 239 240 if (instruction instanceof MergeInstruction) { 241 addMergedValues((MergeInstruction) instruction); 242 } 243 244 for (PseudoValue inputValue : instruction.getInputValues()) { 245 addValueUsage(inputValue, instruction); 246 for (PseudoValue mergedValue : getMergedValues(inputValue)) { 247 addValueUsage(mergedValue, instruction); 248 } 249 } 250 if (PseudocodeUtilsKt.calcSideEffectFree(instruction)) { 251 sideEffectFree.add(instruction); 252 } 253 } 254 255 @Override 256 @NotNull 257 public SubroutineExitInstruction getExitInstruction() { 258 return exitInstruction; 259 } 260 261 @Override 262 @NotNull 263 public SubroutineSinkInstruction getSinkInstruction() { 264 return sinkInstruction; 265 } 266 267 @Override 268 @NotNull 269 public SubroutineEnterInstruction getEnterInstruction() { 270 return (SubroutineEnterInstruction) mutableInstructionList.get(0); 271 } 272 273 @Nullable 274 @Override 275 public PseudoValue getElementValue(@Nullable KtElement element) { 276 return elementsToValues.get(element); 277 } 278 279 @NotNull 280 @Override 281 public List<? extends KtElement> getValueElements(@Nullable PseudoValue value) { 282 List<? extends KtElement> result = elementsToValues.getKeysByValue(value); 283 return result != null ? result : Collections.<KtElement>emptyList(); 284 } 285 286 @NotNull 287 @Override 288 public List<? extends Instruction> getUsages(@Nullable PseudoValue value) { 289 List<? extends Instruction> result = valueUsages.get(value); 290 return result != null ? result : Collections.<Instruction>emptyList(); 291 } 292 293 @Override 294 public boolean isSideEffectFree(@NotNull Instruction instruction) { 295 return sideEffectFree.contains(instruction); 296 } 297 298 /*package*/ void bindElementToValue(@NotNull KtElement element, @NotNull PseudoValue value) { 299 elementsToValues.put(element, value); 300 } 301 302 /*package*/ void bindLabel(Label label) { 303 ((PseudocodeLabel) label).setTargetInstructionIndex(mutableInstructionList.size()); 304 } 305 306 private Set<PseudoValue> getMergedValues(@NotNull PseudoValue value) { 307 Set<PseudoValue> result = mergedValues.get(value); 308 return result != null ? result : Collections.<PseudoValue>emptySet(); 309 } 310 311 private void addMergedValues(@NotNull MergeInstruction instruction) { 312 Set<PseudoValue> result = new LinkedHashSet<PseudoValue>(); 313 for (PseudoValue value : instruction.getInputValues()) { 314 result.addAll(getMergedValues(value)); 315 result.add(value); 316 } 317 mergedValues.put(instruction.getOutputValue(), result); 318 } 319 320 private void addValueUsage(PseudoValue value, Instruction usage) { 321 if (usage instanceof MergeInstruction) return; 322 MapsKt.getOrPut( 323 valueUsages, 324 value, 325 new Function0<List<Instruction>>() { 326 @Override 327 public List<Instruction> invoke() { 328 return Lists.newArrayList(); 329 } 330 } 331 ).add(usage); 332 } 333 334 public void postProcess() { 335 if (postPrecessed) return; 336 postPrecessed = true; 337 errorInstruction.setSink(getSinkInstruction()); 338 exitInstruction.setSink(getSinkInstruction()); 339 int index = 0; 340 for (Instruction instruction : mutableInstructionList) { 341 //recursively invokes 'postProcess' for local declarations 342 processInstruction(instruction, index); 343 index++; 344 } 345 if (getParent() != null) return; 346 347 // Collecting reachable instructions should be done after processing all instructions 348 // (including instructions in local declarations) to avoid being in incomplete state. 349 collectAndCacheReachableInstructions(); 350 for (LocalFunctionDeclarationInstruction localFunctionDeclarationInstruction : getLocalDeclarations()) { 351 ((PseudocodeImpl) localFunctionDeclarationInstruction.getBody()).collectAndCacheReachableInstructions(); 352 } 353 } 354 355 private void collectAndCacheReachableInstructions() { 356 Set<Instruction> reachableInstructions = collectReachableInstructions(); 357 for (Instruction instruction : mutableInstructionList) { 358 if (reachableInstructions.contains(instruction)) { 359 instructions.add(instruction); 360 } 361 } 362 markDeadInstructions(); 363 } 364 365 private void processInstruction(Instruction instruction, final int currentPosition) { 366 instruction.accept(new InstructionVisitor() { 367 @Override 368 public void visitInstructionWithNext(@NotNull InstructionWithNext instruction) { 369 instruction.setNext(getNextPosition(currentPosition)); 370 } 371 372 @Override 373 public void visitJump(@NotNull AbstractJumpInstruction instruction) { 374 instruction.setResolvedTarget(getJumpTarget(instruction.getTargetLabel())); 375 } 376 377 @Override 378 public void visitNondeterministicJump(@NotNull NondeterministicJumpInstruction instruction) { 379 instruction.setNext(getNextPosition(currentPosition)); 380 List<Label> targetLabels = instruction.getTargetLabels(); 381 for (Label targetLabel : targetLabels) { 382 instruction.setResolvedTarget(targetLabel, getJumpTarget(targetLabel)); 383 } 384 } 385 386 @Override 387 public void visitConditionalJump(@NotNull ConditionalJumpInstruction instruction) { 388 Instruction nextInstruction = getNextPosition(currentPosition); 389 Instruction jumpTarget = getJumpTarget(instruction.getTargetLabel()); 390 if (instruction.getOnTrue()) { 391 instruction.setNextOnFalse(nextInstruction); 392 instruction.setNextOnTrue(jumpTarget); 393 } 394 else { 395 instruction.setNextOnFalse(jumpTarget); 396 instruction.setNextOnTrue(nextInstruction); 397 } 398 visitJump(instruction); 399 } 400 401 @Override 402 public void visitLocalFunctionDeclarationInstruction(@NotNull LocalFunctionDeclarationInstruction instruction) { 403 PseudocodeImpl body = (PseudocodeImpl) instruction.getBody(); 404 body.setParent(PseudocodeImpl.this); 405 body.postProcess(); 406 instruction.setNext(getSinkInstruction()); 407 } 408 409 @Override 410 public void visitSubroutineExit(@NotNull SubroutineExitInstruction instruction) { 411 // Nothing 412 } 413 414 @Override 415 public void visitSubroutineSink(@NotNull SubroutineSinkInstruction instruction) { 416 // Nothing 417 } 418 419 @Override 420 public void visitInstruction(@NotNull Instruction instruction) { 421 throw new UnsupportedOperationException(instruction.toString()); 422 } 423 }); 424 } 425 426 private Set<Instruction> collectReachableInstructions() { 427 Set<Instruction> visited = Sets.newHashSet(); 428 PseudocodeTraverserKt.traverseFollowingInstructions(getEnterInstruction(), visited, FORWARD, 429 new Function1<Instruction, TraverseInstructionResult>() { 430 @Override 431 public TraverseInstructionResult invoke(Instruction instruction) { 432 if (instruction instanceof MagicInstruction && 433 ((MagicInstruction) instruction).getKind() == MagicKind.EXHAUSTIVE_WHEN_ELSE) { 434 return TraverseInstructionResult.SKIP; 435 } 436 return TraverseInstructionResult.CONTINUE; 437 } 438 }); 439 if (!visited.contains(getExitInstruction())) { 440 visited.add(getExitInstruction()); 441 } 442 if (!visited.contains(errorInstruction)) { 443 visited.add(errorInstruction); 444 } 445 if (!visited.contains(getSinkInstruction())) { 446 visited.add(getSinkInstruction()); 447 } 448 return visited; 449 } 450 451 private void markDeadInstructions() { 452 Set<Instruction> instructionSet = Sets.newHashSet(instructions); 453 for (Instruction instruction : mutableInstructionList) { 454 if (!instructionSet.contains(instruction)) { 455 ((InstructionImpl)instruction).setMarkedAsDead(true); 456 for (Instruction nextInstruction : instruction.getNextInstructions()) { 457 nextInstruction.getPreviousInstructions().remove(instruction); 458 } 459 } 460 } 461 } 462 463 @NotNull 464 private Instruction getJumpTarget(@NotNull Label targetLabel) { 465 return ((PseudocodeLabel)targetLabel).resolveToInstruction(); 466 } 467 468 @NotNull 469 private Instruction getNextPosition(int currentPosition) { 470 int targetPosition = currentPosition + 1; 471 assert targetPosition < mutableInstructionList.size() : currentPosition; 472 return mutableInstructionList.get(targetPosition); 473 } 474 475 @Override 476 public PseudocodeImpl copy() { 477 PseudocodeImpl result = new PseudocodeImpl(correspondingElement); 478 result.repeatWhole(this); 479 return result; 480 } 481 482 private void repeatWhole(@NotNull PseudocodeImpl originalPseudocode) { 483 repeatInternal(originalPseudocode, null, null, 0); 484 parent = originalPseudocode.parent; 485 } 486 487 public int repeatPart(@NotNull Label startLabel, @NotNull Label finishLabel, int labelCount) { 488 return repeatInternal(((PseudocodeLabel) startLabel).getPseudocode(), startLabel, finishLabel, labelCount); 489 } 490 491 private int repeatInternal( 492 @NotNull PseudocodeImpl originalPseudocode, 493 @Nullable Label startLabel, @Nullable Label finishLabel, 494 int labelCount) { 495 Integer startIndex = startLabel != null ? ((PseudocodeLabel) startLabel).getTargetInstructionIndex() : Integer.valueOf(0); 496 assert startIndex != null; 497 Integer finishIndex = finishLabel != null 498 ? ((PseudocodeLabel) finishLabel).getTargetInstructionIndex() 499 : Integer.valueOf(originalPseudocode.mutableInstructionList.size()); 500 assert finishIndex != null; 501 502 Map<Label, Label> originalToCopy = Maps.newLinkedHashMap(); 503 Multimap<Instruction, Label> originalLabelsForInstruction = HashMultimap.create(); 504 for (PseudocodeLabel label : originalPseudocode.labels) { 505 Integer index = label.getTargetInstructionIndex(); 506 if (index == null) continue; //label is not bounded yet 507 if (label == startLabel || label == finishLabel) continue; 508 509 if (startIndex <= index && index <= finishIndex) { 510 originalToCopy.put(label, label.copy(labelCount++)); 511 originalLabelsForInstruction.put(getJumpTarget(label), label); 512 } 513 } 514 for (Label label : originalToCopy.values()) { 515 labels.add((PseudocodeLabel) label); 516 } 517 for (int index = startIndex; index < finishIndex; index++) { 518 Instruction originalInstruction = originalPseudocode.mutableInstructionList.get(index); 519 repeatLabelsBindingForInstruction(originalInstruction, originalToCopy, originalLabelsForInstruction); 520 Instruction copy = copyInstruction(originalInstruction, originalToCopy); 521 addInstruction(copy); 522 if (originalInstruction == originalPseudocode.errorInstruction && copy instanceof SubroutineExitInstruction) { 523 errorInstruction = (SubroutineExitInstruction) copy; 524 } 525 if (originalInstruction == originalPseudocode.exitInstruction && copy instanceof SubroutineExitInstruction) { 526 exitInstruction = (SubroutineExitInstruction) copy; 527 } 528 if (originalInstruction == originalPseudocode.sinkInstruction && copy instanceof SubroutineSinkInstruction) { 529 sinkInstruction = (SubroutineSinkInstruction) copy; 530 } 531 } 532 if (finishIndex < mutableInstructionList.size()) { 533 repeatLabelsBindingForInstruction(originalPseudocode.mutableInstructionList.get(finishIndex), 534 originalToCopy, 535 originalLabelsForInstruction); 536 } 537 return labelCount; 538 } 539 540 private void repeatLabelsBindingForInstruction( 541 @NotNull Instruction originalInstruction, 542 @NotNull Map<Label, Label> originalToCopy, 543 @NotNull Multimap<Instruction, Label> originalLabelsForInstruction 544 ) { 545 for (Label originalLabel : originalLabelsForInstruction.get(originalInstruction)) { 546 bindLabel(originalToCopy.get(originalLabel)); 547 } 548 } 549 550 private static Instruction copyInstruction(@NotNull Instruction instruction, @NotNull Map<Label, Label> originalToCopy) { 551 if (instruction instanceof AbstractJumpInstruction) { 552 Label originalTarget = ((AbstractJumpInstruction) instruction).getTargetLabel(); 553 if (originalToCopy.containsKey(originalTarget)) { 554 return ((AbstractJumpInstruction)instruction).copy(originalToCopy.get(originalTarget)); 555 } 556 } 557 if (instruction instanceof NondeterministicJumpInstruction) { 558 List<Label> originalTargets = ((NondeterministicJumpInstruction) instruction).getTargetLabels(); 559 List<Label> copyTargets = copyLabels(originalTargets, originalToCopy); 560 return ((NondeterministicJumpInstruction) instruction).copy(copyTargets); 561 } 562 return ((InstructionImpl)instruction).copy(); 563 } 564 565 @NotNull 566 private static List<Label> copyLabels(Collection<Label> labels, Map<Label, Label> originalToCopy) { 567 List<Label> newLabels = Lists.newArrayList(); 568 for (Label label : labels) { 569 Label newLabel = originalToCopy.get(label); 570 newLabels.add(newLabel != null ? newLabel : label); 571 } 572 return newLabels; 573 } 574 }