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 kotlin.Function1; 022 import kotlin.Unit; 023 import org.jetbrains.annotations.NotNull; 024 import org.jetbrains.annotations.Nullable; 025 import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode; 026 import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeUtil; 027 import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction; 028 import org.jetbrains.kotlin.cfg.pseudocode.instructions.LexicalScope; 029 import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.ReadValueInstruction; 030 import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.WriteValueInstruction; 031 import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction; 032 import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.VariableDeclarationInstruction; 033 import org.jetbrains.kotlin.cfg.pseudocodeTraverser.Edges; 034 import org.jetbrains.kotlin.cfg.pseudocodeTraverser.PseudocodeTraverserPackage; 035 import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; 036 import org.jetbrains.kotlin.descriptors.VariableDescriptor; 037 import org.jetbrains.kotlin.psi.JetDeclaration; 038 import org.jetbrains.kotlin.psi.JetProperty; 039 import org.jetbrains.kotlin.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.kotlin.cfg.pseudocodeTraverser.TraversalOrder.BACKWARD; 047 import static org.jetbrains.kotlin.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 }