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            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    }