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