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.Maps;
020 import com.google.common.collect.Sets;
021 import org.jetbrains.annotations.NotNull;
022 import org.jetbrains.annotations.Nullable;
023 import org.jetbrains.jet.lang.cfg.PseudocodeTraverser.*;
024 import org.jetbrains.jet.lang.cfg.pseudocode.*;
025 import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
026 import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
027 import org.jetbrains.jet.lang.psi.JetDeclaration;
028 import org.jetbrains.jet.lang.psi.JetProperty;
029 import org.jetbrains.jet.lang.resolve.BindingContext;
030
031 import java.util.Collection;
032 import java.util.Collections;
033 import java.util.Map;
034 import java.util.Set;
035
036 import static org.jetbrains.jet.lang.cfg.PseudocodeTraverser.LookInsideStrategy.ANALYSE_LOCAL_DECLARATIONS;
037 import static org.jetbrains.jet.lang.cfg.PseudocodeTraverser.LookInsideStrategy.SKIP_LOCAL_DECLARATIONS;
038 import static org.jetbrains.jet.lang.cfg.PseudocodeTraverser.TraversalOrder.BACKWARD;
039 import static org.jetbrains.jet.lang.cfg.PseudocodeTraverser.TraversalOrder.FORWARD;
040
041 public class PseudocodeVariablesData {
042 private final Pseudocode pseudocode;
043 private final BindingContext bindingContext;
044
045 private final Map<Pseudocode, Set<VariableDescriptor>> declaredVariablesForDeclaration = Maps.newHashMap();
046 private final Map<Pseudocode, Set<VariableDescriptor>> usedVariablesForDeclaration = Maps.newHashMap();
047
048 private Map<Instruction, Edges<Map<VariableDescriptor, VariableInitState>>> variableInitializers;
049
050 public PseudocodeVariablesData(@NotNull Pseudocode pseudocode, @NotNull BindingContext bindingContext) {
051 this.pseudocode = pseudocode;
052 this.bindingContext = bindingContext;
053 }
054
055 @NotNull
056 public Pseudocode getPseudocode() {
057 return pseudocode;
058 }
059
060 @NotNull
061 public Set<VariableDescriptor> getUsedVariables(@NotNull Pseudocode pseudocode) {
062 Set<VariableDescriptor> usedVariables = usedVariablesForDeclaration.get(pseudocode);
063 if (usedVariables == null) {
064 final Set<VariableDescriptor> result = Sets.newHashSet();
065 PseudocodeTraverser.traverse(pseudocode, FORWARD, new InstructionAnalyzeStrategy() {
066 @Override
067 public void execute(@NotNull Instruction instruction) {
068 VariableDescriptor variableDescriptor = PseudocodeUtil.extractVariableDescriptorIfAny(instruction, false,
069 bindingContext);
070 if (variableDescriptor != null) {
071 result.add(variableDescriptor);
072 }
073 }
074 });
075 usedVariables = Collections.unmodifiableSet(result);
076 usedVariablesForDeclaration.put(pseudocode, usedVariables);
077 }
078 return usedVariables;
079 }
080
081 @NotNull
082 public Set<VariableDescriptor> getDeclaredVariables(@NotNull Pseudocode pseudocode, boolean includeInsideLocalDeclarations) {
083 if (!includeInsideLocalDeclarations) {
084 return getUpperLevelDeclaredVariables(pseudocode);
085 }
086 Set<VariableDescriptor> declaredVariables = Sets.newHashSet();
087 declaredVariables.addAll(getUpperLevelDeclaredVariables(pseudocode));
088
089 for (LocalDeclarationInstruction localDeclarationInstruction : pseudocode.getLocalDeclarations()) {
090 Pseudocode localPseudocode = localDeclarationInstruction.getBody();
091 declaredVariables.addAll(getUpperLevelDeclaredVariables(localPseudocode));
092 }
093 return declaredVariables;
094 }
095
096 @NotNull
097 private Set<VariableDescriptor> getUpperLevelDeclaredVariables(@NotNull Pseudocode pseudocode) {
098 Set<VariableDescriptor> declaredVariables = declaredVariablesForDeclaration.get(pseudocode);
099 if (declaredVariables == null) {
100 declaredVariables = computeDeclaredVariablesForPseudocode(pseudocode);
101 declaredVariablesForDeclaration.put(pseudocode, declaredVariables);
102 }
103 return declaredVariables;
104 }
105
106 @NotNull
107 private Set<VariableDescriptor> computeDeclaredVariablesForPseudocode(Pseudocode pseudocode) {
108 Set<VariableDescriptor> declaredVariables = Sets.newHashSet();
109 for (Instruction instruction : pseudocode.getInstructions()) {
110 if (instruction instanceof VariableDeclarationInstruction) {
111 JetDeclaration variableDeclarationElement = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement();
112 DeclarationDescriptor descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, variableDeclarationElement);
113 if (descriptor != null) {
114 assert descriptor instanceof VariableDescriptor;
115 declaredVariables.add((VariableDescriptor) descriptor);
116 }
117 }
118 }
119 return Collections.unmodifiableSet(declaredVariables);
120 }
121
122 // variable initializers
123
124 @NotNull
125 public Map<Instruction, Edges<Map<VariableDescriptor, VariableInitState>>> getVariableInitializers() {
126 if (variableInitializers == null) {
127 variableInitializers = getVariableInitializers(pseudocode);
128 }
129
130 return variableInitializers;
131 }
132
133 @NotNull
134 private Map<Instruction, Edges<Map<VariableDescriptor, VariableInitState>>> getVariableInitializers(@NotNull Pseudocode pseudocode) {
135
136 Set<VariableDescriptor> usedVariables = getUsedVariables(pseudocode);
137 Set<VariableDescriptor> declaredVariables = getDeclaredVariables(pseudocode, false);
138 Map<VariableDescriptor, VariableInitState> initialMap = Collections.emptyMap();
139 Map<VariableDescriptor, VariableInitState> initialMapForStartInstruction = prepareInitializersMapForStartInstruction(
140 usedVariables, declaredVariables);
141
142 Map<Instruction, Edges<Map<VariableDescriptor, VariableInitState>>> variableInitializersMap = PseudocodeTraverser.collectData(
143 pseudocode, FORWARD, SKIP_LOCAL_DECLARATIONS,
144 initialMap, initialMapForStartInstruction, new PseudocodeTraverser.InstructionDataMergeStrategy<Map<VariableDescriptor, VariableInitState>>() {
145 @Override
146 public Edges<Map<VariableDescriptor, VariableInitState>> execute(
147 @NotNull Instruction instruction, @NotNull Collection<Map<VariableDescriptor, VariableInitState>> incomingEdgesData) {
148
149 Map<VariableDescriptor, VariableInitState> enterInstructionData = mergeIncomingEdgesDataForInitializers(incomingEdgesData);
150 Map<VariableDescriptor, VariableInitState> exitInstructionData =
151 addVariableInitStateFromCurrentInstructionIfAny(instruction, enterInstructionData);
152 return Edges.create(enterInstructionData, exitInstructionData);
153 }
154 });
155
156
157 for (LocalDeclarationInstruction localDeclarationInstruction : pseudocode.getLocalDeclarations()) {
158 Pseudocode localPseudocode = localDeclarationInstruction.getBody();
159 Map<Instruction, Edges<Map<VariableDescriptor, VariableInitState>>> initializersForLocalDeclaration = getVariableInitializers(localPseudocode);
160
161 for (Instruction instruction : initializersForLocalDeclaration.keySet()) {
162 //todo
163 if (!variableInitializersMap.containsKey(instruction)) {
164 variableInitializersMap.put(instruction, initializersForLocalDeclaration.get(instruction));
165 }
166 }
167 variableInitializersMap.putAll(initializersForLocalDeclaration);
168 }
169 return variableInitializersMap;
170 }
171
172 @NotNull
173 private static Map<VariableDescriptor, VariableInitState> prepareInitializersMapForStartInstruction(
174 @NotNull Collection<VariableDescriptor> usedVariables,
175 @NotNull Collection<VariableDescriptor> declaredVariables) {
176
177 Map<VariableDescriptor, VariableInitState> initialMapForStartInstruction = Maps.newHashMap();
178 VariableInitState initializedForExternalVariable = VariableInitState.create(true);
179 VariableInitState notInitializedForDeclaredVariable = VariableInitState.create(false);
180
181 for (VariableDescriptor variable : usedVariables) {
182 if (declaredVariables.contains(variable)) {
183 initialMapForStartInstruction.put(variable, notInitializedForDeclaredVariable);
184 }
185 else {
186 initialMapForStartInstruction.put(variable, initializedForExternalVariable);
187 }
188 }
189 return initialMapForStartInstruction;
190 }
191
192 @NotNull
193 private static Map<VariableDescriptor, VariableInitState> mergeIncomingEdgesDataForInitializers(
194 @NotNull Collection<Map<VariableDescriptor, VariableInitState>> incomingEdgesData) {
195
196 Set<VariableDescriptor> variablesInScope = Sets.newHashSet();
197 for (Map<VariableDescriptor, VariableInitState> edgeData : incomingEdgesData) {
198 variablesInScope.addAll(edgeData.keySet());
199 }
200
201 Map<VariableDescriptor, VariableInitState> enterInstructionData = Maps.newHashMap();
202 for (VariableDescriptor variable : variablesInScope) {
203 boolean isInitialized = true;
204 boolean isDeclared = true;
205 for (Map<VariableDescriptor, VariableInitState> edgeData : incomingEdgesData) {
206 VariableInitState initState = edgeData.get(variable);
207 if (initState != null) {
208 if (!initState.isInitialized) {
209 isInitialized = false;
210 }
211 if (!initState.isDeclared) {
212 isDeclared = false;
213 }
214 }
215 }
216 enterInstructionData.put(variable, VariableInitState.create(isInitialized, isDeclared));
217 }
218 return enterInstructionData;
219 }
220
221 @NotNull
222 private Map<VariableDescriptor, VariableInitState> addVariableInitStateFromCurrentInstructionIfAny(
223 @NotNull Instruction instruction, @NotNull Map<VariableDescriptor, VariableInitState> enterInstructionData) {
224
225 if (!(instruction instanceof WriteValueInstruction) && !(instruction instanceof VariableDeclarationInstruction)) {
226 return enterInstructionData;
227 }
228 VariableDescriptor variable = PseudocodeUtil.extractVariableDescriptorIfAny(instruction, false, bindingContext);
229 if (variable == null) {
230 return enterInstructionData;
231 }
232 Map<VariableDescriptor, VariableInitState> exitInstructionData = Maps.newHashMap(enterInstructionData);
233 if (instruction instanceof WriteValueInstruction) {
234 VariableInitState enterInitState = enterInstructionData.get(variable);
235 VariableInitState initializationAtThisElement =
236 VariableInitState.create(((WriteValueInstruction) instruction).getElement() instanceof JetProperty, enterInitState);
237 exitInstructionData.put(variable, initializationAtThisElement);
238 }
239 else { // instruction instanceof VariableDeclarationInstruction
240 VariableInitState enterInitState = enterInstructionData.get(variable);
241 if (enterInitState == null || !enterInitState.isInitialized || !enterInitState.isDeclared) {
242 boolean isInitialized = enterInitState != null && enterInitState.isInitialized;
243 VariableInitState variableDeclarationInfo = VariableInitState.create(isInitialized, true);
244 exitInstructionData.put(variable, variableDeclarationInfo);
245 }
246 }
247 return exitInstructionData;
248 }
249
250 // variable use
251
252 @NotNull
253 public Map<Instruction, Edges<Map<VariableDescriptor, VariableUseState>>> getVariableUseStatusData() {
254 Map<VariableDescriptor, VariableUseState> sinkInstructionData = Maps.newHashMap();
255 for (VariableDescriptor usedVariable : getUsedVariables(pseudocode)) {
256 sinkInstructionData.put(usedVariable, VariableUseState.UNUSED);
257 }
258 InstructionDataMergeStrategy<Map<VariableDescriptor, VariableUseState>> collectVariableUseStatusStrategy =
259 new InstructionDataMergeStrategy<Map<VariableDescriptor, VariableUseState>>() {
260 @Override
261 public Edges<Map<VariableDescriptor, VariableUseState>> execute(
262 @NotNull Instruction instruction,
263 @NotNull Collection<Map<VariableDescriptor, VariableUseState>> incomingEdgesData
264 ) {
265
266 Map<VariableDescriptor, VariableUseState> enterResult = Maps.newHashMap();
267 for (Map<VariableDescriptor, VariableUseState> edgeData : incomingEdgesData) {
268 for (Map.Entry<VariableDescriptor, VariableUseState> entry : edgeData.entrySet()) {
269 VariableDescriptor variableDescriptor = entry.getKey();
270 VariableUseState variableUseState = entry.getValue();
271 enterResult.put(variableDescriptor, variableUseState.merge(enterResult.get(variableDescriptor)));
272 }
273 }
274 VariableDescriptor variableDescriptor = PseudocodeUtil.extractVariableDescriptorIfAny(instruction, true,
275 bindingContext);
276 if (variableDescriptor == null ||
277 (!(instruction instanceof ReadValueInstruction) && !(instruction instanceof WriteValueInstruction))) {
278 return Edges.create(enterResult, enterResult);
279 }
280 Map<VariableDescriptor, VariableUseState> exitResult = Maps.newHashMap(enterResult);
281 if (instruction instanceof ReadValueInstruction) {
282 exitResult.put(variableDescriptor, VariableUseState.LAST_READ);
283 }
284 else { //instruction instanceof WriteValueInstruction
285 VariableUseState variableUseState = enterResult.get(variableDescriptor);
286 if (variableUseState == null) {
287 variableUseState = VariableUseState.UNUSED;
288 }
289 switch (variableUseState) {
290 case UNUSED:
291 case ONLY_WRITTEN_NEVER_READ:
292 exitResult.put(variableDescriptor, VariableUseState.ONLY_WRITTEN_NEVER_READ);
293 break;
294 case LAST_WRITTEN:
295 case LAST_READ:
296 exitResult.put(variableDescriptor, VariableUseState.LAST_WRITTEN);
297 }
298 }
299 return Edges.create(enterResult, exitResult);
300 }
301 };
302 return PseudocodeTraverser.collectData(pseudocode, BACKWARD, ANALYSE_LOCAL_DECLARATIONS,
303 Collections.<VariableDescriptor, VariableUseState>emptyMap(),
304 sinkInstructionData, collectVariableUseStatusStrategy);
305 }
306
307 public static class VariableInitState {
308 public final boolean isInitialized;
309 public final boolean isDeclared;
310
311 private VariableInitState(boolean isInitialized, boolean isDeclared) {
312 this.isInitialized = isInitialized;
313 this.isDeclared = isDeclared;
314 }
315
316 private static final VariableInitState VS_TT = new VariableInitState(true, true);
317 private static final VariableInitState VS_TF = new VariableInitState(true, false);
318 private static final VariableInitState VS_FT = new VariableInitState(false, true);
319 private static final VariableInitState VS_FF = new VariableInitState(false, false);
320
321
322 private static VariableInitState create(boolean isInitialized, boolean isDeclared) {
323 if (isInitialized) {
324 if (isDeclared) return VS_TT;
325 return VS_TF;
326 }
327 if (isDeclared) return VS_FT;
328 return VS_FF;
329 }
330
331 private static VariableInitState create(boolean isInitialized) {
332 return create(isInitialized, false);
333 }
334
335 private static VariableInitState create(boolean isDeclaredHere, @Nullable VariableInitState mergedEdgesData) {
336 return create(true, isDeclaredHere || (mergedEdgesData != null && mergedEdgesData.isDeclared));
337 }
338 }
339
340 public static enum VariableUseState {
341 LAST_READ(3),
342 LAST_WRITTEN(2),
343 ONLY_WRITTEN_NEVER_READ(1),
344 UNUSED(0);
345
346 private final int importance;
347
348 VariableUseState(int importance) {
349 this.importance = importance;
350 }
351
352 private VariableUseState merge(@Nullable VariableUseState variableUseState) {
353 if (variableUseState == null || importance > variableUseState.importance) return this;
354 return variableUseState;
355 }
356
357 public static boolean isUsed(@Nullable VariableUseState variableUseState) {
358 return variableUseState != null && variableUseState != UNUSED;
359 }
360 }
361 }