Merge commit 'de5e2c1192ff73d5936b36a8fc46127cf17de185' into dev-release
diff --git a/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java b/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
index c856d87..107efb4 100644
--- a/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
+++ b/src/main/java/com/android/tools/r8/BackportedMethodListCommand.java
@@ -4,8 +4,8 @@
package com.android.tools.r8;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfigurationParser;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
diff --git a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
index 69c1ec8..c292f33 100644
--- a/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCompilerCommand.java
@@ -7,8 +7,8 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.inspector.Inspector;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfigurationParser;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 1ef4a6e..c23b48c 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -12,7 +12,7 @@
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
import com.android.tools.r8.inspector.Inspector;
import com.android.tools.r8.inspector.internal.InspectorImpl;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.ProguardConfigurationParser;
import com.android.tools.r8.shaking.ProguardConfigurationRule;
diff --git a/src/main/java/com/android/tools/r8/DumpOptions.java b/src/main/java/com/android/tools/r8/DumpOptions.java
index f923ffa..3b502b5 100644
--- a/src/main/java/com/android/tools/r8/DumpOptions.java
+++ b/src/main/java/com/android/tools/r8/DumpOptions.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.features.FeatureSplitConfiguration;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
import com.android.tools.r8.shaking.ProguardConfiguration;
import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.android.tools.r8.utils.InternalOptions.DesugarState;
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
index a47844f..19d4a98 100644
--- a/src/main/java/com/android/tools/r8/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
@@ -35,8 +35,8 @@
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfigurationParser;
import com.android.tools.r8.jar.CfApplicationWriter;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.origin.Origin;
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java
index 9ec502c..7cd95e5 100644
--- a/src/main/java/com/android/tools/r8/L8Command.java
+++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -12,7 +12,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
import com.android.tools.r8.inspector.Inspector;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 6edb405..3bd0653 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -46,9 +46,9 @@
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
-import com.android.tools.r8.ir.desugar.RecordRewriter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeterLibraryTypeSynthesizor;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
+import com.android.tools.r8.ir.desugar.records.RecordRewriter;
import com.android.tools.r8.ir.optimize.AssertionsRewriter;
import com.android.tools.r8.ir.optimize.MethodPoolCollection;
import com.android.tools.r8.ir.optimize.NestReducer;
@@ -307,8 +307,8 @@
MainDexListBuilder.checkForAssumedLibraryTypes(appView.appInfo());
}
if (!options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()) {
- DesugaredLibraryRetargeter.checkForAssumedLibraryTypes(appView);
- DesugaredLibraryRetargeter.amendLibraryWithRetargetedMembers(appView);
+ DesugaredLibraryRetargeterLibraryTypeSynthesizor.checkForAssumedLibraryTypes(appView);
+ DesugaredLibraryRetargeterLibraryTypeSynthesizor.amendLibraryWithRetargetedMembers(appView);
}
InterfaceMethodRewriter.checkForAssumedLibraryTypes(appView.appInfo(), options);
BackportedMethodRewriter.registerAssumedLibraryTypes(options);
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 844d01a..af8aa34 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -14,7 +14,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.inspector.Inspector;
import com.android.tools.r8.inspector.internal.InspectorImpl;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
import com.android.tools.r8.shaking.ProguardConfiguration;
diff --git a/src/main/java/com/android/tools/r8/cf/CfPrinter.java b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
index 64dd9a5..c6ba520 100644
--- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java
+++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -37,6 +37,7 @@
import com.android.tools.r8.cf.code.CfNeg;
import com.android.tools.r8.cf.code.CfNew;
import com.android.tools.r8.cf.code.CfNewArray;
+import com.android.tools.r8.cf.code.CfNewUnboxedEnum;
import com.android.tools.r8.cf.code.CfNop;
import com.android.tools.r8.cf.code.CfNumberConversion;
import com.android.tools.r8.cf.code.CfPosition;
@@ -503,6 +504,12 @@
}
}
+ public void print(CfNewUnboxedEnum newInstance) {
+ indent();
+ builder.append("newunboxedenum ");
+ appendClass(newInstance.getType());
+ }
+
public void print(CfMultiANewArray multiANewArray) {
indent();
builder.append("multianewarray ");
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfNewUnboxedEnum.java b/src/main/java/com/android/tools/r8/cf/code/CfNewUnboxedEnum.java
new file mode 100644
index 0000000..89f8686
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/cf/code/CfNewUnboxedEnum.java
@@ -0,0 +1,124 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.cf.code;
+
+import com.android.tools.r8.cf.CfPrinter;
+import com.android.tools.r8.cf.code.CfFrame.FrameType;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.CfCompareHelper;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.InitClassLens;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.CfSourceCode;
+import com.android.tools.r8.ir.conversion.CfState;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
+import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
+import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.structural.CompareToVisitor;
+import java.util.ListIterator;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/** The class-file representation of {@link com.android.tools.r8.ir.code.NewUnboxedEnumInstance}. */
+public class CfNewUnboxedEnum extends CfInstruction implements CfTypeInstruction {
+
+ private final DexType type;
+ private final int ordinal;
+
+ public CfNewUnboxedEnum(DexType type, int ordinal) {
+ this.type = type;
+ this.ordinal = ordinal;
+ }
+
+ @Override
+ public CfTypeInstruction asTypeInstruction() {
+ return this;
+ }
+
+ @Override
+ public boolean isTypeInstruction() {
+ return true;
+ }
+
+ @Override
+ public DexType getType() {
+ return type;
+ }
+
+ @Override
+ public CfInstruction withType(DexType newType) {
+ return new CfNewUnboxedEnum(newType, ordinal);
+ }
+
+ @Override
+ public int getCompareToId() {
+ return Opcodes.NEW;
+ }
+
+ @Override
+ public int internalAcceptCompareTo(
+ CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) {
+ return type.acceptCompareTo(((CfNewUnboxedEnum) other).type, visitor);
+ }
+
+ @Override
+ public void write(
+ AppView<?> appView,
+ ProgramMethod context,
+ DexItemFactory dexItemFactory,
+ GraphLens graphLens,
+ InitClassLens initClassLens,
+ NamingLens namingLens,
+ LensCodeRewriterUtils rewriter,
+ MethodVisitor visitor) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public void print(CfPrinter printer) {
+ printer.print(this);
+ }
+
+ @Override
+ void internalRegisterUse(
+ UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) {
+ registry.registerNewUnboxedEnumInstance(type);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) {
+ builder.addNewUnboxedEnumInstance(state.push(type).register, type, ordinal);
+ }
+
+ @Override
+ public ConstraintWithTarget inliningConstraint(
+ InliningConstraints inliningConstraints, CfCode code, ProgramMethod context) {
+ return inliningConstraints.forNewUnboxedEnumInstance(type, context);
+ }
+
+ @Override
+ public void evaluate(
+ CfFrameVerificationHelper frameBuilder,
+ DexType context,
+ DexType returnType,
+ DexItemFactory factory,
+ InitClassLens initClassLens) {
+ // ... →
+ // ..., objectref
+ frameBuilder.push(FrameType.initialized(type));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/code/DexNewUnboxedEnumInstance.java b/src/main/java/com/android/tools/r8/code/DexNewUnboxedEnumInstance.java
new file mode 100644
index 0000000..f9c26e6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/code/DexNewUnboxedEnumInstance.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2016, 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.code;
+
+import com.android.tools.r8.dex.IndexedItemCollection;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.ObjectToOffsetMapping;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.conversion.IRBuilder;
+import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils;
+import com.android.tools.r8.utils.structural.StructuralSpecification;
+import java.nio.ShortBuffer;
+
+/** The dex representation of {@link com.android.tools.r8.ir.code.NewUnboxedEnumInstance}. */
+public class DexNewUnboxedEnumInstance extends Format21c<DexType> {
+
+ public static final int OPCODE = 0x22;
+ public static final String NAME = "NewUnboxedEnumInstance";
+ public static final String SMALI_NAME = "new-unboxed-enum-instance";
+
+ private final int ordinal;
+
+ public DexNewUnboxedEnumInstance(int AA, DexType BBBB, int ordinal) {
+ super(AA, BBBB);
+ this.ordinal = ordinal;
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public String getSmaliName() {
+ return SMALI_NAME;
+ }
+
+ @Override
+ public int getOpcode() {
+ throw new Unreachable();
+ }
+
+ @Override
+ void internalSubSpecify(StructuralSpecification<Format21c<DexType>, ?> spec) {
+ spec.withItem(i -> i.BBBB);
+ }
+
+ @Override
+ public void collectIndexedItems(
+ IndexedItemCollection indexedItems,
+ ProgramMethod context,
+ GraphLens graphLens,
+ LensCodeRewriterUtils rewriter) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public void write(
+ ShortBuffer dest,
+ ProgramMethod context,
+ GraphLens graphLens,
+ ObjectToOffsetMapping mapping,
+ LensCodeRewriterUtils rewriter) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public void registerUse(UseRegistry registry) {
+ registry.registerNewUnboxedEnumInstance(getType());
+ }
+
+ public DexType getType() {
+ return BBBB;
+ }
+
+ @Override
+ public void buildIR(IRBuilder builder) {
+ builder.addNewUnboxedEnumInstance(AA, getType(), ordinal);
+ }
+
+ @Override
+ public boolean canThrow() {
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryKeepRuleGenerator.java b/src/main/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryKeepRuleGenerator.java
index 6386b61..c7c920b 100644
--- a/src/main/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryKeepRuleGenerator.java
+++ b/src/main/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryKeepRuleGenerator.java
@@ -13,7 +13,7 @@
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.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.references.ArrayReference;
import com.android.tools.r8.references.ClassReference;
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 3d1a246..49cab56 100644
--- a/src/main/java/com/android/tools/r8/graph/CfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/CfCode.java
@@ -5,7 +5,6 @@
import static com.android.tools.r8.graph.DexCode.FAKE_THIS_PREFIX;
import static com.android.tools.r8.graph.DexCode.FAKE_THIS_SUFFIX;
-import static org.objectweb.asm.Opcodes.ACC_STATIC;
import com.android.tools.r8.cf.CfPrinter;
import com.android.tools.r8.cf.CfVersion;
@@ -896,15 +895,11 @@
private Int2ReferenceSortedMap<FrameType> computeInitialLocals(
DexType context, DexEncodedMethod method, RewrittenPrototypeDescription protoTypeChanges) {
- int accessFlags =
- protoTypeChanges.isEmpty()
- ? method.accessFlags.modifiedFlags
- : method.accessFlags.originalFlags;
Int2ReferenceSortedMap<FrameType> initialLocals = new Int2ReferenceAVLTreeMap<>();
int index = 0;
if (method.isInstanceInitializer()) {
initialLocals.put(index++, FrameType.uninitializedThis());
- } else if (!MethodAccessFlags.isSet(ACC_STATIC, accessFlags)) {
+ } else if (!method.getAccessFlags().isStatic()) {
initialLocals.put(index++, FrameType.initialized(context));
}
ArgumentInfoCollection argumentsInfo = protoTypeChanges.getArgumentInfoCollection();
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index 41dd2c1..40b630c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -237,7 +237,7 @@
return methodCollection.removeMethod(method);
}
- public void setDirectMethods(List<DexEncodedMethod> methods) {
+ public void setDirectMethods(Collection<DexEncodedMethod> methods) {
setDirectMethods(methods.toArray(DexEncodedMethod.EMPTY_ARRAY));
}
@@ -323,6 +323,15 @@
instanceFields(predicate).forEach(consumer);
}
+ public void forEachStaticField(Consumer<DexEncodedField> consumer) {
+ forEachStaticFieldMatching(alwaysTrue(), consumer);
+ }
+
+ public void forEachStaticFieldMatching(
+ Predicate<DexEncodedField> predicate, Consumer<DexEncodedField> consumer) {
+ staticFields(predicate).forEach(consumer);
+ }
+
public TraversalContinuation traverseFields(Function<DexEncodedField, TraversalContinuation> fn) {
for (DexEncodedField field : fields()) {
if (fn.apply(field).shouldBreak()) {
@@ -366,6 +375,12 @@
assert verifyNoDuplicateFields();
}
+ public DexEncodedField[] clearStaticFields() {
+ DexEncodedField[] previousFields = staticFields;
+ setStaticFields(DexEncodedField.EMPTY_ARRAY);
+ return previousFields;
+ }
+
public void removeStaticField(int index) {
DexEncodedField[] newFields = new DexEncodedField[staticFields.length - 1];
System.arraycopy(staticFields, 0, newFields, 0, index);
@@ -385,6 +400,10 @@
assert verifyNoDuplicateFields();
}
+ public void setStaticFields(Collection<DexEncodedField> fields) {
+ setStaticFields(fields.toArray(DexEncodedField.EMPTY_ARRAY));
+ }
+
public boolean definesStaticField(DexField field) {
for (DexEncodedField encodedField : staticFields()) {
if (encodedField.getReference() == field) {
@@ -448,8 +467,10 @@
assert verifyNoDuplicateFields();
}
- public void clearInstanceFields() {
+ public DexEncodedField[] clearInstanceFields() {
+ DexEncodedField[] previousFields = instanceFields;
instanceFields = DexEncodedField.EMPTY_ARRAY;
+ return previousFields;
}
private boolean verifyCorrectnessOfFieldHolder(DexEncodedField field) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
index da8323c..9e1b306 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -401,6 +401,11 @@
return this;
}
+ public Builder modifyAccessFlags(Consumer<FieldAccessFlags> consumer) {
+ consumer.accept(accessFlags);
+ return this;
+ }
+
public Builder setAbstractValue(
AbstractValue abstractValue, AppView<AppInfoWithLiveness> appView) {
return addBuildConsumer(
@@ -409,6 +414,15 @@
.recordFieldHasAbstractValue(fixedUpField, appView, abstractValue));
}
+ public Builder clearAnnotations() {
+ return setAnnotations(DexAnnotationSet.empty());
+ }
+
+ public Builder setAnnotations(DexAnnotationSet annotations) {
+ this.annotations = annotations;
+ return this;
+ }
+
private Builder addBuildConsumer(Consumer<DexEncodedField> consumer) {
this.buildConsumer = this.buildConsumer.andThen(consumer);
return this;
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 36cb33c..0dc90ad 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -222,7 +222,8 @@
assert defaultInterfaceMethodImplementation == null;
assert implementation != null;
assert code != null;
- assert code == implementation.getCode();
+ // TODO(b/183998768): Once R8 desugars in the enqueuer this should always be invalid code.
+ assert InvalidCode.isInvalidCode(code) || code == implementation.getCode();
accessFlags.setAbstract();
removeCode();
defaultInterfaceMethodImplementation = implementation;
@@ -1588,6 +1589,28 @@
!isLibraryMethodOverride.isUnknown(), isLibraryMethodOverride);
}
+ public Builder unsetIsLibraryMethodOverride() {
+ this.isLibraryMethodOverride = OptionalBool.UNKNOWN;
+ return this;
+ }
+
+ public Builder clearAnnotations() {
+ return setAnnotations(DexAnnotationSet.empty());
+ }
+
+ public Builder clearParameterAnnotations() {
+ return setParameterAnnotations(ParameterAnnotationsList.empty());
+ }
+
+ public Builder clearAllAnnotations() {
+ return clearAnnotations().clearParameterAnnotations();
+ }
+
+ public Builder setAnnotations(DexAnnotationSet annotations) {
+ this.annotations = annotations;
+ return this;
+ }
+
public Builder setParameterAnnotations(ParameterAnnotationsList parameterAnnotations) {
this.parameterAnnotations = parameterAnnotations;
return this;
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 d7a48c1..25cf31b 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -393,6 +393,8 @@
public final DexType stringBuilderType = createStaticallyKnownType(stringBuilderDescriptor);
public final DexType stringBufferType = createStaticallyKnownType(stringBufferDescriptor);
+ public final DexType javaLangAnnotationRetentionPolicyType =
+ createStaticallyKnownType("Ljava/lang/annotation/RetentionPolicy;");
public final DexType javaLangReflectArrayType =
createStaticallyKnownType("Ljava/lang/reflect/Array;");
public final DexType javaLangSystemType = createStaticallyKnownType(javaLangSystemDescriptor);
@@ -569,6 +571,8 @@
public final EnumMembers enumMembers = new EnumMembers();
public final JavaLangReflectArrayMembers javaLangReflectArrayMembers =
new JavaLangReflectArrayMembers();
+ public final JavaLangAnnotationRetentionPolicyMembers javaLangAnnotationRetentionPolicyMembers =
+ new JavaLangAnnotationRetentionPolicyMembers();
public final JavaLangSystemMethods javaLangSystemMethods = new JavaLangSystemMethods();
public final NullPointerExceptionMethods npeMethods = new NullPointerExceptionMethods();
public final IllegalArgumentExceptionMethods illegalArgumentExceptionMethods =
@@ -1499,6 +1503,15 @@
}
}
+ public class JavaLangAnnotationRetentionPolicyMembers {
+
+ public final DexField CLASS =
+ createField(
+ javaLangAnnotationRetentionPolicyType, javaLangAnnotationRetentionPolicyType, "CLASS");
+
+ private JavaLangAnnotationRetentionPolicyMembers() {}
+ }
+
public class JavaLangReflectArrayMembers {
public final DexMethod newInstanceMethodWithDimensions =
@@ -1533,6 +1546,8 @@
public final DexMethod nameMethod;
public final DexMethod toString;
public final DexMethod compareTo;
+ public final DexMethod compareToWithObject =
+ createMethod(enumType, createProto(intType, objectType), "compareTo");
public final DexMethod equals;
public final DexMethod hashCode;
@@ -2208,33 +2223,28 @@
}
/**
- * Tries to find a method name for insertion into the class {@code target} of the form baseName$n,
+ * Tries to find a method name for insertion into the class {@code holder} of the form baseName$n,
* where {@code baseName} is supplied by the user, and {@code n} is picked to be the first number
* so that {@code isFresh.apply(method)} returns {@code true}.
*/
- public DexField createFreshFieldName(DexField template, Predicate<DexField> isFresh) {
- return internalCreateFreshFieldName(template, null, isFresh);
- }
-
- /**
- * Tries to find a method name for insertion into the class {@code target} of the form
- * baseName$holder$n, where {@code baseName} and {@code holder} are supplied by the user, and
- * {@code n} is picked to be the first number so that {@code isFresh.apply(method)} returns {@code
- * true}.
- *
- * @param holder indicates where the method originates from.
- */
- public DexField createFreshFieldNameWithHolderSuffix(
- DexField template, DexType holder, Predicate<DexField> isFresh) {
- return internalCreateFreshFieldName(template, holder, isFresh);
+ public DexField createFreshFieldNameWithoutHolder(
+ DexType holder, DexType type, String baseName, Predicate<DexField> isFresh) {
+ return internalCreateFreshFieldName(null, holder, type, baseName, isFresh);
}
private DexField internalCreateFreshFieldName(
- DexField template, DexType holder, Predicate<DexField> isFresh) {
+ DexType originalHolder,
+ DexType newHolder,
+ DexType type,
+ String baseName,
+ Predicate<DexField> isFresh) {
return createFreshMember(
- name -> Optional.of(template.withName(name, this)).filter(isFresh),
- template.name.toSourceString(),
- holder);
+ name -> {
+ DexField candidate = createField(newHolder, type, name);
+ return isFresh.test(candidate) ? Optional.of(candidate) : Optional.empty();
+ },
+ baseName,
+ originalHolder);
}
public DexMethod createClassInitializer(DexType holder) {
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 d46c103..5b3456c 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -206,6 +206,10 @@
forEachInstanceField(field -> consumer.accept(new ProgramField(this, field)));
}
+ public void forEachProgramStaticField(Consumer<? super ProgramField> consumer) {
+ forEachStaticField(field -> consumer.accept(new ProgramField(this, field)));
+ }
+
public void forEachProgramMember(Consumer<? super ProgramMember<?, ?>> consumer) {
forEachProgramField(consumer);
forEachProgramMethod(consumer);
diff --git a/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java b/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
index dbc2119..554cadc 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldResolutionResult.java
@@ -30,6 +30,10 @@
return null;
}
+ public ProgramField getProgramField() {
+ return null;
+ }
+
public boolean isSuccessfulResolution() {
return false;
}
@@ -102,6 +106,13 @@
}
@Override
+ public ProgramField getProgramField() {
+ return resolvedHolder.isProgramClass()
+ ? new ProgramField(resolvedHolder.asProgramClass(), resolvedField)
+ : null;
+ }
+
+ @Override
public OptionalBool isAccessibleFrom(
ProgramDefinition context, AppInfoWithClassHierarchy appInfo) {
return AccessControl.isMemberAccessible(this, context, appInfo);
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
index 457ef38..254f5a4 100644
--- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
@@ -375,6 +375,13 @@
}
}
+ public void injectInterfaces(
+ DexDefinitionSupplier definitions, DexProgramClass clazz, List<DexClass> newInterfaces) {
+ for (DexClass newInterface : newInterfaces) {
+ populateInstantiatedHierarchy(definitions, newInterface.type, clazz);
+ }
+ }
+
private void populateInstantiatedHierarchy(DexDefinitionSupplier definitions, DexClass clazz) {
if (clazz.superType != null) {
populateInstantiatedHierarchy(definitions, clazz.superType, clazz);
diff --git a/src/main/java/com/android/tools/r8/graph/PrunedItems.java b/src/main/java/com/android/tools/r8/graph/PrunedItems.java
index 7441412..1f8aa68 100644
--- a/src/main/java/com/android/tools/r8/graph/PrunedItems.java
+++ b/src/main/java/com/android/tools/r8/graph/PrunedItems.java
@@ -14,16 +14,22 @@
private final Set<DexReference> additionalPinnedItems;
private final Set<DexType> noLongerSyntheticItems;
private final Set<DexType> removedClasses;
+ private final Set<DexField> removedFields;
+ private final Set<DexMethod> removedMethods;
private PrunedItems(
DexApplication prunedApp,
Set<DexReference> additionalPinnedItems,
Set<DexType> noLongerSyntheticItems,
- Set<DexType> removedClasses) {
+ Set<DexType> removedClasses,
+ Set<DexField> removedFields,
+ Set<DexMethod> removedMethods) {
this.prunedApp = prunedApp;
this.additionalPinnedItems = additionalPinnedItems;
this.noLongerSyntheticItems = noLongerSyntheticItems;
this.removedClasses = removedClasses;
+ this.removedFields = removedFields;
+ this.removedMethods = removedMethods;
}
public static Builder builder() {
@@ -38,6 +44,14 @@
return removedClasses.isEmpty() && additionalPinnedItems.isEmpty();
}
+ public boolean isRemoved(DexField field) {
+ return removedFields.contains(field);
+ }
+
+ public boolean isRemoved(DexMethod method) {
+ return removedMethods.contains(method);
+ }
+
public boolean isRemoved(DexType type) {
return removedClasses.contains(type);
}
@@ -62,6 +76,10 @@
return removedClasses;
}
+ public Set<DexMethod> getRemovedMethods() {
+ return removedMethods;
+ }
+
public static class Builder {
private DexApplication prunedApp;
@@ -69,6 +87,8 @@
private final Set<DexReference> additionalPinnedItems = Sets.newIdentityHashSet();
private final Set<DexType> noLongerSyntheticItems = Sets.newIdentityHashSet();
private final Set<DexType> removedClasses = Sets.newIdentityHashSet();
+ private final Set<DexField> removedFields = Sets.newIdentityHashSet();
+ private final Set<DexMethod> removedMethods = Sets.newIdentityHashSet();
public Builder setPrunedApp(DexApplication prunedApp) {
this.prunedApp = prunedApp;
@@ -92,9 +112,24 @@
return this;
}
+ public Builder addRemovedField(DexField removedField) {
+ removedFields.add(removedField);
+ return this;
+ }
+
+ public Builder addRemovedMethod(DexMethod removedMethod) {
+ removedMethods.add(removedMethod);
+ return this;
+ }
+
public PrunedItems build() {
return new PrunedItems(
- prunedApp, additionalPinnedItems, noLongerSyntheticItems, removedClasses);
+ prunedApp,
+ additionalPinnedItems,
+ noLongerSyntheticItems,
+ removedClasses,
+ removedFields,
+ removedMethods);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/UseRegistry.java b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
index 708a02f..c9c0bc9 100644
--- a/src/main/java/com/android/tools/r8/graph/UseRegistry.java
+++ b/src/main/java/com/android/tools/r8/graph/UseRegistry.java
@@ -55,6 +55,10 @@
registerTypeReference(type);
}
+ public void registerNewUnboxedEnumInstance(DexType type) {
+ registerTypeReference(type);
+ }
+
public abstract void registerStaticFieldRead(DexField field);
public void registerStaticFieldReadFromMethodHandle(DexField field) {
diff --git a/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java b/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java
index ee41edb..28d6be7 100644
--- a/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java
+++ b/src/main/java/com/android/tools/r8/graph/analysis/DesugaredLibraryConversionWrapperAnalysis.java
@@ -8,8 +8,8 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter.Mode;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter.Mode;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import java.util.function.Consumer;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
index d1e98c7..db0178c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -312,8 +312,8 @@
void mergeStaticFields() {
group.forEachSource(classStaticFieldsMerger::addFields);
- classStaticFieldsMerger.merge(group.getTarget());
- group.forEachSource(clazz -> clazz.setStaticFields(null));
+ classStaticFieldsMerger.merge();
+ group.forEachSource(DexClass::clearStaticFields);
}
public void mergeGroup(
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassStaticFieldsMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassStaticFieldsMerger.java
index 80180f6..f94d407 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassStaticFieldsMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassStaticFieldsMerger.java
@@ -14,36 +14,41 @@
import java.util.Map;
public class ClassStaticFieldsMerger {
- private final Builder lensBuilder;
- private final MergeGroup group;
- private final Map<DexField, DexEncodedField> targetFields = new LinkedHashMap<>();
+
private final DexItemFactory dexItemFactory;
+ private final MergeGroup group;
+ private final Builder lensBuilder;
+
+ private final Map<DexField, DexEncodedField> targetFields = new LinkedHashMap<>();
public ClassStaticFieldsMerger(
AppView<?> appView, HorizontalClassMergerGraphLens.Builder lensBuilder, MergeGroup group) {
- this.lensBuilder = lensBuilder;
-
- this.group = group;
- // Add mappings for all target fields.
- group
- .getTarget()
- .staticFields()
- .forEach(field -> targetFields.put(field.getReference(), field));
-
this.dexItemFactory = appView.dexItemFactory();
+ this.group = group;
+ this.lensBuilder = lensBuilder;
}
private boolean isFresh(DexField fieldReference) {
- return !targetFields.containsKey(fieldReference);
+ if (group.getTarget().lookupField(fieldReference) != null) {
+ // The target class has an instance or static field with the given reference.
+ return false;
+ }
+ if (targetFields.containsKey(fieldReference)) {
+ // We have already committed another static field from a source class in the merge group to
+ // the given field reference (but the field is not yet added to the target class).
+ return false;
+ }
+ return true;
}
private void addField(DexEncodedField field) {
DexField oldFieldReference = field.getReference();
- DexField templateReference =
- field.getReference().withHolder(group.getTarget().getType(), dexItemFactory);
DexField newFieldReference =
- dexItemFactory.createFreshFieldNameWithHolderSuffix(
- templateReference, field.getHolderType(), this::isFresh);
+ dexItemFactory.createFreshFieldNameWithoutHolder(
+ group.getTarget().getType(),
+ field.getType(),
+ field.getName().toString(),
+ this::isFresh);
field = field.toTypeSubstitutedField(newFieldReference);
targetFields.put(newFieldReference, field);
@@ -52,10 +57,10 @@
}
public void addFields(DexProgramClass toMerge) {
- toMerge.staticFields().forEach(this::addField);
+ toMerge.forEachStaticField(this::addField);
}
- public void merge(DexProgramClass clazz) {
- clazz.setStaticFields(targetFields.values().toArray(DexEncodedField.EMPTY_ARRAY));
+ public void merge() {
+ group.getTarget().appendStaticFields(targetFields.values());
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
index 20eb5a3..2b12d6d 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -5,7 +5,9 @@
package com.android.tools.r8.horizontalclassmerging;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMember;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.NestedGraphLens;
@@ -15,6 +17,7 @@
import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeHashMap;
import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap;
+import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneMap;
import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneRepresentativeMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Streams;
@@ -83,18 +86,21 @@
public static class Builder {
- private final MutableBidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap =
- BidirectionalManyToOneRepresentativeHashMap.newIdentityHashMap();
- private final BidirectionalManyToOneHashMap<DexMethod, DexMethod> methodMap =
+ private final MutableBidirectionalManyToOneRepresentativeMap<DexField, DexField>
+ newFieldSignatures = BidirectionalManyToOneRepresentativeHashMap.newIdentityHashMap();
+ private final MutableBidirectionalManyToOneMap<DexMethod, DexMethod> methodMap =
BidirectionalManyToOneHashMap.newIdentityHashMap();
- private final BidirectionalManyToOneRepresentativeHashMap<DexMethod, DexMethod>
+ private final MutableBidirectionalManyToOneRepresentativeMap<DexMethod, DexMethod>
newMethodSignatures = BidirectionalManyToOneRepresentativeHashMap.newIdentityHashMap();
private final Map<DexMethod, List<ExtraParameter>> methodExtraParameters =
new IdentityHashMap<>();
- private final BidirectionalManyToOneHashMap<DexMethod, DexMethod> pendingMethodMapUpdates =
+ private final MutableBidirectionalManyToOneMap<DexMethod, DexMethod> pendingMethodMapUpdates =
BidirectionalManyToOneHashMap.newIdentityHashMap();
- private final BidirectionalManyToOneRepresentativeHashMap<DexMethod, DexMethod>
+ private final MutableBidirectionalManyToOneRepresentativeMap<DexField, DexField>
+ pendingNewFieldSignatureUpdates =
+ BidirectionalManyToOneRepresentativeHashMap.newIdentityHashMap();
+ private final MutableBidirectionalManyToOneRepresentativeMap<DexMethod, DexMethod>
pendingNewMethodSignatureUpdates =
BidirectionalManyToOneRepresentativeHashMap.newIdentityHashMap();
@@ -103,6 +109,7 @@
HorizontalClassMergerGraphLens build(
AppView<?> appView, HorizontallyMergedClasses mergedClasses) {
assert pendingMethodMapUpdates.isEmpty();
+ assert pendingNewFieldSignatureUpdates.isEmpty();
assert pendingNewMethodSignatureUpdates.isEmpty();
assert newMethodSignatures.values().stream()
.allMatch(
@@ -115,7 +122,7 @@
appView,
mergedClasses,
methodExtraParameters,
- fieldMap,
+ newFieldSignatures,
methodMap.getForwardMap(),
newMethodSignatures);
}
@@ -126,7 +133,7 @@
}
void recordNewFieldSignature(DexField oldFieldSignature, DexField newFieldSignature) {
- fieldMap.put(oldFieldSignature, newFieldSignature);
+ newFieldSignatures.put(oldFieldSignature, newFieldSignature);
}
void recordNewFieldSignature(
@@ -135,26 +142,20 @@
DexField representative) {
assert Streams.stream(oldFieldSignatures)
.anyMatch(oldFieldSignature -> oldFieldSignature != newFieldSignature);
- assert Streams.stream(oldFieldSignatures).noneMatch(fieldMap::containsValue);
+ assert Streams.stream(oldFieldSignatures).noneMatch(newFieldSignatures::containsValue);
assert Iterables.contains(oldFieldSignatures, representative);
for (DexField oldFieldSignature : oldFieldSignatures) {
recordNewFieldSignature(oldFieldSignature, newFieldSignature);
}
- fieldMap.setRepresentative(newFieldSignature, representative);
+ newFieldSignatures.setRepresentative(newFieldSignature, representative);
}
void fixupField(DexField oldFieldSignature, DexField newFieldSignature) {
- DexField representative = fieldMap.removeRepresentativeFor(oldFieldSignature);
- Set<DexField> originalFieldSignatures = fieldMap.removeValue(oldFieldSignature);
- if (originalFieldSignatures.isEmpty()) {
- fieldMap.put(oldFieldSignature, newFieldSignature);
- } else if (originalFieldSignatures.size() == 1) {
- fieldMap.put(originalFieldSignatures, newFieldSignature);
- } else {
- assert representative != null;
- fieldMap.put(originalFieldSignatures, newFieldSignature);
- fieldMap.setRepresentative(newFieldSignature, representative);
- }
+ fixupOriginalMemberSignatures(
+ oldFieldSignature,
+ newFieldSignature,
+ newFieldSignatures,
+ pendingNewFieldSignatureUpdates);
}
void mapMethod(DexMethod oldMethodSignature, DexMethod newMethodSignature) {
@@ -195,7 +196,11 @@
void fixupMethod(DexMethod oldMethodSignature, DexMethod newMethodSignature) {
fixupMethodMap(oldMethodSignature, newMethodSignature);
- fixupOriginalMethodSignatures(oldMethodSignature, newMethodSignature);
+ fixupOriginalMemberSignatures(
+ oldMethodSignature,
+ newMethodSignature,
+ newMethodSignatures,
+ pendingNewMethodSignatureUpdates);
}
private void fixupMethodMap(DexMethod oldMethodSignature, DexMethod newMethodSignature) {
@@ -209,18 +214,22 @@
}
}
- private void fixupOriginalMethodSignatures(
- DexMethod oldMethodSignature, DexMethod newMethodSignature) {
- Set<DexMethod> oldMethodSignatures = newMethodSignatures.getKeys(oldMethodSignature);
- if (oldMethodSignatures.isEmpty()) {
- pendingNewMethodSignatureUpdates.put(oldMethodSignature, newMethodSignature);
+ private <D extends DexEncodedMember<D, R>, R extends DexMember<D, R>>
+ void fixupOriginalMemberSignatures(
+ R oldMemberSignature,
+ R newMemberSignature,
+ MutableBidirectionalManyToOneRepresentativeMap<R, R> newMemberSignatures,
+ MutableBidirectionalManyToOneRepresentativeMap<R, R> pendingNewMemberSignatureUpdates) {
+ Set<R> oldMemberSignatures = newMemberSignatures.getKeys(oldMemberSignature);
+ if (oldMemberSignatures.isEmpty()) {
+ pendingNewMemberSignatureUpdates.put(oldMemberSignature, newMemberSignature);
} else {
- for (DexMethod originalMethodSignature : oldMethodSignatures) {
- pendingNewMethodSignatureUpdates.put(originalMethodSignature, newMethodSignature);
+ for (R originalMethodSignature : oldMemberSignatures) {
+ pendingNewMemberSignatureUpdates.put(originalMethodSignature, newMemberSignature);
}
- DexMethod representative = newMethodSignatures.getRepresentativeKey(oldMethodSignature);
+ R representative = newMemberSignatures.getRepresentativeKey(oldMemberSignature);
if (representative != null) {
- pendingNewMethodSignatureUpdates.setRepresentative(newMethodSignature, representative);
+ pendingNewMemberSignatureUpdates.setRepresentative(newMemberSignature, representative);
}
}
}
@@ -231,16 +240,24 @@
pendingMethodMapUpdates.forEachManyToOneMapping(methodMap::put);
pendingMethodMapUpdates.clear();
- // Commit pending original method signatures updates.
- newMethodSignatures.removeAll(pendingNewMethodSignatureUpdates.keySet());
- pendingNewMethodSignatureUpdates.forEachManyToOneMapping(
+ // Commit pending original field and method signatures updates.
+ commitPendingNewMemberSignatureUpdates(newFieldSignatures, pendingNewFieldSignatureUpdates);
+ commitPendingNewMemberSignatureUpdates(newMethodSignatures, pendingNewMethodSignatureUpdates);
+ }
+
+ private <D extends DexEncodedMember<D, R>, R extends DexMember<D, R>>
+ void commitPendingNewMemberSignatureUpdates(
+ MutableBidirectionalManyToOneRepresentativeMap<R, R> newMemberSignatures,
+ MutableBidirectionalManyToOneRepresentativeMap<R, R> pendingNewMemberSignatureUpdates) {
+ newMemberSignatures.removeAll(pendingNewMemberSignatureUpdates.keySet());
+ pendingNewMemberSignatureUpdates.forEachManyToOneMapping(
(keys, value, representative) -> {
- newMethodSignatures.put(keys, value);
+ newMemberSignatures.put(keys, value);
if (keys.size() > 1) {
- newMethodSignatures.setRepresentative(value, representative);
+ newMemberSignatures.setRepresentative(value, representative);
}
});
- pendingNewMethodSignatureUpdates.clear();
+ pendingNewMemberSignatureUpdates.clear();
}
/**
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
index 4abddc6..89dcd0a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/PolicyScheduler.java
@@ -150,12 +150,11 @@
RuntimeTypeCheckInfo runtimeTypeCheckInfo) {
ImmutableList.Builder<Policy> builder = ImmutableList.builder();
- addRequiredMultiClassPolicies(appView, mode, builder);
+ addRequiredMultiClassPolicies(appView, mode, runtimeTypeCheckInfo, builder);
if (!appView.options().horizontalClassMergerOptions().isRestrictedToSynthetics()) {
AppView<AppInfoWithLiveness> appViewWithLiveness = appView.withLiveness();
- addMultiClassPoliciesForMergingNonSyntheticClasses(
- appViewWithLiveness, runtimeTypeCheckInfo, builder);
+ addMultiClassPoliciesForMergingNonSyntheticClasses(appViewWithLiveness, builder);
}
if (mode.isInitial()) {
@@ -189,6 +188,7 @@
private static void addRequiredMultiClassPolicies(
AppView<? extends AppInfoWithClassHierarchy> appView,
Mode mode,
+ RuntimeTypeCheckInfo runtimeTypeCheckInfo,
ImmutableList.Builder<Policy> builder) {
builder.add(
new CheckAbstractClasses(appView),
@@ -201,15 +201,14 @@
new SyntheticItemsPolicy(appView, mode),
new RespectPackageBoundaries(appView),
new NoDifferentApiReferenceLevel(appView),
+ new NoIndirectRuntimeTypeChecks(appView, runtimeTypeCheckInfo),
new PreventClassMethodAndDefaultMethodCollisions(appView));
}
private static void addMultiClassPoliciesForMergingNonSyntheticClasses(
AppView<AppInfoWithLiveness> appView,
- RuntimeTypeCheckInfo runtimeTypeCheckInfo,
ImmutableList.Builder<Policy> builder) {
- builder.add(
- new NoDeadLocks(appView), new NoIndirectRuntimeTypeChecks(appView, runtimeTypeCheckInfo));
+ builder.add(new NoDeadLocks(appView));
}
private static void addMultiClassPoliciesForInterfaceMerging(
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
index 442489d..b85eab3 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -7,7 +7,6 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass.FieldSetter;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
@@ -22,6 +21,7 @@
import com.android.tools.r8.graph.TreeFixerBase;
import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
import com.android.tools.r8.shaking.AnnotationFixer;
+import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.OptionalBool;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
@@ -168,8 +168,11 @@
.getMethodCollection()
.replaceAllDirectMethods(method -> fixupDirectMethod(newMethodReferences, method));
- fixupFields(clazz.staticFields(), clazz::setStaticField);
- fixupFields(clazz.instanceFields(), clazz::setInstanceField);
+ Set<DexField> newFieldReferences = Sets.newIdentityHashSet();
+ DexEncodedField[] instanceFields = clazz.clearInstanceFields();
+ DexEncodedField[] staticFields = clazz.clearStaticFields();
+ clazz.setInstanceFields(fixupFields(instanceFields, newFieldReferences));
+ clazz.setStaticFields(fixupFields(staticFields, newFieldReferences));
lensBuilder.commitPendingUpdates();
@@ -219,8 +222,13 @@
.getMethodCollection()
.replaceDirectMethods(method -> fixupDirectMethod(newDirectMethods, method));
iface.getMethodCollection().replaceVirtualMethods(this::fixupVirtualInterfaceMethod);
- fixupFields(iface.staticFields(), iface::setStaticField);
- fixupFields(iface.instanceFields(), iface::setInstanceField);
+
+ assert !iface.hasInstanceFields();
+
+ Set<DexField> newFieldReferences = Sets.newIdentityHashSet();
+ DexEncodedField[] staticFields = iface.clearStaticFields();
+ iface.setStaticFields(fixupFields(staticFields, newFieldReferences));
+
lensBuilder.commitPendingUpdates();
}
@@ -371,35 +379,40 @@
return fixupProgramMethod(newMethodReference, method);
}
- private void fixupFields(List<DexEncodedField> fields, FieldSetter setter) {
- if (fields == null) {
- return;
+ private DexEncodedField[] fixupFields(
+ DexEncodedField[] fields, Set<DexField> newFieldReferences) {
+ if (fields == null || ArrayUtils.isEmpty(fields)) {
+ return DexEncodedField.EMPTY_ARRAY;
}
- Set<DexField> existingFields = Sets.newIdentityHashSet();
- for (int i = 0; i < fields.size(); i++) {
- DexEncodedField oldField = fields.get(i);
+ DexEncodedField[] newFields = new DexEncodedField[fields.length];
+ for (int i = 0; i < fields.length; i++) {
+ DexEncodedField oldField = fields[i];
DexField oldFieldReference = oldField.getReference();
DexField newFieldReference = fixupFieldReference(oldFieldReference);
// Rename the field if it already exists.
- if (!existingFields.add(newFieldReference)) {
+ if (!newFieldReferences.add(newFieldReference)) {
DexField template = newFieldReference;
newFieldReference =
dexItemFactory.createFreshMember(
tryName ->
Optional.of(template.withName(tryName, dexItemFactory))
- .filter(tryMethod -> !existingFields.contains(tryMethod)),
+ .filter(tryMethod -> !newFieldReferences.contains(tryMethod)),
newFieldReference.name.toSourceString());
- boolean added = existingFields.add(newFieldReference);
+ boolean added = newFieldReferences.add(newFieldReference);
assert added;
}
if (newFieldReference != oldFieldReference) {
lensBuilder.fixupField(oldFieldReference, newFieldReference);
- setter.setField(i, oldField.toTypeSubstitutedField(newFieldReference));
+ newFields[i] = oldField.toTypeSubstitutedField(newFieldReference);
+ } else {
+ newFields[i] = oldField;
}
}
+
+ return newFields;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java
index eb26afa..c791889 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/InitializedClassesOnNormalExitAnalysis.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.NewUnboxedEnumInstance;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.Sets;
import java.util.ArrayList;
@@ -153,5 +154,11 @@
markInitializedOnNormalExit(instruction.clazz);
return null;
}
+
+ @Override
+ public Void visit(NewUnboxedEnumInstance instruction) {
+ assert false;
+ return null;
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
index 2cf0214..d870dc7 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.DexEncodedField;
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.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
@@ -23,6 +24,8 @@
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeNewArray;
+import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.LogicalBinop;
import com.android.tools.r8.ir.code.NewArrayEmpty;
import com.android.tools.r8.ir.code.NewArrayFilledData;
import com.android.tools.r8.ir.code.NewInstance;
@@ -77,10 +80,12 @@
private final AppView<?> appView;
private final ProgramMethod context;
+ private final DexItemFactory dexItemFactory;
public ValueMayDependOnEnvironmentAnalysis(AppView<?> appView, IRCode code) {
this.appView = appView;
this.context = code.context();
+ this.dexItemFactory = appView.dexItemFactory();
}
public boolean anyValueMayDependOnEnvironment(Iterable<Value> values) {
@@ -146,6 +151,10 @@
return addConstantValueToValueGraph(value)
|| addArrayValueToValueGraph(
value, node, graph, consumedInstructions, mutableValues, worklist)
+ || addInvokeVirtualValueToValueGraph(
+ value, node, graph, consumedInstructions, mutableValues, worklist)
+ || addLogicalBinopValueToValueGraph(
+ value, node, graph, consumedInstructions, mutableValues, worklist)
|| addNewInstanceValueToValueGraph(
value, node, graph, consumedInstructions, mutableValues, worklist);
}
@@ -237,6 +246,54 @@
return true;
}
+ private boolean addInvokeVirtualValueToValueGraph(
+ Value value,
+ Node node,
+ ValueGraph graph,
+ Set<Instruction> consumedInstructions,
+ Set<Value> mutableValues,
+ WorkList<Value> worklist) {
+ if (!value.isDefinedByInstructionSatisfying(Instruction::isInvokeVirtual)) {
+ return false;
+ }
+
+ InvokeVirtual invoke = value.getDefinition().asInvokeVirtual();
+ if (invoke.getInvokedMethod() == dexItemFactory.classMethods.desiredAssertionStatus) {
+ // We treat the value from calling MyClass.class.desiredAssertionStatus() as being independent
+ // of the environment if MyClass is not pinned.
+ return isNonPinnedClassConstant(invoke.getReceiver());
+ }
+
+ return false;
+ }
+
+ private boolean isNonPinnedClassConstant(Value value) {
+ Value root = value.getAliasedValue();
+ return root.isDefinedByInstructionSatisfying(Instruction::isConstClass)
+ && !appView.getKeepInfo().isPinned(root.getDefinition().asConstClass().getType(), appView);
+ }
+
+ private boolean addLogicalBinopValueToValueGraph(
+ Value value,
+ Node node,
+ ValueGraph graph,
+ Set<Instruction> consumedInstructions,
+ Set<Value> mutableValues,
+ WorkList<Value> worklist) {
+ if (!value.isDefinedByInstructionSatisfying(Instruction::isLogicalBinop)) {
+ return false;
+ }
+
+ // The result of a logical binop depends on the environment if any of the operands does.
+ LogicalBinop logicalBinop = value.getDefinition().asLogicalBinop();
+ for (Value inValue : logicalBinop.inValues()) {
+ graph.addDirectedEdge(node, graph.createNodeIfAbsent(inValue));
+ worklist.addIfNotSeen(inValue);
+ }
+
+ return true;
+ }
+
private boolean addNewInstanceValueToValueGraph(
Value value,
Node node,
@@ -255,8 +312,7 @@
}
// Find the single constructor invocation.
- InvokeDirect constructorInvoke =
- newInstance.getUniqueConstructorInvoke(appView.dexItemFactory());
+ InvokeDirect constructorInvoke = newInstance.getUniqueConstructorInvoke(dexItemFactory);
if (constructorInvoke == null || constructorInvoke.getInvokedMethod().holder != clazz.type) {
// Didn't find a (valid) constructor invocation, give up.
return false;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java
index 7d13544..a5977df 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
/**
* Implements a lifted subset lattice for fields.
@@ -70,5 +71,6 @@
return lessThanOrEqual(other) && !equals(other);
}
- public abstract AbstractFieldSet rewrittenWithLens(AppView<?> appView, GraphLens lens);
+ public abstract AbstractFieldSet rewrittenWithLens(
+ AppView<?> appView, GraphLens lens, PrunedItems prunedItems);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
index f84e13d..cdb9eb9 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.SetUtils;
import com.google.common.collect.Sets;
@@ -69,10 +70,14 @@
}
@Override
- public AbstractFieldSet rewrittenWithLens(AppView<?> appView, GraphLens lens) {
+ public AbstractFieldSet rewrittenWithLens(
+ AppView<?> appView, GraphLens lens, PrunedItems prunedItems) {
assert !isEmpty();
ConcreteMutableFieldSet rewrittenSet = new ConcreteMutableFieldSet();
for (DexEncodedField field : fields) {
+ if (prunedItems.isRemoved(field.getReference())) {
+ continue;
+ }
DexField rewrittenFieldReference = lens.lookupField(field.getReference());
DexClass holder = appView.definitionForHolder(rewrittenFieldReference);
DexEncodedField rewrittenField = rewrittenFieldReference.lookupOnClass(holder);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java
index 9a7c7aa..d5d694f 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
public class EmptyFieldSet extends AbstractFieldSet implements KnownFieldSet {
@@ -39,7 +40,8 @@
}
@Override
- public AbstractFieldSet rewrittenWithLens(AppView<?> appView, GraphLens lens) {
+ public AbstractFieldSet rewrittenWithLens(
+ AppView<?> appView, GraphLens lens, PrunedItems prunedItems) {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java
index 75a1bab..32ddba9 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
public class UnknownFieldSet extends AbstractFieldSet {
@@ -34,7 +35,8 @@
}
@Override
- public AbstractFieldSet rewrittenWithLens(AppView<?> appView, GraphLens lens) {
+ public AbstractFieldSet rewrittenWithLens(
+ AppView<?> appView, GraphLens lens, PrunedItems prunedItems) {
return this;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/AlwaysSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/AlwaysSimpleInliningConstraint.java
index a164607..93cdb16 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/AlwaysSimpleInliningConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/AlwaysSimpleInliningConstraint.java
@@ -36,7 +36,8 @@
}
@Override
- public SimpleInliningConstraint rewrittenWithUnboxedArguments(IntList unboxedArgumentIndices) {
+ public SimpleInliningConstraint rewrittenWithUnboxedArguments(
+ IntList unboxedArgumentIndices, SimpleInliningConstraintFactory factory) {
return this;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/BooleanFalseSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/BooleanFalseSimpleInliningConstraint.java
index 351d51d..2c88a60 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/BooleanFalseSimpleInliningConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/BooleanFalseSimpleInliningConstraint.java
@@ -41,7 +41,8 @@
}
@Override
- public SimpleInliningConstraint rewrittenWithUnboxedArguments(IntList unboxedArgumentIndices) {
+ public SimpleInliningConstraint rewrittenWithUnboxedArguments(
+ IntList unboxedArgumentIndices, SimpleInliningConstraintFactory factory) {
return this;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/BooleanTrueSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/BooleanTrueSimpleInliningConstraint.java
index bf3fde3..46c29b0 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/BooleanTrueSimpleInliningConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/BooleanTrueSimpleInliningConstraint.java
@@ -41,7 +41,8 @@
}
@Override
- public SimpleInliningConstraint rewrittenWithUnboxedArguments(IntList unboxedArgumentIndices) {
+ public SimpleInliningConstraint rewrittenWithUnboxedArguments(
+ IntList unboxedArgumentIndices, SimpleInliningConstraintFactory factory) {
return this;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/EqualToNumberSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/EqualToNumberSimpleInliningConstraint.java
new file mode 100644
index 0000000..a321670
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/EqualToNumberSimpleInliningConstraint.java
@@ -0,0 +1,46 @@
+// 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.ir.analysis.inlining;
+
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+import it.unimi.dsi.fastutil.ints.IntList;
+
+public class EqualToNumberSimpleInliningConstraint extends SimpleInliningArgumentConstraint {
+
+ private final long rawValue;
+
+ private EqualToNumberSimpleInliningConstraint(int argumentIndex, long rawValue) {
+ super(argumentIndex);
+ this.rawValue = rawValue;
+ }
+
+ static EqualToNumberSimpleInliningConstraint create(
+ int argumentIndex, long rawValue, SimpleInliningConstraintFactory witness) {
+ assert witness != null;
+ return new EqualToNumberSimpleInliningConstraint(argumentIndex, rawValue);
+ }
+
+ @Override
+ public boolean isSatisfied(InvokeMethod invoke) {
+ Value argumentRoot = getArgument(invoke).getAliasedValue();
+ return argumentRoot.isDefinedByInstructionSatisfying(Instruction::isConstNumber)
+ && argumentRoot.getDefinition().asConstNumber().getRawValue() == rawValue;
+ }
+
+ @Override
+ public SimpleInliningConstraint fixupAfterRemovingThisParameter(
+ SimpleInliningConstraintFactory factory) {
+ assert getArgumentIndex() > 0;
+ return factory.createNumberConstraint(getArgumentIndex() - 1, rawValue);
+ }
+
+ @Override
+ public SimpleInliningConstraint rewrittenWithUnboxedArguments(
+ IntList unboxedArgumentIndices, SimpleInliningConstraintFactory factory) {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NeverSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NeverSimpleInliningConstraint.java
index 4568e5f..809dbb9 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NeverSimpleInliningConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NeverSimpleInliningConstraint.java
@@ -35,7 +35,8 @@
}
@Override
- public SimpleInliningConstraint rewrittenWithUnboxedArguments(IntList unboxedArgumentIndices) {
+ public SimpleInliningConstraint rewrittenWithUnboxedArguments(
+ IntList unboxedArgumentIndices, SimpleInliningConstraintFactory factory) {
return this;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NotEqualToNumberSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NotEqualToNumberSimpleInliningConstraint.java
new file mode 100644
index 0000000..4739231
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NotEqualToNumberSimpleInliningConstraint.java
@@ -0,0 +1,46 @@
+// 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.ir.analysis.inlining;
+
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Value;
+import it.unimi.dsi.fastutil.ints.IntList;
+
+public class NotEqualToNumberSimpleInliningConstraint extends SimpleInliningArgumentConstraint {
+
+ private final long rawValue;
+
+ private NotEqualToNumberSimpleInliningConstraint(int argumentIndex, long rawValue) {
+ super(argumentIndex);
+ this.rawValue = rawValue;
+ }
+
+ static NotEqualToNumberSimpleInliningConstraint create(
+ int argumentIndex, long rawValue, SimpleInliningConstraintFactory witness) {
+ assert witness != null;
+ return new NotEqualToNumberSimpleInliningConstraint(argumentIndex, rawValue);
+ }
+
+ @Override
+ public boolean isSatisfied(InvokeMethod invoke) {
+ Value argumentRoot = getArgument(invoke).getAliasedValue();
+ return argumentRoot.isDefinedByInstructionSatisfying(Instruction::isConstNumber)
+ && argumentRoot.getDefinition().asConstNumber().getRawValue() != rawValue;
+ }
+
+ @Override
+ public SimpleInliningConstraint fixupAfterRemovingThisParameter(
+ SimpleInliningConstraintFactory factory) {
+ assert getArgumentIndex() > 0;
+ return factory.createNotNumberConstraint(getArgumentIndex() - 1, rawValue);
+ }
+
+ @Override
+ public SimpleInliningConstraint rewrittenWithUnboxedArguments(
+ IntList unboxedArgumentIndices, SimpleInliningConstraintFactory factory) {
+ return this;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NotNullSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NotNullSimpleInliningConstraint.java
index 0834cba..00288f8 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NotNullSimpleInliningConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NotNullSimpleInliningConstraint.java
@@ -41,10 +41,10 @@
}
@Override
- public SimpleInliningConstraint rewrittenWithUnboxedArguments(IntList unboxedArgumentIndices) {
+ public SimpleInliningConstraint rewrittenWithUnboxedArguments(
+ IntList unboxedArgumentIndices, SimpleInliningConstraintFactory factory) {
if (unboxedArgumentIndices.contains(getArgumentIndex())) {
- // TODO(b/176067541): Could be refined to an argument-equals-int constraint.
- return NeverSimpleInliningConstraint.getInstance();
+ return factory.createNotNumberConstraint(getArgumentIndex(), 0);
}
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java
index 19607ee..5a0d3cf 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java
@@ -45,10 +45,10 @@
}
@Override
- public SimpleInliningConstraint rewrittenWithUnboxedArguments(IntList unboxedArgumentIndices) {
+ public SimpleInliningConstraint rewrittenWithUnboxedArguments(
+ IntList unboxedArgumentIndices, SimpleInliningConstraintFactory factory) {
if (unboxedArgumentIndices.contains(getArgumentIndex())) {
- // TODO(b/176067541): Could be refined to an argument-equals-int constraint.
- return NeverSimpleInliningConstraint.getInstance();
+ return factory.createNumberConstraint(getArgumentIndex(), 0);
}
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraint.java
index df108ac..82c3f7d 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraint.java
@@ -110,5 +110,5 @@
SimpleInliningConstraintFactory factory);
public abstract SimpleInliningConstraint rewrittenWithUnboxedArguments(
- IntList unboxedArgumentIndices);
+ IntList unboxedArgumentIndices, SimpleInliningConstraintFactory factory);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintAnalysis.java
index 1a7e997..6d79b82 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintAnalysis.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.Sets;
+import java.util.OptionalLong;
import java.util.Set;
/**
@@ -105,43 +106,46 @@
switch (instruction.opcode()) {
case IF:
If ifInstruction = instruction.asIf();
- if (ifInstruction.isZeroTest()) {
- Value lhs = ifInstruction.lhs().getAliasedValue();
- if (lhs.isArgument() && !lhs.isThis()) {
- int argumentIndex = lhs.getDefinition().asArgument().getIndex();
- DexType argumentType = method.getDefinition().getArgumentType(argumentIndex);
- int currentDepth = instructionDepth;
-
- // Compute the constraint for which paths through the true target are guaranteed to exit
- // early.
- SimpleInliningConstraint trueTargetConstraint =
- computeConstraintFromIfZeroTest(
- argumentIndex, argumentType, ifInstruction.getType())
- // Only recurse into the true target if the constraint from the if-instruction
- // is not 'never'.
- .lazyMeet(
- () ->
- analyzeInstructionsInBlock(
- ifInstruction.getTrueTarget(), currentDepth));
-
- // Compute the constraint for which paths through the false target are guaranteed to
- // exit early.
- SimpleInliningConstraint fallthroughTargetConstraint =
- computeConstraintFromIfZeroTest(
- argumentIndex, argumentType, ifInstruction.getType().inverted())
- // Only recurse into the false target if the constraint from the if-instruction
- // is not 'never'.
- .lazyMeet(
- () ->
- analyzeInstructionsInBlock(
- ifInstruction.fallthroughBlock(), currentDepth));
-
- // Paths going through this basic block are guaranteed to exit early if the true target
- // is guaranteed to exit early or the false target is.
- return trueTargetConstraint.join(fallthroughTargetConstraint);
- }
+ Value singleArgumentOperand = getSingleArgumentOperand(ifInstruction);
+ if (singleArgumentOperand == null || singleArgumentOperand.isThis()) {
+ break;
}
- break;
+
+ Value otherOperand =
+ ifInstruction.isZeroTest()
+ ? null
+ : ifInstruction.getOperand(
+ 1 - ifInstruction.inValues().indexOf(singleArgumentOperand));
+
+ int argumentIndex =
+ singleArgumentOperand.getAliasedValue().getDefinition().asArgument().getIndex();
+ DexType argumentType = method.getDefinition().getArgumentType(argumentIndex);
+ int currentDepth = instructionDepth;
+
+ // Compute the constraint for which paths through the true target are guaranteed to exit
+ // early.
+ SimpleInliningConstraint trueTargetConstraint =
+ computeConstraintFromIfTest(
+ argumentIndex, argumentType, otherOperand, ifInstruction.getType())
+ // Only recurse into the true target if the constraint from the if-instruction
+ // is not 'never'.
+ .lazyMeet(
+ () -> analyzeInstructionsInBlock(ifInstruction.getTrueTarget(), currentDepth));
+
+ // Compute the constraint for which paths through the false target are guaranteed to
+ // exit early.
+ SimpleInliningConstraint fallthroughTargetConstraint =
+ computeConstraintFromIfTest(
+ argumentIndex, argumentType, otherOperand, ifInstruction.getType().inverted())
+ // Only recurse into the false target if the constraint from the if-instruction
+ // is not 'never'.
+ .lazyMeet(
+ () ->
+ analyzeInstructionsInBlock(ifInstruction.fallthroughBlock(), currentDepth));
+
+ // Paths going through this basic block are guaranteed to exit early if the true target
+ // is guaranteed to exit early or the false target is.
+ return trueTargetConstraint.join(fallthroughTargetConstraint);
case GOTO:
return analyzeInstructionsInBlock(instruction.asGoto().getTarget(), instructionDepth);
@@ -162,24 +166,39 @@
return NeverSimpleInliningConstraint.getInstance();
}
- private SimpleInliningConstraint computeConstraintFromIfZeroTest(
- int argumentIndex, DexType argumentType, If.Type type) {
+ private SimpleInliningConstraint computeConstraintFromIfTest(
+ int argumentIndex, DexType argumentType, Value otherOperand, If.Type type) {
+ boolean isZeroTest = otherOperand == null;
switch (type) {
case EQ:
- if (argumentType.isReferenceType()) {
- return factory.createNullConstraint(argumentIndex);
- }
- if (argumentType.isBooleanType()) {
- return factory.createBooleanFalseConstraint(argumentIndex);
+ if (isZeroTest) {
+ if (argumentType.isReferenceType()) {
+ return factory.createNullConstraint(argumentIndex);
+ }
+ if (argumentType.isBooleanType()) {
+ return factory.createBooleanFalseConstraint(argumentIndex);
+ }
+ } else if (argumentType.isPrimitiveType()) {
+ OptionalLong rawValue = getRawNumberValue(otherOperand);
+ if (rawValue.isPresent()) {
+ return factory.createNumberConstraint(argumentIndex, rawValue.getAsLong());
+ }
}
return NeverSimpleInliningConstraint.getInstance();
case NE:
- if (argumentType.isReferenceType()) {
- return factory.createNotNullConstraint(argumentIndex);
- }
- if (argumentType.isBooleanType()) {
- return factory.createBooleanTrueConstraint(argumentIndex);
+ if (isZeroTest) {
+ if (argumentType.isReferenceType()) {
+ return factory.createNotNullConstraint(argumentIndex);
+ }
+ if (argumentType.isBooleanType()) {
+ return factory.createBooleanTrueConstraint(argumentIndex);
+ }
+ } else if (argumentType.isPrimitiveType()) {
+ OptionalLong rawValue = getRawNumberValue(otherOperand);
+ if (rawValue.isPresent()) {
+ return factory.createNotNumberConstraint(argumentIndex, rawValue.getAsLong());
+ }
}
return NeverSimpleInliningConstraint.getInstance();
@@ -187,4 +206,33 @@
return NeverSimpleInliningConstraint.getInstance();
}
}
+
+ private OptionalLong getRawNumberValue(Value value) {
+ Value root = value.getAliasedValue();
+ if (root.isDefinedByInstructionSatisfying(Instruction::isConstNumber)) {
+ return OptionalLong.of(root.getDefinition().asConstNumber().getRawValue());
+ }
+ return OptionalLong.empty();
+ }
+
+ private Value getSingleArgumentOperand(If ifInstruction) {
+ Value singleArgumentOperand = null;
+
+ Value lhs = ifInstruction.lhs();
+ if (lhs.getAliasedValue().isArgument()) {
+ singleArgumentOperand = lhs;
+ }
+
+ if (!ifInstruction.isZeroTest()) {
+ Value rhs = ifInstruction.rhs();
+ if (rhs.getAliasedValue().isArgument()) {
+ if (singleArgumentOperand != null) {
+ return null;
+ }
+ singleArgumentOperand = rhs;
+ }
+ }
+
+ return singleArgumentOperand;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintConjunction.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintConjunction.java
index c993b12..ae2e07b 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintConjunction.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintConjunction.java
@@ -77,11 +77,12 @@
}
@Override
- public SimpleInliningConstraint rewrittenWithUnboxedArguments(IntList unboxedArgumentIndices) {
+ public SimpleInliningConstraint rewrittenWithUnboxedArguments(
+ IntList unboxedArgumentIndices, SimpleInliningConstraintFactory factory) {
List<SimpleInliningConstraint> rewrittenConstraints =
ListUtils.mapOrElse(
constraints,
- constraint -> constraint.rewrittenWithUnboxedArguments(unboxedArgumentIndices),
+ constraint -> constraint.rewrittenWithUnboxedArguments(unboxedArgumentIndices, factory),
null);
return rewrittenConstraints != null
? new SimpleInliningConstraintConjunction(rewrittenConstraints)
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintDisjunction.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintDisjunction.java
index a6b418d..42bcaf6 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintDisjunction.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintDisjunction.java
@@ -77,11 +77,12 @@
}
@Override
- public SimpleInliningConstraint rewrittenWithUnboxedArguments(IntList unboxedArgumentIndices) {
+ public SimpleInliningConstraint rewrittenWithUnboxedArguments(
+ IntList unboxedArgumentIndices, SimpleInliningConstraintFactory factory) {
List<SimpleInliningConstraint> rewrittenConstraints =
ListUtils.mapOrElse(
constraints,
- constraint -> constraint.rewrittenWithUnboxedArguments(unboxedArgumentIndices),
+ constraint -> constraint.rewrittenWithUnboxedArguments(unboxedArgumentIndices, factory),
null);
return rewrittenConstraints != null
? new SimpleInliningConstraintDisjunction(rewrittenConstraints)
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintFactory.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintFactory.java
index 99603e8..ed3cd55 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintFactory.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintFactory.java
@@ -77,6 +77,16 @@
() -> NullSimpleInliningConstraint.create(argumentIndex, this));
}
+ public NotEqualToNumberSimpleInliningConstraint createNotNumberConstraint(
+ int argumentIndex, long rawValue) {
+ return NotEqualToNumberSimpleInliningConstraint.create(argumentIndex, rawValue, this);
+ }
+
+ public EqualToNumberSimpleInliningConstraint createNumberConstraint(
+ int argumentIndex, long rawValue) {
+ return EqualToNumberSimpleInliningConstraint.create(argumentIndex, rawValue, this);
+ }
+
private <T extends SimpleInliningArgumentConstraint> T createArgumentConstraint(
int argumentIndex, T[] lowConstraints, Map<Integer, T> highConstraints, Supplier<T> fn) {
return argumentIndex < lowConstraints.length
diff --git a/src/main/java/com/android/tools/r8/ir/code/Assume.java b/src/main/java/com/android/tools/r8/ir/code/Assume.java
index 9c70c92..3a51eec 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Assume.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Assume.java
@@ -246,7 +246,7 @@
@Override
public boolean throwsOnNullInput() {
- return true;
+ return hasNonNullAssumption();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
index 7f80abf..0fa4236 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstClass.java
@@ -32,6 +32,14 @@
this.clazz = clazz;
}
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public DexType getType() {
+ return clazz;
+ }
+
@Override
public int opcode() {
return Opcodes.CONST_CLASS;
@@ -189,4 +197,24 @@
}
return UnknownValue.getInstance();
}
+
+ public static class Builder extends BuilderBase<Builder, ConstClass> {
+
+ private DexType type;
+
+ public Builder setType(DexType type) {
+ this.type = type;
+ return this;
+ }
+
+ @Override
+ public ConstClass build() {
+ return amend(new ConstClass(outValue, type));
+ }
+
+ @Override
+ public Builder self() {
+ return this;
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 708ffcf..5517fb8 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -98,6 +98,10 @@
return position == null ? "???" : position.toString();
}
+ public Value getOperand(int index) {
+ return inValues().get(index);
+ }
+
public List<Value> inValues() {
return inValues;
}
@@ -1011,6 +1015,14 @@
return null;
}
+ public boolean isNewUnboxedEnumInstance() {
+ return false;
+ }
+
+ public NewUnboxedEnumInstance asNewUnboxedEnumInstance() {
+ return null;
+ }
+
public boolean isNot() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionVisitor.java b/src/main/java/com/android/tools/r8/ir/code/InstructionVisitor.java
index a374439..8aa631d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionVisitor.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionVisitor.java
@@ -108,6 +108,8 @@
T visit(NewInstance instruction);
+ T visit(NewUnboxedEnumInstance instruction);
+
T visit(Not instruction);
T visit(NumberConversion instruction);
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
index ae02e8b..50899ca 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -38,6 +38,10 @@
this.clazz = clazz;
}
+ public DexType getType() {
+ return clazz;
+ }
+
public InvokeDirect getUniqueConstructorInvoke(DexItemFactory dexItemFactory) {
return IRCodeUtils.getUniqueConstructorInvoke(outValue(), dexItemFactory);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
new file mode 100644
index 0000000..8230766
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
@@ -0,0 +1,161 @@
+// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.cf.LoadStoreHelper;
+import com.android.tools.r8.cf.TypeVerificationHelper;
+import com.android.tools.r8.cf.code.CfNewUnboxedEnum;
+import com.android.tools.r8.code.DexNewUnboxedEnumInstance;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
+import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.conversion.CfBuilder;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
+import com.android.tools.r8.ir.optimize.InliningConstraints;
+
+/**
+ * Special instruction used by {@link com.android.tools.r8.ir.optimize.enums.EnumUnboxer}.
+ *
+ * <p>When applying the enum unboxer to the application, we move the class initializer of each
+ * unboxed enum to its utility class, and change each {@link NewInstance} instruction that
+ * instantiates the unboxed enum into a {@link NewUnboxedEnumInstance} that holds the ordinal of the
+ * enum instance.
+ *
+ * <p>The {@link NewUnboxedEnumInstance} is an instruction that produces an (initialized) instance
+ * of the unboxed enum, i.e., the out-type is a non-nullable class type. This is important for the
+ * code to type check until lens code rewriting, which replaces the {@link NewUnboxedEnumInstance}
+ * instructions by {@link ConstNumber} instructions.
+ *
+ * <p>Note: The {@link NewUnboxedEnumInstance} is only used from {@link
+ * com.android.tools.r8.ir.optimize.enums.EnumUnboxer#unboxEnums} until the execution of the {@link
+ * com.android.tools.r8.ir.conversion.PostMethodProcessor}. There should be no instances of {@link
+ * NewUnboxedEnumInstance} (nor {@link CfNewUnboxedEnum}, {@link DexNewUnboxedEnumInstance}) after
+ * IR processing has finished.
+ */
+public class NewUnboxedEnumInstance extends Instruction {
+
+ public final DexType clazz;
+ private final int ordinal;
+
+ public NewUnboxedEnumInstance(DexType clazz, int ordinal, Value dest) {
+ super(dest);
+ assert clazz != null;
+ this.clazz = clazz;
+ this.ordinal = ordinal;
+ }
+
+ public int getOrdinal() {
+ return ordinal;
+ }
+
+ public DexType getType() {
+ return clazz;
+ }
+
+ @Override
+ public int opcode() {
+ return Opcodes.NEW_UNBOXED_ENUM_INSTANCE;
+ }
+
+ @Override
+ public <T> T accept(InstructionVisitor<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ int dest = builder.allocatedRegister(outValue(), getNumber());
+ builder.add(this, new DexNewUnboxedEnumInstance(dest, clazz, ordinal));
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + " " + clazz;
+ }
+
+ @Override
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
+ return other.isNewUnboxedEnumInstance() && other.asNewUnboxedEnumInstance().clazz == clazz;
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ assert false : "NewUnboxedEnumInstance has no register arguments";
+ return 0;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ return Constants.U8BIT_MAX;
+ }
+
+ @Override
+ public boolean instructionTypeCanThrow() {
+ // Depending on how this instruction is lowered to CF/DEX the instruction type may throw. If we
+ // lower the instruction to a const-number, then it can't throw, but if we lower it to something
+ // that triggers the class initialization of the enum utility class, then it could throw.
+ return true;
+ }
+
+ @Override
+ public boolean isNewUnboxedEnumInstance() {
+ return true;
+ }
+
+ @Override
+ public NewUnboxedEnumInstance asNewUnboxedEnumInstance() {
+ return this;
+ }
+
+ @Override
+ public ConstraintWithTarget inliningConstraint(
+ InliningConstraints inliningConstraints, ProgramMethod context) {
+ return inliningConstraints.forNewUnboxedEnumInstance(clazz, context);
+ }
+
+ @Override
+ public boolean hasInvariantOutType() {
+ return true;
+ }
+
+ @Override
+ public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+ helper.storeOutValue(this, it);
+ }
+
+ @Override
+ public void buildCf(CfBuilder builder) {
+ builder.add(new CfNewUnboxedEnum(clazz, ordinal));
+ }
+
+ @Override
+ public DexType computeVerificationType(AppView<?> appView, TypeVerificationHelper helper) {
+ return clazz;
+ }
+
+ @Override
+ public TypeElement evaluate(AppView<?> appView) {
+ return TypeElement.fromDexType(clazz, Nullability.definitelyNotNull(), appView);
+ }
+
+ @Override
+ public boolean instructionMayTriggerMethodInvocation(AppView<?> appView, ProgramMethod context) {
+ // Conservatively return true.
+ return true;
+ }
+
+ @Override
+ public boolean verifyTypes(AppView<?> appView, VerifyTypesHelper verifyTypesHelper) {
+ TypeElement type = getOutType();
+ assert type.isClassType();
+ assert type.asClassType().getClassType() == clazz || appView.options().testing.allowTypeErrors;
+ assert type.isDefinitelyNotNull();
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Opcodes.java b/src/main/java/com/android/tools/r8/ir/code/Opcodes.java
index 51b19d8e..d2c79d4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Opcodes.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Opcodes.java
@@ -56,22 +56,23 @@
int NEW_ARRAY_EMPTY = 47;
int NEW_ARRAY_FILLED_DATA = 48;
int NEW_INSTANCE = 49;
- int NOT = 50;
- int NUMBER_CONVERSION = 51;
- int OR = 52;
- int POP = 53;
- int REM = 54;
- int RETURN = 55;
- int SHL = 56;
- int SHR = 57;
- int STATIC_GET = 58;
- int STATIC_PUT = 59;
- int STORE = 60;
- int STRING_SWITCH = 61;
- int SUB = 62;
- int SWAP = 63;
- int THROW = 64;
- int USHR = 65;
- int XOR = 66;
- int UNINITIALIZED_THIS_LOCAL_READ = 67;
+ int NEW_UNBOXED_ENUM_INSTANCE = 50;
+ int NOT = 51;
+ int NUMBER_CONVERSION = 52;
+ int OR = 53;
+ int POP = 54;
+ int REM = 55;
+ int RETURN = 56;
+ int SHL = 57;
+ int SHR = 58;
+ int STATIC_GET = 59;
+ int STATIC_PUT = 60;
+ int STORE = 61;
+ int STRING_SWITCH = 62;
+ int SUB = 63;
+ int SWAP = 64;
+ int THROW = 65;
+ int USHR = 66;
+ int XOR = 67;
+ int UNINITIALIZED_THIS_LOCAL_READ = 68;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticFieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/StaticFieldInstruction.java
index 9b0e28a..dc41e4a 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticFieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticFieldInstruction.java
@@ -5,11 +5,14 @@
package com.android.tools.r8.ir.code;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.Instruction.SideEffectAssumption;
public interface StaticFieldInstruction {
+ DexField getField();
+
boolean hasOutValue();
Value outValue();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
index 32be662..48656ec 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CallGraphBuilderBase.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
@@ -20,6 +19,7 @@
import com.android.tools.r8.graph.FieldAccessInfoCollection;
import com.android.tools.r8.graph.GraphLens.MethodLookupResult;
import com.android.tools.r8.graph.LookupResult;
+import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.UseRegistry;
@@ -44,6 +44,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
+import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -185,7 +186,7 @@
assert !context.getDefinition().isBridge()
|| singleTarget.getDefinition() != context.getDefinition();
// For static invokes, the class could be initialized.
- if (type == Invoke.Type.STATIC) {
+ if (type.isStatic()) {
addClassInitializerTarget(singleTarget.getHolder());
}
addCallEdge(singleTarget, false);
@@ -252,28 +253,22 @@
}
}
- private void processFieldRead(DexField field) {
- if (!field.holder.isClassType()) {
+ private void processFieldRead(DexField reference) {
+ if (!reference.holder.isClassType()) {
return;
}
- DexEncodedField encodedField = appView.appInfo().resolveField(field).getResolvedField();
- if (encodedField == null || appView.appInfo().isPinned(encodedField.getReference())) {
- return;
- }
-
- DexProgramClass clazz =
- asProgramClassOrNull(appView.definitionFor(encodedField.getHolderType()));
- if (clazz == null) {
+ ProgramField field = appView.appInfo().resolveField(reference).getProgramField();
+ if (field == null || appView.appInfo().isPinned(field)) {
return;
}
// Each static field access implicitly triggers the class initializer.
- if (encodedField.isStatic()) {
- addClassInitializerTarget(clazz);
+ if (field.getAccessFlags().isStatic()) {
+ addClassInitializerTarget(field.getHolder());
}
- FieldAccessInfo fieldAccessInfo = fieldAccessInfoCollection.get(encodedField.getReference());
+ FieldAccessInfo fieldAccessInfo = fieldAccessInfoCollection.get(field.getReference());
if (fieldAccessInfo != null && fieldAccessInfo.hasKnownWriteContexts()) {
if (fieldAccessInfo.getNumberOfWriteContexts() == 1) {
fieldAccessInfo.forEachWriteContext(this::addFieldReadEdge);
@@ -281,12 +276,12 @@
}
}
- private void processFieldWrite(DexField field) {
- if (field.holder.isClassType()) {
- DexEncodedField encodedField = appView.appInfo().resolveField(field).getResolvedField();
- if (encodedField != null && encodedField.isStatic()) {
+ private void processFieldWrite(DexField reference) {
+ if (reference.getHolderType().isClassType()) {
+ ProgramField field = appView.appInfo().resolveField(reference).getProgramField();
+ if (field != null && field.getAccessFlags().isStatic()) {
// Each static field access implicitly triggers the class initializer.
- addClassInitializerTarget(field.holder);
+ addClassInitializerTarget(field.getHolder());
}
}
}
@@ -426,6 +421,11 @@
// Nodes on the DFS stack.
private Map<Node, StackEntryInfo> stackEntryInfo = new IdentityHashMap<>();
+ // Subset of the DFS stack, where the nodes on the stack are class initializers.
+ //
+ // This stack is used to efficiently compute if there is a class initializer on the stack.
+ private Deque<Node> clinitCallStack = new ArrayDeque<>();
+
// Subset of the DFS stack, where the nodes on the stack satisfy that the edge from the
// predecessor to the node itself is a field read edge.
//
@@ -471,6 +471,7 @@
private void prepareForNewTraversal() {
assert calleesToBeRemoved.isEmpty();
+ assert clinitCallStack.isEmpty();
assert stack.isEmpty();
assert stackEntryInfo.isEmpty();
assert writersToBeRemoved.isEmpty();
@@ -480,6 +481,7 @@
}
private void reset() {
+ assert clinitCallStack.isEmpty();
assert marked.isEmpty();
assert revisit.isEmpty();
assert stack.isEmpty();
@@ -624,28 +626,36 @@
// Otherwise, it is a call edge. Check if there is a field read edge in the cycle, and if
// so, remove that edge.
- if (!writerStack.isEmpty()) {
- Node lastKnownWriter = writerStack.peek();
- StackEntryInfo lastKnownWriterStackEntryInfo = stackEntryInfo.get(lastKnownWriter);
- boolean cycleContainsLastKnownWriter =
- lastKnownWriterStackEntryInfo.index > calleeOrWriterStackEntryInfo.index;
- if (cycleContainsLastKnownWriter) {
- assert verifyCycleSatisfies(
+ if (!writerStack.isEmpty()
+ && removeIncomingEdgeOnStack(
+ writerStack.peek(),
calleeOrWriter,
- cycle ->
- cycle.contains(lastKnownWriter)
- && cycle.contains(lastKnownWriterStackEntryInfo.predecessor));
- if (!lastKnownWriterStackEntryInfo.processed) {
- removeFieldReadEdge(lastKnownWriterStackEntryInfo.predecessor, lastKnownWriter);
- revisit.add(lastKnownWriter);
- lastKnownWriterStackEntryInfo.processed = true;
- }
- continue;
- }
+ calleeOrWriterStackEntryInfo,
+ this::removeFieldReadEdge)) {
+ continue;
}
- // It is a call edge, and the cycle does not contain any field read edges. In this case, we
- // remove the call edge if it is safe according to force inlining.
+ // It is a call edge and the cycle does not contain any field read edges.
+ // If it is a call edge to a <clinit>, then remove it.
+ if (calleeOrWriter.getMethod().isClassInitializer()) {
+ // Calls to class initializers are always safe to remove.
+ assert callEdgeRemovalIsSafe(callerOrReader, calleeOrWriter);
+ removeCallEdge(callerOrReader, calleeOrWriter);
+ continue;
+ }
+
+ // Otherwise, check if there is a call edge to a <clinit> method in the cycle, and if so,
+ // remove that edge.
+ if (!clinitCallStack.isEmpty()
+ && removeIncomingEdgeOnStack(
+ clinitCallStack.peek(),
+ calleeOrWriter,
+ calleeOrWriterStackEntryInfo,
+ this::removeCallEdge)) {
+ continue;
+ }
+
+ // Otherwise, we remove the call edge if it is safe according to force inlining.
if (callEdgeRemovalIsSafe(callerOrReader, calleeOrWriter)) {
// Break the cycle by removing the edge node->calleeOrWriter.
// Need to remove `calleeOrWriter` from `node.callees` using the iterator to prevent a
@@ -669,6 +679,7 @@
// Break the cycle by removing the edge caller->callee.
removeCallEdge(edge.caller, edge.callee);
+ revisit.add(edge.callee);
}
// Recover the stack.
@@ -681,8 +692,12 @@
stack.push(node);
assert !stackEntryInfo.containsKey(node);
stackEntryInfo.put(node, new StackEntryInfo(stack.size() - 1, predecessor));
- if (predecessor != null && predecessor.getWritersWithDeterministicOrder().contains(node)) {
- writerStack.push(node);
+ if (predecessor != null) {
+ if (node.getMethod().isClassInitializer() && node.hasCaller(predecessor)) {
+ clinitCallStack.push(node);
+ } else if (predecessor.getWritersWithDeterministicOrder().contains(node)) {
+ writerStack.push(node);
+ }
}
}
@@ -691,7 +706,10 @@
assert popped == node;
assert stackEntryInfo.containsKey(node);
stackEntryInfo.remove(node);
- if (writerStack.peek() == popped) {
+ if (clinitCallStack.peek() == popped) {
+ assert writerStack.peek() != popped;
+ clinitCallStack.pop();
+ } else if (writerStack.peek() == popped) {
writerStack.pop();
}
}
@@ -704,6 +722,28 @@
writersToBeRemoved.computeIfAbsent(reader, ignore -> Sets.newIdentityHashSet()).add(writer);
}
+ private boolean removeIncomingEdgeOnStack(
+ Node target,
+ Node currentCalleeOrWriter,
+ StackEntryInfo currentCalleeOrWriterStackEntryInfo,
+ BiConsumer<Node, Node> edgeRemover) {
+ StackEntryInfo targetStackEntryInfo = stackEntryInfo.get(target);
+ boolean cycleContainsTarget =
+ targetStackEntryInfo.index > currentCalleeOrWriterStackEntryInfo.index;
+ if (cycleContainsTarget) {
+ assert verifyCycleSatisfies(
+ currentCalleeOrWriter,
+ cycle -> cycle.contains(target) && cycle.contains(targetStackEntryInfo.predecessor));
+ if (!targetStackEntryInfo.processed) {
+ edgeRemover.accept(targetStackEntryInfo.predecessor, target);
+ revisit.add(target);
+ targetStackEntryInfo.processed = true;
+ }
+ return true;
+ }
+ return false;
+ }
+
private LinkedList<Node> extractCycle(Node entry) {
LinkedList<Node> cycle = new LinkedList<>();
do {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
index 83e0ff5..0266f4b 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
@@ -13,6 +13,8 @@
import com.android.tools.r8.ir.desugar.CfClassDesugaringEventConsumer.D8CfClassDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer.D8CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer.D8CfPostProcessingDesugaringEventConsumer;
import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.collect.Sets;
import java.util.ArrayList;
@@ -107,11 +109,15 @@
assert instructionDesugaringEventConsumer.verifyNothingToFinalize();
}
- converter.finalizeDesugaredLibraryRetargeting(instructionDesugaringEventConsumer);
- assert instructionDesugaringEventConsumer.verifyNothingToFinalize();
-
classes = deferred;
}
+
+ D8CfPostProcessingDesugaringEventConsumer eventConsumer =
+ CfPostProcessingDesugaringEventConsumer.createForD8(methodProcessor, appView);
+ methodProcessor.newWave();
+ converter.postProcessDesugaring(eventConsumer);
+ methodProcessor.awaitMethodProcessing();
+ eventConsumer.finalizeDesugaring();
}
abstract void convertClass(
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
index 4ba4555..9bcee66 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.conversion;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.contexts.CompilationContext.ProcessorContext;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexProgramClass;
@@ -55,6 +56,11 @@
}
@Override
+ public MethodProcessingContext createMethodProcessingContext(ProgramMethod method) {
+ return processorContext.createMethodProcessingContext(method);
+ }
+
+ @Override
public boolean isProcessedConcurrently(ProgramMethod method) {
// In D8 all methods are considered independently compiled.
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index ba1a15c..2563fa8 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -91,6 +91,7 @@
import com.android.tools.r8.ir.code.NewArrayEmpty;
import com.android.tools.r8.ir.code.NewArrayFilledData;
import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.NewUnboxedEnumInstance;
import com.android.tools.r8.ir.code.Not;
import com.android.tools.r8.ir.code.NumberConversion;
import com.android.tools.r8.ir.code.NumberGenerator;
@@ -1813,6 +1814,14 @@
addInstruction(instruction);
}
+ public void addNewUnboxedEnumInstance(int dest, DexType type, int ordinal) {
+ TypeElement instanceType = TypeElement.fromDexType(type, definitelyNotNull(), appView);
+ Value out = writeRegister(dest, instanceType, ThrowingInfo.CAN_THROW);
+ NewUnboxedEnumInstance instruction = new NewUnboxedEnumInstance(type, ordinal, out);
+ assert instruction.instructionTypeCanThrow();
+ addInstruction(instruction);
+ }
+
public void addReturn(int value) {
DexType returnType = method.getDefinition().returnType();
if (returnType.isVoidType()) {
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 a9e34e2..b73d9d7 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
@@ -49,10 +49,12 @@
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer.D8CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringCollection;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.CovariantReturnTypeAnnotationTransformer;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter.Mode;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter.Mode;
+import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceProcessor;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor;
import com.android.tools.r8.ir.desugar.lambda.LambdaDeserializationMethodRemover;
@@ -139,7 +141,6 @@
private final StringBuilderOptimizer stringBuilderOptimizer;
private final IdempotentFunctionCallCanonicalizer idempotentFunctionCallCanonicalizer;
private final InterfaceMethodRewriter interfaceMethodRewriter;
- private final DesugaredLibraryRetargeter desugaredLibraryRetargeter;
private final ClassInliner classInliner;
private final ClassStaticizer classStaticizer;
private final InternalOptions options;
@@ -225,7 +226,6 @@
assert options.desugarState.isOn();
this.instructionDesugaring = CfInstructionDesugaringCollection.create(appView);
this.classDesugaring = instructionDesugaring.createClassDesugaringCollection();
- this.desugaredLibraryRetargeter = null; // Managed cf to cf.
this.interfaceMethodRewriter =
options.desugaredLibraryConfiguration.getEmulateLibraryInterface().isEmpty()
? null
@@ -258,11 +258,6 @@
? CfInstructionDesugaringCollection.empty()
: CfInstructionDesugaringCollection.create(appView);
this.classDesugaring = instructionDesugaring.createClassDesugaringCollection();
- this.desugaredLibraryRetargeter =
- options.desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()
- || !appView.enableWholeProgramOptimizations()
- ? null
- : new DesugaredLibraryRetargeter(appView);
this.interfaceMethodRewriter =
options.isInterfaceMethodDesugaringEnabled()
? new InterfaceMethodRewriter(appView, this)
@@ -372,10 +367,9 @@
D8NestBasedAccessDesugaring::clearNestAttributes);
}
- public void finalizeDesugaredLibraryRetargeting(
- D8CfInstructionDesugaringEventConsumer instructionDesugaringEventConsumer) {
- instructionDesugaring.withDesugaredLibraryRetargeter(
- retargeter -> retargeter.finalizeDesugaring(instructionDesugaringEventConsumer));
+ void postProcessDesugaring(CfPostProcessingDesugaringEventConsumer eventConsumer) {
+ CfPostProcessingDesugaringCollection.create(appView, instructionDesugaring.getRetargetingInfo())
+ .postProcessingDesugaring(eventConsumer);
}
private void staticizeClasses(
@@ -392,25 +386,23 @@
}
}
- private void desugarInterfaceMethods(
- Builder<?> builder,
- Flavor includeAllResources,
- ExecutorService executorService)
+ private void finalizeInterfaceMethodRewritingThroughIR(ExecutorService executorService)
throws ExecutionException {
assert !appView.getSyntheticItems().hasPendingSyntheticClasses();
if (interfaceMethodRewriter != null) {
- interfaceMethodRewriter.desugarInterfaceMethods(
- builder, includeAllResources, executorService);
+ interfaceMethodRewriter.finalizeInterfaceMethodRewritingThroughIR(this, executorService);
}
}
- private void synthesizeRetargetClass(ExecutorService executorService) throws ExecutionException {
- if (desugaredLibraryRetargeter != null) {
- desugaredLibraryRetargeter.synthesizeRetargetClasses(this, executorService);
+ private void runInterfaceDesugaringProcessors(
+ Flavor includeAllResources, ExecutorService executorService) throws ExecutionException {
+ assert !appView.getSyntheticItems().hasPendingSyntheticClasses();
+ if (interfaceMethodRewriter != null) {
+ interfaceMethodRewriter.runInterfaceDesugaringProcessors(
+ this, includeAllResources, executorService);
}
}
-
private void synthesizeEnumUnboxingUtilityMethods(ExecutorService executorService)
throws ExecutionException {
if (enumUnboxer != null) {
@@ -448,7 +440,10 @@
// Build a new application with jumbo string info,
Builder<?> builder = application.builder().setHighestSortingString(highestSortingString);
- desugarInterfaceMethods(builder, ExcludeDexResources, executor);
+ runInterfaceDesugaringProcessors(ExcludeDexResources, executor);
+ if (appView.options().isDesugaredLibraryCompilation()) {
+ EmulatedInterfaceProcessor.filterEmulatedInterfaceSubInterfaces(appView, builder);
+ }
processCovariantReturnTypeAnnotations(builder);
generateDesugaredLibraryAPIWrappers(builder, executor);
@@ -743,7 +738,7 @@
.run(executorService, feedback, timing);
}
if (enumUnboxer != null) {
- enumUnboxer.unboxEnums(postMethodProcessorBuilder, executorService, feedback);
+ enumUnboxer.unboxEnums(this, postMethodProcessorBuilder, executorService, feedback);
} else {
appView.setUnboxedEnums(EnumDataMap.empty());
}
@@ -764,6 +759,11 @@
}
timing.end();
+ if (enumUnboxer != null) {
+ // TODO(b/190098858): Uncomment when methods are synthesized on-the-fly.
+ // enumUnboxer.unsetRewriter();
+ }
+
// All the code that should be impacted by the lenses inserted between phase 1 and phase 2
// have now been processed and rewritten, we clear code lens rewriting so that the class
// staticizer and phase 3 does not perform again the rewriting.
@@ -793,11 +793,11 @@
builder.setHighestSortingString(highestSortingString);
printPhase("Interface method desugaring");
- desugarInterfaceMethods(builder, IncludeAllResources, executorService);
+ finalizeInterfaceMethodRewritingThroughIR(executorService);
+ runInterfaceDesugaringProcessors(IncludeAllResources, executorService);
feedback.updateVisibleOptimizationInfo();
printPhase("Utility classes synthesis");
- synthesizeRetargetClass(executorService);
synthesizeEnumUnboxingUtilityMethods(executorService);
printPhase("Desugared library API Conversion finalization");
@@ -1221,7 +1221,7 @@
if (appView.graphLens().hasCodeRewritings()) {
assert lensCodeRewriter != null;
timing.begin("Lens rewrite");
- lensCodeRewriter.rewrite(code, context);
+ lensCodeRewriter.rewrite(code, context, methodProcessor);
timing.end();
}
@@ -1461,14 +1461,6 @@
timing.end();
}
- if (desugaredLibraryRetargeter != null) {
- // The desugaredLibraryRetargeter should run before backportedMethodRewriter to be able to
- // perform backport rewriting before the methods can be retargeted.
- timing.begin("Retarget library methods");
- desugaredLibraryRetargeter.desugar(code);
- timing.end();
- }
-
previous = printMethod(code, "IR after lambda desugaring (SSA)", previous);
assert code.verifyTypes(appView);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index f44ec67..0cf836d 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -26,6 +26,7 @@
import static com.android.tools.r8.ir.code.Opcodes.MOVE_EXCEPTION;
import static com.android.tools.r8.ir.code.Opcodes.NEW_ARRAY_EMPTY;
import static com.android.tools.r8.ir.code.Opcodes.NEW_INSTANCE;
+import static com.android.tools.r8.ir.code.Opcodes.NEW_UNBOXED_ENUM_INSTANCE;
import static com.android.tools.r8.ir.code.Opcodes.RETURN;
import static com.android.tools.r8.ir.code.Opcodes.STATIC_GET;
import static com.android.tools.r8.ir.code.Opcodes.STATIC_PUT;
@@ -139,9 +140,11 @@
}
/** Replace type appearances, invoke targets and field accesses with actual definitions. */
- public void rewrite(IRCode code, ProgramMethod method) {
+ public void rewrite(IRCode code, ProgramMethod method, MethodProcessor methodProcessor) {
Set<Phi> affectedPhis =
- enumUnboxer != null ? enumUnboxer.rewriteCode(code) : Sets.newIdentityHashSet();
+ enumUnboxer != null
+ ? enumUnboxer.rewriteCode(code, methodProcessor)
+ : Sets.newIdentityHashSet();
GraphLens graphLens = appView.graphLens();
DexItemFactory factory = appView.dexItemFactory();
// Rewriting types that affects phi can cause us to compute TOP for cyclic phi's. To solve this
@@ -590,6 +593,9 @@
}
break;
+ case NEW_UNBOXED_ENUM_INSTANCE:
+ break;
+
case RETURN:
{
Return ret = current.asReturn();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java
index 140e0ce..9857098 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.conversion;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.ProgramMethod;
public abstract class MethodProcessor {
@@ -11,6 +12,8 @@
return false;
}
+ public abstract MethodProcessingContext createMethodProcessingContext(ProgramMethod method);
+
public abstract boolean isProcessedConcurrently(ProgramMethod method);
public abstract boolean shouldApplyCodeRewritings(ProgramMethod method);
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 cb8eeab..0d1c587 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
@@ -11,7 +11,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.UseRegistry;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
class NeedsIRDesugarUseRegistry extends UseRegistry {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
index f637225..e40cae1 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
@@ -45,6 +45,11 @@
}
@Override
+ public MethodProcessingContext createMethodProcessingContext(ProgramMethod method) {
+ return processorContext.createMethodProcessingContext(method);
+ }
+
+ @Override
public boolean shouldApplyCodeRewritings(ProgramMethod method) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
index 9d9b9f1..a8e5508 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
@@ -43,6 +43,11 @@
}
@Override
+ public MethodProcessingContext createMethodProcessingContext(ProgramMethod method) {
+ return processorContext.createMethodProcessingContext(method);
+ }
+
+ @Override
public boolean shouldApplyCodeRewritings(ProgramMethod method) {
assert !wave.contains(method);
return !processed.contains(method);
@@ -67,6 +72,10 @@
put(postOptimization.methodsToRevisit());
}
+ public void removePrunedMethods(Iterable<DexMethod> prunedMethod) {
+ methodsToReprocessBuilder.removeAll(prunedMethod);
+ }
+
// Some optimizations may change methods, creating new instances of the encoded methods with a
// new signature. The compiler needs to update the set of methods that must be reprocessed
// according to the graph lens.
@@ -128,8 +137,7 @@
assert feedback.noUpdatesLeft();
ThreadUtils.processItems(
wave,
- method ->
- consumer.accept(method, processorContext.createMethodProcessingContext(method)),
+ method -> consumer.accept(method, createMethodProcessingContext(method)),
executorService);
feedback.updateVisibleOptimizationInfo();
processed.addAll(wave);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
index 8969cba..9bd75fe 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
@@ -41,6 +41,8 @@
private final PostMethodProcessor.Builder postMethodProcessorBuilder;
private final Deque<SortedProgramMethodSet> waves;
+ private ProcessorContext processorContext;
+
private PrimaryMethodProcessor(
AppView<AppInfoWithLiveness> appView,
PostMethodProcessor.Builder postMethodProcessorBuilder,
@@ -62,6 +64,11 @@
}
@Override
+ public MethodProcessingContext createMethodProcessingContext(ProgramMethod method) {
+ return processorContext.createMethodProcessingContext(method);
+ }
+
+ @Override
public boolean isPrimaryMethodProcessor() {
return true;
}
@@ -125,7 +132,7 @@
TimingMerger merger =
timing.beginMerger("primary-processor", ThreadUtils.getNumberOfThreads(executorService));
while (!waves.isEmpty()) {
- ProcessorContext processorContext = appView.createProcessorContext();
+ processorContext = appView.createProcessorContext();
wave = waves.removeFirst();
assert !wave.isEmpty();
assert waveExtension.isEmpty();
@@ -135,9 +142,7 @@
ThreadUtils.processItemsWithResults(
wave,
method -> {
- Timing time =
- consumer.apply(
- method, processorContext.createMethodProcessingContext(method));
+ Timing time = consumer.apply(method, createMethodProcessingContext(method));
time.end();
return time;
},
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 b287221..197ab34 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
@@ -32,6 +32,7 @@
import com.android.tools.r8.ir.desugar.backports.ObjectsMethodRewrites;
import com.android.tools.r8.ir.desugar.backports.OptionalMethodRewrites;
import com.android.tools.r8.ir.desugar.backports.SparseArrayMethodRewrites;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeter;
import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfClassDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfClassDesugaringCollection.java
index 9b7c709..70d5123 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfClassDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfClassDesugaringCollection.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.desugar;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.ir.desugar.records.RecordRewriter;
/** Interface for desugaring a class. */
public abstract class CfClassDesugaringCollection {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfClassDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfClassDesugaringEventConsumer.java
index 9b02ab5..ef09aba 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfClassDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfClassDesugaringEventConsumer.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.conversion.D8MethodProcessor;
+import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer;
public abstract class CfClassDesugaringEventConsumer implements RecordDesugaringEventConsumer {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
index 148ea56..2d84083 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringCollection.java
@@ -7,9 +7,9 @@
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.RetargetingInfo;
import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
import com.android.tools.r8.utils.ThrowingConsumer;
-import java.util.function.Consumer;
/**
* Abstracts a collection of low-level desugarings (i.e., mappings from class-file instructions to
@@ -56,8 +56,5 @@
public abstract <T extends Throwable> void withD8NestBasedAccessDesugaring(
ThrowingConsumer<D8NestBasedAccessDesugaring, T> consumer) throws T;
- public abstract void withDesugaredLibraryRetargeter(
- Consumer<DesugaredLibraryRetargeter> consumer);
-
- public abstract void withRecordRewriter(Consumer<RecordRewriter> consumer);
+ public abstract RetargetingInfo getRetargetingInfo();
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
index 1c6b337..f05c16b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClasspathClass;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexReference;
@@ -14,13 +15,16 @@
import com.android.tools.r8.ir.conversion.ClassConverterResult;
import com.android.tools.r8.ir.conversion.D8MethodProcessor;
import com.android.tools.r8.ir.desugar.backports.BackportedMethodDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeterInstructionEventConsumer;
import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialBridgeInfo;
import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialToSelfDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.lambda.LambdaDeserializationMethodRemover;
import com.android.tools.r8.ir.desugar.lambda.LambdaDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.twr.TwrCloseResourceDesugaringEventConsumer;
+import com.android.tools.r8.shaking.Enqueuer.SyntheticAdditions;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collections;
@@ -44,7 +48,7 @@
RecordDesugaringEventConsumer,
TwrCloseResourceDesugaringEventConsumer,
InterfaceMethodDesugaringEventConsumer,
- DesugaredLibraryRetargeterEventConsumer {
+ DesugaredLibraryRetargeterInstructionEventConsumer {
public static D8CfInstructionDesugaringEventConsumer createForD8(
D8MethodProcessor methodProcessor) {
@@ -54,9 +58,10 @@
public static R8CfInstructionDesugaringEventConsumer createForR8(
AppView<? extends AppInfoWithClassHierarchy> appView,
BiConsumer<LambdaClass, ProgramMethod> lambdaClassConsumer,
- BiConsumer<ProgramMethod, ProgramMethod> twrCloseResourceMethodConsumer) {
+ BiConsumer<ProgramMethod, ProgramMethod> twrCloseResourceMethodConsumer,
+ SyntheticAdditions additions) {
return new R8CfInstructionDesugaringEventConsumer(
- appView, lambdaClassConsumer, twrCloseResourceMethodConsumer);
+ appView, lambdaClassConsumer, twrCloseResourceMethodConsumer, additions);
}
public static CfInstructionDesugaringEventConsumer createForDesugaredCode() {
@@ -73,7 +78,7 @@
}
@Override
- public void acceptForwardingMethod(ProgramMethod method) {
+ public void acceptInterfaceInjection(DexProgramClass clazz, DexClass newInterface) {
assert false;
}
@@ -155,12 +160,12 @@
@Override
public void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz) {
- // Intentionnaly empty.
+ // Intentionally empty.
}
@Override
- public void acceptForwardingMethod(ProgramMethod method) {
- methodProcessor.scheduleDesugaredMethodForProcessing(method);
+ public void acceptInterfaceInjection(DexProgramClass clazz, DexClass newInterface) {
+ // Intentionally empty.
}
@Override
@@ -287,6 +292,7 @@
// synthetic items.
private final BiConsumer<LambdaClass, ProgramMethod> lambdaClassConsumer;
private final BiConsumer<ProgramMethod, ProgramMethod> twrCloseResourceMethodConsumer;
+ private final SyntheticAdditions additions;
private final Map<LambdaClass, ProgramMethod> synthesizedLambdaClasses =
new IdentityHashMap<>();
@@ -295,10 +301,12 @@
public R8CfInstructionDesugaringEventConsumer(
AppView<? extends AppInfoWithClassHierarchy> appView,
BiConsumer<LambdaClass, ProgramMethod> lambdaClassConsumer,
- BiConsumer<ProgramMethod, ProgramMethod> twrCloseResourceMethodConsumer) {
+ BiConsumer<ProgramMethod, ProgramMethod> twrCloseResourceMethodConsumer,
+ SyntheticAdditions additions) {
this.appView = appView;
this.lambdaClassConsumer = lambdaClassConsumer;
this.twrCloseResourceMethodConsumer = twrCloseResourceMethodConsumer;
+ this.additions = additions;
}
@Override
@@ -308,15 +316,13 @@
}
@Override
- public void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz) {
- // TODO(b/188767735): R8 currently relies on IR desugaring.
- // The classpath class should be marked as liveNonProgramType.
+ public void acceptInterfaceInjection(DexProgramClass clazz, DexClass newInterface) {
+ additions.injectInterface(clazz, newInterface);
}
@Override
- public void acceptForwardingMethod(ProgramMethod method) {
- // TODO(b/188767735): R8 currently relies on IR desugaring.
- // The method should be marked live, and assert everything it references is traced.
+ public void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz) {
+ additions.addLiveClasspathClass(clazz);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaring.java
new file mode 100644
index 0000000..2a2de5f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaring.java
@@ -0,0 +1,9 @@
+// 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.ir.desugar;
+
+public interface CfPostProcessingDesugaring {
+
+ void postProcessingDesugaring(CfPostProcessingDesugaringEventConsumer eventConsumer);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
new file mode 100644
index 0000000..f659ad8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
@@ -0,0 +1,74 @@
+// 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.ir.desugar;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeterPostProcessor;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.RetargetingInfo;
+import java.util.Collections;
+import java.util.List;
+
+public abstract class CfPostProcessingDesugaringCollection {
+
+ public static CfPostProcessingDesugaringCollection create(
+ AppView<?> appView, RetargetingInfo retargetingInfo) {
+ if (appView.options().desugarState.isOn()) {
+ return NonEmptyCfPostProcessingDesugaringCollection.create(appView, retargetingInfo);
+ }
+ return empty();
+ }
+
+ static CfPostProcessingDesugaringCollection empty() {
+ return EmptyCfPostProcessingDesugaringCollection.getInstance();
+ }
+
+ public abstract void postProcessingDesugaring(
+ CfPostProcessingDesugaringEventConsumer eventConsumer);
+
+ public static class NonEmptyCfPostProcessingDesugaringCollection
+ extends CfPostProcessingDesugaringCollection {
+
+ private final List<CfPostProcessingDesugaring> desugarings;
+
+ public NonEmptyCfPostProcessingDesugaringCollection(
+ List<CfPostProcessingDesugaring> desugarings) {
+ this.desugarings = desugarings;
+ }
+
+ public static CfPostProcessingDesugaringCollection create(
+ AppView<?> appView, RetargetingInfo retargetingInfo) {
+ if (appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()) {
+ return empty();
+ }
+ return new NonEmptyCfPostProcessingDesugaringCollection(
+ Collections.singletonList(
+ new DesugaredLibraryRetargeterPostProcessor(appView, retargetingInfo)));
+ }
+
+ @Override
+ public void postProcessingDesugaring(CfPostProcessingDesugaringEventConsumer eventConsumer) {
+ for (CfPostProcessingDesugaring desugaring : desugarings) {
+ desugaring.postProcessingDesugaring(eventConsumer);
+ }
+ }
+ }
+
+ public static class EmptyCfPostProcessingDesugaringCollection
+ extends CfPostProcessingDesugaringCollection {
+
+ private static final EmptyCfPostProcessingDesugaringCollection INSTANCE =
+ new EmptyCfPostProcessingDesugaringCollection();
+
+ private EmptyCfPostProcessingDesugaringCollection() {}
+
+ private static EmptyCfPostProcessingDesugaringCollection getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public void postProcessingDesugaring(CfPostProcessingDesugaringEventConsumer eventConsumer) {
+ // Intentionally empty.
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
new file mode 100644
index 0000000..ec4f197
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
@@ -0,0 +1,113 @@
+// 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.ir.desugar;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClasspathClass;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.conversion.D8MethodProcessor;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeterInstructionEventConsumer.DesugaredLibraryRetargeterPostProcessingEventConsumer;
+import com.android.tools.r8.shaking.Enqueuer.SyntheticAdditions;
+import java.util.function.Consumer;
+
+/**
+ * Specialized Event consumer for desugaring finalization. During finalization, it is not possible
+ * to run any more instruction desugaring. If there are dependencies in between various desugaring,
+ * explicit calls must be done here.
+ */
+public abstract class CfPostProcessingDesugaringEventConsumer
+ implements DesugaredLibraryRetargeterPostProcessingEventConsumer {
+ protected DesugaredLibraryAPIConverter desugaredLibraryAPIConverter;
+
+ protected CfPostProcessingDesugaringEventConsumer(AppView<?> appView) {
+ this.desugaredLibraryAPIConverter = new DesugaredLibraryAPIConverter(appView, null);
+ }
+
+ public static D8CfPostProcessingDesugaringEventConsumer createForD8(
+ D8MethodProcessor methodProcessor, AppView<?> appView) {
+ return new D8CfPostProcessingDesugaringEventConsumer(methodProcessor, appView);
+ }
+
+ public static R8PostProcessingDesugaringEventConsumer createForR8(
+ AppView<?> appView, Consumer<ProgramMethod> methodConsumer, SyntheticAdditions additions) {
+ return new R8PostProcessingDesugaringEventConsumer(appView, methodConsumer, additions);
+ }
+
+ public void finalizeDesugaring() {
+ desugaredLibraryAPIConverter.generateTrackingWarnings();
+ }
+
+ public static class D8CfPostProcessingDesugaringEventConsumer
+ extends CfPostProcessingDesugaringEventConsumer {
+ private final D8MethodProcessor methodProcessor;
+
+ private D8CfPostProcessingDesugaringEventConsumer(
+ D8MethodProcessor methodProcessor, AppView<?> appView) {
+ super(appView);
+ this.methodProcessor = methodProcessor;
+ }
+
+ @Override
+ public void acceptDesugaredLibraryRetargeterDispatchProgramClass(DexProgramClass clazz) {
+ methodProcessor.scheduleDesugaredMethodsForProcessing(clazz.programMethods());
+ }
+
+ @Override
+ public void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void acceptInterfaceInjection(DexProgramClass clazz, DexClass newInterface) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void acceptForwardingMethod(ProgramMethod method) {
+ methodProcessor.scheduleDesugaredMethodForProcessing(method);
+ // TODO(b/189912077): Uncomment when API conversion is performed cf to cf in D8.
+ // desugaredLibraryAPIConverter.generateCallbackIfRequired(method);
+ }
+ }
+
+ public static class R8PostProcessingDesugaringEventConsumer
+ extends CfPostProcessingDesugaringEventConsumer {
+ private final Consumer<ProgramMethod> methodConsumer;
+ private final SyntheticAdditions additions;
+
+ protected R8PostProcessingDesugaringEventConsumer(
+ AppView<?> appView, Consumer<ProgramMethod> methodConsumer, SyntheticAdditions additions) {
+ super(appView);
+ this.methodConsumer = methodConsumer;
+ this.additions = additions;
+ }
+
+ @Override
+ public void acceptDesugaredLibraryRetargeterDispatchProgramClass(DexProgramClass clazz) {
+ clazz.programMethods().forEach(methodConsumer);
+ }
+
+ @Override
+ public void acceptInterfaceInjection(DexProgramClass clazz, DexClass newInterface) {
+ additions.injectInterface(clazz, newInterface);
+ }
+
+ @Override
+ public void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz) {
+ additions.addLiveClasspathClass(clazz);
+ }
+
+ @Override
+ public void acceptForwardingMethod(ProgramMethod method) {
+ methodConsumer.accept(method);
+ ProgramMethod callback = desugaredLibraryAPIConverter.generateCallbackIfRequired(method);
+ if (callback != null) {
+ methodConsumer.accept(callback);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
deleted file mode 100644
index eb354b0..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeter.java
+++ /dev/null
@@ -1,812 +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.ir.desugar;
-
-import static com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter.InvokeRetargetingResult.NO_REWRITING;
-
-import com.android.tools.r8.ProgramResource.Kind;
-import com.android.tools.r8.cf.code.CfInstruction;
-import com.android.tools.r8.cf.code.CfInvoke;
-import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
-import com.android.tools.r8.dex.Constants;
-import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.ClassAccessFlags;
-import com.android.tools.r8.graph.ClasspathOrLibraryClass;
-import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexClass;
-import com.android.tools.r8.graph.DexClassAndMethod;
-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.DexLibraryClass;
-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.DexString;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexTypeList;
-import com.android.tools.r8.graph.DirectMappedDexApplication;
-import com.android.tools.r8.graph.EnclosingMethodAttribute;
-import com.android.tools.r8.graph.GenericSignature.ClassSignature;
-import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
-import com.android.tools.r8.graph.InnerClassAttribute;
-import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.NestHostClassAttribute;
-import com.android.tools.r8.graph.NestMemberClassAttribute;
-import com.android.tools.r8.graph.ParameterAnnotationsList;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.ResolutionResult;
-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.InvokeMethod;
-import com.android.tools.r8.ir.code.InvokeStatic;
-import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.ir.synthetic.EmulateInterfaceSyntheticCfCodeProvider;
-import com.android.tools.r8.origin.SynthesizedOrigin;
-import com.android.tools.r8.synthesis.SyntheticClassBuilder;
-import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
-import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.StringDiagnostic;
-import com.android.tools.r8.utils.WorkList;
-import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
-import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
-import com.google.common.collect.Maps;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.IdentityHashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import org.objectweb.asm.Opcodes;
-
-public class DesugaredLibraryRetargeter implements CfInstructionDesugaring {
-
- private final AppView<?> appView;
- private final Map<DexMethod, DexMethod> retargetLibraryMember = new IdentityHashMap<>();
- // Map nonFinalRewrite hold a methodName -> method mapping for methods which are rewritten while
- // the holder is non final. In this case d8 needs to force resolution of given methods to see if
- // the invoke needs to be rewritten.
- private final Map<DexString, List<DexMethod>> nonFinalHolderRewrites = new IdentityHashMap<>();
- // Non final virtual library methods requiring generation of emulated dispatch.
- private final DexClassAndMethodSet emulatedDispatchMethods = DexClassAndMethodSet.create();
-
- private final SortedProgramMethodSet forwardingMethods = SortedProgramMethodSet.create();
-
- public DesugaredLibraryRetargeter(AppView<?> appView) {
- this.appView = appView;
- if (appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()) {
- return;
- }
- new RetargetingSetup().setUpRetargeting();
- }
-
- public static void checkForAssumedLibraryTypes(AppView<?> appView) {
- Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
- appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember();
- for (DexString methodName : retargetCoreLibMember.keySet()) {
- for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
- DexClass typeClass = appView.definitionFor(inType);
- if (typeClass == null) {
- warnMissingRetargetCoreLibraryMember(inType, appView);
- }
- }
- }
- }
-
- public static void amendLibraryWithRetargetedMembers(AppView<AppInfoWithClassHierarchy> appView) {
- Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
- appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember();
- Map<DexType, DexLibraryClass> synthesizedLibraryClasses =
- synthesizeLibraryClassesForRetargetedMembers(appView, retargetCoreLibMember);
- Map<DexLibraryClass, Set<DexEncodedMethod>> synthesizedLibraryMethods =
- synthesizedMembersForRetargetClasses(
- appView, retargetCoreLibMember, synthesizedLibraryClasses);
- synthesizedLibraryMethods.forEach(DexLibraryClass::addDirectMethods);
- DirectMappedDexApplication newApplication =
- appView
- .appInfo()
- .app()
- .asDirect()
- .builder()
- .addLibraryClasses(synthesizedLibraryClasses.values())
- .build();
- appView.setAppInfo(appView.appInfo().rebuildWithClassHierarchy(app -> newApplication));
- }
-
- private static Map<DexType, DexLibraryClass> synthesizeLibraryClassesForRetargetedMembers(
- AppView<AppInfoWithClassHierarchy> appView,
- Map<DexString, Map<DexType, DexType>> retargetCoreLibMember) {
- DexItemFactory dexItemFactory = appView.dexItemFactory();
- Map<DexType, DexLibraryClass> synthesizedLibraryClasses = new LinkedHashMap<>();
- for (Map<DexType, DexType> oldToNewTypeMap : retargetCoreLibMember.values()) {
- for (DexType newType : oldToNewTypeMap.values()) {
- if (appView.definitionFor(newType) == null) {
- synthesizedLibraryClasses.computeIfAbsent(
- newType,
- type ->
- // Synthesize a library class with the given name. Note that this is assuming that
- // the library class inherits directly from java.lang.Object, does not implement
- // any interfaces, etc.
- new DexLibraryClass(
- type,
- Kind.CF,
- new SynthesizedOrigin(
- "Desugared library retargeter", DesugaredLibraryRetargeter.class),
- ClassAccessFlags.fromCfAccessFlags(Constants.ACC_PUBLIC),
- dexItemFactory.objectType,
- DexTypeList.empty(),
- dexItemFactory.createString("DesugaredLibraryRetargeter"),
- NestHostClassAttribute.none(),
- NestMemberClassAttribute.emptyList(),
- EnclosingMethodAttribute.none(),
- InnerClassAttribute.emptyList(),
- ClassSignature.noSignature(),
- DexAnnotationSet.empty(),
- DexEncodedField.EMPTY_ARRAY,
- DexEncodedField.EMPTY_ARRAY,
- DexEncodedMethod.EMPTY_ARRAY,
- DexEncodedMethod.EMPTY_ARRAY,
- dexItemFactory.getSkipNameValidationForTesting()));
- }
- }
- }
- return synthesizedLibraryClasses;
- }
-
- private static Map<DexLibraryClass, Set<DexEncodedMethod>> synthesizedMembersForRetargetClasses(
- AppView<AppInfoWithClassHierarchy> appView,
- Map<DexString, Map<DexType, DexType>> retargetCoreLibMember,
- Map<DexType, DexLibraryClass> synthesizedLibraryClasses) {
- DexItemFactory dexItemFactory = appView.dexItemFactory();
- Map<DexLibraryClass, Set<DexEncodedMethod>> synthesizedMembers = new IdentityHashMap<>();
- for (Entry<DexString, Map<DexType, DexType>> entry : retargetCoreLibMember.entrySet()) {
- DexString methodName = entry.getKey();
- Map<DexType, DexType> types = entry.getValue();
- types.forEach(
- (oldType, newType) -> {
- DexClass oldClass = appView.definitionFor(oldType);
- DexLibraryClass newClass = synthesizedLibraryClasses.get(newType);
- if (oldClass == null || newClass == null) {
- return;
- }
- for (DexEncodedMethod method :
- oldClass.methods(method -> method.getName() == methodName)) {
- DexMethod retargetMethod = method.getReference().withHolder(newType, dexItemFactory);
- if (!method.isStatic()) {
- retargetMethod = retargetMethod.withExtraArgumentPrepended(oldType, dexItemFactory);
- }
- synthesizedMembers
- .computeIfAbsent(
- newClass,
- ignore -> new TreeSet<>(Comparator.comparing(DexEncodedMethod::getReference)))
- .add(
- new DexEncodedMethod(
- retargetMethod,
- MethodAccessFlags.fromCfAccessFlags(
- Constants.ACC_PUBLIC | Constants.ACC_STATIC, false),
- MethodTypeSignature.noSignature(),
- DexAnnotationSet.empty(),
- ParameterAnnotationsList.empty(),
- null,
- true));
- }
- });
- }
- return synthesizedMembers;
- }
-
- private static void warnMissingRetargetCoreLibraryMember(DexType type, AppView<?> appView) {
- StringDiagnostic warning =
- new StringDiagnostic(
- "Cannot retarget core library member "
- + type.getName()
- + " because the class is missing.");
- appView.options().reporter.warning(warning);
- }
-
- // Used by the ListOfBackportedMethods utility.
- void visit(Consumer<DexMethod> consumer) {
- retargetLibraryMember.keySet().forEach(consumer);
- }
-
- @Override
- public Collection<CfInstruction> desugarInstruction(
- CfInstruction instruction,
- FreshLocalProvider freshLocalProvider,
- LocalStackAllocator localStackAllocator,
- CfInstructionDesugaringEventConsumer eventConsumer,
- ProgramMethod context,
- MethodProcessingContext methodProcessingContext,
- DexItemFactory dexItemFactory) {
- InvokeRetargetingResult invokeRetargetingResult = computeNewInvokeTarget(instruction, context);
-
- if (!invokeRetargetingResult.hasNewInvokeTarget()) {
- return null;
- }
-
- DexMethod newInvokeTarget = invokeRetargetingResult.getNewInvokeTarget(eventConsumer);
- return Collections.singletonList(
- new CfInvoke(Opcodes.INVOKESTATIC, newInvokeTarget, instruction.asInvoke().isInterface()));
- }
-
- @Override
- public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
- return computeNewInvokeTarget(instruction, context).hasNewInvokeTarget();
- }
-
- @Deprecated // Use Cf to Cf desugaring instead.
- public void desugar(IRCode code) {
- if (retargetLibraryMember.isEmpty()) {
- return;
- }
-
- InstructionListIterator iterator = code.instructionListIterator();
- while (iterator.hasNext()) {
- Instruction instruction = iterator.next();
- if (!instruction.isInvokeMethod()) {
- continue;
- }
-
- InvokeMethod invoke = instruction.asInvokeMethod();
- DexMethod invokedMethod = invoke.getInvokedMethod();
- boolean isInterface = invoke.getInterfaceBit();
-
- InvokeRetargetingResult invokeRetargetingResult =
- computeNewInvokeTarget(
- invokedMethod, isInterface, invoke.isInvokeSuper(), code.context());
- if (invokeRetargetingResult.hasNewInvokeTarget()) {
- DexMethod newInvokeTarget = invokeRetargetingResult.getNewInvokeTarget(null);
- iterator.replaceCurrentInstruction(
- new InvokeStatic(newInvokeTarget, invoke.outValue(), invoke.inValues()));
- }
- }
- }
-
- static class InvokeRetargetingResult {
-
- static InvokeRetargetingResult NO_REWRITING =
- new InvokeRetargetingResult(false, ignored -> null);
-
- private final boolean hasNewInvokeTarget;
- private final Function<DesugaredLibraryRetargeterEventConsumer, DexMethod>
- newInvokeTargetSupplier;
-
- static InvokeRetargetingResult createInvokeRetargetingResult(DexMethod retarget) {
- if (retarget == null) {
- return NO_REWRITING;
- }
- return new InvokeRetargetingResult(true, ignored -> retarget);
- }
-
- private InvokeRetargetingResult(
- boolean hasNewInvokeTarget,
- Function<DesugaredLibraryRetargeterEventConsumer, DexMethod> newInvokeTargetSupplier) {
- this.hasNewInvokeTarget = hasNewInvokeTarget;
- this.newInvokeTargetSupplier = newInvokeTargetSupplier;
- }
-
- public boolean hasNewInvokeTarget() {
- return hasNewInvokeTarget;
- }
-
- public DexMethod getNewInvokeTarget(DesugaredLibraryRetargeterEventConsumer eventConsumer) {
- assert hasNewInvokeTarget();
- return newInvokeTargetSupplier.apply(eventConsumer);
- }
- }
-
- public boolean hasNewInvokeTarget(
- DexMethod invokedMethod, boolean isInterface, boolean isInvokeSuper, ProgramMethod context) {
- return computeNewInvokeTarget(invokedMethod, isInterface, isInvokeSuper, context)
- .hasNewInvokeTarget();
- }
-
- private InvokeRetargetingResult computeNewInvokeTarget(
- CfInstruction instruction, ProgramMethod context) {
- if (retargetLibraryMember.isEmpty() || !instruction.isInvoke()) {
- return NO_REWRITING;
- }
- CfInvoke cfInvoke = instruction.asInvoke();
- return computeNewInvokeTarget(
- cfInvoke.getMethod(),
- cfInvoke.isInterface(),
- cfInvoke.isInvokeSuper(context.getHolderType()),
- context);
- }
-
- private InvokeRetargetingResult computeNewInvokeTarget(
- DexMethod invokedMethod, boolean isInterface, boolean isInvokeSuper, ProgramMethod context) {
- InvokeRetargetingResult retarget = computeRetargetedMethod(invokedMethod, isInterface);
- if (!retarget.hasNewInvokeTarget()) {
- return NO_REWRITING;
- }
- if (isInvokeSuper && matchesNonFinalHolderRewrite(invokedMethod)) {
- DexClassAndMethod superTarget =
- appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context);
- // Final methods can be rewritten as a normal invoke.
- if (superTarget != null && !superTarget.getAccessFlags().isFinal()) {
- return InvokeRetargetingResult.createInvokeRetargetingResult(
- appView.options().desugaredLibraryConfiguration.retargetMethod(superTarget, appView));
- }
- }
- return retarget;
- }
-
- private InvokeRetargetingResult computeRetargetedMethod(
- DexMethod invokedMethod, boolean isInterface) {
- InvokeRetargetingResult invokeRetargetingResult = computeRetargetLibraryMember(invokedMethod);
- if (!invokeRetargetingResult.hasNewInvokeTarget()) {
- if (!matchesNonFinalHolderRewrite(invokedMethod)) {
- return NO_REWRITING;
- }
- // We need to force resolution, even on d8, to know if the invoke has to be rewritten.
- ResolutionResult resolutionResult =
- appView.appInfoForDesugaring().resolveMethod(invokedMethod, isInterface);
- if (resolutionResult.isFailedResolution()) {
- return NO_REWRITING;
- }
- DexEncodedMethod singleTarget = resolutionResult.getSingleTarget();
- assert singleTarget != null;
- invokeRetargetingResult = computeRetargetLibraryMember(singleTarget.getReference());
- }
- return invokeRetargetingResult;
- }
-
- private InvokeRetargetingResult computeRetargetLibraryMember(DexMethod method) {
- DexClassAndMethod emulatedMethod = emulatedDispatchMethods.get(method);
- if (emulatedMethod != null) {
- assert !emulatedMethod.getAccessFlags().isStatic();
- return new InvokeRetargetingResult(
- true,
- eventConsumer -> {
- DexType newHolder =
- ensureEmulatedHolderDispatchMethod(emulatedMethod, eventConsumer).type;
- return computeRetargetMethod(
- method, emulatedMethod.getAccessFlags().isStatic(), newHolder);
- });
- }
- return InvokeRetargetingResult.createInvokeRetargetingResult(retargetLibraryMember.get(method));
- }
-
- private boolean matchesNonFinalHolderRewrite(DexMethod method) {
- List<DexMethod> dexMethods = nonFinalHolderRewrites.get(method.name);
- if (dexMethods == null) {
- return false;
- }
- for (DexMethod dexMethod : dexMethods) {
- if (method.match(dexMethod)) {
- return true;
- }
- }
- return false;
- }
-
- DexMethod computeRetargetMethod(DexMethod method, boolean isStatic, DexType newHolder) {
- DexItemFactory factory = appView.dexItemFactory();
- DexProto newProto = isStatic ? method.getProto() : factory.prependHolderToProto(method);
- return factory.createMethod(newHolder, newProto, method.getName());
- }
-
- private class RetargetingSetup {
-
- private void setUpRetargeting() {
- DesugaredLibraryConfiguration desugaredLibraryConfiguration =
- appView.options().desugaredLibraryConfiguration;
- Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
- desugaredLibraryConfiguration.getRetargetCoreLibMember();
- for (DexString methodName : retargetCoreLibMember.keySet()) {
- for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
- DexClass typeClass = appView.definitionFor(inType);
- if (typeClass != null) {
- DexType newHolder = retargetCoreLibMember.get(methodName).get(inType);
- List<DexClassAndMethod> found = findMethodsWithName(methodName, typeClass);
- for (DexClassAndMethod method : found) {
- boolean emulatedDispatch = false;
- DexMethod methodReference = method.getReference();
- if (!typeClass.isFinal()) {
- nonFinalHolderRewrites.putIfAbsent(method.getName(), new ArrayList<>());
- nonFinalHolderRewrites.get(method.getName()).add(methodReference);
- if (!method.getAccessFlags().isStatic()) {
- if (isEmulatedInterfaceDispatch(method)) {
- // In this case interface method rewriter takes care of it.
- continue;
- } else if (!method.getAccessFlags().isFinal()) {
- // Virtual rewrites require emulated dispatch for inheritance.
- // The call is rewritten to the dispatch holder class instead.
- emulatedDispatchMethods.add(method);
- emulatedDispatch = true;
- }
- }
- }
- if (!emulatedDispatch) {
- retargetLibraryMember.put(
- methodReference,
- computeRetargetMethod(
- methodReference, method.getAccessFlags().isStatic(), newHolder));
- }
- }
- }
- }
- }
- if (desugaredLibraryConfiguration.isLibraryCompilation()) {
- // TODO(b/177977763): This is only a workaround rewriting invokes of j.u.Arrays.deepEquals0
- // to j.u.DesugarArrays.deepEquals0.
- DexItemFactory itemFactory = appView.options().dexItemFactory();
- DexString name = itemFactory.createString("deepEquals0");
- DexProto proto =
- itemFactory.createProto(
- itemFactory.booleanType, itemFactory.objectType, itemFactory.objectType);
- DexMethod source =
- itemFactory.createMethod(
- itemFactory.createType(itemFactory.arraysDescriptor), proto, name);
- DexMethod target =
- computeRetargetMethod(
- source, true, itemFactory.createType("Ljava/util/DesugarArrays;"));
- retargetLibraryMember.put(source, target);
-
- // TODO(b/181629049): This is only a workaround rewriting invokes of
- // j.u.TimeZone.getTimeZone taking a java.time.ZoneId.
- name = itemFactory.createString("getTimeZone");
- proto =
- itemFactory.createProto(
- itemFactory.createType("Ljava/util/TimeZone;"),
- itemFactory.createType("Ljava/time/ZoneId;"));
- source =
- itemFactory.createMethod(itemFactory.createType("Ljava/util/TimeZone;"), proto, name);
- target =
- computeRetargetMethod(
- source, true, itemFactory.createType("Ljava/util/DesugarTimeZone;"));
- retargetLibraryMember.put(source, target);
- }
- }
-
- private boolean isEmulatedInterfaceDispatch(DexClassAndMethod method) {
- // Answers true if this method is already managed through emulated interface dispatch.
- Map<DexType, DexType> emulateLibraryInterface =
- appView.options().desugaredLibraryConfiguration.getEmulateLibraryInterface();
- if (emulateLibraryInterface.isEmpty()) {
- return false;
- }
- DexMethod methodToFind = method.getReference();
-
- // Look-up all superclass and interfaces, if an emulated interface is found, and it implements
- // the method, answers true.
- WorkList<DexClass> worklist = WorkList.newIdentityWorkList(method.getHolder());
- while (worklist.hasNext()) {
- DexClass clazz = worklist.next();
- if (clazz.isInterface()
- && emulateLibraryInterface.containsKey(clazz.getType())
- && clazz.lookupMethod(methodToFind) != null) {
- return true;
- }
- // All super types are library class, or we are doing L8 compilation.
- clazz.forEachImmediateSupertype(
- superType -> {
- DexClass superClass = appView.definitionFor(superType);
- if (superClass != null) {
- worklist.addIfNotSeen(superClass);
- }
- });
- }
- return false;
- }
-
- private List<DexClassAndMethod> findMethodsWithName(DexString methodName, DexClass clazz) {
- List<DexClassAndMethod> found = new ArrayList<>();
- clazz.forEachClassMethodMatching(
- definition -> definition.getName() == methodName, found::add);
- assert !found.isEmpty()
- : "Should have found a method (library specifications) for "
- + clazz.toSourceString()
- + "."
- + methodName
- + ". Maybe the library used for the compilation should be newer.";
- return found;
- }
- }
-
- public void finalizeDesugaring(DesugaredLibraryRetargeterEventConsumer eventConsumer) {
- new EmulatedDispatchTreeFixer().fixApp(eventConsumer);
- }
-
- private void rewriteType(DexType type) {
- String newName =
- appView.options().desugaredLibraryConfiguration.convertJavaNameToDesugaredLibrary(type);
- DexType newType =
- appView.dexItemFactory().createType(DescriptorUtils.javaTypeToDescriptor(newName));
- appView.rewritePrefix.rewriteType(type, newType);
- }
-
- public DexClass ensureEmulatedHolderDispatchMethod(
- DexClassAndMethod emulatedDispatchMethod,
- DesugaredLibraryRetargeterEventConsumer eventConsumer) {
- assert eventConsumer != null || appView.enableWholeProgramOptimizations();
- DexClass interfaceClass =
- ensureEmulatedInterfaceDispatchMethod(emulatedDispatchMethod, eventConsumer);
- DexMethod itfMethod =
- interfaceClass.lookupMethod(emulatedDispatchMethod.getReference()).getReference();
- DexClass holderDispatch;
- if (appView.options().isDesugaredLibraryCompilation()) {
- holderDispatch =
- appView
- .getSyntheticItems()
- .ensureFixedClass(
- SyntheticKind.RETARGET_CLASS,
- emulatedDispatchMethod.getHolder(),
- appView,
- classBuilder ->
- buildHolderDispatchMethod(classBuilder, emulatedDispatchMethod, itfMethod),
- clazz -> {
- if (eventConsumer != null) {
- eventConsumer.acceptDesugaredLibraryRetargeterDispatchProgramClass(clazz);
- }
- });
- } else {
- ClasspathOrLibraryClass context =
- emulatedDispatchMethod.getHolder().asClasspathOrLibraryClass();
- assert context != null;
- holderDispatch =
- appView
- .getSyntheticItems()
- .ensureFixedClasspathClass(
- SyntheticKind.RETARGET_CLASS,
- context,
- appView,
- classBuilder ->
- buildHolderDispatchMethod(classBuilder, emulatedDispatchMethod, itfMethod),
- clazz -> {
- if (eventConsumer != null) {
- eventConsumer.acceptDesugaredLibraryRetargeterDispatchClasspathClass(clazz);
- }
- });
- }
- rewriteType(holderDispatch.type);
- return holderDispatch;
- }
-
- public DexClass ensureEmulatedInterfaceDispatchMethod(
- DexClassAndMethod emulatedDispatchMethod,
- DesugaredLibraryRetargeterEventConsumer eventConsumer) {
- assert eventConsumer != null || appView.enableWholeProgramOptimizations();
- DexClass interfaceDispatch;
- if (appView.options().isDesugaredLibraryCompilation()) {
- interfaceDispatch =
- appView
- .getSyntheticItems()
- .ensureFixedClass(
- SyntheticKind.RETARGET_INTERFACE,
- emulatedDispatchMethod.getHolder(),
- appView,
- classBuilder ->
- buildInterfaceDispatchMethod(classBuilder, emulatedDispatchMethod),
- clazz -> {
- if (eventConsumer != null) {
- eventConsumer.acceptDesugaredLibraryRetargeterDispatchProgramClass(clazz);
- }
- });
- } else {
- ClasspathOrLibraryClass context =
- emulatedDispatchMethod.getHolder().asClasspathOrLibraryClass();
- assert context != null;
- interfaceDispatch =
- appView
- .getSyntheticItems()
- .ensureFixedClasspathClass(
- SyntheticKind.RETARGET_INTERFACE,
- context,
- appView,
- classBuilder ->
- buildInterfaceDispatchMethod(classBuilder, emulatedDispatchMethod),
- clazz -> {
- if (eventConsumer != null) {
- eventConsumer.acceptDesugaredLibraryRetargeterDispatchClasspathClass(clazz);
- }
- });
- }
- rewriteType(interfaceDispatch.type);
- return interfaceDispatch;
- }
-
- private void buildInterfaceDispatchMethod(
- SyntheticClassBuilder<?, ?> classBuilder, DexClassAndMethod emulatedDispatchMethod) {
- classBuilder
- .setInterface()
- .addMethod(
- methodBuilder -> {
- MethodAccessFlags flags =
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC | Constants.ACC_ABSTRACT | Constants.ACC_SYNTHETIC,
- false);
- methodBuilder
- .setName(emulatedDispatchMethod.getName())
- .setProto(emulatedDispatchMethod.getProto())
- .setAccessFlags(flags);
- });
- }
-
- private <SCB extends SyntheticClassBuilder<?, ?>> void buildHolderDispatchMethod(
- SCB classBuilder, DexClassAndMethod emulatedDispatchMethod, DexMethod itfMethod) {
- classBuilder.addMethod(
- methodBuilder -> {
- DexMethod desugarMethod =
- appView
- .options()
- .desugaredLibraryConfiguration
- .retargetMethod(emulatedDispatchMethod, appView);
- assert desugarMethod
- != null; // This method is reached only for retarget core lib members.
- methodBuilder
- .setName(emulatedDispatchMethod.getName())
- .setProto(desugarMethod.proto)
- .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
- .setCode(
- methodSig ->
- new EmulateInterfaceSyntheticCfCodeProvider(
- emulatedDispatchMethod.getHolderType(),
- desugarMethod,
- itfMethod,
- Collections.emptyList(),
- appView)
- .generateCfCode());
- });
- }
-
- @Deprecated // Use Cf to Cf desugaring.
- public void synthesizeRetargetClasses(IRConverter converter, ExecutorService executorService)
- throws ExecutionException {
- assert appView.enableWholeProgramOptimizations();
- new EmulatedDispatchTreeFixer().fixApp(null);
- converter.processMethodsConcurrently(forwardingMethods, executorService);
- }
-
- // The rewrite of virtual calls requires to go through emulate dispatch. This class is responsible
- // for inserting interfaces on library boundaries and forwarding methods in the program, and to
- // synthesize the interfaces and emulated dispatch classes in the desugared library.
- class EmulatedDispatchTreeFixer {
-
- void fixApp(DesugaredLibraryRetargeterEventConsumer eventConsumer) {
- if (appView.options().isDesugaredLibraryCompilation()) {
- synthesizeEmulatedDispatchMethods(eventConsumer);
- } else {
- addInterfacesAndForwardingMethods(eventConsumer);
- }
- }
-
- private void addInterfacesAndForwardingMethods(
- DesugaredLibraryRetargeterEventConsumer eventConsumer) {
- assert !appView.options().isDesugaredLibraryCompilation();
- Map<DexType, List<DexClassAndMethod>> map = Maps.newIdentityHashMap();
- for (DexClassAndMethod emulatedDispatchMethod : emulatedDispatchMethods) {
- map.putIfAbsent(emulatedDispatchMethod.getHolderType(), new ArrayList<>(1));
- map.get(emulatedDispatchMethod.getHolderType()).add(emulatedDispatchMethod);
- }
- for (DexProgramClass clazz : appView.appInfo().classes()) {
- if (clazz.superType == null) {
- assert clazz.type == appView.dexItemFactory().objectType : clazz.type.toSourceString();
- continue;
- }
- DexClass superclass = appView.definitionFor(clazz.superType);
- // Only performs computation if superclass is a library class, but not object to filter out
- // the most common case.
- if (superclass != null
- && superclass.isLibraryClass()
- && superclass.type != appView.dexItemFactory().objectType) {
- map.forEach(
- (type, methods) -> {
- if (inherit(superclass.asLibraryClass(), type, emulatedDispatchMethods)) {
- addInterfacesAndForwardingMethods(eventConsumer, clazz, methods);
- }
- });
- }
- }
- }
-
- private boolean inherit(
- DexLibraryClass clazz, DexType typeToInherit, DexClassAndMethodSet retarget) {
- DexLibraryClass current = clazz;
- while (current.type != appView.dexItemFactory().objectType) {
- if (current.type == typeToInherit) {
- return true;
- }
- DexClass dexClass = appView.definitionFor(current.superType);
- if (dexClass == null || dexClass.isClasspathClass()) {
- reportInvalidLibrarySupertype(current, retarget);
- return false;
- } else if (dexClass.isProgramClass()) {
- // If dexClass is a program class, then it is already correctly desugared.
- return false;
- }
- current = dexClass.asLibraryClass();
- }
- return false;
- }
-
- private void addInterfacesAndForwardingMethods(
- DesugaredLibraryRetargeterEventConsumer eventConsumer,
- DexProgramClass clazz,
- List<DexClassAndMethod> methods) {
- // DesugaredLibraryRetargeter emulate dispatch: insertion of a marker interface & forwarding
- // methods.
- // We cannot use the ClassProcessor since this applies up to 26, while the ClassProcessor
- // applies up to 24.
- for (DexClassAndMethod method : methods) {
- DexClass dexClass = ensureEmulatedInterfaceDispatchMethod(method, eventConsumer);
- if (clazz.interfaces.contains(dexClass.type)) {
- // The class has already been desugared.
- continue;
- }
- clazz.addExtraInterfaces(Collections.singletonList(new ClassTypeSignature(dexClass.type)));
- if (clazz.lookupVirtualMethod(method.getReference()) == null) {
- DexEncodedMethod newMethod = createForwardingMethod(method, clazz);
- clazz.addVirtualMethod(newMethod);
- if (eventConsumer != null) {
- eventConsumer.acceptForwardingMethod(new ProgramMethod(clazz, newMethod));
- } else {
- assert appView.enableWholeProgramOptimizations();
- forwardingMethods.add(new ProgramMethod(clazz, newMethod));
- }
- }
- }
- }
-
- private DexEncodedMethod createForwardingMethod(DexClassAndMethod target, DexClass clazz) {
- // NOTE: Never add a forwarding method to methods of classes unknown or coming from
- // android.jar
- // even if this results in invalid code, these classes are never desugared.
- // In desugared library, emulated interface methods can be overridden by retarget lib members.
- DexMethod forwardMethod =
- appView.options().desugaredLibraryConfiguration.retargetMethod(target, appView);
- assert forwardMethod != null && forwardMethod != target.getReference();
- return DexEncodedMethod.createDesugaringForwardingMethod(
- target, clazz, forwardMethod, appView.dexItemFactory());
- }
-
- private void synthesizeEmulatedDispatchMethods(
- DesugaredLibraryRetargeterEventConsumer eventConsumer) {
- assert appView.options().isDesugaredLibraryCompilation();
- if (emulatedDispatchMethods.isEmpty()) {
- return;
- }
- for (DexClassAndMethod emulatedDispatchMethod : emulatedDispatchMethods) {
- ensureEmulatedHolderDispatchMethod(emulatedDispatchMethod, eventConsumer);
- }
- }
-
- private void reportInvalidLibrarySupertype(
- DexLibraryClass libraryClass, DexClassAndMethodSet retarget) {
- DexClass dexClass = appView.definitionFor(libraryClass.superType);
- String message;
- if (dexClass == null) {
- message = "missing";
- } else if (dexClass.isClasspathClass()) {
- message = "a classpath class";
- } else {
- message = "INVALID";
- assert false;
- }
- appView
- .options()
- .warningInvalidLibrarySuperclassForDesugar(
- dexClass == null ? libraryClass.getOrigin() : dexClass.getOrigin(),
- libraryClass.type,
- libraryClass.superType,
- message,
- retarget);
- }
-
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeterEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeterEventConsumer.java
deleted file mode 100644
index 3806aa6..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryRetargeterEventConsumer.java
+++ /dev/null
@@ -1,18 +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.ir.desugar;
-
-import com.android.tools.r8.graph.DexClasspathClass;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.ProgramMethod;
-
-public interface DesugaredLibraryRetargeterEventConsumer {
-
- void acceptDesugaredLibraryRetargeterDispatchProgramClass(DexProgramClass clazz);
-
- void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz);
-
- void acceptForwardingMethod(ProgramMethod method);
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
index 5b25c84..6cd9be6 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/EmptyCfInstructionDesugaringCollection.java
@@ -7,9 +7,9 @@
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.desugar.CfClassDesugaringCollection.EmptyCfClassDesugaringCollection;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.RetargetingInfo;
import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
import com.android.tools.r8.utils.ThrowingConsumer;
-import java.util.function.Consumer;
public class EmptyCfInstructionDesugaringCollection extends CfInstructionDesugaringCollection {
@@ -58,12 +58,7 @@
}
@Override
- public void withDesugaredLibraryRetargeter(Consumer<DesugaredLibraryRetargeter> consumer) {
- // Intentionally empty.
- }
-
- @Override
- public void withRecordRewriter(Consumer<RecordRewriter> consumer) {
- // Intentionally empty.
+ public RetargetingInfo getRetargetingInfo() {
+ return null;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
index 723e8b1..8437128 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/NonEmptyCfInstructionDesugaringCollection.java
@@ -14,11 +14,14 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.desugar.CfClassDesugaringCollection.EmptyCfClassDesugaringCollection;
import com.android.tools.r8.ir.desugar.CfClassDesugaringCollection.NonEmptyCfClassDesugaringCollection;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.RetargetingInfo;
import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialToSelfDesugaring;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring;
import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
import com.android.tools.r8.ir.desugar.nest.NestBasedAccessDesugaring;
+import com.android.tools.r8.ir.desugar.records.RecordRewriter;
import com.android.tools.r8.ir.desugar.stringconcat.StringConcatInstructionDesugaring;
import com.android.tools.r8.ir.desugar.twr.TwrCloseResourceInstructionDesugaring;
import com.android.tools.r8.utils.IntBox;
@@ -30,7 +33,6 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
-import java.util.function.Consumer;
public class NonEmptyCfInstructionDesugaringCollection extends CfInstructionDesugaringCollection {
@@ -53,7 +55,6 @@
BackportedMethodRewriter backportedMethodRewriter = null;
desugaredLibraryRetargeter =
appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember().isEmpty()
- || appView.enableWholeProgramOptimizations()
? null
: new DesugaredLibraryRetargeter(appView);
if (desugaredLibraryRetargeter != null) {
@@ -305,16 +306,10 @@
}
@Override
- public void withDesugaredLibraryRetargeter(Consumer<DesugaredLibraryRetargeter> consumer) {
+ public RetargetingInfo getRetargetingInfo() {
if (desugaredLibraryRetargeter != null) {
- consumer.accept(desugaredLibraryRetargeter);
+ return desugaredLibraryRetargeter.getRetargetingInfo();
}
- }
-
- @Override
- public void withRecordRewriter(Consumer<RecordRewriter> consumer) {
- if (recordRewriter != null) {
- consumer.accept(recordRewriter);
- }
+ return null;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java
similarity index 97%
rename from src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
rename to src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java
index 5fc1537..42db4ed 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryAPIConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryAPIConverter.java
@@ -1,8 +1,8 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// 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.ir.desugar;
+package com.android.tools.r8.ir.desugar.desugaredlibrary;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
@@ -164,6 +164,18 @@
}
}
+ public ProgramMethod generateCallbackIfRequired(ProgramMethod method) {
+ if (!shouldRegisterCallback(method)) {
+ return null;
+ }
+ if (trackedCallBackAPIs != null) {
+ trackedCallBackAPIs.add(method.getReference());
+ }
+ ProgramMethod callback = generateCallbackMethod(method.getDefinition(), method.getHolder());
+ method.getHolder().addVirtualMethod(callback.getDefinition());
+ return callback;
+ }
+
public boolean shouldRegisterCallback(ProgramMethod method) {
// Any override of a library method can be called by the library.
// We duplicate the method to have a vivified type version callable by the library and
@@ -186,6 +198,19 @@
.containsKey(method.getHolderType())) {
return false;
}
+ // In R8 we should be in the enqueuer, therefore we can duplicate a default method and both
+ // methods will be desugared.
+ // In D8, this happens after interface method desugaring, we cannot introduce new default
+ // methods, but we do not need to since this is a library override (invokes will resolve) and
+ // all implementors have been enhanced with a forwarding method which will be duplicated.
+ if (!appView.enableWholeProgramOptimizations()) {
+ if (method.getHolder().isInterface()
+ && method.getDefinition().isDefaultMethod()
+ && (!appView.options().canUseDefaultAndStaticInterfaceMethods()
+ || appView.options().isDesugaredLibraryCompilation())) {
+ return false;
+ }
+ }
if (!appView.options().desugaredLibraryConfiguration.supportAllCallbacksFromLibrary
&& appView.options().isDesugaredLibraryCompilation()) {
return false;
@@ -247,19 +272,6 @@
}
private synchronized void registerCallback(ProgramMethod method) {
- // In R8 we should be in the enqueuer, therefore we can duplicate a default method and both
- // methods will be desugared.
- // In D8, this happens after interface method desugaring, we cannot introduce new default
- // methods, but we do not need to since this is a library override (invokes will resolve) and
- // all implementors have been enhanced with a forwarding method which will be duplicated.
- if (!appView.enableWholeProgramOptimizations()) {
- if (method.getHolder().isInterface()
- && method.getDefinition().isDefaultMethod()
- && (!appView.options().canUseDefaultAndStaticInterfaceMethods()
- || appView.options().isDesugaredLibraryCompilation())) {
- return;
- }
- }
if (trackedCallBackAPIs != null) {
trackedCallBackAPIs.add(method.getReference());
}
@@ -315,12 +327,7 @@
}
public SortedProgramMethodSet generateCallbackMethods() {
- if (appView.options().testing.trackDesugaredAPIConversions) {
- generateTrackDesugaredAPIWarnings(trackedAPIs, "");
- generateTrackDesugaredAPIWarnings(trackedCallBackAPIs, "callback ");
- trackedAPIs.clear();
- trackedCallBackAPIs.clear();
- }
+ generateTrackingWarnings();
SortedProgramMethodSet allCallbackMethods = SortedProgramMethodSet.create();
pendingCallBackMethods.forEach(
(clazz, callbacks) -> {
@@ -337,6 +344,15 @@
return allCallbackMethods;
}
+ public void generateTrackingWarnings() {
+ if (appView.options().testing.trackDesugaredAPIConversions) {
+ generateTrackDesugaredAPIWarnings(trackedAPIs, "");
+ generateTrackDesugaredAPIWarnings(trackedCallBackAPIs, "callback ");
+ trackedAPIs.clear();
+ trackedCallBackAPIs.clear();
+ }
+ }
+
public void synthesizeWrappers(Consumer<DexClasspathClass> synthesizedCallback) {
wrapperSynthesizor.synthesizeWrappersForClasspath(synthesizedCallback);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryConfiguration.java
similarity index 98%
rename from src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
rename to src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryConfiguration.java
index a069091..cf81745 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfiguration.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryConfiguration.java
@@ -1,7 +1,7 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// 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.ir.desugar;
+package com.android.tools.r8.ir.desugar.desugaredlibrary;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.AppView;
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.desugar.PrefixRewritingMapper;
import com.android.tools.r8.ir.desugar.PrefixRewritingMapper.DesugarPrefixRewritingMapper;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryConfigurationParser.java
similarity index 98%
rename from src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
rename to src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryConfigurationParser.java
index 9b0da2d..9b0862e 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryConfigurationParser.java
@@ -1,8 +1,8 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// 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.ir.desugar;
+package com.android.tools.r8.ir.desugar.desugaredlibrary;
import com.android.tools.r8.StringResource;
import com.android.tools.r8.graph.DexItemFactory;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeter.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeter.java
new file mode 100644
index 0000000..43b26e1
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeter.java
@@ -0,0 +1,248 @@
+// 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.ir.desugar.desugaredlibrary;
+
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeter.InvokeRetargetingResult.NO_REWRITING;
+
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfInvoke;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
+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.DexProto;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.ResolutionResult;
+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.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.FreshLocalProvider;
+import com.android.tools.r8.ir.desugar.LocalStackAllocator;
+import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import org.objectweb.asm.Opcodes;
+
+public class DesugaredLibraryRetargeter implements CfInstructionDesugaring {
+
+ private final AppView<?> appView;
+ private final DesugaredLibraryRetargeterSyntheticHelper syntheticHelper;
+
+ private final RetargetingInfo retargetingInfo;
+ private final Map<DexMethod, DexMethod> retargetLibraryMember;
+ private final Map<DexString, List<DexMethod>> nonFinalHolderRewrites;
+ private final DexClassAndMethodSet emulatedDispatchMethods;
+
+ public DesugaredLibraryRetargeter(AppView<?> appView) {
+ this.appView = appView;
+ this.syntheticHelper = new DesugaredLibraryRetargeterSyntheticHelper(appView);
+ retargetingInfo = RetargetingInfo.get(appView);
+ retargetLibraryMember = retargetingInfo.getRetargetLibraryMember();
+ nonFinalHolderRewrites = retargetingInfo.getNonFinalHolderRewrites();
+ emulatedDispatchMethods = retargetingInfo.getEmulatedDispatchMethods();
+ }
+
+ // Used by the ListOfBackportedMethods utility.
+ public void visit(Consumer<DexMethod> consumer) {
+ retargetLibraryMember.keySet().forEach(consumer);
+ }
+
+ public RetargetingInfo getRetargetingInfo() {
+ return retargetingInfo;
+ }
+
+ @Override
+ public Collection<CfInstruction> desugarInstruction(
+ CfInstruction instruction,
+ FreshLocalProvider freshLocalProvider,
+ LocalStackAllocator localStackAllocator,
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ ProgramMethod context,
+ MethodProcessingContext methodProcessingContext,
+ DexItemFactory dexItemFactory) {
+ InvokeRetargetingResult invokeRetargetingResult = computeNewInvokeTarget(instruction, context);
+
+ if (!invokeRetargetingResult.hasNewInvokeTarget()) {
+ return null;
+ }
+
+ DexMethod newInvokeTarget = invokeRetargetingResult.getNewInvokeTarget(eventConsumer);
+ return Collections.singletonList(
+ new CfInvoke(Opcodes.INVOKESTATIC, newInvokeTarget, instruction.asInvoke().isInterface()));
+ }
+
+ @Override
+ public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) {
+ return computeNewInvokeTarget(instruction, context).hasNewInvokeTarget();
+ }
+
+ @Deprecated // Use Cf to Cf desugaring instead.
+ public void desugar(IRCode code) {
+ if (retargetLibraryMember.isEmpty()) {
+ return;
+ }
+
+ InstructionListIterator iterator = code.instructionListIterator();
+ while (iterator.hasNext()) {
+ Instruction instruction = iterator.next();
+ if (!instruction.isInvokeMethod()) {
+ continue;
+ }
+
+ InvokeMethod invoke = instruction.asInvokeMethod();
+ DexMethod invokedMethod = invoke.getInvokedMethod();
+ boolean isInterface = invoke.getInterfaceBit();
+
+ InvokeRetargetingResult invokeRetargetingResult =
+ computeNewInvokeTarget(
+ invokedMethod, isInterface, invoke.isInvokeSuper(), code.context());
+ if (invokeRetargetingResult.hasNewInvokeTarget()) {
+ DexMethod newInvokeTarget = invokeRetargetingResult.getNewInvokeTarget(null);
+ iterator.replaceCurrentInstruction(
+ new InvokeStatic(newInvokeTarget, invoke.outValue(), invoke.inValues()));
+ }
+ }
+ }
+
+ static class InvokeRetargetingResult {
+
+ static InvokeRetargetingResult NO_REWRITING =
+ new InvokeRetargetingResult(false, ignored -> null);
+
+ private final boolean hasNewInvokeTarget;
+ private final Function<DesugaredLibraryRetargeterInstructionEventConsumer, DexMethod>
+ newInvokeTargetSupplier;
+
+ static InvokeRetargetingResult createInvokeRetargetingResult(DexMethod retarget) {
+ if (retarget == null) {
+ return NO_REWRITING;
+ }
+ return new InvokeRetargetingResult(true, ignored -> retarget);
+ }
+
+ private InvokeRetargetingResult(
+ boolean hasNewInvokeTarget,
+ Function<DesugaredLibraryRetargeterInstructionEventConsumer, DexMethod>
+ newInvokeTargetSupplier) {
+ this.hasNewInvokeTarget = hasNewInvokeTarget;
+ this.newInvokeTargetSupplier = newInvokeTargetSupplier;
+ }
+
+ public boolean hasNewInvokeTarget() {
+ return hasNewInvokeTarget;
+ }
+
+ public DexMethod getNewInvokeTarget(
+ DesugaredLibraryRetargeterInstructionEventConsumer eventConsumer) {
+ assert hasNewInvokeTarget();
+ return newInvokeTargetSupplier.apply(eventConsumer);
+ }
+ }
+
+ public boolean hasNewInvokeTarget(
+ DexMethod invokedMethod, boolean isInterface, boolean isInvokeSuper, ProgramMethod context) {
+ return computeNewInvokeTarget(invokedMethod, isInterface, isInvokeSuper, context)
+ .hasNewInvokeTarget();
+ }
+
+ private InvokeRetargetingResult computeNewInvokeTarget(
+ CfInstruction instruction, ProgramMethod context) {
+ if (retargetLibraryMember.isEmpty() || !instruction.isInvoke()) {
+ return NO_REWRITING;
+ }
+ CfInvoke cfInvoke = instruction.asInvoke();
+ return computeNewInvokeTarget(
+ cfInvoke.getMethod(),
+ cfInvoke.isInterface(),
+ cfInvoke.isInvokeSuper(context.getHolderType()),
+ context);
+ }
+
+ private InvokeRetargetingResult computeNewInvokeTarget(
+ DexMethod invokedMethod, boolean isInterface, boolean isInvokeSuper, ProgramMethod context) {
+ InvokeRetargetingResult retarget = computeRetargetedMethod(invokedMethod, isInterface);
+ if (!retarget.hasNewInvokeTarget()) {
+ return NO_REWRITING;
+ }
+ if (isInvokeSuper && matchesNonFinalHolderRewrite(invokedMethod)) {
+ DexClassAndMethod superTarget =
+ appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context);
+ // Final methods can be rewritten as a normal invoke.
+ if (superTarget != null && !superTarget.getAccessFlags().isFinal()) {
+ return InvokeRetargetingResult.createInvokeRetargetingResult(
+ appView.options().desugaredLibraryConfiguration.retargetMethod(superTarget, appView));
+ }
+ }
+ return retarget;
+ }
+
+ private InvokeRetargetingResult computeRetargetedMethod(
+ DexMethod invokedMethod, boolean isInterface) {
+ InvokeRetargetingResult invokeRetargetingResult = computeRetargetLibraryMember(invokedMethod);
+ if (!invokeRetargetingResult.hasNewInvokeTarget()) {
+ if (!matchesNonFinalHolderRewrite(invokedMethod)) {
+ return NO_REWRITING;
+ }
+ // We need to force resolution, even on d8, to know if the invoke has to be rewritten.
+ ResolutionResult resolutionResult =
+ appView.appInfoForDesugaring().resolveMethod(invokedMethod, isInterface);
+ if (resolutionResult.isFailedResolution()) {
+ return NO_REWRITING;
+ }
+ DexEncodedMethod singleTarget = resolutionResult.getSingleTarget();
+ assert singleTarget != null;
+ invokeRetargetingResult = computeRetargetLibraryMember(singleTarget.getReference());
+ }
+ return invokeRetargetingResult;
+ }
+
+ private InvokeRetargetingResult computeRetargetLibraryMember(DexMethod method) {
+ DexClassAndMethod emulatedMethod = emulatedDispatchMethods.get(method);
+ if (emulatedMethod != null) {
+ assert !emulatedMethod.getAccessFlags().isStatic();
+ return new InvokeRetargetingResult(
+ true,
+ eventConsumer -> {
+ DexType newHolder =
+ syntheticHelper.ensureEmulatedHolderDispatchMethod(emulatedMethod, eventConsumer)
+ .type;
+ return computeRetargetMethod(
+ method, emulatedMethod.getAccessFlags().isStatic(), newHolder);
+ });
+ }
+ return InvokeRetargetingResult.createInvokeRetargetingResult(retargetLibraryMember.get(method));
+ }
+
+ private boolean matchesNonFinalHolderRewrite(DexMethod method) {
+ List<DexMethod> dexMethods = nonFinalHolderRewrites.get(method.name);
+ if (dexMethods == null) {
+ return false;
+ }
+ for (DexMethod dexMethod : dexMethods) {
+ if (method.match(dexMethod)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ DexMethod computeRetargetMethod(DexMethod method, boolean isStatic, DexType newHolder) {
+ DexItemFactory factory = appView.dexItemFactory();
+ DexProto newProto = isStatic ? method.getProto() : factory.prependHolderToProto(method);
+ return factory.createMethod(newHolder, newProto, method.getName());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterInstructionEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterInstructionEventConsumer.java
new file mode 100644
index 0000000..ae7630d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterInstructionEventConsumer.java
@@ -0,0 +1,25 @@
+// 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.ir.desugar.desugaredlibrary;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClasspathClass;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramMethod;
+
+public interface DesugaredLibraryRetargeterInstructionEventConsumer {
+
+ void acceptDesugaredLibraryRetargeterDispatchProgramClass(DexProgramClass clazz);
+
+ void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz);
+
+ void acceptInterfaceInjection(DexProgramClass clazz, DexClass newInterface);
+
+ interface DesugaredLibraryRetargeterPostProcessingEventConsumer
+ extends DesugaredLibraryRetargeterInstructionEventConsumer {
+
+ void acceptForwardingMethod(ProgramMethod method);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterLibraryTypeSynthesizor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterLibraryTypeSynthesizor.java
new file mode 100644
index 0000000..8c5a747
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterLibraryTypeSynthesizor.java
@@ -0,0 +1,166 @@
+// 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.ir.desugar.desugaredlibrary;
+
+import com.android.tools.r8.ProgramResource.Kind;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.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.DexLibraryClass;
+import com.android.tools.r8.graph.DexMethod;
+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.DirectMappedDexApplication;
+import com.android.tools.r8.graph.EnclosingMethodAttribute;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
+import com.android.tools.r8.graph.InnerClassAttribute;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.NestHostClassAttribute;
+import com.android.tools.r8.graph.NestMemberClassAttribute;
+import com.android.tools.r8.graph.ParameterAnnotationsList;
+import com.android.tools.r8.origin.SynthesizedOrigin;
+import com.android.tools.r8.utils.StringDiagnostic;
+import java.util.Comparator;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeSet;
+
+public class DesugaredLibraryRetargeterLibraryTypeSynthesizor {
+
+ public static void checkForAssumedLibraryTypes(AppView<?> appView) {
+ Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
+ appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember();
+ for (DexString methodName : retargetCoreLibMember.keySet()) {
+ for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
+ DexClass typeClass = appView.definitionFor(inType);
+ if (typeClass == null) {
+ warnMissingRetargetCoreLibraryMember(inType, appView);
+ }
+ }
+ }
+ }
+
+ public static void amendLibraryWithRetargetedMembers(AppView<AppInfoWithClassHierarchy> appView) {
+ Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
+ appView.options().desugaredLibraryConfiguration.getRetargetCoreLibMember();
+ Map<DexType, DexLibraryClass> synthesizedLibraryClasses =
+ synthesizeLibraryClassesForRetargetedMembers(appView, retargetCoreLibMember);
+ Map<DexLibraryClass, Set<DexEncodedMethod>> synthesizedLibraryMethods =
+ synthesizedMembersForRetargetClasses(
+ appView, retargetCoreLibMember, synthesizedLibraryClasses);
+ synthesizedLibraryMethods.forEach(DexLibraryClass::addDirectMethods);
+ DirectMappedDexApplication newApplication =
+ appView
+ .appInfo()
+ .app()
+ .asDirect()
+ .builder()
+ .addLibraryClasses(synthesizedLibraryClasses.values())
+ .build();
+ appView.setAppInfo(appView.appInfo().rebuildWithClassHierarchy(app -> newApplication));
+ }
+
+ private static Map<DexType, DexLibraryClass> synthesizeLibraryClassesForRetargetedMembers(
+ AppView<AppInfoWithClassHierarchy> appView,
+ Map<DexString, Map<DexType, DexType>> retargetCoreLibMember) {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ Map<DexType, DexLibraryClass> synthesizedLibraryClasses = new LinkedHashMap<>();
+ for (Map<DexType, DexType> oldToNewTypeMap : retargetCoreLibMember.values()) {
+ for (DexType newType : oldToNewTypeMap.values()) {
+ if (appView.definitionFor(newType) == null) {
+ synthesizedLibraryClasses.computeIfAbsent(
+ newType,
+ type ->
+ // Synthesize a library class with the given name. Note that this is assuming that
+ // the library class inherits directly from java.lang.Object, does not implement
+ // any interfaces, etc.
+ new DexLibraryClass(
+ type,
+ Kind.CF,
+ new SynthesizedOrigin(
+ "Desugared library retargeter", DesugaredLibraryRetargeter.class),
+ ClassAccessFlags.fromCfAccessFlags(Constants.ACC_PUBLIC),
+ dexItemFactory.objectType,
+ DexTypeList.empty(),
+ dexItemFactory.createString("DesugaredLibraryRetargeter"),
+ NestHostClassAttribute.none(),
+ NestMemberClassAttribute.emptyList(),
+ EnclosingMethodAttribute.none(),
+ InnerClassAttribute.emptyList(),
+ ClassSignature.noSignature(),
+ DexAnnotationSet.empty(),
+ DexEncodedField.EMPTY_ARRAY,
+ DexEncodedField.EMPTY_ARRAY,
+ DexEncodedMethod.EMPTY_ARRAY,
+ DexEncodedMethod.EMPTY_ARRAY,
+ dexItemFactory.getSkipNameValidationForTesting()));
+ }
+ }
+ }
+ return synthesizedLibraryClasses;
+ }
+
+ private static Map<DexLibraryClass, Set<DexEncodedMethod>> synthesizedMembersForRetargetClasses(
+ AppView<AppInfoWithClassHierarchy> appView,
+ Map<DexString, Map<DexType, DexType>> retargetCoreLibMember,
+ Map<DexType, DexLibraryClass> synthesizedLibraryClasses) {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ Map<DexLibraryClass, Set<DexEncodedMethod>> synthesizedMembers = new IdentityHashMap<>();
+ for (Entry<DexString, Map<DexType, DexType>> entry : retargetCoreLibMember.entrySet()) {
+ DexString methodName = entry.getKey();
+ Map<DexType, DexType> types = entry.getValue();
+ types.forEach(
+ (oldType, newType) -> {
+ DexClass oldClass = appView.definitionFor(oldType);
+ DexLibraryClass newClass = synthesizedLibraryClasses.get(newType);
+ if (oldClass == null || newClass == null) {
+ return;
+ }
+ for (DexEncodedMethod method :
+ oldClass.methods(method -> method.getName() == methodName)) {
+ DexMethod retargetMethod = method.getReference().withHolder(newType, dexItemFactory);
+ if (!method.isStatic()) {
+ retargetMethod = retargetMethod.withExtraArgumentPrepended(oldType, dexItemFactory);
+ }
+ synthesizedMembers
+ .computeIfAbsent(
+ newClass,
+ ignore -> new TreeSet<>(Comparator.comparing(DexEncodedMethod::getReference)))
+ .add(
+ new DexEncodedMethod(
+ retargetMethod,
+ MethodAccessFlags.fromCfAccessFlags(
+ Constants.ACC_PUBLIC | Constants.ACC_STATIC, false),
+ MethodTypeSignature.noSignature(),
+ DexAnnotationSet.empty(),
+ ParameterAnnotationsList.empty(),
+ null,
+ true));
+ }
+ });
+ }
+ return synthesizedMembers;
+ }
+
+ private static void warnMissingRetargetCoreLibraryMember(DexType type, AppView<?> appView) {
+ StringDiagnostic warning =
+ new StringDiagnostic(
+ "Cannot retarget core library member "
+ + type.getName()
+ + " because the class is missing.");
+ appView.options().reporter.warning(warning);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterPostProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterPostProcessor.java
new file mode 100644
index 0000000..0b0039e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterPostProcessor.java
@@ -0,0 +1,174 @@
+// 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.ir.desugar.desugaredlibrary;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexLibraryClass;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaring;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeterInstructionEventConsumer.DesugaredLibraryRetargeterPostProcessingEventConsumer;
+import com.android.tools.r8.utils.OptionalBool;
+import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
+import com.google.common.collect.Maps;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+// The rewrite of virtual calls requires to go through emulate dispatch. This class is responsible
+// for inserting interfaces on library boundaries and forwarding methods in the program, and to
+// synthesize the interfaces and emulated dispatch classes in the desugared library.
+public class DesugaredLibraryRetargeterPostProcessor implements CfPostProcessingDesugaring {
+
+ private final AppView<?> appView;
+ private final DesugaredLibraryRetargeterSyntheticHelper syntheticHelper;
+ private final DexClassAndMethodSet emulatedDispatchMethods;
+
+ public DesugaredLibraryRetargeterPostProcessor(
+ AppView<?> appView, RetargetingInfo retargetingInfo) {
+ this.appView = appView;
+ this.syntheticHelper = new DesugaredLibraryRetargeterSyntheticHelper(appView);
+ emulatedDispatchMethods = retargetingInfo.getEmulatedDispatchMethods();
+ }
+
+ @Override
+ public void postProcessingDesugaring(CfPostProcessingDesugaringEventConsumer eventConsumer) {
+ if (appView.options().isDesugaredLibraryCompilation()) {
+ ensureEmulatedDispatchMethodsSynthesized(eventConsumer);
+ } else {
+ ensureInterfacesAndForwardingMethodsSynthesized(eventConsumer);
+ }
+ }
+
+ private void ensureInterfacesAndForwardingMethodsSynthesized(
+ DesugaredLibraryRetargeterPostProcessingEventConsumer eventConsumer) {
+ assert !appView.options().isDesugaredLibraryCompilation();
+ Map<DexType, List<DexClassAndMethod>> map = Maps.newIdentityHashMap();
+ for (DexClassAndMethod emulatedDispatchMethod : emulatedDispatchMethods) {
+ map.putIfAbsent(emulatedDispatchMethod.getHolderType(), new ArrayList<>(1));
+ map.get(emulatedDispatchMethod.getHolderType()).add(emulatedDispatchMethod);
+ }
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
+ if (clazz.superType == null) {
+ assert clazz.type == appView.dexItemFactory().objectType : clazz.type.toSourceString();
+ continue;
+ }
+ DexClass superclass = appView.definitionFor(clazz.superType);
+ // Only performs computation if superclass is a library class, but not object to filter out
+ // the most common case.
+ if (superclass != null
+ && superclass.isLibraryClass()
+ && superclass.type != appView.dexItemFactory().objectType) {
+ map.forEach(
+ (type, methods) -> {
+ if (inherit(superclass.asLibraryClass(), type, emulatedDispatchMethods)) {
+ ensureInterfacesAndForwardingMethodsSynthesized(eventConsumer, clazz, methods);
+ }
+ });
+ }
+ }
+ }
+
+ private boolean inherit(
+ DexLibraryClass clazz, DexType typeToInherit, DexClassAndMethodSet retarget) {
+ DexLibraryClass current = clazz;
+ while (current.type != appView.dexItemFactory().objectType) {
+ if (current.type == typeToInherit) {
+ return true;
+ }
+ DexClass dexClass = appView.definitionFor(current.superType);
+ if (dexClass == null || dexClass.isClasspathClass()) {
+ reportInvalidLibrarySupertype(current, retarget);
+ return false;
+ } else if (dexClass.isProgramClass()) {
+ // If dexClass is a program class, then it is already correctly desugared.
+ return false;
+ }
+ current = dexClass.asLibraryClass();
+ }
+ return false;
+ }
+
+ private void ensureInterfacesAndForwardingMethodsSynthesized(
+ DesugaredLibraryRetargeterPostProcessingEventConsumer eventConsumer,
+ DexProgramClass clazz,
+ List<DexClassAndMethod> methods) {
+ // DesugaredLibraryRetargeter emulate dispatch: insertion of a marker interface & forwarding
+ // methods.
+ // We cannot use the ClassProcessor since this applies up to 26, while the ClassProcessor
+ // applies up to 24.
+ for (DexClassAndMethod method : methods) {
+ DexClass newInterface =
+ syntheticHelper.ensureEmulatedInterfaceDispatchMethod(method, eventConsumer);
+ if (clazz.interfaces.contains(newInterface.type)) {
+ // The class has already been desugared.
+ continue;
+ }
+ clazz.addExtraInterfaces(
+ Collections.singletonList(new ClassTypeSignature(newInterface.type)));
+ eventConsumer.acceptInterfaceInjection(clazz, newInterface);
+ if (clazz.lookupVirtualMethod(method.getReference()) == null) {
+ DexEncodedMethod newMethod = createForwardingMethod(method, clazz);
+ clazz.addVirtualMethod(newMethod);
+ eventConsumer.acceptForwardingMethod(new ProgramMethod(clazz, newMethod));
+ }
+ }
+ }
+
+ private DexEncodedMethod createForwardingMethod(DexClassAndMethod target, DexClass clazz) {
+ // NOTE: Never add a forwarding method to methods of classes unknown or coming from
+ // android.jar
+ // even if this results in invalid code, these classes are never desugared.
+ // In desugared library, emulated interface methods can be overridden by retarget lib members.
+ DexMethod forwardMethod =
+ appView.options().desugaredLibraryConfiguration.retargetMethod(target, appView);
+ assert forwardMethod != null && forwardMethod != target.getReference();
+ DexEncodedMethod desugaringForwardingMethod =
+ DexEncodedMethod.createDesugaringForwardingMethod(
+ target, clazz, forwardMethod, appView.dexItemFactory());
+ desugaringForwardingMethod.setLibraryMethodOverride(OptionalBool.TRUE);
+ return desugaringForwardingMethod;
+ }
+
+ private void ensureEmulatedDispatchMethodsSynthesized(
+ DesugaredLibraryRetargeterInstructionEventConsumer eventConsumer) {
+ assert appView.options().isDesugaredLibraryCompilation();
+ if (emulatedDispatchMethods.isEmpty()) {
+ return;
+ }
+ for (DexClassAndMethod emulatedDispatchMethod : emulatedDispatchMethods) {
+ syntheticHelper.ensureEmulatedHolderDispatchMethod(emulatedDispatchMethod, eventConsumer);
+ }
+ }
+
+ private void reportInvalidLibrarySupertype(
+ DexLibraryClass libraryClass, DexClassAndMethodSet retarget) {
+ DexClass dexClass = appView.definitionFor(libraryClass.superType);
+ String message;
+ if (dexClass == null) {
+ message = "missing";
+ } else if (dexClass.isClasspathClass()) {
+ message = "a classpath class";
+ } else {
+ message = "INVALID";
+ assert false;
+ }
+ appView
+ .options()
+ .warningInvalidLibrarySuperclassForDesugar(
+ dexClass == null ? libraryClass.getOrigin() : dexClass.getOrigin(),
+ libraryClass.type,
+ libraryClass.superType,
+ message,
+ retarget);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterSyntheticHelper.java
new file mode 100644
index 0000000..1f8322f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryRetargeterSyntheticHelper.java
@@ -0,0 +1,169 @@
+// 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.ir.desugar.desugaredlibrary;
+
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ClasspathOrLibraryClass;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.ir.synthetic.EmulateInterfaceSyntheticCfCodeProvider;
+import com.android.tools.r8.synthesis.SyntheticClassBuilder;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.DescriptorUtils;
+import java.util.Collections;
+
+public class DesugaredLibraryRetargeterSyntheticHelper {
+
+ private final AppView<?> appView;
+
+ public DesugaredLibraryRetargeterSyntheticHelper(AppView<?> appView) {
+ this.appView = appView;
+ }
+
+ public DexClass ensureEmulatedHolderDispatchMethod(
+ DexClassAndMethod emulatedDispatchMethod,
+ DesugaredLibraryRetargeterInstructionEventConsumer eventConsumer) {
+ assert eventConsumer != null || appView.enableWholeProgramOptimizations();
+ DexClass interfaceClass =
+ ensureEmulatedInterfaceDispatchMethod(emulatedDispatchMethod, eventConsumer);
+ DexMethod itfMethod =
+ interfaceClass.lookupMethod(emulatedDispatchMethod.getReference()).getReference();
+ DexClass holderDispatch;
+ if (appView.options().isDesugaredLibraryCompilation()) {
+ holderDispatch =
+ appView
+ .getSyntheticItems()
+ .ensureFixedClass(
+ SyntheticKind.RETARGET_CLASS,
+ emulatedDispatchMethod.getHolder(),
+ appView,
+ classBuilder ->
+ buildHolderDispatchMethod(classBuilder, emulatedDispatchMethod, itfMethod),
+ clazz -> {
+ if (eventConsumer != null) {
+ eventConsumer.acceptDesugaredLibraryRetargeterDispatchProgramClass(clazz);
+ }
+ });
+ } else {
+ ClasspathOrLibraryClass context =
+ emulatedDispatchMethod.getHolder().asClasspathOrLibraryClass();
+ assert context != null;
+ holderDispatch =
+ appView
+ .getSyntheticItems()
+ .ensureFixedClasspathClass(
+ SyntheticKind.RETARGET_CLASS,
+ context,
+ appView,
+ classBuilder ->
+ buildHolderDispatchMethod(classBuilder, emulatedDispatchMethod, itfMethod),
+ clazz -> {
+ if (eventConsumer != null) {
+ eventConsumer.acceptDesugaredLibraryRetargeterDispatchClasspathClass(clazz);
+ }
+ });
+ }
+ rewriteType(holderDispatch.type);
+ return holderDispatch;
+ }
+
+ public DexClass ensureEmulatedInterfaceDispatchMethod(
+ DexClassAndMethod emulatedDispatchMethod,
+ DesugaredLibraryRetargeterInstructionEventConsumer eventConsumer) {
+ assert eventConsumer != null || appView.enableWholeProgramOptimizations();
+ DexClass interfaceDispatch;
+ if (appView.options().isDesugaredLibraryCompilation()) {
+ interfaceDispatch =
+ appView
+ .getSyntheticItems()
+ .ensureFixedClass(
+ SyntheticKind.RETARGET_INTERFACE,
+ emulatedDispatchMethod.getHolder(),
+ appView,
+ classBuilder ->
+ buildInterfaceDispatchMethod(classBuilder, emulatedDispatchMethod),
+ clazz -> {
+ if (eventConsumer != null) {
+ eventConsumer.acceptDesugaredLibraryRetargeterDispatchProgramClass(clazz);
+ }
+ });
+ } else {
+ ClasspathOrLibraryClass context =
+ emulatedDispatchMethod.getHolder().asClasspathOrLibraryClass();
+ assert context != null;
+ interfaceDispatch =
+ appView
+ .getSyntheticItems()
+ .ensureFixedClasspathClass(
+ SyntheticKind.RETARGET_INTERFACE,
+ context,
+ appView,
+ classBuilder ->
+ buildInterfaceDispatchMethod(classBuilder, emulatedDispatchMethod),
+ clazz -> {
+ if (eventConsumer != null) {
+ eventConsumer.acceptDesugaredLibraryRetargeterDispatchClasspathClass(clazz);
+ }
+ });
+ }
+ rewriteType(interfaceDispatch.type);
+ return interfaceDispatch;
+ }
+
+ private void buildInterfaceDispatchMethod(
+ SyntheticClassBuilder<?, ?> classBuilder, DexClassAndMethod emulatedDispatchMethod) {
+ classBuilder
+ .setInterface()
+ .addMethod(
+ methodBuilder -> {
+ MethodAccessFlags flags =
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC | Constants.ACC_ABSTRACT | Constants.ACC_SYNTHETIC,
+ false);
+ methodBuilder
+ .setName(emulatedDispatchMethod.getName())
+ .setProto(emulatedDispatchMethod.getProto())
+ .setAccessFlags(flags);
+ });
+ }
+
+ private <SCB extends SyntheticClassBuilder<?, ?>> void buildHolderDispatchMethod(
+ SCB classBuilder, DexClassAndMethod emulatedDispatchMethod, DexMethod itfMethod) {
+ classBuilder.addMethod(
+ methodBuilder -> {
+ DexMethod desugarMethod =
+ appView
+ .options()
+ .desugaredLibraryConfiguration
+ .retargetMethod(emulatedDispatchMethod, appView);
+ assert desugarMethod
+ != null; // This method is reached only for retarget core lib members.
+ methodBuilder
+ .setName(emulatedDispatchMethod.getName())
+ .setProto(desugarMethod.proto)
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setCode(
+ methodSig ->
+ new EmulateInterfaceSyntheticCfCodeProvider(
+ emulatedDispatchMethod.getHolderType(),
+ desugarMethod,
+ itfMethod,
+ Collections.emptyList(),
+ appView)
+ .generateCfCode());
+ });
+ }
+
+ private void rewriteType(DexType type) {
+ String newName =
+ appView.options().desugaredLibraryConfiguration.convertJavaNameToDesugaredLibrary(type);
+ DexType newType =
+ appView.dexItemFactory().createType(DescriptorUtils.javaTypeToDescriptor(newName));
+ appView.rewritePrefix.rewriteType(type, newType);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java
similarity index 99%
rename from src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
rename to src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java
index 00c8bff..6c1e889 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/DesugaredLibraryWrapperSynthesizer.java
@@ -1,8 +1,8 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// 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.ir.desugar;
+package com.android.tools.r8.ir.desugar.desugaredlibrary;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/RetargetingInfo.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/RetargetingInfo.java
new file mode 100644
index 0000000..525801f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/RetargetingInfo.java
@@ -0,0 +1,195 @@
+// 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.ir.desugar.desugaredlibrary;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+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.DexType;
+import com.android.tools.r8.utils.WorkList;
+import com.android.tools.r8.utils.collections.DexClassAndMethodSet;
+import com.google.common.collect.ImmutableMap;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class RetargetingInfo {
+
+ private final Map<DexMethod, DexMethod> retargetLibraryMember;
+ // Map nonFinalRewrite hold a methodName -> method mapping for methods which are rewritten while
+ // the holder is non final. In this case d8 needs to force resolution of given methods to see if
+ // the invoke needs to be rewritten.
+ private final Map<DexString, List<DexMethod>> nonFinalHolderRewrites;
+ // Non final virtual library methods requiring generation of emulated dispatch.
+ private final DexClassAndMethodSet emulatedDispatchMethods;
+
+ RetargetingInfo(
+ Map<DexMethod, DexMethod> retargetLibraryMember,
+ Map<DexString, List<DexMethod>> nonFinalHolderRewrites,
+ DexClassAndMethodSet emulatedDispatchMethods) {
+ this.retargetLibraryMember = retargetLibraryMember;
+ this.nonFinalHolderRewrites = nonFinalHolderRewrites;
+ this.emulatedDispatchMethods = emulatedDispatchMethods;
+ }
+
+ public static synchronized RetargetingInfo get(AppView<?> appView) {
+ return new RetargetingInfoBuilder(appView).computeRetargetingInfo();
+ }
+
+ public Map<DexMethod, DexMethod> getRetargetLibraryMember() {
+ return retargetLibraryMember;
+ }
+
+ public Map<DexString, List<DexMethod>> getNonFinalHolderRewrites() {
+ return nonFinalHolderRewrites;
+ }
+
+ public DexClassAndMethodSet getEmulatedDispatchMethods() {
+ return emulatedDispatchMethods;
+ }
+
+ private static class RetargetingInfoBuilder {
+
+ private final AppView<?> appView;
+ private final Map<DexMethod, DexMethod> retargetLibraryMember = new IdentityHashMap<>();
+ private final Map<DexString, List<DexMethod>> nonFinalHolderRewrites = new IdentityHashMap<>();
+ private final DexClassAndMethodSet emulatedDispatchMethods = DexClassAndMethodSet.create();
+
+ public RetargetingInfoBuilder(AppView<?> appView) {
+ this.appView = appView;
+ }
+
+ private RetargetingInfo computeRetargetingInfo() {
+ DesugaredLibraryConfiguration desugaredLibraryConfiguration =
+ appView.options().desugaredLibraryConfiguration;
+ Map<DexString, Map<DexType, DexType>> retargetCoreLibMember =
+ desugaredLibraryConfiguration.getRetargetCoreLibMember();
+ if (retargetCoreLibMember.isEmpty()) {
+ return new RetargetingInfo(
+ ImmutableMap.of(), ImmutableMap.of(), DexClassAndMethodSet.empty());
+ }
+ for (DexString methodName : retargetCoreLibMember.keySet()) {
+ for (DexType inType : retargetCoreLibMember.get(methodName).keySet()) {
+ DexClass typeClass = appView.definitionFor(inType);
+ if (typeClass != null) {
+ DexType newHolder = retargetCoreLibMember.get(methodName).get(inType);
+ List<DexClassAndMethod> found = findMethodsWithName(methodName, typeClass);
+ for (DexClassAndMethod method : found) {
+ boolean emulatedDispatch = false;
+ DexMethod methodReference = method.getReference();
+ if (!typeClass.isFinal()) {
+ nonFinalHolderRewrites.putIfAbsent(method.getName(), new ArrayList<>());
+ nonFinalHolderRewrites.get(method.getName()).add(methodReference);
+ if (!method.getAccessFlags().isStatic()) {
+ if (isEmulatedInterfaceDispatch(method)) {
+ // In this case interface method rewriter takes care of it.
+ continue;
+ } else if (!method.getAccessFlags().isFinal()) {
+ // Virtual rewrites require emulated dispatch for inheritance.
+ // The call is rewritten to the dispatch holder class instead.
+ emulatedDispatchMethods.add(method);
+ emulatedDispatch = true;
+ }
+ }
+ }
+ if (!emulatedDispatch) {
+ retargetLibraryMember.put(
+ methodReference,
+ computeRetargetMethod(
+ methodReference, method.getAccessFlags().isStatic(), newHolder));
+ }
+ }
+ }
+ }
+ }
+ if (desugaredLibraryConfiguration.isLibraryCompilation()) {
+ // TODO(b/177977763): This is only a workaround rewriting invokes of j.u.Arrays.deepEquals0
+ // to j.u.DesugarArrays.deepEquals0.
+ DexItemFactory itemFactory = appView.options().dexItemFactory();
+ DexString name = itemFactory.createString("deepEquals0");
+ DexProto proto =
+ itemFactory.createProto(
+ itemFactory.booleanType, itemFactory.objectType, itemFactory.objectType);
+ DexMethod source =
+ itemFactory.createMethod(
+ itemFactory.createType(itemFactory.arraysDescriptor), proto, name);
+ DexMethod target =
+ computeRetargetMethod(
+ source, true, itemFactory.createType("Ljava/util/DesugarArrays;"));
+ retargetLibraryMember.put(source, target);
+ // TODO(b/181629049): This is only a workaround rewriting invokes of
+ // j.u.TimeZone.getTimeZone taking a java.time.ZoneId.
+ name = itemFactory.createString("getTimeZone");
+ proto =
+ itemFactory.createProto(
+ itemFactory.createType("Ljava/util/TimeZone;"),
+ itemFactory.createType("Ljava/time/ZoneId;"));
+ source =
+ itemFactory.createMethod(itemFactory.createType("Ljava/util/TimeZone;"), proto, name);
+ target =
+ computeRetargetMethod(
+ source, true, itemFactory.createType("Ljava/util/DesugarTimeZone;"));
+ retargetLibraryMember.put(source, target);
+ }
+ return new RetargetingInfo(
+ ImmutableMap.copyOf(retargetLibraryMember),
+ ImmutableMap.copyOf(nonFinalHolderRewrites),
+ emulatedDispatchMethods);
+ }
+
+ DexMethod computeRetargetMethod(DexMethod method, boolean isStatic, DexType newHolder) {
+ DexItemFactory factory = appView.dexItemFactory();
+ DexProto newProto = isStatic ? method.getProto() : factory.prependHolderToProto(method);
+ return factory.createMethod(newHolder, newProto, method.getName());
+ }
+
+ private boolean isEmulatedInterfaceDispatch(DexClassAndMethod method) {
+ // Answers true if this method is already managed through emulated interface dispatch.
+ Map<DexType, DexType> emulateLibraryInterface =
+ appView.options().desugaredLibraryConfiguration.getEmulateLibraryInterface();
+ if (emulateLibraryInterface.isEmpty()) {
+ return false;
+ }
+ DexMethod methodToFind = method.getReference();
+ // Look-up all superclass and interfaces, if an emulated interface is found, and it implements
+ // the method, answers true.
+ WorkList<DexClass> worklist = WorkList.newIdentityWorkList(method.getHolder());
+ while (worklist.hasNext()) {
+ DexClass clazz = worklist.next();
+ if (clazz.isInterface()
+ && emulateLibraryInterface.containsKey(clazz.getType())
+ && clazz.lookupMethod(methodToFind) != null) {
+ return true;
+ }
+ // All super types are library class, or we are doing L8 compilation.
+ clazz.forEachImmediateSupertype(
+ superType -> {
+ DexClass superClass = appView.definitionFor(superType);
+ if (superClass != null) {
+ worklist.addIfNotSeen(superClass);
+ }
+ });
+ }
+ return false;
+ }
+
+ private List<DexClassAndMethod> findMethodsWithName(DexString methodName, DexClass clazz) {
+ List<DexClassAndMethod> found = new ArrayList<>();
+ clazz.forEachClassMethodMatching(
+ definition -> definition.getName() == methodName, found::add);
+ assert !found.isEmpty()
+ : "Should have found a method (library specifications) for "
+ + clazz.toSourceString()
+ + "."
+ + methodName
+ + ". Maybe the library used for the compilation should be newer.";
+ return found;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index 3381ece..d86d22c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -14,7 +14,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
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.DexClassAndMember;
import com.android.tools.r8.graph.DexClassAndMethod;
@@ -390,8 +389,7 @@
// We introduce forwarding methods only once all desugaring has been performed to avoid
// confusing the look-up with inserted forwarding methods.
@Override
- public final void finalizeProcessing(
- DexApplication.Builder<?> builder, ProgramMethodSet synthesizedMethods) {
+ public final void finalizeProcessing(ProgramMethodSet synthesizedMethods) {
newSyntheticMethods.forEach(
(clazz, newForwardingMethods) -> {
clazz.addVirtualMethods(newForwardingMethods.toDefinitionSet());
@@ -819,7 +817,7 @@
// In desugared library, emulated interface methods can be overridden by retarget lib members.
DexMethod forwardMethod =
target.getHolder().isInterface()
- ? rewriter.defaultAsMethodOfCompanionClass(target)
+ ? rewriter.ensureDefaultAsMethodOfCompanionClassStub(target).getReference()
: appView.options().desugaredLibraryConfiguration.retargetMethod(target, appView);
DexEncodedMethod desugaringForwardingMethod =
DexEncodedMethod.createDesugaringForwardingMethod(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
index 30fc05c..b9e9889 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceProcessor.java
@@ -5,9 +5,8 @@
import static com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.emulateInterfaceLibraryMethod;
-import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexApplication.Builder;
+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.DexMethod;
@@ -37,6 +36,9 @@
import java.util.Set;
public final class EmulatedInterfaceProcessor implements InterfaceDesugaringProcessor {
+
+ private static final String JUNK_SUFFIX = "$JUNK";
+
private final AppView<?> appView;
private final InterfaceMethodRewriter rewriter;
private final Map<DexType, DexType> emulatedInterfaces;
@@ -163,20 +165,20 @@
private void synthesizeEmulatedInterfaceMethod(
ProgramMethod method, DexProgramClass theInterface, SyntheticMethodBuilder methodBuilder) {
+ assert !method.getDefinition().isStatic();
DexMethod libraryMethod =
method
.getReference()
.withHolder(emulatedInterfaces.get(theInterface.type), appView.dexItemFactory());
DexMethod companionMethod =
- method.getAccessFlags().isStatic()
- ? rewriter.staticAsMethodOfCompanionClass(method)
- : rewriter.defaultAsMethodOfCompanionClass(method);
+ rewriter.ensureDefaultAsMethodOfProgramCompanionClassStub(method).getReference();
List<Pair<DexType, DexMethod>> extraDispatchCases =
getDispatchCases(method, theInterface, companionMethod);
DexMethod emulatedMethod = emulateInterfaceLibraryMethod(method, appView.dexItemFactory());
methodBuilder
.setName(emulatedMethod.getName())
.setProto(emulatedMethod.getProto())
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
.setCode(
theMethod ->
new EmulateInterfaceSyntheticCfCodeProvider(
@@ -185,10 +187,7 @@
libraryMethod,
extraDispatchCases,
appView)
- .generateCfCode())
- .setAccessFlags(
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_SYNTHETIC | Constants.ACC_STATIC | Constants.ACC_PUBLIC, false));
+ .generateCfCode());
}
private List<Pair<DexType, DexMethod>> getDispatchCases(
@@ -283,7 +282,7 @@
}
@Override
- public void finalizeProcessing(Builder<?> builder, ProgramMethodSet synthesizedMethods) {
+ public void finalizeProcessing(ProgramMethodSet synthesizedMethods) {
warnMissingEmulatedInterfaces();
if (!appView.options().isDesugaredLibraryCompilation()) {
return;
@@ -298,24 +297,29 @@
}
}
}
- // TODO(b/183918843): Investigate what to do for the filtering, the minimum would be to make
- // the rewriting rule explicit instead of using the synthesized class prefix.
- filterEmulatedInterfaceSubInterfaces(builder);
}
- private void filterEmulatedInterfaceSubInterfaces(Builder<?> builder) {
+ // TODO(b/183918843): Investigate what to do. The whole method is trying to fill a hole in the
+ // desugaring library specifications by patching types and classes through questionable renaming.
+ public static void filterEmulatedInterfaceSubInterfaces(
+ AppView<?> appView, DexApplication.Builder<?> builder) {
+ assert appView.options().isDesugaredLibraryCompilation();
ArrayList<DexProgramClass> filteredProgramClasses = new ArrayList<>();
- for (DexProgramClass clazz : builder.getProgramClasses()) {
+ for (DexProgramClass clazz : appView.appInfo().classes()) {
if (clazz.isInterface()
- && !rewriter.isEmulatedInterface(clazz.type)
+ && !appView
+ .options()
+ .desugaredLibraryConfiguration
+ .getEmulateLibraryInterface()
+ .containsKey(clazz.type)
&& !appView.rewritePrefix.hasRewrittenType(clazz.type, appView)
- && isEmulatedInterfaceSubInterface(clazz)) {
+ && isEmulatedInterfaceSubInterface(clazz, appView)) {
String newName =
appView
.options()
.desugaredLibraryConfiguration
.convertJavaNameToDesugaredLibrary(clazz.type);
- rewriter.addCompanionClassRewriteRule(clazz.type, newName);
+ InterfaceMethodRewriter.addCompanionClassRewriteRule(clazz.type, newName, appView);
} else {
filteredProgramClasses.add(clazz);
}
@@ -323,12 +327,21 @@
builder.replaceProgramClasses(filteredProgramClasses);
}
- private boolean isEmulatedInterfaceSubInterface(DexClass subInterface) {
- assert !rewriter.isEmulatedInterface(subInterface.type);
+ private static boolean isEmulatedInterfaceSubInterface(
+ DexClass subInterface, AppView<?> appView) {
+ assert !appView
+ .options()
+ .desugaredLibraryConfiguration
+ .getEmulateLibraryInterface()
+ .containsKey(subInterface.type);
LinkedList<DexType> workList = new LinkedList<>(Arrays.asList(subInterface.interfaces.values));
while (!workList.isEmpty()) {
DexType next = workList.removeFirst();
- if (rewriter.isEmulatedInterface(next)) {
+ if (appView
+ .options()
+ .desugaredLibraryConfiguration
+ .getEmulateLibraryInterface()
+ .containsKey(next)) {
return true;
}
DexClass nextClass = appView.definitionFor(next);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringProcessor.java
index c429fc7..7f3d047 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringProcessor.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.ir.desugar.itf;
-import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
@@ -21,5 +20,5 @@
// All finalization phases of all desugaring processors are performed sequentially.
// Complex computations should be avoided if possible here and be moved to the concurrent phase.
// Classes may be mutated here (new methods can be inserted, etc.).
- void finalizeProcessing(DexApplication.Builder<?> builder, ProgramMethodSet synthesizedMethods);
+ void finalizeProcessing(ProgramMethodSet synthesizedMethods);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
new file mode 100644
index 0000000..731316f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodProcessorFacade.java
@@ -0,0 +1,88 @@
+// 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.ir.desugar.itf;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+class InterfaceMethodProcessorFacade {
+
+ private final AppView<?> appView;
+
+ InterfaceMethodProcessorFacade(AppView<?> appView) {
+ this.appView = appView;
+ }
+
+ /** Runs the interfaceProcessor, the class processor and the emulated interface processor. */
+ void runInterfaceDesugaringProcessors(
+ InterfaceMethodRewriter rewriter,
+ IRConverter converter,
+ Flavor flavour,
+ ExecutorService executorService)
+ throws ExecutionException {
+ // During L8 compilation, emulated interfaces are processed to be renamed, to have
+ // their interfaces fixed-up and to generate the emulated dispatch code.
+ EmulatedInterfaceProcessor emulatedInterfaceProcessor =
+ new EmulatedInterfaceProcessor(appView, rewriter);
+
+ // Process all classes first. Add missing forwarding methods to
+ // replace desugared default interface methods.
+ ClassProcessor classProcessor = new ClassProcessor(appView, rewriter);
+
+ // Process interfaces, create companion or dispatch class if needed, move static
+ // methods to companion class, copy default interface methods to companion classes,
+ // make original default methods abstract, remove bridge methods, create dispatch
+ // classes if needed.
+ InterfaceProcessor interfaceProcessor = new InterfaceProcessor(appView, rewriter);
+
+ // The interface processors must be ordered so that finalization of the processing is performed
+ // in that order. The emulatedInterfaceProcessor has to be last at this point to avoid renaming
+ // emulated interfaces before the other processing.
+ ImmutableList<InterfaceDesugaringProcessor> orderedInterfaceDesugaringProcessors =
+ ImmutableList.of(classProcessor, interfaceProcessor, emulatedInterfaceProcessor);
+
+ SortedProgramMethodSet sortedSynthesizedMethods = SortedProgramMethodSet.createConcurrent();
+ processClassesConcurrently(
+ orderedInterfaceDesugaringProcessors, sortedSynthesizedMethods, flavour, executorService);
+ assert converter != null;
+ converter.processMethodsConcurrently(sortedSynthesizedMethods, executorService);
+ }
+
+ private boolean shouldProcess(DexProgramClass clazz, Flavor flavour) {
+ if (appView.isAlreadyLibraryDesugared(clazz)) {
+ return false;
+ }
+ return (!clazz.originatesFromDexResource() || flavour == Flavor.IncludeAllResources);
+ }
+
+ private void processClassesConcurrently(
+ List<InterfaceDesugaringProcessor> processors,
+ SortedProgramMethodSet sortedSynthesizedMethods,
+ Flavor flavour,
+ ExecutorService executorService)
+ throws ExecutionException {
+ ThreadUtils.processItems(
+ Iterables.filter(
+ appView.appInfo().classes(), (DexProgramClass clazz) -> shouldProcess(clazz, flavour)),
+ clazz -> {
+ for (InterfaceDesugaringProcessor processor : processors) {
+ processor.process(clazz, sortedSynthesizedMethods);
+ }
+ },
+ executorService);
+ for (InterfaceDesugaringProcessor processor : processors) {
+ processor.finalizeProcessing(sortedSynthesizedMethods);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
index 55598d1..413f803 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodRewriter.java
@@ -25,7 +25,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.ClasspathOrLibraryClass;
-import com.android.tools.r8.graph.DexApplication.Builder;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
@@ -33,11 +32,11 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexMethodHandle;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.InvalidCode;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
@@ -57,10 +56,10 @@
import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryRetargeter;
import com.android.tools.r8.ir.desugar.FreshLocalProvider;
import com.android.tools.r8.ir.desugar.LocalStackAllocator;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeter;
import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring;
import com.android.tools.r8.ir.desugar.stringconcat.StringConcatInstructionDesugaring;
import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations;
@@ -75,17 +74,13 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.IteratorUtils;
import com.android.tools.r8.utils.Pair;
-import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.android.tools.r8.utils.collections.SortedProgramMethodSet;
import com.android.tools.r8.utils.structural.Ordered;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
@@ -132,7 +127,6 @@
public static final String PRIVATE_METHOD_PREFIX = "$private$";
private final AppView<?> appView;
- private final IRConverter converter;
private final InternalOptions options;
final DexItemFactory factory;
private final Map<DexType, DexType> emulatedInterfaces;
@@ -165,7 +159,6 @@
BackportedMethodRewriter rewriter,
DesugaredLibraryRetargeter desugaredLibraryRetargeter) {
this.appView = appView;
- this.converter = null;
this.backportedMethodRewriter = rewriter;
this.desugaredLibraryRetargeter = desugaredLibraryRetargeter;
this.options = appView.options();
@@ -179,7 +172,6 @@
public InterfaceMethodRewriter(AppView<?> appView, IRConverter converter) {
assert converter != null;
this.appView = appView;
- this.converter = converter;
this.backportedMethodRewriter = null;
this.desugaredLibraryRetargeter = null;
this.options = appView.options();
@@ -249,10 +241,17 @@
}
void addCompanionClassRewriteRule(DexType interfaceType, String rewrittenType) {
+ addCompanionClassRewriteRule(interfaceType, rewrittenType, appView);
+ }
+
+ static void addCompanionClassRewriteRule(
+ DexType interfaceType, String rewrittenType, AppView<?> appView) {
appView.rewritePrefix.rewriteType(
- getCompanionClassType(interfaceType),
- factory.createType(
- DescriptorUtils.javaTypeToDescriptor(rewrittenType + COMPANION_CLASS_NAME_SUFFIX)));
+ getCompanionClassType(interfaceType, appView.dexItemFactory()),
+ appView
+ .dexItemFactory()
+ .createType(
+ DescriptorUtils.javaTypeToDescriptor(rewrittenType + COMPANION_CLASS_NAME_SUFFIX)));
}
boolean isEmulatedInterface(DexType itf) {
@@ -744,7 +743,7 @@
// TODO(b/183998768): Why does this not create a stub on the class path?
: privateAsMethodOfCompanionClass(directTarget);
} else {
- companionMethod = defaultAsMethodOfCompanionClass(directTarget);
+ companionMethod = ensureDefaultAsMethodOfCompanionClassStub(directTarget).getReference();
}
return rewriteInvoke.apply(companionMethod);
} else {
@@ -754,7 +753,8 @@
if (virtualTarget != null) {
// This is a invoke-direct call to a virtual method.
assert invokeNeedsRewriting(invokedMethod, DIRECT);
- return rewriteInvoke.apply(defaultAsMethodOfCompanionClass(virtualTarget));
+ return rewriteInvoke.apply(
+ ensureDefaultAsMethodOfCompanionClassStub(virtualTarget).getReference());
} else {
// The below assert is here because a well-type program should have a target, but we
// cannot throw a compilation error, since we have no knowledge about the input.
@@ -925,7 +925,8 @@
DexClass holder = target.getHolder();
if (holder.isLibraryClass() && holder.isInterface()) {
assert invokeNeedsRewriting(invokedMethod, SUPER);
- return rewriteInvoke.apply(defaultAsMethodOfCompanionClass(target));
+ return rewriteInvoke.apply(
+ ensureDefaultAsMethodOfCompanionClassStub(target).getReference());
}
}
}
@@ -938,25 +939,23 @@
DexClassAndMethod superTarget =
appView.appInfoForDesugaring().lookupSuperTarget(invokedMethod, context);
if (superTarget != null && superTarget.isLibraryMethod()) {
+ assert invokeNeedsRewriting(invokedMethod, SUPER);
// Rewriting is required because the super invoke resolves into a missing
// method (method is on desugared library). Find out if it needs to be
- // retarget or if it just calls a companion class method and rewrite.
+ // retargeted or if it just calls a companion class method and rewrite.
DexMethod retargetMethod =
options.desugaredLibraryConfiguration.retargetMethod(superTarget, appView);
- if (retargetMethod == null) {
- DexMethod originalCompanionMethod = defaultAsMethodOfCompanionClass(superTarget);
- DexMethod companionMethod =
- factory.createMethod(
- getCompanionClassType(emulatedItf),
- factory.protoWithDifferentFirstParameter(
- originalCompanionMethod.proto, emulatedItf),
- originalCompanionMethod.name);
- assert invokeNeedsRewriting(invokedMethod, SUPER);
- return rewriteInvoke.apply(companionMethod);
- } else {
- assert invokeNeedsRewriting(invokedMethod, SUPER);
+ if (retargetMethod != null) {
return rewriteInvoke.apply(retargetMethod);
}
+ DexClassAndMethod emulatedMethod =
+ superTarget.getReference().lookupMemberOnClass(appView.definitionFor(emulatedItf));
+ if (emulatedMethod == null) {
+ assert false;
+ return null;
+ }
+ DexClassAndMethod companionMethod = ensureDefaultAsMethodOfCompanionClassStub(emulatedMethod);
+ return rewriteInvoke.apply(companionMethod.getReference());
}
return null;
}
@@ -1194,6 +1193,16 @@
return factory.createType(interfaceTypeDescriptor);
}
+ DexClassAndMethod ensureDefaultAsMethodOfCompanionClassStub(DexClassAndMethod method) {
+ if (method.isProgramMethod()) {
+ return ensureDefaultAsMethodOfProgramCompanionClassStub(method.asProgramMethod());
+ }
+ ClasspathOrLibraryClass context = method.getHolder().asClasspathOrLibraryClass();
+ DexMethod companionMethodReference =
+ defaultAsMethodOfCompanionClass(method.getReference(), appView.dexItemFactory());
+ return ensureMethodOfClasspathCompanionClassStub(companionMethodReference, context, appView);
+ }
+
DexClassAndMethod ensureStaticAsMethodOfCompanionClassStub(DexClassAndMethod method) {
if (method.isProgramMethod()) {
return ensureStaticAsMethodOfProgramCompanionClassStub(method.asProgramMethod());
@@ -1204,6 +1213,37 @@
}
}
+ ProgramMethod ensureDefaultAsMethodOfProgramCompanionClassStub(ProgramMethod method) {
+ DexEncodedMethod virtual = method.getDefinition();
+ DexMethod companionMethod =
+ defaultAsMethodOfCompanionClass(method.getReference(), appView.dexItemFactory());
+ return InterfaceProcessor.ensureCompanionMethod(
+ method.getHolder(),
+ companionMethod.getName(),
+ companionMethod.getProto(),
+ appView,
+ methodBuilder -> {
+ MethodAccessFlags newFlags = method.getAccessFlags().copy();
+ newFlags.promoteToStatic();
+ methodBuilder
+ .setAccessFlags(newFlags)
+ .setGenericSignature(MethodTypeSignature.noSignature())
+ .setAnnotations(
+ virtual
+ .annotations()
+ .methodParametersWithFakeThisArguments(appView.dexItemFactory()))
+ .setParameterAnnotationsList(
+ virtual.getParameterAnnotations().withFakeThisParameter())
+ // TODO(b/183998768): Once R8 desugars in the enqueuer this should set an invalid
+ // code to ensure it is never used before desugared and installed.
+ .setCode(
+ ignored ->
+ appView.enableWholeProgramOptimizations()
+ ? virtual.getCode()
+ : InvalidCode.getInstance());
+ });
+ }
+
ProgramMethod ensurePrivateAsMethodOfProgramCompanionClassStub(ProgramMethod method) {
DexMethod companionMethod =
privateAsMethodOfCompanionClass(method.getReference(), appView.dexItemFactory());
@@ -1222,6 +1262,7 @@
.setAccessFlags(newFlags)
.setGenericSignature(definition.getGenericSignature())
.setAnnotations(definition.annotations())
+ // TODO(b/183998768): Should this not also be updating with a fake 'this'
.setParameterAnnotationsList(definition.getParameterAnnotations())
// TODO(b/183998768): Once R8 desugars in the enqueuer this should set an invalid
// code to ensure it is never used before desugared and installed.
@@ -1272,13 +1313,6 @@
return instanceAsMethodOfCompanionClass(method, DEFAULT_METHOD_PREFIX, factory);
}
- public final DexMethod defaultAsMethodOfCompanionClass(DexClassAndMethod method) {
- DexItemFactory dexItemFactory = appView.dexItemFactory();
- DexMethod rewritten = defaultAsMethodOfCompanionClass(method.getReference(), dexItemFactory);
- recordCompanionClassReference(appView, method, rewritten);
- return rewritten;
- }
-
// Represent a private instance interface method as a method of companion class.
static DexMethod privateAsMethodOfCompanionClass(DexMethod method, DexItemFactory factory) {
// Add an implicit argument to represent the receiver.
@@ -1289,17 +1323,6 @@
return privateAsMethodOfCompanionClass(method.getReference(), factory);
}
- private static DexClassAndMethod recordCompanionClassReference(
- AppView<?> appView, DexClassAndMethod method, DexMethod rewritten) {
- ClasspathOrLibraryClass context = method.getHolder().asClasspathOrLibraryClass();
- // If the interface class is a program class, we shouldn't need to synthesize the companion
- // class on the classpath.
- if (context == null) {
- return null;
- }
- return ensureMethodOfClasspathCompanionClassStub(rewritten, context, appView);
- }
-
private static DexClassAndMethod ensureMethodOfClasspathCompanionClassStub(
DexMethod companionMethodReference, ClasspathOrLibraryClass context, AppView<?> appView) {
return appView
@@ -1343,74 +1366,22 @@
});
}
- /**
- * Move static and default interface methods to companion classes, add missing methods to forward
- * to moved default methods implementation.
- */
- public void desugarInterfaceMethods(
- Builder<?> builder, Flavor flavour, ExecutorService executorService)
- throws ExecutionException {
- // During L8 compilation, emulated interfaces are processed to be renamed, to have
- // their interfaces fixed-up and to generate the emulated dispatch code.
- EmulatedInterfaceProcessor emulatedInterfaceProcessor =
- new EmulatedInterfaceProcessor(appView, this);
-
- // Process all classes first. Add missing forwarding methods to
- // replace desugared default interface methods.
- ClassProcessor classProcessor = new ClassProcessor(appView, this);
-
- // Process interfaces, create companion or dispatch class if needed, move static
- // methods to companion class, copy default interface methods to companion classes,
- // make original default methods abstract, remove bridge methods, create dispatch
- // classes if needed.
- InterfaceProcessor interfaceProcessor = new InterfaceProcessor(appView, this);
-
- // The interface processors must be ordered so that finalization of the processing is performed
- // in that order. The emulatedInterfaceProcessor has to be last at this point to avoid renaming
- // emulated interfaces before the other processing.
- ImmutableList<InterfaceDesugaringProcessor> orderedInterfaceDesugaringProcessors =
- ImmutableList.of(classProcessor, interfaceProcessor, emulatedInterfaceProcessor);
- processClassesConcurrently(
- orderedInterfaceDesugaringProcessors, builder, flavour, executorService);
-
+ public void finalizeInterfaceMethodRewritingThroughIR(
+ IRConverter converter, ExecutorService executorService) throws ExecutionException {
SortedProgramMethodSet sortedSynthesizedMethods = SortedProgramMethodSet.create();
sortedSynthesizedMethods.addAll(synthesizedMethods);
converter.processMethodsConcurrently(sortedSynthesizedMethods, executorService);
// Cached data is not needed any more.
- clear();
- }
-
- private void clear() {
this.cache.clear();
this.synthesizedMethods.clear();
}
- private boolean shouldProcess(DexProgramClass clazz, Flavor flavour) {
- if (appView.isAlreadyLibraryDesugared(clazz)) {
- return false;
- }
- return (!clazz.originatesFromDexResource() || flavour == Flavor.IncludeAllResources);
- }
-
- private void processClassesConcurrently(
- List<InterfaceDesugaringProcessor> processors,
- Builder<?> builder,
- Flavor flavour,
- ExecutorService executorService)
+ public void runInterfaceDesugaringProcessors(
+ IRConverter converter, Flavor flavour, ExecutorService executorService)
throws ExecutionException {
- ThreadUtils.processItems(
- Iterables.filter(
- builder.getProgramClasses(), (DexProgramClass clazz) -> shouldProcess(clazz, flavour)),
- clazz -> {
- for (InterfaceDesugaringProcessor processor : processors) {
- processor.process(clazz, synthesizedMethods);
- }
- },
- executorService);
- for (InterfaceDesugaringProcessor processor : processors) {
- processor.finalizeProcessing(builder, synthesizedMethods);
- }
+ new InterfaceMethodProcessorFacade(appView)
+ .runInterfaceDesugaringProcessors(this, converter, flavour, executorService);
}
final boolean isDefaultMethod(DexEncodedMethod method) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
index 799b584..cb752ad 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessor.java
@@ -19,7 +19,6 @@
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexAnnotationSet;
-import com.android.tools.r8.graph.DexApplication.Builder;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
@@ -34,7 +33,6 @@
import com.android.tools.r8.graph.FieldAccessFlags;
import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature;
import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
-import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InvalidCode;
import com.android.tools.r8.graph.MethodAccessFlags;
@@ -183,11 +181,12 @@
private DexEncodedField createStaticClinitFieldToTriggerInterfaceInitialization(
DexProgramClass iface) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
- DexField clinitFieldTemplateReference =
- dexItemFactory.createField(iface.getType(), dexItemFactory.intType, "$desugar$clinit");
DexField clinitFieldReference =
- dexItemFactory.createFreshFieldName(
- clinitFieldTemplateReference, candidate -> iface.lookupField(candidate) == null);
+ dexItemFactory.createFreshFieldNameWithoutHolder(
+ iface.getType(),
+ dexItemFactory.intType,
+ "$desugar$clinit",
+ candidate -> iface.lookupField(candidate) == null);
return new DexEncodedField(
clinitFieldReference,
FieldAccessFlags.builder().setPackagePrivate().setStatic().setSynthetic().build(),
@@ -231,44 +230,19 @@
+ method.toSourceString(),
iface.origin);
}
-
- // Create a new method in a companion class to represent default method implementation.
- DexMethod companionMethod = rewriter.defaultAsMethodOfCompanionClass(method);
-
Code code = virtual.getCode();
if (code == null) {
throw new CompilationError(
"Code is missing for default " + "interface method: " + method.toSourceString(),
iface.origin);
}
-
- MethodAccessFlags newFlags = method.getAccessFlags().copy();
- newFlags.promoteToStatic();
+ // Create a new method in a companion class to represent default method implementation.
+ ProgramMethod companion = rewriter.ensureDefaultAsMethodOfProgramCompanionClassStub(method);
DexEncodedMethod.setDebugInfoWithFakeThisParameter(
- code, companionMethod.getArity(), appView);
-
- ensureCompanionMethod(
- iface,
- companionMethod.getName(),
- companionMethod.getProto(),
- appView,
- methodBuilder ->
- methodBuilder
- .setAccessFlags(newFlags)
- .setGenericSignature(MethodTypeSignature.noSignature())
- .setAnnotations(
- virtual
- .annotations()
- .methodParametersWithFakeThisArguments(appView.dexItemFactory()))
- .setParameterAnnotationsList(
- virtual.getParameterAnnotations().withFakeThisParameter())
- .setCode(ignored -> virtual.getCode())
- .setOnBuildConsumer(
- implMethod -> {
- implMethod.copyMetadata(virtual);
- getPostProcessingInterfaceInfo(iface)
- .mapDefaultMethodToCompanionMethod(virtual, implMethod);
- }));
+ code, companion.getReference().getArity(), appView);
+ finalizeMoveToCompanionMethod(method, companion);
+ getPostProcessingInterfaceInfo(iface)
+ .mapDefaultMethodToCompanionMethod(virtual, companion.getDefinition());
}
}
}
@@ -299,7 +273,6 @@
companion = rewriter.ensureStaticAsMethodOfProgramCompanionClassStub(method);
} else {
assert definition.isPrivate();
- companion = rewriter.ensurePrivateAsMethodOfProgramCompanionClassStub(method);
Code code = definition.getCode();
if (code == null) {
throw new CompilationError(
@@ -308,24 +281,31 @@
+ method.getReference().toSourceString(),
iface.origin);
}
+ companion = rewriter.ensurePrivateAsMethodOfProgramCompanionClassStub(method);
DexEncodedMethod.setDebugInfoWithFakeThisParameter(
code, companion.getReference().getArity(), appView);
}
- // TODO(b/183998768): R8 should also install an "invalid code" object until the actual code
- // moves.
- assert appView.enableWholeProgramOptimizations()
- || InvalidCode.isInvalidCode(companion.getDefinition().getCode());
- if (definition.hasClassFileVersion()) {
- companion.getDefinition().downgradeClassFileVersion(definition.getClassFileVersion());
- }
- companion.getDefinition().setCode(definition.getCode(), appView);
+ finalizeMoveToCompanionMethod(method, companion);
getPostProcessingInterfaceInfo(iface)
.moveMethod(method.getReference(), companion.getReference());
- definition.setCode(InvalidCode.getInstance(), appView);
}
}
+ private void finalizeMoveToCompanionMethod(ProgramMethod method, ProgramMethod companion) {
+ // TODO(b/183998768): R8 should also install an "invalid code" object until the actual code
+ // moves.
+ assert appView.enableWholeProgramOptimizations()
+ || InvalidCode.isInvalidCode(companion.getDefinition().getCode());
+ DexProgramClass iface = method.getHolder();
+ DexEncodedMethod definition = method.getDefinition();
+ if (definition.hasClassFileVersion()) {
+ companion.getDefinition().downgradeClassFileVersion(definition.getClassFileVersion());
+ }
+ companion.getDefinition().setCode(definition.getCode(), appView);
+ definition.setCode(InvalidCode.getInstance(), appView);
+ }
+
private void clearDirectMethods(DexProgramClass iface) {
DexEncodedMethod clinit = iface.getClassInitializer();
MethodCollection methodCollection = iface.getMethodCollection();
@@ -461,7 +441,7 @@
}
@Override
- public void finalizeProcessing(Builder<?> builder, ProgramMethodSet synthesizedMethods) {
+ public void finalizeProcessing(ProgramMethodSet synthesizedMethods) {
InterfaceProcessorNestedGraphLens graphLens = postProcessInterfaces();
if (appView.enableWholeProgramOptimizations() && graphLens != null) {
appView.setGraphLens(graphLens);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/RecordCfMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordCfMethods.java
similarity index 99%
rename from src/main/java/com/android/tools/r8/ir/desugar/RecordCfMethods.java
rename to src/main/java/com/android/tools/r8/ir/desugar/records/RecordCfMethods.java
index 47bc98b..0de85b5 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/RecordCfMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordCfMethods.java
@@ -6,7 +6,7 @@
// GENERATED FILE. DO NOT EDIT! See GenerateRecordMethods.java.
// ***********************************************************************************
-package com.android.tools.r8.ir.desugar;
+package com.android.tools.r8.ir.desugar.records;
import com.android.tools.r8.cf.code.CfArithmeticBinop;
import com.android.tools.r8.cf.code.CfArrayLength;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/RecordDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaringEventConsumer.java
similarity index 90%
rename from src/main/java/com/android/tools/r8/ir/desugar/RecordDesugaringEventConsumer.java
rename to src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaringEventConsumer.java
index 61eb249..5affc2b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/RecordDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaringEventConsumer.java
@@ -2,7 +2,7 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.ir.desugar;
+package com.android.tools.r8.ir.desugar.records;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramMethod;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/RecordRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
similarity index 97%
rename from src/main/java/com/android/tools/r8/ir/desugar/RecordRewriter.java
rename to src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
index 2e71826..5333665 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/RecordRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
@@ -2,7 +2,7 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.ir.desugar;
+package com.android.tools.r8.ir.desugar.records;
import com.android.tools.r8.cf.code.CfConstString;
import com.android.tools.r8.cf.code.CfFieldInstruction;
@@ -35,6 +35,12 @@
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.CfClassDesugaring;
+import com.android.tools.r8.ir.desugar.CfClassDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.FreshLocalProvider;
+import com.android.tools.r8.ir.desugar.LocalStackAllocator;
import com.android.tools.r8.ir.synthetic.CallObjectInitCfCodeProvider;
import com.android.tools.r8.ir.synthetic.RecordGetFieldsAsObjectsCfCodeProvider;
import com.android.tools.r8.naming.dexitembasedstring.ClassNameComputationInfo;
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 9e82361..e4d045d 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
@@ -732,7 +732,7 @@
if (inliningIRProvider.shouldApplyCodeRewritings(target)) {
assert lensCodeRewriter != null;
- lensCodeRewriter.rewrite(code, target);
+ lensCodeRewriter.rewrite(code, target, inliningIRProvider.getMethodProcessor());
}
if (options.testing.inlineeIrModifier != null) {
options.testing.inlineeIrModifier.accept(code);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
index e00c267..17db09c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
@@ -292,6 +292,10 @@
return ConstraintWithTarget.classIsVisible(context, type, appView);
}
+ public ConstraintWithTarget forNewUnboxedEnumInstance(DexType type, ProgramMethod context) {
+ return ConstraintWithTarget.ALWAYS;
+ }
+
public ConstraintWithTarget forAssume() {
return ConstraintWithTarget.ALWAYS;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumDataMap.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumDataMap.java
index b47ec05..264fc7b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumDataMap.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumDataMap.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldKnownData;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -23,6 +24,10 @@
this.map = map;
}
+ public boolean isUnboxedEnum(DexProgramClass clazz) {
+ return isUnboxedEnum(clazz.getType());
+ }
+
public boolean isUnboxedEnum(DexType type) {
return map.containsKey(type);
}
@@ -110,8 +115,16 @@
return unboxedValues.get(field);
}
+ public boolean hasUnboxedValueFor(ProgramField field) {
+ return hasUnboxedValueFor(field.getReference());
+ }
+
public boolean hasUnboxedValueFor(DexField field) {
- return unboxedValues.get(field) != null;
+ return unboxedValues.containsKey(field);
+ }
+
+ public boolean matchesValuesField(ProgramField field) {
+ return matchesValuesField(field.getReference());
}
public boolean matchesValuesField(DexField field) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
index e9b1e25..07ce458 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -38,6 +38,7 @@
import com.android.tools.r8.graph.FieldResolutionResult;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.ResolutionResult;
import com.android.tools.r8.graph.UseRegistry;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues;
@@ -62,6 +63,7 @@
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.Opcodes;
import com.android.tools.r8.ir.code.Phi;
@@ -69,6 +71,7 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.conversion.PostMethodProcessor;
import com.android.tools.r8.ir.optimize.Inliner.Constraint;
import com.android.tools.r8.ir.optimize.enums.EnumDataMap.EnumData;
@@ -251,7 +254,8 @@
case Opcodes.INSTANCE_GET:
case Opcodes.STATIC_PUT:
case INSTANCE_PUT:
- analyzeFieldInstruction(instruction.asFieldInstruction(), code);
+ analyzeFieldInstruction(
+ instruction.asFieldInstruction(), eligibleEnums, code.context());
break;
default: // Nothing to do for other instructions.
}
@@ -279,13 +283,15 @@
}
}
- private void analyzeFieldInstruction(FieldInstruction fieldInstruction, IRCode code) {
+ private void analyzeFieldInstruction(
+ FieldInstruction fieldInstruction, Set<DexType> eligibleEnums, ProgramMethod context) {
DexField field = fieldInstruction.getField();
DexProgramClass enumClass = getEnumUnboxingCandidateOrNull(field.holder);
if (enumClass != null) {
- FieldResolutionResult resolutionResult =
- appView.appInfo().resolveField(field, code.context());
- if (resolutionResult.isFailedOrUnknownResolution()) {
+ FieldResolutionResult resolutionResult = appView.appInfo().resolveField(field, context);
+ if (resolutionResult.isSuccessfulResolution()) {
+ eligibleEnums.add(enumClass.getType());
+ } else {
markEnumAsUnboxable(Reason.UNRESOLVABLE_FIELD, enumClass);
}
}
@@ -366,36 +372,58 @@
return;
}
for (Instruction user : constClass.outValue().aliasedUsers()) {
- if (user.isAssume()) {
- continue;
+ if (!isLegitimateConstClassUser(user, context, enumClass)) {
+ markEnumAsUnboxable(Reason.CONST_CLASS, enumClass);
+ return;
}
- if (user.isInvokeVirtual()
- && isUnboxableNameMethod(user.asInvokeVirtual().getInvokedMethod())) {
- continue;
- }
- if (user.isInvokeStatic()) {
- DexClassAndMethod singleTarget = user.asInvokeStatic().lookupSingleTarget(appView, context);
- if (singleTarget != null) {
- if (singleTarget.getReference() == factory.enumMembers.valueOf) {
- // The name data is required for the correct mapping from the enum name to the ordinal
- // in the valueOf utility method.
- addRequiredNameData(enumClass);
- markMethodDependsOnLibraryModelisation(context);
- continue;
- }
- if (singleTarget.getReference()
- == factory.javaLangReflectArrayMembers.newInstanceMethodWithDimensions) {
- markMethodDependsOnLibraryModelisation(context);
- continue;
- }
- }
- }
- markEnumAsUnboxable(Reason.CONST_CLASS, enumClass);
- return;
}
eligibleEnums.add(enumType);
}
+ private boolean isLegitimateConstClassUser(
+ Instruction user, ProgramMethod context, DexProgramClass enumClass) {
+ if (user.isAssume()) {
+ if (user.outValue().hasPhiUsers()) {
+ return false;
+ }
+ return true;
+ }
+
+ if (user.isInvokeStatic()) {
+ DexClassAndMethod singleTarget = user.asInvokeStatic().lookupSingleTarget(appView, context);
+ if (singleTarget == null) {
+ return false;
+ }
+ if (singleTarget.getReference() == factory.enumMembers.valueOf) {
+ // The name data is required for the correct mapping from the enum name to the ordinal
+ // in the valueOf utility method.
+ addRequiredNameData(enumClass);
+ markMethodDependsOnLibraryModelisation(context);
+ return true;
+ }
+ if (singleTarget.getReference()
+ == factory.javaLangReflectArrayMembers.newInstanceMethodWithDimensions) {
+ markMethodDependsOnLibraryModelisation(context);
+ return true;
+ }
+ }
+
+ if (user.isInvokeVirtual()) {
+ InvokeVirtual invoke = user.asInvokeVirtual();
+ DexMethod invokedMethod = invoke.getInvokedMethod();
+ if (invokedMethod == factory.classMethods.desiredAssertionStatus) {
+ // Only valid in the enum's class initializer, since the class constant must be rewritten
+ // to LocalEnumUtility.class instead of int.class.
+ return context.getDefinition().isClassInitializer() && context.getHolder() == enumClass;
+ }
+ if (isUnboxableNameMethod(invokedMethod)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
private void addRequiredNameData(DexProgramClass enumClass) {
enumUnboxingCandidatesInfo.addRequiredEnumInstanceFieldData(
enumClass, factory.enumMembers.nameField);
@@ -465,6 +493,7 @@
}
public void unboxEnums(
+ IRConverter converter,
PostMethodProcessor.Builder postBuilder,
ExecutorService executorService,
OptimizationFeedbackDelayed feedback)
@@ -472,12 +501,16 @@
assert candidatesToRemoveInWave.isEmpty();
EnumDataMap enumDataMap = finishAnalysis();
assert candidatesToRemoveInWave.isEmpty();
+
// At this point the enum unboxing candidates are no longer candidates, they will all be
// unboxed. We extract the now immutable enums to unbox information and clear the candidate
// info.
if (enumUnboxingCandidatesInfo.isEmpty()) {
+ assert enumDataMap.isEmpty();
+ appView.setUnboxedEnums(enumDataMap);
return;
}
+
ImmutableSet<DexType> enumsToUnbox = enumUnboxingCandidatesInfo.candidates();
ImmutableSet<DexProgramClass> enumClassesToUnbox =
enumUnboxingCandidatesInfo.candidateClasses();
@@ -501,25 +534,29 @@
utilityClass -> utilityClass.getDefinition().forEachProgramMethod(postBuilder::add));
fieldAccessInfoCollectionModifierBuilder.build().modify(appView);
- enumUnboxerRewriter = new EnumUnboxingRewriter(appView, enumDataMap, utilityClasses);
- EnumUnboxingLens enumUnboxingLens =
- new EnumUnboxingTreeFixer(appView, enumsToUnbox, utilityClasses, enumUnboxerRewriter)
- .fixupTypeReferences();
- enumUnboxerRewriter.setEnumUnboxingLens(enumUnboxingLens);
+ EnumUnboxingTreeFixer.Result treeFixerResult =
+ new EnumUnboxingTreeFixer(appView, enumDataMap, enumClassesToUnbox, utilityClasses)
+ .fixupTypeReferences(converter, executorService);
+ EnumUnboxingLens enumUnboxingLens = treeFixerResult.getLens();
+ enumUnboxerRewriter =
+ new EnumUnboxingRewriter(appView, converter, enumUnboxingLens, enumDataMap, utilityClasses);
appView.setUnboxedEnums(enumDataMap);
GraphLens previousLens = appView.graphLens();
appView.rewriteWithLensAndApplication(enumUnboxingLens, appBuilder.build());
- updateOptimizationInfos(executorService, feedback);
+ updateOptimizationInfos(executorService, feedback, treeFixerResult.getPrunedItems());
postBuilder.put(dependencies);
// Methods depending on library modelisation need to be reprocessed so they are peephole
// optimized.
postBuilder.put(methodsDependingOnLibraryModelisation);
methodsDependingOnLibraryModelisation.clear();
+ postBuilder.removePrunedMethods(treeFixerResult.getPrunedItems().getRemovedMethods());
postBuilder.rewrittenWithLens(appView, previousLens);
}
private void updateOptimizationInfos(
- ExecutorService executorService, OptimizationFeedbackDelayed feedback)
+ ExecutorService executorService,
+ OptimizationFeedbackDelayed feedback,
+ PrunedItems prunedItems)
throws ExecutionException {
feedback.fixupOptimizationInfos(
appView,
@@ -538,7 +575,7 @@
optimizationInfo
.fixupClassTypeReferences(appView, appView.graphLens())
.fixupAbstractReturnValue(appView, appView.graphLens())
- .fixupInstanceInitializerInfo(appView, appView.graphLens());
+ .fixupInstanceInitializerInfo(appView, appView.graphLens(), prunedItems);
}
});
}
@@ -1223,53 +1260,93 @@
Value enumValue,
DexMethod singleTargetReference,
DexClass targetHolder) {
- if (targetHolder.getType() != factory.enumType) {
- // System.identityHashCode(Object) is supported for proto enums.
- // Object#getClass without outValue and Objects.requireNonNull are supported since R8
- // rewrites explicit null checks to such instructions.
- if (singleTargetReference == factory.javaLangSystemMethods.identityHashCode) {
+ // Calls to java.lang.Enum.
+ if (targetHolder.getType() == factory.enumType) {
+ // TODO(b/147860220): EnumSet and EnumMap may be interesting to model.
+ if (singleTargetReference == factory.enumMembers.compareTo
+ || singleTargetReference == factory.enumMembers.compareToWithObject) {
+ DexProgramClass otherEnumClass =
+ getEnumUnboxingCandidateOrNull(invoke.getLastArgument().getType());
+ if (otherEnumClass == enumClass || invoke.getLastArgument().getType().isNullType()) {
+ return Reason.ELIGIBLE;
+ }
+ } else if (singleTargetReference == factory.enumMembers.equals) {
return Reason.ELIGIBLE;
- }
- if (singleTargetReference == factory.stringMembers.valueOf) {
+ } else if (singleTargetReference == factory.enumMembers.nameMethod
+ || singleTargetReference == factory.enumMembers.toString) {
+ assert invoke.asInvokeMethodWithReceiver().getReceiver() == enumValue;
addRequiredNameData(enumClass);
return Reason.ELIGIBLE;
- }
- if (singleTargetReference == factory.stringBuilderMethods.appendObject
- || singleTargetReference == factory.stringBufferMethods.appendObject) {
- addRequiredNameData(enumClass);
+ } else if (singleTargetReference == factory.enumMembers.ordinalMethod) {
return Reason.ELIGIBLE;
+ } else if (singleTargetReference == factory.enumMembers.hashCode) {
+ return Reason.ELIGIBLE;
+ } else if (singleTargetReference == factory.enumMembers.constructor) {
+ // Enum constructor call is allowed only if called from an enum initializer.
+ if (code.method().isInstanceInitializer() && code.context().getHolder() == enumClass) {
+ return Reason.ELIGIBLE;
+ }
}
- if (singleTargetReference == factory.objectMembers.getClass
- && (!invoke.hasOutValue() || !invoke.outValue().hasAnyUsers())) {
+ return new UnsupportedLibraryInvokeReason(singleTargetReference);
+ }
+
+ // Calls to java.lang.Object.
+ if (targetHolder.getType() == factory.objectType) {
+ // Object#getClass without outValue is important since R8 rewrites explicit null checks to
+ // such instructions.
+ if (singleTargetReference == factory.objectMembers.getClass && invoke.hasUnusedOutValue()) {
// This is a hidden null check.
return Reason.ELIGIBLE;
}
+ return new UnsupportedLibraryInvokeReason(singleTargetReference);
+ }
+
+ // Calls to java.lang.Objects.
+ if (targetHolder.getType() == factory.objectsType) {
+ // Objects#requireNonNull is important since R8 rewrites explicit null checks to such
+ // instructions.
if (singleTargetReference == factory.objectsMethods.requireNonNull
|| singleTargetReference == factory.objectsMethods.requireNonNullWithMessage) {
return Reason.ELIGIBLE;
}
return new UnsupportedLibraryInvokeReason(singleTargetReference);
}
- // TODO(b/147860220): EnumSet and EnumMap may be interesting to model.
- if (singleTargetReference == factory.enumMembers.compareTo) {
- return Reason.ELIGIBLE;
- } else if (singleTargetReference == factory.enumMembers.equals) {
- return Reason.ELIGIBLE;
- } else if (singleTargetReference == factory.enumMembers.nameMethod
- || singleTargetReference == factory.enumMembers.toString) {
- assert invoke.asInvokeMethodWithReceiver().getReceiver() == enumValue;
- addRequiredNameData(enumClass);
- return Reason.ELIGIBLE;
- } else if (singleTargetReference == factory.enumMembers.ordinalMethod) {
- return Reason.ELIGIBLE;
- } else if (singleTargetReference == factory.enumMembers.hashCode) {
- return Reason.ELIGIBLE;
- } else if (singleTargetReference == factory.enumMembers.constructor) {
- // Enum constructor call is allowed only if called from an enum initializer.
- if (code.method().isInstanceInitializer() && code.context().getHolder() == enumClass) {
+
+ // Calls to java.lang.String.
+ if (targetHolder.getType() == factory.stringType) {
+ if (singleTargetReference == factory.stringMembers.valueOf) {
+ addRequiredNameData(enumClass);
return Reason.ELIGIBLE;
}
+ return new UnsupportedLibraryInvokeReason(singleTargetReference);
}
+
+ // Calls to java.lang.StringBuilder and java.lang.StringBuffer.
+ if (targetHolder.getType() == factory.stringBuilderType
+ || targetHolder.getType() == factory.stringBufferType) {
+ if (singleTargetReference == factory.stringBuilderMethods.appendObject
+ || singleTargetReference == factory.stringBufferMethods.appendObject) {
+ addRequiredNameData(enumClass);
+ return Reason.ELIGIBLE;
+ }
+ return new UnsupportedLibraryInvokeReason(singleTargetReference);
+ }
+
+ // Calls to java.lang.System.
+ if (targetHolder.getType() == factory.javaLangSystemType) {
+ if (singleTargetReference == factory.javaLangSystemMethods.arraycopy) {
+ // Important for Kotlin 1.5 enums, which use arraycopy to create a copy of $VALUES instead
+ // of int[].clone().
+ return Reason.ELIGIBLE;
+ }
+ if (singleTargetReference == factory.javaLangSystemMethods.identityHashCode) {
+ // Important for proto enum unboxing.
+ return Reason.ELIGIBLE;
+ }
+ return new UnsupportedLibraryInvokeReason(singleTargetReference);
+ }
+
+ // Unsupported holder.
return new UnsupportedLibraryInvokeReason(singleTargetReference);
}
@@ -1387,11 +1464,11 @@
return false;
}
- public Set<Phi> rewriteCode(IRCode code) {
+ public Set<Phi> rewriteCode(IRCode code, MethodProcessor methodProcessor) {
// This has no effect during primary processing since the enumUnboxerRewriter is set
// in between primary and post processing.
if (enumUnboxerRewriter != null) {
- return enumUnboxerRewriter.rewriteCode(code);
+ return enumUnboxerRewriter.rewriteCode(code, methodProcessor);
}
return Sets.newIdentityHashSet();
}
@@ -1402,4 +1479,8 @@
enumUnboxerRewriter.synthesizeEnumUnboxingUtilityMethods(converter, executorService);
}
}
+
+ public void unsetRewriter() {
+ enumUnboxerRewriter = null;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
index 62b05a9..8530a9d3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.ir.optimize.enums;
import com.android.tools.r8.graph.AppView;
-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.DexMember;
@@ -13,7 +12,6 @@
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.optimize.enums.eligibility.Reason;
-import com.android.tools.r8.ir.optimize.enums.eligibility.Reason.UnsupportedStaticFieldReason;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepInfoCollection;
@@ -70,48 +68,9 @@
}
result = false;
}
- if (!enumHasBasicStaticFields(clazz)) {
- result = false;
- }
return result;
}
- // The enum should have the $VALUES static field and only fields directly referencing the enum
- // instances.
- private boolean enumHasBasicStaticFields(DexProgramClass clazz) {
- boolean result = true;
- for (DexEncodedField staticField : clazz.staticFields()) {
- if (isEnumField(staticField, clazz)) {
- // Enum field, valid, do nothing.
- } else if (matchesValuesField(staticField, clazz, factory)) {
- // Field $VALUES, valid, do nothing.
- } else if (appView.appInfo().isFieldRead(staticField)) {
- // Only non read static fields are valid, and they are assumed unused.
- if (!enumUnboxer.reportFailure(
- clazz, new UnsupportedStaticFieldReason(staticField.getReference()))) {
- return false;
- }
- result = false;
- }
- }
- return result;
- }
-
- static boolean isEnumField(DexEncodedField staticField, DexProgramClass enumClass) {
- return staticField.getType() == enumClass.getType()
- && staticField.isEnum()
- && staticField.isFinal();
- }
-
- static boolean matchesValuesField(
- DexEncodedField staticField, DexProgramClass enumClass, DexItemFactory factory) {
- return staticField.getType().isArrayType()
- && staticField.getType().toArrayElementType(factory) == enumClass.getType()
- && staticField.isSynthetic()
- && staticField.isFinal()
- && staticField.getName() == factory.enumValuesFieldName;
- }
-
private void removeEnumsInAnnotations() {
for (DexProgramClass clazz : appView.appInfo().classes()) {
if (clazz.isAnnotation()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
index 1e27370..58cb128 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
@@ -6,11 +6,13 @@
import com.android.tools.r8.graph.AppView;
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.DexType;
import com.android.tools.r8.graph.NestedGraphLens;
import com.android.tools.r8.graph.RewrittenPrototypeDescription;
import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap;
import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap;
@@ -18,6 +20,7 @@
import com.google.common.collect.ImmutableMap;
import java.util.IdentityHashMap;
import java.util.Map;
+import java.util.Set;
class EnumUnboxingLens extends NestedGraphLens {
@@ -54,26 +57,31 @@
return type;
}
- public static Builder enumUnboxingLensBuilder() {
- return new Builder();
+ public static Builder enumUnboxingLensBuilder(AppView<AppInfoWithLiveness> appView) {
+ return new Builder(appView);
}
static class Builder {
- protected final Map<DexType, DexType> typeMap = new IdentityHashMap<>();
- protected final MutableBidirectionalOneToOneMap<DexField, DexField> newFieldSignatures =
+ private final DexItemFactory dexItemFactory;
+ private final Map<DexType, DexType> typeMap = new IdentityHashMap<>();
+ private final MutableBidirectionalOneToOneMap<DexField, DexField> newFieldSignatures =
new BidirectionalOneToOneHashMap<>();
- protected final MutableBidirectionalOneToOneMap<DexMethod, DexMethod> newMethodSignatures =
+ private final MutableBidirectionalOneToOneMap<DexMethod, DexMethod> newMethodSignatures =
new BidirectionalOneToOneHashMap<>();
private Map<DexMethod, RewrittenPrototypeDescription> prototypeChangesPerMethod =
new IdentityHashMap<>();
- public void map(DexType from, DexType to) {
- if (from == to) {
- return;
+ Builder(AppView<AppInfoWithLiveness> appView) {
+ this.dexItemFactory = appView.dexItemFactory();
+ }
+
+ public Builder mapUnboxedEnums(Set<DexType> enumsToUnbox) {
+ for (DexType enumToUnbox : enumsToUnbox) {
+ typeMap.put(enumToUnbox, dexItemFactory.intType);
}
- typeMap.put(from, to);
+ return this;
}
public void move(DexField from, DexField to) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index 3bbd636..fe3049f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -37,10 +37,12 @@
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.MemberType;
+import com.android.tools.r8.ir.code.NewUnboxedEnumInstance;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldKnownData;
import com.android.tools.r8.ir.synthetic.EnumUnboxingCfCodeProvider;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -68,10 +70,11 @@
private static final CfVersion REQUIRED_CLASS_FILE_VERSION = CfVersion.V1_8;
private final AppView<AppInfoWithLiveness> appView;
+ private final IRConverter converter;
private final DexItemFactory factory;
private final InternalOptions options;
private final EnumDataMap unboxedEnumsData;
- private EnumUnboxingLens enumUnboxingLens;
+ private final EnumUnboxingLens enumUnboxingLens;
private final EnumUnboxingUtilityClasses utilityClasses;
private final Map<DexMethod, DexEncodedMethod> utilityMethods = new ConcurrentHashMap<>();
@@ -84,11 +87,15 @@
EnumUnboxingRewriter(
AppView<AppInfoWithLiveness> appView,
+ IRConverter converter,
+ EnumUnboxingLens enumUnboxingLens,
EnumDataMap unboxedEnumsInstanceFieldData,
EnumUnboxingUtilityClasses utilityClasses) {
this.appView = appView;
+ this.converter = converter;
this.factory = appView.dexItemFactory();
this.options = appView.options();
+ this.enumUnboxingLens = enumUnboxingLens;
this.unboxedEnumsData = unboxedEnumsInstanceFieldData;
this.utilityClasses = utilityClasses;
@@ -130,11 +137,7 @@
return utilityClasses.getSharedUtilityClass();
}
- public void setEnumUnboxingLens(EnumUnboxingLens enumUnboxingLens) {
- this.enumUnboxingLens = enumUnboxingLens;
- }
-
- Set<Phi> rewriteCode(IRCode code) {
+ Set<Phi> rewriteCode(IRCode code, MethodProcessor methodProcessor) {
// We should not process the enum methods, they will be removed and they may contain invalid
// rewriting rules.
if (unboxedEnumsData.isEmpty()) {
@@ -181,9 +184,13 @@
replaceEnumInvoke(
iterator, invokeMethod, equalsUtilityMethod, m -> synthesizeEqualsMethod());
continue;
- } else if (invokedMethod == factory.enumMembers.compareTo) {
+ } else if (invokedMethod == factory.enumMembers.compareTo
+ || invokedMethod == factory.enumMembers.compareToWithObject) {
replaceEnumInvoke(
- iterator, invokeMethod, compareToUtilityMethod, m -> synthesizeCompareToMethod());
+ iterator,
+ invokeMethod,
+ getSharedUtilityClass()
+ .ensureCompareToMethod(appView, converter, methodProcessor));
continue;
} else if (invokedMethod == factory.enumMembers.nameMethod) {
rewriteNameMethod(iterator, invokeMethod, enumType);
@@ -240,72 +247,8 @@
}
}
} else if (instruction.isInvokeStatic()) {
- InvokeStatic invokeStatic = instruction.asInvokeStatic();
- DexClassAndMethod singleTarget = invokeStatic.lookupSingleTarget(appView, context);
- if (singleTarget == null) {
- continue;
- }
- DexMethod invokedMethod = singleTarget.getReference();
- if (invokedMethod == factory.enumMembers.valueOf
- && invokeStatic.getArgument(0).isConstClass()) {
- DexType enumType =
- invokeStatic.getArgument(0).getConstInstruction().asConstClass().getValue();
- if (unboxedEnumsData.isUnboxedEnum(enumType)) {
- DexMethod valueOfMethod = computeValueOfUtilityMethod(enumType);
- Value outValue = invokeStatic.outValue();
- Value rewrittenOutValue = null;
- if (outValue != null) {
- rewrittenOutValue = code.createValue(TypeElement.getInt());
- affectedPhis.addAll(outValue.uniquePhiUsers());
- }
- InvokeStatic invoke =
- new InvokeStatic(
- valueOfMethod,
- rewrittenOutValue,
- Collections.singletonList(invokeStatic.inValues().get(1)));
- iterator.replaceCurrentInstruction(invoke);
- convertedEnums.put(invoke, enumType);
- continue;
- }
- } else if (invokedMethod == factory.javaLangSystemMethods.identityHashCode) {
- assert invokeStatic.arguments().size() == 1;
- Value argument = invokeStatic.getArgument(0);
- DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
- if (enumType != null) {
- invokeStatic.outValue().replaceUsers(argument);
- iterator.removeOrReplaceByDebugLocalRead();
- }
- } else if (invokedMethod == factory.stringMembers.valueOf) {
- assert invokeStatic.arguments().size() == 1;
- Value argument = invokeStatic.getArgument(0);
- DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
- if (enumType != null) {
- DexMethod stringValueOfMethod = computeStringValueOfUtilityMethod(enumType);
- iterator.replaceCurrentInstruction(
- new InvokeStatic(
- stringValueOfMethod, invokeStatic.outValue(), invokeStatic.arguments()));
- continue;
- }
- } else if (invokedMethod == factory.objectsMethods.requireNonNull) {
- assert invokeStatic.arguments().size() == 1;
- Value argument = invokeStatic.getArgument(0);
- DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
- if (enumType != null) {
- replaceEnumInvoke(
- iterator, invokeStatic, zeroCheckMethod, m -> synthesizeZeroCheckMethod());
- }
- } else if (invokedMethod == factory.objectsMethods.requireNonNullWithMessage) {
- assert invokeStatic.arguments().size() == 2;
- Value argument = invokeStatic.getArgument(0);
- DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
- if (enumType != null) {
- replaceEnumInvoke(
- iterator,
- invokeStatic,
- zeroCheckMessageMethod,
- m -> synthesizeZeroCheckMessageMethod());
- }
- }
+ rewriteInvokeStatic(
+ instruction.asInvokeStatic(), code, context, convertedEnums, iterator, affectedPhis);
}
if (instruction.isStaticGet()) {
StaticGet staticGet = instruction.asStaticGet();
@@ -343,14 +286,14 @@
// array. This is needed because the javac generated implementation of MyEnum.values()
// is implemented as `return $VALUES.clone()`.
removeRedundantValuesArrayCloning(invoke, instructionsToRemove, seenBlocks);
- } else {
+ } else if (unboxedEnumsData.hasUnboxedValueFor(field)) {
// Replace by ordinal + 1 for null check (null is 0).
- assert unboxedEnumsData.hasUnboxedValueFor(field)
- : "Invalid read to " + field.name + ", error during enum analysis";
ConstNumber intConstant =
code.createIntConstant(unboxedEnumsData.getUnboxedValue(field));
iterator.replaceCurrentInstruction(intConstant);
convertedEnums.put(intConstant, holder);
+ } else {
+ // Nothing to do, handled by lens code rewriting.
}
}
@@ -387,12 +330,119 @@
}
assert validateArrayAccess(arrayAccess);
}
+
+ if (instruction.isNewUnboxedEnumInstance()) {
+ NewUnboxedEnumInstance newUnboxedEnumInstance = instruction.asNewUnboxedEnumInstance();
+ assert unboxedEnumsData.isUnboxedEnum(newUnboxedEnumInstance.getType());
+ iterator.replaceCurrentInstruction(
+ code.createIntConstant(
+ EnumUnboxer.ordinalToUnboxedInt(newUnboxedEnumInstance.getOrdinal())));
+ }
}
}
assert code.isConsistentSSABeforeTypesAreCorrect();
return affectedPhis;
}
+ private void rewriteInvokeStatic(
+ InvokeStatic invoke,
+ IRCode code,
+ ProgramMethod context,
+ Map<Instruction, DexType> convertedEnums,
+ InstructionListIterator instructionIterator,
+ Set<Phi> affectedPhis) {
+ DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, context);
+ if (singleTarget == null) {
+ return;
+ }
+ DexMethod invokedMethod = singleTarget.getReference();
+
+ // Calls to java.lang.Enum.
+ if (invokedMethod.getHolderType() == factory.enumType) {
+ if (invokedMethod == factory.enumMembers.valueOf) {
+ if (!invoke.getFirstArgument().isConstClass()) {
+ return;
+ }
+ DexType enumType =
+ invoke.getFirstArgument().getConstInstruction().asConstClass().getValue();
+ if (!unboxedEnumsData.isUnboxedEnum(enumType)) {
+ return;
+ }
+ DexMethod valueOfMethod = computeValueOfUtilityMethod(enumType);
+ Value outValue = invoke.outValue();
+ Value rewrittenOutValue = null;
+ if (outValue != null) {
+ rewrittenOutValue = code.createValue(TypeElement.getInt());
+ affectedPhis.addAll(outValue.uniquePhiUsers());
+ }
+ InvokeStatic replacement =
+ new InvokeStatic(
+ valueOfMethod,
+ rewrittenOutValue,
+ Collections.singletonList(invoke.inValues().get(1)));
+ instructionIterator.replaceCurrentInstruction(replacement);
+ convertedEnums.put(replacement, enumType);
+ }
+ return;
+ }
+
+ // Calls to java.lang.Objects.
+ if (invokedMethod.getHolderType() == factory.objectsType) {
+ if (invokedMethod == factory.objectsMethods.requireNonNull) {
+ assert invoke.arguments().size() == 1;
+ Value argument = invoke.getFirstArgument();
+ DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
+ if (enumType != null) {
+ replaceEnumInvoke(
+ instructionIterator, invoke, zeroCheckMethod, m -> synthesizeZeroCheckMethod());
+ }
+ } else if (invokedMethod == factory.objectsMethods.requireNonNullWithMessage) {
+ assert invoke.arguments().size() == 2;
+ Value argument = invoke.getFirstArgument();
+ DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
+ if (enumType != null) {
+ replaceEnumInvoke(
+ instructionIterator,
+ invoke,
+ zeroCheckMessageMethod,
+ m -> synthesizeZeroCheckMessageMethod());
+ }
+ }
+ return;
+ }
+
+ // Calls to java.lang.String.
+ if (invokedMethod.getHolderType() == factory.stringType) {
+ if (invokedMethod == factory.stringMembers.valueOf) {
+ assert invoke.arguments().size() == 1;
+ Value argument = invoke.getFirstArgument();
+ DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
+ if (enumType != null) {
+ DexMethod stringValueOfMethod = computeStringValueOfUtilityMethod(enumType);
+ instructionIterator.replaceCurrentInstruction(
+ new InvokeStatic(stringValueOfMethod, invoke.outValue(), invoke.arguments()));
+ }
+ }
+ return;
+ }
+
+ // Calls to java.lang.System.
+ if (invokedMethod.getHolderType() == factory.javaLangSystemType) {
+ if (invokedMethod == factory.javaLangSystemMethods.arraycopy) {
+ // Intentionally empty.
+ } else if (invokedMethod == factory.javaLangSystemMethods.identityHashCode) {
+ assert invoke.arguments().size() == 1;
+ Value argument = invoke.getFirstArgument();
+ DexType enumType = getEnumTypeOrNull(argument, convertedEnums);
+ if (enumType != null) {
+ invoke.outValue().replaceUsers(argument);
+ instructionIterator.removeOrReplaceByDebugLocalRead();
+ }
+ }
+ return;
+ }
+ }
+
private void removeRedundantValuesArrayCloning(
InvokeStatic invoke, Set<Instruction> instructionsToRemove, Set<BasicBlock> seenBlocks) {
for (Instruction user : invoke.outValue().aliasedUsers()) {
@@ -457,11 +507,18 @@
}
private void replaceEnumInvoke(
+ InstructionListIterator iterator, InvokeMethod invoke, ProgramMethod method) {
+ replaceEnumInvoke(iterator, invoke, method.getReference(), null);
+ }
+
+ private void replaceEnumInvoke(
InstructionListIterator iterator,
InvokeMethod invoke,
DexMethod method,
Function<DexMethod, DexEncodedMethod> synthesizor) {
- utilityMethods.computeIfAbsent(method, synthesizor);
+ if (synthesizor != null) {
+ utilityMethods.computeIfAbsent(method, synthesizor);
+ }
InvokeStatic replacement =
new InvokeStatic(
method, invoke.hasUnusedOutValue() ? null : invoke.outValue(), invoke.arguments());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index 8bab979..243e721 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -4,126 +4,409 @@
package com.android.tools.r8.ir.optimize.enums;
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMember;
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.DexProto;
-import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
+import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult;
+import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
+import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.ConstClass;
+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.InvokeDirect;
+import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.NewUnboxedEnumInstance;
+import com.android.tools.r8.ir.code.StaticPut;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.optimize.enums.EnumDataMap.EnumData;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
+import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.OptionalBool;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.IdentityHashMap;
+import java.util.Collection;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.function.Predicate;
class EnumUnboxingTreeFixer {
- private final Map<DexType, List<DexEncodedMethod>> unboxedEnumsMethods = new IdentityHashMap<>();
- private final EnumUnboxingLens.Builder lensBuilder = EnumUnboxingLens.enumUnboxingLensBuilder();
+ private final EnumUnboxingLens.Builder lensBuilder;
private final AppView<AppInfoWithLiveness> appView;
private final DexItemFactory factory;
- private final Set<DexType> enumsToUnbox;
+ private final EnumDataMap enumDataMap;
+ private final Set<DexProgramClass> unboxedEnums;
private final EnumUnboxingUtilityClasses utilityClasses;
- private final EnumUnboxingRewriter enumUnboxerRewriter;
EnumUnboxingTreeFixer(
AppView<AppInfoWithLiveness> appView,
- Set<DexType> enumsToUnbox,
- EnumUnboxingUtilityClasses utilityClasses,
- EnumUnboxingRewriter enumUnboxerRewriter) {
+ EnumDataMap enumDataMap,
+ Set<DexProgramClass> unboxedEnums,
+ EnumUnboxingUtilityClasses utilityClasses) {
this.appView = appView;
+ this.enumDataMap = enumDataMap;
this.factory = appView.dexItemFactory();
- this.enumsToUnbox = enumsToUnbox;
+ this.lensBuilder =
+ EnumUnboxingLens.enumUnboxingLensBuilder(appView)
+ .mapUnboxedEnums(enumDataMap.getUnboxedEnums());
+ this.unboxedEnums = unboxedEnums;
this.utilityClasses = utilityClasses;
- this.enumUnboxerRewriter = enumUnboxerRewriter;
}
- EnumUnboxingLens fixupTypeReferences() {
- assert enumUnboxerRewriter != null;
+ Result fixupTypeReferences(IRConverter converter, ExecutorService executorService)
+ throws ExecutionException {
+ PrunedItems.Builder prunedItemsBuilder = PrunedItems.builder();
+
+ // We do this before so that we can still perform lookup of definitions.
+ fixupEnumClassInitializers(converter, executorService);
+
// Fix all methods and fields using enums to unbox.
+ // TODO(b/191617665): Parallelize this fixup.
for (DexProgramClass clazz : appView.appInfo().classes()) {
- if (enumsToUnbox.contains(clazz.getType())) {
- // Clear the initializers and move the static methods to the new location.
- Set<DexEncodedMethod> methodsToRemove = Sets.newIdentityHashSet();
- clazz
- .methods()
- .forEach(
- m -> {
- if (m.isInitializer()) {
- clearEnumToUnboxMethod(m);
- } else {
- DexType newHolder = utilityClasses.getLocalUtilityClass(clazz).getType();
- List<DexEncodedMethod> movedMethods =
- unboxedEnumsMethods.computeIfAbsent(newHolder, k -> new ArrayList<>());
- movedMethods.add(fixupEncodedMethodToUtility(m, newHolder));
- methodsToRemove.add(m);
- }
- });
- clazz.getMethodCollection().removeMethods(methodsToRemove);
+ if (enumDataMap.isUnboxedEnum(clazz)) {
+ // Clear the initializers and move the other methods to the new location.
+ LocalEnumUnboxingUtilityClass localUtilityClass =
+ utilityClasses.getLocalUtilityClass(clazz);
+ Collection<DexEncodedField> localUtilityFields =
+ createLocalUtilityFields(clazz, localUtilityClass, prunedItemsBuilder);
+ Collection<DexEncodedMethod> localUtilityMethods =
+ createLocalUtilityMethods(clazz, localUtilityClass, prunedItemsBuilder);
+
+ // Cleanup old class.
+ clazz.clearInstanceFields();
+ clazz.clearStaticFields();
+ clazz.getMethodCollection().clearDirectMethods();
+ clazz.getMethodCollection().clearVirtualMethods();
+
+ // Update members on the local utility class.
+ localUtilityClass.getDefinition().setDirectMethods(localUtilityMethods);
+ localUtilityClass.getDefinition().setStaticFields(localUtilityFields);
} else {
- clazz
- .getMethodCollection()
- .replaceMethods(method -> this.fixupEncodedMethod(clazz, method));
+ clazz.getMethodCollection().replaceMethods(method -> fixupEncodedMethod(clazz, method));
fixupFields(clazz.staticFields(), clazz::setStaticField);
fixupFields(clazz.instanceFields(), clazz::setInstanceField);
}
}
- for (DexType toUnbox : enumsToUnbox) {
- lensBuilder.map(toUnbox, factory.intType);
+
+ return new Result(lensBuilder.build(appView), prunedItemsBuilder.build());
+ }
+
+ private void fixupEnumClassInitializers(IRConverter converter, ExecutorService executorService)
+ throws ExecutionException {
+ DexEncodedField ordinalField =
+ appView
+ .appInfo()
+ .resolveField(appView.dexItemFactory().enumMembers.ordinalField)
+ .getResolvedField();
+ ThreadUtils.processItems(
+ unboxedEnums,
+ unboxedEnum -> fixupEnumClassInitializer(converter, unboxedEnum, ordinalField),
+ executorService);
+ }
+
+ private void fixupEnumClassInitializer(
+ IRConverter converter, DexProgramClass unboxedEnum, DexEncodedField ordinalField) {
+ if (!unboxedEnum.hasClassInitializer()) {
+ assert unboxedEnum.staticFields().isEmpty();
+ return;
}
- unboxedEnumsMethods.forEach(
- (newHolderType, movedMethods) -> {
- DexProgramClass newHolderClass = appView.definitionFor(newHolderType).asProgramClass();
- movedMethods.sort(Comparator.comparing(DexEncodedMember::getReference));
- newHolderClass.addDirectMethods(movedMethods);
+
+ ProgramMethod classInitializer = unboxedEnum.getProgramClassInitializer();
+ EnumData enumData = enumDataMap.get(unboxedEnum);
+ LocalEnumUnboxingUtilityClass localUtilityClass =
+ utilityClasses.getLocalUtilityClass(unboxedEnum);
+
+ // Rewrite enum instantiations + remove static-puts to pruned fields.
+ IRCode code = classInitializer.buildIR(appView);
+ ListIterator<BasicBlock> blockIterator = code.listIterator();
+ Set<Instruction> instructionsToRemove = Sets.newIdentityHashSet();
+ while (blockIterator.hasNext()) {
+ BasicBlock block = blockIterator.next();
+ InstructionListIterator instructionIterator = block.listIterator(code);
+ while (instructionIterator.hasNext()) {
+ Instruction instruction = instructionIterator.next();
+ if (instructionsToRemove.remove(instruction)) {
+ instructionIterator.removeOrReplaceByDebugLocalRead();
+ continue;
+ }
+
+ if (instruction.isConstClass()) {
+ // Rewrite MyEnum.class.desiredAssertionStatus() to
+ // LocalEnumUtility.class.desiredAssertionStatus() instead of
+ // int.class.desiredAssertionStatus().
+ ConstClass constClass = instruction.asConstClass();
+ if (constClass.getType() != unboxedEnum.getType()) {
+ continue;
+ }
+
+ List<InvokeVirtual> desiredAssertionStatusUsers = new ArrayList<>();
+ for (Instruction user : constClass.outValue().aliasedUsers()) {
+ if (user.isInvokeVirtual()) {
+ InvokeVirtual invoke = user.asInvokeVirtual();
+ if (invoke.getInvokedMethod()
+ == appView.dexItemFactory().classMethods.desiredAssertionStatus) {
+ desiredAssertionStatusUsers.add(invoke);
+ }
+ }
+ }
+
+ if (!desiredAssertionStatusUsers.isEmpty()) {
+ ConstClass newConstClass =
+ ConstClass.builder()
+ .setType(localUtilityClass.getType())
+ .setFreshOutValue(
+ code, TypeElement.classClassType(appView, definitelyNotNull()))
+ .setPosition(constClass.getPosition())
+ .build();
+ instructionIterator.add(newConstClass);
+ constClass
+ .outValue()
+ .replaceSelectiveInstructionUsers(
+ newConstClass.outValue(), desiredAssertionStatusUsers::contains);
+ }
+ } else if (instruction.isNewInstance()) {
+ NewInstance newInstance = instruction.asNewInstance();
+ DexType rewrittenType = appView.graphLens().lookupType(newInstance.getType());
+ if (rewrittenType == unboxedEnum.getType()) {
+ InvokeDirect constructorInvoke =
+ newInstance.getUniqueConstructorInvoke(appView.dexItemFactory());
+ assert constructorInvoke != null;
+
+ ProgramMethod constructor =
+ unboxedEnum.lookupProgramMethod(constructorInvoke.getInvokedMethod());
+ assert constructor != null;
+
+ InstanceFieldInitializationInfo ordinalInitializationInfo =
+ constructor
+ .getDefinition()
+ .getOptimizationInfo()
+ .getInstanceInitializerInfo(constructorInvoke)
+ .fieldInitializationInfos()
+ .get(ordinalField);
+
+ int ordinal;
+ if (ordinalInitializationInfo.isArgumentInitializationInfo()) {
+ Value ordinalValue =
+ constructorInvoke
+ .getArgument(
+ ordinalInitializationInfo
+ .asArgumentInitializationInfo()
+ .getArgumentIndex())
+ .getAliasedValue();
+ assert ordinalValue.isDefinedByInstructionSatisfying(Instruction::isConstNumber);
+ ordinal = ordinalValue.getDefinition().asConstNumber().getIntValue();
+ } else {
+ assert ordinalInitializationInfo.isSingleValue();
+ assert ordinalInitializationInfo.asSingleValue().isSingleNumberValue();
+ ordinal =
+ ordinalInitializationInfo.asSingleValue().asSingleNumberValue().getIntValue();
+ }
+
+ // Replace by an instruction that produces a value of class type UnboxedEnum (for the
+ // code to type check), which can easily be rewritten to a const-number instruction in
+ // the enum unboxing rewriter.
+ instructionIterator.replaceCurrentInstruction(
+ new NewUnboxedEnumInstance(
+ unboxedEnum.getType(),
+ ordinal,
+ code.createValue(
+ ClassTypeElement.create(
+ unboxedEnum.getType(), definitelyNotNull(), appView))));
+
+ instructionsToRemove.add(constructorInvoke);
+ }
+ } else if (instruction.isStaticPut()) {
+ StaticPut staticPut = instruction.asStaticPut();
+ DexField rewrittenField = appView.graphLens().lookupField(staticPut.getField());
+ if (rewrittenField.getHolderType() != unboxedEnum.getType()) {
+ continue;
+ }
+
+ SuccessfulFieldResolutionResult resolutionResult =
+ appView.appInfo().resolveField(rewrittenField).asSuccessfulResolution();
+ if (resolutionResult != null
+ && resolutionResult.getResolvedHolder().isProgramClass()
+ && isPrunedAfterEnumUnboxing(resolutionResult.getProgramField(), enumData)) {
+ instructionIterator.removeOrReplaceByDebugLocalRead();
+ }
+ }
+ }
+ }
+
+ if (!instructionsToRemove.isEmpty()) {
+ InstructionListIterator instructionIterator = code.instructionListIterator();
+ while (instructionIterator.hasNext()) {
+ if (instructionsToRemove.remove(instructionIterator.next())) {
+ instructionIterator.removeOrReplaceByDebugLocalRead();
+ }
+ }
+ }
+
+ converter.removeDeadCodeAndFinalizeIR(
+ code, OptimizationFeedbackIgnore.getInstance(), Timing.empty());
+ }
+
+ private Collection<DexEncodedField> createLocalUtilityFields(
+ DexProgramClass unboxedEnum,
+ LocalEnumUnboxingUtilityClass localUtilityClass,
+ PrunedItems.Builder prunedItemsBuilder) {
+ EnumData enumData = enumDataMap.get(unboxedEnum);
+ Map<DexField, DexEncodedField> localUtilityFields =
+ new LinkedHashMap<>(unboxedEnum.staticFields().size());
+ assert localUtilityClass.getDefinition().staticFields().isEmpty();
+
+ unboxedEnum.forEachProgramField(
+ field -> {
+ if (isPrunedAfterEnumUnboxing(field, enumData)) {
+ prunedItemsBuilder.addRemovedField(field.getReference());
+ return;
+ }
+
+ DexEncodedField newLocalUtilityField =
+ createLocalUtilityField(
+ field,
+ localUtilityClass,
+ newFieldSignature -> !localUtilityFields.containsKey(newFieldSignature));
+ assert !localUtilityFields.containsKey(newLocalUtilityField.getReference());
+ localUtilityFields.put(newLocalUtilityField.getReference(), newLocalUtilityField);
});
- return lensBuilder.build(appView);
+ return localUtilityFields.values();
}
- private void clearEnumToUnboxMethod(DexEncodedMethod enumMethod) {
- // The compiler may have references to the enum methods, but such methods will be removed
- // and they cannot be reprocessed since their rewriting through the lensCodeRewriter/
- // enumUnboxerRewriter will generate invalid code.
- // To work around this problem we clear such methods, i.e., we replace the code object by
- // an empty throwing code object, so reprocessing won't take time and will be valid.
- enumMethod.setCode(enumMethod.buildEmptyThrowingCode(appView.options()), appView);
+ private DexEncodedField createLocalUtilityField(
+ ProgramField field,
+ LocalEnumUnboxingUtilityClass localUtilityClass,
+ Predicate<DexField> availableFieldSignatures) {
+ // Create a new, fresh field signature on the local utility class.
+ DexField newFieldSignature =
+ factory.createFreshFieldNameWithoutHolder(
+ localUtilityClass.getType(),
+ fixupType(field.getType()),
+ field.getName().toString(),
+ availableFieldSignatures);
+
+ // Record the move.
+ lensBuilder.move(field.getReference(), newFieldSignature);
+
+ // Clear annotations and publicize.
+ return field
+ .getDefinition()
+ .toTypeSubstitutedField(
+ newFieldSignature,
+ builder ->
+ builder
+ .clearAnnotations()
+ .modifyAccessFlags(
+ accessFlags -> {
+ assert accessFlags.isStatic();
+ accessFlags.promoteToPublic();
+ }));
}
- private DexEncodedMethod fixupEncodedMethodToUtility(
- DexEncodedMethod encodedMethod, DexType newHolder) {
- DexMethod method = encodedMethod.getReference();
- DexString newMethodName =
- factory.createString(
- enumUnboxerRewriter.compatibleName(method.holder)
- + "$"
- + (encodedMethod.isStatic() ? "s" : "v")
- + "$"
- + method.name.toString());
- DexProto proto = encodedMethod.isStatic() ? method.proto : factory.prependHolderToProto(method);
- DexMethod newMethod = factory.createMethod(newHolder, fixupProto(proto), newMethodName);
- assert appView.definitionFor(encodedMethod.getHolderType()).lookupMethod(newMethod) == null;
- lensBuilder.move(method, newMethod, encodedMethod.isStatic(), true);
- encodedMethod.accessFlags.promoteToPublic();
- encodedMethod.accessFlags.promoteToStatic();
- encodedMethod.clearAnnotations();
- encodedMethod.clearParameterAnnotations();
- return encodedMethod.toTypeSubstitutedMethod(
- newMethod, builder -> builder.setCompilationState(encodedMethod.getCompilationState()));
+ private Collection<DexEncodedMethod> createLocalUtilityMethods(
+ DexProgramClass unboxedEnum,
+ LocalEnumUnboxingUtilityClass localUtilityClass,
+ PrunedItems.Builder prunedItemsBuilder) {
+ Map<DexMethod, DexEncodedMethod> localUtilityMethods =
+ new LinkedHashMap<>(
+ localUtilityClass.getDefinition().getMethodCollection().size()
+ + unboxedEnum.getMethodCollection().size());
+ localUtilityClass
+ .getDefinition()
+ .forEachMethod(method -> localUtilityMethods.put(method.getReference(), method));
+
+ unboxedEnum.forEachProgramMethod(
+ method -> {
+ if (method.getDefinition().isInstanceInitializer()) {
+ prunedItemsBuilder.addRemovedMethod(method.getReference());
+ } else {
+ DexEncodedMethod newLocalUtilityMethod =
+ createLocalUtilityMethod(
+ method,
+ localUtilityClass,
+ newMethodSignature -> !localUtilityMethods.containsKey(newMethodSignature));
+ assert !localUtilityMethods.containsKey(newLocalUtilityMethod.getReference());
+ localUtilityMethods.put(newLocalUtilityMethod.getReference(), newLocalUtilityMethod);
+ }
+ });
+ return localUtilityMethods.values();
+ }
+
+ private DexEncodedMethod createLocalUtilityMethod(
+ ProgramMethod method,
+ LocalEnumUnboxingUtilityClass localUtilityClass,
+ Predicate<DexMethod> availableMethodSignatures) {
+ DexMethod methodReference = method.getReference();
+
+ // Create a new, fresh method signature on the local utility class.
+ DexMethod newMethod =
+ method.getDefinition().isClassInitializer()
+ ? factory.createClassInitializer(localUtilityClass.getType())
+ : factory.createFreshMethodNameWithoutHolder(
+ method.getName().toString(),
+ fixupProto(
+ method.getAccessFlags().isStatic()
+ ? method.getProto()
+ : factory.prependHolderToProto(methodReference)),
+ localUtilityClass.getType(),
+ availableMethodSignatures);
+
+ // Record the move.
+ lensBuilder.move(methodReference, newMethod, method.getDefinition().isStatic(), true);
+
+ return method
+ .getDefinition()
+ .toTypeSubstitutedMethod(
+ newMethod,
+ builder ->
+ builder
+ .clearAllAnnotations()
+ .modifyAccessFlags(
+ accessFlags -> {
+ if (method.getDefinition().isClassInitializer()) {
+ assert accessFlags.isStatic();
+ } else {
+ accessFlags.promoteToPublic();
+ accessFlags.promoteToStatic();
+ }
+ })
+ .setCompilationState(method.getDefinition().getCompilationState())
+ .unsetIsLibraryMethodOverride());
+ }
+
+ private boolean isPrunedAfterEnumUnboxing(ProgramField field, EnumData enumData) {
+ return !field.getAccessFlags().isStatic()
+ || ((enumData.hasUnboxedValueFor(field) || enumData.matchesValuesField(field))
+ && !field.getDefinition().getOptimizationInfo().isDead());
}
private DexEncodedMethod fixupEncodedMethod(DexProgramClass holder, DexEncodedMethod method) {
@@ -168,7 +451,8 @@
return method
.getOptimizationInfo()
.getSimpleInliningConstraint()
- .rewrittenWithUnboxedArguments(unboxedArgumentIndices);
+ .rewrittenWithUnboxedArguments(
+ unboxedArgumentIndices, appView.simpleInliningConstraintFactory());
}
private DexMethod ensureUniqueMethod(DexEncodedMethod encodedMethod, DexMethod newMethod) {
@@ -234,12 +518,7 @@
}
return type.replaceBaseType(fixed, factory);
}
- if (type.isClassType() && enumsToUnbox.contains(type)) {
- DexType intType = factory.intType;
- lensBuilder.map(type, intType);
- return intType;
- }
- return type;
+ return type.isClassType() && enumDataMap.isUnboxedEnum(type) ? factory.intType : type;
}
private DexType[] fixupTypes(DexType[] types) {
@@ -249,4 +528,23 @@
}
return result;
}
+
+ public static class Result {
+
+ private final EnumUnboxingLens lens;
+ private final PrunedItems prunedItems;
+
+ Result(EnumUnboxingLens lens, PrunedItems prunedItems) {
+ this.lens = lens;
+ this.prunedItems = prunedItems;
+ }
+
+ EnumUnboxingLens getLens() {
+ return lens;
+ }
+
+ PrunedItems getPrunedItems() {
+ return prunedItems;
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingUtilityClasses.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingUtilityClasses.java
index 1753375..05d4955 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingUtilityClasses.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingUtilityClasses.java
@@ -73,7 +73,7 @@
SharedEnumUnboxingUtilityClass sharedUtilityClass =
SharedEnumUnboxingUtilityClass.builder(
appView, enumDataMap, enumsToUnbox, fieldAccessInfoCollectionModifierBuilder)
- .build(appBuilder);
+ .build();
ImmutableMap<DexType, LocalEnumUnboxingUtilityClass> localUtilityClasses =
createLocalUtilityClasses(enumsToUnbox, appBuilder);
this.localUtilityClasses = localUtilityClasses;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
index 19adc9f..3e556e6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
@@ -26,21 +26,21 @@
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.graph.DexTypeList;
-import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.graph.FieldAccessFlags;
-import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
-import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.origin.SynthesizedOrigin;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.ConsumerUtils;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collections;
@@ -50,17 +50,16 @@
public class SharedEnumUnboxingUtilityClass extends EnumUnboxingUtilityClass {
- public static final String ENUM_UNBOXING_SHARED_UTILITY_CLASS_SUFFIX =
- "$r8$EnumUnboxingSharedUtility";
-
private final DexProgramClass sharedUtilityClass;
- private final ProgramField valuesField;
+ private final DexProgramClass synthesizingContext;
private final ProgramMethod valuesMethod;
public SharedEnumUnboxingUtilityClass(
- DexProgramClass sharedUtilityClass, ProgramField valuesField, ProgramMethod valuesMethod) {
+ DexProgramClass sharedUtilityClass,
+ DexProgramClass synthesizingContext,
+ ProgramMethod valuesMethod) {
this.sharedUtilityClass = sharedUtilityClass;
- this.valuesField = valuesField;
+ this.synthesizingContext = synthesizingContext;
this.valuesMethod = valuesMethod;
}
@@ -73,15 +72,46 @@
appView, enumDataMap, enumsToUnbox, fieldAccessInfoCollectionModifierBuilder);
}
+ public ProgramMethod ensureCompareToMethod(
+ AppView<AppInfoWithLiveness> appView,
+ IRConverter converter,
+ MethodProcessor methodProcessor) {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ // TODO(b/191957637): Consider creating free flowing static methods instead. The synthetic
+ // infrastructure needs to be augmented with a new method ensureFixedMethod() or
+ // ensureFixedFreeFlowingMethod() for this, if we want to create only one utility method (and
+ // not one per use context).
+ return appView
+ .getSyntheticItems()
+ .ensureFixedClassMethod(
+ dexItemFactory.enumMembers.compareTo.getName(),
+ dexItemFactory.createProto(
+ dexItemFactory.intType, dexItemFactory.intType, dexItemFactory.intType),
+ SyntheticKind.ENUM_UNBOXING_SHARED_UTILITY_CLASS,
+ synthesizingContext,
+ appView,
+ ConsumerUtils.emptyConsumer(),
+ methodBuilder ->
+ methodBuilder
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setCode(
+ method ->
+ EnumUnboxingCfMethods.EnumUnboxingMethods_compareTo(
+ appView.options(), method))
+ .setClassFileVersion(CfVersion.V1_6),
+ newMethod ->
+ converter.processDesugaredMethod(
+ newMethod,
+ OptimizationFeedbackSimple.getInstance(),
+ methodProcessor,
+ methodProcessor.createMethodProcessingContext(newMethod)));
+ }
+
@Override
public DexProgramClass getDefinition() {
return sharedUtilityClass;
}
- public ProgramField getValuesField() {
- return valuesField;
- }
-
public ProgramMethod getValuesMethod() {
return valuesMethod;
}
@@ -95,12 +125,10 @@
private final AppView<AppInfoWithLiveness> appView;
private final DexItemFactory dexItemFactory;
private final EnumDataMap enumDataMap;
- private final Set<DexProgramClass> enumsToUnbox;
private final FieldAccessInfoCollectionModifier.Builder
fieldAccessInfoCollectionModifierBuilder;
- private final DexType sharedUtilityClassType;
+ private final DexProgramClass synthesizingContext;
- private DexEncodedField valuesField;
private DexEncodedMethod valuesMethod;
private Builder(
@@ -108,52 +136,40 @@
EnumDataMap enumDataMap,
Set<DexProgramClass> enumsToUnbox,
FieldAccessInfoCollectionModifier.Builder fieldAccessInfoCollectionModifierBuilder) {
+ DexProgramClass synthesizingContext = findDeterministicContextType(enumsToUnbox);
this.appView = appView;
this.dexItemFactory = appView.dexItemFactory();
this.enumDataMap = enumDataMap;
- this.enumsToUnbox = enumsToUnbox;
this.fieldAccessInfoCollectionModifierBuilder = fieldAccessInfoCollectionModifierBuilder;
- this.sharedUtilityClassType =
- EnumUnboxingUtilityClasses.Builder.getUtilityClassType(
- findDeterministicContextType(enumsToUnbox),
- ENUM_UNBOXING_SHARED_UTILITY_CLASS_SUFFIX,
- dexItemFactory);
-
- assert appView.appInfo().definitionForWithoutExistenceAssert(sharedUtilityClassType) == null;
+ this.synthesizingContext = synthesizingContext;
}
- SharedEnumUnboxingUtilityClass build(DirectMappedDexApplication.Builder appBuilder) {
+ SharedEnumUnboxingUtilityClass build() {
DexProgramClass clazz = createClass();
- appBuilder.addSynthesizedClass(clazz);
- appView.appInfo().addSynthesizedClassToBase(clazz, enumsToUnbox);
return new SharedEnumUnboxingUtilityClass(
- clazz, new ProgramField(clazz, valuesField), new ProgramMethod(clazz, valuesMethod));
+ clazz, synthesizingContext, new ProgramMethod(clazz, valuesMethod));
}
private DexProgramClass createClass() {
- DexEncodedField valuesField = createValuesField(sharedUtilityClassType);
- return new DexProgramClass(
- sharedUtilityClassType,
- null,
- new SynthesizedOrigin("enum unboxing", EnumUnboxer.class),
- ClassAccessFlags.createPublicFinalSynthetic(),
- dexItemFactory.objectType,
- DexTypeList.empty(),
- null,
- null,
- Collections.emptyList(),
- null,
- Collections.emptyList(),
- ClassSignature.noSignature(),
- DexAnnotationSet.empty(),
- new DexEncodedField[] {valuesField},
- DexEncodedField.EMPTY_ARRAY,
- new DexEncodedMethod[] {
- createClassInitializer(valuesField), createValuesMethod(valuesField)
- },
- DexEncodedMethod.EMPTY_ARRAY,
- dexItemFactory.getSkipNameValidationForTesting(),
- DexProgramClass::checksumFromType);
+ DexProgramClass clazz =
+ appView
+ .getSyntheticItems()
+ .createFixedClass(
+ SyntheticKind.ENUM_UNBOXING_SHARED_UTILITY_CLASS,
+ synthesizingContext,
+ appView,
+ classBuilder -> {
+ DexType sharedUtilityClassType = classBuilder.getType();
+ DexEncodedField valuesField = createValuesField(sharedUtilityClassType);
+ classBuilder
+ .setDirectMethods(
+ ImmutableList.of(
+ createClassInitializer(sharedUtilityClassType, valuesField),
+ createValuesMethod(sharedUtilityClassType, valuesField)))
+ .setStaticFields(ImmutableList.of(valuesField));
+ });
+ assert clazz.getAccessFlags().equals(ClassAccessFlags.createPublicFinalSynthetic());
+ return clazz;
}
// Fields.
@@ -172,25 +188,26 @@
fieldAccessInfoCollectionModifierBuilder
.recordFieldReadInUnknownContext(valuesField.getReference())
.recordFieldWriteInUnknownContext(valuesField.getReference());
- this.valuesField = valuesField;
return valuesField;
}
// Methods.
- private DexEncodedMethod createClassInitializer(DexEncodedField valuesField) {
+ private DexEncodedMethod createClassInitializer(
+ DexType sharedUtilityClassType, DexEncodedField valuesField) {
return new DexEncodedMethod(
dexItemFactory.createClassInitializer(sharedUtilityClassType),
MethodAccessFlags.createForClassInitializer(),
MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- createClassInitializerCode(valuesField),
+ createClassInitializerCode(sharedUtilityClassType, valuesField),
DexEncodedMethod.D8_R8_SYNTHESIZED,
CfVersion.V1_6);
}
- private CfCode createClassInitializerCode(DexEncodedField valuesField) {
+ private CfCode createClassInitializerCode(
+ DexType sharedUtilityClassType, DexEncodedField valuesField) {
int maxValuesArraySize = enumDataMap.getMaxValuesSize();
int numberOfInstructions = 4 + maxValuesArraySize * 4;
List<CfInstruction> instructions = new ArrayList<>(numberOfInstructions);
@@ -217,7 +234,8 @@
Collections.emptyList());
}
- private DexEncodedMethod createValuesMethod(DexEncodedField valuesField) {
+ private DexEncodedMethod createValuesMethod(
+ DexType sharedUtilityClassType, DexEncodedField valuesField) {
DexEncodedMethod valuesMethod =
new DexEncodedMethod(
dexItemFactory.createMethod(
@@ -228,14 +246,15 @@
MethodTypeSignature.noSignature(),
DexAnnotationSet.empty(),
ParameterAnnotationsList.empty(),
- createValuesMethodCode(valuesField),
+ createValuesMethodCode(sharedUtilityClassType, valuesField),
DexEncodedMethod.D8_R8_SYNTHESIZED,
CfVersion.V1_6);
this.valuesMethod = valuesMethod;
return valuesMethod;
}
- private CfCode createValuesMethodCode(DexEncodedField valuesField) {
+ private CfCode createValuesMethodCode(
+ DexType sharedUtilityClassType, DexEncodedField valuesField) {
int maxStack = 5;
int maxLocals = 2;
int argumentLocalSlot = 0;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
index 5b99126..7a0dae1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.ir.analysis.inlining.NeverSimpleInliningConstraint;
import com.android.tools.r8.ir.analysis.inlining.SimpleInliningConstraint;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
@@ -190,9 +191,9 @@
}
public MutableMethodOptimizationInfo fixupInstanceInitializerInfo(
- AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, PrunedItems prunedItems) {
instanceInitializerInfoCollection =
- instanceInitializerInfoCollection.rewrittenWithLens(appView, lens);
+ instanceInitializerInfoCollection.rewrittenWithLens(appView, lens, prunedItems);
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java
index ede372a..89674f5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedback.java
@@ -30,9 +30,7 @@
MemberOptimizationInfo<?> optimizationInfo = member.getOptimizationInfo();
if (optimizationInfo.isMutableOptimizationInfo()) {
member.accept(
- field -> {
- fixup(field, optimizationInfo.asMutableFieldOptimizationInfo());
- },
+ field -> fixup(field, optimizationInfo.asMutableFieldOptimizationInfo()),
method -> fixup(method, optimizationInfo.asMutableMethodOptimizationInfo()));
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextInsensitiveInstanceInitializerInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextInsensitiveInstanceInitializerInfoCollection.java
index 6cf070d..537bda3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextInsensitiveInstanceInitializerInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextInsensitiveInstanceInitializerInfoCollection.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -35,8 +36,9 @@
@Override
public ContextInsensitiveInstanceInitializerInfoCollection rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens) {
- NonTrivialInstanceInitializerInfo rewrittenInfo = info.rewrittenWithLens(appView, lens);
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, PrunedItems prunedItems) {
+ NonTrivialInstanceInitializerInfo rewrittenInfo =
+ info.rewrittenWithLens(appView, lens, prunedItems);
if (rewrittenInfo != info) {
return new ContextInsensitiveInstanceInitializerInfoCollection(rewrittenInfo);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextSensitiveInstanceInitializerInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextSensitiveInstanceInitializerInfoCollection.java
index 3347587..acaeef9 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextSensitiveInstanceInitializerInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/ContextSensitiveInstanceInitializerInfoCollection.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.google.common.collect.ImmutableMap;
@@ -49,9 +50,11 @@
@Override
public InstanceInitializerInfoCollection rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, PrunedItems prunedItems) {
Builder builder = builder();
- infos.forEach((context, info) -> builder.put(context, info.rewrittenWithLens(appView, lens)));
+ infos.forEach(
+ (context, info) ->
+ builder.put(context, info.rewrittenWithLens(appView, lens, prunedItems)));
return builder.build();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
index e976680..8391de8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/DefaultInstanceInitializerInfo.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.UnknownFieldSet;
import com.android.tools.r8.ir.optimize.info.field.EmptyInstanceFieldInitializationInfoCollection;
@@ -66,7 +67,7 @@
@Override
public InstanceInitializerInfo rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, PrunedItems prunedItems) {
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/EmptyInstanceInitializerInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/EmptyInstanceInitializerInfoCollection.java
index d7d3560..7100e35 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/EmptyInstanceInitializerInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/EmptyInstanceInitializerInfoCollection.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -37,7 +38,7 @@
@Override
public EmptyInstanceInitializerInfoCollection rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, PrunedItems prunedItems) {
return this;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java
index 27576e9..edd78e5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfo.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -71,5 +72,5 @@
}
public abstract InstanceInitializerInfo rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens);
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, PrunedItems prunedItems);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfoCollection.java
index ce53a6a..0139cf0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/InstanceInitializerInfoCollection.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.MapUtils;
@@ -36,7 +37,7 @@
public abstract boolean isEmpty();
public abstract InstanceInitializerInfoCollection rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens);
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, PrunedItems prunedItems);
public static class Builder {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
index 9dd2750..7066531 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.AbstractFieldSet;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.ConcreteMutableFieldSet;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.EmptyFieldSet;
@@ -99,11 +100,11 @@
@Override
public NonTrivialInstanceInitializerInfo rewrittenWithLens(
- AppView<AppInfoWithLiveness> appView, GraphLens lens) {
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, PrunedItems prunedItems) {
return new NonTrivialInstanceInitializerInfo(
data,
fieldInitializationInfos.rewrittenWithLens(appView, lens),
- readSet.rewrittenWithLens(appView, lens),
+ readSet.rewrittenWithLens(appView, lens, prunedItems),
lens.getRenamedMethodSignature(parent));
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningIRProvider.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningIRProvider.java
index dba0cde..35afed1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningIRProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/InliningIRProvider.java
@@ -60,6 +60,10 @@
assert existing == null;
}
+ public MethodProcessor getMethodProcessor() {
+ return methodProcessor;
+ }
+
public boolean verifyIRCacheIsEmpty() {
assert cache.isEmpty();
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodSideEffectModelCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodSideEffectModelCollection.java
index 3703148..0e8d33c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodSideEffectModelCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodSideEffectModelCollection.java
@@ -68,6 +68,7 @@
return ImmutableSet.<DexMethod>builder()
.add(dexItemFactory.booleanMembers.toString)
.add(dexItemFactory.byteMembers.toString)
+ .add(dexItemFactory.classMethods.desiredAssertionStatus)
.add(dexItemFactory.charMembers.toString)
.add(dexItemFactory.doubleMembers.toString)
.add(dexItemFactory.enumMembers.constructor)
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
index 254511e..db93ced 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
@@ -31,7 +31,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
import com.android.tools.r8.utils.collections.ImmutableDeque;
import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap;
import java.util.ArrayList;
diff --git a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
index ac1ad75..6a4089d 100644
--- a/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
+++ b/src/main/java/com/android/tools/r8/shaking/AnnotationRemover.java
@@ -14,6 +14,7 @@
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.graph.DexValue;
import com.android.tools.r8.graph.EnclosingMethodAttribute;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.InnerClassAttribute;
@@ -272,12 +273,46 @@
} else {
definition.rewriteAllAnnotations(
(annotation, isParameterAnnotation) ->
- DexAnnotation.isJavaLangRetentionAnnotation(annotation, appView.dexItemFactory())
- ? annotation
- : null);
+ shouldRetainAnnotationOnAnnotationClass(annotation) ? annotation : null);
}
}
+ private boolean shouldRetainAnnotationOnAnnotationClass(DexAnnotation annotation) {
+ if (DexAnnotation.isAnnotationDefaultAnnotation(annotation, appView.dexItemFactory())) {
+ return shouldRetainAnnotationDefaultAnnotationOnAnnotationClass(annotation);
+ }
+ if (DexAnnotation.isJavaLangRetentionAnnotation(annotation, appView.dexItemFactory())) {
+ return shouldRetainRetentionAnnotationOnAnnotationClass(annotation);
+ }
+ return false;
+ }
+
+ private boolean shouldRetainAnnotationDefaultAnnotationOnAnnotationClass(
+ DexAnnotation annotation) {
+ // We currently always retain the @AnnotationDefault annotations for annotation classes. In full
+ // mode we could consider only retaining @AnnotationDefault annotations for pinned annotations,
+ // as this is consistent with removing all annotations for non-kept items.
+ return true;
+ }
+
+ private boolean shouldRetainRetentionAnnotationOnAnnotationClass(DexAnnotation annotation) {
+ // Retain @Retention annotations that are different from @Retention(RetentionPolicy.CLASS).
+ if (annotation.annotation.getNumberOfElements() != 1) {
+ return true;
+ }
+ DexAnnotationElement element = annotation.annotation.getElement(0);
+ if (element.name != appView.dexItemFactory().valueString) {
+ return true;
+ }
+ DexValue value = element.getValue();
+ if (!value.isDexValueEnum()
+ || value.asDexValueEnum().getValue()
+ != appView.dexItemFactory().javaLangAnnotationRetentionPolicyMembers.CLASS) {
+ return true;
+ }
+ return false;
+ }
+
private void stripAttributes(DexProgramClass clazz, KeepClassInfo keepInfo) {
// If [clazz] is mentioned by a keep rule, it could be used for reflection, and we therefore
// need to keep the enclosing method and inner classes attributes, if requested. In Proguard
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 a4478d6..2ff9008 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -46,8 +46,8 @@
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter;
import com.android.tools.r8.synthesis.CommittedItems;
import com.android.tools.r8.utils.CollectionUtils;
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 ea9b2d7..9537206 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -100,9 +100,12 @@
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer.R8CfInstructionDesugaringEventConsumer;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringCollection;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer.R8PostProcessingDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.LambdaClass;
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter;
import com.android.tools.r8.kotlin.KotlinMetadataEnqueuerExtension;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.identifiernamestring.IdentifierNameStringLookupResult;
@@ -360,7 +363,7 @@
private final LiveFieldsSet liveFields;
/** A queue of items that need processing. Different items trigger different actions. */
- private final EnqueuerWorklist workList;
+ private EnqueuerWorklist workList;
private final ProguardCompatibilityActions.Builder proguardCompatibilityActionsBuilder;
@@ -1742,6 +1745,8 @@
|| appView.appInfo().getMainDexInfo().isTracedRoot(clazz, appView.getSyntheticItems())
: "Class " + clazz.toSourceString() + " was not a main dex root in the first round";
+ assert !appView.unboxedEnums().isUnboxedEnum(clazz);
+
// Mark types in inner-class attributes referenced.
{
BiConsumer<DexType, ProgramDerivedContext> missingClassConsumer =
@@ -3118,7 +3123,7 @@
}
}
- private static class SyntheticAdditions {
+ public static class SyntheticAdditions {
private final ProcessorContext processorContext;
private Map<DexMethod, MethodProcessingContext> methodProcessingContexts =
@@ -3130,6 +3135,8 @@
Map<DexType, DexClasspathClass> syntheticClasspathClasses = new IdentityHashMap<>();
+ Map<DexProgramClass, List<DexClass>> injectedInterfaces = new IdentityHashMap<>();
+
// Subset of live methods that need have keep requirements.
List<Pair<ProgramMethod, Consumer<KeepMethodInfo.Joiner>>> liveMethodsWithKeepActions =
new ArrayList<>();
@@ -3152,25 +3159,36 @@
return empty;
}
- void addLiveClasspathClass(DexClasspathClass clazz) {
+ public void addLiveClasspathClass(DexClasspathClass clazz) {
DexClasspathClass old = syntheticClasspathClasses.put(clazz.type, clazz);
assert old == null;
}
- void addLiveMethod(ProgramMethod method) {
+ public void addLiveMethod(ProgramMethod method) {
DexMethod signature = method.getDefinition().getReference();
assert !liveMethods.containsKey(signature);
liveMethods.put(signature, method);
}
+ public void injectInterface(DexProgramClass clazz, DexClass newInterface) {
+ List<DexClass> newInterfaces =
+ injectedInterfaces.computeIfAbsent(clazz, ignored -> new ArrayList<>());
+ newInterfaces.add(newInterface);
+ }
+
void addLiveMethodWithKeepAction(
ProgramMethod method, Consumer<KeepMethodInfo.Joiner> keepAction) {
addLiveMethod(method);
liveMethodsWithKeepActions.add(new Pair<>(method, keepAction));
}
+ public ProgramMethodSet getLiveMethods() {
+ ProgramMethodSet set = ProgramMethodSet.create();
+ liveMethods.values().forEach(set::add);
+ return set;
+ }
+
void enqueueWorkItems(Enqueuer enqueuer) {
- assert !isEmpty();
assert enqueuer.mode.isInitialTreeShaking();
// All synthetic additions are initial tree shaking only. No need to track keep reasons.
@@ -3188,6 +3206,11 @@
enqueuer.workList.enqueueMarkMethodLiveAction(liveMethod, liveMethod, fakeReason);
}
enqueuer.liveNonProgramTypes.addAll(syntheticClasspathClasses.values());
+ injectedInterfaces.forEach(
+ (clazz, itfs) -> {
+ enqueuer.objectAllocationInfoCollection.injectInterfaces(
+ enqueuer.appInfo(), clazz, itfs);
+ });
}
}
@@ -3224,7 +3247,8 @@
CfInstructionDesugaringEventConsumer.createForR8(
appView,
this::recordLambdaSynthesizingContext,
- this::recordTwrCloseResourceMethodSynthesizingContext);
+ this::recordTwrCloseResourceMethodSynthesizingContext,
+ additions);
ThreadUtils.processItems(
pendingDesugaring,
method ->
@@ -3612,6 +3636,10 @@
break;
}
+ if (mode.isInitialTreeShaking()) {
+ postProcessingDesugaring();
+ }
+
if (Log.ENABLED) {
Set<DexEncodedMethod> allLive = Sets.newIdentityHashSet();
Set<DexEncodedMethod> reachableNotLive = Sets.difference(allLive, liveMethods.getItems());
@@ -3627,6 +3655,37 @@
}
}
+ private void postProcessingDesugaring() {
+ SyntheticAdditions syntheticAdditions =
+ new SyntheticAdditions(appView.createProcessorContext());
+
+ R8PostProcessingDesugaringEventConsumer eventConsumer =
+ CfPostProcessingDesugaringEventConsumer.createForR8(
+ appView, syntheticAdditions::addLiveMethod, syntheticAdditions);
+ CfPostProcessingDesugaringCollection.create(appView, desugaring.getRetargetingInfo())
+ .postProcessingDesugaring(eventConsumer);
+
+ if (syntheticAdditions.isEmpty()) {
+ return;
+ }
+
+ // Commit the pending synthetics and recompute subtypes.
+ appInfo = appInfo.rebuildWithClassHierarchy(app -> app);
+ appView.setAppInfo(appInfo);
+ subtypingInfo = new SubtypingInfo(appView);
+
+ syntheticAdditions.enqueueWorkItems(this);
+
+ workList = workList.nonPushable(syntheticAdditions.getLiveMethods());
+
+ while (!workList.isEmpty()) {
+ EnqueuerAction action = workList.poll();
+ action.run(this);
+ }
+
+ eventConsumer.finalizeDesugaring();
+ }
+
private long getNumberOfLiveItems() {
long result = liveTypes.items.size();
result += liveMethods.items.size();
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
index 65081d5..38e40dd 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.shaking;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
@@ -14,10 +15,11 @@
import com.android.tools.r8.shaking.GraphReporter.KeepReasonWitness;
import com.android.tools.r8.utils.Action;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
import java.util.ArrayDeque;
import java.util.Queue;
-public class EnqueuerWorklist {
+public abstract class EnqueuerWorklist {
public abstract static class EnqueuerAction {
public abstract void run(Enqueuer enqueuer);
@@ -279,15 +281,16 @@
}
}
- private final Enqueuer enqueuer;
- private final Queue<EnqueuerAction> queue = new ArrayDeque<>();
-
- private EnqueuerWorklist(Enqueuer enqueuer) {
- this.enqueuer = enqueuer;
- }
+ final Enqueuer enqueuer;
+ final Queue<EnqueuerAction> queue;
public static EnqueuerWorklist createWorklist(Enqueuer enqueuer) {
- return new EnqueuerWorklist(enqueuer);
+ return new PushableEnqueuerWorkList(enqueuer);
+ }
+
+ private EnqueuerWorklist(Enqueuer enqueuer, Queue<EnqueuerAction> queue) {
+ this.enqueuer = enqueuer;
+ this.queue = queue;
}
public boolean isEmpty() {
@@ -298,88 +301,275 @@
return queue.poll();
}
- boolean enqueueAssertAction(Action assertion) {
- if (InternalOptions.assertionsEnabled()) {
- queue.add(new AssertAction(assertion));
- }
- return true;
- }
+ abstract EnqueuerWorklist nonPushable(ProgramMethodSet enqueuedMarkMethodLive);
- void enqueueMarkReachableDirectAction(
- DexMethod method, ProgramDefinition context, KeepReason reason) {
- queue.add(new MarkReachableDirectAction(method, context, reason));
- }
+ abstract boolean enqueueAssertAction(Action assertion);
- void enqueueMarkReachableSuperAction(DexMethod method, ProgramMethod from) {
- queue.add(new MarkReachableSuperAction(method, from));
- }
+ abstract void enqueueMarkReachableDirectAction(
+ DexMethod method, ProgramDefinition context, KeepReason reason);
- public void enqueueMarkFieldAsReachableAction(
- ProgramField field, ProgramDefinition context, KeepReason reason) {
- queue.add(new MarkFieldAsReachableAction(field, context, reason));
- }
+ abstract void enqueueMarkReachableSuperAction(DexMethod method, ProgramMethod from);
- // TODO(b/142378367): Context is the containing method that is cause of the instantiation.
- // Consider updating call sites with the context information to increase precision where possible.
- public void enqueueMarkInstantiatedAction(
+ public abstract void enqueueMarkFieldAsReachableAction(
+ ProgramField field, ProgramDefinition context, KeepReason reason);
+
+ public abstract void enqueueMarkInstantiatedAction(
DexProgramClass clazz,
ProgramMethod context,
InstantiationReason instantiationReason,
- KeepReason keepReason) {
- assert !clazz.isAnnotation();
- assert !clazz.isInterface();
- queue.add(new MarkInstantiatedAction(clazz, context, instantiationReason, keepReason));
- }
+ KeepReason keepReason);
- void enqueueMarkAnnotationInstantiatedAction(DexProgramClass clazz, KeepReasonWitness reason) {
- assert clazz.isAnnotation();
- assert clazz.isInterface();
- queue.add(new MarkAnnotationInstantiatedAction(clazz, reason));
- }
+ abstract void enqueueMarkAnnotationInstantiatedAction(
+ DexProgramClass clazz, KeepReasonWitness reason);
- void enqueueMarkInterfaceInstantiatedAction(DexProgramClass clazz, KeepReasonWitness reason) {
- assert !clazz.isAnnotation();
- assert clazz.isInterface();
- queue.add(new MarkInterfaceInstantiatedAction(clazz, reason));
- }
+ abstract void enqueueMarkInterfaceInstantiatedAction(
+ DexProgramClass clazz, KeepReasonWitness reason);
- boolean enqueueMarkMethodLiveAction(
- ProgramMethod method, ProgramDefinition context, KeepReason reason) {
- if (enqueuer.addLiveMethod(method, reason)) {
- queue.add(new MarkMethodLiveAction(method, context));
- if (!enqueuer.isMethodTargeted(method)) {
- queue.add(new TraceMethodDefinitionExcludingCodeAction(method));
+ abstract boolean enqueueMarkMethodLiveAction(
+ ProgramMethod method, ProgramDefinition context, KeepReason reason);
+
+ abstract void enqueueMarkMethodKeptAction(ProgramMethod method, KeepReason reason);
+
+ abstract void enqueueMarkFieldKeptAction(ProgramField field, KeepReasonWitness witness);
+
+ public abstract void enqueueTraceCodeAction(ProgramMethod method);
+
+ public abstract void enqueueTraceConstClassAction(DexType type, ProgramMethod context);
+
+ public abstract void enqueueTraceInvokeDirectAction(
+ DexMethod invokedMethod, ProgramMethod context);
+
+ public abstract void enqueueTraceNewInstanceAction(DexType type, ProgramMethod context);
+
+ public abstract void enqueueTraceStaticFieldRead(DexField field, ProgramMethod context);
+
+ static class PushableEnqueuerWorkList extends EnqueuerWorklist {
+
+ PushableEnqueuerWorkList(Enqueuer enqueuer) {
+ super(enqueuer, new ArrayDeque<>());
+ }
+
+ @Override
+ EnqueuerWorklist nonPushable(ProgramMethodSet enqueuedMarkMethodLive) {
+ return new NonPushableEnqueuerWorklist(this, enqueuedMarkMethodLive);
+ }
+
+ @Override
+ boolean enqueueAssertAction(Action assertion) {
+ if (InternalOptions.assertionsEnabled()) {
+ queue.add(new AssertAction(assertion));
}
return true;
}
- return false;
+
+ @Override
+ void enqueueMarkReachableDirectAction(
+ DexMethod method, ProgramDefinition context, KeepReason reason) {
+ queue.add(new MarkReachableDirectAction(method, context, reason));
+ }
+
+ @Override
+ void enqueueMarkReachableSuperAction(DexMethod method, ProgramMethod from) {
+ queue.add(new MarkReachableSuperAction(method, from));
+ }
+
+ @Override
+ public void enqueueMarkFieldAsReachableAction(
+ ProgramField field, ProgramDefinition context, KeepReason reason) {
+ queue.add(new MarkFieldAsReachableAction(field, context, reason));
+ }
+
+ // TODO(b/142378367): Context is the containing method that is cause of the instantiation.
+ // Consider updating call sites with the context information to increase precision where
+ // possible.
+ @Override
+ public void enqueueMarkInstantiatedAction(
+ DexProgramClass clazz,
+ ProgramMethod context,
+ InstantiationReason instantiationReason,
+ KeepReason keepReason) {
+ assert !clazz.isAnnotation();
+ assert !clazz.isInterface();
+ queue.add(new MarkInstantiatedAction(clazz, context, instantiationReason, keepReason));
+ }
+
+ @Override
+ void enqueueMarkAnnotationInstantiatedAction(DexProgramClass clazz, KeepReasonWitness reason) {
+ assert clazz.isAnnotation();
+ assert clazz.isInterface();
+ queue.add(new MarkAnnotationInstantiatedAction(clazz, reason));
+ }
+
+ @Override
+ void enqueueMarkInterfaceInstantiatedAction(DexProgramClass clazz, KeepReasonWitness reason) {
+ assert !clazz.isAnnotation();
+ assert clazz.isInterface();
+ queue.add(new MarkInterfaceInstantiatedAction(clazz, reason));
+ }
+
+ @Override
+ boolean enqueueMarkMethodLiveAction(
+ ProgramMethod method, ProgramDefinition context, KeepReason reason) {
+ if (enqueuer.addLiveMethod(method, reason)) {
+ queue.add(new MarkMethodLiveAction(method, context));
+ if (!enqueuer.isMethodTargeted(method)) {
+ queue.add(new TraceMethodDefinitionExcludingCodeAction(method));
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ void enqueueMarkMethodKeptAction(ProgramMethod method, KeepReason reason) {
+ queue.add(new MarkMethodKeptAction(method, reason));
+ }
+
+ @Override
+ void enqueueMarkFieldKeptAction(ProgramField field, KeepReasonWitness witness) {
+ queue.add(new MarkFieldKeptAction(field, witness));
+ }
+
+ @Override
+ public void enqueueTraceCodeAction(ProgramMethod method) {
+ queue.add(new TraceCodeAction(method));
+ }
+
+ @Override
+ public void enqueueTraceConstClassAction(DexType type, ProgramMethod context) {
+ queue.add(new TraceConstClassAction(type, context));
+ }
+
+ @Override
+ public void enqueueTraceInvokeDirectAction(DexMethod invokedMethod, ProgramMethod context) {
+ queue.add(new TraceInvokeDirectAction(invokedMethod, context));
+ }
+
+ @Override
+ public void enqueueTraceNewInstanceAction(DexType type, ProgramMethod context) {
+ queue.add(new TraceNewInstanceAction(type, context));
+ }
+
+ @Override
+ public void enqueueTraceStaticFieldRead(DexField field, ProgramMethod context) {
+ queue.add(new TraceStaticFieldReadAction(field, context));
+ }
}
- void enqueueMarkMethodKeptAction(ProgramMethod method, KeepReason reason) {
- queue.add(new MarkMethodKeptAction(method, reason));
- }
+ public static class NonPushableEnqueuerWorklist extends EnqueuerWorklist {
- void enqueueMarkFieldKeptAction(ProgramField field, KeepReasonWitness witness) {
- queue.add(new MarkFieldKeptAction(field, witness));
- }
+ private ProgramMethodSet enqueuedMarkMethodLive;
- public void enqueueTraceCodeAction(ProgramMethod method) {
- queue.add(new TraceCodeAction(method));
- }
+ private NonPushableEnqueuerWorklist(
+ PushableEnqueuerWorkList workList, ProgramMethodSet enqueuedMarkMethodLive) {
+ super(workList.enqueuer, workList.queue);
+ this.enqueuedMarkMethodLive = enqueuedMarkMethodLive;
+ }
- public void enqueueTraceConstClassAction(DexType type, ProgramMethod context) {
- queue.add(new TraceConstClassAction(type, context));
- }
+ @Override
+ EnqueuerWorklist nonPushable(ProgramMethodSet enqueuedMarkMethodLive) {
+ return this;
+ }
- public void enqueueTraceInvokeDirectAction(DexMethod invokedMethod, ProgramMethod context) {
- queue.add(new TraceInvokeDirectAction(invokedMethod, context));
- }
+ private Unreachable attemptToEnqueue() {
+ throw new Unreachable("Attempt to enqueue an action in a non pushable enqueuer work list.");
+ }
- public void enqueueTraceNewInstanceAction(DexType type, ProgramMethod context) {
- queue.add(new TraceNewInstanceAction(type, context));
- }
+ @Override
+ boolean enqueueAssertAction(Action assertion) {
+ throw attemptToEnqueue();
+ }
- public void enqueueTraceStaticFieldRead(DexField field, ProgramMethod context) {
- queue.add(new TraceStaticFieldReadAction(field, context));
+ @Override
+ void enqueueMarkReachableDirectAction(
+ DexMethod method, ProgramDefinition context, KeepReason reason) {
+ throw attemptToEnqueue();
+ }
+
+ @Override
+ void enqueueMarkReachableSuperAction(DexMethod method, ProgramMethod from) {
+
+ throw attemptToEnqueue();
+ }
+
+ @Override
+ public void enqueueMarkFieldAsReachableAction(
+ ProgramField field, ProgramDefinition context, KeepReason reason) {
+
+ throw attemptToEnqueue();
+ }
+
+ @Override
+ public void enqueueMarkInstantiatedAction(
+ DexProgramClass clazz,
+ ProgramMethod context,
+ InstantiationReason instantiationReason,
+ KeepReason keepReason) {
+
+ throw attemptToEnqueue();
+ }
+
+ @Override
+ void enqueueMarkAnnotationInstantiatedAction(DexProgramClass clazz, KeepReasonWitness reason) {
+
+ throw attemptToEnqueue();
+ }
+
+ @Override
+ void enqueueMarkInterfaceInstantiatedAction(DexProgramClass clazz, KeepReasonWitness reason) {
+
+ throw attemptToEnqueue();
+ }
+
+ @Override
+ boolean enqueueMarkMethodLiveAction(
+ ProgramMethod method, ProgramDefinition context, KeepReason reason) {
+ if (enqueuedMarkMethodLive.contains(method)) {
+ return false;
+ }
+ throw attemptToEnqueue();
+ }
+
+ @Override
+ void enqueueMarkMethodKeptAction(ProgramMethod method, KeepReason reason) {
+
+ throw attemptToEnqueue();
+ }
+
+ @Override
+ void enqueueMarkFieldKeptAction(ProgramField field, KeepReasonWitness witness) {
+
+ throw attemptToEnqueue();
+ }
+
+ @Override
+ public void enqueueTraceCodeAction(ProgramMethod method) {
+
+ throw attemptToEnqueue();
+ }
+
+ @Override
+ public void enqueueTraceConstClassAction(DexType type, ProgramMethod context) {
+
+ throw attemptToEnqueue();
+ }
+
+ @Override
+ public void enqueueTraceInvokeDirectAction(DexMethod invokedMethod, ProgramMethod context) {
+
+ throw attemptToEnqueue();
+ }
+
+ @Override
+ public void enqueueTraceNewInstanceAction(DexType type, ProgramMethod context) {
+
+ throw attemptToEnqueue();
+ }
+
+ @Override
+ public void enqueueTraceStaticFieldRead(DexField field, ProgramMethod context) {
+
+ throw attemptToEnqueue();
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
index 2cc626d..5ffc72b 100644
--- a/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
+++ b/src/main/java/com/android/tools/r8/shaking/MissingClasses.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.shaking;
-import static com.android.tools.r8.ir.desugar.DesugaredLibraryAPIConverter.DESCRIPTOR_VIVIFIED_PREFIX;
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter.DESCRIPTOR_VIVIFIED_PREFIX;
import static com.android.tools.r8.utils.collections.IdentityHashSetFromMap.newProgramDerivedContextSet;
import com.android.tools.r8.diagnostic.MissingDefinitionsDiagnostic;
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 230af5f..6f390ea 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -1133,8 +1133,8 @@
// Step 3: Clear the members of the source class since they have now been moved to the target.
source.getMethodCollection().clearDirectMethods();
source.getMethodCollection().clearVirtualMethods();
- source.setInstanceFields(null);
- source.setStaticFields(null);
+ source.clearInstanceFields();
+ source.clearStaticFields();
// Step 4: Record merging.
mergedClasses.put(source.type, target.type);
assert !abortMerge;
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 f7e235f..f9bdee5 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.synthesis;
+import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
+
import com.android.tools.r8.FeatureSplit;
import com.android.tools.r8.contexts.CompilationContext.UniqueContext;
import com.android.tools.r8.features.ClassToFeatureSplitMap;
@@ -568,15 +570,37 @@
DexString name,
DexProto proto,
SyntheticKind kind,
- DexProgramClass context,
+ ProgramDefinition context,
AppView<?> appView,
Consumer<SyntheticProgramClassBuilder> buildClassCallback,
Consumer<SyntheticMethodBuilder> buildMethodCallback) {
+ return ensureFixedClassMethod(
+ name,
+ proto,
+ kind,
+ context,
+ appView,
+ buildClassCallback,
+ buildMethodCallback,
+ emptyConsumer());
+ }
+
+ public ProgramMethod ensureFixedClassMethod(
+ DexString name,
+ DexProto proto,
+ SyntheticKind kind,
+ ProgramDefinition context,
+ AppView<?> appView,
+ Consumer<SyntheticProgramClassBuilder> buildClassCallback,
+ Consumer<SyntheticMethodBuilder> buildMethodCallback,
+ Consumer<ProgramMethod> newMethodCallback) {
DexProgramClass clazz =
- ensureFixedClass(kind, context, appView, buildClassCallback, ignored -> {});
+ ensureFixedClass(
+ kind, context.getContextClass(), appView, buildClassCallback, emptyConsumer());
DexMethod methodReference = appView.dexItemFactory().createMethod(clazz.getType(), proto, name);
DexEncodedMethod methodDefinition =
- internalEnsureMethod(methodReference, clazz, kind, appView, buildMethodCallback);
+ internalEnsureMethod(
+ methodReference, clazz, kind, appView, buildMethodCallback, newMethodCallback);
return new ProgramMethod(clazz, methodDefinition);
}
@@ -637,16 +661,18 @@
DexMethod methodReference =
appView.dexItemFactory().createMethod(clazz.getType(), methodProto, methodName);
DexEncodedMethod methodDefinition =
- internalEnsureMethod(methodReference, clazz, kind, appView, buildMethodCallback);
+ internalEnsureMethod(
+ methodReference, clazz, kind, appView, buildMethodCallback, emptyConsumer());
return DexClassAndMethod.create(clazz, methodDefinition);
}
- private DexEncodedMethod internalEnsureMethod(
+ private <T extends DexClassAndMethod> DexEncodedMethod internalEnsureMethod(
DexMethod methodReference,
DexClass clazz,
SyntheticKind kind,
AppView<?> appView,
- Consumer<SyntheticMethodBuilder> buildMethodCallback) {
+ Consumer<SyntheticMethodBuilder> buildMethodCallback,
+ Consumer<T> newMethodCallback) {
MethodCollection methodCollection = clazz.getMethodCollection();
DexEncodedMethod methodDefinition = methodCollection.getMethod(methodReference);
if (methodDefinition != null) {
@@ -667,6 +693,7 @@
// and the creation of the method code. The code can then be constructed outside the lock.
methodDefinition = builder.build();
methodCollection.addMethod(methodDefinition);
+ newMethodCallback.accept((T) DexClassAndMethod.create(clazz, methodDefinition));
return methodDefinition;
}
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
index c142281..edee559 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -9,6 +9,8 @@
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.DescriptorUtils;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
public class SyntheticNaming {
@@ -23,6 +25,7 @@
*/
public enum SyntheticKind {
// Class synthetics.
+ ENUM_UNBOXING_SHARED_UTILITY_CLASS("$EnumUnboxingSharedUtility", 24, false, true),
RECORD_TAG("", 1, false, true, true),
COMPANION_CLASS("$-CC", 2, false, true),
EMULATED_INTERFACE_CLASS("$-EL", 3, false, true),
@@ -48,6 +51,10 @@
SERVICE_LOADER("ServiceLoad", 18, true),
OUTLINE("Outline", 19, true);
+ static {
+ assert verifyNoOverlappingIds();
+ }
+
public final String descriptor;
public final int id;
public final boolean isSingleSyntheticMethod;
@@ -100,6 +107,16 @@
}
return null;
}
+
+ private static boolean verifyNoOverlappingIds() {
+ Int2ReferenceMap<SyntheticKind> idToKind = new Int2ReferenceOpenHashMap<>();
+ for (SyntheticKind kind : values()) {
+ SyntheticKind kindWithSameId = idToKind.put(kind.id, kind);
+ assert kindWithSameId == null
+ : "Synthetic kind " + idToKind + " has same id as " + kindWithSameId;
+ }
+ return true;
+ }
}
private static final String SYNTHETIC_CLASS_SEPARATOR = "$$";
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 7509f2a..e82e04d 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -45,7 +45,7 @@
import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses;
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
import com.android.tools.r8.ir.desugar.nest.Nest;
import com.android.tools.r8.ir.optimize.Inliner;
import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
diff --git a/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java b/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java
index f4115a4..5152dde 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/LongLivedProgramMethodSetBuilder.java
@@ -47,6 +47,14 @@
methods.forEach(this::add);
}
+ public void remove(DexMethod method) {
+ methods.remove(method);
+ }
+
+ public void removeAll(Iterable<DexMethod> methods) {
+ methods.forEach(this::remove);
+ }
+
public void rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLens applied) {
Set<DexMethod> newMethods = Sets.newIdentityHashSet();
for (DexMethod method : methods) {
diff --git a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
index 830047f..8791b27 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
@@ -104,9 +104,6 @@
if (!enableTreeShaking) {
command.add("-dontshrink");
}
- if (!enableOptimization) {
- command.add("-dontoptimize");
- }
if (!enableMinification) {
command.add("-dontobfuscate");
}
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 73abcee..88738f0 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -30,7 +30,6 @@
extends TestCompilerBuilder<C, B, CR, RR, T> {
protected boolean enableTreeShaking = true;
- protected boolean enableOptimization = true;
protected boolean enableMinification = true;
private final Set<Class<? extends Annotation>> addedTestingAnnotations =
@@ -74,15 +73,6 @@
return treeShaking(false);
}
- public T optimization(boolean enable) {
- enableOptimization = enable;
- return self();
- }
-
- public T noOptimization() {
- return optimization(false);
- }
-
public T minification(boolean enable) {
enableMinification = enable;
return self();
@@ -112,6 +102,10 @@
return addKeepRules(Arrays.asList(rules));
}
+ public T addDontOptimize() {
+ return addKeepRules("-dontoptimize");
+ }
+
public T addDontWarn(Class<?>... classes) {
for (Class<?> clazz : classes) {
addDontWarn(clazz.getTypeName());
@@ -330,6 +324,10 @@
return addKeepRules("-keepattributes " + String.join(",", attributes));
}
+ public T addKeepAttributeAnnotationDefault() {
+ return addKeepAttributes(ProguardKeepAttributes.ANNOTATION_DEFAULT);
+ }
+
public T addKeepAttributeExceptions() {
return addKeepAttributes(ProguardKeepAttributes.EXCEPTIONS);
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelDesugaredLibraryReferenceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelDesugaredLibraryReferenceTest.java
new file mode 100644
index 0000000..38f7b45
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelDesugaredLibraryReferenceTest.java
@@ -0,0 +1,88 @@
+// 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.apimodel;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.lang.reflect.Method;
+import java.time.Clock;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ApiModelDesugaredLibraryReferenceTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final boolean shrinkDesugaredLibrary;
+
+ @Parameters(name = "{0}, shrinkDesugaredLibrary: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDexRuntimes().withAllApiLevels().build(), BooleanUtils.values());
+ }
+
+ public ApiModelDesugaredLibraryReferenceTest(
+ TestParameters parameters, boolean shrinkDesugaredLibrary) {
+ this.parameters = parameters;
+ this.shrinkDesugaredLibrary = shrinkDesugaredLibrary;
+ }
+
+ @Test
+ public void testClockR8() throws Exception {
+ KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters);
+ Method printZone = CustomLibClass.class.getDeclaredMethod("printZone");
+ Method main = Executor.class.getDeclaredMethod("main", String[].class);
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Executor.class, CustomLibClass.class)
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.P))
+ .addKeepMainRule(Executor.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer)
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(
+ ApiModelingTestHelper.addTracedApiReferenceLevelCallBack(
+ (reference, apiLevel) -> {
+ if (reference.equals(Reference.methodFromMethod(printZone))) {
+ // TODO(b/191617445): This should probably always be parameters.getApiLevel()
+ assertEquals(AndroidApiLevel.O.max(parameters.getApiLevel()), apiLevel);
+ }
+ }))
+ .compile()
+ .addDesugaredCoreLibraryRunClassPath(
+ this::buildDesugaredLibrary,
+ parameters.getApiLevel(),
+ keepRuleConsumer.get(),
+ shrinkDesugaredLibrary)
+ .run(parameters.getRuntime(), Executor.class)
+ .assertSuccessWithOutputLines("Z")
+ // TODO(b/191617445): We should always be able to inline
+ .inspect(
+ ApiModelingTestHelper.verifyThat(parameters, printZone)
+ .inlinedIntoFromApiLevel(main, AndroidApiLevel.O));
+ }
+
+ static class Executor {
+
+ public static void main(String[] args) {
+ CustomLibClass.printZone();
+ }
+ }
+
+ static class CustomLibClass {
+
+ public static void printZone() {
+ System.out.println(Clock.systemUTC().getZone());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/SyntheticLambdaWithMissingInterfaceMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/SyntheticLambdaWithMissingInterfaceMergingTest.java
new file mode 100644
index 0000000..6f9a788
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/SyntheticLambdaWithMissingInterfaceMergingTest.java
@@ -0,0 +1,69 @@
+// 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.classmerging.horizontal;
+
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestShrinkerBuilder;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class SyntheticLambdaWithMissingInterfaceMergingTest extends TestBase {
+
+ @Parameter(0)
+ public boolean enableOptimization;
+
+ @Parameter(1)
+ public TestParameters parameters;
+
+ @Parameters(name = "{1}, optimize: {0}")
+ public static List<Object[]> parameters() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class, I.class)
+ .addDontWarn(J.class)
+ .addKeepClassAndMembersRules(Main.class)
+ .addHorizontallyMergedClassesInspector(
+ HorizontallyMergedClassesInspector::assertNoClassesMerged)
+ .applyIf(!enableOptimization, TestShrinkerBuilder::addDontOptimize)
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("I");
+ }
+
+ static class Main {
+ public static void main(String[] args) {
+ I i = () -> System.out.println("I");
+ i.m1();
+ }
+
+ static void dead() {
+ J j = () -> System.out.println("J");
+ j.m2();
+ }
+ }
+
+ interface I {
+ void m1();
+ }
+
+ interface J {
+ void m2();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/compatproguard/ForNameTest.java b/src/test/java/com/android/tools/r8/compatproguard/ForNameTest.java
index 9976c33..4b72ec5 100644
--- a/src/test/java/com/android/tools/r8/compatproguard/ForNameTest.java
+++ b/src/test/java/com/android/tools/r8/compatproguard/ForNameTest.java
@@ -42,7 +42,7 @@
.addKeepMainRule(CLASS_NAME)
// Add main dex rule to disable Class.forName() optimization.
.addMainDexRules("-keep class " + CLASS_NAME)
- .noOptimization()
+ .addDontOptimize()
.noTreeShaking()
.setMinApi(AndroidApiLevel.B));
@@ -78,7 +78,7 @@
.addKeepMainRule(CLASS_NAME)
// Add main dex rule to disable Class.forName() optimization.
.addMainDexRules("-keep class " + CLASS_NAME)
- .noOptimization()
+ .addDontOptimize()
.noMinification()
.noTreeShaking()
.setMinApi(AndroidApiLevel.B));
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarLambdaContextDuplicateInLibraryTest.java b/src/test/java/com/android/tools/r8/desugar/DesugarLambdaContextDuplicateInLibraryTest.java
new file mode 100644
index 0000000..98839fa
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/DesugarLambdaContextDuplicateInLibraryTest.java
@@ -0,0 +1,127 @@
+// 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;
+
+import static org.junit.Assert.assertTrue;
+
+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.references.Reference;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class DesugarLambdaContextDuplicateInLibraryTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("library string", "Hello", "world!");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ public DesugarLambdaContextDuplicateInLibraryTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private static final Class<?> MAIN = TestClass.class;
+ private static final Class<?> PROGRAM_CONTEXT = MAIN;
+ private static final Class<?> LIBRARY_CONTEXT = A.class;
+ private static final List<Class<?>> PROGRAM = ImmutableList.of(MAIN);
+ private static final List<Class<?>> LIBRARY =
+ ImmutableList.of(LibraryInterface.class, LIBRARY_CONTEXT);
+
+ private static final MethodReference pinnedPrintLn() throws Exception {
+ return Reference.methodFromMethod(
+ TestClass.class.getDeclaredMethod("println", LibraryInterface.class));
+ }
+
+ @Test
+ public void testOnlyProgram() throws Exception {
+ testForR8(parameters.getBackend())
+ .noMinification()
+ .addProgramClasses(PROGRAM)
+ .addProgramClasses(LIBRARY)
+ .addKeepMainRule(MAIN)
+ .addKeepMethodRules(pinnedPrintLn())
+ .setMinApi(parameters.getApiLevel())
+ .addHorizontallyMergedClassesInspector(
+ inspector -> {
+ // The regression test relies on the library type being the target. A change to
+ // synthetic sorting could change this order. If so, update this test by finding a
+ // new name to recover the order.
+ assertTrue(
+ inspector.getSources().stream()
+ .allMatch(t -> t.toDescriptorString().contains(binaryName(PROGRAM_CONTEXT))));
+ assertTrue(
+ inspector.getTargets().stream()
+ .allMatch(t -> t.toDescriptorString().contains(binaryName(LIBRARY_CONTEXT))));
+ })
+ .run(parameters.getRuntime(), MAIN)
+ .assertSuccessWithOutput(EXPECTED);
+ }
+
+ @Test
+ public void testDuplicate() throws Exception {
+ testForR8(parameters.getBackend())
+ .noMinification() // Don't minify so the name collision will happen.
+ .addProgramClasses(PROGRAM)
+ .addProgramClasses(LIBRARY)
+ .addLibraryClasses(LIBRARY)
+ .addDefaultRuntimeLibrary(parameters)
+ .addKeepMainRule(MAIN)
+ .addKeepMethodRules(pinnedPrintLn())
+ .setMinApi(parameters.getApiLevel())
+ // Use a checksum filter to simulate the classes being found on bootclasspath by removing
+ // then from the program output.
+ .setIncludeClassesChecksum(true)
+ .apply(
+ b ->
+ b.getBuilder()
+ .setDexClassChecksumFilter(
+ (desc, checksum) -> !desc.contains(binaryName(LIBRARY_CONTEXT))))
+ .compile()
+ .addRunClasspathClasses(LIBRARY)
+ .run(parameters.getRuntime(), MAIN)
+ .applyIf(
+ parameters.isCfRuntime(),
+ r -> r.assertSuccessWithOutput(EXPECTED),
+ // TODO(b/191747442): A library class and its derivatives should be pinned.
+ r -> r.assertFailure());
+ }
+
+ interface LibraryInterface {
+ String str();
+ }
+
+ // Library class with a synthetic. Named A to help it be ordered as the primary for merging.
+ static class A {
+
+ public static LibraryInterface getStrFn() {
+ // Ensure a static lambda is created in the library.
+ return () -> "library string";
+ }
+ }
+
+ static class TestClass {
+
+ static void println(LibraryInterface fn) {
+ System.out.println(fn.str());
+ }
+
+ public static void main(String[] args) {
+ println(A.getStrFn());
+ println(() -> "Hello");
+ println(() -> "world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
index e0f9136..ddebca5 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/BufferedReaderTest.java
@@ -10,8 +10,8 @@
import com.android.tools.r8.StringResource;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfigurationParser;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.InternalOptions;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java
index 348351b..2ef985e 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryConfigurationParsingTest.java
@@ -20,8 +20,8 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfigurationParser;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AbortException;
import com.android.tools.r8.utils.AndroidApiLevel;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryMismatchTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryMismatchTest.java
index 888436d..6eb1418 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryMismatchTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryMismatchTest.java
@@ -13,7 +13,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.errors.DesugaredLibraryMismatchDiagnostic;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import java.nio.file.Path;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
index 2414cf7..6453792 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DesugaredLibraryTestBase.java
@@ -22,8 +22,8 @@
import com.android.tools.r8.TestState;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfigurationParser;
import com.android.tools.r8.tracereferences.TraceReferences;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.FileUtils;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
index 9cb9cea..5c42b95 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
@@ -16,8 +16,8 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfigurationParser;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.MethodReference;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InconsistentPrefixTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InconsistentPrefixTest.java
index 84684da..43056e6 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InconsistentPrefixTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/InconsistentPrefixTest.java
@@ -10,7 +10,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
import com.android.tools.r8.jasmin.JasminBuilder;
import java.nio.file.Path;
import java.util.HashMap;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
index f5f2275..f253398 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
@@ -15,8 +15,8 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfigurationParser;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
index 213324c..b2973f5 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ObjectsTest.java
@@ -15,8 +15,8 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfigurationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfigurationParser;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.InternalOptions;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetAndBackportTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetAndBackportTest.java
index 40d658d..05fdd28 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetAndBackportTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/RetargetAndBackportTest.java
@@ -8,7 +8,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllTimeConversionTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllTimeConversionTest.java
index 1d79e26..143f18b 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllTimeConversionTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/conversiontests/AllTimeConversionTest.java
@@ -5,8 +5,9 @@
package com.android.tools.r8.desugar.desugaredlibrary.conversiontests;
import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertTrue;
+import static junit.framework.TestCase.fail;
+import com.android.tools.r8.Diagnostic;
import com.android.tools.r8.TestDiagnosticMessages;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ToolHelper;
@@ -114,22 +115,20 @@
}
private void assertTrackedAPIS(TestDiagnosticMessages diagnosticMessages) {
- assertTrue(
- diagnosticMessages
- .getWarnings()
- .get(0)
- .getDiagnosticMessage()
- .startsWith("Tracked desugared API conversions:"));
- assertEquals(
- 9, diagnosticMessages.getWarnings().get(0).getDiagnosticMessage().split("\n").length);
- assertTrue(
- diagnosticMessages
- .getWarnings()
- .get(1)
- .getDiagnosticMessage()
- .startsWith("Tracked callback desugared API conversions:"));
- assertEquals(
- 1, diagnosticMessages.getWarnings().get(1).getDiagnosticMessage().split("\n").length);
+ int trackedAPI = 0;
+ int trackedCallbackAPI = 0;
+ for (Diagnostic warning : diagnosticMessages.getWarnings()) {
+ String message = warning.getDiagnosticMessage();
+ if (message.startsWith("Tracked desugared API conversions:")) {
+ trackedAPI += message.split("\n").length - 1;
+ } else if (message.startsWith("Tracked callback desugared API conversions:")) {
+ trackedCallbackAPI += message.split("\n").length - 1;
+ } else {
+ fail();
+ }
+ }
+ assertEquals(8, trackedAPI);
+ assertEquals(0, trackedCallbackAPI);
}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/GenerateRecordMethods.java b/src/test/java/com/android/tools/r8/desugar/records/GenerateRecordMethods.java
index e3078a2..cbc7ef2 100644
--- a/src/test/java/com/android/tools/r8/desugar/records/GenerateRecordMethods.java
+++ b/src/test/java/com/android/tools/r8/desugar/records/GenerateRecordMethods.java
@@ -19,7 +19,7 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.desugar.RecordRewriter;
+import com.android.tools.r8.ir.desugar.records.RecordRewriter;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.FileUtils;
import com.google.common.collect.ImmutableList;
@@ -41,7 +41,7 @@
@RunWith(Parameterized.class)
public class GenerateRecordMethods extends MethodGenerationBase {
private final DexType GENERATED_TYPE =
- factory.createType("Lcom/android/tools/r8/ir/desugar/RecordCfMethods;");
+ factory.createType("Lcom/android/tools/r8/ir/desugar/records/RecordCfMethods;");
private final DexType RECORD_STUB_TYPE =
factory.createType(DescriptorUtils.javaTypeToDescriptor(RecordStub.class.getTypeName()));
private final List<Class<?>> METHOD_TEMPLATE_CLASSES = ImmutableList.of(RecordMethods.class);
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingEnumUnboxingTest.java
index e7b5dce..e77f42a 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingEnumUnboxingTest.java
@@ -9,8 +9,8 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.enumunboxing.examplelib1.JavaLibrary1;
-import com.android.tools.r8.ir.optimize.enums.SharedEnumUnboxingUtilityClass;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.nio.file.Path;
@@ -87,11 +87,9 @@
assertTrue(
codeInspector.allClasses().stream()
.anyMatch(
- c ->
- c.getOriginalName()
- .contains(
- SharedEnumUnboxingUtilityClass
- .ENUM_UNBOXING_SHARED_UTILITY_CLASS_SUFFIX)));
+ clazz ->
+ SyntheticItemsTestUtils.isEnumUnboxingSharedUtilityClass(
+ clazz.getOriginalReference())));
}
static class App {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingMergeEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingMergeEnumUnboxingTest.java
index 4ef092d..e7651fb 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingMergeEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/DoubleProcessingMergeEnumUnboxingTest.java
@@ -10,8 +10,8 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.enumunboxing.examplelib1.JavaLibrary1;
import com.android.tools.r8.enumunboxing.examplelib2.JavaLibrary2;
-import com.android.tools.r8.ir.optimize.enums.SharedEnumUnboxingUtilityClass;
import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import java.nio.file.Path;
@@ -92,11 +92,9 @@
assertTrue(
codeInspector.allClasses().stream()
.anyMatch(
- c ->
- c.getOriginalName()
- .contains(
- SharedEnumUnboxingUtilityClass
- .ENUM_UNBOXING_SHARED_UTILITY_CLASS_SUFFIX)));
+ clazz ->
+ SyntheticItemsTestUtils.isEnumUnboxingSharedUtilityClass(
+ clazz.getOriginalReference())));
}
static class App {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingB160535628Test.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingB160535628Test.java
index 2a8105f..19b4a26 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingB160535628Test.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingB160535628Test.java
@@ -52,7 +52,10 @@
.addEnumUnboxingInspector(
inspector ->
inspector
- .assertUnboxed(Lib.LibEnum.class)
+ // Without the studio keep rules, LibEnum.valueOf() is removed, which is
+ // used in this compilation, causing LibEnum to be ineligible for unboxing.
+ .assertUnboxedIf(
+ !missingStaticMethods || enumKeepRules.isStudio(), Lib.LibEnum.class)
.assertUnboxedIf(!missingStaticMethods, Lib.LibEnumStaticMethod.class))
.allowDiagnosticMessages()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumWithAssertionsDisabledStaticFieldTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumWithAssertionsDisabledStaticFieldTest.java
new file mode 100644
index 0000000..2f2dbc3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumWithAssertionsDisabledStaticFieldTest.java
@@ -0,0 +1,88 @@
+// 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.enumunboxing;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.AssertionsConfiguration.AssertionTransformation;
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.utils.BooleanUtils;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class EnumWithAssertionsDisabledStaticFieldTest extends TestBase {
+
+ @Parameter(0)
+ public AssertionTransformation assertionTransformation;
+
+ @Parameter(1)
+ public boolean enableRuntimeAssertions;
+
+ @Parameter(2)
+ public TestParameters parameters;
+
+ @Parameters(name = "{2}, transformation: {0}, -ea: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ AssertionTransformation.values(),
+ BooleanUtils.values(),
+ getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ @Test
+ public void test() throws Exception {
+ assumeTrue(parameters.isCfRuntime() || !enableRuntimeAssertions);
+ testForR8(parameters.getBackend())
+ .addProgramClasses(
+ EnumWithAssertionsDisabledStaticFieldMainClass.class,
+ EnumWithAssertionsDisabledStaticFieldEnumClass.class)
+ .addKeepMainRule(EnumWithAssertionsDisabledStaticFieldMainClass.class)
+ .addEnumUnboxingInspector(
+ inspector ->
+ inspector.assertUnboxed(EnumWithAssertionsDisabledStaticFieldEnumClass.class))
+ .addAssertionsConfiguration(
+ builder -> builder.setTransformation(assertionTransformation).setScopeAll().build())
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .enableRuntimeAssertions(enableRuntimeAssertions)
+ .run(parameters.getRuntime(), EnumWithAssertionsDisabledStaticFieldMainClass.class)
+ .applyIf(
+ assertionTransformation == AssertionTransformation.ENABLE
+ || (assertionTransformation == AssertionTransformation.PASSTHROUGH
+ && enableRuntimeAssertions),
+ result -> result.assertFailureWithErrorThatThrows(AssertionError.class),
+ TestRunResult::assertSuccessWithEmptyOutput);
+ }
+}
+
+// Intentionally added as a top-level class because the $assertionsDisabled field is always
+// synthesized on the outer-most class.
+class EnumWithAssertionsDisabledStaticFieldMainClass {
+
+ public static void main(String[] args) {
+ EnumWithAssertionsDisabledStaticFieldEnumClass.A.fail();
+ }
+}
+
+@NeverClassInline
+enum EnumWithAssertionsDisabledStaticFieldEnumClass {
+ A;
+
+ @NeverInline
+ public void fail() {
+ assert false;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumWithStaticFieldUnboxingTest.java
similarity index 87%
rename from src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingTest.java
rename to src/test/java/com/android/tools/r8/enumunboxing/EnumWithStaticFieldUnboxingTest.java
index e3df746..9e8e577 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/FailingEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumWithStaticFieldUnboxingTest.java
@@ -13,7 +13,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class FailingEnumUnboxingTest extends EnumUnboxingTestBase {
+public class EnumWithStaticFieldUnboxingTest extends EnumUnboxingTestBase {
private final TestParameters parameters;
private final boolean enumValueOptimization;
@@ -24,7 +24,7 @@
return enumUnboxingTestParameters();
}
- public FailingEnumUnboxingTest(
+ public EnumWithStaticFieldUnboxingTest(
TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules) {
this.parameters = parameters;
this.enumValueOptimization = enumValueOptimization;
@@ -34,13 +34,13 @@
@Test
public void testEnumUnboxingFailure() throws Exception {
testForR8(parameters.getBackend())
- .addInnerClasses(FailingEnumUnboxingTest.class)
+ .addInnerClasses(EnumWithStaticFieldUnboxingTest.class)
.addKeepMainRule(EnumStaticFieldMain.class)
.enableNeverClassInliningAnnotations()
.addKeepRules(enumKeepRules.getKeepRules())
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.addEnumUnboxingInspector(
- inspector -> inspector.assertNotUnboxed(EnumStaticFieldMain.EnumStaticField.class))
+ inspector -> inspector.assertUnboxed(EnumStaticFieldMain.EnumStaticField.class))
.setMinApi(parameters.getApiLevel())
.compile()
.run(parameters.getRuntime(), EnumStaticFieldMain.class)
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
index ec665d3..252b11d 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -212,6 +213,11 @@
static class PrimaryMethodProcessorMock extends MethodProcessorWithWave {
@Override
+ public MethodProcessingContext createMethodProcessingContext(ProgramMethod method) {
+ throw new Unreachable();
+ }
+
+ @Override
public boolean shouldApplyCodeRewritings(ProgramMethod method) {
return false;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
index 548ce4d..f8dcce6 100644
--- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
@@ -55,12 +55,12 @@
@Test
public void testAsIs() throws Exception {
- test(builder -> builder.noMinification().noOptimization().noTreeShaking());
+ test(builder -> builder.noMinification().addDontOptimize().noTreeShaking());
}
@Test
public void testDontShrinkAndDontOptimize() throws Exception {
- test(builder -> builder.noOptimization().noTreeShaking());
+ test(builder -> builder.addDontOptimize().noTreeShaking());
}
@Test
@@ -100,7 +100,7 @@
@Test
public void testDontOptimize() throws Exception {
- test(TestShrinkerBuilder::noOptimization);
+ test(TestShrinkerBuilder::addDontOptimize);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
index 598087a..c990029 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
@@ -98,7 +98,7 @@
.addDontWarn(PKG + ".**")
.allowDiagnosticWarningMessages()
// -dontoptimize so that basic code structure is kept.
- .noOptimization()
+ .addDontOptimize()
.compile()
.inspect(this::inspect)
.assertAllWarningMessagesMatch(
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java b/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java
index 3883528..06a87e4 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/b135627418/B135627418.java
@@ -12,7 +12,7 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryConfiguration;
import com.android.tools.r8.memberrebinding.b135627418.library.Drawable;
import com.android.tools.r8.memberrebinding.b135627418.library.DrawableWrapper;
import com.android.tools.r8.memberrebinding.b135627418.library.InsetDrawable;
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index 220bea7..b2380c3 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -36,6 +36,7 @@
import com.android.tools.r8.retrace.stacktraces.MemberFieldOverlapStackTrace;
import com.android.tools.r8.retrace.stacktraces.MultipleDotsInFileNameStackTrace;
import com.android.tools.r8.retrace.stacktraces.NamedModuleStackTrace;
+import com.android.tools.r8.retrace.stacktraces.NoObfuscatedLineNumberWithOverrideTest;
import com.android.tools.r8.retrace.stacktraces.NoObfuscationRangeMappingWithStackTrace;
import com.android.tools.r8.retrace.stacktraces.NullStackTrace;
import com.android.tools.r8.retrace.stacktraces.ObfucatedExceptionClassStackTrace;
@@ -258,6 +259,11 @@
runExperimentalRetraceTest(new SyntheticLambdaMethodWithInliningStackTrace());
}
+ @Test
+ public void testNoObfuscatedLineNumberWithOverrideTest() throws Exception {
+ runRetraceTest(new NoObfuscatedLineNumberWithOverrideTest());
+ }
+
private void inspectRetraceTest(
StackTraceForTest stackTraceForTest, Consumer<Retracer> inspection) {
inspection.accept(
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/NoObfuscatedLineNumberWithOverrideTest.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/NoObfuscatedLineNumberWithOverrideTest.java
new file mode 100644
index 0000000..3f11112
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/NoObfuscatedLineNumberWithOverrideTest.java
@@ -0,0 +1,51 @@
+// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.retrace.stacktraces;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+
+public class NoObfuscatedLineNumberWithOverrideTest implements StackTraceForTest {
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return Arrays.asList(
+ "Exception in thread \"main\" java.lang.NullPointerException",
+ "\tat com.android.tools.r8.naming.retrace.Main.main(Unknown Source)",
+ "\tat com.android.tools.r8.naming.retrace.Main.overload(Unknown Source)",
+ "\tat com.android.tools.r8.naming.retrace.Main.definedOverload(Unknown Source)",
+ "\tat com.android.tools.r8.naming.retrace.Main.mainPC(:3)");
+ }
+
+ @Override
+ public String mapping() {
+ return StringUtils.lines(
+ "com.android.tools.r8.naming.retrace.Main -> com.android.tools.r8.naming.retrace.Main:",
+ " void main(java.lang.String):3 -> main",
+ " void definedOverload():7 -> definedOverload",
+ " void definedOverload(java.lang.String):11 -> definedOverload",
+ " void overload1():7 -> overload",
+ " void overload2(java.lang.String):11 -> overload",
+ " void mainPC(java.lang.String[]):42 -> mainPC");
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return Arrays.asList(
+ "Exception in thread \"main\" java.lang.NullPointerException",
+ // TODO(b/191513686): Could be retrace to ...Main.main(Main.java:3)
+ "\tat com.android.tools.r8.naming.retrace.Main.main(Main.java)",
+ "\tat com.android.tools.r8.naming.retrace.Main.overload1(Main.java)",
+ "\t<OR> at com.android.tools.r8.naming.retrace.Main.overload2(Main.java)",
+ "\tat com.android.tools.r8.naming.retrace.Main.definedOverload(Main.java)",
+ "\tat com.android.tools.r8.naming.retrace.Main.mainPC(Main.java:42)");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/AlwaysRetainNonDefaultRetentionAnnotationTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/AlwaysRetainNonDefaultRetentionAnnotationTest.java
new file mode 100644
index 0000000..cfeb5c1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/AlwaysRetainNonDefaultRetentionAnnotationTest.java
@@ -0,0 +1,127 @@
+// Copyright (c) 2018, 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.shaking.annotations;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.google.common.collect.ImmutableList;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class AlwaysRetainNonDefaultRetentionAnnotationTest extends TestBase {
+
+ private final boolean enableProguardCompatibilityMode;
+ private final boolean keepAllowShrinking;
+ private final TestParameters parameters;
+
+ @Parameters(name = "{2}, compat: {0}, keep: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(),
+ BooleanUtils.values(),
+ getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ public AlwaysRetainNonDefaultRetentionAnnotationTest(
+ boolean enableProguardCompatibilityMode,
+ boolean keepAllowShrinking,
+ TestParameters parameters) {
+ this.enableProguardCompatibilityMode = enableProguardCompatibilityMode;
+ this.keepAllowShrinking = keepAllowShrinking;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ assumeTrue(!enableProguardCompatibilityMode || !keepAllowShrinking);
+ testForR8Compat(parameters.getBackend(), enableProguardCompatibilityMode)
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .addKeepRuntimeInvisibleAnnotations()
+ .addKeepRuntimeVisibleAnnotations()
+ .applyIf(
+ keepAllowShrinking,
+ builder -> {
+ assertFalse(enableProguardCompatibilityMode);
+ builder.addKeepRules(
+ "-keep,allowshrinking,allowobfuscation class "
+ + MyClassAnnotation.class.getTypeName());
+ builder.addKeepRules(
+ "-keep,allowshrinking,allowobfuscation class "
+ + MyRuntimeAnnotation.class.getTypeName());
+ })
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject classAnnotationClassSubject = inspector.clazz(MyClassAnnotation.class);
+ assertThat(classAnnotationClassSubject, isPresent());
+ assertThat(
+ classAnnotationClassSubject.annotation(Retention.class.getTypeName()),
+ onlyIf(isFullModeWithoutKeepRule(), isAbsent()));
+ assertThat(
+ classAnnotationClassSubject.annotation(Target.class.getTypeName()),
+ onlyIf(isFullModeWithoutKeepRule(), isAbsent()));
+ assertThat(
+ classAnnotationClassSubject.annotation(MyClassAnnotation.class.getTypeName()),
+ onlyIf(isFullModeWithoutKeepRule(), isAbsent()));
+
+ ClassSubject runtimeAnnotationClassSubject =
+ inspector.clazz(MyRuntimeAnnotation.class);
+ assertThat(runtimeAnnotationClassSubject, isPresent());
+ assertThat(
+ runtimeAnnotationClassSubject.annotation(Retention.class.getTypeName()),
+ isPresent());
+ assertThat(
+ runtimeAnnotationClassSubject.annotation(Target.class.getTypeName()),
+ onlyIf(isFullModeWithoutKeepRule(), isAbsent()));
+ assertThat(
+ runtimeAnnotationClassSubject.annotation(MyRuntimeAnnotation.class.getTypeName()),
+ onlyIf(isFullModeWithoutKeepRule(), isAbsent()));
+ })
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines(
+ isFullModeWithoutKeepRule() ? ImmutableList.of("0", "1") : ImmutableList.of("2", "3"));
+ }
+
+ private boolean isFullModeWithoutKeepRule() {
+ return !enableProguardCompatibilityMode && !keepAllowShrinking;
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ System.out.println(MyClassAnnotation.class.getAnnotations().length);
+ System.out.println(MyRuntimeAnnotation.class.getAnnotations().length);
+ }
+ }
+
+ @Retention(RetentionPolicy.CLASS)
+ @Target({ElementType.TYPE})
+ @MyClassAnnotation
+ @interface MyClassAnnotation {}
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.TYPE})
+ @MyRuntimeAnnotation
+ @interface MyRuntimeAnnotation {}
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/AlwaysRetainRetentionAnnotationTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/AlwaysRetainRetentionAnnotationTest.java
deleted file mode 100644
index 6e25e4ae..0000000
--- a/src/test/java/com/android/tools/r8/shaking/annotations/AlwaysRetainRetentionAnnotationTest.java
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright (c) 2018, 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.shaking.annotations;
-
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assume.assumeTrue;
-
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.shaking.enums.EnumInAnnotationTest.MyAnnotation;
-import com.android.tools.r8.utils.BooleanUtils;
-import com.android.tools.r8.utils.codeinspector.AnnotationSubject;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.util.List;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class AlwaysRetainRetentionAnnotationTest extends TestBase {
-
- private final boolean enableProguardCompatibilityMode;
- private final boolean keepAllowShrinking;
- private final TestParameters parameters;
-
- @Parameters(name = "{2}, compat: {0}, keep: {1}")
- public static List<Object[]> data() {
- return buildParameters(
- BooleanUtils.values(),
- BooleanUtils.values(),
- getTestParameters().withAllRuntimesAndApiLevels().build());
- }
-
- public AlwaysRetainRetentionAnnotationTest(
- boolean enableProguardCompatibilityMode,
- boolean keepAllowShrinking,
- TestParameters parameters) {
- this.enableProguardCompatibilityMode = enableProguardCompatibilityMode;
- this.keepAllowShrinking = keepAllowShrinking;
- this.parameters = parameters;
- }
-
- @Test
- public void test() throws Exception {
- assumeTrue(!enableProguardCompatibilityMode || !keepAllowShrinking);
- testForR8Compat(parameters.getBackend(), enableProguardCompatibilityMode)
- .addInnerClasses(getClass())
- .addKeepMainRule(TestClass.class)
- .addKeepRuntimeVisibleAnnotations()
- .applyIf(
- keepAllowShrinking,
- builder -> {
- assertFalse(enableProguardCompatibilityMode);
- builder.addKeepRules(
- "-keep,allowshrinking,allowobfuscation class "
- + MyAnnotation.class.getTypeName());
- })
- .setMinApi(parameters.getApiLevel())
- .compile()
- .inspect(
- inspector -> {
- ClassSubject annotationClassSubject = inspector.clazz(MyAnnotation.class);
- assertThat(annotationClassSubject, isPresent());
-
- AnnotationSubject retentionAnnotationSubject =
- annotationClassSubject.annotation(Retention.class.getTypeName());
- assertThat(retentionAnnotationSubject, isPresent());
-
- AnnotationSubject targetAnnotationSubject =
- annotationClassSubject.annotation(Target.class.getTypeName());
- assertThat(targetAnnotationSubject, onlyIf(shouldOnlyRetainRetention(), isAbsent()));
-
- AnnotationSubject myAnnotationAnnotationSubject =
- annotationClassSubject.annotation(MyAnnotation.class.getTypeName());
- assertThat(
- myAnnotationAnnotationSubject, onlyIf(shouldOnlyRetainRetention(), isAbsent()));
- })
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutputLines(shouldOnlyRetainRetention() ? "1" : "3");
- }
-
- private boolean shouldOnlyRetainRetention() {
- return !enableProguardCompatibilityMode && !keepAllowShrinking;
- }
-
- static class TestClass {
-
- public static void main(String[] args) {
- System.out.println(MyAnnotation.class.getAnnotations().length);
- }
- }
-
- @Retention(RetentionPolicy.RUNTIME)
- @Target({ElementType.TYPE})
- @MyAnnotation
- @interface MyAnnotation {}
-}
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/annotationdefault/DefaultValueForAnnotationWithExplicitValueShrinkingTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/annotationdefault/DefaultValueForAnnotationWithExplicitValueShrinkingTest.java
new file mode 100644
index 0000000..fecefb9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/annotationdefault/DefaultValueForAnnotationWithExplicitValueShrinkingTest.java
@@ -0,0 +1,83 @@
+// 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.shaking.annotations.annotationdefault;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class DefaultValueForAnnotationWithExplicitValueShrinkingTest extends TestBase {
+
+ @Parameter(0)
+ public boolean enableProguardCompatibilityMode;
+
+ @Parameter(1)
+ public TestParameters parameters;
+
+ @Parameters(name = "{1}, compat: {0}")
+ public static List<Object[]> params() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8Compat(parameters.getBackend(), enableProguardCompatibilityMode)
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addKeepAttributeAnnotationDefault()
+ .addKeepRuntimeVisibleAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .apply(
+ compileResult -> {
+ CodeInspector inspector = compileResult.inspector();
+
+ // MyAnnotation has a @Retention annotation and an @AnnotationDefault annotation.
+ // TODO(b/191741002): The default value for MyAnnotation.value() is unused, thus there
+ // is no need to retain the @AnnotationDefault annotation.
+ ClassSubject annotationClassSubject = inspector.clazz(MyAnnotation.class);
+ assertThat(annotationClassSubject, isPresent());
+ assertThat(
+ annotationClassSubject.annotation(Retention.class.getTypeName()), isPresent());
+ assertThat(
+ annotationClassSubject.annotation("dalvik.annotation.AnnotationDefault"),
+ isPresent());
+ assertEquals(2, annotationClassSubject.getDexProgramClass().annotations().size());
+
+ compileResult
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(Main.class.getTypeName());
+ });
+ }
+
+ @MyAnnotation(value = Main.class)
+ static class Main {
+ public static void main(String[] args) {
+ MyAnnotation myAnnotation = Main.class.getAnnotation(MyAnnotation.class);
+ System.out.println(myAnnotation.value().getName());
+ }
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface MyAnnotation {
+ Class<?> value() default Object.class;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/annotationdefault/DefaultValueForLiveButNotKeptAnnotationShrinkingTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/annotationdefault/DefaultValueForLiveButNotKeptAnnotationShrinkingTest.java
new file mode 100644
index 0000000..5f3bf9b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/annotationdefault/DefaultValueForLiveButNotKeptAnnotationShrinkingTest.java
@@ -0,0 +1,80 @@
+// 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.shaking.annotations.annotationdefault;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class DefaultValueForLiveButNotKeptAnnotationShrinkingTest extends TestBase {
+
+ @Parameter(0)
+ public boolean enableProguardCompatibilityMode;
+
+ @Parameter(1)
+ public TestParameters parameters;
+
+ @Parameters(name = "{1}, compat: {0}")
+ public static List<Object[]> params() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8Compat(parameters.getBackend(), enableProguardCompatibilityMode)
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addKeepClassAndMembersRules(MyAnnotation.class)
+ .addKeepAttributeAnnotationDefault()
+ .addKeepRuntimeVisibleAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .apply(
+ compileResult -> {
+ CodeInspector inspector = compileResult.inspector();
+
+ // MyAnnotation has a @Retention annotation and an @AnnotationDefault annotation.
+ ClassSubject annotationClassSubject = inspector.clazz(MyAnnotation.class);
+ assertThat(annotationClassSubject, isPresent());
+ assertThat(
+ annotationClassSubject.annotation(Retention.class.getTypeName()), isPresent());
+ assertThat(
+ annotationClassSubject.annotation("dalvik.annotation.AnnotationDefault"),
+ isPresent());
+ assertEquals(2, annotationClassSubject.getDexProgramClass().annotations().size());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(Object.class.getTypeName());
+ }
+
+ @MyAnnotation
+ static class Main {
+ public static void main(String[] args) {
+ MyAnnotation myAnnotation = Main.class.getAnnotation(MyAnnotation.class);
+ System.out.println(myAnnotation.value().getName());
+ }
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface MyAnnotation {
+ Class<?> value() default Object.class;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/annotationdefault/DefaultValueForUnusedAnnotationShrinkingTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/annotationdefault/DefaultValueForUnusedAnnotationShrinkingTest.java
new file mode 100644
index 0000000..9adafb6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/annotationdefault/DefaultValueForUnusedAnnotationShrinkingTest.java
@@ -0,0 +1,83 @@
+// 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.shaking.annotations.annotationdefault;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class DefaultValueForUnusedAnnotationShrinkingTest extends TestBase {
+
+ @Parameter(0)
+ public boolean enableProguardCompatibilityMode;
+
+ @Parameter(1)
+ public TestParameters parameters;
+
+ @Parameters(name = "{1}, compat: {0}")
+ public static List<Object[]> params() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build());
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8Compat(parameters.getBackend(), enableProguardCompatibilityMode)
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addKeepAttributeAnnotationDefault()
+ .addKeepRuntimeVisibleAnnotations()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .apply(
+ compileResult -> {
+ CodeInspector inspector = compileResult.inspector();
+
+ // MyAnnotation has a @Retention annotation and an @AnnotationDefault annotation.
+ // TODO(b/191741002): MyAnnotation.value() is unused, thus there is no need to retain
+ // the @AnnotationDefault annotation.
+ ClassSubject annotationClassSubject = inspector.clazz(MyAnnotation.class);
+ assertThat(annotationClassSubject, isPresent());
+ assertThat(
+ annotationClassSubject.annotation(Retention.class.getTypeName()), isPresent());
+ assertThat(
+ annotationClassSubject.annotation("dalvik.annotation.AnnotationDefault"),
+ isPresent());
+ assertEquals(2, annotationClassSubject.getDexProgramClass().annotations().size());
+
+ compileResult
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines(annotationClassSubject.getFinalName());
+ });
+ }
+
+ @MyAnnotation
+ static class Main {
+ public static void main(String[] args) {
+ MyAnnotation myAnnotation = Main.class.getAnnotation(MyAnnotation.class);
+ System.out.println(myAnnotation.annotationType().getName());
+ }
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface MyAnnotation {
+ Class<?> value() default Object.class;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
index dfcf7e8..1b4ec31 100644
--- a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
+++ b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -65,6 +65,11 @@
originalMethod.getMethodDescriptor());
}
+ public static boolean isEnumUnboxingSharedUtilityClass(ClassReference reference) {
+ return SyntheticNaming.isSynthetic(
+ reference, null, SyntheticKind.ENUM_UNBOXING_SHARED_UTILITY_CLASS);
+ }
+
public static boolean isExternalSynthetic(ClassReference reference) {
for (SyntheticKind kind : SyntheticKind.values()) {
if (kind == SyntheticKind.RECORD_TAG) {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java
index d554d50..c075406 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java
@@ -43,10 +43,14 @@
}
public EnumUnboxingInspector assertUnboxedIf(boolean condition, Class<? extends Enum<?>> clazz) {
+ return assertUnboxedIf(condition, clazz.getTypeName());
+ }
+
+ public EnumUnboxingInspector assertUnboxedIf(boolean condition, String className) {
if (condition) {
- assertUnboxed(clazz);
+ assertUnboxed(className);
} else {
- assertNotUnboxed(clazz);
+ assertNotUnboxed(className);
}
return this;
}
@@ -65,6 +69,13 @@
return this;
}
+ public EnumUnboxingInspector assertNotUnboxed(String typeName) {
+ assertFalse(
+ unboxedEnums.isUnboxedEnum(
+ dexItemFactory.createType(DescriptorUtils.javaTypeToDescriptor(typeName))));
+ return this;
+ }
+
@SafeVarargs
public final EnumUnboxingInspector assertNotUnboxed(Class<? extends Enum<?>>... classes) {
for (Class<? extends Enum<?>> clazz : classes) {
diff --git a/third_party/opensource-apps/tivi.tar.gz.sha1 b/third_party/opensource-apps/tivi.tar.gz.sha1
index 50f9741..dc3e882 100644
--- a/third_party/opensource-apps/tivi.tar.gz.sha1
+++ b/third_party/opensource-apps/tivi.tar.gz.sha1
@@ -1 +1 @@
-cde92f3abe4e6a10a6c7ec865d6240c7b625a3a2
\ No newline at end of file
+b5b44fb38064e69308e980fd33651ce03a0b1977
\ No newline at end of file
diff --git a/tools/chrome_data.py b/tools/chrome_data.py
index 7649257..0d4329d 100644
--- a/tools/chrome_data.py
+++ b/tools/chrome_data.py
@@ -278,3 +278,18 @@
},
},
}
+
+def GetLatestVersion():
+ return '200520-monochrome_public_minimal_apks'
+
+def GetName():
+ return 'chrome'
+
+def GetMemoryData(version):
+ assert version == '200520-monochrome_public_minimal_apks'
+ return {
+ 'find-xmx-min': 600,
+ 'find-xmx-max': 700,
+ 'find-xmx-range': 16,
+ 'oom-threshold': 625,
+ }
diff --git a/tools/compiledump.py b/tools/compiledump.py
index 192145b..46b09b8 100755
--- a/tools/compiledump.py
+++ b/tools/compiledump.py
@@ -62,7 +62,7 @@
default=False,
action='store_true')
parser.add_argument(
- '--printtimes',
+ '--print-times',
help='Print timing information from r8',
default=False,
action='store_true')
@@ -300,7 +300,7 @@
cmd.append('-Xmx' + args.xmx)
if args.ea:
cmd.append('-ea')
- if args.printtimes:
+ if args.print_times:
cmd.append('-Dcom.android.tools.r8.printtimes=1')
if hasattr(args, 'properties'):
cmd.extend(args.properties);
@@ -345,7 +345,7 @@
cmd.extend(otherargs)
utils.PrintCmd(cmd)
try:
- print(subprocess.check_output(cmd, stderr=subprocess.STDOUT))
+ print(subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode('utf-8'))
return 0
except subprocess.CalledProcessError as e:
if args.nolib \
diff --git a/tools/internal_test.py b/tools/internal_test.py
index 2571efc..cc2ee93 100755
--- a/tools/internal_test.py
+++ b/tools/internal_test.py
@@ -38,6 +38,9 @@
import utils
import run_on_app
+import chrome_data
+import iosched_data
+import r8_data
import youtube_data
# How often the bot/tester should check state
@@ -57,54 +60,18 @@
EXITCODE = 'exitcode'
TIMED_OUT = 'timed_out'
-BENCHMARK_APPS = [
- {
- 'app': 'r8',
- 'version': 'cf',
- 'find-xmx-min': 128,
- 'find-xmx-max': 400,
- 'find-xmx-range': 16,
- 'oom-threshold': 247,
- },
- {
- 'app': 'chrome',
- 'version': '180917',
- 'find-xmx-min': 256,
- 'find-xmx-max': 450,
- 'find-xmx-range': 16,
- 'oom-threshold': 340,
- },
- {
- 'app': 'youtube',
- 'version': youtube_data.LATEST_VERSION,
- 'find-xmx-min': 2800,
- 'find-xmx-max': 3200,
- 'find-xmx-range': 64,
- 'oom-threshold': 3000,
- # TODO(b/143431825): Youtube can OOM randomly in memory configurations
- # that should work.
- 'skip-find-xmx-max': True,
- },
- {
- 'app': 'iosched',
- 'version': '2019',
- 'find-xmx-min': 128,
- 'find-xmx-max': 1024,
- 'find-xmx-range': 16,
- # TODO(b/183371778): Figure out why the need to bump this
- 'oom-threshold': 329,
- },
-]
+BENCHMARK_APPS = [chrome_data, iosched_data, r8_data, youtube_data]
-def find_min_xmx_command(record):
+def find_min_xmx_command(app_data):
+ record = app_data.GetMemoryData(app_data.GetLatestVersion())
assert record['find-xmx-min'] < record['find-xmx-max']
assert record['find-xmx-range'] < record['find-xmx-max'] - record['find-xmx-min']
return [
'tools/run_on_app.py',
'--compiler=r8',
'--compiler-build=lib',
- '--app=%s' % record['app'],
- '--version=%s' % record['version'],
+ '--app=%s' % app_data.GetName(),
+ '--version=%s' % app_data.GetLatestVersion(),
'--no-debug',
'--no-build',
'--find-min-xmx',
@@ -113,27 +80,29 @@
'--find-min-xmx-range-size=%s' % record['find-xmx-range'],
'--find-min-xmx-archive']
-def compile_with_memory_max_command(record):
+def compile_with_memory_max_command(app_data):
# TODO(b/152939233): Remove this special handling when fixed.
- factor = 1.25 if record['app'] == 'chrome' else 1.15
+ factor = 1.25 if app_data.GetName() == 'chrome' else 1.15
+ record = app_data.GetMemoryData(app_data.GetLatestVersion())
return [] if 'skip-find-xmx-max' in record else [
'tools/run_on_app.py',
'--compiler=r8',
'--compiler-build=lib',
- '--app=%s' % record['app'],
- '--version=%s' % record['version'],
+ '--app=%s' % app_data.GetName(),
+ '--version=%s' % app_data.GetLatestVersion(),
'--no-debug',
'--no-build',
'--max-memory=%s' % int(record['oom-threshold'] * factor)
]
-def compile_with_memory_min_command(record):
+def compile_with_memory_min_command(app_data):
+ record = app_data.GetMemoryData(app_data.GetLatestVersion())
return [
'tools/run_on_app.py',
'--compiler=r8',
'--compiler-build=lib',
- '--app=%s' % record['app'],
- '--version=%s' % record['version'],
+ '--app=%s' % app_data.GetName(),
+ '--version=%s' % app_data.GetLatestVersion(),
'--no-debug',
'--no-build',
'--expect-oom',
@@ -176,16 +145,23 @@
default=False, action='store_true')
return result.parse_args()
-def get_own_file_content():
+def get_file_contents():
+ contents = []
with open(sys.argv[0], 'r') as us:
- return us.read()
+ contents.append(us.read())
+ for app_data in BENCHMARK_APPS:
+ with open(app_data.__file__, 'r') as us:
+ contents.append(us.read())
+ return contents
-def restart_if_new_version(original_content):
- new_content = get_own_file_content()
- log('Lengths %s %s' % (len(original_content), len(new_content)))
+def restart_if_new_version(original_contents):
+ new_contents = get_file_contents()
+ log('Lengths %s %s' % (
+ [len(data) for data in original_contents],
+ [len(data) for data in new_contents]))
log('is main %s ' % utils.is_main())
# Restart if the script got updated.
- if new_content != original_content:
+ if new_contents != original_contents:
log('Restarting tools/internal_test.py, content changed')
os.execv(sys.argv[0], sys.argv)
@@ -313,7 +289,7 @@
def run_continuously():
# If this script changes, we will restart ourselves
- own_content = get_own_file_content()
+ own_content = get_file_contents()
while True:
restart_if_new_version(own_content)
print_magic_file_state()
diff --git a/tools/iosched_data.py b/tools/iosched_data.py
index 5aac7b0..edd0663 100644
--- a/tools/iosched_data.py
+++ b/tools/iosched_data.py
@@ -176,3 +176,19 @@
},
},
}
+
+def GetLatestVersion():
+ return '2019'
+
+def GetName():
+ return 'iosched'
+
+def GetMemoryData(version):
+ assert version == '2019'
+ return {
+ 'find-xmx-min': 128,
+ 'find-xmx-max': 1024,
+ 'find-xmx-range': 16,
+ # TODO(b/183371778): Figure out why the need to bump this.
+ 'oom-threshold': 329,
+ }
diff --git a/tools/r8_data.py b/tools/r8_data.py
index c4c509f..6818af3 100644
--- a/tools/r8_data.py
+++ b/tools/r8_data.py
@@ -8,7 +8,7 @@
ANDROID_L_API = '21'
VERSIONS = {
- 'cf': {
+ '1.2.11-dev': {
'deploy': {
'inputs': [utils.PINNED_R8_JAR],
'pgconf': [os.path.join(utils.REPO_ROOT, 'src', 'main', 'keep.txt')],
@@ -22,3 +22,18 @@
}
}
}
+
+def GetLatestVersion():
+ return '1.2.11-dev'
+
+def GetName():
+ return 'r8'
+
+def GetMemoryData(version):
+ assert version == '1.2.11-dev'
+ return {
+ 'find-xmx-min': 128,
+ 'find-xmx-max': 400,
+ 'find-xmx-range': 16,
+ 'oom-threshold': 247,
+ }
diff --git a/tools/run_on_app_dump.py b/tools/run_on_app_dump.py
index 6acce42..30205f9 100755
--- a/tools/run_on_app_dump.py
+++ b/tools/run_on_app_dump.py
@@ -10,7 +10,7 @@
import gradle
import hashlib
import jdk
-import optparse
+import argparse
import os
import shutil
import sys
@@ -664,6 +664,7 @@
'config_file_consumer': remove_print_lines,
'properties': app.compiler_properties,
'disable_desugared_lib': False,
+ 'print_times': options.print_times,
})
app_jar = os.path.join(
@@ -843,103 +844,108 @@
def parse_options(argv):
- result = optparse.OptionParser()
- result.add_option('--app',
- help='What app to run on',
- choices=[app.name for app in APPS],
- action='append')
- result.add_option('--app-collection', '--app_collection',
- help='What app collection to run',
- choices=[collection.name for collection in APP_COLLECTIONS],
- action='append')
- result.add_option('--app-logging-filter', '--app_logging_filter',
- help='The apps for which to turn on logging',
- action='append')
- result.add_option('--bot',
- help='Running on bot, use third_party dependency.',
- default=False,
- action='store_true')
- result.add_option('--generate-golem-config', '--generate_golem_config',
- help='Generate a new config for golem.',
- default=False,
- action='store_true')
- result.add_option('--debug-agent',
- help='Enable Java debug agent and suspend compilation '
- '(default disabled)',
- default=False,
- action='store_true')
- result.add_option('--disable-assertions', '--disable_assertions',
- help='Disable assertions when compiling',
- default=False,
- action='store_true')
- result.add_option('--emulator-id', '--emulator_id',
- help='Id of the emulator to use',
- default='emulator-5554')
- result.add_option('--golem',
- help='Running on golem, do not download',
- default=False,
- action='store_true')
- result.add_option('--hash',
- help='The commit of R8 to use')
- result.add_option('--internal',
- help='Run internal apps if set, otherwise run opensource',
- default=False,
- action='store_true')
- result.add_option('--keystore',
- help='Path to app.keystore',
- default=os.path.join(utils.TOOLS_DIR, 'debug.keystore'))
- result.add_option('--keystore-password', '--keystore_password',
- help='Password for app.keystore',
- default='android')
- result.add_option('--monkey',
- help='Whether to install and run app(s) with monkey',
- default=False,
- action='store_true')
- result.add_option('--monkey-events', '--monkey_events',
- help='Number of events that the monkey should trigger',
- default=250,
- type=int)
- result.add_option('--no-build', '--no_build',
- help='Run without building ToT first (only when using ToT)',
- default=False,
- action='store_true')
- result.add_option('--no-logging', '--no_logging',
- help='Disable logging except for errors',
- default=False,
- action='store_true')
- result.add_option('--print-dexsegments',
- metavar='BENCHMARKNAME',
- help='Print the sizes of individual dex segments as ' +
- '\'<BENCHMARKNAME>-<APP>-<segment>(CodeSize): '
- '<bytes>\'')
- result.add_option('--print-runtimeraw',
- metavar='BENCHMARKNAME',
- help='Print the line \'<BENCHMARKNAME>(RunTimeRaw):' +
- ' <elapsed> ms\' at the end where <elapsed> is' +
- ' the elapsed time in milliseconds.')
- result.add_option('--quiet',
- help='Disable verbose logging',
- default=False,
- action='store_true')
- result.add_option('--r8-compilation-steps', '--r8_compilation_steps',
- help='Number of times R8 should be run on each app',
- default=2,
- type=int)
- result.add_option('--run-tests', '--run_tests',
- help='Whether to run instrumentation tests',
- default=False,
- action='store_true')
- result.add_option('--sign-apks', '--sign_apks',
- help='Whether the APKs should be signed',
- default=False,
- action='store_true')
- result.add_option('--shrinker',
- help='The shrinkers to use (by default, all are run)',
- action='append')
- result.add_option('--version',
- default='main',
- help='The version of R8 to use (e.g., 1.4.51)')
- (options, args) = result.parse_args(argv)
+ result = argparse.ArgumentParser(description = 'Run/compile dump artifacts.')
+ result.add_argument('--app',
+ help='What app to run on',
+ choices=[app.name for app in APPS],
+ action='append')
+ result.add_argument('--app-collection', '--app_collection',
+ help='What app collection to run',
+ choices=[collection.name for collection in
+ APP_COLLECTIONS],
+ action='append')
+ result.add_argument('--app-logging-filter', '--app_logging_filter',
+ help='The apps for which to turn on logging',
+ action='append')
+ result.add_argument('--bot',
+ help='Running on bot, use third_party dependency.',
+ default=False,
+ action='store_true')
+ result.add_argument('--generate-golem-config', '--generate_golem_config',
+ help='Generate a new config for golem.',
+ default=False,
+ action='store_true')
+ result.add_argument('--debug-agent',
+ help='Enable Java debug agent and suspend compilation '
+ '(default disabled)',
+ default=False,
+ action='store_true')
+ result.add_argument('--disable-assertions', '--disable_assertions',
+ help='Disable assertions when compiling',
+ default=False,
+ action='store_true')
+ result.add_argument('--emulator-id', '--emulator_id',
+ help='Id of the emulator to use',
+ default='emulator-5554')
+ result.add_argument('--golem',
+ help='Running on golem, do not download',
+ default=False,
+ action='store_true')
+ result.add_argument('--hash',
+ help='The commit of R8 to use')
+ result.add_argument('--internal',
+ help='Run internal apps if set, otherwise run opensource',
+ default=False,
+ action='store_true')
+ result.add_argument('--keystore',
+ help='Path to app.keystore',
+ default=os.path.join(utils.TOOLS_DIR, 'debug.keystore'))
+ result.add_argument('--keystore-password', '--keystore_password',
+ help='Password for app.keystore',
+ default='android')
+ result.add_argument('--monkey',
+ help='Whether to install and run app(s) with monkey',
+ default=False,
+ action='store_true')
+ result.add_argument('--monkey-events', '--monkey_events',
+ help='Number of events that the monkey should trigger',
+ default=250,
+ type=int)
+ result.add_argument('--no-build', '--no_build',
+ help='Run without building first (only when using ToT)',
+ default=False,
+ action='store_true')
+ result.add_argument('--no-logging', '--no_logging',
+ help='Disable logging except for errors',
+ default=False,
+ action='store_true')
+ result.add_argument('--print-times',
+ help='Print timing information from r8',
+ default=False,
+ action='store_true')
+ result.add_argument('--print-dexsegments',
+ metavar='BENCHMARKNAME',
+ help='Print the sizes of individual dex segments as ' +
+ '\'<BENCHMARKNAME>-<APP>-<segment>(CodeSize): '
+ '<bytes>\'')
+ result.add_argument('--print-runtimeraw',
+ metavar='BENCHMARKNAME',
+ help='Print the line \'<BENCHMARKNAME>(RunTimeRaw):' +
+ ' <elapsed> ms\' at the end where <elapsed> is' +
+ ' the elapsed time in milliseconds.')
+ result.add_argument('--quiet',
+ help='Disable verbose logging',
+ default=False,
+ action='store_true')
+ result.add_argument('--r8-compilation-steps', '--r8_compilation_steps',
+ help='Number of times R8 should be run on each app',
+ default=2,
+ type=int)
+ result.add_argument('--run-tests', '--run_tests',
+ help='Whether to run instrumentation tests',
+ default=False,
+ action='store_true')
+ result.add_argument('--sign-apks', '--sign_apks',
+ help='Whether the APKs should be signed',
+ default=False,
+ action='store_true')
+ result.add_argument('--shrinker',
+ help='The shrinkers to use (by default, all are run)',
+ action='append')
+ result.add_argument('--version',
+ default='main',
+ help='The version of R8 to use (e.g., 1.4.51)')
+ (options, args) = result.parse_known_args(argv)
if options.app or options.app_collection:
if not options.app:
diff --git a/tools/youtube_data.py b/tools/youtube_data.py
index 827041a..64bc995 100644
--- a/tools/youtube_data.py
+++ b/tools/youtube_data.py
@@ -115,3 +115,21 @@
}
},
}
+
+def GetLatestVersion():
+ return LATEST_VERSION
+
+def GetName():
+ return 'youtube'
+
+def GetMemoryData(version):
+ assert version == '16.20'
+ return {
+ 'find-xmx-min': 2800,
+ 'find-xmx-max': 3200,
+ 'find-xmx-range': 64,
+ 'oom-threshold': 3000,
+ # TODO(b/143431825): Youtube can OOM randomly in memory configurations
+ # that should work.
+ 'skip-find-xmx-max': True,
+ }