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;
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 Set<LocalFunctionDeclarationInstruction> localDeclarations = null;
082        //todo getters
083        private final Map<JetElement, Instruction> representativeInstructions = new HashMap<JetElement, Instruction>();
084        private final Map<JetExpression, LoopInfo> loopInfo = Maps.newHashMap();
085        
086        private final List<PseudocodeLabel> labels = new ArrayList<PseudocodeLabel>();
087    
088        private final JetElement correspondingElement;
089        private SubroutineExitInstruction exitInstruction;
090        private SubroutineSinkInstruction sinkInstruction;
091        private SubroutineExitInstruction errorInstruction;
092        private boolean postPrecessed = false;
093    
094        public PseudocodeImpl(JetElement correspondingElement) {
095            this.correspondingElement = correspondingElement;
096        }
097    
098        @NotNull
099        @Override
100        public JetElement getCorrespondingElement() {
101            return correspondingElement;
102        }
103    
104        @NotNull
105        @Override
106        public Set<LocalFunctionDeclarationInstruction> getLocalDeclarations() {
107            if (localDeclarations == null) {
108                localDeclarations = getLocalDeclarations(this);
109            }
110            return localDeclarations;
111        }
112    
113        @NotNull
114        private static Set<LocalFunctionDeclarationInstruction> getLocalDeclarations(@NotNull Pseudocode pseudocode) {
115            Set<LocalFunctionDeclarationInstruction> localDeclarations = Sets.newLinkedHashSet();
116            for (Instruction instruction : pseudocode.getInstructions()) {
117                if (instruction instanceof LocalFunctionDeclarationInstruction) {
118                    localDeclarations.add((LocalFunctionDeclarationInstruction) instruction);
119                    localDeclarations.addAll(getLocalDeclarations(((LocalFunctionDeclarationInstruction)instruction).getBody()));
120                }
121            }
122            return localDeclarations;
123        }
124    
125        /*package*/ PseudocodeLabel createLabel(String name) {
126            PseudocodeLabel label = new PseudocodeLabel(name);
127            labels.add(label);
128            return label;
129        }
130        
131        @Override
132        @NotNull
133        public List<Instruction> getInstructions() {
134            return instructions;
135        }
136    
137        @NotNull
138        @Override
139        public List<Instruction> getReversedInstructions() {
140            LinkedHashSet<Instruction> traversedInstructions = Sets.newLinkedHashSet();
141            PseudocodeTraverser.traverseFollowingInstructions(sinkInstruction, traversedInstructions, BACKWARD, null);
142            if (traversedInstructions.size() < instructions.size()) {
143                List<Instruction> simplyReversedInstructions = Lists.newArrayList(instructions);
144                Collections.reverse(simplyReversedInstructions);
145                for (Instruction instruction : simplyReversedInstructions) {
146                    if (!traversedInstructions.contains(instruction)) {
147                        PseudocodeTraverser.traverseFollowingInstructions(instruction, traversedInstructions, BACKWARD, null);
148                    }
149                }
150            }
151            return Lists.newArrayList(traversedInstructions);
152        }
153    
154        //for tests only
155        @NotNull
156        public List<Instruction> getAllInstructions() {
157            return mutableInstructionList;
158        }
159    
160        @Override
161        @NotNull
162        public List<Instruction> getDeadInstructions() {
163            List<Instruction> deadInstructions = Lists.newArrayList();
164            for (Instruction instruction : mutableInstructionList) {
165                if (isDead(instruction)) {
166                    deadInstructions.add(instruction);
167                }
168            }
169            return deadInstructions;
170        }
171    
172        private static boolean isDead(@NotNull Instruction instruction) {
173            if (!((InstructionImpl)instruction).isDead()) return false;
174            for (Instruction copy : instruction.getCopies()) {
175                if (!((InstructionImpl)copy).isDead()) return false;
176            }
177            return true;
178        }
179    
180        //for tests only
181        @NotNull
182        public List<PseudocodeLabel> getLabels() {
183            return labels;
184        }
185    
186        /*package*/ void addExitInstruction(SubroutineExitInstruction exitInstruction) {
187            addInstruction(exitInstruction);
188            assert this.exitInstruction == null;
189            this.exitInstruction = exitInstruction;
190        }
191        
192        /*package*/ void addSinkInstruction(SubroutineSinkInstruction sinkInstruction) {
193            addInstruction(sinkInstruction);
194            assert this.sinkInstruction == null;
195            this.sinkInstruction = sinkInstruction;
196        }
197    
198        /*package*/ void addErrorInstruction(SubroutineExitInstruction errorInstruction) {
199            addInstruction(errorInstruction);
200            assert this.errorInstruction == null;
201            this.errorInstruction = errorInstruction;
202        }
203    
204        /*package*/ void addInstruction(Instruction instruction) {
205            mutableInstructionList.add(instruction);
206            instruction.setOwner(this);
207    
208            if (instruction instanceof JetElementInstruction) {
209                JetElementInstruction elementInstruction = (JetElementInstruction) instruction;
210                representativeInstructions.put(elementInstruction.getElement(), instruction);
211            }
212        }
213    
214        /*package*/ void recordLoopInfo(JetExpression expression, LoopInfo blockInfo) {
215            loopInfo.put(expression, blockInfo);
216        }
217    
218        @Override
219        @NotNull
220        public SubroutineExitInstruction getExitInstruction() {
221            return exitInstruction;
222        }
223    
224        @Override
225        @NotNull
226        public SubroutineSinkInstruction getSinkInstruction() {
227            return sinkInstruction;
228        }
229    
230        @Override
231        @NotNull
232        public SubroutineEnterInstruction getEnterInstruction() {
233            return (SubroutineEnterInstruction) mutableInstructionList.get(0);
234        }
235    
236        /*package*/ void bindLabel(Label label) {
237            ((PseudocodeLabel) label).setTargetInstructionIndex(mutableInstructionList.size());
238        }
239    
240        public void postProcess() {
241            if (postPrecessed) return;
242            postPrecessed = true;
243            errorInstruction.setSink(getSinkInstruction());
244            exitInstruction.setSink(getSinkInstruction());
245            for (int i = 0, instructionsSize = mutableInstructionList.size(); i < instructionsSize; i++) {
246                processInstruction(mutableInstructionList.get(i), i);
247            }
248            Set<Instruction> reachableInstructions = collectReachableInstructions();
249            for (Instruction instruction : mutableInstructionList) {
250                if (reachableInstructions.contains(instruction)) {
251                    instructions.add(instruction);
252                }
253            }
254            markDeadInstructions();
255        }
256    
257        private void processInstruction(Instruction instruction, final int currentPosition) {
258            instruction.accept(new InstructionVisitor() {
259                @Override
260                public void visitInstructionWithNext(InstructionWithNext instruction) {
261                    instruction.setNext(getNextPosition(currentPosition));
262                }
263    
264                @Override
265                public void visitJump(AbstractJumpInstruction instruction) {
266                    instruction.setResolvedTarget(getJumpTarget(instruction.getTargetLabel()));
267                }
268    
269                @Override
270                public void visitNondeterministicJump(NondeterministicJumpInstruction instruction) {
271                    instruction.setNext(getNextPosition(currentPosition));
272                    List<Label> targetLabels = instruction.getTargetLabels();
273                    for (Label targetLabel : targetLabels) {
274                        instruction.setResolvedTarget(targetLabel, getJumpTarget(targetLabel));
275                    }
276                }
277    
278                @Override
279                public void visitConditionalJump(ConditionalJumpInstruction instruction) {
280                    Instruction nextInstruction = getNextPosition(currentPosition);
281                    Instruction jumpTarget = getJumpTarget(instruction.getTargetLabel());
282                    if (instruction.onTrue()) {
283                        instruction.setNextOnFalse(nextInstruction);
284                        instruction.setNextOnTrue(jumpTarget);
285                    }
286                    else {
287                        instruction.setNextOnFalse(jumpTarget);
288                        instruction.setNextOnTrue(nextInstruction);
289                    }
290                    visitJump(instruction);
291                }
292    
293                @Override
294                public void visitLocalFunctionDeclarationInstruction(LocalFunctionDeclarationInstruction instruction) {
295                    ((PseudocodeImpl)instruction.getBody()).postProcess();
296                    instruction.setNext(getSinkInstruction());
297                }
298    
299                @Override
300                public void visitSubroutineExit(SubroutineExitInstruction instruction) {
301                    // Nothing
302                }
303    
304                @Override
305                public void visitSubroutineSink(SubroutineSinkInstruction instruction) {
306                    // Nothing
307                }
308    
309                @Override
310                public void visitInstruction(Instruction instruction) {
311                    throw new UnsupportedOperationException(instruction.toString());
312                }
313            });
314        }
315    
316        private Set<Instruction> collectReachableInstructions() {
317            Set<Instruction> visited = Sets.newHashSet();
318            PseudocodeTraverser.traverseFollowingInstructions(getEnterInstruction(), visited, FORWARD, null);
319            if (!visited.contains(getExitInstruction())) {
320                visited.add(getExitInstruction());
321            }
322            if (!visited.contains(errorInstruction)) {
323                visited.add(errorInstruction);
324            }
325            if (!visited.contains(getSinkInstruction())) {
326                visited.add(getSinkInstruction());
327            }
328            return visited;
329        }
330    
331        private void markDeadInstructions() {
332            Set<Instruction> instructionSet = Sets.newHashSet(instructions);
333            for (Instruction instruction : mutableInstructionList) {
334                if (!instructionSet.contains(instruction)) {
335                    ((InstructionImpl)instruction).die();
336                    for (Instruction nextInstruction : instruction.getNextInstructions()) {
337                        nextInstruction.getPreviousInstructions().remove(instruction);
338                    }
339                }
340            }
341        }
342    
343        @NotNull
344        private Instruction getJumpTarget(@NotNull Label targetLabel) {
345            return ((PseudocodeLabel)targetLabel).resolveToInstruction();
346        }
347    
348        @NotNull
349        private Instruction getNextPosition(int currentPosition) {
350            int targetPosition = currentPosition + 1;
351            assert targetPosition < mutableInstructionList.size() : currentPosition;
352            return mutableInstructionList.get(targetPosition);
353        }
354    
355        public void repeatPart(@NotNull Label startLabel, @NotNull Label finishLabel) {
356            Integer startIndex = ((PseudocodeLabel) startLabel).getTargetInstructionIndex();
357            assert startIndex != null;
358            Integer finishIndex = ((PseudocodeLabel) finishLabel).getTargetInstructionIndex();
359            assert finishIndex != null;
360    
361            Map<Label, Label> originalToCopy = Maps.newHashMap();
362            Multimap<Instruction, Label> originalLabelsForInstruction = HashMultimap.create();
363            for (PseudocodeLabel label : labels) {
364                Integer index = label.getTargetInstructionIndex();
365                if (index == null) continue; //label is not bounded yet
366                if (label == startLabel || label == finishLabel) continue;
367    
368                if (startIndex <= index && index <= finishIndex) {
369                    originalToCopy.put(label, label.copy());
370                    originalLabelsForInstruction.put(getJumpTarget(label), label);
371                }
372            }
373            for (int index = startIndex; index < finishIndex; index++) {
374                Instruction originalInstruction = mutableInstructionList.get(index);
375                repeatLabelsBindingForInstruction(originalInstruction, originalToCopy, originalLabelsForInstruction);
376                addInstruction(copyInstruction(originalInstruction, originalToCopy));
377            }
378            repeatLabelsBindingForInstruction(mutableInstructionList.get(finishIndex), originalToCopy, originalLabelsForInstruction);
379        }
380    
381        private void repeatLabelsBindingForInstruction(
382                @NotNull Instruction originalInstruction,
383                @NotNull Map<Label, Label> originalToCopy,
384                @NotNull Multimap<Instruction, Label> originalLabelsForInstruction
385        ) {
386            for (Label originalLabel : originalLabelsForInstruction.get(originalInstruction)) {
387                bindLabel(originalToCopy.get(originalLabel));
388            }
389        }
390    
391        private Instruction copyInstruction(@NotNull Instruction instruction, @NotNull Map<Label, Label> originalToCopy) {
392            if (instruction instanceof AbstractJumpInstruction) {
393                Label originalTarget = ((AbstractJumpInstruction) instruction).getTargetLabel();
394                if (originalToCopy.containsKey(originalTarget)) {
395                    return ((AbstractJumpInstruction)instruction).copy(originalToCopy.get(originalTarget));
396                }
397            }
398            if (instruction instanceof NondeterministicJumpInstruction) {
399                List<Label> originalTargets = ((NondeterministicJumpInstruction) instruction).getTargetLabels();
400                List<Label> copyTargets = copyLabels(originalTargets, originalToCopy);
401                return ((NondeterministicJumpInstruction) instruction).copy(copyTargets);
402            }
403            return ((InstructionImpl)instruction).copy();
404        }
405    
406        @NotNull
407        private List<Label> copyLabels(Collection<Label> labels, Map<Label, Label> originalToCopy) {
408            List<Label> newLabels = Lists.newArrayList();
409            for (Label label : labels) {
410                Label newLabel = originalToCopy.get(label);
411                newLabels.add(newLabel != null ? newLabel : label);
412            }
413            return newLabels;
414        }
415    }