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 }