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