Merge commit '80bb4c3a9fe91ce7bf9c54297894efb8af21dc52' into dev-release
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index 65521a9..959f32b 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -176,6 +176,15 @@
}
}
builders {
+ name: "linux-jdk11"
+ mixins: "linux"
+ mixins: "normal"
+ priority: 26
+ recipe {
+ properties_j: "test_options:[\"--runtimes=jdk11\", \"--no_internal\", \"--one_line_per_test\", \"--archive_failures\"]"
+ }
+ }
+ builders {
name: "linux_release"
mixins: "normal"
mixins: "linux"
@@ -184,6 +193,33 @@
}
}
builders {
+ name: "linux-jdk8_release"
+ mixins: "linux"
+ mixins: "normal"
+ priority: 26
+ recipe {
+ properties_j: "test_options:[\"--runtimes=jdk8\", \"--no_internal\", \"--one_line_per_test\", \"--archive_failures\"]"
+ }
+ }
+ builders {
+ name: "linux-jdk9_release"
+ mixins: "linux"
+ mixins: "normal"
+ priority: 26
+ recipe {
+ properties_j: "test_options:[\"--runtimes=jdk9\", \"--no_internal\", \"--one_line_per_test\", \"--archive_failures\"]"
+ }
+ }
+ builders {
+ name: "linux-jdk11_release"
+ mixins: "linux"
+ mixins: "normal"
+ priority: 26
+ recipe {
+ properties_j: "test_options:[\"--runtimes=jdk11\", \"--no_internal\", \"--one_line_per_test\", \"--archive_failures\"]"
+ }
+ }
+ builders {
name: "linux-android-4.0.4"
mixins: "linux"
mixins: "normal"
diff --git a/infra/config/global/luci-milo.cfg b/infra/config/global/luci-milo.cfg
index 0e7614f..1d4ce51 100644
--- a/infra/config/global/luci-milo.cfg
+++ b/infra/config/global/luci-milo.cfg
@@ -26,6 +26,11 @@
short_name: "jdk9"
}
builders {
+ name: "buildbucket/luci.r8.ci/linux-jdk11"
+ category: "R8"
+ short_name: "jdk11"
+ }
+ builders {
name: "buildbucket/luci.r8.ci/linux-android-4.0.4"
category: "R8"
short_name: "4.0.4"
@@ -111,6 +116,21 @@
short_name: "linux"
}
builders {
+ name: "buildbucket/luci.r8.ci/linux-jdk8_release"
+ category: "R8 release"
+ short_name: "jdk8"
+ }
+ builders {
+ name: "buildbucket/luci.r8.ci/linux-jdk9_release"
+ category: "R8 release"
+ short_name: "jdk9"
+ }
+ builders {
+ name: "buildbucket/luci.r8.ci/linux-jdk11_release"
+ category: "R8 release"
+ short_name: "jdk11"
+ }
+ builders {
name: "buildbucket/luci.r8.ci/linux-android-4.0.4_release"
category: "R8 release"
short_name: "4.0.4"
diff --git a/infra/config/global/luci-scheduler.cfg b/infra/config/global/luci-scheduler.cfg
index f819ee4..edc7dbf 100644
--- a/infra/config/global/luci-scheduler.cfg
+++ b/infra/config/global/luci-scheduler.cfg
@@ -29,6 +29,7 @@
triggers: "linux"
triggers: "linux-jdk8"
triggers: "linux-jdk9"
+ triggers: "linux-jdk11"
triggers: "linux-android-4.0.4"
triggers: "linux-android-4.4.4"
triggers: "linux-android-5.1.1"
@@ -75,6 +76,10 @@
path_regexps: "src/main/java/com/android/tools/r8/Version.java"
}
triggers: "archive_release"
+ triggers: "linux_release"
+ triggers: "linux-jdk8_release"
+ triggers: "linux-jdk9_release"
+ triggers: "linux-jdk11_release"
triggers: "linux-android-4.0.4_release"
triggers: "linux-android-4.4.4_release"
triggers: "linux-android-5.1.1_release"
@@ -85,7 +90,6 @@
triggers: "linux-android-10.0.0_release"
triggers: "linux-internal_release"
triggers: "linux-jctf_release"
- triggers: "linux_release"
triggers: "r8cf-linux-jctf_release"
triggers: "windows_release"
}
@@ -200,6 +204,20 @@
}
job {
+ id: "linux-jdk8_release"
+ acl_sets: "default"
+ triggering_policy: {
+ kind: GREEDY_BATCHING
+ max_concurrent_invocations: 2
+ }
+ buildbucket {
+ server: "cr-buildbucket.appspot.com"
+ bucket: "luci.r8.ci"
+ builder: "linux-jdk8_release"
+ }
+}
+
+job {
id: "linux-jdk9"
acl_sets: "default"
triggering_policy: {
@@ -214,6 +232,48 @@
}
job {
+ id: "linux-jdk9_release"
+ acl_sets: "default"
+ triggering_policy: {
+ kind: GREEDY_BATCHING
+ max_concurrent_invocations: 2
+ }
+ buildbucket {
+ server: "cr-buildbucket.appspot.com"
+ bucket: "luci.r8.ci"
+ builder: "linux-jdk9_release"
+ }
+}
+
+job {
+ id: "linux-jdk11"
+ acl_sets: "default"
+ triggering_policy: {
+ kind: GREEDY_BATCHING
+ max_concurrent_invocations: 2
+ }
+ buildbucket {
+ server: "cr-buildbucket.appspot.com"
+ bucket: "luci.r8.ci"
+ builder: "linux-jdk11"
+ }
+}
+
+job {
+ id: "linux-jdk11_release"
+ acl_sets: "default"
+ triggering_policy: {
+ kind: GREEDY_BATCHING
+ max_concurrent_invocations: 2
+ }
+ buildbucket {
+ server: "cr-buildbucket.appspot.com"
+ bucket: "luci.r8.ci"
+ builder: "linux-jdk11_release"
+ }
+}
+
+job {
id: "linux-android-4.0.4"
acl_sets: "default"
triggering_policy: {
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 9b3f92c..b1c7df4 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -310,6 +310,7 @@
// Since tracing is not lens aware, this needs to be done prior to synthetic finalization
// which will construct a graph lens.
if (!options.mainDexKeepRules.isEmpty()) {
+ appView.dexItemFactory().clearTypeElementsCache();
MainDexInfo mainDexInfo =
new GenerateMainDexList(options)
.traceMainDex(
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 27108a1..af10925 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -430,6 +430,9 @@
private DumpOptions dumpOptions() {
DumpOptions.Builder builder = DumpOptions.builder(Tool.L8);
dumpBaseCommandOptions(builder);
+ if (r8Command != null) {
+ builder.setProguardConfiguration(r8Command.getInternalOptions().getProguardConfiguration());
+ }
return builder.setDesugaredLibraryConfiguration(libraryConfiguration).build();
}
}
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 546fad4..8a4c9cc 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -103,6 +103,7 @@
import com.android.tools.r8.shaking.VerticalClassMergerGraphLens;
import com.android.tools.r8.shaking.WhyAreYouKeepingConsumer;
import com.android.tools.r8.synthesis.SyntheticFinalization;
+import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.CfgPrinter;
@@ -291,6 +292,7 @@
appView = AppView.createForR8(application, mainDexInfo);
appView.setAppServices(AppServices.builder(appView).build());
+ SyntheticItems.collectSyntheticInputs(appView);
}
// Check for potentially having pass-through of Cf-code for kotlin libraries.
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index df1a88b..e0c1cc2 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -297,7 +297,12 @@
() -> {
try {
String content = map.getString();
- builder.setProguardMap(ClassNameMapper.mapperFromString(content));
+ builder.setProguardMap(
+ ClassNameMapper.mapperFromString(
+ content,
+ options.reporter,
+ false,
+ options.testing.enableExperimentalMapFileVersion));
} catch (IOException | ResourceException e) {
throw new CompilationError("Failure to read proguard map file", e, map.getOrigin());
}
diff --git a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
index f4e85a8..2261228 100644
--- a/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
+++ b/src/main/java/com/android/tools/r8/dex/CodeToKeep.java
@@ -79,9 +79,12 @@
@Override
void recordMethod(DexMethod method) {
- if (shouldKeep(method.holder)) {
- keepClass(method.holder);
- toKeep.get(method.holder).methods.add(method);
+ DexType baseType = method.holder.toBaseType(options.dexItemFactory());
+ if (shouldKeep(baseType)) {
+ keepClass(baseType);
+ if (!method.holder.isArrayType()) {
+ toKeep.get(method.holder).methods.add(method);
+ }
}
if (shouldKeep(method.proto.returnType)) {
keepClass(method.proto.returnType);
@@ -95,9 +98,12 @@
@Override
void recordField(DexField field) {
- if (shouldKeep(field.holder)) {
- keepClass(field.holder);
- toKeep.get(field.holder).fields.add(field);
+ DexType baseType = field.holder.toBaseType(options.dexItemFactory());
+ if (shouldKeep(baseType)) {
+ keepClass(baseType);
+ if (!field.holder.isArrayType()) {
+ toKeep.get(field.holder).fields.add(field);
+ }
}
if (shouldKeep(field.type)) {
keepClass(field.type);
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 7321cc0..1cd36de 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -558,6 +558,10 @@
return true;
}
+ public boolean hasClassHierarchy() {
+ return appInfo().hasClassHierarchy();
+ }
+
@SuppressWarnings("unchecked")
public AppView<AppInfoWithClassHierarchy> withClassHierarchy() {
return appInfo.hasClassHierarchy()
@@ -565,6 +569,12 @@
: null;
}
+ @SuppressWarnings("unchecked")
+ public AppView<AppInfo> withoutClassHierarchy() {
+ assert !hasClassHierarchy();
+ return (AppView<AppInfo>) this;
+ }
+
public boolean hasLiveness() {
return appInfo().hasLiveness();
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index ceb543f..646fca7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -536,6 +536,8 @@
public final DexType androidUtilPropertyType =
createStaticallyKnownType("Landroid/util/Property;");
public final DexType androidViewViewType = createStaticallyKnownType("Landroid/view/View;");
+ public final DexType androidUtilSparseArrayType =
+ createStaticallyKnownType("Landroid/util/SparseArray;");
public final StringBuildingMethods stringBuilderMethods =
new StringBuildingMethods(stringBuilderType);
@@ -581,6 +583,8 @@
public final AndroidSystemOsConstantsMembers androidSystemOsConstantsMembers =
new AndroidSystemOsConstantsMembers();
public final AndroidViewViewMembers androidViewViewMembers = new AndroidViewViewMembers();
+ public final AndroidUtilSparseArrayMembers androidUtilSparseArrayMembers =
+ new AndroidUtilSparseArrayMembers();
// java.**
public final JavaIoFileMembers javaIoFileMembers = new JavaIoFileMembers();
@@ -959,6 +963,13 @@
}
}
+ public class AndroidUtilSparseArrayMembers extends LibraryMembers {
+ public final DexMethod put =
+ createMethod(androidUtilSparseArrayType, createProto(voidType, intType, objectType), "put");
+ public final DexMethod set =
+ createMethod(androidUtilSparseArrayType, createProto(voidType, intType, objectType), "set");
+ }
+
public class BooleanMembers extends LibraryMembers {
public final DexField FALSE = createField(boxedBooleanType, boxedBooleanType, "FALSE");
diff --git a/src/main/java/com/android/tools/r8/graph/DexString.java b/src/main/java/com/android/tools/r8/graph/DexString.java
index 031ee9d..af2ad74 100644
--- a/src/main/java/com/android/tools/r8/graph/DexString.java
+++ b/src/main/java/com/android/tools/r8/graph/DexString.java
@@ -40,6 +40,10 @@
return this;
}
+ public int size() {
+ return size;
+ }
+
@Override
public StructuralMapping<DexString> getStructuralMapping() {
// Structural accept is never accessed as all accept methods are defined directly.
diff --git a/src/main/java/com/android/tools/r8/graph/EnclosingMethodAttribute.java b/src/main/java/com/android/tools/r8/graph/EnclosingMethodAttribute.java
index 82f34d7..9cec3cb 100644
--- a/src/main/java/com/android/tools/r8/graph/EnclosingMethodAttribute.java
+++ b/src/main/java/com/android/tools/r8/graph/EnclosingMethodAttribute.java
@@ -55,6 +55,10 @@
return enclosingClass;
}
+ public DexType getEnclosingType() {
+ return enclosingMethod != null ? enclosingMethod.getHolderType() : enclosingClass;
+ }
+
@Override
public int hashCode() {
assert (enclosingClass == null) != (enclosingMethod == null);
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignature.java b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
index bf7c42b..e38a163 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignature.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
@@ -7,11 +7,11 @@
import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromClassBinaryName;
import static com.google.common.base.Predicates.alwaysTrue;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.Reporter;
import com.google.common.collect.ImmutableList;
import java.lang.reflect.GenericSignatureFormatError;
import java.nio.CharBuffer;
@@ -139,6 +139,10 @@
return false;
}
+ default boolean isValid() {
+ return !isInvalid();
+ }
+
DexDefinitionSignature<T> toInvalid();
}
@@ -153,6 +157,7 @@
this.name = name;
this.classBound = classBound;
this.interfaceBounds = interfaceBounds;
+ assert interfaceBounds != null;
}
public String getName() {
@@ -516,7 +521,9 @@
this.type = type;
this.typeArguments = typeArguments;
this.enclosingTypeSignature = enclosingTypeSignature;
+ assert type != DexItemFactory.nullValueType || indicator == WildcardIndicator.NOT_AN_ARGUMENT;
assert typeArguments.stream().allMatch(FieldTypeSignature::isArgument);
+ assert typeArguments.stream().allMatch(FieldTypeSignature::hasSignature);
}
public DexType type() {
@@ -540,6 +547,7 @@
@Override
public ClassTypeSignature asArgument(WildcardIndicator indicator) {
assert indicator != WildcardIndicator.NOT_AN_ARGUMENT;
+ assert hasSignature();
return new ClassTypeSignature(type, typeArguments, enclosingTypeSignature, indicator);
}
@@ -857,7 +865,7 @@
String signature,
Origin origin,
DexItemFactory factory,
- Reporter reporter) {
+ DiagnosticsHandler diagnosticsHandler) {
if (signature == null || signature.isEmpty()) {
return ClassSignature.NO_CLASS_SIGNATURE;
}
@@ -865,7 +873,7 @@
try {
return parser.parseClassSignature(signature);
} catch (GenericSignatureFormatError e) {
- reporter.warning(
+ diagnosticsHandler.warning(
GenericSignatureDiagnostic.invalidClassSignature(signature, className, origin, e));
return ClassSignature.NO_CLASS_SIGNATURE;
}
@@ -876,7 +884,7 @@
String signature,
Origin origin,
DexItemFactory factory,
- Reporter reporter) {
+ DiagnosticsHandler diagnosticsHandler) {
if (signature == null || signature.isEmpty()) {
return NO_FIELD_TYPE_SIGNATURE;
}
@@ -884,7 +892,7 @@
try {
return parser.parseFieldTypeSignature(signature);
} catch (GenericSignatureFormatError e) {
- reporter.warning(
+ diagnosticsHandler.warning(
GenericSignatureDiagnostic.invalidFieldSignature(signature, fieldName, origin, e));
return GenericSignature.NO_FIELD_TYPE_SIGNATURE;
}
@@ -895,7 +903,7 @@
String signature,
Origin origin,
DexItemFactory factory,
- Reporter reporter) {
+ DiagnosticsHandler diagnosticsHandler) {
if (signature == null || signature.isEmpty()) {
return MethodTypeSignature.NO_METHOD_TYPE_SIGNATURE;
}
@@ -903,7 +911,7 @@
try {
return parser.parseMethodTypeSignature(signature);
} catch (GenericSignatureFormatError e) {
- reporter.warning(
+ diagnosticsHandler.warning(
GenericSignatureDiagnostic.invalidMethodSignature(signature, methodName, origin, e));
return MethodTypeSignature.NO_METHOD_TYPE_SIGNATURE;
}
@@ -1071,7 +1079,7 @@
builder.add(parseFieldTypeSignature());
}
if (builder == null) {
- return new FormalTypeParameter(typeParameterIdentifier, classBound, null);
+ return new FormalTypeParameter(typeParameterIdentifier, classBound, EMPTY_TYPE_ARGUMENTS);
}
return new FormalTypeParameter(typeParameterIdentifier, classBound, builder.build());
}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java
index ea66ba7..e889a40 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureEnqueuerAnalysis.java
@@ -4,17 +4,9 @@
package com.android.tools.r8.graph;
-import com.android.tools.r8.graph.GenericSignature.ClassSignature;
-import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
-import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
-import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
-import com.android.tools.r8.graph.GenericSignature.ReturnType;
-import com.android.tools.r8.graph.GenericSignature.TypeSignature;
import com.android.tools.r8.graph.analysis.EnqueuerAnalysis;
import com.android.tools.r8.shaking.Enqueuer.EnqueuerDefinitionSupplier;
import com.android.tools.r8.shaking.EnqueuerWorklist;
-import java.util.List;
public class GenericSignatureEnqueuerAnalysis extends EnqueuerAnalysis {
@@ -26,164 +18,20 @@
@Override
public void processNewlyLiveClass(DexProgramClass clazz, EnqueuerWorklist worklist) {
- new GenericSignatureTypeVisitor(clazz, enqueuerDefinitionSupplier)
+ new GenericSignatureTypeVisitor(clazz, enqueuerDefinitionSupplier::definitionFor)
.visitClassSignature(clazz.getClassSignature());
}
@Override
public void processNewlyLiveField(ProgramField field, ProgramDefinition context) {
- new GenericSignatureTypeVisitor(context, enqueuerDefinitionSupplier)
+ new GenericSignatureTypeVisitor(context, enqueuerDefinitionSupplier::definitionFor)
.visitFieldTypeSignature(field.getDefinition().getGenericSignature());
}
@Override
public void processNewlyLiveMethod(ProgramMethod method, ProgramDefinition context) {
- new GenericSignatureTypeVisitor(context, enqueuerDefinitionSupplier)
+ new GenericSignatureTypeVisitor(context, enqueuerDefinitionSupplier::definitionFor)
.visitMethodSignature(method.getDefinition().getGenericSignature());
}
- private static class GenericSignatureTypeVisitor implements GenericSignatureVisitor {
-
- private final ProgramDefinition context;
- private final EnqueuerDefinitionSupplier enqueuerDefinitionSupplier;
-
- private GenericSignatureTypeVisitor(
- ProgramDefinition context, EnqueuerDefinitionSupplier enqueuerDefinitionSupplier) {
- this.context = context;
- this.enqueuerDefinitionSupplier = enqueuerDefinitionSupplier;
- }
-
- @Override
- public ClassSignature visitClassSignature(ClassSignature classSignature) {
- if (classSignature.hasNoSignature()) {
- return classSignature;
- }
- return classSignature.visit(this);
- }
-
- @Override
- public MethodTypeSignature visitMethodSignature(MethodTypeSignature methodSignature) {
- if (methodSignature.hasNoSignature()) {
- return methodSignature;
- }
- return methodSignature.visit(this);
- }
-
- @Override
- public FieldTypeSignature visitFieldTypeSignature(FieldTypeSignature fieldSignature) {
- if (fieldSignature.hasNoSignature()) {
- return fieldSignature;
- }
- if (fieldSignature.isStar()) {
- return fieldSignature;
- }
- if (fieldSignature.isTypeVariableSignature()) {
- return fieldSignature;
- }
- if (fieldSignature.isArrayTypeSignature()) {
- fieldSignature.asArrayTypeSignature().visit(this);
- return fieldSignature;
- }
- assert fieldSignature.isClassTypeSignature();
- return fieldSignature.asClassTypeSignature().visit(this);
- }
-
- @Override
- public List<FormalTypeParameter> visitFormalTypeParameters(
- List<FormalTypeParameter> formalTypeParameters) {
- formalTypeParameters.forEach(formalTypeParameter -> formalTypeParameter.visit(this));
- return formalTypeParameters;
- }
-
- @Override
- public FieldTypeSignature visitClassBound(FieldTypeSignature fieldSignature) {
- return visitFieldTypeSignature(fieldSignature);
- }
-
- @Override
- public List<FieldTypeSignature> visitInterfaceBounds(List<FieldTypeSignature> fieldSignatures) {
- if (fieldSignatures == null) {
- return null;
- }
- fieldSignatures.forEach(this::visitInterfaceBound);
- return fieldSignatures;
- }
-
- @Override
- public FieldTypeSignature visitInterfaceBound(FieldTypeSignature fieldSignature) {
- return visitFieldTypeSignature(fieldSignature);
- }
-
- @Override
- public ClassTypeSignature visitSuperClass(ClassTypeSignature classTypeSignature) {
- return classTypeSignature.visit(this);
- }
-
- @Override
- public List<ClassTypeSignature> visitSuperInterfaces(
- List<ClassTypeSignature> interfaceSignatures) {
- if (interfaceSignatures == null) {
- return null;
- }
- interfaceSignatures.forEach(this::visitSuperInterface);
- return interfaceSignatures;
- }
-
- @Override
- public ClassTypeSignature visitSuperInterface(ClassTypeSignature classTypeSignature) {
- return classTypeSignature.visit(this);
- }
-
- @Override
- public TypeSignature visitTypeSignature(TypeSignature typeSignature) {
- if (typeSignature.isBaseTypeSignature()) {
- return typeSignature;
- }
- assert typeSignature.isFieldTypeSignature();
- return visitFieldTypeSignature(typeSignature.asFieldTypeSignature());
- }
-
- @Override
- public ClassTypeSignature visitSimpleClass(ClassTypeSignature classTypeSignature) {
- return classTypeSignature.visit(this);
- }
-
- @Override
- public ReturnType visitReturnType(ReturnType returnType) {
- if (returnType.isVoidDescriptor()) {
- return returnType;
- }
- visitTypeSignature(returnType.typeSignature);
- return returnType;
- }
-
- @Override
- public List<TypeSignature> visitMethodTypeSignatures(List<TypeSignature> typeSignatures) {
- typeSignatures.forEach(this::visitTypeSignature);
- return typeSignatures;
- }
-
- @Override
- public List<TypeSignature> visitThrowsSignatures(List<TypeSignature> typeSignatures) {
- typeSignatures.forEach(this::visitTypeSignature);
- return typeSignatures;
- }
-
- @Override
- public List<FieldTypeSignature> visitTypeArguments(List<FieldTypeSignature> typeArguments) {
- typeArguments.forEach(this::visitFieldTypeSignature);
- return typeArguments;
- }
-
- @Override
- public FormalTypeParameter visitFormalTypeParameter(FormalTypeParameter formalTypeParameter) {
- return formalTypeParameter.visit(this);
- }
-
- @Override
- public DexType visitType(DexType type) {
- enqueuerDefinitionSupplier.definitionFor(type, context);
- return type;
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignaturePartialTypeArgumentApplier.java b/src/main/java/com/android/tools/r8/graph/GenericSignaturePartialTypeArgumentApplier.java
new file mode 100644
index 0000000..2c17201
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignaturePartialTypeArgumentApplier.java
@@ -0,0 +1,206 @@
+// Copyright (c) 2021, 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.graph;
+
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.ReturnType;
+import com.android.tools.r8.graph.GenericSignature.StarFieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.TypeSignature;
+import com.android.tools.r8.graph.GenericSignature.WildcardIndicator;
+import com.android.tools.r8.utils.ListUtils;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class GenericSignaturePartialTypeArgumentApplier implements GenericSignatureVisitor {
+
+ private final Map<String, DexType> substitutions;
+ private final DexType objectType;
+ private final Set<String> introducedClassTypeVariables = new HashSet<>();
+ private final Set<String> introducedMethodTypeVariables = new HashSet<>();
+
+ // Wildcards can only be called be used in certain positions:
+ // https://docs.oracle.com/javase/tutorial/java/generics/wildcards.html
+ private boolean canUseWildcardInArguments = true;
+
+ private GenericSignaturePartialTypeArgumentApplier(
+ Map<String, DexType> substitutions, DexType objectType) {
+ this.substitutions = substitutions;
+ this.objectType = objectType;
+ }
+
+ public static GenericSignaturePartialTypeArgumentApplier build(
+ AppView<?> appView, ClassSignature classSignature, Map<String, DexType> substitutions) {
+ GenericSignaturePartialTypeArgumentApplier applier =
+ new GenericSignaturePartialTypeArgumentApplier(
+ substitutions, appView.dexItemFactory().objectType);
+ classSignature.formalTypeParameters.forEach(
+ parameter -> applier.introducedClassTypeVariables.add(parameter.name));
+ return applier;
+ }
+
+ @Override
+ public ClassSignature visitClassSignature(ClassSignature classSignature) {
+ return classSignature.visit(this);
+ }
+
+ @Override
+ public MethodTypeSignature visitMethodSignature(MethodTypeSignature methodSignature) {
+ assert introducedMethodTypeVariables.isEmpty();
+ methodSignature.formalTypeParameters.forEach(
+ parameter -> introducedMethodTypeVariables.add(parameter.name));
+ MethodTypeSignature rewritten = methodSignature.visit(this);
+ introducedMethodTypeVariables.clear();
+ return rewritten;
+ }
+
+ @Override
+ public DexType visitType(DexType type) {
+ return type;
+ }
+
+ @Override
+ public TypeSignature visitTypeSignature(TypeSignature typeSignature) {
+ if (typeSignature.isBaseTypeSignature()) {
+ return typeSignature;
+ }
+ return visitFieldTypeSignature(typeSignature.asFieldTypeSignature());
+ }
+
+ @Override
+ public FormalTypeParameter visitFormalTypeParameter(FormalTypeParameter formalTypeParameter) {
+ FormalTypeParameter rewritten = formalTypeParameter.visit(this);
+ // Guard against no information being present in bounds.
+ assert (rewritten.getClassBound() != null && rewritten.getClassBound().hasSignature())
+ || !rewritten.getInterfaceBounds().isEmpty();
+ return rewritten;
+ }
+
+ @Override
+ public List<FieldTypeSignature> visitInterfaceBounds(List<FieldTypeSignature> fieldSignatures) {
+ if (fieldSignatures == null || fieldSignatures.isEmpty()) {
+ return fieldSignatures;
+ }
+ return ListUtils.mapOrElse(fieldSignatures, this::visitFieldTypeSignature);
+ }
+
+ @Override
+ public List<ClassTypeSignature> visitSuperInterfaces(
+ List<ClassTypeSignature> interfaceSignatures) {
+ if (interfaceSignatures.isEmpty()) {
+ return interfaceSignatures;
+ }
+ canUseWildcardInArguments = false;
+ List<ClassTypeSignature> map =
+ ListUtils.mapOrElse(interfaceSignatures, this::visitSuperInterface);
+ canUseWildcardInArguments = true;
+ return map;
+ }
+
+ @Override
+ public List<FieldTypeSignature> visitTypeArguments(List<FieldTypeSignature> typeArguments) {
+ if (typeArguments.isEmpty()) {
+ return typeArguments;
+ }
+ return ListUtils.mapOrElse(typeArguments, this::visitFieldTypeSignature);
+ }
+
+ @Override
+ public ClassTypeSignature visitSuperInterface(ClassTypeSignature classTypeSignature) {
+ return classTypeSignature.visit(this);
+ }
+
+ @Override
+ public FieldTypeSignature visitClassBound(FieldTypeSignature fieldSignature) {
+ return visitFieldTypeSignature(fieldSignature);
+ }
+
+ @Override
+ public FieldTypeSignature visitInterfaceBound(FieldTypeSignature fieldSignature) {
+ return visitFieldTypeSignature(fieldSignature);
+ }
+
+ @Override
+ public ClassTypeSignature visitSimpleClass(ClassTypeSignature classTypeSignature) {
+ return classTypeSignature.visit(this);
+ }
+
+ @Override
+ public List<TypeSignature> visitThrowsSignatures(List<TypeSignature> typeSignatures) {
+ if (typeSignatures.isEmpty()) {
+ return typeSignatures;
+ }
+ return ListUtils.mapOrElse(typeSignatures, this::visitTypeSignature);
+ }
+
+ @Override
+ public ReturnType visitReturnType(ReturnType returnType) {
+ if (returnType.isVoidDescriptor()) {
+ return returnType;
+ }
+ TypeSignature originalSignature = returnType.typeSignature;
+ TypeSignature rewrittenSignature = visitTypeSignature(originalSignature);
+ if (originalSignature == rewrittenSignature) {
+ return returnType;
+ }
+ return new ReturnType(rewrittenSignature);
+ }
+
+ @Override
+ public List<FormalTypeParameter> visitFormalTypeParameters(
+ List<FormalTypeParameter> formalTypeParameters) {
+ if (formalTypeParameters.isEmpty()) {
+ return formalTypeParameters;
+ }
+ return ListUtils.mapOrElse(formalTypeParameters, this::visitFormalTypeParameter);
+ }
+
+ @Override
+ public List<TypeSignature> visitMethodTypeSignatures(List<TypeSignature> typeSignatures) {
+ if (typeSignatures.isEmpty()) {
+ return typeSignatures;
+ }
+ return ListUtils.mapOrElse(typeSignatures, this::visitTypeSignature);
+ }
+
+ @Override
+ public ClassTypeSignature visitSuperClass(ClassTypeSignature classTypeSignature) {
+ canUseWildcardInArguments = false;
+ ClassTypeSignature visit = classTypeSignature.visit(this);
+ canUseWildcardInArguments = true;
+ return visit;
+ }
+
+ @Override
+ public FieldTypeSignature visitFieldTypeSignature(FieldTypeSignature fieldSignature) {
+ if (fieldSignature.isStar()) {
+ return fieldSignature;
+ } else if (fieldSignature.isClassTypeSignature()) {
+ return fieldSignature.asClassTypeSignature().visit(this);
+ } else if (fieldSignature.isArrayTypeSignature()) {
+ return fieldSignature.asArrayTypeSignature().visit(this);
+ } else {
+ assert fieldSignature.isTypeVariableSignature();
+ String typeVariableName = fieldSignature.asTypeVariableSignature().typeVariable();
+ if (substitutions.containsKey(typeVariableName)
+ && !introducedClassTypeVariables.contains(typeVariableName)
+ && !introducedMethodTypeVariables.contains(typeVariableName)) {
+ DexType substitution = substitutions.get(typeVariableName);
+ if (substitution == null) {
+ substitution = objectType;
+ }
+ return substitution == objectType && canUseWildcardInArguments
+ ? StarFieldTypeSignature.getStarFieldTypeSignature()
+ : new ClassTypeSignature(substitution).asArgument(WildcardIndicator.NONE);
+ }
+ return fieldSignature;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
index 6d31cca..6df0d8b 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
@@ -4,10 +4,7 @@
package com.android.tools.r8.graph;
-import static com.android.tools.r8.graph.GenericSignature.EMPTY_SUPER_INTERFACES;
import static com.android.tools.r8.graph.GenericSignature.EMPTY_TYPE_ARGUMENTS;
-import static com.android.tools.r8.graph.GenericSignature.EMPTY_TYPE_PARAMS;
-import static com.android.tools.r8.graph.GenericSignature.EMPTY_TYPE_SIGNATURES;
import static com.google.common.base.Predicates.alwaysFalse;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
@@ -28,9 +25,9 @@
private final DexItemFactory factory;
private final Predicate<DexType> wasPruned;
private final Function<DexType, DexType> lookupType;
- private final DexProgramClass context;
+ private final DexType context;
- private final FieldTypeSignature objectTypeSignature;
+ private final ClassTypeSignature objectTypeSignature;
public GenericSignatureTypeRewriter(AppView<?> appView, DexProgramClass context) {
this(
@@ -39,14 +36,14 @@
? appView.appInfo().withLiveness()::wasPruned
: alwaysFalse(),
appView.graphLens()::lookupType,
- context);
+ context.getType());
}
public GenericSignatureTypeRewriter(
DexItemFactory factory,
Predicate<DexType> wasPruned,
Function<DexType, DexType> lookupType,
- DexProgramClass context) {
+ DexType context) {
this.factory = factory;
this.wasPruned = wasPruned;
this.lookupType = lookupType;
@@ -120,20 +117,28 @@
public List<FormalTypeParameter> visitFormalTypeParameters(
List<FormalTypeParameter> formalTypeParameters) {
if (formalTypeParameters.isEmpty()) {
- return EMPTY_TYPE_PARAMS;
+ return formalTypeParameters;
}
return ListUtils.mapOrElse(formalTypeParameters, this::visitFormalTypeParameter);
}
@Override
public FormalTypeParameter visitFormalTypeParameter(FormalTypeParameter formalTypeParameter) {
- return formalTypeParameter.visit(this);
+ FormalTypeParameter rewritten = formalTypeParameter.visit(this);
+ // Guard against no information being present in bounds.
+ boolean isEmptyClassBound =
+ rewritten.getClassBound() == null || rewritten.getClassBound().hasNoSignature();
+ if (isEmptyClassBound && rewritten.getInterfaceBounds().isEmpty()) {
+ return new FormalTypeParameter(
+ formalTypeParameter.getName(), objectTypeSignature, rewritten.getInterfaceBounds());
+ }
+ return rewritten;
}
@Override
public ClassTypeSignature visitSuperClass(ClassTypeSignature classTypeSignature) {
ClassTypeSignature rewritten = classTypeSignature.visit(this);
- return rewritten == null || rewritten.type() == context.type
+ return rewritten == null || rewritten.type() == context
? new ClassTypeSignature(factory.objectType, EMPTY_TYPE_ARGUMENTS)
: rewritten;
}
@@ -142,7 +147,7 @@
public List<ClassTypeSignature> visitSuperInterfaces(
List<ClassTypeSignature> interfaceSignatures) {
if (interfaceSignatures.isEmpty()) {
- return EMPTY_SUPER_INTERFACES;
+ return interfaceSignatures;
}
return ListUtils.mapOrElse(interfaceSignatures, this::visitSuperInterface);
}
@@ -150,13 +155,13 @@
@Override
public ClassTypeSignature visitSuperInterface(ClassTypeSignature classTypeSignature) {
ClassTypeSignature rewritten = classTypeSignature.visit(this);
- return rewritten == null || rewritten.type() == context.type ? null : rewritten;
+ return rewritten == null || rewritten.type() == context ? null : rewritten;
}
@Override
public List<TypeSignature> visitMethodTypeSignatures(List<TypeSignature> typeSignatures) {
if (typeSignatures.isEmpty()) {
- return EMPTY_TYPE_SIGNATURES;
+ return typeSignatures;
}
return ListUtils.mapOrElse(
typeSignatures,
@@ -186,7 +191,7 @@
@Override
public List<TypeSignature> visitThrowsSignatures(List<TypeSignature> typeSignatures) {
if (typeSignatures.isEmpty()) {
- return EMPTY_TYPE_SIGNATURES;
+ return typeSignatures;
}
// If a throwing type is no longer found we remove it from the signature.
return ListUtils.mapOrElse(typeSignatures, this::visitTypeSignature);
@@ -199,11 +204,8 @@
@Override
public List<FieldTypeSignature> visitInterfaceBounds(List<FieldTypeSignature> fieldSignatures) {
- if (fieldSignatures == null) {
- return null;
- }
- if (fieldSignatures.isEmpty()) {
- return EMPTY_TYPE_ARGUMENTS;
+ if (fieldSignatures == null || fieldSignatures.isEmpty()) {
+ return fieldSignatures;
}
return ListUtils.mapOrElse(fieldSignatures, this::visitFieldTypeSignature);
}
@@ -221,7 +223,7 @@
@Override
public List<FieldTypeSignature> visitTypeArguments(List<FieldTypeSignature> typeArguments) {
if (typeArguments.isEmpty()) {
- return EMPTY_TYPE_ARGUMENTS;
+ return typeArguments;
}
return ListUtils.mapOrElse(
typeArguments,
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeVariableRemover.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeVariableRemover.java
new file mode 100644
index 0000000..f33c9c3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeVariableRemover.java
@@ -0,0 +1,125 @@
+// Copyright (c) 2021, 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.graph;
+
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Predicate;
+
+public class GenericSignatureTypeVariableRemover {
+
+ private final AppView<?> appView;
+ private final Predicate<InnerClassAttribute> innerClassPruned;
+ private final Predicate<EnclosingMethodAttribute> enclosingClassOrMethodPruned;
+
+ public GenericSignatureTypeVariableRemover(
+ AppView<?> appView,
+ Predicate<InnerClassAttribute> innerClassPruned,
+ Predicate<EnclosingMethodAttribute> enclosingClassOrMethodPruned) {
+ this.appView = appView;
+ this.innerClassPruned = innerClassPruned;
+ this.enclosingClassOrMethodPruned = enclosingClassOrMethodPruned;
+ }
+
+ public void removeDeadGenericSignatureTypeVariables(DexProgramClass clazz) {
+ if (clazz.getClassSignature().hasNoSignature() || clazz.getClassSignature().isInvalid()) {
+ return;
+ }
+ Map<String, DexType> substitutions = new HashMap<>();
+ getPrunedTypeParameters(clazz, substitutions, false);
+ if (substitutions.isEmpty()) {
+ return;
+ }
+ GenericSignaturePartialTypeArgumentApplier genericSignatureTypeArgumentApplier =
+ GenericSignaturePartialTypeArgumentApplier.build(
+ appView, clazz.getClassSignature(), substitutions);
+ clazz.setClassSignature(
+ genericSignatureTypeArgumentApplier.visitClassSignature(clazz.getClassSignature()));
+ clazz
+ .methods()
+ .forEach(
+ method -> {
+ if (method.getGenericSignature().hasSignature()
+ && method.getGenericSignature().isValid()
+ && method.isVirtualMethod()) {
+ method.setGenericSignature(
+ genericSignatureTypeArgumentApplier.visitMethodSignature(
+ method.getGenericSignature()));
+ }
+ });
+ clazz
+ .instanceFields()
+ .forEach(
+ field -> {
+ if (field.getGenericSignature().hasSignature()
+ && field.getGenericSignature().isValid()) {
+ field.setGenericSignature(
+ genericSignatureTypeArgumentApplier.visitFieldTypeSignature(
+ field.getGenericSignature()));
+ }
+ });
+ }
+
+ private void getPrunedTypeParameters(
+ DexClass clazz, Map<String, DexType> substitutions, boolean seenPruned) {
+ InnerClassAttribute innerClassAttribute = clazz.getInnerClassAttributeForThisClass();
+ if (innerClassAttribute != null
+ && innerClassAttribute.getOuter() != null
+ && (seenPruned || innerClassPruned.test(innerClassAttribute))) {
+ DexClass outerClass = appView.definitionFor(innerClassAttribute.getOuter());
+ if (outerClass != null && outerClass.getClassSignature().isValid()) {
+ updateMap(outerClass.getClassSignature().getFormalTypeParameters(), substitutions);
+ getPrunedTypeParameters(outerClass, substitutions, true);
+ }
+ }
+ if (clazz.getEnclosingMethodAttribute() != null
+ && (seenPruned || enclosingClassOrMethodPruned.test(clazz.getEnclosingMethodAttribute()))) {
+ DexClass outerClass =
+ appView.definitionFor(clazz.getEnclosingMethodAttribute().getEnclosingType());
+ if (outerClass == null) {
+ return;
+ }
+ if (clazz.getEnclosingMethodAttribute().getEnclosingMethod() != null) {
+ DexEncodedMethod enclosingMethod =
+ outerClass.lookupMethod(clazz.getEnclosingMethodAttribute().getEnclosingMethod());
+ if (enclosingMethod != null) {
+ updateMap(enclosingMethod.getGenericSignature().getFormalTypeParameters(), substitutions);
+ if (enclosingMethod.isStatic()) {
+ return;
+ }
+ }
+ }
+ if (outerClass.getClassSignature().isValid()) {
+ updateMap(outerClass.getClassSignature().getFormalTypeParameters(), substitutions);
+ }
+ getPrunedTypeParameters(outerClass, substitutions, true);
+ }
+ }
+
+ private void updateMap(
+ List<FormalTypeParameter> formalTypeParameters, Map<String, DexType> substitutions) {
+ // We are updating the map going from inner most to outer, thus the any overriding formal type
+ // parameters will be in the substitution map already.
+ formalTypeParameters.forEach(
+ parameter -> {
+ if (substitutions.containsKey(parameter.getName())) {
+ return;
+ }
+ // The null substitution will use the wildcard as argument, which is smaller than using
+ // Ljava/lang/Object;
+ DexType substitution = null;
+ FieldTypeSignature classBound = parameter.getClassBound();
+ if (classBound != null
+ && classBound.hasSignature()
+ && classBound.isClassTypeSignature()) {
+ substitution = classBound.asClassTypeSignature().type();
+ }
+ substitutions.put(parameter.getName(), substitution);
+ });
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeVisitor.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeVisitor.java
new file mode 100644
index 0000000..dff8d5d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeVisitor.java
@@ -0,0 +1,160 @@
+// Copyright (c) 2021, 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.graph;
+
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.ReturnType;
+import com.android.tools.r8.graph.GenericSignature.TypeSignature;
+import java.util.List;
+import java.util.function.BiConsumer;
+
+class GenericSignatureTypeVisitor implements GenericSignatureVisitor {
+
+ private final ProgramDefinition context;
+ private final BiConsumer<DexType, ProgramDefinition> visitedTypeConsumer;
+
+ GenericSignatureTypeVisitor(
+ ProgramDefinition context, BiConsumer<DexType, ProgramDefinition> visitedTypeConsumer) {
+ this.context = context;
+ this.visitedTypeConsumer = visitedTypeConsumer;
+ }
+
+ @Override
+ public ClassSignature visitClassSignature(ClassSignature classSignature) {
+ if (classSignature.hasNoSignature()) {
+ return classSignature;
+ }
+ return classSignature.visit(this);
+ }
+
+ @Override
+ public MethodTypeSignature visitMethodSignature(MethodTypeSignature methodSignature) {
+ if (methodSignature.hasNoSignature()) {
+ return methodSignature;
+ }
+ return methodSignature.visit(this);
+ }
+
+ @Override
+ public FieldTypeSignature visitFieldTypeSignature(FieldTypeSignature fieldSignature) {
+ if (fieldSignature.hasNoSignature()) {
+ return fieldSignature;
+ }
+ if (fieldSignature.isStar()) {
+ return fieldSignature;
+ }
+ if (fieldSignature.isTypeVariableSignature()) {
+ return fieldSignature;
+ }
+ if (fieldSignature.isArrayTypeSignature()) {
+ fieldSignature.asArrayTypeSignature().visit(this);
+ return fieldSignature;
+ }
+ assert fieldSignature.isClassTypeSignature();
+ return fieldSignature.asClassTypeSignature().visit(this);
+ }
+
+ @Override
+ public List<FormalTypeParameter> visitFormalTypeParameters(
+ List<FormalTypeParameter> formalTypeParameters) {
+ formalTypeParameters.forEach(this::visitFormalTypeParameter);
+ return formalTypeParameters;
+ }
+
+ @Override
+ public FieldTypeSignature visitClassBound(FieldTypeSignature fieldSignature) {
+ return visitFieldTypeSignature(fieldSignature);
+ }
+
+ @Override
+ public List<FieldTypeSignature> visitInterfaceBounds(List<FieldTypeSignature> fieldSignatures) {
+ if (fieldSignatures == null) {
+ return null;
+ }
+ fieldSignatures.forEach(this::visitInterfaceBound);
+ return fieldSignatures;
+ }
+
+ @Override
+ public FieldTypeSignature visitInterfaceBound(FieldTypeSignature fieldSignature) {
+ return visitFieldTypeSignature(fieldSignature);
+ }
+
+ @Override
+ public ClassTypeSignature visitSuperClass(ClassTypeSignature classTypeSignature) {
+ return classTypeSignature.visit(this);
+ }
+
+ @Override
+ public List<ClassTypeSignature> visitSuperInterfaces(
+ List<ClassTypeSignature> interfaceSignatures) {
+ if (interfaceSignatures == null) {
+ return null;
+ }
+ interfaceSignatures.forEach(this::visitSuperInterface);
+ return interfaceSignatures;
+ }
+
+ @Override
+ public ClassTypeSignature visitSuperInterface(ClassTypeSignature classTypeSignature) {
+ return classTypeSignature.visit(this);
+ }
+
+ @Override
+ public TypeSignature visitTypeSignature(TypeSignature typeSignature) {
+ if (typeSignature.isBaseTypeSignature()) {
+ return typeSignature;
+ }
+ assert typeSignature.isFieldTypeSignature();
+ return visitFieldTypeSignature(typeSignature.asFieldTypeSignature());
+ }
+
+ @Override
+ public ClassTypeSignature visitSimpleClass(ClassTypeSignature classTypeSignature) {
+ return classTypeSignature.visit(this);
+ }
+
+ @Override
+ public ReturnType visitReturnType(ReturnType returnType) {
+ if (returnType.isVoidDescriptor()) {
+ return returnType;
+ }
+ visitTypeSignature(returnType.typeSignature);
+ return returnType;
+ }
+
+ @Override
+ public List<TypeSignature> visitMethodTypeSignatures(List<TypeSignature> typeSignatures) {
+ typeSignatures.forEach(this::visitTypeSignature);
+ return typeSignatures;
+ }
+
+ @Override
+ public List<TypeSignature> visitThrowsSignatures(List<TypeSignature> typeSignatures) {
+ typeSignatures.forEach(this::visitTypeSignature);
+ return typeSignatures;
+ }
+
+ @Override
+ public List<FieldTypeSignature> visitTypeArguments(List<FieldTypeSignature> typeArguments) {
+ typeArguments.forEach(this::visitFieldTypeSignature);
+ return typeArguments;
+ }
+
+ @Override
+ public FormalTypeParameter visitFormalTypeParameter(FormalTypeParameter formalTypeParameter) {
+ return formalTypeParameter.visit(this);
+ }
+
+ @Override
+ public DexType visitType(DexType type) {
+ visitedTypeConsumer.accept(type, context);
+ return type;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
index 23cb57c..a429d5c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
@@ -4,13 +4,13 @@
package com.android.tools.r8.horizontalclassmerging;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMergerGraphLens.Builder;
import com.android.tools.r8.horizontalclassmerging.policies.SameInstanceFields.InstanceFieldInfo;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.IterableUtils;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
@@ -22,7 +22,7 @@
public class ClassInstanceFieldsMerger {
- private final AppView<AppInfoWithLiveness> appView;
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final Builder lensBuilder;
private DexEncodedField classIdField;
@@ -31,7 +31,7 @@
private final Map<DexEncodedField, List<DexEncodedField>> fieldMappings = new LinkedHashMap<>();
public ClassInstanceFieldsMerger(
- AppView<AppInfoWithLiveness> appView,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
HorizontalClassMergerGraphLens.Builder lensBuilder,
MergeGroup group) {
this.appView = appView;
@@ -128,8 +128,9 @@
public DexEncodedField[] merge() {
List<DexEncodedField> newFields = new ArrayList<>();
- assert classIdField != null;
- newFields.add(classIdField);
+ if (classIdField != null) {
+ newFields.add(classIdField);
+ }
fieldMappings.forEach(
(targetField, oldFields) ->
newFields.add(mergeSourceFieldsToTargetField(targetField, oldFields)));
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index a47acd6..23b27f3 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexAnnotationSet;
@@ -17,6 +18,7 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexMethodSignature;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
@@ -30,10 +32,8 @@
import com.android.tools.r8.ir.analysis.value.NumberFromIntervalValue;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.KeepClassInfo;
import com.android.tools.r8.utils.IterableUtils;
-import com.android.tools.r8.utils.MethodSignatureEquivalence;
-import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
@@ -41,6 +41,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -58,7 +59,7 @@
private static final OptimizationFeedback feedback = OptimizationFeedbackSimple.getInstance();
- private final AppView<AppInfoWithLiveness> appView;
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final MergeGroup group;
private final DexItemFactory dexItemFactory;
private final ClassInitializerSynthesizedCode classInitializerSynthesizedCode;
@@ -72,7 +73,7 @@
private final Collection<ConstructorMerger> constructorMergers;
private ClassMerger(
- AppView<AppInfoWithLiveness> appView,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
HorizontalClassMergerGraphLens.Builder lensBuilder,
MergeGroup group,
Collection<VirtualMethodMerger> virtualMethodMergers,
@@ -191,6 +192,8 @@
}
void appendClassIdField() {
+ assert appView.hasLiveness();
+
boolean deprecated = false;
boolean d8R8Synthesized = true;
DexEncodedField classIdField =
@@ -211,7 +214,7 @@
// be able to recognize that {0, 1, 2, 3} is useless, we record that the value of the field is
// known to be in [0; 3] here.
NumberFromIntervalValue abstractValue = new NumberFromIntervalValue(0, group.size() - 1);
- feedback.recordFieldHasAbstractValue(classIdField, appView, abstractValue);
+ feedback.recordFieldHasAbstractValue(classIdField, appView.withLiveness(), abstractValue);
classInstanceFieldsMerger.setClassIdField(classIdField);
}
@@ -261,7 +264,10 @@
public void mergeGroup(SyntheticArgumentClass syntheticArgumentClass) {
fixAccessFlags();
- appendClassIdField();
+
+ if (group.hasClassIdField()) {
+ appendClassIdField();
+ }
mergeAnnotations();
mergeInterfaces();
@@ -275,102 +281,131 @@
}
public static class Builder {
- private final AppView<AppInfoWithLiveness> appView;
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final MergeGroup group;
- private final ClassInitializerSynthesizedCode.Builder classInitializerSynthesizedCodeBuilder =
- new ClassInitializerSynthesizedCode.Builder();
- private final Map<DexProto, ConstructorMerger.Builder> constructorMergerBuilders =
- new LinkedHashMap<>();
- private final List<ConstructorMerger.Builder> unmergedConstructorBuilders = new ArrayList<>();
- private final Map<Wrapper<DexMethod>, VirtualMethodMerger.Builder> virtualMethodMergerBuilders =
- new LinkedHashMap<>();
- public Builder(AppView<AppInfoWithLiveness> appView, MergeGroup group) {
+ public Builder(AppView<? extends AppInfoWithClassHierarchy> appView, MergeGroup group) {
this.appView = appView;
this.group = group;
}
- private Builder setup() {
- DexItemFactory dexItemFactory = appView.dexItemFactory();
- DexProgramClass target =
- IterableUtils.findOrDefault(group, DexClass::isPublic, group.iterator().next());
- // TODO(b/165498187): ensure the name for the field is fresh
- group.setClassIdField(
- dexItemFactory.createField(
- target.getType(), dexItemFactory.intType, CLASS_ID_FIELD_NAME));
- group.setTarget(target);
- setupForMethodMerging(target);
- group.forEachSource(this::setupForMethodMerging);
- return this;
- }
-
- private void setupForMethodMerging(DexProgramClass toMerge) {
- if (toMerge.hasClassInitializer()) {
- classInitializerSynthesizedCodeBuilder.add(toMerge.getClassInitializer());
+ private void selectTarget() {
+ Iterable<DexProgramClass> candidates = Iterables.filter(group, DexClass::isPublic);
+ if (IterableUtils.isEmpty(candidates)) {
+ candidates = group;
}
- toMerge.forEachProgramDirectMethodMatching(
- DexEncodedMethod::isInstanceInitializer, this::addConstructor);
- toMerge.forEachProgramVirtualMethod(this::addVirtualMethod);
+ Iterator<DexProgramClass> candidateIterator = candidates.iterator();
+ DexProgramClass target = IterableUtils.first(candidates);
+ while (candidateIterator.hasNext()) {
+ DexProgramClass current = candidateIterator.next();
+ KeepClassInfo keepClassInfo = appView.getKeepInfo().getClassInfo(current);
+ if (keepClassInfo.isMinificationAllowed(appView.options())) {
+ target = current;
+ break;
+ }
+ // Select the target with the shortest name.
+ if (current.getType().getDescriptor().size() < target.getType().getDescriptor().size) {
+ target = current;
+ }
+ }
+ group.setTarget(appView.testing().horizontalClassMergingTarget.apply(candidates, target));
}
- private void addConstructor(ProgramMethod method) {
- assert method.getDefinition().isInstanceInitializer();
+ private ClassInitializerSynthesizedCode createClassInitializerMerger() {
+ ClassInitializerSynthesizedCode.Builder builder =
+ new ClassInitializerSynthesizedCode.Builder();
+ group.forEach(
+ clazz -> {
+ if (clazz.hasClassInitializer()) {
+ builder.add(clazz.getClassInitializer());
+ }
+ });
+ return builder.build();
+ }
+
+ private List<ConstructorMerger> createInstanceInitializerMergers() {
+ List<ConstructorMerger> constructorMergers = new ArrayList<>();
if (appView.options().horizontalClassMergerOptions().isConstructorMergingEnabled()) {
- constructorMergerBuilders
- .computeIfAbsent(
- method.getDefinition().getProto(), ignore -> new ConstructorMerger.Builder(appView))
- .add(method.getDefinition());
+ Map<DexProto, ConstructorMerger.Builder> buildersByProto = new LinkedHashMap<>();
+ group.forEach(
+ clazz ->
+ clazz.forEachProgramDirectMethodMatching(
+ DexEncodedMethod::isInstanceInitializer,
+ method ->
+ buildersByProto
+ .computeIfAbsent(
+ method.getDefinition().getProto(),
+ ignore -> new ConstructorMerger.Builder(appView))
+ .add(method.getDefinition())));
+ for (ConstructorMerger.Builder builder : buildersByProto.values()) {
+ constructorMergers.addAll(builder.build(group));
+ }
} else {
- unmergedConstructorBuilders.add(
- new ConstructorMerger.Builder(appView).add(method.getDefinition()));
+ group.forEach(
+ clazz ->
+ clazz.forEachProgramDirectMethodMatching(
+ DexEncodedMethod::isInstanceInitializer,
+ method ->
+ constructorMergers.addAll(
+ new ConstructorMerger.Builder(appView)
+ .add(method.getDefinition())
+ .build(group))));
}
+
+ // Try and merge the constructors with the most arguments first, to avoid using synthetic
+ // arguments if possible.
+ constructorMergers.sort(Comparator.comparing(ConstructorMerger::getArity).reversed());
+ return constructorMergers;
}
- private void addVirtualMethod(ProgramMethod method) {
- assert method.getDefinition().isNonPrivateVirtualMethod();
- virtualMethodMergerBuilders
- .computeIfAbsent(
- MethodSignatureEquivalence.get().wrap(method.getReference()),
- ignore -> new VirtualMethodMerger.Builder())
- .add(method);
- }
-
- private Collection<ConstructorMerger.Builder> getConstructorMergerBuilders() {
- return appView.options().horizontalClassMergerOptions().isConstructorMergingEnabled()
- ? constructorMergerBuilders.values()
- : unmergedConstructorBuilders;
- }
-
- public ClassMerger build(
- HorizontalClassMergerGraphLens.Builder lensBuilder) {
- setup();
+ private List<VirtualMethodMerger> createVirtualMethodMergers() {
+ Map<DexMethodSignature, VirtualMethodMerger.Builder> virtualMethodMergerBuilders =
+ new LinkedHashMap<>();
+ group.forEach(
+ clazz ->
+ clazz.forEachProgramVirtualMethod(
+ virtualMethod ->
+ virtualMethodMergerBuilders
+ .computeIfAbsent(
+ virtualMethod.getReference().getSignature(),
+ ignore -> new VirtualMethodMerger.Builder())
+ .add(virtualMethod)));
List<VirtualMethodMerger> virtualMethodMergers =
new ArrayList<>(virtualMethodMergerBuilders.size());
for (VirtualMethodMerger.Builder builder : virtualMethodMergerBuilders.values()) {
virtualMethodMergers.add(builder.build(appView, group));
}
- // Try and merge the functions with the most arguments first, to avoid using synthetic
- // arguments if possible.
- virtualMethodMergers.sort(Comparator.comparing(VirtualMethodMerger::getArity).reversed());
+ return virtualMethodMergers;
+ }
- List<ConstructorMerger> constructorMergers =
- new ArrayList<>(constructorMergerBuilders.size());
- for (ConstructorMerger.Builder builder : getConstructorMergerBuilders()) {
- constructorMergers.addAll(builder.build(appView, group));
+ private void createClassIdField() {
+ // TODO(b/165498187): ensure the name for the field is fresh
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ group.setClassIdField(
+ dexItemFactory.createField(
+ group.getTarget().getType(), dexItemFactory.intType, CLASS_ID_FIELD_NAME));
+ }
+
+ public ClassMerger build(
+ HorizontalClassMergerGraphLens.Builder lensBuilder) {
+ selectTarget();
+
+ List<VirtualMethodMerger> virtualMethodMergers = createVirtualMethodMergers();
+
+ boolean requiresClassIdField =
+ virtualMethodMergers.stream()
+ .anyMatch(virtualMethodMerger -> !virtualMethodMerger.isNopOrTrivial());
+ if (requiresClassIdField) {
+ createClassIdField();
}
- // Try and merge the functions with the most arguments first, to avoid using synthetic
- // arguments if possible.
- virtualMethodMergers.sort(Comparator.comparing(VirtualMethodMerger::getArity).reversed());
- constructorMergers.sort(Comparator.comparing(ConstructorMerger::getArity).reversed());
-
return new ClassMerger(
appView,
lensBuilder,
group,
virtualMethodMergers,
- constructorMergers,
- classInitializerSynthesizedCodeBuilder.build());
+ createInstanceInitializerMergers(),
+ createClassInitializerMerger());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPoint.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPoint.java
index 732a5d2..d650b17 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPoint.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorEntryPoint.java
@@ -49,6 +49,10 @@
this.classIdField = classIdField;
}
+ private boolean hasClassIdField() {
+ return classIdField != null;
+ }
+
void addConstructorInvoke(DexMethod typeConstructor) {
add(
builder -> {
@@ -66,11 +70,13 @@
/** Assign the given register to the class id field. */
void addRegisterClassIdAssignment(int idRegister) {
+ assert hasClassIdField();
add(builder -> builder.addInstancePut(idRegister, getReceiverRegister(), classIdField));
}
/** Assign the given constant integer value to the class id field. */
void addConstantRegisterClassIdAssignment(int classId) {
+ assert hasClassIdField();
int idRegister = nextRegister(ValueType.INT);
add(builder -> builder.addIntConst(idRegister, classId));
addRegisterClassIdAssignment(idRegister);
@@ -82,7 +88,9 @@
// The class id register is always the first synthetic argument.
int idRegister = getParamRegister(exampleTargetConstructor.getArity());
- addRegisterClassIdAssignment(idRegister);
+ if (hasClassIdField()) {
+ addRegisterClassIdAssignment(idRegister);
+ }
int[] keys = new int[typeConstructorCount - 1];
int[] offsets = new int[typeConstructorCount - 1];
@@ -115,7 +123,9 @@
protected void prepareSingleConstructorInstructions() {
Entry<DexMethod> entry = typeConstructors.int2ReferenceEntrySet().first();
- addConstantRegisterClassIdAssignment(entry.getIntKey());
+ if (hasClassIdField()) {
+ addConstantRegisterClassIdAssignment(entry.getIntKey());
+ }
addConstructorInvoke(entry.getValue());
add(IRBuilder::addReturn, endsBlock);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
index 13673fa..25066e7 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -20,7 +21,6 @@
import com.android.tools.r8.ir.conversion.ExtraConstantIntParameter;
import com.android.tools.r8.ir.conversion.ExtraParameter;
import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.structural.Ordered;
import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
@@ -69,11 +69,10 @@
public static class Builder {
private int estimatedDexCodeSize;
private final List<List<DexEncodedMethod>> constructorGroups = new ArrayList<>();
- private AppView<AppInfoWithLiveness> appView;
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
- public Builder(AppView<AppInfoWithLiveness> appView) {
+ public Builder(AppView<? extends AppInfoWithClassHierarchy> appView) {
this.appView = appView;
-
createNewGroup();
}
@@ -96,7 +95,7 @@
return this;
}
- public List<ConstructorMerger> build(AppView<?> appView, MergeGroup group) {
+ public List<ConstructorMerger> build(MergeGroup group) {
assert constructorGroups.stream().noneMatch(List::isEmpty);
return ListUtils.map(
constructorGroups, constructors -> new ConstructorMerger(appView, group, constructors));
@@ -185,7 +184,7 @@
new ConstructorEntryPointSynthesizedCode(
typeConstructorClassMap,
newConstructorReference,
- group.getClassIdField(),
+ group.hasClassIdField() ? group.getClassIdField() : null,
bridgeConstructorReference);
DexEncodedMethod newConstructor =
new DexEncodedMethod(
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index 7085879..eff9460 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.horizontalclassmerging;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.horizontalclassmerging.policies.AllInstantiatedOrUninstantiated;
@@ -15,6 +16,7 @@
import com.android.tools.r8.horizontalclassmerging.policies.NoAnnotationClasses;
import com.android.tools.r8.horizontalclassmerging.policies.NoClassAnnotationCollisions;
import com.android.tools.r8.horizontalclassmerging.policies.NoClassInitializerWithObservableSideEffects;
+import com.android.tools.r8.horizontalclassmerging.policies.NoDeadEnumLiteMaps;
import com.android.tools.r8.horizontalclassmerging.policies.NoDirectRuntimeTypeChecks;
import com.android.tools.r8.horizontalclassmerging.policies.NoEnums;
import com.android.tools.r8.horizontalclassmerging.policies.NoIndirectRuntimeTypeChecks;
@@ -38,7 +40,6 @@
import com.android.tools.r8.horizontalclassmerging.policies.SyntheticItemsPolicy;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
-import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.shaking.RuntimeTypeCheckInfo;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
@@ -49,9 +50,19 @@
public class HorizontalClassMerger {
- private final AppView<AppInfoWithLiveness> appView;
+ // TODO(b/181846319): Add 'FINAL' mode that runs after synthetic finalization.
+ public enum Mode {
+ INITIAL;
- public HorizontalClassMerger(AppView<AppInfoWithLiveness> appView) {
+ public boolean isInitial() {
+ return this == INITIAL;
+ }
+ }
+
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
+ private final Mode mode = Mode.INITIAL;
+
+ public HorizontalClassMerger(AppView<? extends AppInfoWithClassHierarchy> appView) {
this.appView = appView;
assert appView.options().enableInlining;
}
@@ -76,7 +87,9 @@
// Merge the classes.
List<ClassMerger> classMergers = initializeClassMergers(lensBuilder, groups);
SyntheticArgumentClass syntheticArgumentClass =
- new SyntheticArgumentClass.Builder(appView).build(groups);
+ mode.isInitial()
+ ? new SyntheticArgumentClass.Builder(appView.withLiveness()).build(groups)
+ : null;
applyClassMergers(classMergers, syntheticArgumentClass);
// Generate the graph lens.
@@ -87,8 +100,9 @@
createLens(mergedClasses, lensBuilder, syntheticArgumentClass);
// Prune keep info.
- KeepInfoCollection keepInfo = appView.appInfo().getKeepInfo();
- keepInfo.mutate(mutator -> mutator.removeKeepInfoForPrunedItems(mergedClasses.getSources()));
+ appView
+ .getKeepInfo()
+ .mutate(mutator -> mutator.removeKeepInfoForPrunedItems(mergedClasses.getSources()));
return new HorizontalClassMergerResult(createFieldAccessInfoCollectionModifier(groups), lens);
}
@@ -98,21 +112,26 @@
FieldAccessInfoCollectionModifier.Builder builder =
new FieldAccessInfoCollectionModifier.Builder();
for (MergeGroup group : groups) {
- DexProgramClass target = group.getTarget();
- target.forEachProgramInstanceInitializerMatching(
- definition -> definition.getCode().isHorizontalClassMergingCode(),
- method -> builder.recordFieldWrittenInContext(group.getClassIdField(), method));
- target.forEachProgramVirtualMethodMatching(
- definition -> definition.hasCode() && definition.getCode().isHorizontalClassMergingCode(),
- method -> builder.recordFieldReadInContext(group.getClassIdField(), method));
+ if (group.hasClassIdField()) {
+ DexProgramClass target = group.getTarget();
+ target.forEachProgramInstanceInitializerMatching(
+ definition -> definition.getCode().isHorizontalClassMergingCode(),
+ method -> builder.recordFieldWrittenInContext(group.getClassIdField(), method));
+ target.forEachProgramVirtualMethodMatching(
+ definition ->
+ definition.hasCode() && definition.getCode().isHorizontalClassMergingCode(),
+ method -> builder.recordFieldReadInContext(group.getClassIdField(), method));
+ }
}
return builder.build();
}
private List<Policy> getPolicies(RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
+ AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
List<SingleClassPolicy> singleClassPolicies =
ImmutableList.of(
- new NotMatchedByNoHorizontalClassMerging(appView),
+ new NotMatchedByNoHorizontalClassMerging(appViewWithLiveness),
+ new NoDeadEnumLiteMaps(appViewWithLiveness),
new NoAnnotationClasses(),
new NoEnums(appView),
new NoInnerClasses(),
@@ -125,7 +144,7 @@
new NoServiceLoaders(appView),
new NotVerticallyMergedIntoSubtype(appView),
new NoDirectRuntimeTypeChecks(runtimeTypeCheckInfo),
- new DontInlinePolicy(appView));
+ new DontInlinePolicy(appViewWithLiveness));
List<MultiClassPolicy> multiClassPolicies =
ImmutableList.of(
new SameInstanceFields(appView),
@@ -135,13 +154,13 @@
new NoIndirectRuntimeTypeChecks(appView, runtimeTypeCheckInfo),
new PreventMethodImplementation(appView),
new PreventMergeIntoDifferentMainDexGroups(appView),
- new AllInstantiatedOrUninstantiated(appView),
+ new AllInstantiatedOrUninstantiated(appViewWithLiveness),
new SameParentClass(),
new SameNestHost(appView),
- new PreserveMethodCharacteristics(appView),
+ new PreserveMethodCharacteristics(appViewWithLiveness),
new SameFeatureSplit(appView),
new RespectPackageBoundaries(appView),
- new DontMergeSynchronizedClasses(appView),
+ new DontMergeSynchronizedClasses(appViewWithLiveness),
new MinimizeFieldCasts(),
new LimitGroups(appView));
return ImmutableList.<Policy>builder()
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/SubtypingForrestForClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/SubtypingForrestForClasses.java
index 05440b4..afb02cd 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/SubtypingForrestForClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/SubtypingForrestForClasses.java
@@ -4,9 +4,9 @@
package com.android.tools.r8.horizontalclassmerging;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -30,12 +30,12 @@
* </code>
*/
public class SubtypingForrestForClasses {
- private final AppView<AppInfoWithLiveness> appView;
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final Collection<DexProgramClass> roots = new ArrayList<>();
private final Map<DexProgramClass, List<DexProgramClass>> subtypeMap = new IdentityHashMap<>();
- public SubtypingForrestForClasses(AppView<AppInfoWithLiveness> appView) {
+ public SubtypingForrestForClasses(AppView<? extends AppInfoWithClassHierarchy> appView) {
this.appView = appView;
calculateSubtyping(appView.appInfo().classes());
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
index c66d5de..d475710 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.horizontalclassmerging;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass.FieldSetter;
import com.android.tools.r8.graph.DexEncodedField;
@@ -19,7 +20,6 @@
import com.android.tools.r8.graph.TreeFixerBase;
import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
import com.android.tools.r8.shaking.AnnotationFixer;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.OptionalBool;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
@@ -37,16 +37,17 @@
* tracked in {@link TreeFixer#lensBuilder}.
*/
class TreeFixer extends TreeFixerBase {
+
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final HorizontallyMergedClasses mergedClasses;
private final HorizontalClassMergerGraphLens.Builder lensBuilder;
- private final AppView<AppInfoWithLiveness> appView;
private final DexItemFactory dexItemFactory;
private final SyntheticArgumentClass syntheticArgumentClass;
private final BiMap<DexMethodSignature, DexMethodSignature> reservedInterfaceSignatures =
HashBiMap.create();
public TreeFixer(
- AppView<AppInfoWithLiveness> appView,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
HorizontallyMergedClasses mergedClasses,
HorizontalClassMergerGraphLens.Builder lensBuilder,
SyntheticArgumentClass syntheticArgumentClass) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
index c144128..efcbb27 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.horizontalclassmerging;
import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -18,7 +19,6 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
import com.android.tools.r8.ir.synthetic.AbstractSynthesizedCode;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.structural.Ordered;
@@ -30,21 +30,22 @@
import java.util.List;
public class VirtualMethodMerger {
+
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final DexItemFactory dexItemFactory;
private final MergeGroup group;
private final List<ProgramMethod> methods;
- private final AppView<AppInfoWithLiveness> appView;
private final DexMethod superMethod;
public VirtualMethodMerger(
- AppView<AppInfoWithLiveness> appView,
+ AppView<? extends AppInfoWithClassHierarchy> appView,
MergeGroup group,
List<ProgramMethod> methods,
DexMethod superMethod) {
+ this.appView = appView;
this.dexItemFactory = appView.dexItemFactory();
this.group = group;
this.methods = methods;
- this.appView = appView;
this.superMethod = superMethod;
}
@@ -57,7 +58,8 @@
}
/** Get the super method handle if this method overrides a parent method. */
- private DexMethod superMethod(AppView<AppInfoWithLiveness> appView, DexProgramClass target) {
+ private DexMethod superMethod(
+ AppView<? extends AppInfoWithClassHierarchy> appView, DexProgramClass target) {
DexMethod template = methods.iterator().next().getReference();
SingleResolutionResult resolutionResult =
appView
@@ -79,7 +81,8 @@
return resolutionResult.getResolvedMethod().getReference();
}
- public VirtualMethodMerger build(AppView<AppInfoWithLiveness> appView, MergeGroup group) {
+ public VirtualMethodMerger build(
+ AppView<? extends AppInfoWithClassHierarchy> appView, MergeGroup group) {
// If not all the classes are in the merge group, find the fallback super method to call.
DexMethod superMethod =
methods.size() < group.size() ? superMethod(appView, group.getTarget()) : null;
@@ -171,6 +174,10 @@
return numberOfNonAbstractMethods <= 1;
}
+ boolean isNopOrTrivial() {
+ return isNop() || isTrivial();
+ }
+
/**
* If there is only a single method that does not override anything then it is safe to just move
* it to the target type if it is not already in it.
@@ -221,12 +228,11 @@
assert !methods.isEmpty();
// Handle trivial merges.
- if (isNop() || isTrivial()) {
+ if (isNopOrTrivial()) {
mergeTrivial(classMethodsBuilder, lensBuilder);
return;
}
-
Int2ReferenceSortedMap<DexMethod> classIdToMethodMap = new Int2ReferenceAVLTreeMap<>();
CfVersion classFileVersion = null;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckAbstractClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckAbstractClasses.java
index e78b196..41fba55 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckAbstractClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/CheckAbstractClasses.java
@@ -4,11 +4,11 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
import com.android.tools.r8.horizontalclassmerging.policies.CheckAbstractClasses.AbstractClassification;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
public class CheckAbstractClasses extends MultiClassSameReferencePolicy<AbstractClassification> {
@@ -20,7 +20,7 @@
private final InternalOptions options;
- public CheckAbstractClasses(AppView<AppInfoWithLiveness> appView) {
+ public CheckAbstractClasses(AppView<? extends AppInfoWithClassHierarchy> appView) {
this.options = appView.options();
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitGroups.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitGroups.java
index 80e1292..c798f04 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitGroups.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitGroups.java
@@ -4,11 +4,11 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.horizontalclassmerging.MergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
@@ -17,7 +17,7 @@
private final int maxGroupSize;
- public LimitGroups(AppView<AppInfoWithLiveness> appView) {
+ public LimitGroups(AppView<? extends AppInfoWithClassHierarchy> appView) {
maxGroupSize = appView.options().horizontalClassMergerOptions().getMaxGroupSize();
assert maxGroupSize >= 2;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDeadEnumLiteMaps.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDeadEnumLiteMaps.java
new file mode 100644
index 0000000..3706d0a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoDeadEnumLiteMaps.java
@@ -0,0 +1,35 @@
+// Copyright (c) 2020, 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.horizontalclassmerging.policies;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
+import com.android.tools.r8.ir.analysis.proto.EnumLiteProtoShrinker;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import java.util.Collections;
+import java.util.Set;
+
+public class NoDeadEnumLiteMaps extends SingleClassPolicy {
+
+ private final Set<DexType> deadEnumLiteMaps;
+
+ public NoDeadEnumLiteMaps(AppView<AppInfoWithLiveness> appView) {
+ this.deadEnumLiteMaps =
+ appView.withProtoEnumShrinker(
+ EnumLiteProtoShrinker::getDeadEnumLiteMaps, Collections.emptySet());
+ }
+
+ @Override
+ public boolean canMerge(DexProgramClass clazz) {
+ return !deadEnumLiteMaps.contains(clazz.getType());
+ }
+
+ @Override
+ public String getName() {
+ return "NoDeadEnumLiteMaps";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoEnums.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoEnums.java
index 8bea8a4..e037345 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoEnums.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoEnums.java
@@ -4,20 +4,20 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+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.DexProgramClass;
import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import it.unimi.dsi.fastutil.objects.Reference2BooleanMap;
import it.unimi.dsi.fastutil.objects.Reference2BooleanOpenHashMap;
public class NoEnums extends SingleClassPolicy {
- private final AppView<AppInfoWithLiveness> appView;
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final Reference2BooleanMap<DexClass> cache = new Reference2BooleanOpenHashMap<>();
- public NoEnums(AppView<AppInfoWithLiveness> appView) {
+ public NoEnums(AppView<? extends AppInfoWithClassHierarchy> appView) {
this.appView = appView;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIndirectRuntimeTypeChecks.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIndirectRuntimeTypeChecks.java
index 8de67d6..12b6ba4 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIndirectRuntimeTypeChecks.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIndirectRuntimeTypeChecks.java
@@ -4,26 +4,27 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+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.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.RuntimeTypeCheckInfo;
import it.unimi.dsi.fastutil.objects.Reference2BooleanMap;
import it.unimi.dsi.fastutil.objects.Reference2BooleanOpenHashMap;
public class NoIndirectRuntimeTypeChecks extends MultiClassSameReferencePolicy<DexTypeList> {
- private final AppView<AppInfoWithLiveness> appView;
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final RuntimeTypeCheckInfo runtimeTypeCheckInfo;
private final Reference2BooleanMap<DexType> cache = new Reference2BooleanOpenHashMap<>();
public NoIndirectRuntimeTypeChecks(
- AppView<AppInfoWithLiveness> appView, RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
+ AppView<? extends AppInfoWithClassHierarchy> appView,
+ RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
this.appView = appView;
this.runtimeTypeCheckInfo = runtimeTypeCheckInfo;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKeepRules.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKeepRules.java
index ec8a5aa..a13a445 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKeepRules.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKeepRules.java
@@ -4,34 +4,37 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexMember;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.KeepInfoCollection;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import java.util.Set;
public class NoKeepRules extends SingleClassPolicy {
- private final AppView<AppInfoWithLiveness> appView;
+
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
+ private final KeepInfoCollection keepInfo;
+
private final Set<DexType> dontMergeTypes = Sets.newIdentityHashSet();
- public NoKeepRules(AppView<AppInfoWithLiveness> appView) {
+ public NoKeepRules(AppView<? extends AppInfoWithClassHierarchy> appView) {
this.appView = appView;
-
+ this.keepInfo = appView.getKeepInfo();
appView.appInfo().classes().forEach(this::processClass);
}
private void processClass(DexProgramClass programClass) {
DexType type = programClass.getType();
- boolean pinProgramClass = appView.appInfo().isPinned(type);
-
+ boolean pinProgramClass = keepInfo.isPinned(type, appView);
for (DexEncodedMember<?, ?> member : programClass.members()) {
DexMember<?, ?> reference = member.getReference();
- if (appView.appInfo().isPinned(reference)) {
+ if (keepInfo.isPinned(reference, appView)) {
pinProgramClass = true;
Iterables.addAll(
dontMergeTypes,
@@ -39,7 +42,6 @@
reference.getReferencedBaseTypes(appView.dexItemFactory()), DexType::isClassType));
}
}
-
if (pinProgramClass) {
dontMergeTypes.add(type);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoServiceLoaders.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoServiceLoaders.java
index b1f0d99..30bee86 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoServiceLoaders.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoServiceLoaders.java
@@ -4,18 +4,18 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Set;
public class NoServiceLoaders extends SingleClassPolicy {
- private final AppView<AppInfoWithLiveness> appView;
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final Set<DexType> allServiceImplementations;
- public NoServiceLoaders(AppView<AppInfoWithLiveness> appView) {
+ public NoServiceLoaders(AppView<? extends AppInfoWithClassHierarchy> appView) {
this.appView = appView;
allServiceImplementations = appView.appServices().computeAllServiceImplementations();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotMatchedByNoHorizontalClassMerging.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotMatchedByNoHorizontalClassMerging.java
index 8751afb..86ad07c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotMatchedByNoHorizontalClassMerging.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotMatchedByNoHorizontalClassMerging.java
@@ -6,29 +6,20 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
-import com.android.tools.r8.ir.analysis.proto.EnumLiteProtoShrinker;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import java.util.Collections;
-import java.util.Set;
public class NotMatchedByNoHorizontalClassMerging extends SingleClassPolicy {
private final AppView<AppInfoWithLiveness> appView;
- private final Set<DexType> deadEnumLiteMaps;
public NotMatchedByNoHorizontalClassMerging(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
- this.deadEnumLiteMaps =
- appView.withProtoEnumShrinker(
- EnumLiteProtoShrinker::getDeadEnumLiteMaps, Collections.emptySet());
}
@Override
public boolean canMerge(DexProgramClass program) {
- return !deadEnumLiteMaps.contains(program.getType())
- && !appView.appInfo().isNoHorizontalClassMergingOfType(program.getType());
+ return !appView.appInfo().isNoHorizontalClassMergingOfType(program.getType());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotVerticallyMergedIntoSubtype.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotVerticallyMergedIntoSubtype.java
index 92d50d1..062e695 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotVerticallyMergedIntoSubtype.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NotVerticallyMergedIntoSubtype.java
@@ -4,15 +4,15 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
public class NotVerticallyMergedIntoSubtype extends SingleClassPolicy {
- private final AppView<AppInfoWithLiveness> appView;
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
- public NotVerticallyMergedIntoSubtype(AppView<AppInfoWithLiveness> appView) {
+ public NotVerticallyMergedIntoSubtype(AppView<? extends AppInfoWithClassHierarchy> appView) {
this.appView = appView;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoDifferentMainDexGroups.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoDifferentMainDexGroups.java
index 38df5cf..9d529d3 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoDifferentMainDexGroups.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMergeIntoDifferentMainDexGroups.java
@@ -4,10 +4,10 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.MainDexInfo;
import com.android.tools.r8.shaking.MainDexInfo.MainDexGroup;
import com.android.tools.r8.synthesis.SyntheticItems;
@@ -18,7 +18,8 @@
private final MainDexInfo mainDexInfo;
private final SyntheticItems synthetics;
- public PreventMergeIntoDifferentMainDexGroups(AppView<AppInfoWithLiveness> appView) {
+ public PreventMergeIntoDifferentMainDexGroups(
+ AppView<? extends AppInfoWithClassHierarchy> appView) {
mainDexInfo = appView.appInfo().getMainDexInfo();
synthetics = appView.getSyntheticItems();
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMethodImplementation.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMethodImplementation.java
index b182330..5bffb33 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMethodImplementation.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/PreventMethodImplementation.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+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.DexEncodedMethod;
@@ -15,7 +16,6 @@
import com.android.tools.r8.horizontalclassmerging.MergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
import com.android.tools.r8.horizontalclassmerging.SubtypingForrestForClasses;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.collections.DexMethodSignatureSet;
import java.util.Collection;
import java.util.IdentityHashMap;
@@ -49,7 +49,8 @@
* <p>See: https://docs.oracle.com/javase/specs/jvms/se15/html/jvms-5.html#jvms-5.4.3.3)
*/
public class PreventMethodImplementation extends MultiClassPolicy {
- private final AppView<AppInfoWithLiveness> appView;
+
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final SubtypingForrestForClasses subtypingForrestForClasses;
private final InterfaceDefaultSignaturesCache interfaceDefaultMethodsCache =
@@ -123,7 +124,7 @@
}
}
- public PreventMethodImplementation(AppView<AppInfoWithLiveness> appView) {
+ public PreventMethodImplementation(AppView<? extends AppInfoWithClassHierarchy> appView) {
this.appView = appView;
this.subtypingForrestForClasses = new SubtypingForrestForClasses(appView);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
index 78cfe13..8f59c36 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/RespectPackageBoundaries.java
@@ -4,12 +4,12 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.horizontalclassmerging.MergeGroup;
import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.VerticalClassMerger.IllegalAccessDetector;
import com.android.tools.r8.utils.TraversalContinuation;
import java.util.ArrayList;
@@ -18,9 +18,10 @@
import java.util.Map;
public class RespectPackageBoundaries extends MultiClassPolicy {
- private final AppView<AppInfoWithLiveness> appView;
- public RespectPackageBoundaries(AppView<AppInfoWithLiveness> appView) {
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
+
+ public RespectPackageBoundaries(AppView<? extends AppInfoWithClassHierarchy> appView) {
this.appView = appView;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFeatureSplit.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFeatureSplit.java
index b3ec017..58054a2 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFeatureSplit.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameFeatureSplit.java
@@ -5,15 +5,15 @@
package com.android.tools.r8.horizontalclassmerging.policies;
import com.android.tools.r8.FeatureSplit;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
public class SameFeatureSplit extends MultiClassSameReferencePolicy<FeatureSplit> {
- private final AppView<AppInfoWithLiveness> appView;
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
- public SameFeatureSplit(AppView<AppInfoWithLiveness> appView) {
+ public SameFeatureSplit(AppView<? extends AppInfoWithClassHierarchy> appView) {
this.appView = appView;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameInstanceFields.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameInstanceFields.java
index 291aae5..391fb75 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameInstanceFields.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameInstanceFields.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexItemFactory;
@@ -12,7 +13,6 @@
import com.android.tools.r8.graph.FieldAccessFlags;
import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
import com.android.tools.r8.horizontalclassmerging.policies.SameInstanceFields.InstanceFieldInfo;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import java.util.Objects;
@@ -21,7 +21,7 @@
private final DexItemFactory dexItemFactory;
- public SameInstanceFields(AppView<AppInfoWithLiveness> appView) {
+ public SameInstanceFields(AppView<? extends AppInfoWithClassHierarchy> appView) {
this.dexItemFactory = appView.dexItemFactory();
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameNestHost.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameNestHost.java
index fc8e39c..a4ba653 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameNestHost.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SameNestHost.java
@@ -4,18 +4,18 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
public class SameNestHost extends MultiClassSameReferencePolicy<DexType> {
private final DexItemFactory dexItemFactory;
- public SameNestHost(AppView<AppInfoWithLiveness> appView) {
+ public SameNestHost(AppView<? extends AppInfoWithClassHierarchy> appView) {
this.dexItemFactory = appView.dexItemFactory();
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SyntheticItemsPolicy.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SyntheticItemsPolicy.java
index cbc9b3d..6a84212 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SyntheticItemsPolicy.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/SyntheticItemsPolicy.java
@@ -4,11 +4,11 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.horizontalclassmerging.MultiClassSameReferencePolicy;
import com.android.tools.r8.horizontalclassmerging.policies.SyntheticItemsPolicy.ClassKind;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
@@ -19,9 +19,9 @@
NOT_SYNTHETIC
}
- private final AppView<AppInfoWithLiveness> appView;
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
- public SyntheticItemsPolicy(AppView<AppInfoWithLiveness> appView) {
+ public SyntheticItemsPolicy(AppView<? extends AppInfoWithClassHierarchy> appView) {
this.appView = appView;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/CanonicalPositions.java b/src/main/java/com/android/tools/r8/ir/code/CanonicalPositions.java
index de25bd7..06537e3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CanonicalPositions.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CanonicalPositions.java
@@ -17,6 +17,7 @@
private final Position callerPosition;
private final Map<Position, Position> canonicalPositions;
private final Position preamblePosition;
+ private final boolean isCompilerSynthesizedInlinee;
// Lazily computed synthetic position for shared exceptional exits in synchronized methods.
private Position syntheticPosition;
@@ -24,18 +25,22 @@
public CanonicalPositions(
Position callerPosition,
int expectedPositionsCount,
- DexMethod method) {
+ DexMethod method,
+ boolean methodIsSynthesized) {
canonicalPositions =
new HashMap<>(1 + (callerPosition == null ? 0 : 1) + expectedPositionsCount);
- this.callerPosition = callerPosition;
if (callerPosition != null) {
- canonicalPositions.put(callerPosition, callerPosition);
+ this.callerPosition = getCanonical(callerPosition);
+ isCompilerSynthesizedInlinee = methodIsSynthesized;
+ preamblePosition =
+ methodIsSynthesized
+ ? callerPosition
+ : getCanonical(new Position(0, null, method, callerPosition));
+ } else {
+ this.callerPosition = null;
+ isCompilerSynthesizedInlinee = false;
+ preamblePosition = getCanonical(Position.synthetic(0, method, null));
}
- preamblePosition =
- callerPosition == null
- ? Position.synthetic(0, method, null)
- : new Position(0, null, method, callerPosition);
- canonicalPositions.put(preamblePosition, preamblePosition);
}
public Position getPreamblePosition() {
@@ -53,16 +58,21 @@
/**
* Append callerPosition (supplied in constructor) to the end of caller's caller chain and return
- * the canonical instance. Always returns null if preserveCaller (also supplied in constructor) is
- * false.
+ * the canonical instance.
*/
public Position canonicalizeCallerPosition(Position caller) {
if (caller == null) {
return callerPosition;
}
if (caller.callerPosition == null && callerPosition == null) {
+ // This is itself the outer-most position.
return getCanonical(caller);
}
+ if (caller.callerPosition == null && isCompilerSynthesizedInlinee) {
+ // This is the outer-most position of the inlinee (eg, the inlinee itself).
+ // If compiler synthesized, strip it from the position info by directly returning caller.
+ return callerPosition;
+ }
Position callerOfCaller = canonicalizeCallerPosition(caller.callerPosition);
return getCanonical(
caller.isNone()
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
index 1d7f619..9f464b8 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfSourceCode.java
@@ -246,7 +246,12 @@
}
}
this.state = new CfState(origin);
- canonicalPositions = new CanonicalPositions(callerPosition, cfPositionCount, originalMethod);
+ canonicalPositions =
+ new CanonicalPositions(
+ callerPosition,
+ cfPositionCount,
+ originalMethod,
+ method.getDefinition().isD8R8Synthesized());
internalOutputMode = appView.options().getInternalOutputMode();
needsGeneratedMethodSynchronization =
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
index facf351..6c0279a 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexSourceCode.java
@@ -87,7 +87,8 @@
new CanonicalPositions(
callerPosition,
debugEntries == null ? 0 : debugEntries.size(),
- originalMethod);
+ originalMethod,
+ method.getDefinition().isD8R8Synthesized());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
index 7bb5741..832fd8c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/BackportedMethodRewriter.java
@@ -31,6 +31,7 @@
import com.android.tools.r8.ir.desugar.backports.NumericMethodRewrites;
import com.android.tools.r8.ir.desugar.backports.ObjectsMethodRewrites;
import com.android.tools.r8.ir.desugar.backports.OptionalMethodRewrites;
+import com.android.tools.r8.ir.desugar.backports.SparseArrayMethodRewrites;
import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
@@ -1055,6 +1056,13 @@
new MethodGenerator(
method, BackportedMethods::MathMethods_floorModLongInt, "floorModLongInt"));
}
+
+ // android.util.SparseArray
+
+ // void android.util.SparseArray.set(int, Object))
+ addProvider(
+ new InvokeRewriter(
+ factory.androidUtilSparseArrayMembers.set, SparseArrayMethodRewrites.rewriteSet()));
}
private void initializeJava9MethodProviders(DexItemFactory factory) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/SparseArrayMethodRewrites.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/SparseArrayMethodRewrites.java
new file mode 100644
index 0000000..e1858e2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/SparseArrayMethodRewrites.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2019, 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.ir.desugar.backports;
+
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter.MethodInvokeRewriter;
+import org.objectweb.asm.Opcodes;
+
+public final class SparseArrayMethodRewrites {
+
+ private SparseArrayMethodRewrites() {}
+
+ public static MethodInvokeRewriter rewriteSet() {
+ // Rewrite android/util/SparseArray#set to android/util/SparseArray#put
+ return (invoke, factory) ->
+ new CfInvoke(Opcodes.INVOKEVIRTUAL, factory.androidUtilSparseArrayMembers.put, false);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
index 26742bd..9d7d84c 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -100,25 +100,35 @@
}
public static ClassNameMapper mapperFromString(
- String contents, DiagnosticsHandler diagnosticsHandler, boolean allowEmptyMappedRanges)
+ String contents,
+ DiagnosticsHandler diagnosticsHandler,
+ boolean allowEmptyMappedRanges,
+ boolean allowExperimentalMapping)
throws IOException {
return mapperFromBufferedReader(
- CharSource.wrap(contents).openBufferedStream(), diagnosticsHandler, allowEmptyMappedRanges);
+ CharSource.wrap(contents).openBufferedStream(),
+ diagnosticsHandler,
+ allowEmptyMappedRanges,
+ allowExperimentalMapping);
}
private static ClassNameMapper mapperFromBufferedReader(
BufferedReader reader, DiagnosticsHandler diagnosticsHandler) throws IOException {
- return mapperFromBufferedReader(reader, diagnosticsHandler, false);
+ return mapperFromBufferedReader(reader, diagnosticsHandler, false, false);
}
public static ClassNameMapper mapperFromBufferedReader(
- BufferedReader reader, DiagnosticsHandler diagnosticsHandler, boolean allowEmptyMappedRanges)
+ BufferedReader reader,
+ DiagnosticsHandler diagnosticsHandler,
+ boolean allowEmptyMappedRanges,
+ boolean allowExperimentalMapping)
throws IOException {
try (ProguardMapReader proguardReader =
new ProguardMapReader(
reader,
diagnosticsHandler != null ? diagnosticsHandler : new Reporter(),
- allowEmptyMappedRanges)) {
+ allowEmptyMappedRanges,
+ allowExperimentalMapping)) {
ClassNameMapper.Builder builder = ClassNameMapper.builder();
proguardReader.parse(builder);
return builder.build();
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
index ae65b10..f1e483f 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
@@ -9,9 +9,9 @@
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.naming.MemberNaming.Signature;
import com.android.tools.r8.naming.ProguardMap.Builder;
+import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
import com.android.tools.r8.naming.mappinginformation.MappingInformation;
import com.android.tools.r8.naming.mappinginformation.MappingInformationDiagnostics;
-import com.android.tools.r8.naming.mappinginformation.MetaInfMappingInformation;
import com.android.tools.r8.position.TextPosition;
import com.android.tools.r8.utils.IdentifierUtils;
import com.android.tools.r8.utils.StringUtils;
@@ -66,6 +66,7 @@
private final JsonParser jsonParser = new JsonParser();
private final DiagnosticsHandler diagnosticsHandler;
private final boolean allowEmptyMappedRanges;
+ private final boolean allowExperimentalMapping;
@Override
public void close() throws IOException {
@@ -75,10 +76,12 @@
ProguardMapReader(
BufferedReader reader,
DiagnosticsHandler diagnosticsHandler,
- boolean allowEmptyMappedRanges) {
+ boolean allowEmptyMappedRanges,
+ boolean allowExperimentalMapping) {
this.reader = reader;
this.diagnosticsHandler = diagnosticsHandler;
this.allowEmptyMappedRanges = allowEmptyMappedRanges;
+ this.allowExperimentalMapping = allowExperimentalMapping;
assert reader != null;
assert diagnosticsHandler != null;
}
@@ -266,9 +269,18 @@
diagnosticsHandler,
lineNo,
info -> {
- MetaInfMappingInformation generatorInfo = info.asMetaInfMappingInformation();
+ MapVersionMappingInformation generatorInfo = info.asMetaInfMappingInformation();
if (generatorInfo != null) {
- version = generatorInfo.getMapVersion();
+ if (generatorInfo.getMapVersion().equals(MapVersion.MapVersionExperimental)) {
+ // A mapping file that is marked "experimental" will be treated as an unversioned
+ // file if the compiler/tool is not explicitly running with experimental support.
+ version =
+ allowExperimentalMapping
+ ? MapVersion.MapVersionExperimental
+ : MapVersion.MapVersionNone;
+ } else {
+ version = generatorInfo.getMapVersion();
+ }
}
onMappingInfo.accept(info);
});
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
index 73ae362..ccc2ef7 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapSupplier.java
@@ -7,7 +7,7 @@
import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.Version;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.naming.mappinginformation.MetaInfMappingInformation;
+import com.android.tools.r8.naming.mappinginformation.MapVersionMappingInformation;
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.ChainableStringConsumer;
import com.android.tools.r8.utils.ExceptionUtils;
@@ -107,7 +107,7 @@
if (mapVersion.isGreaterThan(MapVersion.MapVersionNone)) {
builder
.append("# ")
- .append(new MetaInfMappingInformation(mapVersion).serialize())
+ .append(new MapVersionMappingInformation(mapVersion).serialize())
.append("\n");
}
consumer.accept(builder.toString(), reporter);
diff --git a/src/main/java/com/android/tools/r8/naming/SeedMapper.java b/src/main/java/com/android/tools/r8/naming/SeedMapper.java
index 927cf6e..dee503b 100644
--- a/src/main/java/com/android/tools/r8/naming/SeedMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/SeedMapper.java
@@ -74,7 +74,7 @@
private static SeedMapper seedMapperFromInputStream(Reporter reporter, InputStream in)
throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
- try (ProguardMapReader proguardReader = new ProguardMapReader(reader, reporter, false)) {
+ try (ProguardMapReader proguardReader = new ProguardMapReader(reader, reporter, false, false)) {
SeedMapper.Builder builder = SeedMapper.builder(reporter);
proguardReader.parse(builder);
return builder.build();
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/MetaInfMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/MapVersionMappingInformation.java
similarity index 83%
rename from src/main/java/com/android/tools/r8/naming/mappinginformation/MetaInfMappingInformation.java
rename to src/main/java/com/android/tools/r8/naming/mappinginformation/MapVersionMappingInformation.java
index ea3824a..cd72831 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/MetaInfMappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/MapVersionMappingInformation.java
@@ -12,14 +12,14 @@
import com.google.gson.JsonPrimitive;
import java.util.function.Consumer;
-public class MetaInfMappingInformation extends MappingInformation {
+public class MapVersionMappingInformation extends MappingInformation {
- public static final String ID = "com.android.tools.r8.metainf";
- public static final String MAP_VERSION_KEY = "map-version";
+ public static final String ID = "com.android.tools.r8.mapping";
+ public static final String MAP_VERSION_KEY = "version";
private final MapVersion mapVersion;
- public MetaInfMappingInformation(MapVersion mapVersion) {
+ public MapVersionMappingInformation(MapVersion mapVersion) {
super();
this.mapVersion = mapVersion;
}
@@ -35,7 +35,7 @@
}
@Override
- public MetaInfMappingInformation asMetaInfMappingInformation() {
+ public MapVersionMappingInformation asMetaInfMappingInformation() {
return this;
}
@@ -73,6 +73,6 @@
if (mapVersion == null) {
return;
}
- onMappingInfo.accept(new MetaInfMappingInformation(mapVersion));
+ onMappingInfo.accept(new MapVersionMappingInformation(mapVersion));
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
index 145080f..f8f1e53 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/MappingInformation.java
@@ -22,7 +22,7 @@
return false;
}
- public MetaInfMappingInformation asMetaInfMappingInformation() {
+ public MapVersionMappingInformation asMetaInfMappingInformation() {
return null;
}
@@ -83,8 +83,8 @@
int lineNumber,
Consumer<MappingInformation> onMappingInfo) {
switch (id) {
- case MetaInfMappingInformation.ID:
- MetaInfMappingInformation.deserialize(
+ case MapVersionMappingInformation.ID:
+ MapVersionMappingInformation.deserialize(
version, object, diagnosticsHandler, lineNumber, onMappingInfo);
return;
case FileNameInformation.ID:
diff --git a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
index 2c57aa4..3893e32 100644
--- a/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
+++ b/src/main/java/com/android/tools/r8/naming/signature/GenericSignatureRewriter.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.GenericSignatureTypeRewriter;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.ThreadUtils;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
@@ -20,13 +19,11 @@
private final AppView<?> appView;
private final NamingLens namingLens;
private final InternalOptions options;
- private final Reporter reporter;
public GenericSignatureRewriter(AppView<?> appView, NamingLens namingLens) {
this.appView = appView;
this.namingLens = namingLens;
this.options = appView.options();
- this.reporter = options.reporter;
}
public void run(Iterable<? extends DexProgramClass> classes, ExecutorService executorService)
diff --git a/src/main/java/com/android/tools/r8/retrace/Retrace.java b/src/main/java/com/android/tools/r8/retrace/Retrace.java
index 71d88c3..faf8616 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.retrace.RetraceCommand.Builder;
import com.android.tools.r8.retrace.internal.PlainStackTraceLineParser;
import com.android.tools.r8.retrace.internal.RetraceAbortException;
+import com.android.tools.r8.retrace.internal.RetracerImpl;
import com.android.tools.r8.retrace.internal.StackTraceRegularExpressionParser;
import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.ListUtils;
@@ -224,13 +225,25 @@
* @param command The command that describes the desired behavior of this retrace invocation.
*/
public static void run(RetraceCommand command) {
+ boolean allowExperimentalMapVersion =
+ System.getProperty("com.android.tools.r8.experimentalmapping") != null;
+ runForTesting(command, allowExperimentalMapVersion);
+ }
+
+ static void runForTesting(RetraceCommand command, boolean allowExperimentalMapping) {
try {
Timing timing = Timing.create("R8 retrace", command.printMemory());
timing.begin("Read proguard map");
RetraceOptions options = command.getOptions();
DiagnosticsHandler diagnosticsHandler = options.getDiagnosticsHandler();
+ // The setup of a retracer should likely also follow a builder pattern instead of having
+ // static create methods. That would avoid the need to method overload the construction here
+ // and the default create would become the default build of a retracer.
Retracer retracer =
- Retracer.createDefault(options.getProguardMapProducer(), diagnosticsHandler);
+ RetracerImpl.create(
+ options.getProguardMapProducer(),
+ options.getDiagnosticsHandler(),
+ allowExperimentalMapping);
timing.end();
timing.begin("Report result");
StringRetrace stringRetrace =
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceHelper.java b/src/main/java/com/android/tools/r8/retrace/RetraceHelper.java
new file mode 100644
index 0000000..c966897
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceHelper.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2021, 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.retrace;
+
+/** Non-kept class for internal access from tests. */
+public class RetraceHelper {
+
+ public static void runForTesting(RetraceCommand command, boolean allowExperimentalMapping) {
+ Retrace.runForTesting(command, allowExperimentalMapping);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/Retracer.java b/src/main/java/com/android/tools/r8/retrace/Retracer.java
index 015dac8..b99f20c 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retracer.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retracer.java
@@ -28,6 +28,6 @@
static Retracer createDefault(
ProguardMapProducer proguardMapProducer, DiagnosticsHandler diagnosticsHandler) {
- return RetracerImpl.create(proguardMapProducer, diagnosticsHandler);
+ return RetracerImpl.create(proguardMapProducer, diagnosticsHandler, false);
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java
index 33a7d80..d1c5874 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetracerImpl.java
@@ -26,7 +26,9 @@
}
public static RetracerImpl create(
- ProguardMapProducer proguardMapProducer, DiagnosticsHandler diagnosticsHandler) {
+ ProguardMapProducer proguardMapProducer,
+ DiagnosticsHandler diagnosticsHandler,
+ boolean allowExperimentalMapping) {
if (proguardMapProducer instanceof DirectClassNameMapperProguardMapProducer) {
return new RetracerImpl(
((DirectClassNameMapperProguardMapProducer) proguardMapProducer).getClassNameMapper());
@@ -34,7 +36,10 @@
try {
ClassNameMapper classNameMapper =
ClassNameMapper.mapperFromBufferedReader(
- new BufferedReader(proguardMapProducer.get()), diagnosticsHandler, true);
+ new BufferedReader(proguardMapProducer.get()),
+ diagnosticsHandler,
+ true,
+ allowExperimentalMapping);
return new RetracerImpl(classNameMapper);
} catch (Throwable throwable) {
throw new InvalidMappingFileException(throwable);
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index 86c8ef6..7c2162c 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -2983,7 +2983,8 @@
ImmutableSet<ProguardKeepRuleBase> keepAllSet =
ImmutableSet.of(appView.options().getProguardConfiguration().getKeepAllRule());
for (DexProgramClass clazz : appView.appInfo().classes()) {
- if (appView.getSyntheticItems().isNonLegacySynthetic(clazz)) {
+ if (appView.getSyntheticItems().isSyntheticClass(clazz)
+ && !appView.getSyntheticItems().isSubjectToKeepRules(clazz)) {
// Don't treat compiler synthesized classes as kept roots.
continue;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index ef2f149..577ecc2 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.EnclosingMethodAttribute;
+import com.android.tools.r8.graph.GenericSignatureTypeVariableRemover;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.NestMemberClassAttribute;
import com.android.tools.r8.ir.optimize.info.MutableFieldOptimizationInfo;
@@ -45,6 +46,7 @@
private final UnusedItemsPrinter unusedItemsPrinter;
private final Set<DexType> prunedTypes = Sets.newIdentityHashSet();
private final Set<DexMethod> methodsToKeepForConfigurationDebugging = Sets.newIdentityHashSet();
+ private final GenericSignatureTypeVariableRemover typeVariableRemover;
public TreePruner(AppView<AppInfoWithLiveness> appView) {
this(appView, DefaultTreePrunerConfiguration.getInstance());
@@ -61,6 +63,11 @@
ExceptionUtils.withConsumeResourceHandler(
options.reporter, options.usageInformationConsumer, s))
: UnusedItemsPrinter.DONT_PRINT;
+ this.typeVariableRemover =
+ new GenericSignatureTypeVariableRemover(
+ appView,
+ this::isAttributeReferencingMissingOrPrunedType,
+ this::isAttributeReferencingPrunedItem);
}
public DirectMappedDexApplication run(ExecutorService executorService) throws ExecutionException {
@@ -193,6 +200,7 @@
if (reachableStaticFields != null) {
clazz.setStaticFields(reachableStaticFields);
}
+ typeVariableRemover.removeDeadGenericSignatureTypeVariables(clazz);
clazz.removeInnerClasses(this::isAttributeReferencingMissingOrPrunedType);
clazz.removeEnclosingMethodAttribute(this::isAttributeReferencingPrunedItem);
rewriteNestAttributes(clazz);
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 3475116..800332f 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -9,6 +9,7 @@
import static com.android.tools.r8.ir.code.Invoke.Type.STATIC;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.Code;
@@ -1793,10 +1794,11 @@
private boolean foundIllegalAccess;
private ProgramMethod context;
- private final AppView<AppInfoWithLiveness> appView;
+ private final AppView<? extends AppInfoWithClassHierarchy> appView;
private final DexClass source;
- public IllegalAccessDetector(AppView<AppInfoWithLiveness> appView, DexClass source) {
+ public IllegalAccessDetector(
+ AppView<? extends AppInfoWithClassHierarchy> appView, DexClass source) {
super(appView.dexItemFactory());
this.appView = appView;
this.source = source;
diff --git a/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java b/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java
index 7dbdec5..a201d6a 100644
--- a/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java
+++ b/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.utils.BooleanBox;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.Collection;
import java.util.Map;
@@ -29,6 +30,7 @@
null;
private ImmutableMap.Builder<DexType, SyntheticMethodReference> newNonLegacyMethods = null;
private ImmutableMap.Builder<DexType, LegacySyntheticReference> newLegacyClasses = null;
+ private ImmutableSet.Builder<DexType> newSyntheticInputs = null;
public Builder(CommittedSyntheticsCollection parent) {
this.parent = parent;
@@ -81,6 +83,11 @@
return this;
}
+ Builder addToSyntheticInputs() {
+ newSyntheticInputs = ImmutableSet.builder();
+ return this;
+ }
+
public CommittedSyntheticsCollection build() {
if (newNonLegacyClasses == null && newNonLegacyMethods == null && newLegacyClasses == null) {
return parent;
@@ -97,13 +104,22 @@
newLegacyClasses == null
? parent.legacyTypes
: newLegacyClasses.putAll(parent.legacyTypes).build();
+ ImmutableSet<DexType> allSyntheticInputs =
+ newSyntheticInputs == null
+ ? parent.syntheticInputs
+ : newSyntheticInputs
+ .addAll(allNonLegacyClasses.keySet())
+ .addAll(allNonLegacyMethods.keySet())
+ .addAll(allLegacyClasses.keySet())
+ .build();
return new CommittedSyntheticsCollection(
- allLegacyClasses, allNonLegacyMethods, allNonLegacyClasses);
+ allLegacyClasses, allNonLegacyMethods, allNonLegacyClasses, allSyntheticInputs);
}
}
private static final CommittedSyntheticsCollection EMPTY =
- new CommittedSyntheticsCollection(ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of());
+ new CommittedSyntheticsCollection(
+ ImmutableMap.of(), ImmutableMap.of(), ImmutableMap.of(), ImmutableSet.of());
/**
* Immutable set of synthetic types in the application (eg, committed).
@@ -118,13 +134,18 @@
/** Mapping from synthetic type to its synthetic class item description. */
private final ImmutableMap<DexType, SyntheticProgramClassReference> nonLegacyClasses;
+ /** Set of synthetic types that were present in the input. */
+ private final ImmutableSet<DexType> syntheticInputs;
+
public CommittedSyntheticsCollection(
ImmutableMap<DexType, LegacySyntheticReference> legacyTypes,
ImmutableMap<DexType, SyntheticMethodReference> nonLegacyMethods,
- ImmutableMap<DexType, SyntheticProgramClassReference> nonLegacyClasses) {
+ ImmutableMap<DexType, SyntheticProgramClassReference> nonLegacyClasses,
+ ImmutableSet<DexType> syntheticInputs) {
this.legacyTypes = legacyTypes;
this.nonLegacyMethods = nonLegacyMethods;
this.nonLegacyClasses = nonLegacyClasses;
+ this.syntheticInputs = syntheticInputs;
assert legacyTypes.size() + nonLegacyMethods.size() + nonLegacyClasses.size()
== Sets.union(
Sets.union(nonLegacyMethods.keySet(), nonLegacyClasses.keySet()),
@@ -140,8 +161,15 @@
return new Builder(this);
}
+ Builder builderForSyntheticInputs() {
+ return new Builder(this).addToSyntheticInputs();
+ }
+
boolean isEmpty() {
- return legacyTypes.isEmpty() && nonLegacyMethods.isEmpty() && nonLegacyClasses.isEmpty();
+ boolean empty =
+ legacyTypes.isEmpty() && nonLegacyMethods.isEmpty() && nonLegacyClasses.isEmpty();
+ assert !empty || syntheticInputs.isEmpty();
+ return empty;
}
boolean containsType(DexType type) {
@@ -156,6 +184,10 @@
return nonLegacyMethods.containsKey(type) || nonLegacyClasses.containsKey(type);
}
+ public boolean containsSyntheticInput(DexType type) {
+ return syntheticInputs.contains(type);
+ }
+
public ImmutableMap<DexType, LegacySyntheticReference> getLegacyTypes() {
return legacyTypes;
}
@@ -176,6 +208,10 @@
return nonLegacyClasses.get(type);
}
+ public void forEachSyntheticInput(Consumer<DexType> fn) {
+ syntheticInputs.forEach(fn);
+ }
+
public void forEachNonLegacyItem(Consumer<SyntheticReference<?, ?, ?>> fn) {
nonLegacyMethods.forEach((t, r) -> fn.accept(r));
nonLegacyClasses.forEach((t, r) -> fn.accept(r));
@@ -217,7 +253,16 @@
return new CommittedSyntheticsCollection(
rewriteItems(legacyTypes, lens),
rewriteItems(nonLegacyMethods, lens),
- rewriteItems(nonLegacyClasses, lens));
+ rewriteItems(nonLegacyClasses, lens),
+ rewriteItems(syntheticInputs, lens));
+ }
+
+ private static ImmutableSet<DexType> rewriteItems(Set<DexType> items, NonIdentityGraphLens lens) {
+ ImmutableSet.Builder<DexType> rewrittenItems = ImmutableSet.builder();
+ for (DexType item : items) {
+ rewrittenItems.add(lens.lookupType(item));
+ }
+ return rewrittenItems.build();
}
private static <R extends Rewritable<R>> ImmutableMap<DexType, R> rewriteItems(
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
index 8f883ea..5fa5498 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -35,6 +35,7 @@
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.hash.HashCode;
import java.util.ArrayList;
@@ -237,6 +238,12 @@
}
});
+ SyntheticFinalizationGraphLens syntheticFinalizationGraphLens = lensBuilder.build(appView);
+
+ ImmutableSet.Builder<DexType> finalInputSyntheticsBuilder = ImmutableSet.builder();
+ committed.forEachSyntheticInput(
+ type -> finalInputSyntheticsBuilder.add(syntheticFinalizationGraphLens.lookupType(type)));
+
// TODO(b/181858113): Remove once deprecated main-dex-list is removed.
MainDexInfo.Builder mainDexInfoBuilder = appView.appInfo().getMainDexInfo().builderFromCopy();
derivedMainDexTypes.forEach(mainDexInfoBuilder::addList);
@@ -246,9 +253,12 @@
SyntheticItems.INVALID_ID_AFTER_SYNTHETIC_FINALIZATION,
application,
new CommittedSyntheticsCollection(
- committed.getLegacyTypes(), finalMethods, finalClasses),
+ committed.getLegacyTypes(),
+ finalMethods,
+ finalClasses,
+ finalInputSyntheticsBuilder.build()),
ImmutableList.of()),
- lensBuilder.build(appView),
+ syntheticFinalizationGraphLens,
PrunedItems.builder().setPrunedApp(application).addRemovedClasses(prunedSynthetics).build(),
mainDexInfoBuilder.build());
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index d6c93aa..b90083a 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -109,7 +109,7 @@
this.committed = committed;
}
- public static void collectSyntheticInputs(AppView<AppInfo> appView) {
+ public static void collectSyntheticInputs(AppView<?> appView) {
// Collecting synthetic items must be the very first task after application build.
SyntheticItems synthetics = appView.getSyntheticItems();
assert synthetics.nextSyntheticId == 0;
@@ -119,7 +119,8 @@
// If the compilation is in intermediate mode the synthetics should just be passed through.
return;
}
- CommittedSyntheticsCollection.Builder builder = synthetics.committed.builder();
+ CommittedSyntheticsCollection.Builder builder =
+ synthetics.committed.builderForSyntheticInputs();
// TODO(b/158159959): Consider identifying synthetics in the input reader to speed this up.
for (DexProgramClass clazz : appView.appInfo().classes()) {
SyntheticMarker marker =
@@ -127,10 +128,9 @@
if (marker.isSyntheticMethods()) {
clazz.forEachProgramMethod(
// TODO(b/158159959): Support having multiple methods per class.
- method -> {
- builder.addNonLegacyMethod(
- new SyntheticMethodDefinition(marker.getKind(), marker.getContext(), method));
- });
+ method ->
+ builder.addNonLegacyMethod(
+ new SyntheticMethodDefinition(marker.getKind(), marker.getContext(), method)));
} else if (marker.isSyntheticClass()) {
builder.addNonLegacyClass(
new SyntheticProgramClassDefinition(marker.getKind(), marker.getContext(), clazz));
@@ -143,7 +143,15 @@
CommittedItems commit =
new CommittedItems(
synthetics.nextSyntheticId, appView.appInfo().app(), committed, ImmutableList.of());
- appView.setAppInfo(new AppInfo(commit, appView.appInfo().getMainDexInfo()));
+ if (appView.appInfo().hasClassHierarchy()) {
+ appView
+ .withClassHierarchy()
+ .setAppInfo(appView.appInfo().withClassHierarchy().rebuildWithClassHierarchy(commit));
+ } else {
+ appView
+ .withoutClassHierarchy()
+ .setAppInfo(new AppInfo(commit, appView.appInfo().getMainDexInfo()));
+ }
}
// Predicates and accessors.
@@ -239,6 +247,11 @@
return null;
}
+ public boolean isSubjectToKeepRules(DexProgramClass clazz) {
+ assert isSyntheticClass(clazz);
+ return committed.containsSyntheticInput(clazz.getType());
+ }
+
public boolean isSyntheticClass(DexType type) {
return isLegacySyntheticClass(type) || isNonLegacySynthetic(type);
}
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 210ae9b..2cb1e9e 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -82,6 +82,7 @@
import java.util.Set;
import java.util.TreeSet;
import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -1268,6 +1269,8 @@
public BiConsumer<DexItemFactory, HorizontallyMergedClasses> horizontallyMergedClassesConsumer =
ConsumerUtils.emptyBiConsumer();
+ public BiFunction<Iterable<DexProgramClass>, DexProgramClass, DexProgramClass>
+ horizontalClassMergingTarget = (candidates, target) -> target;
public BiConsumer<DexItemFactory, EnumDataMap> unboxedEnumsConsumer =
ConsumerUtils.emptyBiConsumer();
@@ -1515,8 +1518,7 @@
}
public boolean canUseDexPcAsDebugInformation() {
- // TODO(b/37830524): Enable for min-api 26 (OREO) and above.
- return enablePcDebugInfoOutput;
+ return enablePcDebugInfoOutput && !debug && hasMinApi(AndroidApiLevel.O);
}
public boolean isInterfaceMethodDesugaringEnabled() {
diff --git a/src/main/java/com/android/tools/r8/utils/IterableUtils.java b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
index 1354a33..edf9879 100644
--- a/src/main/java/com/android/tools/r8/utils/IterableUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
@@ -51,6 +51,10 @@
return defaultValue;
}
+ public static <T> T first(Iterable<T> iterable) {
+ return iterable.iterator().next();
+ }
+
public static <T> int firstIndexMatching(Iterable<T> iterable, Predicate<T> tester) {
int i = 0;
for (T element : iterable) {
diff --git a/src/test/java/com/android/tools/r8/D8TestRunResult.java b/src/test/java/com/android/tools/r8/D8TestRunResult.java
index c4427d1..4e54359 100644
--- a/src/test/java/com/android/tools/r8/D8TestRunResult.java
+++ b/src/test/java/com/android/tools/r8/D8TestRunResult.java
@@ -38,6 +38,6 @@
if (proguardMap == null) {
return super.getStackTrace();
}
- return super.getStackTrace().retrace(proguardMap);
+ return super.getStackTrace().retraceAllowExperimentalMapping(proguardMap);
}
}
diff --git a/src/test/java/com/android/tools/r8/KotlinTestParameters.java b/src/test/java/com/android/tools/r8/KotlinTestParameters.java
index 69a1cff..8c4d024 100644
--- a/src/test/java/com/android/tools/r8/KotlinTestParameters.java
+++ b/src/test/java/com/android/tools/r8/KotlinTestParameters.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertNotNull;
import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler;
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
import com.android.tools.r8.ToolHelper.KotlinTargetVersion;
import java.util.ArrayList;
import java.util.List;
@@ -81,7 +82,12 @@
int index = 0;
for (KotlinCompiler kotlinc : compilers) {
for (KotlinTargetVersion targetVersion : targetVersions) {
- testParameters.add(new KotlinTestParameters(kotlinc, targetVersion, index++));
+ // KotlinTargetVersion java 6 is deprecated from kotlinc 1.5 and forward, no need to run
+ // tests on that target.
+ if (targetVersion != KotlinTargetVersion.JAVA_6
+ || kotlinc.isNot(KotlinCompilerVersion.KOTLINC_1_5_0_M2)) {
+ testParameters.add(new KotlinTestParameters(kotlinc, targetVersion, index++));
+ }
}
}
return new KotlinTestParametersCollection(testParameters);
diff --git a/src/test/java/com/android/tools/r8/R8TestRunResult.java b/src/test/java/com/android/tools/r8/R8TestRunResult.java
index f35f686..3ef4901 100644
--- a/src/test/java/com/android/tools/r8/R8TestRunResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestRunResult.java
@@ -49,7 +49,7 @@
@Override
public StackTrace getStackTrace() {
- return super.getStackTrace().retrace(proguardMap);
+ return super.getStackTrace().retraceAllowExperimentalMapping(proguardMap);
}
public StackTrace getOriginalStackTrace() {
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardModifyDiagnosticsLevelTest.java b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardModifyDiagnosticsLevelTest.java
index ffe8230..94b6238 100644
--- a/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardModifyDiagnosticsLevelTest.java
+++ b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardModifyDiagnosticsLevelTest.java
@@ -78,7 +78,7 @@
}
@Test
- public void dontFailCompilationOnCheckDiscardedFailure() {
+ public void dontFailCompilationIfCheckDiscardedFails() {
try {
testForR8(Backend.DEX)
.addProgramClasses(
diff --git a/src/test/java/com/android/tools/r8/d8/IncompatiblePrimitiveTypesTest.java b/src/test/java/com/android/tools/r8/d8/IncompatiblePrimitiveTypesTest.java
index 1ecdaac..6117999 100644
--- a/src/test/java/com/android/tools/r8/d8/IncompatiblePrimitiveTypesTest.java
+++ b/src/test/java/com/android/tools/r8/d8/IncompatiblePrimitiveTypesTest.java
@@ -84,17 +84,13 @@
.addProgramFiles(inputJar)
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), "TestClass");
- TestRunResult<?> dxResult =
- testForDX().addProgramFiles(inputJar).run(parameters.getRuntime(), "TestClass");
if (parameters.getRuntime().asDex().getVm().getVersion().isNewerThan(Version.V4_4_4)) {
d8Result.assertSuccessWithOutput(expectedOutput);
- dxResult.assertSuccessWithOutput(expectedOutput);
} else {
// TODO(b/119812046): On Art 4.0.4 and 4.4.4 it is a verification error to use one short type
- // as another short type.
+ // as another short type.
Matcher<String> expectedError = containsString("java.lang.VerifyError");
d8Result.assertFailureWithErrorThatMatches(expectedError);
- dxResult.assertFailureWithErrorThatMatches(expectedError);
}
}
}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java
index 8c2ef82..270e7cd 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/EnsureNoDebugInfoEmittedForPcOnlyTestRunner.java
@@ -4,29 +4,28 @@
package com.android.tools.r8.debuginfo;
-import static com.android.tools.r8.Collectors.toSingle;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineFrame;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineStack;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isTopOfStackTrace;
+import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForFileNameAndLineNumber;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertNull;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
-import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper.DexVm.Version;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.retrace.RetraceFrameResult;
+import com.android.tools.r8.graph.DexDebugEntry;
+import com.android.tools.r8.graph.DexDebugEntryBuilder;
+import com.android.tools.r8.naming.retrace.StackTrace;
+import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.InstructionSubject;
-import com.android.tools.r8.utils.codeinspector.Matchers.LinePosition;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import com.google.common.collect.ImmutableList;
-import java.io.IOException;
-import java.util.concurrent.ExecutionException;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -43,76 +42,125 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters()
- .withDexRuntimesStartingFromIncluding(Version.V8_1_0)
- .withAllApiLevels()
- .build();
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
}
public EnsureNoDebugInfoEmittedForPcOnlyTestRunner(TestParameters parameters) {
this.parameters = parameters;
}
+ private boolean apiLevelSupportsPcOutput() {
+ return parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.O);
+ }
+
+ // TODO(b/37830524): Remove when activated.
+ private void enablePcDebugInfoOutput(InternalOptions options) {
+ options.enablePcDebugInfoOutput = true;
+ }
+
@Test
- public void testNoEmittedDebugInfo()
- throws ExecutionException, CompilationFailedException, IOException, NoSuchMethodException {
+ public void testD8Debug() throws Exception {
+ testForD8(parameters.getBackend())
+ .debug()
+ .addProgramClasses(MAIN)
+ .setMinApi(parameters.getApiLevel())
+ .internalEnableMappingOutput()
+ .addOptionsModification(this::enablePcDebugInfoOutput)
+ .run(parameters.getRuntime(), MAIN)
+ // For a debug build we always expect the output to have actual line information.
+ .inspectFailure(this::checkHasLineNumberInfo)
+ .inspectStackTrace(this::checkExpectedStackTrace);
+ }
+
+ @Test
+ public void testD8Release() throws Exception {
+ testForD8(parameters.getBackend())
+ .release()
+ .addProgramClasses(MAIN)
+ .setMinApi(parameters.getApiLevel())
+ .internalEnableMappingOutput()
+ .addOptionsModification(this::enablePcDebugInfoOutput)
+ .run(parameters.getRuntime(), MAIN)
+ .inspectFailure(
+ inspector -> {
+ if (apiLevelSupportsPcOutput()) {
+ checkNoDebugInfo(inspector, 5);
+ } else {
+ checkHasLineNumberInfo(inspector);
+ }
+ })
+ .inspectStackTrace(this::checkExpectedStackTrace);
+ }
+
+ @Test
+ public void testD8ReleaseWithoutMapOutput() throws Exception {
+ testForD8(parameters.getBackend())
+ .release()
+ .addProgramClasses(MAIN)
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(this::enablePcDebugInfoOutput)
+ .run(parameters.getRuntime(), MAIN)
+ // If compiling without a map output actual debug info should also be retained. Otherwise
+ // there would not be any way to obtain the actual lines.
+ .inspectFailure(this::checkHasLineNumberInfo)
+ .inspectStackTrace(this::checkExpectedStackTrace);
+ }
+
+ @Test
+ public void testNoEmittedDebugInfoR8() throws Exception {
+ assumeTrue(apiLevelSupportsPcOutput());
testForR8(parameters.getBackend())
.addProgramClasses(MAIN)
.addKeepMainRule(MAIN)
.addKeepAttributeLineNumberTable()
.setMinApi(parameters.getApiLevel())
- .addOptionsModification(
- internalOptions -> {
- // TODO(b/37830524): Remove when activated.
- internalOptions.enablePcDebugInfoOutput = true;
- })
+ .addOptionsModification(this::enablePcDebugInfoOutput)
.run(parameters.getRuntime(), MAIN)
.inspectOriginalStackTrace(
(stackTrace, inspector) -> {
assertEquals(MAIN.getTypeName(), stackTrace.get(0).className);
assertEquals("main", stackTrace.get(0).methodName);
- inspect(inspector);
+ checkNoDebugInfo(inspector, 1);
})
- .inspectStackTrace(
- (stackTrace, codeInspector) -> {
- MethodSubject mainSubject = codeInspector.clazz(MAIN).uniqueMethodWithName("main");
- LinePosition inlineStack =
- LinePosition.stack(
- LinePosition.create(
- Reference.methodFromMethod(MAIN.getDeclaredMethod("a")),
- INLINED_DEX_PC,
- 11,
- FILENAME_MAIN),
- LinePosition.create(
- Reference.methodFromMethod(MAIN.getDeclaredMethod("b")),
- INLINED_DEX_PC,
- 18,
- FILENAME_MAIN),
- LinePosition.create(
- mainSubject.asFoundMethodSubject().asMethodReference(),
- INLINED_DEX_PC,
- 23,
- FILENAME_MAIN));
- RetraceFrameResult retraceResult =
- mainSubject
- .streamInstructions()
- .filter(InstructionSubject::isThrow)
- .collect(toSingle())
- .retracePcPosition(codeInspector.retrace(), mainSubject);
- assertThat(retraceResult, isInlineFrame());
- assertThat(retraceResult, isInlineStack(inlineStack));
- assertThat(
- retraceResult,
- isTopOfStackTrace(
- stackTrace,
- ImmutableList.of(INLINED_DEX_PC, INLINED_DEX_PC, INLINED_DEX_PC)));
- });
+ .inspectStackTrace(this::checkExpectedStackTrace);
}
- private void inspect(CodeInspector inspector) {
+ private void checkNoDebugInfo(CodeInspector inspector, int expectedMethodsInMain) {
ClassSubject clazz = inspector.clazz(MAIN);
- assertEquals(1, clazz.allMethods().size());
+ assertEquals(expectedMethodsInMain, clazz.allMethods().size());
MethodSubject main = clazz.uniqueMethodWithName("main");
assertNull(main.getMethod().getCode().asDexCode().getDebugInfo());
}
+
+ private void checkHasLineNumberInfo(CodeInspector inspector) {
+ ClassSubject clazz = inspector.clazz(MAIN);
+ MethodSubject main = clazz.uniqueMethodWithName("main");
+ List<DexDebugEntry> entries =
+ new DexDebugEntryBuilder(main.getMethod(), inspector.getFactory()).build();
+ Set<Integer> lines = entries.stream().map(e -> e.line).collect(Collectors.toSet());
+ // Check some of the lines in main are present (not 27 as it may be optimized out).
+ assertTrue(lines.contains(22));
+ assertTrue(lines.contains(23));
+ assertTrue(lines.contains(25));
+ }
+
+ private void checkExpectedStackTrace(StackTrace stackTrace) {
+ assertThat(
+ stackTrace,
+ isSameExceptForFileNameAndLineNumber(
+ StackTrace.builder()
+ .add(line("a", 11))
+ .add(line("b", 18))
+ .add(line("main", 23))
+ .build()));
+ }
+
+ private StackTraceLine line(String method, int line) {
+ return StackTraceLine.builder()
+ .setClassName(MAIN.getTypeName())
+ .setMethodName(method)
+ .setLineNumber(line)
+ .setFileName(FILENAME_MAIN)
+ .build();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
index 646dbf9..e4dd6d9 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/AbstractBackportTest.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
import it.unimi.dsi.fastutil.ints.Int2IntAVLTreeMap;
import it.unimi.dsi.fastutil.ints.Int2IntSortedMap;
import java.io.IOException;
@@ -29,25 +30,71 @@
abstract class AbstractBackportTest extends TestBase {
protected final TestParameters parameters;
- private final Class<?> targetClass;
- private final Class<?> testClass;
+ private final ClassInfo targetClass;
+ private final ClassInfo testClass;
private final Path testJar;
private final String testClassName;
private final Int2IntSortedMap invokeStaticCounts = new Int2IntAVLTreeMap();
private final Set<String> ignoredInvokes = new HashSet<>();
+ private static class ClassInfo {
+ private final Class<?> clazz;
+ private final List<byte[]> classFileData;
+
+ private ClassInfo(Class<?> clazz) {
+ this.clazz = clazz;
+ this.classFileData = null;
+ }
+
+ private ClassInfo(byte[] classFileData) {
+ this.clazz = null;
+ this.classFileData = ImmutableList.of(classFileData);
+ }
+
+ private ClassInfo(List<byte[]> classFileData) {
+ this.clazz = null;
+ this.classFileData = classFileData;
+ }
+
+ String getName() {
+ return clazz != null ? clazz.getName() : extractClassName(classFileData.get(0));
+ }
+
+ TestBuilder<?, ?> addAsProgramClass(TestBuilder<?, ?> builder) throws IOException {
+ if (clazz != null) {
+ return builder.addProgramClassesAndInnerClasses(clazz);
+ } else {
+ return builder.addProgramClassFileData(classFileData);
+ }
+ }
+ }
+
AbstractBackportTest(TestParameters parameters, Class<?> targetClass,
Class<?> testClass) {
- this(parameters, targetClass, testClass, null, null);
+ this(parameters, new ClassInfo(targetClass), new ClassInfo(testClass), null, null);
+ }
+
+ AbstractBackportTest(
+ TestParameters parameters, byte[] targetClassFileData, List<byte[]> testClassFileData) {
+ this(
+ parameters,
+ new ClassInfo(targetClassFileData),
+ new ClassInfo(testClassFileData),
+ null,
+ null);
}
AbstractBackportTest(TestParameters parameters, Class<?> targetClass,
Path testJar, String testClassName) {
- this(parameters, targetClass, null, testJar, testClassName);
+ this(parameters, new ClassInfo(targetClass), null, testJar, testClassName);
}
- private AbstractBackportTest(TestParameters parameters, Class<?> targetClass,
- Class<?> testClass, Path testJar, String testClassName) {
+ private AbstractBackportTest(
+ TestParameters parameters,
+ ClassInfo targetClass,
+ ClassInfo testClass,
+ Path testJar,
+ String testClassName) {
this.parameters = parameters;
this.targetClass = targetClass;
this.testClass = testClass;
@@ -83,7 +130,7 @@
private void configureProgram(TestBuilder<?, ?> builder) throws IOException {
builder.addProgramClasses(MiniAssert.class, IgnoreInvokes.class);
if (testClass != null) {
- builder.addProgramClassesAndInnerClasses(testClass);
+ testClass.addAsProgramClass(builder);
} else {
builder.addProgramFiles(testJar);
}
@@ -188,5 +235,9 @@
"Expected <" + expected + "> to be same instance as <" + actual + '>');
}
}
+
+ static void fail(String message) {
+ throw new AssertionError("Failed: " + message);
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/SparseArrayBackportTest.java b/src/test/java/com/android/tools/r8/desugar/backports/SparseArrayBackportTest.java
new file mode 100644
index 0000000..61cf95d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/backports/SparseArrayBackportTest.java
@@ -0,0 +1,78 @@
+// Copyright (c) 2019, 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.desugar.backports;
+
+import com.android.tools.r8.TestParameters;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class SparseArrayBackportTest extends AbstractBackportTest {
+
+ private static final String SPARSE_ARRAY_DESCRIPTOR = "Landroid/util/SparseArray;";
+
+ @Parameters(name = "{0}")
+ public static Iterable<?> data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public SparseArrayBackportTest(TestParameters parameters) throws IOException {
+ super(
+ parameters,
+ SparseArrayBackportTest.getSparseArray(),
+ ImmutableList.of(
+ SparseArrayBackportTest.getTestRunner(), SparseArrayBackportTest.getSparseArray()));
+
+ // The constructor is used by the test and put has been available since API 1 and is the
+ // method set is rewritten to.
+ ignoreInvokes("<init>");
+ ignoreInvokes("put");
+ }
+
+ private static byte[] getSparseArray() throws IOException {
+ return transformer(SparseArray.class).setClassDescriptor(SPARSE_ARRAY_DESCRIPTOR).transform();
+ }
+
+ private static byte[] getTestRunner() throws IOException {
+ return transformer(TestRunner.class)
+ .replaceClassDescriptorInMethodInstructions(
+ descriptor(SparseArray.class), SPARSE_ARRAY_DESCRIPTOR)
+ .transform();
+ }
+
+ public static class SparseArray {
+ public void set(int index, Object value) {
+ TestRunner.doFail("set should not be called");
+ }
+
+ public void put(int index, Object value) {
+ TestRunner.doAssertEquals(42, index);
+ TestRunner.doAssertEquals("Forty two", value);
+ }
+ }
+
+ public static class TestRunner extends MiniAssert {
+
+ public static void main(String[] args) {
+ new SparseArray().set(42, "Forty two");
+ }
+
+ // Forwards to MiniAssert to avoid having to make it public.
+ public static void doAssertEquals(int expected, int actual) {
+ MiniAssert.assertEquals(expected, actual);
+ }
+
+ public static void doAssertEquals(Object expected, Object actual) {
+ MiniAssert.assertEquals(expected, actual);
+ }
+
+ public static void doFail(String message) {
+ MiniAssert.fail(message);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryCloneTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryCloneTest.java
new file mode 100644
index 0000000..73e5d67
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryCloneTest.java
@@ -0,0 +1,85 @@
+// Copyright (c) 2021, 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.desugar.desugaredlibrary;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.time.DayOfWeek;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class DesugaredLibraryCloneTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+ private final String EXPECTED = "Just another manic MONDAY";
+
+ @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDexRuntimes().withAllApiLevels().build(), BooleanUtils.values());
+ }
+
+ public DesugaredLibraryCloneTest(TestParameters parameters, boolean shrinkDesugaredLibrary) {
+ this.parameters = parameters;
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8()
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ DayOfWeek[] dayOfWeeks = new DayOfWeek[args.length + 1];
+ if (args.length == 0) {
+ dayOfWeeks[0] = DayOfWeek.MONDAY;
+ }
+ print(dayOfWeeks.clone());
+ }
+
+ public static void print(DayOfWeek[] arr) {
+ if (arr.length > 0) {
+ System.out.println("Just another manic " + arr[0]);
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryDumpInputsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryDumpInputsTest.java
index ea80848..03f861b 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryDumpInputsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryDumpInputsTest.java
@@ -5,6 +5,7 @@
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestParameters;
@@ -122,7 +123,17 @@
assertTrue(Files.exists(unzipped.resolve("program.jar")));
assertTrue(Files.exists(unzipped.resolve("library.jar")));
assertTrue(Files.exists(unzipped.resolve("desugared-library.json")));
- assertTrue(Files.exists(unzipped.resolve("build.properties")));
+ Path buildPropertiesPath = unzipped.resolve("build.properties");
+ assertTrue(Files.exists(buildPropertiesPath));
+ List<String> buildProperties = Files.readAllLines(buildPropertiesPath);
+ assertTrue(buildProperties.get(0).startsWith("tool="));
+ boolean isD8 = buildProperties.get(0).equals("tool=D8");
+ boolean isR8 = buildProperties.get(0).equals("tool=R8");
+ if ((shrinkDesugaredLibrary || isR8) && !isD8) {
+ assertTrue(Files.exists(unzipped.resolve("proguard.config")));
+ } else {
+ assertFalse(Files.exists(unzipped.resolve("proguard.config")));
+ }
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
index aa298a7..1bfbaac 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterMergeRegression.java
@@ -60,6 +60,13 @@
Consumer<R8FullTestBuilder> configurator =
r8FullTestBuilder ->
r8FullTestBuilder
+ .addOptionsModification(
+ options ->
+ options.testing.horizontalClassMergingTarget =
+ (candidates, target) -> candidates.iterator().next())
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector.assertMergedInto(BaseWithStatic.class, AFeatureWithStatic.class))
.enableNoVerticalClassMergingAnnotations()
.enableInliningAnnotations()
.noMinification();
@@ -127,7 +134,6 @@
}
}
- // Name is important, see predicate in tests/
public static class AFeatureWithStatic {
@NeverInline
diff --git a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
index df62f93..86176a0 100644
--- a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
@@ -8,7 +8,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.D8TestCompileResult;
@@ -122,7 +121,7 @@
assertEquals(1, classSignature.formalTypeParameters.size());
FormalTypeParameter formalTypeParameter = classSignature.formalTypeParameters.get(0);
assertEquals("T", formalTypeParameter.name);
- assertNull(formalTypeParameter.interfaceBounds);
+ assertTrue(formalTypeParameter.interfaceBounds.isEmpty());
assertTrue(formalTypeParameter.classBound.isClassTypeSignature());
ClassTypeSignature classBoundSignature = formalTypeParameter.classBound.asClassTypeSignature();
assertEquals(y.getDexProgramClass().type, classBoundSignature.type);
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/ClassSignatureTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/ClassSignatureTest.java
index 239d924..12a819d 100644
--- a/src/test/java/com/android/tools/r8/graph/genericsignature/ClassSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/ClassSignatureTest.java
@@ -8,7 +8,9 @@
import static com.google.common.base.Predicates.alwaysFalse;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessages;
@@ -16,12 +18,15 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GenericSignature;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.GenericSignaturePrinter;
+import com.android.tools.r8.graph.GenericSignatureTypeRewriter;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.Reporter;
+import java.util.function.Function;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -153,6 +158,39 @@
assertThrows(AssertionError.class, () -> testParsingAndPrintingError("<>Lfoo/bar/baz<TT;>;"));
}
+ @Test
+ public void testPruningInterfaceBound() {
+ DexItemFactory factory = new DexItemFactory();
+ DexType context = factory.createType("Lj$/util/stream/Node$OfPrimitive;");
+ String className = "j$.util.stream.Node$OfPrimitive";
+ TestDiagnosticMessagesImpl testDiagnosticMessages = new TestDiagnosticMessagesImpl();
+ ClassSignature parsedClassSignature =
+ GenericSignature.parseClassSignature(
+ className,
+ "<T_SPLITR::Lj$/util/Spliterator$OfPrimitive;T_NODE:Ljava/lang/Object;>"
+ + "Ljava/lang/Object;",
+ Origin.unknown(),
+ factory,
+ testDiagnosticMessages);
+ testDiagnosticMessages.assertNoMessages();
+ assertTrue(parsedClassSignature.hasSignature());
+ GenericSignatureTypeRewriter rewriter =
+ new GenericSignatureTypeRewriter(
+ factory,
+ dexType -> dexType.toDescriptorString().equals("Lj$/util/Spliterator$OfPrimitive;"),
+ Function.identity(),
+ context);
+ ClassSignature rewritten = rewriter.rewrite(parsedClassSignature);
+ assertNotNull(rewritten);
+ assertTrue(rewritten.hasSignature());
+ ClassSignature reparsed =
+ GenericSignature.parseClassSignature(
+ className, rewritten.toString(), Origin.unknown(), factory, testDiagnosticMessages);
+ assertTrue(reparsed.hasSignature());
+ testDiagnosticMessages.assertNoMessages();
+ assertEquals(rewritten.toString(), reparsed.toString());
+ }
+
private void testParsingAndPrintingEqual(String signature) {
ClassSignature parsed =
GenericSignature.parseClassSignature(
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureKeepReferencesPruneTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureKeepReferencesPruneTest.java
new file mode 100644
index 0000000..1c3ddbb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureKeepReferencesPruneTest.java
@@ -0,0 +1,112 @@
+// Copyright (c) 2021, 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.graph.genericsignature;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.graph.genericsignature.testclasses.Foo;
+import com.android.tools.r8.graph.genericsignature.testclasses.I;
+import com.android.tools.r8.graph.genericsignature.testclasses.J;
+import com.android.tools.r8.graph.genericsignature.testclasses.K;
+import com.android.tools.r8.graph.genericsignature.testclasses.L;
+import com.android.tools.r8.graph.genericsignature.testclasses.Main;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class GenericSignatureKeepReferencesPruneTest extends TestBase {
+
+ private final String[] EXPECTED =
+ new String[] {
+ Foo.class.getTypeName() + "<java.lang.String>",
+ I.class.getTypeName()
+ + "<java.lang.Integer, "
+ + Foo.class.getTypeName()
+ + "<java.lang.Integer>>",
+ I.class.getTypeName()
+ + "<java.lang.String, "
+ + Foo.class.getTypeName()
+ + "<java.lang.String>>",
+ "Hello world"
+ };
+
+ private final TestParameters parameters;
+ private final boolean isCompat;
+
+ @Parameters(name = "{0}, isCompat: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+ }
+
+ public GenericSignatureKeepReferencesPruneTest(TestParameters parameters, boolean isCompat) {
+ this.parameters = parameters;
+ this.isCompat = isCompat;
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ testForJvm()
+ .addProgramClasses(I.class, Foo.class, J.class, K.class, L.class)
+ .addProgramClassesAndInnerClasses(Main.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ (isCompat ? testForR8Compat(parameters.getBackend()) : testForR8(parameters.getBackend()))
+ .addProgramClasses(I.class, Foo.class, J.class, K.class, L.class)
+ .addProgramClassesAndInnerClasses(Main.class)
+ .addKeepClassAndMembersRules(Main.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepAttributeSignature()
+ .addKeepAttributeInnerClassesAndEnclosingMethod()
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .noMinification()
+ .run(parameters.getRuntime(), Main.class)
+ // TODO(b/184927364): Should have different output due to pruning the inner class.
+ .assertSuccessWithOutputLines(EXPECTED)
+ .inspect(this::inspectSignatures);
+ }
+
+ private void inspectSignatures(CodeInspector inspector) {
+ ClassSubject fooClass = inspector.clazz(Foo.class);
+ assertThat(fooClass, isPresent());
+ // TODO(b/184927364): Fullmode should not keep the interface bound.
+ assertEquals(
+ "<T::Ljava/lang/Comparable<TT;>;>Ljava/lang/Object;",
+ fooClass.getFinalSignatureAttribute());
+ ClassSubject iClass = inspector.clazz(I.class);
+ assertThat(iClass, isPresent());
+ // TODO(b/184927364): Fullmode should not keep the interface and class bound.
+ assertEquals(
+ "<T::Ljava/lang/Comparable<TT;>;R:L" + binaryName(Foo.class) + "<TT;>;>Ljava/lang/Object;",
+ iClass.getFinalSignatureAttribute());
+ ClassSubject fooInnerClass = inspector.clazz(Main.class.getTypeName() + "$1");
+ assertThat(fooInnerClass, isPresent());
+ // TODO(b/184927364): Fullmode should completely remove this signature
+ assertEquals(
+ "Ljava/lang/Object;L"
+ + binaryName(I.class)
+ + "<Ljava/lang/String;L"
+ + binaryName(Foo.class)
+ + "<Ljava/lang/String;>;>;",
+ fooInnerClass.getFinalSignatureAttribute());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePrunedOuterTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePrunedOuterTest.java
new file mode 100644
index 0000000..608b8d3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignaturePrunedOuterTest.java
@@ -0,0 +1,125 @@
+// Copyright (c) 2021, 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.graph.genericsignature;
+
+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.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class GenericSignaturePrunedOuterTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final boolean isCompat;
+
+ @Parameters(name = "{0}, isCompat: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
+ }
+
+ public GenericSignaturePrunedOuterTest(TestParameters parameters, boolean isCompat) {
+ this.parameters = parameters;
+ this.isCompat = isCompat;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ (isCompat ? testForR8Compat(parameters.getBackend()) : testForR8(parameters.getBackend()))
+ .addInnerClasses(getClass())
+ .addKeepClassAndMembersRules(Foo.class)
+ .addKeepMainRule(Main.class)
+ .addKeepAttributeSignature()
+ .addKeepAttributeInnerClassesAndEnclosingMethod()
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(options -> options.horizontalClassMergerOptions().disable())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(
+ "Bar::enclosingMethod", "Hello World", "Bar::enclosingMethod2", "Hello World")
+ .inspect(this::checkSignatures);
+ }
+
+ public void checkSignatures(CodeInspector inspector) {
+ checkSignature(
+ inspector.clazz(Bar.class.getTypeName() + "$1"),
+ "L"
+ + binaryName(Foo.class)
+ + "<"
+ + descriptor(Object.class)
+ + descriptor(Main.class)
+ + ">;");
+ checkSignature(
+ inspector.clazz(Bar.class.getTypeName() + "$2"),
+ "L"
+ + binaryName(Foo.class)
+ + "<"
+ + descriptor(Object.class)
+ + descriptor(Object.class)
+ + ">;");
+ }
+
+ private void checkSignature(ClassSubject classSubject, String expectedSignature) {
+ assertThat(classSubject, isPresent());
+ // TODO(b/185098797): Make sure to work for full mode.
+ if (!isCompat) {
+ return;
+ }
+ assertEquals(expectedSignature, classSubject.getFinalSignatureAttribute());
+ }
+
+ public abstract static class Foo<T, R> {
+
+ R foo(T r) {
+ System.out.println("Hello World");
+ return null;
+ }
+ }
+
+ public static class Bar {
+
+ public static <T, R extends Main> Foo<T, R> enclosingMethod() {
+ return new Foo<T, R>() {
+ @Override
+ R foo(T r) {
+ System.out.println("Bar::enclosingMethod");
+ return super.foo(r);
+ }
+ };
+ }
+
+ public static <T, R> Foo<T, R> enclosingMethod2() {
+ return new Foo<T, R>() {
+ @Override
+ R foo(T r) {
+ System.out.println("Bar::enclosingMethod2");
+ return super.foo(r);
+ }
+ };
+ }
+
+ public static void run() {
+ enclosingMethod().foo(null);
+ enclosingMethod2().foo(null);
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ Bar.run();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/testclasses/Foo.java b/src/test/java/com/android/tools/r8/graph/genericsignature/testclasses/Foo.java
new file mode 100644
index 0000000..81000d6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/testclasses/Foo.java
@@ -0,0 +1,19 @@
+// Copyright (c) 2021, 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.graph.genericsignature.testclasses;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+
+@NeverClassInline
+public class Foo<T extends Comparable<T>> implements J, K<T> {
+
+ @Override
+ @NeverInline
+ public String bar(String t) {
+ System.out.println("Foo::bar");
+ return t;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/testclasses/I.java b/src/test/java/com/android/tools/r8/graph/genericsignature/testclasses/I.java
new file mode 100644
index 0000000..089286d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/testclasses/I.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2021, 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.graph.genericsignature.testclasses;
+
+import com.android.tools.r8.NeverInline;
+
+public interface I<T extends Comparable<T>, R extends Foo<T>> extends L<R> {
+ @NeverInline
+ T method(T t);
+}
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/testclasses/J.java b/src/test/java/com/android/tools/r8/graph/genericsignature/testclasses/J.java
new file mode 100644
index 0000000..8deed10
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/testclasses/J.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2021, 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.graph.genericsignature.testclasses;
+
+public interface J {
+
+ String bar(String t);
+}
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/testclasses/K.java b/src/test/java/com/android/tools/r8/graph/genericsignature/testclasses/K.java
new file mode 100644
index 0000000..608c12d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/testclasses/K.java
@@ -0,0 +1,7 @@
+// Copyright (c) 2021, 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.graph.genericsignature.testclasses;
+
+public interface K<T> {}
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/testclasses/L.java b/src/test/java/com/android/tools/r8/graph/genericsignature/testclasses/L.java
new file mode 100644
index 0000000..bf3895b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/testclasses/L.java
@@ -0,0 +1,7 @@
+// Copyright (c) 2021, 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.graph.genericsignature.testclasses;
+
+public interface L<T> {}
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/testclasses/Main.java b/src/test/java/com/android/tools/r8/graph/genericsignature/testclasses/Main.java
new file mode 100644
index 0000000..ccf9f04
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/testclasses/Main.java
@@ -0,0 +1,39 @@
+// Copyright (c) 2021, 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.graph.genericsignature.testclasses;
+
+import java.lang.reflect.Type;
+
+public class Main extends Foo<String> implements I<Integer, Foo<Integer>> {
+
+ public static <T extends I<String, Foo<String>>> T test(T t) {
+ for (Type genericInterface : t.getClass().getGenericInterfaces()) {
+ System.out.println(genericInterface);
+ }
+ t.method("Hello world");
+ return t;
+ }
+
+ public static void main(String[] args) {
+ System.out.println(Main.class.getGenericSuperclass());
+ for (Type genericInterface : Main.class.getGenericInterfaces()) {
+ System.out.println(genericInterface);
+ }
+ test(
+ new I<String, Foo<String>>() {
+ @Override
+ public String method(String s) {
+ System.out.println(s);
+ return s;
+ }
+ });
+ }
+
+ @Override
+ public Integer method(Integer integer) {
+ System.out.println("Main::method");
+ return integer;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java
index 9ba1f75..f1f19ff 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java
@@ -222,12 +222,21 @@
mainClass),
builder ->
builder
+ .addHorizontallyMergedClassesInspector(
+ horizontallyMergedClassesInspector ->
+ horizontallyMergedClassesInspector
+ .assertIsCompleteMergeGroup(
+ NonNullParamInterfaceImpl.class,
+ NonNullParamAfterInvokeInterface.class)
+ .assertMergedInto(
+ NonNullParamAfterInvokeInterface.class,
+ NonNullParamInterfaceImpl.class))
.addOptionsModification(this::disableDevirtualization)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoVerticalClassMergingAnnotations());
- ClassSubject mainSubject = inspector.clazz(NonNullParamAfterInvokeInterface.class);
+ ClassSubject mainSubject = inspector.clazz(NonNullParamInterfaceImpl.class);
assertThat(mainSubject, isPresent());
MethodSubject checkViaCall = mainSubject.uniqueMethodWithName("checkViaCall");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
index 38bb02e..9cb52da 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlinerTest.java
@@ -91,8 +91,10 @@
.addHorizontallyMergedClassesInspector(
inspector ->
inspector
- .assertMergedInto(Iface1Impl.class, CycleReferenceBA.class)
- .assertMergedInto(Iface2Impl.class, CycleReferenceBA.class))
+ .assertIsCompleteMergeGroup(
+ Iface1Impl.class, Iface2Impl.class, CycleReferenceBA.class)
+ .assertMergedInto(CycleReferenceBA.class, Iface1Impl.class)
+ .assertMergedInto(Iface2Impl.class, Iface1Impl.class))
.allowAccessModification()
.noMinification()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
index a4b4d35..495eae6 100644
--- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -53,12 +53,6 @@
protected final TestParameters testParameters;
- // Some tests defined in subclasses, e.g., Metadata tests, don't care about access relaxation.
- protected AbstractR8KotlinTestBase(
- TestParameters parameters, KotlinTestParameters kotlinParameters) {
- this(parameters, kotlinParameters, false);
- }
-
protected AbstractR8KotlinTestBase(
TestParameters parameters,
KotlinTestParameters kotlinParameters,
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index 65e33d1..c8af414 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -71,6 +71,8 @@
testBuilder
// TODO(jsjeon): Introduce @NeverInline to kotlinR8TestResources
.addKeepRules("-neverinline class * { void test*State*(...); }")
+ .addNoHorizontalClassMergingRule(
+ "class_inliner_lambda_j_style.SamIface$Consumer")
.addHorizontallyMergedClassesInspector(
inspector ->
inspector
@@ -102,7 +104,9 @@
testBuilder ->
testBuilder
// TODO(jsjeon): Introduce @NeverInline to kotlinR8TestResources
- .addKeepRules("-neverinline class * { void test*State*(...); }"))
+ .addKeepRules("-neverinline class * { void test*State*(...); }")
+ .addNoHorizontalClassMergingRule(
+ "class_inliner_lambda_j_style.SamIface$Consumer"))
.inspect(
inspector -> {
// TODO(b/173337498): MainKt$testStateless$1 should always be class inlined.
diff --git a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
index 00c9978..4769427 100644
--- a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
@@ -48,7 +48,10 @@
final String mainClassName = ex1.getClassName();
final String extraRules = neverInlineMethod(mainClassName, testMethodSignature);
- runTest(FOLDER, mainClassName, testBuilder -> testBuilder.addKeepRules(extraRules))
+ runTest(
+ FOLDER,
+ mainClassName,
+ testBuilder -> testBuilder.addKeepRules(extraRules).allowAccessModification())
.inspect(
inspector -> {
ClassSubject clazz = checkClassIsKept(inspector, ex1.getClassName());
diff --git a/src/test/java/com/android/tools/r8/naming/MapReaderVersionTest.java b/src/test/java/com/android/tools/r8/naming/MapReaderVersionTest.java
index c678a70..5a4db90 100644
--- a/src/test/java/com/android/tools/r8/naming/MapReaderVersionTest.java
+++ b/src/test/java/com/android/tools/r8/naming/MapReaderVersionTest.java
@@ -5,12 +5,14 @@
import static junit.framework.TestCase.assertEquals;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessagesImpl;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.naming.mappinginformation.MappingInformation;
import com.android.tools.r8.utils.StringUtils;
+import com.google.common.io.CharSource;
import java.io.IOException;
import org.junit.Assert;
import org.junit.Test;
@@ -30,23 +32,33 @@
parameters.assertNoneRuntime();
}
+ private static ClassNameMapper read(DiagnosticsHandler diagnosticsHandler, String... lines)
+ throws IOException {
+ return ClassNameMapper.mapperFromBufferedReader(
+ CharSource.wrap(StringUtils.joinLines(lines)).openBufferedStream(),
+ diagnosticsHandler,
+ false,
+ true);
+ }
+
+ private static ClassNameMapper read(String... lines) throws IOException {
+ return read(null, lines);
+ }
+
@Test
public void testNoVersion() throws IOException {
ClassNameMapper mapper =
- ClassNameMapper.mapperFromString(
- StringUtils.joinLines(
- "pkg.Foo -> a.a:", "# { id: \"com.android.tools.r8.synthesized\" }"));
+ read("pkg.Foo -> a.a:", "# { id: \"com.android.tools.r8.synthesized\" }");
assertMapping("a.a", "pkg.Foo", false, mapper);
}
@Test
public void testExperimentalVersion() throws IOException {
ClassNameMapper mapper =
- ClassNameMapper.mapperFromString(
- StringUtils.joinLines(
- "# { id: 'com.android.tools.r8.metainf', map-version: 'experimental' }",
- "pkg.Foo -> a.a:",
- "# { id: 'com.android.tools.r8.synthesized' }"));
+ read(
+ "# { id: 'com.android.tools.r8.mapping', version: 'experimental' }",
+ "pkg.Foo -> a.a:",
+ "# { id: 'com.android.tools.r8.synthesized' }");
assertMapping("a.a", "pkg.Foo", true, mapper);
}
@@ -54,21 +66,20 @@
public void testConcatMapFiles() throws IOException {
TestDiagnosticMessagesImpl diagnostics = new TestDiagnosticMessagesImpl();
ClassNameMapper mapper =
- ClassNameMapper.mapperFromString(
- StringUtils.joinLines(
- // Default map-version is none.
- "pkg.Foo -> a.a:",
- "# { id: 'com.android.tools.r8.synthesized' }",
- // Section with map-version experimental.
- "# { id: 'com.android.tools.r8.metainf', map-version: 'experimental' }",
- "pkg.Bar -> a.b:",
- "# { id: 'com.android.tools.r8.synthesized' }",
- // Section reverting map-version back to none (to support tooling that
- // concatenates).
- "# { id: 'com.android.tools.r8.metainf', map-version: 'none' }",
- "pkg.Baz -> a.c:",
- "# { id: 'com.android.tools.r8.synthesized' }"),
- diagnostics);
+ read(
+ diagnostics,
+ // Default map-version is none.
+ "pkg.Foo -> a.a:",
+ "# { id: 'com.android.tools.r8.synthesized' }",
+ // Section with map-version experimental.
+ "# { id: 'com.android.tools.r8.mapping', version: 'experimental' }",
+ "pkg.Bar -> a.b:",
+ "# { id: 'com.android.tools.r8.synthesized' }",
+ // Section reverting map-version back to none (to support tooling that
+ // concatenates).
+ "# { id: 'com.android.tools.r8.mapping', version: 'none' }",
+ "pkg.Baz -> a.c:",
+ "# { id: 'com.android.tools.r8.synthesized' }");
diagnostics.assertNoMessages();
assertMapping("a.a", "pkg.Foo", false, mapper);
assertMapping("a.b", "pkg.Bar", true, mapper);
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/DesugarLambdaRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/DesugarLambdaRetraceTest.java
index f59b75d..1fbc7cf 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/DesugarLambdaRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/DesugarLambdaRetraceTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.naming.retrace;
-import static com.android.tools.r8.ir.desugar.LambdaClass.R8_LAMBDA_ACCESSOR_METHOD_PREFIX;
import static com.android.tools.r8.naming.retrace.StackTrace.isSame;
import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForFileName;
import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForFileNameAndLineNumber;
@@ -13,10 +12,8 @@
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8TestBuilder;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.BooleanUtils;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
@@ -59,73 +56,32 @@
return mode == CompilationMode.RELEASE ? 2 : 4;
}
- private boolean isSynthesizedLambdaFrame(StackTraceLine line) {
- // TODO(141287349): The mapping should not map the external name to the internal name!
- return SyntheticItemsTestUtils.isInternalLambda(Reference.classFromTypeName(line.className))
- || line.methodName.startsWith(R8_LAMBDA_ACCESSOR_METHOD_PREFIX);
- }
-
- private void checkLambdaFrames(StackTrace retracedStackTrace) {
- StackTrace lambdaFrames = retracedStackTrace.filter(this::isSynthesizedLambdaFrame);
- assertEquals(2, lambdaFrames.size());
-
- StackTraceLine syntheticLambdaClassFrame = lambdaFrames.get(1);
- if (syntheticLambdaClassFrame.hasLineNumber()) {
- assertEquals(mode == CompilationMode.RELEASE ? 0 : 2, syntheticLambdaClassFrame.lineNumber);
- }
- // Proguard retrace will take the class name until the first $ to construct the file
- // name, so for "-$$Lambda$...", the file name becomes "-.java".
- // TODO(b/141287349): Format the class name of desugared lambda classes.
- // assertEquals("-.java", syntheticLambdaClassFrame.fileName);
- }
-
private void checkIsSame(StackTrace actualStackTrace, StackTrace retracedStackTrace) {
// Even when SourceFile is present retrace replaces the file name in the stack trace.
- if (parameters.isCfRuntime()) {
- assertThat(retracedStackTrace, isSame(expectedStackTrace));
- } else {
- // With the frame from the lambda class filtered out the stack trace is the same.
- assertThat(
- retracedStackTrace.filter(line -> !isSynthesizedLambdaFrame(line)),
- isSame(expectedStackTrace));
- // Check the frame from the lambda class.
- checkLambdaFrames(retracedStackTrace);
- }
+ assertThat(retracedStackTrace, isSame(expectedStackTrace));
assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size());
}
private void checkIsSameExceptForFileName(
StackTrace actualStackTrace, StackTrace retracedStackTrace) {
// Even when SourceFile is present retrace replaces the file name in the stack trace.
- if (parameters.isCfRuntime()) {
- assertThat(retracedStackTrace, isSameExceptForFileName(expectedStackTrace));
- } else {
- // With the frames from the lambda class filtered out the stack trace is the same.
- assertThat(
- retracedStackTrace.filter(line -> !isSynthesizedLambdaFrame(line)),
- isSameExceptForFileName(expectedStackTrace));
- // Check the frame from the lambda class.
- checkLambdaFrames(retracedStackTrace);
- }
+ assertThat(retracedStackTrace, isSameExceptForFileName(expectedStackTrace));
assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size());
}
private void checkIsSameExceptForFileNameAndLineNumber(
StackTrace actualStackTrace, StackTrace retracedStackTrace) {
// Even when SourceFile is present retrace replaces the file name in the stack trace.
- if (parameters.isCfRuntime()) {
- assertThat(retracedStackTrace, isSameExceptForFileNameAndLineNumber(expectedStackTrace));
- } else {
- // With the frame from the lambda class filtered out the stack trace is the same.
- assertThat(
- retracedStackTrace.filter(line -> !isSynthesizedLambdaFrame(line)),
- isSameExceptForFileNameAndLineNumber(expectedStackTrace));
- // Check the frame from the lambda class.
- checkLambdaFrames(retracedStackTrace);
- }
+ assertThat(retracedStackTrace, isSameExceptForFileNameAndLineNumber(expectedStackTrace));
assertEquals(expectedActualStackTraceHeight(), actualStackTrace.size());
}
+ @Override
+ public void configure(R8TestBuilder<?> builder) {
+ // Enable pruning of lambda synthetics in retrace.
+ builder.enableExperimentalMapFileVersion();
+ }
+
@Test
public void testSourceFileAndLineNumberTable() throws Exception {
runTest(ImmutableList.of("-keepattributes SourceFile,LineNumberTable"), this::checkIsSame);
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
index 74eeed2..0cc94d7 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
@@ -12,8 +12,8 @@
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.retrace.ProguardMapProducer;
-import com.android.tools.r8.retrace.Retrace;
import com.android.tools.r8.retrace.RetraceCommand;
+import com.android.tools.r8.retrace.RetraceHelper;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.base.Equivalence;
import java.util.ArrayList;
@@ -227,7 +227,6 @@
private final String originalStderr;
private StackTrace(List<StackTraceLine> stackTraceLines, String originalStderr) {
- assert stackTraceLines.size() > 0;
this.stackTraceLines = stackTraceLines;
this.originalStderr = originalStderr;
}
@@ -301,16 +300,25 @@
return extractFromJvm(result.getStdErr());
}
+ public StackTrace retraceAllowExperimentalMapping(String map) {
+ return retrace(map, null, true);
+ }
+
public StackTrace retrace(String map) {
- return retrace(map, null);
+ return retrace(map, null, true);
}
public StackTrace retrace(String map, String regularExpression) {
+ return retrace(map, regularExpression, true);
+ }
+
+ public StackTrace retrace(
+ String map, String regularExpression, boolean allowExperimentalMapping) {
class Box {
List<String> result;
}
Box box = new Box();
- Retrace.run(
+ RetraceHelper.runForTesting(
RetraceCommand.builder()
.setProguardMapProducer(ProguardMapProducer.fromString(map))
.setStackTrace(
@@ -319,7 +327,8 @@
.collect(Collectors.toList()))
.setRegularExpression(regularExpression)
.setRetracedStackTraceConsumer(retraced -> box.result = retraced)
- .build());
+ .build(),
+ allowExperimentalMapping);
// Keep the original stderr in the retraced stacktrace.
return new StackTrace(internalExtractFromJvm(StringUtils.lines(box.result)), originalStderr);
}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java b/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java
index 38f7725..f7aae45 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceLambdaTest.java
@@ -92,7 +92,7 @@
stackTrace -> {
int frames = parameters.isCfRuntime() ? 2 : 1;
checkRawStackTraceFrameCount(stackTrace, frames, "Expected everything to be inlined");
- checkCurrentlyIncorrectStackTrace(stackTrace);
+ checkExpectedStackTrace(stackTrace);
});
}
@@ -173,29 +173,6 @@
.build()));
}
- private void checkCurrentlyIncorrectStackTrace(StackTrace stackTrace) {
- assertThat(
- stackTrace,
- isSameExceptForFileNameAndLineNumber(
- StackTrace.builder()
- .addWithoutFileNameAndLineNumber(Main.class, RetraceLambdaTest.JAVAC_LAMBDA_METHOD)
- .applyIf(
- parameters.isDexRuntime(),
- b ->
- b
- // TODO(b/172014416): Lambda bridges should be marked synthetic
- // and removed.
- .addWithoutFileNameAndLineNumber(Main.class, LAMBDA_BRIDGE_METHOD)
- // TODO(b/172014416): The frame mapping should have removed this
- // entry.
- // TODO(b/172014416): Synthetics should not map back to internal
- // names.
- .addWithoutFileNameAndLineNumber(INTERNAL_LAMBDA_CLASS, "run"))
- .addWithoutFileNameAndLineNumber(Main.class, "runIt")
- .addWithoutFileNameAndLineNumber(Main.class, "main")
- .build()));
- }
-
public interface MyRunner {
void run();
}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index a1707c5..d7a13c8 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -258,12 +258,12 @@
@Test
public void testRetraceSynthesizedLambda() throws Exception {
- runRetraceTest(new SyntheticLambdaMethodStackTrace());
+ runExperimentalRetraceTest(new SyntheticLambdaMethodStackTrace());
}
@Test
public void testRetraceSynthesizedLambdaWithInlining() throws Exception {
- runRetraceTest(new SyntheticLambdaMethodWithInliningStackTrace());
+ runExperimentalRetraceTest(new SyntheticLambdaMethodWithInliningStackTrace());
}
private void inspectRetraceTest(
@@ -276,6 +276,16 @@
private TestDiagnosticMessagesImpl runRetraceTest(StackTraceForTest stackTraceForTest)
throws Exception {
+ return runRetraceTest(stackTraceForTest, false);
+ }
+
+ private TestDiagnosticMessagesImpl runExperimentalRetraceTest(StackTraceForTest stackTraceForTest)
+ throws Exception {
+ return runRetraceTest(stackTraceForTest, true);
+ }
+
+ private TestDiagnosticMessagesImpl runRetraceTest(
+ StackTraceForTest stackTraceForTest, boolean allowExperimentalMapping) throws Exception {
if (external) {
assumeTrue(useRegExpParsing);
assumeTrue(testParameters.isCfRuntime());
@@ -296,6 +306,9 @@
command.add("-ea");
command.add("-cp");
command.add(ToolHelper.R8_RETRACE_JAR.toString());
+ if (allowExperimentalMapping) {
+ command.add("-Dcom.android.tools.r8.experimentalmapping");
+ }
command.add("com.android.tools.r8.retrace.Retrace");
command.add(mappingFile.toString());
command.add(stackTraceFile.toString());
@@ -321,7 +334,7 @@
StringUtils.joinLines(stackTraceForTest.retracedStackTrace()),
StringUtils.joinLines(retraced)))
.build();
- Retrace.run(retraceCommand);
+ Retrace.runForTesting(retraceCommand, allowExperimentalMapping);
return diagnosticsHandler;
}
}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/SyntheticLambdaMethodStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/SyntheticLambdaMethodStackTrace.java
index efe3f0f..793fc0c 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/SyntheticLambdaMethodStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/SyntheticLambdaMethodStackTrace.java
@@ -34,7 +34,7 @@
@Override
public String mapping() {
return StringUtils.lines(
- "# {'id':'com.android.tools.r8.metainf','map-version':'experimental'}",
+ "# {'id':'com.android.tools.r8.mapping','version':'experimental'}",
"example.Main -> example.Main:",
" 1:1:void main(java.lang.String[]):123 -> main",
"example.Foo -> a.a:",
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/SyntheticLambdaMethodWithInliningStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/SyntheticLambdaMethodWithInliningStackTrace.java
index 5fda85f..c132733 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/SyntheticLambdaMethodWithInliningStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/SyntheticLambdaMethodWithInliningStackTrace.java
@@ -33,7 +33,7 @@
@Override
public String mapping() {
return StringUtils.lines(
- "# {'id':'com.android.tools.r8.metainf','map-version':'experimental'}",
+ "# {'id':'com.android.tools.r8.mapping','version':'experimental'}",
"example.Main -> example.Main:",
" 1:1:void main(java.lang.String[]):123 -> main",
"example.Foo -> a.a:",
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedParameterTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedParameterTypeTest.java
index 3961905..32d30d9 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedParameterTypeTest.java
@@ -10,6 +10,8 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -52,6 +54,7 @@
public static class MergedParameterTypeWithCollisionTest extends MergedTypeBaseTest {
+ @NoHorizontalClassMerging
static class SuperTestClass {
public static void method(A obj) {
@@ -80,6 +83,12 @@
}
@Override
+ public void configure(R8FullTestBuilder builder) {
+ super.configure(builder);
+ builder.enableNoHorizontalClassMergingAnnotations();
+ }
+
+ @Override
public Class<?> getTestClass() {
return TestClass.class;
}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedReturnTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedReturnTypeTest.java
index 9173e66..5a03792 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedReturnTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedReturnTypeTest.java
@@ -11,6 +11,7 @@
import static org.junit.Assert.assertNotEquals;
import com.android.tools.r8.AssumeMayHaveSideEffects;
+import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -61,6 +62,7 @@
public static class MergedReturnTypeWithCollisionTest extends MergedTypeBaseTest {
+ @NoHorizontalClassMerging
static class SuperTestClass {
@AssumeMayHaveSideEffects
@@ -92,7 +94,7 @@
@Override
public void configure(R8FullTestBuilder builder) {
super.configure(builder);
- builder.enableSideEffectAnnotations();
+ builder.enableNoHorizontalClassMergingAnnotations().enableSideEffectAnnotations();
}
@Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 077af2f..0533c6b 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -139,6 +139,8 @@
// The inspector allows building IR for a method. An output type must be defined for that.
internalOptions.programConsumer = DexIndexedConsumer.emptyConsumer();
}
+ // Always allow use of experimental map-file reading in the inspector.
+ internalOptions.testing.enableExperimentalMapFileVersion = true;
return internalOptions;
}
diff --git a/tools/compiledump.py b/tools/compiledump.py
index 1e336c1..15b4f85 100755
--- a/tools/compiledump.py
+++ b/tools/compiledump.py
@@ -204,7 +204,7 @@
return args.version
def determine_compiler(args, dump):
- compilers = ['d8', 'r8', 'r8full']
+ compilers = ['d8', 'r8', 'r8full', 'l8']
if args.compiler not in compilers:
error("Unable to determine a compiler to use. Specified %s,"
" Valid options: %s" % (args.compiler, ', '.join(compilers)))
@@ -301,6 +301,8 @@
cmd.extend(['-cp', '%s:%s' % (wrapper_dir, jar)])
if compiler == 'd8':
cmd.append('com.android.tools.r8.D8')
+ if compiler == 'l8':
+ cmd.append('com.android.tools.r8.L8')
if compiler.startswith('r8'):
cmd.append('com.android.tools.r8.utils.CompileDumpCompatR8')
if compiler == 'r8':
@@ -313,7 +315,7 @@
determine_feature_output(feature_jar, temp)])
if dump.library_jar():
cmd.extend(['--lib', dump.library_jar()])
- if dump.classpath_jar():
+ if dump.classpath_jar() and compiler != 'l8':
cmd.extend(['--classpath', dump.classpath_jar()])
if dump.desugared_library_json() and not args.disable_desugared_lib:
cmd.extend(['--desugared-lib', dump.desugared_library_json()])
@@ -323,7 +325,10 @@
cmd.extend(['--pg-conf', dump.config_file()])
if dump.main_dex_rules_resource():
cmd.extend(['--main-dex-rules', dump.main_dex_rules_resource()])
- if compiler != 'd8':
+ if compiler == 'l8':
+ if dump.config_file():
+ cmd.extend(['--pg-map-output', '%s.map' % out])
+ elif compiler != 'd8':
cmd.extend(['--pg-map-output', '%s.map' % out])
if min_api:
cmd.extend(['--min-api', min_api])
diff --git a/tools/d8.py b/tools/d8.py
index 18a4a67..833f7f5 100755
--- a/tools/d8.py
+++ b/tools/d8.py
@@ -3,8 +3,38 @@
# 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.
+import utils
+
+import optparse
import sys
import toolhelper
+def ParseOptions(argv):
+ parser = optparse.OptionParser(usage='%prog [options] -- [D8 options]')
+ parser.add_option(
+ '-c',
+ '--commit-hash',
+ '--commit_hash',
+ help='Commit hash of D8 to use.',
+ default=None)
+ parser.add_option(
+ '--version',
+ help='Version of D8 to use.',
+ default=None)
+ parser.add_option(
+ '--tag',
+ help='Tag of D8 to use.',
+ default=None)
+ return parser.parse_args(argv)
+
+def main(argv):
+ (options, args) = ParseOptions(sys.argv)
+ d8_args = args[1:]
+ return toolhelper.run(
+ 'd8',
+ d8_args,
+ jar=utils.find_r8_jar_from_options(options),
+ main='com.android.tools.r8.D8')
+
if __name__ == '__main__':
- sys.exit(toolhelper.run('d8', sys.argv[1:]))
+ sys.exit(main(sys.argv[1:]))
diff --git a/tools/retrace.py b/tools/retrace.py
index b0a1ec8..ea06566 100755
--- a/tools/retrace.py
+++ b/tools/retrace.py
@@ -53,52 +53,24 @@
return parser.parse_args()
-def find_version_or_hash_from_tag(tag_or_hash):
- info = subprocess.check_output([
- 'git',
- 'show',
- tag_or_hash,
- '-s',
- '--format=oneline']).splitlines()[-1].split()
- # The info should be on the following form [hash,"Version",version]
- if len(info) == 3 and len(info[0]) == 40 and info[1] == "Version":
- return info[2]
- return None
-
-
def main():
args = parse_arguments()
- if args.tag:
- hash_or_version = find_version_or_hash_from_tag(args.tag)
- else:
- hash_or_version = args.commit_hash or args.version
+ map_path = utils.find_cloud_storage_file_from_options(
+ 'r8lib.jar.map', args, orElse=args.map)
return run(
- args.map,
- hash_or_version,
+ map_path,
args.stacktrace,
args.commit_hash is not None,
args.no_r8lib,
quiet=args.quiet,
debug=args.debug_agent)
-def run(map_path, hash_or_version, stacktrace, is_hash, no_r8lib, quiet=False,
- debug=False):
- if hash_or_version:
- download_path = archive.GetUploadDestination(
- hash_or_version,
- 'r8lib.jar.map',
- is_hash)
- if utils.file_exists_on_cloud_storage(download_path):
- map_path = tempfile.NamedTemporaryFile().name
- utils.download_file_from_cloud_storage(download_path, map_path)
- else:
- print('Could not find map file from argument: %s.' % hash_or_version)
- return 1
-
+def run(map_path, stacktrace, is_hash, no_r8lib, quiet=False, debug=False):
retrace_args = [jdk.GetJavaExecutable()]
if debug:
- retrace_args.append('-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005')
+ retrace_args.append(
+ '-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005')
retrace_args += [
'-cp',
diff --git a/tools/test.py b/tools/test.py
index ea6a880..6734106 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -7,6 +7,8 @@
# if an argument is given, run only tests with that pattern. This script will
# force the tests to run, even if no input changed.
+import archive_desugar_jdk_libs
+import notify
import optparse
import os
import shutil
@@ -16,9 +18,7 @@
import time
import uuid
-import archive_desugar_jdk_libs
import gradle
-import notify
import utils
ALL_ART_VMS = [
@@ -386,14 +386,9 @@
for art_vm in vms_to_test:
vm_suffix = "_" + options.dex_vm_kind if art_vm != "default" else ""
runtimes = ['dex-' + art_vm]
- # Only append the "none" runtime and JVMs if running on the "default" DEX VM.
+ # Append the "none" runtime and default JVM if running the "default" DEX VM.
if art_vm == "default":
- # TODO(b/170454076): Remove special casing for bot when rex-script has
- # been migrated to account for runtimes.
- if utils.is_bot():
runtimes.extend(['jdk11', 'none'])
- else:
- runtimes.extend(['jdk8', 'jdk9', 'jdk11', 'none'])
return_code = gradle.RunGradle(
gradle_args + [
'-Pdex_vm=%s' % art_vm + vm_suffix,
diff --git a/tools/utils.py b/tools/utils.py
index 5c1b32b..bd28ef3 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -103,6 +103,46 @@
f.write(str(value))
archive_file(name, gs_dir, tempfile)
+def find_cloud_storage_file_from_options(name, options, orElse=None):
+ # Import archive on-demand since archive depends on utils.
+ from archive import GetUploadDestination
+ hash_or_version = find_hash_or_version_from_options(options)
+ if not hash_or_version:
+ return orElse
+ is_hash = options.commit_hash is not None
+ download_path = GetUploadDestination(hash_or_version, name, is_hash)
+ if file_exists_on_cloud_storage(download_path):
+ out = tempfile.NamedTemporaryFile().name
+ download_file_from_cloud_storage(download_path, out)
+ return out
+ else:
+ raise Exception('Could not find file {} from hash/version: {}.'
+ .format(name, hash_or_version))
+
+def find_r8_jar_from_options(options):
+ return find_cloud_storage_file_from_options('r8.jar', options)
+
+def find_r8_lib_jar_from_options(options):
+ return find_cloud_storage_file_from_options('r8lib.jar', options)
+
+def find_hash_or_version_from_options(options):
+ if options.tag:
+ return find_hash_or_version_from_tag(options.tag)
+ else:
+ return options.commit_hash or options.version
+
+def find_hash_or_version_from_tag(tag_or_hash):
+ info = subprocess.check_output([
+ 'git',
+ 'show',
+ tag_or_hash,
+ '-s',
+ '--format=oneline']).splitlines()[-1].split()
+ # The info should be on the following form [hash,"Version",version]
+ if len(info) == 3 and len(info[0]) == 40 and info[1] == "Version":
+ return info[2]
+ return None
+
def getAndroidHome():
return os.environ.get(
ANDROID_HOME_ENVIROMENT_NAME, os.path.join(USER_HOME, 'Android', 'Sdk'))