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