Merge commit '39aeec9f246b0c59b7819e75989be2d8a243105a' into dev-release
diff --git a/.gitignore b/.gitignore
index b382c4f..7ba42cd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -79,8 +79,10 @@
third_party/internal/*
third_party/iosched_2019
third_party/iosched_2019.tar.gz
-third_party/jacoco.tar.gz
-third_party/jacoco/*
+third_party/jacoco/0.8.2/*
+third_party/jacoco/0.8.2.tar.gz
+third_party/jacoco/0.8.6/*
+third_party/jacoco/0.8.6.tar.gz
third_party/jasmin
third_party/jasmin.tar.gz
third_party/jctf
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index a872ae8..faa8808 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -37,10 +37,14 @@
git diff -U0 $(git cl upstream) | %s -p1 -i
+or fix formatting, commit and upload:
+
+ git diff -U0 $(git cl upstream) | %s -p1 -i && git commit -a --amend --no-edit && git cl upload
+
or bypass the checks with:
- cl upload --bypass-hooks
- """ % FMT_CMD))
+ git cl upload --bypass-hooks
+ """ % (FMT_CMD, FMT_CMD)))
return results
def CheckDeterministicDebuggingChanged(input_api, output_api, branch):
diff --git a/build.gradle b/build.gradle
index 54f0898..5153337 100644
--- a/build.gradle
+++ b/build.gradle
@@ -318,7 +318,8 @@
"gradle/gradle",
"google-java-format",
"iosched_2019",
- "jacoco",
+ "jacoco/0.8.2",
+ "jacoco/0.8.6",
"jasmin",
"jctf",
"junit",
diff --git a/src/library_desugar/desugar_jdk_libs.json b/src/library_desugar/desugar_jdk_libs.json
index d46b12d..945ad60 100644
--- a/src/library_desugar/desugar_jdk_libs.json
+++ b/src/library_desugar/desugar_jdk_libs.json
@@ -2,7 +2,7 @@
"configuration_format_version": 3,
"group_id" : "com.tools.android",
"artifact_id" : "desugar_jdk_libs",
- "version": "1.0.11",
+ "version": "1.0.12",
"required_compilation_api_level": 26,
"synthesized_library_classes_package_prefix": "j$.",
"common_flags": [
@@ -246,6 +246,9 @@
"-keeppackagenames j$",
"-keepclassmembers class j$.util.IntSummaryStatistics { long count; long sum; int min; int max; }",
"-keepclassmembers class j$.util.LongSummaryStatistics { long count; long sum; long min; long max; }",
- "-keepclassmembers class j$.util.DoubleSummaryStatistics { long count; double sum; double min; double max; }"
+ "-keepclassmembers class j$.util.DoubleSummaryStatistics { long count; double sum; double min; double max; }",
+ "-keepattributes Signature",
+ "-keepattributes EnclosingMethod",
+ "-keepattributes InnerClasses"
]
}
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
index c295175..6a2a8c5 100644
--- a/src/main/java/com/android/tools/r8/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
@@ -25,6 +25,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.LazyLoadedDexApplication;
import com.android.tools.r8.graph.ParameterAnnotationsList;
@@ -142,6 +143,7 @@
new DexEncodedMethod(
method.method,
method.accessFlags,
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
code,
@@ -174,7 +176,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
- ClassSignature.NO_CLASS_SIGNATURE,
+ ClassSignature.noSignature(),
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index e767011..ab78b10 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.dex.ApplicationWriter;
import com.android.tools.r8.dex.Marker;
import com.android.tools.r8.dex.Marker.Tool;
+import com.android.tools.r8.errors.CheckDiscardDiagnostic;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.experimental.graphinfo.GraphConsumer;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -559,7 +560,7 @@
timing.end();
}
}
- if (options.enableHorizontalClassMerging) {
+ if (options.enableHorizontalClassMerging && options.enableInlining) {
timing.begin("HorizontalClassMerger");
HorizontalClassMerger merger =
new HorizontalClassMerger(
@@ -1082,16 +1083,11 @@
timing);
}
}
- for (DexDefinition definition : failed) {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- whyAreYouKeepingConsumer.printWhyAreYouKeeping(
- enqueuer.getGraphReporter().getGraphNode(definition.toReference()),
- new PrintStream(baos));
- options.reporter.info(
- new StringDiagnostic(
- "Item " + definition.toSourceString() + " was not discarded.\n" + baos.toString()));
- }
- throw new CompilationError("Discard checks failed.");
+ options.reporter.error(
+ new CheckDiscardDiagnostic.Builder()
+ .addFailedItems(failed, enqueuer.getGraphReporter(), whyAreYouKeepingConsumer)
+ .build());
+ options.reporter.failIfPendingErrors();
}
private static boolean verifyNoJarApplicationReaders(Collection<DexProgramClass> classes) {
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 0375b6b..f9c2050 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -868,6 +868,12 @@
internal.enableEnumUnboxing = false;
}
+ if (!internal.enableInlining) {
+ // If R8 cannot perform inlining, then the synthetic constructors would not inline the called
+ // constructors, producing invalid code.
+ internal.enableHorizontalClassMerging = false;
+ }
+
// Amend the proguard-map consumer with options from the proguard configuration.
internal.proguardMapConsumer =
wrapStringConsumer(
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
index 26e029a..3ad8ed2 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfInvoke.java
@@ -351,7 +351,16 @@
private Type invokeTypeForInvokeSpecialToNonInitMethodOnHolder(
AppView<?> appView, CfSourceCode code) {
boolean desugaringEnabled = appView.options().isInterfaceMethodDesugaringEnabled();
- DexEncodedMethod encodedMethod = lookupMethodOnHolder(appView, method);
+ MethodLookupResult lookupResult = appView.graphLens().lookupMethod(method, method, Type.DIRECT);
+ if (lookupResult.getType() == Type.VIRTUAL) {
+ // The method has been publicized. We can't always expect private methods that have been
+ // publicized to be final. For example, if a private method A.m() is publicized, and A is
+ // subsequently merged with a class B, with declares a public non-final method B.m(), then the
+ // horizontal class merger will merge A.m() and B.m() into a new non-final public method.
+ return Type.VIRTUAL;
+ }
+ DexMethod rewrittenMethod = lookupResult.getReference();
+ DexEncodedMethod encodedMethod = lookupMethodOnHolder(appView, rewrittenMethod);
if (encodedMethod == null) {
// The method is not defined on the class, we can use super to target. When desugaring
// default interface methods, it is expected they are targeted with invoke-direct.
@@ -375,12 +384,10 @@
}
private DexEncodedMethod lookupMethodOnHolder(AppView<?> appView, DexMethod method) {
- MethodLookupResult lookupResult = appView.graphLens().lookupMethod(method, method, Type.DIRECT);
- DexMethod rewrittenMethod = lookupResult.getReference();
// Directly lookup the program type for holder. This bypasses lookup order as well as looks
// directly on the application data, which bypasses and indirection or validation.
- DexProgramClass clazz = appView.appInfo().unsafeDirectProgramTypeLookup(rewrittenMethod.holder);
+ DexProgramClass clazz = appView.appInfo().unsafeDirectProgramTypeLookup(method.getHolderType());
assert clazz != null;
- return clazz.lookupMethod(rewrittenMethod);
+ return clazz.lookupMethod(method);
}
}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java b/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java
index 8e9752b..01ff28f 100644
--- a/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java
+++ b/src/main/java/com/android/tools/r8/cf/code/CfTryCatch.java
@@ -4,7 +4,9 @@
package com.android.tools.r8.cf.code;
import com.android.tools.r8.graph.CfCompareHelper;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.CatchHandlers;
import com.android.tools.r8.ir.conversion.CfBuilder;
@@ -54,4 +56,8 @@
.thenComparing(c -> c.targets, ComparatorUtils.listComparator(helper::compareLabels))
.compare(this, other);
}
+
+ public void internalRegisterUse(UseRegistry registry, DexClassAndMethod context) {
+ guards.forEach(registry::registerExceptionGuard);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index f39795b..77a7d70 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -461,6 +461,7 @@
for (DexProgramClass clazz : appView.appInfo().classes()) {
insertAttributeAnnotationsForClass(clazz);
clazz.fields().forEach(this::insertAttributeAnnotationsForField);
+ clazz.methods().forEach(this::insertAttributeAnnotationsForMethod);
}
}
@@ -546,18 +547,33 @@
}
private void insertAttributeAnnotationsForField(DexEncodedField field) {
- if (field.getFieldSignature().hasNoSignature()) {
+ if (field.getGenericSignature().hasNoSignature()) {
return;
}
- // Append the annotations to annotations array of the class.
+ // Append the annotations to annotations array of the field.
field.setAnnotations(
new DexAnnotationSet(
ArrayUtils.appendSingleElement(
field.annotations().annotations,
DexAnnotation.createSignatureAnnotation(
- field.getFieldSignature().toRenamedString(namingLens, isTypeMissing),
+ field.getGenericSignature().toRenamedString(namingLens, isTypeMissing),
options.itemFactory))));
- field.clearFieldSignature();
+ field.clearGenericSignature();
+ }
+
+ private void insertAttributeAnnotationsForMethod(DexEncodedMethod method) {
+ if (method.getGenericSignature().hasNoSignature()) {
+ return;
+ }
+ // Append the annotations to annotations array of the method.
+ method.setAnnotations(
+ new DexAnnotationSet(
+ ArrayUtils.appendSingleElement(
+ method.annotations().annotations,
+ DexAnnotation.createSignatureAnnotation(
+ method.getGenericSignature().toRenamedString(namingLens, isTypeMissing),
+ options.itemFactory))));
+ method.clearGenericSignature();
}
private void setCallSiteContexts(ExecutorService executorService) throws ExecutionException {
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index 6f00265..4941d0b 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -674,11 +674,22 @@
code = codes.get(codeOff);
}
DexMethod method = indexedItems.getMethod(methodIndex);
+ DexAnnotationSet methodAnnotations = annotationIterator.getNextFor(method);
+ String methodSignature = DexAnnotation.getSignature(methodAnnotations, dexItemFactory);
+ if (methodSignature != null) {
+ methodAnnotations = methodAnnotations.getWithout(dexItemFactory.annotationSignature);
+ }
methods[i] =
new DexEncodedMethod(
method,
accessFlags,
- annotationIterator.getNextFor(method),
+ GenericSignature.parseMethodSignature(
+ method.name.toString(),
+ methodSignature,
+ origin,
+ dexItemFactory,
+ options.reporter),
+ methodAnnotations,
parameterAnnotationsIterator.getNextFor(method),
code);
}
@@ -1343,7 +1354,7 @@
private EnclosingMethodAttribute enclosingMethodAttribute = null;
private List<InnerClassAttribute> innerClasses = null;
private List<DexAnnotation> lazyAnnotations = null;
- private ClassSignature classSignature = ClassSignature.NO_CLASS_SIGNATURE;
+ private ClassSignature classSignature = ClassSignature.noSignature();
public DexAnnotationSet getAnnotations() {
if (lazyAnnotations != null) {
diff --git a/src/main/java/com/android/tools/r8/errors/CheckDiscardDiagnostic.java b/src/main/java/com/android/tools/r8/errors/CheckDiscardDiagnostic.java
new file mode 100644
index 0000000..8073696
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/errors/CheckDiscardDiagnostic.java
@@ -0,0 +1,75 @@
+// 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.errors;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.Keep;
+import com.android.tools.r8.graph.DexDefinition;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import com.android.tools.r8.shaking.GraphReporter;
+import com.android.tools.r8.shaking.WhyAreYouKeepingConsumer;
+import com.google.common.collect.ImmutableList;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.List;
+
+@Keep
+public class CheckDiscardDiagnostic implements Diagnostic {
+
+ private final List<String> messages;
+
+ public static class Builder {
+ private ImmutableList.Builder<String> messagesBuilder = ImmutableList.builder();
+
+ public Builder addFailedItems(
+ List<DexDefinition> failed,
+ GraphReporter graphReporter,
+ WhyAreYouKeepingConsumer whyAreYouKeepingConsumer) {
+ for (DexDefinition definition : failed) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ whyAreYouKeepingConsumer.printWhyAreYouKeeping(
+ graphReporter.getGraphNode(definition.toReference()), new PrintStream(baos));
+ messagesBuilder.add(
+ "Item " + definition.toSourceString() + " was not discarded.\n" + baos.toString());
+ }
+ return this;
+ }
+
+ public CheckDiscardDiagnostic build() {
+ return new CheckDiscardDiagnostic(messagesBuilder.build());
+ }
+ }
+
+ private CheckDiscardDiagnostic(List<String> messages) {
+ this.messages = messages;
+ }
+
+ /** The origin of a -checkdiscarded failure is not unique. (The whole app is to blame.) */
+ @Override
+ public Origin getOrigin() {
+ return Origin.unknown();
+ }
+
+ /** The position of a -checkdiscarded failure is always unknown. */
+ @Override
+ public Position getPosition() {
+ return Position.UNKNOWN;
+ }
+
+ @Override
+ public String getDiagnosticMessage() {
+ StringBuilder builder = new StringBuilder("Discard checks failed.");
+ if (messages.size() > 0) {
+ builder.append(System.lineSeparator());
+ builder.append("The following items where not discarded");
+ messages.forEach(
+ message -> {
+ builder.append(System.lineSeparator());
+ builder.append(message);
+ });
+ }
+ return builder.toString();
+ }
+}
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 6bf8757..a7bd6c1 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -30,6 +30,7 @@
import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.InternalOptions.TestingOptions;
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.google.common.base.Predicates;
@@ -398,6 +399,10 @@
return appInfo.options();
}
+ public TestingOptions testing() {
+ return options().testing;
+ }
+
public RootSet rootSet() {
return rootSet;
}
@@ -440,6 +445,9 @@
HorizontallyMergedLambdaClasses horizontallyMergedLambdaClasses) {
assert this.horizontallyMergedLambdaClasses == null;
this.horizontallyMergedLambdaClasses = horizontallyMergedLambdaClasses;
+ testing()
+ .horizontallyMergedLambdaClassesConsumer
+ .accept(dexItemFactory(), horizontallyMergedLambdaClasses);
}
/**
@@ -453,6 +461,7 @@
public void setHorizontallyMergedClasses(HorizontallyMergedClasses horizontallyMergedClasses) {
assert this.horizontallyMergedClasses == null;
this.horizontallyMergedClasses = horizontallyMergedClasses;
+ testing().horizontallyMergedClassesConsumer.accept(dexItemFactory(), horizontallyMergedClasses);
}
/**
@@ -466,6 +475,7 @@
public void setVerticallyMergedClasses(VerticallyMergedClasses verticallyMergedClasses) {
assert this.verticallyMergedClasses == null;
this.verticallyMergedClasses = verticallyMergedClasses;
+ testing().verticallyMergedClassesConsumer.accept(dexItemFactory(), verticallyMergedClasses);
}
public EnumValueInfoMapCollection unboxedEnums() {
@@ -473,7 +483,9 @@
}
public void setUnboxedEnums(EnumValueInfoMapCollection unboxedEnums) {
+ assert this.unboxedEnums.isEmpty();
this.unboxedEnums = unboxedEnums;
+ testing().unboxedEnumsConsumer.accept(dexItemFactory(), unboxedEnums);
}
public boolean validateUnboxedEnumsHaveBeenPruned() {
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 141484c..a08d65e 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -508,7 +508,7 @@
for (CfInstruction instruction : instructions) {
instruction.registerUse(registry, method);
}
- tryCatchRanges.forEach(tryCatch -> tryCatch.guards.forEach(registry::registerTypeReference));
+ tryCatchRanges.forEach(tryCatch -> tryCatch.internalRegisterUse(registry, method));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index d883e3e..f23ffde 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -769,7 +769,7 @@
}
public void clearClassSignature() {
- classSignature = ClassSignature.NO_CLASS_SIGNATURE;
+ classSignature = ClassSignature.noSignature();
}
public void removeInnerClasses(Predicate<InnerClassAttribute> predicate) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index 8e51e49..b484342 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.graph;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
-import static com.android.tools.r8.graph.GenericSignature.NO_FIELD_TYPE_SIGNATURE;
import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
@@ -31,7 +30,7 @@
private DexValue staticValue;
private final boolean deprecated;
/** Generic signature information if the attribute is present in the input */
- private FieldTypeSignature fieldSignature;
+ private FieldTypeSignature genericSignature;
private FieldOptimizationInfo optimizationInfo = DefaultFieldOptimizationInfo.getInstance();
private KotlinFieldLevelInfo kotlinMemberInfo = NO_KOTLIN_INFO;
@@ -39,7 +38,7 @@
public DexEncodedField(
DexField field,
FieldAccessFlags accessFlags,
- FieldTypeSignature fieldSignature,
+ FieldTypeSignature genericSignature,
DexAnnotationSet annotations,
DexValue staticValue,
boolean deprecated) {
@@ -48,18 +47,18 @@
this.accessFlags = accessFlags;
this.staticValue = staticValue;
this.deprecated = deprecated;
- this.fieldSignature = fieldSignature;
- assert fieldSignature != null;
- assert GenericSignatureUtils.verifyNoDuplicateGenericDefinitions(fieldSignature, annotations);
+ this.genericSignature = genericSignature;
+ assert genericSignature != null;
+ assert GenericSignatureUtils.verifyNoDuplicateGenericDefinitions(genericSignature, annotations);
}
public DexEncodedField(
DexField field,
FieldAccessFlags accessFlags,
- FieldTypeSignature fieldSignature,
+ FieldTypeSignature genericSignature,
DexAnnotationSet annotations,
DexValue staticValue) {
- this(field, accessFlags, fieldSignature, annotations, staticValue, false);
+ this(field, accessFlags, genericSignature, annotations, staticValue, false);
}
public DexType type() {
@@ -299,7 +298,7 @@
}
// TODO(b/169923358): Consider removing the fieldSignature here.
DexEncodedField result =
- new DexEncodedField(field, accessFlags, fieldSignature, annotations(), staticValue);
+ new DexEncodedField(field, accessFlags, genericSignature, annotations(), staticValue);
result.optimizationInfo =
optimizationInfo.isMutableFieldOptimizationInfo()
? optimizationInfo.asMutableFieldOptimizationInfo().mutableCopy()
@@ -322,16 +321,16 @@
return true;
}
- public FieldTypeSignature getFieldSignature() {
- return fieldSignature;
+ public FieldTypeSignature getGenericSignature() {
+ return genericSignature;
}
- public void setFieldSignature(FieldTypeSignature fieldSignature) {
- assert fieldSignature != null;
- this.fieldSignature = fieldSignature;
+ public void setGenericSignature(FieldTypeSignature genericSignature) {
+ assert genericSignature != null;
+ this.genericSignature = genericSignature;
}
- public void clearFieldSignature() {
- this.fieldSignature = NO_FIELD_TYPE_SIGNATURE;
+ public void clearGenericSignature() {
+ this.genericSignature = FieldTypeSignature.noSignature();
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index c30f69c..a972aed 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -42,6 +42,7 @@
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.errors.InternalCompilerError;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.code.NumericType;
@@ -131,6 +132,7 @@
new DexEncodedMethod(
null,
MethodAccessFlags.fromDexAccessFlags(0),
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
null);
@@ -149,6 +151,8 @@
private CallSiteOptimizationInfo callSiteOptimizationInfo = CallSiteOptimizationInfo.bottom();
private int classFileVersion;
private KotlinMethodLevelInfo kotlinMemberInfo = NO_KOTLIN_INFO;
+ /** Generic signature information if the attribute is present in the input */
+ private MethodTypeSignature genericSignature;
private DexEncodedMethod defaultInterfaceMethodImplementation = null;
@@ -225,25 +229,44 @@
public DexEncodedMethod(
DexMethod method,
MethodAccessFlags accessFlags,
+ MethodTypeSignature genericSignature,
DexAnnotationSet annotations,
ParameterAnnotationsList parameterAnnotationsList,
Code code) {
- this(method, accessFlags, annotations, parameterAnnotationsList, code, false, -1);
+ this(
+ method,
+ accessFlags,
+ genericSignature,
+ annotations,
+ parameterAnnotationsList,
+ code,
+ false,
+ -1);
}
public DexEncodedMethod(
DexMethod method,
MethodAccessFlags accessFlags,
+ MethodTypeSignature genericSignature,
DexAnnotationSet annotations,
ParameterAnnotationsList parameterAnnotationsList,
Code code,
boolean d8R8Synthesized) {
- this(method, accessFlags, annotations, parameterAnnotationsList, code, d8R8Synthesized, -1);
+ this(
+ method,
+ accessFlags,
+ genericSignature,
+ annotations,
+ parameterAnnotationsList,
+ code,
+ d8R8Synthesized,
+ -1);
}
public DexEncodedMethod(
DexMethod method,
MethodAccessFlags accessFlags,
+ MethodTypeSignature genericSignature,
DexAnnotationSet annotations,
ParameterAnnotationsList parameterAnnotationsList,
Code code,
@@ -252,6 +275,7 @@
this(
method,
accessFlags,
+ genericSignature,
annotations,
parameterAnnotationsList,
code,
@@ -263,6 +287,7 @@
public DexEncodedMethod(
DexMethod method,
MethodAccessFlags accessFlags,
+ MethodTypeSignature genericSignature,
DexAnnotationSet annotations,
ParameterAnnotationsList parameterAnnotationsList,
Code code,
@@ -273,6 +298,7 @@
this.method = method;
this.accessFlags = accessFlags;
this.deprecated = deprecated;
+ this.genericSignature = genericSignature;
this.parameterAnnotationsList = parameterAnnotationsList;
this.code = code;
this.classFileVersion = classFileVersion;
@@ -1146,6 +1172,7 @@
new DexEncodedMethod(
newMethod,
accessFlags,
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
code,
@@ -1175,6 +1202,7 @@
return new DexEncodedMethod(
newMethod,
accessFlags,
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
code,
@@ -1278,6 +1306,7 @@
return new DexEncodedMethod(
newMethod,
newFlags,
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
new SynthesizedCode(forwardSourceCodeBuilder::build),
@@ -1399,6 +1428,19 @@
}
}
+ public MethodTypeSignature getGenericSignature() {
+ return genericSignature;
+ }
+
+ public void setGenericSignature(MethodTypeSignature genericSignature) {
+ assert genericSignature != null;
+ this.genericSignature = genericSignature;
+ }
+
+ public void clearGenericSignature() {
+ this.genericSignature = MethodTypeSignature.noSignature();
+ }
+
private static Builder syntheticBuilder(DexEncodedMethod from) {
return new Builder(from, true);
}
@@ -1411,6 +1453,7 @@
private DexMethod method;
private final MethodAccessFlags accessFlags;
+ private final MethodTypeSignature genericSignature;
private final DexAnnotationSet annotations;
private OptionalBool isLibraryMethodOverride = OptionalBool.UNKNOWN;
private ParameterAnnotationsList parameterAnnotations;
@@ -1429,6 +1472,7 @@
// Copy all the mutable state of a DexEncodedMethod here.
method = from.method;
accessFlags = from.accessFlags.copy();
+ genericSignature = from.getGenericSignature();
annotations = from.annotations();
code = from.code;
compilationState = CompilationState.NOT_PROCESSED;
@@ -1529,6 +1573,7 @@
new DexEncodedMethod(
method,
accessFlags,
+ genericSignature,
annotations,
parameterAnnotations,
code,
diff --git a/src/main/java/com/android/tools/r8/graph/DexField.java b/src/main/java/com/android/tools/r8/graph/DexField.java
index 012030b..87adbf7 100644
--- a/src/main/java/com/android/tools/r8/graph/DexField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexField.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.Reference;
+import java.util.Collections;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -116,6 +117,11 @@
}
@Override
+ public Iterable<DexType> getReferencedTypes() {
+ return Collections.singleton(type);
+ }
+
+ @Override
public int slowCompareTo(DexField other) {
int result = holder.slowCompareTo(other.holder);
if (result != 0) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexMember.java b/src/main/java/com/android/tools/r8/graph/DexMember.java
index a1767c3..5c34e83 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMember.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import com.google.common.collect.Iterables;
+
public abstract class DexMember<D extends DexEncodedMember<D, R>, R extends DexMember<D, R>>
extends DexReference implements PresortedComparable<R> {
@@ -33,4 +35,10 @@
public DexMember<D, R> asDexMember() {
return this;
}
+
+ public abstract Iterable<DexType> getReferencedTypes();
+
+ public Iterable<DexType> getReferencedBaseTypes(DexItemFactory dexItemFactory) {
+ return Iterables.transform(getReferencedTypes(), type -> type.toBaseType(dexItemFactory));
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index 212f5ca..9757af4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -142,6 +142,11 @@
}
@Override
+ public Iterable<DexType> getReferencedTypes() {
+ return proto.getTypes();
+ }
+
+ @Override
public int computeHashCode() {
return holder.hashCode()
+ proto.hashCode() * 7
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index e61ff61..1a8d506 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -3,7 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
-import static com.android.tools.r8.graph.GenericSignature.EMPTY_TYPE_ARGUMENTS;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.NO_KOTLIN_INFO;
import static com.google.common.base.Predicates.alwaysTrue;
@@ -480,7 +479,26 @@
methodCollection.addDirectMethod(directMethod);
}
- public void addExtraInterfaces(List<DexType> extraInterfaces, DexItemFactory factory) {
+ public void replaceInterfaces(List<ClassTypeSignature> newInterfaces) {
+ if (newInterfaces.isEmpty()) {
+ return;
+ }
+ clearInterfaces();
+ addExtraInterfaces(newInterfaces);
+ }
+
+ private void clearInterfaces() {
+ interfaces = DexTypeList.empty();
+ if (classSignature.hasSignature()) {
+ classSignature =
+ new ClassSignature(
+ classSignature.formalTypeParameters,
+ classSignature.superClassSignature,
+ ImmutableList.of());
+ }
+ }
+
+ public void addExtraInterfaces(List<ClassTypeSignature> extraInterfaces) {
if (extraInterfaces.isEmpty()) {
return;
}
@@ -488,25 +506,24 @@
addExtraInterfacesToSignatureIfPresent(extraInterfaces);
}
- private void addExtraInterfacesToInterfacesArray(List<DexType> extraInterfaces) {
+ private void addExtraInterfacesToInterfacesArray(List<ClassTypeSignature> extraInterfaces) {
DexType[] newInterfaces =
Arrays.copyOf(interfaces.values, interfaces.size() + extraInterfaces.size());
for (int i = interfaces.size(); i < newInterfaces.length; i++) {
- newInterfaces[i] = extraInterfaces.get(i - interfaces.size());
+ newInterfaces[i] = extraInterfaces.get(i - interfaces.size()).type();
}
interfaces = new DexTypeList(newInterfaces);
}
- private void addExtraInterfacesToSignatureIfPresent(List<DexType> extraInterfaces) {
- // We need to introduce the extra interfaces to the generic signature.
- // At this point we cheat and pretend the extraInterfaces simply don't use any generic types.
+ private void addExtraInterfacesToSignatureIfPresent(List<ClassTypeSignature> extraInterfaces) {
+ // We introduce the extra interfaces to the generic signature.
if (classSignature.hasNoSignature() || extraInterfaces.isEmpty()) {
return;
}
ImmutableList.Builder<ClassTypeSignature> interfacesBuilder =
ImmutableList.<ClassTypeSignature>builder().addAll(classSignature.superInterfaceSignatures);
- for (DexType extraInterface : extraInterfaces) {
- interfacesBuilder.add(new ClassTypeSignature(extraInterface, EMPTY_TYPE_ARGUMENTS));
+ for (ClassTypeSignature extraInterface : extraInterfaces) {
+ interfacesBuilder.add(extraInterface);
}
classSignature =
new ClassSignature(
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 bbff32c..a92dc75 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignature.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
@@ -128,6 +128,12 @@
default MethodTypeSignature asMethodTypeSignature() {
return null;
}
+
+ boolean hasSignature();
+
+ default boolean hasNoSignature() {
+ return !hasSignature();
+ }
}
public static class FormalTypeParameter {
@@ -168,7 +174,7 @@
public static class ClassSignature implements DexDefinitionSignature<DexClass> {
- public static final ClassSignature NO_CLASS_SIGNATURE =
+ private static final ClassSignature NO_CLASS_SIGNATURE =
new ClassSignature(EMPTY_TYPE_PARAMS, NO_FIELD_TYPE_SIGNATURE, EMPTY_SUPER_INTERFACES);
final List<FormalTypeParameter> formalTypeParameters;
@@ -195,14 +201,11 @@
return superInterfaceSignatures;
}
+ @Override
public boolean hasSignature() {
return this != NO_CLASS_SIGNATURE;
}
- public boolean hasNoSignature() {
- return !hasSignature();
- }
-
@Override
public boolean isClassSignature() {
return true;
@@ -235,6 +238,10 @@
public String toString() {
return toRenamedString(NamingLens.getIdentityLens(), alwaysTrue());
}
+
+ public static ClassSignature noSignature() {
+ return NO_CLASS_SIGNATURE;
+ }
}
public abstract static class TypeSignature {
@@ -318,14 +325,11 @@
return null;
}
+ @Override
public boolean hasSignature() {
return this != GenericSignature.NO_FIELD_TYPE_SIGNATURE;
}
- public boolean hasNoSignature() {
- return !hasSignature();
- }
-
public abstract FieldTypeSignature asArgument(WildcardIndicator indicator);
public boolean isStar() {
@@ -346,6 +350,10 @@
public String toString() {
return toRenamedString(NamingLens.getIdentityLens(), alwaysTrue());
}
+
+ public static FieldTypeSignature noSignature() {
+ return NO_FIELD_TYPE_SIGNATURE;
+ }
}
static final class StarFieldTypeSignature extends FieldTypeSignature {
@@ -367,7 +375,7 @@
}
}
- public static final ClassTypeSignature NO_FIELD_TYPE_SIGNATURE =
+ private static final ClassTypeSignature NO_FIELD_TYPE_SIGNATURE =
new ClassTypeSignature(DexItemFactory.nullValueType, EMPTY_TYPE_ARGUMENTS);
public static class ClassTypeSignature extends FieldTypeSignature {
@@ -383,7 +391,11 @@
ClassTypeSignature enclosingTypeSignature;
ClassTypeSignature innerTypeSignature;
- ClassTypeSignature(DexType type, List<FieldTypeSignature> typeArguments) {
+ public ClassTypeSignature(DexType type) {
+ this(type, EMPTY_TYPE_ARGUMENTS);
+ }
+
+ public ClassTypeSignature(DexType type, List<FieldTypeSignature> typeArguments) {
this(type, typeArguments, WildcardIndicator.NOT_AN_ARGUMENT);
}
@@ -579,7 +591,7 @@
public static class MethodTypeSignature implements DexDefinitionSignature<DexEncodedMethod> {
- public static final MethodTypeSignature NO_METHOD_TYPE_SIGNATURE =
+ private static final MethodTypeSignature NO_METHOD_TYPE_SIGNATURE =
new MethodTypeSignature(
EMPTY_TYPE_PARAMS, EMPTY_TYPE_SIGNATURES, ReturnType.VOID, EMPTY_TYPE_SIGNATURES);
@@ -588,6 +600,10 @@
final ReturnType returnType;
final List<TypeSignature> throwsSignatures;
+ public static MethodTypeSignature noSignature() {
+ return NO_METHOD_TYPE_SIGNATURE;
+ }
+
MethodTypeSignature(
final List<FormalTypeParameter> formalTypeParameters,
List<TypeSignature> typeSignatures,
@@ -623,6 +639,7 @@
return true;
}
+ @Override
public boolean hasSignature() {
return this != NO_METHOD_TYPE_SIGNATURE;
}
@@ -642,6 +659,21 @@
public List<FormalTypeParameter> getFormalTypeParameters() {
return formalTypeParameters;
}
+
+ public String toRenamedString(NamingLens namingLens, Predicate<DexType> isTypeMissing) {
+ if (hasNoSignature()) {
+ return null;
+ }
+ GenericSignaturePrinter genericSignaturePrinter =
+ new GenericSignaturePrinter(namingLens, isTypeMissing);
+ genericSignaturePrinter.visitMethodSignature(this);
+ return genericSignaturePrinter.toString();
+ }
+
+ @Override
+ public String toString() {
+ return toRenamedString(NamingLens.getIdentityLens(), alwaysTrue());
+ }
}
public static ClassSignature parseClassSignature(
@@ -1013,7 +1045,6 @@
if (symbol == '^') {
do {
scanSymbol();
-
// ThrowsSignature ::= ("^" ClassTypeSignature) | ("^" TypeVariableSignature).
if (symbol == 'T') {
throwsSignatureBuilder.add(updateTypeVariableSignature());
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java b/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java
index e75ee78..f68675f 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java
@@ -8,6 +8,8 @@
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.GenericSignature.WildcardIndicator;
import com.android.tools.r8.naming.NamingLens;
@@ -38,6 +40,35 @@
}
@Override
+ public void visitMethodSignature(MethodTypeSignature methodSignature) {
+ methodSignature.visit(this);
+ }
+
+ @Override
+ public void visitMethodTypeSignatures(List<TypeSignature> typeSignatures) {
+ sb.append("(");
+ typeSignatures.forEach(this::visitTypeSignature);
+ sb.append(")");
+ }
+
+ @Override
+ public void visitReturnType(ReturnType returnType) {
+ if (returnType.isVoidDescriptor()) {
+ sb.append("V");
+ } else {
+ visitTypeSignature(returnType.typeSignature);
+ }
+ }
+
+ @Override
+ public void visitThrowsSignatures(List<TypeSignature> typeSignatures) {
+ for (TypeSignature typeSignature : typeSignatures) {
+ sb.append("^");
+ visitTypeSignature(typeSignature);
+ }
+ }
+
+ @Override
public void visitFormalTypeParameters(List<FormalTypeParameter> formalTypeParameters) {
if (formalTypeParameters.isEmpty()) {
return;
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 4cce25e..7804c5f 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
@@ -7,7 +7,8 @@
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.NO_FIELD_TYPE_SIGNATURE;
+import static com.android.tools.r8.graph.GenericSignature.EMPTY_TYPE_SIGNATURES;
+import static com.android.tools.r8.graph.GenericSignature.FieldTypeSignature.noSignature;
import static com.android.tools.r8.graph.GenericSignature.StarFieldTypeSignature.STAR_FIELD_TYPE_SIGNATURE;
import com.android.tools.r8.graph.GenericSignature.ArrayTypeSignature;
@@ -15,6 +16,8 @@
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.shaking.AppInfoWithLiveness;
import java.util.ArrayList;
@@ -48,6 +51,13 @@
return new TypeSignatureRewriter().run(fieldTypeSignature);
}
+ public MethodTypeSignature rewrite(MethodTypeSignature methodTypeSignature) {
+ if (methodTypeSignature.hasNoSignature() || appView.graphLens().isIdentityLens()) {
+ return methodTypeSignature;
+ }
+ return new MethodTypeSignatureRewriter().run(methodTypeSignature);
+ }
+
private class ClassSignatureRewriter implements GenericSignatureVisitor {
private final List<FormalTypeParameter> rewrittenTypeParameters = new ArrayList<>();
@@ -90,7 +100,7 @@
&& rewrittenSuperInterfaces.isEmpty()
&& rewrittenSuperClass.isNoSignature()
&& rewrittenSuperClass.type == appView.dexItemFactory().objectType) {
- return ClassSignature.NO_CLASS_SIGNATURE;
+ return ClassSignature.noSignature();
}
return new ClassSignature(
rewrittenTypeParameters.isEmpty() ? EMPTY_TYPE_PARAMS : rewrittenTypeParameters,
@@ -99,9 +109,77 @@
}
}
+ private class MethodTypeSignatureRewriter implements GenericSignatureVisitor {
+
+ private final List<FormalTypeParameter> rewrittenTypeParameters = new ArrayList<>();
+ private final List<TypeSignature> rewrittenTypeSignatures = new ArrayList<>();
+
+ ReturnType rewrittenReturnType = null;
+ private final List<TypeSignature> rewrittenThrowsSignatures = new ArrayList<>();
+
+ @Override
+ public void visitFormalTypeParameters(List<FormalTypeParameter> formalTypeParameters) {
+ for (FormalTypeParameter formalTypeParameter : formalTypeParameters) {
+ rewrittenTypeParameters.add(new FormalTypeParameterRewriter().run(formalTypeParameter));
+ }
+ }
+
+ @Override
+ public void visitMethodTypeSignatures(List<TypeSignature> typeSignatures) {
+ for (TypeSignature typeSignature : typeSignatures) {
+ TypeSignature rewrittenType = new TypeSignatureRewriter().run(typeSignature);
+ rewrittenTypeSignatures.add(rewrittenType == null ? objectTypeSignature : rewrittenType);
+ }
+ }
+
+ @Override
+ public void visitReturnType(ReturnType returnType) {
+ if (returnType.isVoidDescriptor()) {
+ rewrittenReturnType = ReturnType.VOID;
+ } else {
+ TypeSignature originalType = returnType.typeSignature();
+ TypeSignature rewrittenType = new TypeSignatureRewriter().run(originalType);
+ if (rewrittenType == null) {
+ rewrittenReturnType = ReturnType.VOID;
+ } else if (rewrittenType == originalType) {
+ rewrittenReturnType = returnType;
+ } else {
+ rewrittenReturnType = new ReturnType(rewrittenType);
+ }
+ }
+ }
+
+ @Override
+ public void visitThrowsSignatures(List<TypeSignature> typeSignatures) {
+ for (TypeSignature typeSignature : typeSignatures) {
+ TypeSignature rewrittenType = new TypeSignatureRewriter().run(typeSignature);
+ // If a throwing type is no longer found we remove it from the signature.
+ if (rewrittenType != null) {
+ rewrittenThrowsSignatures.add(rewrittenType);
+ }
+ }
+ }
+
+ private MethodTypeSignature run(MethodTypeSignature methodTypeSignature) {
+ methodTypeSignature.visit(this);
+ assert rewrittenReturnType != null;
+ if (rewrittenTypeParameters.isEmpty()
+ && rewrittenTypeSignatures.isEmpty()
+ && rewrittenReturnType.isVoidDescriptor()
+ && rewrittenThrowsSignatures.isEmpty()) {
+ return MethodTypeSignature.noSignature();
+ }
+ return new MethodTypeSignature(
+ rewrittenTypeParameters.isEmpty() ? EMPTY_TYPE_PARAMS : rewrittenTypeParameters,
+ rewrittenTypeSignatures.isEmpty() ? EMPTY_TYPE_SIGNATURES : rewrittenTypeSignatures,
+ rewrittenReturnType,
+ rewrittenThrowsSignatures.isEmpty() ? EMPTY_TYPE_SIGNATURES : rewrittenThrowsSignatures);
+ }
+ }
+
private class FormalTypeParameterRewriter implements GenericSignatureVisitor {
- private FieldTypeSignature rewrittenClassBound = NO_FIELD_TYPE_SIGNATURE;
+ private FieldTypeSignature rewrittenClassBound = noSignature();
private final List<FieldTypeSignature> rewrittenInterfaceBounds = new ArrayList<>();
@Override
@@ -126,7 +204,7 @@
}
return new FormalTypeParameter(
formalTypeParameter.name,
- rewrittenClassBound == null ? NO_FIELD_TYPE_SIGNATURE : rewrittenClassBound,
+ rewrittenClassBound == null ? noSignature() : rewrittenClassBound,
rewrittenInterfaceBounds.isEmpty() ? EMPTY_TYPE_ARGUMENTS : rewrittenInterfaceBounds);
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureUtils.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureUtils.java
index 7c0dcd2..a3d2eb4 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureUtils.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureUtils.java
@@ -4,10 +4,6 @@
package com.android.tools.r8.graph;
-import static com.android.tools.r8.graph.GenericSignature.ClassSignature.NO_CLASS_SIGNATURE;
-import static com.android.tools.r8.graph.GenericSignature.MethodTypeSignature.NO_METHOD_TYPE_SIGNATURE;
-import static com.android.tools.r8.graph.GenericSignature.NO_FIELD_TYPE_SIGNATURE;
-
import com.android.tools.r8.graph.GenericSignature.DexDefinitionSignature;
public class GenericSignatureUtils {
@@ -15,10 +11,7 @@
public static boolean verifyNoDuplicateGenericDefinitions(
DexDefinitionSignature<?> signature, DexAnnotationSet annotations) {
assert signature != null;
- if (signature == NO_METHOD_TYPE_SIGNATURE
- || signature == NO_FIELD_TYPE_SIGNATURE
- || signature == NO_CLASS_SIGNATURE
- || annotations == null) {
+ if (signature.hasNoSignature() || annotations == null) {
return true;
}
// The check is on the string descriptor to allow for not passing in a factory.
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureVisitor.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureVisitor.java
index 6d9385e..d3840dd 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureVisitor.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureVisitor.java
@@ -9,9 +9,9 @@
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.naming.MemberNaming.MethodSignature;
import java.util.List;
public interface GenericSignatureVisitor {
@@ -20,7 +20,7 @@
throw new Unreachable("Implement if visited");
}
- default void visitMethodSignature(MethodSignature methodSignature) {
+ default void visitMethodSignature(MethodTypeSignature methodSignature) {
throw new Unreachable("Implement if visited");
}
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index 7681f24..f2de6db 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -950,10 +950,7 @@
@Override
public DexMethod getOriginalMethodSignature(DexMethod method) {
- DexMethod originalMethod =
- originalMethodSignatures != null
- ? originalMethodSignatures.getOrDefault(method, method)
- : method;
+ DexMethod originalMethod = internalGetPreviousMethodSignature(method);
return getPrevious().getOriginalMethodSignature(originalMethod);
}
@@ -971,9 +968,7 @@
return originalMethod;
}
DexMethod renamedMethod = getPrevious().getRenamedMethodSignature(originalMethod, applied);
- return originalMethodSignatures != null
- ? originalMethodSignatures.inverse().getOrDefault(renamedMethod, renamedMethod)
- : renamedMethod;
+ return internalGetNextMethodSignature(renamedMethod);
}
@Override
@@ -1064,6 +1059,12 @@
: method;
}
+ protected DexMethod internalGetNextMethodSignature(DexMethod method) {
+ return originalMethodSignatures != null
+ ? originalMethodSignatures.inverse().getOrDefault(method, method)
+ : method;
+ }
+
@Override
public DexMethod lookupGetFieldForMethod(DexField field, DexMethod context) {
return getPrevious().lookupGetFieldForMethod(field, context);
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index ccaa837..b71bfa0 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -34,6 +34,7 @@
import com.android.tools.r8.graph.DexValue.DexValueType;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.jar.CfApplicationWriter;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
@@ -208,7 +209,7 @@
private final List<NestMemberClassAttribute> nestMembers = new ArrayList<>();
private EnclosingMethodAttribute enclosingMember = null;
private final List<InnerClassAttribute> innerClasses = new ArrayList<>();
- private ClassSignature classSignature = ClassSignature.NO_CLASS_SIGNATURE;
+ private ClassSignature classSignature = ClassSignature.noSignature();
private List<DexAnnotation> annotations = null;
private List<DexAnnotationElement> defaultAnnotations = null;
private final List<DexEncodedField> staticFields = new ArrayList<>();
@@ -680,6 +681,7 @@
private List<List<DexAnnotation>> parameterAnnotationsLists = null;
private List<DexValue> parameterNames = null;
private List<DexValue> parameterFlags = null;
+ private final MethodTypeSignature genericSignature;
final DexMethod method;
final MethodAccessFlags flags;
final boolean deprecated;
@@ -702,10 +704,13 @@
addAnnotation(DexAnnotation.createThrowsAnnotation(
values, parent.application.getFactory()));
}
- if (signature != null && !signature.isEmpty()) {
- addAnnotation(DexAnnotation.createSignatureAnnotation(
- signature, parent.application.getFactory()));
- }
+ genericSignature =
+ GenericSignature.parseMethodSignature(
+ name,
+ signature,
+ parent.origin,
+ parent.application.getFactory(),
+ parent.application.options.reporter);
}
@Override
@@ -832,6 +837,7 @@
new DexEncodedMethod(
method,
flags,
+ genericSignature,
createAnnotationSet(annotations, options),
parameterAnnotationsList,
code,
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
index 033182e..4a3327a 100644
--- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -75,6 +75,10 @@
registerTypeReference(type);
}
+ public void registerExceptionGuard(DexType guard) {
+ registerTypeReference(guard);
+ }
+
public void registerMethodHandle(DexMethodHandle methodHandle, MethodHandleUse use) {
switch (methodHandle.type) {
case INSTANCE_GET:
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
index d60a589..eb82383 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/ClassInitializerAssertionEnablingAnalysis.java
@@ -4,12 +4,14 @@
package com.android.tools.r8.graph.analysis;
+import com.android.tools.r8.cf.code.CfArrayStore;
import com.android.tools.r8.cf.code.CfConstNumber;
import com.android.tools.r8.cf.code.CfFieldInstruction;
import com.android.tools.r8.cf.code.CfGoto;
import com.android.tools.r8.cf.code.CfIf;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.cf.code.CfLoad;
import com.android.tools.r8.cf.code.CfLogicalBinop;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.Code;
@@ -90,6 +92,15 @@
// 16: invokevirtual #43 // Method java/lang/Class.desiredAssertionStatus:()Z
// 19: putstatic #45 // Field ENABLED:Z
// 22: return
+ //
+ // When JaCoCo instrumentation is applied the following instruction sequence is injected into
+ // each branch to register code coverage. This includes <clinit> where the vsalue for field
+ // $assertionsDisabled is determined by a branch structure.
+ //
+ // +0: aload_X
+ // +1: iconst_Y / ldc
+ // +2: iconst_Z / ldc
+ // +3 bastore
private static List<Class<?>> javacInstructionSequence =
ImmutableList.of(
@@ -100,6 +111,8 @@
CfFieldInstruction.class);
private static List<Class<?>> r8InstructionSequence =
ImmutableList.of(CfConstNumber.class, CfLogicalBinop.class, CfFieldInstruction.class);
+ private static List<Class<?>> jacocoInstructionSequence =
+ ImmutableList.of(CfLoad.class, CfConstNumber.class, CfConstNumber.class, CfArrayStore.class);
private boolean hasJavacClinitAssertionCode(CfCode code) {
for (int i = 0; i < code.getInstructions().size(); i++) {
@@ -160,6 +173,17 @@
return false;
}
+ private boolean skipSequence(List<Class<?>> sequence, CfCode code, int fromIndex) {
+ int i = fromIndex;
+ for (; i < code.getInstructions().size() && i - fromIndex < sequence.size(); i++) {
+ CfInstruction instruction = code.getInstructions().get(i);
+ if (instruction.getClass() != sequence.get(i - fromIndex)) {
+ return false;
+ }
+ }
+ return i - fromIndex == sequence.size();
+ }
+
private CfFieldInstruction isJavacInstructionSequence(CfCode code, int fromIndex) {
List<Class<?>> sequence = javacInstructionSequence;
int nextExpectedInstructionIndex = 0;
@@ -168,12 +192,27 @@
i < code.getInstructions().size() && nextExpectedInstructionIndex < sequence.size();
i++) {
instruction = code.getInstructions().get(i);
- if (instruction.isLabel() || instruction.isFrame()) {
- // Just ignore labels and frames.
+ if (instruction.isLabel() || instruction.isFrame() || instruction.isPosition()) {
+ // Just ignore labels, frames and positions.
continue;
}
if (instruction.getClass() != sequence.get(nextExpectedInstructionIndex)) {
- break;
+ // Skip injected JaCoCo injected code.
+ if (instruction.getClass() == jacocoInstructionSequence.get(0)
+ && skipSequence(jacocoInstructionSequence, code, i)) {
+ i += jacocoInstructionSequence.size();
+ if (i >= code.getInstructions().size()) {
+ break;
+ }
+ instruction = code.getInstructions().get(i);
+ }
+ if (instruction.isLabel() || instruction.isFrame() || instruction.isPosition()) {
+ // Just ignore labels, frames and positions.
+ continue;
+ }
+ if (instruction.getClass() != sequence.get(nextExpectedInstructionIndex)) {
+ break;
+ }
}
nextExpectedInstructionIndex++;
}
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerExceptionGuardAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerExceptionGuardAnalysis.java
new file mode 100644
index 0000000..1bf392e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/graph/analysis/EnqueuerExceptionGuardAnalysis.java
@@ -0,0 +1,12 @@
+// 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.graph.analysis;
+
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+
+public interface EnqueuerExceptionGuardAnalysis {
+ void traceExceptionGuard(DexType guard, ProgramMethod context);
+}
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java b/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java
index 505dfd5..285e397 100644
--- a/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java
+++ b/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.google.common.collect.ImmutableSet;
import java.util.Set;
public class HorizontallyMergedLambdaClasses implements MergedClasses {
@@ -17,6 +18,10 @@
this.sources = sources;
}
+ public static HorizontallyMergedLambdaClasses empty() {
+ return new HorizontallyMergedLambdaClasses(ImmutableSet.of());
+ }
+
@Override
public boolean verifyAllSourcesPruned(AppView<AppInfoWithLiveness> appView) {
for (DexType source : sources) {
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 4a4936b..f4158c5 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -4,8 +4,6 @@
package com.android.tools.r8.horizontalclassmerging;
-import static com.android.tools.r8.graph.GenericSignature.NO_FIELD_TYPE_SIGNATURE;
-
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotationSet;
@@ -18,6 +16,7 @@
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldAccessFlags;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
@@ -153,7 +152,7 @@
classIdField,
FieldAccessFlags.fromSharedAccessFlags(
Constants.ACC_PUBLIC + Constants.ACC_FINAL + Constants.ACC_SYNTHETIC),
- NO_FIELD_TYPE_SIGNATURE,
+ FieldTypeSignature.noSignature(),
DexAnnotationSet.empty(),
null);
target.appendInstanceField(encodedField);
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 9baf6ad..b6f3058 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.ir.conversion.ExtraConstantIntParameter;
@@ -90,6 +91,10 @@
DexEncodedMethod encodedMethod = constructor.toTypeSubstitutedMethod(method);
encodedMethod.getMutableOptimizationInfo().markForceInline();
+ encodedMethod.accessFlags.unsetConstructor();
+ encodedMethod.accessFlags.unsetPublic();
+ encodedMethod.accessFlags.unsetProtected();
+ encodedMethod.accessFlags.setPrivate();
target.addDirectMethod(encodedMethod);
return method;
}
@@ -164,6 +169,7 @@
new DexEncodedMethod(
newConstructorReference,
getAccessFlags(),
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
synthesizedCode,
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 adc4f12..b1d0dbe 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.horizontalclassmerging.policies.DontMergeIntoLessVisible;
import com.android.tools.r8.horizontalclassmerging.policies.DontMergeSynchronizedClasses;
import com.android.tools.r8.horizontalclassmerging.policies.NoAbstractClasses;
import com.android.tools.r8.horizontalclassmerging.policies.NoAnnotations;
@@ -48,6 +49,7 @@
MainDexTracingResult mainDexTracingResult,
ClassMergingEnqueuerExtension classMergingEnqueuerExtension) {
this.appView = appView;
+ assert appView.options().enableInlining;
List<Policy> policies =
ImmutableList.of(
@@ -71,7 +73,10 @@
new PreventChangingVisibility(),
new SameFeatureSplit(appView),
new RespectPackageBoundaries(appView),
- new DontMergeSynchronizedClasses(appView)
+ new DontMergeSynchronizedClasses(appView),
+ // TODO(b/166577694): no policies should be run after this policy, as it would
+ // potentially break tests
+ new DontMergeIntoLessVisible()
// TODO: add policies
);
@@ -91,6 +96,7 @@
Collection<Collection<DexProgramClass>> groups = policyExecutor.run(classes.values());
// If there are no groups, then end horizontal class merging.
if (groups.isEmpty()) {
+ appView.setHorizontallyMergedClasses(HorizontallyMergedClasses.empty());
return null;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
index 01058e2..653e31a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
@@ -15,12 +15,17 @@
import java.util.Set;
public class HorizontallyMergedClasses implements MergedClasses {
+
private final BidirectionalManyToOneMap<DexType, DexType> mergedClasses;
public HorizontallyMergedClasses(BidirectionalManyToOneMap<DexType, DexType> mergedClasses) {
this.mergedClasses = mergedClasses;
}
+ public static HorizontallyMergedClasses empty() {
+ return new HorizontallyMergedClasses(new BidirectionalManyToOneMap<>());
+ }
+
public DexType getMergeTargetOrDefault(DexType type) {
return mergedClasses.getOrDefault(type, type);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
index ec8d495..28ec4a9 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
@@ -76,7 +76,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
- ClassSignature.NO_CLASS_SIGNATURE,
+ ClassSignature.noSignature(),
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
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 0f9fdc1..7342124 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramMethod;
@@ -18,6 +19,7 @@
import com.android.tools.r8.ir.synthetic.AbstractSynthesizedCode;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
+import com.google.common.collect.Iterables;
import it.unimi.dsi.fastutil.ints.Int2ReferenceAVLTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceSortedMap;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
@@ -106,7 +108,11 @@
private MethodAccessFlags getAccessFlags() {
// TODO(b/164998929): ensure this behaviour is correct, should probably calculate upper bound
- return methods.iterator().next().getDefinition().getAccessFlags();
+ MethodAccessFlags flags = methods.iterator().next().getDefinition().getAccessFlags().copy();
+ if (flags.isFinal() && Iterables.any(methods, method -> !method.getAccessFlags().isFinal())) {
+ flags.unsetFinal();
+ }
+ return flags;
}
@@ -154,12 +160,12 @@
}
// Use the first of the original methods as the original method for the merged constructor.
+ DexMethod templateReference = methods.iterator().next().getReference();
DexMethod originalMethodReference =
- appView.graphLens().getOriginalMethodSignature(methods.iterator().next().getReference());
+ appView.graphLens().getOriginalMethodSignature(templateReference);
DexMethod newMethodReference =
- dexItemFactory.createMethod(
- target.type, originalMethodReference.proto, originalMethodReference.name);
+ dexItemFactory.createMethod(target.type, templateReference.proto, templateReference.name);
AbstractSynthesizedCode synthesizedCode =
new VirtualMethodEntryPointSynthesizedCode(
classIdToMethodMap,
@@ -171,6 +177,7 @@
new DexEncodedMethod(
newMethodReference,
getAccessFlags(),
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
synthesizedCode,
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeIntoLessVisible.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeIntoLessVisible.java
new file mode 100644
index 0000000..e579310
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/DontMergeIntoLessVisible.java
@@ -0,0 +1,33 @@
+// 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.DexProgramClass;
+import com.android.tools.r8.horizontalclassmerging.MultiClassPolicy;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+public class DontMergeIntoLessVisible extends MultiClassPolicy {
+ @Override
+ public Collection<Collection<DexProgramClass>> apply(Collection<DexProgramClass> group) {
+ Iterator<DexProgramClass> iterator = group.iterator();
+ while (iterator.hasNext()) {
+ DexProgramClass clazz = iterator.next();
+ if (clazz.isPublic()) {
+ iterator.remove();
+ List<DexProgramClass> newGroup = new LinkedList<>();
+ newGroup.add(clazz);
+ newGroup.addAll(group);
+ group = newGroup;
+ break;
+ }
+ }
+
+ return Collections.singletonList(group);
+ }
+}
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 6181c41..969f43a 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,27 +4,50 @@
package com.android.tools.r8.horizontalclassmerging.policies;
+
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.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 Set<DexType> dontMergeTypes = Sets.newIdentityHashSet();
public NoKeepRules(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
+
+ appView.appInfo().classes().forEach(this::processClass);
+ }
+
+ private void processClass(DexProgramClass programClass) {
+ DexType type = programClass.getType();
+ boolean pinProgramClass = appView.appInfo().isPinned(type);
+
+ for (DexEncodedMember<?, ?> member : programClass.members()) {
+ DexMember<?, ?> reference = member.toReference();
+ if (appView.appInfo().isPinned(reference)) {
+ pinProgramClass = true;
+ Iterables.addAll(
+ dontMergeTypes,
+ Iterables.filter(
+ reference.getReferencedBaseTypes(appView.dexItemFactory()), DexType::isClassType));
+ }
+ }
+
+ if (pinProgramClass) {
+ dontMergeTypes.add(type);
+ }
}
@Override
public boolean canMerge(DexProgramClass program) {
- DexType type = program.getType();
- boolean anyPinned =
- appView.appInfo().isPinned(type)
- || Iterables.any(
- program.members(), member -> appView.appInfo().isPinned(member.toReference()));
- return !anyPinned;
+ return !dontMergeTypes.contains(program.getType());
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoRuntimeTypeChecks.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoRuntimeTypeChecks.java
index dff9967..23f10b2 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoRuntimeTypeChecks.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoRuntimeTypeChecks.java
@@ -18,7 +18,6 @@
@Override
public boolean canMerge(DexProgramClass clazz) {
// We currently assume we only merge classes that implement the same set of interfaces.
- return !(this.classMergingEnqueuerExtension.isCheckCastType(clazz)
- || this.classMergingEnqueuerExtension.isInstanceOfType(clazz));
+ return !classMergingEnqueuerExtension.isRuntimeCheckType(clazz);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
index 8b63913..94b9489 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeAnalysis.java
@@ -64,6 +64,13 @@
analyzeValues(values, Mode.WIDENING);
}
+ public void narrowing(IRCode code) {
+ mode = Mode.NARROWING;
+ assert worklist.isEmpty();
+ code.topologicallySortedBlocks().forEach(this::analyzeBasicBlock);
+ analyze();
+ }
+
public void narrowing(Iterable<? extends Value> values) {
// TODO(b/125492155) Not sorting causes us to have non-deterministic behaviour. This should be
// removed when the bug is fixed.
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionOrPhi.java b/src/main/java/com/android/tools/r8/ir/code/InstructionOrPhi.java
index 144bbdb..0ae5aaf 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionOrPhi.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionOrPhi.java
@@ -18,6 +18,10 @@
return false;
}
+ default boolean isStackMapPhi() {
+ return false;
+ }
+
default Phi asPhi() {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Phi.java b/src/main/java/com/android/tools/r8/ir/code/Phi.java
index c1de847..d7abfac 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Phi.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Phi.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.BasicBlock.EdgeType;
import com.android.tools.r8.ir.conversion.IRBuilder;
@@ -448,9 +449,9 @@
return result;
}
- // TODO(b/169120386) This class is added to ensure we do not narrow or widen phi's in D8 when
- // having stack map information. It should be removed when we are certain to never widen or
- // narrowing phi's in D8.
+ // TODO(b/169120386) This class is added to ensure we do not narrow or widen the type of phi's
+ // in D8 when having stack map information. We do allow for narrowing on nullable information.
+ // It should be removed when we are certain to never widen or narrowing phi's in D8.
public static class StackMapPhi extends Phi {
public StackMapPhi(
@@ -478,7 +479,20 @@
@Override
public TypeElement computePhiType(AppView<?> appView) {
assert !appView.enableWholeProgramOptimizations();
- return type;
+ if (type.isPrimitiveType()) {
+ return type;
+ }
+ assert type.isReferenceType();
+ Nullability nullability = Nullability.bottom();
+ for (Value operand : getOperands()) {
+ nullability = nullability.join(operand.type.nullability());
+ }
+ return type.asReferenceType().getOrCreateVariant(nullability);
+ }
+
+ @Override
+ public boolean isStackMapPhi() {
+ return true;
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index 6382b86..8f2c15e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -723,9 +723,11 @@
// TODO(b/169137397): We may have ended up generating StackMapPhi's before concluding
// having incorrect stack map types. Figure out a way to clean that up.
new TypeAnalysis(appView).widening(ir);
+ } else {
+ assert canUseStackMapTypes() && !hasIncorrectStackMapTypes;
+ assert allPhisAreStackMapPhis(ir);
+ new TypeAnalysis(appView).narrowing(ir);
}
- // TODO(b/169137397): If we have canUseStackMapTypes() && !hasIncorrectStackMapTypes we should
- // have that all phi's are stack-map phis.
// Update the IR code if collected call site optimization info has something useful.
// While aggregation of parameter information at call sites would be more precise than static
@@ -755,6 +757,15 @@
return !appView.enableWholeProgramOptimizations() && source.hasValidTypesFromStackMap();
}
+ private boolean allPhisAreStackMapPhis(IRCode ir) {
+ ir.instructions()
+ .forEach(
+ instruction -> {
+ assert !instruction.isPhi() || instruction.isStackMapPhi();
+ });
+ return true;
+ }
+
public void constrainType(Value value, ValueTypeConstraint constraint) {
value.constrainType(constraint, method.getReference(), origin, appView.options().reporter);
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index f475e89..b7964c8 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -22,8 +22,10 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
import com.android.tools.r8.ir.analysis.TypeChecker;
import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation;
@@ -705,6 +707,8 @@
}
if (enumUnboxer != null) {
enumUnboxer.unboxEnums(postMethodProcessorBuilder, executorService, feedback);
+ } else {
+ appView.setUnboxedEnums(EnumValueInfoMapCollection.empty());
}
if (!options.debug) {
new TrivialFieldAccessReprocessor(appView.withLiveness(), postMethodProcessorBuilder)
@@ -935,7 +939,10 @@
if (lambdaMerger != null) {
lambdaMerger.applyLambdaClassMapping(
application, this, feedback, builder, executorService);
+ } else {
+ appView.setHorizontallyMergedLambdaClasses(HorizontallyMergedLambdaClasses.empty());
}
+ assert appView.horizontallyMergedLambdaClasses() != null;
}
private void generateDesugaredLibraryAPIWrappers(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index 3f40cba..f2ae9a1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramMethod;
@@ -363,6 +364,7 @@
new DexEncodedMethod(
newMethod,
MethodAccessFlags.fromCfAccessFlags(Opcodes.ACC_PUBLIC, false),
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
new SynthesizedCode(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
index 01fb238..fd4abda 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
@@ -167,6 +167,7 @@
new DexEncodedMethod(
newMethod,
newAccessFlags,
+ methodDefinition.getGenericSignature(),
methodDefinition
.annotations()
.keepIf(x -> !isCovariantReturnTypeAnnotation(x.annotation)),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryEmulatedInterfaceDuplicator.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryEmulatedInterfaceDuplicator.java
new file mode 100644
index 0000000..6f72091
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryEmulatedInterfaceDuplicator.java
@@ -0,0 +1,124 @@
+// 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.ir.desugar;
+
+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.GenericSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class DesugaredLibraryEmulatedInterfaceDuplicator {
+
+ final AppView<?> appView;
+ final Map<DexType, DexType> emulatedInterfaces;
+
+ public DesugaredLibraryEmulatedInterfaceDuplicator(AppView<?> appView) {
+ this.appView = appView;
+ emulatedInterfaces =
+ appView.options().desugaredLibraryConfiguration.getEmulateLibraryInterface();
+ }
+
+ public void duplicateEmulatedInterfaces() {
+ // All classes implementing an emulated interface now implements the interface and the
+ // emulated one, as well as hidden overrides, for correct emulated dispatch.
+ // We not that duplicated interfaces won't feature the correct type parameters in the
+ // class signature since such signature is expected to be unused.
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ if (clazz.type == appView.dexItemFactory().objectType) {
+ continue;
+ }
+ if (emulatedInterfaces.containsKey(clazz.type)) {
+ transformEmulatedInterfaces(clazz);
+ } else {
+ duplicateEmulatedInterfaces(clazz);
+ }
+ }
+ }
+
+ private void transformEmulatedInterfaces(DexProgramClass clazz) {
+ List<ClassTypeSignature> newInterfaces = new ArrayList<>();
+ GenericSignature.ClassSignature classSignature = clazz.getClassSignature();
+ for (int i = 0; i < clazz.interfaces.size(); i++) {
+ DexType itf = clazz.interfaces.values[i];
+ assert emulatedInterfaces.containsKey(itf);
+ List<FieldTypeSignature> typeArguments;
+ if (classSignature == null) {
+ typeArguments = Collections.emptyList();
+ } else {
+ ClassTypeSignature classTypeSignature = classSignature.superInterfaceSignatures().get(i);
+ assert itf == classTypeSignature.type();
+ typeArguments = classTypeSignature.typeArguments();
+ }
+ newInterfaces.add(new ClassTypeSignature(emulatedInterfaces.get(itf), typeArguments));
+ }
+ clazz.replaceInterfaces(newInterfaces);
+ }
+
+ private void duplicateEmulatedInterfaces(DexProgramClass clazz) {
+ List<DexType> extraInterfaces = new ArrayList<>();
+ LinkedList<DexClass> workList = new LinkedList<>();
+ Set<DexType> processed = Sets.newIdentityHashSet();
+ workList.add(clazz);
+ while (!workList.isEmpty()) {
+ DexClass dexClass = workList.removeFirst();
+ if (processed.contains(dexClass.type)) {
+ continue;
+ }
+ processed.add(dexClass.type);
+ if (dexClass.superType != appView.dexItemFactory().objectType) {
+ processSuperType(clazz.superType, extraInterfaces, workList);
+ }
+ for (DexType itf : dexClass.interfaces) {
+ processSuperType(itf, extraInterfaces, workList);
+ }
+ }
+ extraInterfaces = removeDuplicates(extraInterfaces);
+ List<ClassTypeSignature> extraInterfaceSignatures = new ArrayList<>();
+ for (DexType extraInterface : extraInterfaces) {
+ extraInterfaceSignatures.add(new ClassTypeSignature(extraInterface));
+ }
+ clazz.addExtraInterfaces(extraInterfaceSignatures);
+ }
+
+ private List<DexType> removeDuplicates(List<DexType> extraInterfaces) {
+ if (extraInterfaces.size() <= 1) {
+ return extraInterfaces;
+ }
+ // TODO(b/161399032): It would be nice to remove duplicate based on inheritance, i.e.,
+ // if there is ConcurrentMap<K,V> and Map<K,V>, Map<K,V> can be removed.
+ return new ArrayList<>(new HashSet<>(extraInterfaces));
+ }
+
+ void processSuperType(
+ DexType superType, List<DexType> extraInterfaces, LinkedList<DexClass> workList) {
+ if (emulatedInterfaces.containsKey(superType)) {
+ extraInterfaces.add(emulatedInterfaces.get(superType));
+ } else {
+ DexClass superClass = appView.definitionFor(superType);
+ if (shouldProcessSuperclass(superClass)) {
+ workList.add(superClass);
+ }
+ }
+ }
+
+ private boolean shouldProcessSuperclass(DexClass superclazz) {
+ if (appView.options().isDesugaredLibraryCompilation()) {
+ return false;
+ }
+ // TODO(b/161399032): Pay-as-you-go design: stop duplication on library boundaries.
+ return superclazz != null && superclazz.isLibraryClass();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
index 2d4fb70..68a2df3 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
@@ -23,6 +23,8 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ResolutionResult;
@@ -112,7 +114,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
- ClassSignature.NO_CLASS_SIGNATURE,
+ ClassSignature.noSignature(),
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
@@ -374,7 +376,7 @@
// applies up to 24.
for (DexEncodedMethod method : methods) {
clazz.addExtraInterfaces(
- Collections.singletonList(dispatchInterfaceTypeFor(method)), appView.dexItemFactory());
+ Collections.singletonList(new ClassTypeSignature(dispatchInterfaceTypeFor(method))));
if (clazz.lookupVirtualMethod(method.getReference()) == null) {
DexEncodedMethod newMethod = createForwardingMethod(method, clazz);
clazz.addVirtualMethod(newMethod);
@@ -447,7 +449,13 @@
emulatedDispatchMethod.getProto(),
emulatedDispatchMethod.getName());
return new DexEncodedMethod(
- newMethod, flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), null, true);
+ newMethod,
+ flags,
+ MethodTypeSignature.noSignature(),
+ DexAnnotationSet.empty(),
+ ParameterAnnotationsList.empty(),
+ null,
+ true);
}
private DexEncodedMethod generateHolderDispatchMethod(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
index 88fc45a..e2f3271 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
@@ -4,8 +4,6 @@
package com.android.tools.r8.ir.desugar;
-import static com.android.tools.r8.graph.GenericSignature.NO_FIELD_TYPE_SIGNATURE;
-
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
@@ -28,6 +26,8 @@
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.FieldAccessFlags;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.ir.conversion.IRConverter;
@@ -233,7 +233,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
- ClassSignature.NO_CLASS_SIGNATURE,
+ ClassSignature.noSignature(),
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY, // No static fields.
new DexEncodedField[] {wrapperField},
@@ -375,6 +375,7 @@
return new DexEncodedMethod(
methodToInstall,
newFlags,
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
code,
@@ -430,7 +431,7 @@
FieldAccessFlags fieldAccessFlags =
FieldAccessFlags.fromCfAccessFlags(Constants.ACC_FINAL | Constants.ACC_SYNTHETIC);
return new DexEncodedField(
- field, fieldAccessFlags, NO_FIELD_TYPE_SIGNATURE, DexAnnotationSet.empty(), null);
+ field, fieldAccessFlags, FieldTypeSignature.noSignature(), DexAnnotationSet.empty(), null);
}
private DexEncodedMethod synthesizeConstructor(DexField field) {
@@ -452,6 +453,7 @@
return new DexEncodedMethod(
methodToInstall,
accessFlags,
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
code,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 6d37c32..61a5c56 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -54,7 +54,6 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.IdentityHashMap;
-import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
@@ -713,7 +712,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
- ClassSignature.NO_CLASS_SIGNATURE,
+ ClassSignature.noSignature(),
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
@@ -923,63 +922,6 @@
return newMethods;
}
- private void duplicateEmulatedInterfaces() {
- // All classes implementing an emulated interface now implements the interface and the
- // emulated one, as well as hidden overrides, for correct emulated dispatch.
- for (DexProgramClass clazz : appView.appInfo().classes()) {
- if (clazz.type == appView.dexItemFactory().objectType) {
- continue;
- }
- List<DexType> extraInterfaces = new ArrayList<>();
- for (DexType type : clazz.interfaces.values) {
- if (emulatedInterfaces.containsKey(type)) {
- extraInterfaces.add(emulatedInterfaces.get(type));
- }
- }
- if (!appView.options().isDesugaredLibraryCompilation()) {
- assert clazz.superType != null;
- DexClass superClazz = appView.definitionFor(clazz.superType);
- if (superClazz != null && superClazz.isLibraryClass()) {
- List<DexType> itfs = emulatedInterfacesOf(superClazz);
- for (DexType itf : itfs) {
- extraInterfaces.add(emulatedInterfaces.get(itf));
- }
- }
- // Remove duplicates.
- if (extraInterfaces.size() > 1) {
- extraInterfaces = new ArrayList<>(new LinkedHashSet<>(extraInterfaces));
- }
- }
- clazz.addExtraInterfaces(extraInterfaces, appView.dexItemFactory());
- }
- }
-
- private List<DexType> emulatedInterfacesOf(DexClass superClazz) {
- if (superClazz.type == factory.objectType) {
- return Collections.emptyList();
- }
- ArrayList<DexType> itfs = new ArrayList<>();
- LinkedList<DexType> workList = new LinkedList<>();
- workList.add(superClazz.type);
- while (!workList.isEmpty()) {
- DexType dexType = workList.removeFirst();
- DexClass dexClass = appView.definitionFor(dexType);
- if (dexClass != null) {
- if (dexClass.superType != factory.objectType) {
- workList.add(dexClass.superType);
- }
- for (DexType itf : dexClass.interfaces.values) {
- if (emulatedInterfaces.containsKey(itf)) {
- itfs.add(itf);
- } else {
- workList.add(itf);
- }
- }
- }
- }
- return itfs;
- }
-
/**
* Move static and default interface methods to companion classes, add missing methods to forward
* to moved default methods implementation.
@@ -992,7 +934,7 @@
if (appView.options().isDesugaredLibraryCompilation()) {
generateEmulateInterfaceLibrary(builder);
}
- duplicateEmulatedInterfaces();
+ new DesugaredLibraryEmulatedInterfaceDuplicator(appView).duplicateEmulatedInterfaces();
// Process all classes first. Add missing forwarding methods to
// replace desugared default interface methods.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
index 069f4b3..99230aa 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -27,6 +27,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
import com.android.tools.r8.graph.MethodAccessFlags;
@@ -99,6 +100,7 @@
new DexEncodedMethod(
companionMethod,
newFlags,
+ virtual.getGenericSignature(),
virtual.annotations(),
virtual.parameterAnnotationsList,
code,
@@ -106,7 +108,7 @@
implMethod.copyMetadata(virtual);
virtual.setDefaultInterfaceMethodImplementation(implMethod);
companionMethods.add(implMethod);
- graphLensBuilder.recordOrigin(implMethod.method, virtual.method);
+ graphLensBuilder.recordCodeMovedToCompanionClass(virtual.method, implMethod.method);
}
// Remove bridge methods.
@@ -139,6 +141,7 @@
new DexEncodedMethod(
companionMethod,
newFlags,
+ direct.getGenericSignature(),
direct.annotations(),
direct.parameterAnnotationsList,
direct.getCode(),
@@ -166,6 +169,7 @@
new DexEncodedMethod(
companionMethod,
newFlags,
+ direct.getGenericSignature(),
direct.annotations(),
direct.parameterAnnotationsList,
code,
@@ -213,7 +217,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
- ClassSignature.NO_CLASS_SIGNATURE,
+ ClassSignature.noSignature(),
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
@@ -271,6 +275,7 @@
newMethod,
MethodAccessFlags.fromSharedAccessFlags(
Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC, false),
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
forwardMethodBuilder.build(),
@@ -298,7 +303,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
- ClassSignature.NO_CLASS_SIGNATURE,
+ ClassSignature.noSignature(),
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
@@ -448,15 +453,17 @@
}
@Override
- public DexMethod getOriginalMethodSignature(DexMethod method) {
- DexMethod originalMethod = extraOriginalMethodSignatures.get(method);
- if (originalMethod == null) {
- originalMethod =
- originalMethodSignatures != null
- ? originalMethodSignatures.getOrDefault(method, method)
- : method;
- }
- return getPrevious().getOriginalMethodSignature(originalMethod);
+ protected DexMethod internalGetPreviousMethodSignature(DexMethod method) {
+ return extraOriginalMethodSignatures.getOrDefault(
+ method, originalMethodSignatures.getOrDefault(method, method));
+ }
+
+ @Override
+ protected DexMethod internalGetNextMethodSignature(DexMethod method) {
+ return originalMethodSignatures
+ .inverse()
+ .getOrDefault(
+ method, extraOriginalMethodSignatures.inverse().getOrDefault(method, method));
}
@Override
@@ -472,11 +479,10 @@
private final BiMap<DexMethod, DexMethod> extraOriginalMethodSignatures = HashBiMap.create();
- public void recordOrigin(DexMethod method, DexMethod origin) {
- if (method == origin) {
- return;
- }
- extraOriginalMethodSignatures.put(method, origin);
+ public void recordCodeMovedToCompanionClass(DexMethod from, DexMethod to) {
+ assert from != to;
+ originalMethodSignatures.put(from, from);
+ extraOriginalMethodSignatures.put(to, from);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index 6f4cad3..c9e47c2 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -4,8 +4,6 @@
package com.android.tools.r8.ir.desugar;
-import static com.android.tools.r8.graph.GenericSignature.NO_FIELD_TYPE_SIGNATURE;
-
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
@@ -26,6 +24,8 @@
import com.android.tools.r8.graph.DexValue.DexValueNull;
import com.android.tools.r8.graph.FieldAccessFlags;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramMethod;
@@ -163,7 +163,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
- ClassSignature.NO_CLASS_SIGNATURE,
+ ClassSignature.noSignature(),
DexAnnotationSet.empty(),
synthesizeStaticFields(),
synthesizeInstanceFields(),
@@ -234,6 +234,7 @@
mainMethod,
MethodAccessFlags.fromSharedAccessFlags(
Constants.ACC_PUBLIC | Constants.ACC_FINAL, false),
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
LambdaMainMethodSourceCode.build(this, mainMethod),
@@ -252,6 +253,7 @@
| Constants.ACC_SYNTHETIC
| Constants.ACC_BRIDGE,
false),
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
LambdaBridgeMethodSourceCode.build(this, bridgeMethod, mainMethod),
@@ -273,6 +275,7 @@
(stateless ? Constants.ACC_PRIVATE : Constants.ACC_PUBLIC)
| Constants.ACC_SYNTHETIC,
true),
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
LambdaConstructorSourceCode.build(this),
@@ -285,6 +288,7 @@
classConstructor,
MethodAccessFlags.fromSharedAccessFlags(
Constants.ACC_SYNTHETIC | Constants.ACC_STATIC, true),
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
LambdaClassConstructorSourceCode.build(this),
@@ -306,7 +310,7 @@
new DexEncodedField(
getCaptureField(i),
accessFlags,
- NO_FIELD_TYPE_SIGNATURE,
+ FieldTypeSignature.noSignature(),
DexAnnotationSet.empty(),
null);
}
@@ -330,7 +334,7 @@
| Constants.ACC_FINAL
| Constants.ACC_SYNTHETIC
| Constants.ACC_STATIC),
- NO_FIELD_TYPE_SIGNATURE,
+ FieldTypeSignature.noSignature(),
DexAnnotationSet.empty(),
DexValueNull.NULL);
return fields;
@@ -622,12 +626,13 @@
new DexEncodedMethod(
callTarget,
newAccessFlags,
+ encodedMethod.getGenericSignature(),
encodedMethod.annotations(),
encodedMethod.parameterAnnotationsList,
encodedMethod.getCode(),
true);
newMethod.copyMetadata(encodedMethod);
- rewriter.lensBuilder.move(encodedMethod.method, callTarget);
+ rewriter.forcefullyMoveMethod(encodedMethod.method, callTarget);
DexEncodedMethod.setDebugInfoWithFakeThisParameter(
newMethod.getCode(), callTarget.getArity(), appView);
@@ -694,12 +699,13 @@
new DexEncodedMethod(
callTarget,
newAccessFlags,
+ encodedMethod.getGenericSignature(),
encodedMethod.annotations(),
encodedMethod.parameterAnnotationsList,
encodedMethod.getCode(),
true);
newMethod.copyMetadata(encodedMethod);
- rewriter.lensBuilder.move(encodedMethod.method, callTarget);
+ rewriter.forcefullyMoveMethod(encodedMethod.method, callTarget);
return newMethod;
});
return new ProgramMethod(implMethodHolder, replacement);
@@ -723,6 +729,7 @@
new DexEncodedMethod(
callTarget,
accessorFlags,
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
new SynthesizedCode(
@@ -761,6 +768,7 @@
new DexEncodedMethod(
callTarget,
accessorFlags,
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
AccessorMethodSourceCode.build(LambdaClass.this, callTarget),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index 7bb8d44..11cff1a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -46,6 +46,7 @@
import com.google.common.base.Suppliers;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.IdentityHashMap;
@@ -77,7 +78,8 @@
final DexString instanceFieldName;
- final LambdaRewriterLens.Builder lensBuilder = LambdaRewriterLens.builder();
+ private final LambdaRewriterLens.Builder lensBuilder = LambdaRewriterLens.builder();
+ private final Set<DexMethod> forcefullyMovedMethods = Sets.newIdentityHashSet();
// Maps call sites seen so far to inferred lambda descriptor. It is intended
// to help avoid re-matching call sites we already seen. Note that same call
@@ -95,6 +97,15 @@
this.instanceFieldName = appView.dexItemFactory().createString(LAMBDA_INSTANCE_FIELD_NAME);
}
+ void forcefullyMoveMethod(DexMethod from, DexMethod to) {
+ lensBuilder.move(from, to);
+ forcefullyMovedMethods.add(from);
+ }
+
+ public Set<DexMethod> getForcefullyMovedMethods() {
+ return forcefullyMovedMethods;
+ }
+
private void synthesizeAccessibilityBridgesForLambdaClassesD8(
Collection<LambdaClass> lambdaClasses, IRConverter converter, ExecutorService executorService)
throws ExecutionException {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
index 01b87d2..c0a1269 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NestBasedAccessDesugaring.java
@@ -186,7 +186,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
- ClassSignature.NO_CLASS_SIGNATURE,
+ ClassSignature.noSignature(),
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
index 7b0d6b0..23cd37d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
@@ -20,6 +20,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.ir.code.IRCode;
@@ -135,8 +136,15 @@
options, twrCloseResourceMethod);
MethodAccessFlags flags = MethodAccessFlags.fromSharedAccessFlags(
Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC, false);
- DexEncodedMethod method = new DexEncodedMethod(twrCloseResourceMethod,
- flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), code, true);
+ DexEncodedMethod method =
+ new DexEncodedMethod(
+ twrCloseResourceMethod,
+ flags,
+ MethodTypeSignature.noSignature(),
+ DexAnnotationSet.empty(),
+ ParameterAnnotationsList.empty(),
+ code,
+ true);
// Create utility class.
DexProgramClass utilityClass =
@@ -152,7 +160,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
- ClassSignature.NO_CLASS_SIGNATURE,
+ ClassSignature.noSignature(),
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 56d0a08..9e16ab0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -26,6 +26,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
@@ -1361,6 +1362,7 @@
new DexEncodedMethod(
method,
methodAccess,
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
new OutlineCode(outline),
@@ -1392,7 +1394,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
- ClassSignature.NO_CLASS_SIGNATURE,
+ ClassSignature.noSignature(),
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY, // Static fields.
DexEncodedField.EMPTY_ARRAY, // Instance fields.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
index 7a81edc..1694a7b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.MethodCollection;
import com.android.tools.r8.graph.ParameterAnnotationsList;
@@ -218,6 +219,7 @@
methodReference,
MethodAccessFlags.fromSharedAccessFlags(
Constants.ACC_PUBLIC | Constants.ACC_STATIC, false),
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
ServiceLoaderSourceCode.generate(serviceType, classes, appView.dexItemFactory()),
@@ -253,7 +255,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
- ClassSignature.NO_CLASS_SIGNATURE,
+ ClassSignature.noSignature(),
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY, // Static fields.
DexEncodedField.EMPTY_ARRAY, // Instance fields.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index 4a2c485..d815826 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.optimize.enums;
-import static com.android.tools.r8.graph.GenericSignature.NO_FIELD_TYPE_SIGNATURE;
import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
import com.android.tools.r8.dex.Constants;
@@ -24,6 +23,8 @@
import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfo;
import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap;
import com.android.tools.r8.graph.FieldAccessFlags;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramMethod;
@@ -422,7 +423,7 @@
field,
FieldAccessFlags.fromSharedAccessFlags(
Constants.ACC_SYNTHETIC | Constants.ACC_STATIC | Constants.ACC_PUBLIC),
- NO_FIELD_TYPE_SIGNATURE,
+ FieldTypeSignature.noSignature(),
DexAnnotationSet.empty(),
null);
}
@@ -633,6 +634,7 @@
return new DexEncodedMethod(
method,
synthesizedMethodAccessFlags(sync),
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
cfCode,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index df641af..4df9c8d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -144,7 +144,7 @@
private DexMethod ensureUniqueMethod(DexEncodedMethod encodedMethod, DexMethod newMethod) {
DexClass holder = appView.definitionFor(encodedMethod.holder());
assert holder != null;
- if (encodedMethod.isInstanceInitializer()) {
+ if (newMethod.isInstanceInitializer(appView.dexItemFactory())) {
newMethod =
factory.createInstanceInitializerWithFreshProto(
newMethod,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/UnboxedEnumMemberRelocator.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/UnboxedEnumMemberRelocator.java
index b199c47..c203772 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/UnboxedEnumMemberRelocator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/UnboxedEnumMemberRelocator.java
@@ -122,7 +122,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
- ClassSignature.NO_CLASS_SIGNATURE,
+ ClassSignature.noSignature(),
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
index 3bb366d..ee35087 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/LambdaMerger.java
@@ -252,15 +252,16 @@
.filter(cls -> !appView.appInfo().isPinned(cls.type))
.filter(
cls ->
- cls.getKotlinInfo().isSyntheticClass()
- && cls.getKotlinInfo().asSyntheticClass().isLambda()
+ appView.testing().kotlinLambdaMergerFactoryForClass.apply(cls) != null
&& KotlinLambdaGroupIdFactory.hasValidAnnotations(kotlin, cls)
&& !appView.appInfo().getClassToFeatureSplitMap().isInFeature(cls))
.sorted((a, b) -> a.type.slowCompareTo(b.type)) // Ensure stable ordering.
.forEachOrdered(
lambda -> {
try {
- LambdaGroupId id = KotlinLambdaGroupIdFactory.create(appView, kotlin, lambda);
+ KotlinLambdaGroupIdFactory lambdaGroupIdFactory =
+ appView.testing().kotlinLambdaMergerFactoryForClass.apply(lambda);
+ LambdaGroupId id = lambdaGroupIdFactory.validateAndCreate(appView, kotlin, lambda);
LambdaGroup group = groups.computeIfAbsent(id, LambdaGroupId::createGroup);
group.add(lambda);
lambdas.put(lambda.type, group);
@@ -341,6 +342,7 @@
ExecutorService executorService)
throws ExecutionException {
if (lambdas.isEmpty()) {
+ appView.setHorizontallyMergedLambdaClasses(HorizontallyMergedLambdaClasses.empty());
return;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java
index 2e4e1a9..df72677 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/JStyleLambdaGroupIdFactory.java
@@ -15,19 +15,22 @@
import com.android.tools.r8.kotlin.Kotlin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-final class JStyleLambdaGroupIdFactory extends KotlinLambdaGroupIdFactory {
- static final JStyleLambdaGroupIdFactory INSTANCE = new JStyleLambdaGroupIdFactory();
+public final class JStyleLambdaGroupIdFactory extends KotlinLambdaGroupIdFactory {
+ private static final JStyleLambdaGroupIdFactory INSTANCE = new JStyleLambdaGroupIdFactory();
+
+ private JStyleLambdaGroupIdFactory() {}
+
+ public static JStyleLambdaGroupIdFactory getInstance() {
+ return INSTANCE;
+ }
@Override
- LambdaGroupId validateAndCreate(
+ public LambdaGroupId validateAndCreate(
AppView<AppInfoWithLiveness> appView, Kotlin kotlin, DexClass lambda)
throws LambdaStructureError {
boolean accessRelaxed =
appView.options().getProguardConfiguration().isAccessModificationAllowed();
- assert lambda.getKotlinInfo().isSyntheticClass();
- assert lambda.getKotlinInfo().asSyntheticClass().isJavaStyleLambda();
-
// Ignore ACC_SUPER.
ClassAccessFlags copy = lambda.accessFlags.copy();
copy.unsetSuper();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java
index 6f7650b..cc212bf 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KStyleLambdaGroupIdFactory.java
@@ -15,19 +15,22 @@
import com.android.tools.r8.kotlin.Kotlin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-final class KStyleLambdaGroupIdFactory extends KotlinLambdaGroupIdFactory {
- static final KotlinLambdaGroupIdFactory INSTANCE = new KStyleLambdaGroupIdFactory();
+public final class KStyleLambdaGroupIdFactory extends KotlinLambdaGroupIdFactory {
+ private static final KStyleLambdaGroupIdFactory INSTANCE = new KStyleLambdaGroupIdFactory();
+
+ private KStyleLambdaGroupIdFactory() {}
+
+ public static KStyleLambdaGroupIdFactory getInstance() {
+ return INSTANCE;
+ }
@Override
- LambdaGroupId validateAndCreate(
+ public LambdaGroupId validateAndCreate(
AppView<AppInfoWithLiveness> appView, Kotlin kotlin, DexClass lambda)
throws LambdaStructureError {
boolean accessRelaxed =
appView.options().getProguardConfiguration().isAccessModificationAllowed();
- assert lambda.getKotlinInfo().isSyntheticClass();
- assert lambda.getKotlinInfo().asSyntheticClass().isKotlinStyleLambda();
-
// Ignore ACC_SUPER.
ClassAccessFlags copy = lambda.accessFlags.copy();
copy.unsetSuper();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
index a29db28..977bdcf 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupClassBuilder.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.optimize.lambda.kotlin;
-import static com.android.tools.r8.graph.GenericSignature.NO_FIELD_TYPE_SIGNATURE;
import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -25,6 +24,8 @@
import com.android.tools.r8.graph.EnclosingMethodAttribute;
import com.android.tools.r8.graph.GenericSignature;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
@@ -158,6 +159,7 @@
new DexEncodedMethod(
method,
accessFlags,
+ MethodTypeSignature.noSignature(),
isMainMethod ? id.mainMethodAnnotations : DexAnnotationSet.empty(),
isMainMethod
? id.mainMethodParamAnnotations
@@ -252,6 +254,7 @@
new DexEncodedMethod(
initializerMethod,
CONSTRUCTOR_FLAGS_RELAXED, // always create access-relaxed constructor.
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
new SynthesizedCode(
@@ -271,6 +274,7 @@
new DexEncodedMethod(
method,
CLASS_INITIALIZER_FLAGS,
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
new SynthesizedCode(
@@ -293,7 +297,7 @@
new DexEncodedField(
group.getLambdaIdField(factory),
CAPTURE_FIELD_FLAGS_RELAXED,
- NO_FIELD_TYPE_SIGNATURE,
+ FieldTypeSignature.noSignature(),
DexAnnotationSet.empty(),
null);
@@ -302,7 +306,7 @@
new DexEncodedField(
group.getCaptureField(factory, id),
CAPTURE_FIELD_FLAGS_RELAXED,
- NO_FIELD_TYPE_SIGNATURE,
+ FieldTypeSignature.noSignature(),
DexAnnotationSet.empty(),
null);
}
@@ -326,7 +330,7 @@
new DexEncodedField(
field,
SINGLETON_FIELD_FLAGS,
- NO_FIELD_TYPE_SIGNATURE,
+ FieldTypeSignature.noSignature(),
DexAnnotationSet.empty(),
DexValueNull.NULL);
result.add(encodedField);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
index 5a90f80..472d861 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/lambda/kotlin/KotlinLambdaGroupIdFactory.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.ir.optimize.lambda.CaptureSignature;
@@ -23,27 +24,26 @@
KotlinLambdaGroupIdFactory() {
}
- // Creates a lambda group id for kotlin style lambda. Should never return null, if the lambda
- // does not pass pre-requirements (mostly by not meeting high-level structure expectations)
- // should throw LambdaStructureError leaving the caller to decide if/how it needs to be reported.
+ public static KotlinLambdaGroupIdFactory getFactoryForClass(DexProgramClass clazz) {
+ if (clazz.getKotlinInfo().isSyntheticClass()
+ && clazz.getKotlinInfo().asSyntheticClass().isLambda()) {
+ if (clazz.getKotlinInfo().asSyntheticClass().isKotlinStyleLambda()) {
+ return KStyleLambdaGroupIdFactory.getInstance();
+ }
+ assert clazz.getKotlinInfo().asSyntheticClass().isJavaStyleLambda();
+ return JStyleLambdaGroupIdFactory.getInstance();
+ }
+ return null;
+ }
+
+ // Creates a lambda group id for a Java or Kotlin style lambda. Never returns null, but may throw
+ // a LambdaStructureError if the lambda does not pass pre-requirements (mostly by not meeting
+ // high-level structure expectations).
//
// At this point we only perform high-level checks before qualifying the lambda as a candidate
// for merging and assigning lambda group id. We can NOT perform checks on method bodies since
// they may not be converted yet, we'll do that in KStyleLambdaClassValidator.
- public static LambdaGroupId create(
- AppView<AppInfoWithLiveness> appView, Kotlin kotlin, DexClass lambda)
- throws LambdaStructureError {
-
- assert lambda.getKotlinInfo().isSyntheticClass();
- if (lambda.getKotlinInfo().asSyntheticClass().isKotlinStyleLambda()) {
- return KStyleLambdaGroupIdFactory.INSTANCE.validateAndCreate(appView, kotlin, lambda);
- }
-
- assert lambda.getKotlinInfo().asSyntheticClass().isJavaStyleLambda();
- return JStyleLambdaGroupIdFactory.INSTANCE.validateAndCreate(appView, kotlin, lambda);
- }
-
- abstract LambdaGroupId validateAndCreate(
+ public abstract LambdaGroupId validateAndCreate(
AppView<AppInfoWithLiveness> appView, Kotlin kotlin, DexClass lambda)
throws LambdaStructureError;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryFieldSynthesis.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryFieldSynthesis.java
index a20b8bf..c411d08 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryFieldSynthesis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryFieldSynthesis.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.ir.optimize.library;
import static com.android.tools.r8.graph.DexLibraryClass.asLibraryClassOrNull;
-import static com.android.tools.r8.graph.GenericSignature.NO_FIELD_TYPE_SIGNATURE;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
@@ -14,6 +13,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexLibraryClass;
import com.android.tools.r8.graph.FieldAccessFlags;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
/**
* This class synthesizes library fields that we rely on for modeling.
@@ -39,7 +39,7 @@
field,
FieldAccessFlags.fromCfAccessFlags(
Constants.ACC_PRIVATE | Constants.ACC_FINAL),
- NO_FIELD_TYPE_SIGNATURE,
+ FieldTypeSignature.noSignature(),
DexAnnotationSet.empty(),
null));
}
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index ae7fcf5..b914a14 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -274,20 +274,6 @@
return encodedAnnotation.elements[0].value;
}
- private String getSignature(DexAnnotationSet annotations) {
- DexValue value =
- getSystemAnnotationValue(annotations, application.dexItemFactory.annotationSignature);
- if (value == null) {
- return null;
- }
- // Signature has already been minified by ClassNameMinifier.renameTypesInGenericSignatures().
- StringBuilder res = new StringBuilder();
- for (DexValue part : value.asDexValueArray().getValues()) {
- res.append(part.asDexValueString().getValue().toString());
- }
- return res.toString();
- }
-
private String getSourceDebugExtension(DexAnnotationSet annotations) {
DexValue debugExtensions =
getSystemAnnotationValue(
@@ -340,7 +326,7 @@
}
String name = namingLens.lookupName(field.field).toString();
String desc = namingLens.lookupDescriptor(field.field.type).toString();
- String signature = field.getFieldSignature().toRenamedString(namingLens, isTypeMissing);
+ String signature = field.getGenericSignature().toRenamedString(namingLens, isTypeMissing);
Object value = getStaticValue(field);
FieldVisitor visitor = writer.visitField(access, name, desc, signature, value);
writeAnnotations(visitor::visitAnnotation, field.annotations().annotations);
@@ -360,7 +346,8 @@
}
String name = namingLens.lookupName(method.getReference()).toString();
String desc = definition.descriptor(namingLens);
- String signature = getSignature(definition.annotations());
+ String signature =
+ method.getDefinition().getGenericSignature().toRenamedString(namingLens, isTypeMissing);
String[] exceptions = getExceptions(definition.annotations());
MethodVisitor visitor = writer.visitMethod(access, name, desc, signature, exceptions);
if (defaults.containsKey(definition.getName())) {
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 1a51edf..2c57aa4 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
@@ -4,30 +4,15 @@
package com.android.tools.r8.naming.signature;
-import static com.android.tools.r8.utils.DescriptorUtils.getClassBinaryNameFromDescriptor;
-import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromClassBinaryName;
-
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexAnnotation;
-import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.GenericSignatureTypeRewriter;
import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
-import java.lang.reflect.GenericSignatureFormatError;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-import java.util.function.Predicate;
-import java.util.function.Supplier;
// TODO(b/169516860): We should generalize this to handle rewriting of attributes in general.
public class GenericSignatureRewriter {
@@ -60,238 +45,16 @@
clazz -> {
GenericSignatureTypeRewriter genericSignatureTypeRewriter =
new GenericSignatureTypeRewriter(appView, clazz);
- GenericSignatureCollector genericSignatureCollector =
- new GenericSignatureCollector(clazz);
- GenericSignatureParser<DexType> genericSignatureParser =
- new GenericSignatureParser<>(genericSignatureCollector);
- ClassSignature classSignature = clazz.getClassSignature();
- if (classSignature.hasSignature()) {
- // TODO(b/129925954): We still have to rewrite to capture the lastWrittenType.
- // The design is utterly broken.
- DexAnnotation classSignatureAnnotation =
- DexAnnotation.createSignatureAnnotation(
- classSignature.toString(), options.itemFactory);
- rewriteGenericSignatures(
- new DexAnnotationSet(new DexAnnotation[] {classSignatureAnnotation}),
- genericSignatureParser::parseClassSignature,
- genericSignatureCollector::getRenamedSignature,
- (signature, e) ->
- options.warningInvalidSignature(clazz, clazz.getOrigin(), signature, e));
- }
- clazz.setClassSignature(genericSignatureTypeRewriter.rewrite(classSignature));
+ clazz.setClassSignature(genericSignatureTypeRewriter.rewrite(clazz.getClassSignature()));
clazz.forEachField(
field ->
- field.setFieldSignature(
- genericSignatureTypeRewriter.rewrite(field.getFieldSignature())));
+ field.setGenericSignature(
+ genericSignatureTypeRewriter.rewrite(field.getGenericSignature())));
clazz.forEachMethod(
method ->
- method.setAnnotations(
- rewriteGenericSignatures(
- method.annotations(),
- genericSignatureParser::parseMethodSignature,
- genericSignatureCollector::getRenamedSignature,
- (signature, e) ->
- options.warningInvalidSignature(
- method, clazz.getOrigin(), signature, e))));
+ method.setGenericSignature(
+ genericSignatureTypeRewriter.rewrite(method.getGenericSignature())));
},
executorService);
}
-
- // TODO(b/129925954): Remove this when using modeled signatures for methods and fields.
- private DexAnnotationSet rewriteGenericSignatures(
- DexAnnotationSet annotations,
- Consumer<String> parser,
- Supplier<String> collector,
- BiConsumer<String, GenericSignatureFormatError> parseError) {
- // There can be no more than one signature annotation in an annotation set.
- final int VALID = -1;
- int invalid = VALID;
- DexAnnotation[] rewrittenAnnotations = null;
- for (int i = 0; i < annotations.annotations.length && invalid == VALID; i++) {
- DexAnnotation annotation = annotations.annotations[i];
- if (DexAnnotation.isSignatureAnnotation(annotation, appView.dexItemFactory())) {
- if (rewrittenAnnotations == null) {
- rewrittenAnnotations = new DexAnnotation[annotations.annotations.length];
- System.arraycopy(annotations.annotations, 0, rewrittenAnnotations, 0, i);
- }
- String signature = DexAnnotation.getSignature(annotation);
- try {
- parser.accept(signature);
- String renamedSignature = collector.get();
- assert verifyConsistentRenaming(parser, collector, renamedSignature);
- DexAnnotation signatureAnnotation =
- DexAnnotation.createSignatureAnnotation(renamedSignature, appView.dexItemFactory());
- rewrittenAnnotations[i] = signatureAnnotation;
- } catch (GenericSignatureFormatError e) {
- parseError.accept(signature, e);
- invalid = i;
- }
- } else if (rewrittenAnnotations != null) {
- rewrittenAnnotations[i] = annotation;
- }
- }
-
- // Return the rewritten signatures if it was valid and could be rewritten.
- if (invalid == VALID) {
- return rewrittenAnnotations != null
- ? new DexAnnotationSet(rewrittenAnnotations)
- : annotations;
- }
- // Remove invalid signature if found.
- DexAnnotation[] prunedAnnotations =
- new DexAnnotation[annotations.annotations.length - 1];
- int dest = 0;
- for (int i = 0; i < annotations.annotations.length; i++) {
- if (i != invalid) {
- prunedAnnotations[dest++] = annotations.annotations[i];
- }
- }
- assert dest == prunedAnnotations.length;
- return new DexAnnotationSet(prunedAnnotations);
- }
-
- /**
- * Calling this method will clobber the parsed signature in the collector - ideally with the same
- * string. Only use this after the original result has been collected.
- */
- private boolean verifyConsistentRenaming(
- Consumer<String> parser, Supplier<String> collector, String renamedSignature) {
- if (!options.testing.assertConsistentRenamingOfSignature) {
- return true;
- }
- parser.accept(renamedSignature);
- String reRenamedSignature = collector.get();
- assert renamedSignature.equals(reRenamedSignature);
- return true;
- }
-
- private class GenericSignatureCollector implements GenericSignatureAction<DexType> {
- private StringBuilder renamedSignature;
- private final DexProgramClass currentClassContext;
- private DexType lastWrittenType = null;
-
- GenericSignatureCollector(DexProgramClass clazz) {
- this.currentClassContext = clazz;
- }
-
- String getRenamedSignature() {
- return renamedSignature.toString();
- }
-
- @Override
- public void parsedSymbol(char symbol) {
- if (symbol == ';' && lastWrittenType == null) {
- // The type was never written (maybe because it was merged with it's subtype).
- return;
- }
- // If the super-class or interface has been merged, we will stop writing out type
- // arguments, resulting in a signature on the form '<>' if we do not remove it.
- if (symbol == '>' && removeWrittenCharacter(c -> c == '<')) {
- return;
- }
- renamedSignature.append(symbol);
- }
-
- @Override
- public void parsedIdentifier(String identifier) {
- renamedSignature.append(identifier);
- }
-
- @Override
- public DexType parsedTypeName(String name, ParserPosition parserPosition) {
- if (parserPosition == ParserPosition.ENCLOSING_INNER_OR_TYPE_ANNOTATION
- && lastWrittenType == null) {
- // We are writing type-arguments for a merged class.
- removeWrittenClassCharacter();
- return null;
- }
- String originalDescriptor = getDescriptorFromClassBinaryName(name);
- DexType type =
- appView.graphLens().lookupType(appView.dexItemFactory().createType(originalDescriptor));
- if (appView.appInfo().hasLiveness() && appView.withLiveness().appInfo().wasPruned(type)) {
- type = appView.dexItemFactory().objectType;
- }
- DexString renamedDescriptor = namingLens.lookupDescriptor(type);
- if (parserPosition == ParserPosition.CLASS_SUPER_OR_INTERFACE_ANNOTATION
- && currentClassContext != null) {
- // We may have merged the type down to the current class type.
- DexString classDescriptor = currentClassContext.type.descriptor;
- if (!originalDescriptor.equals(classDescriptor.toString())
- && renamedDescriptor.equals(classDescriptor)) {
- lastWrittenType = null;
- removeWrittenClassCharacter();
- return type;
- }
- }
- renamedSignature.append(getClassBinaryNameFromDescriptor(renamedDescriptor.toString()));
- lastWrittenType = type;
- return type;
- }
-
- private boolean removeWrittenCharacter(Predicate<Character> removeIf) {
- int index = renamedSignature.length() - 1;
- if (index < 0 || !removeIf.test(renamedSignature.charAt(index))) {
- return false;
- }
- renamedSignature.deleteCharAt(index);
- return true;
- }
-
- private void removeWrittenClassCharacter() {
- removeWrittenCharacter(c -> c == 'L');
- }
-
- @Override
- public DexType parsedInnerTypeName(DexType enclosingType, String name) {
- if (enclosingType == null) {
- // We are writing inner type names
- removeWrittenClassCharacter();
- return null;
- }
- assert enclosingType.isClassType();
- String enclosingDescriptor = enclosingType.toDescriptorString();
- DexType type =
- appView
- .dexItemFactory()
- .createType(
- getDescriptorFromClassBinaryName(
- getClassBinaryNameFromDescriptor(enclosingDescriptor)
- + DescriptorUtils.INNER_CLASS_SEPARATOR
- + name));
- type = appView.graphLens().lookupType(type);
- String renamedDescriptor = namingLens.lookupDescriptor(type).toString();
- if (!renamedDescriptor.equals(type.toDescriptorString())) {
- // TODO(b/147504070): If this is a merged class equal to the class context, do not add.
- // Pick the renamed inner class from the fully renamed binary name.
- String fullRenamedBinaryName = getClassBinaryNameFromDescriptor(renamedDescriptor);
- String enclosingRenamedBinaryName =
- getClassBinaryNameFromDescriptor(namingLens.lookupDescriptor(enclosingType).toString());
- int innerClassPos = enclosingRenamedBinaryName.length() + 1;
- if (innerClassPos < fullRenamedBinaryName.length()) {
- renamedSignature.append(fullRenamedBinaryName.substring(innerClassPos));
- } else if (appView.options().keepInnerClassStructure()) {
- reporter.warning(
- new StringDiagnostic(
- "Should have retained InnerClasses attribute of " + type + ".",
- appView.appInfo().originFor(type)));
- renamedSignature.append(name);
- }
- } else {
- // Did not find the class - keep the inner class name as is.
- // TODO(b/110085899): Warn about missing classes in signatures?
- renamedSignature.append(name);
- }
- return type;
- }
-
- @Override
- public void start() {
- renamedSignature = new StringBuilder();
- }
-
- @Override
- public void stop() {
- // nothing to do
- }
- }
}
diff --git a/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java b/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java
index d36cbee..5d2fef0 100644
--- a/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java
@@ -60,7 +60,7 @@
assert clazz != null;
DexEncodedMethod actualEncodedTarget = clazz.lookupVirtualMethod(signatureInCurrentWorld);
assert actualEncodedTarget != null;
- assert actualEncodedTarget.isPublicized();
+ assert actualEncodedTarget.isPublic();
return true;
}
diff --git a/src/main/java/com/android/tools/r8/retrace/DirectClassNameMapperProguardMapProducer.java b/src/main/java/com/android/tools/r8/retrace/DirectClassNameMapperProguardMapProducer.java
new file mode 100644
index 0000000..8efe05a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/DirectClassNameMapperProguardMapProducer.java
@@ -0,0 +1,18 @@
+// 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.retrace;
+
+import com.android.tools.r8.naming.ClassNameMapper;
+import com.android.tools.r8.retrace.RetraceCommand.ProguardMapProducer;
+
+public interface DirectClassNameMapperProguardMapProducer extends ProguardMapProducer {
+
+ ClassNameMapper getClassNameMapper();
+
+ @Override
+ default String get() {
+ throw new RuntimeException("Should not be called for DirectClassNameMapperProguardMapProducer");
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/retrace/InvalidMappingFileException.java b/src/main/java/com/android/tools/r8/retrace/InvalidMappingFileException.java
new file mode 100644
index 0000000..86e81e1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/retrace/InvalidMappingFileException.java
@@ -0,0 +1,20 @@
+// 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.retrace;
+
+import com.android.tools.r8.Keep;
+
+@Keep
+public final class InvalidMappingFileException extends RuntimeException {
+
+ public InvalidMappingFileException(Throwable throwable) {
+ super(throwable);
+ }
+
+ @Override
+ public String getMessage() {
+ return "Unable to parse mapping file";
+ }
+}
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 f34a5aa..adf9583 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -10,9 +10,9 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.Keep;
import com.android.tools.r8.Version;
-import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.retrace.RetraceCommand.Builder;
import com.android.tools.r8.retrace.RetraceCommand.ProguardMapProducer;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.OptionsParsing;
import com.android.tools.r8.utils.OptionsParsing.ParseContext;
import com.android.tools.r8.utils.StringDiagnostic;
@@ -120,7 +120,15 @@
new StringDiagnostic(String.format("Could not find mapping file '%s'.", mappingPath)));
throw new RetraceAbortException();
}
- return () -> new String(Files.readAllBytes(path));
+ return () -> {
+ try {
+ return new String(Files.readAllBytes(path));
+ } catch (IOException e) {
+ diagnosticsHandler.error(
+ new StringDiagnostic(String.format("Could not open mapping file '%s'.", mappingPath)));
+ throw new RuntimeException(e);
+ }
+ };
}
private static List<String> getStackTraceFromFile(
@@ -142,11 +150,9 @@
try {
Timing timing = Timing.create("R8 retrace", command.printMemory());
timing.begin("Read proguard map");
- ClassNameMapper classNameMapper =
- ClassNameMapper.mapperFromString(
- command.proguardMapProducer.get(), command.diagnosticsHandler);
+ RetraceApi retracer =
+ Retracer.create(command.proguardMapProducer, command.diagnosticsHandler);
timing.end();
- RetraceApi retracer = Retracer.create(classNameMapper);
RetraceCommandLineResult result;
timing.begin("Parse and Retrace");
if (command.regularExpression != null) {
@@ -171,10 +177,9 @@
if (command.printTimes()) {
timing.report();
}
- } catch (IOException ex) {
- command.diagnosticsHandler.error(
- new StringDiagnostic("Could not open mapping input stream: " + ex.getMessage()));
- throw new RetraceAbortException();
+ } catch (InvalidMappingFileException e) {
+ command.diagnosticsHandler.error(new ExceptionDiagnostic(e));
+ throw e;
}
}
@@ -207,6 +212,7 @@
return;
}
assert Arrays.asList(mappedArgs).contains("--help");
+ System.out.println("Retrace " + Version.getVersionString());
System.out.print(USAGE_MESSAGE);
return;
}
@@ -233,6 +239,7 @@
}
private static List<String> getStackTraceFromStandardInput() {
+ System.out.println("Waiting for stack-trace input...");
Scanner sc = new Scanner(new InputStreamReader(System.in, Charsets.UTF_8));
List<String> readLines = new ArrayList<>();
while (sc.hasNext()) {
@@ -252,9 +259,11 @@
action.run();
} catch (RetraceAbortException e) {
// Detail of the errors were already reported
+ System.err.println(StringUtils.LINE_SEPARATOR + USAGE_MESSAGE + StringUtils.LINE_SEPARATOR);
System.exit(STATUS_ERROR);
} catch (RuntimeException e) {
System.err.println("Retrace failed with an internal error.");
+ System.err.println(StringUtils.LINE_SEPARATOR + USAGE_MESSAGE + StringUtils.LINE_SEPARATOR);
Throwable cause = e.getCause() == null ? e : e.getCause();
cause.printStackTrace();
System.exit(STATUS_ERROR);
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java b/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
index 9b6694f..b6247df 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceCommand.java
@@ -6,7 +6,6 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.Keep;
-import java.io.IOException;
import java.util.List;
import java.util.function.Consumer;
@@ -147,6 +146,6 @@
@Keep
public interface ProguardMapProducer {
- String get() throws IOException;
+ String get();
}
}
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 31619e7..583ca7c 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retracer.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retracer.java
@@ -4,12 +4,14 @@
package com.android.tools.r8.retrace;
+import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.Keep;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.retrace.RetraceCommand.ProguardMapProducer;
/** A default implementation for the retrace api using the ClassNameMapper defined in R8. */
@Keep
@@ -22,8 +24,19 @@
assert classNameMapper != null;
}
- public static RetraceApi create(ClassNameMapper classNameMapper) {
- return new Retracer(classNameMapper);
+ public static RetraceApi create(
+ ProguardMapProducer proguardMapProducer, DiagnosticsHandler diagnosticsHandler) {
+ if (proguardMapProducer instanceof DirectClassNameMapperProguardMapProducer) {
+ return new Retracer(
+ ((DirectClassNameMapperProguardMapProducer) proguardMapProducer).getClassNameMapper());
+ }
+ try {
+ ClassNameMapper classNameMapper =
+ ClassNameMapper.mapperFromString(proguardMapProducer.get(), diagnosticsHandler);
+ return new Retracer(classNameMapper);
+ } catch (Throwable throwable) {
+ throw new InvalidMappingFileException(throwable);
+ }
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index fe2861a..46e4629 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -83,15 +83,10 @@
assert !DexAnnotation.isMemberClassesAnnotation(annotation, dexItemFactory);
assert !DexAnnotation.isEnclosingMethodAnnotation(annotation, dexItemFactory);
assert !DexAnnotation.isEnclosingClassAnnotation(annotation, dexItemFactory);
- // TODO(b/129925954): Signature is being represented as a class attribute.
- assert !holder.isDexClass()
- || !DexAnnotation.isSignatureAnnotation(annotation, dexItemFactory);
+ assert !DexAnnotation.isSignatureAnnotation(annotation, dexItemFactory);
if (config.exceptions && DexAnnotation.isThrowingAnnotation(annotation, dexItemFactory)) {
return true;
}
- if (config.signature && DexAnnotation.isSignatureAnnotation(annotation, dexItemFactory)) {
- return true;
- }
if (DexAnnotation.isSourceDebugExtension(annotation, dexItemFactory)) {
assert holder.isDexClass();
appView.setSourceDebugExtensionForType(
@@ -202,13 +197,16 @@
method.annotations().rewrite(annotation -> rewriteAnnotation(method, annotation)));
method.parameterAnnotationsList =
method.parameterAnnotationsList.keepIf(this::filterParameterAnnotations);
+ if (!keep.signature) {
+ method.clearGenericSignature();
+ }
}
private void processField(DexEncodedField field) {
field.setAnnotations(
field.annotations().rewrite(annotation -> rewriteAnnotation(field, annotation)));
if (!keep.signature) {
- field.clearFieldSignature();
+ field.clearGenericSignature();
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java b/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
index 4786db4..3c83acd 100644
--- a/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/shaking/ClassInitFieldSynthesizer.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.shaking;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
-import static com.android.tools.r8.graph.GenericSignature.NO_FIELD_TYPE_SIGNATURE;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
@@ -16,6 +15,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldAccessFlags;
+import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Visibility;
@@ -81,7 +81,7 @@
new DexEncodedField(
appView.dexItemFactory().createField(clazz.type, clinitField.type, clinitField.name),
accessFlags,
- NO_FIELD_TYPE_SIGNATURE,
+ FieldTypeSignature.noSignature(),
DexAnnotationSet.empty(),
null);
clazz.appendStaticField(encodedClinitField);
diff --git a/src/main/java/com/android/tools/r8/shaking/ClassMergingEnqueuerExtension.java b/src/main/java/com/android/tools/r8/shaking/ClassMergingEnqueuerExtension.java
index a6d7326..6a2151b 100644
--- a/src/main/java/com/android/tools/r8/shaking/ClassMergingEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/shaking/ClassMergingEnqueuerExtension.java
@@ -9,15 +9,19 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.analysis.EnqueuerCheckCastAnalysis;
+import com.android.tools.r8.graph.analysis.EnqueuerExceptionGuardAnalysis;
import com.android.tools.r8.graph.analysis.EnqueuerInstanceOfAnalysis;
import com.google.common.collect.Sets;
import java.util.Set;
public class ClassMergingEnqueuerExtension
- implements EnqueuerInstanceOfAnalysis, EnqueuerCheckCastAnalysis {
+ implements EnqueuerInstanceOfAnalysis,
+ EnqueuerCheckCastAnalysis,
+ EnqueuerExceptionGuardAnalysis {
private final Set<DexType> instanceOfTypes = Sets.newIdentityHashSet();
private final Set<DexType> checkCastTypes = Sets.newIdentityHashSet();
+ private final Set<DexType> exceptionGuardTypes = Sets.newIdentityHashSet();
private final DexItemFactory factory;
public ClassMergingEnqueuerExtension(DexItemFactory factory) {
@@ -34,6 +38,11 @@
instanceOfTypes.add(type.toBaseType(factory));
}
+ @Override
+ public void traceExceptionGuard(DexType guard, ProgramMethod context) {
+ exceptionGuardTypes.add(guard);
+ }
+
public boolean isCheckCastType(DexProgramClass clazz) {
return checkCastTypes.contains(clazz.type);
}
@@ -42,7 +51,18 @@
return instanceOfTypes.contains(clazz.type);
}
+ public boolean isExceptionGuardType(DexProgramClass clazz) {
+ return exceptionGuardTypes.contains(clazz.type);
+ }
+
+ public boolean isRuntimeCheckType(DexProgramClass clazz) {
+ return isInstanceOfType(clazz) || isCheckCastType(clazz) || isExceptionGuardType(clazz);
+ }
+
public void attach(Enqueuer enqueuer) {
- enqueuer.registerInstanceOfAnalysis(this).registerCheckCastAnalysis(this);
+ enqueuer
+ .registerInstanceOfAnalysis(this)
+ .registerCheckCastAnalysis(this)
+ .registerExceptionGuardAnalysis(this);
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
index 34fb593..b67d90d 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -134,6 +134,11 @@
}
@Override
+ public void registerExceptionGuard(DexType guard) {
+ enqueuer.traceExceptionGuard(guard, context);
+ }
+
+ @Override
public void registerMethodHandle(DexMethodHandle methodHandle, MethodHandleUse use) {
super.registerMethodHandle(methodHandle, use);
enqueuer.traceMethodHandle(methodHandle, use, context);
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 d1200b4..ec4e675 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -65,6 +65,7 @@
import com.android.tools.r8.graph.analysis.DesugaredLibraryConversionWrapperAnalysis;
import com.android.tools.r8.graph.analysis.EnqueuerAnalysis;
import com.android.tools.r8.graph.analysis.EnqueuerCheckCastAnalysis;
+import com.android.tools.r8.graph.analysis.EnqueuerExceptionGuardAnalysis;
import com.android.tools.r8.graph.analysis.EnqueuerInstanceOfAnalysis;
import com.android.tools.r8.graph.analysis.EnqueuerInvokeAnalysis;
import com.android.tools.r8.ir.analysis.proto.ProtoEnqueuerUseRegistry;
@@ -170,6 +171,10 @@
public boolean isTracingMainDex() {
return this == MAIN_DEX_TRACING;
}
+
+ public boolean isWhyAreYouKeeping() {
+ return this == WHY_ARE_YOU_KEEPING;
+ }
}
private final boolean forceProguardCompatibility;
@@ -178,6 +183,7 @@
private Set<EnqueuerAnalysis> analyses = Sets.newIdentityHashSet();
private Set<EnqueuerInvokeAnalysis> invokeAnalyses = Sets.newIdentityHashSet();
private Set<EnqueuerInstanceOfAnalysis> instanceOfAnalyses = Sets.newIdentityHashSet();
+ private Set<EnqueuerExceptionGuardAnalysis> exceptionGuardAnalyses = Sets.newIdentityHashSet();
private Set<EnqueuerCheckCastAnalysis> checkCastAnalyses = Sets.newIdentityHashSet();
// Don't hold a direct pointer to app info (use appView).
@@ -263,8 +269,6 @@
* Set of direct methods that are the immediate target of an invoke-dynamic.
*/
private final Set<DexMethod> methodsTargetedByInvokeDynamic = Sets.newIdentityHashSet();
- /** Set of direct lambda implementation methods that have been desugared, thus they may move. */
- private final Set<DexMethod> desugaredLambdaImplementationMethods = Sets.newIdentityHashSet();
/**
* Set of virtual methods that are the immediate target of an invoke-direct.
*/
@@ -438,6 +442,11 @@
return this;
}
+ public Enqueuer registerExceptionGuardAnalysis(EnqueuerExceptionGuardAnalysis analysis) {
+ exceptionGuardAnalyses.add(analysis);
+ return this;
+ }
+
public void setAnnotationRemoverBuilder(AnnotationRemover.Builder annotationRemoverBuilder) {
this.annotationRemoverBuilder = annotationRemoverBuilder;
}
@@ -865,9 +874,6 @@
classesWithSerializableLambdas.add(context.getHolder());
}
}
- if (descriptor.delegatesToLambdaImplMethod()) {
- desugaredLambdaImplementationMethods.add(descriptor.implHandle.asMethod());
- }
} else {
markLambdaAsInstantiated(descriptor, context);
transitionMethodsForInstantiatedLambda(descriptor);
@@ -1032,6 +1038,11 @@
traceTypeReference(type, currentMethod);
}
+ void traceExceptionGuard(DexType guard, ProgramMethod currentMethod) {
+ exceptionGuardAnalyses.forEach(analysis -> analysis.traceExceptionGuard(guard, currentMethod));
+ traceTypeReference(guard, currentMethod);
+ }
+
void traceInvokeDirect(DexMethod invokedMethod, ProgramMethod context) {
boolean skipTracing =
registerDeferredActionForDeadProtoBuilder(
@@ -2728,6 +2739,11 @@
finalizeLibraryMethodOverrideInformation();
analyses.forEach(analyses -> analyses.done(this));
assert verifyKeptGraph();
+ if (mode.isWhyAreYouKeeping()) {
+ // For why are you keeping the information is reported through the kept graph callbacks and
+ // no AppInfo is returned.
+ return null;
+ }
AppInfoWithLiveness appInfoWithLiveness = createAppInfo(appInfo);
if (options.testing.enqueuerInspector != null) {
options.testing.enqueuerInspector.accept(appInfoWithLiveness, mode);
@@ -3088,6 +3104,16 @@
liveMethods.add(accessor, graphReporter.fakeReportShouldNotBeUsed());
}
});
+ unpinLambdaMethods();
+ }
+
+ // TODO(b/157700141): Determine if this is the right way to allow modification of pinned lambdas.
+ private void unpinLambdaMethods() {
+ assert lambdaRewriter != null;
+ for (DexMethod method : lambdaRewriter.getForcefullyMovedMethods()) {
+ keepInfo.unsafeUnpinMethod(method);
+ rootSet.prune(method);
+ }
}
private boolean verifyMissingTypes() {
@@ -3296,7 +3322,6 @@
} finally {
timing.end();
}
- unpinLambdaMethods();
}
private long getNumberOfLiveItems() {
@@ -3392,17 +3417,6 @@
action.getAction().accept(builder);
}
- // TODO(b/157700141): Determine if this is the right way to avoid modification of pinned lambdas.
- private void unpinLambdaMethods() {
- assert desugaredLambdaImplementationMethods.isEmpty()
- || options.desugarState == DesugarState.ON;
- for (DexMethod method : desugaredLambdaImplementationMethods) {
- keepInfo.unsafeUnpinMethod(method);
- rootSet.prune(method);
- }
- desugaredLambdaImplementationMethods.clear();
- }
-
void retainAnnotationForFinalTreeShaking(List<DexAnnotation> annotations) {
assert mode.isInitialTreeShaking();
if (annotationRemoverBuilder != null) {
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
index 0881bef..d0834d5 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
@@ -383,25 +383,6 @@
joinMethod(method, KeepInfo.Joiner::pin);
}
- public void unsafeAllowMinificationOfMethod(ProgramMethod method) {
- KeepMethodInfo info = keepMethodInfo.get(method.getReference());
- if (info != null && !info.internalIsMinificationAllowed()) {
- keepMethodInfo.put(method.getReference(), info.builder().allowMinification().build());
- }
- }
-
- // Unpinning a method represents a non-monotonic change to the keep info of that item.
- // This is generally unsound as it requires additional analysis to determine that a method that
- // was pinned no longer is. A known sound example is the enum analysis that will identify
- // non-escaping enums on enum types that are not pinned, thus their methods do not need to be
- // retained even if a rule has marked them as conditionally pinned.
- public void unsafeUnpinMethod(ProgramMethod method) {
- // This asserts that the holder is not pinned as some analysis must have established that the
- // type is not "present" and thus the method need not be pinned.
- assert !getClassInfo(method.getHolder()).isPinned();
- unsafeUnpinMethod(method.getReference());
- }
-
// TODO(b/157700141): Avoid pinning/unpinning references.
@Deprecated
public void unsafeUnpinMethod(DexMethod method) {
@@ -455,22 +436,6 @@
joinField(field, KeepInfo.Joiner::pin);
}
- public void unsafeAllowMinificationOfField(ProgramField field) {
- assert !getClassInfo(field.getHolder()).isPinned();
- KeepFieldInfo info = keepFieldInfo.get(field.getReference());
- if (info != null && !info.internalIsMinificationAllowed()) {
- keepFieldInfo.put(field.getReference(), info.builder().allowAccessModification().build());
- }
- }
-
- public void unsafeUnpinField(ProgramField field) {
- assert !getClassInfo(field.getHolder()).isPinned();
- KeepFieldInfo info = this.keepFieldInfo.get(field.getReference());
- if (info != null && info.isPinned()) {
- keepFieldInfo.put(field.getReference(), info.builder().unpin().build());
- }
- }
-
@Override
public KeepInfoCollection mutate(Consumer<MutableKeepInfoCollection> mutator) {
mutator.accept(this);
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 3f8077f..9462690 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -27,6 +27,7 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
@@ -1257,6 +1258,7 @@
new DexEncodedMethod(
newMethod,
accessFlags,
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
code,
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
index 80c47f2..4f289c7 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
@@ -106,7 +106,7 @@
nestMembers,
enclosingMembers,
innerClasses,
- ClassSignature.NO_CLASS_SIGNATURE,
+ ClassSignature.noSignature(),
DexAnnotationSet.empty(),
staticFields,
instanceFields,
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
index 7cc0c32..ee17db8 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
@@ -4,14 +4,13 @@
package com.android.tools.r8.synthesis;
import com.android.tools.r8.graph.Code;
-import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
-import java.util.List;
public class SyntheticMethodBuilder {
@@ -24,7 +23,6 @@
private DexProto proto = null;
private SyntheticCodeGenerator codeGenerator = null;
private MethodAccessFlags accessFlags = null;
- private List<DexAnnotation> annotations = null;
SyntheticMethodBuilder(SyntheticClassBuilder parent, String name) {
this.parent = parent;
@@ -53,8 +51,9 @@
new DexEncodedMethod(
methodSignature,
getAccessFlags(),
- getAnnotations(),
- getParameterAnnotations(),
+ MethodTypeSignature.noSignature(),
+ DexAnnotationSet.empty(),
+ ParameterAnnotationsList.empty(),
getCodeObject(methodSignature),
isCompilerSynthesized);
assert isValidSyntheticMethod(method);
@@ -83,16 +82,6 @@
return accessFlags;
}
- private DexAnnotationSet getAnnotations() {
- return annotations == null
- ? DexAnnotationSet.empty()
- : new DexAnnotationSet(annotations.toArray(DexAnnotation.EMPTY_ARRAY));
- }
-
- private ParameterAnnotationsList getParameterAnnotations() {
- return ParameterAnnotationsList.empty();
- }
-
private Code getCodeObject(DexMethod methodSignature) {
return codeGenerator.generate(methodSignature);
}
diff --git a/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java b/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
index c1c7d5a..8d66524 100644
--- a/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ConsumerUtils.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.utils;
import java.util.Set;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class ConsumerUtils {
@@ -21,6 +22,10 @@
return ignore -> {};
}
+ public static <S, T> BiConsumer<S, T> emptyBiConsumer() {
+ return (s, t) -> {};
+ }
+
public static <T> ThrowingConsumer<T, RuntimeException> emptyThrowingConsumer() {
return ignore -> {};
}
diff --git a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
index a4cef91..bfd8334 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -339,6 +339,16 @@
}
/**
+ * Convert class to a binary name.
+ *
+ * @param clazz a java.lang.Class reference
+ * @return class binary name i.e. "java/lang/Object"
+ */
+ public static String getClassBinaryName(Class<?> clazz) {
+ return getBinaryNameFromJavaType(clazz.getTypeName());
+ }
+
+ /**
* Get package java name from a class descriptor.
*
* @param descriptor a class descriptor i.e. "Ljava/lang/Object;"
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 a429fb1..8bc1a55 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -33,13 +33,19 @@
import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
+import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
+import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.conversion.MethodProcessingId;
import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
import com.android.tools.r8.ir.optimize.Inliner;
+import com.android.tools.r8.ir.optimize.lambda.kotlin.KotlinLambdaGroupIdFactory;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.references.Reference;
@@ -1214,6 +1220,9 @@
public BiConsumer<AppInfoWithLiveness, Enqueuer.Mode> enqueuerInspector = null;
+ public Function<DexProgramClass, KotlinLambdaGroupIdFactory> kotlinLambdaMergerFactoryForClass =
+ KotlinLambdaGroupIdFactory::getFactoryForClass;
+
public BiConsumer<ProgramMethod, MethodProcessingId> methodProcessingIdConsumer = null;
public Function<AppView<AppInfoWithLiveness>, RepackagingConfiguration>
@@ -1222,6 +1231,18 @@
new DefaultRepackagingConfiguration(
appView.dexItemFactory(), appView.options().getProguardConfiguration());
+ public BiConsumer<DexItemFactory, HorizontallyMergedClasses> horizontallyMergedClassesConsumer =
+ ConsumerUtils.emptyBiConsumer();
+
+ public BiConsumer<DexItemFactory, HorizontallyMergedLambdaClasses>
+ horizontallyMergedLambdaClassesConsumer = ConsumerUtils.emptyBiConsumer();
+
+ public BiConsumer<DexItemFactory, EnumValueInfoMapCollection> unboxedEnumsConsumer =
+ ConsumerUtils.emptyBiConsumer();
+
+ public BiConsumer<DexItemFactory, VerticallyMergedClasses> verticallyMergedClassesConsumer =
+ ConsumerUtils.emptyBiConsumer();
+
public Consumer<Deque<SortedProgramMethodSet>> waveModifier = waves -> {};
/**
@@ -1274,6 +1295,7 @@
System.getProperty("com.android.tools.r8.trackDesugaredAPIConversions") != null;
public boolean forceLibBackportsInL8CfToCf = false;
public boolean enumUnboxingRewriteJavaCGeneratedMethod = false;
+ // TODO(b/154793333): Enable assertions always when resolved.
public boolean assertConsistentRenamingOfSignature = false;
public boolean allowStaticInterfaceMethodsForPreNApiLevel = false;
public int verificationSizeLimitInBytesOverride = -1;
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 af3208f..15a8198 100644
--- a/src/main/java/com/android/tools/r8/utils/IterableUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
@@ -49,4 +49,8 @@
iterable.forEach(result::add);
return result;
}
+
+ public static <T> boolean isEmpty(Iterable<T> iterable) {
+ return !iterable.iterator().hasNext();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidPTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidPTest.java
index 8edbd69..9ac01a0 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidPTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidPTest.java
@@ -51,7 +51,6 @@
@Test
public void invokeCustomWithShrinking() throws Throwable {
- expectThrowsWithHorizontalClassMerging();
test("invokecustom-with-shrinking", "invokecustom", "InvokeCustom")
.withMinApiLevel(AndroidApiLevel.P.getLevel())
.withBuilderTransformation(builder ->
diff --git a/src/test/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
index 85c6b96..9b63b86 100644
--- a/src/test/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
@@ -173,6 +173,12 @@
return proguardMap;
}
+ public Path writeProguardMap() throws IOException {
+ Path file = state.getNewTempFolder().resolve("out.zip");
+ writeProguardMap(file);
+ return file;
+ }
+
public R8TestCompileResult writeProguardMap(Path path) throws IOException {
FileUtils.writeTextFile(path, getProguardMap());
return self();
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index d7bebd8..a92fefd 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -1648,6 +1648,10 @@
return path;
}
+ public static DexType toDexType(Class<?> clazz, DexItemFactory dexItemFactory) {
+ return dexItemFactory.createType(descriptor(clazz));
+ }
+
public static String binaryName(Class<?> clazz) {
return DescriptorUtils.getBinaryNameFromJavaType(typeName(clazz));
}
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 3e5ba5f..4149659 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -17,6 +17,10 @@
import com.android.tools.r8.utils.ForwardingOutputStream;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ThrowingOutputStream;
+import com.android.tools.r8.utils.codeinspector.EnumUnboxingInspector;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedLambdaClassesInspector;
+import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
import com.google.common.base.Suppliers;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -103,6 +107,47 @@
});
}
+ public T addEnumUnboxingInspector(Consumer<EnumUnboxingInspector> inspector) {
+ return addOptionsModification(
+ options ->
+ options.testing.unboxedEnumsConsumer =
+ ((dexItemFactory, unboxedEnums) ->
+ inspector.accept(new EnumUnboxingInspector(dexItemFactory, unboxedEnums))));
+ }
+
+ public T addHorizontallyMergedClassesInspector(
+ Consumer<HorizontallyMergedClassesInspector> inspector) {
+ return addOptionsModification(
+ options ->
+ options.testing.horizontallyMergedClassesConsumer =
+ ((dexItemFactory, horizontallyMergedClasses) ->
+ inspector.accept(
+ new HorizontallyMergedClassesInspector(
+ dexItemFactory, horizontallyMergedClasses))));
+ }
+
+ public T addHorizontallyMergedLambdaClassesInspector(
+ Consumer<HorizontallyMergedLambdaClassesInspector> inspector) {
+ return addOptionsModification(
+ options ->
+ options.testing.horizontallyMergedLambdaClassesConsumer =
+ ((dexItemFactory, horizontallyMergedLambdaClasses) ->
+ inspector.accept(
+ new HorizontallyMergedLambdaClassesInspector(
+ dexItemFactory, horizontallyMergedLambdaClasses))));
+ }
+
+ public T addVerticallyMergedClassesInspector(
+ Consumer<VerticallyMergedClassesInspector> inspector) {
+ return addOptionsModification(
+ options ->
+ options.testing.verticallyMergedClassesConsumer =
+ ((dexItemFactory, verticallyMergedClasses) ->
+ inspector.accept(
+ new VerticallyMergedClassesInspector(
+ dexItemFactory, verticallyMergedClasses))));
+ }
+
public CR compile() throws CompilationFailedException {
AndroidAppConsumers sink = new AndroidAppConsumers();
builder.setProgramConsumer(sink.wrapProgramConsumer(programConsumer));
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 9740354..7e1a627 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -159,7 +159,9 @@
private static final String PROGUARD5_2_1 = "third_party/proguard/proguard5.2.1/bin/proguard";
private static final String PROGUARD6_0_1 = "third_party/proguard/proguard6.0.1/bin/proguard";
private static final String PROGUARD = PROGUARD5_2_1;
- public static final String JACOCO_AGENT = "third_party/jacoco/org.jacoco.agent-0.8.2-runtime.jar";
+ public static final String JACOCO_AGENT =
+ "third_party/jacoco/0.8.2/org.jacoco.agent-0.8.2-runtime.jar";
+ public static final String JACOCO_CLI = "third_party/jacoco/0.8.6/lib/jacococli.jar";
public static final String PROGUARD_SETTINGS_FOR_INTERNAL_APPS = "third_party/proguardsettings/";
private static final String RETRACE6_0_1 = "third_party/proguard/proguard6.0.1/bin/retrace";
@@ -264,6 +266,10 @@
return isOlderThanOrEqual(Version.V4_4_4);
}
+ public boolean isDefault() {
+ return this == DEFAULT;
+ }
+
public boolean isLatest() {
return this == V10_0_0;
}
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java b/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
index 09bf5e1..76ebd58 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/B77836766.java
@@ -64,7 +64,6 @@
*/
@Test
public void test_bridgeTargetInBase_differentBridges() throws Exception {
- expectThrowsWithHorizontalClassMerging();
JasminBuilder jasminBuilder = new JasminBuilder();
ClassBuilder absCls = jasminBuilder.addClass("AbsCls");
@@ -186,7 +185,6 @@
*/
@Test
public void test_bridgeTargetInBase_bridgeAndNonBridge() throws Exception {
- expectThrowsWithHorizontalClassMerging();
JasminBuilder jasminBuilder = new JasminBuilder();
ClassBuilder baseCls = jasminBuilder.addClass("Base");
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/B162969014.java b/src/test/java/com/android/tools/r8/checkdiscarded/B162969014.java
index 6eb081f..f19d1c1 100644
--- a/src/test/java/com/android/tools/r8/checkdiscarded/B162969014.java
+++ b/src/test/java/com/android/tools/r8/checkdiscarded/B162969014.java
@@ -4,10 +4,11 @@
package com.android.tools.r8.checkdiscarded;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
+import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.AssumeNoSideEffects;
@@ -54,10 +55,17 @@
.enableAssumeNoSideEffectsAnnotations()
.enableInliningAnnotations()
.setMinApi(parameters.getApiLevel())
- .compile();
+ .compileWithExpectedDiagnostics(
+ diagnostics -> {
+ if (checkLogIsDiscarded) {
+ diagnostics.assertErrorsMatch(
+ diagnosticMessage(containsString("Discard checks failed.")));
+ } else {
+ diagnostics.assertNoErrors();
+ }
+ });
} catch (CompilationFailedException e) {
assertTrue(checkLogIsDiscarded);
- assertEquals(e.getCause().getMessage(), "Discard checks failed.");
return;
}
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardModifyDiagnosticsLevelTest.java b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardModifyDiagnosticsLevelTest.java
new file mode 100644
index 0000000..bac9468
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardModifyDiagnosticsLevelTest.java
@@ -0,0 +1,106 @@
+// Copyright (c) 2017, 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.checkdiscarded;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.hamcrest.core.StringContains.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.DiagnosticsLevel;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.checkdiscarded.testclasses.Main;
+import com.android.tools.r8.checkdiscarded.testclasses.UnusedClass;
+import com.android.tools.r8.checkdiscarded.testclasses.UsedClass;
+import com.android.tools.r8.errors.CheckDiscardDiagnostic;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import java.util.Collection;
+import java.util.List;
+import org.hamcrest.Matcher;
+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 CheckDiscardModifyDiagnosticsLevelTest extends TestBase {
+
+ @Parameters(name = "{0}, {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withNoneRuntime().build(), DiagnosticsLevel.values());
+ }
+
+ private final DiagnosticsLevel mappedLevel;
+
+ public CheckDiscardModifyDiagnosticsLevelTest(TestParameters parameters, DiagnosticsLevel level) {
+ parameters.assertNoneRuntime();
+ this.mappedLevel = level;
+ }
+
+ private void noInlining(InternalOptions options) {
+ options.enableInlining = false;
+ }
+
+ private Matcher<Diagnostic> discardCheckFailedMatcher() {
+ return diagnosticMessage(
+ allOf(
+ startsWith("Discard checks failed"),
+ containsString("UsedClass was not discarded"),
+ containsString("is instantiated in")));
+ }
+
+ private Collection<Matcher<Diagnostic>> errorMatchers() {
+ return mappedLevel == DiagnosticsLevel.ERROR
+ ? ImmutableList.of(discardCheckFailedMatcher())
+ : ImmutableList.of();
+ }
+
+ private Collection<Matcher<Diagnostic>> warningMatchers() {
+ return mappedLevel == DiagnosticsLevel.WARNING
+ ? ImmutableList.of(discardCheckFailedMatcher())
+ : ImmutableList.of();
+ }
+
+ private Collection<Matcher<Diagnostic>> infoMatchers() {
+ return mappedLevel == DiagnosticsLevel.INFO
+ ? ImmutableList.of(discardCheckFailedMatcher())
+ : ImmutableList.of();
+ }
+
+ @Test
+ public void dontFailCompilationOnCheckDiscardedFailure() {
+ try {
+ testForR8(Backend.DEX)
+ .addProgramClasses(UnusedClass.class, UsedClass.class, Main.class)
+ .addKeepMainRule(Main.class)
+ .addKeepRules("-checkdiscard class " + UsedClass.class.getTypeName())
+ .addOptionsModification(this::noInlining)
+ .setDiagnosticsLevelModifier(
+ (level, diagnostic) -> {
+ if (diagnostic instanceof CheckDiscardDiagnostic) {
+ return mappedLevel;
+ } else {
+ return level;
+ }
+ })
+ .allowDiagnosticMessages()
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics
+ .assertErrorsMatch(errorMatchers())
+ .assertWarningsMatch(warningMatchers())
+ .assertInfosMatch(infoMatchers()));
+ assertTrue(mappedLevel == DiagnosticsLevel.INFO || mappedLevel == DiagnosticsLevel.WARNING);
+ } catch (CompilationFailedException e) {
+ assertEquals(mappedLevel, DiagnosticsLevel.ERROR);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java
index df38cb2..61f8917 100644
--- a/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java
+++ b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java
@@ -23,7 +23,6 @@
import com.android.tools.r8.checkdiscarded.testclasses.WillStay;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.function.Consumer;
import org.junit.Test;
@@ -90,16 +89,16 @@
Consumer<TestDiagnosticMessages> check =
diagnostics ->
diagnostics
+ .assertNoInfos()
.assertNoWarnings()
- .assertErrorsMatch(diagnosticMessage(containsString("Discard checks failed")))
- .assertInfosMatch(
- ImmutableList.of(
+ .assertErrorsMatch(
+ diagnosticMessage(
allOf(
- diagnosticMessage(containsString("UsedClass was not discarded")),
- diagnosticMessage(containsString("is instantiated in"))),
- allOf(
- diagnosticMessage(containsString("Main was not discarded")),
- diagnosticMessage(containsString("is referenced in keep rule")))));
+ containsString("Discard checks failed"),
+ containsString("UsedClass was not discarded"),
+ containsString("is instantiated in"),
+ containsString("Main was not discarded"),
+ containsString("is referenced in keep rule"))));
compile(WillStay.class, false, check);
}
@@ -113,12 +112,14 @@
Consumer<TestDiagnosticMessages> check =
diagnostics ->
diagnostics
+ .assertNoInfos()
.assertNoWarnings()
- .assertErrorsMatch(diagnosticMessage(containsString("Discard checks failed")))
- .assertInfosMatch(
- allOf(
- diagnosticMessage(containsString("was not discarded")),
- diagnosticMessage(containsString("is invoked from"))));
+ .assertErrorsMatch(
+ diagnosticMessage(
+ allOf(
+ containsString("Discard checks failed"),
+ containsString("was not discarded"),
+ containsString("is invoked from"))));
compile(WillStay.class, true, check);
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/CompatKeepConstructorLiveTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/CompatKeepConstructorLiveTest.java
new file mode 100644
index 0000000..9ccc2d4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/CompatKeepConstructorLiveTest.java
@@ -0,0 +1,62 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import org.junit.Test;
+
+public class CompatKeepConstructorLiveTest extends HorizontalClassMergingTestBase {
+ public CompatKeepConstructorLiveTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8Compat(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("b: main", "true")
+ .inspect(
+ codeInspector -> {
+ ClassSubject aClassSubject = codeInspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ assertThat(aClassSubject.init(), isPresent());
+
+ assertThat(codeInspector.clazz(A.class), isPresent());
+ assertThat(
+ codeInspector.clazz(B.class), notIf(isPresent(), enableHorizontalClassMerging));
+ });
+ }
+
+ @NeverClassInline
+ public static class A {}
+
+ @NeverClassInline
+ public static class B {
+ public B(String v) {
+ System.out.println("b: " + v);
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) throws Exception {
+ new B("main");
+ System.out.println(A.class.toString().length() > 0);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/DistinguishExceptionClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/DistinguishExceptionClassesTest.java
new file mode 100644
index 0000000..c751735
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/DistinguishExceptionClassesTest.java
@@ -0,0 +1,57 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+
+public class DistinguishExceptionClassesTest extends HorizontalClassMergingTestBase {
+ public DistinguishExceptionClassesTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("test success")
+ .inspect(
+ codeInspector -> {
+ assertThat(codeInspector.clazz(Exception1.class), isPresent());
+ assertThat(codeInspector.clazz(Exception2.class), isPresent());
+ });
+ }
+
+ public static class Exception1 extends Exception {}
+
+ public static class Exception2 extends Exception {}
+
+ public static class Main {
+ public static void main(String[] args) {
+ try {
+ try {
+ if (System.currentTimeMillis() > 0) {
+ throw new Exception2();
+ } else {
+ throw new Exception1();
+ }
+ } catch (Exception1 ex) {
+ System.out.println("test failed");
+ }
+ } catch (Exception2 ex) {
+ System.out.println("test success");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergePackagePrivateWithPublicClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergePackagePrivateWithPublicClassTest.java
new file mode 100644
index 0000000..a1c8faa
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergePackagePrivateWithPublicClassTest.java
@@ -0,0 +1,51 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.classmerging.horizontal.testclasses.PackagePrivateClassRunner;
+import org.junit.Test;
+
+public class MergePackagePrivateWithPublicClassTest extends HorizontalClassMergingTestBase {
+
+ public MergePackagePrivateWithPublicClassTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addProgramClasses(
+ PackagePrivateClassRunner.class, PackagePrivateClassRunner.getPrivateClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("package private")
+ .inspect(
+ codeInspector -> {
+ assertThat(codeInspector.clazz(PackagePrivateClassRunner.class), isPresent());
+ assertThat(
+ codeInspector.clazz(PackagePrivateClassRunner.getPrivateClass()),
+ notIf(isPresent(), enableHorizontalClassMerging));
+ });
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ new PackagePrivateClassRunner().run();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java
new file mode 100644
index 0000000..91dc3b1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java
@@ -0,0 +1,69 @@
+// 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.classmerging.horizontal;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.classmerging.horizontal.testclasses.NonReboundFieldAccessOnMergedClassTestClasses;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class NonReboundFieldAccessOnMergedClassTest extends HorizontalClassMergingTestBase {
+
+ public NonReboundFieldAccessOnMergedClassTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void test() throws Exception {
+ if (enableHorizontalClassMerging) {
+ thrown.expect(Throwable.class);
+ }
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addInnerClasses(NonReboundFieldAccessOnMergedClassTestClasses.class)
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .addHorizontallyMergedLambdaClassesInspector(
+ inspector -> {
+ if (enableHorizontalClassMerging) {
+ inspector.assertMerged(C.class, D.class);
+ }
+ })
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.print(new C("Hello").greeting);
+ System.out.println(new D(" world!").greeting);
+ }
+ }
+
+ @NeverClassInline
+ static class C extends NonReboundFieldAccessOnMergedClassTestClasses.B {
+
+ public C(String greeting) {
+ super(greeting);
+ }
+ }
+
+ @NeverClassInline
+ static class D extends NonReboundFieldAccessOnMergedClassTestClasses.B {
+
+ public D(String greeting) {
+ super(greeting);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
new file mode 100644
index 0000000..3581801
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
@@ -0,0 +1,81 @@
+// 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.classmerging.horizontal;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.classmerging.horizontal.NonReboundFieldAccessOnMergedClassTest.C;
+import com.android.tools.r8.classmerging.horizontal.testclasses.NonReboundFieldAccessWithMergedTypeTestClasses;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class NonReboundFieldAccessWithMergedTypeTest extends HorizontalClassMergingTestBase {
+
+ public NonReboundFieldAccessWithMergedTypeTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void test() throws Exception {
+ if (enableHorizontalClassMerging) {
+ thrown.expect(Throwable.class);
+ }
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addInnerClasses(NonReboundFieldAccessWithMergedTypeTestClasses.class)
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .addHorizontallyMergedClassesInspector(
+ inspector -> {
+ if (enableHorizontalClassMerging) {
+ inspector.assertMerged(HelloGreeting.class, WorldGreeting.class);
+ inspector.assertMergedIntoDifferentType(WorldGreeting.class);
+ }
+ })
+ .enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.print(new HelloGreeting());
+ System.out.println(new C(new WorldGreeting()).greeting);
+ }
+ }
+
+ @NeverClassInline
+ static class C extends NonReboundFieldAccessWithMergedTypeTestClasses.B {
+
+ public C(WorldGreeting greeting) {
+ super(greeting);
+ }
+ }
+
+ public static class HelloGreeting {
+
+ @Override
+ public String toString() {
+ return "Hello";
+ }
+ }
+
+ public static class WorldGreeting {
+
+ @Override
+ public String toString() {
+ return " world!";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassMemberReferenceTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassMemberReferenceTest.java
new file mode 100644
index 0000000..4a22fe3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassMemberReferenceTest.java
@@ -0,0 +1,161 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.R8TestRunResult;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import org.junit.Test;
+
+public class PinnedClassMemberReferenceTest extends HorizontalClassMergingTestBase {
+ public PinnedClassMemberReferenceTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ private R8FullTestBuilder testCommon() throws Exception {
+ return testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .noMinification()
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel());
+ }
+
+ private R8TestRunResult runAndAssertOutput(R8FullTestBuilder builder) throws Exception {
+ return builder
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(
+ "a", "b", "foo a: bar", "foo b: baz", "fields a: bar", "fields b: baz");
+ }
+
+ @Test
+ public void testWithoutKeepRules() throws Exception {
+ // This is just a small check ensure that without the keep rules the classes are merged.
+ assumeTrue(enableHorizontalClassMerging);
+ assumeTrue(parameters.isCfRuntime());
+
+ runAndAssertOutput(testCommon())
+ .inspect(
+ codeInspector -> {
+ ClassSubject aClassSubject = codeInspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ assertThat(codeInspector.clazz(B.class), not(isPresent()));
+
+ ClassSubject cClassSubject = codeInspector.clazz(C.class);
+ assertThat(cClassSubject, isPresent());
+ assertThat(cClassSubject.field(aClassSubject.getFinalName(), "a"), isPresent());
+ assertThat(cClassSubject.field(aClassSubject.getFinalName(), "b"), isPresent());
+
+ assertThat(
+ cClassSubject.method("void", "foo", aClassSubject.getFinalName()), isPresent());
+ });
+ }
+
+ @Test
+ public void testWithKeepRules() throws Exception {
+ runAndAssertOutput(
+ testCommon()
+ .addKeepRules(
+ "-keepclassmembers class " + C.class.getTypeName() + " { ",
+ " " + A.class.getTypeName() + " a;",
+ " " + C.class.getTypeName() + " c;",
+ " void foo(" + A.class.getTypeName() + ");",
+ " void foo(" + B.class.getTypeName() + ");",
+ "}"))
+ .inspect(
+ codeInspector -> {
+ ClassSubject aClassSubject = codeInspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ ClassSubject bClassSubject = codeInspector.clazz(B.class);
+ assertThat(bClassSubject, isPresent());
+
+ ClassSubject cClassSubject = codeInspector.clazz(C.class);
+ assertThat(cClassSubject, isPresent());
+ assertThat(cClassSubject.field(aClassSubject.getFinalName(), "a"), isPresent());
+ assertThat(cClassSubject.field(bClassSubject.getFinalName(), "b"), isPresent());
+
+ assertThat(
+ cClassSubject.method("void", "foo", aClassSubject.getFinalName()), isPresent());
+ assertThat(
+ cClassSubject.method("void", "foo", bClassSubject.getFinalName()), isPresent());
+ });
+ }
+
+ @NeverClassInline
+ public static class A {
+ public A() {
+ System.out.println("a");
+ }
+
+ @NeverInline
+ public String bar() {
+ return "bar";
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ public B() {
+ System.out.println("b");
+ }
+
+ @NeverInline
+ public String baz() {
+ return "baz";
+ }
+ }
+
+ @NeverClassInline
+ public static class C {
+ A a;
+ B b;
+
+ public C(A a, B b) {
+ this.a = a;
+ this.b = b;
+ }
+
+ @NeverInline
+ public void foo(A a2) {
+ System.out.println("foo a: " + a2.bar());
+ }
+
+ @NeverInline
+ public void foo(B b) {
+ System.out.println("foo b: " + b.baz());
+ }
+
+ @NeverInline
+ public void fields() {
+ System.out.println("fields a: " + a.bar());
+ System.out.println("fields b: " + b.baz());
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ A a = new A();
+ B b = new B();
+ C c = new C(a, b);
+ c.foo(a);
+ c.foo(b);
+ c.fields();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticalMergingPreoptimizedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticalMergingPreoptimizedTest.java
new file mode 100644
index 0000000..7731d5e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticalMergingPreoptimizedTest.java
@@ -0,0 +1,99 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsNot.not;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+
+public class VerticalMergingPreoptimizedTest extends HorizontalClassMergingTestBase {
+
+ public VerticalMergingPreoptimizedTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(
+ "changed", "print a", "foo", "print b", "foo", "unused argument")
+ .inspect(
+ codeInspector -> {
+ assertThat(codeInspector.clazz(Parent.class), not(isPresent()));
+ assertThat(codeInspector.clazz(Changed.class), isPresent());
+ assertThat(codeInspector.clazz(A.class), isPresent());
+ assertThat(
+ codeInspector.clazz(B.class), notIf(isPresent(), enableHorizontalClassMerging));
+ });
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ public static class Parent {
+ @NeverInline
+ public void foo() {
+ System.out.println("foo");
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ public static class Changed extends Parent {
+ public Changed() {
+ System.out.println("changed");
+ }
+ }
+
+ @NeverClassInline
+ public static class A {
+ @NeverInline
+ public void print(Parent p) {
+ System.out.println("print a");
+ p.foo();
+ }
+ }
+
+ @NeverClassInline
+ public static class B {
+ @NeverInline
+ public void print(Parent p) {
+ System.out.println("print b");
+ p.foo();
+ }
+
+ @NeverInline
+ public void unusedArgument(Parent p) {
+ System.out.println("unused argument");
+ }
+ }
+
+ public static class Main {
+ public static void main(String[] args) {
+ Parent p = new Changed();
+ A a = new A();
+ B b = new B();
+ a.print(p);
+ b.print(p);
+ b.unusedArgument(p);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingOfFinalAndNonFinalMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingOfFinalAndNonFinalMethodTest.java
new file mode 100644
index 0000000..f9eff69
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingOfFinalAndNonFinalMethodTest.java
@@ -0,0 +1,84 @@
+// 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.classmerging.horizontal;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+
+public class VirtualMethodMergingOfFinalAndNonFinalMethodTest
+ extends HorizontalClassMergingTestBase {
+
+ public VirtualMethodMergingOfFinalAndNonFinalMethodTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ assertThat(inspector.clazz(A.class), isPresent());
+ assertThat(
+ inspector.clazz(B.class), notIf(isPresent(), enableHorizontalClassMerging));
+ assertThat(inspector.clazz(C.class), isPresent());
+ })
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("A.foo()", "B.foo()", "C.foo()");
+ }
+
+ @NeverClassInline
+ public static class A {
+
+ @NeverInline
+ public final void foo() {
+ System.out.println("A.foo()");
+ }
+ }
+
+ @NeverClassInline
+ @NoVerticalClassMerging
+ public static class B {
+
+ @NeverInline
+ public void foo() {
+ System.out.println("B.foo()");
+ }
+ }
+
+ @NeverClassInline
+ public static class C extends B {
+
+ @NeverInline
+ @Override
+ public void foo() {
+ System.out.println("C.foo()");
+ }
+ }
+
+ public static class TestClass {
+ public static void main(String[] args) {
+ new A().foo();
+ new B().foo();
+ new C().foo();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingOfPublicizedMethodsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingOfPublicizedMethodsTest.java
new file mode 100644
index 0000000..5df1264
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingOfPublicizedMethodsTest.java
@@ -0,0 +1,180 @@
+// 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.classmerging.horizontal;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestParameters;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class VirtualMethodMergingOfPublicizedMethodsTest extends HorizontalClassMergingTestBase {
+
+ public VirtualMethodMergingOfPublicizedMethodsTest(
+ TestParameters parameters, boolean enableHorizontalClassMerging) {
+ super(parameters, enableHorizontalClassMerging);
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .addOptionsModification(
+ options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging)
+ .allowAccessModification()
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines(
+ "A.privateAndPrivate()",
+ "B.privateAndPrivate()",
+ "A.privateAndPackagePrivate()",
+ "B.privateAndPackagePrivate()",
+ "A.privateAndPublic()",
+ "B.privateAndPublic()",
+ "A.packagePrivateAndPrivate()",
+ "B.packagePrivateAndPrivate()",
+ "A.packagePrivateAndPackagePrivate()",
+ "B.packagePrivateAndPackagePrivate()",
+ "A.packagePrivateAndPublic()",
+ "B.packagePrivateAndPublic()",
+ "A.publicAndPrivate()",
+ "B.publicAndPrivate()",
+ "A.publicAndPackagePrivate()",
+ "B.publicAndPackagePrivate()",
+ "A.publicAndPublic()",
+ "B.publicAndPublic()");
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ A a = new A();
+ B b = new B();
+ a.privateAndPrivate();
+ b.privateAndPrivate();
+ a.privateAndPackagePrivate();
+ b.privateAndPackagePrivate();
+ a.privateAndPublic();
+ b.privateAndPublic();
+ a.packagePrivateAndPrivate();
+ b.packagePrivateAndPrivate();
+ a.packagePrivateAndPackagePrivate();
+ b.packagePrivateAndPackagePrivate();
+ a.packagePrivateAndPublic();
+ b.packagePrivateAndPublic();
+ a.publicAndPrivate();
+ b.publicAndPrivate();
+ a.publicAndPackagePrivate();
+ b.publicAndPackagePrivate();
+ a.publicAndPublic();
+ b.publicAndPublic();
+ }
+ }
+
+ @NeverClassInline
+ static class A {
+
+ @NeverInline
+ private void privateAndPrivate() {
+ System.out.println("A.privateAndPrivate()");
+ }
+
+ @NeverInline
+ private void privateAndPackagePrivate() {
+ System.out.println("A.privateAndPackagePrivate()");
+ }
+
+ @NeverInline
+ private void privateAndPublic() {
+ System.out.println("A.privateAndPublic()");
+ }
+
+ @NeverInline
+ void packagePrivateAndPrivate() {
+ System.out.println("A.packagePrivateAndPrivate()");
+ }
+
+ @NeverInline
+ void packagePrivateAndPackagePrivate() {
+ System.out.println("A.packagePrivateAndPackagePrivate()");
+ }
+
+ @NeverInline
+ void packagePrivateAndPublic() {
+ System.out.println("A.packagePrivateAndPublic()");
+ }
+
+ @NeverInline
+ public void publicAndPrivate() {
+ System.out.println("A.publicAndPrivate()");
+ }
+
+ @NeverInline
+ public void publicAndPackagePrivate() {
+ System.out.println("A.publicAndPackagePrivate()");
+ }
+
+ @NeverInline
+ public void publicAndPublic() {
+ System.out.println("A.publicAndPublic()");
+ }
+ }
+
+ @NeverClassInline
+ static class B {
+
+ @NeverInline
+ private void privateAndPrivate() {
+ System.out.println("B.privateAndPrivate()");
+ }
+
+ @NeverInline
+ void privateAndPackagePrivate() {
+ System.out.println("B.privateAndPackagePrivate()");
+ }
+
+ @NeverInline
+ public void privateAndPublic() {
+ System.out.println("B.privateAndPublic()");
+ }
+
+ @NeverInline
+ private void packagePrivateAndPrivate() {
+ System.out.println("B.packagePrivateAndPrivate()");
+ }
+
+ @NeverInline
+ void packagePrivateAndPackagePrivate() {
+ System.out.println("B.packagePrivateAndPackagePrivate()");
+ }
+
+ @NeverInline
+ public void packagePrivateAndPublic() {
+ System.out.println("B.packagePrivateAndPublic()");
+ }
+
+ @NeverInline
+ private void publicAndPrivate() {
+ System.out.println("B.publicAndPrivate()");
+ }
+
+ @NeverInline
+ void publicAndPackagePrivate() {
+ System.out.println("B.publicAndPackagePrivate()");
+ }
+
+ @NeverInline
+ public void publicAndPublic() {
+ System.out.println("B.publicAndPublic()");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/NonReboundFieldAccessOnMergedClassTestClasses.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/NonReboundFieldAccessOnMergedClassTestClasses.java
new file mode 100644
index 0000000..0aaf197
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/NonReboundFieldAccessOnMergedClassTestClasses.java
@@ -0,0 +1,28 @@
+// 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.classmerging.horizontal.testclasses;
+
+import com.android.tools.r8.NoVerticalClassMerging;
+
+public class NonReboundFieldAccessOnMergedClassTestClasses {
+
+ @NoVerticalClassMerging
+ static class A {
+
+ public String greeting;
+
+ A(String greeting) {
+ this.greeting = greeting;
+ }
+ }
+
+ @NoVerticalClassMerging
+ public static class B extends A {
+
+ public B(String greeting) {
+ super(greeting);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/NonReboundFieldAccessWithMergedTypeTestClasses.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/NonReboundFieldAccessWithMergedTypeTestClasses.java
new file mode 100644
index 0000000..ce3354e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/NonReboundFieldAccessWithMergedTypeTestClasses.java
@@ -0,0 +1,31 @@
+// 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.classmerging.horizontal.testclasses;
+
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.classmerging.horizontal.NonReboundFieldAccessWithMergedTypeTest.WorldGreeting;
+
+public class NonReboundFieldAccessWithMergedTypeTestClasses {
+
+ @NoHorizontalClassMerging
+ @NoVerticalClassMerging
+ static class A {
+
+ public WorldGreeting greeting;
+
+ A(WorldGreeting greeting) {
+ this.greeting = greeting;
+ }
+ }
+
+ @NoVerticalClassMerging
+ public static class B extends A {
+
+ public B(WorldGreeting greeting) {
+ super(greeting);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/PackagePrivateClass.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/PackagePrivateClass.java
new file mode 100644
index 0000000..7deccb2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/PackagePrivateClass.java
@@ -0,0 +1,14 @@
+// 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.classmerging.horizontal.testclasses;
+
+import com.android.tools.r8.NeverClassInline;
+
+@NeverClassInline
+class PackagePrivateClass {
+ public PackagePrivateClass() {
+ System.out.println("package private");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/PackagePrivateClassRunner.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/PackagePrivateClassRunner.java
new file mode 100644
index 0000000..9c10bcc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/PackagePrivateClassRunner.java
@@ -0,0 +1,23 @@
+// 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.classmerging.horizontal.testclasses;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+
+@NeverClassInline
+public class PackagePrivateClassRunner {
+
+ public PackagePrivateClassRunner() {}
+
+ @NeverInline
+ public void run() {
+ new PackagePrivateClass();
+ }
+
+ public static Class<?> getPrivateClass() {
+ return PackagePrivateClass.class;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/ClassesHaveBeenMergedTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/ClassesHaveBeenMergedTest.java
new file mode 100644
index 0000000..6ed6dd9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/ClassesHaveBeenMergedTest.java
@@ -0,0 +1,193 @@
+// 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.classmerging.vertical;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
+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 ClassesHaveBeenMergedTest extends VerticalClassMergerTestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return TestBase.getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public ClassesHaveBeenMergedTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test
+ public void testClassesHaveBeenMerged() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(ClassesHaveBeenMergedTest.class)
+ .addKeepMainRule(TestClass.class)
+ .addVerticallyMergedClassesInspector(this::inspectVerticallyMergedClasses)
+ .enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccess();
+ }
+
+ private void inspectVerticallyMergedClasses(VerticallyMergedClassesInspector inspector) {
+ inspector.assertMergedIntoSubtype(
+ GenericInterface.class,
+ GenericAbstractClass.class,
+ Outer.SuperClass.class,
+ SuperClass.class);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ assertThat(inspector.clazz(GenericInterfaceImpl.class), isPresent());
+ assertThat(inspector.clazz(Outer.SubClass.class), isPresent());
+ assertThat(inspector.clazz(SubClass.class), isPresent());
+ assertThat(inspector.clazz(GenericInterface.class), not(isPresent()));
+ assertThat(inspector.clazz(GenericAbstractClass.class), not(isPresent()));
+ assertThat(inspector.clazz(Outer.SuperClass.class), not(isPresent()));
+ assertThat(inspector.clazz(SuperClass.class), not(isPresent()));
+ }
+
+ public static class TestClass {
+
+ public static void main(String... args) {
+ GenericInterface<?> iface = new GenericInterfaceImpl();
+ callMethodOnIface(iface);
+ GenericAbstractClass<?> clazz = new GenericAbstractClassImpl();
+ callMethodOnAbstractClass(clazz);
+ Outer outer = new Outer();
+ Outer.SubClass inner = outer.getInstance();
+ System.out.println(outer.getInstance().method());
+ System.out.println(new SubClass(42));
+
+ // Ensure that the instantiations are not dead code eliminated.
+ escape(clazz);
+ escape(iface);
+ escape(inner);
+ escape(outer);
+ }
+
+ private static void callMethodOnIface(GenericInterface<?> iface) {
+ System.out.println(iface.method());
+ }
+
+ private static void callMethodOnAbstractClass(GenericAbstractClass<?> clazz) {
+ System.out.println(clazz.method());
+ System.out.println(clazz.otherMethod());
+ }
+
+ @NeverInline
+ static void escape(Object o) {
+ if (System.currentTimeMillis() < 0) {
+ System.out.println(o);
+ }
+ }
+ }
+
+ public abstract static class GenericAbstractClass<T> {
+
+ public abstract T method();
+
+ public T otherMethod() {
+ return null;
+ }
+ }
+
+ public static class GenericAbstractClassImpl extends GenericAbstractClass<String> {
+
+ @Override
+ public String method() {
+ return "Hello from GenericAbstractClassImpl";
+ }
+
+ @Override
+ public String otherMethod() {
+ return "otherMethod";
+ }
+ }
+
+ public interface GenericInterface<T> {
+
+ T method();
+ }
+
+ @NoHorizontalClassMerging
+ public static class GenericInterfaceImpl implements GenericInterface<String> {
+
+ @Override
+ public String method() {
+ return "method";
+ }
+ }
+
+ public static class SuperClass {
+
+ private final int field;
+
+ public SuperClass(int field) {
+ this.field = field;
+ }
+
+ public int getField() {
+ return field;
+ }
+ }
+
+ public static class SubClass extends SuperClass {
+
+ private int field;
+
+ public SubClass(int field) {
+ this(field, field + 100);
+ }
+
+ public SubClass(int one, int other) {
+ super(one);
+ field = other;
+ }
+
+ public String toString() {
+ return "is " + field + " " + getField();
+ }
+ }
+
+ static class Outer {
+
+ /**
+ * This class is package private to trigger the generation of bridge methods for the visibility
+ * change of methods from public subtypes.
+ */
+ static class SuperClass {
+
+ public String method() {
+ return "Method in SuperClass.";
+ }
+ }
+
+ @NoHorizontalClassMerging
+ public static class SubClass extends SuperClass {
+ // Intentionally left empty.
+ }
+
+ public SubClass getInstance() {
+ return new SubClass();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/ExceptionTablesTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/ExceptionTablesTest.java
new file mode 100644
index 0000000..ffe849e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/ExceptionTablesTest.java
@@ -0,0 +1,124 @@
+// 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.classmerging.vertical;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+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.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
+import java.util.stream.IntStream;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// If an exception class A is merged into another exception class B, then all exception tables
+// should be updated, and class A should be removed entirely.
+@RunWith(Parameterized.class)
+public class ExceptionTablesTest extends VerticalClassMergerTestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return TestBase.getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public ExceptionTablesTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test
+ public void testClassesHaveBeenMerged() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(ExceptionTablesTest.class)
+ .addKeepMainRule(TestClass.class)
+ .addVerticallyMergedClassesInspector(this::inspectVerticallyMergedClasses)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccess();
+ }
+
+ private void inspectVerticallyMergedClasses(VerticallyMergedClassesInspector inspector) {
+ inspector.assertMergedIntoSubtype(ExceptionA.class, Exception1.class);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ assertThat(inspector.clazz(TestClass.class), isPresent());
+ assertThat(inspector.clazz(ExceptionB.class), isPresent());
+ assertThat(inspector.clazz(Exception2.class), isPresent());
+ assertThat(inspector.clazz(ExceptionA.class), not(isPresent()));
+ assertThat(inspector.clazz(Exception1.class), not(isPresent()));
+
+ // Check that the second catch handler has been removed.
+ MethodSubject mainMethodSubject = inspector.clazz(TestClass.class).mainMethod();
+ assertThat(mainMethodSubject, isPresent());
+ assertEquals(
+ 2,
+ mainMethodSubject
+ .streamTryCatches()
+ .flatMapToInt(x -> IntStream.of(x.getNumberOfHandlers()))
+ .sum());
+ }
+
+ public static class TestClass {
+
+ public static void main(String[] args) {
+ // The following will lead to a catch handler for ExceptionA, which is merged into ExceptionB.
+ try {
+ doSomethingThatMightThrowExceptionB();
+ doSomethingThatMightThrowException2();
+ } catch (ExceptionB exception) {
+ System.out.println("Caught exception: " + exception.getMessage());
+ } catch (ExceptionA exception) {
+ System.out.println("Caught exception: " + exception.getMessage());
+ } catch (Exception2 exception) {
+ System.out.println("Caught exception: " + exception.getMessage());
+ } catch (Exception1 exception) {
+ System.out.println("Caught exception: " + exception.getMessage());
+ }
+ }
+
+ private static void doSomethingThatMightThrowExceptionB() throws ExceptionB {
+ throw new ExceptionB("Ouch!");
+ }
+
+ private static void doSomethingThatMightThrowException2() throws Exception2 {
+ throw new Exception2("Ouch!");
+ }
+ }
+
+ // Will be merged into ExceptionB when class merging is enabled.
+ public static class ExceptionA extends Exception {
+ public ExceptionA(String message) {
+ super(message);
+ }
+ }
+
+ public static class ExceptionB extends ExceptionA {
+ public ExceptionB(String message) {
+ super(message);
+ }
+ }
+
+ public static class Exception1 extends Exception {
+ public Exception1(String message) {
+ super(message);
+ }
+ }
+
+ public static class Exception2 extends Exception1 {
+ public Exception2(String message) {
+ super(message);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/NoIllegalClassAccessWithAccessModificationsTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/NoIllegalClassAccessWithAccessModificationsTest.java
new file mode 100644
index 0000000..35e27eb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/NoIllegalClassAccessWithAccessModificationsTest.java
@@ -0,0 +1,113 @@
+// 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.classmerging.vertical;
+
+import static com.android.tools.r8.classmerging.vertical.testclasses.NoIllegalClassAccessWithAccessModificationsTestClasses.getSimpleInterfaceImplClass;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.classmerging.vertical.testclasses.NoIllegalClassAccessWithAccessModificationsTestClasses;
+import com.android.tools.r8.classmerging.vertical.testclasses.NoIllegalClassAccessWithAccessModificationsTestClasses.SimpleInterfaceFactory;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
+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 NoIllegalClassAccessWithAccessModificationsTest extends VerticalClassMergerTestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return TestBase.getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public NoIllegalClassAccessWithAccessModificationsTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(NoIllegalClassAccessWithAccessModificationsTest.class)
+ .addInnerClasses(NoIllegalClassAccessWithAccessModificationsTestClasses.class)
+ .addKeepMainRule(TestClass.class)
+ .addVerticallyMergedClassesInspector(this::inspectVerticallyMergedClasses)
+ .allowAccessModification()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccess();
+ }
+
+ private void inspectVerticallyMergedClasses(VerticallyMergedClassesInspector inspector) {
+ inspector.assertMergedIntoSubtype(SimpleInterface.class, OtherSimpleInterface.class);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ assertThat(inspector.clazz(TestClass.class), isPresent());
+ assertThat(inspector.clazz(getSimpleInterfaceImplClass()), isPresent());
+ assertThat(inspector.clazz(OtherSimpleInterfaceImpl.class), isPresent());
+ assertThat(inspector.clazz(SimpleInterface.class), not(isPresent()));
+ assertThat(inspector.clazz(OtherSimpleInterface.class), not(isPresent()));
+ }
+
+ public static class TestClass {
+
+ public static void main(String[] args) {
+ // Without access modifications, it is not possible to merge the interface SimpleInterface
+ // into
+ // SimpleInterfaceImpl, since this would lead to an illegal class access here.
+ SimpleInterface x = SimpleInterfaceFactory.create();
+ x.foo();
+
+ // Without access modifications, it is not possible to merge the interface
+ // OtherSimpleInterface
+ // into OtherSimpleInterfaceImpl, since this could lead to an illegal class access if another
+ // package references OtherSimpleInterface.
+ OtherSimpleInterface y = new OtherSimpleInterfaceImpl();
+ y.bar();
+
+ // Ensure that the instantiations are not dead code eliminated.
+ escape(x);
+ escape(y);
+ }
+
+ @NeverInline
+ static void escape(Object o) {
+ if (System.currentTimeMillis() < 0) {
+ System.out.println(o);
+ }
+ }
+ }
+
+ // Should only be merged into OtherSimpleInterfaceImpl if access modifications are allowed.
+ public interface SimpleInterface {
+
+ void foo();
+ }
+
+ // Should only be merged into OtherSimpleInterfaceImpl if access modifications are allowed.
+ public interface OtherSimpleInterface {
+
+ void bar();
+ }
+
+ private static class OtherSimpleInterfaceImpl implements OtherSimpleInterface {
+
+ @Override
+ public void bar() {
+ System.out.println("In bar on OtherSimpleInterfaceImpl");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessOnMergedClassTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessOnMergedClassTest.java
new file mode 100644
index 0000000..ecf49fc
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessOnMergedClassTest.java
@@ -0,0 +1,70 @@
+// 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.classmerging.vertical;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.classmerging.vertical.NonReboundFieldAccessWithMergedTypeTest.GreetingBase;
+import com.android.tools.r8.classmerging.vertical.testclasses.NonReboundFieldAccessOnMergedClassTestClasses;
+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 NonReboundFieldAccessOnMergedClassTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection params() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public NonReboundFieldAccessOnMergedClassTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ thrown.expect(Throwable.class);
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addInnerClasses(NonReboundFieldAccessOnMergedClassTestClasses.class)
+ .addKeepMainRule(Main.class)
+ .addVerticallyMergedClassesInspector(
+ inspector -> inspector.assertMergedIntoSubtype(GreetingBase.class))
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ C c = new D("Hello world!");
+ System.out.println(c.greeting);
+ }
+ }
+
+ static class C extends NonReboundFieldAccessOnMergedClassTestClasses.B {
+
+ public C(String greeting) {
+ super(greeting);
+ }
+ }
+
+ @NeverClassInline
+ static class D extends C {
+
+ public D(String greeting) {
+ super(greeting);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessWithMergedTypeTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessWithMergedTypeTest.java
new file mode 100644
index 0000000..5a3f831
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/NonReboundFieldAccessWithMergedTypeTest.java
@@ -0,0 +1,72 @@
+// 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.classmerging.vertical;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.classmerging.vertical.testclasses.NonReboundFieldAccessWithMergedTypeTestClasses;
+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 NonReboundFieldAccessWithMergedTypeTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection params() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public NonReboundFieldAccessWithMergedTypeTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ thrown.expect(Throwable.class);
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addInnerClasses(NonReboundFieldAccessWithMergedTypeTestClasses.class)
+ .addKeepMainRule(Main.class)
+ .addVerticallyMergedClassesInspector(
+ inspector -> inspector.assertMergedIntoSubtype(GreetingBase.class))
+ .enableNeverClassInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(new C(new Greeting()).greeting);
+ }
+ }
+
+ @NeverClassInline
+ static class C extends NonReboundFieldAccessWithMergedTypeTestClasses.B {
+
+ public C(GreetingBase greeting) {
+ super(greeting);
+ }
+ }
+
+ public static class GreetingBase {}
+
+ public static class Greeting extends GreetingBase {
+
+ @Override
+ public String toString() {
+ return "Hello world!";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/SyntheticBridgeSignaturesTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/SyntheticBridgeSignaturesTest.java
new file mode 100644
index 0000000..829e56b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/SyntheticBridgeSignaturesTest.java
@@ -0,0 +1,133 @@
+// 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.classmerging.vertical;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ir.optimize.Inliner.Reason;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
+import com.google.common.collect.ImmutableSet;
+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 SyntheticBridgeSignaturesTest extends VerticalClassMergerTestBase {
+
+ // Try both with and without inlining. If the bridge signatures are not updated properly, and
+ // inlining is enabled, then there can be issues with our inlining invariants regarding the
+ // outermost caller. If inlining is disabled, there is a risk that the methods will end up
+ // having the wrong signatures, or that the generated Proguard maps are incorrect (this will be
+ // caught by the debugging test, which is carried out by the call to runTestOnInput()).
+ private final boolean allowInlining;
+
+ @Parameters(name = "{1}, inlining: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), TestBase.getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ public SyntheticBridgeSignaturesTest(boolean allowInlining, TestParameters parameters) {
+ super(parameters);
+ this.allowInlining = allowInlining;
+ }
+
+ @Test
+ public void test() throws Throwable {
+ R8TestCompileResult compileResult =
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .addOptionsModification(
+ options -> {
+ if (!allowInlining) {
+ options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
+ }
+ })
+ .addVerticallyMergedClassesInspector(this::inspectVerticallyMergedClasses)
+ .enableInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+
+ compileResult
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccess();
+
+ if (parameters.isDexRuntime()) {
+ runDebugTest(TestClass.class, compileResult);
+ }
+ }
+
+ private void inspectVerticallyMergedClasses(VerticallyMergedClassesInspector inspector) {
+ inspector.assertMergedIntoSubtype(A.class, B.class);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ assertThat(inspector.clazz(TestClass.class), isPresent());
+ assertThat(inspector.clazz(ASub.class), isPresent());
+ assertThat(inspector.clazz(BSub.class), isPresent());
+ assertThat(inspector.clazz(A.class), not(isPresent()));
+ assertThat(inspector.clazz(B.class), not(isPresent()));
+ }
+
+ static class TestClass {
+
+ // If A is merged into ASub first, then the synthetic bridge for "void A.m(B)" will originally
+ // get the signature "void ASub.m(B)". Otherwise, if B is merged into BSub first, then the
+ // synthetic bridge will get the signature "void BSub.m(A)". In either case, it is important
+ // that
+ // the signatures of the bridge methods are updated after all classes have been merged
+ // vertically.
+ public static void main(String[] args) {
+ ASub a = new ASub();
+ BSub b = new BSub();
+ a.m(b);
+ b.m(a);
+
+ // Ensure that the instantiations are not dead code eliminated.
+ escape(a);
+ escape(b);
+ }
+
+ @NeverInline
+ static void escape(Object o) {
+ if (System.currentTimeMillis() < 0) {
+ System.out.println(o);
+ }
+ }
+ }
+
+ private static class A {
+
+ public void m(B object) {
+ System.out.println("In A.m()");
+ }
+ }
+
+ private static class ASub extends A {}
+
+ private static class B {
+
+ public void m(A object) {
+ System.out.println("In B.m()");
+ }
+ }
+
+ @NoHorizontalClassMerging
+ private static class BSub extends B {}
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerDebugTestRunner.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerDebugTestRunner.java
new file mode 100644
index 0000000..e542559
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerDebugTestRunner.java
@@ -0,0 +1,100 @@
+// 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.classmerging.vertical;
+
+import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.debug.DebugTestBase;
+import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command;
+import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.DebuggeeState;
+import com.android.tools.r8.debug.DexDebugTestConfig;
+import com.android.tools.r8.utils.AndroidApp;
+import java.io.File;
+import java.nio.file.Path;
+import org.junit.rules.TemporaryFolder;
+
+public class VerticalClassMergerDebugTestRunner extends DebugTestBase {
+
+ private final String main;
+ private final TemporaryFolder temp;
+
+ private DebugTestRunner runner = null;
+
+ public VerticalClassMergerDebugTestRunner(String main, TemporaryFolder temp) {
+ this.main = main;
+ this.temp = temp;
+ }
+
+ public void run(AndroidApp app, Path proguardMapPath) throws Throwable {
+ Path appPath = File.createTempFile("app", ".zip", temp.getRoot()).toPath();
+ app.writeToZip(appPath, OutputMode.DexIndexed);
+
+ DexDebugTestConfig config = new DexDebugTestConfig(appPath);
+ config.allowUnprocessedCommands();
+ config.setProguardMap(proguardMapPath);
+
+ this.runner =
+ getDebugTestRunner(
+ config, main, breakpoint(main, "main"), run(), stepIntoUntilNoLongerInApp());
+ this.runner.runBare();
+ }
+
+ private void checkState(DebuggeeState state) {
+ // If a class pkg.A is merged into pkg.B, and a method pkg.A.m() needs to be renamed, then
+ // it will be renamed to pkg.B.m$pkg$A(). Since all tests are in the package "classmerging",
+ // we check that no methods in the debugging state (i.e., after the Proguard map has been
+ // applied) contain "$classmerging$.
+ String qualifiedMethodSignature =
+ state.getClassSignature() + "->" + state.getMethodName() + state.getMethodSignature();
+ boolean holderIsCompanionClass = state.getClassName().endsWith(COMPANION_CLASS_NAME_SUFFIX);
+ if (!holderIsCompanionClass) {
+ assertThat(qualifiedMethodSignature, not(containsString("$classmerging$")));
+ }
+ }
+
+ // Keeps stepping in until it is no longer in a class from the classmerging package.
+ // Then starts stepping out until it is again in the classmerging package.
+ private Command stepIntoUntilNoLongerInApp() {
+ return stepUntil(
+ StepKind.INTO,
+ StepLevel.INSTRUCTION,
+ state -> {
+ if (state.getClassSignature().contains("classmerging")) {
+ checkState(state);
+
+ // Continue stepping into.
+ return false;
+ }
+
+ // Stop stepping into.
+ runner.enqueueCommandFirst(stepOutUntilInApp());
+ return true;
+ });
+ }
+
+ // Keeps stepping out until it is in a class from the classmerging package.
+ // Then starts stepping in until it is no longer in the classmerging package.
+ private Command stepOutUntilInApp() {
+ return stepUntil(
+ StepKind.OUT,
+ StepLevel.INSTRUCTION,
+ state -> {
+ if (state.getClassSignature().contains("classmerging")) {
+ checkState(state);
+
+ // Stop stepping out.
+ runner.enqueueCommandFirst(stepIntoUntilNoLongerInApp());
+ return true;
+ }
+
+ // Continue stepping out.
+ return false;
+ });
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index e831c02..4a3cc91 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -3,11 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.classmerging.vertical;
-import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX;
import static com.android.tools.r8.smali.SmaliBuilder.buildCode;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -16,7 +14,6 @@
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.OutputMode;
import com.android.tools.r8.ProgramResourceProvider;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.R8TestCompileResult;
@@ -25,10 +22,6 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
-import com.android.tools.r8.debug.DebugTestBase;
-import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command;
-import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.DebuggeeState;
-import com.android.tools.r8.debug.DexDebugTestConfig;
import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.jasmin.JasminBuilder;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
@@ -49,7 +42,6 @@
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Streams;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
@@ -57,10 +49,8 @@
import java.nio.file.Paths;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.function.Predicate;
-import java.util.stream.IntStream;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -101,7 +91,7 @@
}
private void runR8(Path proguardConfig, Consumer<InternalOptions> optionsConsumer)
- throws IOException, ExecutionException, CompilationFailedException {
+ throws IOException, CompilationFailedException {
inspector =
testForR8(parameters.getBackend())
.addProgramFiles(EXAMPLE_JAR)
@@ -124,19 +114,6 @@
);
@Test
- public void testClassesHaveBeenMerged() throws Throwable {
- expectThrowsWithHorizontalClassMerging();
- runR8(EXAMPLE_KEEP, this::configure);
- // GenericInterface should be merged into GenericInterfaceImpl.
- for (String candidate : CAN_BE_MERGED) {
- assertThat(inspector.clazz(candidate), not(isPresent()));
- }
- assertThat(inspector.clazz("classmerging.GenericInterfaceImpl"), isPresent());
- assertThat(inspector.clazz("classmerging.Outer$SubClass"), isPresent());
- assertThat(inspector.clazz("classmerging.SubClass"), isPresent());
- }
-
- @Test
public void testClassesHaveNotBeenMerged() throws Throwable {
runR8(DONT_OPTIMIZE, null);
for (String candidate : CAN_BE_MERGED) {
@@ -1008,48 +985,6 @@
preservedClassNames::contains);
}
- @Test
- public void testSyntheticBridgeSignatures() throws Throwable {
- expectThrowsWithHorizontalClassMerging();
- // Try both with and without inlining. If the bridge signatures are not updated properly, and
- // inlining is enabled, then there can be issues with our inlining invariants regarding the
- // outermost caller. If inlining is disabled, there is a risk that the methods will end up
- // having the wrong signatures, or that the generated Proguard maps are incorrect (this will be
- // caught by the debugging test, which is carried out by the call to runTestOnInput()).
- for (boolean allowInlining : ImmutableList.of(false, true)) {
- String main = "classmerging.SyntheticBridgeSignaturesTest";
- Path[] programFiles =
- new Path[] {
- CF_DIR.resolve("SyntheticBridgeSignaturesTest.class"),
- CF_DIR.resolve("SyntheticBridgeSignaturesTest$A.class"),
- CF_DIR.resolve("SyntheticBridgeSignaturesTest$ASub.class"),
- CF_DIR.resolve("SyntheticBridgeSignaturesTest$B.class"),
- CF_DIR.resolve("SyntheticBridgeSignaturesTest$BSub.class")
- };
- Set<String> preservedClassNames =
- ImmutableSet.of(
- "classmerging.SyntheticBridgeSignaturesTest",
- "classmerging.SyntheticBridgeSignaturesTest$ASub",
- "classmerging.SyntheticBridgeSignaturesTest$BSub");
- runTestOnInput(
- testForR8(parameters.getBackend())
- .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
- .addOptionsModification(this::configure)
- .addOptionsModification(
- options -> {
- if (!allowInlining) {
- options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
- }
- })
- .allowUnusedProguardConfigurationRules(),
- main,
- readProgramFiles(programFiles),
- preservedClassNames::contains,
- // TODO(christofferqa): The debug test fails when inlining is not allowed.
- allowInlining ? new VerticalClassMergerDebugTest(main) : null);
- }
- }
-
private static String jasminCodeForPrinting(String message) {
return buildCode(
"getstatic java/lang/System/out Ljava/io/PrintStream;",
@@ -1080,50 +1015,6 @@
preservedClassNames::contains);
}
- // If an exception class A is merged into another exception class B, then all exception tables
- // should be updated, and class A should be removed entirely.
- @Test
- public void testExceptionTables() throws Throwable {
- expectThrowsWithHorizontalClassMerging();
- String main = "classmerging.ExceptionTest";
- Path[] programFiles =
- new Path[] {
- CF_DIR.resolve("ExceptionTest.class"),
- CF_DIR.resolve("ExceptionTest$ExceptionA.class"),
- CF_DIR.resolve("ExceptionTest$ExceptionB.class"),
- CF_DIR.resolve("ExceptionTest$Exception1.class"),
- CF_DIR.resolve("ExceptionTest$Exception2.class")
- };
- Set<String> preservedClassNames =
- ImmutableSet.of(
- "classmerging.ExceptionTest",
- "classmerging.ExceptionTest$ExceptionB",
- "classmerging.ExceptionTest$Exception2");
- CodeInspector inspector =
- runTest(
- testForR8(parameters.getBackend())
- .addKeepRules(getProguardConfig(EXAMPLE_KEEP))
- .allowUnusedProguardConfigurationRules(),
- main,
- programFiles,
- preservedClassNames::contains)
- .inspector();
-
- ClassSubject mainClass = inspector.clazz(main);
- assertThat(mainClass, isPresent());
-
- MethodSubject mainMethod =
- mainClass.method("void", "main", ImmutableList.of("java.lang.String[]"));
- assertThat(mainMethod, isPresent());
-
- // Check that the second catch handler has been removed.
- assertEquals(
- 2,
- Streams.stream(mainMethod.iterateTryCatches())
- .flatMapToInt(x -> IntStream.of(x.getNumberOfHandlers()))
- .sum());
- }
-
@Test
public void testMergeDefaultMethodIntoClass() throws Throwable {
String main = "classmerging.MergeDefaultMethodIntoClassTest";
@@ -1219,42 +1110,6 @@
preservedClassNames::contains);
}
- @Test
- public void testNoIllegalClassAccessWithAccessModifications() throws Throwable {
- expectThrowsWithHorizontalClassMerging();
- // If access modifications are allowed then SimpleInterface should be merged into
- // SimpleInterfaceImpl.
- String main = "classmerging.SimpleInterfaceAccessTest";
- Path[] programFiles =
- new Path[] {
- CF_DIR.resolve("SimpleInterfaceAccessTest.class"),
- CF_DIR.resolve("SimpleInterfaceAccessTest$SimpleInterface.class"),
- CF_DIR.resolve("SimpleInterfaceAccessTest$OtherSimpleInterface.class"),
- CF_DIR.resolve("SimpleInterfaceAccessTest$OtherSimpleInterfaceImpl.class"),
- CF_DIR.resolve("pkg/SimpleInterfaceImplRetriever.class"),
- CF_DIR.resolve("pkg/SimpleInterfaceImplRetriever$SimpleInterfaceImpl.class")
- };
- ImmutableSet<String> preservedClassNames =
- ImmutableSet.of(
- "classmerging.SimpleInterfaceAccessTest",
- "classmerging.SimpleInterfaceAccessTest$OtherSimpleInterfaceImpl",
- "classmerging.pkg.SimpleInterfaceImplRetriever",
- "classmerging.pkg.SimpleInterfaceImplRetriever$SimpleInterfaceImpl");
- // Allow access modifications (and prevent SimpleInterfaceImplRetriever from being removed as
- // a result of inlining).
- runTest(
- testForR8(parameters.getBackend())
- .addKeepRules(
- getProguardConfig(
- EXAMPLE_KEEP,
- "-allowaccessmodification",
- "-keep public class classmerging.pkg.SimpleInterfaceImplRetriever"))
- .allowUnusedProguardConfigurationRules(),
- main,
- programFiles,
- preservedClassNames::contains);
- }
-
// TODO(christofferqa): This test checks that the invoke-super instruction in B is not rewritten
// into an invoke-direct instruction after it gets merged into class C. We should add a test that
// checks that this works with and without inlining of the method B.m().
@@ -1325,7 +1180,7 @@
main,
input,
preservedClassNames,
- new VerticalClassMergerDebugTest(main));
+ new VerticalClassMergerDebugTestRunner(main, temp));
}
private R8TestCompileResult runTestOnInput(
@@ -1333,7 +1188,7 @@
String main,
AndroidApp input,
Predicate<String> preservedClassNames,
- VerticalClassMergerDebugTest debugTestRunner)
+ VerticalClassMergerDebugTestRunner debugTestRunner)
throws Throwable {
R8TestCompileResult compileResult =
builder
@@ -1379,7 +1234,7 @@
// Check that we never come across a method that has a name with "$classmerging$" in it during
// debugging.
if (debugTestRunner != null && parameters.isDexRuntime()) {
- debugTestRunner.test(compileResult.app, proguardMapPath);
+ debugTestRunner.run(compileResult.app, proguardMapPath);
}
return compileResult;
}
@@ -1397,81 +1252,4 @@
return builder.toString();
}
- private class VerticalClassMergerDebugTest extends DebugTestBase {
-
- private final String main;
- private DebugTestRunner runner = null;
-
- public VerticalClassMergerDebugTest(String main) {
- this.main = main;
- }
-
- public void test(AndroidApp app, Path proguardMapPath) throws Throwable {
- Path appPath =
- File.createTempFile("app", ".zip", VerticalClassMergerTest.this.temp.getRoot()).toPath();
- app.writeToZip(appPath, OutputMode.DexIndexed);
-
- DexDebugTestConfig config = new DexDebugTestConfig(appPath);
- config.allowUnprocessedCommands();
- config.setProguardMap(proguardMapPath);
-
- this.runner =
- getDebugTestRunner(
- config, main, breakpoint(main, "main"), run(), stepIntoUntilNoLongerInApp());
- this.runner.runBare();
- }
-
- private void checkState(DebuggeeState state) {
- // If a class pkg.A is merged into pkg.B, and a method pkg.A.m() needs to be renamed, then
- // it will be renamed to pkg.B.m$pkg$A(). Since all tests are in the package "classmerging",
- // we check that no methods in the debugging state (i.e., after the Proguard map has been
- // applied) contain "$classmerging$.
- String qualifiedMethodSignature =
- state.getClassSignature() + "->" + state.getMethodName() + state.getMethodSignature();
- boolean holderIsCompanionClass = state.getClassName().endsWith(COMPANION_CLASS_NAME_SUFFIX);
- if (!holderIsCompanionClass) {
- assertThat(qualifiedMethodSignature, not(containsString("$classmerging$")));
- }
- }
-
- // Keeps stepping in until it is no longer in a class from the classmerging package.
- // Then starts stepping out until it is again in the classmerging package.
- private Command stepIntoUntilNoLongerInApp() {
- return stepUntil(
- StepKind.INTO,
- StepLevel.INSTRUCTION,
- state -> {
- if (state.getClassSignature().contains("classmerging")) {
- checkState(state);
-
- // Continue stepping into.
- return false;
- }
-
- // Stop stepping into.
- runner.enqueueCommandFirst(stepOutUntilInApp());
- return true;
- });
- }
-
- // Keeps stepping out until it is in a class from the classmerging package.
- // Then starts stepping in until it is no longer in the classmerging package.
- private Command stepOutUntilInApp() {
- return stepUntil(
- StepKind.OUT,
- StepLevel.INSTRUCTION,
- state -> {
- if (state.getClassSignature().contains("classmerging")) {
- checkState(state);
-
- // Stop stepping out.
- runner.enqueueCommandFirst(stepIntoUntilNoLongerInApp());
- return true;
- }
-
- // Continue stepping out.
- return false;
- });
- }
- }
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTestBase.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTestBase.java
new file mode 100644
index 0000000..c11fcbd
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTestBase.java
@@ -0,0 +1,28 @@
+// 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.classmerging.vertical;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
+
+public abstract class VerticalClassMergerTestBase extends TestBase {
+
+ protected final TestParameters parameters;
+
+ public VerticalClassMergerTestBase(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ public void runDebugTest(Class<?> mainClass, R8TestCompileResult compileResult) throws Throwable {
+ assertTrue(parameters.isDexRuntime());
+ new VerticalClassMergerDebugTestRunner(mainClass.getTypeName(), temp)
+ .run(compileResult.app, compileResult.writeProguardMap());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithNonVisibleAnnotation.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithNonVisibleAnnotationTest.java
similarity index 78%
rename from src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithNonVisibleAnnotation.java
rename to src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithNonVisibleAnnotationTest.java
index bb33801..795837e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithNonVisibleAnnotation.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergingWithNonVisibleAnnotationTest.java
@@ -14,8 +14,8 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.classmerging.vertical.testclasses.Outer;
-import com.android.tools.r8.classmerging.vertical.testclasses.Outer.Base;
+import com.android.tools.r8.classmerging.vertical.testclasses.VerticalClassMergingWithNonVisibleAnnotationTestClasses;
+import com.android.tools.r8.classmerging.vertical.testclasses.VerticalClassMergingWithNonVisibleAnnotationTestClasses.Base;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -26,7 +26,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class VerticalClassMergingWithNonVisibleAnnotation extends TestBase {
+public class VerticalClassMergingWithNonVisibleAnnotationTest extends TestBase {
private final TestParameters parameters;
@@ -35,18 +35,20 @@
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- public VerticalClassMergingWithNonVisibleAnnotation(TestParameters parameters) {
+ public VerticalClassMergingWithNonVisibleAnnotationTest(TestParameters parameters) {
this.parameters = parameters;
}
@Test
public void testR8() throws Exception {
testForR8(parameters.getBackend())
- .addInnerClasses(Outer.class)
+ .addInnerClasses(VerticalClassMergingWithNonVisibleAnnotationTestClasses.class)
.addProgramClasses(Sub.class)
.setMinApi(parameters.getApiLevel())
.addKeepMainRule(Sub.class)
- .addKeepClassRules(Outer.class.getPackage().getName() + ".Outer$Private* { *; }")
+ .addKeepClassRules(
+ VerticalClassMergingWithNonVisibleAnnotationTestClasses.class.getTypeName()
+ + "$Private* { *; }")
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
@@ -65,7 +67,8 @@
assertThat(foo, isPresent());
AnnotationSubject privateMethodAnnotation =
foo.annotation(
- Outer.class.getPackage().getName() + ".Outer$PrivateMethodAnnotation");
+ VerticalClassMergingWithNonVisibleAnnotationTestClasses.class.getTypeName()
+ + "$PrivateMethodAnnotation");
assertThat(privateMethodAnnotation, isPresent());
});
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/testclasses/NoIllegalClassAccessWithAccessModificationsTestClasses.java b/src/test/java/com/android/tools/r8/classmerging/vertical/testclasses/NoIllegalClassAccessWithAccessModificationsTestClasses.java
new file mode 100644
index 0000000..b16d11b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/testclasses/NoIllegalClassAccessWithAccessModificationsTestClasses.java
@@ -0,0 +1,34 @@
+// 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.classmerging.vertical.testclasses;
+
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.classmerging.vertical.NoIllegalClassAccessWithAccessModificationsTest.SimpleInterface;
+
+public class NoIllegalClassAccessWithAccessModificationsTestClasses {
+
+ public static Class<?> getSimpleInterfaceImplClass() {
+ return SimpleInterfaceImpl.class;
+ }
+
+ public static class SimpleInterfaceFactory {
+
+ public static SimpleInterface create() {
+ return new SimpleInterfaceImpl();
+ }
+ }
+
+ // This class is intentionally marked private. It is not possible to merge the interface
+ // SimpleInterface into SimpleInterfaceImpl, since this would lead to an illegal class access
+ // in SimpleInterfaceAccessTest.
+ @NoHorizontalClassMerging
+ private static class SimpleInterfaceImpl implements SimpleInterface {
+
+ @Override
+ public void foo() {
+ System.out.println("In foo on SimpleInterfaceImpl");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/testclasses/NonReboundFieldAccessOnMergedClassTestClasses.java b/src/test/java/com/android/tools/r8/classmerging/vertical/testclasses/NonReboundFieldAccessOnMergedClassTestClasses.java
new file mode 100644
index 0000000..7653f54
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/testclasses/NonReboundFieldAccessOnMergedClassTestClasses.java
@@ -0,0 +1,27 @@
+// 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.classmerging.vertical.testclasses;
+
+import com.android.tools.r8.NoVerticalClassMerging;
+
+public class NonReboundFieldAccessOnMergedClassTestClasses {
+
+ @NoVerticalClassMerging
+ static class A {
+
+ public String greeting;
+
+ A(String greeting) {
+ this.greeting = greeting;
+ }
+ }
+
+ public static class B extends A {
+
+ public B(String greeting) {
+ super(greeting);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/testclasses/NonReboundFieldAccessWithMergedTypeTestClasses.java b/src/test/java/com/android/tools/r8/classmerging/vertical/testclasses/NonReboundFieldAccessWithMergedTypeTestClasses.java
new file mode 100644
index 0000000..aac0481
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/testclasses/NonReboundFieldAccessWithMergedTypeTestClasses.java
@@ -0,0 +1,29 @@
+// 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.classmerging.vertical.testclasses;
+
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.classmerging.vertical.NonReboundFieldAccessWithMergedTypeTest.GreetingBase;
+
+public class NonReboundFieldAccessWithMergedTypeTestClasses {
+
+ @NoVerticalClassMerging
+ static class A {
+
+ public GreetingBase greeting;
+
+ A(GreetingBase greeting) {
+ this.greeting = greeting;
+ }
+ }
+
+ @NoVerticalClassMerging
+ public static class B extends A {
+
+ public B(GreetingBase greeting) {
+ super(greeting);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/testclasses/Outer.java b/src/test/java/com/android/tools/r8/classmerging/vertical/testclasses/VerticalClassMergingWithNonVisibleAnnotationTestClasses.java
similarity index 92%
rename from src/test/java/com/android/tools/r8/classmerging/vertical/testclasses/Outer.java
rename to src/test/java/com/android/tools/r8/classmerging/vertical/testclasses/VerticalClassMergingWithNonVisibleAnnotationTestClasses.java
index 46ef8c8..3b8bf5b 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/testclasses/Outer.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/testclasses/VerticalClassMergingWithNonVisibleAnnotationTestClasses.java
@@ -8,7 +8,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-public class Outer {
+public class VerticalClassMergingWithNonVisibleAnnotationTestClasses {
@Retention(RetentionPolicy.RUNTIME)
private @interface PrivateClassAnnotation {}
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithAnonymousClass.java b/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithAnonymousClass.java
index b2d5b54..5d84439 100644
--- a/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithAnonymousClass.java
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithAnonymousClass.java
@@ -113,18 +113,17 @@
public void testR8() throws Exception {
try {
testForR8(parameters.getBackend())
- // TODO(b/158752316, b/157700141): Disable inlining to keep the synthetic lambda methods.
- .addOptionsModification(options -> options.enableInlining = false)
.addInnerClasses(DesugarLambdaWithAnonymousClass.class)
.setMinApi(parameters.getApiLevel())
- .addKeepRules("-keep class * { *; }")
- // Keeping the synthetic lambda methods is currently not supported - they are
- // forcefully unpinned. The following rule has no effect. See b/b/157700141.
- .addKeepRules("-keep class **.*$TestClass { synthetic *; }")
+ // Keep the synthesized inner classes.
+ .addKeepRules("-keep class **.*$TestClass$1")
+ .addKeepRules("-keep class **.*$TestClass$2")
+ // Keep the outer context: TestClass *and* the synthetic lambda methods.
+ .addKeepRules("-keep class **.*$TestClass { private synthetic void lambda$*(*); }")
.addKeepAttributes("InnerClasses", "EnclosingMethod")
- .compile()
- .inspect(this::checkEnclosingMethod)
+ .addKeepMainRule(TestClass.class)
.run(parameters.getRuntime(), TestClass.class)
+ .inspect(this::checkEnclosingMethod)
.assertSuccessWithOutputLines(
parameters.isCfRuntime() ? EXPECTED_JAVAC_RESULT : EXPECTED_DESUGARED_RESULT);
assertFalse(parameters.isDexRuntime());
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithLocalClass.java b/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithLocalClass.java
index 8c539d2..f272f26 100644
--- a/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithLocalClass.java
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithLocalClass.java
@@ -36,9 +36,6 @@
"Hello from inside lambda$test$0$DesugarLambdaWithLocalClass$TestClass",
"Hello from inside lambda$testStatic$1");
- private List<String> EXPECTED_DESUGARED_RESULT_R8_WITHOUT_INLINING =
- ImmutableList.of("Hello from inside lambda$test$0", "Hello from inside lambda$testStatic$1");
-
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
@@ -113,22 +110,18 @@
@Test
public void testR8() throws Exception {
testForR8(parameters.getBackend())
- // TODO(b/158752316, b/157700141): Disable inlining to keep the synthetic lambda methods.
- .addOptionsModification(options -> options.enableInlining = false)
.addInnerClasses(DesugarLambdaWithLocalClass.class)
.setMinApi(parameters.getApiLevel())
- .addKeepRules("-keep class * { *; }")
- // Keeping the synthetic lambda methods is currently not supported - they are
- // forcefully unpinned. The following rule has no effect. See b/b/157700141.
- .addKeepRules("-keep class **.*$TestClass { synthetic *; }")
+ // Keep the synthesized inner classes.
+ .addKeepRules("-keep class **.*$TestClass$*MyConsumerImpl")
+ // Keep the outer context: TestClass *and* the synthetic lambda methods.
+ .addKeepRules("-keep class **.*$TestClass { private synthetic void lambda$*(*); }")
.addKeepAttributes("InnerClasses", "EnclosingMethod")
- .compile()
- .inspect(this::checkEnclosingMethod)
+ .addKeepMainRule(TestClass.class)
.run(parameters.getRuntime(), TestClass.class)
+ .inspect(this::checkEnclosingMethod)
.assertSuccessWithOutputLines(
- parameters.isCfRuntime()
- ? EXPECTED_JAVAC_RESULT
- : EXPECTED_DESUGARED_RESULT_R8_WITHOUT_INLINING);
+ parameters.isCfRuntime() ? EXPECTED_JAVAC_RESULT : EXPECTED_JAVAC_RESULT);
}
public interface MyConsumer<T> {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
index 380703c..c2a8275 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
@@ -150,8 +150,6 @@
@Test
public void testBufferedReaderD8() throws Exception {
- expectThrowsWithHorizontalClassMergingIf(
- shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
Assume.assumeTrue(parameters.getRuntime().isDex());
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
@@ -180,8 +178,6 @@
@Test
public void testBufferedReaderR8() throws Exception {
- expectThrowsWithHorizontalClassMergingIf(
- shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
Assume.assumeTrue(parameters.getRuntime().isDex());
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForR8(parameters.getBackend())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionForwardingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionForwardingTest.java
index 0a3ce0a..42582e6 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionForwardingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionForwardingTest.java
@@ -46,7 +46,9 @@
@Test
public void testCustomCollectionD8() throws Exception {
expectThrowsWithHorizontalClassMergingIf(
- shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
+ shrinkDesugaredLibrary
+ && parameters.getApiLevel().isLessThan(AndroidApiLevel.N)
+ && !parameters.getDexRuntimeVersion().isDefault());
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
.addInnerClasses(CustomCollectionForwardingTest.class)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
index eadaa9b..2330854 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/CustomCollectionTest.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.D8TestRunResult;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
@@ -53,8 +52,6 @@
@Test
public void testCustomCollectionD8() throws Exception {
- expectThrowsWithHorizontalClassMergingIf(
- shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
D8TestRunResult d8TestRunResult =
testForD8()
@@ -87,8 +84,6 @@
@Test
public void testCustomCollectionR8() throws Exception {
- expectThrowsWithHorizontalClassMergingIf(
- shrinkDesugaredLibrary && parameters.getApiLevel().isLessThan(AndroidApiLevel.N));
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
R8TestRunResult r8TestRunResult =
testForR8(Backend.DEX)
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LibraryEmptySubclassInterfaceTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LibraryEmptySubclassInterfaceTest.java
index 98e1917..ced1c31 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LibraryEmptySubclassInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LibraryEmptySubclassInterfaceTest.java
@@ -8,7 +8,6 @@
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@@ -58,9 +57,7 @@
return;
}
String keepRules = keepRuleConsumer.get();
- assertThat(keepRules, containsString("-keep class j$.util.Map"));
assertThat(keepRules, containsString("-keep class j$.util.concurrent.ConcurrentHashMap"));
- assertThat(keepRules, containsString("-keep class j$.util.concurrent.ConcurrentMap"));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SummaryStatisticsConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SummaryStatisticsConversionTest.java
index 9bce171..7fe00d5 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SummaryStatisticsConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/SummaryStatisticsConversionTest.java
@@ -66,7 +66,6 @@
@Test
public void testStatsD8() throws Exception {
- expectThrowsWithHorizontalClassMergingIf(shrinkDesugaredLibrary && shrinkDesugaredLibrary);
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForD8()
.setMinApi(parameters.getApiLevel())
@@ -86,7 +85,6 @@
@Test
public void testStatsR8() throws Exception {
- expectThrowsWithHorizontalClassMergingIf(shrinkDesugaredLibrary && shrinkDesugaredLibrary);
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
testForR8(parameters.getBackend())
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/AllMapsTestClass.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/AllMapsTestClass.java
new file mode 100644
index 0000000..4c15672
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/AllMapsTestClass.java
@@ -0,0 +1,347 @@
+// 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.desugar.desugaredlibrary.gson;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+public class AllMapsTestClass {
+ // Program class extending ConcurrentHashMap.
+ static class NullableConcurrentHashMap<K, V> extends ConcurrentHashMap<K, V> {
+ NullableConcurrentHashMap() {
+ super();
+ }
+
+ @SuppressWarnings("NullableProblems")
+ @Override
+ public V put(K key, V value) {
+ if (key == null || value == null) {
+ return null;
+ }
+ return super.put(key, value);
+ }
+
+ @Override
+ public void putAll(Map<? extends K, ? extends V> m) {
+ for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+ // Program class extending the library class HashMap, implementing Map.
+ static class NullableHashMap<K, V> extends HashMap<K, V> {
+ NullableHashMap() {
+ super();
+ }
+
+ @SuppressWarnings("NullableProblems")
+ @Override
+ public V put(K key, V value) {
+ if (key == null || value == null) {
+ return null;
+ }
+ return super.put(key, value);
+ }
+
+ @Override
+ public void putAll(Map<? extends K, ? extends V> m) {
+ for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+ // Program class implementing Map.
+ static class NullableMap<K, V> implements Map<K, V> {
+ private HashMap<K, V> map = new HashMap<>();
+
+ NullableMap() {
+ super();
+ }
+
+ @Override
+ public int size() {
+ return map.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return map.containsKey(key);
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ return map.containsValue(value);
+ }
+
+ @Override
+ public V get(Object key) {
+ return map.get(key);
+ }
+
+ @Override
+ public V put(K key, V value) {
+ return map.put(key, value);
+ }
+
+ @Override
+ public V remove(Object key) {
+ return map.remove(key);
+ }
+
+ @Override
+ public void putAll(Map<? extends K, ? extends V> m) {
+ for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ @Override
+ public void clear() {
+ map.clear();
+ }
+
+ @Override
+ public Set<K> keySet() {
+ return map.keySet();
+ }
+
+ @Override
+ public Collection<V> values() {
+ return map.values();
+ }
+
+ @Override
+ public Set<Entry<K, V>> entrySet() {
+ return map.entrySet();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof NullableMap)) return false;
+ NullableMap<?, ?> that = (NullableMap<?, ?>) o;
+ return map.equals(that.map);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(map);
+ }
+ }
+ // Program class implementing ConcurrentMap.
+ static class NullableConcurrentMap<K, V> implements ConcurrentMap<K, V> {
+ private HashMap<K, V> map = new HashMap<>();
+
+ NullableConcurrentMap() {
+ super();
+ }
+
+ @Override
+ public int size() {
+ return map.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return map.containsKey(key);
+ }
+
+ @Override
+ public boolean containsValue(Object value) {
+ return map.containsValue(value);
+ }
+
+ @Override
+ public V get(Object key) {
+ return map.get(key);
+ }
+
+ @Override
+ public V put(K key, V value) {
+ return map.put(key, value);
+ }
+
+ @Override
+ public V remove(Object key) {
+ return map.remove(key);
+ }
+
+ @Override
+ public void putAll(Map<? extends K, ? extends V> m) {
+ for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+
+ @Override
+ public void clear() {
+ map.clear();
+ }
+
+ @Override
+ public Set<K> keySet() {
+ return map.keySet();
+ }
+
+ @Override
+ public Collection<V> values() {
+ return map.values();
+ }
+
+ @Override
+ public Set<Entry<K, V>> entrySet() {
+ return map.entrySet();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof NullableConcurrentMap)) return false;
+ NullableConcurrentMap<?, ?> that = (NullableConcurrentMap<?, ?>) o;
+ return map.equals(that.map);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(map);
+ }
+
+ @Override
+ public V putIfAbsent(K key, V value) {
+ return null;
+ }
+
+ @Override
+ public boolean remove(Object key, Object value) {
+ return false;
+ }
+
+ @Override
+ public boolean replace(K key, V oldValue, V newValue) {
+ return false;
+ }
+
+ @Override
+ public V replace(K key, V value) {
+ return null;
+ }
+ }
+
+ static class Data {
+ final int id;
+ final String name;
+
+ Data(int id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Data)) return false;
+ Data data = (Data) o;
+ return id == data.id && name.equals(data.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, name);
+ }
+
+ @Override
+ public String toString() {
+ return "Data{" + "id=" + id + ", name='" + name + '\'' + '}';
+ }
+ }
+
+ public static void main(String[] args) {
+ Gson gson = new Gson();
+
+ HashMap<Integer, Data> hashMap = new HashMap<>();
+ NullableHashMap<Integer, Data> nullableHashMap = new NullableHashMap<>();
+ NullableMap<Integer, Data> nullableMap = new NullableMap<>();
+ NullableConcurrentMap<Integer, Data> nullableConcurrentMap = new NullableConcurrentMap<>();
+ ConcurrentHashMap<Integer, Data> concurrentHashMap = new ConcurrentHashMap<>();
+ NullableConcurrentHashMap<Integer, Data> nullableConcurrentHashMap =
+ new NullableConcurrentHashMap<>();
+
+ fillMap(hashMap);
+ fillMap(nullableHashMap);
+ fillMap(nullableMap);
+ fillMap(nullableConcurrentMap);
+ fillMap(concurrentHashMap);
+ fillMap(nullableConcurrentHashMap);
+
+ // Serialization.
+ String hashMapJson = gson.toJson(hashMap);
+ String nullableHashMapJson = gson.toJson(nullableHashMap);
+ String nullableMapJson = gson.toJson(nullableMap);
+ String nullableConcurrentMapJson = gson.toJson(nullableConcurrentMap);
+ String concurrentHashMapJson = gson.toJson(concurrentHashMap);
+ String nullableConcurrentHashMapJson = gson.toJson(nullableConcurrentHashMap);
+
+ // Deserialization.
+ Type hashMapType = new TypeToken<HashMap<Integer, Data>>() {}.getType();
+ HashMap<Integer, Data> hashMapDeserialized = gson.fromJson(hashMapJson, hashMapType);
+ Type nullableHashMapType = new TypeToken<NullableHashMap<Integer, Data>>() {}.getType();
+ NullableHashMap<Integer, Data> nullableHashMapDeserialized =
+ gson.fromJson(nullableHashMapJson, nullableHashMapType);
+ Type nullableMapType = new TypeToken<NullableMap<Integer, Data>>() {}.getType();
+ NullableMap<Integer, Data> nullableMapDeserialized =
+ gson.fromJson(nullableMapJson, nullableMapType);
+ Type nullableConcurrentMapType =
+ new TypeToken<NullableConcurrentMap<Integer, Data>>() {}.getType();
+ NullableConcurrentMap<Integer, Data> nullableConcurrentMapDeserialized =
+ gson.fromJson(nullableConcurrentMapJson, nullableConcurrentMapType);
+ Type concurrentHashMapType = new TypeToken<ConcurrentHashMap<Integer, Data>>() {}.getType();
+ ConcurrentHashMap<Integer, Data> concurrentHashMapDeserialized =
+ gson.fromJson(concurrentHashMapJson, concurrentHashMapType);
+ Type nullableConcurrentHashMapType =
+ new TypeToken<NullableConcurrentHashMap<Integer, Data>>() {}.getType();
+ NullableConcurrentHashMap<Integer, Data> nullableConcurrentHashMapDeserialized =
+ gson.fromJson(nullableConcurrentHashMapJson, nullableConcurrentHashMapType);
+
+ // Printing.
+ System.out.println(hashMap.getClass() == hashMapDeserialized.getClass());
+ System.out.println(hashMap.equals(hashMapDeserialized));
+ System.out.println(nullableHashMap.getClass() == nullableHashMapDeserialized.getClass());
+ System.out.println(nullableHashMap.equals(nullableHashMapDeserialized));
+ System.out.println(nullableMap.getClass() == nullableMapDeserialized.getClass());
+ System.out.println(nullableMap.equals(nullableMapDeserialized));
+ System.out.println(
+ nullableConcurrentMap.getClass() == nullableConcurrentMapDeserialized.getClass());
+ System.out.println(nullableConcurrentMap.equals(nullableConcurrentMapDeserialized));
+ System.out.println(concurrentHashMap.getClass() == concurrentHashMapDeserialized.getClass());
+ System.out.println(concurrentHashMap.equals(concurrentHashMapDeserialized));
+ System.out.println(
+ nullableConcurrentHashMap.getClass() == nullableConcurrentHashMapDeserialized.getClass());
+ System.out.println(nullableConcurrentHashMap.equals(nullableConcurrentHashMapDeserialized));
+ }
+
+ public static void fillMap(Map<Integer, Data> map) {
+ map.put(1, new Data(1, "a"));
+ map.put(2, new Data(2, "b"));
+ map.put(3, new Data(3, "c"));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LibrarySubclassInterfaceTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GetGenericInterfaceTest.java
similarity index 97%
rename from src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LibrarySubclassInterfaceTest.java
rename to src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GetGenericInterfaceTest.java
index 29bb1cf..249f26f 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LibrarySubclassInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GetGenericInterfaceTest.java
@@ -1,14 +1,14 @@
// 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
+// 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;
+package com.android.tools.r8.desugar.desugaredlibrary.gson;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.utils.BooleanUtils;
import dalvik.system.PathClassLoader;
import java.sql.SQLDataException;
@@ -32,7 +32,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class LibrarySubclassInterfaceTest extends DesugaredLibraryTestBase {
+public class GetGenericInterfaceTest extends DesugaredLibraryTestBase {
private final TestParameters parameters;
private final boolean shrinkDesugaredLibrary;
@@ -43,7 +43,7 @@
BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
}
- public LibrarySubclassInterfaceTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ public GetGenericInterfaceTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
this.parameters = parameters;
}
@@ -53,7 +53,7 @@
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
String stdOut =
testForD8()
- .addInnerClasses(LibrarySubclassInterfaceTest.class)
+ .addInnerClasses(GetGenericInterfaceTest.class)
.setMinApi(parameters.getApiLevel())
.enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
.compile()
@@ -73,7 +73,7 @@
KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
String stdOut =
testForR8(Backend.DEX)
- .addInnerClasses(LibrarySubclassInterfaceTest.class)
+ .addInnerClasses(GetGenericInterfaceTest.class)
.addKeepMainRule(Executor.class)
.noMinification()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonAllMapsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonAllMapsTest.java
new file mode 100644
index 0000000..45e6af7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonAllMapsTest.java
@@ -0,0 +1,77 @@
+// 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.desugar.desugaredlibrary.gson;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
+import org.junit.Assume;
+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 GsonAllMapsTest extends GsonDesugaredLibraryTestBase {
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+ private static final String[] EXPECTED_RESULT =
+ new String[] {
+ "true", "true", "true", "true", "true", "true", "true", "true", "true", "true", "true",
+ "true"
+ };
+
+ @Parameters(name = "shrinkDesugaredLibrary: {0}, runtime: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ public GsonAllMapsTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testGsonMapD8() throws Exception {
+ Assume.assumeTrue(requiresEmulatedInterfaceCoreLibDesugaring(parameters));
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8(parameters.getBackend())
+ .addProgramClassesAndInnerClasses(AllMapsTestClass.class)
+ .addProgramFiles(GSON_2_8_1_JAR)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), AllMapsTestClass.class)
+ .assertSuccessWithOutputLines(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testGsonMapR8() throws Exception {
+ Assume.assumeTrue(requiresEmulatedInterfaceCoreLibDesugaring(parameters));
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForR8(parameters.getBackend())
+ .addProgramClassesAndInnerClasses(AllMapsTestClass.class)
+ .addProgramFiles(GSON_2_8_1_JAR)
+ .addKeepMainRule(AllMapsTestClass.class)
+ .addKeepRuleFiles(GSON_CONFIGURATION)
+ .allowUnusedProguardConfigurationRules()
+ .allowDiagnosticMessages()
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), AllMapsTestClass.class)
+ .assertSuccessWithOutputLines(EXPECTED_RESULT);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonDesugaredLibraryTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonDesugaredLibraryTestBase.java
new file mode 100644
index 0000000..1904ff7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonDesugaredLibraryTestBase.java
@@ -0,0 +1,14 @@
+// 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.desugar.desugaredlibrary.gson;
+
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public abstract class GsonDesugaredLibraryTestBase extends DesugaredLibraryTestBase {
+ protected static final Path GSON_CONFIGURATION =
+ Paths.get("src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/gson.cfg");
+ protected static final Path GSON_2_8_1_JAR = Paths.get("third_party/iosched_2019/gson-2.8.1.jar");
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonMapTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonMapTest.java
deleted file mode 100644
index fa5c6d3..0000000
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonMapTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-// 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.desugar.desugaredlibrary.gson;
-
-import com.android.tools.r8.R8TestRunResult;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
-import com.android.tools.r8.desugar.desugaredlibrary.gson.TestClasses.TestClass;
-import com.android.tools.r8.utils.BooleanUtils;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.List;
-import org.junit.Assume;
-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 GsonMapTest extends DesugaredLibraryTestBase {
-
- private final TestParameters parameters;
- private final boolean shrinkDesugaredLibrary;
- private static final Path GSON_CONFIGURATION =
- Paths.get("src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/gson.cfg");
- private static final Path GSON_2_8_1_JAR = Paths.get("third_party/iosched_2019/gson-2.8.1.jar");
-
- @Parameters(name = "shrinkDesugaredLibrary: {0}, runtime: {1}")
- public static List<Object[]> data() {
- return buildParameters(
- BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
- }
-
- public GsonMapTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
- this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
- this.parameters = parameters;
- }
-
- @Test
- public void testGsonMap() throws Exception {
- Assume.assumeTrue(requiresEmulatedInterfaceCoreLibDesugaring(parameters));
- KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
- R8TestRunResult runResult =
- testForR8(parameters.getBackend())
- .addProgramClassesAndInnerClasses(TestClasses.class)
- .addProgramFiles(GSON_2_8_1_JAR)
- .addKeepMainRule(TestClass.class)
- .addKeepRuleFiles(GSON_CONFIGURATION)
- .allowUnusedProguardConfigurationRules()
- .addOptionsModification(opt -> opt.ignoreMissingClasses = true)
- .allowDiagnosticMessages()
- .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
- .setMinApi(parameters.getApiLevel())
- .compile()
- .addDesugaredCoreLibraryRunClassPath(
- this::buildDesugaredLibrary,
- parameters.getApiLevel(),
- keepRuleConsumer.get(),
- shrinkDesugaredLibrary)
- .run(parameters.getRuntime(), TestClass.class);
- // TODO(b/167649682): Should be always true.
- // .assertSuccessWithOutputLines("true", "true", "true", "true", "true");
- if (shrinkDesugaredLibrary) {
- runResult.assertSuccessWithOutputLines("true", "true", "false", "false", "false");
- } else {
- runResult.assertSuccessWithOutputLines("true", "true", "false", "true", "false");
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonOptionalTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonOptionalTest.java
new file mode 100644
index 0000000..d3fd68a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/GsonOptionalTest.java
@@ -0,0 +1,73 @@
+// 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.desugar.desugaredlibrary.gson;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class GsonOptionalTest extends GsonDesugaredLibraryTestBase {
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+
+ @Parameterized.Parameters(name = "shrinkDesugaredLibrary: {0}, runtime: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ public GsonOptionalTest(boolean shrinkDesugaredLibrary, TestParameters parameters) {
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testGsonOptionalD8() throws Exception {
+ Assume.assumeTrue(requiresEmulatedInterfaceCoreLibDesugaring(parameters));
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForD8(parameters.getBackend())
+ .addProgramClassesAndInnerClasses(OptionalTestClass.class)
+ .addProgramFiles(GSON_2_8_1_JAR)
+ .addOptionsModification(opt -> opt.ignoreMissingClasses = true)
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), OptionalTestClass.class)
+ .assertSuccessWithOutputLines("true", "true");
+ }
+
+ @Test
+ public void testGsonOptionalR8() throws Exception {
+ Assume.assumeTrue(requiresEmulatedInterfaceCoreLibDesugaring(parameters));
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ testForR8(parameters.getBackend())
+ .addProgramClassesAndInnerClasses(OptionalTestClass.class)
+ .addProgramFiles(GSON_2_8_1_JAR)
+ .addKeepMainRule(OptionalTestClass.class)
+ .addKeepRuleFiles(GSON_CONFIGURATION)
+ .allowUnusedProguardConfigurationRules()
+ .addOptionsModification(opt -> opt.ignoreMissingClasses = true)
+ .allowDiagnosticMessages()
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), OptionalTestClass.class)
+ .assertSuccessWithOutputLines("true", "true");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/OptionalTestClass.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/OptionalTestClass.java
new file mode 100644
index 0000000..fbc2c31
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/OptionalTestClass.java
@@ -0,0 +1,95 @@
+// 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.desugar.desugaredlibrary.gson;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.TypeAdapter;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.JsonWriter;
+import java.io.IOException;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Objects;
+import java.util.Optional;
+
+public class OptionalTestClass {
+ static class Data {
+ final int id;
+ final String name;
+
+ Data(int id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Data)) return false;
+ Data data = (Data) o;
+ return id == data.id && name.equals(data.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, name);
+ }
+
+ @Override
+ public String toString() {
+ return "Data{" + "id=" + id + ", name='" + name + '\'' + '}';
+ }
+ }
+
+ static class OptionalAdapter<T> extends TypeAdapter<Optional<T>> {
+ private final TypeAdapter<T> delegate;
+
+ public OptionalAdapter(TypeAdapter<T> delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void write(JsonWriter out, Optional<T> value) throws IOException {
+ if (!value.isPresent()) {
+ out.nullValue();
+ return;
+ }
+ delegate.write(out, value.get());
+ }
+
+ @Override
+ public Optional<T> read(JsonReader in) throws IOException {
+ if (in.peek() == JsonToken.NULL) {
+ in.nextNull();
+ return Optional.empty();
+ }
+ return Optional.of(delegate.read(in));
+ }
+
+ @SuppressWarnings("unchecked")
+ public static OptionalAdapter getInstance(TypeToken typeToken) {
+ TypeAdapter delegate;
+ Type type = typeToken.getType();
+ assert type instanceof ParameterizedType;
+ Type innerType = ((ParameterizedType) type).getActualTypeArguments()[0];
+ delegate = new Gson().getAdapter(TypeToken.get(innerType));
+ return new OptionalAdapter<>(delegate);
+ }
+ }
+
+ public static void main(String[] args) {
+ GsonBuilder builder = new GsonBuilder();
+ builder.registerTypeAdapter(
+ Optional.class, OptionalAdapter.getInstance(new TypeToken<Optional<Data>>() {}));
+ Gson gson = builder.create();
+ Optional<Data> optionalData = Optional.of(new Data(1, "a"));
+ String optionalDataSerialized = gson.toJson(optionalData);
+ Optional<Data> optionalDataDeserialized = gson.fromJson(optionalDataSerialized, Optional.class);
+ System.out.println(optionalData.getClass() == optionalDataDeserialized.getClass());
+ System.out.println(optionalData.equals(optionalDataDeserialized));
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/TestClasses.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/TestClasses.java
deleted file mode 100644
index 7b57814..0000000
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/TestClasses.java
+++ /dev/null
@@ -1,221 +0,0 @@
-// 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.desugar.desugaredlibrary.gson;
-
-import com.google.gson.Gson;
-import com.google.gson.reflect.TypeToken;
-import java.lang.reflect.Type;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-public class TestClasses {
-
- // Program class extending ConcurrentHashMap.
- static class NullableConcurrentHashMap<K, V> extends ConcurrentHashMap<K, V> {
- NullableConcurrentHashMap() {
- super();
- }
-
- @SuppressWarnings("NullableProblems")
- @Override
- public V put(K key, V value) {
- if (key == null || value == null) {
- return null;
- }
- return super.put(key, value);
- }
-
- @Override
- public void putAll(Map<? extends K, ? extends V> m) {
- for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
- put(entry.getKey(), entry.getValue());
- }
- }
- }
-
- // Program class extending the library class HashMap, implementing Map.
- static class NullableHashMap<K, V> extends HashMap<K, V> {
- NullableHashMap() {
- super();
- }
-
- @SuppressWarnings("NullableProblems")
- @Override
- public V put(K key, V value) {
- if (key == null || value == null) {
- return null;
- }
- return super.put(key, value);
- }
-
- @Override
- public void putAll(Map<? extends K, ? extends V> m) {
- for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
- put(entry.getKey(), entry.getValue());
- }
- }
- }
-
- // Program class implementing Map.
- static class NullableMap<K, V> implements Map<K, V> {
- private Map<K, V> map = new HashMap<>();
-
- NullableMap() {
- super();
- }
-
- @Override
- public int size() {
- return map.size();
- }
-
- @Override
- public boolean isEmpty() {
- return map.isEmpty();
- }
-
- @Override
- public boolean containsKey(Object key) {
- return map.containsKey(key);
- }
-
- @Override
- public boolean containsValue(Object value) {
- return map.containsValue(value);
- }
-
- @Override
- public V get(Object key) {
- return map.get(key);
- }
-
- @Nullable
- @Override
- public V put(K key, V value) {
- return map.put(key, value);
- }
-
- @Override
- public V remove(Object key) {
- return map.remove(key);
- }
-
- @Override
- public void putAll(@NotNull Map<? extends K, ? extends V> m) {
- for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
- put(entry.getKey(), entry.getValue());
- }
- }
-
- @Override
- public void clear() {
- map.clear();
- }
-
- @NotNull
- @Override
- public Set<K> keySet() {
- return map.keySet();
- }
-
- @NotNull
- @Override
- public Collection<V> values() {
- return map.values();
- }
-
- @NotNull
- @Override
- public Set<Entry<K, V>> entrySet() {
- return map.entrySet();
- }
- }
-
- static class Data {
- final int id;
- final String name;
-
- Data(int id, String name) {
- this.id = id;
- this.name = name;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof Data)) return false;
- Data data = (Data) o;
- return id == data.id && name.equals(data.name);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(id, name);
- }
-
- @Override
- public String toString() {
- return "Data{" + "id=" + id + ", name='" + name + '\'' + '}';
- }
- }
-
- static class TestClass {
-
- public static void main(String[] args) {
- Gson gson = new Gson();
- HashMap<Integer, Data> hashMap = new HashMap<>();
- NullableHashMap<Integer, Data> nullableHashMap = new NullableHashMap<>();
- NullableMap<Integer, Data> nullableMap = new NullableMap<>();
- ConcurrentHashMap<Integer, Data> concurrentHashMap = new ConcurrentHashMap<>();
- NullableConcurrentHashMap<Integer, Data> nullableConcurrentHashMap =
- new NullableConcurrentHashMap<>();
-
- fillMap(hashMap);
- fillMap(nullableHashMap);
- fillMap(nullableMap);
- fillMap(concurrentHashMap);
- fillMap(nullableConcurrentHashMap);
-
- String hashMapJson = gson.toJson(hashMap);
- String nullableHashMapJson = gson.toJson(nullableHashMap);
- String nullableMapJson = gson.toJson(nullableMap);
- String concurrentHashMapJson = gson.toJson(concurrentHashMap);
- String nullableConcurrentHashMapJson = gson.toJson(nullableConcurrentHashMap);
-
- Type hashMapType = new TypeToken<HashMap<Integer, Data>>() {}.getType();
- HashMap<Integer, Data> hashMapDeserialized = gson.fromJson(hashMapJson, hashMapType);
- Type nullableHashMapType = new TypeToken<HashMap<Integer, Data>>() {}.getType();
- HashMap<Integer, Data> nullableHashMapDeserialized =
- gson.fromJson(nullableHashMapJson, nullableHashMapType);
- Type nullableMapType = new TypeToken<HashMap<Integer, Data>>() {}.getType();
- HashMap<Integer, Data> nullableMapDeserialized =
- gson.fromJson(nullableMapJson, nullableMapType);
- Type concurrentHashMapType = new TypeToken<ConcurrentHashMap<Integer, Data>>() {}.getType();
- ConcurrentHashMap<Integer, Data> concurrentHashMapDeserialized =
- gson.fromJson(concurrentHashMapJson, concurrentHashMapType);
- Type nullableConcurrentHashMapType =
- new TypeToken<NullableConcurrentHashMap<Integer, Data>>() {}.getType();
- NullableConcurrentHashMap<Integer, Data> nullableConcurrentHashMapDeserialized =
- gson.fromJson(nullableConcurrentHashMapJson, nullableConcurrentHashMapType);
-
- System.out.println(hashMap.equals(hashMapDeserialized));
- System.out.println(nullableHashMap.equals(nullableHashMapDeserialized));
- System.out.println(nullableMap.equals(nullableMapDeserialized));
- System.out.println(concurrentHashMap.equals(concurrentHashMapDeserialized));
- System.out.println(nullableConcurrentHashMap.equals(nullableConcurrentHashMapDeserialized));
- }
-
- public static void fillMap(Map<Integer, Data> map) {
- map.put(1, new Data(1, "a"));
- map.put(2, new Data(2, "b"));
- map.put(3, new Data(3, "c"));
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/gson.cfg b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/gson.cfg
index 51f38a6..da7bcba 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/gson.cfg
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/gson/gson.cfg
@@ -1,9 +1,8 @@
# 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.
-
-# Gson uses generic type information stored in a class file when working with fields. R8
-# removes such information by default, so configure it to keep all of it.
+# Gson uses generic type information stored in a class file when working with fields.
+# R8 removes such information by default, so configure it to keep all of it.
-keepattributes Signature
-keepattributes EnclosingMethod
-keepattributes InnerClasses
@@ -14,10 +13,13 @@
# Gson specific classes
-dontwarn sun.misc.Unsafe
-#-keep class com.google.gson.stream.** { *; }
# Application classes that will be serialized/deserialized over Gson
--keep class com.android.tools.r8.desugar.desugaredlibrary.gson.GsonMapTest.Data { <fields>; }
--keep class com.android.tools.r8.desugar.desugaredlibrary.gson.GsonMapTest.NullableConcurrentHashMap { <fields>; }
+-keep class com.android.tools.r8.desugar.desugaredlibrary.gson.AllMapsTestClasses$Data { <fields>; }
+-keep class com.android.tools.r8.desugar.desugaredlibrary.gson.AllMapsTestClasses$NullableConcurrentHashMap
+-keep class com.android.tools.r8.desugar.desugaredlibrary.gson.AllMapsTestClasses$NullableHashMap
+-keep class com.android.tools.r8.desugar.desugaredlibrary.gson.AllMapsTestClasses$NullableMap
+-keep class com.android.tools.r8.desugar.desugaredlibrary.gson.AllMapsTestClasses$NullableConcurrentMap
+-keep class com.android.tools.r8.desugar.desugaredlibrary.gson.OptionalTestClass$Data { <fields>; }
# Prevent R8 from stripping interface information from TypeAdapter, TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
index 762916e..e1d892d 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/Java11R8CompilationTest.java
@@ -47,6 +47,7 @@
@Test
public void testR8CompiledWithR8() throws Exception {
+ expectThrowsWithHorizontalClassMerging();
testForR8(parameters.getBackend())
.setMinApi(parameters.getApiLevel())
.addProgramFiles(ToolHelper.R8_WITH_RELOCATED_DEPS_11_JAR)
diff --git a/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java b/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
index c72cf47..e5b4c08 100644
--- a/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
+++ b/src/test/java/com/android/tools/r8/dex/JumboStringProcessing.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramMethod;
@@ -155,7 +156,12 @@
MethodAccessFlags flags = MethodAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC, false);
DexEncodedMethod method =
new DexEncodedMethod(
- null, flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), code);
+ null,
+ flags,
+ MethodTypeSignature.noSignature(),
+ DexAnnotationSet.empty(),
+ ParameterAnnotationsList.empty(),
+ code);
return new JumboStringRewriter(method, string, factory).rewrite();
}
}
diff --git a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
index b134ae5..81ae312 100644
--- a/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
+++ b/src/test/java/com/android/tools/r8/dex/SharedClassWritingTest.java
@@ -26,6 +26,7 @@
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.LazyLoadedDexApplication;
@@ -87,6 +88,7 @@
dexItemFactory.createMethod(
holder, dexItemFactory.createProto(dexItemFactory.voidType), "theMethod"),
MethodAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC, false),
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
code);
@@ -113,7 +115,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
- ClassSignature.NO_CLASS_SIGNATURE,
+ ClassSignature.noSignature(),
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
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 2519477..0011245 100644
--- a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
@@ -23,13 +23,13 @@
import com.android.tools.r8.graph.GenericSignature.TypeSignature;
import com.android.tools.r8.graph.GenericSignature.WildcardIndicator;
import com.android.tools.r8.graph.GenericSignatureTestClassA.I;
-import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.util.List;
import java.util.function.Consumer;
@@ -140,7 +140,7 @@
DexEncodedField field = yyInZZ.getField();
assertNotNull(field);
- fieldTypeSignature = field.getFieldSignature();
+ fieldTypeSignature = field.getGenericSignature();
assertTrue(fieldTypeSignature.hasSignature());
// field type: A$Y$YY
@@ -157,13 +157,7 @@
method = newYY.getMethod();
assertNotNull(method);
- methodTypeSignature =
- GenericSignature.parseMethodSignature(
- method.qualifiedName(),
- getGenericSignature(method, appView),
- Origin.unknown(),
- appView.dexItemFactory(),
- appView.options().reporter);
+ methodTypeSignature = method.getGenericSignature();
assertNotNull(methodTypeSignature);
assertEquals(1, methodTypeSignature.formalTypeParameters.size());
@@ -205,13 +199,7 @@
method = convertToYY.getMethod();
assertNotNull(method);
- methodTypeSignature =
- GenericSignature.parseMethodSignature(
- method.qualifiedName(),
- getGenericSignature(method, appView),
- Origin.unknown(),
- appView.dexItemFactory(),
- appView.options().reporter);
+ methodTypeSignature = method.getGenericSignature();
assertNotNull(methodTypeSignature);
// return type: Function<A$Y$ZZ<TT>, A$Y$YY>
@@ -248,13 +236,7 @@
assertNotNull(method);
// return type: void
- methodTypeSignature =
- GenericSignature.parseMethodSignature(
- method.qualifiedName(),
- getGenericSignature(method, appView),
- Origin.unknown(),
- appView.dexItemFactory(),
- appView.options().reporter);
+ methodTypeSignature = method.getGenericSignature();
assertNotNull(methodTypeSignature);
returnType = methodTypeSignature.returnType();
assertTrue(returnType.isVoidDescriptor());
@@ -302,7 +284,7 @@
checkMethodWildCard(y.uniqueMethodWithName("bar"), appView, WildcardIndicator.NEGATIVE);
// Check for star
checkFieldTypeSignature(
- y.uniqueMethodWithName("baz"),
+ y.uniqueMethodWithName("baz").asFoundMethodSubject(),
appView,
typeSignature -> {
assertTrue(typeSignature.isStar());
@@ -314,7 +296,7 @@
AppView<AppInfoWithLiveness> appView,
WildcardIndicator indicator) {
checkFieldTypeSignature(
- methodSubject,
+ methodSubject.asFoundMethodSubject(),
appView,
typeSignature -> {
assertTrue(typeSignature.isTypeVariableSignature());
@@ -323,16 +305,10 @@
}
private void checkFieldTypeSignature(
- MethodSubject methodSubject,
+ FoundMethodSubject methodSubject,
AppView<AppInfoWithLiveness> appView,
Consumer<FieldTypeSignature> fieldTypeConsumer) {
- MethodTypeSignature methodTypeSignature =
- GenericSignature.parseMethodSignature(
- methodSubject.getOriginalName(),
- getGenericSignature(methodSubject.getMethod(), appView),
- Origin.unknown(),
- appView.dexItemFactory(),
- appView.options().reporter);
+ MethodTypeSignature methodTypeSignature = methodSubject.getMethod().getGenericSignature();
TypeSignature typeSignature = methodTypeSignature.returnType.typeSignature;
FieldTypeSignature fieldTypeSignature = typeSignature.asFieldTypeSignature();
assertTrue(fieldTypeSignature.isClassTypeSignature());
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 fade8ed..e02f313 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
@@ -5,7 +5,6 @@
package com.android.tools.r8.graph.genericsignature;
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
-import static com.android.tools.r8.graph.GenericSignature.ClassSignature.NO_CLASS_SIGNATURE;
import static com.google.common.base.Predicates.alwaysFalse;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
@@ -111,9 +110,9 @@
@Test
public void testSuperClassError() {
- TestDiagnosticMessages testDiagnosticMessages = testParsingAndPrintingError("Lfoo/bar/baz");
- testDiagnosticMessages.assertAllWarningsMatch(
- diagnosticMessage(containsString("Invalid signature 'Lfoo/bar/baz'")));
+ testParsingAndPrintingError("Lfoo/bar/baz")
+ .assertAllWarningsMatch(
+ diagnosticMessage(containsString("Invalid signature 'Lfoo/bar/baz'")));
}
@Test
@@ -143,7 +142,12 @@
}
@Test
- public void testFormalTypeParameters2() {}
+ public void testFormalTypeParameterEmptyInterfaceError() {
+ testParsingAndPrintingError("<T:Ljava/lang/Object;:>Lfoo/bar/baz<TT;>;")
+ .assertAllWarningsMatch(
+ diagnosticMessage(
+ containsString("Invalid signature '<T:Ljava/lang/Object;:>Lfoo/bar/baz<TT;>;'")));
+ }
@Test
public void testFormalTypeParametersEmptyError() {
@@ -171,7 +175,7 @@
Origin.unknown(),
new DexItemFactory(),
new Reporter(testDiagnosticMessages));
- assertEquals(NO_CLASS_SIGNATURE, parsed);
+ assertEquals(ClassSignature.noSignature(), parsed);
return testDiagnosticMessages;
}
}
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/FieldSignatureTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/FieldSignatureTest.java
index b0200fa..b6e34ad 100644
--- a/src/test/java/com/android/tools/r8/graph/genericsignature/FieldSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/FieldSignatureTest.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.graph.genericsignature;
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
-import static com.android.tools.r8.graph.GenericSignature.NO_FIELD_TYPE_SIGNATURE;
import static com.google.common.base.Predicates.alwaysFalse;
import static org.hamcrest.CoreMatchers.containsString;
import static org.junit.Assert.assertEquals;
@@ -104,7 +103,7 @@
Origin.unknown(),
new DexItemFactory(),
new Reporter(testDiagnosticMessages));
- assertEquals(NO_FIELD_TYPE_SIGNATURE, parsed);
+ assertEquals(FieldTypeSignature.noSignature(), parsed);
return testDiagnosticMessages;
}
}
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/FormalTypeParameterClassBoundPruneTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/FormalTypeParameterClassBoundPruneTest.java
new file mode 100644
index 0000000..1221f73
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/FormalTypeParameterClassBoundPruneTest.java
@@ -0,0 +1,115 @@
+// 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.graph.genericsignature;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.lang.reflect.TypeVariable;
+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 FormalTypeParameterClassBoundPruneTest extends TestBase {
+
+ private final TestParameters parameters;
+ private final String INTERFACE_BOUND =
+ "L" + DescriptorUtils.getBinaryNameFromJavaType(Interface.class.getTypeName()) + "<TT;>;";
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ public FormalTypeParameterClassBoundPruneTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testRuntimeWithNoPrunedSuperType() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClassFileData(
+ transformer(Main.class).removeInnerClasses().transform(),
+ transformer(Super.class).removeInnerClasses().transform(),
+ transformer(Interface.class).removeInnerClasses().transform())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(
+ Super.class.getTypeName() + "<T>", Interface.class.getTypeName() + "<T>");
+ }
+
+ @Test
+ public void testRuntimeWithPrunedSuperType() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClassFileData(
+ transformer(Main.class)
+ .removeInnerClasses()
+ .setGenericSignature("<T::" + INTERFACE_BOUND + ">Ljava/lang/Object;")
+ .transform(),
+ transformer(Super.class).removeInnerClasses().transform(),
+ transformer(Interface.class).removeInnerClasses().transform())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(Interface.class.getTypeName() + "<T>");
+ }
+
+ @Test
+ public void testRuntimeWithObjectSuperType() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClassFileData(
+ transformer(Main.class)
+ .removeInnerClasses()
+ .setGenericSignature(
+ "<T:Ljava/lang/Object;:" + INTERFACE_BOUND + ">Ljava/lang/Object;")
+ .transform(),
+ transformer(Super.class).removeInnerClasses().transform(),
+ transformer(Interface.class).removeInnerClasses().transform())
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(
+ "class java.lang.Object", Interface.class.getTypeName() + "<T>");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(
+ transformer(Main.class).removeInnerClasses().transform(),
+ transformer(Super.class).removeInnerClasses().transform(),
+ transformer(Interface.class).removeInnerClasses().transform())
+ .addKeepMainRule(Main.class)
+ .addKeepClassRules(Interface.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepAttributes(
+ ProguardKeepAttributes.SIGNATURE,
+ ProguardKeepAttributes.INNER_CLASSES,
+ ProguardKeepAttributes.ENCLOSING_METHOD)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(Interface.class.getTypeName() + "<T>")
+ .inspect(
+ inspector -> {
+ assertThat(inspector.clazz(Super.class), not(isPresent()));
+ });
+ }
+
+ public static class Super<T> {}
+
+ public interface Interface<T> {}
+
+ public static class Main<T extends Super<T> & Interface<T>> {
+
+ public static void main(String[] args) {
+ TypeVariable<Class<Main>> typeParameter = Main.class.getTypeParameters()[0];
+ for (java.lang.reflect.Type bound : typeParameter.getBounds()) {
+ System.out.println(bound.toString());
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/MethodSignatureTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/MethodSignatureTest.java
new file mode 100644
index 0000000..c751918
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/MethodSignatureTest.java
@@ -0,0 +1,136 @@
+// 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.graph.genericsignature;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+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.assertThrows;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
+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.GenericSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
+import com.android.tools.r8.graph.GenericSignaturePrinter;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.Reporter;
+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 MethodSignatureTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public MethodSignatureTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testEmptyVoid() {
+ testParsingAndPrintingEqual("()V");
+ }
+
+ @Test
+ public void testThrowsOnly() {
+ testParsingAndPrintingError("^TT;")
+ .assertAllErrorsMatch(
+ diagnosticMessage(containsString("Invalid signature '^TT;' for method A.")));
+ }
+
+ @Test
+ public void testArguments() {
+ testParsingAndPrintingEqual("(TT;Lfoo/bar/Baz;[I)V");
+ }
+
+ @Test
+ public void testReturnType() {
+ testParsingAndPrintingEqual("()Lfoo/Baz;");
+ }
+
+ @Test
+ public void testTypeVariableReturn() {
+ testParsingAndPrintingEqual("()TR;");
+ }
+
+ @Test
+ public void testThrowsSingle() {
+ testParsingAndPrintingEqual("(Lfoo/Bar;)V^Ljava/lang/Exception;");
+ }
+
+ @Test
+ public void testThrowsMultiple() {
+ testParsingAndPrintingEqual("(Lfoo/Bar;)V^Ljava/lang/Exception;^TT;^Lfoo/bar/Baz;");
+ }
+
+ @Test
+ public void testThrowsMultipleError() {
+ // TODO(b/170287583): This should throw an error.
+ assertThrows(
+ AssertionError.class,
+ () -> testParsingAndPrintingEqual("(Lfoo/Bar;)V^Ljava/lang/Exception;^TT;Lfoo/bar/Baz;"));
+ }
+
+ @Test
+ public void testTypeArgument() {
+ testParsingAndPrintingEqual("<T:Ljava/lang/Object;>([I)V");
+ }
+
+ @Test
+ public void testTypeArgumentMultiple() {
+ testParsingAndPrintingEqual("<T:Ljava/lang/Object;R::LConsumer<TT;>;>([I)V");
+ }
+
+ @Test
+ public void testTypeArgumentMultipleThrows() {
+ testParsingAndPrintingEqual(
+ "<T:Ljava/lang/Object;R::LConsumer<TT;>;>([Lfoo<TR;>;)Lbaz<TR;>;^TR;^Lfoo<TT;>;");
+ }
+
+ @Test
+ public void testFormalTypeParameterEmptyInterfaceError() {
+ testParsingAndPrintingError("<T:Ljava/lang/Object;:>Lfoo/bar/baz<TT;>;")
+ .assertAllWarningsMatch(
+ diagnosticMessage(
+ containsString("Invalid signature '<T:Ljava/lang/Object;:>Lfoo/bar/baz<TT;>;'")));
+ }
+
+ private void testParsingAndPrintingEqual(String signature) {
+ MethodTypeSignature parsed =
+ GenericSignature.parseMethodSignature(
+ "A", signature, Origin.unknown(), new DexItemFactory(), new Reporter());
+ GenericSignaturePrinter genericSignaturePrinter =
+ new GenericSignaturePrinter(NamingLens.getIdentityLens(), alwaysFalse());
+ genericSignaturePrinter.visitMethodSignature(parsed);
+ String outSignature = genericSignaturePrinter.toString();
+ assertEquals(signature, outSignature);
+ }
+
+ private TestDiagnosticMessages testParsingAndPrintingError(String signature) {
+ TestDiagnosticMessagesImpl testDiagnosticMessages = new TestDiagnosticMessagesImpl();
+ MethodTypeSignature parsed =
+ GenericSignature.parseMethodSignature(
+ "A",
+ signature,
+ Origin.unknown(),
+ new DexItemFactory(),
+ new Reporter(testDiagnosticMessages));
+ assertEquals(MethodTypeSignature.noSignature(), parsed);
+ return testDiagnosticMessages;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java b/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java
index 99b186f..4e41e64 100644
--- a/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramMethod;
@@ -36,7 +37,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
- ClassSignature.NO_CLASS_SIGNATURE,
+ ClassSignature.noSignature(),
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
@@ -55,6 +56,7 @@
new DexEncodedMethod(
signature,
MethodAccessFlags.fromDexAccessFlags(0),
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
null));
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/PruneNotNullBranchD8Test.java b/src/test/java/com/android/tools/r8/ir/optimize/PruneNotNullBranchD8Test.java
index 870787f..ae08b92 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/PruneNotNullBranchD8Test.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/PruneNotNullBranchD8Test.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
@@ -48,8 +49,13 @@
assertThat(main, isPresent());
MethodSubject mainMethod = main.mainMethod();
assertThat(mainMethod, isPresent());
- // TODO(b/170060113): We should be able to remove the throw.
- assertTrue(mainMethod.streamInstructions().anyMatch(InstructionSubject::isThrow));
+ if (parameters.isDexRuntime()) {
+ assertFalse(mainMethod.streamInstructions().anyMatch(InstructionSubject::isThrow));
+ } else {
+ assert parameters.isCfRuntime();
+ // We are not going through IR when running in CF
+ assertTrue(mainMethod.streamInstructions().anyMatch(InstructionSubject::isThrow));
+ }
});
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromStaticInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromStaticInterfaceMethodTest.java
new file mode 100644
index 0000000..ec4c859
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromStaticInterfaceMethodTest.java
@@ -0,0 +1,114 @@
+// 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.optimize.outliner;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
+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.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class OutlineFromStaticInterfaceMethodTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public OutlineFromStaticInterfaceMethodTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .addOptionsModification(
+ options -> {
+ if (parameters.isCfRuntime()) {
+ assert !options.outline.enabled;
+ options.outline.enabled = true;
+ }
+ options.outline.threshold = 2;
+ options.outline.minSize = 2;
+ })
+ .enableInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(this::inspect)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!", "Hello world!");
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject interfaceSubject =
+ parameters.isCfRuntime()
+ || parameters
+ .getApiLevel()
+ .isGreaterThanOrEqualTo(apiLevelWithStaticInterfaceMethodsSupport())
+ ? inspector.clazz(I.class)
+ : inspector.clazz(
+ I.class.getTypeName() + InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX);
+ assertThat(interfaceSubject, isPresent());
+
+ MethodSubject greetMethodSubject = interfaceSubject.uniqueMethodWithName("greet");
+ assertThat(greetMethodSubject, isPresent());
+ assertEquals(
+ 1,
+ greetMethodSubject.streamInstructions().filter(InstructionSubject::isInvokeStatic).count());
+ }
+
+ static class TestClass {
+
+ public static void main(String... args) {
+ greet();
+ I.greet();
+ }
+
+ @NeverInline
+ static void greet() {
+ Greeter.hello();
+ Greeter.world();
+ }
+ }
+
+ interface I {
+
+ @NeverInline
+ static void greet() {
+ Greeter.hello();
+ Greeter.world();
+ }
+ }
+
+ @NeverClassInline
+ public static class Greeter {
+
+ @NeverInline
+ public static void hello() {
+ System.out.print("Hello");
+ }
+
+ @NeverInline
+ public static void world() {
+ System.out.println(" world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/JStyleKotlinLambdaMergingWithEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/JStyleKotlinLambdaMergingWithEnumUnboxingTest.java
new file mode 100644
index 0000000..eb7b242
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/JStyleKotlinLambdaMergingWithEnumUnboxingTest.java
@@ -0,0 +1,122 @@
+// 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.kotlin.lambda;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.ir.optimize.lambda.kotlin.JStyleLambdaGroupIdFactory;
+import com.android.tools.r8.ir.optimize.lambda.kotlin.KotlinLambdaGroupIdFactory;
+import com.android.tools.r8.kotlin.lambda.JStyleKotlinLambdaMergingWithEnumUnboxingTest.Main.EnumUnboxingCandidate;
+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 JStyleKotlinLambdaMergingWithEnumUnboxingTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return TestBase.getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public JStyleKotlinLambdaMergingWithEnumUnboxingTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addDefaultRuntimeLibrary(parameters)
+ .addLibraryFiles(ToolHelper.getKotlinStdlibJar())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options ->
+ options.testing.kotlinLambdaMergerFactoryForClass =
+ this::getKotlinLambdaMergerFactoryForClass)
+ .addHorizontallyMergedLambdaClassesInspector(
+ inspector -> inspector.assertMerged(Lambda1.class, Lambda2.class))
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(EnumUnboxingCandidate.class))
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Lambda1.method()", "Lambda2.method()");
+ }
+
+ private KotlinLambdaGroupIdFactory getKotlinLambdaMergerFactoryForClass(DexProgramClass clazz) {
+ String typeName = clazz.getType().toSourceString();
+ if (typeName.equals(Lambda1.class.getTypeName())
+ || typeName.equals(Lambda2.class.getTypeName())) {
+ return JStyleLambdaGroupIdFactory.getInstance();
+ }
+ return null;
+ }
+
+ static class Main {
+
+ @NeverClassInline
+ public enum EnumUnboxingCandidate {
+ LAMBDA1,
+ LAMBDA2
+ }
+
+ public static void main(String[] args) {
+ accept(createLambda(EnumUnboxingCandidate.LAMBDA1));
+ accept(createLambda(EnumUnboxingCandidate.LAMBDA2));
+ }
+
+ @NeverInline
+ static I createLambda(EnumUnboxingCandidate value) {
+ switch (value) {
+ case LAMBDA1:
+ return new Lambda1();
+ case LAMBDA2:
+ return new Lambda2();
+ default:
+ throw new RuntimeException();
+ }
+ }
+
+ @NeverInline
+ static void accept(I instance) {
+ instance.method();
+ }
+ }
+
+ interface I {
+
+ void method();
+ }
+
+ @NeverClassInline
+ public static final class Lambda1 implements I {
+
+ @NeverInline
+ @Override
+ public final void method() {
+ System.out.println("Lambda1.method()");
+ }
+ }
+
+ @NeverClassInline
+ public static final class Lambda2 implements I {
+
+ @NeverInline
+ @Override
+ public final void method() {
+ System.out.println("Lambda2.method()");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java
new file mode 100644
index 0000000..61abad2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java
@@ -0,0 +1,129 @@
+// 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.kotlin.lambda;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.ir.optimize.lambda.kotlin.KStyleLambdaGroupIdFactory;
+import com.android.tools.r8.ir.optimize.lambda.kotlin.KotlinLambdaGroupIdFactory;
+import com.android.tools.r8.kotlin.lambda.KStyleKotlinLambdaMergingWithEnumUnboxingTest.Main.EnumUnboxingCandidate;
+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 KStyleKotlinLambdaMergingWithEnumUnboxingTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return TestBase.getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public KStyleKotlinLambdaMergingWithEnumUnboxingTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addProgramFiles(ToolHelper.getKotlinStdlibJar())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options ->
+ options.testing.kotlinLambdaMergerFactoryForClass =
+ this::getKotlinLambdaMergerFactoryForClass)
+ .addHorizontallyMergedLambdaClassesInspector(
+ inspector -> inspector.assertMerged(Lambda1.class, Lambda2.class))
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(EnumUnboxingCandidate.class))
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .noMinification()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Lambda1.method()", "Lambda2.method()");
+ }
+
+ private KotlinLambdaGroupIdFactory getKotlinLambdaMergerFactoryForClass(DexProgramClass clazz) {
+ String typeName = clazz.getType().toSourceString();
+ if (typeName.equals(Lambda1.class.getTypeName())
+ || typeName.equals(Lambda2.class.getTypeName())) {
+ return KStyleLambdaGroupIdFactory.getInstance();
+ }
+ return null;
+ }
+
+ static class Main {
+
+ @NeverClassInline
+ public enum EnumUnboxingCandidate {
+ LAMBDA1,
+ LAMBDA2
+ }
+
+ public static void main(String[] args) {
+ accept(createLambda(EnumUnboxingCandidate.LAMBDA1));
+ accept(createLambda(EnumUnboxingCandidate.LAMBDA2));
+ }
+
+ @NeverInline
+ static kotlin.jvm.functions.Function0<kotlin.Unit> createLambda(EnumUnboxingCandidate value) {
+ switch (value) {
+ case LAMBDA1:
+ return new Lambda1();
+ case LAMBDA2:
+ return new Lambda2();
+ default:
+ throw new RuntimeException();
+ }
+ }
+
+ @NeverInline
+ static void accept(kotlin.jvm.functions.Function0<kotlin.Unit> instance) {
+ instance.invoke();
+ }
+ }
+
+ @NeverClassInline
+ public static final class Lambda1 extends kotlin.jvm.internal.Lambda<kotlin.Unit>
+ implements kotlin.jvm.functions.Function0<kotlin.Unit> {
+
+ public Lambda1() {
+ super(0);
+ }
+
+ @NeverInline
+ @Override
+ public final kotlin.Unit invoke() {
+ System.out.println("Lambda1.method()");
+ return null;
+ }
+ }
+
+ @NeverClassInline
+ public static final class Lambda2 extends kotlin.jvm.internal.Lambda<kotlin.Unit>
+ implements kotlin.jvm.functions.Function0<kotlin.Unit> {
+
+ public Lambda2() {
+ super(0);
+ }
+
+ @NeverInline
+ @Override
+ public final kotlin.Unit invoke() {
+ System.out.println("Lambda2.method()");
+ return null;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
index 3705c2d..2498467 100644
--- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
@@ -302,7 +302,6 @@
@Test
public void testTrivialKs() throws Exception {
- expectThrowsWithHorizontalClassMergingIf(allowAccessModification);
final String mainClassName = "lambdas_kstyle_trivial.MainKt";
runTest(
"lambdas_kstyle_trivial",
@@ -384,7 +383,6 @@
@Test
public void testGenericsNoSignatureKs() throws Exception {
- expectThrowsWithHorizontalClassMerging();
final String mainClassName = "lambdas_kstyle_generics.MainKt";
runTest(
"lambdas_kstyle_generics",
@@ -413,7 +411,6 @@
@Test
public void testInnerClassesAndEnclosingMethodsKs() throws Exception {
- expectThrowsWithHorizontalClassMerging();
final String mainClassName = "lambdas_kstyle_generics.MainKt";
runTest(
"lambdas_kstyle_generics",
@@ -478,7 +475,6 @@
@Test
public void testTrivialJs() throws Exception {
- expectThrowsWithHorizontalClassMergingIf(allowAccessModification);
final String mainClassName = "lambdas_jstyle_trivial.MainKt";
runTest(
"lambdas_jstyle_trivial",
@@ -528,7 +524,6 @@
@Test
public void testSingleton() throws Exception {
- expectThrowsWithHorizontalClassMergingIf(allowAccessModification);
final String mainClassName = "lambdas_singleton.MainKt";
runTest(
"lambdas_singleton",
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index fd09ef3..da157c2 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -47,6 +47,7 @@
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InitClassLens;
import com.android.tools.r8.graph.MethodAccessFlags;
@@ -831,7 +832,7 @@
Collections.emptyList(),
null,
Collections.emptyList(),
- ClassSignature.NO_CLASS_SIGNATURE,
+ ClassSignature.noSignature(),
DexAnnotationSet.empty(),
DexEncodedField.EMPTY_ARRAY,
DexEncodedField.EMPTY_ARRAY,
@@ -857,6 +858,7 @@
new DexEncodedMethod(
voidReturnMethod,
access,
+ MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
code);
diff --git a/src/test/java/com/android/tools/r8/naming/MinifierMethodSignatureTest.java b/src/test/java/com/android/tools/r8/naming/MinifierMethodSignatureTest.java
index a275bdb..f88443d 100644
--- a/src/test/java/com/android/tools/r8/naming/MinifierMethodSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/naming/MinifierMethodSignatureTest.java
@@ -4,8 +4,11 @@
package com.android.tools.r8.naming;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -24,12 +27,13 @@
import static org.objectweb.asm.Opcodes.RETURN;
import static org.objectweb.asm.Opcodes.V1_8;
-import com.android.tools.r8.DiagnosticsChecker;
-import com.android.tools.r8.R8Command;
-import com.android.tools.r8.StringConsumer;
+import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
-import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.TestDiagnosticMessages;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
@@ -47,6 +51,180 @@
@RunWith(Parameterized.class)
public class MinifierMethodSignatureTest extends TestBase {
+
+ private final String genericSignature = "<T:Ljava/lang/Throwable;>(TT;LMethods<TT;>.Inner;)TT;";
+ private final String parameterizedReturnSignature = "()LMethods<TX;>.Inner;";
+ private final String parameterizedArgumentsSignature = "(TX;LMethods<TX;>.Inner;)V";
+ private final String parametrizedThrowsSignature = "()V^TX;";
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public MinifierMethodSignatureTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void originalJavacSignatures() throws Exception {
+ // Test using the signatures generated by javac.
+ runTest(ImmutableMap.of(), this::noWarnings, this::noInspection);
+ }
+
+ @Test
+ public void signatureEmpty() throws Exception {
+ testSingleMethod(
+ "generic",
+ "",
+ this::noWarnings,
+ inspector -> {
+ noSignatureAttribute(lookupGeneric(inspector));
+ });
+ }
+
+ @Test
+ public void signatureInvalid() throws Exception {
+ testSingleMethod(
+ "generic",
+ "X",
+ diagnostics -> {
+ diagnostics.assertWarningsCount(1);
+ diagnostics.assertWarningsMatch(
+ diagnosticMessage(
+ allOf(
+ containsString("Invalid signature 'X' for method generic"),
+ containsString("Expected ( at position 1"))));
+ },
+ inspector -> noSignatureAttribute(lookupGeneric(inspector)));
+ }
+
+ @Test
+ public void classNotFound() throws Exception {
+ String signature = "<T:LNotFound;>(TT;LAlsoNotFound<TT;>.InnerNotFound.InnerAlsoNotFound;)TT;";
+ testSingleMethod(
+ "generic",
+ signature,
+ this::noWarnings,
+ inspector -> {
+ ClassSubject methods = inspector.clazz("Methods");
+ MethodSubject method =
+ methods.method(
+ "java.lang.Throwable",
+ "generic",
+ ImmutableList.of("java.lang.Throwable", "Methods$Inner"));
+ assertThat(inspector.clazz("NotFound"), not(isPresent()));
+ assertEquals(signature, method.getOriginalSignatureAttribute());
+ });
+ }
+
+ @Test
+ public void multipleWarnings() throws Exception {
+ runTest(
+ ImmutableMap.of(
+ "generic", "X",
+ "parameterizedReturn", "X",
+ "parameterizedArguments", "X"),
+ diagnostics -> {
+ diagnostics.assertWarningsCount(3);
+ },
+ inspector -> {
+ noSignatureAttribute(lookupGeneric(inspector));
+ noSignatureAttribute(lookupParameterizedReturn(inspector));
+ noSignatureAttribute(lookupParameterizedArguments(inspector));
+ });
+ }
+
+ private void testSingleMethod(
+ String name,
+ String signature,
+ Consumer<TestDiagnosticMessages> diagnostics,
+ Consumer<CodeInspector> inspector)
+ throws Exception {
+ ImmutableMap<String, String> signatures = ImmutableMap.of(name, signature);
+ runTest(signatures, diagnostics, inspector);
+ }
+
+ private void isOriginUnknown(Origin origin) {
+ assertSame(Origin.unknown(), origin);
+ }
+
+ private void noWarnings(TestDiagnosticMessages messages) {
+ messages.assertNoWarnings();
+ }
+
+ private void noInspection(CodeInspector inspector) {}
+
+ private void noSignatureAttribute(MethodSubject method) {
+ assertThat(method, isPresent());
+ assertNull(method.getFinalSignatureAttribute());
+ assertNull(method.getOriginalSignatureAttribute());
+ }
+
+ public void runTest(
+ ImmutableMap<String, String> signatures,
+ Consumer<TestDiagnosticMessages> diagnostics,
+ Consumer<CodeInspector> inspect)
+ throws Exception {
+
+ R8TestCompileResult compileResult =
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(dumpMethods(signatures), dumpInner())
+ .addKeepAttributes(
+ ProguardKeepAttributes.INNER_CLASSES,
+ ProguardKeepAttributes.ENCLOSING_METHOD,
+ ProguardKeepAttributes.SIGNATURE)
+ .addKeepAllClassesRuleWithAllowObfuscation()
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(
+ internalOptions ->
+ internalOptions.testing.disableMappingToOriginalProgramVerification = true)
+ .allowDiagnosticMessages()
+ .compile();
+
+ CodeInspector inspector = compileResult.inspector();
+
+ // All classes are kept, and renamed.
+ ClassSubject clazz = inspector.clazz("Methods");
+ assertThat(clazz, isPresentAndRenamed());
+ assertThat(inspector.clazz("Methods$Inner"), isPresentAndRenamed());
+
+ MethodSubject generic = lookupGeneric(inspector);
+ MethodSubject parameterizedReturn = lookupParameterizedReturn(inspector);
+ MethodSubject parameterizedArguments = lookupParameterizedArguments(inspector);
+ MethodSubject parametrizedThrows =
+ clazz.method("void", "parametrizedThrows", ImmutableList.of());
+
+ // Check that all methods have been renamed
+ assertThat(generic, isPresentAndRenamed());
+ assertThat(parameterizedReturn, isPresentAndRenamed());
+ assertThat(parameterizedArguments, isPresentAndRenamed());
+ assertThat(parametrizedThrows, isPresentAndRenamed());
+
+ // Test that methods have their original signature if the default was provided.
+ if (!signatures.containsKey("generic")) {
+ assertEquals(genericSignature, generic.getOriginalSignatureAttribute());
+ }
+ if (!signatures.containsKey("parameterizedReturn")) {
+ assertEquals(
+ parameterizedReturnSignature, parameterizedReturn.getOriginalSignatureAttribute());
+ }
+ if (!signatures.containsKey("parameterizedArguments")) {
+ assertEquals(
+ parameterizedArgumentsSignature, parameterizedArguments.getOriginalSignatureAttribute());
+ }
+ if (!signatures.containsKey("parametrizedThrows")) {
+ assertEquals(parametrizedThrowsSignature, parametrizedThrows.getOriginalSignatureAttribute());
+ }
+
+ inspect.accept(inspector);
+ compileResult.getDiagnosticMessages().assertNoErrors();
+ compileResult.getDiagnosticMessages().assertNoInfos();
+ diagnostics.accept(compileResult.getDiagnosticMessages());
+ }
+
/*
class Methods<X extends Throwable> {
@@ -59,23 +237,7 @@
}
*/
-
- private String genericSignature = "<T:Ljava/lang/Throwable;>(TT;LMethods<TT;>.Inner;)TT;";
- private String parameterizedReturnSignature = "()LMethods<TX;>.Inner;";
- private String parameterizedArgumentsSignature = "(TX;LMethods<TX;>.Inner;)V";
- private String parametrizedThrowsSignature = "()V^TX;";
- private Backend backend;
-
- @Parameters(name = "Backend: {0}")
- public static Backend[] data() {
- return ToolHelper.getBackends();
- }
-
- public MinifierMethodSignatureTest(Backend backend) {
- this.backend = backend;
- }
-
- private byte[] dumpMethods(Map<String, String> signatures) throws Exception {
+ private byte[] dumpMethods(Map<String, String> signatures) {
ClassWriter cw = new ClassWriter(0);
MethodVisitor mv;
@@ -192,140 +354,4 @@
"void", "parameterizedArguments", ImmutableList.of("java.lang.Throwable", "Methods$Inner"));
}
- public void runTest(
- ImmutableMap<String, String> signatures,
- Consumer<DiagnosticsChecker> diagnostics,
- Consumer<CodeInspector> inspect)
- throws Exception {
- DiagnosticsChecker checker = new DiagnosticsChecker();
- CodeInspector inspector =
- new CodeInspector(
- ToolHelper.runR8(
- R8Command.builder(checker)
- .addClassProgramData(dumpMethods(signatures), Origin.unknown())
- .addClassProgramData(dumpInner(), Origin.unknown())
- .addProguardConfiguration(
- ImmutableList.of(
- "-keepattributes InnerClasses,EnclosingMethod,Signature",
- "-keep,allowobfuscation class ** { *; }"),
- Origin.unknown())
- .setProgramConsumer(emptyConsumer(backend))
- .addLibraryFiles(runtimeJar(backend))
- .setProguardMapConsumer(StringConsumer.emptyConsumer())
- .build()));
- // All classes are kept, and renamed.
- ClassSubject clazz = inspector.clazz("Methods");
- assertThat(clazz, isPresentAndRenamed());
- assertThat(inspector.clazz("Methods$Inner"), isPresentAndRenamed());
-
- MethodSubject generic = lookupGeneric(inspector);
- MethodSubject parameterizedReturn = lookupParameterizedReturn(inspector);
- MethodSubject parameterizedArguments = lookupParameterizedArguments(inspector);
- MethodSubject parametrizedThrows =
- clazz.method("void", "parametrizedThrows", ImmutableList.of());
-
- // Check that all methods have been renamed
- assertThat(generic, isPresentAndRenamed());
- assertThat(parameterizedReturn, isPresentAndRenamed());
- assertThat(parameterizedArguments, isPresentAndRenamed());
- assertThat(parametrizedThrows, isPresentAndRenamed());
-
- // Test that methods have their original signature if the default was provided.
- if (!signatures.containsKey("generic")) {
- assertEquals(genericSignature, generic.getOriginalSignatureAttribute());
- }
- if (!signatures.containsKey("parameterizedReturn")) {
- assertEquals(
- parameterizedReturnSignature, parameterizedReturn.getOriginalSignatureAttribute());
- }
- if (!signatures.containsKey("parameterizedArguments")) {
- assertEquals(
- parameterizedArgumentsSignature, parameterizedArguments.getOriginalSignatureAttribute());
- }
- if (!signatures.containsKey("parametrizedThrows")) {
- assertEquals(
- parametrizedThrowsSignature, parametrizedThrows.getOriginalSignatureAttribute());
- }
-
- diagnostics.accept(checker);
- inspect.accept(inspector);
- }
-
- private void testSingleMethod(String name, String signature,
- Consumer<DiagnosticsChecker> diagnostics,
- Consumer<CodeInspector> inspector)
- throws Exception {
- ImmutableMap<String, String> signatures = ImmutableMap.of(name, signature);
- runTest(signatures, diagnostics, inspector);
- }
-
- private void isOriginUnknown(Origin origin) {
- assertSame(Origin.unknown(), origin);
- }
-
- private void noWarnings(DiagnosticsChecker checker) {
- assertEquals(0, checker.warnings.size());
- }
-
- private void noInspection(CodeInspector inspector) {
- }
-
- private void noSignatureAttribute(MethodSubject method) {
- assertThat(method, isPresent());
- assertNull(method.getFinalSignatureAttribute());
- assertNull(method.getOriginalSignatureAttribute());
- }
-
- @Test
- public void originalJavacSignatures() throws Exception {
- // Test using the signatures generated by javac.
- runTest(ImmutableMap.of(), this::noWarnings, this::noInspection);
- }
-
- @Test
- public void signatureEmpty() throws Exception {
- testSingleMethod("generic", "", this::noWarnings, inspector -> {
- noSignatureAttribute(lookupGeneric(inspector));
- });
- }
-
- @Test
- public void signatureInvalid() throws Exception {
- testSingleMethod("generic", "X", diagnostics -> {
- assertEquals(1, diagnostics.warnings.size());
- DiagnosticsChecker.checkDiagnostic(diagnostics.warnings.get(0), this::isOriginUnknown,
- "Invalid signature 'X' for method",
- "java.lang.Throwable Methods.generic(java.lang.Throwable, Methods$Inner)",
- "Expected ( at position 1");
- }, inspector -> noSignatureAttribute(lookupGeneric(inspector)));
- }
-
- @Test
- public void classNotFound() throws Exception {
- String signature = "<T:LNotFound;>(TT;LAlsoNotFound<TT;>.InnerNotFound.InnerAlsoNotFound;)TT;";
- testSingleMethod("generic", signature, this::noWarnings,
- inspector -> {
- ClassSubject methods = inspector.clazz("Methods");
- MethodSubject method =
- methods.method("java.lang.Throwable", "generic",
- ImmutableList.of("java.lang.Throwable", "Methods$Inner"));
- assertThat(inspector.clazz("NotFound"), not(isPresent()));
- assertEquals(signature, method.getOriginalSignatureAttribute());
- });
- }
-
- @Test
- public void multipleWarnings() throws Exception {
- runTest(ImmutableMap.of(
- "generic", "X",
- "parameterizedReturn", "X",
- "parameterizedArguments", "X"
- ), diagnostics -> {
- assertEquals(3, diagnostics.warnings.size());
- }, inspector -> {
- noSignatureAttribute(lookupGeneric(inspector));
- noSignatureAttribute(lookupParameterizedReturn(inspector));
- noSignatureAttribute(lookupParameterizedArguments(inspector));
- });
- }
}
diff --git a/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java b/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
index 8757685..6ababf1 100644
--- a/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
+++ b/src/test/java/com/android/tools/r8/naming/b124357885/B124357885Test.java
@@ -10,19 +10,15 @@
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.graph.DexAnnotationElement;
-import com.android.tools.r8.graph.DexValue;
-import com.android.tools.r8.graph.DexValue.DexValueArray;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.lang.reflect.Method;
@@ -50,17 +46,7 @@
this.minification = minification;
}
- private void checkSignatureAnnotation(CodeInspector inspector, AnnotationSubject signature) {
- DexAnnotationElement[] elements = signature.getAnnotation().elements;
- assertEquals(1, elements.length);
- assertEquals("value", elements[0].name.toString());
- assertTrue(elements[0].value.isDexValueArray());
- DexValueArray array = elements[0].value.asDexValueArray();
- StringBuilder builder = new StringBuilder();
- for (DexValue value : array.getValues()) {
- assertTrue(value.isDexValueString());
- builder.append(value.asDexValueString().value);
- }
+ private void checkSignature(CodeInspector inspector, String signature) {
String fooImplFinalDescriptor =
DescriptorUtils.javaTypeToDescriptor(inspector.clazz(FooImpl.class).getFinalName());
StringBuilder expected =
@@ -71,7 +57,7 @@
.append("<Ljava/lang/String;>")
// Add the ; after the generic type.
.append(";");
- assertEquals(expected.toString(), builder.toString());
+ assertEquals(expected.toString(), signature);
}
@Test
@@ -90,14 +76,16 @@
.inspect(
inspector -> {
assertThat(inspector.clazz(Main.class), isPresentAndNotRenamed());
- assertThat(inspector.clazz(Service.class), isPresentAndRenamed(minification));
assertThat(inspector.clazz(Foo.class), not(isPresent()));
assertThat(inspector.clazz(FooImpl.class), isPresentAndRenamed(minification));
+ ClassSubject serviceClass = inspector.clazz(Service.class);
+ assertThat(serviceClass, isPresentAndRenamed(minification));
// TODO(124477502): Using uniqueMethodWithName("fooList") does not work.
- assertEquals(1, inspector.clazz(Service.class).allMethods().size());
- MethodSubject fooList = inspector.clazz(Service.class).allMethods().get(0);
- AnnotationSubject signature = fooList.annotation("dalvik.annotation.Signature");
- checkSignatureAnnotation(inspector, signature);
+ assertEquals(1, serviceClass.allMethods().size());
+ MethodSubject fooList = serviceClass.allMethods().get(0);
+ assertThat(fooList, isPresent());
+ checkSignature(
+ inspector, fooList.asFoundMethodSubject().getFinalSignatureAttribute());
});
String fooImplFinalName = compileResult.inspector().clazz(FooImpl.class).getFinalName();
diff --git a/src/test/java/com/android/tools/r8/naming/signature/GenericSignatureRenamingTest.java b/src/test/java/com/android/tools/r8/naming/signature/GenericSignatureRenamingTest.java
index adf7ba7..e945182 100644
--- a/src/test/java/com/android/tools/r8/naming/signature/GenericSignatureRenamingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/signature/GenericSignatureRenamingTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.naming.signature;
-import static org.junit.Assert.assertThrows;
import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationMode;
@@ -71,18 +70,13 @@
}
@Test
- public void testR8WithAssertEnabled() {
- // TODO(b/154793333): Enable assertions always when resolved.
- assertThrows(
- AssertionError.class,
- () -> {
- test(
- testForR8(parameters.getBackend())
- .addKeepRules("-dontobfuscate")
- .addOptionsModification(
- internalOptions ->
- internalOptions.testing.assertConsistentRenamingOfSignature = true));
- });
+ public void testR8WithAssertEnabled() throws Exception {
+ test(
+ testForR8(parameters.getBackend())
+ .addKeepRules("-dontobfuscate")
+ .addOptionsModification(
+ internalOptions ->
+ internalOptions.testing.assertConsistentRenamingOfSignature = true));
}
private void test(R8TestBuilder<?> builder) throws Exception {
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
index 15ed0d9..85e6cbc 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceCommandLineTests.java
@@ -7,6 +7,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.StringContains.containsString;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
@@ -38,6 +39,8 @@
public class RetraceCommandLineTests {
private static final boolean testExternal = false;
+ private static final String WAITING_MESSAGE =
+ "Waiting for stack-trace input..." + StringUtils.LINE_SEPARATOR;
@Rule public TemporaryFolder folder = new TemporaryFolder();
@@ -64,6 +67,18 @@
}
@Test
+ public void testInvalidMappingFile() throws IOException {
+ Path mappingFile = folder.newFile("mapping.txt").toPath();
+ Files.write(mappingFile, "foo.bar.baz <- is invalid mapping".getBytes());
+ Path stackTraceFile = folder.newFile("stacktrace.txt").toPath();
+ Files.write(stackTraceFile, new byte[0]);
+ runAbortTest(
+ containsString("Unable to parse mapping file"),
+ mappingFile.toString(),
+ stackTraceFile.toString());
+ }
+
+ @Test
public void testVerbose() throws IOException {
FoundMethodVerboseStackTrace stackTrace = new FoundMethodVerboseStackTrace();
runTest(
@@ -135,7 +150,7 @@
public void testHelp() throws IOException {
ProcessResult processResult = runRetraceCommandLine(null, Arrays.asList("--help"));
assertEquals(0, processResult.exitCode);
- assertEquals(Retrace.USAGE_MESSAGE, processResult.stdout);
+ assertThat(processResult.stdout, containsString(Retrace.USAGE_MESSAGE));
}
@Test
@@ -155,6 +170,12 @@
runTest("", SMILEY_EMOJI, true, SMILEY_EMOJI + StringUtils.LINE_SEPARATOR);
}
+ @Test
+ public void testHelpMessageOnStdIn() throws IOException {
+ ProcessResult processResult = runRetrace("", "", true);
+ assertTrue(processResult.stdout.startsWith(WAITING_MESSAGE));
+ }
+
private final String nonMappableStackTrace =
StringUtils.lines(
"com.android.r8.R8Exception: Problem when compiling program",
@@ -171,7 +192,12 @@
throws IOException {
ProcessResult result = runRetrace(mapping, stackTrace, stacktraceStdIn, args);
assertEquals(0, result.exitCode);
- assertEquals(expected, result.stdout);
+ String stdOut = result.stdout;
+ if (stacktraceStdIn) {
+ assertTrue(result.stdout.startsWith(WAITING_MESSAGE));
+ stdOut = result.stdout.substring(WAITING_MESSAGE.length());
+ }
+ assertEquals(expected, stdOut);
}
private void runAbortTest(Matcher<String> errorMatch, String... args) throws IOException {
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceFieldTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceFieldTests.java
new file mode 100644
index 0000000..3a6ffe6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceFieldTests.java
@@ -0,0 +1,25 @@
+// 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.retrace;
+
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
+import com.android.tools.r8.retrace.mappings.FieldsWithSameMinifiedNameMapping;
+import com.android.tools.r8.retrace.mappings.MappingForTest;
+import java.util.function.Consumer;
+import org.junit.Test;
+
+public class RetraceFieldTests {
+
+ @Test
+ public void testFieldsWithSameMinifiedName() throws Exception {
+ FieldsWithSameMinifiedNameMapping mapping = new FieldsWithSameMinifiedNameMapping();
+ runRetraceTest(mapping, mapping::inspect);
+ }
+
+ private void runRetraceTest(MappingForTest mappingForTest, Consumer<RetraceApi> inspection)
+ throws Exception {
+ inspection.accept(Retracer.create(mappingForTest::mapping, new TestDiagnosticMessagesImpl()));
+ }
+}
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 731e807..bffcd14 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -14,7 +14,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessagesImpl;
import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.retrace.Retrace.RetraceAbortException;
import com.android.tools.r8.retrace.stacktraces.ActualBotStackTraceBase;
import com.android.tools.r8.retrace.stacktraces.ActualIdentityStackTrace;
@@ -203,11 +202,8 @@
private void inspectRetraceTest(
StackTraceForTest stackTraceForTest, Consumer<RetraceApi> inspection) throws Exception {
- TestDiagnosticMessagesImpl diagnosticsHandler = new TestDiagnosticMessagesImpl();
- ClassNameMapper classNameMapper =
- ClassNameMapper.mapperFromString(stackTraceForTest.mapping(), diagnosticsHandler);
- RetraceApi retracer = Retracer.create(classNameMapper);
- inspection.accept(retracer);
+ inspection.accept(
+ Retracer.create(stackTraceForTest::mapping, new TestDiagnosticMessagesImpl()));
}
private TestDiagnosticMessagesImpl runRetraceTest(StackTraceForTest stackTraceForTest) {
diff --git a/src/test/java/com/android/tools/r8/retrace/mappings/FieldsWithSameMinifiedNameMapping.java b/src/test/java/com/android/tools/r8/retrace/mappings/FieldsWithSameMinifiedNameMapping.java
new file mode 100644
index 0000000..488a00a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/mappings/FieldsWithSameMinifiedNameMapping.java
@@ -0,0 +1,56 @@
+// 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.retrace.mappings;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.retrace.RetraceApi;
+import com.android.tools.r8.retrace.RetraceFieldResult;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class FieldsWithSameMinifiedNameMapping implements MappingForTest {
+
+ @Override
+ public String mapping() {
+ // TODO(b/169953605): Add enough information to the map to allow precise retracing.
+ return StringUtils.lines(
+ "foo.bar.Baz -> foo.bar.Baz:", " java.lang.Object f1 -> a", " java.lang.String f2 -> a");
+ }
+
+ public void inspect(RetraceApi retracer) {
+ FieldReference f1FieldReference =
+ Reference.field(
+ Reference.classFromTypeName("foo.bar.Baz"),
+ "f1",
+ Reference.classFromTypeName("java.lang.Object"));
+ FieldReference f2FieldReference =
+ Reference.field(
+ Reference.classFromTypeName("foo.bar.Baz"),
+ "f2",
+ Reference.classFromTypeName("java.lang.String"));
+
+ FieldReference mappedF1FieldReference =
+ Reference.field(
+ Reference.classFromTypeName("foo.bar.Baz"),
+ "a",
+ Reference.classFromTypeName("java.lang.Object"));
+
+ RetraceFieldResult result = retracer.retrace(mappedF1FieldReference);
+ // TODO(b/169829306): Result should not be ambigious.
+ assertTrue(result.isAmbiguous());
+
+ List<FieldReference> retracedFields =
+ result.stream()
+ .map(f -> f.getField().asKnown().getFieldReference())
+ .collect(Collectors.toList());
+ assertEquals(ImmutableList.of(f1FieldReference, f2FieldReference), retracedFields);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/mappings/MappingForTest.java b/src/test/java/com/android/tools/r8/retrace/mappings/MappingForTest.java
new file mode 100644
index 0000000..8008ee4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/mappings/MappingForTest.java
@@ -0,0 +1,10 @@
+// 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.retrace.mappings;
+
+public interface MappingForTest {
+
+ String mapping();
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/MemberFieldOverlapStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/MemberFieldOverlapStackTrace.java
index 9071654..3ca3297 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/MemberFieldOverlapStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/MemberFieldOverlapStackTrace.java
@@ -4,15 +4,18 @@
package com.android.tools.r8.retrace.stacktraces;
+import static junit.framework.TestCase.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.retrace.RetraceApi;
import com.android.tools.r8.retrace.RetraceFieldResult;
+import com.android.tools.r8.retrace.RetraceFieldResult.Element;
import com.android.tools.r8.utils.StringUtils;
import java.util.Arrays;
import java.util.List;
+import java.util.Optional;
public class MemberFieldOverlapStackTrace implements StackTraceForTest {
@@ -41,8 +44,11 @@
public void inspectField(RetraceApi retracer) {
RetraceFieldResult result =
- retracer.retrace(Reference.classFromTypeName("a.A")).lookupField("field");
- assertTrue(result.stream().findAny().isPresent());
+ retracer.retrace(Reference.classFromTypeName("a.A")).lookupField("a");
assertFalse(result.isAmbiguous());
+ assertEquals(1, result.stream().count());
+ Optional<Element> field = result.stream().findFirst();
+ assertTrue(field.isPresent());
+ assertEquals("field", field.get().getField().getFieldName());
}
}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsCheckerUtils.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsCheckerUtils.java
new file mode 100644
index 0000000..c9236d4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsCheckerUtils.java
@@ -0,0 +1,33 @@
+// 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.rewrite.assertions;
+
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.Matchers;
+import org.hamcrest.MatcherAssert;
+import org.junit.Assert;
+
+public class AssertionsCheckerUtils {
+
+ static void checkAssertionCodeEnabled(ClassSubject subject, String methodName) {
+ MatcherAssert.assertThat(subject, Matchers.isPresent());
+ // <clinit> is removed by R8.
+ if (subject.uniqueMethodWithName("<clinit>").isPresent()) {
+ Assert.assertFalse(
+ subject
+ .uniqueMethodWithName("<clinit>")
+ .streamInstructions()
+ .anyMatch(InstructionSubject::isStaticPut));
+ }
+ Assert.assertTrue(
+ subject
+ .uniqueMethodWithName(methodName)
+ .streamInstructions()
+ .anyMatch(InstructionSubject::isThrow));
+ }
+
+ private AssertionsCheckerUtils() {}
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsConfigurationJacocoTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsConfigurationJacocoTest.java
new file mode 100644
index 0000000..11829f6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsConfigurationJacocoTest.java
@@ -0,0 +1,135 @@
+// 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.rewrite.assertions;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.AssertionsConfiguration;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.rewrite.assertions.testclasses.A;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.objectweb.asm.Opcodes;
+
+@RunWith(Parameterized.class)
+public class AssertionsConfigurationJacocoTest extends TestBase implements Opcodes {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevels().build();
+ }
+
+ public AssertionsConfigurationJacocoTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private void checkAssertionCodeEnabled(CodeInspector inspector) {
+ AssertionsCheckerUtils.checkAssertionCodeEnabled(inspector.clazz(A.class), "m");
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ Assume.assumeTrue(parameters.isDexRuntime());
+ testForD8()
+ .addProgramClasses(TestClass.class, MockJacocoInit.class)
+ .addProgramClassFileData(transformClassWithJacocoInstrumentation(A.class))
+ .setMinApi(parameters.getApiLevel())
+ .addAssertionsConfiguration(AssertionsConfiguration.Builder::enableAllAssertions)
+ .run(parameters.getRuntime(), TestClass.class)
+ .inspect(this::checkAssertionCodeEnabled)
+ .assertSuccessWithOutputLines("AssertionError in A");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class, MockJacocoInit.class)
+ .addProgramClassFileData(transformClassWithJacocoInstrumentation(A.class))
+ .addKeepMainRule(TestClass.class)
+ .addKeepClassAndMembersRules(A.class, MockJacocoInit.class)
+ .setMinApi(parameters.getApiLevel())
+ .addAssertionsConfiguration(AssertionsConfiguration.Builder::enableAllAssertions)
+ .run(parameters.getRuntime(), TestClass.class)
+ .inspect(this::checkAssertionCodeEnabled)
+ .assertSuccessWithOutputLines("AssertionError in A");
+ }
+
+ private byte[] transformClassWithJacocoInstrumentation(Class<?> clazz) throws IOException {
+ // Instrument the class with Jacoco.
+ Path dir = temp.newFolder().toPath();
+ runJacoco(ToolHelper.getClassFileForTestClass(clazz), dir);
+ // Rewrite the invocation of Jacoco initialization (getProbes invocation) for class with a Mock.
+ Path jacoco =
+ dir.resolve(
+ A.class.getTypeName().substring(clazz.getPackage().getName().length() + 1) + ".class");
+ return transformer(jacoco, Reference.classFromClass(clazz))
+ .transformMethodInsnInMethod(
+ "$jacocoInit",
+ (opcode, owner, name, descriptor, isInterface, continuation) -> {
+ assertEquals(INVOKESTATIC, opcode);
+ assertEquals("getProbes", name);
+ continuation.visitMethodInsn(
+ INVOKESTATIC,
+ DescriptorUtils.getClassBinaryName(MockJacocoInit.class),
+ "getProbes",
+ descriptor,
+ isInterface);
+ })
+ .transform();
+ }
+
+ private void runJacoco(Path input, Path outdir) throws IOException {
+ List<String> cmdline = new ArrayList<>();
+ cmdline.add(TestRuntime.getSystemRuntime().asCf().getJavaExecutable().toString());
+ cmdline.add("-jar");
+ cmdline.add(ToolHelper.JACOCO_CLI);
+ cmdline.add("instrument");
+ cmdline.add(input.toString());
+ cmdline.add("--dest");
+ cmdline.add(outdir.toString());
+ ProcessBuilder builder = new ProcessBuilder(cmdline);
+ ProcessResult javacResult = ToolHelper.runProcess(builder);
+ assertEquals(javacResult.toString(), 0, javacResult.exitCode);
+ }
+
+ public static class MockJacocoInit {
+
+ public static boolean[] getProbes(long a, String b, int c) {
+ // For the test class A an array of size 6 is sufficient.
+ return new boolean[6];
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ try {
+ A.m();
+ } catch (AssertionError e) {
+ System.out.println("AssertionError in A");
+ }
+ }
+ }
+
+ // The test class A cannot be an inner class of AssertionsConfigurationJacocoTest, as then the
+ // desiredAssertionStatus() on the outer AssertionsConfigurationJacocoTest will be used and
+ // and that is not part of the input.
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsConfigurationTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsConfigurationTest.java
index 01c28c1..064cfdb 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsConfigurationTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionsConfigurationTest.java
@@ -143,25 +143,8 @@
checkAssertionCodeRemoved(inspector.clazz(clazz));
}
- private void checkAssertionCodeEnabled(ClassSubject subject) {
- assertThat(subject, isPresent());
- // <clinit> is removed by R8.
- if (subject.uniqueMethodWithName("<clinit>").isPresent()) {
- assertFalse(
- subject
- .uniqueMethodWithName("<clinit>")
- .streamInstructions()
- .anyMatch(InstructionSubject::isStaticPut));
- }
- assertTrue(
- subject
- .uniqueMethodWithName("m")
- .streamInstructions()
- .anyMatch(InstructionSubject::isThrow));
- }
-
private void checkAssertionCodeEnabled(CodeInspector inspector, Class<?> clazz) {
- checkAssertionCodeEnabled(inspector.clazz(clazz));
+ AssertionsCheckerUtils.checkAssertionCodeEnabled(inspector.clazz(clazz), "m");
}
private void checkAssertionCodeLeft(CodeInspector inspector, Class<?> clazz) {
@@ -421,8 +404,8 @@
.compile()
.inspect(
inspector -> {
- checkAssertionCodeEnabled(inspector.clazz("Class1"));
- checkAssertionCodeEnabled(inspector.clazz("Class2"));
+ AssertionsCheckerUtils.checkAssertionCodeEnabled(inspector.clazz("Class1"), "m");
+ AssertionsCheckerUtils.checkAssertionCodeEnabled(inspector.clazz("Class2"), "m");
checkAssertionCodeRemoved(inspector.clazz(class1));
checkAssertionCodeRemoved(inspector.clazz(class2));
})
@@ -472,8 +455,10 @@
.compile()
.inspect(
inspector -> {
- checkAssertionCodeEnabled(inspector.clazz(TestClassForInnerClass.class));
- checkAssertionCodeEnabled(inspector.clazz(TestClassForInnerClass.InnerClass.class));
+ AssertionsCheckerUtils.checkAssertionCodeEnabled(
+ inspector.clazz(TestClassForInnerClass.class), "m");
+ AssertionsCheckerUtils.checkAssertionCodeEnabled(
+ inspector.clazz(TestClassForInnerClass.InnerClass.class), "m");
})
.run(parameters.getRuntime(), TestClassForInnerClass.class)
.assertSuccessWithOutputLines(
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/A.java b/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/A.java
new file mode 100644
index 0000000..2115568
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/testclasses/A.java
@@ -0,0 +1,12 @@
+// 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.rewrite.assertions.testclasses;
+
+public class A {
+
+ public static void m() {
+ assert false;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java
index a1a2748..d91a2fd 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShaking18Test.java
@@ -39,7 +39,6 @@
@Test
public void test() throws Exception {
- expectThrowsWithHorizontalClassMerging();
runTest(
TreeShaking18Test::unusedRemoved,
null,
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAbstractMethodRemovalTest.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAbstractMethodRemovalTest.java
index a82fede..b008254 100644
--- a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAbstractMethodRemovalTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAbstractMethodRemovalTest.java
@@ -37,7 +37,6 @@
@Test
public void test() throws Exception {
- expectThrowsWithHorizontalClassMerging();
runTest(
null,
null,
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index d9f3b1d..269c6c2 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -306,6 +306,22 @@
});
}
+ public ClassFileTransformer setGenericSignature(String newGenericSignature) {
+ return addClassTransformer(
+ new ClassTransformer() {
+ @Override
+ public void visit(
+ int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
+ super.visit(version, access, name, newGenericSignature, superName, interfaces);
+ }
+ });
+ }
+
public ClassFileTransformer setNest(Class<?> host, Class<?>... members) {
assert !Arrays.asList(members).contains(host);
return setMinVersion(CfVm.JDK11)
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 efaf58b..1772e48 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
@@ -3,10 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils.codeinspector;
-import static org.junit.Assert.assertTrue;
-
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestDiagnosticMessagesImpl;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfTryCatch;
import com.android.tools.r8.code.Instruction;
@@ -16,7 +15,6 @@
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexAnnotation;
-import com.android.tools.r8.graph.DexAnnotationElement;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
@@ -25,8 +23,6 @@
import com.android.tools.r8.graph.DexCode.TryHandler;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexValue;
-import com.android.tools.r8.graph.DexValue.DexValueArray;
import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.ClassNamingForNameMapper;
@@ -38,6 +34,7 @@
import com.android.tools.r8.references.FieldReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.retrace.DirectClassNameMapperProguardMapProducer;
import com.android.tools.r8.retrace.RetraceApi;
import com.android.tools.r8.retrace.Retracer;
import com.android.tools.r8.utils.AndroidApp;
@@ -227,23 +224,6 @@
return null;
}
- public String getFinalSignatureAttribute(DexAnnotationSet annotations) {
- DexAnnotation annotation = findAnnotation("dalvik.annotation.Signature", annotations);
- if (annotation == null) {
- return null;
- }
- assert annotation.annotation.elements.length == 1;
- DexAnnotationElement element = annotation.annotation.elements[0];
- assert element.value.isDexValueArray();
- StringBuilder builder = new StringBuilder();
- DexValueArray valueArray = element.value.asDexValueArray();
- for (DexValue value : valueArray.getValues()) {
- assertTrue(value.isDexValueString());
- builder.append(value.asDexValueString().getValue());
- }
- return builder.toString();
- }
-
public String getOriginalSignatureAttribute(
String finalSignature, BiConsumer<GenericSignatureParser, String> parse) {
if (finalSignature == null || mapping == null) {
@@ -484,6 +464,24 @@
}
public RetraceApi retrace() {
- return Retracer.create(mapping == null ? ClassNameMapper.builder().build() : mapping);
+ return Retracer.create(
+ new InternalProguardMapProducer(
+ mapping == null ? ClassNameMapper.builder().build() : mapping),
+ new TestDiagnosticMessagesImpl());
+ }
+
+ public static class InternalProguardMapProducer
+ implements DirectClassNameMapperProguardMapProducer {
+
+ public final ClassNameMapper prebuiltMapper;
+
+ public InternalProguardMapProducer(ClassNameMapper prebuiltMapper) {
+ this.prebuiltMapper = prebuiltMapper;
+ }
+
+ @Override
+ public ClassNameMapper getClassNameMapper() {
+ return prebuiltMapper;
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java
new file mode 100644
index 0000000..8e5495e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java
@@ -0,0 +1,36 @@
+// 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.utils.codeinspector;
+
+import static com.android.tools.r8.TestBase.toDexType;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.EnumValueInfoMapCollection;
+
+public class EnumUnboxingInspector {
+
+ private final DexItemFactory dexItemFactory;
+ private final EnumValueInfoMapCollection unboxedEnums;
+
+ public EnumUnboxingInspector(
+ DexItemFactory dexItemFactory, EnumValueInfoMapCollection unboxedEnums) {
+ this.dexItemFactory = dexItemFactory;
+ this.unboxedEnums = unboxedEnums;
+ }
+
+ public EnumUnboxingInspector assertUnboxed(Class<? extends Enum<?>> clazz) {
+ assertTrue(unboxedEnums.containsEnum(toDexType(clazz, dexItemFactory)));
+ return this;
+ }
+
+ @SafeVarargs
+ public final EnumUnboxingInspector assertUnboxed(Class<? extends Enum<?>>... classes) {
+ for (Class<? extends Enum<?>> clazz : classes) {
+ assertUnboxed(clazz);
+ }
+ return this;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
index fce43c7..4504213 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundFieldSubject.java
@@ -134,12 +134,12 @@
@Override
public String getOriginalSignatureAttribute() {
return codeInspector.getOriginalSignatureAttribute(
- dexField.getFieldSignature().toString(), GenericSignatureParser::parseFieldSignature);
+ getFinalSignatureAttribute(), GenericSignatureParser::parseFieldSignature);
}
@Override
public String getFinalSignatureAttribute() {
- return dexField.getFieldSignature().toString();
+ return dexField.getGenericSignature().toString();
}
@Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
index 4e49582..4d5e0d3 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundMethodSubject.java
@@ -186,8 +186,7 @@
@Override
public String getOriginalSignatureAttribute() {
return codeInspector.getOriginalSignatureAttribute(
- codeInspector.getFinalSignatureAttribute(dexMethod.annotations()),
- GenericSignatureParser::parseMethodSignature);
+ getFinalSignatureAttribute(), GenericSignatureParser::parseMethodSignature);
}
public DexMethod getOriginalDexMethod(DexItemFactory dexItemFactory) {
@@ -201,7 +200,7 @@
@Override
public String getFinalSignatureAttribute() {
- return codeInspector.getFinalSignatureAttribute(dexMethod.annotations());
+ return dexMethod.getGenericSignature().toString();
}
public Iterable<InstructionSubject> instructions() {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
new file mode 100644
index 0000000..1f14df7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
@@ -0,0 +1,49 @@
+// 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.utils.codeinspector;
+
+import static com.android.tools.r8.TestBase.toDexType;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
+
+public class HorizontallyMergedClassesInspector {
+
+ private final DexItemFactory dexItemFactory;
+ private final HorizontallyMergedClasses horizontallyMergedClasses;
+
+ public HorizontallyMergedClassesInspector(
+ DexItemFactory dexItemFactory, HorizontallyMergedClasses horizontallyMergedClasses) {
+ this.dexItemFactory = dexItemFactory;
+ this.horizontallyMergedClasses = horizontallyMergedClasses;
+ }
+
+ public HorizontallyMergedClassesInspector assertMerged(Class<?> clazz) {
+ assertTrue(
+ horizontallyMergedClasses.hasBeenMergedOrIsMergeTarget(toDexType(clazz, dexItemFactory)));
+ return this;
+ }
+
+ public HorizontallyMergedClassesInspector assertMerged(Class<?>... classes) {
+ for (Class<?> clazz : classes) {
+ assertMerged(clazz);
+ }
+ return this;
+ }
+
+ public HorizontallyMergedClassesInspector assertMergedIntoDifferentType(Class<?> clazz) {
+ assertTrue(
+ horizontallyMergedClasses.hasBeenMergedIntoDifferentType(toDexType(clazz, dexItemFactory)));
+ return this;
+ }
+
+ public HorizontallyMergedClassesInspector assertMergedIntoDifferentType(Class<?>... classes) {
+ for (Class<?> clazz : classes) {
+ assertMergedIntoDifferentType(clazz);
+ }
+ return this;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedLambdaClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedLambdaClassesInspector.java
new file mode 100644
index 0000000..4b3579f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedLambdaClassesInspector.java
@@ -0,0 +1,36 @@
+// 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.utils.codeinspector;
+
+import static com.android.tools.r8.TestBase.toDexType;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses;
+
+public class HorizontallyMergedLambdaClassesInspector {
+
+ private final DexItemFactory dexItemFactory;
+ private final HorizontallyMergedLambdaClasses horizontallyMergedLambdaClasses;
+
+ public HorizontallyMergedLambdaClassesInspector(
+ DexItemFactory dexItemFactory,
+ HorizontallyMergedLambdaClasses horizontallyMergedLambdaClasses) {
+ this.dexItemFactory = dexItemFactory;
+ this.horizontallyMergedLambdaClasses = horizontallyMergedLambdaClasses;
+ }
+
+ public HorizontallyMergedLambdaClassesInspector assertMerged(Class<?> clazz) {
+ assertTrue(horizontallyMergedLambdaClasses.hasBeenMerged(toDexType(clazz, dexItemFactory)));
+ return this;
+ }
+
+ public HorizontallyMergedLambdaClassesInspector assertMerged(Class<?>... classes) {
+ for (Class<?> clazz : classes) {
+ assertMerged(clazz);
+ }
+ return this;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/VerticallyMergedClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/VerticallyMergedClassesInspector.java
new file mode 100644
index 0000000..bd64711
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/VerticallyMergedClassesInspector.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.utils.codeinspector;
+
+import static com.android.tools.r8.TestBase.toDexType;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses;
+
+public class VerticallyMergedClassesInspector {
+
+ private final DexItemFactory dexItemFactory;
+ private final VerticallyMergedClasses verticallyMergedClasses;
+
+ public VerticallyMergedClassesInspector(
+ DexItemFactory dexItemFactory, VerticallyMergedClasses verticallyMergedClasses) {
+ this.dexItemFactory = dexItemFactory;
+ this.verticallyMergedClasses = verticallyMergedClasses;
+ }
+
+ public VerticallyMergedClassesInspector assertMergedIntoSubtype(Class<?> clazz) {
+ assertTrue(verticallyMergedClasses.hasBeenMergedIntoSubtype(toDexType(clazz, dexItemFactory)));
+ return this;
+ }
+
+ public VerticallyMergedClassesInspector assertMergedIntoSubtype(Class<?>... classes) {
+ for (Class<?> clazz : classes) {
+ assertMergedIntoSubtype(clazz);
+ }
+ return this;
+ }
+}
diff --git a/third_party/jacoco.tar.gz.sha1 b/third_party/jacoco.tar.gz.sha1
deleted file mode 100644
index 8a0462f..0000000
--- a/third_party/jacoco.tar.gz.sha1
+++ /dev/null
@@ -1 +0,0 @@
-b968df99f88434e2a2a35445ac860ee6a2fa16e3
\ No newline at end of file
diff --git a/third_party/jacoco/0.8.2.tar.gz.sha1 b/third_party/jacoco/0.8.2.tar.gz.sha1
new file mode 100644
index 0000000..f33b98a
--- /dev/null
+++ b/third_party/jacoco/0.8.2.tar.gz.sha1
@@ -0,0 +1 @@
+b8ee2e058a52ff8ca73ae9b6a99426baa2be1626
\ No newline at end of file
diff --git a/third_party/jacoco/0.8.6.tar.gz.sha1 b/third_party/jacoco/0.8.6.tar.gz.sha1
new file mode 100644
index 0000000..47fefc1
--- /dev/null
+++ b/third_party/jacoco/0.8.6.tar.gz.sha1
@@ -0,0 +1 @@
+471ccc4aa6d69a684aa400f08dc6a15e17b4ba25
\ No newline at end of file
diff --git a/tools/test.py b/tools/test.py
index bb8dbd2..64d2ae0 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -335,7 +335,12 @@
runtimes = ['dex-' + art_vm]
# Only append the "none" runtime and JVMs if running on the "default" DEX VM.
if art_vm == "default":
- runtimes.extend(['jdk8', 'jdk9', 'jdk11', 'none'])
+ # 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,