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.KotlinPackage;
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.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 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<JetElement, PseudoValue> elementsToValues = new BidirectionalMap<JetElement, 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<JetElement, Instruction> representativeInstructions = new HashMap<JetElement, Instruction>();
108
109 private final List<PseudocodeLabel> labels = new ArrayList<PseudocodeLabel>();
110
111 private final JetElement correspondingElement;
112 private SubroutineExitInstruction exitInstruction;
113 private SubroutineSinkInstruction sinkInstruction;
114 private SubroutineExitInstruction errorInstruction;
115 private boolean postPrecessed = false;
116
117 public PseudocodeImpl(JetElement correspondingElement) {
118 this.correspondingElement = correspondingElement;
119 }
120
121 @NotNull
122 @Override
123 public JetElement 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 PseudocodeTraverserPackage.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 PseudocodeTraverserPackage.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 (PseudocodePackage.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 JetElement element) {
272 return elementsToValues.get(element);
273 }
274
275 @NotNull
276 @Override
277 public List<? extends JetElement> getValueElements(@Nullable PseudoValue value) {
278 List<? extends JetElement> result = elementsToValues.getKeysByValue(value);
279 return result != null ? result : Collections.<JetElement>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 JetElement 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 KotlinPackage.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 PseudocodeTraverserPackage.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 }