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