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}