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