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 }