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    }