Revert changes to hygienic synthetics.
This includes:
Revert "Reland "Hygienic lambda desugaring.""
Commit 0a22c262e790a2b93a2f9940f14d481736b95b9c.
Revert "Hygienic desugaring of try-with-resource $closeResource."
Commit 286bdd43da638250433c62704937a3ab8a28b9c7.
Revert "Hygienic service loader rewriting."
Commit f637b908436d2cdeb457ad0bfc666798e9c39f0a.
Revert "Disable failing test."
Commit 000bc0cad75eba0f4df6b48997311002f20b24a4.
Change-Id: Ia4edaf2d3784d5cb92a3f3acd4993cc53dedd391
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index b762557..4def5d0 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -271,9 +271,6 @@
if (result != null) {
appView.setAppInfo(new AppInfo(result.commit, appView.appInfo().getMainDexClasses()));
appView.pruneItems(result.prunedItems);
- if (result.lens != null) {
- appView.setGraphLens(result.lens);
- }
}
new CfApplicationWriter(
appView,
@@ -318,9 +315,6 @@
if (result != null) {
appView.setAppInfo(new AppInfo(result.commit, appView.appInfo().getMainDexClasses()));
appView.pruneItems(result.prunedItems);
- if (result.lens != null) {
- appView.setGraphLens(result.lens);
- }
}
new ApplicationWriter(
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index e611c5a..f610d83 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -133,9 +133,6 @@
if (result != null) {
appView.setAppInfo(new AppInfo(result.commit, appView.appInfo().getMainDexClasses()));
appView.pruneItems(result.prunedItems);
- if (result.lens != null) {
- appView.setGraphLens(result.lens);
- }
}
NamingLens namingLens = PrefixRewritingNamingLens.createPrefixRewritingNamingLens(appView);
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 125b84d..9e7a967 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -838,21 +838,12 @@
appView.getSyntheticItems().computeFinalSynthetics(appView);
if (result != null) {
if (appView.appInfo().hasLiveness()) {
- if (result.lens == null) {
- appViewWithLiveness.setAppInfo(
- appViewWithLiveness.appInfo().rebuildWithLiveness(result.commit));
- } else {
- appViewWithLiveness.rewriteWithLensAndApplication(
- result.lens, result.commit.getApplication().asDirect());
- }
- appViewWithLiveness.pruneItems(result.prunedItems);
+ appViewWithLiveness.setAppInfo(
+ appViewWithLiveness.appInfo().rebuildWithLiveness(result.commit));
} else {
appView.setAppInfo(appView.appInfo().rebuildWithClassHierarchy(result.commit));
- appView.pruneItems(result.prunedItems);
- if (result.lens != null) {
- appView.setGraphLens(result.lens);
- }
}
+ appViewWithLiveness.pruneItems(result.prunedItems);
}
// Perform minification.
diff --git a/src/main/java/com/android/tools/r8/code/Instruction.java b/src/main/java/com/android/tools/r8/code/Instruction.java
index ccfd713..977dafd 100644
--- a/src/main/java/com/android/tools/r8/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/code/Instruction.java
@@ -332,9 +332,6 @@
@Override
public final void acceptHashing(HashingVisitor visitor) {
// Rather than traverse the full instruction, the compare ID will likely give a reasonable hash.
- // TODO(b/158159959): This will likely lead to a lot of distinct synthetics hashing to the same
- // hash as many have the same instruction pattern such as an invoke of the impl method or a
- // field access.
visitor.visitInt(getCompareToId());
}
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index bbe4a90..ef6b7cd 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -55,7 +55,7 @@
import com.android.tools.r8.naming.MemberNaming.Signature;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.position.MethodPosition;
-import com.android.tools.r8.synthesis.SyntheticNaming;
+import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DexVersion;
import com.android.tools.r8.utils.InternalOptions;
@@ -323,7 +323,7 @@
for (DexType type : mapping.getTypes()) {
if (type.isClassType()) {
assert DexString.isValidSimpleName(apiLevel, type.getName());
- assert SyntheticNaming.verifyNotInternalSynthetic(type);
+ assert SyntheticItems.verifyNotInternalSynthetic(type);
}
}
diff --git a/src/main/java/com/android/tools/r8/dex/VirtualFile.java b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
index 7da0bc5..917f0de 100644
--- a/src/main/java/com/android/tools/r8/dex/VirtualFile.java
+++ b/src/main/java/com/android/tools/r8/dex/VirtualFile.java
@@ -28,7 +28,7 @@
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.shaking.MainDexClasses;
-import com.android.tools.r8.synthesis.SyntheticNaming;
+import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -667,7 +667,7 @@
@Override
public boolean addType(DexType type) {
- assert SyntheticNaming.verifyNotInternalSynthetic(type);
+ assert SyntheticItems.verifyNotInternalSynthetic(type);
return types.add(type);
}
@@ -790,7 +790,7 @@
@Override
public boolean addType(DexType type) {
- assert SyntheticNaming.verifyNotInternalSynthetic(type);
+ assert SyntheticItems.verifyNotInternalSynthetic(type);
return maybeInsert(type, types, base.types);
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index 302dff4..cfacdbb 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -123,6 +123,11 @@
}
}
+ public Collection<DexProgramClass> synthesizedClasses() {
+ assert checkIfObsolete();
+ return syntheticItems.getPendingSyntheticClasses();
+ }
+
public Collection<DexProgramClass> classes() {
assert checkIfObsolete();
return app.classes();
diff --git a/src/main/java/com/android/tools/r8/graph/CfCode.java b/src/main/java/com/android/tools/r8/graph/CfCode.java
index 4d45d55..eb83898 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -247,9 +247,6 @@
public void acceptHashing(HashingVisitor visitor) {
// Rather than hash the entire content, hash the sizes and each instruction "type" which
// should provide a fast yet reasonably distinct key.
- // TODO(b/158159959): This will likely lead to a lot of distinct synthetics hashing to the same
- // hash as many have the same instruction pattern such as an invoke of the impl method or a
- // field access.
visitor.visitInt(instructions.size());
visitor.visitInt(tryCatchRanges.size());
visitor.visitInt(localVariables.size());
diff --git a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
index 3483fbb..bcb7974 100644
--- a/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
+++ b/src/main/java/com/android/tools/r8/graph/DexAnnotation.java
@@ -14,8 +14,6 @@
import com.android.tools.r8.graph.DexValue.DexValueString;
import com.android.tools.r8.graph.DexValue.DexValueType;
import com.android.tools.r8.ir.desugar.CovariantReturnTypeAnnotationTransformer;
-import com.android.tools.r8.synthesis.SyntheticNaming;
-import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.structural.StructuralItem;
@@ -401,18 +399,13 @@
}
public static DexAnnotation createAnnotationSynthesizedClass(
- SyntheticKind kind, DexType synthesizingContext, DexItemFactory dexItemFactory) {
- DexAnnotationElement kindElement =
- new DexAnnotationElement(
- dexItemFactory.kindString,
- new DexValueString(dexItemFactory.createString(kind.descriptor)));
- DexAnnotationElement typeElement =
- new DexAnnotationElement(dexItemFactory.valueString, new DexValueType(synthesizingContext));
+ DexType synthesizingContext, DexItemFactory dexItemFactory) {
+ DexValueType value = new DexValueType(synthesizingContext);
+ DexAnnotationElement element = new DexAnnotationElement(dexItemFactory.valueString, value);
return new DexAnnotation(
VISIBILITY_BUILD,
new DexEncodedAnnotation(
- dexItemFactory.annotationSynthesizedClass,
- new DexAnnotationElement[] {kindElement, typeElement}));
+ dexItemFactory.annotationSynthesizedClass, new DexAnnotationElement[] {element}));
}
public static boolean hasSynthesizedClassAnnotation(
@@ -420,7 +413,7 @@
return getSynthesizedClassAnnotationContextType(annotations, factory) != null;
}
- public static Pair<SyntheticKind, DexType> getSynthesizedClassAnnotationContextType(
+ public static DexType getSynthesizedClassAnnotationContextType(
DexAnnotationSet annotations, DexItemFactory factory) {
if (annotations.size() != 1) {
return null;
@@ -429,31 +422,17 @@
if (annotation.annotation.type != factory.annotationSynthesizedClass) {
return null;
}
- if (annotation.annotation.elements.length != 2) {
+ if (annotation.annotation.elements.length != 1) {
return null;
}
- assert factory.kindString.isLessThan(factory.valueString);
- DexAnnotationElement kindElement = annotation.annotation.elements[0];
- if (kindElement.name != factory.kindString) {
+ DexAnnotationElement element = annotation.annotation.elements[0];
+ if (element.name != factory.valueString) {
return null;
}
- if (!kindElement.value.isDexValueString()) {
+ if (!element.value.isDexValueType()) {
return null;
}
- SyntheticKind kind =
- SyntheticNaming.SyntheticKind.fromDescriptor(
- kindElement.value.asDexValueString().getValue().toString());
- if (kind == null) {
- return null;
- }
- DexAnnotationElement valueElement = annotation.annotation.elements[1];
- if (valueElement.name != factory.valueString) {
- return null;
- }
- if (!valueElement.value.isDexValueType()) {
- return null;
- }
- return new Pair<>(kind, valueElement.value.asDexValueType().getValue());
+ return element.value.asDexValueType().getValue();
}
public static DexAnnotation createAnnotationSynthesizedClassMap(
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index 2236b6c..91203c8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -76,12 +76,14 @@
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.structural.CompareToVisitor;
+import com.android.tools.r8.utils.structural.CompareToVisitorWithTypeEquivalence;
import com.android.tools.r8.utils.structural.HashingVisitor;
+import com.android.tools.r8.utils.structural.HashingVisitorWithTypeEquivalence;
import com.android.tools.r8.utils.structural.Ordered;
-import com.android.tools.r8.utils.structural.StructuralItem;
-import com.android.tools.r8.utils.structural.StructuralMapping;
+import com.android.tools.r8.utils.structural.RepresentativeMap;
import com.android.tools.r8.utils.structural.StructuralSpecification;
import com.google.common.collect.ImmutableList;
+import com.google.common.hash.Hasher;
import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import java.util.ArrayList;
@@ -93,8 +95,7 @@
import java.util.function.IntPredicate;
import org.objectweb.asm.Opcodes;
-public class DexEncodedMethod extends DexEncodedMember<DexEncodedMethod, DexMethod>
- implements StructuralItem<DexEncodedMethod> {
+public class DexEncodedMethod extends DexEncodedMember<DexEncodedMethod, DexMethod> {
public static final String CONFIGURATION_DEBUGGING_PREFIX = "Shaking error: Missing method in ";
@@ -337,16 +338,6 @@
return deprecated;
}
- @Override
- public DexEncodedMethod self() {
- return this;
- }
-
- @Override
- public StructuralMapping<DexEncodedMethod> getStructuralMapping() {
- return DexEncodedMethod::syntheticSpecify;
- }
-
// Visitor specifying the structure of the method with respect to its "synthetic" content.
// TODO(b/171867022): Generalize this so that it determines any method in full.
private static void syntheticSpecify(StructuralSpecification<DexEncodedMethod, ?> spec) {
@@ -365,12 +356,28 @@
DexEncodedMethod::hashCodeObject);
}
+ public void hashSyntheticContent(Hasher hasher, RepresentativeMap map) {
+ HashingVisitorWithTypeEquivalence.run(this, hasher, map, DexEncodedMethod::syntheticSpecify);
+ }
+
+ public boolean isSyntheticContentEqual(DexEncodedMethod other) {
+ return syntheticCompareTo(other) == 0;
+ }
+
+ public int syntheticCompareTo(DexEncodedMethod other) {
+ // Consider the holder types to be equivalent, using the holder of this method as the
+ // representative.
+ RepresentativeMap map = t -> t == other.getHolderType() ? getHolderType() : t;
+ return CompareToVisitorWithTypeEquivalence.run(
+ this, other, map, DexEncodedMethod::syntheticSpecify);
+ }
+
private static int compareCodeObject(Code code1, Code code2, CompareToVisitor visitor) {
if (code1.isCfCode() && code2.isCfCode()) {
return code1.asCfCode().acceptCompareTo(code2.asCfCode(), visitor);
}
if (code1.isDexCode() && code2.isDexCode()) {
- return code1.asDexCode().acceptCompareTo(code2.asDexCode(), visitor);
+ return visitor.visit(code1.asDexCode(), code2.asDexCode(), DexCode::compareTo);
}
throw new Unreachable(
"Unexpected attempt to compare incompatible synthetic objects: " + code1 + " and " + code2);
@@ -380,7 +387,9 @@
if (code.isCfCode()) {
code.asCfCode().acceptHashing(visitor);
} else {
- code.asDexCode().acceptHashing(visitor);
+ // TODO(b/158159959): Implement a more precise hashing on code objects.
+ assert code.isDexCode();
+ visitor.visitInt(code.hashCode());
}
}
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 2591960..e6d824a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.graph;
import static com.android.tools.r8.ir.analysis.type.ClassTypeElement.computeLeastUpperBoundOfInterfaces;
+import static com.android.tools.r8.ir.optimize.ServiceLoaderRewriter.SERVICE_LOADER_CLASS_NAME;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.dex.Marker;
@@ -298,7 +299,6 @@
public final DexString throwableArrayDescriptor = createString("[Ljava/lang/Throwable;");
public final DexString valueString = createString("value");
- public final DexString kindString = createString("kind");
public final DexType booleanType = createStaticallyKnownType(booleanDescriptor);
public final DexType byteType = createStaticallyKnownType(byteDescriptor);
@@ -369,6 +369,8 @@
createStaticallyKnownType(invocationHandlerDescriptor);
public final DexType proxyType = createStaticallyKnownType(proxyDescriptor);
public final DexType serviceLoaderType = createStaticallyKnownType(serviceLoaderDescriptor);
+ public final DexType serviceLoaderRewrittenClassType =
+ createStaticallyKnownType("L" + SERVICE_LOADER_CLASS_NAME + ";");
public final DexType serviceLoaderConfigurationErrorType =
createStaticallyKnownType(serviceLoaderConfigurationErrorDescriptor);
public final DexType listType = createStaticallyKnownType(listDescriptor);
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 7d69899..2a6ebf5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -21,9 +21,6 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.TraversalContinuation;
import com.android.tools.r8.utils.structural.Ordered;
-import com.android.tools.r8.utils.structural.StructuralItem;
-import com.android.tools.r8.utils.structural.StructuralMapping;
-import com.android.tools.r8.utils.structural.StructuralSpecification;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
@@ -40,7 +37,7 @@
import java.util.function.Supplier;
public class DexProgramClass extends DexClass
- implements ProgramDefinition, Supplier<DexProgramClass>, StructuralItem<DexProgramClass> {
+ implements ProgramDefinition, Supplier<DexProgramClass> {
@FunctionalInterface
public interface ChecksumSupplier {
@@ -147,36 +144,6 @@
synthesizedDirectlyFrom.forEach(this::addSynthesizedFrom);
}
- @Override
- public DexProgramClass self() {
- return this;
- }
-
- @Override
- public StructuralMapping<DexProgramClass> getStructuralMapping() {
- return DexProgramClass::specify;
- }
-
- private static void specify(StructuralSpecification<DexProgramClass, ?> spec) {
- spec.withItem(c -> c.type)
- .withItem(c -> c.superType)
- .withItem(c -> c.interfaces)
- .withItem(c -> c.accessFlags)
- .withNullableItem(c -> c.sourceFile)
- .withNullableItem(c -> c.initialClassFileVersion)
- .withBool(c -> c.deprecated)
- .withNullableItem(DexClass::getNestHostClassAttribute)
- .withItemCollection(DexClass::getNestMembersClassAttributes)
- .withItem(DexDefinition::annotations)
- // TODO(b/158159959): Make signatures structural.
- .withAssert(c -> c.classSignature == ClassSignature.noSignature())
- .withItemArray(c -> c.staticFields)
- .withItemArray(c -> c.instanceFields)
- .withItemCollection(DexClass::allMethodsSorted)
- // TODO(b/168584485): Synthesized-from is being removed (empty for new synthetics).
- .withAssert(c -> c.synthesizedFrom.isEmpty());
- }
-
public void forEachProgramField(Consumer<? super ProgramField> consumer) {
forEachField(field -> consumer.accept(new ProgramField(this, field)));
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index d2fb517..27284ea 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -8,6 +8,7 @@
import static com.android.tools.r8.ir.desugar.DesugaredLibraryWrapperSynthesizer.VIVIFIED_TYPE_WRAPPER_SUFFIX;
import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX;
import static com.android.tools.r8.ir.desugar.InterfaceMethodRewriter.EMULATE_LIBRARY_CLASS_NAME_SUFFIX;
+import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX;
import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_GROUP_CLASS_NAME_PREFIX;
import static com.android.tools.r8.ir.optimize.enums.UnboxedEnumMemberRelocator.ENUM_UNBOXING_UTILITY_CLASS_SUFFIX;
@@ -16,8 +17,10 @@
import com.android.tools.r8.horizontalclassmerging.SyntheticArgumentClass;
import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
import com.android.tools.r8.ir.desugar.NestBasedAccessDesugaring;
+import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
+import com.android.tools.r8.ir.optimize.ServiceLoaderRewriter;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.synthesis.SyntheticNaming;
+import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
import com.android.tools.r8.utils.structural.CompareToVisitor;
@@ -38,13 +41,7 @@
// Bundletool is merging classes that may originate from a build with an old version of R8.
// Allow merging of classes that use names from older versions of R8.
private static List<String> OLD_SYNTHESIZED_NAMES =
- ImmutableList.of(
- "$r8$backportedMethods$utility",
- "$r8$java8methods$utility",
- "$r8$twr$utility",
- "$-DC",
- "$$ServiceLoaderMethods",
- "-$$Lambda$");
+ ImmutableList.of("$r8$backportedMethods$utility", "$r8$java8methods$utility", "$-DC");
public final DexString descriptor;
private String toStringCache = null;
@@ -305,6 +302,12 @@
}
// TODO(b/158159959): Remove usage of name-based identification.
+ public boolean isD8R8SynthesizedLambdaClassType() {
+ String name = toSourceString();
+ return name.contains(LAMBDA_CLASS_NAME_PREFIX);
+ }
+
+ // TODO(b/158159959): Remove usage of name-based identification.
public boolean isD8R8SynthesizedClassType() {
String name = toSourceString();
// The synthesized classes listed here must always be unique to a program context and thus
@@ -315,7 +318,8 @@
|| name.contains(ENUM_UNBOXING_UTILITY_CLASS_SUFFIX)
|| name.contains(SyntheticArgumentClass.SYNTHETIC_CLASS_SUFFIX)
// New and hygienic synthesis infrastructure.
- || SyntheticNaming.isSyntheticName(name)
+ || name.contains(SyntheticItems.INTERNAL_SYNTHETIC_CLASS_SEPARATOR)
+ || name.contains(SyntheticItems.EXTERNAL_SYNTHETIC_CLASS_SEPARATOR)
// Only generated in core lib.
|| name.contains(EMULATE_LIBRARY_CLASS_NAME_SUFFIX)
|| name.contains(TYPE_WRAPPER_SUFFIX)
@@ -333,9 +337,12 @@
private static boolean isSynthesizedTypeThatCouldBeDuplicated(String name) {
// Any entry that is removed from here must be added to OLD_SYNTHESIZED_NAMES to ensure that
// newer releases can be used to merge previous builds.
- return name.contains(LAMBDA_GROUP_CLASS_NAME_PREFIX) // Could collide.
+ return name.contains(LAMBDA_CLASS_NAME_PREFIX) // Could collide.
+ || name.contains(LAMBDA_GROUP_CLASS_NAME_PREFIX) // Could collide.
|| name.contains(OutlineOptions.CLASS_NAME) // Global singleton.
- || name.contains(NestBasedAccessDesugaring.NEST_CONSTRUCTOR_NAME); // Global singleton.
+ || name.contains(TwrCloseResourceRewriter.UTILITY_CLASS_NAME) // Global singleton.
+ || name.contains(NestBasedAccessDesugaring.NEST_CONSTRUCTOR_NAME) // Global singleton.
+ || name.contains(ServiceLoaderRewriter.SERVICE_LOADER_CLASS_NAME); // Global singleton.
}
private boolean oldSynthesizedName(String name) {
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java
index 2328408..0e3d784 100644
--- a/src/main/java/com/android/tools/r8/graph/GraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -5,6 +5,7 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.horizontalclassmerging.ClassMerger.CLASS_ID_FIELD_NAME;
+import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX;
import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_INSTANCE_FIELD_NAME;
import com.android.tools.r8.errors.Unreachable;
@@ -619,7 +620,7 @@
// that they can be mapped back to the original program.
DexField originalField = getOriginalFieldSignature(field.getReference());
assert originalFields.contains(originalField)
- || isD8R8SynthesizedField(originalField, appView)
+ || isD8R8SynthesizedField(originalField, dexItemFactory)
: "Unable to map field `"
+ field.getReference().toSourceString()
+ "` back to original program";
@@ -637,16 +638,16 @@
return true;
}
- private boolean isD8R8SynthesizedField(DexField field, AppView<?> appView) {
+ private boolean isD8R8SynthesizedField(DexField field, DexItemFactory dexItemFactory) {
// TODO(b/167947782): Should be a general check to see if the field is D8/R8 synthesized
// instead of relying on field names.
- if (field.match(appView.dexItemFactory().objectMembers.clinitField)) {
+ if (field.match(dexItemFactory.objectMembers.clinitField)) {
return true;
}
if (field.getName().toSourceString().equals(CLASS_ID_FIELD_NAME)) {
return true;
}
- if (appView.getSyntheticItems().isSyntheticClass(field.getHolderType())
+ if (field.getHolderType().toSourceString().contains(LAMBDA_CLASS_NAME_PREFIX)
&& field.getName().toSourceString().equals(LAMBDA_INSTANCE_FIELD_NAME)) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/graph/NestHostClassAttribute.java b/src/main/java/com/android/tools/r8/graph/NestHostClassAttribute.java
index c9969a6..e007f4e 100644
--- a/src/main/java/com/android/tools/r8/graph/NestHostClassAttribute.java
+++ b/src/main/java/com/android/tools/r8/graph/NestHostClassAttribute.java
@@ -5,19 +5,12 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.utils.structural.StructuralItem;
-import com.android.tools.r8.utils.structural.StructuralMapping;
-import com.android.tools.r8.utils.structural.StructuralSpecification;
import org.objectweb.asm.ClassWriter;
-public class NestHostClassAttribute implements StructuralItem<NestHostClassAttribute> {
+public class NestHostClassAttribute {
private final DexType nestHost;
- private static void specify(StructuralSpecification<NestHostClassAttribute, ?> spec) {
- spec.withItem(a -> a.nestHost);
- }
-
public NestHostClassAttribute(DexType nestHost) {
this.nestHost = nestHost;
}
@@ -34,14 +27,4 @@
assert nestHost != null;
writer.visitNestHost(lens.lookupInternalName(nestHost));
}
-
- @Override
- public NestHostClassAttribute self() {
- return this;
- }
-
- @Override
- public StructuralMapping<NestHostClassAttribute> getStructuralMapping() {
- return NestHostClassAttribute::specify;
- }
}
diff --git a/src/main/java/com/android/tools/r8/graph/NestMemberClassAttribute.java b/src/main/java/com/android/tools/r8/graph/NestMemberClassAttribute.java
index 0d7c19d..f9d1a35 100644
--- a/src/main/java/com/android/tools/r8/graph/NestMemberClassAttribute.java
+++ b/src/main/java/com/android/tools/r8/graph/NestMemberClassAttribute.java
@@ -5,21 +5,14 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.utils.structural.StructuralItem;
-import com.android.tools.r8.utils.structural.StructuralMapping;
-import com.android.tools.r8.utils.structural.StructuralSpecification;
import java.util.Collections;
import java.util.List;
import org.objectweb.asm.ClassWriter;
-public class NestMemberClassAttribute implements StructuralItem<NestMemberClassAttribute> {
+public class NestMemberClassAttribute {
private final DexType nestMember;
- private static void specify(StructuralSpecification<NestMemberClassAttribute, ?> spec) {
- spec.withItem(a -> a.nestMember);
- }
-
public NestMemberClassAttribute(DexType nestMember) {
this.nestMember = nestMember;
}
@@ -36,14 +29,4 @@
assert nestMember != null;
writer.visitNestMember(lens.lookupInternalName(nestMember));
}
-
- @Override
- public NestMemberClassAttribute self() {
- return this;
- }
-
- @Override
- public StructuralMapping<NestMemberClassAttribute> getStructuralMapping() {
- return NestMemberClassAttribute::specify;
- }
}
diff --git a/src/main/java/com/android/tools/r8/graph/TreeFixerBase.java b/src/main/java/com/android/tools/r8/graph/TreeFixerBase.java
index c1d5981..a10db7147 100644
--- a/src/main/java/com/android/tools/r8/graph/TreeFixerBase.java
+++ b/src/main/java/com/android/tools/r8/graph/TreeFixerBase.java
@@ -15,7 +15,7 @@
private final AppView<?> appView;
private final DexItemFactory dexItemFactory;
- private final Map<DexType, DexProgramClass> programClassCache = new IdentityHashMap<>();
+ private Map<DexType, DexProgramClass> newProgramClasses = null;
private final Map<DexType, DexProgramClass> synthesizedFromClasses = new IdentityHashMap<>();
private final Map<DexProto, DexProto> protoFixupCache = new IdentityHashMap<>();
@@ -53,13 +53,13 @@
}
/** Fixup a collection of classes. */
- public List<DexProgramClass> fixupClasses(Collection<DexProgramClass> classes) {
- List<DexProgramClass> newProgramClasses = new ArrayList<>();
+ public Collection<DexProgramClass> fixupClasses(Collection<DexProgramClass> classes) {
+ assert newProgramClasses == null;
+ newProgramClasses = new IdentityHashMap<>();
for (DexProgramClass clazz : classes) {
- newProgramClasses.add(
- programClassCache.computeIfAbsent(clazz.getType(), ignore -> fixupClass(clazz)));
+ newProgramClasses.computeIfAbsent(clazz.getType(), ignore -> fixupClass(clazz));
}
- return newProgramClasses;
+ return newProgramClasses.values();
}
// Should remain private as the correctness of the fixup requires the lazy 'newProgramClasses'.
@@ -70,7 +70,7 @@
clazz.getOriginKind(),
clazz.getOrigin(),
clazz.getAccessFlags(),
- clazz.superType == null ? null : fixupType(clazz.superType),
+ fixupType(clazz.superType),
fixupTypeList(clazz.interfaces),
clazz.getSourceFile(),
fixupNestHost(clazz.getNestHostClassAttribute()),
@@ -250,6 +250,7 @@
// Should remain private as its correctness relies on the setup of 'newProgramClasses'.
private Collection<DexProgramClass> fixupSynthesizedFrom(
Collection<DexProgramClass> synthesizedFrom) {
+ assert newProgramClasses != null;
if (synthesizedFrom.isEmpty()) {
return synthesizedFrom;
}
@@ -260,7 +261,7 @@
// is no longer in the application?
Map<DexType, DexProgramClass> classes =
appView.appInfo().definitionForWithoutExistenceAssert(clazz.getType()) != null
- ? programClassCache
+ ? newProgramClasses
: synthesizedFromClasses;
DexProgramClass newClass =
classes.computeIfAbsent(clazz.getType(), ignore -> fixupClass(clazz));
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index aa79316..d08eb3e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -240,9 +240,7 @@
new DesugaredLibraryAPIConverter(appView, Mode.GENERATE_CALLBACKS_AND_WRAPPERS);
this.backportedMethodRewriter = new BackportedMethodRewriter(appView);
this.twrCloseResourceRewriter =
- TwrCloseResourceRewriter.enableTwrCloseResourceDesugaring(appView.options())
- ? new TwrCloseResourceRewriter(appView)
- : null;
+ enableTwrCloseResourceDesugaring() ? new TwrCloseResourceRewriter(appView, this) : null;
this.d8NestBasedAccessDesugaring =
options.shouldDesugarNests() ? new D8NestBasedAccessDesugaring(appView) : null;
this.lambdaMerger = null;
@@ -276,9 +274,8 @@
? new InterfaceMethodRewriter(appView, this)
: null;
this.twrCloseResourceRewriter =
- (TwrCloseResourceRewriter.enableTwrCloseResourceDesugaring(options)
- && !appView.enableWholeProgramOptimizations())
- ? new TwrCloseResourceRewriter(appView)
+ (options.desugarState == DesugarState.ON && enableTwrCloseResourceDesugaring())
+ ? new TwrCloseResourceRewriter(appView, this)
: null;
this.backportedMethodRewriter =
(options.desugarState == DesugarState.ON && !appView.enableWholeProgramOptimizations())
@@ -385,6 +382,20 @@
this(AppView.createForD8(appInfo), timing, printer, MainDexTracingResult.NONE);
}
+ private boolean enableTwrCloseResourceDesugaring() {
+ return enableTryWithResourcesDesugaring() && !options.canUseTwrCloseResourceMethod();
+ }
+
+ private boolean enableTryWithResourcesDesugaring() {
+ switch (options.tryWithResourcesDesugaring) {
+ case Off:
+ return false;
+ case Auto:
+ return !options.canUseSuppressedExceptions();
+ }
+ throw new Unreachable();
+ }
+
private void removeLambdaDeserializationMethods() {
if (lambdaRewriter != null) {
lambdaRewriter.removeLambdaDeserializationMethods(appView.appInfo().classes());
@@ -398,10 +409,11 @@
}
}
- private void synthesizeLambdaClasses(ExecutorService executorService) throws ExecutionException {
+ private void synthesizeLambdaClasses(Builder<?> builder, ExecutorService executorService)
+ throws ExecutionException {
if (lambdaRewriter != null) {
assert !appView.enableWholeProgramOptimizations();
- lambdaRewriter.finalizeLambdaDesugaringForD8(this, executorService);
+ lambdaRewriter.finalizeLambdaDesugaringForD8(builder, this, executorService);
}
}
@@ -423,16 +435,16 @@
Flavor includeAllResources,
ExecutorService executorService)
throws ExecutionException {
- assert !appView.getSyntheticItems().hasPendingSyntheticClasses();
if (interfaceMethodRewriter != null) {
interfaceMethodRewriter.desugarInterfaceMethods(
builder, includeAllResources, executorService);
}
}
- private void processTwrCloseResourceUtilityMethods() {
+ private void synthesizeTwrCloseResourceUtilityClass(
+ Builder<?> builder, ExecutorService executorService) throws ExecutionException {
if (twrCloseResourceRewriter != null) {
- twrCloseResourceRewriter.processSynthesizedMethods(this);
+ twrCloseResourceRewriter.synthesizeUtilityClass(builder, executorService, options);
}
}
@@ -509,21 +521,9 @@
builder.setHighestSortingString(highestSortingString);
desugarNestBasedAccess(builder, executor);
-
- // Synthesize lambda classes and commit to the app in full.
- synthesizeLambdaClasses(executor);
- processTwrCloseResourceUtilityMethods();
- if (appView.getSyntheticItems().hasPendingSyntheticClasses()) {
- appView.setAppInfo(
- new AppInfo(
- appView.appInfo().getSyntheticItems().commit(builder.build()),
- appView.appInfo().getMainDexClasses()));
- application = appView.appInfo().app();
- builder = application.builder();
- builder.setHighestSortingString(highestSortingString);
- }
-
+ synthesizeLambdaClasses(builder, executor);
desugarInterfaceMethods(builder, ExcludeDexResources, executor);
+ synthesizeTwrCloseResourceUtilityClass(builder, executor);
processSynthesizedJava8UtilityClasses(executor);
synthesizeRetargetClass(builder, executor);
synthesizeInvokeSpecialBridges(executor);
@@ -532,10 +532,10 @@
timing.end();
- application = builder.build();
+ DexApplication app = builder.build();
appView.setAppInfo(
new AppInfo(
- appView.appInfo().getSyntheticItems().commit(application),
+ appView.appInfo().getSyntheticItems().commit(app),
appView.appInfo().getMainDexClasses()));
}
@@ -793,14 +793,6 @@
appView.clearCodeRewritings();
}
- // Commit synthetics before creating a builder (otherwise the builder will not include the
- // synthetics.)
- if (appView.getSyntheticItems().hasPendingSyntheticClasses()) {
- appView.setAppInfo(
- appView
- .appInfo()
- .rebuildWithLiveness(appView.getSyntheticItems().commit(appView.appInfo().app())));
- }
// Build a new application with jumbo string info.
Builder<?> builder = appView.appInfo().app().builder();
builder.setHighestSortingString(highestSortingString);
@@ -810,6 +802,7 @@
feedback.updateVisibleOptimizationInfo();
printPhase("Utility classes synthesis");
+ synthesizeTwrCloseResourceUtilityClass(builder, executorService);
processSynthesizedJava8UtilityClasses(executorService);
synthesizeRetargetClass(builder, executorService);
synthesizeEnumUnboxingUtilityMethods(executorService);
@@ -821,9 +814,11 @@
printPhase("Desugared library API Conversion finalization");
generateDesugaredLibraryAPIWrappers(builder, executorService);
- if (serviceLoaderRewriter != null) {
+ if (serviceLoaderRewriter != null && serviceLoaderRewriter.getSynthesizedClass() != null) {
+ appView.appInfo().addSynthesizedClass(serviceLoaderRewriter.getSynthesizedClass(), true);
processSynthesizedServiceLoaderMethods(
- serviceLoaderRewriter.getServiceLoadMethods(), executorService);
+ serviceLoaderRewriter.getSynthesizedClass(), executorService);
+ builder.addSynthesizedClass(serviceLoaderRewriter.getSynthesizedClass());
}
// Update optimization info for all synthesized methods at once.
@@ -964,10 +959,11 @@
}
private void processSynthesizedServiceLoaderMethods(
- List<ProgramMethod> serviceLoadMethods, ExecutorService executorService)
- throws ExecutionException {
+ DexProgramClass synthesizedClass, ExecutorService executorService) throws ExecutionException {
ThreadUtils.processItems(
- serviceLoadMethods, this::forEachSynthesizedServiceLoaderMethod, executorService);
+ synthesizedClass::forEachProgramMethod,
+ this::forEachSynthesizedServiceLoaderMethod,
+ executorService);
}
private void forEachSynthesizedServiceLoaderMethod(ProgramMethod method) {
@@ -1478,7 +1474,7 @@
deadCodeRemover.run(code, timing);
assert code.isConsistentSSA();
- if (options.desugarState == DesugarState.ON && options.enableTryWithResourcesDesugaring()) {
+ if (options.desugarState == DesugarState.ON && enableTryWithResourcesDesugaring()) {
timing.begin("Rewrite Throwable suppresed methods");
codeRewriter.rewriteThrowableAddAndGetSuppressed(code);
timing.end();
@@ -1565,7 +1561,7 @@
if (twrCloseResourceRewriter != null) {
timing.begin("Rewrite TWR close");
- twrCloseResourceRewriter.rewriteIR(code);
+ twrCloseResourceRewriter.rewriteMethodCode(code);
timing.end();
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java b/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
index a89e3f9..f348214 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/NeedsIRDesugarUseRegistry.java
@@ -67,13 +67,6 @@
registerDesugaredLibraryAPIConverter(method);
}
- private void registerTwrCloseResourceRewriting(DexMethod method) {
- if (!needsDesugarging) {
- needsDesugarging =
- TwrCloseResourceRewriter.isTwrCloseResourceMethod(method, appView.dexItemFactory());
- }
- }
-
private void registerBackportedMethodRewriting(DexMethod method) {
if (!needsDesugarging) {
needsDesugarging = backportedMethodRewriter.needsDesugaring(method);
@@ -106,7 +99,9 @@
@Override
public void registerInvokeStatic(DexMethod method) {
- registerTwrCloseResourceRewriting(method);
+ if (!needsDesugarging) {
+ needsDesugarging = TwrCloseResourceRewriter.isSynthesizedCloseResourceMethod(method, appView);
+ }
registerBackportedMethodRewriting(method);
registerLibraryRetargeting(method, false);
registerInterfaceMethodRewriting(method, false);
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 ace471b..40b18c9 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
@@ -7,6 +7,7 @@
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -32,7 +33,6 @@
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.synthesis.SyntheticNaming;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
@@ -1406,13 +1406,15 @@
return appInfo
.getSyntheticItems()
.createMethod(
- SyntheticNaming.SyntheticKind.BACKPORT,
context,
appInfo.dexItemFactory(),
builder ->
builder
.setProto(getProto(appInfo.dexItemFactory()))
- .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setAccessFlags(
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC,
+ false))
.setCode(
methodSig -> generateTemplateMethod(appInfo.app().options, methodSig)));
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 9ea3aa3..1529d2f 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.DesugarGraphConsumer;
import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.graph.AppInfo;
@@ -56,7 +57,6 @@
import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.shaking.MainDexClasses;
-import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.IterableUtils;
@@ -406,13 +406,17 @@
appView
.getSyntheticItems()
.createMethod(
- SyntheticNaming.SyntheticKind.STATIC_INTERFACE_CALL,
context.getHolder(),
factory,
syntheticMethodBuilder ->
syntheticMethodBuilder
.setProto(invokedMethod.proto)
- .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setAccessFlags(
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC
+ | Constants.ACC_STATIC
+ | Constants.ACC_SYNTHETIC,
+ false))
.setCode(
m ->
ForwardMethodBuilder.builder(factory)
@@ -1124,7 +1128,6 @@
Builder<?> builder, Flavor flavour, Consumer<ProgramMethod> newSynthesizedMethodConsumer) {
ClassProcessor processor = new ClassProcessor(appView, this, newSynthesizedMethodConsumer);
// First we compute all desugaring *without* introducing forwarding methods.
- assert appView.getSyntheticItems().verifyNonLegacySyntheticsAreCommitted();
for (DexProgramClass clazz : builder.getProgramClasses()) {
if (shouldProcess(clazz, flavour, false)) {
if (appView.isAlreadyLibraryDesugared(clazz)) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index 966cfec..a50581e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -19,8 +20,10 @@
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DexValue.DexValueNull;
import com.android.tools.r8.graph.FieldAccessFlags;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
@@ -34,11 +37,18 @@
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode;
import com.android.tools.r8.ir.synthetic.SynthesizedCode;
-import com.android.tools.r8.synthesis.SyntheticClassBuilder;
+import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.utils.OptionalBool;
+import com.google.common.base.Suppliers;
+import com.google.common.primitives.Longs;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Supplier;
+import java.util.zip.CRC32;
/**
* Represents lambda class generated for a lambda descriptor in context of lambda instantiation
@@ -67,27 +77,31 @@
final DexMethod classConstructor;
public final DexField lambdaField;
public final Target target;
-
- // Considered final but is set after due to circularity in allocation.
- private DexProgramClass clazz = null;
+ public final AtomicBoolean addToMainDexList = new AtomicBoolean(false);
+ private final Collection<DexProgramClass> synthesizedFrom = new ArrayList<>(1);
+ private final Supplier<DexProgramClass> lazyDexClass =
+ Suppliers.memoize(this::synthesizeLambdaClass); // NOTE: thread-safe.
LambdaClass(
- SyntheticClassBuilder builder,
AppView<?> appView,
LambdaRewriter rewriter,
ProgramMethod accessedFrom,
+ DexType lambdaClassType,
LambdaDescriptor descriptor) {
assert rewriter != null;
+ assert lambdaClassType != null;
assert descriptor != null;
- this.type = builder.getType();
+
this.appView = appView;
this.rewriter = rewriter;
+ this.type = lambdaClassType;
this.descriptor = descriptor;
- DexItemFactory factory = builder.getFactory();
+ DexItemFactory factory = appView.dexItemFactory();
DexProto constructorProto = factory.createProto(
factory.voidType, descriptor.captures.values);
- this.constructor = factory.createMethod(type, constructorProto, factory.constructorMethodName);
+ this.constructor =
+ factory.createMethod(lambdaClassType, constructorProto, factory.constructorMethodName);
this.target = createTarget(accessedFrom);
@@ -95,32 +109,98 @@
this.classConstructor =
!stateless
? null
- : factory.createMethod(type, constructorProto, factory.classConstructorMethodName);
+ : factory.createMethod(
+ lambdaClassType, constructorProto, factory.classConstructorMethodName);
this.lambdaField =
- !stateless ? null : factory.createField(type, type, rewriter.instanceFieldName);
-
- // Synthesize the program class one all fields are set.
- synthesizeLambdaClass(builder);
+ !stateless
+ ? null
+ : factory.createField(lambdaClassType, lambdaClassType, rewriter.instanceFieldName);
}
- public final DexProgramClass getLambdaProgramClass() {
- assert clazz != null;
+ // Generate unique lambda class type for lambda descriptor and instantiation point context.
+ public static DexType createLambdaClassType(
+ AppView<?> appView, ProgramMethod accessedFrom, LambdaDescriptor match) {
+ StringBuilder lambdaClassDescriptor = new StringBuilder("L");
+
+ // We always create lambda class in the same package where it is referenced.
+ String packageDescriptor = accessedFrom.getHolderType().getPackageDescriptor();
+ if (!packageDescriptor.isEmpty()) {
+ lambdaClassDescriptor.append(packageDescriptor).append('/');
+ }
+
+ // Lambda class name prefix
+ lambdaClassDescriptor.append(LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX);
+
+ // If the lambda class should match 1:1 the class it is accessed from, we
+ // just add the name of this type to make lambda class name unique.
+ // It also helps link the class lambda originated from in some cases.
+ if (match.delegatesToLambdaImplMethod() || match.needsAccessor(accessedFrom)) {
+ lambdaClassDescriptor.append(accessedFrom.getHolderType().getName()).append('$');
+ }
+
+ // Add unique lambda descriptor id
+ lambdaClassDescriptor.append(match.uniqueId).append(';');
+ return appView.dexItemFactory().createType(lambdaClassDescriptor.toString());
+ }
+
+ public final DexProgramClass getOrCreateLambdaClass() {
+ return lazyDexClass.get();
+ }
+
+ private DexProgramClass synthesizeLambdaClass() {
+ DexMethod mainMethod =
+ appView.dexItemFactory().createMethod(type, descriptor.erasedProto, descriptor.name);
+
+ DexProgramClass clazz =
+ new DexProgramClass(
+ type,
+ null,
+ new SynthesizedOrigin("lambda desugaring", getClass()),
+ // Make the synthesized class public, as it might end up being accessed from a different
+ // classloader (package private access is not allowed across classloaders, b/72538146).
+ ClassAccessFlags.fromDexAccessFlags(
+ Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC),
+ appView.dexItemFactory().objectType,
+ buildInterfaces(),
+ appView.dexItemFactory().createString("lambda"),
+ null,
+ Collections.emptyList(),
+ null,
+ Collections.emptyList(),
+ ClassSignature.noSignature(),
+ DexAnnotationSet.empty(),
+ synthesizeStaticFields(),
+ synthesizeInstanceFields(),
+ synthesizeDirectMethods(),
+ synthesizeVirtualMethods(mainMethod),
+ appView.dexItemFactory().getSkipNameValidationForTesting(),
+ LambdaClass::computeChecksumForSynthesizedClass);
+ appView.appInfo().addSynthesizedClass(clazz, false);
+
+ // The method addSynthesizedFrom() may be called concurrently. To avoid a Concurrent-
+ // ModificationException we must use synchronization.
+ synchronized (synthesizedFrom) {
+ synthesizedFrom.forEach(clazz::addSynthesizedFrom);
+ }
return clazz;
}
- void setClass(DexProgramClass clazz) {
- assert this.clazz == null;
- assert clazz != null;
- assert type == clazz.type;
- this.clazz = clazz;
- }
+ private static long computeChecksumForSynthesizedClass(DexProgramClass clazz) {
+ // Checksum of synthesized classes are compute based off the depending input. This might
+ // create false positives (ie: unchanged lambda class detected as changed even thought only
+ // an unrelated part from a synthesizedFrom class is changed).
- private void synthesizeLambdaClass(SyntheticClassBuilder builder) {
- builder.setInterfaces(descriptor.interfaces);
- synthesizeStaticFields(builder);
- synthesizeInstanceFields(builder);
- synthesizeDirectMethods(builder);
- synthesizeVirtualMethods(builder);
+ // Ideally, we should use some hashcode of the dex program class that is deterministic across
+ // compiles.
+ Collection<DexProgramClass> synthesizedFrom = clazz.getSynthesizedFrom();
+ ByteBuffer buffer = ByteBuffer.allocate(synthesizedFrom.size() * Longs.BYTES);
+ for (DexProgramClass from : synthesizedFrom) {
+ buffer.putLong(from.getChecksum());
+ }
+ CRC32 crc = new CRC32();
+ byte[] array = buffer.array();
+ crc.update(array, 0, array.length);
+ return crc.getValue();
}
final DexField getCaptureField(int index) {
@@ -136,15 +216,24 @@
return descriptor.isStateless();
}
- // Synthesize virtual methods.
- private void synthesizeVirtualMethods(SyntheticClassBuilder builder) {
- DexMethod mainMethod =
- appView.dexItemFactory().createMethod(type, descriptor.erasedProto, descriptor.name);
+ void addSynthesizedFrom(DexProgramClass clazz) {
+ assert clazz != null;
+ synchronized (synthesizedFrom) {
+ if (synthesizedFrom.add(clazz)) {
+ // The lambda class may already have been synthesized, and we therefore need to update the
+ // synthesized lambda class as well.
+ getOrCreateLambdaClass().addSynthesizedFrom(clazz);
+ }
+ }
+ }
- List<DexEncodedMethod> methods = new ArrayList<>(1 + descriptor.bridges.size());
+ // Synthesize virtual methods.
+ private DexEncodedMethod[] synthesizeVirtualMethods(DexMethod mainMethod) {
+ DexEncodedMethod[] methods = new DexEncodedMethod[1 + descriptor.bridges.size()];
+ int index = 0;
// Synthesize main method.
- methods.add(
+ methods[index++] =
new DexEncodedMethod(
mainMethod,
MethodAccessFlags.fromSharedAccessFlags(
@@ -153,13 +242,13 @@
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
LambdaMainMethodSourceCode.build(this, mainMethod),
- true));
+ true);
// Synthesize bridge methods.
for (DexProto bridgeProto : descriptor.bridges) {
DexMethod bridgeMethod =
appView.dexItemFactory().createMethod(type, bridgeProto, descriptor.name);
- methods.add(
+ methods[index++] =
new DexEncodedMethod(
bridgeMethod,
MethodAccessFlags.fromSharedAccessFlags(
@@ -172,18 +261,18 @@
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
LambdaBridgeMethodSourceCode.build(this, bridgeMethod, mainMethod),
- true));
+ true);
}
- builder.setVirtualMethods(methods);
+ return methods;
}
// Synthesize direct methods.
- private void synthesizeDirectMethods(SyntheticClassBuilder builder) {
+ private DexEncodedMethod[] synthesizeDirectMethods() {
boolean stateless = isStateless();
- List<DexEncodedMethod> methods = new ArrayList<>(stateless ? 2 : 1);
+ DexEncodedMethod[] methods = new DexEncodedMethod[stateless ? 2 : 1];
// Constructor.
- methods.add(
+ methods[0] =
new DexEncodedMethod(
constructor,
MethodAccessFlags.fromSharedAccessFlags(
@@ -194,11 +283,11 @@
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
LambdaConstructorSourceCode.build(this),
- true));
+ true);
// Class constructor for stateless lambda classes.
if (stateless) {
- methods.add(
+ methods[1] =
new DexEncodedMethod(
classConstructor,
MethodAccessFlags.fromSharedAccessFlags(
@@ -207,50 +296,61 @@
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
LambdaClassConstructorSourceCode.build(this),
- true));
- feedback.classInitializerMayBePostponed(methods.get(1));
+ true);
+ feedback.classInitializerMayBePostponed(methods[1]);
}
- builder.setDirectMethods(methods);
+ return methods;
}
// Synthesize instance fields to represent captured values.
- private void synthesizeInstanceFields(SyntheticClassBuilder builder) {
+ private DexEncodedField[] synthesizeInstanceFields() {
DexType[] fieldTypes = descriptor.captures.values;
int fieldCount = fieldTypes.length;
- List<DexEncodedField> fields = new ArrayList<>(fieldCount);
+ DexEncodedField[] fields = new DexEncodedField[fieldCount];
for (int i = 0; i < fieldCount; i++) {
FieldAccessFlags accessFlags =
FieldAccessFlags.fromSharedAccessFlags(
Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC);
- fields.add(
+ fields[i] =
new DexEncodedField(
getCaptureField(i),
accessFlags,
FieldTypeSignature.noSignature(),
DexAnnotationSet.empty(),
- null));
+ null);
}
- builder.setInstanceFields(fields);
+ return fields;
}
// Synthesize static fields to represent singleton instance.
- private void synthesizeStaticFields(SyntheticClassBuilder builder) {
- if (isStateless()) {
- // Create instance field for stateless lambda.
- assert this.lambdaField != null;
- builder.setStaticFields(
- Collections.singletonList(
- new DexEncodedField(
- this.lambdaField,
- FieldAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC
- | Constants.ACC_FINAL
- | Constants.ACC_SYNTHETIC
- | Constants.ACC_STATIC),
- FieldTypeSignature.noSignature(),
- DexAnnotationSet.empty(),
- DexValueNull.NULL)));
+ private DexEncodedField[] synthesizeStaticFields() {
+ if (!isStateless()) {
+ return DexEncodedField.EMPTY_ARRAY;
}
+
+ // Create instance field for stateless lambda.
+ assert this.lambdaField != null;
+ DexEncodedField[] fields = new DexEncodedField[1];
+ fields[0] =
+ new DexEncodedField(
+ this.lambdaField,
+ FieldAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC
+ | Constants.ACC_FINAL
+ | Constants.ACC_SYNTHETIC
+ | Constants.ACC_STATIC),
+ FieldTypeSignature.noSignature(),
+ DexAnnotationSet.empty(),
+ DexValueNull.NULL);
+ return fields;
+ }
+
+ // Build a list of implemented interfaces.
+ private DexTypeList buildInterfaces() {
+ List<DexType> interfaces = descriptor.interfaces;
+ return interfaces.isEmpty()
+ ? DexTypeList.empty()
+ : new DexTypeList(interfaces.toArray(DexType.EMPTY_ARRAY));
}
// Creates a delegation target for this particular lambda class. Note that we
@@ -543,15 +643,11 @@
newMethod.getCode(), callTarget.getArity(), appView);
return newMethod;
});
- if (replacement != null) {
- return new ProgramMethod(implMethodHolder, replacement);
- }
- // The method might already have been moved by another invoke-dynamic targeting it.
- // If so, it must be defined on the holder.
- ProgramMethod modified = implMethodHolder.lookupProgramMethod(callTarget);
- assert modified != null;
- assert modified.getDefinition().isNonPrivateVirtualMethod();
- return modified;
+
+ assert replacement != null
+ : "Unexpected failure to find direct lambda target for: " + implMethod.qualifiedName();
+
+ return new ProgramMethod(implMethodHolder, replacement);
}
}
@@ -617,27 +713,11 @@
rewriter.forcefullyMoveMethod(encodedMethod.method, callTarget);
return newMethod;
});
- if (replacement != null) {
- return new ProgramMethod(implMethodHolder, replacement);
- }
- // The method might already have been moved by another invoke-dynamic targeting it.
- // If so, it must be defined on the holder.
- ProgramMethod modified = implMethodHolder.lookupProgramMethod(callTarget);
- assert modified != null;
- assert modified.getDefinition().isNonPrivateVirtualMethod();
- return modified;
+ return new ProgramMethod(implMethodHolder, replacement);
}
private ProgramMethod createSyntheticAccessor(
DexMethod implMethod, DexProgramClass implMethodHolder) {
- // The accessor might already have been created by another invoke-dynamic targeting it.
- ProgramMethod existing = implMethodHolder.lookupProgramMethod(callTarget);
- if (existing != null) {
- assert existing.getAccessFlags().isSynthetic();
- assert existing.getAccessFlags().isPublic();
- assert existing.getDefinition().isVirtualMethod();
- return existing;
- }
MethodAccessFlags accessorFlags =
MethodAccessFlags.fromSharedAccessFlags(
Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC, false);
@@ -682,15 +762,6 @@
DexProgramClass accessorClass = appView.definitionForProgramType(callTarget.holder);
assert accessorClass != null;
- // The accessor might already have been created by another invoke-dynamic targeting it.
- ProgramMethod existing = accessorClass.lookupProgramMethod(callTarget);
- if (existing != null) {
- assert existing.getAccessFlags().isSynthetic();
- assert existing.getAccessFlags().isPublic();
- assert existing.getAccessFlags().isStatic();
- return existing;
- }
-
// Always make the method public to provide access when r8 minification is allowed to move
// the lambda class accessing this method to another package (-allowaccessmodification).
MethodAccessFlags accessorFlags =
@@ -708,7 +779,11 @@
AccessorMethodSourceCode.build(LambdaClass.this, callTarget),
true);
- accessorClass.addDirectMethod(accessorEncodedMethod);
+ // We may arrive here concurrently so we need must update the methods of the class atomically.
+ synchronized (accessorClass) {
+ accessorClass.addDirectMethod(accessorEncodedMethod);
+ }
+
return new ProgramMethod(accessorClass, accessorEncodedMethod);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
index ee459b4..f464fdd 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -16,6 +16,7 @@
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.DexApplication.Builder;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
@@ -27,10 +28,19 @@
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeCustom;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.StaticGet;
+import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.synthesis.SyntheticNaming;
-import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
@@ -39,8 +49,9 @@
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
+import java.util.IdentityHashMap;
import java.util.List;
+import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@@ -58,6 +69,7 @@
public class LambdaRewriter {
// Public for testing.
+ public static final String LAMBDA_CLASS_NAME_PREFIX = "-$$Lambda$";
public static final String LAMBDA_GROUP_CLASS_NAME_PREFIX = "-$$LambdaGroup$";
static final String EXPECTED_LAMBDA_METHOD_PREFIX = "lambda$";
public static final String LAMBDA_INSTANCE_FIELD_NAME = "INSTANCE";
@@ -69,10 +81,16 @@
private final LambdaRewriterLens.Builder lensBuilder = LambdaRewriterLens.builder();
private final Set<DexMethod> forcefullyMovedMethods = Sets.newIdentityHashSet();
+ // Maps call sites seen so far to inferred lambda descriptor. It is intended
+ // to help avoid re-matching call sites we already seen. Note that same call
+ // site may match one or several lambda classes.
+ //
+ // NOTE: synchronize concurrent access on `knownCallSites`.
+ private final Map<DexCallSite, LambdaDescriptor> knownCallSites = new IdentityHashMap<>();
// Maps lambda class type into lambda class representation. Since lambda class
// type uniquely defines lambda class, effectively canonicalizes lambda classes.
// NOTE: synchronize concurrent access on `knownLambdaClasses`.
- private final List<LambdaClass> knownLambdaClasses = new ArrayList<>();
+ private final Map<DexType, LambdaClass> knownLambdaClasses = new IdentityHashMap<>();
public LambdaRewriter(AppView<?> appView) {
this.appView = appView;
@@ -120,7 +138,7 @@
if (descriptor == null) {
return null;
}
- return createLambdaClass(descriptor, method);
+ return getOrCreateLambdaClass(descriptor, method);
});
}
@@ -191,10 +209,16 @@
}
/** Generates lambda classes and adds them to the builder. */
- public void finalizeLambdaDesugaringForD8(IRConverter converter, ExecutorService executorService)
+ public void finalizeLambdaDesugaringForD8(
+ Builder<?> builder, IRConverter converter, ExecutorService executorService)
throws ExecutionException {
synthesizeAccessibilityBridgesForLambdaClassesD8(
- knownLambdaClasses, converter, executorService);
+ knownLambdaClasses.values(), converter, executorService);
+ for (LambdaClass lambdaClass : knownLambdaClasses.values()) {
+ DexProgramClass synthesizedClass = lambdaClass.getOrCreateLambdaClass();
+ appView.appInfo().addSynthesizedClass(synthesizedClass, lambdaClass.addToMainDexList.get());
+ builder.addSynthesizedClass(synthesizedClass);
+ }
fixup();
optimizeSynthesizedClasses(converter, executorService);
}
@@ -202,35 +226,183 @@
private void optimizeSynthesizedClasses(IRConverter converter, ExecutorService executorService)
throws ExecutionException {
converter.optimizeSynthesizedClasses(
- knownLambdaClasses.stream()
- .map(LambdaClass::getLambdaProgramClass)
+ knownLambdaClasses.values().stream()
+ .map(LambdaClass::getOrCreateLambdaClass)
.collect(ImmutableSet.toImmutableSet()),
executorService);
}
- // Creates a lambda class corresponding to the lambda descriptor and context.
- public LambdaClass createLambdaClass(LambdaDescriptor descriptor, ProgramMethod accessedFrom) {
- Box<LambdaClass> box = new Box<>();
- DexProgramClass clazz =
- appView
- .getSyntheticItems()
- .createClass(
- SyntheticNaming.SyntheticKind.LAMBDA,
- accessedFrom.getHolder(),
- appView.dexItemFactory(),
- builder ->
- box.set(new LambdaClass(builder, appView, this, accessedFrom, descriptor)));
- // Immediately set the actual program class on the lambda.
- LambdaClass lambdaClass = box.get();
- lambdaClass.setClass(clazz);
- synchronized (knownLambdaClasses) {
- knownLambdaClasses.add(lambdaClass);
+ // Matches invoke-custom instruction operands to infer lambda descriptor
+ // corresponding to this lambda invocation point.
+ //
+ // Returns the lambda descriptor or `MATCH_FAILED`.
+ private LambdaDescriptor inferLambdaDescriptor(DexCallSite callSite, ProgramMethod context) {
+ // We check the map before and after inferring lambda descriptor to minimize time
+ // spent in synchronized block. As a result we may throw away calculated descriptor
+ // in rare case when another thread has same call site processed concurrently,
+ // but this is a low price to pay comparing to making whole method synchronous.
+ LambdaDescriptor descriptor = getKnown(knownCallSites, callSite);
+ return descriptor != null
+ ? descriptor
+ : putIfAbsent(
+ knownCallSites,
+ callSite,
+ LambdaDescriptor.infer(callSite, appView.appInfoForDesugaring(), context));
+ }
+
+ // Returns a lambda class corresponding to the lambda descriptor and context,
+ // creates the class if it does not yet exist.
+ public LambdaClass getOrCreateLambdaClass(
+ LambdaDescriptor descriptor, ProgramMethod accessedFrom) {
+ DexType lambdaClassType = LambdaClass.createLambdaClassType(appView, accessedFrom, descriptor);
+ // We check the map twice to to minimize time spent in synchronized block.
+ LambdaClass lambdaClass = getKnown(knownLambdaClasses, lambdaClassType);
+ if (lambdaClass == null) {
+ lambdaClass =
+ putIfAbsent(
+ knownLambdaClasses,
+ lambdaClassType,
+ new LambdaClass(appView, this, accessedFrom, lambdaClassType, descriptor));
+ if (appView.options().isDesugaredLibraryCompilation()) {
+ DexType rewrittenType =
+ appView.rewritePrefix.rewrittenType(accessedFrom.getHolderType(), appView);
+ if (rewrittenType == null) {
+ rewrittenType =
+ appView
+ .options()
+ .desugaredLibraryConfiguration
+ .getEmulateLibraryInterface()
+ .get(accessedFrom.getHolderType());
+ }
+ if (rewrittenType != null) {
+ addRewritingPrefix(accessedFrom, rewrittenType, lambdaClassType);
+ }
+ }
+ }
+ lambdaClass.addSynthesizedFrom(accessedFrom.getHolder());
+ if (appView.appInfo().getMainDexClasses().contains(accessedFrom.getHolder())) {
+ lambdaClass.addToMainDexList.set(true);
}
return lambdaClass;
}
- public Collection<LambdaClass> getKnownLambdaClasses() {
- return Collections.unmodifiableList(knownLambdaClasses);
+ private LambdaClass getKnownLambdaClass(LambdaDescriptor descriptor, ProgramMethod accessedFrom) {
+ DexType lambdaClassType = LambdaClass.createLambdaClassType(appView, accessedFrom, descriptor);
+ return getKnown(knownLambdaClasses, lambdaClassType);
+ }
+
+ private void addRewritingPrefix(
+ ProgramMethod context, DexType rewritten, DexType lambdaClassType) {
+ String javaName = lambdaClassType.toString();
+ String typeString = context.getHolderType().toString();
+ String actualPrefix = typeString.substring(0, typeString.lastIndexOf('.'));
+ String rewrittenString = rewritten.toString();
+ String actualRewrittenPrefix = rewrittenString.substring(0, rewrittenString.lastIndexOf('.'));
+ assert javaName.startsWith(actualPrefix);
+ appView.rewritePrefix.rewriteType(
+ lambdaClassType,
+ appView
+ .dexItemFactory()
+ .createType(
+ DescriptorUtils.javaTypeToDescriptor(
+ actualRewrittenPrefix + javaName.substring(actualPrefix.length()))));
+ }
+
+ private static <K, V> V getKnown(Map<K, V> map, K key) {
+ synchronized (map) {
+ return map.get(key);
+ }
+ }
+
+ private static <K, V> V putIfAbsent(Map<K, V> map, K key, V value) {
+ synchronized (map) {
+ V known = map.get(key);
+ if (known != null) {
+ return known;
+ }
+ map.put(key, value);
+ return value;
+ }
+ }
+
+ // Patches invoke-custom instruction to create or get an instance
+ // of the generated lambda class.
+ private void patchInstruction(
+ InvokeCustom invoke,
+ LambdaClass lambdaClass,
+ IRCode code,
+ ListIterator<BasicBlock> blocks,
+ InstructionListIterator instructions,
+ Set<Value> affectedValues) {
+ assert lambdaClass != null;
+ assert instructions != null;
+
+ // The value representing new lambda instance: we reuse the
+ // value from the original invoke-custom instruction, and thus
+ // all its usages.
+ Value lambdaInstanceValue = invoke.outValue();
+ if (lambdaInstanceValue == null) {
+ // The out value might be empty in case it was optimized out.
+ lambdaInstanceValue =
+ code.createValue(
+ TypeElement.fromDexType(lambdaClass.type, Nullability.maybeNull(), appView));
+ } else {
+ affectedValues.add(lambdaInstanceValue);
+ }
+
+ // For stateless lambdas we replace InvokeCustom instruction with StaticGet
+ // reading the value of INSTANCE field created for singleton lambda class.
+ if (lambdaClass.isStateless()) {
+ instructions.replaceCurrentInstruction(
+ new StaticGet(lambdaInstanceValue, lambdaClass.lambdaField));
+ // Note that since we replace one throwing operation with another we don't need
+ // to have any special handling for catch handlers.
+ return;
+ }
+
+ // For stateful lambdas we always create a new instance since we need to pass
+ // captured values to the constructor.
+ //
+ // We replace InvokeCustom instruction with a new NewInstance instruction
+ // instantiating lambda followed by InvokeDirect instruction calling a
+ // constructor on it.
+ //
+ // original:
+ // Invoke-Custom rResult <- { rArg0, rArg1, ... }; call site: ...
+ //
+ // result:
+ // NewInstance rResult <- LambdaClass
+ // Invoke-Direct { rResult, rArg0, rArg1, ... }; method: void LambdaClass.<init>(...)
+ lambdaInstanceValue.setType(
+ lambdaInstanceValue.getType().asReferenceType().asDefinitelyNotNull());
+ NewInstance newInstance = new NewInstance(lambdaClass.type, lambdaInstanceValue);
+ instructions.replaceCurrentInstruction(newInstance);
+
+ List<Value> arguments = new ArrayList<>();
+ arguments.add(lambdaInstanceValue);
+ arguments.addAll(invoke.arguments()); // Optional captures.
+ InvokeDirect constructorCall =
+ new InvokeDirect(lambdaClass.constructor, null /* no return value */, arguments);
+ instructions.add(constructorCall);
+ constructorCall.setPosition(newInstance.getPosition());
+
+ // If we don't have catch handlers we are done.
+ if (!constructorCall.getBlock().hasCatchHandlers()) {
+ return;
+ }
+
+ // Move the iterator back to position it between the two instructions, split
+ // the block between the two instructions, and copy the catch handlers.
+ instructions.previous();
+ assert instructions.peekNext().isInvokeDirect();
+ BasicBlock currentBlock = newInstance.getBlock();
+ BasicBlock nextBlock = instructions.split(code, blocks);
+ assert !instructions.hasNext();
+ nextBlock.copyCatchHandlers(code, blocks, currentBlock, appView.options());
+ }
+
+ public Map<DexType, LambdaClass> getKnownLambdaClasses() {
+ return knownLambdaClasses;
}
public NestedGraphLens fixup() {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
index 98acb2d..f7be74d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/TwrCloseResourceRewriter.java
@@ -4,137 +4,178 @@
package com.android.tools.r8.ir.desugar;
-import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.ClassAccessFlags;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexApplication.Builder;
+import com.android.tools.r8.graph.DexEncodedField;
+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.DexProgramClass;
import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.desugar.backports.BackportedMethods;
-import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.origin.SynthesizedOrigin;
+import com.android.tools.r8.shaking.MainDexClasses;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.InternalOptions.DesugarState;
-import com.google.common.base.Suppliers;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Consumer;
-import java.util.function.Supplier;
-import org.objectweb.asm.Opcodes;
+import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
-// Try with resources close-resource desugaring.
+// Try with resources outlining processor. Handles $closeResource methods
+// synthesized by java 9 compiler.
//
-// Rewrites $closeResource methods synthesized by java compilers to work on older DEX runtimes.
-// All invocations to $closeResource methods are rewritten to target a compiler generated version
-// that is correct on older DEX runtimes where not all types implement AutoClosable as expected.
+// Works in two phases:
+// a. during method code processing finds all references to $closeResource(...) synthesized
+// by java compiler, and replaces them with references to a special utility class method.
+// b. after all methods are processed and if there was at least one method referencing
+// $closeResource(...), it synthesizes utility class with appropriate methods.
//
// Note that we don't remove $closeResource(...) synthesized by java compiler, relying on
// tree shaking to remove them since now they should not be referenced.
-// TODO(b/177401708): D8 does not tree shake so we should remove the now unused method.
+//
public final class TwrCloseResourceRewriter {
+ public static final String UTILITY_CLASS_NAME = "$r8$twr$utility";
+ public static final String UTILITY_CLASS_DESCRIPTOR = "L$r8$twr$utility;";
+
private final AppView<?> appView;
- private final DexProto twrCloseResourceProto;
- private final List<ProgramMethod> synthesizedMethods = new ArrayList<>();
+ private final IRConverter converter;
- public static boolean enableTwrCloseResourceDesugaring(InternalOptions options) {
- return options.desugarState == DesugarState.ON
- && options.enableTryWithResourcesDesugaring()
- && !options.canUseTwrCloseResourceMethod();
- }
+ private final DexMethod twrCloseResourceMethod;
- public TwrCloseResourceRewriter(AppView<?> appView) {
+ private final Set<DexProgramClass> referencingClasses = Sets.newConcurrentHashSet();
+
+ public TwrCloseResourceRewriter(AppView<?> appView, IRConverter converter) {
this.appView = appView;
+ this.converter = converter;
+
DexItemFactory dexItemFactory = appView.dexItemFactory();
- twrCloseResourceProto =
+ DexType twrUtilityClass = dexItemFactory.createType(UTILITY_CLASS_DESCRIPTOR);
+ DexProto twrCloseResourceProto =
dexItemFactory.createProto(
dexItemFactory.voidType, dexItemFactory.throwableType, dexItemFactory.objectType);
+ this.twrCloseResourceMethod =
+ dexItemFactory.createMethod(
+ twrUtilityClass, twrCloseResourceProto, dexItemFactory.twrCloseResourceMethodName);
}
- public int rewriteCf(ProgramMethod method, Consumer<ProgramMethod> newMethodCallback) {
- CfCode code = method.getDefinition().getCode().asCfCode();
- List<CfInstruction> instructions = code.getInstructions();
- Supplier<List<CfInstruction>> lazyNewInstructions =
- Suppliers.memoize(() -> new ArrayList<>(instructions));
- int replaced = 0;
- int newInstructionDelta = 0;
- for (int i = 0; i < instructions.size(); i++) {
- CfInvoke invoke = instructions.get(i).asInvoke();
- if (invoke == null
- || invoke.getOpcode() != Opcodes.INVOKESTATIC
- || !isTwrCloseResourceMethod(invoke.getMethod(), appView.dexItemFactory())) {
- continue;
- }
- // Synthesize a new method.
- ProgramMethod closeMethod = createSyntheticCloseResourceMethod(method);
- newMethodCallback.accept(closeMethod);
- // Rewrite the invoke to the new synthetic.
- int newInstructionIndex = i + newInstructionDelta;
- lazyNewInstructions
- .get()
- .set(
- newInstructionIndex,
- new CfInvoke(Opcodes.INVOKESTATIC, closeMethod.getReference(), false));
- ++replaced;
- }
- if (replaced > 0) {
- code.setInstructions(lazyNewInstructions.get());
- }
- return replaced;
+ public static boolean isUtilityClassDescriptor(DexType type) {
+ return type.descriptor.toString().equals(UTILITY_CLASS_DESCRIPTOR);
}
// Rewrites calls to $closeResource() method. Can be invoked concurrently.
- public void rewriteIR(IRCode code) {
+ public void rewriteMethodCode(IRCode code) {
InstructionListIterator iterator = code.instructionListIterator();
+ AppInfo appInfo = appView.appInfo();
while (iterator.hasNext()) {
- InvokeStatic invoke = iterator.next().asInvokeStatic();
- if (invoke == null
- || !isTwrCloseResourceMethod(invoke.getInvokedMethod(), appView.dexItemFactory())) {
+ Instruction instruction = iterator.next();
+ if (!instruction.isInvokeStatic()) {
+ continue;
+ }
+ InvokeStatic invoke = instruction.asInvokeStatic();
+ if (!isSynthesizedCloseResourceMethod(invoke.getInvokedMethod(), appView)) {
continue;
}
- // Replace with a call to a synthetic utility.
+ // Replace with a call to a synthetic utility with the only
+ // implementation of the method $closeResource.
assert invoke.outValue() == null;
assert invoke.inValues().size() == 2;
- ProgramMethod closeResourceMethod = createSyntheticCloseResourceMethod(code.context());
- InvokeStatic newInvoke =
- new InvokeStatic(closeResourceMethod.getReference(), null, invoke.inValues());
- iterator.replaceCurrentInstruction(newInvoke);
- synchronized (synthesizedMethods) {
- synthesizedMethods.add(closeResourceMethod);
- }
+ iterator.replaceCurrentInstruction(
+ new InvokeStatic(twrCloseResourceMethod, null, invoke.inValues()));
+
+ // Mark as a class referencing utility class.
+ referencingClasses.add(appInfo.definitionFor(code.method().getHolderType()).asProgramClass());
}
}
- public static boolean isTwrCloseResourceMethod(DexMethod method, DexItemFactory factory) {
- return method.name == factory.twrCloseResourceMethodName
- && method.proto == factory.twrCloseResourceMethodProto;
+ public static boolean isSynthesizedCloseResourceMethod(DexMethod method, AppView<?> appView) {
+ DexMethod original = appView.graphLens().getOriginalMethodSignature(method);
+ assert original != null;
+ // We consider all methods of *any* class with expected name and signature
+ // to be synthesized by java 9 compiler for try-with-resources, reasoning:
+ //
+ // * we need to look to all potential classes because the calls might be
+ // moved by inlining.
+ // * theoretically we could check appropriate encoded method for having
+ // right attributes, but it still does not guarantee much since we also
+ // need to look into code and doing this seems an overkill
+ //
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ return original.name == dexItemFactory.twrCloseResourceMethodName
+ && original.proto == dexItemFactory.twrCloseResourceMethodProto;
}
- private ProgramMethod createSyntheticCloseResourceMethod(ProgramMethod method) {
- return appView
- .getSyntheticItems()
- .createMethod(
- SyntheticKind.TWR_CLOSE_RESOURCE,
- method,
- appView.dexItemFactory(),
- methodBuilder ->
- methodBuilder
- .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
- .setProto(twrCloseResourceProto)
- .setCode(
- m ->
- BackportedMethods.CloseResourceMethod_closeResourceImpl(
- appView.options(), m)));
- }
+ public void synthesizeUtilityClass(
+ Builder<?> builder, ExecutorService executorService, InternalOptions options)
+ throws ExecutionException {
+ if (referencingClasses.isEmpty()) {
+ return;
+ }
- public void processSynthesizedMethods(IRConverter converter) {
- synthesizedMethods.forEach(converter::optimizeSynthesizedMethod);
+ // The only encoded method.
+ CfCode code =
+ BackportedMethods.CloseResourceMethod_closeResourceImpl(options, twrCloseResourceMethod);
+ MethodAccessFlags flags =
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC, false);
+ DexEncodedMethod method =
+ new DexEncodedMethod(
+ twrCloseResourceMethod,
+ flags,
+ MethodTypeSignature.noSignature(),
+ DexAnnotationSet.empty(),
+ ParameterAnnotationsList.empty(),
+ code,
+ true);
+
+ // Create utility class.
+ DexProgramClass utilityClass =
+ new DexProgramClass(
+ twrCloseResourceMethod.holder,
+ null,
+ new SynthesizedOrigin("twr utility class", getClass()),
+ ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC),
+ appView.dexItemFactory().objectType,
+ DexTypeList.empty(),
+ null,
+ null,
+ Collections.emptyList(),
+ null,
+ Collections.emptyList(),
+ ClassSignature.noSignature(),
+ DexAnnotationSet.empty(),
+ DexEncodedField.EMPTY_ARRAY,
+ DexEncodedField.EMPTY_ARRAY,
+ new DexEncodedMethod[] {method},
+ DexEncodedMethod.EMPTY_ARRAY,
+ appView.dexItemFactory().getSkipNameValidationForTesting(),
+ DexProgramClass::checksumFromType,
+ referencingClasses);
+
+ // Process created class and method.
+ AppInfo appInfo = appView.appInfo();
+ MainDexClasses mainDexClasses = appInfo.getMainDexClasses();
+ boolean addToMainDexList = mainDexClasses.containsAnyOf(referencingClasses);
+ appInfo.addSynthesizedClass(utilityClass, addToMainDexList);
+ converter.optimizeSynthesizedClass(utilityClass, executorService);
+ builder.addSynthesizedClass(utilityClass);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index 38c4e6f..1cce76e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -49,6 +49,7 @@
import com.android.tools.r8.ir.conversion.LensCodeRewriter;
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.conversion.PostOptimization;
+import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.ir.optimize.inliner.DefaultInliningReasonStrategy;
@@ -129,7 +130,9 @@
}
if (extraNeverInlineMethods.contains(
- appView.graphLens().getOriginalMethodSignature(singleTargetReference))) {
+ appView.graphLens().getOriginalMethodSignature(singleTargetReference))
+ || TwrCloseResourceRewriter.isSynthesizedCloseResourceMethod(
+ singleTargetReference, appView)) {
whyAreYouNotInliningReporter.reportExtraNeverInline();
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
index efc440b..1694a7b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
@@ -4,15 +4,25 @@
package com.android.tools.r8.ir.optimize;
+import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ClassAccessFlags;
+import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.MethodCollection;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.ir.code.ConstClass;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -22,13 +32,15 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.MethodProcessingId;
import com.android.tools.r8.ir.desugar.ServiceLoaderSourceCode;
+import com.android.tools.r8.origin.SynthesizedOrigin;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
/**
* ServiceLoaderRewriter will attempt to rewrite calls on the form of: ServiceLoader.load(X.class,
@@ -59,15 +71,19 @@
*/
public class ServiceLoaderRewriter {
+ public static final String SERVICE_LOADER_CLASS_NAME = "$$ServiceLoaderMethods";
+ private static final String SERVICE_LOADER_METHOD_PREFIX_NAME = "$load";
+
+ private AtomicReference<DexProgramClass> synthesizedClass = new AtomicReference<>();
+
private final AppView<AppInfoWithLiveness> appView;
- private final List<ProgramMethod> serviceLoadMethods = new ArrayList<>();
public ServiceLoaderRewriter(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
}
- public List<ProgramMethod> getServiceLoadMethods() {
- return serviceLoadMethods;
+ public DexProgramClass getSynthesizedClass() {
+ return synthesizedClass.get();
}
public void rewrite(IRCode code, MethodProcessingId methodProcessingId) {
@@ -170,7 +186,7 @@
constClass.getValue(),
service -> {
DexEncodedMethod addedMethod =
- createSynthesizedMethod(service, classes, methodProcessingId, code.context());
+ createSynthesizedMethod(service, classes, methodProcessingId);
if (appView.options().isGeneratingClassFiles()) {
addedMethod.upgradeClassFileVersion(code.method().getClassFileVersion());
}
@@ -183,31 +199,75 @@
}
private DexEncodedMethod createSynthesizedMethod(
- DexType serviceType,
- List<DexClass> classes,
- MethodProcessingId methodProcessingId,
- ProgramMethod context) {
+ DexType serviceType, List<DexClass> classes, MethodProcessingId methodProcessingId) {
+ MethodCollection methodCollection = getOrSetSynthesizedClass().getMethodCollection();
+ String methodNamePrefix = SERVICE_LOADER_METHOD_PREFIX_NAME + "$";
DexProto proto = appView.dexItemFactory().createProto(appView.dexItemFactory().iteratorType);
- ProgramMethod method =
- appView
- .getSyntheticItems()
- .createMethod(
- SyntheticKind.SERVICE_LOADER,
- context,
- appView.dexItemFactory(),
- builder ->
- builder
- .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
- .setProto(proto)
- .setCode(
- m ->
- ServiceLoaderSourceCode.generate(
- serviceType, classes, appView.dexItemFactory())),
- methodProcessingId);
- synchronized (serviceLoadMethods) {
- serviceLoadMethods.add(method);
+ synchronized (methodCollection) {
+ DexMethod methodReference;
+ do {
+ methodReference =
+ appView
+ .dexItemFactory()
+ .createMethod(
+ appView.dexItemFactory().serviceLoaderRewrittenClassType,
+ proto,
+ methodNamePrefix + methodProcessingId.getAndIncrementId());
+ } while (methodCollection.getMethod(methodReference) != null);
+ DexEncodedMethod method =
+ new DexEncodedMethod(
+ methodReference,
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC | Constants.ACC_STATIC, false),
+ MethodTypeSignature.noSignature(),
+ DexAnnotationSet.empty(),
+ ParameterAnnotationsList.empty(),
+ ServiceLoaderSourceCode.generate(serviceType, classes, appView.dexItemFactory()),
+ true);
+ methodCollection.addDirectMethod(method);
+ return method;
}
- return method.getDefinition();
+ }
+
+ private DexProgramClass getOrSetSynthesizedClass() {
+ if (synthesizedClass.get() != null) {
+ return synthesizedClass.get();
+ }
+ assert !appView.options().encodeChecksums;
+ ChecksumSupplier checksumSupplier = DexProgramClass::invalidChecksumRequest;
+ DexProgramClass clazz =
+ synthesizedClass.updateAndGet(
+ existingClass -> {
+ if (existingClass != null) {
+ return existingClass;
+ }
+ DexProgramClass newClass =
+ new DexProgramClass(
+ appView.dexItemFactory().serviceLoaderRewrittenClassType,
+ null,
+ new SynthesizedOrigin("Service Loader desugaring", getClass()),
+ ClassAccessFlags.fromDexAccessFlags(
+ Constants.ACC_FINAL | Constants.ACC_SYNTHETIC | Constants.ACC_PUBLIC),
+ appView.dexItemFactory().objectType,
+ DexTypeList.empty(),
+ appView.dexItemFactory().createString("ServiceLoader"),
+ null,
+ Collections.emptyList(),
+ null,
+ Collections.emptyList(),
+ ClassSignature.noSignature(),
+ DexAnnotationSet.empty(),
+ DexEncodedField.EMPTY_ARRAY, // Static fields.
+ DexEncodedField.EMPTY_ARRAY, // Instance fields.
+ DexEncodedMethod.EMPTY_ARRAY,
+ DexEncodedMethod.EMPTY_ARRAY, // Virtual methods.
+ appView.dexItemFactory().getSkipNameValidationForTesting(),
+ checksumSupplier);
+ newClass.getMethodCollection().useSortedBacking();
+ return newClass;
+ });
+ assert clazz != null;
+ return clazz;
}
/**
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java b/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
index a972b39..1b55f13 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
@@ -15,7 +15,6 @@
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.optimize.templates.CfUtilityMethodsForCodeOptimizations;
import com.android.tools.r8.synthesis.SyntheticItems;
-import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.utils.InternalOptions;
public class UtilityMethodsForCodeOptimizations {
@@ -32,7 +31,6 @@
SyntheticItems syntheticItems = appView.getSyntheticItems();
ProgramMethod syntheticMethod =
syntheticItems.createMethod(
- SyntheticNaming.SyntheticKind.TO_STRING_IF_NOT_NULL,
context,
dexItemFactory,
builder ->
@@ -62,7 +60,6 @@
SyntheticItems syntheticItems = appView.getSyntheticItems();
ProgramMethod syntheticMethod =
syntheticItems.createMethod(
- SyntheticNaming.SyntheticKind.THROW_CCE_IF_NOT_NULL,
context,
dexItemFactory,
builder ->
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index 9868a64..b5dfee6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -1090,18 +1090,10 @@
} else if (classInitializerSideEffect.canBePostponed()) {
feedback.classInitializerMayBePostponed(method);
} else {
- assert options.debug
- || appView
- .getSyntheticItems()
- .verifySyntheticLambdaProperty(
- context.getHolder(),
- lambdaClass ->
- appView.appInfo().hasPinnedInstanceInitializer(lambdaClass.getType())
- || appView
- .options()
- .horizontalClassMergerOptions()
- .isJavaLambdaMergingEnabled(),
- nonLambdaClass -> true)
+ assert !context.getHolderType().isD8R8SynthesizedLambdaClassType()
+ || options.debug
+ || appView.appInfo().hasPinnedInstanceInitializer(context.getHolderType())
+ || appView.options().horizontalClassMergerOptions().isJavaLambdaMergingEnabled()
: "Unexpected observable side effects from lambda `" + context.toSourceString() + "`";
}
return;
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index cf6a117..ef724ec 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -40,7 +40,7 @@
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.naming.ProguardMapSupplier;
import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.synthesis.SyntheticNaming;
+import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.AsmUtils;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -124,7 +124,7 @@
marker.isRelocator() ? Optional.empty() : Optional.of(marker.toString());
LensCodeRewriterUtils rewriter = new LensCodeRewriterUtils(appView);
for (DexProgramClass clazz : application.classes()) {
- assert SyntheticNaming.verifyNotInternalSynthetic(clazz.getType());
+ assert SyntheticItems.verifyNotInternalSynthetic(clazz.getType());
try {
writeClass(clazz, consumer, rewriter, markerString);
} catch (ClassTooLargeException e) {
@@ -194,7 +194,6 @@
for (int i = 0; i < clazz.interfaces.values.length; i++) {
interfaces[i] = namingLens.lookupInternalName(clazz.interfaces.values[i]);
}
- assert SyntheticNaming.verifyNotInternalSynthetic(name);
writer.visit(version.raw(), access, name, signature, superName, interfaces);
writeAnnotations(writer::visitAnnotation, clazz.annotations().annotations);
ImmutableMap<DexString, DexValue> defaults = getAnnotationDefaults(clazz.annotations());
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 5619d97..6b316f7 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -47,6 +47,7 @@
import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
+import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
import com.android.tools.r8.synthesis.CommittedItems;
import com.android.tools.r8.utils.CollectionUtils;
import com.android.tools.r8.utils.InternalOptions;
@@ -268,7 +269,7 @@
this.switchMaps = switchMaps;
this.lockCandidates = lockCandidates;
this.initClassReferences = initClassReferences;
- assert verify();
+ verify();
}
private AppInfoWithLiveness(AppInfoWithLiveness previous, CommittedItems committedItems) {
@@ -365,11 +366,10 @@
previous.initClassReferences);
}
- private boolean verify() {
+ private void verify() {
assert keepInfo.verifyPinnedTypesAreLive(liveTypes);
assert objectAllocationInfoCollection.verifyAllocatedTypesAreLive(
liveTypes, getMissingClasses(), this);
- return true;
}
private static KeepInfoCollection extendPinnedItems(
@@ -454,7 +454,7 @@
this.lockCandidates = previous.lockCandidates;
this.initClassReferences = previous.initClassReferences;
previous.markObsolete();
- assert verify();
+ verify();
}
public static AppInfoWithLivenessModifier modifier() {
@@ -471,6 +471,7 @@
|| InterfaceMethodRewriter.isCompanionClassType(type)
|| InterfaceMethodRewriter.isEmulatedLibraryClassType(type)
|| type.toDescriptorString().startsWith("Lj$/$r8$retargetLibraryMember$")
+ || TwrCloseResourceRewriter.isUtilityClassDescriptor(type)
// TODO(b/150736225): Not sure how to remove these.
|| DesugaredLibraryAPIConverter.isVivifiedType(type)
: "Failed lookup of non-missing type: " + type;
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 37c66c4..c15fdb8 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -86,7 +86,6 @@
import com.android.tools.r8.ir.desugar.LambdaClass;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.ir.desugar.LambdaRewriter;
-import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
import com.android.tools.r8.kotlin.KotlinMetadataEnqueuerExtension;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.identifiernamestring.IdentifierNameStringLookupResult;
@@ -360,14 +359,12 @@
private final LambdaRewriter lambdaRewriter;
private final BackportedMethodRewriter backportRewriter;
- private final TwrCloseResourceRewriter twrCloseResourceRewriter;
private final DesugaredLibraryConversionWrapperAnalysis desugaredLibraryWrapperAnalysis;
private final Map<DexType, Pair<LambdaClass, ProgramMethod>> lambdaClasses =
new IdentityHashMap<>();
private final ProgramMethodMap<Map<DexCallSite, LambdaClass>> lambdaCallSites =
ProgramMethodMap.create();
private final Map<DexMethod, ProgramMethod> methodsWithBackports = new IdentityHashMap<>();
- private final Map<DexMethod, ProgramMethod> methodsWithTwrCloseResource = new IdentityHashMap<>();
private final Set<DexProgramClass> classesWithSerializableLambdas = Sets.newIdentityHashSet();
Enqueuer(
@@ -406,10 +403,6 @@
lambdaRewriter = options.desugarState == DesugarState.ON ? new LambdaRewriter(appView) : null;
backportRewriter =
options.desugarState == DesugarState.ON ? new BackportedMethodRewriter(appView) : null;
- twrCloseResourceRewriter =
- TwrCloseResourceRewriter.enableTwrCloseResourceDesugaring(options)
- ? new TwrCloseResourceRewriter(appView)
- : null;
objectAllocationInfoCollection =
ObjectAllocationInfoCollectionImpl.builder(mode.isInitialTreeShaking(), graphReporter);
@@ -928,7 +921,7 @@
assert contextMethod.getCode().isCfCode() : "Unexpected input type with lambdas";
CfCode code = contextMethod.getCode().asCfCode();
if (code != null) {
- LambdaClass lambdaClass = lambdaRewriter.createLambdaClass(descriptor, context);
+ LambdaClass lambdaClass = lambdaRewriter.getOrCreateLambdaClass(descriptor, context);
lambdaClasses.put(lambdaClass.type, new Pair<>(lambdaClass, context));
lambdaCallSites
.computeIfAbsent(context, k -> new IdentityHashMap<>())
@@ -1241,24 +1234,12 @@
return false;
}
- private boolean registerCloseResource(DexMethod invokedMethod, ProgramMethod context) {
- if (twrCloseResourceRewriter != null
- && TwrCloseResourceRewriter.isTwrCloseResourceMethod(
- invokedMethod, appView.dexItemFactory())) {
- methodsWithTwrCloseResource.putIfAbsent(context.getReference(), context);
- return true;
- }
- return false;
- }
-
private void traceInvokeStatic(
DexMethod invokedMethod, ProgramMethod context, KeepReason reason) {
if (registerBackportInvoke(invokedMethod, context)) {
return;
}
- if (registerCloseResource(invokedMethod, context)) {
- return;
- }
+
DexItemFactory dexItemFactory = appView.dexItemFactory();
if (dexItemFactory.classMethods.isReflectiveClassLookup(invokedMethod)
|| dexItemFactory.atomicFieldUpdaterMethods.isFieldUpdater(invokedMethod)) {
@@ -3057,9 +3038,13 @@
return empty;
}
- void addInstantiatedClass(DexProgramClass clazz, ProgramMethod context) {
+ void addInstantiatedClass(
+ DexProgramClass clazz, ProgramMethod context, boolean isMainDexClass) {
assert !syntheticInstantiations.containsKey(clazz.type);
syntheticInstantiations.put(clazz.type, new Pair<>(clazz, context));
+ if (isMainDexClass) {
+ mainDexTypes.add(clazz);
+ }
}
void addClasspathClass(DexClasspathClass clazz) {
@@ -3081,6 +3066,10 @@
void amendApplication(Builder appBuilder) {
assert !isEmpty();
+ for (Pair<DexProgramClass, ProgramMethod> clazzAndContext :
+ syntheticInstantiations.values()) {
+ appBuilder.addProgramClass(clazzAndContext.getFirst());
+ }
appBuilder.addClasspathClasses(syntheticClasspathClasses.values());
}
@@ -3151,7 +3140,6 @@
synthesizeLambdas(additions);
synthesizeLibraryConversionWrappers(additions);
synthesizeBackports(additions);
- synthesizeTwrCloseResource(additions);
if (additions.isEmpty()) {
return;
}
@@ -3195,12 +3183,6 @@
}
}
- private void synthesizeTwrCloseResource(SyntheticAdditions additions) {
- for (ProgramMethod method : methodsWithTwrCloseResource.values()) {
- twrCloseResourceRewriter.rewriteCf(method, additions::addLiveMethod);
- }
- }
-
private void synthesizeLambdas(SyntheticAdditions additions) {
if (lambdaRewriter == null || lambdaClasses.isEmpty()) {
assert lambdaCallSites.isEmpty();
@@ -3211,8 +3193,8 @@
// Add all desugared classes to the application, main-dex list, and mark them instantiated.
LambdaClass lambdaClass = lambdaClassAndContext.getFirst();
ProgramMethod context = lambdaClassAndContext.getSecond();
- DexProgramClass programClass = lambdaClass.getLambdaProgramClass();
- additions.addInstantiatedClass(programClass, context);
+ DexProgramClass programClass = lambdaClass.getOrCreateLambdaClass();
+ additions.addInstantiatedClass(programClass, context, lambdaClass.addToMainDexList.get());
// Mark the instance constructor targeted and live.
DexEncodedMethod constructor = programClass.lookupDirectMethod(lambdaClass.constructor);
KeepReason reason = KeepReason.instantiatedIn(context);
@@ -3367,9 +3349,10 @@
lambdaRewriter
.getKnownLambdaClasses()
.forEach(
- lambda -> {
- DexProgramClass synthesizedClass = lambda.getLambdaProgramClass();
+ (type, lambda) -> {
+ DexProgramClass synthesizedClass = lambda.getOrCreateLambdaClass();
assert synthesizedClass != null;
+ assert synthesizedClass == appInfo().definitionForWithoutExistenceAssert(type);
assert liveTypes.contains(synthesizedClass);
if (synthesizedClass == null) {
return;
diff --git a/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java b/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java
index e60dfa3..424fd8d 100644
--- a/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/CommittedItems.java
@@ -7,6 +7,8 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexType;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import java.util.Collection;
import java.util.function.Function;
@@ -26,19 +28,23 @@
// Immutable package accessible fields to allow SyntheticItems creation.
final DexApplication application;
final int nextSyntheticId;
- final CommittedSyntheticsCollection committed;
+ final ImmutableSet<DexType> legacySyntheticTypes;
+ final ImmutableMap<DexType, SyntheticReference> syntheticItems;
final ImmutableList<DexType> committedTypes;
CommittedItems(
int nextSyntheticId,
DexApplication application,
- CommittedSyntheticsCollection committed,
+ ImmutableSet<DexType> legacySyntheticTypes,
+ ImmutableMap<DexType, SyntheticReference> syntheticItems,
ImmutableList<DexType> committedTypes) {
+ assert verifyTypesAreInApp(application, legacySyntheticTypes);
+ assert verifyTypesAreInApp(application, syntheticItems.keySet());
this.nextSyntheticId = nextSyntheticId;
this.application = application;
- this.committed = committed;
+ this.legacySyntheticTypes = legacySyntheticTypes;
+ this.syntheticItems = syntheticItems;
this.committedTypes = committedTypes;
- committed.verifyTypesAreInApp(application);
}
// Conversion to a mutable synthetic items collection. Should only be used in AppInfo creation.
@@ -56,7 +62,7 @@
@Deprecated
public Collection<DexType> getLegacySyntheticTypes() {
- return committed.getLegacyTypes();
+ return legacySyntheticTypes;
}
@Override
@@ -64,4 +70,11 @@
// All synthetic types are committed to the application so lookup is just the base lookup.
return baseDefinitionFor.apply(type);
}
+
+ private static boolean verifyTypesAreInApp(DexApplication app, Collection<DexType> types) {
+ for (DexType type : types) {
+ assert app.programDefinitionFor(type) != null : "Missing synthetic: " + type;
+ }
+ return true;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java b/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java
deleted file mode 100644
index 582a39a..0000000
--- a/src/main/java/com/android/tools/r8/synthesis/CommittedSyntheticsCollection.java
+++ /dev/null
@@ -1,243 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.synthesis;
-
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
-import com.android.tools.r8.graph.PrunedItems;
-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;
-import java.util.Set;
-import java.util.function.Consumer;
-
-/**
- * Immutable collection of committed items.
- *
- * <p>This structure is to make it easier to pass the items from SyntheticItems to CommittedItems
- * and back while also providing a builder for updating the committed synthetics.
- */
-class CommittedSyntheticsCollection {
-
- static class Builder {
- private final CommittedSyntheticsCollection parent;
- private ImmutableMap.Builder<DexType, SyntheticClassReference> newNonLegacyClasses = null;
- private ImmutableMap.Builder<DexType, SyntheticMethodReference> newNonLegacyMethods = null;
- private ImmutableSet.Builder<DexType> newLegacyClasses = null;
-
- public Builder(CommittedSyntheticsCollection parent) {
- this.parent = parent;
- }
-
- public Builder addItem(SyntheticDefinition<?, ?> definition) {
- definition.toReference().apply(this::addNonLegacyMethod, this::addNonLegacyClass);
- return this;
- }
-
- public Builder addNonLegacyClass(SyntheticClassDefinition definition) {
- return addNonLegacyClass(definition.toReference());
- }
-
- public Builder addNonLegacyClass(SyntheticClassReference reference) {
- if (newNonLegacyClasses == null) {
- newNonLegacyClasses = ImmutableMap.builder();
- }
- newNonLegacyClasses.put(reference.getHolder(), reference);
- return this;
- }
-
- public Builder addNonLegacyMethod(SyntheticMethodDefinition definition) {
- return addNonLegacyMethod(definition.toReference());
- }
-
- public Builder addNonLegacyMethod(SyntheticMethodReference reference) {
- if (newNonLegacyMethods == null) {
- newNonLegacyMethods = ImmutableMap.builder();
- }
- newNonLegacyMethods.put(reference.getHolder(), reference);
- return this;
- }
-
- public Builder addLegacyClasses(Collection<DexProgramClass> classes) {
- if (newLegacyClasses == null) {
- newLegacyClasses = ImmutableSet.builder();
- }
- classes.forEach(c -> newLegacyClasses.add(c.getType()));
- return this;
- }
-
- public Builder addLegacyClass(DexType type) {
- if (newLegacyClasses == null) {
- newLegacyClasses = ImmutableSet.builder();
- }
- newLegacyClasses.add(type);
- return this;
- }
-
- public CommittedSyntheticsCollection build() {
- if (newNonLegacyClasses == null && newNonLegacyMethods == null && newLegacyClasses == null) {
- return parent;
- }
- ImmutableMap<DexType, SyntheticClassReference> allNonLegacyClasses =
- newNonLegacyClasses == null
- ? parent.nonLegacyClasses
- : newNonLegacyClasses.putAll(parent.nonLegacyClasses).build();
- ImmutableMap<DexType, SyntheticMethodReference> allNonLegacyMethods =
- newNonLegacyMethods == null
- ? parent.nonLegacyMethods
- : newNonLegacyMethods.putAll(parent.nonLegacyMethods).build();
- ImmutableSet<DexType> allLegacyClasses =
- newLegacyClasses == null
- ? parent.legacyTypes
- : newLegacyClasses.addAll(parent.legacyTypes).build();
- return new CommittedSyntheticsCollection(
- allLegacyClasses, allNonLegacyMethods, allNonLegacyClasses);
- }
- }
-
- private static final CommittedSyntheticsCollection EMPTY =
- new CommittedSyntheticsCollection(ImmutableSet.of(), ImmutableMap.of(), ImmutableMap.of());
-
- /**
- * Immutable set of synthetic types in the application (eg, committed).
- *
- * <p>TODO(b/158159959): Remove legacy support.
- */
- private final ImmutableSet<DexType> legacyTypes;
-
- /** Mapping from synthetic type to its synthetic method item description. */
- private final ImmutableMap<DexType, SyntheticMethodReference> nonLegacyMethods;
-
- /** Mapping from synthetic type to its synthetic class item description. */
- private final ImmutableMap<DexType, SyntheticClassReference> nonLegacyClasses;
-
- public CommittedSyntheticsCollection(
- ImmutableSet<DexType> legacyTypes,
- ImmutableMap<DexType, SyntheticMethodReference> nonLegacyMethods,
- ImmutableMap<DexType, SyntheticClassReference> nonLegacyClasses) {
- this.legacyTypes = legacyTypes;
- this.nonLegacyMethods = nonLegacyMethods;
- this.nonLegacyClasses = nonLegacyClasses;
- assert legacyTypes.size() + nonLegacyMethods.size() + nonLegacyClasses.size()
- == Sets.union(Sets.union(nonLegacyMethods.keySet(), nonLegacyClasses.keySet()), legacyTypes)
- .size();
- }
-
- public static CommittedSyntheticsCollection empty() {
- return EMPTY;
- }
-
- Builder builder() {
- return new Builder(this);
- }
-
- boolean isEmpty() {
- return legacyTypes.isEmpty() && nonLegacyMethods.isEmpty() && nonLegacyClasses.isEmpty();
- }
-
- boolean containsType(DexType type) {
- return containsLegacyType(type) || containsNonLegacyType(type);
- }
-
- public boolean containsLegacyType(DexType type) {
- return legacyTypes.contains(type);
- }
-
- public boolean containsNonLegacyType(DexType type) {
- return nonLegacyMethods.containsKey(type) || nonLegacyClasses.containsKey(type);
- }
-
- public ImmutableSet<DexType> getLegacyTypes() {
- return legacyTypes;
- }
-
- public ImmutableMap<DexType, SyntheticMethodReference> getNonLegacyMethods() {
- return nonLegacyMethods;
- }
-
- public ImmutableMap<DexType, SyntheticClassReference> getNonLegacyClasses() {
- return nonLegacyClasses;
- }
-
- public SyntheticReference<?, ?> getNonLegacyItem(DexType type) {
- SyntheticMethodReference reference = nonLegacyMethods.get(type);
- if (reference != null) {
- return reference;
- }
- return nonLegacyClasses.get(type);
- }
-
- public void forEachNonLegacyItem(Consumer<SyntheticReference<?, ?>> fn) {
- nonLegacyMethods.forEach((t, r) -> fn.accept(r));
- nonLegacyClasses.forEach((t, r) -> fn.accept(r));
- }
-
- CommittedSyntheticsCollection pruneItems(PrunedItems prunedItems) {
- Set<DexType> removed = prunedItems.getNoLongerSyntheticItems();
- if (removed.isEmpty()) {
- return this;
- }
- Builder builder = CommittedSyntheticsCollection.empty().builder();
- boolean changed = false;
- for (DexType type : legacyTypes) {
- if (removed.contains(type)) {
- changed = true;
- } else {
- builder.addLegacyClass(type);
- }
- }
- for (SyntheticMethodReference reference : nonLegacyMethods.values()) {
- if (removed.contains(reference.getHolder())) {
- changed = true;
- } else {
- builder.addNonLegacyMethod(reference);
- }
- }
- for (SyntheticClassReference reference : nonLegacyClasses.values()) {
- if (removed.contains(reference.getHolder())) {
- changed = true;
- } else {
- builder.addNonLegacyClass(reference);
- }
- }
- return changed ? builder.build() : this;
- }
-
- CommittedSyntheticsCollection rewriteWithLens(NonIdentityGraphLens lens) {
- return new CommittedSyntheticsCollection(
- lens.rewriteTypes(legacyTypes),
- rewriteItems(nonLegacyMethods, lens),
- rewriteItems(nonLegacyClasses, lens));
- }
-
- private static <R extends SyntheticReference<R, ?>> ImmutableMap<DexType, R> rewriteItems(
- Map<DexType, R> items, NonIdentityGraphLens lens) {
- ImmutableMap.Builder<DexType, R> rewrittenItems = ImmutableMap.builder();
- for (R reference : items.values()) {
- R rewritten = reference.rewrite(lens);
- if (rewritten != null) {
- rewrittenItems.put(rewritten.getHolder(), rewritten);
- }
- }
- return rewrittenItems.build();
- }
-
- boolean verifyTypesAreInApp(DexApplication application) {
- assert verifyTypesAreInApp(application, legacyTypes);
- assert verifyTypesAreInApp(application, nonLegacyMethods.keySet());
- assert verifyTypesAreInApp(application, nonLegacyClasses.keySet());
- return true;
- }
-
- private static boolean verifyTypesAreInApp(DexApplication app, Collection<DexType> types) {
- for (DexType type : types) {
- assert app.programDefinitionFor(type) != null : "Missing synthetic: " + type;
- }
- return true;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
index 2e371d0..f5d4b66 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SynthesizingContext.java
@@ -14,7 +14,6 @@
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.MainDexClasses;
-import com.android.tools.r8.synthesis.SyntheticNaming.Phase;
import java.util.Comparator;
import java.util.Set;
@@ -54,20 +53,6 @@
return new SynthesizingContext(synthesizingContextType, clazz.type, clazz.origin);
}
- static SynthesizingContext fromSyntheticContextChange(
- DexType syntheticType, SynthesizingContext oldContext, DexItemFactory factory) {
- String descriptor = syntheticType.toDescriptorString();
- int i = descriptor.indexOf(SyntheticNaming.getPhaseSeparator(Phase.INTERNAL));
- if (i <= 0) {
- assert false : "Unexpected synthetic without internal separator: " + syntheticType;
- return null;
- }
- DexType newContext = factory.createType(descriptor.substring(0, i) + ";");
- return newContext == oldContext.getSynthesizingContextType()
- ? oldContext
- : new SynthesizingContext(newContext, newContext, oldContext.inputContextOrigin);
- }
-
private SynthesizingContext(
DexType synthesizingContextType, DexType inputContextType, Origin inputContextOrigin) {
this.synthesizingContextType = synthesizingContextType;
@@ -90,6 +75,14 @@
return inputContextOrigin;
}
+ DexType createHygienicType(String syntheticId, DexItemFactory factory) {
+ // If the context is a synthetic input, then use its annotated context as the hygienic context.
+ String contextDesc = synthesizingContextType.toDescriptorString();
+ String prefix = contextDesc.substring(0, contextDesc.length() - 1);
+ String suffix = SyntheticItems.INTERNAL_SYNTHETIC_CLASS_SEPARATOR + syntheticId + ";";
+ return factory.createType(prefix + suffix);
+ }
+
SynthesizingContext rewrite(NonIdentityGraphLens lens) {
DexType rewrittenInputeContextType = lens.lookupType(inputContextType);
DexType rewrittenSynthesizingContextType = lens.lookupType(synthesizingContextType);
@@ -134,17 +127,15 @@
void addIfDerivedFromMainDexClass(
DexProgramClass externalSyntheticClass,
MainDexClasses mainDexClasses,
- Set<DexType> allMainDexTypes) {
+ Set<DexType> allMainDexTypes,
+ Set<DexType> derivedMainDexTypesToIgnore) {
// The input context type (not the annotated context) determines if the derived class is to be
// in main dex.
// TODO(b/168584485): Once resolved allMainDexTypes == mainDexClasses.
if (allMainDexTypes.contains(inputContextType)) {
mainDexClasses.add(externalSyntheticClass);
+ // Mark the type as to be ignored when computing main-dex placement for legacy types.
+ derivedMainDexTypesToIgnore.add(inputContextType);
}
}
-
- @Override
- public String toString() {
- return "SynthesizingContext{" + getSynthesizingContextType() + "}";
- }
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
index 9d2460b..4f289c7 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
@@ -33,10 +33,8 @@
private DexType superType;
private DexTypeList interfaces = DexTypeList.empty();
- private List<DexEncodedField> staticFields = new ArrayList<>();
- private List<DexEncodedField> instanceFields = new ArrayList<>();
- private List<DexEncodedMethod> directMethods = new ArrayList<>();
- private List<DexEncodedMethod> virtualMethods = new ArrayList<>();
+
+ private int nextMethodId = 0;
private List<SyntheticMethodBuilder> methods = new ArrayList<>();
SyntheticClassBuilder(DexType type, SynthesizingContext context, DexItemFactory factory) {
@@ -54,40 +52,12 @@
return type;
}
- public SyntheticClassBuilder setInterfaces(List<DexType> interfaces) {
- this.interfaces =
- interfaces.isEmpty()
- ? DexTypeList.empty()
- : new DexTypeList(interfaces.toArray(DexType.EMPTY_ARRAY));
- return this;
- }
-
- public SyntheticClassBuilder setStaticFields(List<DexEncodedField> fields) {
- staticFields.clear();
- staticFields.addAll(fields);
- return this;
- }
-
- public SyntheticClassBuilder setInstanceFields(List<DexEncodedField> fields) {
- instanceFields.clear();
- instanceFields.addAll(fields);
- return this;
- }
-
- public SyntheticClassBuilder setDirectMethods(Iterable<DexEncodedMethod> methods) {
- directMethods.clear();
- methods.forEach(directMethods::add);
- return this;
- }
-
- public SyntheticClassBuilder setVirtualMethods(Iterable<DexEncodedMethod> methods) {
- virtualMethods.clear();
- methods.forEach(virtualMethods::add);
- return this;
+ private String getNextMethodName() {
+ return SyntheticItems.INTERNAL_SYNTHETIC_METHOD_PREFIX + nextMethodId++;
}
public SyntheticClassBuilder addMethod(Consumer<SyntheticMethodBuilder> fn) {
- SyntheticMethodBuilder method = new SyntheticMethodBuilder(this);
+ SyntheticMethodBuilder method = new SyntheticMethodBuilder(this, getNextMethodName());
fn.accept(method);
methods.add(method);
return this;
@@ -95,27 +65,35 @@
DexProgramClass build() {
ClassAccessFlags accessFlags =
- ClassAccessFlags.fromSharedAccessFlags(
- Constants.ACC_FINAL | Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC);
+ ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC);
Kind originKind = null;
DexString sourceFile = null;
NestHostClassAttribute nestHost = null;
List<NestMemberClassAttribute> nestMembers = Collections.emptyList();
EnclosingMethodAttribute enclosingMembers = null;
List<InnerClassAttribute> innerClasses = Collections.emptyList();
+ DexEncodedField[] staticFields = DexEncodedField.EMPTY_ARRAY;
+ DexEncodedField[] instanceFields = DexEncodedField.EMPTY_ARRAY;
+ DexEncodedMethod[] directMethods = DexEncodedMethod.EMPTY_ARRAY;
+ DexEncodedMethod[] virtualMethods = DexEncodedMethod.EMPTY_ARRAY;
+ assert !methods.isEmpty();
+ List<DexEncodedMethod> directs = new ArrayList<>(methods.size());
+ List<DexEncodedMethod> virtuals = new ArrayList<>(methods.size());
for (SyntheticMethodBuilder builder : methods) {
DexEncodedMethod method = builder.build();
if (method.isNonPrivateVirtualMethod()) {
- virtualMethods.add(method);
+ virtuals.add(method);
} else {
- directMethods.add(method);
+ directs.add(method);
}
}
- long checksum =
- 7 * (long) directMethods.hashCode()
- + 11 * (long) virtualMethods.hashCode()
- + 13 * (long) staticFields.hashCode()
- + 17 * (long) instanceFields.hashCode();
+ if (!directs.isEmpty()) {
+ directMethods = directs.toArray(new DexEncodedMethod[directs.size()]);
+ }
+ if (!virtuals.isEmpty()) {
+ virtualMethods = virtuals.toArray(new DexEncodedMethod[virtuals.size()]);
+ }
+ long checksum = 7 * (long) directs.hashCode() + 11 * (long) virtuals.hashCode();
return new DexProgramClass(
type,
originKind,
@@ -130,10 +108,10 @@
innerClasses,
ClassSignature.noSignature(),
DexAnnotationSet.empty(),
- staticFields.toArray(new DexEncodedField[staticFields.size()]),
- instanceFields.toArray(new DexEncodedField[instanceFields.size()]),
- directMethods.toArray(new DexEncodedMethod[directMethods.size()]),
- virtualMethods.toArray(new DexEncodedMethod[virtualMethods.size()]),
+ staticFields,
+ instanceFields,
+ directMethods,
+ virtualMethods,
factory.getSkipNameValidationForTesting(),
c -> checksum);
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassDefinition.java
deleted file mode 100644
index 4b0b2cf..0000000
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassDefinition.java
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.synthesis;
-
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
-import com.android.tools.r8.utils.structural.RepresentativeMap;
-import com.google.common.hash.Hasher;
-
-/**
- * Definition of a synthetic class item.
- *
- * <p>This class is internal to the synthetic items collection, thus package-protected.
- */
-class SyntheticClassDefinition
- extends SyntheticDefinition<SyntheticClassReference, SyntheticClassDefinition> {
-
- private final DexProgramClass clazz;
-
- SyntheticClassDefinition(SyntheticKind kind, SynthesizingContext context, DexProgramClass clazz) {
- super(kind, context);
- this.clazz = clazz;
- }
-
- public DexProgramClass getProgramClass() {
- return clazz;
- }
-
- @Override
- SyntheticClassReference toReference() {
- return new SyntheticClassReference(getKind(), getContext(), clazz.getType());
- }
-
- @Override
- DexProgramClass getHolder() {
- return clazz;
- }
-
- @Override
- public boolean isValid() {
- return clazz.isPublic() && clazz.isFinal() && clazz.accessFlags.isSynthetic();
- }
-
- @Override
- void internalComputeHash(Hasher hasher, RepresentativeMap map) {
- clazz.hashWithTypeEquivalence(hasher, map);
- }
-
- @Override
- int internalCompareTo(SyntheticClassDefinition o, RepresentativeMap map) {
- return clazz.compareWithTypeEquivalenceTo(o.clazz, map);
- }
-
- @Override
- public String toString() {
- return "SyntheticClass{ clazz = "
- + clazz.type.toSourceString()
- + ", kind = "
- + getKind()
- + ", context = "
- + getContext()
- + " }";
- }
-}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassReference.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassReference.java
deleted file mode 100644
index 9c79ab9..0000000
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassReference.java
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.synthesis;
-
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
-import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-/**
- * Reference to a synthetic class item.
- *
- * <p>This class is internal to the synthetic items collection, thus package-protected.
- */
-class SyntheticClassReference
- extends SyntheticReference<SyntheticClassReference, SyntheticClassDefinition> {
- final DexType type;
-
- SyntheticClassReference(SyntheticKind kind, SynthesizingContext context, DexType type) {
- super(kind, context);
- this.type = type;
- }
-
- @Override
- DexType getHolder() {
- return type;
- }
-
- @Override
- SyntheticClassDefinition lookupDefinition(Function<DexType, DexClass> definitions) {
- DexClass clazz = definitions.apply(type);
- if (clazz == null) {
- return null;
- }
- assert clazz.isProgramClass();
- return new SyntheticClassDefinition(getKind(), getContext(), clazz.asProgramClass());
- }
-
- @Override
- SyntheticClassReference rewrite(NonIdentityGraphLens lens) {
- DexType rewritten = lens.lookupType(type);
- // If the reference has been non-trivially rewritten the compiler has changed it and it can no
- // longer be considered a synthetic. The context may or may not have changed.
- if (type != rewritten && !lens.isSimpleRenaming(type, rewritten)) {
- // If the referenced item is rewritten, it should be moved to another holder as the
- // synthetic holder is no longer part of the synthetic collection.
- assert SyntheticNaming.verifyNotInternalSynthetic(rewritten);
- return null;
- }
- SynthesizingContext context = getContext().rewrite(lens);
- if (context == getContext() && rewritten == type) {
- return this;
- }
- // Ensure that if a synthetic moves its context moves consistently.
- if (type != rewritten) {
- context =
- SynthesizingContext.fromSyntheticContextChange(rewritten, context, lens.dexItemFactory());
- if (context == null) {
- return null;
- }
- }
- return new SyntheticClassReference(getKind(), context, rewritten);
- }
-
- @Override
- void apply(
- Consumer<SyntheticMethodReference> onMethod, Consumer<SyntheticClassReference> onClass) {
- onClass.accept(this);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
index 526f4fc..01f8825 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticDefinition.java
@@ -4,71 +4,30 @@
package com.android.tools.r8.synthesis;
import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.structural.RepresentativeMap;
import com.google.common.hash.HashCode;
-import com.google.common.hash.Hasher;
-import com.google.common.hash.Hashing;
/**
* Base type for the definition of a synthetic item.
*
* <p>This class is internal to the synthetic items collection, thus package-protected.
*/
-abstract class SyntheticDefinition<
- R extends SyntheticReference<R, D>, D extends SyntheticDefinition<R, D>> {
-
- private final SyntheticKind kind;
+abstract class SyntheticDefinition {
private final SynthesizingContext context;
- SyntheticDefinition(SyntheticKind kind, SynthesizingContext context) {
- assert kind != null;
- assert context != null;
- this.kind = kind;
+ SyntheticDefinition(SynthesizingContext context) {
this.context = context;
}
- abstract R toReference();
+ abstract SyntheticReference toReference();
- final SyntheticKind getKind() {
- return kind;
- }
-
- final SynthesizingContext getContext() {
+ SynthesizingContext getContext() {
return context;
}
abstract DexProgramClass getHolder();
- final HashCode computeHash(RepresentativeMap map, boolean intermediate) {
- Hasher hasher = Hashing.murmur3_128().newHasher();
- if (intermediate) {
- // If in intermediate mode, include the context type as sharing is restricted to within a
- // single context.
- getContext().getSynthesizingContextType().hashWithTypeEquivalence(hasher, map);
- }
- internalComputeHash(hasher, map);
- return hasher.hash();
- }
+ abstract HashCode computeHash(RepresentativeMap map, boolean intermediate);
- abstract void internalComputeHash(Hasher hasher, RepresentativeMap map);
-
- final boolean isEquivalentTo(D other, boolean includeContext) {
- return compareTo(other, includeContext) == 0;
- }
-
- int compareTo(D other, boolean includeContext) {
- if (includeContext) {
- int order = getContext().compareTo(other.getContext());
- if (order != 0) {
- return order;
- }
- }
- RepresentativeMap map = t -> t == other.getHolder().getType() ? getHolder().getType() : t;
- return internalCompareTo(other, map);
- }
-
- abstract int internalCompareTo(D other, RepresentativeMap map);
-
- public abstract boolean isValid();
+ abstract boolean isEquivalentTo(SyntheticDefinition other, boolean intermediate);
}
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 67391a2..c18b29d 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticFinalization.java
@@ -6,31 +6,22 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.GraphLens.Builder;
import com.android.tools.r8.graph.GraphLens.NestedGraphLens;
-import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.graph.TreeFixerBase;
-import com.android.tools.r8.ir.code.NumberGenerator;
import com.android.tools.r8.shaking.MainDexClasses;
-import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.collections.BidirectionalManyToManyRepresentativeMap;
-import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeHashMap;
-import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
-import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
import com.android.tools.r8.utils.structural.RepresentativeMap;
import com.google.common.collect.ArrayListMultimap;
-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.ListMultimap;
import com.google.common.collect.Sets;
import com.google.common.hash.HashCode;
@@ -41,146 +32,37 @@
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
-import java.util.function.Function;
+import java.util.function.Predicate;
public class SyntheticFinalization {
public static class Result {
public final CommittedItems commit;
- public final NonIdentityGraphLens lens;
public final PrunedItems prunedItems;
- public Result(
- CommittedItems commit, SyntheticFinalizationGraphLens lens, PrunedItems prunedItems) {
+ public Result(CommittedItems commit, PrunedItems prunedItems) {
this.commit = commit;
- this.lens = lens;
this.prunedItems = prunedItems;
}
}
- public static class SyntheticFinalizationGraphLens extends NestedGraphLens {
+ private static class EquivalenceGroup<T extends SyntheticDefinition & Comparable<T>>
+ implements Comparable<EquivalenceGroup<T>> {
+ private List<T> members;
- private final Map<DexType, DexType> syntheticTypeMap;
- private final Map<DexMethod, DexMethod> syntheticMethodsMap;
-
- private SyntheticFinalizationGraphLens(
- GraphLens previous,
- Map<DexType, DexType> syntheticClassesMap,
- Map<DexMethod, DexMethod> syntheticMethodsMap,
- Map<DexType, DexType> typeMap,
- BidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap,
- Map<DexMethod, DexMethod> methodMap,
- BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod> originalMethodSignatures,
- DexItemFactory factory) {
- super(typeMap, methodMap, fieldMap, originalMethodSignatures, previous, factory);
- this.syntheticTypeMap = syntheticClassesMap;
- this.syntheticMethodsMap = syntheticMethodsMap;
+ EquivalenceGroup(T singleton) {
+ this(singleton, Collections.singletonList(singleton));
}
- // The mapping is many to one, so the inverse is only defined up to equivalence groups.
- // Override the access to renamed signatures to first check for synthetic mappings before
- // using the original item mappings of the
-
- @Override
- public DexField getRenamedFieldSignature(DexField originalField) {
- if (syntheticTypeMap.containsKey(originalField.holder)) {
- DexField renamed = fieldMap.get(originalField);
- if (renamed != null) {
- return renamed;
- }
- }
- return super.getRenamedFieldSignature(originalField);
- }
-
- @Override
- public DexMethod getRenamedMethodSignature(DexMethod originalMethod, GraphLens applied) {
- if (syntheticTypeMap.containsKey(originalMethod.holder)) {
- DexMethod renamed = methodMap.get(originalMethod);
- if (renamed != null) {
- return renamed;
- }
- }
- DexMethod renamed = syntheticMethodsMap.get(originalMethod);
- return renamed != null ? renamed : super.getRenamedMethodSignature(originalMethod, applied);
- }
- }
-
- private static class Builder {
-
- // Forward mapping of internal to external synthetics.
- Map<DexType, DexType> syntheticClassesMap = new IdentityHashMap<>();
- Map<DexMethod, DexMethod> syntheticMethodsMap = new IdentityHashMap<>();
-
- Map<DexType, DexType> typeMap = new IdentityHashMap<>();
- BidirectionalManyToOneRepresentativeHashMap<DexField, DexField> fieldMap =
- new BidirectionalManyToOneRepresentativeHashMap<>();
- Map<DexMethod, DexMethod> methodMap = new IdentityHashMap<>();
-
- protected final BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures =
- new BidirectionalOneToOneHashMap<>();
-
- void moveSyntheticClass(DexType from, DexType to) {
- assert !syntheticClassesMap.containsKey(from);
- syntheticClassesMap.put(from, to);
- typeMap.put(from, to);
- }
-
- void moveSyntheticMethod(DexMethod from, DexMethod to) {
- assert !syntheticMethodsMap.containsKey(from);
- syntheticMethodsMap.put(from, to);
- methodMap.put(from, to);
- }
-
- void move(DexType from, DexType to) {
- typeMap.put(from, to);
- }
-
- void move(DexField from, DexField to) {
- fieldMap.put(from, to);
- }
-
- void move(DexMethod from, DexMethod to) {
- methodMap.put(from, to);
- originalMethodSignatures.put(to, from);
- }
-
- SyntheticFinalizationGraphLens build(GraphLens previous, DexItemFactory factory) {
- assert verifySubMap(syntheticClassesMap, typeMap);
- if (typeMap.isEmpty() && fieldMap.isEmpty() && methodMap.isEmpty()) {
- return null;
- }
- return new SyntheticFinalizationGraphLens(
- previous,
- syntheticClassesMap,
- syntheticMethodsMap,
- typeMap,
- fieldMap,
- methodMap,
- originalMethodSignatures,
- factory);
- }
-
- private static <K, V> boolean verifySubMap(Map<K, V> sub, Map<K, V> sup) {
- for (Entry<K, V> entry : sub.entrySet()) {
- assert sup.get(entry.getKey()) == entry.getValue();
- }
- return true;
- }
- }
-
- public static class EquivalenceGroup<T extends SyntheticDefinition<?, T>> {
- private final List<T> members;
-
- public EquivalenceGroup(T representative, List<T> members) {
+ EquivalenceGroup(T representative, List<T> members) {
assert !members.isEmpty();
assert members.get(0) == representative;
this.members = members;
}
- public T getRepresentative() {
+ T getRepresentative() {
return members.get(0);
}
@@ -188,103 +70,89 @@
return members;
}
- public int compareToIncludingContext(EquivalenceGroup<T> other) {
- return getRepresentative().compareTo(other.getRepresentative(), true);
- }
-
- public int compareTo(EquivalenceGroup<T> other, boolean includeContext) {
- return getRepresentative().compareTo(other.getRepresentative(), includeContext);
- }
-
@Override
- public String toString() {
- return "EquivalenceGroup{ members = "
- + members.size()
- + ", repr = "
- + getRepresentative()
- + " }";
+ public int compareTo(EquivalenceGroup<T> other) {
+ return getRepresentative().compareTo(other.getRepresentative());
}
}
private final InternalOptions options;
- private final CommittedSyntheticsCollection synthetics;
+ private final ImmutableSet<DexType> legacySyntheticTypes;
+ private final ImmutableMap<DexType, SyntheticReference> syntheticItems;
- SyntheticFinalization(InternalOptions options, CommittedSyntheticsCollection synthetics) {
+ SyntheticFinalization(
+ InternalOptions options,
+ ImmutableSet<DexType> legacySyntheticTypes,
+ ImmutableMap<DexType, SyntheticReference> syntheticItems) {
this.options = options;
- this.synthetics = synthetics;
+ this.legacySyntheticTypes = legacySyntheticTypes;
+ this.syntheticItems = syntheticItems;
}
public Result computeFinalSynthetics(AppView<?> appView) {
assert verifyNoNestedSynthetics();
- DexApplication application;
+ DexApplication application = appView.appInfo().app();
MainDexClasses mainDexClasses = appView.appInfo().getMainDexClasses();
+ GraphLens graphLens = appView.graphLens();
+
+ Map<DexType, SyntheticMethodDefinition> methodDefinitions =
+ lookupSyntheticMethodDefinitions(application);
+
+ Collection<List<SyntheticMethodDefinition>> potentialEquivalences =
+ computePotentialEquivalences(methodDefinitions, options.intermediate);
+
+ Map<DexType, EquivalenceGroup<SyntheticMethodDefinition>> equivalences =
+ computeActualEquivalences(potentialEquivalences, options.intermediate, options.itemFactory);
+
+ Builder lensBuilder = NestedGraphLens.builder();
+ List<DexProgramClass> newProgramClasses = new ArrayList<>();
List<DexProgramClass> finalSyntheticClasses = new ArrayList<>();
- Builder lensBuilder = new Builder();
- {
- Map<DexType, NumberGenerator> generators = new IdentityHashMap<>();
- application =
- buildLensAndProgram(
- appView,
- computeEquivalences(appView, synthetics.getNonLegacyMethods().values(), generators),
- computeEquivalences(appView, synthetics.getNonLegacyClasses().values(), generators),
- mainDexClasses,
- lensBuilder,
- finalSyntheticClasses);
- }
+ Set<DexType> derivedMainDexTypesToIgnore = Sets.newIdentityHashSet();
+ buildLensAndProgram(
+ appView,
+ equivalences,
+ syntheticItems::containsKey,
+ mainDexClasses,
+ lensBuilder,
+ newProgramClasses,
+ finalSyntheticClasses,
+ derivedMainDexTypesToIgnore);
+
+ newProgramClasses.addAll(finalSyntheticClasses);
handleSynthesizedClassMapping(
- finalSyntheticClasses, application, options, mainDexClasses, lensBuilder.typeMap);
+ finalSyntheticClasses, application, options, mainDexClasses, derivedMainDexTypesToIgnore);
+ DexApplication app = application.builder().replaceProgramClasses(newProgramClasses).build();
+
+ appView.setGraphLens(lensBuilder.build(options.itemFactory, graphLens));
assert appView.appInfo().getMainDexClasses() == mainDexClasses;
Set<DexType> finalSyntheticTypes = Sets.newIdentityHashSet();
finalSyntheticClasses.forEach(clazz -> finalSyntheticTypes.add(clazz.getType()));
Set<DexType> prunedSynthetics = Sets.newIdentityHashSet();
- synthetics.forEachNonLegacyItem(
- reference -> {
- DexType type = reference.getHolder();
- if (!finalSyntheticTypes.contains(type)) {
- prunedSynthetics.add(type);
- }
- });
+ for (DexType type : syntheticItems.keySet()) {
+ if (!finalSyntheticTypes.contains(type)) {
+ prunedSynthetics.add(type);
+ }
+ }
return new Result(
new CommittedItems(
SyntheticItems.INVALID_ID_AFTER_SYNTHETIC_FINALIZATION,
- application,
- new CommittedSyntheticsCollection(
- synthetics.getLegacyTypes(), ImmutableMap.of(), ImmutableMap.of()),
+ app,
+ legacySyntheticTypes,
+ ImmutableMap.of(),
ImmutableList.of()),
- lensBuilder.build(appView.graphLens(), appView.dexItemFactory()),
- PrunedItems.builder()
- .setPrunedApp(application)
- .addRemovedClasses(prunedSynthetics)
- .build());
- }
-
- private <R extends SyntheticReference<R, D>, D extends SyntheticDefinition<R, D>>
- Map<DexType, EquivalenceGroup<D>> computeEquivalences(
- AppView<?> appView,
- ImmutableCollection<R> references,
- Map<DexType, NumberGenerator> generators) {
- boolean intermediate = appView.options().intermediate;
- Map<DexType, D> definitions = lookupDefinitions(appView, references);
- Collection<List<D>> potentialEquivalences =
- computePotentialEquivalences(definitions, intermediate, appView.dexItemFactory());
- return computeActualEquivalences(potentialEquivalences, generators, appView, intermediate);
- }
-
- private boolean isNotSyntheticType(DexType type) {
- return !synthetics.containsNonLegacyType(type);
+ PrunedItems.builder().setPrunedApp(app).addRemovedClasses(prunedSynthetics).build());
}
private boolean verifyNoNestedSynthetics() {
// Check that a context is never itself synthetic class.
- synthetics.forEachNonLegacyItem(
- item -> {
- assert isNotSyntheticType(item.getContext().getSynthesizingContextType());
- });
+ for (SyntheticReference item : syntheticItems.values()) {
+ assert !syntheticItems.containsKey(item.getContext().getSynthesizingContextType());
+ }
return true;
}
@@ -293,7 +161,7 @@
DexApplication application,
InternalOptions options,
MainDexClasses mainDexClasses,
- Map<DexType, DexType> derivedMainDexTypesToIgnore) {
+ Set<DexType> derivedMainDexTypesToIgnore) {
boolean includeSynthesizedClassMappingInOutput = shouldAnnotateSynthetics(options);
if (includeSynthesizedClassMappingInOutput) {
updateSynthesizedClassMapping(application, finalSyntheticClasses);
@@ -309,7 +177,7 @@
DexApplication application, List<DexProgramClass> finalSyntheticClasses) {
ListMultimap<DexProgramClass, DexProgramClass> originalToSynthesized =
ArrayListMultimap.create();
- for (DexType type : synthetics.getLegacyTypes()) {
+ for (DexType type : legacySyntheticTypes) {
DexProgramClass clazz = DexProgramClass.asProgramClassOrNull(application.definitionFor(type));
if (clazz != null) {
for (DexProgramClass origin : clazz.getSynthesizedFrom()) {
@@ -346,7 +214,7 @@
private void updateMainDexListWithSynthesizedClassMap(
DexApplication application,
MainDexClasses mainDexClasses,
- Map<DexType, DexType> derivedMainDexTypesToIgnore) {
+ Set<DexType> derivedMainDexTypesToIgnore) {
if (mainDexClasses.isEmpty()) {
return;
}
@@ -360,11 +228,12 @@
DexAnnotation.readAnnotationSynthesizedClassMap(
programClass, application.dexItemFactory);
for (DexType type : derived) {
- DexType mappedType = derivedMainDexTypesToIgnore.getOrDefault(type, type);
- DexProgramClass syntheticClass =
- DexProgramClass.asProgramClassOrNull(application.definitionFor(mappedType));
- if (syntheticClass != null) {
- newMainDexClasses.add(syntheticClass);
+ if (!derivedMainDexTypesToIgnore.contains(type)) {
+ DexProgramClass syntheticClass =
+ DexProgramClass.asProgramClassOrNull(application.definitionFor(type));
+ if (syntheticClass != null) {
+ newMainDexClasses.add(syntheticClass);
+ }
}
}
}
@@ -379,16 +248,24 @@
}
}
- private static DexApplication buildLensAndProgram(
+ private static void buildLensAndProgram(
AppView<?> appView,
Map<DexType, EquivalenceGroup<SyntheticMethodDefinition>> syntheticMethodGroups,
- Map<DexType, EquivalenceGroup<SyntheticClassDefinition>> syntheticClassGroups,
+ Predicate<DexType> isSyntheticType,
MainDexClasses mainDexClasses,
Builder lensBuilder,
- List<DexProgramClass> newSyntheticClasses) {
- DexApplication application = appView.appInfo().app();
+ List<DexProgramClass> normalClasses,
+ List<DexProgramClass> newSyntheticClasses,
+ Set<DexType> derivedMainDexTypesToIgnore) {
DexItemFactory factory = appView.dexItemFactory();
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ if (!isSyntheticType.test(clazz.type)) {
+ assert SyntheticItems.verifyNotInternalSynthetic(clazz.type);
+ normalClasses.add(clazz);
+ }
+ }
+
// TODO(b/168584485): Remove this once class-mapping support is removed.
Set<DexType> derivedMainDexTypes = Sets.newIdentityHashSet();
mainDexClasses.forEach(
@@ -403,178 +280,60 @@
}
});
- Set<DexType> pruned = Sets.newIdentityHashSet();
syntheticMethodGroups.forEach(
(syntheticType, syntheticGroup) -> {
SyntheticMethodDefinition representative = syntheticGroup.getRepresentative();
SynthesizingContext context = representative.getContext();
context.registerPrefixRewriting(syntheticType, appView);
- DexProgramClass externalSyntheticClass =
- createExternalMethodClass(syntheticType, representative, factory);
- newSyntheticClasses.add(externalSyntheticClass);
- addSyntheticMarker(representative.getKind(), externalSyntheticClass, context, appView);
+ SyntheticClassBuilder builder =
+ new SyntheticClassBuilder(syntheticType, context, factory);
+ // TODO(b/158159959): Support grouping multiple methods per synthetic class.
+ builder.addMethod(
+ methodBuilder -> {
+ DexEncodedMethod definition = representative.getMethod().getDefinition();
+ methodBuilder
+ .setAccessFlags(definition.accessFlags)
+ .setProto(definition.getProto())
+ .setClassFileVersion(
+ definition.hasClassFileVersion() ? definition.getClassFileVersion() : null)
+ .setCode(m -> definition.getCode());
+ });
+ DexProgramClass externalSyntheticClass = builder.build();
+ if (shouldAnnotateSynthetics(appView.options())) {
+ externalSyntheticClass.setAnnotations(
+ externalSyntheticClass
+ .annotations()
+ .getWithAddedOrReplaced(
+ DexAnnotation.createAnnotationSynthesizedClass(
+ context.getSynthesizingContextType(), factory)));
+ }
assert externalSyntheticClass.getMethodCollection().size() == 1;
DexEncodedMethod externalSyntheticMethod =
externalSyntheticClass.methods().iterator().next();
- for (SyntheticMethodDefinition member : syntheticGroup.getMembers()) {
- DexMethod memberReference = member.getMethod().getReference();
- pruned.add(member.getHolder().getType());
- if (memberReference != externalSyntheticMethod.method) {
- lensBuilder.moveSyntheticMethod(memberReference, externalSyntheticMethod.method);
- }
- }
- });
-
- List<DexProgramClass> deduplicatedClasses = new ArrayList<>();
- syntheticClassGroups.forEach(
- (syntheticType, syntheticGroup) -> {
- SyntheticClassDefinition representative = syntheticGroup.getRepresentative();
- SynthesizingContext context = representative.getContext();
- context.registerPrefixRewriting(syntheticType, appView);
- DexProgramClass externalSyntheticClass = representative.getProgramClass();
- newSyntheticClasses.add(externalSyntheticClass);
- addSyntheticMarker(representative.getKind(), externalSyntheticClass, context, appView);
- for (SyntheticClassDefinition member : syntheticGroup.getMembers()) {
- DexProgramClass memberClass = member.getProgramClass();
- DexType memberType = memberClass.getType();
- pruned.add(memberType);
- if (memberType != syntheticType) {
- lensBuilder.moveSyntheticClass(memberType, syntheticType);
- }
- // The aliasing of the non-representative members needs to be recorded manually.
- if (member != representative) {
- deduplicatedClasses.add(memberClass);
- }
- }
- });
-
- List<DexProgramClass> newProgramClasses = new ArrayList<>(newSyntheticClasses);
- for (DexProgramClass clazz : application.classes()) {
- if (!pruned.contains(clazz.type)) {
- newProgramClasses.add(clazz);
- }
- }
- application = application.builder().replaceProgramClasses(newProgramClasses).build();
-
- // We can only assert that the method container classes are in here as the classes need
- // to be rewritten by the tree-fixer.
- for (DexType key : syntheticMethodGroups.keySet()) {
- assert application.definitionFor(key) != null;
- }
-
- newSyntheticClasses.clear();
-
- DexApplication.Builder<?> builder = application.builder();
- TreeFixerBase treeFixer =
- new TreeFixerBase(appView) {
- @Override
- public DexType mapClassType(DexType type) {
- return lensBuilder.syntheticClassesMap.getOrDefault(type, type);
- }
-
- @Override
- public void recordFieldChange(DexField from, DexField to) {
- lensBuilder.move(from, to);
- }
-
- @Override
- public void recordMethodChange(DexMethod from, DexMethod to) {
- lensBuilder.move(from, to);
- }
-
- @Override
- public void recordClassChange(DexType from, DexType to) {
- lensBuilder.move(from, to);
- }
- };
- treeFixer.fixupClasses(deduplicatedClasses);
- builder.replaceProgramClasses(treeFixer.fixupClasses(application.classes()));
- application = builder.build();
-
- // Add the synthesized from after repackaging which changed class definitions.
- final DexApplication appForLookup = application;
- syntheticClassGroups.forEach(
- (syntheticType, syntheticGroup) -> {
- DexProgramClass externalSyntheticClass = appForLookup.programDefinitionFor(syntheticType);
- newSyntheticClasses.add(externalSyntheticClass);
- for (SyntheticClassDefinition member : syntheticGroup.getMembers()) {
- addMainDexAndSynthesizedFromForMember(
- member,
- externalSyntheticClass,
- mainDexClasses,
- derivedMainDexTypes,
- appForLookup::programDefinitionFor);
- }
- });
- syntheticMethodGroups.forEach(
- (syntheticType, syntheticGroup) -> {
- DexProgramClass externalSyntheticClass = appForLookup.programDefinitionFor(syntheticType);
newSyntheticClasses.add(externalSyntheticClass);
for (SyntheticMethodDefinition member : syntheticGroup.getMembers()) {
- addMainDexAndSynthesizedFromForMember(
- member,
- externalSyntheticClass,
- mainDexClasses,
- derivedMainDexTypes,
- appForLookup::programDefinitionFor);
+ if (member.getMethod().getReference() != externalSyntheticMethod.method) {
+ lensBuilder.map(member.getMethod().getReference(), externalSyntheticMethod.method);
+ }
+ member
+ .getContext()
+ .addIfDerivedFromMainDexClass(
+ externalSyntheticClass,
+ mainDexClasses,
+ derivedMainDexTypes,
+ derivedMainDexTypesToIgnore);
+ // TODO(b/168584485): Remove this once class-mapping support is removed.
+ DexProgramClass from =
+ DexProgramClass.asProgramClassOrNull(
+ appView
+ .appInfo()
+ .definitionForWithoutExistenceAssert(
+ member.getContext().getSynthesizingContextType()));
+ if (from != null) {
+ externalSyntheticClass.addSynthesizedFrom(from);
+ }
}
});
-
- for (DexType key : syntheticMethodGroups.keySet()) {
- assert application.definitionFor(key) != null;
- }
-
- for (DexType key : syntheticClassGroups.keySet()) {
- assert application.definitionFor(key) != null;
- }
-
- return application;
- }
-
- private static void addSyntheticMarker(
- SyntheticKind kind,
- DexProgramClass externalSyntheticClass,
- SynthesizingContext context,
- AppView<?> appView) {
- if (shouldAnnotateSynthetics(appView.options())) {
- SyntheticMarker.addMarkerToClass(
- externalSyntheticClass, kind, context, appView.dexItemFactory());
- }
- }
-
- private static DexProgramClass createExternalMethodClass(
- DexType syntheticType, SyntheticMethodDefinition representative, DexItemFactory factory) {
- SyntheticClassBuilder builder =
- new SyntheticClassBuilder(syntheticType, representative.getContext(), factory);
- // TODO(b/158159959): Support grouping multiple methods per synthetic class.
- builder.addMethod(
- methodBuilder -> {
- DexEncodedMethod definition = representative.getMethod().getDefinition();
- methodBuilder
- .setName(SyntheticNaming.INTERNAL_SYNTHETIC_METHOD_PREFIX)
- .setAccessFlags(definition.accessFlags)
- .setProto(definition.getProto())
- .setClassFileVersion(
- definition.hasClassFileVersion() ? definition.getClassFileVersion() : null)
- .setCode(m -> definition.getCode());
- });
- return builder.build();
- }
-
- private static void addMainDexAndSynthesizedFromForMember(
- SyntheticDefinition<?, ?> member,
- DexProgramClass externalSyntheticClass,
- MainDexClasses mainDexClasses,
- Set<DexType> derivedMainDexTypes,
- Function<DexType, DexProgramClass> definitions) {
- member
- .getContext()
- .addIfDerivedFromMainDexClass(externalSyntheticClass, mainDexClasses, derivedMainDexTypes);
- // TODO(b/168584485): Remove this once class-mapping support is removed.
- DexProgramClass from = definitions.apply(member.getContext().getSynthesizingContextType());
- if (from != null) {
- externalSyntheticClass.addSynthesizedFrom(from);
- }
}
private static boolean shouldAnnotateSynthetics(InternalOptions options) {
@@ -586,12 +345,9 @@
return options.intermediate && !options.cfToCfDesugar;
}
- private <T extends SyntheticDefinition<?, T>>
+ private static <T extends SyntheticDefinition & Comparable<T>>
Map<DexType, EquivalenceGroup<T>> computeActualEquivalences(
- Collection<List<T>> potentialEquivalences,
- Map<DexType, NumberGenerator> generators,
- AppView<?> appView,
- boolean intermediate) {
+ Collection<List<T>> potentialEquivalences, boolean intermediate, DexItemFactory factory) {
Map<DexType, List<EquivalenceGroup<T>>> groupsPerContext = new IdentityHashMap<>();
potentialEquivalences.forEach(
members -> {
@@ -612,24 +368,20 @@
Map<DexType, EquivalenceGroup<T>> equivalences = new IdentityHashMap<>();
groupsPerContext.forEach(
(context, groups) -> {
- // Sort the equivalence groups that go into 'context' including the context type of the
- // representative which is equal to 'context' here (see assert below).
- groups.sort(EquivalenceGroup::compareToIncludingContext);
+ groups.sort(EquivalenceGroup::compareTo);
for (int i = 0; i < groups.size(); i++) {
EquivalenceGroup<T> group = groups.get(i);
- assert group.getRepresentative().getContext().getSynthesizingContextType() == context;
// Two equivalence groups in same context type must be distinct otherwise the assignment
// of the synthetic name will be non-deterministic between the two.
assert i == 0 || checkGroupsAreDistinct(groups.get(i - 1), group);
- SyntheticKind kind = group.members.get(0).getKind();
- DexType representativeType = createExternalType(kind, context, generators, appView);
+ DexType representativeType = createExternalType(context, i, factory);
equivalences.put(representativeType, group);
}
});
return equivalences;
}
- private static <T extends SyntheticDefinition<?, T>> List<List<T>> groupEquivalent(
+ private static <T extends SyntheticDefinition & Comparable<T>> List<List<T>> groupEquivalent(
List<T> potentialEquivalence, boolean intermediate) {
List<List<T>> groups = new ArrayList<>();
// Each other member is in a shared group if it is actually equivalent to the first member.
@@ -651,61 +403,42 @@
return groups;
}
- private static <T extends SyntheticDefinition<?, T>> boolean checkGroupsAreDistinct(
+ private static <T extends SyntheticDefinition & Comparable<T>> boolean checkGroupsAreDistinct(
EquivalenceGroup<T> g1, EquivalenceGroup<T> g2) {
- int order = g1.compareToIncludingContext(g2);
- assert order != 0;
- assert order != g2.compareToIncludingContext(g1);
+ assert g1.compareTo(g2) != 0;
return true;
}
- private static <T extends SyntheticDefinition<?, T>> T findDeterministicRepresentative(
+ private static <T extends SyntheticDefinition & Comparable<T>> T findDeterministicRepresentative(
List<T> members) {
// Pick a deterministic member as representative.
T smallest = members.get(0);
for (int i = 1; i < members.size(); i++) {
T next = members.get(i);
- if (next.compareTo(smallest, true) < 0) {
+ if (next.compareTo(smallest) < 0) {
smallest = next;
}
}
return smallest;
}
- private DexType createExternalType(
- SyntheticKind kind,
- DexType representativeContext,
- Map<DexType, NumberGenerator> generators,
- AppView<?> appView) {
- NumberGenerator generator =
- generators.computeIfAbsent(representativeContext, k -> new NumberGenerator());
- DexType externalType;
- do {
- externalType =
- SyntheticNaming.createExternalType(
- kind,
- representativeContext,
- Integer.toString(generator.next()),
- appView.dexItemFactory());
- DexClass clazz = appView.appInfo().definitionForWithoutExistenceAssert(externalType);
- if (clazz != null && isNotSyntheticType(clazz.type)) {
- assert options.testing.allowConflictingSyntheticTypes
- : "Unexpected creation of an existing external synthetic type: " + clazz;
- externalType = null;
- }
- } while (externalType == null);
- return externalType;
+ private static DexType createExternalType(
+ DexType representativeContext, int nextContextId, DexItemFactory factory) {
+ return factory.createType(
+ DescriptorUtils.getDescriptorFromClassBinaryName(
+ representativeContext.getInternalName()
+ + SyntheticItems.EXTERNAL_SYNTHETIC_CLASS_SEPARATOR
+ + nextContextId));
}
- private static <T extends SyntheticDefinition<?, T>>
- Collection<List<T>> computePotentialEquivalences(
- Map<DexType, T> definitions, boolean intermediate, DexItemFactory factory) {
+ private static <T extends SyntheticDefinition> Collection<List<T>> computePotentialEquivalences(
+ Map<DexType, T> definitions, boolean intermediate) {
if (definitions.isEmpty()) {
return Collections.emptyList();
}
- // Map all synthetic types to the java 'void' type. This is not an actual valid type, so it
- // cannot collide with any valid java type providing a good hashing key for the synthetics.
- RepresentativeMap map = t -> definitions.containsKey(t) ? factory.voidType : t;
+ Set<DexType> allTypes = definitions.keySet();
+ DexType representative = allTypes.iterator().next();
+ RepresentativeMap map = t -> allTypes.contains(t) ? representative : t;
Map<HashCode, List<T>> equivalences = new HashMap<>(definitions.size());
for (T definition : definitions.values()) {
HashCode hash = definition.computeHash(map, intermediate);
@@ -714,24 +447,25 @@
return equivalences.values();
}
- private <R extends SyntheticReference<R, D>, D extends SyntheticDefinition<R, D>>
- Map<DexType, D> lookupDefinitions(AppView<?> appView, Collection<R> references) {
- Map<DexType, D> definitions = new IdentityHashMap<>(references.size());
- for (R reference : references) {
- D definition = reference.lookupDefinition(appView::definitionFor);
- if (definition == null) {
+ private Map<DexType, SyntheticMethodDefinition> lookupSyntheticMethodDefinitions(
+ DexApplication finalApp) {
+ Map<DexType, SyntheticMethodDefinition> methods = new IdentityHashMap<>(syntheticItems.size());
+ for (SyntheticReference reference : syntheticItems.values()) {
+ SyntheticDefinition definition = reference.lookupDefinition(finalApp::definitionFor);
+ if (definition == null || !(definition instanceof SyntheticMethodDefinition)) {
// We expect pruned definitions to have been removed.
assert false;
continue;
}
- if (definition.isValid()) {
- definitions.put(reference.getHolder(), definition);
+ SyntheticMethodDefinition method = (SyntheticMethodDefinition) definition;
+ if (SyntheticMethodBuilder.isValidSyntheticMethod(method.getMethod().getDefinition())) {
+ methods.put(method.getHolder().getType(), method);
} else {
// Failing this check indicates that an optimization has modified the synthetic in a
// disruptive way.
assert false;
}
}
- return definitions;
+ return methods;
}
}
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 179f990..bc7d42e 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -6,8 +6,12 @@
import com.android.tools.r8.errors.InternalCompilerError;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ClassAccessFlags;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
@@ -17,8 +21,11 @@
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.ir.conversion.MethodProcessingId;
import com.android.tools.r8.synthesis.SyntheticFinalization.Result;
-import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSet.Builder;
+import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -28,112 +35,148 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import java.util.function.Function;
-import java.util.function.Predicate;
import java.util.function.Supplier;
-import java.util.stream.Collectors;
public class SyntheticItems implements SyntheticDefinitionsProvider {
static final int INVALID_ID_AFTER_SYNTHETIC_FINALIZATION = -1;
+ /**
+ * The internal synthetic class separator is only used for representing synthetic items during
+ * compilation. In particular, this separator must never be used to write synthetic classes to the
+ * final compilation result.
+ */
+ public static final String INTERNAL_SYNTHETIC_CLASS_SEPARATOR = "-$$InternalSynthetic";
+
+ /**
+ * The external synthetic class separator is used when writing classes. It may appear in types
+ * during compilation as the output of a compilation may be the input to another.
+ */
+ public static final String EXTERNAL_SYNTHETIC_CLASS_SEPARATOR = "-$$ExternalSynthetic";
+
+ /** Method prefix when generating synthetic methods in a class. */
+ public static final String INTERNAL_SYNTHETIC_METHOD_PREFIX = "m";
+
+ public static boolean verifyNotInternalSynthetic(DexType type) {
+ assert !type.toDescriptorString().contains(SyntheticItems.INTERNAL_SYNTHETIC_CLASS_SEPARATOR);
+ return true;
+ }
+
/** Globally incremented id for the next internal synthetic class. */
private int nextSyntheticId;
- /** Collection of pending items. */
- private static class PendingSynthetics {
- /**
- * Thread safe collection of synthesized classes that are not yet committed to the application.
- *
- * <p>TODO(b/158159959): Remove legacy support.
- */
- private final Map<DexType, DexProgramClass> legacyClasses = new ConcurrentHashMap<>();
+ /**
+ * Thread safe collection of synthesized classes that are not yet committed to the application.
+ *
+ * <p>TODO(b/158159959): Remove legacy support.
+ */
+ private final Map<DexType, DexProgramClass> legacyPendingClasses = new ConcurrentHashMap<>();
- /** Thread safe collection of synthetic items not yet committed to the application. */
- private final ConcurrentHashMap<DexType, SyntheticDefinition<?, ?>> nonLegacyDefinitions =
- new ConcurrentHashMap<>();
+ /**
+ * Immutable set of synthetic types in the application (eg, committed).
+ *
+ * <p>TODO(b/158159959): Remove legacy support.
+ */
+ private final ImmutableSet<DexType> legacySyntheticTypes;
- boolean isEmpty() {
- return legacyClasses.isEmpty() && nonLegacyDefinitions.isEmpty();
- }
+ /** Thread safe collection of synthetic items not yet committed to the application. */
+ private final ConcurrentHashMap<DexType, SyntheticDefinition> pendingDefinitions =
+ new ConcurrentHashMap<>();
- boolean containsType(DexType type) {
- return legacyClasses.containsKey(type) || nonLegacyDefinitions.containsKey(type);
- }
-
- boolean verifyNotRewritten(NonIdentityGraphLens lens) {
- assert legacyClasses.keySet().equals(lens.rewriteTypes(legacyClasses.keySet()));
- assert nonLegacyDefinitions.keySet().equals(lens.rewriteTypes(nonLegacyDefinitions.keySet()));
- return true;
- }
-
- Collection<DexProgramClass> getAllClasses() {
- List<DexProgramClass> allPending =
- new ArrayList<>(nonLegacyDefinitions.size() + legacyClasses.size());
- for (SyntheticDefinition<?, ?> item : nonLegacyDefinitions.values()) {
- allPending.add(item.getHolder());
- }
- allPending.addAll(legacyClasses.values());
- return Collections.unmodifiableList(allPending);
- }
- }
-
- private final CommittedSyntheticsCollection committed;
-
- private final PendingSynthetics pending = new PendingSynthetics();
+ /** Mapping from synthetic type to its synthetic description. */
+ private final ImmutableMap<DexType, SyntheticReference> nonLecacySyntheticItems;
// Only for use from initial AppInfo/AppInfoWithClassHierarchy create functions. */
public static CommittedItems createInitialSyntheticItems(DexApplication application) {
return new CommittedItems(
- 0, application, CommittedSyntheticsCollection.empty(), ImmutableList.of());
+ 0, application, ImmutableSet.of(), ImmutableMap.of(), ImmutableList.of());
}
// Only for conversion to a mutable synthetic items collection.
SyntheticItems(CommittedItems commit) {
- this(commit.nextSyntheticId, commit.committed);
+ this(commit.nextSyntheticId, commit.legacySyntheticTypes, commit.syntheticItems);
}
- private SyntheticItems(int nextSyntheticId, CommittedSyntheticsCollection committed) {
+ private SyntheticItems(
+ int nextSyntheticId,
+ ImmutableSet<DexType> legacySyntheticTypes,
+ ImmutableMap<DexType, SyntheticReference> nonLecacySyntheticItems) {
this.nextSyntheticId = nextSyntheticId;
- this.committed = committed;
+ this.legacySyntheticTypes = legacySyntheticTypes;
+ this.nonLecacySyntheticItems = nonLecacySyntheticItems;
+ assert Sets.intersection(nonLecacySyntheticItems.keySet(), legacySyntheticTypes).isEmpty();
}
public static void collectSyntheticInputs(AppView<AppInfo> appView) {
// Collecting synthetic items must be the very first task after application build.
SyntheticItems synthetics = appView.getSyntheticItems();
assert synthetics.nextSyntheticId == 0;
- assert synthetics.committed.isEmpty();
- assert synthetics.pending.isEmpty();
+ assert synthetics.nonLecacySyntheticItems.isEmpty();
+ assert !synthetics.hasPendingSyntheticClasses();
if (appView.options().intermediate) {
// If the compilation is in intermediate mode the synthetics should just be passed through.
return;
}
- CommittedSyntheticsCollection.Builder builder = synthetics.committed.builder();
+ ImmutableMap.Builder<DexType, SyntheticReference> pending = ImmutableMap.builder();
// TODO(b/158159959): Consider identifying synthetics in the input reader to speed this up.
for (DexProgramClass clazz : appView.appInfo().classes()) {
- SyntheticMarker marker =
- SyntheticMarker.stripMarkerFromClass(clazz, appView.dexItemFactory());
- if (marker.isSyntheticMethods()) {
- clazz.forEachProgramMethod(
- // TODO(b/158159959): Support having multiple methods per class.
- method -> {
- builder.addNonLegacyMethod(
- new SyntheticMethodDefinition(marker.getKind(), marker.getContext(), method));
- });
- } else if (marker.isSyntheticClass()) {
- builder.addNonLegacyClass(
- new SyntheticClassDefinition(marker.getKind(), marker.getContext(), clazz));
+ DexType annotatedContextType = isSynthesizedMethodsContainer(clazz, appView.dexItemFactory());
+ if (annotatedContextType == null) {
+ continue;
}
+ clazz.setAnnotations(DexAnnotationSet.empty());
+ SynthesizingContext context =
+ SynthesizingContext.fromSyntheticInputClass(clazz, annotatedContextType);
+ clazz.forEachProgramMethod(
+ // TODO(b/158159959): Support having multiple methods per class.
+ method -> {
+ method.getDefinition().setAnnotations(DexAnnotationSet.empty());
+ pending.put(clazz.type, new SyntheticMethodDefinition(context, method).toReference());
+ });
}
- CommittedSyntheticsCollection committed = builder.build();
- if (committed.isEmpty()) {
+ pending.putAll(synthetics.nonLecacySyntheticItems);
+ ImmutableMap<DexType, SyntheticReference> nonLegacySyntheticItems = pending.build();
+ if (nonLegacySyntheticItems.isEmpty()) {
return;
}
CommittedItems commit =
new CommittedItems(
- synthetics.nextSyntheticId, appView.appInfo().app(), committed, ImmutableList.of());
+ synthetics.nextSyntheticId,
+ appView.appInfo().app(),
+ synthetics.legacySyntheticTypes,
+ nonLegacySyntheticItems,
+ ImmutableList.of());
appView.setAppInfo(new AppInfo(commit, appView.appInfo().getMainDexClasses()));
}
+ private static DexType isSynthesizedMethodsContainer(
+ DexProgramClass clazz, DexItemFactory factory) {
+ ClassAccessFlags flags = clazz.accessFlags;
+ if (!flags.isSynthetic() || flags.isAbstract() || flags.isEnum()) {
+ return null;
+ }
+ DexType contextType =
+ DexAnnotation.getSynthesizedClassAnnotationContextType(clazz.annotations(), factory);
+ if (contextType == null) {
+ return null;
+ }
+ if (clazz.superType != factory.objectType) {
+ return null;
+ }
+ if (!clazz.interfaces.isEmpty()) {
+ return null;
+ }
+ if (clazz.annotations().size() != 1) {
+ return null;
+ }
+ for (DexEncodedMethod method : clazz.methods()) {
+ if (!SyntheticMethodBuilder.isValidSyntheticMethod(method)) {
+ return null;
+ }
+ }
+ return contextType;
+ }
+
// Internal synthetic id creation helpers.
private synchronized String getNextSyntheticId() {
@@ -148,52 +191,49 @@
@Override
public DexClass definitionFor(DexType type, Function<DexType, DexClass> baseDefinitionFor) {
- DexProgramClass clazz = pending.legacyClasses.get(type);
- if (clazz == null) {
- SyntheticDefinition<?, ?> item = pending.nonLegacyDefinitions.get(type);
+ DexProgramClass pending = legacyPendingClasses.get(type);
+ if (pending == null) {
+ SyntheticDefinition item = pendingDefinitions.get(type);
if (item != null) {
- clazz = item.getHolder();
+ pending = item.getHolder();
}
}
- if (clazz != null) {
+ if (pending != null) {
assert baseDefinitionFor.apply(type) == null
: "Pending synthetic definition also present in the active program: " + type;
- return clazz;
+ return pending;
}
return baseDefinitionFor.apply(type);
}
- public boolean verifyNonLegacySyntheticsAreCommitted() {
- assert pending.nonLegacyDefinitions.isEmpty()
- : "Uncommitted synthetics: "
- + pending.nonLegacyDefinitions.keySet().stream()
- .map(DexType::getName)
- .collect(Collectors.joining());
- return true;
- }
-
public boolean hasPendingSyntheticClasses() {
- return !pending.isEmpty();
+ return !legacyPendingClasses.isEmpty() || !pendingDefinitions.isEmpty();
}
public Collection<DexProgramClass> getPendingSyntheticClasses() {
- return pending.getAllClasses();
+ List<DexProgramClass> pending =
+ new ArrayList<>(pendingDefinitions.size() + legacyPendingClasses.size());
+ for (SyntheticDefinition item : pendingDefinitions.values()) {
+ pending.add(item.getHolder());
+ }
+ pending.addAll(legacyPendingClasses.values());
+ return Collections.unmodifiableList(pending);
}
private boolean isCommittedSynthetic(DexType type) {
- return committed.containsType(type);
+ return nonLecacySyntheticItems.containsKey(type) || legacySyntheticTypes.contains(type);
}
private boolean isLegacyCommittedSynthetic(DexType type) {
- return committed.containsLegacyType(type);
+ return legacySyntheticTypes.contains(type);
}
public boolean isPendingSynthetic(DexType type) {
- return pending.containsType(type);
+ return pendingDefinitions.containsKey(type) || legacyPendingClasses.containsKey(type);
}
public boolean isLegacyPendingSynthetic(DexType type) {
- return pending.legacyClasses.containsKey(type);
+ return legacyPendingClasses.containsKey(type);
}
public boolean isSyntheticClass(DexType type) {
@@ -207,27 +247,6 @@
return isSyntheticClass(clazz.type);
}
- // The compiler should not inspect the kind of a synthetic, so this provided only as a assertion
- // utility.
- public boolean verifySyntheticLambdaProperty(
- DexProgramClass clazz,
- Predicate<DexProgramClass> ifIsLambda,
- Predicate<DexProgramClass> ifNotLambda) {
- SyntheticReference<?, ?> reference = committed.getNonLegacyItem(clazz.getType());
- if (reference == null) {
- SyntheticDefinition<?, ?> definition = pending.nonLegacyDefinitions.get(clazz.getType());
- if (definition != null) {
- reference = definition.toReference();
- }
- }
- if (reference != null && reference.getKind() == SyntheticKind.LAMBDA) {
- assert ifIsLambda.test(clazz);
- } else {
- assert ifNotLambda.test(clazz);
- }
- return true;
- }
-
public boolean isLegacySyntheticClass(DexType type) {
return isLegacyCommittedSynthetic(type) || isLegacyPendingSynthetic(type);
}
@@ -237,21 +256,18 @@
}
public Collection<DexProgramClass> getLegacyPendingClasses() {
- return Collections.unmodifiableCollection(pending.legacyClasses.values());
+ return Collections.unmodifiableCollection(legacyPendingClasses.values());
}
private SynthesizingContext getSynthesizingContext(ProgramDefinition context) {
- DexType contextType = context.getContextType();
- SyntheticDefinition<?, ?> existingDefinition = pending.nonLegacyDefinitions.get(contextType);
- if (existingDefinition != null) {
- return existingDefinition.getContext();
+ SyntheticDefinition pendingItemContext = pendingDefinitions.get(context.getContextType());
+ if (pendingItemContext != null) {
+ return pendingItemContext.getContext();
}
- SyntheticReference<?, ?> existingReference = committed.getNonLegacyItem(contextType);
- if (existingReference != null) {
- return existingReference.getContext();
- }
- // This context is not nested in an existing synthetic context so create a new "leaf" context.
- return SynthesizingContext.fromNonSyntheticInputContext(context);
+ SyntheticReference committedItemContext = nonLecacySyntheticItems.get(context.getContextType());
+ return committedItemContext != null
+ ? committedItemContext.getContext()
+ : SynthesizingContext.fromNonSyntheticInputContext(context);
}
// Addition and creation of synthetic items.
@@ -260,49 +276,25 @@
public void addLegacySyntheticClass(DexProgramClass clazz) {
assert clazz.type.isD8R8SynthesizedClassType();
assert !isCommittedSynthetic(clazz.type);
- assert !pending.nonLegacyDefinitions.containsKey(clazz.type);
- DexProgramClass previous = pending.legacyClasses.put(clazz.type, clazz);
+ DexProgramClass previous = legacyPendingClasses.put(clazz.type, clazz);
assert previous == null || previous == clazz;
}
- public DexProgramClass createClass(
- SyntheticKind kind,
- DexProgramClass context,
- DexItemFactory factory,
- Consumer<SyntheticClassBuilder> fn) {
- // Obtain the outer synthesizing context in the case the context itself is synthetic.
- // This is to ensure a flat input-type -> synthetic-item mapping.
- SynthesizingContext outerContext = getSynthesizingContext(context);
- DexType type =
- SyntheticNaming.createInternalType(kind, outerContext, getNextSyntheticId(), factory);
- SyntheticClassBuilder classBuilder = new SyntheticClassBuilder(type, outerContext, factory);
- fn.accept(classBuilder);
- DexProgramClass clazz = classBuilder.build();
- addPendingDefinition(new SyntheticClassDefinition(kind, outerContext, clazz));
- return clazz;
- }
-
/** Create a single synthetic method item. */
public ProgramMethod createMethod(
- SyntheticKind kind,
- ProgramDefinition context,
- DexItemFactory factory,
- Consumer<SyntheticMethodBuilder> fn) {
- return createMethod(kind, context, factory, fn, this::getNextSyntheticId);
+ ProgramDefinition context, DexItemFactory factory, Consumer<SyntheticMethodBuilder> fn) {
+ return createMethod(context, factory, fn, this::getNextSyntheticId);
}
public ProgramMethod createMethod(
- SyntheticKind kind,
ProgramDefinition context,
DexItemFactory factory,
Consumer<SyntheticMethodBuilder> fn,
MethodProcessingId methodProcessingId) {
- return createMethod(
- kind, context, factory, fn, methodProcessingId::getFullyQualifiedIdAndIncrement);
+ return createMethod(context, factory, fn, methodProcessingId::getFullyQualifiedIdAndIncrement);
}
private ProgramMethod createMethod(
- SyntheticKind kind,
ProgramDefinition context,
DexItemFactory factory,
Consumer<SyntheticMethodBuilder> fn,
@@ -311,20 +303,16 @@
// Obtain the outer synthesizing context in the case the context itself is synthetic.
// This is to ensure a flat input-type -> synthetic-item mapping.
SynthesizingContext outerContext = getSynthesizingContext(context);
- DexType type =
- SyntheticNaming.createInternalType(kind, outerContext, syntheticIdSupplier.get(), factory);
+ DexType type = outerContext.createHygienicType(syntheticIdSupplier.get(), factory);
SyntheticClassBuilder classBuilder = new SyntheticClassBuilder(type, outerContext, factory);
- DexProgramClass clazz =
- classBuilder
- .addMethod(fn.andThen(m -> m.setName(SyntheticNaming.INTERNAL_SYNTHETIC_METHOD_PREFIX)))
- .build();
+ DexProgramClass clazz = classBuilder.addMethod(fn).build();
ProgramMethod method = new ProgramMethod(clazz, clazz.methods().iterator().next());
- addPendingDefinition(new SyntheticMethodDefinition(kind, outerContext, method));
+ addPendingDefinition(new SyntheticMethodDefinition(outerContext, method));
return method;
}
- private void addPendingDefinition(SyntheticDefinition<?, ?> definition) {
- pending.nonLegacyDefinitions.put(definition.getHolder().getType(), definition);
+ private void addPendingDefinition(SyntheticDefinition definition) {
+ pendingDefinitions.put(definition.getHolder().getType(), definition);
}
// Commit of the synthetic items to a new fully populated application.
@@ -334,48 +322,125 @@
}
public CommittedItems commitPrunedItems(PrunedItems prunedItems) {
- return commit(prunedItems, pending, committed, nextSyntheticId);
+ return commit(
+ prunedItems.getPrunedApp(),
+ prunedItems.getNoLongerSyntheticItems(),
+ legacyPendingClasses,
+ legacySyntheticTypes,
+ pendingDefinitions,
+ nonLecacySyntheticItems,
+ nextSyntheticId);
}
public CommittedItems commitRewrittenWithLens(
DexApplication application, NonIdentityGraphLens lens) {
- assert pending.verifyNotRewritten(lens);
+ // Rewrite the previously committed synthetic types.
+ ImmutableSet<DexType> rewrittenLegacyTypes = lens.rewriteTypes(this.legacySyntheticTypes);
+ ImmutableMap.Builder<DexType, SyntheticReference> rewrittenItems = ImmutableMap.builder();
+ for (SyntheticReference reference : nonLecacySyntheticItems.values()) {
+ SyntheticReference rewritten = reference.rewrite(lens);
+ if (rewritten != null) {
+ rewrittenItems.put(rewritten.getHolder(), rewritten);
+ }
+ }
+ // No pending item should need rewriting.
+ assert legacyPendingClasses.keySet().equals(lens.rewriteTypes(legacyPendingClasses.keySet()));
+ assert pendingDefinitions.keySet().equals(lens.rewriteTypes(pendingDefinitions.keySet()));
return commit(
- PrunedItems.empty(application), pending, committed.rewriteWithLens(lens), nextSyntheticId);
+ application,
+ Collections.emptySet(),
+ legacyPendingClasses,
+ rewrittenLegacyTypes,
+ pendingDefinitions,
+ rewrittenItems.build(),
+ nextSyntheticId);
}
private static CommittedItems commit(
- PrunedItems prunedItems,
- PendingSynthetics pending,
- CommittedSyntheticsCollection committed,
+ DexApplication application,
+ Set<DexType> removedClasses,
+ Map<DexType, DexProgramClass> legacyPendingClasses,
+ ImmutableSet<DexType> legacySyntheticTypes,
+ ConcurrentHashMap<DexType, SyntheticDefinition> pendingDefinitions,
+ ImmutableMap<DexType, SyntheticReference> syntheticItems,
int nextSyntheticId) {
- DexApplication application = prunedItems.getPrunedApp();
- Set<DexType> removedClasses = prunedItems.getNoLongerSyntheticItems();
- CommittedSyntheticsCollection.Builder builder = committed.builder();
- // Legacy synthetics must already have been committed to the app.
- assert verifyClassesAreInApp(application, pending.legacyClasses.values());
- builder.addLegacyClasses(pending.legacyClasses.values());
- // Compute the synthetic additions and add them to the application.
+ // Legacy synthetics must already have been committed.
+ assert verifyClassesAreInApp(application, legacyPendingClasses.values());
+ // Add the set of legacy definitions to the synthetic types.
+ ImmutableSet<DexType> mergedLegacyTypes = legacySyntheticTypes;
+ if (!legacyPendingClasses.isEmpty() || !removedClasses.isEmpty()) {
+ ImmutableSet.Builder<DexType> legacyBuilder = ImmutableSet.builder();
+ filteredAdd(legacySyntheticTypes, removedClasses, legacyBuilder);
+ filteredAdd(legacyPendingClasses.keySet(), removedClasses, legacyBuilder);
+ mergedLegacyTypes = legacyBuilder.build();
+ }
+ // The set of synthetic items is the union of the previous types plus the pending additions.
+ ImmutableMap<DexType, SyntheticReference> mergedItems;
ImmutableList<DexType> additions;
DexApplication amendedApplication;
- if (pending.nonLegacyDefinitions.isEmpty()) {
+ if (pendingDefinitions.isEmpty()) {
+ mergedItems = filteredCopy(syntheticItems, removedClasses);
additions = ImmutableList.of();
amendedApplication = application;
} else {
DexApplication.Builder<?> appBuilder = application.builder();
+ ImmutableMap.Builder<DexType, SyntheticReference> itemsBuilder = ImmutableMap.builder();
ImmutableList.Builder<DexType> additionsBuilder = ImmutableList.builder();
- for (SyntheticDefinition<?, ?> definition : pending.nonLegacyDefinitions.values()) {
- if (!removedClasses.contains(definition.getHolder().getType())) {
- additionsBuilder.add(definition.getHolder().getType());
- appBuilder.addProgramClass(definition.getHolder());
- builder.addItem(definition);
+ for (SyntheticDefinition definition : pendingDefinitions.values()) {
+ if (removedClasses.contains(definition.getHolder().getType())) {
+ continue;
}
+ SyntheticReference reference = definition.toReference();
+ itemsBuilder.put(reference.getHolder(), reference);
+ additionsBuilder.add(definition.getHolder().getType());
+ appBuilder.addProgramClass(definition.getHolder());
}
+ filteredAdd(syntheticItems, removedClasses, itemsBuilder);
+ mergedItems = itemsBuilder.build();
additions = additionsBuilder.build();
amendedApplication = appBuilder.build();
}
return new CommittedItems(
- nextSyntheticId, amendedApplication, builder.build().pruneItems(prunedItems), additions);
+ nextSyntheticId, amendedApplication, mergedLegacyTypes, mergedItems, additions);
+ }
+
+ private static void filteredAdd(
+ Set<DexType> input, Set<DexType> excludeSet, Builder<DexType> result) {
+ if (excludeSet.isEmpty()) {
+ result.addAll(input);
+ } else {
+ for (DexType type : input) {
+ if (!excludeSet.contains(type)) {
+ result.add(type);
+ }
+ }
+ }
+ }
+
+ private static ImmutableMap<DexType, SyntheticReference> filteredCopy(
+ ImmutableMap<DexType, SyntheticReference> syntheticItems, Set<DexType> removedClasses) {
+ if (removedClasses.isEmpty()) {
+ return syntheticItems;
+ }
+ ImmutableMap.Builder<DexType, SyntheticReference> builder = ImmutableMap.builder();
+ filteredAdd(syntheticItems, removedClasses, builder);
+ return builder.build();
+ }
+
+ private static void filteredAdd(
+ ImmutableMap<DexType, SyntheticReference> syntheticItems,
+ Set<DexType> removedClasses,
+ ImmutableMap.Builder<DexType, SyntheticReference> builder) {
+ if (removedClasses.isEmpty()) {
+ builder.putAll(syntheticItems);
+ } else {
+ syntheticItems.forEach(
+ (t, r) -> {
+ if (!removedClasses.contains(t)) {
+ builder.put(t, r);
+ }
+ });
+ }
}
private static boolean verifyClassesAreInApp(
@@ -390,6 +455,8 @@
public Result computeFinalSynthetics(AppView<?> appView) {
assert !hasPendingSyntheticClasses();
- return new SyntheticFinalization(appView.options(), committed).computeFinalSynthetics(appView);
+ return new SyntheticFinalization(
+ appView.options(), legacySyntheticTypes, nonLecacySyntheticItems)
+ .computeFinalSynthetics(appView);
}
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java
deleted file mode 100644
index 03b7e36..0000000
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMarker.java
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.synthesis;
-
-import com.android.tools.r8.graph.ClassAccessFlags;
-import com.android.tools.r8.graph.DexAnnotation;
-import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
-import com.android.tools.r8.utils.Pair;
-
-public class SyntheticMarker {
-
- public static void addMarkerToClass(
- DexProgramClass clazz,
- SyntheticKind kind,
- SynthesizingContext context,
- DexItemFactory factory) {
- clazz.setAnnotations(
- clazz
- .annotations()
- .getWithAddedOrReplaced(
- DexAnnotation.createAnnotationSynthesizedClass(
- kind, context.getSynthesizingContextType(), factory)));
- }
-
- public static SyntheticMarker stripMarkerFromClass(
- DexProgramClass clazz, DexItemFactory factory) {
- SyntheticMarker marker = internalStripMarkerFromClass(clazz, factory);
- assert marker != NO_MARKER
- || DexAnnotation.getSynthesizedClassAnnotationContextType(clazz.annotations(), factory)
- == null;
- return marker;
- }
-
- private static SyntheticMarker internalStripMarkerFromClass(
- DexProgramClass clazz, DexItemFactory factory) {
- ClassAccessFlags flags = clazz.accessFlags;
- if (clazz.superType != factory.objectType) {
- return NO_MARKER;
- }
- if (!flags.isSynthetic() || flags.isAbstract() || flags.isEnum()) {
- return NO_MARKER;
- }
- Pair<SyntheticKind, DexType> info =
- DexAnnotation.getSynthesizedClassAnnotationContextType(clazz.annotations(), factory);
- if (info == null) {
- return NO_MARKER;
- }
- assert clazz.annotations().size() == 1;
- SyntheticKind kind = info.getFirst();
- DexType context = info.getSecond();
- if (kind.isSingleSyntheticMethod) {
- if (!clazz.interfaces.isEmpty()) {
- return NO_MARKER;
- }
- for (DexEncodedMethod method : clazz.methods()) {
- if (!SyntheticMethodBuilder.isValidSyntheticMethod(method)) {
- return NO_MARKER;
- }
- }
- }
- clazz.setAnnotations(DexAnnotationSet.empty());
- return new SyntheticMarker(kind, SynthesizingContext.fromSyntheticInputClass(clazz, context));
- }
-
- private static final SyntheticMarker NO_MARKER = new SyntheticMarker(null, null);
-
- private final SyntheticKind kind;
- private final SynthesizingContext context;
-
- public SyntheticMarker(SyntheticKind kind, SynthesizingContext context) {
- this.kind = kind;
- this.context = context;
- }
-
- public boolean isSyntheticMethods() {
- return kind != null && kind.isSingleSyntheticMethod;
- }
-
- public boolean isSyntheticClass() {
- return kind != null && !kind.isSingleSyntheticMethod;
- }
-
- public SyntheticKind getKind() {
- return kind;
- }
-
- public SynthesizingContext getContext() {
- return context;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
index 60e3079..9fcc7d9 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
@@ -21,25 +20,15 @@
}
private final SyntheticClassBuilder parent;
- private DexString name = null;
+ private final String name;
private DexProto proto = null;
private CfVersion classFileVersion;
private SyntheticCodeGenerator codeGenerator = null;
private MethodAccessFlags accessFlags = null;
- SyntheticMethodBuilder(SyntheticClassBuilder parent) {
+ SyntheticMethodBuilder(SyntheticClassBuilder parent, String name) {
this.parent = parent;
- }
-
- public SyntheticMethodBuilder setName(String name) {
- return setName(parent.getFactory().createString(name));
- }
-
- public SyntheticMethodBuilder setName(DexString name) {
- assert name != null;
- assert this.name == null;
this.name = name;
- return this;
}
public SyntheticMethodBuilder setProto(DexProto proto) {
@@ -63,7 +52,6 @@
}
DexEncodedMethod build() {
- assert name != null;
boolean isCompilerSynthesized = true;
DexMethod methodSignature = getMethodSignature();
DexEncodedMethod method =
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodDefinition.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodDefinition.java
index 0ba1127..ecc1a81 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodDefinition.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodDefinition.java
@@ -3,24 +3,27 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.synthesis;
+import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.structural.RepresentativeMap;
+import com.google.common.hash.HashCode;
import com.google.common.hash.Hasher;
+import com.google.common.hash.Hashing;
+import java.util.Comparator;
/**
* Definition of a synthetic method item.
*
* <p>This class is internal to the synthetic items collection, thus package-protected.
*/
-class SyntheticMethodDefinition
- extends SyntheticDefinition<SyntheticMethodReference, SyntheticMethodDefinition> {
+class SyntheticMethodDefinition extends SyntheticDefinition
+ implements Comparable<SyntheticMethodDefinition> {
private final ProgramMethod method;
- SyntheticMethodDefinition(SyntheticKind kind, SynthesizingContext context, ProgramMethod method) {
- super(kind, context);
+ SyntheticMethodDefinition(SynthesizingContext context, ProgramMethod method) {
+ super(context);
this.method = method;
}
@@ -29,8 +32,8 @@
}
@Override
- SyntheticMethodReference toReference() {
- return new SyntheticMethodReference(getKind(), getContext(), method.getReference());
+ SyntheticReference toReference() {
+ return new SyntheticMethodReference(getContext(), method.getReference());
}
@Override
@@ -39,18 +42,39 @@
}
@Override
- void internalComputeHash(Hasher hasher, RepresentativeMap map) {
- method.getDefinition().hashWithTypeEquivalence(hasher, map);
+ HashCode computeHash(RepresentativeMap map, boolean intermediate) {
+ Hasher hasher = Hashing.sha256().newHasher();
+ if (intermediate) {
+ // If in intermediate mode, include the context type as sharing is restricted to within a
+ // single context.
+ hasher.putInt(getContext().getSynthesizingContextType().hashCode());
+ }
+ method.getDefinition().hashSyntheticContent(hasher, map);
+ return hasher.hash();
}
@Override
- int internalCompareTo(SyntheticMethodDefinition other, RepresentativeMap map) {
- return method.getDefinition().compareWithTypeEquivalenceTo(other.method.getDefinition(), map);
+ boolean isEquivalentTo(SyntheticDefinition other, boolean intermediate) {
+ if (!(other instanceof SyntheticMethodDefinition)) {
+ return false;
+ }
+ if (intermediate
+ && getContext().getSynthesizingContextType()
+ != other.getContext().getSynthesizingContextType()) {
+ // If in intermediate mode, only synthetics within the same context should be considered
+ // equal.
+ return false;
+ }
+ SyntheticMethodDefinition o = (SyntheticMethodDefinition) other;
+ return method.getDefinition().isSyntheticContentEqual(o.method.getDefinition());
}
+ // Since methods are sharable they must define an order from which representatives can be found.
@Override
- public boolean isValid() {
- return SyntheticMethodBuilder.isValidSyntheticMethod(method.getDefinition());
+ public int compareTo(SyntheticMethodDefinition other) {
+ return Comparator.comparing(SyntheticMethodDefinition::getContext)
+ .thenComparing(m -> m.method.getDefinition(), DexEncodedMethod::syntheticCompareTo)
+ .compare(this, other);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java
index cade86e..28913d0 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodReference.java
@@ -5,11 +5,10 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
-import java.util.function.Consumer;
import java.util.function.Function;
/**
@@ -17,35 +16,37 @@
*
* <p>This class is internal to the synthetic items collection, thus package-protected.
*/
-class SyntheticMethodReference
- extends SyntheticReference<SyntheticMethodReference, SyntheticMethodDefinition> {
+class SyntheticMethodReference extends SyntheticReference {
final DexMethod method;
- SyntheticMethodReference(SyntheticKind kind, SynthesizingContext context, DexMethod method) {
- super(kind, context);
+ SyntheticMethodReference(SynthesizingContext context, DexMethod method) {
+ super(context);
this.method = method;
}
@Override
+ DexReference getReference() {
+ return method;
+ }
+
+ @Override
DexType getHolder() {
return method.holder;
}
@Override
- SyntheticMethodDefinition lookupDefinition(Function<DexType, DexClass> definitions) {
+ SyntheticDefinition lookupDefinition(Function<DexType, DexClass> definitions) {
DexClass clazz = definitions.apply(method.holder);
if (clazz == null) {
return null;
}
assert clazz.isProgramClass();
ProgramMethod definition = clazz.asProgramClass().lookupProgramMethod(method);
- return definition != null
- ? new SyntheticMethodDefinition(getKind(), getContext(), definition)
- : null;
+ return definition != null ? new SyntheticMethodDefinition(getContext(), definition) : null;
}
@Override
- SyntheticMethodReference rewrite(NonIdentityGraphLens lens) {
+ SyntheticReference rewrite(NonIdentityGraphLens lens) {
DexMethod rewritten = lens.lookupMethod(method);
// If the reference has been non-trivially rewritten the compiler has changed it and it can no
// longer be considered a synthetic. The context may or may not have changed.
@@ -53,28 +54,12 @@
// If the referenced item is rewritten, it should be moved to another holder as the
// synthetic holder is no longer part of the synthetic collection.
assert method.holder != rewritten.holder;
- assert SyntheticNaming.verifyNotInternalSynthetic(rewritten.holder);
+ assert SyntheticItems.verifyNotInternalSynthetic(rewritten.holder);
return null;
}
SynthesizingContext context = getContext().rewrite(lens);
- if (context == getContext() && rewritten == method) {
- return this;
- }
- // Ensure that if a synthetic moves its context moves consistently.
- if (method != rewritten) {
- context =
- SynthesizingContext.fromSyntheticContextChange(
- rewritten.holder, context, lens.dexItemFactory());
- if (context == null) {
- return null;
- }
- }
- return new SyntheticMethodReference(getKind(), context, rewritten);
- }
-
- @Override
- void apply(
- Consumer<SyntheticMethodReference> onMethod, Consumer<SyntheticClassReference> onClass) {
- onMethod.accept(this);
+ return context == getContext() && rewritten == method
+ ? this
+ : new SyntheticMethodReference(context, rewritten);
}
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
deleted file mode 100644
index 9947f15..0000000
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ /dev/null
@@ -1,163 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.synthesis;
-
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.utils.DescriptorUtils;
-
-public class SyntheticNaming {
-
- /**
- * Enumeration of all kinds of synthetic items.
- *
- * <p>The synthetic kinds are used to provide hinting about what a synthetic item represents. The
- * kinds must *not* be used be the compiler and are only meant for "debugging". The compiler and
- * its test may use the kind information as part of asserting properties of the compiler. The kind
- * will be put into any non-minified synthetic name and thus the kind "descriptor" must be a
- * distinct for each kind.
- */
- public enum SyntheticKind {
- // Class synthetics.
- LAMBDA("Lambda", false),
- // Method synthetics.
- BACKPORT("Backport", true),
- STATIC_INTERFACE_CALL("StaticInterfaceCall", true),
- TO_STRING_IF_NOT_NULL("ToStringIfNotNull", true),
- THROW_CCE_IF_NOT_NULL("ThrowCCEIfNotNull", true),
- TWR_CLOSE_RESOURCE("TwrCloseResource", true),
- SERVICE_LOADER("ServiceLoad", true);
-
- public final String descriptor;
- public final boolean isSingleSyntheticMethod;
-
- SyntheticKind(String descriptor, boolean isSingleSyntheticMethod) {
- this.descriptor = descriptor;
- this.isSingleSyntheticMethod = isSingleSyntheticMethod;
- }
-
- public static SyntheticKind fromDescriptor(String descriptor) {
- for (SyntheticKind kind : values()) {
- if (kind.descriptor.equals(descriptor)) {
- return kind;
- }
- }
- return null;
- }
- }
-
- /**
- * The internal synthetic class separator is only used for representing synthetic items during
- * compilation. In particular, this separator must never be used to write synthetic classes to the
- * final compilation result.
- */
- private static final String INTERNAL_SYNTHETIC_CLASS_SEPARATOR = "-$$InternalSynthetic";
- /**
- * The external synthetic class separator is used when writing classes. It may appear in types
- * during compilation as the output of a compilation may be the input to another.
- */
- private static final String EXTERNAL_SYNTHETIC_CLASS_SEPARATOR = "-$$ExternalSynthetic";
- /** Method prefix when generating synthetic methods in a class. */
- static final String INTERNAL_SYNTHETIC_METHOD_PREFIX = "m";
-
- // TODO(b/158159959): Remove usage of name-based identification.
- public static boolean isSyntheticName(String typeName) {
- return typeName.contains(INTERNAL_SYNTHETIC_CLASS_SEPARATOR)
- || typeName.contains(EXTERNAL_SYNTHETIC_CLASS_SEPARATOR);
- }
-
- static DexType createInternalType(
- SyntheticKind kind, SynthesizingContext context, String id, DexItemFactory factory) {
- return createType(
- INTERNAL_SYNTHETIC_CLASS_SEPARATOR,
- kind,
- context.getSynthesizingContextType(),
- id,
- factory);
- }
-
- static DexType createExternalType(
- SyntheticKind kind, DexType context, String id, DexItemFactory factory) {
- return createType(EXTERNAL_SYNTHETIC_CLASS_SEPARATOR, kind, context, id, factory);
- }
-
- private static DexType createType(
- String separator, SyntheticKind kind, DexType context, String id, DexItemFactory factory) {
- return factory.createType(createDescriptor(separator, kind, context.getInternalName(), id));
- }
-
- private static String createDescriptor(
- String separator, SyntheticKind kind, String context, String id) {
- return DescriptorUtils.getDescriptorFromClassBinaryName(
- context + separator + kind.descriptor + id);
- }
-
- public static boolean verifyNotInternalSynthetic(DexType type) {
- return verifyNotInternalSynthetic(type.toDescriptorString());
- }
-
- public static boolean verifyNotInternalSynthetic(ClassReference reference) {
- return verifyNotInternalSynthetic(reference.getDescriptor());
- }
-
- public static boolean verifyNotInternalSynthetic(String typeBinaryNameOrDescriptor) {
- assert !typeBinaryNameOrDescriptor.contains(INTERNAL_SYNTHETIC_CLASS_SEPARATOR);
- return true;
- }
-
- // Visible via package protection in SyntheticItemsTestUtils.
-
- enum Phase {
- INTERNAL,
- EXTERNAL
- }
-
- static String getPhaseSeparator(Phase phase) {
- assert phase != null;
- return phase == Phase.INTERNAL
- ? INTERNAL_SYNTHETIC_CLASS_SEPARATOR
- : EXTERNAL_SYNTHETIC_CLASS_SEPARATOR;
- }
-
- static ClassReference makeSyntheticReferenceForTest(
- ClassReference context, SyntheticKind kind, String id) {
- return Reference.classFromDescriptor(
- createDescriptor(EXTERNAL_SYNTHETIC_CLASS_SEPARATOR, kind, context.getBinaryName(), id));
- }
-
- static boolean isSynthetic(ClassReference clazz, Phase phase, SyntheticKind kind) {
- String typeName = clazz.getTypeName();
- String separator = getPhaseSeparator(phase);
- int i = typeName.indexOf(separator);
- return i >= 0 && checkMatchFrom(kind, typeName, i, separator);
- }
-
- private static boolean checkMatchFrom(
- SyntheticKind kind, String name, int i, String externalSyntheticClassSeparator) {
- int end = i + externalSyntheticClassSeparator.length() + kind.descriptor.length();
- if (end >= name.length()) {
- return false;
- }
- String prefix = name.substring(i, end);
- return prefix.equals(externalSyntheticClassSeparator + kind.descriptor)
- && isInt(name.substring(end));
- }
-
- private static boolean isInt(String str) {
- if (str.isEmpty()) {
- return false;
- }
- if ('0' == str.charAt(0)) {
- return str.length() == 1;
- }
- for (int i = 0; i < str.length(); i++) {
- if (!Character.isDigit(str.charAt(i))) {
- return false;
- }
- }
- return true;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticReference.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticReference.java
index ea33564..6618378 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticReference.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticReference.java
@@ -4,10 +4,9 @@
package com.android.tools.r8.synthesis;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens;
-import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
-import java.util.function.Consumer;
import java.util.function.Function;
/**
@@ -15,24 +14,16 @@
*
* <p>This class is internal to the synthetic items collection, thus package-protected.
*/
-abstract class SyntheticReference<
- R extends SyntheticReference<R, D>, D extends SyntheticDefinition<R, D>> {
-
- private final SyntheticKind kind;
+abstract class SyntheticReference {
private final SynthesizingContext context;
- SyntheticReference(SyntheticKind kind, SynthesizingContext context) {
- assert kind != null;
- assert context != null;
- this.kind = kind;
+ SyntheticReference(SynthesizingContext context) {
this.context = context;
}
- abstract D lookupDefinition(Function<DexType, DexClass> definitions);
+ abstract SyntheticDefinition lookupDefinition(Function<DexType, DexClass> definitions);
- final SyntheticKind getKind() {
- return kind;
- }
+ abstract DexReference getReference();
final SynthesizingContext getContext() {
return context;
@@ -40,8 +31,5 @@
abstract DexType getHolder();
- abstract R rewrite(NonIdentityGraphLens lens);
-
- abstract void apply(
- Consumer<SyntheticMethodReference> onMethod, Consumer<SyntheticClassReference> onClass);
+ abstract SyntheticReference rewrite(NonIdentityGraphLens lens);
}
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 d0e0a52..db914a4 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1280,7 +1280,6 @@
public boolean enable = true;
public boolean enableConstructorMerging = true;
- // TODO(b/174809311): Update or remove the option and its tests after new lambdas synthetics.
public boolean enableJavaLambdaMerging = false;
public boolean enableKotlinLambdaMerging = true;
@@ -1474,8 +1473,6 @@
public boolean allowInvalidCfAccessFlags =
System.getProperty("com.android.tools.r8.allowInvalidCfAccessFlags") != null;
- public boolean allowConflictingSyntheticTypes = false;
-
// Flag to allow processing of resources in D8. A data resource consumer still needs to be
// specified.
public boolean enableD8ResourcesPassThrough = false;
@@ -1606,16 +1603,6 @@
return !isDesugaring() || hasMinApi(AndroidApiLevel.K);
}
- public boolean enableTryWithResourcesDesugaring() {
- switch (tryWithResourcesDesugaring) {
- case Off:
- return false;
- case Auto:
- return !canUseSuppressedExceptions();
- }
- throw new Unreachable();
- }
-
public boolean canUsePrivateInterfaceMethods() {
return !isDesugaring() || hasMinApi(AndroidApiLevel.N);
}
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index fe6feab..40ae9ce 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -40,14 +40,6 @@
return -1;
}
- public static <S, T> List<T> map(Iterable<S> list, Function<S, T> fn) {
- List<T> result = new ArrayList<>();
- for (S element : list) {
- result.add(fn.apply(element));
- }
- return result;
- }
-
public static <S, T> List<T> map(Collection<S> list, Function<S, T> fn) {
List<T> result = new ArrayList<>(list.size());
for (S element : list) {
diff --git a/src/main/java/com/android/tools/r8/utils/Pair.java b/src/main/java/com/android/tools/r8/utils/Pair.java
index c2f3489..ac58f56 100644
--- a/src/main/java/com/android/tools/r8/utils/Pair.java
+++ b/src/main/java/com/android/tools/r8/utils/Pair.java
@@ -50,9 +50,4 @@
public boolean equals(Object obj) {
throw new Unreachable("Pair does not want to support equality!");
}
-
- @Override
- public String toString() {
- return "Pair{" + first + ", " + second + '}';
- }
}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java
index c721599..234732c 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java
@@ -18,39 +18,29 @@
/** Base class to share most visiting methods */
public abstract class CompareToVisitorBase extends CompareToVisitor {
- private static boolean DEBUG = false;
-
- // Helper to debug insert a breakpoint on order values.
- public static int debug(int order) {
- if (DEBUG && order != 0) {
- return order;
- }
- return order;
- }
-
@Override
public final int visitBool(boolean value1, boolean value2) {
- return debug(Boolean.compare(value1, value2));
+ return Boolean.compare(value1, value2);
}
@Override
public final int visitInt(int value1, int value2) {
- return debug(Integer.compare(value1, value2));
+ return Integer.compare(value1, value2);
}
@Override
public int visitLong(long value1, long value2) {
- return debug(Long.compare(value1, value2));
+ return Long.compare(value1, value2);
}
@Override
public int visitFloat(float value1, float value2) {
- return debug(Float.compare(value1, value2));
+ return Float.compare(value1, value2);
}
@Override
public int visitDouble(double value1, double value2) {
- return debug(Double.compare(value1, value2));
+ return Double.compare(value1, value2);
}
@Override
@@ -63,12 +53,12 @@
if (order == 0) {
order = visitBool(it1.hasNext(), it2.hasNext());
}
- return debug(order);
+ return order;
}
@Override
public int visitDexString(DexString string1, DexString string2) {
- return debug(string1.compareTo(string2));
+ return string1.compareTo(string2);
}
@Override
@@ -84,19 +74,19 @@
order = visitDexMethod(reference1.asDexMethod(), reference2.asDexMethod());
}
}
- return debug(order);
+ return order;
}
@Override
public final <S> int visit(S item1, S item2, Comparator<S> comparator) {
- return debug(comparator.compare(item1, item2));
+ return comparator.compare(item1, item2);
}
@Override
public final <S> int visit(S item1, S item2, StructuralMapping<S> accept) {
ItemSpecification<S> itemVisitor = new ItemSpecification<>(item1, item2, this);
accept.apply(itemVisitor);
- return debug(itemVisitor.order);
+ return itemVisitor.order;
}
private static class ItemSpecification<T>
@@ -208,7 +198,7 @@
}
@Override
- protected <S> ItemSpecification<T> withCustomItemIterator(
+ protected <S> ItemSpecification<T> withItemIterator(
Function<T, Iterator<S>> getter, CompareToAccept<S> compare, HashingAccept<S> hasher) {
if (order == 0) {
order = parent.visitItemIterator(getter.apply(item1), getter.apply(item2), compare);
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithNamingLens.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithNamingLens.java
index 7569929..f581065 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithNamingLens.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithNamingLens.java
@@ -29,35 +29,34 @@
@Override
public int visitDexType(DexType type1, DexType type2) {
- return debug(
- namingLens
- .lookupDescriptor(type1)
- .acceptCompareTo(namingLens.lookupDescriptor(type2), this));
+ return namingLens
+ .lookupDescriptor(type1)
+ .acceptCompareTo(namingLens.lookupDescriptor(type2), this);
}
@Override
public int visitDexField(DexField field1, DexField field2) {
int order = field1.holder.acceptCompareTo(field2.holder, this);
if (order != 0) {
- return debug(order);
+ return order;
}
order = namingLens.lookupName(field1).acceptCompareTo(namingLens.lookupName(field2), this);
if (order != 0) {
- return debug(order);
+ return order;
}
- return debug(field1.type.acceptCompareTo(field2.type, this));
+ return field1.type.acceptCompareTo(field2.type, this);
}
@Override
public int visitDexMethod(DexMethod method1, DexMethod method2) {
int order = method1.holder.acceptCompareTo(method2.holder, this);
if (order != 0) {
- return debug(order);
+ return order;
}
order = namingLens.lookupName(method1).acceptCompareTo(namingLens.lookupName(method2), this);
if (order != 0) {
- return debug(order);
+ return order;
}
- return debug(method1.proto.acceptCompareTo(method2.proto, this));
+ return method1.proto.acceptCompareTo(method2.proto, this);
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeEquivalence.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeEquivalence.java
index ccb5c9e..89bd02b 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeEquivalence.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorWithTypeEquivalence.java
@@ -28,6 +28,6 @@
public int visitDexType(DexType type1, DexType type2) {
DexType repr1 = representatives.getRepresentative(type1);
DexType repr2 = representatives.getRepresentative(type2);
- return debug(repr1.getDescriptor().acceptCompareTo(repr2.getDescriptor(), this));
+ return repr1.getDescriptor().acceptCompareTo(repr2.getDescriptor(), this);
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java b/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java
index 244d404..892183d 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java
@@ -105,7 +105,7 @@
}
@Override
- protected <S> HashCodeVisitor<T> withCustomItemIterator(
+ protected <S> HashCodeVisitor<T> withItemIterator(
Function<T, Iterator<S>> getter, CompareToAccept<S> compare, HashingAccept<S> hasher) {
Iterator<S> it = getter.apply(item);
while (it.hasNext()) {
diff --git a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java
index 6c388fb..e63ac6c 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java
@@ -170,7 +170,7 @@
}
@Override
- protected <S> ItemSpecification<T> withCustomItemIterator(
+ protected <S> ItemSpecification<T> withItemIterator(
Function<T, Iterator<S>> getter, CompareToAccept<S> compare, HashingAccept<S> hasher) {
parent.visitItemIterator(getter.apply(item), hasher);
return this;
diff --git a/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java b/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java
index 69d8796..a81fc18 100644
--- a/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java
+++ b/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java
@@ -51,12 +51,12 @@
HashingAccept<S> hasher);
/** Base implementation for visiting an enumeration of items. */
- protected abstract <S> V withCustomItemIterator(
+ protected abstract <S> V withItemIterator(
Function<T, Iterator<S>> getter, CompareToAccept<S> compare, HashingAccept<S> hasher);
public final <S> V withCustomItemCollection(
Function<T, Collection<S>> getter, StructuralAcceptor<S> acceptor) {
- return withCustomItemIterator(getter.andThen(Collection::iterator), acceptor, acceptor);
+ return withItemIterator(getter.andThen(Collection::iterator), acceptor, acceptor);
}
/**
@@ -79,23 +79,24 @@
predicate, getter, StructuralItem::acceptCompareTo, StructuralItem::acceptHashing);
}
- public final <S extends StructuralItem<S>> V withItemIterator(Function<T, Iterator<S>> getter) {
- return withCustomItemIterator(
- getter, StructuralItem::acceptCompareTo, StructuralItem::acceptHashing);
- }
-
public final <S extends StructuralItem<S>> V withItemCollection(
Function<T, Collection<S>> getter) {
- return withItemIterator(getter.andThen(Collection::iterator));
+ return withItemIterator(
+ getter.andThen(Collection::iterator),
+ StructuralItem::acceptCompareTo,
+ StructuralItem::acceptHashing);
}
public final <S extends StructuralItem<S>> V withItemArray(Function<T, S[]> getter) {
- return withItemIterator(getter.andThen(a -> Arrays.asList(a).iterator()));
+ return withItemIterator(
+ getter.andThen(a -> Arrays.asList(a).iterator()),
+ StructuralItem::acceptCompareTo,
+ StructuralItem::acceptHashing);
}
public final <S extends StructuralItem<S>> V withItemArrayAllowingNullMembers(
Function<T, S[]> getter) {
- return withCustomItemIterator(
+ return withItemIterator(
getter.andThen(a -> Arrays.asList(a).iterator()),
(a, b, visitor) -> {
if (a == null || b == null) {
diff --git a/src/test/examplesAndroidO/multidex004/ref-list-1.txt b/src/test/examplesAndroidO/multidex004/ref-list-1.txt
index c817c33..ac6e888 100644
--- a/src/test/examplesAndroidO/multidex004/ref-list-1.txt
+++ b/src/test/examplesAndroidO/multidex004/ref-list-1.txt
@@ -1,4 +1,4 @@
-Lmultidex004/MainActivity-$$ExternalSyntheticLambda0;
+Lmultidex004/-$$Lambda$MainActivity$g120D_43GXrTOaB3kfYt6wSIJh4;
Lmultidex004/MainActivity;
Lmultidex004/VersionInterface;
Lmultidex004/VersionStatic;
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
index 82cfebf..d25c465 100644
--- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -13,9 +13,9 @@
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.ir.desugar.LambdaRewriter;
+import com.android.tools.r8.ir.desugar.TwrCloseResourceRewriter;
+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.DescriptorUtils;
@@ -93,12 +93,11 @@
for (String descriptor : descriptors) {
// classes are either lambda classes used by the main class, companion classes of the main
// interface, the main class/interface, or for JDK9, desugaring of try-with-resources.
- ClassReference reference = Reference.classFromDescriptor(descriptor);
Assert.assertTrue(
- descriptor.endsWith(InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX + ";")
- || SyntheticItemsTestUtils.isExternalTwrCloseMethod(reference)
- || SyntheticItemsTestUtils.isExternalLambda(reference)
- || SyntheticItemsTestUtils.isExternalStaticInterfaceCall(reference)
+ descriptor.contains(LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX)
+ || descriptor.endsWith(InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX + ";")
+ || descriptor.equals(TwrCloseResourceRewriter.UTILITY_CLASS_DESCRIPTOR)
+ || descriptor.contains(SyntheticItems.EXTERNAL_SYNTHETIC_CLASS_SEPARATOR)
|| descriptor.equals(mainClassDescriptor));
}
String classDescriptor =
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
index 389b0fa..7157943 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -107,14 +107,14 @@
.withOptionConsumer(opts -> opts.enableClassInlining = false)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 102, "lambdadesugaring"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 101, "lambdadesugaring"))
.run();
test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
.withMinApiLevel(ToolHelper.getMinApiLevelForDexVmNoHigherThan(AndroidApiLevel.K))
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 7, "lambdadesugaring"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 6, "lambdadesugaring"))
.run();
}
@@ -146,14 +146,14 @@
.withOptionConsumer(opts -> opts.enableClassInlining = false)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 102, "lambdadesugaring"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 101, "lambdadesugaring"))
.run();
test("lambdadesugaring", "lambdadesugaring", "LambdaDesugaring")
.withMinApiLevel(AndroidApiLevel.N)
.withBuilderTransformation(
b -> b.addProguardConfiguration(PROGUARD_OPTIONS, Origin.unknown()))
- .withDexCheck(inspector -> checkLambdaCount(inspector, 7, "lambdadesugaring"))
+ .withDexCheck(inspector -> checkLambdaCount(inspector, 6, "lambdadesugaring"))
.run();
}
diff --git a/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java b/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
index 53bea05..9f20f9e 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
@@ -32,7 +32,6 @@
import java.util.function.Consumer;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -270,7 +269,6 @@
}
@Test
- @Ignore("b/177532008: Re-enable once fixed.")
public void testTwrCloseResourceMethod() throws Throwable {
TestRunner<?> test = test("twr-close-resource", "twrcloseresource", "TwrCloseResourceTest");
test
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index bb69054..e1d4c9e 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -1702,10 +1702,6 @@
return AndroidApiLevel.L;
}
- public static AndroidApiLevel apiLevelWithTwrCloseResourceSupport() {
- return AndroidApiLevel.K;
- }
-
public static boolean canUseJavaUtilObjects(TestParameters parameters) {
return parameters.isCfRuntime()
|| parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.K);
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/JavaLambdaMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/JavaLambdaMergingTest.java
index 63c1f18..adabb3e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/JavaLambdaMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/JavaLambdaMergingTest.java
@@ -4,18 +4,16 @@
package com.android.tools.r8.classmerging.horizontal;
+import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
import java.util.Set;
import java.util.stream.Collectors;
-import org.junit.Ignore;
import org.junit.Test;
public class JavaLambdaMergingTest extends HorizontalClassMergingTestBase {
@@ -25,7 +23,6 @@
}
@Test
- @Ignore("b/174809311): Test does not work with hygienic lambdas. Rewrite or remove")
public void test() throws Exception {
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
@@ -41,12 +38,16 @@
inspector -> {
Set<DexType> lambdaSources =
inspector.getSources().stream()
- .filter(JavaLambdaMergingTest::isLambda)
+ .filter(x -> x.toSourceString().contains(LAMBDA_CLASS_NAME_PREFIX))
.collect(Collectors.toSet());
assertEquals(3, lambdaSources.size());
DexType firstTarget = inspector.getTarget(lambdaSources.iterator().next());
for (DexType lambdaSource : lambdaSources) {
- assertTrue(isLambda(inspector.getTarget(lambdaSource)));
+ assertTrue(
+ inspector
+ .getTarget(lambdaSource)
+ .toSourceString()
+ .contains(LAMBDA_CLASS_NAME_PREFIX));
assertEquals(firstTarget, inspector.getTarget(lambdaSource));
}
})
@@ -58,11 +59,6 @@
.assertSuccessWithOutputLines("Hello world!");
}
- private static boolean isLambda(DexType type) {
- return SyntheticItemsTestUtils.isExternalLambda(
- Reference.classFromDescriptor(type.toDescriptorString()));
- }
-
public static class Main {
public static void main(String[] args) {
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index a5a1a8a..13d59dc 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.naming.ClassNameMapper;
import com.android.tools.r8.naming.ClassNamingForNameMapper;
import com.android.tools.r8.naming.ClassNamingForNameMapper.MappedRange;
@@ -2162,28 +2161,8 @@
}
private static boolean isInLambdaClass(VmMirror mirror, Location location) {
- // TODO(b/174809573): These "lambda" specific methods are highly questionable.
- // Determine the exact filtering behavior of intellij (which is very likely not name
- // based) and update this filter accordingly.
- CommandPacket cmd =
- new CommandPacket(
- ReferenceTypeCommandSet.CommandSetID, ReferenceTypeCommandSet.ModifiersCommand);
- cmd.setNextValueAsReferenceTypeID(location.classID);
- ReplyPacket reply = mirror.performCommand(cmd);
- mirror.checkReply(reply);
- int modifiers = reply.getNextValueAsInt();
- ClassAccessFlags flags = ClassAccessFlags.fromCfAccessFlags(modifiers);
- if (!flags.isSynthetic()) {
- return false;
- }
- String signature = mirror.getClassSignature(location.classID);
- if (signature.contains("-CC")) {
- // TODO(b/174809573): The need to return false here indicates a questionable test
- // expectation. Either the test is incorrect or there is a bug in our generation of
- // -CC classes marked as synthetic as that would lead to unwanted debugger filtering.
- return false;
- }
- return true;
+ String classSig = mirror.getClassSignature(location.classID);
+ return classSig.contains("$$Lambda$");
}
private static boolean isLambdaMethod(VmMirror mirror, Location location) {
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeLambdaTest.java b/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeLambdaTest.java
index 875ead2..8f304c6 100644
--- a/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarMissingTypeLambdaTest.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.desugar;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -17,7 +19,6 @@
import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic;
import com.android.tools.r8.position.Position;
import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import org.junit.Test;
@@ -78,7 +79,7 @@
assertEquals(
Reference.classFromClass(MissingInterface.class), desugarWarning.getMissingType());
// TODO(b/132671303): The context class should not be the synthesized lambda class.
- assertTrue(SyntheticItemsTestUtils.isInternalLambda(desugarWarning.getContextType()));
+ assertThat(desugarWarning.getContextType().getDescriptor(), containsString("$$Lambda"));
// TODO(b/132671303): The position info should be the method context.
assertEquals(Position.UNKNOWN, desugarWarning.getPosition());
}
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarToClassFile.java b/src/test/java/com/android/tools/r8/desugar/DesugarToClassFile.java
index dc6e894..c9377fe 100644
--- a/src/test/java/com/android/tools/r8/desugar/DesugarToClassFile.java
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarToClassFile.java
@@ -48,7 +48,7 @@
private void checkHasLambdaClass(CodeInspector inspector) {
assertTrue(
inspector.allClasses().stream()
- .anyMatch(subject -> subject.isSynthesizedJavaLambdaClass()));
+ .anyMatch(subject -> subject.getOriginalName().contains("-$$Lambda$")));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java b/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
index acfc021..d094c78 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/BackportDuplicationTest.java
@@ -3,6 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.desugar.backports;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
@@ -13,10 +16,10 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.desugar.backports.AbstractBackportTest.MiniAssert;
import com.android.tools.r8.references.MethodReference;
-import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
-import com.android.tools.r8.synthesis.SyntheticNaming;
+import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.google.common.collect.ImmutableList;
@@ -197,7 +200,9 @@
private void checkNoOriginalsAndNoInternalSynthetics(CodeInspector inspector) {
inspector.forAllClasses(
clazz -> {
- SyntheticNaming.verifyNotInternalSynthetic(clazz.getFinalReference());
+ assertThat(
+ clazz.getFinalName(),
+ not(containsString(SyntheticItems.INTERNAL_SYNTHETIC_CLASS_SEPARATOR)));
if (!clazz.getOriginalName().equals(MiniAssert.class.getTypeName())) {
clazz.forAllMethods(
method ->
@@ -228,11 +233,11 @@
// of intermediates.
Set<MethodReference> expectedSynthetics =
ImmutableSet.of(
- SyntheticItemsTestUtils.syntheticBackportMethod(
+ SyntheticItemsTestUtils.syntheticMethod(
User1.class, 0, Character.class.getMethod("compare", char.class, char.class)),
- SyntheticItemsTestUtils.syntheticBackportMethod(
+ SyntheticItemsTestUtils.syntheticMethod(
User1.class, 1, Boolean.class.getMethod("compare", boolean.class, boolean.class)),
- SyntheticItemsTestUtils.syntheticBackportMethod(
+ SyntheticItemsTestUtils.syntheticMethod(
User2.class, 0, Integer.class.getMethod("compare", int.class, int.class)));
assertEquals(expectedSynthetics, getSyntheticMethods(inspector));
}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/BackportMainDexTest.java b/src/test/java/com/android/tools/r8/desugar/backports/BackportMainDexTest.java
index 05f84af..45f8957 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/BackportMainDexTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/BackportMainDexTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.desugar.backports;
+import static com.android.tools.r8.synthesis.SyntheticItems.EXTERNAL_SYNTHETIC_CLASS_SEPARATOR;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
@@ -24,11 +25,11 @@
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -56,6 +57,11 @@
static final List<Class<?>> MAIN_DEX_LIST_CLASSES =
ImmutableList.of(MiniAssert.class, TestClass.class, User2.class);
+ static final String SyntheticUnderUser1 =
+ User1.class.getTypeName() + EXTERNAL_SYNTHETIC_CLASS_SEPARATOR;
+ static final String SyntheticUnderUser2 =
+ User2.class.getTypeName() + EXTERNAL_SYNTHETIC_CLASS_SEPARATOR;
+
private final TestParameters parameters;
@Parameterized.Parameters(name = "{0}")
@@ -335,16 +341,16 @@
private ImmutableSet<MethodReference> getNonMainDexExpectedSynthetics()
throws NoSuchMethodException {
return ImmutableSet.of(
- SyntheticItemsTestUtils.syntheticBackportMethod(
+ SyntheticItemsTestUtils.syntheticMethod(
User1.class, 1, Boolean.class.getMethod("compare", boolean.class, boolean.class)));
}
private ImmutableSet<MethodReference> getMainDexExpectedSynthetics()
throws NoSuchMethodException {
return ImmutableSet.of(
- SyntheticItemsTestUtils.syntheticBackportMethod(
+ SyntheticItemsTestUtils.syntheticMethod(
User1.class, 0, Character.class.getMethod("compare", char.class, char.class)),
- SyntheticItemsTestUtils.syntheticBackportMethod(
+ SyntheticItemsTestUtils.syntheticMethod(
User2.class, 0, Integer.class.getMethod("compare", int.class, int.class)));
}
diff --git a/src/test/java/com/android/tools/r8/desugar/bridge/LambdaReturnTypeBridgeTest.java b/src/test/java/com/android/tools/r8/desugar/bridge/LambdaReturnTypeBridgeTest.java
index fdb1044..2dbcc30 100644
--- a/src/test/java/com/android/tools/r8/desugar/bridge/LambdaReturnTypeBridgeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/bridge/LambdaReturnTypeBridgeTest.java
@@ -62,7 +62,12 @@
codeInspector -> {
boolean foundBridge = false;
for (FoundClassSubject clazz : codeInspector.allClasses()) {
- if (clazz.isSynthesizedJavaLambdaClass()) {
+ if (clazz
+ .getOriginalName()
+ .contains(
+ "-$$Lambda$"
+ + LambdaWithMultipleImplementingInterfaces.class.getSimpleName()
+ + "$")) {
// Find bridge method and check whether or not it has a cast.
for (FoundMethodSubject bridge : clazz.allMethods(FoundMethodSubject::isBridge)) {
foundBridge = true;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryDeterminismTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryDeterminismTest.java
index bfb1885..f7b5e5d 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryDeterminismTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryDeterminismTest.java
@@ -17,16 +17,11 @@
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import com.google.common.collect.Sets;
-import com.google.common.collect.Sets.SetView;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -58,29 +53,12 @@
private void assertIdenticalInspectors(Path libDexFile1, Path libDexFile2) throws IOException {
CodeInspector i1 = new CodeInspector(libDexFile1.resolve("classes.dex"));
CodeInspector i2 = new CodeInspector(libDexFile2.resolve("classes.dex"));
- assertIdenticalInspectors(i1, i2);
- }
-
- public static void assertIdenticalInspectors(CodeInspector i1, CodeInspector i2) {
assertEquals(i1.allClasses().size(), i2.allClasses().size());
Map<DexEncodedMethod, DexEncodedMethod> diffs = new IdentityHashMap<>();
for (FoundClassSubject clazz1 : i1.allClasses()) {
ClassSubject clazz = i2.clazz(clazz1.getOriginalName());
assertTrue(clazz.isPresent());
FoundClassSubject clazz2 = clazz.asFoundClassSubject();
- Set<String> methods1 =
- clazz1.allMethods().stream().map(m -> m.toString()).collect(Collectors.toSet());
- Set<String> methods2 =
- clazz2.allMethods().stream().map(m -> m.toString()).collect(Collectors.toSet());
- SetView<String> union = Sets.union(methods1, methods2);
- assertEquals(
- "Inspector 1 contains more methods",
- Collections.emptySet(),
- Sets.difference(union, methods1));
- assertEquals(
- "Inspector 2 contains more methods",
- Collections.emptySet(),
- Sets.difference(union, methods2));
assertEquals(clazz1.allMethods().size(), clazz2.allMethods().size());
for (FoundMethodSubject method1 : clazz1.allMethods()) {
MethodSubject method = clazz2.method(method1.asMethodReference());
@@ -101,7 +79,7 @@
assertTrue(printDiffs(diffs), diffs.isEmpty());
}
- private static String printDiffs(Map<DexEncodedMethod, DexEncodedMethod> diffs) {
+ private String printDiffs(Map<DexEncodedMethod, DexEncodedMethod> diffs) {
StringBuilder sb = new StringBuilder();
sb.append("The following methods had differences from one dex file to the other (")
.append(diffs.size())
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/DeduplicateLambdasWithDefaultMethodsTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/DeduplicateLambdasWithDefaultMethodsTest.java
deleted file mode 100644
index b252cf4..0000000
--- a/src/test/java/com/android/tools/r8/desugar/lambdas/DeduplicateLambdasWithDefaultMethodsTest.java
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.desugar.lambdas;
-
-import static org.junit.Assert.assertEquals;
-
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
-import com.google.common.collect.ImmutableSet;
-import java.util.stream.Collectors;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class DeduplicateLambdasWithDefaultMethodsTest extends TestBase {
-
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withNoneRuntime().build();
- }
-
- public DeduplicateLambdasWithDefaultMethodsTest(TestParameters parameters) {
- parameters.assertNoneRuntime();
- }
-
- @Test
- public void test() throws Exception {
- assertEquals(
- ImmutableSet.of(
- Reference.classFromClass(I.class),
- Reference.classFromClass(TestClass.class),
- SyntheticItemsTestUtils.syntheticCompanionClass(I.class),
- SyntheticItemsTestUtils.syntheticLambdaClass(TestClass.class, 0)),
- testForD8(Backend.CF)
- .addInnerClasses(getClass())
- .setIntermediate(true)
- .setMinApi(AndroidApiLevel.B)
- .compile()
- .inspector()
- .allClasses()
- .stream()
- .map(FoundClassSubject::getFinalReference)
- .collect(Collectors.toSet()));
- }
-
- interface I {
- void foo();
-
- // Lots of methods which may cause the ordering of methods on a class to change between builds.
- default void a() {
- System.out.print("a");
- }
-
- default void b() {
- System.out.print("b");
- }
-
- default void c() {
- System.out.print("c");
- }
-
- default void x() {
- System.out.print("x");
- }
-
- default void y() {
- System.out.print("y");
- }
-
- default void z() {
- System.out.print("z");
- }
- }
-
- public static class TestClass {
- private static void foo() {
- System.out.println("foo");
- }
-
- private static void pI(I i) {
- i.a();
- i.b();
- i.c();
- i.x();
- i.y();
- i.z();
- i.foo();
- }
-
- public static void main(String[] args) {
- // Duplication of the same lambda, each of which should become a shared instance.
- pI(TestClass::foo);
- pI(TestClass::foo);
- pI(TestClass::foo);
- pI(TestClass::foo);
- pI(TestClass::foo);
- pI(TestClass::foo);
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaEqualityTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaEqualityTest.java
deleted file mode 100644
index 90c8009..0000000
--- a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaEqualityTest.java
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.desugar.lambdas;
-
-import static org.junit.Assume.assumeTrue;
-
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.utils.StringUtils;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-/**
- * These tests document the behavior of lambdas w.r.t identity and equality.
- *
- * <p>The D8 and R8 compilers take the stance that a program should not rely on either identity or
- * equality of any lambda metafactory allocated lambda. Thus the status of these tests differ
- * between JVM, D8/CF, D8/DEX and R8 runs as the compilers may or may not share classes and
- * allocations as seen fit.
- */
-@RunWith(Parameterized.class)
-public class LambdaEqualityTest extends TestBase {
-
- static final String EXPECTED_JAVAC =
- StringUtils.lines(
- "Same method refs",
- "true",
- "true",
- "true",
- "Different method refs",
- "false",
- "false",
- "false",
- "Empty lambda",
- "false",
- "false",
- "false");
-
- static final String EXPECTED_D8 =
- StringUtils.lines(
- "Same method refs",
- "true",
- "true",
- "true",
- "Different method refs",
- "true", // D8 will share the class for the method references.
- "false",
- "false",
- "Empty lambda",
- "false",
- "false",
- "false");
-
- static final String EXPECTED_R8 =
- StringUtils.lines(
- "Same method refs",
- "true",
- "true",
- "true",
- "Different method refs",
- "true", // R8 will share the class for the method references.
- "false",
- "false",
- "Empty lambda",
- "true", // R8 will eliminate the call to the impl method thus making lambdas equal.
- "true",
- "true");
-
- private final TestParameters parameters;
-
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
- }
-
- public LambdaEqualityTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
- @Test
- public void testJvm() throws Exception {
- assumeTrue(parameters.isCfRuntime());
- testForRuntime(parameters)
- .addInnerClasses(LambdaEqualityTest.class)
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(EXPECTED_JAVAC);
- }
-
- @Test
- public void testD8() throws Exception {
- testForD8(parameters.getBackend())
- .addInnerClasses(LambdaEqualityTest.class)
- .setMinApi(parameters.getApiLevel())
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(EXPECTED_D8);
- }
-
- @Test
- public void testR8() throws Exception {
- testForR8(parameters.getBackend())
- .addInnerClasses(LambdaEqualityTest.class)
- .setMinApi(parameters.getApiLevel())
- .addKeepMainRule(TestClass.class)
- .addKeepMethodRules(
- Reference.methodFromMethod(
- TestClass.class.getDeclaredMethod(
- "compare", String.class, MyInterface.class, MyInterface.class)))
- .run(parameters.getRuntime(), TestClass.class)
- // The use of invoke dynamics prohibits the optimization and sharing of lambdas in R8.
- .assertSuccessWithOutput(parameters.isCfRuntime() ? EXPECTED_JAVAC : EXPECTED_R8);
- }
-
- interface MyInterface {
- void foo();
- }
-
- static class TestClass {
-
- public static void compare(String msg, MyInterface i1, MyInterface i2) {
- System.out.println(msg);
- System.out.println(i1.getClass() == i2.getClass());
- System.out.println(i1 == i2);
- System.out.println(i1.equals(i2));
- }
-
- public static void main(String[] args) {
- MyInterface println = System.out::println;
- // These lambdas are physically the same and should remain so in all cases.
- compare("Same method refs", println, println);
- // These lambdas can be shared as they reference the same actual function.
- compare("Different method refs", println, System.out::println);
- // These lambdas cannot be shared (by D8) as javac will generate a lambda$main$X for each.
- compare("Empty lambda", () -> {}, () -> {});
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaNamingConflictTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaNamingConflictTest.java
deleted file mode 100644
index 1a6e364..0000000
--- a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaNamingConflictTest.java
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.desugar.lambdas;
-
-import static org.junit.Assume.assumeTrue;
-
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.StringUtils;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class LambdaNamingConflictTest extends TestBase {
-
- static final String EXPECTED = StringUtils.lines("boo!");
-
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters()
- .withAllRuntimes()
- .withApiLevel(AndroidApiLevel.B)
- .enableApiLevelsForCf()
- .build();
- }
-
- // The expected synthetic name is the context of the lambda, TestClass, and the first id.
- private static final ClassReference CONFLICTING_NAME =
- SyntheticItemsTestUtils.syntheticLambdaClass(TestClass.class, 0);
-
- private final TestParameters parameters;
-
- public LambdaNamingConflictTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
- @Test
- public void testJvm() throws Exception {
- assumeTrue(parameters.isCfRuntime());
- testForJvm()
- .addProgramClasses(I.class)
- .addProgramClassFileData(getConflictingNameClass())
- .addProgramClassFileData(getTransformedMainClass())
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(EXPECTED);
- }
-
- @Test
- public void testD8() throws Exception {
- testForD8(parameters.getBackend())
- .addProgramClasses(I.class)
- .addProgramClassFileData(getConflictingNameClass())
- .addProgramClassFileData(getTransformedMainClass())
- .setMinApi(parameters.getApiLevel())
- .addOptionsModification(o -> o.testing.allowConflictingSyntheticTypes = true)
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(EXPECTED);
- }
-
- @Test
- public void testR8() throws Exception {
- testForR8(parameters.getBackend())
- .addProgramClasses(I.class)
- .addProgramClassFileData(getConflictingNameClass())
- .addProgramClassFileData(getTransformedMainClass())
- .setMinApi(parameters.getApiLevel())
- .addOptionsModification(o -> o.testing.allowConflictingSyntheticTypes = true)
- .addKeepMainRule(TestClass.class)
- // Ensure that R8 cannot remove or rename the conflicting name.
- .addKeepClassAndMembersRules(CONFLICTING_NAME.getTypeName())
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(EXPECTED);
- }
-
- private byte[] getTransformedMainClass() throws Exception {
- return transformer(TestClass.class)
- .transformMethodInsnInMethod(
- "main",
- (opcode, owner, name, descriptor, isInterface, visitor) ->
- visitor.visitMethodInsn(
- opcode, CONFLICTING_NAME.getBinaryName(), name, descriptor, isInterface))
- .transform();
- }
-
- private byte[] getConflictingNameClass() throws Exception {
- return transformer(WillBeConflictingName.class)
- .setClassDescriptor(CONFLICTING_NAME.getDescriptor())
- .transform();
- }
-
- interface I {
- void bar();
- }
-
- static class WillBeConflictingName {
- public static void foo(I i) {
- i.bar();
- }
- }
-
- static class TestClass {
-
- public static void main(String[] args) {
- WillBeConflictingName.foo(() -> System.out.println("boo!"));
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaStaticInstanceFieldDuplicationTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaStaticInstanceFieldDuplicationTest.java
deleted file mode 100644
index ac5b076..0000000
--- a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaStaticInstanceFieldDuplicationTest.java
+++ /dev/null
@@ -1,281 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.desugar.lambdas;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
-
-import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.references.MethodReference;
-import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.InstructionSubject;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import com.google.common.collect.Sets.SetView;
-import java.nio.file.Path;
-import java.util.HashSet;
-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;
-
-@RunWith(Parameterized.class)
-public class LambdaStaticInstanceFieldDuplicationTest extends TestBase {
-
- static final String EXPECTED = StringUtils.lines("User1.1", "User1.2", "User2");
-
- static final List<Class<?>> CLASSES =
- ImmutableList.of(TestClass.class, MyConsumer.class, Accept.class, User1.class, User2.class);
-
- static final List<String> CLASS_TYPE_NAMES =
- CLASSES.stream().map(Class::getTypeName).collect(Collectors.toList());
-
- private final TestParameters parameters;
-
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters()
- .withAllRuntimes()
- .withApiLevel(AndroidApiLevel.J)
- .enableApiLevelsForCf()
- .build();
- }
-
- public LambdaStaticInstanceFieldDuplicationTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
- @Test
- public void testR8() throws Exception {
- // R8 does not support desugaring with class file output so this test is only valid for DEX.
- assumeTrue(parameters.isDexRuntime());
- runR8(false);
- runR8(true);
- }
-
- private void runR8(boolean minify) throws Exception {
- testForR8(parameters.getBackend())
- .addProgramClasses(CLASSES)
- .addKeepMainRule(TestClass.class)
- // Prevent R8 from eliminating the lambdas by keeping the application of them.
- .addKeepClassAndMembersRules(Accept.class)
- .setMinApi(parameters.getApiLevel())
- .minification(minify)
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(EXPECTED)
- .inspect(this::checkNoOriginalsAndNoInternalSynthetics);
- }
-
- @Test
- public void testD8() throws Exception {
- testForD8(parameters.getBackend())
- .addProgramClasses(CLASSES)
- .setMinApi(parameters.getApiLevel())
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(EXPECTED)
- .inspect(this::checkNoOriginalsAndNoInternalSynthetics)
- .inspect(this::checkExpectedSynthetics);
- ;
- }
-
- @Test
- public void testD8Merging() throws Exception {
- assumeTrue(
- "b/147485959: Merging does not happen for CF due to lack of synthetic annotations",
- parameters.isDexRuntime());
- boolean intermediate = true;
- runD8Merging(intermediate);
- }
-
- @Test
- public void testD8MergingNonIntermediate() throws Exception {
- boolean intermediate = false;
- runD8Merging(intermediate);
- }
-
- private void runD8Merging(boolean intermediate) throws Exception {
- // Compile part 1 of the input (maybe intermediate)
- Path out1 =
- testForD8(parameters.getBackend())
- .addProgramClasses(User1.class)
- .addClasspathClasses(CLASSES)
- .setMinApi(parameters.getApiLevel())
- .setIntermediate(intermediate)
- .compile()
- .writeToZip();
-
- // Compile part 2 of the input (maybe intermediate)
- Path out2 =
- testForD8(parameters.getBackend())
- .addProgramClasses(User2.class)
- .addClasspathClasses(CLASSES)
- .setMinApi(parameters.getApiLevel())
- .setIntermediate(intermediate)
- .compile()
- .writeToZip();
-
- SetView<MethodReference> syntheticsInParts =
- Sets.union(
- getSyntheticMethods(new CodeInspector(out1)),
- getSyntheticMethods(new CodeInspector(out2)));
-
- // Merge parts as an intermediate artifact.
- // This will not merge synthetics regardless of the setting of intermediate.
- Path out3 = temp.newFolder().toPath().resolve("out3.zip");
- testForD8(parameters.getBackend())
- .addProgramClasses(TestClass.class, MyConsumer.class, Accept.class)
- .addProgramFiles(out1, out2)
- .setMinApi(parameters.getApiLevel())
- .setIntermediate(true)
- .compile()
- .writeToZip(out3)
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(EXPECTED)
- .inspect(this::checkNoOriginalsAndNoInternalSynthetics)
- .inspect(inspector -> assertEquals(syntheticsInParts, getSyntheticMethods(inspector)));
-
- // Finally do a non-intermediate merge.
- testForD8(parameters.getBackend())
- .addProgramFiles(out3)
- .setMinApi(parameters.getApiLevel())
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(EXPECTED)
- .inspect(this::checkNoOriginalsAndNoInternalSynthetics)
- .inspect(
- inspector -> {
- if (intermediate) {
- // If all previous builds where intermediate then synthetics are merged.
- checkExpectedSynthetics(inspector);
- } else {
- // Otherwise merging non-intermediate artifacts, synthetics will not be identified.
- // Check that they are exactly as in the part inputs.
- assertEquals(syntheticsInParts, getSyntheticMethods(inspector));
- }
- });
- }
-
- @Test
- public void testD8FilePerClassFile() throws Exception {
- assumeTrue(parameters.isDexRuntime());
- runD8FilePerMode(OutputMode.DexFilePerClassFile);
- }
-
- @Test
- public void testD8FilePerClass() throws Exception {
- assumeTrue(parameters.isDexRuntime());
- runD8FilePerMode(OutputMode.DexFilePerClass);
- }
-
- public void runD8FilePerMode(OutputMode outputMode) throws Exception {
- Path perClassOutput =
- testForD8(parameters.getBackend())
- .setOutputMode(outputMode)
- .addProgramClasses(CLASSES)
- .setMinApi(parameters.getApiLevel())
- .compile()
- .writeToZip();
- testForD8()
- .addProgramFiles(perClassOutput)
- .setMinApi(parameters.getApiLevel())
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(EXPECTED)
- .inspect(this::checkNoOriginalsAndNoInternalSynthetics)
- .inspect(this::checkExpectedSynthetics);
- }
-
- private void checkNoOriginalsAndNoInternalSynthetics(CodeInspector inspector) {
- inspector.forAllClasses(
- clazz -> {
- assertFalse(SyntheticItemsTestUtils.isInternalLambda(clazz.getFinalReference()));
- clazz.forAllMethods(
- method ->
- assertTrue(
- "Unexpected invoke dynamic:\n" + method.getMethod().codeToString(),
- method.isAbstract()
- || method
- .streamInstructions()
- .noneMatch(InstructionSubject::isInvokeDynamic)));
- });
- }
-
- private Set<MethodReference> getSyntheticMethods(CodeInspector inspector) {
- Set<MethodReference> methods = new HashSet<>();
- inspector.allClasses().stream()
- .filter(c -> !CLASS_TYPE_NAMES.contains(c.getFinalName()))
- .forEach(
- c ->
- c.allMethods(m -> !m.isInstanceInitializer() && !m.isClassInitializer())
- .forEach(m -> methods.add(m.asMethodReference())));
- return methods;
- }
-
- private void checkExpectedSynthetics(CodeInspector inspector) throws Exception {
- // Hardcoded set of expected synthetics in a "final" build. This set could change if the
- // compiler makes any changes to the naming, sorting or grouping of synthetics. It is hard-coded
- // here to check that the compiler generates this deterministically for any single run or merge
- // of intermediates.
- Set<MethodReference> expectedSynthetics =
- ImmutableSet.of(
- // User1 has two lambdas.
- SyntheticItemsTestUtils.syntheticLambdaMethod(
- User1.class, 0, MyConsumer.class.getMethod("accept", Object.class)),
- SyntheticItemsTestUtils.syntheticLambdaMethod(
- User1.class, 1, MyConsumer.class.getMethod("accept", Object.class)),
- // User2 has one lambda.
- SyntheticItemsTestUtils.syntheticLambdaMethod(
- User2.class, 0, MyConsumer.class.getMethod("accept", Object.class)));
- assertEquals(expectedSynthetics, getSyntheticMethods(inspector));
- }
-
- interface MyConsumer {
- void accept(Object o);
- }
-
- static class Accept {
- public static void accept(Object o, MyConsumer consumer) {
- consumer.accept(o);
- }
- }
-
- static class User1 {
-
- private static void testSystemPrintln1() {
- // The lambda will reference a lambda$x method on User1 which is created by javac for each
- // lambda on the class. Thus there can be no sharing unless R8 inlines the lambda method into
- // the desugared lambda classes.
- Accept.accept("1.1", o -> System.out.println("User" + o));
- }
-
- private static void testSystemPrintln2() {
- Accept.accept("1.2", o -> System.out.println("User" + o));
- }
- }
-
- static class User2 {
-
- private static void testSystemPrintln() {
- Accept.accept("2", o -> System.out.println("User" + o));
- }
- }
-
- static class TestClass {
-
- public static void main(String[] args) {
- User1.testSystemPrintln1();
- User1.testSystemPrintln2();
- User2.testSystemPrintln();
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaToSysOutPrintlnDuplicationTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaToSysOutPrintlnDuplicationTest.java
deleted file mode 100644
index 5b73125..0000000
--- a/src/test/java/com/android/tools/r8/desugar/lambdas/LambdaToSysOutPrintlnDuplicationTest.java
+++ /dev/null
@@ -1,265 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.desugar.lambdas;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
-
-import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.references.MethodReference;
-import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
-import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.InstructionSubject;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import com.google.common.collect.Sets.SetView;
-import java.nio.file.Path;
-import java.util.HashSet;
-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;
-
-@RunWith(Parameterized.class)
-public class LambdaToSysOutPrintlnDuplicationTest extends TestBase {
-
- static final String EXPECTED = StringUtils.lines("User1", "User2");
-
- static final List<Class<?>> CLASSES =
- ImmutableList.of(TestClass.class, MyConsumer.class, Accept.class, User1.class, User2.class);
-
- static final List<String> CLASS_TYPE_NAMES =
- CLASSES.stream().map(Class::getTypeName).collect(Collectors.toList());
-
- private final TestParameters parameters;
-
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters()
- .withAllRuntimes()
- .withApiLevel(AndroidApiLevel.J)
- .enableApiLevelsForCf()
- .build();
- }
-
- public LambdaToSysOutPrintlnDuplicationTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
- @Test
- public void testR8() throws Exception {
- // R8 does not support desugaring with class file output so this test is only valid for DEX.
- assumeTrue(parameters.isDexRuntime());
- runR8(false);
- runR8(true);
- }
-
- private void runR8(boolean minify) throws Exception {
- testForR8(parameters.getBackend())
- .addProgramClasses(CLASSES)
- .addKeepMainRule(TestClass.class)
- .setMinApi(parameters.getApiLevel())
- .minification(minify)
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(EXPECTED)
- .inspect(this::checkNoOriginalsAndNoInternalSynthetics);
- }
-
- @Test
- public void testD8() throws Exception {
- testForD8(parameters.getBackend())
- .addProgramClasses(CLASSES)
- .setMinApi(parameters.getApiLevel())
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(EXPECTED)
- .inspect(this::checkNoOriginalsAndNoInternalSynthetics)
- .inspect(this::checkExpectedSynthetics);
- ;
- }
-
- @Test
- public void testD8Merging() throws Exception {
- assumeTrue(
- "b/147485959: Merging does not happen for CF due to lack of synthetic annotations",
- parameters.isDexRuntime());
- boolean intermediate = true;
- runD8Merging(intermediate);
- }
-
- @Test
- public void testD8MergingNonIntermediate() throws Exception {
- boolean intermediate = false;
- runD8Merging(intermediate);
- }
-
- private void runD8Merging(boolean intermediate) throws Exception {
- // Compile part 1 of the input (maybe intermediate)
- Path out1 =
- testForD8(parameters.getBackend())
- .addProgramClasses(User1.class)
- .addClasspathClasses(CLASSES)
- .setMinApi(parameters.getApiLevel())
- .setIntermediate(intermediate)
- .compile()
- .writeToZip();
-
- // Compile part 2 of the input (maybe intermediate)
- Path out2 =
- testForD8(parameters.getBackend())
- .addProgramClasses(User2.class)
- .addClasspathClasses(CLASSES)
- .setMinApi(parameters.getApiLevel())
- .setIntermediate(intermediate)
- .compile()
- .writeToZip();
-
- SetView<MethodReference> syntheticsInParts =
- Sets.union(
- getSyntheticMethods(new CodeInspector(out1)),
- getSyntheticMethods(new CodeInspector(out2)));
-
- // Merge parts as an intermediate artifact.
- // This will not merge synthetics regardless of the setting of intermediate.
- Path out3 = temp.newFolder().toPath().resolve("out3.zip");
- testForD8(parameters.getBackend())
- .addProgramClasses(TestClass.class, MyConsumer.class, Accept.class)
- .addProgramFiles(out1, out2)
- .setMinApi(parameters.getApiLevel())
- .setIntermediate(true)
- .compile()
- .writeToZip(out3)
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(EXPECTED)
- .inspect(this::checkNoOriginalsAndNoInternalSynthetics)
- .inspect(inspector -> assertEquals(syntheticsInParts, getSyntheticMethods(inspector)));
-
- // Finally do a non-intermediate merge.
- testForD8(parameters.getBackend())
- .addProgramFiles(out3)
- .setMinApi(parameters.getApiLevel())
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(EXPECTED)
- .inspect(this::checkNoOriginalsAndNoInternalSynthetics)
- .inspect(
- inspector -> {
- if (intermediate) {
- // If all previous builds where intermediate then synthetics are merged.
- checkExpectedSynthetics(inspector);
- } else {
- // Otherwise merging non-intermediate artifacts, synthetics will not be identified.
- // Check that they are exactly as in the part inputs.
- assertEquals(syntheticsInParts, getSyntheticMethods(inspector));
- }
- });
- }
-
- @Test
- public void testD8FilePerClassFile() throws Exception {
- assumeTrue(parameters.isDexRuntime());
- runD8FilePerMode(OutputMode.DexFilePerClassFile);
- }
-
- @Test
- public void testD8FilePerClass() throws Exception {
- assumeTrue(parameters.isDexRuntime());
- runD8FilePerMode(OutputMode.DexFilePerClass);
- }
-
- public void runD8FilePerMode(OutputMode outputMode) throws Exception {
- Path perClassOutput =
- testForD8(parameters.getBackend())
- .setOutputMode(outputMode)
- .addProgramClasses(CLASSES)
- .setMinApi(parameters.getApiLevel())
- .compile()
- .writeToZip();
- testForD8()
- .addProgramFiles(perClassOutput)
- .setMinApi(parameters.getApiLevel())
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(EXPECTED)
- .inspect(this::checkNoOriginalsAndNoInternalSynthetics)
- .inspect(this::checkExpectedSynthetics);
- }
-
- private void checkNoOriginalsAndNoInternalSynthetics(CodeInspector inspector) {
- inspector.forAllClasses(
- clazz -> {
- assertFalse(SyntheticItemsTestUtils.isInternalLambda(clazz.getFinalReference()));
- clazz.forAllMethods(
- method ->
- assertTrue(
- "Unexpected invoke dynamic:\n" + method.getMethod().codeToString(),
- method.isAbstract()
- || method
- .streamInstructions()
- .noneMatch(InstructionSubject::isInvokeDynamic)));
- });
- }
-
- private Set<MethodReference> getSyntheticMethods(CodeInspector inspector) {
- Set<MethodReference> methods = new HashSet<>();
- inspector.allClasses().stream()
- .filter(c -> !CLASS_TYPE_NAMES.contains(c.getFinalName()))
- .forEach(
- c ->
- c.allMethods(m -> !m.isInstanceInitializer())
- .forEach(m -> methods.add(m.asMethodReference())));
- return methods;
- }
-
- private void checkExpectedSynthetics(CodeInspector inspector) throws Exception {
- // Hardcoded set of expected synthetics in a "final" build. This set could change if the
- // compiler makes any changes to the naming, sorting or grouping of synthetics. It is hard-coded
- // here to check that the compiler generates this deterministically for any single run or merge
- // of intermediates.
- Set<MethodReference> expectedSynthetics =
- ImmutableSet.of(
- SyntheticItemsTestUtils.syntheticLambdaMethod(
- User1.class, 0, MyConsumer.class.getMethod("accept", Object.class)));
- assertEquals(expectedSynthetics, getSyntheticMethods(inspector));
- }
-
- interface MyConsumer {
- void accept(Object o);
- }
-
- static class Accept {
- public static void accept(Object o, MyConsumer consumer) {
- consumer.accept(o);
- }
- }
-
- static class User1 {
-
- private static void testSystemPrintln() {
- Accept.accept("User1", System.out::println);
- }
- }
-
- static class User2 {
-
- private static void testSystemPrintln() {
- Accept.accept("User2", System.out::println);
- }
- }
-
- static class TestClass {
-
- public static void main(String[] args) {
- User1.testSystemPrintln();
- User2.testSystemPrintln();
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/LegacyLambdaMergeTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/LegacyLambdaMergeTest.java
deleted file mode 100644
index dd00acf..0000000
--- a/src/test/java/com/android/tools/r8/desugar/lambdas/LegacyLambdaMergeTest.java
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.desugar.lambdas;
-
-import static org.junit.Assume.assumeTrue;
-
-import com.android.tools.r8.D8TestCompileResult;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.graph.AccessFlags;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.transformers.ClassFileTransformer.MethodInsnTransform;
-import com.android.tools.r8.transformers.ClassFileTransformer.TypeInsnTransform;
-import com.android.tools.r8.utils.StringUtils;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.objectweb.asm.MethodVisitor;
-
-@RunWith(Parameterized.class)
-public class LegacyLambdaMergeTest extends TestBase {
-
- static final String EXPECTED = StringUtils.lines("Hello, world");
-
- private final TestParameters parameters;
-
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().withAllApiLevels().build();
- }
-
- public LegacyLambdaMergeTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
- @Test
- public void testReference() throws Exception {
- assumeTrue(parameters.isCfRuntime());
- testForJvm()
- .addProgramClassFileData(getTransformedMain())
- // Add the lambda twice (JVM just picks the first).
- .addProgramClassFileData(getTransformedLambda())
- .addProgramClassFileData(getTransformedLambda())
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(EXPECTED);
- }
-
- @Test
- public void testD8() throws Exception {
- // Merging legacy lambdas is only valid for DEX inputs, thus also not R8 applicable.
- assumeTrue(parameters.isDexRuntime());
- D8TestCompileResult lambda =
- testForD8()
- .setMinApi(parameters.getApiLevel())
- .addProgramClassFileData(getTransformedLambda())
- .compile();
- testForD8()
- .setMinApi(parameters.getApiLevel())
- .addProgramClassFileData(getTransformedMain())
- // Add the lambda twice.
- .addProgramFiles(lambda.writeToZip())
- .addProgramFiles(lambda.writeToZip())
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(EXPECTED);
- }
-
- private ClassReference LAMBDA =
- Reference.classFromDescriptor(
- Reference.classFromClass(WillBeLambda.class)
- .getDescriptor()
- .replace("WillBeLambda", "-$$Lambda$XYZ"));
-
- private byte[] getTransformedLambda() throws Exception {
- return transformer(WillBeLambda.class)
- .setClassDescriptor(LAMBDA.getDescriptor())
- .setAccessFlags(AccessFlags::setSynthetic)
- .transform();
- }
-
- private byte[] getTransformedMain() throws Exception {
- return transformer(TestClass.class)
- .transformMethodInsnInMethod(
- "main",
- new MethodInsnTransform() {
- @Override
- public void visitMethodInsn(
- int opcode,
- String owner,
- String name,
- String descriptor,
- boolean isInterface,
- MethodVisitor visitor) {
- visitor.visitMethodInsn(
- opcode, LAMBDA.getBinaryName(), name, descriptor, isInterface);
- }
- })
- .transformTypeInsnInMethod(
- "main",
- new TypeInsnTransform() {
- @Override
- public void visitTypeInsn(int opcode, String type, MethodVisitor visitor) {
- visitor.visitTypeInsn(opcode, LAMBDA.getBinaryName());
- }
- })
- .transform();
- }
-
- static class WillBeLambda {
- public void foo() {
- System.out.println("Hello, world");
- }
- }
-
- static class TestClass {
-
- public static void main(String[] args) {
- new WillBeLambda().foo();
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/mergedcontext/MergedContextTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/mergedcontext/MergedContextTest.java
deleted file mode 100644
index 35e31d5..0000000
--- a/src/test/java/com/android/tools/r8/desugar/lambdas/mergedcontext/MergedContextTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-// 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.lambdas.mergedcontext;
-
-import com.android.tools.r8.NeverClassInline;
-import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.NoHorizontalClassMerging;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.StringUtils;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class MergedContextTest extends TestBase {
-
- static final String EXPECTED = StringUtils.lines("B::foo", "C::bar");
-
- private final TestParameters parameters;
-
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().withAllApiLevels().build();
- }
-
- public MergedContextTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
- @Test
- public void test() throws Exception {
- testForR8(parameters.getBackend())
- .addProgramClasses(TestClass.class, A.class, B.class, C.class)
- .addKeepClassAndMembersRules(TestClass.class)
- .addKeepRules("-repackageclasses \"repackaged\"")
- .enableInliningAnnotations()
- .enableNoHorizontalClassMergingAnnotations()
- .enableNeverClassInliningAnnotations()
- .setMinApi(parameters.getApiLevel())
- .addHorizontallyMergedClassesInspector(
- inspector -> {
- inspector.assertClassNotMerged(C.class);
- inspector.assertMergedInto(B.class, A.class);
- })
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(EXPECTED);
- }
-
- /* This class will be merged with class B (with A being the result). This class has a package
- * protected access to ensure that it cannot be repackaged. */
- @NeverClassInline
- public static class A {
-
- @NeverInline
- public void ensureNotRepackaged() {
- TestClass.packageProtectedMethodToDisableRepackage();
- }
- }
-
- /* This class is merged into A. */
- @NeverClassInline
- public static class B {
-
- @NeverInline
- public Runnable foo() {
- C c = new C();
- // This synthetic lambda class uses package protected access to C. Its context will initially
- // be B, thus the synthetic will internally be B-$$Synthetic. The lambda can be repackaged
- // together with the accessed class C. However, once A and B are merged as A, the context
- // implicitly changes. If repackaging does not either see or adjust the context, the result
- // will be that the external synthetic lambda will become A-$$Synthetic,
- // with the consequence that the call to repackaged.C.protectedMethod() will throw IAE.
- return () -> {
- System.out.println("B::foo");
- c.packageProtectedMethod();
- };
- }
- }
-
- @NeverClassInline
- @NoHorizontalClassMerging
- public static class C {
-
- @NeverInline
- void packageProtectedMethod() {
- System.out.println("C::bar");
- }
- }
-
- static class TestClass {
-
- static void packageProtectedMethodToDisableRepackage() {
- if (System.nanoTime() < 0) {
- throw new RuntimeException();
- }
- }
-
- public static void main(String[] args) {
- new A().ensureNotRepackaged();
- new B().foo().run();
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java b/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
deleted file mode 100644
index dde98e2..0000000
--- a/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
+++ /dev/null
@@ -1,157 +0,0 @@
-// 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.twr;
-
-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.TestParametersCollection;
-import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.ZipUtils;
-import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
-import java.io.IOException;
-import java.util.List;
-import java.util.jar.JarFile;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-
-@RunWith(Parameterized.class)
-public class TwrCloseResourceDuplicationTest extends TestBase {
-
- static final int INPUT_CLASSES = 3;
-
- static final String EXPECTED =
- StringUtils.lines(
- "foo opened 1",
- "foo post close 1",
- "foo opened 2",
- "foo caught from 2: RuntimeException",
- "foo post close 2",
- "bar opened 1",
- "bar post close 1",
- "bar opened 2",
- "bar caught from 2: RuntimeException",
- "bar post close 2");
-
- private final TestParameters parameters;
-
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
- }
-
- public TwrCloseResourceDuplicationTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
- private String getZipFile() throws IOException {
- return ZipUtils.ZipBuilder.builder(temp.newFile("file.zip").toPath())
- // DEX VMs from 4.4 up-to 9.0 including, will fail if no entry is added.
- .addBytes("entry", new byte[1])
- .build()
- .toString();
- }
-
- @Test
- public void testJvm() throws Exception {
- assumeTrue(parameters.isCfRuntime());
- testForJvm()
- .addInnerClasses(getClass())
- .run(parameters.getRuntime(), TestClass.class, getZipFile())
- .assertSuccessWithOutput(EXPECTED);
- }
-
- @Test
- public void testD8() throws Exception {
- testForD8(parameters.getBackend())
- .addInnerClasses(getClass())
- .setMinApi(parameters.getApiLevel())
- .run(parameters.getRuntime(), TestClass.class, getZipFile())
- .assertSuccessWithOutput(EXPECTED)
- .inspect(
- inspector -> {
- // There should be exactly one synthetic class besides the three program classes.
- int expectedSynthetics =
- parameters.getApiLevel().isLessThan(apiLevelWithTwrCloseResourceSupport())
- ? 1
- : 0;
- assertEquals(INPUT_CLASSES + expectedSynthetics, inspector.allClasses().size());
- });
- }
-
- @Test
- public void testR8() throws Exception {
- assumeTrue(parameters.isDexRuntime());
- testForR8(parameters.getBackend())
- .addInnerClasses(getClass())
- .addKeepMainRule(TestClass.class)
- .addKeepClassAndMembersRules(Foo.class, Bar.class)
- .setMinApi(parameters.getApiLevel())
- .noMinification()
- .run(parameters.getRuntime(), TestClass.class, getZipFile())
- .assertSuccessWithOutput(EXPECTED)
- .inspect(
- inspector -> {
- // R8 will optimize the generated methods for the two cases below where the thrown
- // exception is known or not, thus the synthetic methods will be 2.
- int expectedSynthetics =
- parameters.getApiLevel().isLessThan(apiLevelWithTwrCloseResourceSupport())
- ? 2
- : 0;
- List<FoundClassSubject> foundClassSubjects = inspector.allClasses();
- assertEquals(INPUT_CLASSES + expectedSynthetics, foundClassSubjects.size());
- });
- }
-
- static class Foo {
- void foo(String name) {
- try (JarFile f = new JarFile(name)) {
- System.out.println("foo opened 1");
- } catch (Exception e) {
- System.out.println("foo caught from 1: " + e.getClass().getSimpleName());
- } finally {
- System.out.println("foo post close 1");
- }
- try (JarFile f = new JarFile(name)) {
- System.out.println("foo opened 2");
- throw new RuntimeException();
- } catch (Exception e) {
- System.out.println("foo caught from 2: " + e.getClass().getSimpleName());
- } finally {
- System.out.println("foo post close 2");
- }
- }
- }
-
- static class Bar {
- void bar(String name) {
- try (JarFile f = new JarFile(name)) {
- System.out.println("bar opened 1");
- } catch (Exception e) {
- System.out.println("bar caught from 1: " + e.getClass().getSimpleName());
- } finally {
- System.out.println("bar post close 1");
- }
- try (JarFile f = new JarFile(name)) {
- System.out.println("bar opened 2");
- throw new RuntimeException();
- } catch (Exception e) {
- System.out.println("bar caught from 2: " + e.getClass().getSimpleName());
- } finally {
- System.out.println("bar post close 2");
- }
- }
- }
-
- static class TestClass {
-
- public static void main(String[] args) {
- new Foo().foo(args[0]);
- new Bar().bar(args[0]);
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/desugaring/lambdanames/PackageDependentLambdaNamesTest.java b/src/test/java/com/android/tools/r8/desugaring/lambdanames/PackageDependentLambdaNamesTest.java
index 5730599..934a15b 100644
--- a/src/test/java/com/android/tools/r8/desugaring/lambdanames/PackageDependentLambdaNamesTest.java
+++ b/src/test/java/com/android/tools/r8/desugaring/lambdanames/PackageDependentLambdaNamesTest.java
@@ -47,9 +47,9 @@
if (parameters.isDexRuntime()) {
result.inspect(
inspector -> {
- // With the hygienic synthetics the reference to System.out::print can always be shared.
+ // When in the same package we expect the two System.out::print lambdas to be shared.
assertEquals(
- 2,
+ samePackage ? 2 : 3,
inspector.allClasses().stream()
.filter(c -> c.isSynthesizedJavaLambdaClass())
.count());
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..e0266bd 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
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.optimize.classinliner;
+import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX;
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.CoreMatchers.containsString;
@@ -321,8 +322,8 @@
Set<String> expectedTypes = Sets.newHashSet("java.lang.StringBuilder");
expectedTypes.addAll(
inspector.allClasses().stream()
- .filter(FoundClassSubject::isSynthesizedJavaLambdaClass)
.map(FoundClassSubject::getFinalName)
+ .filter(name -> name.contains(LAMBDA_CLASS_NAME_PREFIX))
.collect(Collectors.toList()));
assertEquals(expectedTypes, collectTypes(clazz.uniqueMethodWithName("testStatefulLambda")));
assertTrue(
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java
index 1bbcb9e..7565cf7 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListOutputTest.java
@@ -6,7 +6,6 @@
import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -17,7 +16,6 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.FileUtils;
import com.google.common.collect.ImmutableList;
@@ -61,8 +59,8 @@
@Override
public void finished(DiagnosticsHandler handler) {
String string = builder.toString();
- assertThat(string, containsString(testClassMainDexName));
- assertThat(string, SyntheticItemsTestUtils.containsExternalSyntheticReference());
+ assertTrue(string.contains(testClassMainDexName));
+ assertTrue(string.contains("Lambda"));
}
}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
index cf2aeeb..52b60fe 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexTracingTest.java
@@ -24,9 +24,9 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ir.desugar.LambdaRewriter;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.shaking.WhyAreYouKeepingConsumer;
-import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -433,13 +433,13 @@
for (int i = 0; i < refList.length; i++) {
String reference = refList[i].trim();
// The main dex list generator does not do any lambda desugaring.
- if (!isExternalSyntheticLambda(reference)) {
+ if (!isLambda(reference)) {
if (mainDexGeneratorMainDexList.size() <= i - nonLambdaOffset) {
fail("Main dex list generator is missing '" + reference + "'");
}
String fromList = mainDexGeneratorMainDexList.get(i - nonLambdaOffset);
String fromConsumer = mainDexGeneratorMainDexListFromConsumer.get(i - nonLambdaOffset);
- if (isExternalSyntheticLambda(fromList)) {
+ if (isLambda(fromList)) {
assertEquals(Backend.DEX, backend);
assertEquals(fromList, fromConsumer);
nonLambdaOffset--;
@@ -481,8 +481,8 @@
assertArrayEquals(entriesUnsorted, entriesSorted);
}
- private boolean isExternalSyntheticLambda(String mainDexEntry) {
- return SyntheticItemsTestUtils.isExternalLambda(Reference.classFromDescriptor(mainDexEntry));
+ private boolean isLambda(String mainDexEntry) {
+ return mainDexEntry.contains(LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX);
}
private String mainDexStringToDescriptor(String mainDexString) {
@@ -492,8 +492,11 @@
}
private void checkSameMainDexEntry(String reference, String computed) {
- if (isExternalSyntheticLambda(reference)) {
- // For synthetic classes we check that the context classes match.
+ if (isLambda(reference)) {
+ // For lambda classes we check that there is a lambda class for the right containing
+ // class. However, we do not check the hash for the generated lambda class. The hash
+ // changes for different compiler versions because different compiler versions generate
+ // different lambda implementation method names.
reference = reference.substring(0, reference.lastIndexOf('$'));
computed = computed.substring(0, computed.lastIndexOf('$'));
}
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java
index 9fa7fe9..d102004 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexWithSynthesizedClassesTest.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.ir.desugar.LambdaRewriter;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -100,11 +100,10 @@
inspector.allClasses().stream()
.anyMatch(
clazz ->
- clazz.isSynthesizedJavaLambdaClass()
+ clazz.getOriginalName().contains(LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX)
&& clazz
- .getOriginalReference()
- .equals(
- SyntheticItemsTestUtils.syntheticLambdaClass(lambdaHolder, 0))));
+ .getOriginalName()
+ .contains("$" + lambdaHolder.getSimpleName() + "$")));
}
static class TestClass {
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 6ae3a6f..3959d84 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
@@ -14,8 +14,6 @@
import com.android.tools.r8.CompilationMode;
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,8 +57,7 @@
}
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));
+ return line.className.contains("-$$Lambda$");
}
private void checkLambdaFrame(StackTrace retracedStackTrace) {
diff --git a/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderMultipleCallsTest.java b/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderMultipleCallsTest.java
index fd1a05a..688a96f 100644
--- a/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderMultipleCallsTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/ServiceLoaderMultipleCallsTest.java
@@ -4,10 +4,11 @@
package com.android.tools.r8.rewrite;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertNull;
import static junit.framework.TestCase.assertTrue;
-import static org.junit.Assert.assertFalse;
+import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.DataEntryResource;
@@ -19,7 +20,6 @@
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import java.io.IOException;
import java.nio.file.Path;
@@ -109,18 +109,11 @@
inspector -> {
// Check that we have actually rewritten the calls to ServiceLoader.load.
assertEquals(0, getServiceLoaderLoads(inspector, MainRunner.class));
- // Check the synthesize service loader method is a single shared method.
- // Due to minification we just check there is only a single synthetic class with a
- // single static method.
- boolean found = false;
- for (FoundClassSubject clazz : inspector.allClasses()) {
- if (clazz.isSynthetic()) {
- assertFalse(found);
- found = true;
- assertEquals(1, clazz.allMethods().size());
- clazz.forAllMethods(m -> assertTrue(m.isStatic()));
- }
- }
+ // Check that the synthesize service loader class holds two methods, one for each
+ // context.
+ ClassSubject serviceLoaderMethods = inspector.clazz("$$ServiceLoaderMethods");
+ assertThat(serviceLoaderMethods, isPresent());
+ assertEquals(2, serviceLoaderMethods.allMethods().size());
});
// Check that we have removed the service configuration from META-INF/services.
diff --git a/src/test/java/com/android/tools/r8/shaking/PreserveDesugaredLambdaTest.java b/src/test/java/com/android/tools/r8/shaking/PreserveDesugaredLambdaTest.java
index c2f8041..cd153f2 100644
--- a/src/test/java/com/android/tools/r8/shaking/PreserveDesugaredLambdaTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/PreserveDesugaredLambdaTest.java
@@ -79,7 +79,8 @@
codeInspector.allClasses().stream()
.anyMatch(
c -> {
- if (c.isSynthesizedJavaLambdaClass()) {
+ if (c.getOriginalName()
+ .contains("-$$Lambda$PreserveDesugaredLambdaTest$Main")) {
assertThat(c.uniqueMethodWithName("computeTheFoo"), isPresent());
return true;
}
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptViaClassInitializerTestRunner.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptViaClassInitializerTestRunner.java
index 436c030..4d385a0 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptViaClassInitializerTestRunner.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptViaClassInitializerTestRunner.java
@@ -21,7 +21,6 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.shaking.WhyAreYouKeepingConsumer;
-import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.graphinspector.GraphInspector;
@@ -29,7 +28,6 @@
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.function.Supplier;
-import org.hamcrest.Matcher;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -131,13 +129,12 @@
consumer.printWhyAreYouKeeping(classFromClass(A.class), new PrintStream(baos));
assertThat(baos.toString(), containsString(KEPT_REASON_SUFFIX));
- // TODO(b/124499108): Currently (internal) synthetic lambda classes are referenced,
+ // TODO(b/124499108): Currently synthetic lambda classes are referenced,
// should be their originating context.
- Matcher<String> hasLambda = SyntheticItemsTestUtils.containsInternalSyntheticReference();
if (parameters.isDexRuntime()) {
- assertThat(baos.toString(), hasLambda);
+ assertThat(baos.toString(), containsString("-$$Lambda$"));
} else {
- assertThat(baos.toString(), not(hasLambda));
+ assertThat(baos.toString(), not(containsString("-$$Lambda$")));
}
}
}
diff --git a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
deleted file mode 100644
index 438d3f9..0000000
--- a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.synthesis;
-
-import static org.hamcrest.CoreMatchers.containsString;
-
-import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
-import com.android.tools.r8.references.ClassReference;
-import com.android.tools.r8.references.MethodReference;
-import com.android.tools.r8.references.Reference;
-import com.android.tools.r8.synthesis.SyntheticNaming.Phase;
-import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
-import java.lang.reflect.Method;
-import org.hamcrest.Matcher;
-
-public class SyntheticItemsTestUtils {
-
- public static ClassReference syntheticCompanionClass(Class<?> clazz) {
- return Reference.classFromDescriptor(
- InterfaceMethodRewriter.getCompanionClassDescriptor(
- Reference.classFromClass(clazz).getDescriptor()));
- }
-
- private static ClassReference syntheticClass(Class<?> clazz, SyntheticKind kind, int id) {
- return SyntheticNaming.makeSyntheticReferenceForTest(
- Reference.classFromClass(clazz), kind, "" + id);
- }
-
- public static MethodReference syntheticBackportMethod(Class<?> clazz, int id, Method method) {
- ClassReference syntheticHolder =
- syntheticClass(clazz, SyntheticNaming.SyntheticKind.BACKPORT, id);
- MethodReference originalMethod = Reference.methodFromMethod(method);
- return Reference.methodFromDescriptor(
- syntheticHolder.getDescriptor(),
- SyntheticNaming.INTERNAL_SYNTHETIC_METHOD_PREFIX,
- originalMethod.getMethodDescriptor());
- }
-
- public static ClassReference syntheticLambdaClass(Class<?> clazz, int id) {
- return syntheticClass(clazz, SyntheticNaming.SyntheticKind.LAMBDA, id);
- }
-
- public static MethodReference syntheticLambdaMethod(Class<?> clazz, int id, Method method) {
- ClassReference syntheticHolder = syntheticLambdaClass(clazz, id);
- MethodReference originalMethod = Reference.methodFromMethod(method);
- return Reference.methodFromDescriptor(
- syntheticHolder.getDescriptor(),
- originalMethod.getMethodName(),
- originalMethod.getMethodDescriptor());
- }
-
- public static boolean isInternalLambda(ClassReference reference) {
- return SyntheticNaming.isSynthetic(reference, Phase.INTERNAL, SyntheticKind.LAMBDA);
- }
-
- public static boolean isExternalLambda(ClassReference reference) {
- return SyntheticNaming.isSynthetic(reference, Phase.EXTERNAL, SyntheticKind.LAMBDA);
- }
-
- public static boolean isExternalStaticInterfaceCall(ClassReference reference) {
- return SyntheticNaming.isSynthetic(
- reference, Phase.EXTERNAL, SyntheticKind.STATIC_INTERFACE_CALL);
- }
-
- public static boolean isExternalTwrCloseMethod(ClassReference reference) {
- return SyntheticNaming.isSynthetic(reference, Phase.EXTERNAL, SyntheticKind.TWR_CLOSE_RESOURCE);
- }
-
- public static Matcher<String> containsInternalSyntheticReference() {
- return containsString(SyntheticNaming.getPhaseSeparator(Phase.INTERNAL));
- }
-
- public static Matcher<String> containsExternalSyntheticReference() {
- return containsString(SyntheticNaming.getPhaseSeparator(Phase.EXTERNAL));
- }
-}
diff --git a/src/test/java/com/android/tools/r8/utils/SyntheticItemsTestUtils.java b/src/test/java/com/android/tools/r8/utils/SyntheticItemsTestUtils.java
new file mode 100644
index 0000000..9e78bac
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/utils/SyntheticItemsTestUtils.java
@@ -0,0 +1,27 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.SyntheticItems;
+import java.lang.reflect.Method;
+
+public class SyntheticItemsTestUtils {
+
+ public static ClassReference syntheticClass(Class<?> clazz, int id) {
+ return Reference.classFromTypeName(
+ clazz.getTypeName() + SyntheticItems.EXTERNAL_SYNTHETIC_CLASS_SEPARATOR + id);
+ }
+
+ public static MethodReference syntheticMethod(Class<?> clazz, int id, Method method) {
+ ClassReference syntheticHolder = syntheticClass(clazz, id);
+ MethodReference originalMethod = Reference.methodFromMethod(method);
+ return Reference.methodFromDescriptor(
+ syntheticHolder.getDescriptor(),
+ SyntheticItems.INTERNAL_SYNTHETIC_METHOD_PREFIX + 0,
+ originalMethod.getMethodDescriptor());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
index a42fa4b..ffe8151 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
@@ -136,11 +136,6 @@
}
@Override
- public ClassReference getOriginalReference() {
- return null;
- }
-
- @Override
public ClassReference getFinalReference() {
return null;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
index f927e62..3a9976f 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CfInstructionSubject.java
@@ -285,7 +285,6 @@
&& ((CfInvoke) instruction).getOpcode() == Opcodes.INVOKESPECIAL;
}
- @Override
public boolean isInvokeDynamic() {
return instruction instanceof CfInvokeDynamic;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
index 20d7466..a2d32eb 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -191,8 +191,6 @@
public abstract String getOriginalBinaryName();
- public abstract ClassReference getOriginalReference();
-
public abstract ClassReference getFinalReference();
public abstract String getFinalName();
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
index 2ae5f05..4b9f733 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/DexInstructionSubject.java
@@ -209,11 +209,6 @@
return false;
}
- @Override
- public boolean isInvokeDynamic() {
- return isInvokeCustom();
- }
-
public boolean isInvokeCustom() {
return instruction instanceof InvokeCustom || instruction instanceof InvokeCustomRange;
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index 4220b50..a676666 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -34,7 +34,6 @@
import com.android.tools.r8.retrace.RetraceTypeResult;
import com.android.tools.r8.retrace.RetracedField;
import com.android.tools.r8.retrace.Retracer;
-import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ZipUtils;
@@ -390,11 +389,6 @@
}
@Override
- public ClassReference getOriginalReference() {
- return Reference.classFromDescriptor(getOriginalDescriptor());
- }
-
- @Override
public ClassReference getFinalReference() {
return Reference.classFromDescriptor(getFinalDescriptor());
}
@@ -436,9 +430,7 @@
@Override
public boolean isSynthesizedJavaLambdaClass() {
- // TODO(141287349): Make this precise based on the map input.
- return SyntheticItemsTestUtils.isExternalLambda(getOriginalReference())
- || SyntheticItemsTestUtils.isExternalLambda(getFinalReference());
+ return dexClass.type.getName().contains("$Lambda$");
}
@Override
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
index 8b34260..56436e9 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/InstructionSubject.java
@@ -43,8 +43,6 @@
boolean isInvokeSpecial();
- boolean isInvokeDynamic();
-
DexMethod getMethod();
boolean isNop();
diff --git a/tools/toolhelper.py b/tools/toolhelper.py
index 1cfd0f0..45d84e2 100644
--- a/tools/toolhelper.py
+++ b/tools/toolhelper.py
@@ -27,10 +27,6 @@
cmd.append(jdk.GetJavaExecutable())
if extra_args:
cmd.extend(extra_args)
- agent, args = extract_debug_agent_from_args(args)
- if agent:
- cmd.append(
- '-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005')
if debug:
cmd.append('-ea')
if profile:
@@ -107,13 +103,3 @@
else:
args.append(arg)
return lib, args
-
-def extract_debug_agent_from_args(input_args):
- agent = False
- args = []
- for arg in input_args:
- if arg in ('--debug-agent', '--debug_agent'):
- agent = True
- else:
- args.append(arg)
- return agent, args