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.pseudocodeTraverser.PseudocodeTraverserPackage;
025    import org.jetbrains.jet.lang.psi.JetElement;
026    import org.jetbrains.jet.lang.psi.JetExpression;
027    
028    import java.util.*;
029    
030    import static org.jetbrains.jet.lang.cfg.pseudocodeTraverser.TraversalOrder.BACKWARD;
031    import static org.jetbrains.jet.lang.cfg.pseudocodeTraverser.TraversalOrder.FORWARD;
032    
033    public class PseudocodeImpl implements Pseudocode {
034    
035        public class PseudocodeLabel implements Label {
036            private final String name;
037            private Integer targetInstructionIndex;
038    
039    
040            private PseudocodeLabel(String name) {
041                this.name = name;
042            }
043    
044            @Override
045            public String getName() {
046                return name;
047            }
048    
049            @Override
050            public String toString() {
051                return name;
052            }
053    
054            public Integer getTargetInstructionIndex() {
055                return targetInstructionIndex;
056            }
057    
058            public void setTargetInstructionIndex(int targetInstructionIndex) {
059                this.targetInstructionIndex = targetInstructionIndex;
060            }
061    
062            @Nullable
063            private List<Instruction> resolve() {
064                assert targetInstructionIndex != null;
065                return mutableInstructionList.subList(getTargetInstructionIndex(), mutableInstructionList.size());
066            }
067    
068            public Instruction resolveToInstruction() {
069                assert targetInstructionIndex != null;
070                return mutableInstructionList.get(targetInstructionIndex);
071            }
072    
073            public Label copy() {
074                return new PseudocodeLabel("copy " + name);
075            }
076        }
077    
078        private final List<Instruction> mutableInstructionList = new ArrayList<Instruction>();
079        private final List<Instruction> instructions = new ArrayList<Instruction>();
080    
081        private Pseudocode parent = null;
082        private Set<LocalFunctionDeclarationInstruction> localDeclarations = null;
083        //todo getters
084        private final Map<JetElement, Instruction> representativeInstructions = new HashMap<JetElement, Instruction>();
085        private final Map<JetExpression, LoopInfo> loopInfo = Maps.newHashMap();
086        
087        private final List<PseudocodeLabel> labels = new ArrayList<PseudocodeLabel>();
088    
089        private final JetElement correspondingElement;
090        private SubroutineExitInstruction exitInstruction;
091        private SubroutineSinkInstruction sinkInstruction;
092        private SubroutineExitInstruction errorInstruction;
093        private boolean postPrecessed = false;
094    
095        public PseudocodeImpl(JetElement correspondingElement) {
096            this.correspondingElement = correspondingElement;
097        }
098    
099        @NotNull
100        @Override
101        public JetElement getCorrespondingElement() {
102            return correspondingElement;
103        }
104    
105        @NotNull
106        @Override
107        public Set<LocalFunctionDeclarationInstruction> getLocalDeclarations() {
108            if (localDeclarations == null) {
109                localDeclarations = getLocalDeclarations(this);
110            }
111            return localDeclarations;
112        }
113    
114        @NotNull
115        private static Set<LocalFunctionDeclarationInstruction> getLocalDeclarations(@NotNull Pseudocode pseudocode) {
116            Set<LocalFunctionDeclarationInstruction> localDeclarations = Sets.newLinkedHashSet();
117            for (Instruction instruction : ((PseudocodeImpl)pseudocode).mutableInstructionList) {
118                if (instruction instanceof LocalFunctionDeclarationInstruction) {
119                    localDeclarations.add((LocalFunctionDeclarationInstruction) instruction);
120                    localDeclarations.addAll(getLocalDeclarations(((LocalFunctionDeclarationInstruction)instruction).getBody()));
121                }
122            }
123            return localDeclarations;
124        }
125    
126        @Override
127        @Nullable
128        public Pseudocode getParent() {
129            return parent;
130        }
131    
132        private void setParent(Pseudocode parent) {
133            this.parent = parent;
134        }
135    
136        @NotNull
137        public Pseudocode getRootPseudocode() {
138            Pseudocode parent = getParent();
139            while (parent != null) {
140                if (parent.getParent() == null) return parent;
141                parent = parent.getParent();
142            }
143            return this;
144        }
145    
146        /*package*/ PseudocodeLabel createLabel(String name) {
147            PseudocodeLabel label = new PseudocodeLabel(name);
148            labels.add(label);
149            return label;
150        }
151        
152        @Override
153        @NotNull
154        public List<Instruction> getInstructions() {
155            return instructions;
156        }
157    
158        @NotNull
159        @Override
160        public List<Instruction> getReversedInstructions() {
161            LinkedHashSet<Instruction> traversedInstructions = Sets.newLinkedHashSet();
162            PseudocodeTraverserPackage.traverseFollowingInstructions(sinkInstruction, traversedInstructions, BACKWARD, null);
163            if (traversedInstructions.size() < instructions.size()) {
164                List<Instruction> simplyReversedInstructions = Lists.newArrayList(instructions);
165                Collections.reverse(simplyReversedInstructions);
166                for (Instruction instruction : simplyReversedInstructions) {
167                    if (!traversedInstructions.contains(instruction)) {
168                        PseudocodeTraverserPackage.traverseFollowingInstructions(instruction, traversedInstructions, BACKWARD, null);
169                    }
170                }
171            }
172            return Lists.newArrayList(traversedInstructions);
173        }
174    
175        //for tests only
176        @NotNull
177        public List<Instruction> getAllInstructions() {
178            return mutableInstructionList;
179        }
180    
181        @Override
182        @NotNull
183        public List<Instruction> getDeadInstructions() {
184            List<Instruction> deadInstructions = Lists.newArrayList();
185            for (Instruction instruction : mutableInstructionList) {
186                if (isDead(instruction)) {
187                    deadInstructions.add(instruction);
188                }
189            }
190            return deadInstructions;
191        }
192    
193        private static boolean isDead(@NotNull Instruction instruction) {
194            if (!((InstructionImpl)instruction).isDead()) return false;
195            for (Instruction copy : instruction.getCopies()) {
196                if (!((InstructionImpl)copy).isDead()) return false;
197            }
198            return true;
199        }
200    
201        //for tests only
202        @NotNull
203        public List<PseudocodeLabel> getLabels() {
204            return labels;
205        }
206    
207        /*package*/ void addExitInstruction(SubroutineExitInstruction exitInstruction) {
208            addInstruction(exitInstruction);
209            assert this.exitInstruction == null;
210            this.exitInstruction = exitInstruction;
211        }
212        
213        /*package*/ void addSinkInstruction(SubroutineSinkInstruction sinkInstruction) {
214            addInstruction(sinkInstruction);
215            assert this.sinkInstruction == null;
216            this.sinkInstruction = sinkInstruction;
217        }
218    
219        /*package*/ void addErrorInstruction(SubroutineExitInstruction errorInstruction) {
220            addInstruction(errorInstruction);
221            assert this.errorInstruction == null;
222            this.errorInstruction = errorInstruction;
223        }
224    
225        /*package*/ void addInstruction(Instruction instruction) {
226            mutableInstructionList.add(instruction);
227            instruction.setOwner(this);
228    
229            if (instruction instanceof JetElementInstruction) {
230                JetElementInstruction elementInstruction = (JetElementInstruction) instruction;
231                representativeInstructions.put(elementInstruction.getElement(), instruction);
232            }
233        }
234    
235        /*package*/ void recordLoopInfo(JetExpression expression, LoopInfo blockInfo) {
236            loopInfo.put(expression, blockInfo);
237        }
238    
239        @Override
240        @NotNull
241        public SubroutineExitInstruction getExitInstruction() {
242            return exitInstruction;
243        }
244    
245        @Override
246        @NotNull
247        public SubroutineSinkInstruction getSinkInstruction() {
248            return sinkInstruction;
249        }
250    
251        @Override
252        @NotNull
253        public SubroutineEnterInstruction getEnterInstruction() {
254            return (SubroutineEnterInstruction) mutableInstructionList.get(0);
255        }
256    
257        /*package*/ void bindLabel(Label label) {
258            ((PseudocodeLabel) label).setTargetInstructionIndex(mutableInstructionList.size());
259        }
260    
261        public void postProcess() {
262            if (postPrecessed) return;
263            postPrecessed = true;
264            errorInstruction.setSink(getSinkInstruction());
265            exitInstruction.setSink(getSinkInstruction());
266            int index = 0;
267            for (Instruction instruction : mutableInstructionList) {
268                //recursively invokes 'postProcess' for local declarations
269                processInstruction(instruction, index);
270                index++;
271            }
272            if (getParent() != null) return;
273    
274            // Collecting reachable instructions should be done after processing all instructions
275            // (including instructions in local declarations) to avoid being in incomplete state.
276            collectAndCacheReachableInstructions();
277            for (LocalFunctionDeclarationInstruction localFunctionDeclarationInstruction : getLocalDeclarations()) {
278                ((PseudocodeImpl) localFunctionDeclarationInstruction.getBody()).collectAndCacheReachableInstructions();
279            }
280        }
281    
282        private void collectAndCacheReachableInstructions() {
283            Set<Instruction> reachableInstructions = collectReachableInstructions();
284            for (Instruction instruction : mutableInstructionList) {
285                if (reachableInstructions.contains(instruction)) {
286                    instructions.add(instruction);
287                }
288            }
289            markDeadInstructions();
290        }
291    
292        private void processInstruction(Instruction instruction, final int currentPosition) {
293            instruction.accept(new InstructionVisitor() {
294                @Override
295                public void visitInstructionWithNext(InstructionWithNext instruction) {
296                    instruction.setNext(getNextPosition(currentPosition));
297                }
298    
299                @Override
300                public void visitJump(AbstractJumpInstruction instruction) {
301                    instruction.setResolvedTarget(getJumpTarget(instruction.getTargetLabel()));
302                }
303    
304                @Override
305                public void visitNondeterministicJump(NondeterministicJumpInstruction instruction) {
306                    instruction.setNext(getNextPosition(currentPosition));
307                    List<Label> targetLabels = instruction.getTargetLabels();
308                    for (Label targetLabel : targetLabels) {
309                        instruction.setResolvedTarget(targetLabel, getJumpTarget(targetLabel));
310                    }
311                }
312    
313                @Override
314                public void visitConditionalJump(ConditionalJumpInstruction instruction) {
315                    Instruction nextInstruction = getNextPosition(currentPosition);
316                    Instruction jumpTarget = getJumpTarget(instruction.getTargetLabel());
317                    if (instruction.onTrue()) {
318                        instruction.setNextOnFalse(nextInstruction);
319                        instruction.setNextOnTrue(jumpTarget);
320                    }
321                    else {
322                        instruction.setNextOnFalse(jumpTarget);
323                        instruction.setNextOnTrue(nextInstruction);
324                    }
325                    visitJump(instruction);
326                }
327    
328                @Override
329                public void visitLocalFunctionDeclarationInstruction(LocalFunctionDeclarationInstruction instruction) {
330                    PseudocodeImpl body = (PseudocodeImpl) instruction.getBody();
331                    body.setParent(PseudocodeImpl.this);
332                    body.postProcess();
333                    instruction.setNext(getSinkInstruction());
334                }
335    
336                @Override
337                public void visitSubroutineExit(SubroutineExitInstruction instruction) {
338                    // Nothing
339                }
340    
341                @Override
342                public void visitSubroutineSink(SubroutineSinkInstruction instruction) {
343                    // Nothing
344                }
345    
346                @Override
347                public void visitInstruction(Instruction instruction) {
348                    throw new UnsupportedOperationException(instruction.toString());
349                }
350            });
351        }
352    
353        private Set<Instruction> collectReachableInstructions() {
354            Set<Instruction> visited = Sets.newHashSet();
355            PseudocodeTraverserPackage.traverseFollowingInstructions(getEnterInstruction(), visited, FORWARD, null);
356            if (!visited.contains(getExitInstruction())) {
357                visited.add(getExitInstruction());
358            }
359            if (!visited.contains(errorInstruction)) {
360                visited.add(errorInstruction);
361            }
362            if (!visited.contains(getSinkInstruction())) {
363                visited.add(getSinkInstruction());
364            }
365            return visited;
366        }
367    
368        private void markDeadInstructions() {
369            Set<Instruction> instructionSet = Sets.newHashSet(instructions);
370            for (Instruction instruction : mutableInstructionList) {
371                if (!instructionSet.contains(instruction)) {
372                    ((InstructionImpl)instruction).die();
373                    for (Instruction nextInstruction : instruction.getNextInstructions()) {
374                        nextInstruction.getPreviousInstructions().remove(instruction);
375                    }
376                }
377            }
378        }
379    
380        @NotNull
381        private Instruction getJumpTarget(@NotNull Label targetLabel) {
382            return ((PseudocodeLabel)targetLabel).resolveToInstruction();
383        }
384    
385        @NotNull
386        private Instruction getNextPosition(int currentPosition) {
387            int targetPosition = currentPosition + 1;
388            assert targetPosition < mutableInstructionList.size() : currentPosition;
389            return mutableInstructionList.get(targetPosition);
390        }
391    
392        public void repeatPart(@NotNull Label startLabel, @NotNull Label finishLabel) {
393            Integer startIndex = ((PseudocodeLabel) startLabel).getTargetInstructionIndex();
394            assert startIndex != null;
395            Integer finishIndex = ((PseudocodeLabel) finishLabel).getTargetInstructionIndex();
396            assert finishIndex != null;
397    
398            Map<Label, Label> originalToCopy = Maps.newHashMap();
399            Multimap<Instruction, Label> originalLabelsForInstruction = HashMultimap.create();
400            for (PseudocodeLabel label : labels) {
401                Integer index = label.getTargetInstructionIndex();
402                if (index == null) continue; //label is not bounded yet
403                if (label == startLabel || label == finishLabel) continue;
404    
405                if (startIndex <= index && index <= finishIndex) {
406                    originalToCopy.put(label, label.copy());
407                    originalLabelsForInstruction.put(getJumpTarget(label), label);
408                }
409            }
410            for (int index = startIndex; index < finishIndex; index++) {
411                Instruction originalInstruction = mutableInstructionList.get(index);
412                repeatLabelsBindingForInstruction(originalInstruction, originalToCopy, originalLabelsForInstruction);
413                addInstruction(copyInstruction(originalInstruction, originalToCopy));
414            }
415            repeatLabelsBindingForInstruction(mutableInstructionList.get(finishIndex), originalToCopy, originalLabelsForInstruction);
416        }
417    
418        private void repeatLabelsBindingForInstruction(
419                @NotNull Instruction originalInstruction,
420                @NotNull Map<Label, Label> originalToCopy,
421                @NotNull Multimap<Instruction, Label> originalLabelsForInstruction
422        ) {
423            for (Label originalLabel : originalLabelsForInstruction.get(originalInstruction)) {
424                bindLabel(originalToCopy.get(originalLabel));
425            }
426        }
427    
428        private Instruction copyInstruction(@NotNull Instruction instruction, @NotNull Map<Label, Label> originalToCopy) {
429            if (instruction instanceof AbstractJumpInstruction) {
430                Label originalTarget = ((AbstractJumpInstruction) instruction).getTargetLabel();
431                if (originalToCopy.containsKey(originalTarget)) {
432                    return ((AbstractJumpInstruction)instruction).copy(originalToCopy.get(originalTarget));
433                }
434            }
435            if (instruction instanceof NondeterministicJumpInstruction) {
436                List<Label> originalTargets = ((NondeterministicJumpInstruction) instruction).getTargetLabels();
437                List<Label> copyTargets = copyLabels(originalTargets, originalToCopy);
438                return ((NondeterministicJumpInstruction) instruction).copy(copyTargets);
439            }
440            return ((InstructionImpl)instruction).copy();
441        }
442    
443        @NotNull
444        private List<Label> copyLabels(Collection<Label> labels, Map<Label, Label> originalToCopy) {
445            List<Label> newLabels = Lists.newArrayList();
446            for (Label label : labels) {
447                Label newLabel = originalToCopy.get(label);
448                newLabels.add(newLabel != null ? newLabel : label);
449            }
450            return newLabels;
451        }
452    }