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;
018    
019    import com.google.common.collect.Lists;
020    import com.google.common.collect.Maps;
021    import com.google.common.collect.Sets;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.jet.lang.cfg.pseudocode.*;
025    
026    import java.util.Collection;
027    import java.util.Collections;
028    import java.util.List;
029    import java.util.Map;
030    
031    import static org.jetbrains.jet.lang.cfg.PseudocodeTraverser.TraversalOrder.FORWARD;
032    
033    public class PseudocodeTraverser {
034        
035        public static enum TraversalOrder {
036            FORWARD,
037            BACKWARD
038        }
039        
040        @NotNull
041        private static Instruction getStartInstruction(@NotNull Pseudocode pseudocode, @NotNull TraversalOrder traversalOrder) {
042            return traversalOrder == FORWARD ? pseudocode.getEnterInstruction() : pseudocode.getSinkInstruction();
043        }
044    
045        @NotNull
046        private static Instruction getLastInstruction(@NotNull Pseudocode pseudocode, @NotNull TraversalOrder traversalOrder) {
047            return traversalOrder == FORWARD ? pseudocode.getSinkInstruction() : pseudocode.getEnterInstruction();
048        }
049    
050        @NotNull
051        private static List<Instruction> getInstructions(@NotNull Pseudocode pseudocode, @NotNull TraversalOrder traversalOrder) {
052            return traversalOrder == FORWARD ? pseudocode.getInstructions() : pseudocode.getReversedInstructions();
053        }
054    
055        @NotNull
056        private static Collection<Instruction> getPreviousInstruction(@NotNull Instruction instruction, @NotNull TraversalOrder traversalOrder) {
057            return traversalOrder == FORWARD ? instruction.getPreviousInstructions() : instruction.getNextInstructions();
058        }
059    
060        private static boolean isStartInstruction(@NotNull Instruction instruction, @NotNull TraversalOrder traversalOrder) {
061            return traversalOrder == FORWARD ? instruction instanceof SubroutineEnterInstruction
062                                             : instruction instanceof SubroutineSinkInstruction;
063        }
064    
065        public static enum LookInsideStrategy {
066            ANALYSE_LOCAL_DECLARATIONS,
067            SKIP_LOCAL_DECLARATIONS
068        }
069    
070        private static boolean shouldLookInside(Instruction instruction, LookInsideStrategy lookInside) {
071            return lookInside == LookInsideStrategy.ANALYSE_LOCAL_DECLARATIONS && instruction instanceof LocalDeclarationInstruction;
072        }
073    
074        public static <D> Map<Instruction, Edges<D>> collectData(
075                @NotNull Pseudocode pseudocode, TraversalOrder traversalOrder, LookInsideStrategy lookInside,
076                @NotNull D initialDataValue, @NotNull D initialDataValueForEnterInstruction,
077                @NotNull InstructionDataMergeStrategy<D> instructionDataMergeStrategy) {
078    
079            Map<Instruction, Edges<D>> edgesMap = Maps.newLinkedHashMap();
080            initializeEdgesMap(pseudocode, lookInside, edgesMap, initialDataValue);
081            edgesMap.put(getStartInstruction(pseudocode, traversalOrder), Edges.create(initialDataValueForEnterInstruction, initialDataValueForEnterInstruction));
082    
083            boolean[] changed = new boolean[1];
084            changed[0] = true;
085            while (changed[0]) {
086                changed[0] = false;
087                collectDataFromSubgraph(pseudocode, traversalOrder, lookInside, edgesMap, instructionDataMergeStrategy,
088                                        Collections.<Instruction>emptyList(), changed, false);
089            }
090            return edgesMap;
091        }
092    
093        private static <D> void initializeEdgesMap(
094                @NotNull Pseudocode pseudocode, LookInsideStrategy lookInside,
095                @NotNull Map<Instruction, Edges<D>> edgesMap,
096                @NotNull D initialDataValue) {
097            List<Instruction> instructions = pseudocode.getInstructions();
098            Edges<D> initialEdge = Edges.create(initialDataValue, initialDataValue);
099            for (Instruction instruction : instructions) {
100                edgesMap.put(instruction, initialEdge);
101                if (shouldLookInside(instruction, lookInside)) {
102                    initializeEdgesMap(((LocalDeclarationInstruction) instruction).getBody(), lookInside, edgesMap, initialDataValue);
103                }
104            }
105        }
106    
107        private static <D> void collectDataFromSubgraph(
108                @NotNull Pseudocode pseudocode, TraversalOrder traversalOrder,
109                LookInsideStrategy lookInside,
110                @NotNull Map<Instruction, Edges<D>> edgesMap,
111                @NotNull InstructionDataMergeStrategy<D> instructionDataMergeStrategy,
112                @NotNull Collection<Instruction> previousSubGraphInstructions,
113                boolean[] changed, boolean isLocal) {
114    
115            List<Instruction> instructions = getInstructions(pseudocode, traversalOrder);
116            Instruction startInstruction = getStartInstruction(pseudocode, traversalOrder);
117    
118            for (Instruction instruction : instructions) {
119                boolean isStart = isStartInstruction(instruction, traversalOrder);
120                if (!isLocal && isStart) continue;
121    
122                Collection<Instruction> allPreviousInstructions;
123                Collection<Instruction> previousInstructions = getPreviousInstruction(instruction, traversalOrder);
124    
125                if (instruction == startInstruction && !previousSubGraphInstructions.isEmpty()) {
126                    allPreviousInstructions = Lists.newArrayList(previousInstructions);
127                    allPreviousInstructions.addAll(previousSubGraphInstructions);
128                }
129                else {
130                    allPreviousInstructions = previousInstructions;
131                }
132    
133                if (shouldLookInside(instruction, lookInside)) {
134                    Pseudocode subroutinePseudocode = ((LocalDeclarationInstruction) instruction).getBody();
135                    collectDataFromSubgraph(subroutinePseudocode, traversalOrder, lookInside, edgesMap, instructionDataMergeStrategy,
136                                            previousInstructions,
137                                            changed, true);
138                    Instruction lastInstruction = getLastInstruction(subroutinePseudocode, traversalOrder);
139                    Edges<D> previousValue = edgesMap.get(instruction);
140                    Edges<D> newValue = edgesMap.get(lastInstruction);
141                    if (!previousValue.equals(newValue)) {
142                        changed[0] = true;
143                        edgesMap.put(instruction, newValue);
144                    }
145                    continue;
146                }
147                Edges<D> previousDataValue = edgesMap.get(instruction);
148    
149                Collection<D> incomingEdgesData = Sets.newHashSet();
150    
151                for (Instruction previousInstruction : allPreviousInstructions) {
152                    Edges<D> previousData = edgesMap.get(previousInstruction);
153                    if (previousData != null) {
154                        incomingEdgesData.add(previousData.out);
155                    }
156                }
157                Edges<D> mergedData = instructionDataMergeStrategy.execute(instruction, incomingEdgesData);
158                if (!mergedData.equals(previousDataValue)) {
159                    changed[0] = true;
160                    edgesMap.put(instruction, mergedData);
161                }
162            }
163        }
164    
165        public static void traverse(
166                @NotNull Pseudocode pseudocode, TraversalOrder traversalOrder,
167                InstructionAnalyzeStrategy instructionAnalyzeStrategy) {
168    
169            List<Instruction> instructions = getInstructions(pseudocode, traversalOrder);
170            for (Instruction instruction : instructions) {
171                if (instruction instanceof LocalDeclarationInstruction) {
172                    traverse(((LocalDeclarationInstruction) instruction).getBody(), traversalOrder, instructionAnalyzeStrategy);
173                }
174                instructionAnalyzeStrategy.execute(instruction);
175            }
176        }
177    
178        public static <D> void traverse(
179                @NotNull Pseudocode pseudocode, TraversalOrder traversalOrder,
180                @NotNull Map<Instruction, Edges<D>> edgesMap,
181                @NotNull InstructionDataAnalyzeStrategy<D> instructionDataAnalyzeStrategy) {
182    
183            List<Instruction> instructions = getInstructions(pseudocode, traversalOrder);
184            for (Instruction instruction : instructions) {
185                if (instruction instanceof LocalDeclarationInstruction) {
186                    traverse(((LocalDeclarationInstruction) instruction).getBody(), traversalOrder, edgesMap,
187                             instructionDataAnalyzeStrategy);
188                }
189                Edges<D> edges = edgesMap.get(instruction);
190                instructionDataAnalyzeStrategy.execute(instruction, edges != null ? edges.in : null, edges != null ? edges.out : null);
191            }
192        }
193    
194        public interface InstructionDataMergeStrategy<D> {
195            Edges<D> execute(@NotNull Instruction instruction, @NotNull Collection<D> incomingEdgesData);
196        }
197    
198        public interface InstructionDataAnalyzeStrategy<D> {
199            void execute(@NotNull Instruction instruction, @Nullable D enterData, @Nullable D exitData);
200        }
201    
202        public interface InstructionAnalyzeStrategy {
203            void execute(@NotNull Instruction instruction);
204        }
205    
206        public static class Edges<T> {
207            public final T in;
208            public final T out;
209    
210            Edges(@NotNull T in, @NotNull T out) {
211                this.in = in;
212                this.out = out;
213            }
214    
215            public static <T> Edges<T> create(@NotNull T in, @NotNull T out) {
216                return new Edges<T>(in, out);
217            }
218    
219            @Override
220            public boolean equals(Object o) {
221                if (this == o) return true;
222                if (!(o instanceof Edges)) return false;
223    
224                Edges edges = (Edges) o;
225    
226                if (in != null ? !in.equals(edges.in) : edges.in != null) return false;
227                if (out != null ? !out.equals(edges.out) : edges.out != null) return false;
228    
229                return true;
230            }
231    
232            @Override
233            public int hashCode() {
234                int result = in != null ? in.hashCode() : 0;
235                result = 31 * result + (out != null ? out.hashCode() : 0);
236                return result;
237            }
238        }
239    }