Strengthen parameter types
Change-Id: I469238b0fefbe632baaa4420c0d0289b4c2571dd
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index 4f23975..0cd5a14 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -145,7 +145,7 @@
@Override
public String toString() {
- return "Method " + holder + "." + name + " " + proto.toString();
+ return toSourceString();
}
public MethodReference asMethodReference() {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
index 65e9670..7e54571 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.InterfaceCollection.Builder;
import com.android.tools.r8.utils.BooleanBox;
@@ -154,6 +155,16 @@
return getOrCreateVariant(nullability().meet(nullability));
}
+ public DexType toDexType(DexItemFactory dexItemFactory) {
+ if (type == dexItemFactory.objectType) {
+ DexType singleKnownInterface = getInterfaces().getSingleKnownInterface();
+ if (singleKnownInterface != null) {
+ return singleKnownInterface;
+ }
+ }
+ return type;
+ }
+
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
index 764167c..e34ab51 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -232,7 +232,7 @@
assert outType.equalUpToNullability(castType);
// Check soundness of null information.
- assert inType.nullability().lessThanOrEqual(outType.nullability());
+ assert inType.nullability() == outType.nullability();
// Since we cannot remove the cast the in-value must be different from null.
assert !inType.isNullType();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 0957abc..7d12496 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1588,7 +1588,12 @@
// If the cast is guaranteed to succeed and only there to ensure the program type checks, then
// check if the program would still type check after removing the cast.
- if (checkCast.isSafeCheckCast()) {
+ if (checkCast.isSafeCheckCast()
+ || checkCast
+ .getFirstOperand()
+ .getDynamicType(appViewWithLiveness)
+ .getDynamicUpperBoundType()
+ .lessThanOrEqualUpToNullability(castTypeLattice, appView)) {
TypeElement useType =
TypeUtils.computeUseType(appViewWithLiveness, context, checkCast.outValue());
if (inTypeLattice.lessThanOrEqualUpToNullability(useType, appView)) {
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
index 897aaa8..7c196b1 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
@@ -93,9 +93,11 @@
return method.toTypeSubstitutedMethod(
methodReferenceAfterParameterRemoval,
builder -> {
- RewrittenPrototypeDescription prototypeChanges =
- graphLens.getPrototypeChanges(methodReferenceAfterParameterRemoval);
- builder.apply(prototypeChanges.createParameterAnnotationsRemover(method));
+ if (graphLens.hasPrototypeChanges(methodReferenceAfterParameterRemoval)) {
+ RewrittenPrototypeDescription prototypeChanges =
+ graphLens.getPrototypeChanges(methodReferenceAfterParameterRemoval);
+ builder.apply(prototypeChanges.createParameterAnnotationsRemover(method));
+ }
});
});
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
index 3ed1334..17865e4 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
@@ -35,7 +35,7 @@
}
public boolean hasPrototypeChanges(DexMethod method) {
- return method != internalGetPreviousMethodSignature(method);
+ return prototypeChanges.containsKey(method);
}
public RewrittenPrototypeDescription getPrototypeChanges(DexMethod method) {
@@ -43,6 +43,10 @@
return prototypeChanges.getOrDefault(method, RewrittenPrototypeDescription.none());
}
+ public boolean isAffected(DexMethod method) {
+ return method != internalGetPreviousMethodSignature(method) || hasPrototypeChanges(method);
+ }
+
@Override
protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
FieldLookupResult lookupResult = super.internalDescribeLookupField(previous);
@@ -61,8 +65,7 @@
protected RewrittenPrototypeDescription internalDescribePrototypeChanges(
RewrittenPrototypeDescription prototypeChanges, DexMethod method) {
DexMethod previous = internalGetPreviousMethodSignature(method);
- if (previous == method) {
- assert !this.prototypeChanges.containsKey(method);
+ if (!hasPrototypeChanges(method)) {
return prototypeChanges;
}
RewrittenPrototypeDescription newPrototypeChanges =
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
index db9543f..f9bfde1 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
@@ -315,22 +316,32 @@
continue;
}
DynamicType dynamicType = parameterState.asClassParameter().getDynamicType();
- if (dynamicType.isUnknown()) {
- continue;
- }
DexType staticType = method.getArgumentType(argumentIndex);
- if (!WideningUtils.widenDynamicNonReceiverType(appView, dynamicType, staticType)
- .isUnknown()) {
- continue;
+ if (shouldWidenDynamicTypeToUnknown(dynamicType, staticType)) {
+ methodState.setParameterState(
+ argumentIndex,
+ parameterState.mutableJoin(
+ appView,
+ new ConcreteClassTypeParameterState(AbstractValue.bottom(), DynamicType.unknown()),
+ staticType,
+ StateCloner.getIdentity()));
}
- methodState.setParameterState(
- argumentIndex,
- parameterState.mutableJoin(
- appView,
- new ConcreteClassTypeParameterState(AbstractValue.bottom(), DynamicType.unknown()),
- staticType,
- StateCloner.getIdentity()));
}
return !methodState.isEffectivelyUnknown();
}
+
+ private boolean shouldWidenDynamicTypeToUnknown(DynamicType dynamicType, DexType staticType) {
+ if (dynamicType.isUnknown()) {
+ return false;
+ }
+ if (WideningUtils.widenDynamicNonReceiverType(appView, dynamicType, staticType).isUnknown()) {
+ return true;
+ }
+ TypeElement staticTypeElement = staticType.toTypeElement(appView);
+ TypeElement dynamicUpperBoundType = dynamicType.getDynamicUpperBoundType(staticTypeElement);
+ if (!dynamicUpperBoundType.lessThanOrEqual(staticTypeElement, appView)) {
+ return true;
+ }
+ return false;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
index 6b94973..0264a50 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -47,7 +47,11 @@
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArraySet;
+import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.ints.IntSets;
import java.util.ArrayList;
@@ -62,6 +66,7 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
+import java.util.function.IntFunction;
import java.util.function.IntPredicate;
public class ArgumentPropagatorProgramOptimizer {
@@ -69,22 +74,43 @@
static class AllowedPrototypeChanges {
private static final AllowedPrototypeChanges EMPTY =
- new AllowedPrototypeChanges(false, IntSets.EMPTY_SET);
+ new AllowedPrototypeChanges(false, Int2ReferenceMaps.emptyMap(), IntSets.EMPTY_SET);
boolean canRewriteToVoid;
+ Int2ReferenceMap<DexType> newParameterTypes;
IntSet removableParameterIndices;
- AllowedPrototypeChanges(boolean canRewriteToVoid, IntSet removableParameterIndices) {
+ AllowedPrototypeChanges(
+ boolean canRewriteToVoid,
+ Int2ReferenceMap<DexType> newParameterTypes,
+ IntSet removableParameterIndices) {
this.canRewriteToVoid = canRewriteToVoid;
+ this.newParameterTypes = newParameterTypes;
this.removableParameterIndices = removableParameterIndices;
}
public static AllowedPrototypeChanges create(RewrittenPrototypeDescription prototypeChanges) {
- return prototypeChanges.isEmpty()
- ? empty()
- : new AllowedPrototypeChanges(
- prototypeChanges.hasBeenChangedToReturnVoid(),
- prototypeChanges.getArgumentInfoCollection().getKeys());
+ if (prototypeChanges.isEmpty()) {
+ return empty();
+ }
+ Int2ReferenceMap<DexType> newParameterTypes = new Int2ReferenceOpenHashMap<>();
+ IntSet removableParameterIndices = new IntOpenHashSet();
+ prototypeChanges
+ .getArgumentInfoCollection()
+ .forEach(
+ (argumentIndex, argumentInfo) -> {
+ if (argumentInfo.isRemovedArgumentInfo()) {
+ removableParameterIndices.add(argumentIndex);
+ } else {
+ assert argumentInfo.isRewrittenTypeInfo();
+ RewrittenTypeInfo rewrittenTypeInfo = argumentInfo.asRewrittenTypeInfo();
+ newParameterTypes.put(argumentIndex, rewrittenTypeInfo.getNewType());
+ }
+ });
+ return new AllowedPrototypeChanges(
+ prototypeChanges.hasBeenChangedToReturnVoid(),
+ newParameterTypes,
+ removableParameterIndices);
}
public static AllowedPrototypeChanges empty() {
@@ -103,6 +129,7 @@
}
AllowedPrototypeChanges other = (AllowedPrototypeChanges) obj;
return canRewriteToVoid == other.canRewriteToVoid
+ && newParameterTypes.equals(other.newParameterTypes)
&& removableParameterIndices.equals(other.removableParameterIndices);
}
}
@@ -308,13 +335,23 @@
return;
}
- // Find the parameters that are constant or unused in all methods.
+ // Find the parameters that are either (i) the same constant, (ii) all unused, or (iii)
+ // all possible to strengthen to the same stronger type, in all methods.
+ Int2ReferenceMap<DexType> newParameterTypes = new Int2ReferenceOpenHashMap<>();
IntSet removableVirtualMethodParametersInAllMethods = new IntArraySet();
for (int parameterIndex = 1;
parameterIndex < signature.getProto().getArity() + 1;
parameterIndex++) {
- if (canRemoveParameterFromVirtualMethods(parameterIndex, methods)) {
- removableVirtualMethodParametersInAllMethods.add(parameterIndex);
+ if (!containsImmediateInterfaceOfInstantiatedLambda(methods)) {
+ if (canRemoveParameterFromVirtualMethods(methods, parameterIndex)) {
+ removableVirtualMethodParametersInAllMethods.add(parameterIndex);
+ } else {
+ DexType newParameterType =
+ getNewParameterTypeForVirtualMethods(methods, parameterIndex);
+ if (newParameterType != null) {
+ newParameterTypes.put(parameterIndex, newParameterType);
+ }
+ }
}
}
@@ -323,11 +360,13 @@
getReturnValueForVirtualMethods(signature, methods);
boolean canRewriteVirtualMethodsToVoid = returnValueForVirtualMethods != null;
if (canRewriteVirtualMethodsToVoid
+ || !newParameterTypes.isEmpty()
|| !removableVirtualMethodParametersInAllMethods.isEmpty()) {
allowedPrototypeChangesForVirtualMethods.put(
signature,
new AllowedPrototypeChanges(
canRewriteVirtualMethodsToVoid,
+ newParameterTypes,
removableVirtualMethodParametersInAllMethods));
}
@@ -403,18 +442,24 @@
return returnValue;
}
+ private boolean containsImmediateInterfaceOfInstantiatedLambda(ProgramMethodSet methods) {
+ for (ProgramMethod method : methods) {
+ DexProgramClass holder = method.getHolder();
+ if (holder.isInterface()) {
+ ObjectAllocationInfoCollection objectAllocationInfoCollection =
+ appView.appInfo().getObjectAllocationInfoCollection();
+ if (objectAllocationInfoCollection.isImmediateInterfaceOfInstantiatedLambda(holder)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
private boolean canRemoveParameterFromVirtualMethods(
- int parameterIndex, ProgramMethodSet methods) {
+ ProgramMethodSet methods, int parameterIndex) {
for (ProgramMethod method : methods) {
if (method.getDefinition().isAbstract()) {
- DexProgramClass holder = method.getHolder();
- if (holder.isInterface()) {
- ObjectAllocationInfoCollection objectAllocationInfoCollection =
- appView.appInfo().getObjectAllocationInfoCollection();
- if (objectAllocationInfoCollection.isImmediateInterfaceOfInstantiatedLambda(holder)) {
- return false;
- }
- }
// OK, this parameter can be removed.
continue;
}
@@ -435,6 +480,26 @@
return true;
}
+ private DexType getNewParameterTypeForVirtualMethods(
+ ProgramMethodSet methods, int parameterIndex) {
+ DexType newParameterType = null;
+ for (ProgramMethod method : methods) {
+ if (method.getAccessFlags().isAbstract()) {
+ // OK, this parameter can have any type.
+ continue;
+ }
+ DexType newParameterTypeForMethod = getNewParameterType(method, parameterIndex);
+ if (newParameterTypeForMethod == null
+ || (newParameterType != null && newParameterType != newParameterTypeForMethod)) {
+ return null;
+ }
+ newParameterType = newParameterTypeForMethod;
+ }
+ assert newParameterType == null
+ || newParameterType != methods.getFirst().getArgumentType(parameterIndex);
+ return newParameterType;
+ }
+
// Returns true if the class was changed as a result of argument propagation.
private boolean visitClass(
DexProgramClass clazz,
@@ -654,75 +719,154 @@
}
IntSet removableParameterIndices = allowedPrototypeChanges.removableParameterIndices;
-
+ Int2ReferenceMap<DexType> newParameterTypes = allowedPrototypeChanges.newParameterTypes;
if (method.getAccessFlags().isAbstract()) {
- // Treat the parameters as unused.
- ArgumentInfoCollection.Builder removableParametersBuilder =
- ArgumentInfoCollection.builder();
- for (int removableParameterIndex : removableParameterIndices) {
- removableParametersBuilder.addArgumentInfo(
- removableParameterIndex,
- RemovedArgumentInfo.builder()
- .setType(method.getArgumentType(removableParameterIndex))
- .build());
- }
- return RewrittenPrototypeDescription.create(
- Collections.emptyList(),
- computeReturnChangesForMethod(method, allowedPrototypeChanges.canRewriteToVoid),
- removableParametersBuilder.build());
+ return computePrototypeChangesForAbstractVirtualMethod(
+ method,
+ allowedPrototypeChanges.canRewriteToVoid,
+ newParameterTypes,
+ removableParameterIndices);
}
RewrittenPrototypeDescription prototypeChanges =
computePrototypeChangesForMethod(
method,
allowedPrototypeChanges.canRewriteToVoid,
+ newParameterTypes::get,
removableParameterIndices::contains);
- assert prototypeChanges.getArgumentInfoCollection().size()
+ assert prototypeChanges.getArgumentInfoCollection().numberOfRemovedArguments()
== removableParameterIndices.size();
return prototypeChanges;
}
+ private RewrittenPrototypeDescription computePrototypeChangesForAbstractVirtualMethod(
+ ProgramMethod method,
+ boolean canRewriteToVoid,
+ Int2ReferenceMap<DexType> newParameterTypes,
+ IntSet removableParameterIndices) {
+
+ // Treat the parameters as unused.
+ ArgumentInfoCollection.Builder argumentInfoCollectionBuilder =
+ ArgumentInfoCollection.builder();
+ for (int argumentIndex = 0;
+ argumentIndex < method.getDefinition().getNumberOfArguments();
+ argumentIndex++) {
+ if (removableParameterIndices.contains(argumentIndex)) {
+ argumentInfoCollectionBuilder.addArgumentInfo(
+ argumentIndex,
+ RemovedArgumentInfo.builder().setType(method.getArgumentType(argumentIndex)).build());
+ } else if (newParameterTypes.containsKey(argumentIndex)) {
+ DexType newParameterType = newParameterTypes.get(argumentIndex);
+ argumentInfoCollectionBuilder.addArgumentInfo(
+ argumentIndex,
+ RewrittenTypeInfo.builder()
+ .setCastType(newParameterType)
+ .setOldType(method.getArgumentType(argumentIndex))
+ .setNewType(newParameterType)
+ .build());
+ }
+ }
+ return RewrittenPrototypeDescription.create(
+ Collections.emptyList(),
+ computeReturnChangesForMethod(method, canRewriteToVoid),
+ argumentInfoCollectionBuilder.build());
+ }
+
private RewrittenPrototypeDescription computePrototypeChangesForMethod(ProgramMethod method) {
- return computePrototypeChangesForMethod(method, true, parameterIndex -> true);
+ IntFunction<DexType> parameterIndexToParameterType =
+ appView.getKeepInfo(method).isParameterTypeStrengtheningAllowed(options)
+ ? parameterIndex -> getNewParameterType(method, parameterIndex)
+ : parameterIndex -> null;
+ return computePrototypeChangesForMethod(
+ method, true, parameterIndexToParameterType, parameterIndex -> true);
+ }
+
+ private DexType getNewParameterType(ProgramMethod method, int parameterIndex) {
+ if (!appView.getKeepInfo(method).isParameterTypeStrengtheningAllowed(options)) {
+ return null;
+ }
+ DexType staticType = method.getArgumentType(parameterIndex);
+ if (!staticType.isClassType()) {
+ return null;
+ }
+ DynamicType dynamicType =
+ method.getOptimizationInfo().getArgumentInfos().getDynamicType(parameterIndex);
+ if (dynamicType == null || dynamicType.isUnknown()) {
+ return null;
+ }
+ TypeElement staticTypeElement = staticType.toTypeElement(appView);
+ TypeElement dynamicUpperBoundType = dynamicType.getDynamicUpperBoundType(staticTypeElement);
+ assert dynamicUpperBoundType.lessThanOrEqual(staticTypeElement, appView);
+ assert dynamicUpperBoundType.isReferenceType();
+ if (dynamicUpperBoundType.isNullType()) {
+ return null;
+ }
+ if (dynamicUpperBoundType.isArrayType()) {
+ return null;
+ }
+ assert dynamicUpperBoundType.isClassType();
+ DexType newParameterType = dynamicUpperBoundType.asClassType().toDexType(dexItemFactory);
+ if (newParameterType == staticType) {
+ return null;
+ }
+ return AccessUtils.isAccessibleInSameContextsAs(newParameterType, staticType, appView)
+ ? newParameterType
+ : null;
}
private RewrittenPrototypeDescription computePrototypeChangesForMethod(
ProgramMethod method,
boolean allowToVoidRewriting,
+ IntFunction<DexType> newParameterTypes,
IntPredicate removableParameterIndices) {
return RewrittenPrototypeDescription.create(
Collections.emptyList(),
computeReturnChangesForMethod(method, allowToVoidRewriting),
- computeParameterChangesForMethod(method, removableParameterIndices));
+ computeParameterChangesForMethod(method, newParameterTypes, removableParameterIndices));
}
private ArgumentInfoCollection computeParameterChangesForMethod(
- ProgramMethod method, IntPredicate removableParameterIndices) {
+ ProgramMethod method,
+ IntFunction<DexType> newParameterTypes,
+ IntPredicate removableParameterIndices) {
ConcreteCallSiteOptimizationInfo optimizationInfo =
method.getOptimizationInfo().getArgumentInfos().asConcreteCallSiteOptimizationInfo();
if (optimizationInfo == null) {
return ArgumentInfoCollection.empty();
}
- ArgumentInfoCollection.Builder removableParametersBuilder = ArgumentInfoCollection.builder();
+ ArgumentInfoCollection.Builder parameterChangesBuilder = ArgumentInfoCollection.builder();
for (int argumentIndex = method.getDefinition().getFirstNonReceiverArgumentIndex();
argumentIndex < method.getDefinition().getNumberOfArguments();
argumentIndex++) {
- if (!removableParameterIndices.test(argumentIndex)) {
- continue;
+ if (removableParameterIndices.test(argumentIndex)) {
+ AbstractValue abstractValue = optimizationInfo.getAbstractArgumentValue(argumentIndex);
+ if (abstractValue.isSingleValue()
+ && abstractValue.asSingleValue().isMaterializableInContext(appView, method)) {
+ parameterChangesBuilder.addArgumentInfo(
+ argumentIndex,
+ RemovedArgumentInfo.builder()
+ .setSingleValue(abstractValue.asSingleValue())
+ .setType(method.getArgumentType(argumentIndex))
+ .build());
+ continue;
+ }
}
- AbstractValue abstractValue = optimizationInfo.getAbstractArgumentValue(argumentIndex);
- if (abstractValue.isSingleValue()
- && abstractValue.asSingleValue().isMaterializableInContext(appView, method)) {
- removableParametersBuilder.addArgumentInfo(
+
+ DexType dynamicType = newParameterTypes.apply(argumentIndex);
+ if (dynamicType != null) {
+ DexType staticType = method.getArgumentType(argumentIndex);
+ assert dynamicType != staticType;
+ parameterChangesBuilder.addArgumentInfo(
argumentIndex,
- RemovedArgumentInfo.builder()
- .setSingleValue(abstractValue.asSingleValue())
- .setType(method.getArgumentType(argumentIndex))
+ RewrittenTypeInfo.builder()
+ .setCastType(dynamicType)
+ .setOldType(staticType)
+ .setNewType(dynamicType)
.build());
}
}
- return removableParametersBuilder.build();
+ return parameterChangesBuilder.build();
}
private RewrittenTypeInfo computeReturnChangesForMethod(
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
index 00749bb..cc8bc7f 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
@@ -46,6 +46,10 @@
return backing.get(method);
}
+ public T getFirst() {
+ return iterator().next();
+ }
+
public boolean contains(DexMethod method) {
return backing.containsKey(method);
}
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/KeepNonVisibilityBridgeMethodsTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/KeepNonVisibilityBridgeMethodsTest.java
index f1498b6..2472119 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/KeepNonVisibilityBridgeMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/KeepNonVisibilityBridgeMethodsTest.java
@@ -56,7 +56,8 @@
.addKeepRules(
"-alwaysinline class * { @"
+ AlwaysInline.class.getTypeName()
- + " !synthetic <methods>; }")
+ + " !synthetic <methods>; }",
+ "-noparametertypestrengthening class * { synthetic <methods>; }")
.enableNeverClassInliningAnnotations()
// TODO(b/120764902): MemberSubject.getOriginalName() is not working without the @NeverMerge
// annotation on DataAdapter.Observer.
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentInterfacesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentInterfacesTest.java
index 497e4df..1d6c93f 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentInterfacesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentInterfacesTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterTypeStrengthening;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestParameters;
import org.junit.Test;
@@ -26,6 +27,7 @@
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoParameterTypeStrengtheningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.addHorizontallyMergedClassesInspector(
@@ -78,7 +80,7 @@
public static class Main {
@NeverInline
- public static void foo(I i) {
+ public static void foo(@NoParameterTypeStrengthening I i) {
i.foo();
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java
index 9a5f503..29e7e0e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterTypeStrengthening;
import com.android.tools.r8.TestParameters;
import org.junit.Test;
@@ -25,6 +26,7 @@
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoParameterTypeStrengtheningAnnotations()
.setMinApi(parameters.getApiLevel())
.addHorizontallyMergedClassesInspector(
inspector ->
@@ -72,7 +74,7 @@
public static class Main {
@NeverInline
- public static void foo(I i) {
+ public static void foo(@NoParameterTypeStrengthening I i) {
i.foo();
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InterfacesVisibilityTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InterfacesVisibilityTest.java
index b03e236..508b10b 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/InterfacesVisibilityTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InterfacesVisibilityTest.java
@@ -32,6 +32,7 @@
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoParameterTypeStrengtheningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java
index 1ec170d..51b6b55 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterTypeStrengthening;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.classmerging.horizontal.HorizontalClassMergingTestBase;
@@ -27,6 +28,7 @@
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoParameterTypeStrengtheningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.addHorizontallyMergedClassesInspector(
@@ -79,7 +81,7 @@
public static class Main {
@NeverInline
- public static void doI(J i) {
+ public static void doI(@NoParameterTypeStrengthening J i) {
i.m();
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/InterfacesVisibilityTestClasses.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/InterfacesVisibilityTestClasses.java
index d5a5c14..c857046 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/InterfacesVisibilityTestClasses.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/InterfacesVisibilityTestClasses.java
@@ -7,12 +7,13 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoParameterTypeStrengthening;
import com.android.tools.r8.NoVerticalClassMerging;
public class InterfacesVisibilityTestClasses {
public static class Invoker {
@NeverInline
- public static void invokeFoo(PackagePrivateInterface i) {
+ public static void invokeFoo(@NoParameterTypeStrengthening PackagePrivateInterface i) {
i.foo();
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
index fba166a..62a44a1 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
@@ -326,7 +326,11 @@
m = clazz.method("int", "inlinable", ImmutableList.of("inlining.A"));
assertCounters(INLINABLE, INLINABLE, countInvokes(inspector, m));
- m = clazz.method("int", "notInlinable", ImmutableList.of("inlining.A"));
+ m =
+ clazz.method(
+ "int",
+ "notInlinable",
+ ImmutableList.of("inlining." + (allowAccessModification ? "B" : "A")));
assertCounters(INLINABLE, NEVER_INLINABLE, countInvokes(inspector, m));
m = clazz.method("int", "notInlinableDueToMissingNpe", ImmutableList.of("inlining.A"));
@@ -338,14 +342,16 @@
NEVER_INLINABLE,
countInvokes(inspector, m));
- m = clazz.method("int", "notInlinableOnThrow", ImmutableList.of("java.lang.Throwable"));
+ m =
+ clazz.method(
+ "int", "notInlinableOnThrow", ImmutableList.of("java.lang.IllegalArgumentException"));
assertCounters(ALWAYS_INLINABLE, NEVER_INLINABLE, countInvokes(inspector, m));
m =
clazz.method(
"int",
"notInlinableDueToMissingNpeBeforeThrow",
- ImmutableList.of("java.lang.Throwable"));
+ ImmutableList.of("java.lang.IllegalArgumentException"));
assertCounters(ALWAYS_INLINABLE, NEVER_INLINABLE * 2, countInvokes(inspector, m));
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
index 5811471..0cd06dc 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterTypeStrengthening;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -43,6 +44,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(InvokeDirectPositiveTest.class)
.addKeepMainRule(MAIN)
+ .enableNoParameterTypeStrengtheningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
@@ -107,6 +109,7 @@
}
@NeverInline
+ @NoParameterTypeStrengthening
private void test(Base arg) {
if (arg instanceof Sub1) {
System.out.println("Sub1");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java
index a32daad..2dafd1b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java
@@ -8,6 +8,7 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterTypeStrengthening;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -42,6 +43,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(InvokeStaticPositiveTest.class)
.addKeepMainRule(MAIN)
+ .enableNoParameterTypeStrengtheningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.enableInliningAnnotations()
.addOptionsModification(
@@ -95,6 +97,7 @@
test(new Sub1()); // calls test with Sub1.
}
+ @NoParameterTypeStrengthening
@NeverInline
static void test(Base arg) {
if (arg instanceof Sub1) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
index 99a3ddf..382be79 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterTypeStrengthening;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -43,6 +44,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(InvokeVirtualPositiveTest.class)
.addKeepMainRule(MAIN)
+ .enableNoParameterTypeStrengtheningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
@@ -110,6 +112,7 @@
@NoVerticalClassMerging
@NeverClassInline
static class A {
+ @NoParameterTypeStrengthening
@NeverInline
void m(Base arg) {
if (arg instanceof Sub1) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsMixedTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsMixedTest.java
index 78a0882..a6f80c4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsMixedTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsMixedTest.java
@@ -78,8 +78,11 @@
Assert.assertTrue(
method.getFinalName().equals("main")
|| (method.getFinalSignature().parameters.length == 1
- && (method.getFinalSignature().parameters[0].equals("int")
- || method.getFinalSignature().parameters[0].equals("java.lang.Object"))));
+ && (method.getFinalSignature().parameters[0].equals("int")
+ || method
+ .getFinalSignature()
+ .parameters[0]
+ .equals("java.lang.Integer"))));
});
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java
index c10d1db..fdefaac 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoParameterTypeStrengthening;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -45,6 +46,7 @@
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
+ .enableNoParameterTypeStrengtheningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
@@ -82,6 +84,7 @@
}
@NeverInline
+ @NoParameterTypeStrengthening
private static void indirection(I obj) {
obj.m();
}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceDefaultBridgeTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceDefaultBridgeTest.java
index e11bdfe..4036e4a 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceDefaultBridgeTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceDefaultBridgeTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoParameterTypeStrengthening;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -49,6 +50,7 @@
.addKeepClassAndMembersRules(I.class)
.enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
+ .enableNoParameterTypeStrengtheningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.run(parameters.getRuntime(), newMainTypeName)
.assertSuccessWithOutputLines("I::foo")
@@ -85,6 +87,7 @@
}
@NeverInline
+ @NoParameterTypeStrengthening
private static void callJ(J j) {
j.foo();
}
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ParameterTypeStrengtheningTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ParameterTypeStrengtheningTest.java
new file mode 100644
index 0000000..91abde9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ParameterTypeStrengtheningTest.java
@@ -0,0 +1,124 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ParameterTypeStrengtheningTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ // TODO(b/173398086): uniqueMethodWithName() does not work with argument changes.
+ .noMinification()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ ClassSubject bClassSubject = inspector.clazz(B.class);
+ assertThat(bClassSubject, isPresent());
+
+ // Method testA(I) should be rewritten to testA(A).
+ MethodSubject testAMethodSubject = mainClassSubject.uniqueMethodWithName("testA");
+ assertThat(testAMethodSubject, isPresent());
+ assertEquals(
+ aClassSubject.getFinalName(),
+ testAMethodSubject.getProgramMethod().getParameter(0).getTypeName());
+
+ // Method testB(I) should be rewritten to testB(B).
+ MethodSubject testBMethodSubject = mainClassSubject.uniqueMethodWithName("testB");
+ assertThat(testBMethodSubject, isPresent());
+ assertEquals(
+ bClassSubject.getFinalName(),
+ testBMethodSubject.getProgramMethod().getParameter(0).getTypeName());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A", "B");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ testA(new A());
+ testB(getB());
+ }
+
+ @NeverInline
+ static void testA(I i) {
+ i.m();
+ }
+
+ @NeverInline
+ static void testB(I i) {
+ i.m();
+ }
+
+ @NeverInline
+ static I getB() {
+ return new B();
+ }
+ }
+
+ interface I {
+
+ void m();
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class A implements I {
+
+ @Override
+ public void m() {
+ System.out.println("A");
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class B implements I {
+
+ @Override
+ public void m() {
+ System.out.println("B");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
index 873b1e4..eeb91af 100644
--- a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static org.hamcrest.CoreMatchers.containsString;
@@ -141,7 +142,11 @@
.inspector();
ClassSubject superInterface1 = inspector.clazz(B112452064SuperInterface1.class);
- assertThat(superInterface1, isPresentAndRenamed());
+ if (enableUnusedInterfaceRemoval && enableVerticalClassMerging) {
+ assertThat(superInterface1, isAbsent());
+ } else {
+ assertThat(superInterface1, isPresentAndRenamed());
+ }
MethodSubject foo = superInterface1.uniqueMethodWithName("foo");
assertThat(foo, not(isPresent()));
ClassSubject superInterface2 = inspector.clazz(B112452064SuperInterface2.class);
diff --git a/tools/git_sync_cl_chain.py b/tools/git_sync_cl_chain.py
index ea67f63..09da33e 100755
--- a/tools/git_sync_cl_chain.py
+++ b/tools/git_sync_cl_chain.py
@@ -44,6 +44,9 @@
help='Delete closed branches',
choices=['y', 'n', 'ask'],
default='ask')
+ result.add_option('--from_branch', '-f',
+ help='Uppermost upstream to sync from',
+ default='main')
result.add_option('--leave_upstream', '--leave-upstream',
help='To not update the upstream of the first open branch',
action='store_true')
@@ -81,14 +84,14 @@
break
assert current_branch is not None
- if current_branch.upstream == None:
+ if is_root_branch(current_branch, options):
print('Nothing to sync')
return
stack = []
while current_branch:
stack.append(current_branch)
- if current_branch.upstream is None:
+ if is_root_branch(current_branch, options):
break
current_branch = get_branch_with_name(current_branch.upstream, branches)
@@ -169,6 +172,9 @@
def get_status_for_current_branch():
return utils.RunCmd(['git', 'cl', 'status', '--field', 'status'], quiet=True)[0].strip()
+def is_root_branch(branch, options):
+ return branch == options.from_branch or branch.upstream is None
+
def pull_for_current_branch(branch, options):
if branch.name == 'main' and options.skip_main:
return