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