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