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