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