Delegating data flow info

A delegating data flow info can have a parent data flow info, which it queries
when is queried itself. This helps to avoid lots of copying of the same data
This commit is contained in:
Alexander Udalov
2012-11-15 19:19:31 +04:00
parent 83d56213cd
commit d52387a7eb
2 changed files with 73 additions and 44 deletions
@@ -17,20 +17,14 @@
package org.jetbrains.jet.lang.resolve.calls.autocasts; package org.jetbrains.jet.lang.resolve.calls.autocasts;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Multimaps;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.util.CommonSuppliers;
import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
public interface DataFlowInfo { public interface DataFlowInfo {
public static final DataFlowInfo EMPTY = new DataFlowInfoImpl( DataFlowInfo EMPTY = new DelegatingDataFlowInfo(null, ImmutableMap.<DataFlowValue, Nullability>of(),
ImmutableMap.<DataFlowValue, Nullability>of(), DelegatingDataFlowInfo.newTypeInfo());
Multimaps.newListMultimap(Collections.<DataFlowValue, Collection<JetType>>emptyMap(),
CommonSuppliers.<JetType>getArrayListSupplier()));
@NotNull @NotNull
Nullability getNullability(@NotNull DataFlowValue key); Nullability getNullability(@NotNull DataFlowValue key);
@@ -18,6 +18,7 @@ package org.jetbrains.jet.lang.resolve.calls.autocasts;
import com.google.common.collect.*; import com.google.common.collect.*;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeUtils; import org.jetbrains.jet.lang.types.TypeUtils;
import org.jetbrains.jet.util.CommonSuppliers; import org.jetbrains.jet.util.CommonSuppliers;
@@ -29,15 +30,25 @@ import java.util.Set;
import static org.jetbrains.jet.lang.resolve.calls.autocasts.Nullability.NOT_NULL; import static org.jetbrains.jet.lang.resolve.calls.autocasts.Nullability.NOT_NULL;
public class DataFlowInfoImpl implements DataFlowInfo { /* package */ class DelegatingDataFlowInfo implements DataFlowInfo {
@Nullable
private final DataFlowInfo parent;
@NotNull
private final ImmutableMap<DataFlowValue, Nullability> nullabilityInfo; private final ImmutableMap<DataFlowValue, Nullability> nullabilityInfo;
/** Also immutable */ /** Also immutable */
@NotNull
private final ListMultimap<DataFlowValue, JetType> typeInfo; private final ListMultimap<DataFlowValue, JetType> typeInfo;
/* package */ DataFlowInfoImpl( private static final ListMultimap<DataFlowValue, JetType> EMPTY_TYPE_INFO = newTypeInfo();
/* package */ DelegatingDataFlowInfo(
@Nullable DataFlowInfo parent,
@NotNull ImmutableMap<DataFlowValue, Nullability> nullabilityInfo, @NotNull ImmutableMap<DataFlowValue, Nullability> nullabilityInfo,
@NotNull ListMultimap<DataFlowValue, JetType> typeInfo @NotNull ListMultimap<DataFlowValue, JetType> typeInfo
) { ) {
this.parent = parent;
this.nullabilityInfo = nullabilityInfo; this.nullabilityInfo = nullabilityInfo;
this.typeInfo = typeInfo; this.typeInfo = typeInfo;
} }
@@ -47,87 +58,98 @@ public class DataFlowInfoImpl implements DataFlowInfo {
public Nullability getNullability(@NotNull DataFlowValue key) { public Nullability getNullability(@NotNull DataFlowValue key) {
if (!key.isStableIdentifier()) return key.getImmanentNullability(); if (!key.isStableIdentifier()) return key.getImmanentNullability();
Nullability nullability = nullabilityInfo.get(key); Nullability nullability = nullabilityInfo.get(key);
if (nullability == null) { return nullability != null ? nullability :
nullability = key.getImmanentNullability(); parent != null ? parent.getNullability(key) :
} key.getImmanentNullability();
return nullability;
} }
private boolean putNullability(@NotNull Map<DataFlowValue, Nullability> map, @NotNull DataFlowValue value, @NotNull Nullability nullability) { private boolean putNullability(@NotNull Map<DataFlowValue, Nullability> map, @NotNull DataFlowValue value, @NotNull Nullability nullability) {
if (!value.isStableIdentifier()) return false; if (!value.isStableIdentifier()) return false;
return map.put(value, nullability) != nullability; map.put(value, nullability);
return nullability != getNullability(value);
} }
@Override @Override
@NotNull @NotNull
public List<JetType> getPossibleTypes(@NotNull DataFlowValue key) { public List<JetType> getPossibleTypes(@NotNull DataFlowValue key) {
JetType originalType = key.getType();
List<JetType> types = typeInfo.get(key); List<JetType> types = typeInfo.get(key);
Nullability nullability = getNullability(key); if (getNullability(key).canBeNull()) {
if (nullability.canBeNull()) { if (parent != null) {
types = Lists.newArrayList(types);
addAllUnique(types, parent.getPossibleTypes(key));
}
return types; return types;
} }
List<JetType> enrichedTypes = Lists.newArrayListWithCapacity(types.size());
List<JetType> enrichedTypes = Lists.newArrayListWithCapacity(types.size() + 1);
JetType originalType = key.getType();
if (originalType.isNullable()) { if (originalType.isNullable()) {
enrichedTypes.add(TypeUtils.makeNotNullable(originalType)); enrichedTypes.add(TypeUtils.makeNotNullable(originalType));
} }
for (JetType type: types) { for (JetType type : types) {
if (type.isNullable()) { enrichedTypes.add(TypeUtils.makeNotNullable(type));
enrichedTypes.add(TypeUtils.makeNotNullable(type)); }
}
else { if (parent != null) {
enrichedTypes.add(type); addAllUnique(enrichedTypes, parent.getPossibleTypes(key));
}
return enrichedTypes;
}
private static void addAllUnique(@NotNull List<JetType> toList, @NotNull List<JetType> fromList) {
for (JetType type : fromList) {
if (!toList.contains(type)) {
toList.add(type);
} }
} }
return enrichedTypes;
} }
@Override @Override
@NotNull @NotNull
public DataFlowInfo equate(@NotNull DataFlowValue a, @NotNull DataFlowValue b) { public DataFlowInfo equate(@NotNull DataFlowValue a, @NotNull DataFlowValue b) {
Map<DataFlowValue, Nullability> builder = Maps.newHashMap(nullabilityInfo); Map<DataFlowValue, Nullability> builder = Maps.newHashMap();
Nullability nullabilityOfA = getNullability(a); Nullability nullabilityOfA = getNullability(a);
Nullability nullabilityOfB = getNullability(b); Nullability nullabilityOfB = getNullability(b);
boolean changed = false; boolean changed = false;
changed |= putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB)); changed |= putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB));
changed |= putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA)); changed |= putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA));
return changed ? new DataFlowInfoImpl(ImmutableMap.copyOf(builder), typeInfo) : this; return changed ? new DelegatingDataFlowInfo(this, ImmutableMap.copyOf(builder), EMPTY_TYPE_INFO) : this;
} }
@Override @Override
@NotNull @NotNull
public DataFlowInfo disequate(@NotNull DataFlowValue a, @NotNull DataFlowValue b) { public DataFlowInfo disequate(@NotNull DataFlowValue a, @NotNull DataFlowValue b) {
Map<DataFlowValue, Nullability> builder = Maps.newHashMap(nullabilityInfo); Map<DataFlowValue, Nullability> builder = Maps.newHashMap();
Nullability nullabilityOfA = getNullability(a); Nullability nullabilityOfA = getNullability(a);
Nullability nullabilityOfB = getNullability(b); Nullability nullabilityOfB = getNullability(b);
boolean changed = false; boolean changed = false;
changed |= putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB.invert())); changed |= putNullability(builder, a, nullabilityOfA.refine(nullabilityOfB.invert()));
changed |= putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA.invert())); changed |= putNullability(builder, b, nullabilityOfB.refine(nullabilityOfA.invert()));
return changed ? new DataFlowInfoImpl(ImmutableMap.copyOf(builder), typeInfo) : this; return changed ? new DelegatingDataFlowInfo(this, ImmutableMap.copyOf(builder), EMPTY_TYPE_INFO) : this;
} }
@Override @Override
@NotNull @NotNull
public DataFlowInfo establishSubtyping(@NotNull DataFlowValue[] values, @NotNull JetType type) { public DataFlowInfo establishSubtyping(@NotNull DataFlowValue[] values, @NotNull JetType type) {
ListMultimap<DataFlowValue, JetType> newTypeInfo = copyTypeInfo(); if (values.length == 0) return this;
Map<DataFlowValue, Nullability> newNullabilityInfo = Maps.newHashMap(nullabilityInfo);
boolean changed = false; ListMultimap<DataFlowValue, JetType> newTypeInfo = newTypeInfo();
Map<DataFlowValue, Nullability> newNullabilityInfo = Maps.newHashMap();
for (DataFlowValue value : values) { for (DataFlowValue value : values) {
// if (!value.isStableIdentifier()) continue;
changed = true;
newTypeInfo.put(value, type); newTypeInfo.put(value, type);
if (!type.isNullable()) { if (!type.isNullable()) {
putNullability(newNullabilityInfo, value, NOT_NULL); putNullability(newNullabilityInfo, value, NOT_NULL);
} }
} }
if (!changed) return this; return new DelegatingDataFlowInfo(this, ImmutableMap.copyOf(newNullabilityInfo), newTypeInfo);
return new DataFlowInfoImpl(ImmutableMap.copyOf(newNullabilityInfo), newTypeInfo);
} }
@NotNull
private ListMultimap<DataFlowValue, JetType> copyTypeInfo() { private ListMultimap<DataFlowValue, JetType> copyTypeInfo() {
ListMultimap<DataFlowValue, JetType> newTypeInfo = Multimaps.newListMultimap(Maps.<DataFlowValue, Collection<JetType>>newHashMap(), CommonSuppliers.<JetType>getArrayListSupplier()); ListMultimap<DataFlowValue, JetType> newTypeInfo = newTypeInfo();
newTypeInfo.putAll(typeInfo); newTypeInfo.putAll(typeInfo);
return newTypeInfo; return newTypeInfo;
} }
@@ -135,7 +157,11 @@ public class DataFlowInfoImpl implements DataFlowInfo {
@NotNull @NotNull
@Override @Override
public DataFlowInfo and(@NotNull DataFlowInfo otherInfo) { public DataFlowInfo and(@NotNull DataFlowInfo otherInfo) {
DataFlowInfoImpl other = (DataFlowInfoImpl) otherInfo; if (otherInfo == EMPTY) return this;
assert otherInfo instanceof DelegatingDataFlowInfo : "Unknown DataFlowInfo type: " + otherInfo;
DelegatingDataFlowInfo other = (DelegatingDataFlowInfo) otherInfo;
Map<DataFlowValue, Nullability> nullabilityMapBuilder = Maps.newHashMap(); Map<DataFlowValue, Nullability> nullabilityMapBuilder = Maps.newHashMap();
nullabilityMapBuilder.putAll(nullabilityInfo); nullabilityMapBuilder.putAll(nullabilityInfo);
for (Map.Entry<DataFlowValue, Nullability> entry : other.nullabilityInfo.entrySet()) { for (Map.Entry<DataFlowValue, Nullability> entry : other.nullabilityInfo.entrySet()) {
@@ -152,13 +178,17 @@ public class DataFlowInfoImpl implements DataFlowInfo {
ListMultimap<DataFlowValue, JetType> newTypeInfo = copyTypeInfo(); ListMultimap<DataFlowValue, JetType> newTypeInfo = copyTypeInfo();
newTypeInfo.putAll(other.typeInfo); newTypeInfo.putAll(other.typeInfo);
return new DataFlowInfoImpl(ImmutableMap.copyOf(nullabilityMapBuilder), newTypeInfo); return new DelegatingDataFlowInfo(null, ImmutableMap.copyOf(nullabilityMapBuilder), newTypeInfo);
} }
@NotNull @NotNull
@Override @Override
public DataFlowInfo or(@NotNull DataFlowInfo otherInfo) { public DataFlowInfo or(@NotNull DataFlowInfo otherInfo) {
DataFlowInfoImpl other = (DataFlowInfoImpl) otherInfo; if (otherInfo == EMPTY) return EMPTY;
assert otherInfo instanceof DelegatingDataFlowInfo : "Unknown DataFlowInfo type: " + otherInfo;
DelegatingDataFlowInfo other = (DelegatingDataFlowInfo) otherInfo;
Map<DataFlowValue, Nullability> builder = Maps.newHashMap(nullabilityInfo); Map<DataFlowValue, Nullability> builder = Maps.newHashMap(nullabilityInfo);
builder.keySet().retainAll(other.nullabilityInfo.keySet()); builder.keySet().retainAll(other.nullabilityInfo.keySet());
for (Map.Entry<DataFlowValue, Nullability> entry : builder.entrySet()) { for (Map.Entry<DataFlowValue, Nullability> entry : builder.entrySet()) {
@@ -169,7 +199,7 @@ public class DataFlowInfoImpl implements DataFlowInfo {
builder.put(key, thisFlags.or(otherFlags)); builder.put(key, thisFlags.or(otherFlags));
} }
ListMultimap<DataFlowValue, JetType> newTypeInfo = Multimaps.newListMultimap(Maps.<DataFlowValue, Collection<JetType>>newHashMap(), CommonSuppliers.<JetType>getArrayListSupplier()); ListMultimap<DataFlowValue, JetType> newTypeInfo = newTypeInfo();
Set<DataFlowValue> keys = Sets.newHashSet(typeInfo.keySet()); Set<DataFlowValue> keys = Sets.newHashSet(typeInfo.keySet());
keys.retainAll(other.typeInfo.keySet()); keys.retainAll(other.typeInfo.keySet());
@@ -184,7 +214,12 @@ public class DataFlowInfoImpl implements DataFlowInfo {
newTypeInfo.putAll(key, newTypes); newTypeInfo.putAll(key, newTypes);
} }
return new DataFlowInfoImpl(ImmutableMap.copyOf(builder), newTypeInfo); return new DelegatingDataFlowInfo(null, ImmutableMap.copyOf(builder), newTypeInfo);
}
@NotNull
/* package */ static ListMultimap<DataFlowValue, JetType> newTypeInfo() {
return Multimaps.newListMultimap(Maps.<DataFlowValue, Collection<JetType>>newHashMap(), CommonSuppliers.<JetType>getArrayListSupplier());
} }
@Override @Override
@@ -199,4 +234,4 @@ public class DataFlowInfoImpl implements DataFlowInfo {
} }
return "Non-trivial DataFlowInfo"; return "Non-trivial DataFlowInfo";
} }
} }