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