Refactoring: DataFlowValue.Kind introduced

This commit is contained in:
Mikhail Glukhikh
2015-09-07 16:15:05 +03:00
parent 8348f204cd
commit 15e46ebc2b
3 changed files with 68 additions and 62 deletions
@@ -27,23 +27,40 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
* In general case it's some r-value.
*/
public class DataFlowValue {
public static final DataFlowValue NULL = new DataFlowValue(new Object(), KotlinBuiltIns.getInstance().getNullableNothingType(), false, false, Nullability.NULL);
public static final DataFlowValue NULLABLE = new DataFlowValue(new Object(), KotlinBuiltIns.getInstance().getNullableAnyType(), false, false, Nullability.UNKNOWN);
public static final DataFlowValue ERROR = new DataFlowValue(new Object(), ErrorUtils.createErrorType("Error type for data flow"), false, false, Nullability.IMPOSSIBLE);
private final boolean stableIdentifier;
private final boolean uncapturedLocalVariable;
public enum Kind {
STABLE_VALUE("stable"),
PREDICTABLE_VARIABLE("predictable"),
UNPREDICTABLE_VARIABLE("unpredictable"),
OTHER("other");
@Override
public String toString() {
return name;
}
Kind(String name) {
this.name = name;
}
private final String name;
public boolean isStable() {
return this == STABLE_VALUE;
}
}
public static final DataFlowValue NULL = new DataFlowValue(new Object(), KotlinBuiltIns.getInstance().getNullableNothingType(), Kind.OTHER, Nullability.NULL);
public static final DataFlowValue ERROR = new DataFlowValue(new Object(), ErrorUtils.createErrorType("Error type for data flow"), Kind.OTHER, Nullability.IMPOSSIBLE);
private final Kind kind;
private final JetType type;
private final Object id;
private final Nullability immanentNullability;
// Use DataFlowValueFactory
/*package*/ DataFlowValue(Object id, JetType type, boolean stableIdentifier, boolean uncapturedLocalVariable, Nullability immanentNullability) {
assert !stableIdentifier || !uncapturedLocalVariable :
"data flow value for object " + id + " cannot be together a stable identifier and an uncaptured local variable";
this.stableIdentifier = stableIdentifier;
this.uncapturedLocalVariable = uncapturedLocalVariable;
/*package*/ DataFlowValue(Object id, JetType type, Kind kind, Nullability immanentNullability) {
this.kind = kind;
this.type = type;
this.id = id;
this.immanentNullability = immanentNullability;
@@ -59,30 +76,17 @@ public class DataFlowValue {
return immanentNullability;
}
/**
* Stable identifier is a non-literal value that is statically known to be immutable
*
* NB: this function is no longer public!
* If you are checking for a possible smart cast, probably you need isPredictable() instead
*/
private boolean isStableIdentifier() {
return stableIdentifier;
public Kind getKind() {
return kind;
}
/**
* Identifier is considered a local variable here if it's mutable (var), local and not captured in a closure
*/
public boolean isUncapturedLocalVariable() {
return uncapturedLocalVariable;
}
/**
* Both stable identifiers and uncaptured local variables are considered "predictable".
* Both stable values and local variables (regardless captured or not) are considered "predictable".
* Predictable means here we do not expect some sudden change of their values,
* like accessing mutable properties in another thread or mutable variables from closures.
* like accessing mutable properties in another thread.
*/
public boolean isPredictable() {
return stableIdentifier || uncapturedLocalVariable;
return kind == Kind.STABLE_VALUE || kind == Kind.PREDICTABLE_VARIABLE;
}
@NotNull
@@ -97,7 +101,7 @@ public class DataFlowValue {
DataFlowValue that = (DataFlowValue) o;
if (stableIdentifier != that.stableIdentifier) return false;
if (kind.isStable() != that.kind.isStable()) return false;
if (id != null ? !id.equals(that.id) : that.id != null) return false;
if (type != null ? !type.equals(that.type) : that.type != null) return false;
@@ -106,12 +110,12 @@ public class DataFlowValue {
@Override
public String toString() {
return (stableIdentifier ? "stable " : "unstable ") + (id == null ? null : id.toString()) + " " + immanentNullability;
return kind.toString() + (id == null ? null : id.toString()) + " " + immanentNullability;
}
@Override
public int hashCode() {
int result = (stableIdentifier ? 1 : 0);
int result = kind.isStable() ? 1 : 0;
result = 31 * result + (type != null ? type.hashCode() : 0);
result = 31 * result + (id != null ? id.hashCode() : 0);
return result;
@@ -81,23 +81,21 @@ public class DataFlowValueFactory {
// fun <T : Any?> foo(x: T) = x!!.hashCode() // there no way in type system to denote that `x!!` is not nullable
return new DataFlowValue(expression,
type,
/* stableIdentifier = */false,
/* uncapturedLocalVariable = */false,
DataFlowValue.Kind.OTHER,
Nullability.NOT_NULL);
}
IdentifierInfo result = getIdForStableIdentifier(expression, bindingContext, containingDeclarationOrModule);
return new DataFlowValue(result == NO_IDENTIFIER_INFO ? expression : result.id,
type,
result.isStable,
result.isLocal,
result.kind,
getImmanentNullability(type));
}
@NotNull
public static DataFlowValue createDataFlowValue(@NotNull ThisReceiver receiver) {
JetType type = receiver.getType();
return new DataFlowValue(receiver, type, true, false, getImmanentNullability(type));
return new DataFlowValue(receiver, type, DataFlowValue.Kind.STABLE_VALUE, getImmanentNullability(type));
}
@NotNull
@@ -118,7 +116,7 @@ public class DataFlowValueFactory {
if (receiverValue instanceof TransientReceiver || receiverValue instanceof ScriptReceiver) {
// SCRIPT: smartcasts data flow
JetType type = receiverValue.getType();
return new DataFlowValue(receiverValue, type, true, false, getImmanentNullability(type));
return new DataFlowValue(receiverValue, type, DataFlowValue.Kind.STABLE_VALUE, getImmanentNullability(type));
}
else if (receiverValue instanceof ClassReceiver || receiverValue instanceof ExtensionReceiver) {
return createDataFlowValue((ThisReceiver) receiverValue);
@@ -145,8 +143,7 @@ public class DataFlowValueFactory {
) {
JetType type = variableDescriptor.getType();
return new DataFlowValue(variableDescriptor, type,
isStableVariable(variableDescriptor, usageContainingModule),
isUncapturedLocalVariable(variableDescriptor, bindingContext),
variableKind(variableDescriptor, usageContainingModule, bindingContext),
getImmanentNullability(type));
}
@@ -157,20 +154,17 @@ public class DataFlowValueFactory {
private static class IdentifierInfo {
public final Object id;
public final boolean isStable;
public final boolean isLocal;
public final DataFlowValue.Kind kind;
public final boolean isPackage;
private IdentifierInfo(Object id, boolean isStable, boolean isLocal, boolean isPackage) {
assert !isStable || !isLocal : "Identifier info for object " + id + " cannot be stable and local at one time";
private IdentifierInfo(Object id, DataFlowValue.Kind kind, boolean isPackage) {
this.id = id;
this.isStable = isStable;
this.isLocal = isLocal;
this.kind = kind;
this.isPackage = isPackage;
}
}
private static final IdentifierInfo NO_IDENTIFIER_INFO = new IdentifierInfo(null, false, false, false) {
private static final IdentifierInfo NO_IDENTIFIER_INFO = new IdentifierInfo(null, DataFlowValue.Kind.OTHER, false) {
@Override
public String toString() {
return "NO_IDENTIFIER_INFO";
@@ -178,18 +172,18 @@ public class DataFlowValueFactory {
};
@NotNull
private static IdentifierInfo createInfo(Object id, boolean isStable, boolean isLocal) {
return new IdentifierInfo(id, isStable, isLocal, false);
private static IdentifierInfo createInfo(Object id, DataFlowValue.Kind kind) {
return new IdentifierInfo(id, kind, false);
}
@NotNull
private static IdentifierInfo createStableInfo(Object id) {
return createInfo(id, true, false);
return createInfo(id, DataFlowValue.Kind.STABLE_VALUE);
}
@NotNull
private static IdentifierInfo createPackageOrClassInfo(Object id) {
return new IdentifierInfo(id, true, false, true);
return new IdentifierInfo(id, DataFlowValue.Kind.STABLE_VALUE, true);
}
@NotNull
@@ -201,9 +195,10 @@ public class DataFlowValueFactory {
return selectorInfo;
}
return createInfo(Pair.create(receiverInfo.id, selectorInfo.id),
receiverInfo.isStable && selectorInfo.isStable,
receiverInfo.kind.isStable() && selectorInfo.kind.isStable()
? DataFlowValue.Kind.STABLE_VALUE
// x.y can never be a local variable
false);
: DataFlowValue.Kind.OTHER);
}
@NotNull
@@ -211,7 +206,7 @@ public class DataFlowValueFactory {
if (argumentInfo == NO_IDENTIFIER_INFO) {
return NO_IDENTIFIER_INFO;
}
return createInfo(Pair.create(expression, argumentInfo.id), argumentInfo.isStable, argumentInfo.isLocal);
return createInfo(Pair.create(expression, argumentInfo.id), argumentInfo.kind);
}
@NotNull
@@ -278,8 +273,7 @@ public class DataFlowValueFactory {
VariableDescriptor variableDescriptor = (VariableDescriptor) declarationDescriptor;
return combineInfo(receiverInfo, createInfo(variableDescriptor,
isStableVariable(variableDescriptor, usageModuleDescriptor),
isUncapturedLocalVariable(variableDescriptor, bindingContext)));
variableKind(variableDescriptor, usageModuleDescriptor, bindingContext)));
}
if (declarationDescriptor instanceof PackageViewDescriptor || declarationDescriptor instanceof ClassDescriptor) {
return createPackageOrClassInfo(declarationDescriptor);
@@ -314,10 +308,18 @@ public class DataFlowValueFactory {
return NO_IDENTIFIER_INFO;
}
public static boolean isUncapturedLocalVariable(@NotNull VariableDescriptor variableDescriptor, @NotNull BindingContext bindingContext) {
return variableDescriptor.isVar()
&& variableDescriptor instanceof LocalVariableDescriptor
&& !BindingContextUtils.isVarCapturedInClosure(bindingContext, variableDescriptor);
public static DataFlowValue.Kind variableKind(
@NotNull VariableDescriptor variableDescriptor,
@Nullable ModuleDescriptor usageModule,
@NotNull BindingContext bindingContext
) {
if (isStableVariable(variableDescriptor, usageModule)) return DataFlowValue.Kind.STABLE_VALUE;
boolean isLocalVar = variableDescriptor.isVar() && variableDescriptor instanceof LocalVariableDescriptor;
if (!isLocalVar) return DataFlowValue.Kind.OTHER;
if (BindingContextUtils.isVarCapturedInClosure(bindingContext, variableDescriptor)) {
return DataFlowValue.Kind.UNPREDICTABLE_VARIABLE;
}
return DataFlowValue.Kind.PREDICTABLE_VARIABLE;
}
/**
@@ -43,8 +43,8 @@ class PreliminaryLoopVisitor extends AssignedVariablesSearcher {
Map<DataFlowValue, Nullability> nullabilityMap = dataFlowInfo.getCompleteNullabilityInfo();
Set<DataFlowValue> valueSetToClear = new LinkedHashSet<DataFlowValue>();
for (DataFlowValue value: nullabilityMap.keySet()) {
// Only uncaptured local variables are under interest here
if (value.isUncapturedLocalVariable() && value.getId() instanceof LocalVariableDescriptor) {
// Only predictable variables are under interest here
if (value.getKind() == DataFlowValue.Kind.PREDICTABLE_VARIABLE && value.getId() instanceof LocalVariableDescriptor) {
LocalVariableDescriptor descriptor = (LocalVariableDescriptor)value.getId();
if (getAssignedNames().contains(descriptor.getName())) {
valueSetToClear.add(value);