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    }