diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java index a30b1de310f..a60398498ff 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/JetFlowInformationProvider.java @@ -19,12 +19,12 @@ package org.jetbrains.jet.lang.cfg; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; -import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiElement; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.cfg.data.*; import org.jetbrains.jet.lang.cfg.pseudocode.*; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.diagnostics.Errors; @@ -50,12 +50,14 @@ import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE; public class JetFlowInformationProvider { private final Map pseudocodeMap; + private final Map pseudocodeDataMap; private BindingTrace trace; public JetFlowInformationProvider(@NotNull JetDeclaration declaration, @NotNull final JetExpression bodyExpression, @NotNull JetControlFlowDataTraceFactory flowDataTraceFactory, @NotNull BindingTrace trace) { this.trace = trace; final JetPseudocodeTrace pseudocodeTrace = flowDataTraceFactory.createTrace(declaration); - pseudocodeMap = new HashMap(); + pseudocodeMap = new LinkedHashMap(); + pseudocodeDataMap = new HashMap(); final Map representativeInstructions = new HashMap(); final Map loopInfo = Maps.newHashMap(); JetPseudocodeTrace wrappedTrace = new JetPseudocodeTrace() { @@ -81,7 +83,9 @@ public class JetFlowInformationProvider { @Override public void close() { pseudocodeTrace.close(); - for (Pseudocode pseudocode : pseudocodeMap.values()) { + List values = Lists.newArrayList(pseudocodeMap.values()); + Collections.reverse(values); + for (Pseudocode pseudocode : values) { pseudocode.postProcess(); } } @@ -91,6 +95,17 @@ public class JetFlowInformationProvider { wrappedTrace.close(); } + private PseudocodeData getPseudocodeData(@NotNull JetElement element) { + PseudocodeData pseudocodeData = pseudocodeDataMap.get(element); + if (pseudocodeData == null) { + Pseudocode pseudocode = pseudocodeMap.get(element); + assert pseudocode != null; + pseudocodeData = new PseudocodeData(pseudocode, trace); + pseudocodeDataMap.put(element, pseudocodeData); + } + return pseudocodeData; + } + private void collectReturnExpressions(@NotNull JetElement subroutine, @NotNull final Collection returnedExpressions) { Pseudocode pseudocode = pseudocodeMap.get(subroutine); assert pseudocode != null; @@ -214,63 +229,49 @@ public class JetFlowInformationProvider { // Uninitialized variables analysis public void markUninitializedVariables(@NotNull JetElement subroutine, final boolean processLocalDeclaration) { - final Pseudocode pseudocode = pseudocodeMap.get(subroutine); - assert pseudocode != null; - - JetControlFlowGraphTraverser> traverser = JetControlFlowGraphTraverser.create(pseudocode, false, true); - - Collection usedVariables = collectUsedVariables(pseudocode); - final Collection declaredVariables = collectDeclaredVariables(subroutine); - Map initialMapForStartInstruction = prepareInitialMapForStartInstruction(usedVariables, declaredVariables); - - traverser.collectInformationFromInstructionGraph(Collections.emptyMap(), initialMapForStartInstruction, - new JetControlFlowGraphTraverser.InstructionDataMergeStrategy>() { - @Override - public Pair, Map> execute( - @NotNull Instruction instruction, - @NotNull Collection> incomingEdgesData) { - - Map enterInstructionData = mergeIncomingEdgesData(incomingEdgesData); - Map exitInstructionData = addVariableInitializerFromCurrentInstructionIfAny(instruction, enterInstructionData); - return Pair.create(enterInstructionData, exitInstructionData); - } - }); - final Collection varWithUninitializedErrorGenerated = Sets.newHashSet(); final Collection varWithValReassignErrorGenerated = Sets.newHashSet(); final boolean processClassOrObject = subroutine instanceof JetClassOrObject; - traverser.traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy>() { + + final PseudocodeData pseudocodeData = getPseudocodeData(subroutine); + pseudocodeData.traverseInstructionsGraph(true, true, new PseudocodeData.TraverseInstructionGraphStrategy() { @Override - public void execute(@NotNull Instruction instruction, @Nullable Map enterData, @Nullable Map exitData) { - assert enterData != null && exitData != null; - VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, true); + public void execute(@NotNull Instruction instruction, @NotNull DeclarationData declarationData, @NotNull InstructionData instructionData) { + //todo move to util + VariableDescriptor variableDescriptor = pseudocodeData.extractVariableDescriptorIfAny(instruction, true); if (variableDescriptor == null) return; + if (!(instruction instanceof ReadValueInstruction) && !(instruction instanceof WriteValueInstruction)) return; + InstructionData.EdgesData variableInitializers = instructionData.getInitializersMap().get( + variableDescriptor); + if (variableInitializers == null) return; if (instruction instanceof ReadValueInstruction) { JetElement element = ((ReadValueInstruction) instruction).getElement(); boolean error = checkBackingField(variableDescriptor, element); - if (!error && declaredVariables.contains(variableDescriptor)) { - checkIsInitialized(variableDescriptor, element, exitData.get(variableDescriptor), varWithUninitializedErrorGenerated); + if (!error && declarationData.declaredVariables.contains(variableDescriptor)) { + checkIsInitialized(variableDescriptor, element, variableInitializers.getOut(), varWithUninitializedErrorGenerated); } + return; } - else if (instruction instanceof WriteValueInstruction) { - JetElement element = ((WriteValueInstruction) instruction).getlValue(); - boolean error = checkBackingField(variableDescriptor, element); - if (!(element instanceof JetExpression)) return; - if (!error && !processLocalDeclaration) { // error has been generated before, while processing outer function of this local declaration - error = checkValReassignment(variableDescriptor, (JetExpression) element, enterData.get(variableDescriptor), varWithValReassignErrorGenerated); - } - if (!error && processClassOrObject) { - error = checkAssignmentBeforeDeclaration(variableDescriptor, (JetExpression) element, enterData.get(variableDescriptor), exitData.get(variableDescriptor)); - } - if (!error && processClassOrObject) { - checkInitializationUsingBackingField(variableDescriptor, (JetExpression) element, enterData.get(variableDescriptor), exitData.get(variableDescriptor)); - } + JetElement element = ((WriteValueInstruction) instruction).getlValue(); + boolean error = checkBackingField(variableDescriptor, element); + if (!(element instanceof JetExpression)) return; + if (!error && !processLocalDeclaration) { // error has been generated before, while processing outer function of this local declaration + error = checkValReassignment(variableDescriptor, (JetExpression) element, variableInitializers.getIn(), varWithValReassignErrorGenerated); + } + if (!error && processClassOrObject) { + error = checkAssignmentBeforeDeclaration(variableDescriptor, (JetExpression) element, variableInitializers.getIn(), variableInitializers.getOut()); + } + if (!error && processClassOrObject) { + checkInitializationUsingBackingField(variableDescriptor, (JetExpression) element, variableInitializers.getIn(), variableInitializers.getOut()); } } }); - recordInitializedVariables(declaredVariables, traverser.getResultInfo()); - analyzeLocalDeclarations(pseudocode, processLocalDeclaration); + Pseudocode pseudocode = pseudocodeData.getPseudocode(); + recordInitializedVariables(pseudocodeData.getDeclarationData(pseudocode), pseudocodeData.getResultInfo(pseudocode)); + for (Pseudocode localPseudocode : pseudocode.getLocalDeclarations()) { + recordInitializedVariables(pseudocodeData.getDeclarationData(localPseudocode), pseudocodeData.getResultInfo(localPseudocode)); + } } private void checkIsInitialized(@NotNull VariableDescriptor variableDescriptor, @@ -436,12 +437,12 @@ public class JetFlowInformationProvider { return false; } - private void recordInitializedVariables(Collection declaredVariables, Map resultInfo) { - for (Map.Entry entry : resultInfo.entrySet()) { - VariableDescriptor variable = entry.getKey(); - if (variable instanceof PropertyDescriptor && declaredVariables.contains(variable)) { - VariableInitializers initializers = entry.getValue(); - trace.record(BindingContext.IS_INITIALIZED, (PropertyDescriptor) variable, initializers.isInitialized()); + private void recordInitializedVariables(DeclarationData declarationData, InstructionData instructionData) { + for (VariableDescriptor variable : declarationData.usedVariables) { + if (variable instanceof PropertyDescriptor && declarationData.declaredVariables.contains(variable)) { + InstructionData.EdgesData variableInitializers = instructionData.getInitializersMap().get(variable); + if (variableInitializers == null) return; + trace.record(BindingContext.IS_INITIALIZED, (PropertyDescriptor) variable, variableInitializers.getIn().isInitialized()); } } } @@ -455,161 +456,33 @@ public class JetFlowInformationProvider { } } - private Map addVariableInitializerFromCurrentInstructionIfAny(Instruction instruction, Map enterInstructionData) { - Map exitInstructionData = Maps.newHashMap(enterInstructionData); - if (instruction instanceof WriteValueInstruction) { - VariableDescriptor variable = extractVariableDescriptorIfAny(instruction, false); - VariableInitializers enterInitializers = enterInstructionData.get(variable); - VariableInitializers initializationAtThisElement = new VariableInitializers(((WriteValueInstruction) instruction).getElement(), enterInitializers); - exitInstructionData.put(variable, initializationAtThisElement); - } - else if (instruction instanceof VariableDeclarationInstruction) { - VariableDescriptor variable = extractVariableDescriptorIfAny(instruction, false); - VariableInitializers enterInitializers = enterInstructionData.get(variable); - if (enterInitializers == null || !enterInitializers.isInitialized() || !enterInitializers.isDeclared()) { - JetElement element = ((VariableDeclarationInstruction) instruction).getElement(); - if (element instanceof JetProperty) { - JetProperty property = (JetProperty) element; - if (property.getInitializer() == null) { - boolean isInitialized = enterInitializers != null && enterInitializers.isInitialized(); - VariableInitializers variableDeclarationInfo = new VariableInitializers(isInitialized, true); - exitInstructionData.put(variable, variableDeclarationInfo); - } - } - } - } - return exitInstructionData; - } - - private Map mergeIncomingEdgesData(Collection> incomingEdgesData) { - Set variablesInScope = Sets.newHashSet(); - for (Map edgeData : incomingEdgesData) { - variablesInScope.addAll(edgeData.keySet()); - } - - Map enterInstructionData = Maps.newHashMap(); - for (VariableDescriptor variable : variablesInScope) { - Set edgesDataForVariable = Sets.newHashSet(); - for (Map edgeData : incomingEdgesData) { - VariableInitializers initializers = edgeData.get(variable); - if (initializers != null) { - edgesDataForVariable.add(initializers); - } - } - enterInstructionData.put(variable, new VariableInitializers(edgesDataForVariable)); - } - return enterInstructionData; - } - - private Map prepareInitialMapForStartInstruction(Collection usedVariables, Collection declaredVariables) { - Map initialMapForStartInstruction = Maps.newHashMap(); - VariableInitializers isInitializedForExternalVariable = new VariableInitializers(true); - VariableInitializers isNotInitializedForDeclaredVariable = new VariableInitializers(false); - - for (VariableDescriptor variable : usedVariables) { - if (declaredVariables.contains(variable)) { - initialMapForStartInstruction.put(variable, isNotInitializedForDeclaredVariable); - } - else { - initialMapForStartInstruction.put(variable, isInitializedForExternalVariable); - } - } - return initialMapForStartInstruction; - } - -//////////////////////////////////////////////////////////////////////////////// - - public void markNotOnlyInvokedFunctionVariables(@NotNull JetElement subroutine, List variables) { - final List functionVariables = Lists.newArrayList(); - for (VariableDescriptor variable : variables) { - if (JetStandardClasses.isFunctionType(variable.getReturnType())) { - functionVariables.add(variable); - } - } - - Pseudocode pseudocode = pseudocodeMap.get(subroutine); - assert pseudocode != null; - - JetControlFlowGraphTraverser.create(pseudocode, true, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy() { - @Override - public void execute(@NotNull Instruction instruction, Void enterData, Void exitData) { - if (instruction instanceof ReadValueInstruction) { - VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false); - if (variableDescriptor != null && functionVariables.contains(variableDescriptor)) { - //check that we only invoke this variable - JetElement element = ((ReadValueInstruction) instruction).getElement(); - if (element instanceof JetSimpleNameExpression && !(element.getParent() instanceof JetCallExpression)) { - trace.report(Errors.FUNCTION_PARAMETERS_OF_INLINE_FUNCTION.on((JetSimpleNameExpression) element, variableDescriptor)); - } - } - } - } - }); - } - //////////////////////////////////////////////////////////////////////////////// // "Unused variable" & "unused value" analyses public void markUnusedVariables(@NotNull JetElement subroutine) { - Pseudocode pseudocode = pseudocodeMap.get(subroutine); - assert pseudocode != null; - JetControlFlowGraphTraverser> traverser = JetControlFlowGraphTraverser.create(pseudocode, true, false); - final Collection declaredVariables = collectDeclaredVariables(subroutine); - Map sinkInstructionData = Maps.newHashMap(); - traverser.collectInformationFromInstructionGraph(Collections.emptyMap(), sinkInstructionData, new JetControlFlowGraphTraverser.InstructionDataMergeStrategy>() { + final PseudocodeData pseudocodeData = getPseudocodeData(subroutine); + pseudocodeData.traverseInstructionsGraph(true, false, new PseudocodeData.TraverseInstructionGraphStrategy() { @Override - public Pair, Map> execute(@NotNull Instruction instruction, @NotNull Collection> incomingEdgesData) { - Map enterResult = Maps.newHashMap(); - for (Map edgeData : incomingEdgesData) { - for (Map.Entry entry : edgeData.entrySet()) { - VariableDescriptor variableDescriptor = entry.getKey(); - VariableStatus variableStatus = entry.getValue(); - enterResult.put(variableDescriptor, variableStatus.merge(enterResult.get(variableDescriptor))); - } - } - Map exitResult = Maps.newHashMap(enterResult); - VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, true); - if (variableDescriptor != null) { - if (instruction instanceof ReadValueInstruction) { - exitResult.put(variableDescriptor, VariableStatus.READ); - } - else if (instruction instanceof WriteValueInstruction) { - VariableStatus variableStatus = enterResult.get(variableDescriptor); - if (variableStatus == null) variableStatus = VariableStatus.UNUSED; - switch(variableStatus) { - case UNUSED: - case ONLY_WRITTEN: - exitResult.put(variableDescriptor, VariableStatus.ONLY_WRITTEN); - break; - case WRITTEN: - case READ: - exitResult.put(variableDescriptor, VariableStatus.WRITTEN); - } - } - } - return new Pair, Map>(enterResult, exitResult); - } - }); - traverser.traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy>() { - @Override - public void execute(@NotNull Instruction instruction, @Nullable Map enterData, @Nullable Map exitData) { - assert enterData != null && exitData != null; - VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false); - if (variableDescriptor == null || !declaredVariables.contains(variableDescriptor) || + public void execute(@NotNull Instruction instruction, @NotNull DeclarationData declarationData, @NotNull InstructionData instructionData) { + VariableDescriptor variableDescriptor = pseudocodeData.extractVariableDescriptorIfAny(instruction, false); + if (variableDescriptor == null || !declarationData.declaredVariables.contains(variableDescriptor) || !DescriptorUtils.isLocal(variableDescriptor.getContainingDeclaration(), variableDescriptor)) return; - VariableStatus variableStatus = enterData.get(variableDescriptor); + InstructionData.EdgesData statusEdgesData = instructionData.getUseStatusMap().get(variableDescriptor); + VariableUseStatus variableUseStatus = statusEdgesData != null ? statusEdgesData.getIn() : null; if (instruction instanceof WriteValueInstruction) { if (trace.get(CAPTURED_IN_CLOSURE, variableDescriptor)) return; JetElement element = ((WriteValueInstruction) instruction).getElement(); - if (variableStatus != VariableStatus.READ) { - if (element instanceof JetBinaryExpression && ((JetBinaryExpression) element).getOperationToken() == JetTokens.EQ) { + if (variableUseStatus != VariableUseStatus.LAST_READ) { + if (element instanceof JetBinaryExpression && + ((JetBinaryExpression) element).getOperationToken() == JetTokens.EQ) { JetExpression right = ((JetBinaryExpression) element).getRight(); if (right != null) { trace.report(Errors.UNUSED_VALUE.on(right, right, variableDescriptor)); } } else if (element instanceof JetPostfixExpression) { - IElementType operationToken = ((JetPostfixExpression) element).getOperationReference().getReferencedNameElementType(); + IElementType operationToken = + ((JetPostfixExpression) element).getOperationReference().getReferencedNameElementType(); if (operationToken == JetTokens.PLUSPLUS || operationToken == JetTokens.MINUSMINUS) { trace.report(Errors.UNUSED_CHANGED_VALUE.on(element, element)); } @@ -621,55 +494,48 @@ public class JetFlowInformationProvider { if (element instanceof JetNamedDeclaration) { PsiElement nameIdentifier = ((JetNamedDeclaration) element).getNameIdentifier(); if (nameIdentifier == null) return; - if (variableStatus == null || variableStatus == VariableStatus.UNUSED) { + if (variableUseStatus == null || variableUseStatus == VariableUseStatus.UNUSED) { if (element instanceof JetProperty) { trace.report(Errors.UNUSED_VARIABLE.on((JetProperty) element, variableDescriptor)); } else if (element instanceof JetParameter) { PsiElement psiElement = element.getParent().getParent(); if (psiElement instanceof JetFunction) { - boolean isMain = (psiElement instanceof JetNamedFunction) && JetMainDetector.isMain((JetNamedFunction) psiElement); - DeclarationDescriptor descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, psiElement); - assert descriptor instanceof FunctionDescriptor; + boolean isMain = (psiElement instanceof JetNamedFunction) && + JetMainDetector.isMain((JetNamedFunction) psiElement); + //todo + if (psiElement instanceof JetFunctionLiteral) { + //psiElement = psiElement.getParent(); + return; + } + DeclarationDescriptor descriptor = + trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, psiElement); + assert descriptor instanceof FunctionDescriptor : psiElement.getText(); FunctionDescriptor functionDescriptor = (FunctionDescriptor) descriptor; - if (!isMain && !functionDescriptor.getModality().isOverridable() && functionDescriptor.getOverriddenDescriptors().isEmpty()) { + if (!isMain && + !functionDescriptor.getModality().isOverridable() && + functionDescriptor.getOverriddenDescriptors().isEmpty()) { trace.report(Errors.UNUSED_PARAMETER.on((JetParameter) element, variableDescriptor)); } } } } - else if (variableStatus == VariableStatus.ONLY_WRITTEN && element instanceof JetProperty) { - trace.report(Errors.ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE.on((JetNamedDeclaration) element, variableDescriptor)); + else if (variableUseStatus == VariableUseStatus.ONLY_WRITTEN_NEVER_READ && element instanceof JetProperty) { + trace.report(Errors.ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE + .on((JetNamedDeclaration) element, variableDescriptor)); } - else if (variableStatus == VariableStatus.WRITTEN && element instanceof JetProperty) { + else if (variableUseStatus == VariableUseStatus.LAST_WRITTEN && element instanceof JetProperty) { JetExpression initializer = ((JetProperty) element).getInitializer(); if (initializer != null) { trace.report(Errors.VARIABLE_WITH_REDUNDANT_INITIALIZER.on(initializer, variableDescriptor)); } } } - } + } }); - } - private static enum VariableStatus { - READ(3), - WRITTEN(2), - ONLY_WRITTEN(1), - UNUSED(0); - - private int importance; - - private VariableStatus(int importance) { - this.importance = importance; - } - - public VariableStatus merge(@Nullable VariableStatus variableStatus) { - if (variableStatus == null || importance > variableStatus.importance) return this; - return variableStatus; - } } //////////////////////////////////////////////////////////////////////////////// @@ -704,133 +570,4 @@ public class JetFlowInformationProvider { } }); } - -//////////////////////////////////////////////////////////////////////////////// -// Util methods 7 - - @Nullable - private VariableDescriptor extractVariableDescriptorIfAny(Instruction instruction, boolean onlyReference) { - JetElement element = null; - if (instruction instanceof ReadValueInstruction) { - element = ((ReadValueInstruction) instruction).getElement(); - } - else if (instruction instanceof WriteValueInstruction) { - element = ((WriteValueInstruction) instruction).getlValue(); - } - else if (instruction instanceof VariableDeclarationInstruction) { - element = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement(); - } - return BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), element, onlyReference); - } - - private Collection collectUsedVariables(Pseudocode pseudocode) { - final Set usedVariables = Sets.newHashSet(); - JetControlFlowGraphTraverser.create(pseudocode, true, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy() { - @Override - public void execute(@NotNull Instruction instruction, Void enterData, Void exitData) { - VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false); - if (variableDescriptor != null) { - usedVariables.add(variableDescriptor); - } - } - }); - return usedVariables; - } - - private Collection collectDeclaredVariables(JetElement element) { - final Pseudocode pseudocode = pseudocodeMap.get(element); - assert pseudocode != null; - - final Set declaredVariables = Sets.newHashSet(); - JetControlFlowGraphTraverser.create(pseudocode, false, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy() { - @Override - public void execute(@NotNull Instruction instruction, @Nullable Void enterData, @Nullable Void exitData) { - if (instruction instanceof VariableDeclarationInstruction) { - JetDeclaration variableDeclarationElement = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement(); - DeclarationDescriptor descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, variableDeclarationElement); - if (descriptor != null) { - assert descriptor instanceof VariableDescriptor; - declaredVariables.add((VariableDescriptor) descriptor); - } - } - } - }); - return declaredVariables; - } - -//////////////////////////////////////////////////////////////////////////////// -// Local class for uninitialized variables analysis - - private static class VariableInitializers { - private Set possibleLocalInitializers = Sets.newHashSet(); - private boolean isInitialized; - private boolean isDeclared; - - public VariableInitializers(boolean isInitialized) { - this(isInitialized, false); - } - - public VariableInitializers(boolean isInitialized, boolean isDeclared) { - this.isInitialized = isInitialized; - this.isDeclared = isDeclared; - } - - public VariableInitializers(JetElement element, @Nullable VariableInitializers previous) { - isInitialized = true; - isDeclared = element instanceof JetProperty || (previous != null && previous.isDeclared()); - possibleLocalInitializers.add(element); - } - - public VariableInitializers(Set edgesData) { - isInitialized = true; - isDeclared = true; - for (VariableInitializers edgeData : edgesData) { - if (!edgeData.isInitialized) { - isInitialized = false; - } - if (!edgeData.isDeclared) { - isDeclared = false; - } - possibleLocalInitializers.addAll(edgeData.possibleLocalInitializers); - } - } - - public Set getPossibleLocalInitializers() { - return possibleLocalInitializers; - } - - public boolean isInitialized() { - return isInitialized; - } - - public boolean isDeclared() { - return isDeclared; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof VariableInitializers)) return false; - - VariableInitializers that = (VariableInitializers) o; - - if (isDeclared != that.isDeclared) return false; - if (isInitialized != that.isInitialized) return false; - if (possibleLocalInitializers != null - ? !possibleLocalInitializers.equals(that.possibleLocalInitializers) - : that.possibleLocalInitializers != null) { - return false; - } - - return true; - } - - @Override - public int hashCode() { - int result = possibleLocalInitializers != null ? possibleLocalInitializers.hashCode() : 0; - result = 31 * result + (isInitialized ? 1 : 0); - result = 31 * result + (isDeclared ? 1 : 0); - return result; - } - } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/data/DeclarationData.java b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/data/DeclarationData.java new file mode 100644 index 00000000000..f22d47136bc --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/data/DeclarationData.java @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.lang.cfg.data; + +import org.jetbrains.jet.lang.descriptors.VariableDescriptor; +import org.jetbrains.jet.lang.psi.JetElement; + +import java.util.Set; + +/** + * @author svtk + */ +public class DeclarationData { + public final JetElement element; + public final PseudocodeData pseudocodeData; + public final Set declaredVariables; + public final Set usedVariables; + + public DeclarationData(JetElement element, + PseudocodeData data, + Set declaredVariables, + Set usedVariables) { + this.element = element; + pseudocodeData = data; + this.declaredVariables = declaredVariables; + this.usedVariables = usedVariables; + } +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/data/InstructionData.java b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/data/InstructionData.java new file mode 100644 index 00000000000..ec77df1997d --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/data/InstructionData.java @@ -0,0 +1,92 @@ +/* + * Copyright 2010-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.lang.cfg.data; + +import com.google.common.collect.Maps; +import com.intellij.openapi.util.Pair; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.cfg.pseudocode.Instruction; +import org.jetbrains.jet.lang.descriptors.VariableDescriptor; + +import java.util.Map; + +/** + * @author svtk + */ +public class InstructionData { + public final Instruction instruction; + public final PseudocodeData pseudocodeData; + + private final Map> initializersMap = Maps.newHashMap(); + private final Map> useStatusMap = Maps.newHashMap(); + + public InstructionData(@NotNull PseudocodeData data, @NotNull Instruction instruction, + @NotNull Pair, Map> pairOfVariableInitializersMap, + @NotNull Pair, Map> pairOfVariableStatusMap) { + pseudocodeData = data; + this.instruction = instruction; + + for (Map.Entry entry : pairOfVariableInitializersMap.second.entrySet()) { + VariableDescriptor variableDescriptor = entry.getKey(); + VariableInitializers in = pairOfVariableInitializersMap.first.get(variableDescriptor); + VariableInitializers out = entry.getValue(); + initializersMap.put(variableDescriptor, EdgesData.create(in, out)); + } + + for (Map.Entry entry : pairOfVariableStatusMap.second.entrySet()) { + VariableDescriptor variableDescriptor = entry.getKey(); + VariableUseStatus in = pairOfVariableStatusMap.first.get(variableDescriptor); + VariableUseStatus out = entry.getValue(); + if (in == null || out == null) continue; + useStatusMap.put(variableDescriptor, EdgesData.create(in, out)); + } + } + + @NotNull + public Map> getInitializersMap() { + return initializersMap; + } + + @NotNull + public Map> getUseStatusMap() { + return useStatusMap; + } + + public static class EdgesData { + private final T in; + private final T out; + + private EdgesData(@NotNull T in, @NotNull T out) { + this.in = in; + this.out = out; + } + + private static EdgesData create(@NotNull T in, @NotNull T out) { + return new EdgesData(in, out); + } + + @NotNull + public T getIn() { + return in; + } + + @NotNull + public T getOut() { + return out; + } + } +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/data/PseudocodeData.java b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/data/PseudocodeData.java new file mode 100644 index 00000000000..d78011e47a4 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/data/PseudocodeData.java @@ -0,0 +1,331 @@ +/* + * Copyright 2010-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.lang.cfg.data; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.intellij.openapi.util.Pair; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.cfg.JetControlFlowGraphTraverser; +import org.jetbrains.jet.lang.cfg.pseudocode.*; +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.descriptors.VariableDescriptor; +import org.jetbrains.jet.lang.psi.JetDeclaration; +import org.jetbrains.jet.lang.psi.JetElement; +import org.jetbrains.jet.lang.psi.JetProperty; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.BindingContextUtils; +import org.jetbrains.jet.lang.resolve.BindingTrace; + +import java.util.*; + +/** + * @author svtk + */ +public class PseudocodeData { + private final Pseudocode pseudocode; + private final Map instructionDataMap = Maps.newLinkedHashMap(); + private final Map declarationDataMap = Maps.newLinkedHashMap(); + private final BindingTrace trace; + + public PseudocodeData(@NotNull Pseudocode pseudocode, @NotNull BindingTrace trace) { + this.pseudocode = pseudocode; + this.trace = trace; + collectDeclarationData(pseudocode); + + DeclarationData declarationData = declarationDataMap.get(pseudocode); + + final Map, Map>> variableInitializersMap = + collectVariableInitializers(pseudocode, declarationData); + final Map, Map>> variableStatusMap = + collectVariableStatusData(); + + JetControlFlowGraphTraverser.create(pseudocode, true, true).traverseAndAnalyzeInstructionGraph( + new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy() { + @Override + public void execute(@NotNull Instruction instruction, Void enterData, Void exitData) { + instructionDataMap.put(instruction, + new InstructionData(PseudocodeData.this, instruction, variableInitializersMap.get(instruction), + variableStatusMap.get(instruction))); + } + }); + } + + @NotNull + public Pseudocode getPseudocode() { + return pseudocode; + } + + @NotNull + public Map getInstructionDataMap() { + return instructionDataMap; + } + + @NotNull + public DeclarationData getDeclarationData(Pseudocode pseudocode) { + return declarationDataMap.get(pseudocode); + } + + @NotNull + public InstructionData getResultInfo(Pseudocode pseudocode) { + return instructionDataMap.get(pseudocode.getExitInstruction()); + } + + public void traverseInstructionsGraph(boolean lookInside, boolean straightDirection, + @NotNull TraverseInstructionGraphStrategy traverseInstructionGraphStrategy) { + traverseInstructionsGraph(pseudocode, lookInside, straightDirection, traverseInstructionGraphStrategy); + } + + private void traverseInstructionsGraph(@NotNull Pseudocode pseudocode, + boolean lookInside, + boolean straightDirection, + @NotNull TraverseInstructionGraphStrategy traverseInstructionGraphStrategy) { + List instructions = pseudocode.getInstructions(); + if (!straightDirection) { + instructions = Lists.newArrayList(instructions); + Collections.reverse(instructions); + } + for (Instruction instruction : instructions) { + if (lookInside && instruction instanceof LocalDeclarationInstruction) { + traverseInstructionsGraph(((LocalDeclarationInstruction) instruction).getBody(), lookInside, straightDirection, + traverseInstructionGraphStrategy + ); + } + traverseInstructionGraphStrategy.execute(instruction, declarationDataMap.get(pseudocode), instructionDataMap.get(instruction)); + } + } + + public interface TraverseInstructionGraphStrategy { + void execute(@NotNull Instruction instruction, @NotNull DeclarationData declarationData, @NotNull InstructionData instructionData); + } + + private void collectDeclarationData(Pseudocode pseudocode) { + DeclarationData declarationData = new DeclarationData(pseudocode.getCorrespondingElement(), this, collectDeclaredVariables(pseudocode), collectUsedVariables(pseudocode)); + declarationDataMap.put(pseudocode, declarationData); + + for (Pseudocode localPseudocode : pseudocode.getLocalDeclarations()) { + collectDeclarationData(localPseudocode); + } + } + + private Set collectUsedVariables(@NotNull Pseudocode pseudocode) { + final Set usedVariables = Sets.newHashSet(); + JetControlFlowGraphTraverser.create(pseudocode, true, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy() { + @Override + public void execute(@NotNull Instruction instruction, @Nullable Void enterData, @Nullable Void exitData) { + VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, false); + if (variableDescriptor != null) { + usedVariables.add(variableDescriptor); + } + } + }); + return usedVariables; + } + + private Set collectDeclaredVariables(@NotNull Pseudocode pseudocode) { + final Set declaredVariables = Sets.newHashSet(); + JetControlFlowGraphTraverser.create(pseudocode, false, true).traverseAndAnalyzeInstructionGraph(new JetControlFlowGraphTraverser.InstructionDataAnalyzeStrategy() { + @Override + public void execute(@NotNull Instruction instruction, @Nullable Void enterData, @Nullable Void exitData) { + if (instruction instanceof VariableDeclarationInstruction) { + JetDeclaration variableDeclarationElement = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement(); + DeclarationDescriptor descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, variableDeclarationElement); + if (descriptor != null) { + assert descriptor instanceof VariableDescriptor; + declaredVariables.add((VariableDescriptor) descriptor); + } + } + } + }); + return declaredVariables; + } + +// variable initializers + + private Map, Map>> collectVariableInitializers( + Pseudocode pseudocode, DeclarationData data) { + + JetControlFlowGraphTraverser> traverser = JetControlFlowGraphTraverser.create(pseudocode, false, true); + + final Map initialMapForStartInstruction = prepareInitialMapForStartInstruction(data.usedVariables, data.declaredVariables); + + traverser.collectInformationFromInstructionGraph(Collections.emptyMap(), initialMapForStartInstruction, + new JetControlFlowGraphTraverser.InstructionDataMergeStrategy>() { + @Override + public Pair, Map> execute( + @NotNull Instruction instruction, + @NotNull Collection> incomingEdgesData) { + + Map enterInstructionData = mergeIncomingEdgesData(incomingEdgesData); + Map exitInstructionData = addVariableInitializerFromCurrentInstructionIfAny(instruction, enterInstructionData); + return Pair.create(enterInstructionData, exitInstructionData); + } + }); + + Map, Map>> result = + traverser.getDataMap(); + for (Pseudocode localPseudocode : pseudocode.getLocalDeclarations()) { + Map, Map>> initializersForLocalDeclaration = + collectVariableInitializers(localPseudocode, declarationDataMap.get(localPseudocode)); + + for (Instruction instruction : initializersForLocalDeclaration.keySet()) { + //todo + if (!result.containsKey(instruction)) { + result.put(instruction, initializersForLocalDeclaration.get(instruction)); + } + } + result.putAll(initializersForLocalDeclaration); + } + + return result; + } + + private Map prepareInitialMapForStartInstruction(Collection usedVariables, Collection declaredVariables) { + Map initialMapForStartInstruction = Maps.newHashMap(); + VariableInitializers isInitializedForExternalVariable = new VariableInitializers(true); + VariableInitializers isNotInitializedForDeclaredVariable = new VariableInitializers(false); + + for (VariableDescriptor variable : usedVariables) { + if (declaredVariables.contains(variable)) { + initialMapForStartInstruction.put(variable, isNotInitializedForDeclaredVariable); + } + else { + initialMapForStartInstruction.put(variable, isInitializedForExternalVariable); + } + } + return initialMapForStartInstruction; + } + + private Map mergeIncomingEdgesData(Collection> incomingEdgesData) { + Set variablesInScope = Sets.newHashSet(); + for (Map edgeData : incomingEdgesData) { + variablesInScope.addAll(edgeData.keySet()); + } + + Map enterInstructionData = Maps.newHashMap(); + for (VariableDescriptor variable : variablesInScope) { + Set edgesDataForVariable = Sets.newHashSet(); + for (Map edgeData : incomingEdgesData) { + VariableInitializers initializers = edgeData.get(variable); + if (initializers != null) { + edgesDataForVariable.add(initializers); + } + } + enterInstructionData.put(variable, new VariableInitializers(edgesDataForVariable)); + } + return enterInstructionData; + } + + private Map addVariableInitializerFromCurrentInstructionIfAny(Instruction instruction, Map enterInstructionData) { + if (!(instruction instanceof WriteValueInstruction) && !(instruction instanceof VariableDeclarationInstruction)) { + return enterInstructionData; + } + VariableDescriptor variable = extractVariableDescriptorIfAny(instruction, false); + if (variable == null) { + return enterInstructionData; + } + Map exitInstructionData = Maps.newHashMap(enterInstructionData); + if (instruction instanceof WriteValueInstruction) { + VariableInitializers enterInitializers = enterInstructionData.get(variable); + VariableInitializers initializationAtThisElement = new VariableInitializers(((WriteValueInstruction) instruction).getElement(), enterInitializers); + exitInstructionData.put(variable, initializationAtThisElement); + } + else { + VariableInitializers enterInitializers = enterInstructionData.get(variable); + if (enterInitializers == null || !enterInitializers.isInitialized() || !enterInitializers.isDeclared()) { + JetElement element = ((VariableDeclarationInstruction) instruction).getElement(); + if (element instanceof JetProperty) { + JetProperty property = (JetProperty) element; + if (property.getInitializer() == null) { + boolean isInitialized = enterInitializers != null && enterInitializers.isInitialized(); + VariableInitializers variableDeclarationInfo = new VariableInitializers(isInitialized, true); + exitInstructionData.put(variable, variableDeclarationInfo); + } + } + } + } + return exitInstructionData; + } + +// variable use + + private Map, Map>> collectVariableStatusData() { + JetControlFlowGraphTraverser> traverser = + JetControlFlowGraphTraverser.create(pseudocode, true, false); + Map sinkInstructionData = Maps.newHashMap(); + for (VariableDescriptor usedVariable : declarationDataMap.get(pseudocode).usedVariables) { + sinkInstructionData.put(usedVariable, VariableUseStatus.UNUSED); + } + traverser.collectInformationFromInstructionGraph(Collections.emptyMap(), sinkInstructionData, + new JetControlFlowGraphTraverser.InstructionDataMergeStrategy>() { + @Override + public Pair, Map> execute(@NotNull Instruction instruction, @NotNull Collection> incomingEdgesData) { + Map enterResult = Maps.newHashMap(); + for (Map edgeData : incomingEdgesData) { + for (Map.Entry entry : edgeData.entrySet()) { + VariableDescriptor variableDescriptor = entry.getKey(); + VariableUseStatus variableUseStatus = entry.getValue(); + enterResult.put(variableDescriptor, variableUseStatus.merge(enterResult.get(variableDescriptor))); + } + } + VariableDescriptor variableDescriptor = extractVariableDescriptorIfAny(instruction, true); + if (variableDescriptor == null || (!(instruction instanceof ReadValueInstruction) && !(instruction instanceof WriteValueInstruction))) { + return Pair.create(enterResult, enterResult); + } + Map exitResult = Maps.newHashMap(enterResult); + if (instruction instanceof ReadValueInstruction) { + exitResult.put(variableDescriptor, VariableUseStatus.LAST_READ); + } + else { + VariableUseStatus variableUseStatus = enterResult.get(variableDescriptor); + if (variableUseStatus == null) variableUseStatus = VariableUseStatus.UNUSED; + switch (variableUseStatus) { + case UNUSED: + case ONLY_WRITTEN_NEVER_READ: + exitResult.put(variableDescriptor, VariableUseStatus.ONLY_WRITTEN_NEVER_READ); + break; + case LAST_WRITTEN: + case LAST_READ: + exitResult.put(variableDescriptor, VariableUseStatus.LAST_WRITTEN); + } + } + return Pair.create(enterResult, exitResult); + } + }); + return traverser.getDataMap(); + } + +// Util methods + + @Nullable + public VariableDescriptor extractVariableDescriptorIfAny(@NotNull Instruction instruction, boolean onlyReference) { + JetElement element = null; + if (instruction instanceof ReadValueInstruction) { + element = ((ReadValueInstruction) instruction).getElement(); + } + else if (instruction instanceof WriteValueInstruction) { + element = ((WriteValueInstruction) instruction).getlValue(); + } + else if (instruction instanceof VariableDeclarationInstruction) { + element = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement(); + } + return BindingContextUtils.extractVariableDescriptorIfAny(trace.getBindingContext(), element, onlyReference); + } + +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/data/VariableInitializers.java b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/data/VariableInitializers.java new file mode 100644 index 00000000000..c30cef1a60f --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/data/VariableInitializers.java @@ -0,0 +1,100 @@ +/* + * Copyright 2010-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.lang.cfg.data; + +import com.google.common.collect.Sets; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.psi.JetElement; +import org.jetbrains.jet.lang.psi.JetProperty; + +import java.util.Set; + +/** +* @author svtk +*/ +public class VariableInitializers { + private final Set possibleLocalInitializers = Sets.newHashSet(); + private boolean isInitialized; + private boolean isDeclared; + + public VariableInitializers(boolean isInitialized) { + this(isInitialized, false); + } + + public VariableInitializers(boolean isInitialized, boolean isDeclared) { + this.isInitialized = isInitialized; + this.isDeclared = isDeclared; + } + + public VariableInitializers(JetElement element, @Nullable VariableInitializers previous) { + isInitialized = true; + isDeclared = element instanceof JetProperty || (previous != null && previous.isDeclared()); + possibleLocalInitializers.add(element); + } + + public VariableInitializers(Set edgesData) { + isInitialized = true; + isDeclared = true; + for (VariableInitializers edgeData : edgesData) { + if (!edgeData.isInitialized) { + isInitialized = false; + } + if (!edgeData.isDeclared) { + isDeclared = false; + } + possibleLocalInitializers.addAll(edgeData.possibleLocalInitializers); + } + } + + public Set getPossibleLocalInitializers() { + return possibleLocalInitializers; + } + + public boolean isInitialized() { + return isInitialized; + } + + public boolean isDeclared() { + return isDeclared; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof VariableInitializers)) return false; + + VariableInitializers that = (VariableInitializers) o; + + if (isDeclared != that.isDeclared) return false; + if (isInitialized != that.isInitialized) return false; + if (possibleLocalInitializers != null + ? !possibleLocalInitializers.equals(that.possibleLocalInitializers) + : that.possibleLocalInitializers != null) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int result = possibleLocalInitializers != null ? possibleLocalInitializers.hashCode() : 0; + result = 31 * result + (isInitialized ? 1 : 0); + result = 31 * result + (isDeclared ? 1 : 0); + return result; + } +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/cfg/data/VariableUseStatus.java b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/data/VariableUseStatus.java new file mode 100644 index 00000000000..0d1b6bb7a9e --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/cfg/data/VariableUseStatus.java @@ -0,0 +1,40 @@ +/* + * Copyright 2010-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.lang.cfg.data; + +import org.jetbrains.annotations.Nullable; + +/** +* @author svtk +*/ +public enum VariableUseStatus { + LAST_READ(3), + LAST_WRITTEN(2), + ONLY_WRITTEN_NEVER_READ(1), + UNUSED(0); + + private final int importance; + + VariableUseStatus(int importance) { + this.importance = importance; + } + + public VariableUseStatus merge(@Nullable VariableUseStatus variableUseStatus) { + if (variableUseStatus == null || importance > variableUseStatus.importance) return this; + return variableUseStatus; + } +}