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..25cf31bc 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, + }