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 017package org.jetbrains.jet.lang.cfg; 018 019import com.google.common.collect.Lists; 020import com.google.common.collect.Maps; 021import com.google.common.collect.Sets; 022import org.jetbrains.annotations.NotNull; 023import org.jetbrains.annotations.Nullable; 024import org.jetbrains.jet.lang.cfg.pseudocode.*; 025 026import java.util.Collection; 027import java.util.Collections; 028import java.util.List; 029import java.util.Map; 030 031import static org.jetbrains.jet.lang.cfg.PseudocodeTraverser.TraversalOrder.FORWARD; 032 033public 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}