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 }