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