Merge commit '9ffdfb9a9dd243e5af5eb377ec7ff1efdc413707' into dev-release
diff --git a/.gitignore b/.gitignore index 07f90a7..885f76d 100644 --- a/.gitignore +++ b/.gitignore
@@ -231,6 +231,8 @@ third_party/r8mappings.tar.gz third_party/remapper third_party/remapper.tar.gz +third_party/retrace +third_party/retrace.tar.gz third_party/retrace_benchmark third_party/retrace_benchmark.tar.gz third_party/retrace_internal
diff --git a/build.gradle b/build.gradle index 8b0525f..08c9141 100644 --- a/build.gradle +++ b/build.gradle
@@ -346,6 +346,7 @@ "proguard/proguard5.2.1", "proguard/proguard6.0.1", "proguard/proguard-7.0.0", + "retrace/binary_compatibility", "r8", "r8-releases/2.0.74", "r8mappings",
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java index 91f6172..2ab471a 100644 --- a/src/main/java/com/android/tools/r8/R8.java +++ b/src/main/java/com/android/tools/r8/R8.java
@@ -473,7 +473,7 @@ // We can now remove visibility bridges. Note that we do not need to update the // invoke-targets here, as the existing invokes will simply dispatch to the now // visible super-method. MemberRebinding, if run, will then dispatch it correctly. - new VisibilityBridgeRemover(appView.withLiveness()).run(); + new VisibilityBridgeRemover(appView.withLiveness()).run(executorService); } } @@ -720,7 +720,7 @@ // Remove unneeded visibility bridges that have been inserted for member rebinding. // This can only be done if we have AppInfoWithLiveness. if (appView.appInfo().hasLiveness()) { - new VisibilityBridgeRemover(appView.withLiveness()).run(); + new VisibilityBridgeRemover(appView.withLiveness()).run(executorService); } else { // If we don't have AppInfoWithLiveness here, it must be because we are not shrinking. When // we are not shrinking, we can't move visibility bridges. In principle, though, it would be
diff --git a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java index fdec52c..ab9165b 100644 --- a/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java +++ b/src/main/java/com/android/tools/r8/cf/CfCodePrinter.java
@@ -12,6 +12,7 @@ import com.android.tools.r8.cf.code.CfCheckCast; import com.android.tools.r8.cf.code.CfCmp; import com.android.tools.r8.cf.code.CfConstClass; +import com.android.tools.r8.cf.code.CfConstDynamic; import com.android.tools.r8.cf.code.CfConstMethodHandle; import com.android.tools.r8.cf.code.CfConstMethodType; import com.android.tools.r8.cf.code.CfConstNull; @@ -388,6 +389,12 @@ } @Override + public void print(CfConstDynamic constDynamic) { + // TODO(b/198143561): Support CfConstDynamic. + throw new Unimplemented(constDynamic.getClass().getSimpleName()); + } + + @Override public void print(CfReturnVoid ret) { printNewInstruction("CfReturnVoid"); }
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 c6ba520..480c9cb 100644 --- a/src/main/java/com/android/tools/r8/cf/CfPrinter.java +++ b/src/main/java/com/android/tools/r8/cf/CfPrinter.java
@@ -10,6 +10,7 @@ import com.android.tools.r8.cf.code.CfCheckCast; import com.android.tools.r8.cf.code.CfCmp; import com.android.tools.r8.cf.code.CfConstClass; +import com.android.tools.r8.cf.code.CfConstDynamic; import com.android.tools.r8.cf.code.CfConstMethodHandle; import com.android.tools.r8.cf.code.CfConstMethodType; import com.android.tools.r8.cf.code.CfConstNull; @@ -326,6 +327,12 @@ appendType(constClass.getType()); } + public void print(CfConstDynamic constDynamic) { + indent(); + builder.append("ldc <dynamic> "); + appendType(constDynamic.getType()); + } + public void print(CfInitClass initClass) { indent(); builder.append("initclass ");
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java b/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java new file mode 100644 index 0000000..e6e1f0d --- /dev/null +++ b/src/main/java/com/android/tools/r8/cf/code/CfConstDynamic.java
@@ -0,0 +1,232 @@ +// 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.cf.code; + +import static com.android.tools.r8.graph.UseRegistry.MethodHandleUse.NOT_ARGUMENT_TO_LAMBDA_METAFACTORY; + +import com.android.tools.r8.cf.CfPrinter; +import com.android.tools.r8.errors.CompilationError; +import com.android.tools.r8.errors.Unimplemented; +import com.android.tools.r8.errors.Unreachable; +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.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.DexMethod; +import com.android.tools.r8.graph.DexMethodHandle; +import com.android.tools.r8.graph.DexString; +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.JarApplicationReader; +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.desugar.constantdynamic.ConstantDynamicReference; +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.ConstantDynamic; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +public class CfConstDynamic extends CfInstruction implements CfTypeInstruction { + + private final ConstantDynamicReference reference; + + public CfConstDynamic( + DexString name, + DexType type, + DexMethodHandle bootstrapMethod, + Object[] bootstrapMethodArguments) { + assert name != null; + assert type != null; + assert bootstrapMethod != null; + assert bootstrapMethodArguments != null; + assert bootstrapMethodArguments.length == 0; + + reference = new ConstantDynamicReference(name, type, bootstrapMethod, bootstrapMethodArguments); + } + + @Override + public CfConstDynamic asConstDynamic() { + return this; + } + + @Override + public boolean isConstDynamic() { + return true; + } + + public ConstantDynamicReference getReference() { + return reference; + } + + public DexString getName() { + return reference.getName(); + } + + public DexMethodHandle getBootstrapMethod() { + return reference.getBootstrapMethod(); + } + + public Object[] getBootstrapMethodArguments() { + return reference.getBootstrapMethodArguments(); + } + + public static CfConstDynamic fromAsmConstantDynamic( + ConstantDynamic insn, JarApplicationReader application, DexType clazz) { + String constantName = insn.getName(); + String constantDescriptor = insn.getDescriptor(); + // TODO(b/178172809): Handle bootstrap arguments. + if (insn.getBootstrapMethodArgumentCount() > 0) { + throw new CompilationError( + "Unsupported dynamic constant (has arguments to bootstrap method)"); + } + if (insn.getBootstrapMethod().getTag() != Opcodes.H_INVOKESTATIC) { + throw new CompilationError("Unsupported dynamic constant (not invoke static)"); + } + if (insn.getBootstrapMethod().getOwner().equals("java/lang/invoke/ConstantBootstraps")) { + throw new CompilationError( + "Unsupported dynamic constant (runtime provided bootstrap method)"); + } + if (application.getTypeFromName(insn.getBootstrapMethod().getOwner()) != clazz) { + throw new CompilationError("Unsupported dynamic constant (different owner)"); + } + // Resolve the bootstrap method. + DexMethodHandle bootstrapMethodHandle = + DexMethodHandle.fromAsmHandle(insn.getBootstrapMethod(), application, clazz); + if (!bootstrapMethodHandle.member.isDexMethod()) { + throw new CompilationError("Unsupported dynamic constant (invalid method handle)"); + } + DexMethod bootstrapMethod = bootstrapMethodHandle.asMethod(); + if (bootstrapMethod.getProto().returnType != application.getTypeFromDescriptor("[Z") + && bootstrapMethod.getProto().returnType + != application.getTypeFromDescriptor("Ljava/lang/Object;")) { + throw new CompilationError("Unsupported dynamic constant (unsupported constant type)"); + } + if (bootstrapMethod.getProto().getParameters().size() != 3) { + throw new CompilationError("Unsupported dynamic constant (unsupported signature)"); + } + if (bootstrapMethod.getProto().getParameters().get(0) != application.getFactory().lookupType) { + throw new CompilationError( + "Unsupported dynamic constant (unexpected type of first argument to bootstrap method"); + } + if (bootstrapMethod.getProto().getParameters().get(1) != application.getFactory().stringType) { + throw new CompilationError( + "Unsupported dynamic constant (unexpected type of second argument to bootstrap method"); + } + if (bootstrapMethod.getProto().getParameters().get(2) != application.getFactory().classType) { + throw new CompilationError( + "Unsupported dynamic constant (unexpected type of third argument to bootstrap method"); + } + return new CfConstDynamic( + application.getString(constantName), + application.getTypeFromDescriptor(constantDescriptor), + bootstrapMethodHandle, + new Object[] {}); + } + + @Override + public int getCompareToId() { + return CfCompareHelper.CONST_DYNAMIC_COMPARE_ID; + } + + @Override + public int internalAcceptCompareTo( + CfInstruction other, CompareToVisitor visitor, CfCompareHelper helper) { + int diff = getName().acceptCompareTo(((CfConstDynamic) other).getName(), visitor); + if (diff != 0) { + return diff; + } + diff = getType().acceptCompareTo(((CfConstDynamic) other).getType(), visitor); + if (diff != 0) { + return diff; + } + return getBootstrapMethod() + .acceptCompareTo(((CfConstDynamic) other).getBootstrapMethod(), visitor); + } + + @Override + public CfTypeInstruction asTypeInstruction() { + return this; + } + + @Override + public boolean isTypeInstruction() { + return true; + } + + @Override + public DexType getType() { + return reference.getType(); + } + + @Override + public CfInstruction withType(DexType newType) { + throw new Unimplemented(); + } + + @Override + public void write( + AppView<?> appView, + ProgramMethod context, + DexItemFactory dexItemFactory, + GraphLens graphLens, + InitClassLens initClassLens, + NamingLens namingLens, + LensCodeRewriterUtils rewriter, + MethodVisitor visitor) { + // TODO(b/198142625): Support CONSTANT_Dynamic for R8 cf to cf. + throw new CompilationError("Unsupported dynamic constant (not desugaring)"); + } + + @Override + public void print(CfPrinter printer) { + printer.print(this); + } + + @Override + public boolean canThrow() { + return true; + } + + @Override + void internalRegisterUse( + UseRegistry registry, DexClassAndMethod context, ListIterator<CfInstruction> iterator) { + registry.registerTypeReference(reference.getType()); + registry.registerMethodHandle( + reference.getBootstrapMethod(), NOT_ARGUMENT_TO_LAMBDA_METAFACTORY); + assert reference.getBootstrapMethodArguments().length == 0; + } + + @Override + public void buildIR(IRBuilder builder, CfState state, CfSourceCode code) { + throw new CompilationError("Unsupported dynamic constant (not desugaring)"); + } + + @Override + public ConstraintWithTarget inliningConstraint( + InliningConstraints inliningConstraints, CfCode code, ProgramMethod context) { + throw new Unreachable(); + } + + @Override + public void evaluate( + CfFrameVerificationHelper frameBuilder, + DexType context, + DexType returnType, + DexItemFactory factory, + InitClassLens initClassLens) { + // ... → + // ..., value + frameBuilder.push(factory.classType); + } +}
diff --git a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java index 6067cc3..188c575 100644 --- a/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java +++ b/src/main/java/com/android/tools/r8/cf/code/CfInstruction.java
@@ -118,6 +118,14 @@ return false; } + public CfConstDynamic asConstDynamic() { + return null; + } + + public boolean isConstDynamic() { + return false; + } + public CfFieldInstruction asFieldInstruction() { return null; }
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java index c8b99a4..dc8e0bb 100644 --- a/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java +++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithClassHierarchy.java
@@ -465,7 +465,7 @@ } assert potentialHolder.isInterface(); for (DexEncodedMethod virtualMethod : potentialHolder.virtualMethods()) { - if (virtualMethod.getReference().hasSameProtoAndName(method.getReference()) + if (virtualMethod.getReference().match(method.getReference()) && virtualMethod.accessFlags.isSameVisibility(method.accessFlags)) { return true; }
diff --git a/src/main/java/com/android/tools/r8/graph/CfCompareHelper.java b/src/main/java/com/android/tools/r8/graph/CfCompareHelper.java index 1108c72..d3c3351 100644 --- a/src/main/java/com/android/tools/r8/graph/CfCompareHelper.java +++ b/src/main/java/com/android/tools/r8/graph/CfCompareHelper.java
@@ -25,6 +25,7 @@ public static final int CONST_NUMBER_COMPARE_ID; public static final int CONST_METHOD_TYPE_COMPARE_ID; public static final int CONST_METHOD_HANDLE_COMPARE_ID; + public static final int CONST_DYNAMIC_COMPARE_ID; public static final int FRAME_COMPARE_ID; public static final int INIT_CLASS_COMPARE_ID; public static final int LABEL_COMPARE_ID; @@ -38,6 +39,7 @@ CONST_NUMBER_COMPARE_ID = ++lastId; CONST_METHOD_TYPE_COMPARE_ID = ++lastId; CONST_METHOD_HANDLE_COMPARE_ID = ++lastId; + CONST_DYNAMIC_COMPARE_ID = ++lastId; FRAME_COMPARE_ID = ++lastId; INIT_CLASS_COMPARE_ID = ++lastId; LABEL_COMPARE_ID = ++lastId;
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 2b8a186..5efaba3 100644 --- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java +++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -374,6 +374,10 @@ this.genericSignature = FieldTypeSignature.noSignature(); } + public static Builder builder() { + return new Builder(); + } + private static Builder builder(DexEncodedField from) { return new Builder(from); } @@ -381,16 +385,18 @@ public static class Builder { private DexField field; - private DexAnnotationSet annotations; + private DexAnnotationSet annotations = DexAnnotationSet.empty(); private FieldAccessFlags accessFlags; - private FieldTypeSignature genericSignature; + private FieldTypeSignature genericSignature = FieldTypeSignature.noSignature(); private DexValue staticValue; private AndroidApiLevel apiLevel; - private FieldOptimizationInfo optimizationInfo; + private FieldOptimizationInfo optimizationInfo = DefaultFieldOptimizationInfo.getInstance(); private boolean deprecated; private boolean d8R8Synthesized; private Consumer<DexEncodedField> buildConsumer = ConsumerUtils.emptyConsumer(); + Builder() {} + Builder(DexEncodedField from) { // Copy all the mutable state of a DexEncodedField here. field = from.getReference(); @@ -445,7 +451,27 @@ return this; } - DexEncodedField build() { + public Builder setAccessFlags(FieldAccessFlags accessFlags) { + this.accessFlags = accessFlags; + return this; + } + + public Builder setD8R8Synthesized() { + this.d8R8Synthesized = true; + return this; + } + + public Builder setApiLevel(AndroidApiLevel apiLevel) { + this.apiLevel = apiLevel; + return this; + } + + public DexEncodedField build() { + assert field != null; + assert accessFlags != null; + assert genericSignature != null; + assert annotations != null; + assert apiLevel != null; DexEncodedField dexEncodedField = new DexEncodedField( field,
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 0243703..6cbb223 100644 --- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java +++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -166,8 +166,6 @@ /** Generic signature information if the attribute is present in the input */ private MethodTypeSignature genericSignature; - private DexEncodedMethod defaultInterfaceMethodImplementation = null; - private OptionalBool isLibraryMethodOverride = OptionalBool.unknown(); private Int2ReferenceMap<DebugLocalInfo> parameterInfo = NO_PARAMETER_INFO; @@ -211,21 +209,6 @@ return compilationState; } - public DexEncodedMethod getDefaultInterfaceMethodImplementation() { - return defaultInterfaceMethodImplementation; - } - - public void setDefaultInterfaceMethodImplementation(DexEncodedMethod implementation) { - assert defaultInterfaceMethodImplementation == null; - assert implementation != null; - assert code != null; - // 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; - } - /** * Flags this method as no longer being obsolete. *
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java index 5eaf805..c2eeffa 100644 --- a/src/main/java/com/android/tools/r8/graph/DexMethod.java +++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -218,14 +218,6 @@ return false; } - /** - * Returns true if the other method has the same name and prototype (including signature and - * return type), false otherwise. - */ - public boolean hasSameProtoAndName(DexMethod other) { - return name == other.name && proto == other.proto; - } - @Override public boolean match(DexMethod method) { return match(method.getProto(), method.getName());
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessFlags.java b/src/main/java/com/android/tools/r8/graph/FieldAccessFlags.java index 850c3cb..29e68bf 100644 --- a/src/main/java/com/android/tools/r8/graph/FieldAccessFlags.java +++ b/src/main/java/com/android/tools/r8/graph/FieldAccessFlags.java
@@ -71,6 +71,11 @@ Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC); } + public static FieldAccessFlags createPrivateStaticSynthetic() { + return fromSharedAccessFlags( + Constants.ACC_PRIVATE | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC); + } + public static FieldAccessFlags createPublicFinalSynthetic() { return fromSharedAccessFlags( Constants.ACC_PUBLIC | Constants.ACC_FINAL | Constants.ACC_SYNTHETIC);
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java index 37dea58..a81d742 100644 --- a/src/main/java/com/android/tools/r8/graph/GraphLens.java +++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -8,7 +8,6 @@ import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.ir.code.Invoke.Type; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor.InterfaceProcessorNestedGraphLens; import com.android.tools.r8.optimize.MemberRebindingIdentityLens; import com.android.tools.r8.optimize.MemberRebindingLens; import com.android.tools.r8.shaking.KeepInfoCollection; @@ -480,14 +479,6 @@ return null; } - public boolean isInterfaceProcessorLens() { - return false; - } - - public InterfaceProcessorNestedGraphLens asInterfaceProcessorLens() { - return null; - } - public GraphLens withCodeRewritingsApplied(DexItemFactory dexItemFactory) { if (hasCodeRewritings()) { return new ClearCodeRewritingGraphLens(dexItemFactory, this);
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java index 55482fc..2f052aa 100644 --- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java +++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -516,10 +516,6 @@ if (!accessFlags.isRecord()) { return; } - // TODO(b/169645628): Support records in all compilation. - if (!application.options.enableExperimentalRecordDesugaring()) { - throw new CompilationError("Records are not supported", origin); - } // TODO(b/169645628): Change this logic if we start stripping the record components. // Another approach would be to mark a bit in fields that are record components instead. String message = "Records are expected to have one record component per instance field.";
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java index 174dca5..c554ca5 100644 --- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java +++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -10,6 +10,7 @@ import com.android.tools.r8.cf.code.CfCheckCast; import com.android.tools.r8.cf.code.CfCmp; import com.android.tools.r8.cf.code.CfConstClass; +import com.android.tools.r8.cf.code.CfConstDynamic; import com.android.tools.r8.cf.code.CfConstMethodHandle; import com.android.tools.r8.cf.code.CfConstMethodType; import com.android.tools.r8.cf.code.CfConstNull; @@ -887,7 +888,9 @@ new CfConstMethodHandle( DexMethodHandle.fromAsmHandle((Handle) cst, application, method.holder))); } else if (cst instanceof ConstantDynamic) { - throw new CompilationError("Unsupported dynamic constant: " + cst.toString()); + instructions.add( + CfConstDynamic.fromAsmConstantDynamic( + (ConstantDynamic) cst, application, method.holder)); } else { throw new CompilationError("Unsupported constant: " + cst.toString()); }
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 7f09192..459c008 100644 --- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java +++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
@@ -534,7 +534,8 @@ private boolean verifyAllSuperTypesAreInHierarchy( DexDefinitionSupplier definitions, Iterable<DexType> dexTypes) { for (DexType supertype : dexTypes) { - assert typeIsInHierarchy(definitions, supertype); + assert typeIsInHierarchy(definitions, supertype) + : "Type not found in hierarchy: " + supertype; } return true; }
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 c00f43c..8f6f293 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
@@ -4,7 +4,6 @@ package com.android.tools.r8.ir.conversion; import static com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor.ExcludeDexResources; -import static com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor.IncludeAllResources; import static com.android.tools.r8.ir.desugar.lambda.D8LambdaDesugaring.rewriteEnclosingLambdaMethodAttributes; import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext; @@ -58,7 +57,6 @@ import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceApplicationRewriter; import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade; 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; import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring; import com.android.tools.r8.ir.optimize.AssertionsRewriter; @@ -362,23 +360,6 @@ } } - private void finalizeInterfaceMethodRewritingThroughIR(ExecutorService executorService) - throws ExecutionException { - assert !appView.getSyntheticItems().hasPendingSyntheticClasses(); - if (interfaceMethodRewriter != null) { - interfaceMethodRewriter.finalizeInterfaceMethodRewritingThroughIR(this, executorService); - } - } - - private void runInterfaceDesugaringProcessorsForR8( - Flavor includeAllResources, ExecutorService executorService) throws ExecutionException { - assert !appView.getSyntheticItems().hasPendingSyntheticClasses(); - if (interfaceMethodRewriter != null) { - interfaceMethodRewriter.runInterfaceDesugaringProcessorsForR8( - this, includeAllResources, executorService); - } - } - private void processCovariantReturnTypeAnnotations(Builder<?> builder) { if (covariantReturnTypeAnnotationTransformer != null) { covariantReturnTypeAnnotationTransformer.process(builder); @@ -460,10 +441,11 @@ CfPostProcessingDesugaringEventConsumer.createForD8(methodProcessor, instructionDesugaring); methodProcessor.newWave(); InterfaceMethodProcessorFacade interfaceDesugaring = - instructionDesugaring.getInterfaceMethodPostProcessingDesugaring(ExcludeDexResources); + instructionDesugaring.getInterfaceMethodPostProcessingDesugaringD8(ExcludeDexResources); CfPostProcessingDesugaringCollection.create( appView, interfaceDesugaring, instructionDesugaring.getRetargetingInfo()) - .postProcessingDesugaring(appView.appInfo().classes(), eventConsumer, executorService); + .postProcessingDesugaring( + appView.appInfo().classes(), m -> true, eventConsumer, executorService); methodProcessor.awaitMethodProcessing(); eventConsumer.finalizeDesugaring(); } @@ -789,11 +771,6 @@ Builder<?> builder = appView.appInfo().app().builder(); builder.setHighestSortingString(highestSortingString); - printPhase("Interface method desugaring"); - finalizeInterfaceMethodRewritingThroughIR(executorService); - runInterfaceDesugaringProcessorsForR8(IncludeAllResources, executorService); - feedback.updateVisibleOptimizationInfo(); - if (serviceLoaderRewriter != null) { processSynthesizedServiceLoaderMethods( serviceLoaderRewriter.getServiceLoadMethods(), executorService); @@ -1466,14 +1443,6 @@ previous = printMethod(code, "IR after class inlining (SSA)", previous); - // TODO(b/183998768): Enable interface method rewriter cf to cf also in R8. - if (interfaceMethodRewriter != null && appView.enableWholeProgramOptimizations()) { - timing.begin("Rewrite interface methods"); - interfaceMethodRewriter.rewriteMethodReferences( - code, methodProcessor, methodProcessingContext); - timing.end(); - } - assert code.verifyTypes(appView); previous = printMethod(code, "IR after interface method rewriting (SSA)", previous); @@ -1504,12 +1473,15 @@ timing.begin("Canonicalize constants"); constantCanonicalizer.canonicalize(appView, code); timing.end(); + previous = printMethod(code, "IR after constant canonicalization (SSA)", previous); timing.begin("Create constants for literal instructions"); codeRewriter.useDedicatedConstantForLitInstruction(code); timing.end(); + previous = printMethod(code, "IR after constant literals (SSA)", previous); timing.begin("Shorten live ranges"); codeRewriter.shortenLiveRanges(code); timing.end(); + previous = printMethod(code, "IR after shorten live ranges (SSA)", previous); } timing.begin("Canonicalize idempotent calls");
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 837d5fc..73f0fe5 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
@@ -707,9 +707,8 @@ DexType oldType, DexType newType, Value initialValue) { - if (initialValue.isConstNumber() - && initialValue.definition.asConstNumber().isZero() - && defaultValueHasChanged(oldType, newType)) { + if (initialValue.getType().isNullType() && defaultValueHasChanged(oldType, newType)) { + assert newType.isIntType(); iterator.previous(); Value rewrittenDefaultValue = iterator.insertConstNumberInstruction(
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 10032bc..5d4f7b6 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
@@ -11,9 +11,11 @@ import com.android.tools.r8.ir.desugar.desugaredlibrary.RetargetingInfo; import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade; import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor; +import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor; import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring; import com.android.tools.r8.utils.ThrowingConsumer; import java.util.function.Consumer; +import java.util.function.Predicate; /** * Abstracts a collection of low-level desugarings (i.e., mappings from class-file instructions to @@ -60,9 +62,12 @@ public abstract <T extends Throwable> void withD8NestBasedAccessDesugaring( ThrowingConsumer<D8NestBasedAccessDesugaring, T> consumer) throws T; - public abstract InterfaceMethodProcessorFacade getInterfaceMethodPostProcessingDesugaring( + public abstract InterfaceMethodProcessorFacade getInterfaceMethodPostProcessingDesugaringD8( Flavor flavor); + public abstract InterfaceMethodProcessorFacade getInterfaceMethodPostProcessingDesugaringR8( + Flavor flavor, Predicate<ProgramMethod> isLiveMethod, InterfaceProcessor processor); + public abstract RetargetingInfo getRetargetingInfo(); public abstract void withDesugaredLibraryAPIConverter(
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 4f6dd6c..2493dd3 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
@@ -14,6 +14,8 @@ 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.constantdynamic.ConstantDynamicClass; +import com.android.tools.r8.ir.desugar.constantdynamic.ConstantDynamicDesugaringEventConsumer; import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterInstructionEventConsumer; import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryAPIConverterEventConsumer; import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialBridgeInfo; @@ -45,6 +47,7 @@ implements BackportedMethodDesugaringEventConsumer, InvokeSpecialToSelfDesugaringEventConsumer, LambdaDesugaringEventConsumer, + ConstantDynamicDesugaringEventConsumer, NestBasedAccessDesugaringEventConsumer, RecordInstructionDesugaringEventConsumer, TwrCloseResourceDesugaringEventConsumer, @@ -61,16 +64,32 @@ public static R8CfInstructionDesugaringEventConsumer createForR8( AppView<? extends AppInfoWithClassHierarchy> appView, BiConsumer<LambdaClass, ProgramMethod> lambdaClassConsumer, + BiConsumer<ConstantDynamicClass, ProgramMethod> constantDynamicClassConsumer, BiConsumer<ProgramMethod, ProgramMethod> twrCloseResourceMethodConsumer, - SyntheticAdditions additions) { + SyntheticAdditions additions, + BiConsumer<ProgramMethod, ProgramMethod> companionMethodConsumer) { return new R8CfInstructionDesugaringEventConsumer( - appView, lambdaClassConsumer, twrCloseResourceMethodConsumer, additions); + appView, + lambdaClassConsumer, + constantDynamicClassConsumer, + twrCloseResourceMethodConsumer, + additions, + companionMethodConsumer); } + // TODO(b/183998768): Remove this event consumer. It should be unneeded for R8 and for D8 the + // desugaring of interface methods should be able to happen up front too avoiding the companion + // callback on nest accessors. public static CfInstructionDesugaringEventConsumer createForDesugaredCode() { return new CfInstructionDesugaringEventConsumer() { @Override + public void acceptCompanionMethod(ProgramMethod method, ProgramMethod companionMethod) { + // A synthesized nest based accessor may itself be defined on an interface, in which case + // desugaring the accessor will result in a rewrite to the companion method. + } + + @Override public void acceptClasspathEmulatedInterface(DexClasspathClass clazz) { assert false; } @@ -127,6 +146,12 @@ } @Override + public void acceptConstantDynamicClass( + ConstantDynamicClass constantDynamicClass, ProgramMethod context) { + assert false; + } + + @Override public void acceptNestFieldGetBridge(ProgramField target, ProgramMethod bridge) { assert false; } @@ -161,12 +186,18 @@ private final Map<DexReference, InvokeSpecialBridgeInfo> pendingInvokeSpecialBridges = new LinkedHashMap<>(); private final List<LambdaClass> synthesizedLambdaClasses = new ArrayList<>(); + private final List<ConstantDynamicClass> synthesizedConstantDynamicClasses = new ArrayList<>(); private D8CfInstructionDesugaringEventConsumer(D8MethodProcessor methodProcessor) { this.methodProcessor = methodProcessor; } @Override + public void acceptCompanionMethod(ProgramMethod method, ProgramMethod companionMethod) { + // Intentionally empty. Methods are moved when processing the interface definition. + } + + @Override public void acceptDesugaredLibraryRetargeterDispatchClasspathClass(DexClasspathClass clazz) { // Intentionally empty. } @@ -207,6 +238,14 @@ } @Override + public void acceptConstantDynamicClass( + ConstantDynamicClass constantDynamicClass, ProgramMethod context) { + synchronized (synthesizedConstantDynamicClasses) { + synthesizedConstantDynamicClasses.add(constantDynamicClass); + } + } + + @Override public void acceptNestFieldGetBridge(ProgramField target, ProgramMethod bridge) { assert false; } @@ -257,6 +296,7 @@ List<ProgramMethod> needsProcessing = new ArrayList<>(); finalizeInvokeSpecialDesugaring(appView, needsProcessing::add); finalizeLambdaDesugaring(classConverterResultBuilder, needsProcessing::add); + finalizeConstantDynamicDesugaring(needsProcessing::add); return needsProcessing; } @@ -299,6 +339,13 @@ synthesizedLambdaClasses.clear(); } + private void finalizeConstantDynamicDesugaring(Consumer<ProgramMethod> needsProcessing) { + for (ConstantDynamicClass constantDynamicClass : synthesizedConstantDynamicClasses) { + constantDynamicClass.getConstantDynamicProgramClass().forEachProgramMethod(needsProcessing); + } + synthesizedLambdaClasses.clear(); + } + public boolean verifyNothingToFinalize() { assert pendingInvokeSpecialBridges.isEmpty(); assert synthesizedLambdaClasses.isEmpty(); @@ -311,25 +358,39 @@ private final AppView<? extends AppInfoWithClassHierarchy> appView; - // TODO(b/180091213): Remove these two consumers when synthesizing contexts are accessible from + // TODO(b/180091213): Remove these three consumers when synthesizing contexts are accessible + // from // synthetic items. private final BiConsumer<LambdaClass, ProgramMethod> lambdaClassConsumer; + private final BiConsumer<ConstantDynamicClass, ProgramMethod> constantDynamicClassConsumer; private final BiConsumer<ProgramMethod, ProgramMethod> twrCloseResourceMethodConsumer; private final SyntheticAdditions additions; private final Map<LambdaClass, ProgramMethod> synthesizedLambdaClasses = new IdentityHashMap<>(); private final List<InvokeSpecialBridgeInfo> pendingInvokeSpecialBridges = new ArrayList<>(); + private final List<ConstantDynamicClass> synthesizedConstantDynamicClasses = new ArrayList<>(); + + private final BiConsumer<ProgramMethod, ProgramMethod> onCompanionMethodCallback; public R8CfInstructionDesugaringEventConsumer( AppView<? extends AppInfoWithClassHierarchy> appView, BiConsumer<LambdaClass, ProgramMethod> lambdaClassConsumer, + BiConsumer<ConstantDynamicClass, ProgramMethod> constantDynamicClassConsumer, BiConsumer<ProgramMethod, ProgramMethod> twrCloseResourceMethodConsumer, - SyntheticAdditions additions) { + SyntheticAdditions additions, + BiConsumer<ProgramMethod, ProgramMethod> onCompanionMethodCallback) { this.appView = appView; this.lambdaClassConsumer = lambdaClassConsumer; + this.constantDynamicClassConsumer = constantDynamicClassConsumer; this.twrCloseResourceMethodConsumer = twrCloseResourceMethodConsumer; this.additions = additions; + this.onCompanionMethodCallback = onCompanionMethodCallback; + } + + @Override + public void acceptCompanionMethod(ProgramMethod method, ProgramMethod companionMethod) { + onCompanionMethodCallback.accept(method, companionMethod); } @Override @@ -344,7 +405,7 @@ @Override public void acceptCompanionClassClinit(ProgramMethod method) { - // TODO(b/183998768): Update this once desugaring is moved to the enqueuer. + // Intentionally empty. The method will be hit by tracing if required. } @Override @@ -354,19 +415,19 @@ @Override public void acceptRecordMethod(ProgramMethod method) { - // Intentionally empty. The method will be hit by the tracing in R8 as if it was - // present in the input code, and thus nothing needs to be done. + // Intentionally empty. The method will be hit by tracing if required. } @Override public void acceptThrowMethod(ProgramMethod method, ProgramMethod context) { - assert false : "TODO(b/183998768): To be implemented"; + // Intentionally empty. The method will be hit by tracing if required. } @Override public void acceptInvokeStaticInterfaceOutliningMethod( ProgramMethod method, ProgramMethod context) { - assert false : "TODO(b/183998768): To be implemented"; + // Intentionally empty. The method will be hit by tracing if required. + additions.addNeverInlineMethod(method); } @Override @@ -376,14 +437,12 @@ @Override public void acceptAPIConversion(ProgramMethod method) { - // Intentionally empty. The method will be hit by the tracing in R8 as if it was - // present in the input code, and thus nothing needs to be done. + // Intentionally empty. The method will be hit by tracing if required. } @Override public void acceptBackportedMethod(ProgramMethod backportedMethod, ProgramMethod context) { - // Intentionally empty. The backported method will be hit by the tracing in R8 as if it was - // present in the input code, and thus nothing needs to be done. + // Intentionally empty. The method will be hit by tracing if required. } @Override @@ -404,6 +463,17 @@ } @Override + public void acceptConstantDynamicClass( + ConstantDynamicClass constantDynamicClass, ProgramMethod context) { + synchronized (synthesizedConstantDynamicClasses) { + synthesizedConstantDynamicClasses.add(constantDynamicClass); + } + // TODO(b/180091213): Remove the recording of the synthesizing context when this is accessible + // from synthetic items. + constantDynamicClassConsumer.accept(constantDynamicClass, context); + } + + @Override public void acceptNestFieldGetBridge(ProgramField target, ProgramMethod bridge) { assert false; } @@ -420,8 +490,6 @@ @Override public void acceptTwrCloseResourceMethod(ProgramMethod closeMethod, ProgramMethod context) { - // Intentionally empty. The close method will be hit by the tracing in R8 as if they were - // present in the input code, and thus nothing needs to be done. // TODO(b/180091213): Remove the recording of the synthesizing context when this is accessible // from synthetic items. twrCloseResourceMethodConsumer.accept(closeMethod, context);
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 index dbba296..a0d130d 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
@@ -5,6 +5,7 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPICallbackSynthesizor; import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeterPostProcessor; import com.android.tools.r8.ir.desugar.desugaredlibrary.RetargetingInfo; @@ -15,6 +16,7 @@ import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; +import java.util.function.Predicate; public abstract class CfPostProcessingDesugaringCollection { @@ -35,6 +37,7 @@ public abstract void postProcessingDesugaring( Collection<DexProgramClass> programClasses, + Predicate<ProgramMethod> isLiveMethod, CfPostProcessingDesugaringEventConsumer eventConsumer, ExecutorService executorService) throws ExecutionException; @@ -83,6 +86,7 @@ @Override public void postProcessingDesugaring( Collection<DexProgramClass> programClasses, + Predicate<ProgramMethod> isLiveMethod, CfPostProcessingDesugaringEventConsumer eventConsumer, ExecutorService executorService) throws ExecutionException { @@ -107,6 +111,7 @@ @Override public void postProcessingDesugaring( Collection<DexProgramClass> programClasses, + Predicate<ProgramMethod> isLiveMethod, CfPostProcessingDesugaringEventConsumer eventConsumer, ExecutorService executorService) throws ExecutionException {
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 index 66042a0..e4a34c4 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
@@ -30,8 +30,9 @@ return new D8CfPostProcessingDesugaringEventConsumer(methodProcessor, instructionDesugaring); } - public static R8PostProcessingDesugaringEventConsumer createForR8(SyntheticAdditions additions) { - return new R8PostProcessingDesugaringEventConsumer(additions); + public static R8PostProcessingDesugaringEventConsumer createForR8( + SyntheticAdditions additions, CfInstructionDesugaringCollection desugaring) { + return new R8PostProcessingDesugaringEventConsumer(additions, desugaring); } public abstract void finalizeDesugaring() throws ExecutionException; @@ -53,10 +54,8 @@ } private void addMethodToReprocess(ProgramMethod method) { - if (instructionDesugaring.needsDesugaring(method)) { - instructionDesugaring.needsDesugaring(method); - } assert !instructionDesugaring.needsDesugaring(method); + assert method.getDefinition().getCode().isCfCode(); methodsToReprocess.add(method); } @@ -71,13 +70,24 @@ } @Override + public void acceptEmulatedInterfaceMarkerInterface( + DexProgramClass clazz, DexClasspathClass newInterface) { + // Intentionally empty. + } + + @Override public void acceptForwardingMethod(ProgramMethod method) { addMethodToReprocess(method); } @Override public void acceptCompanionClassClinit(ProgramMethod method) { - methodsToReprocess.add(method); + addMethodToReprocess(method); + } + + @Override + public void acceptCompanionMethod(ProgramMethod method, ProgramMethod companion) { + // Intentionally empty. The method must be processed on the interface definition. } @Override @@ -103,17 +113,27 @@ extends CfPostProcessingDesugaringEventConsumer { private final SyntheticAdditions additions; + private final CfInstructionDesugaringCollection desugaring; - R8PostProcessingDesugaringEventConsumer(SyntheticAdditions additions) { + R8PostProcessingDesugaringEventConsumer( + SyntheticAdditions additions, CfInstructionDesugaringCollection desugaring) { this.additions = additions; + this.desugaring = desugaring; } @Override - public void finalizeDesugaring() throws ExecutionException { + public void finalizeDesugaring() { // Intentionally empty. } @Override + public void acceptEmulatedInterfaceMarkerInterface( + DexProgramClass clazz, DexClasspathClass newInterface) { + additions.injectInterface(clazz, newInterface); + additions.addLiveClasspathClass(newInterface); + } + + @Override public void acceptInterfaceInjection(DexProgramClass clazz, DexClass newInterface) { additions.injectInterface(clazz, newInterface); } @@ -130,11 +150,19 @@ @Override public void acceptCompanionClassClinit(ProgramMethod method) { - assert false : "TODO(b/183998768): Support Interface processing in R8"; + // Generation of this method must have been done during enqueuing. + assert false; + } + + @Override + public void acceptCompanionMethod(ProgramMethod method, ProgramMethod companion) { + // Generation of this method must have been done during enqueuing. + assert false; } @Override public void acceptAPIConversionCallback(ProgramMethod method) { + assert !desugaring.needsDesugaring(method); additions.addLiveMethod(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 b23f31e..027b27c 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
@@ -10,9 +10,11 @@ import com.android.tools.r8.ir.desugar.desugaredlibrary.RetargetingInfo; import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade; import com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor; +import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor; import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring; import com.android.tools.r8.utils.ThrowingConsumer; import java.util.function.Consumer; +import java.util.function.Predicate; public class EmptyCfInstructionDesugaringCollection extends CfInstructionDesugaringCollection { @@ -61,7 +63,14 @@ } @Override - public InterfaceMethodProcessorFacade getInterfaceMethodPostProcessingDesugaring(Flavor flavor) { + public InterfaceMethodProcessorFacade getInterfaceMethodPostProcessingDesugaringD8( + Flavor flavor) { + return null; + } + + @Override + public InterfaceMethodProcessorFacade getInterfaceMethodPostProcessingDesugaringR8( + Flavor flavor, Predicate<ProgramMethod> isLiveMethod, InterfaceProcessor processor) { return null; }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java index 61e6828..dd815a3 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -6,6 +6,7 @@ import static com.android.tools.r8.ir.desugar.lambda.ForcefullyMovedLambdaMethodConsumer.emptyForcefullyMovedLambdaMethodConsumer; import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer; +import static com.android.tools.r8.utils.DesugarUtils.appendFullyQualifiedHolderToMethodName; import com.android.tools.r8.dex.Constants; import com.android.tools.r8.errors.Unimplemented; @@ -352,17 +353,14 @@ } else { // Otherwise we need to ensure the method can be reached publicly by virtual dispatch. // To avoid potential conflicts on the name of the lambda method once dispatch becomes virtual - // we add the method-holder name as suffix to the lambda-method name. + // we add the fully qualified method-holder name as suffix to the lambda-method name. return new InstanceLambdaImplTarget( appView .dexItemFactory() .createMethod( implMethod.holder, implMethod.proto, - appView - .dexItemFactory() - .createString( - implMethod.name.toString() + "$" + implMethod.holder.getName()))); + appendFullyQualifiedHolderToMethodName(implMethod, appView.dexItemFactory()))); } }
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 b117828..bd9805d 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
@@ -12,6 +12,7 @@ import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.Code; import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.ir.desugar.constantdynamic.ConstantDynamicInstructionDesugaring; import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter; import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryRetargeter; import com.android.tools.r8.ir.desugar.desugaredlibrary.RetargetingInfo; @@ -19,6 +20,7 @@ import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade; 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.itf.InterfaceProcessor; 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; @@ -35,6 +37,7 @@ import java.util.Iterator; import java.util.List; import java.util.function.Consumer; +import java.util.function.Predicate; public class NonEmptyCfInstructionDesugaringCollection extends CfInstructionDesugaringCollection { @@ -73,9 +76,7 @@ if (appView.options().enableTryWithResourcesDesugaring()) { desugarings.add(new TwrInstructionDesugaring(appView)); } - // TODO(b/183998768): Enable interface method rewriter cf to cf also in R8. - if (appView.options().isInterfaceMethodDesugaringEnabled() - && !appView.enableWholeProgramOptimizations()) { + if (appView.options().isInterfaceMethodDesugaringEnabled()) { interfaceMethodRewriter = new InterfaceMethodRewriter( appView, backportedMethodRewriter, desugaredLibraryRetargeter); @@ -83,18 +84,11 @@ } else { interfaceMethodRewriter = null; } - // In R8 interface method rewriting is performed in IR, we still need to filter - // out from API conversion methods desugared by the interface method rewriter. - InterfaceMethodRewriter enforcedInterfaceMethodRewriter = - interfaceMethodRewriter == null && appView.options().isInterfaceMethodDesugaringEnabled() - ? new InterfaceMethodRewriter( - appView, backportedMethodRewriter, desugaredLibraryRetargeter) - : interfaceMethodRewriter; desugaredLibraryAPIConverter = appView.rewritePrefix.isRewriting() ? new DesugaredLibraryAPIConverter( appView, - enforcedInterfaceMethodRewriter, + interfaceMethodRewriter, desugaredLibraryRetargeter, backportedMethodRewriter) : null; @@ -102,6 +96,7 @@ desugarings.add(desugaredLibraryAPIConverter); } desugarings.add(new LambdaInstructionDesugaring(appView)); + desugarings.add(new ConstantDynamicInstructionDesugaring(appView)); desugarings.add(new InvokeSpecialToSelfDesugaring(appView)); desugarings.add(new InvokeToPrivateRewriter()); desugarings.add(new StringConcatInstructionDesugaring(appView)); @@ -182,7 +177,7 @@ IntBox maxStackForInstruction = new IntBox(cfCode.getMaxStack()); List<CfInstruction> desugaredInstructions = - ListUtils.flatMap( + ListUtils.flatMapSameType( cfCode.getInstructions(), instruction -> { Collection<CfInstruction> replacement = @@ -305,7 +300,7 @@ assert !alsoApplicable || (appliedDesugaring instanceof InterfaceMethodRewriter && (desugaring instanceof InvokeToPrivateRewriter - || desugaring instanceof D8NestBasedAccessDesugaring)) + || desugaring instanceof NestBasedAccessDesugaring)) || (appliedDesugaring instanceof TwrInstructionDesugaring && desugaring instanceof InterfaceMethodRewriter) : "Desugaring of " @@ -330,9 +325,18 @@ } @Override - public InterfaceMethodProcessorFacade getInterfaceMethodPostProcessingDesugaring(Flavor flavor) { + public InterfaceMethodProcessorFacade getInterfaceMethodPostProcessingDesugaringD8( + Flavor flavor) { return interfaceMethodRewriter != null - ? interfaceMethodRewriter.getPostProcessingDesugaring(flavor) + ? interfaceMethodRewriter.getPostProcessingDesugaringD8(flavor) + : null; + } + + @Override + public InterfaceMethodProcessorFacade getInterfaceMethodPostProcessingDesugaringR8( + Flavor flavor, Predicate<ProgramMethod> isLiveMethod, InterfaceProcessor processor) { + return interfaceMethodRewriter != null + ? interfaceMethodRewriter.getPostProcessingDesugaringR8(flavor, isLiveMethod, processor) : null; }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java new file mode 100644 index 0000000..bd2e603 --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
@@ -0,0 +1,275 @@ +// 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.constantdynamic; + +import static org.objectweb.asm.Opcodes.GETSTATIC; +import static org.objectweb.asm.Opcodes.INVOKESTATIC; +import static org.objectweb.asm.Opcodes.PUTSTATIC; + +import com.android.tools.r8.cf.code.CfCheckCast; +import com.android.tools.r8.cf.code.CfConstClass; +import com.android.tools.r8.cf.code.CfConstDynamic; +import com.android.tools.r8.cf.code.CfConstNull; +import com.android.tools.r8.cf.code.CfConstNumber; +import com.android.tools.r8.cf.code.CfConstString; +import com.android.tools.r8.cf.code.CfFieldInstruction; +import com.android.tools.r8.cf.code.CfFrame; +import com.android.tools.r8.cf.code.CfFrame.FrameType; +import com.android.tools.r8.cf.code.CfGoto; +import com.android.tools.r8.cf.code.CfIf; +import com.android.tools.r8.cf.code.CfInstruction; +import com.android.tools.r8.cf.code.CfInvoke; +import com.android.tools.r8.cf.code.CfLabel; +import com.android.tools.r8.cf.code.CfLoad; +import com.android.tools.r8.cf.code.CfMonitor; +import com.android.tools.r8.cf.code.CfReturn; +import com.android.tools.r8.cf.code.CfStackInstruction; +import com.android.tools.r8.cf.code.CfStackInstruction.Opcode; +import com.android.tools.r8.cf.code.CfStore; +import com.android.tools.r8.cf.code.CfThrow; +import com.android.tools.r8.cf.code.CfTryCatch; +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.CfCode; +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.DexMethod; +import com.android.tools.r8.graph.DexMethodHandle; +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.FieldAccessFlags; +import com.android.tools.r8.graph.MethodAccessFlags; +import com.android.tools.r8.graph.MethodResolutionResult; +import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult; +import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.ir.code.If; +import com.android.tools.r8.ir.code.Monitor; +import com.android.tools.r8.ir.code.ValueType; +import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.collections.ImmutableDeque; +import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap; +import com.google.common.collect.ImmutableList; +import java.util.List; + +public class ConstantDynamicClass { + public static final String INITIALIZED_FIELD_NAME = "INITIALIZED"; + public static final String CONST_FIELD_NAME = "CONST"; + + final AppView<?> appView; + final ConstantDynamicInstructionDesugaring desugaring; + private final DexType accessedFrom; + public ConstantDynamicReference reference; + public final DexField initializedValueField; + public final DexField constantValueField; + final DexMethod getConstMethod; + + // Considered final but is set after due to circularity in allocation. + private DexProgramClass clazz = null; + + public ConstantDynamicClass( + SyntheticProgramClassBuilder builder, + AppView<?> appView, + ConstantDynamicInstructionDesugaring desugaring, + ProgramMethod accessedFrom, + CfConstDynamic constantDynamic) { + DexItemFactory factory = appView.dexItemFactory(); + this.appView = appView; + this.desugaring = desugaring; + this.accessedFrom = accessedFrom.getHolderType(); + this.reference = constantDynamic.getReference(); + this.constantValueField = + factory.createField( + builder.getType(), constantDynamic.getType(), factory.createString(CONST_FIELD_NAME)); + this.initializedValueField = + factory.createField( + builder.getType(), factory.booleanType, factory.createString(INITIALIZED_FIELD_NAME)); + this.getConstMethod = + factory.createMethod( + builder.getType(), + factory.createProto(constantDynamic.getType()), + factory.createString("get")); + + synthesizeConstantDynamicClass(builder); + } + + /* + Generate code following this pattern: + + class CondySyntheticXXX { + private static boolean INITIALIZED; + private static <constant type> CONST; + + public static get() { + if (!INITIALIZED) { + synchronized (CondySyntheticXXX.class) { + if (!INITIALIZED) { + CONST = bsm(null, "constant name", <constant type>); + INITIALIZED = true; + } + } + } + return value; + } + } + + */ + private void synthesizeConstantDynamicClass(SyntheticProgramClassBuilder builder) { + synthesizeStaticFields(builder); + synthesizeDirectMethods(builder); + } + + private void synthesizeStaticFields(SyntheticProgramClassBuilder builder) { + builder.setStaticFields( + ImmutableList.of( + DexEncodedField.builder() + .setField(this.initializedValueField) + .setAccessFlags(FieldAccessFlags.createPrivateStaticSynthetic()) + .setApiLevel(AndroidApiLevel.minApiLevelIfEnabledOrUnknown(appView)) + .setD8R8Synthesized() + .build(), + DexEncodedField.builder() + .setField(this.constantValueField) + .setAccessFlags(FieldAccessFlags.createPrivateStaticSynthetic()) + .setApiLevel(AndroidApiLevel.minApiLevelIfEnabledOrUnknown(appView)) + .setD8R8Synthesized() + .build())); + } + + private void synthesizeDirectMethods(SyntheticProgramClassBuilder builder) { + builder.setDirectMethods( + ImmutableList.of( + DexEncodedMethod.builder() + .setMethod(getConstMethod) + .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic()) + .setCode(generateGetterCode(builder)) + .setD8R8Synthesized() + .setApiLevelForDefinition(AndroidApiLevel.S) + .setApiLevelForCode(AndroidApiLevel.S) + .build())); + } + + private void invokeBootstrapMethod( + ConstantDynamicReference reference, ImmutableList.Builder<CfInstruction> instructions) { + assert reference.getBootstrapMethod().type.isInvokeStatic(); + + // TODO(b/178172809): Use MethodHandle.invokeWithArguments if supported. + DexMethodHandle bootstrapMethodHandle = reference.getBootstrapMethod(); + DexMethod bootstrapMethodReference = bootstrapMethodHandle.asMethod(); + MethodResolutionResult resolution = + appView + .appInfoForDesugaring() + .resolveMethod(bootstrapMethodReference, bootstrapMethodHandle.isInterface); + if (resolution.isFailedResolution()) { + // TODO(b/178172809): Generate code which throws ICCE. + } + SingleResolutionResult result = resolution.asSingleResolution(); + assert result.getResolvedMethod().isStatic(); + assert result.getResolvedHolder().isProgramClass(); + instructions.add(new CfConstNull()); + instructions.add(new CfConstString(reference.getName())); + instructions.add(new CfConstClass(reference.getType())); + instructions.add(new CfInvoke(INVOKESTATIC, bootstrapMethodReference, false)); + instructions.add(new CfCheckCast(reference.getType())); + + // Ensure that the bootstrap method is accessible from the generated class. + MethodAccessFlags flags = result.getResolvedMethod().getAccessFlags(); + flags.unsetPrivate(); + flags.setPublic(); + } + + private CfCode generateGetterCode(SyntheticProgramClassBuilder builder) { + int maxStack = 3; + int maxLocals = 2; + ImmutableList<CfCode.LocalVariableInfo> localVariables = ImmutableList.of(); + ImmutableList.Builder<CfInstruction> instructions = ImmutableList.builder(); + + CfLabel initializedTrue = new CfLabel(); + CfLabel initializedTrueSecond = new CfLabel(); + CfLabel tryCatchStart = new CfLabel(); + CfLabel tryCatchEnd = new CfLabel(); + CfLabel tryCatchTarget = new CfLabel(); + CfLabel tryCatchEndFinally = new CfLabel(); + + instructions.add(new CfFieldInstruction(GETSTATIC, initializedValueField)); + instructions.add(new CfIf(If.Type.NE, ValueType.INT, initializedTrue)); + + instructions.add(new CfConstClass(builder.getType())); + instructions.add(new CfStackInstruction(Opcode.Dup)); + instructions.add(new CfStore(ValueType.OBJECT, 0)); + instructions.add(new CfMonitor(Monitor.Type.ENTER)); + instructions.add(tryCatchStart); + + instructions.add(new CfFieldInstruction(GETSTATIC, initializedValueField)); + instructions.add(new CfIf(If.Type.NE, ValueType.INT, initializedTrueSecond)); + + invokeBootstrapMethod(reference, instructions); + instructions.add(new CfFieldInstruction(PUTSTATIC, constantValueField)); + instructions.add(new CfConstNumber(1, ValueType.INT)); + instructions.add(new CfFieldInstruction(PUTSTATIC, initializedValueField)); + + instructions.add(initializedTrueSecond); + instructions.add( + new CfFrame( + ImmutableInt2ReferenceSortedMap.of( + new int[] {0}, + new FrameType[] {FrameType.initialized(builder.getFactory().objectType)}), + ImmutableDeque.of())); + instructions.add(new CfLoad(ValueType.OBJECT, 0)); + instructions.add(new CfMonitor(Monitor.Type.EXIT)); + instructions.add(tryCatchEnd); + instructions.add(new CfGoto(initializedTrue)); + + instructions.add(tryCatchTarget); + instructions.add( + new CfFrame( + ImmutableInt2ReferenceSortedMap.of( + new int[] {0}, + new FrameType[] {FrameType.initialized(builder.getFactory().objectType)}), + ImmutableDeque.of(FrameType.initialized(builder.getFactory().throwableType)))); + instructions.add(new CfStore(ValueType.OBJECT, 1)); + instructions.add(new CfLoad(ValueType.OBJECT, 0)); + instructions.add(new CfMonitor(Monitor.Type.EXIT)); + instructions.add(tryCatchEndFinally); + instructions.add(new CfLoad(ValueType.OBJECT, 1)); + instructions.add(new CfThrow()); + + instructions.add(initializedTrue); + instructions.add(new CfFrame(ImmutableInt2ReferenceSortedMap.empty(), ImmutableDeque.of())); + instructions.add(new CfFieldInstruction(GETSTATIC, constantValueField)); + instructions.add(new CfReturn(ValueType.OBJECT)); + + List<CfTryCatch> tryCatchRanges = + ImmutableList.of( + new CfTryCatch( + tryCatchStart, + tryCatchEnd, + ImmutableList.of(builder.getFactory().throwableType), + ImmutableList.of(tryCatchTarget)), + new CfTryCatch( + tryCatchTarget, + tryCatchEndFinally, + ImmutableList.of(builder.getFactory().throwableType), + ImmutableList.of(tryCatchTarget))); + return new CfCode( + builder.getType(), + maxStack, + maxLocals, + instructions.build(), + tryCatchRanges, + localVariables); + } + + public final DexProgramClass getConstantDynamicProgramClass() { + assert clazz != null; + return clazz; + } + + public void setClass(DexProgramClass clazz) { + assert this.clazz == null; + assert clazz != null; + this.clazz = clazz; + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicDesugaringEventConsumer.java new file mode 100644 index 0000000..3c0efd7 --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicDesugaringEventConsumer.java
@@ -0,0 +1,11 @@ +// 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.constantdynamic; + +import com.android.tools.r8.graph.ProgramMethod; + +public interface ConstantDynamicDesugaringEventConsumer { + + void acceptConstantDynamicClass(ConstantDynamicClass lambdaClass, ProgramMethod context); +}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicInstructionDesugaring.java new file mode 100644 index 0000000..a01fdd2 --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicInstructionDesugaring.java
@@ -0,0 +1,129 @@ +// 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.constantdynamic; + +import com.android.tools.r8.cf.code.CfConstDynamic; +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.DexItemFactory; +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.ProgramMethod; +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.synthesis.SyntheticNaming.SyntheticKind; +import com.android.tools.r8.utils.Box; +import com.google.common.collect.ImmutableList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.objectweb.asm.Opcodes; + +public class ConstantDynamicInstructionDesugaring implements CfInstructionDesugaring { + + private final AppView<?> appView; + private final Map<DexType, Map<ConstantDynamicReference, ConstantDynamicClass>> + dynamicConstantSyntheticsPerClass = new ConcurrentHashMap<>(); + + public ConstantDynamicInstructionDesugaring(AppView<?> appView) { + this.appView = appView; + } + + @Override + public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) { + return instruction.isConstDynamic(); + } + + @Override + public Collection<CfInstruction> desugarInstruction( + CfInstruction instruction, + FreshLocalProvider freshLocalProvider, + LocalStackAllocator localStackAllocator, + CfInstructionDesugaringEventConsumer eventConsumer, + ProgramMethod context, + MethodProcessingContext methodProcessingContext, + DexItemFactory dexItemFactory) { + if (instruction.isConstDynamic()) { + return desugarConstDynamicInstruction( + instruction.asConstDynamic(), + freshLocalProvider, + localStackAllocator, + eventConsumer, + context, + methodProcessingContext); + } + return null; + } + + private Collection<CfInstruction> desugarConstDynamicInstruction( + CfConstDynamic invoke, + FreshLocalProvider freshLocalProvider, + LocalStackAllocator localStackAllocator, + ConstantDynamicDesugaringEventConsumer eventConsumer, + ProgramMethod context, + MethodProcessingContext methodProcessingContext) { + ConstantDynamicClass constantDynamicClass = + ensureConstantDynamicClass(invoke, context, methodProcessingContext, eventConsumer); + return ImmutableList.of( + new CfInvoke(Opcodes.INVOKESTATIC, constantDynamicClass.getConstMethod, false)); + } + + // Creates a class corresponding to the constant dynamic symbolic reference and context. + // TODO(b/178172809): Move this ensure handling to the synthetic items handling and move to + // one class for dynamic constants for each class using dynamic constants. + private ConstantDynamicClass ensureConstantDynamicClass( + CfConstDynamic constantDynamic, + ProgramMethod context, + MethodProcessingContext methodProcessingContext, + ConstantDynamicDesugaringEventConsumer eventConsumer) { + Map<ConstantDynamicReference, ConstantDynamicClass> dynamicConstantSyntheticsForClass = + dynamicConstantSyntheticsPerClass.computeIfAbsent( + context.getHolderType(), (ignore) -> new HashMap<>()); + ConstantDynamicClass result = + dynamicConstantSyntheticsForClass.get(constantDynamic.getReference()); + if (result == null) { + synchronized (dynamicConstantSyntheticsForClass) { + result = dynamicConstantSyntheticsForClass.get(constantDynamic.getReference()); + if (result == null) { + result = + createConstantDynamicClass( + constantDynamic, context, methodProcessingContext, eventConsumer); + dynamicConstantSyntheticsForClass.put(constantDynamic.getReference(), result); + } + } + } + return result; + } + + // TODO(b/178172809): Change to use ensureFixedClass. + private ConstantDynamicClass createConstantDynamicClass( + CfConstDynamic constantDynamic, + ProgramMethod context, + MethodProcessingContext methodProcessingContext, + ConstantDynamicDesugaringEventConsumer eventConsumer) { + Box<ConstantDynamicClass> box = new Box<>(); + DexProgramClass clazz = + appView + .getSyntheticItems() + .createClass( + SyntheticKind.CONST_DYNAMIC, + methodProcessingContext.createUniqueContext(), + appView, + builder -> + box.set( + new ConstantDynamicClass( + builder, appView, this, context, constantDynamic))); + // Immediately set the actual program class on the constant dynamic. + ConstantDynamicClass constantDynamicClass = box.get(); + constantDynamicClass.setClass(clazz); + eventConsumer.acceptConstantDynamicClass(constantDynamicClass, context); + return constantDynamicClass; + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicReference.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicReference.java new file mode 100644 index 0000000..da8257c --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicReference.java
@@ -0,0 +1,61 @@ +// 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.constantdynamic; + +import com.android.tools.r8.graph.DexMethodHandle; +import com.android.tools.r8.graph.DexString; +import com.android.tools.r8.graph.DexType; +import java.util.Objects; + +public class ConstantDynamicReference { + private final DexString name; + private final DexType type; + private final DexMethodHandle bootstrapMethod; + private final Object[] bootstrapMethodArguments; + + public ConstantDynamicReference( + DexString name, + DexType type, + DexMethodHandle bootstrapMethod, + Object[] bootstrapMethodArguments) { + assert bootstrapMethodArguments.length == 0; + this.name = name; + this.type = type; + this.bootstrapMethod = bootstrapMethod; + this.bootstrapMethodArguments = bootstrapMethodArguments; + } + + public DexString getName() { + return name; + } + + public DexType getType() { + return type; + } + + public DexMethodHandle getBootstrapMethod() { + return bootstrapMethod; + } + + public Object[] getBootstrapMethodArguments() { + return bootstrapMethodArguments; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof ConstantDynamicReference)) { + return false; + } + ConstantDynamicReference other = (ConstantDynamicReference) obj; + return Objects.equals(name, other.name) + && Objects.equals(type, other.type) + && Objects.equals(bootstrapMethod, other.bootstrapMethod); + } + + @Override + public int hashCode() { + return Objects.hash(name, type, bootstrapMethod); + } +}
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 90a7b35..8661587 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
@@ -24,16 +24,19 @@ import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.GenericSignature; +import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature; import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature; import com.android.tools.r8.graph.LibraryMethod; import com.android.tools.r8.graph.MethodAccessFlags; import com.android.tools.r8.graph.MethodResolutionResult; import com.android.tools.r8.graph.ParameterAnnotationsList; +import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.position.MethodPosition; import com.android.tools.r8.utils.BooleanBox; import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.IterableUtils; import com.android.tools.r8.utils.MethodSignatureEquivalence; +import com.android.tools.r8.utils.OptionalBool; import com.android.tools.r8.utils.WorkList; import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.base.Equivalence.Wrapper; @@ -50,6 +53,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Predicate; import org.objectweb.asm.Opcodes; /** @@ -344,6 +348,7 @@ private final InterfaceDesugaringSyntheticHelper helper; private final MethodSignatureEquivalence equivalence = MethodSignatureEquivalence.get(); private final boolean needsLibraryInfo; + private final Predicate<ProgramMethod> isLiveMethod; // Mapping from program and classpath classes to their information summary. private final Map<DexClass, ClassInfo> classInfo = new ConcurrentHashMap<>(); @@ -358,7 +363,11 @@ private final Map<DexProgramClass, ProgramMethodSet> newSyntheticMethods = new ConcurrentHashMap<>(); - ClassProcessor(AppView<?> appView) { + // Mapping from actual program classes to the extra interfaces needed for emulated dispatch. + private final Map<DexProgramClass, List<ClassTypeSignature>> newExtraInterfaceSignatures = + new ConcurrentHashMap<>(); + + ClassProcessor(AppView<?> appView, Predicate<ProgramMethod> isLiveMethod) { this.appView = appView; this.dexItemFactory = appView.dexItemFactory(); this.helper = new InterfaceDesugaringSyntheticHelper(appView); @@ -369,6 +378,14 @@ .desugaredLibraryConfiguration .getRetargetCoreLibMember() .isEmpty(); + this.isLiveMethod = isLiveMethod; + } + + private boolean isLiveMethod(DexClassAndMethod method) { + if (method.isProgramMethod()) { + return isLiveMethod.test(method.asProgramMethod()); + } + return true; } private boolean needsLibraryInfo() { @@ -396,6 +413,16 @@ clazz.addVirtualMethods(newForwardingMethods.toDefinitionSet()); newForwardingMethods.forEach(eventConsumer::acceptForwardingMethod); }); + newExtraInterfaceSignatures.forEach( + (clazz, extraInterfaceSignatures) -> { + if (!extraInterfaceSignatures.isEmpty()) { + for (ClassTypeSignature signature : extraInterfaceSignatures) { + eventConsumer.acceptEmulatedInterfaceMarkerInterface( + clazz, helper.ensureEmulatedInterfaceMarkerInterface(signature.type())); + } + clazz.addExtraInterfaces(extraInterfaceSignatures); + } + }); } // Computes the set of method signatures that may need forwarding methods on derived classes. @@ -533,7 +560,7 @@ } } }); - clazz.asProgramClass().addExtraInterfaces(extraInterfaceSignatures); + newExtraInterfaceSignatures.put(clazz.asProgramClass(), extraInterfaceSignatures); } private void collectEmulatedInterfaces( @@ -637,7 +664,7 @@ clazz, wrapper.get(), target -> { - if (!superInfo.isTargetedByForwards(target)) { + if (isLiveMethod(target) && !superInfo.isTargetedByForwards(target)) { additionalForwards.add(target); addForwardingMethod(target, clazz); } @@ -821,6 +848,10 @@ DexEncodedMethod desugaringForwardingMethod = DexEncodedMethod.createDesugaringForwardingMethod( target, clazz, forwardMethod, dexItemFactory); + if (!target.isProgramDefinition() + || target.getDefinition().isLibraryMethodOverride().isTrue()) { + desugaringForwardingMethod.setLibraryMethodOverride(OptionalBool.TRUE); + } addSyntheticMethod(clazz.asProgramClass(), desugaringForwardingMethod); }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java index 92a64fe..9ee3606 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceDesugaringSyntheticHelper.java
@@ -17,6 +17,7 @@ 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.DexClasspathClass; import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexField; @@ -32,7 +33,9 @@ import com.android.tools.r8.graph.InvalidCode; import com.android.tools.r8.graph.MethodAccessFlags; import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer; import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceSynthesizerEventConsumer.ClasspathEmulatedInterfaceSynthesizerEventConsumer; +import com.android.tools.r8.synthesis.SyntheticClassBuilder; import com.android.tools.r8.synthesis.SyntheticMethodBuilder; import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind; import com.android.tools.r8.utils.InternalOptions; @@ -41,7 +44,6 @@ import com.google.common.collect.ImmutableList; import java.util.Map; import java.util.Set; -import java.util.function.Consumer; import java.util.function.Predicate; import org.objectweb.asm.Opcodes; @@ -185,6 +187,17 @@ return factory.createType(interfaceTypeDescriptor); } + // TODO(b/198273164): This should take the context class and not just a type. + DexClasspathClass ensureEmulatedInterfaceMarkerInterface(DexType type) { + return appView + .getSyntheticItems() + .ensureFixedClasspathClassFromType( + SyntheticKind.EMULATED_INTERFACE_MARKER_CLASS, + type, + appView, + SyntheticClassBuilder::setInterface); + } + DexClassAndMethod ensureEmulatedInterfaceMethod( DexClassAndMethod method, ClasspathEmulatedInterfaceSynthesizerEventConsumer eventConsumer) { DexMethod emulatedInterfaceMethod = emulateInterfaceLibraryMethod(method); @@ -232,10 +245,10 @@ } DexClassAndMethod ensureStaticAsMethodOfCompanionClassStub( - DexClassAndMethod method, Consumer<ProgramMethod> companionClinitConsumer) { + DexClassAndMethod method, CfInstructionDesugaringEventConsumer eventConsumer) { if (method.isProgramMethod()) { return ensureStaticAsMethodOfProgramCompanionClassStub( - method.asProgramMethod(), companionClinitConsumer); + method.asProgramMethod(), eventConsumer); } else { ClasspathOrLibraryClass context = method.getHolder().asClasspathOrLibraryClass(); DexMethod companionMethodReference = staticAsMethodOfCompanionClass(method); @@ -264,15 +277,7 @@ .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( - syntheticMethod -> - appView.enableWholeProgramOptimizations() - ? virtual - .getCode() - .getCodeAsInlining(syntheticMethod, method.getReference()) - : InvalidCode.getInstance()); + .setCode(ignored -> InvalidCode.getInstance()); }, ignored -> {}); } @@ -297,15 +302,7 @@ .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. - .setCode( - syntheticMethod -> - appView.enableWholeProgramOptimizations() - ? definition - .getCode() - .getCodeAsInlining(syntheticMethod, method.getReference()) - : InvalidCode.getInstance()); + .setCode(ignored -> InvalidCode.getInstance()); }, ignored -> {}); } @@ -368,9 +365,10 @@ } ProgramMethod ensureStaticAsMethodOfProgramCompanionClassStub( - ProgramMethod method, Consumer<ProgramMethod> companionClinitConsumer) { - if (!method.getDefinition().isClassInitializer() && method.getHolder().hasClassInitializer()) { - ensureCompanionClassInitializesInterface(method.getHolder(), companionClinitConsumer); + ProgramMethod method, InterfaceMethodDesugaringBaseEventConsumer eventConsumer) { + assert !method.getDefinition().isClassInitializer(); + if (method.getHolder().hasClassInitializer()) { + ensureCompanionClassInitializesInterface(method.getHolder(), eventConsumer); } DexMethod companionMethodReference = staticAsMethodOfCompanionClass(method); DexEncodedMethod definition = method.getDefinition(); @@ -387,21 +385,29 @@ .setGenericSignature(definition.getGenericSignature()) .setAnnotations(definition.annotations()) .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. - .setCode( - syntheticMethod -> - appView.enableWholeProgramOptimizations() - ? definition - .getCode() - .getCodeAsInlining(syntheticMethod, method.getReference()) - : InvalidCode.getInstance()); + .setCode(ignored -> InvalidCode.getInstance()); }, - ignored -> {}); + companion -> eventConsumer.acceptCompanionMethod(method, companion)); + } + + public ProgramMethod ensureMethodOfProgramCompanionClassStub( + ProgramMethod method, InterfaceMethodDesugaringBaseEventConsumer eventConsumer) { + DexEncodedMethod definition = method.getDefinition(); + assert method.getHolder().isInterface(); + assert definition.isNonAbstractNonNativeMethod(); + assert definition.getCode() != null; + assert !InvalidCode.isInvalidCode(definition.getCode()); + if (definition.isStatic()) { + return ensureStaticAsMethodOfProgramCompanionClassStub(method, eventConsumer); + } + if (definition.isPrivate()) { + return ensurePrivateAsMethodOfProgramCompanionClassStub(method); + } + return ensureDefaultAsMethodOfProgramCompanionClassStub(method); } private void ensureCompanionClassInitializesInterface( - DexProgramClass iface, Consumer<ProgramMethod> companionClinitConsumer) { + DexProgramClass iface, InterfaceMethodDesugaringBaseEventConsumer eventConsumer) { assert hasStaticMethodThatTriggersNonTrivialClassInitializer(iface); InterfaceProcessor.ensureCompanionMethod( iface, @@ -409,7 +415,7 @@ appView.dexItemFactory().createProto(appView.dexItemFactory().voidType), appView, methodBuilder -> createCompanionClassInitializer(iface, methodBuilder), - companionClinitConsumer); + eventConsumer::acceptCompanionClassClinit); } private DexEncodedField ensureStaticClinitFieldToTriggerInterfaceInitialization(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringBaseEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringBaseEventConsumer.java new file mode 100644 index 0000000..4e1a30f --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringBaseEventConsumer.java
@@ -0,0 +1,13 @@ +// 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.ProgramMethod; + +public interface InterfaceMethodDesugaringBaseEventConsumer { + + void acceptCompanionClassClinit(ProgramMethod method); + + void acceptCompanionMethod(ProgramMethod method, ProgramMethod companionMethod); +}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringEventConsumer.java index bdc8816..9a65214 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringEventConsumer.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceMethodDesugaringEventConsumer.java
@@ -6,13 +6,10 @@ import com.android.tools.r8.graph.ProgramMethod; -public interface InterfaceMethodDesugaringEventConsumer { +public interface InterfaceMethodDesugaringEventConsumer + extends InterfaceMethodDesugaringBaseEventConsumer { void acceptThrowMethod(ProgramMethod method, ProgramMethod context); void acceptInvokeStaticInterfaceOutliningMethod(ProgramMethod method, ProgramMethod context); - - void acceptCompanionClassClinit(ProgramMethod method); - - // TODO(b/183998768): Add acceptCompanionClass and acceptEmulatedInterface. }
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 index f989858..94a4e60 100644 --- 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
@@ -7,18 +7,17 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.ProgramMethod; -import com.android.tools.r8.ir.conversion.IRConverter; import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaring; import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer; 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.Collection; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; +import java.util.function.Predicate; public class InterfaceMethodProcessorFacade implements CfPostProcessingDesugaring { @@ -26,18 +25,30 @@ private final Flavor flavour; private final List<InterfaceDesugaringProcessor> interfaceDesugaringProcessors; - InterfaceMethodProcessorFacade(AppView<?> appView, Flavor flavour) { + InterfaceMethodProcessorFacade( + AppView<?> appView, Flavor flavour, Predicate<ProgramMethod> isLiveMethod) { this.appView = appView; this.flavour = flavour; - interfaceDesugaringProcessors = instantiateInterfaceDesugaringProcessors(appView); + interfaceDesugaringProcessors = instantiateInterfaceDesugaringProcessors(appView, isLiveMethod); + } + + InterfaceMethodProcessorFacade( + AppView<?> appView, + Flavor flavour, + Predicate<ProgramMethod> isLiveMethod, + InterfaceProcessor interfaceProcessor) { + this.appView = appView; + this.flavour = flavour; + interfaceDesugaringProcessors = + ImmutableList.of(new ClassProcessor(appView, isLiveMethod), interfaceProcessor); } private List<InterfaceDesugaringProcessor> instantiateInterfaceDesugaringProcessors( - AppView<?> appView) { + AppView<?> appView, Predicate<ProgramMethod> isLiveMethod) { // Process all classes first. Add missing forwarding methods to // replace desugared default interface methods. - ClassProcessor classProcessor = new ClassProcessor(appView); + ClassProcessor classProcessor = new ClassProcessor(appView, isLiveMethod); // Process interfaces, create companion or dispatch class if needed, move static // methods to companion class, copy default interface methods to companion classes, @@ -49,39 +60,6 @@ return ImmutableList.of(classProcessor, interfaceProcessor); } - /** Runs the interfaceProcessor, the class processor and the emulated interface processor. */ - void runInterfaceDesugaringProcessorsForR8(IRConverter converter, ExecutorService executorService) - throws ExecutionException { - - CollectingInterfaceDesugaringEventConsumer eventConsumer = - new CollectingInterfaceDesugaringEventConsumer(); - processClassesConcurrently(appView.appInfo().classes(), eventConsumer, executorService); - converter.processMethodsConcurrently( - eventConsumer.getSortedSynthesizedMethods(), executorService); - } - - // This temporary class avoids the duality between collecting with IR processing and - // having events with the Cf desugaring. - private static class CollectingInterfaceDesugaringEventConsumer - implements InterfaceProcessingDesugaringEventConsumer { - - SortedProgramMethodSet sortedSynthesizedMethods = SortedProgramMethodSet.createConcurrent(); - - @Override - public void acceptForwardingMethod(ProgramMethod method) { - sortedSynthesizedMethods.add(method); - } - - @Override - public void acceptCompanionClassClinit(ProgramMethod method) { - sortedSynthesizedMethods.add(method); - } - - public SortedProgramMethodSet getSortedSynthesizedMethods() { - return sortedSynthesizedMethods; - } - } - private boolean shouldProcess(DexProgramClass clazz, Flavor flavour) { if (appView.isAlreadyLibraryDesugared(clazz)) { return false;
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 8b3f70e..77f2c98 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
@@ -38,10 +38,8 @@ import com.android.tools.r8.graph.MethodAccessFlags; import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult; import com.android.tools.r8.graph.ProgramMethod; -import com.android.tools.r8.ir.analysis.type.TypeAnalysis; import com.android.tools.r8.ir.code.BasicBlock; 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.Invoke.Type; import com.android.tools.r8.ir.code.InvokeMethod; @@ -70,7 +68,6 @@ import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.IteratorUtils; 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.Sets; import java.util.ArrayList; @@ -80,11 +77,10 @@ import java.util.Map; import java.util.Set; 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.Function; +import java.util.function.Predicate; // // Default and static interface method desugaring rewriter (note that lambda @@ -267,7 +263,9 @@ AppInfoWithClassHierarchy appInfoForDesugaring = appView.appInfoForDesugaring(); SingleResolutionResult resolution = appInfoForDesugaring.resolveMethod(method, invokeType == INTERFACE).asSingleResolution(); - if (resolution != null && resolution.getResolvedMethod().isPrivateMethod()) { + if (resolution != null + && (resolution.getResolvedMethod().isPrivate() + || resolution.getResolvedMethod().isStatic())) { return true; } return defaultMethodForEmulatedDispatchOrNull(method, invokeType == INTERFACE) != null; @@ -442,10 +440,15 @@ .resolveMethod(invoke.getMethod(), invoke.isInterface()) .asSingleResolution(); if (resolution != null - && resolution.getResolvedMethod().isPrivateMethod() + && resolution.getResolvedMethod().isPrivate() && resolution.isAccessibleFrom(context, appInfoForDesugaring).isTrue()) { - return rewriteInvokeDirect(invoke.getMethod(), context, rewriteInvoke); + // TODO(b/198267586): What about the private in-accessible case? + return rewriteInvokeDirect(invoke.getMethod(), context, rewriteInvoke, eventConsumer); } + if (resolution != null && resolution.getResolvedMethod().isStatic()) { + return rewriteToThrow.apply(resolution); + } + // TODO(b/198267586): What about an invoke <init>? return rewriteInvokeInterfaceOrInvokeVirtual( invoke.getMethod(), invoke.isInterface(), rewriteInvoke, eventConsumer); } @@ -465,13 +468,14 @@ staticOutliningMethodConsumer, rewriteInvoke, rewriteToThrow, - eventConsumer::acceptCompanionClassClinit); + eventConsumer); } assert invoke.isInvokeSpecial(); if (invoke.isInvokeSuper(context.getHolderType())) { - return rewriteInvokeSuper(invoke.getMethod(), context, rewriteInvoke, rewriteToThrow); + return rewriteInvokeSuper( + invoke.getMethod(), context, rewriteInvoke, rewriteToThrow, eventConsumer); } - return rewriteInvokeDirect(invoke.getMethod(), context, rewriteInvoke); + return rewriteInvokeDirect(invoke.getMethod(), context, rewriteInvoke, eventConsumer); } private Collection<CfInstruction> rewriteInvokeToThrowCf( @@ -568,113 +572,6 @@ } } - // Rewrites the references to static and default interface methods. - // NOTE: can be called for different methods concurrently. - public void rewriteMethodReferences( - IRCode code, - MethodProcessor methodProcessor, - MethodProcessingContext methodProcessingContext) { - ProgramMethod context = code.context(); - if (isSyntheticMethodThatShouldNotBeDoubleProcessed(code.context())) { - // As the synthetics for dispatching to static interface methods are not desugared again - // this can leave a static invoke to a static method on an interface. - leavingStaticInvokeToInterface(context.asProgramMethod()); - return; - } - - Set<Value> affectedValues = Sets.newIdentityHashSet(); - Set<BasicBlock> blocksToRemove = Sets.newIdentityHashSet(); - ListIterator<BasicBlock> blocks = code.listIterator(); - while (blocks.hasNext()) { - BasicBlock block = blocks.next(); - if (blocksToRemove.contains(block)) { - continue; - } - InstructionListIterator instructions = block.listIterator(code); - while (instructions.hasNext()) { - Instruction instruction = instructions.next(); - rewriteMethodReferences( - code, - methodProcessor, - methodProcessingContext, - context, - affectedValues, - blocksToRemove, - blocks, - instructions, - instruction); - } - } - - code.removeBlocks(blocksToRemove); - - if (!affectedValues.isEmpty()) { - new TypeAnalysis(appView).narrowing(affectedValues); - } - - assert code.isConsistentSSA(); - } - - private void rewriteMethodReferences( - IRCode code, - MethodProcessor methodProcessor, - MethodProcessingContext methodProcessingContext, - ProgramMethod context, - Set<Value> affectedValues, - Set<BasicBlock> blocksToRemove, - ListIterator<BasicBlock> blocks, - InstructionListIterator instructions, - Instruction instruction) { - if (instruction.isInvokeCustom()) { - reportInterfaceMethodHandleCallSite(instruction.asInvokeCustom().getCallSite(), context); - return; - } - if (!instruction.isInvokeMethod()) { - return; - } - InvokeMethod invoke = instruction.asInvokeMethod(); - Function<DexMethod, Collection<CfInstruction>> rewriteInvoke = - (newTarget) -> { - instructions.replaceCurrentInstruction( - new InvokeStatic(newTarget, invoke.outValue(), invoke.arguments())); - return null; - }; - if (instruction.isInvokeDirect()) { - rewriteInvokeDirect(invoke.getInvokedMethod(), context, rewriteInvoke); - } else if (instruction.isInvokeVirtual() || instruction.isInvokeInterface()) { - rewriteInvokeInterfaceOrInvokeVirtual( - invoke.getInvokedMethod(), invoke.getInterfaceBit(), rewriteInvoke, null); - } else { - Function<SingleResolutionResult, Collection<CfInstruction>> rewriteToThrow = - (resolutionResult) -> - rewriteInvokeToThrowIR( - invoke, - resolutionResult, - code, - blocks, - instructions, - affectedValues, - blocksToRemove, - methodProcessor, - methodProcessingContext); - if (instruction.isInvokeStatic()) { - // TODO(b/192439456): Make a test to prove resolution is needed here and fix it. - rewriteInvokeStatic( - invoke.getInvokedMethod(), - invoke.getInterfaceBit(), - methodProcessingContext, - context, - synthesizedMethods::add, - rewriteInvoke, - rewriteToThrow, - synthesizedMethods::add); - } else { - assert instruction.isInvokeSuper(); - rewriteInvokeSuper(invoke.getInvokedMethod(), context, rewriteInvoke, rewriteToThrow); - } - } - } - private boolean isSyntheticMethodThatShouldNotBeDoubleProcessed(ProgramMethod method) { return appView.getSyntheticItems().isSyntheticMethodThatShouldNotBeDoubleProcessed(method); } @@ -693,7 +590,8 @@ private Collection<CfInstruction> rewriteInvokeDirect( DexMethod invokedMethod, ProgramMethod context, - Function<DexMethod, Collection<CfInstruction>> rewriteInvoke) { + Function<DexMethod, Collection<CfInstruction>> rewriteInvoke, + InterfaceMethodDesugaringEventConsumer eventConsumer) { if (factory.isConstructor(invokedMethod)) { return null; } @@ -723,19 +621,24 @@ // method is expected to be in the current class since it is private, but desugaring // may move some methods or their code into other classes. assert invokeNeedsRewriting(invokedMethod, DIRECT); + DexClassAndMethod companionMethodDefinition = null; DexMethod companionMethod; if (directTarget.getDefinition().isPrivateMethod()) { - companionMethod = - directTarget.isProgramMethod() - ? helper - .ensurePrivateAsMethodOfProgramCompanionClassStub( - directTarget.asProgramMethod()) - .getReference() - // TODO(b/183998768): Why does this not create a stub on the class path? - : helper.privateAsMethodOfCompanionClass(directTarget); + if (directTarget.isProgramMethod()) { + companionMethodDefinition = + helper.ensurePrivateAsMethodOfProgramCompanionClassStub( + directTarget.asProgramMethod()); + companionMethod = companionMethodDefinition.getReference(); + } else { + // TODO(b/183998768): Why does this not create a stub on the class path? + companionMethod = helper.privateAsMethodOfCompanionClass(directTarget); + } } else { - companionMethod = - helper.ensureDefaultAsMethodOfCompanionClassStub(directTarget).getReference(); + companionMethodDefinition = helper.ensureDefaultAsMethodOfCompanionClassStub(directTarget); + companionMethod = companionMethodDefinition.getReference(); + } + if (companionMethodDefinition != null) { + acceptCompanionMethod(directTarget, companionMethodDefinition, eventConsumer); } return rewriteInvoke.apply(companionMethod); } else { @@ -745,8 +648,10 @@ if (virtualTarget != null) { // This is a invoke-direct call to a virtual method. assert invokeNeedsRewriting(invokedMethod, DIRECT); - return rewriteInvoke.apply( - helper.ensureDefaultAsMethodOfCompanionClassStub(virtualTarget).getReference()); + DexClassAndMethod companionMethod = + helper.ensureDefaultAsMethodOfCompanionClassStub(virtualTarget); + acceptCompanionMethod(virtualTarget, companionMethod, eventConsumer); + return rewriteInvoke.apply(companionMethod.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. @@ -756,6 +661,16 @@ return null; } + private void acceptCompanionMethod( + DexClassAndMethod method, + DexClassAndMethod companion, + InterfaceMethodDesugaringEventConsumer eventConsumer) { + assert method.isProgramMethod() == companion.isProgramMethod(); + if (method.isProgramMethod()) { + eventConsumer.acceptCompanionMethod(method.asProgramMethod(), companion.asProgramMethod()); + } + } + private Collection<CfInstruction> rewriteInvokeStatic( DexMethod invokedMethod, boolean interfaceBit, @@ -764,7 +679,7 @@ Consumer<ProgramMethod> staticOutliningMethodConsumer, Function<DexMethod, Collection<CfInstruction>> rewriteInvoke, Function<SingleResolutionResult, Collection<CfInstruction>> rewriteToThrow, - Consumer<ProgramMethod> companionClinitConsumer) { + CfInstructionDesugaringEventConsumer eventConsumer) { if (appView.getSyntheticItems().isPendingSynthetic(invokedMethod.holder)) { // We did not create this code yet, but it will not require rewriting. return null; @@ -858,9 +773,9 @@ assert resolutionResult != null; assert resolutionResult.getResolvedMethod().isStatic(); assert invokeNeedsRewriting(invokedMethod, STATIC); + DexClassAndMethod method = resolutionResult.getResolutionPair(); DexClassAndMethod companionMethod = - helper.ensureStaticAsMethodOfCompanionClassStub( - resolutionResult.getResolutionPair(), companionClinitConsumer); + helper.ensureStaticAsMethodOfCompanionClassStub(method, eventConsumer); return rewriteInvoke.apply(companionMethod.getReference()); } @@ -868,7 +783,8 @@ DexMethod invokedMethod, ProgramMethod context, Function<DexMethod, Collection<CfInstruction>> rewriteInvoke, - Function<SingleResolutionResult, Collection<CfInstruction>> rewriteToThrow) { + Function<SingleResolutionResult, Collection<CfInstruction>> rewriteToThrow, + InterfaceMethodDesugaringEventConsumer eventConsumer) { DexClass clazz = appView.definitionFor(invokedMethod.holder, context); if (clazz == null) { // NOTE: leave unchanged those calls to undefined targets. This may lead to runtime @@ -901,10 +817,27 @@ // TODO(b/145775365): This should throw IAE. return rewriteToThrow.apply(null); } - return rewriteInvoke.apply( - helper.privateAsMethodOfCompanionClass(resolutionResult.getResolutionPair())); + DexClassAndMethod method = resolutionResult.getResolutionPair(); + DexMethod companionMethod; + if (method.isProgramMethod()) { + ProgramMethod companionMethodDefinition = + helper.ensurePrivateAsMethodOfProgramCompanionClassStub(method.asProgramMethod()); + companionMethod = companionMethodDefinition.getReference(); + eventConsumer.acceptCompanionMethod(method.asProgramMethod(), companionMethodDefinition); + } else { + companionMethod = helper.privateAsMethodOfCompanionClass(method); + } + return rewriteInvoke.apply(companionMethod); } else { + DexClassAndMethod method = resolutionResult.getResolutionPair(); + // TODO(b/183998768): Why do this amend routine. We have done resolution, so would that + // not be the correct target!? I think this is just legacy from before resolution was + // implemented in full. DexMethod amendedMethod = amendDefaultMethod(context.getHolder(), invokedMethod); + assert method.getReference() == amendedMethod; + DexClassAndMethod companionMethod = + helper.ensureDefaultAsMethodOfCompanionClassStub(method); + acceptCompanionMethod(method, companionMethod, eventConsumer); return rewriteInvoke.apply( InterfaceDesugaringSyntheticHelper.defaultAsMethodOfCompanionClass( amendedMethod, appView.dexItemFactory())); @@ -920,8 +853,10 @@ DexClass holder = target.getHolder(); if (holder.isLibraryClass() && holder.isInterface()) { assert invokeNeedsRewriting(invokedMethod, SUPER); - return rewriteInvoke.apply( - helper.ensureDefaultAsMethodOfCompanionClassStub(target).getReference()); + DexClassAndMethod companionTarget = + helper.ensureDefaultAsMethodOfCompanionClassStub(target); + acceptCompanionMethod(target, companionTarget, eventConsumer); + return rewriteInvoke.apply(companionTarget.getReference()); } } } @@ -1121,26 +1056,15 @@ return singleCandidate != null ? singleCandidate : method; } - 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. - this.cache.clear(); - this.synthesizedMethods.clear(); + public InterfaceMethodProcessorFacade getPostProcessingDesugaringD8(Flavor flavour) { + return new InterfaceMethodProcessorFacade(appView, flavour, m -> true); } - public void runInterfaceDesugaringProcessorsForR8( - IRConverter converter, Flavor flavour, ExecutorService executorService) - throws ExecutionException { - getPostProcessingDesugaring(flavour) - .runInterfaceDesugaringProcessorsForR8(converter, executorService); - } - - public InterfaceMethodProcessorFacade getPostProcessingDesugaring(Flavor flavour) { - return new InterfaceMethodProcessorFacade(appView, flavour); + public InterfaceMethodProcessorFacade getPostProcessingDesugaringR8( + Flavor flavour, + Predicate<ProgramMethod> isLiveMethod, + InterfaceProcessor interfaceProcessor) { + return new InterfaceMethodProcessorFacade(appView, flavour, isLiveMethod, interfaceProcessor); } private Origin getMethodOrigin(DexMethod method) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessingDesugaringEventConsumer.java index 8fc50d1..e16f254 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessingDesugaringEventConsumer.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/InterfaceProcessingDesugaringEventConsumer.java
@@ -4,12 +4,18 @@ package com.android.tools.r8.ir.desugar.itf; +import com.android.tools.r8.graph.DexClasspathClass; +import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.ProgramMethod; -public interface InterfaceProcessingDesugaringEventConsumer { +// TODO(b/183998768): Consider forcing the processing of interface methods in D8 akin to R8. +// That would avoid the need to reiterate the interface methods to collect info and this +// could avoid the "base" methods. +public interface InterfaceProcessingDesugaringEventConsumer + extends InterfaceMethodDesugaringBaseEventConsumer { void acceptForwardingMethod(ProgramMethod method); - // TODO(b/183998768): Remove this once interface desugaring is moved to the R8 enqueuer. - void acceptCompanionClassClinit(ProgramMethod method); + void acceptEmulatedInterfaceMarkerInterface( + DexProgramClass clazz, DexClasspathClass newInterface); }
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 ac12741..dd4a7a9 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
@@ -9,7 +9,6 @@ import com.android.tools.r8.code.Instruction; import com.android.tools.r8.code.InvokeSuper; import com.android.tools.r8.errors.CompilationError; -import com.android.tools.r8.errors.Unimplemented; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.Code; import com.android.tools.r8.graph.DexClass; @@ -33,7 +32,6 @@ import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap; import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap; import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap; -import com.android.tools.r8.utils.collections.EmptyBidirectionalOneToOneMap; import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap; import java.util.ArrayDeque; import java.util.ArrayList; @@ -44,6 +42,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiConsumer; import java.util.function.Consumer; // Default and static method interface desugaring processor for interfaces. @@ -59,14 +58,22 @@ private final Map<DexProgramClass, PostProcessingInterfaceInfo> postProcessingInterfaceInfos = new ConcurrentHashMap<>(); - InterfaceProcessor(AppView<?> appView) { + public InterfaceProcessor(AppView<?> appView) { this.appView = appView; helper = new InterfaceDesugaringSyntheticHelper(appView); } + public InterfaceDesugaringSyntheticHelper getHelper() { + return helper; + } + @Override public void process( DexProgramClass iface, InterfaceProcessingDesugaringEventConsumer eventConsumer) { + if (appView.enableWholeProgramOptimizations()) { + // R8 populates all info as part of enqueuing. + return; + } if (!iface.isInterface()) { return; } @@ -75,9 +82,9 @@ } private void analyzeBridges(DexProgramClass iface) { + assert !appView.enableWholeProgramOptimizations(); for (ProgramMethod method : iface.virtualProgramMethods()) { - DexEncodedMethod virtual = method.getDefinition(); - if (!interfaceMethodRemovalChangesApi(virtual, iface)) { + if (!interfaceMethodRemovalChangesApi(method, iface)) { getPostProcessingInterfaceInfo(iface).setHasBridgesToRemove(); return; } @@ -86,9 +93,6 @@ private void ensureCompanionClassMethods( DexProgramClass iface, InterfaceProcessingDesugaringEventConsumer eventConsumer) { - // TODO(b/183998768): Once fixed, the methods should be added for processing. - // D8 and R8 don't need to optimize the methods since they are just moved from interfaces and - // don't need to be re-processed. processVirtualInterfaceMethods(iface); processDirectInterfaceMethods(iface, eventConsumer); } @@ -121,96 +125,76 @@ } private void processVirtualInterfaceMethods(DexProgramClass iface) { + assert !appView.enableWholeProgramOptimizations(); for (ProgramMethod method : iface.virtualProgramMethods()) { DexEncodedMethod virtual = method.getDefinition(); if (helper.isCompatibleDefaultMethod(virtual)) { - if (!canMoveToCompanionClass(virtual)) { - throw new CompilationError( - "One or more instruction is preventing default interface " - + "method from being desugared: " - + method.toSourceString(), - iface.origin); - } - Code code = virtual.getCode(); - if (code == null) { - throw new CompilationError( - "Code is missing for default " + "interface method: " + method.toSourceString(), - iface.origin); - } // Create a new method in a companion class to represent default method implementation. ProgramMethod companion = helper.ensureDefaultAsMethodOfProgramCompanionClassStub(method); - DexEncodedMethod.setDebugInfoWithFakeThisParameter( - code, companion.getReference().getArity(), appView); finalizeMoveToCompanionMethod(method, companion); - getPostProcessingInterfaceInfo(iface) - .mapDefaultMethodToCompanionMethod(virtual, companion.getDefinition()); } } } private void processDirectInterfaceMethods( DexProgramClass iface, InterfaceProcessingDesugaringEventConsumer eventConsumer) { + assert !appView.enableWholeProgramOptimizations(); for (ProgramMethod method : iface.directProgramMethods()) { DexEncodedMethod definition = method.getDefinition(); - if (definition.isClassInitializer()) { - continue; + if (!definition.isClassInitializer()) { + getPostProcessingInterfaceInfo(iface).setHasNonClinitDirectMethods(); + ProgramMethod companion = + helper.ensureMethodOfProgramCompanionClassStub(method, eventConsumer); + finalizeMoveToCompanionMethod(method, companion); } - if (definition.isInstanceInitializer()) { - assert false - : "Unexpected interface instance initializer: " - + method.getReference().toSourceString(); - continue; - } - - getPostProcessingInterfaceInfo(iface).setHasNonClinitDirectMethods(); - - ProgramMethod companion; - if (isStaticMethod(definition)) { - assert definition.isPrivate() || definition.isPublic() - : "Static interface method " - + method.toSourceString() - + " is expected to " - + "either be public or private in " - + iface.origin; - companion = - helper.ensureStaticAsMethodOfProgramCompanionClassStub( - method, eventConsumer::acceptCompanionClassClinit); - } else { - assert definition.isPrivate(); - Code code = definition.getCode(); - if (code == null) { - throw new CompilationError( - "Code is missing for private instance " - + "interface method: " - + method.getReference().toSourceString(), - iface.origin); - } - companion = helper.ensurePrivateAsMethodOfProgramCompanionClassStub(method); - DexEncodedMethod.setDebugInfoWithFakeThisParameter( - code, companion.getReference().getArity(), appView); - } - - finalizeMoveToCompanionMethod(method, companion); - getPostProcessingInterfaceInfo(iface) - .moveMethod(method.getReference(), companion.getReference()); } } - 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()); + public void finalizeMoveToCompanionMethod(ProgramMethod method, ProgramMethod companion) { + assert InvalidCode.isInvalidCode(companion.getDefinition().getCode()); + if (method.getDefinition().getCode() == null) { + throw new CompilationError( + "Code is missing for private instance " + + "interface method: " + + method.getReference().toSourceString(), + method.getOrigin()); + } + if (!canMoveToCompanionClass(method)) { + throw new CompilationError( + "One or more instruction is preventing default interface " + + "method from being desugared: " + + method.toSourceString(), + method.getOrigin()); + } DexProgramClass iface = method.getHolder(); DexEncodedMethod definition = method.getDefinition(); + assert !definition.isInitializer(); + assert !definition.isStatic() || definition.isPrivate() || definition.isPublic() + : "Static interface method " + + method.toSourceString() + + " is expected to " + + "either be public or private in " + + method.getOrigin(); + + if (definition.isStatic() || definition.isPrivate()) { + getPostProcessingInterfaceInfo(iface).setHasNonClinitDirectMethods(); + getPostProcessingInterfaceInfo(iface) + .moveMethod(method.getReference(), companion.getReference()); + } else { + assert helper.isCompatibleDefaultMethod(definition); + getPostProcessingInterfaceInfo(iface) + .mapDefaultMethodToCompanionMethod(method.getDefinition(), companion.getDefinition()); + } if (definition.hasClassFileVersion()) { companion.getDefinition().downgradeClassFileVersion(definition.getClassFileVersion()); } - companion - .getDefinition() - .setCode( - definition.getCode().getCodeAsInlining(companion.getReference(), method.getReference()), - appView); + Code code = + definition.getCode().getCodeAsInlining(companion.getReference(), method.getReference()); + if (!definition.isStatic()) { + DexEncodedMethod.setDebugInfoWithFakeThisParameter( + code, companion.getReference().getArity(), appView); + } + companion.getDefinition().setCode(code, appView); definition.setCode(InvalidCode.getInstance(), appView); } @@ -224,8 +208,8 @@ } } - private boolean canMoveToCompanionClass(DexEncodedMethod method) { - Code code = method.getCode(); + private static boolean canMoveToCompanionClass(ProgramMethod method) { + Code code = method.getDefinition().getCode(); assert code != null; if (code.isDexCode()) { for (Instruction insn : code.asDexCode().instructions) { @@ -254,13 +238,9 @@ // implementation to the companion class of [iface]. This is always the case for non-bridge // methods. Bridge methods that does not override an implementation in a super-interface must // also be kept (such a situation can happen if the vertical class merger merges two interfaces). - private boolean interfaceMethodRemovalChangesApi(DexEncodedMethod method, DexClass iface) { - if (appView.enableWholeProgramOptimizations()) { - if (appView.appInfo().withLiveness().isPinned(method.getReference())) { - return true; - } - } - if (method.accessFlags.isBridge()) { + private boolean interfaceMethodRemovalChangesApi(ProgramMethod method, DexClass iface) { + assert !appView.enableWholeProgramOptimizations(); + if (method.getAccessFlags().isBridge()) { if (appView.options().cfToCfDesugar) { // TODO(b/187176895): Find the compilation causing this to not be removed. return false; @@ -292,27 +272,22 @@ } } - private boolean isStaticMethod(DexEncodedMethod method) { - if (method.accessFlags.isNative()) { - throw new Unimplemented("Native interface methods are not yet supported."); - } - return method.accessFlags.isStatic() - && !appView.dexItemFactory().isClassConstructor(method.getReference()); - } - private InterfaceProcessorNestedGraphLens postProcessInterfaces() { InterfaceProcessorNestedGraphLens.Builder graphLensBuilder = InterfaceProcessorNestedGraphLens.builder(); postProcessingInterfaceInfos.forEach( (iface, info) -> { - if (info.hasNonClinitDirectMethods()) { + if (info.hasNonClinitDirectMethods() || appView.enableWholeProgramOptimizations()) { clearDirectMethods(iface); } if (info.hasDefaultMethodsToImplementationMap()) { info.getDefaultMethodsToImplementation() .forEach( (defaultMethod, companionMethod) -> { - defaultMethod.setDefaultInterfaceMethodImplementation(companionMethod); + assert InvalidCode.isInvalidCode(defaultMethod.getCode()); + assert !InvalidCode.isInvalidCode(companionMethod.getCode()); + defaultMethod.accessFlags.setAbstract(); + defaultMethod.removeCode(); graphLensBuilder.recordCodeMovedToCompanionClass( defaultMethod.getReference(), companionMethod.getReference()); }); @@ -321,6 +296,8 @@ info.getMethodsToMove().forEach(graphLensBuilder::move); } if (info.hasBridgesToRemove()) { + // D8 can remove bridges at this point. + assert !appView.enableWholeProgramOptimizations(); removeBridges(iface); } }); @@ -328,12 +305,12 @@ } private void removeBridges(DexProgramClass iface) { + assert !appView.enableWholeProgramOptimizations(); List<DexEncodedMethod> newVirtualMethods = new ArrayList<>(); for (ProgramMethod method : iface.virtualProgramMethods()) { - DexEncodedMethod virtual = method.getDefinition(); // Remove bridge methods. - if (interfaceMethodRemovalChangesApi(virtual, iface)) { - newVirtualMethods.add(virtual); + if (interfaceMethodRemovalChangesApi(method, iface)) { + newVirtualMethods.add(method.getDefinition()); } } @@ -350,14 +327,11 @@ @Override public void finalizeProcessing(InterfaceProcessingDesugaringEventConsumer eventConsumer) { + // TODO(b/196337368): Simplify this fix-up to be specific for the move of companion methods + // rather than be based on a graph lens. InterfaceProcessorNestedGraphLens graphLens = postProcessInterfaces(); if (graphLens != null) { - if (appView.enableWholeProgramOptimizations()) { - appView.setGraphLens(graphLens); - } new InterfaceMethodRewriterFixup(appView, graphLens).run(); - - graphLens.moveToPending(); } } @@ -366,6 +340,15 @@ iface, ignored -> new PostProcessingInterfaceInfo()); } + public void forEachMethodToMove(BiConsumer<DexMethod, DexMethod> fn) { + postProcessingInterfaceInfos.forEach( + (iface, info) -> { + if (info.methodsToMove != null) { + info.methodsToMove.forEach(fn); + } + }); + } + static class PostProcessingInterfaceInfo { private Map<DexEncodedMethod, DexEncodedMethod> defaultMethodsToImplementation; private Map<DexMethod, DexMethod> methodsToMove; @@ -422,14 +405,11 @@ // Specific lens which remaps invocation types to static since all rewrites performed here // are to static companion methods. - // TODO(b/167345026): Remove the use of this lens. + // TODO(b/196337368): Replace this by a desugaring lens shared for D8 and R8. public static class InterfaceProcessorNestedGraphLens extends NestedGraphLens { - private BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod> extraNewMethodSignatures; - private BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod> - pendingNewMethodSignatures; - private BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod> - pendingExtraNewMethodSignatures; + private final BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod> + extraNewMethodSignatures; public InterfaceProcessorNestedGraphLens( AppView<?> appView, @@ -441,55 +421,12 @@ this.extraNewMethodSignatures = extraNewMethodSignatures; } - public void moveToPending() { - // These are "pending" and installed in the "toggled" state only. - pendingNewMethodSignatures = newMethodSignatures; - pendingExtraNewMethodSignatures = extraNewMethodSignatures; - // The interface methods do not contribute to renaming lens info anymore, so they are cleared. - newMethodSignatures = new EmptyBidirectionalOneToOneMap<>(); - this.extraNewMethodSignatures = new EmptyBidirectionalOneToOneMap<>(); - } - - public static InterfaceProcessorNestedGraphLens find(GraphLens lens) { - if (lens.isInterfaceProcessorLens()) { - return lens.asInterfaceProcessorLens(); - } - if (lens.isIdentityLens()) { - return null; - } - if (lens.isNonIdentityLens()) { - return find(lens.asNonIdentityLens().getPrevious()); - } - assert false; - return null; - } - - public void enableMapping() { - this.newMethodSignatures = pendingExtraNewMethodSignatures; - this.extraNewMethodSignatures = pendingNewMethodSignatures; - } - - public void disableMapping() { - this.newMethodSignatures = new EmptyBidirectionalOneToOneMap<>(); - this.extraNewMethodSignatures = new EmptyBidirectionalOneToOneMap<>(); - } - public BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod> getExtraNewMethodSignatures() { return extraNewMethodSignatures; } @Override - public boolean isInterfaceProcessorLens() { - return true; - } - - @Override - public InterfaceProcessorNestedGraphLens asInterfaceProcessorLens() { - return this; - } - - @Override public boolean isLegitimateToHaveEmptyMappings() { return true; }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java index 39a6ae1..7c26d30 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordRewriter.java
@@ -413,7 +413,6 @@ @Override public boolean needsDesugaring(CfInstruction instruction, ProgramMethod context) { - assert !instruction.isInitClass(); if (instruction.isInvokeDynamic()) { return needsDesugaring(instruction.asInvokeDynamic(), context); }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java index e58024e..ce0646d 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
@@ -399,9 +399,7 @@ }, method -> { targetsToRevisit.add(method); - if (appView.options().testing.callSiteOptimizationInfoInspector != null) { - appView.options().testing.callSiteOptimizationInfoInspector.accept(method); - } + appView.options().testing.callSiteOptimizationInfoInspector.accept(method); }); } if (revisitedMethods != null) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java index c07ad6d..dba8278 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -54,7 +54,6 @@ import com.android.tools.r8.ir.code.ValueTypeConstraint; import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.SourceCode; -import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor.InterfaceProcessorNestedGraphLens; import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget; import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.origin.Origin; @@ -1315,29 +1314,12 @@ public ProgramMethodSet selectMethodsForOutlining() { ProgramMethodSet methodsSelectedForOutlining = ProgramMethodSet.create(); assert outlineSites.isEmpty(); - - // TODO(b/167345026): This is needed to ensure that default interface methods are mapped to - // the corresponding companion methods that contain the code objects. This should be removed - // once default interface methods are desugared prior to the first optimization pass. - InterfaceProcessorNestedGraphLens interfaceProcessorLens = - InterfaceProcessorNestedGraphLens.find(appView.graphLens()); - if (interfaceProcessorLens != null) { - interfaceProcessorLens.enableMapping(); - } - for (LongLivedProgramMethodMultisetBuilder outlineMethods : candidateMethodLists) { if (outlineMethods.size() >= appView.options().outline.threshold) { ProgramMethodMultiset multiset = outlineMethods.build(appView); multiset.forEachEntry((method, ignore) -> methodsSelectedForOutlining.add(method)); } } - - // TODO(b/167345026): Remove once default interface methods are desugared prior to the first - // optimization pass. - if (interfaceProcessorLens != null) { - interfaceProcessorLens.disableMapping(); - } - candidateMethodLists.clear(); return methodsSelectedForOutlining; }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java index ae702f9..d0c82cb 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
@@ -116,7 +116,7 @@ if (dynamicUpperBoundType == null) { continue; } - assert appView.options().callSiteOptimizationOptions().isTypePropagationEnabled(); + assert appView.options().callSiteOptimizationOptions().isDynamicTypePropagationEnabled(); // To avoid the full join of type lattices below, separately check if the nullability of // arguments is improved, and if so, we can eagerly conclude that we've collected useful // call site information for this method. @@ -255,8 +255,6 @@ newCallSiteInfo.dynamicUpperBoundTypes.put( argumentIndex, dynamicType.getDynamicUpperBoundType()); isTop = false; - } else { - newCallSiteInfo.dynamicUpperBoundTypes.put(argumentIndex, staticTypeElement); } } }
diff --git a/src/main/java/com/android/tools/r8/naming/RecordRewritingNamingLens.java b/src/main/java/com/android/tools/r8/naming/RecordRewritingNamingLens.java index e809317..ebada3d 100644 --- a/src/main/java/com/android/tools/r8/naming/RecordRewritingNamingLens.java +++ b/src/main/java/com/android/tools/r8/naming/RecordRewritingNamingLens.java
@@ -83,6 +83,16 @@ } @Override + public boolean hasPrefixRewritingLogic() { + return namingLens.hasPrefixRewritingLogic(); + } + + @Override + public DexString prefixRewrittenType(DexType type) { + return namingLens.prefixRewrittenType(type); + } + + @Override public String lookupPackageName(String packageName) { return namingLens.lookupPackageName(packageName); }
diff --git a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java index f073314..a990959 100644 --- a/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java +++ b/src/main/java/com/android/tools/r8/optimize/VisibilityBridgeRemover.java
@@ -3,18 +3,22 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.optimize; +import static com.android.tools.r8.utils.ThreadUtils.processItems; + import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.graph.PrunedItems; import com.android.tools.r8.logging.Log; import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind; import com.android.tools.r8.shaking.AppInfoWithLiveness; -import com.android.tools.r8.utils.ForEachable; -import com.android.tools.r8.utils.IntBox; import com.google.common.collect.Sets; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; public class VisibilityBridgeRemover { @@ -24,50 +28,14 @@ this.appView = appView; } - private void removeUnneededVisibilityBridgesFromClass(DexProgramClass clazz) { - DexEncodedMethod[] newDirectMethods = - removeUnneededVisibilityBridges( - clazz::forEachProgramDirectMethod, clazz.getMethodCollection().numberOfDirectMethods()); - if (newDirectMethods != null) { - clazz.setDirectMethods(newDirectMethods); - } - DexEncodedMethod[] newVirtualMethods = - removeUnneededVisibilityBridges( - clazz::forEachProgramVirtualMethod, - clazz.getMethodCollection().numberOfVirtualMethods()); - if (newVirtualMethods != null) { - clazz.setVirtualMethods(newVirtualMethods); - } - } - - private DexEncodedMethod[] removeUnneededVisibilityBridges( - ForEachable<ProgramMethod> methods, int size) { - Set<DexEncodedMethod> methodsToBeRemoved = Sets.newIdentityHashSet(); - methods.forEach( - method -> { - if (isUnneededVisibilityBridge(method)) { - methodsToBeRemoved.add(method.getDefinition()); - } - }); - if (!methodsToBeRemoved.isEmpty()) { - DexEncodedMethod[] newMethods = new DexEncodedMethod[size - methodsToBeRemoved.size()]; - IntBox i = new IntBox(0); - methods.forEach( - method -> { - if (!methodsToBeRemoved.contains(method.getDefinition())) { - newMethods[i.getAndIncrement()] = method.getDefinition(); - } - }); - return newMethods; - } - return null; - } - private boolean isUnneededVisibilityBridge(ProgramMethod method) { + // Clean-up the predicate check. if (appView.appInfo().isPinned(method.getReference())) { return false; } DexEncodedMethod definition = method.getDefinition(); + // TODO(b/198133259): Extend to definitions that are not defined as bridges. + // TODO(b/197490164): Remove if method is abstract. if (!definition.isBridge() || definition.isAbstract()) { return false; } @@ -75,33 +43,67 @@ new InvokeSingleTargetExtractor(appView.dexItemFactory()); method.registerCodeReferences(targetExtractor); DexMethod target = targetExtractor.getTarget(); - InvokeKind kind = targetExtractor.getKind(); // javac-generated visibility forward bridge method has same descriptor (name, signature and // return type). - if (target != null && target.hasSameProtoAndName(method.getReference())) { - assert !definition.isPrivate() && !definition.isInstanceInitializer(); - if (kind == InvokeKind.SUPER) { - // This is a visibility forward, so check for the direct target. - DexEncodedMethod targetMethod = - appView.appInfo().unsafeResolveMethodDueToDexFormat(target).getSingleTarget(); - if (targetMethod != null && targetMethod.accessFlags.isPublic()) { - if (Log.ENABLED) { - Log.info( - getClass(), - "Removing visibility forwarding %s -> %s", - method, - targetMethod.getReference()); - } - return true; - } - } + if (target == null || !target.match(method.getReference())) { + return false; } + assert !definition.isPrivate() && !definition.isInstanceInitializer(); + if (!isTargetingSuperMethod(method, targetExtractor.getKind(), target)) { + return false; + } + // This is a visibility forward, so check for the direct target. + DexEncodedMethod targetMethod = + appView.appInfo().unsafeResolveMethodDueToDexFormat(target).getSingleTarget(); + if (targetMethod == null || !targetMethod.accessFlags.isPublic()) { + return false; + } + if (Log.ENABLED) { + Log.info( + getClass(), + "Removing visibility forwarding %s -> %s", + method, + targetMethod.getReference()); + } + return true; + } + + private boolean isTargetingSuperMethod(ProgramMethod method, InvokeKind kind, DexMethod target) { + if (kind == InvokeKind.SUPER) { + return true; + } + if (kind == InvokeKind.STATIC) { + return appView.appInfo().isStrictSubtypeOf(method.getHolderType(), target.holder); + } + assert false : "Unexpected invoke-kind for visibility bridge"; return false; } - public void run() { - for (DexProgramClass clazz : appView.appInfo().classes()) { - removeUnneededVisibilityBridgesFromClass(clazz); - } + public void run(ExecutorService executorService) throws ExecutionException { + // Collect all visibility bridges to remove. + ConcurrentHashMap<DexProgramClass, Set<DexEncodedMethod>> visibilityBridgesToRemove = + new ConcurrentHashMap<>(); + processItems( + appView.appInfo().classes(), + clazz -> { + Set<DexEncodedMethod> bridgesToRemoveForClass = Sets.newIdentityHashSet(); + clazz.forEachProgramMethod( + method -> { + if (isUnneededVisibilityBridge(method)) { + bridgesToRemoveForClass.add(method.getDefinition()); + } + }); + if (!bridgesToRemoveForClass.isEmpty()) { + visibilityBridgesToRemove.put(clazz, bridgesToRemoveForClass); + } + }, + executorService); + // Remove all bridges found. + PrunedItems.Builder builder = PrunedItems.builder(); + visibilityBridgesToRemove.forEach( + (clazz, methods) -> { + clazz.getMethodCollection().removeMethods(methods); + methods.forEach(method -> builder.addRemovedMethod(method.getReference())); + }); } }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java index 2173883..2761296 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
@@ -19,9 +19,11 @@ import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodState; import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference; import com.android.tools.r8.optimize.argumentpropagation.codescanner.VirtualRootMethodsAnalysis; +import com.android.tools.r8.optimize.argumentpropagation.reprocessingcriteria.ArgumentPropagatorReprocessingCriteriaCollection; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.ThreadUtils; import com.android.tools.r8.utils.Timing; +import java.util.Collection; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutionException; @@ -40,6 +42,12 @@ */ private ArgumentPropagatorCodeScanner codeScanner; + /** + * Analyzes the uses of arguments in methods to determine when reprocessing of methods will likely + * not lead to any additional code optimizations. + */ + private ArgumentPropagatorReprocessingCriteriaCollection reprocessingCriteriaCollection; + public ArgumentPropagator(AppView<AppInfoWithLiveness> appView) { assert appView.enableWholeProgramOptimizations(); assert appView.options().isOptimizing(); @@ -63,6 +71,7 @@ timing.begin("Initialize code scanner"); codeScanner = new ArgumentPropagatorCodeScanner(appView); + reprocessingCriteriaCollection = new ArgumentPropagatorReprocessingCriteriaCollection(appView); ImmediateProgramSubtypingInfo immediateSubtypingInfo = ImmediateProgramSubtypingInfo.create(appView); @@ -80,7 +89,7 @@ // Compute the mapping from virtual methods to their root virtual method and the set of // monomorphic virtual methods. new VirtualRootMethodsAnalysis(appView, immediateSubtypingInfo) - .extendVirtualRootMethods(appView.appInfo().classes(), codeScanner); + .extendVirtualRootMethods(classes, codeScanner); }, executorService); @@ -92,12 +101,14 @@ public void scan( ProgramMethod method, IRCode code, MethodProcessor methodProcessor, Timing timing) { if (codeScanner != null) { - // TODO(b/190154391): Do we process synthetic methods using a OneTimeMethodProcessor - // during the primary optimization pass? assert methodProcessor.isPrimaryMethodProcessor(); codeScanner.scan(method, code, timing); + + assert reprocessingCriteriaCollection != null; + reprocessingCriteriaCollection.analyzeArgumentUses(method, code); } else { assert !methodProcessor.isPrimaryMethodProcessor(); + assert reprocessingCriteriaCollection == null; } } @@ -117,8 +128,14 @@ throws ExecutionException { assert !appView.getSyntheticItems().hasPendingSyntheticClasses(); timing.begin("Argument propagator"); - populateParameterOptimizationInfo(executorService, timing); - optimizeMethodParameters(); + ImmediateProgramSubtypingInfo immediateSubtypingInfo = + ImmediateProgramSubtypingInfo.create(appView); + List<Set<DexProgramClass>> stronglyConnectedProgramComponents = + computeStronglyConnectedProgramClasses(appView, immediateSubtypingInfo); + populateParameterOptimizationInfo( + immediateSubtypingInfo, stronglyConnectedProgramComponents, executorService, timing); + optimizeMethodParameters( + immediateSubtypingInfo, stronglyConnectedProgramComponents, executorService); enqueueMethodsForProcessing(postMethodProcessorBuilder); timing.end(); } @@ -127,7 +144,11 @@ * Called by {@link IRConverter} *after* the primary optimization pass to populate the parameter * optimization info. */ - private void populateParameterOptimizationInfo(ExecutorService executorService, Timing timing) + private void populateParameterOptimizationInfo( + ImmediateProgramSubtypingInfo immediateSubtypingInfo, + List<Set<DexProgramClass>> stronglyConnectedProgramComponents, + ExecutorService executorService, + Timing timing) throws ExecutionException { // Unset the scanner since all code objects have been scanned at this point. assert appView.isAllCodeProcessed(); @@ -136,22 +157,40 @@ codeScanner = null; timing.begin("Compute optimization info"); - new ArgumentPropagatorOptimizationInfoPopulator(appView, codeScannerResult) + new ArgumentPropagatorOptimizationInfoPopulator( + appView, + immediateSubtypingInfo, + codeScannerResult, + reprocessingCriteriaCollection, + stronglyConnectedProgramComponents) .populateOptimizationInfo(executorService, timing); + reprocessingCriteriaCollection = null; timing.end(); } /** Called by {@link IRConverter} to optimize method definitions. */ - private void optimizeMethodParameters() { - // TODO(b/190154391): Remove parameters with constant values. - // TODO(b/190154391): Remove unused parameters by simulating they are constant. - // TODO(b/190154391): Strengthen the static type of parameters. - // TODO(b/190154391): If we learn that a method returns a constant, then consider changing its - // return type to void. - // TODO(b/69963623): If we optimize a method to be unconditionally throwing (because it has a - // bottom parameter), then for each caller that becomes unconditionally throwing, we could - // also enqueue the caller's callers for reprocessing. This would propagate the throwing - // information to all call sites. + private void optimizeMethodParameters( + ImmediateProgramSubtypingInfo immediateSubtypingInfo, + List<Set<DexProgramClass>> stronglyConnectedProgramComponents, + ExecutorService executorService) + throws ExecutionException { + Collection<ArgumentPropagatorGraphLens.Builder> partialGraphLensBuilders = + ThreadUtils.processItemsWithResults( + stronglyConnectedProgramComponents, + classes -> + new ArgumentPropagatorProgramOptimizer(appView, immediateSubtypingInfo) + .optimize(classes), + executorService); + + // Merge all the partial, disjoint graph lens builders into a single graph lens. + ArgumentPropagatorGraphLens.Builder graphLensBuilder = + ArgumentPropagatorGraphLens.builder(appView); + partialGraphLensBuilders.forEach(graphLensBuilder::mergeDisjoint); + + ArgumentPropagatorGraphLens graphLens = graphLensBuilder.build(); + if (graphLens != null) { + appView.setGraphLens(graphLens); + } } /** @@ -168,6 +207,7 @@ if (callSiteOptimizationInfo.isConcreteCallSiteOptimizationInfo() && !appView.appInfo().isNeverReprocessMethod(method.getReference())) { postMethodProcessorBuilder.add(method); + appView.testing().callSiteOptimizationInfoInspector.accept(method); } }); }
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java new file mode 100644 index 0000000..0110197 --- /dev/null +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
@@ -0,0 +1,108 @@ +// 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.optimize.argumentpropagation; + +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.graph.GraphLens; +import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens; +import com.android.tools.r8.graph.RewrittenPrototypeDescription; +import com.android.tools.r8.shaking.AppInfoWithLiveness; + +public class ArgumentPropagatorGraphLens extends NonIdentityGraphLens { + + ArgumentPropagatorGraphLens(AppView<AppInfoWithLiveness> appView) { + super(appView); + } + + public static Builder builder(AppView<AppInfoWithLiveness> appView) { + return new Builder(appView); + } + + @Override + public DexType getOriginalType(DexType type) { + return getPrevious().getOriginalType(type); + } + + @Override + public Iterable<DexType> getOriginalTypes(DexType type) { + return getPrevious().getOriginalTypes(type); + } + + @Override + public DexField getOriginalFieldSignature(DexField field) { + return getPrevious().getOriginalFieldSignature(field); + } + + @Override + public DexField getRenamedFieldSignature(DexField originalField) { + return getPrevious().getRenamedFieldSignature(originalField); + } + + @Override + public DexMethod getOriginalMethodSignature(DexMethod method) { + return getPrevious().getOriginalMethodSignature(method); + } + + @Override + public DexMethod getRenamedMethodSignature(DexMethod originalMethod, GraphLens applied) { + return applied != this + ? getPrevious().getRenamedMethodSignature(originalMethod, applied) + : originalMethod; + } + + @Override + protected DexType internalDescribeLookupClassType(DexType previous) { + return previous; + } + + @Override + protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) { + return previous; + } + + @Override + protected MethodLookupResult internalDescribeLookupMethod( + MethodLookupResult previous, DexMethod context) { + return previous; + } + + @Override + protected DexMethod internalGetPreviousMethodSignature(DexMethod method) { + return method; + } + + @Override + public boolean isContextFreeForMethods() { + return getPrevious().isContextFreeForMethods(); + } + + @Override + public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(DexMethod method) { + return getPrevious().lookupPrototypeChangesForMethodDefinition(method); + } + + public static class Builder { + + private final AppView<AppInfoWithLiveness> appView; + + Builder(AppView<AppInfoWithLiveness> appView) { + this.appView = appView; + } + + public ArgumentPropagatorGraphLens.Builder mergeDisjoint( + ArgumentPropagatorGraphLens.Builder partialGraphLensBuilder) { + // TODO(b/190154391): Implement. + return this; + } + + public ArgumentPropagatorGraphLens build() { + // TODO(b/190154391): Implement. + return null; + } + } +}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java index 6d4f3b7..53876ab 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
@@ -4,7 +4,6 @@ package com.android.tools.r8.optimize.argumentpropagation; -import static com.android.tools.r8.optimize.argumentpropagation.utils.StronglyConnectedProgramClasses.computeStronglyConnectedProgramClasses; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexEncodedMethod; @@ -23,6 +22,8 @@ import com.android.tools.r8.optimize.argumentpropagation.propagation.InParameterFlowPropagator; import com.android.tools.r8.optimize.argumentpropagation.propagation.InterfaceMethodArgumentPropagator; import com.android.tools.r8.optimize.argumentpropagation.propagation.VirtualDispatchMethodArgumentPropagator; +import com.android.tools.r8.optimize.argumentpropagation.reprocessingcriteria.ArgumentPropagatorReprocessingCriteriaCollection; +import com.android.tools.r8.optimize.argumentpropagation.reprocessingcriteria.MethodReprocessingCriteria; import com.android.tools.r8.optimize.argumentpropagation.utils.WideningUtils; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.ThreadUtils; @@ -42,20 +43,22 @@ private final AppView<AppInfoWithLiveness> appView; private final MethodStateCollectionByReference methodStates; + private final ArgumentPropagatorReprocessingCriteriaCollection reprocessingCriteriaCollection; private final ImmediateProgramSubtypingInfo immediateSubtypingInfo; - private final List<Set<DexProgramClass>> stronglyConnectedComponents; + private final List<Set<DexProgramClass>> stronglyConnectedProgramComponents; ArgumentPropagatorOptimizationInfoPopulator( - AppView<AppInfoWithLiveness> appView, MethodStateCollectionByReference methodStates) { + AppView<AppInfoWithLiveness> appView, + ImmediateProgramSubtypingInfo immediateSubtypingInfo, + MethodStateCollectionByReference methodStates, + ArgumentPropagatorReprocessingCriteriaCollection reprocessingCriteriaCollection, + List<Set<DexProgramClass>> stronglyConnectedProgramComponents) { this.appView = appView; - this.methodStates = methodStates; - - ImmediateProgramSubtypingInfo immediateSubtypingInfo = - ImmediateProgramSubtypingInfo.create(appView); this.immediateSubtypingInfo = immediateSubtypingInfo; - this.stronglyConnectedComponents = - computeStronglyConnectedProgramClasses(appView, immediateSubtypingInfo); + this.methodStates = methodStates; + this.reprocessingCriteriaCollection = reprocessingCriteriaCollection; + this.stronglyConnectedProgramComponents = stronglyConnectedProgramComponents; } /** @@ -74,7 +77,9 @@ // that the method returns the constant. timing.begin("Propagate argument information for virtual methods"); ThreadUtils.processItems( - stronglyConnectedComponents, this::processStronglyConnectedComponent, executorService); + stronglyConnectedProgramComponents, + this::processStronglyConnectedComponent, + executorService); timing.end(); // Solve the parameter flow constraints. @@ -190,6 +195,14 @@ return true; }); + // If we have any reprocessing criteria for the given method, check that they are satisfied + // before reenqueing. + MethodReprocessingCriteria reprocessingCriteria = + reprocessingCriteriaCollection.getReprocessingCriteria(method); + if (!reprocessingCriteria.shouldReprocess(appView, method, monomorphicMethodState)) { + return; + } + method .getDefinition() .joinCallSiteOptimizationInfo(
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java new file mode 100644 index 0000000..859830b --- /dev/null +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -0,0 +1,37 @@ +// 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.optimize.argumentpropagation; + +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo; +import com.android.tools.r8.shaking.AppInfoWithLiveness; +import java.util.Set; + +public class ArgumentPropagatorProgramOptimizer { + + private final AppView<AppInfoWithLiveness> appView; + private final ImmediateProgramSubtypingInfo immediateSubtypingInfo; + + public ArgumentPropagatorProgramOptimizer( + AppView<AppInfoWithLiveness> appView, ImmediateProgramSubtypingInfo immediateSubtypingInfo) { + this.appView = appView; + this.immediateSubtypingInfo = immediateSubtypingInfo; + } + + // TODO(b/190154391): Remove parameters with constant values. + // TODO(b/190154391): Remove unused parameters by simulating they are constant. + // TODO(b/190154391): Strengthen the static type of parameters. + // TODO(b/190154391): If we learn that a method returns a constant, then consider changing its + // return type to void. + // TODO(b/69963623): If we optimize a method to be unconditionally throwing (because it has a + // bottom parameter), then for each caller that becomes unconditionally throwing, we could + // also enqueue the caller's callers for reprocessing. This would propagate the throwing + // information to all call sites. + public ArgumentPropagatorGraphLens.Builder optimize( + Set<DexProgramClass> stronglyConnectedProgramClasses) { + return ArgumentPropagatorGraphLens.builder(appView); + } +}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java index 6dc6c5c..3244db4 100644 --- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java
@@ -51,6 +51,10 @@ return this; } + public AbstractValue getAbstractValue() { + return abstractValue; + } + @Override public AbstractValue getAbstractValue(AppView<AppInfoWithLiveness> appView) { return abstractValue;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysFalseParameterReprocessingCriteria.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysFalseParameterReprocessingCriteria.java new file mode 100644 index 0000000..90d7e4a --- /dev/null +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysFalseParameterReprocessingCriteria.java
@@ -0,0 +1,56 @@ +// 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.optimize.argumentpropagation.reprocessingcriteria; + +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteParameterState; +import com.android.tools.r8.shaking.AppInfoWithLiveness; + +/** + * Represents that a parameter should never be reprocessed even if we have non-trivial information + * about it (e.g., abstract value, dynamic type, nullability). + * + * <p>Example: This is used for unused parameters. + */ +public class AlwaysFalseParameterReprocessingCriteria extends ParameterReprocessingCriteria { + + public static final AlwaysFalseParameterReprocessingCriteria INSTANCE = + new AlwaysFalseParameterReprocessingCriteria(); + + private AlwaysFalseParameterReprocessingCriteria() {} + + public static AlwaysFalseParameterReprocessingCriteria get() { + return INSTANCE; + } + + @Override + public boolean isNeverReprocess() { + return true; + } + + @Override + public boolean shouldReprocess( + AppView<AppInfoWithLiveness> appView, + ConcreteParameterState parameterState, + DexType parameterType) { + return false; + } + + @Override + public boolean shouldReprocessDueToAbstractValue() { + return false; + } + + @Override + public boolean shouldReprocessDueToDynamicType() { + return false; + } + + @Override + public boolean shouldReprocessDueToNullability() { + return false; + } +}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysTrueParameterReprocessingCriteria.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysTrueParameterReprocessingCriteria.java new file mode 100644 index 0000000..72d382f --- /dev/null +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysTrueParameterReprocessingCriteria.java
@@ -0,0 +1,54 @@ +// 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.optimize.argumentpropagation.reprocessingcriteria; + +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteParameterState; +import com.android.tools.r8.shaking.AppInfoWithLiveness; + +/** + * Represents that a parameter should always be reprocessed if we have non-trivial information about + * it (e.g., abstract value, dynamic type, nullability). + */ +public class AlwaysTrueParameterReprocessingCriteria extends ParameterReprocessingCriteria { + + public static final AlwaysTrueParameterReprocessingCriteria INSTANCE = + new AlwaysTrueParameterReprocessingCriteria(); + + private AlwaysTrueParameterReprocessingCriteria() {} + + public static AlwaysTrueParameterReprocessingCriteria get() { + return INSTANCE; + } + + @Override + public boolean isAlwaysReprocess() { + return true; + } + + @Override + public boolean shouldReprocess( + AppView<AppInfoWithLiveness> appView, + ConcreteParameterState parameterState, + DexType parameterType) { + return true; + } + + @Override + public boolean shouldReprocessDueToAbstractValue() { + return true; + } + + @Override + public boolean shouldReprocessDueToDynamicType() { + return true; + } + + @Override + public boolean shouldReprocessDueToNullability() { + return true; + } +}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ArgumentPropagatorReprocessingCriteriaCollection.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ArgumentPropagatorReprocessingCriteriaCollection.java new file mode 100644 index 0000000..301738e --- /dev/null +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ArgumentPropagatorReprocessingCriteriaCollection.java
@@ -0,0 +1,142 @@ +// 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.optimize.argumentpropagation.reprocessingcriteria; + +import static com.android.tools.r8.ir.code.Opcodes.ASSUME; +import static com.android.tools.r8.ir.code.Opcodes.IF; +import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_GET; +import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_PUT; +import static com.android.tools.r8.ir.code.Opcodes.INVOKE_DIRECT; +import static com.android.tools.r8.ir.code.Opcodes.INVOKE_INTERFACE; +import static com.android.tools.r8.ir.code.Opcodes.INVOKE_STATIC; +import static com.android.tools.r8.ir.code.Opcodes.INVOKE_SUPER; +import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL; +import static com.android.tools.r8.ir.code.Opcodes.RETURN; + +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult; +import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.ir.code.Argument; +import com.android.tools.r8.ir.code.IRCode; +import com.android.tools.r8.ir.code.Instruction; +import com.android.tools.r8.ir.code.InstructionIterator; +import com.android.tools.r8.ir.code.InvokeMethodWithReceiver; +import com.android.tools.r8.shaking.AppInfoWithLiveness; +import it.unimi.dsi.fastutil.ints.Int2ReferenceMap; +import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class ArgumentPropagatorReprocessingCriteriaCollection { + + private final AppView<AppInfoWithLiveness> appView; + + private final Map<DexMethod, MethodReprocessingCriteria> reproccessingCriteria = + new ConcurrentHashMap<>(); + + public ArgumentPropagatorReprocessingCriteriaCollection(AppView<AppInfoWithLiveness> appView) { + this.appView = appView; + } + + public MethodReprocessingCriteria getReprocessingCriteria(ProgramMethod method) { + return reproccessingCriteria.getOrDefault( + method.getReference(), MethodReprocessingCriteria.empty()); + } + + /** + * Analyzes the uses of the method arguments to determine if a given piece of optimization info + * related to the arguments should or should not lead to reprocessing of the given method. + */ + public void analyzeArgumentUses(ProgramMethod method, IRCode code) { + Int2ReferenceMap<ParameterReprocessingCriteria> methodReprocessingCriteria = + new Int2ReferenceOpenHashMap<>(); + + // Analyze each of the Argument instructions. + InstructionIterator instructionIterator = code.entryBlock().iterator(); + for (Argument argument = instructionIterator.next().asArgument(); + argument != null; + argument = instructionIterator.next().asArgument()) { + ParameterReprocessingCriteria reprocessingCriteria = analyzeArgumentUses(argument); + if (!reprocessingCriteria.isAlwaysReprocess()) { + methodReprocessingCriteria.put(argument.getIndex(), reprocessingCriteria); + } + } + + // If there are some parameters which should not be naively reprocessed if they hold non-trivial + // optimization info, then record this information. If the map is empty, then the method should + // always be reprocessed if we find non-trivial optimization info for some of the parameters. + if (!methodReprocessingCriteria.isEmpty()) { + reproccessingCriteria.put( + method.getReference(), new MethodReprocessingCriteria(methodReprocessingCriteria)); + } + } + + private ParameterReprocessingCriteria analyzeArgumentUses(Argument argument) { + // For now, always reprocess if we have non-trivial information about primitive types. + // TODO(b/190154391): Introduce analysis for primitives. + if (argument.getOutType().isPrimitiveType()) { + return ParameterReprocessingCriteria.alwaysReprocess(); + } + + ParameterReprocessingCriteria.Builder builder = ParameterReprocessingCriteria.builder(); + for (Instruction instruction : argument.outValue().aliasedUsers()) { + // TODO(b/190154391): Introduce analysis for usefulness of abstract value and nullability. + builder.setReprocessDueToAbstractValue().setReprocessDueToNullability(); + + switch (instruction.opcode()) { + case ASSUME: + case IF: + case INSTANCE_GET: + case INSTANCE_PUT: + case RETURN: + break; + + case INVOKE_DIRECT: + case INVOKE_STATIC: + // Do not reprocess calls without dynamic dispatch due to dynamic type information. + break; + + case INVOKE_INTERFACE: + case INVOKE_SUPER: + case INVOKE_VIRTUAL: + { + InvokeMethodWithReceiver invoke = instruction.asInvokeMethodWithReceiver(); + + // Do not reprocess calls with dynamic dispatch due to dynamic type information of a + // non-receiver operand. + if (invoke.getReceiver().getAliasedValue() != argument.outValue()) { + break; + } + + // Do not reprocess the method if the invoke resolves to a library method. + SingleResolutionResult resolutionResult = + appView + .appInfo() + .unsafeResolveMethodDueToDexFormat(invoke.getInvokedMethod()) + .asSingleResolution(); + if (resolutionResult == null + || !resolutionResult.getResolvedHolder().isProgramClass()) { + break; + } + + builder.setReprocessDueToDynamicType(); + break; + } + + default: + // Conservatively reprocess the method if we have a non-trivial dynamic type. + builder.setReprocessDueToDynamicType(); + break; + } + + if (builder.shouldAlwaysReprocess()) { + break; + } + } + + return builder.build(); + } +}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/MethodReprocessingCriteria.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/MethodReprocessingCriteria.java new file mode 100644 index 0000000..527ee9e --- /dev/null +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/MethodReprocessingCriteria.java
@@ -0,0 +1,62 @@ +// 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.optimize.argumentpropagation.reprocessingcriteria; + +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.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodState; +import com.android.tools.r8.optimize.argumentpropagation.codescanner.ParameterState; +import com.android.tools.r8.shaking.AppInfoWithLiveness; +import it.unimi.dsi.fastutil.ints.Int2ReferenceMap; +import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap; + +public class MethodReprocessingCriteria { + + public static final MethodReprocessingCriteria EMPTY = new MethodReprocessingCriteria(); + + private final Int2ReferenceMap<ParameterReprocessingCriteria> reproccesingCriteria; + + private MethodReprocessingCriteria() { + this.reproccesingCriteria = new Int2ReferenceOpenHashMap<>(); + } + + public MethodReprocessingCriteria( + Int2ReferenceMap<ParameterReprocessingCriteria> reproccesingCriteria) { + assert !reproccesingCriteria.isEmpty(); + this.reproccesingCriteria = reproccesingCriteria; + } + + public static MethodReprocessingCriteria empty() { + return EMPTY; + } + + public ParameterReprocessingCriteria getParameterReprocessingCriteria(int parameterIndex) { + return reproccesingCriteria.getOrDefault( + parameterIndex, ParameterReprocessingCriteria.alwaysReprocess()); + } + + public boolean shouldReprocess( + AppView<AppInfoWithLiveness> appView, + ProgramMethod method, + ConcreteMonomorphicMethodState methodState) { + for (int parameterIndex = 0; parameterIndex < methodState.size(); parameterIndex++) { + ParameterState parameterState = methodState.getParameterState(parameterIndex); + assert !parameterState.isBottom(); + if (parameterState.isUnknown()) { + continue; + } + + ParameterReprocessingCriteria parameterReprocessingCriteria = + getParameterReprocessingCriteria(parameterIndex); + DexType parameterType = method.getArgumentType(parameterIndex); + if (parameterReprocessingCriteria.shouldReprocess( + appView, parameterState.asConcrete(), parameterType)) { + return true; + } + } + return false; + } +}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/NonTrivialParameterReprocessingCriteria.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/NonTrivialParameterReprocessingCriteria.java new file mode 100644 index 0000000..b55bf13 --- /dev/null +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/NonTrivialParameterReprocessingCriteria.java
@@ -0,0 +1,91 @@ +// 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.optimize.argumentpropagation.reprocessingcriteria; + +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.ir.analysis.type.DynamicType; +import com.android.tools.r8.ir.analysis.type.Nullability; +import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteParameterState; +import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcretePrimitiveTypeParameterState; +import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteReferenceTypeParameterState; +import com.android.tools.r8.optimize.argumentpropagation.utils.WideningUtils; +import com.android.tools.r8.shaking.AppInfoWithLiveness; + +/** + * Represents that a parameter should be reprocessed under certain conditions if we have non-trivial + * information about it (e.g., abstract value, dynamic type, nullability). + * + * <p>Example: If we determine that a parameter should not be reprocessed if we only have + * non-trivial information about its dynamic type, then an instance of this class is used which + * returns false in {@link #shouldReprocessDueToDynamicType()}. + */ +public class NonTrivialParameterReprocessingCriteria extends ParameterReprocessingCriteria { + + public NonTrivialParameterReprocessingCriteria(boolean reprocessDueToDynamicType) { + assert !reprocessDueToDynamicType; + } + + @Override + public boolean shouldReprocess( + AppView<AppInfoWithLiveness> appView, + ConcreteParameterState parameterState, + DexType parameterType) { + if (parameterState.isReferenceParameter()) { + return shouldReprocess(appView, parameterState.asReferenceParameter(), parameterType); + } else { + assert parameterState.isPrimitiveParameter(); + return shouldReprocess(appView, parameterState.asPrimitiveParameter(), parameterType); + } + } + + @Override + public boolean shouldReprocessDueToAbstractValue() { + return true; + } + + @Override + public boolean shouldReprocessDueToDynamicType() { + return false; + } + + @Override + public boolean shouldReprocessDueToNullability() { + return true; + } + + private boolean shouldReprocess( + AppView<AppInfoWithLiveness> appView, + ConcretePrimitiveTypeParameterState parameterState, + DexType parameterType) { + return true; + } + + private boolean shouldReprocess( + AppView<AppInfoWithLiveness> appView, + ConcreteReferenceTypeParameterState parameterState, + DexType parameterType) { + if (shouldReprocessDueToAbstractValue() + && !parameterState.getAbstractValue(appView).isUnknown()) { + return true; + } + if (shouldReprocessDueToDynamicType()) { + DynamicType widenedDynamicType = + WideningUtils.widenDynamicNonReceiverType( + appView, + parameterState.getDynamicType().withNullability(Nullability.maybeNull()), + parameterType); + if (!widenedDynamicType.isUnknown()) { + return true; + } + } + if (shouldReprocessDueToNullability() + && !parameterState.isReceiverParameter() + && !parameterState.getNullability().isUnknown()) { + return true; + } + return false; + } +}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ParameterReprocessingCriteria.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ParameterReprocessingCriteria.java new file mode 100644 index 0000000..9022f81 --- /dev/null +++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ParameterReprocessingCriteria.java
@@ -0,0 +1,86 @@ +// 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.optimize.argumentpropagation.reprocessingcriteria; + +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteParameterState; +import com.android.tools.r8.shaking.AppInfoWithLiveness; + +public abstract class ParameterReprocessingCriteria { + + public static AlwaysTrueParameterReprocessingCriteria alwaysReprocess() { + return AlwaysTrueParameterReprocessingCriteria.get(); + } + + public static AlwaysFalseParameterReprocessingCriteria neverReprocess() { + return AlwaysFalseParameterReprocessingCriteria.get(); + } + + public static Builder builder() { + return new Builder(); + } + + public boolean isAlwaysReprocess() { + return false; + } + + public boolean isNeverReprocess() { + return false; + } + + public abstract boolean shouldReprocess( + AppView<AppInfoWithLiveness> appView, + ConcreteParameterState parameterState, + DexType parameterType); + + public abstract boolean shouldReprocessDueToAbstractValue(); + + public abstract boolean shouldReprocessDueToDynamicType(); + + public abstract boolean shouldReprocessDueToNullability(); + + public static class Builder { + + private boolean reprocessDueToAbstractValue; + private boolean reprocessDueToDynamicType; + private boolean reprocessDueToNullability; + + Builder setReprocessDueToAbstractValue() { + reprocessDueToAbstractValue = true; + return this; + } + + Builder setReprocessDueToDynamicType() { + reprocessDueToDynamicType = true; + return this; + } + + Builder setReprocessDueToNullability() { + reprocessDueToNullability = true; + return this; + } + + boolean shouldAlwaysReprocess() { + return reprocessDueToAbstractValue && reprocessDueToDynamicType && reprocessDueToNullability; + } + + boolean shouldNeverReprocess() { + return !reprocessDueToAbstractValue + && !reprocessDueToDynamicType + && !reprocessDueToNullability; + } + + public ParameterReprocessingCriteria build() { + if (shouldAlwaysReprocess()) { + return alwaysReprocess(); + } + if (shouldNeverReprocess()) { + return neverReprocess(); + } + return new NonTrivialParameterReprocessingCriteria(reprocessDueToDynamicType); + } + } +}
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 be31b16..acc8410 100644 --- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java +++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -1048,6 +1048,10 @@ keepInfo.mutate( keepInfo -> keepInfo.removeKeepInfoForPrunedItems(prunedItems.getRemovedClasses())); } + if (!prunedItems.getRemovedMethods().isEmpty()) { + keepInfo.mutate( + keepInfo -> keepInfo.removeKeepInfoForPrunedItems(prunedItems.getRemovedMethods())); + } return new AppInfoWithLiveness(this, prunedItems); }
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 41eb11b..66e9136 100644 --- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java +++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -6,6 +6,7 @@ import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull; import static com.android.tools.r8.graph.FieldAccessInfoImpl.MISSING_FIELD_ACCESS_INFO; import static com.android.tools.r8.ir.desugar.LambdaDescriptor.isLambdaMetafactoryMethod; +import static com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor.ExcludeDexResources; import static com.android.tools.r8.naming.IdentifierNameStringUtils.identifyIdentifier; import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod; import static com.android.tools.r8.utils.FunctionUtils.ignoreArgument; @@ -59,6 +60,7 @@ import com.android.tools.r8.graph.FieldResolutionResult; import com.android.tools.r8.graph.GenericSignatureEnqueuerAnalysis; import com.android.tools.r8.graph.InnerClassAttribute; +import com.android.tools.r8.graph.InvalidCode; import com.android.tools.r8.graph.LookupLambdaTarget; import com.android.tools.r8.graph.LookupTarget; import com.android.tools.r8.graph.MethodAccessInfoCollection; @@ -98,7 +100,10 @@ import com.android.tools.r8.ir.desugar.LambdaClass; import com.android.tools.r8.ir.desugar.LambdaDescriptor; import com.android.tools.r8.ir.desugar.ProgramAdditions; +import com.android.tools.r8.ir.desugar.constantdynamic.ConstantDynamicClass; import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAPIConverter; +import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade; +import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor; import com.android.tools.r8.kotlin.KotlinMetadataEnqueuerExtension; import com.android.tools.r8.logging.Log; import com.android.tools.r8.naming.identifiernamestring.IdentifierNameStringLookupResult; @@ -120,6 +125,7 @@ import com.android.tools.r8.shaking.ScopedDexMethodSet.AddMethodIfMoreVisibleResult; import com.android.tools.r8.synthesis.SyntheticItems.SynthesizingContextOracle; import com.android.tools.r8.utils.Action; +import com.android.tools.r8.utils.BooleanBox; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.IteratorUtils; import com.android.tools.r8.utils.OptionalBool; @@ -131,10 +137,10 @@ import com.android.tools.r8.utils.Visibility; import com.android.tools.r8.utils.WorkList; import com.android.tools.r8.utils.collections.ProgramFieldSet; +import com.android.tools.r8.utils.collections.ProgramMethodMap; import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.base.Equivalence.Wrapper; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.collect.Sets.SetView; @@ -423,7 +429,20 @@ private final GraphReporter graphReporter; private final CfInstructionDesugaringCollection desugaring; - private final ProgramMethodSet pendingDesugaring = ProgramMethodSet.create(); + private final ProgramMethodSet pendingCodeDesugaring = ProgramMethodSet.create(); + + // Collections for tracing progress on interface method desugaring. + + // The pending method move set is all the methods that need to be moved to companions. + // They may or may not need desugaring. + private final ProgramMethodSet pendingMethodMove = ProgramMethodSet.create(); + + // The inverse map records references to companion methods that may now be active but yet to + // be moved. + private final ProgramMethodMap<ProgramMethod> pendingMethodMoveInverse = + ProgramMethodMap.createConcurrent(); + + private final InterfaceProcessor interfaceProcessor; Enqueuer( AppView<? extends AppInfoWithClassHierarchy> appView, @@ -466,10 +485,13 @@ failedFieldResolutionTargets = SetUtils.newIdentityHashSet(0); liveMethods = new LiveMethodsSet(graphReporter::registerMethod); liveFields = new LiveFieldsSet(graphReporter::registerField); - desugaring = - mode.isInitialTreeShaking() - ? CfInstructionDesugaringCollection.create(appView) - : CfInstructionDesugaringCollection.empty(); + if (mode.isInitialTreeShaking()) { + desugaring = CfInstructionDesugaringCollection.create(appView); + interfaceProcessor = new InterfaceProcessor(appView); + } else { + desugaring = CfInstructionDesugaringCollection.empty(); + interfaceProcessor = null; + } objectAllocationInfoCollection = ObjectAllocationInfoCollectionImpl.builder(mode.isInitialTreeShaking(), graphReporter); @@ -3080,6 +3102,7 @@ public EnqueuerResult traceApplication( RootSet rootSet, ExecutorService executorService, Timing timing) throws ExecutionException { this.rootSet = rootSet; + rootSet.pendingMethodMoveInverse.forEach(pendingMethodMoveInverse::put); // Translate the result of root-set computation into enqueuer actions. if (mode.isTreeShaking() && appView.options().hasProguardConfiguration() @@ -3324,6 +3347,8 @@ private final Map<DexMethod, ProgramMethod> liveMethods = new ConcurrentHashMap<>(); + private final ProgramMethodSet neverInlineMethods = ProgramMethodSet.createConcurrent(); + private final Map<DexType, DexClasspathClass> syntheticClasspathClasses = new ConcurrentHashMap<>(); @@ -3355,7 +3380,7 @@ public void addLiveClasspathClass(DexClasspathClass clazz) { DexClasspathClass old = syntheticClasspathClasses.put(clazz.type, clazz); - assert old == null; + assert old == null || old == clazz; } public void addLiveMethods(Iterable<ProgramMethod> methods) { @@ -3364,8 +3389,12 @@ public void addLiveMethod(ProgramMethod method) { DexMethod signature = method.getDefinition().getReference(); - assert !liveMethods.containsKey(signature); - liveMethods.put(signature, method); + ProgramMethod old = liveMethods.put(signature, method); + assert old == null; + } + + public void addMethodWithDesugaredCodeForTracing(ProgramMethod method) { + desugaredMethods.add(method); } public void injectInterface(DexProgramClass clazz, DexClass newInterface) { @@ -3386,6 +3415,10 @@ return set; } + public void addNeverInlineMethod(ProgramMethod method) { + neverInlineMethods.add(method); + } + void enqueueWorkItems(Enqueuer enqueuer) { assert enqueuer.mode.isInitialTreeShaking(); @@ -3409,6 +3442,8 @@ enqueuer.objectAllocationInfoCollection.injectInterfaces( enqueuer.appInfo(), clazz, itfs); }); + + neverInlineMethods.forEach(m -> enqueuer.rootSet.neverInline.add(m.getReference())); } } @@ -3436,31 +3471,95 @@ additions.enqueueWorkItems(this); } + private boolean mustMoveToInterfaceCompanionMethod(ProgramMethod method) { + return method.getHolder().isInterface() + && method.getDefinition().isNonAbstractNonNativeMethod() + && !method.getDefinition().isInitializer(); + } + + private boolean addToPendingDesugaring(ProgramMethod method) { + if (options.isInterfaceMethodDesugaringEnabled()) { + if (mustMoveToInterfaceCompanionMethod(method)) { + pendingMethodMove.add(method); + return true; + } + ProgramMethod nonMovedMethod = pendingMethodMoveInverse.get(method); + if (nonMovedMethod != null) { + // Any non-moved code must be a proper pending item. + assert InvalidCode.isInvalidCode(method.getDefinition().getCode()); + assert !InvalidCode.isInvalidCode(nonMovedMethod.getDefinition().getCode()); + pendingMethodMove.add(nonMovedMethod); + return true; + } + } + if (desugaring.needsDesugaring(method)) { + pendingCodeDesugaring.add(method); + return true; + } + return false; + } + private void desugar(SyntheticAdditions additions) throws ExecutionException { - if (pendingDesugaring.isEmpty()) { + if (pendingCodeDesugaring.isEmpty() && pendingMethodMove.isEmpty()) { return; } + // All non-moving methods are ready for tracing post desugar. + pendingCodeDesugaring.forEach(additions::addMethodWithDesugaredCodeForTracing); + // Then amend the desugar set with the move methods that need desugaring. + for (ProgramMethod method : pendingMethodMove) { + if (desugaring.needsDesugaring(method)) { + pendingCodeDesugaring.add(method); + } + } + + R8CfInstructionDesugaringEventConsumer eventConsumer = + CfInstructionDesugaringEventConsumer.createForR8( + appView, + this::recordLambdaSynthesizingContext, + this::recordConstantDynamicSynthesizingContext, + this::recordTwrCloseResourceMethodSynthesizingContext, + additions, + (method, companion) -> { + if (!isMethodLive(method)) { + // Record the original placement of the companion method such that we can desugar + // and transfer the code if and when the companion method becomes live. + pendingMethodMoveInverse.put(companion, method); + } + }); + // Prepare desugaring by collecting all the synthetic methods required on program classes. ProgramAdditions programAdditions = new ProgramAdditions(); ThreadUtils.processItems( - pendingDesugaring, method -> desugaring.prepare(method, programAdditions), executorService); + pendingCodeDesugaring, + method -> desugaring.prepare(method, programAdditions), + executorService); programAdditions.apply(executorService); - R8CfInstructionDesugaringEventConsumer desugaringEventConsumer = - CfInstructionDesugaringEventConsumer.createForR8( - appView, - this::recordLambdaSynthesizingContext, - this::recordTwrCloseResourceMethodSynthesizingContext, - additions); + // Then do the actual desugaring. ThreadUtils.processItems( - pendingDesugaring, - method -> - desugaring.desugar(method, additions.getMethodContext(method), desugaringEventConsumer), + pendingCodeDesugaring, + method -> desugaring.desugar(method, additions.getMethodContext(method), eventConsumer), executorService); - desugaringEventConsumer.finalizeDesugaring(); - Iterables.addAll(additions.desugaredMethods, pendingDesugaring); - pendingDesugaring.clear(); + + // Move the pending methods and mark them live and ready for tracing. + for (ProgramMethod method : pendingMethodMove) { + ProgramMethod companion = + interfaceProcessor + .getHelper() + .ensureMethodOfProgramCompanionClassStub(method, eventConsumer); + interfaceProcessor.finalizeMoveToCompanionMethod(method, companion); + pendingMethodMoveInverse.remove(companion); + if (!isMethodLive(companion)) { + additions.addLiveMethod(companion); + } + additions.addMethodWithDesugaredCodeForTracing(companion); + } + + eventConsumer.finalizeDesugaring(); + + pendingMethodMove.clear(); + pendingCodeDesugaring.clear(); } private void recordLambdaSynthesizingContext(LambdaClass lambdaClass, ProgramMethod context) { @@ -3469,6 +3568,13 @@ } } + private void recordConstantDynamicSynthesizingContext( + ConstantDynamicClass constantDynamicClass, ProgramMethod context) { + synchronized (synthesizingContexts) { + synthesizingContexts.put(constantDynamicClass.getConstantDynamicProgramClass(), context); + } + } + private void recordTwrCloseResourceMethodSynthesizingContext( ProgramMethod closeMethod, ProgramMethod context) { synchronized (synthesizingContexts) { @@ -3481,8 +3587,6 @@ DexProgramClass holder = bridge.getHolder(); DexEncodedMethod method = bridge.getDefinition(); holder.addVirtualMethod(method); - additions.addLiveMethodWithKeepAction( - bridge, joiner -> joiner.disallowOptimization().disallowShrinking()); } syntheticInterfaceMethodBridges.clear(); } @@ -3593,15 +3697,15 @@ rootSet.mayHaveSideEffects, rootSet.noSideEffects, rootSet.assumedValues, - rootSet.alwaysInline, - rootSet.forceInline, - rootSet.neverInline, - rootSet.neverInlineDueToSingleCaller, - rootSet.whyAreYouNotInlining, - rootSet.keepConstantArguments, - rootSet.keepUnusedArguments, - rootSet.reprocess, - rootSet.neverReprocess, + amendWithCompanionMethods(rootSet.alwaysInline), + amendWithCompanionMethods(rootSet.forceInline), + amendWithCompanionMethods(rootSet.neverInline), + amendWithCompanionMethods(rootSet.neverInlineDueToSingleCaller), + amendWithCompanionMethods(rootSet.whyAreYouNotInlining), + amendWithCompanionMethods(rootSet.keepConstantArguments), + amendWithCompanionMethods(rootSet.keepUnusedArguments), + amendWithCompanionMethods(rootSet.reprocess), + amendWithCompanionMethods(rootSet.neverReprocess), rootSet.alwaysClassInline, rootSet.neverClassInline, noClassMerging, @@ -3620,6 +3724,22 @@ return new EnqueuerResult(appInfoWithLiveness); } + private Set<DexMethod> amendWithCompanionMethods(Set<DexMethod> methods) { + if (methods.isEmpty() || interfaceProcessor == null) { + return methods; + } + BooleanBox changed = new BooleanBox(false); + ImmutableSet.Builder<DexMethod> builder = ImmutableSet.builder(); + interfaceProcessor.forEachMethodToMove( + (method, companion) -> { + if (methods.contains(method)) { + changed.set(true); + builder.add(companion); + } + }); + return changed.isTrue() ? builder.addAll(methods).build() : methods; + } + private boolean verifyReferences(DexApplication app) { WorkList<DexClass> worklist = WorkList.newIdentityWorkList(); for (DexProgramClass clazz : liveTypes.getItems()) { @@ -3762,6 +3882,16 @@ continue; } + for (DelayedRootSetActionItem delayedRootSetActionItem : + rootSet.delayedRootSetActionItems) { + if (delayedRootSetActionItem.isInterfaceMethodSyntheticBridgeAction()) { + identifySyntheticInterfaceMethodBridges( + delayedRootSetActionItem.asInterfaceMethodSyntheticBridgeAction()); + } + } + + synthesize(); + ConsequentRootSet consequentRootSet = computeDelayedInterfaceMethodSyntheticBridges(); addConsequentRootSet(consequentRootSet); rootSet @@ -3773,11 +3903,6 @@ continue; } - synthesize(); - if (!workList.isEmpty()) { - continue; - } - // Reached the fixpoint. break; } @@ -3808,9 +3933,14 @@ assert workList.isEmpty(); R8PostProcessingDesugaringEventConsumer eventConsumer = - CfPostProcessingDesugaringEventConsumer.createForR8(syntheticAdditions); - CfPostProcessingDesugaringCollection.create(appView, null, desugaring.getRetargetingInfo()) - .postProcessingDesugaring(liveTypes.items, eventConsumer, executorService); + CfPostProcessingDesugaringEventConsumer.createForR8(syntheticAdditions, desugaring); + InterfaceMethodProcessorFacade interfaceDesugaring = + desugaring.getInterfaceMethodPostProcessingDesugaringR8( + ExcludeDexResources, liveMethods::contains, interfaceProcessor); + CfPostProcessingDesugaringCollection.create( + appView, interfaceDesugaring, desugaring.getRetargetingInfo()) + .postProcessingDesugaring( + liveTypes.items, liveMethods::contains, eventConsumer, executorService); if (syntheticAdditions.isEmpty()) { return; @@ -3829,8 +3959,6 @@ EnqueuerAction action = workList.poll(); action.run(this); } - - eventConsumer.finalizeDesugaring(); } private long getNumberOfLiveItems() { @@ -3846,6 +3974,7 @@ // enqueuer, similar to Enqueuer#dependentMinimumKeepClassInfo. rootSet.addConsequentRootSet(consequentRootSet); includeMinimumKeepInfo(consequentRootSet); + consequentRootSet.pendingMethodMoveInverse.forEach(pendingMethodMoveInverse::put); // Check for compatibility rules indicating that the holder must be implicitly kept. if (forceProguardCompatibility) { @@ -3873,11 +4002,10 @@ private final Map<DexMethod, ProgramMethod> syntheticInterfaceMethodBridges = new LinkedHashMap<>(); - private void handleInterfaceMethodSyntheticBridgeAction( - InterfaceMethodSyntheticBridgeAction action, RootSetBuilder builder) { + private void identifySyntheticInterfaceMethodBridges( + InterfaceMethodSyntheticBridgeAction action) { ProgramMethod methodToKeep = action.getMethodToKeep(); ProgramMethod singleTarget = action.getSingleTarget(); - DexEncodedMethod singleTargetMethod = singleTarget.getDefinition(); if (rootSet.isShrinkingDisallowedUnconditionally(singleTarget, options)) { return; } @@ -3886,20 +4014,19 @@ methodToKeep.getDefinition().getReference())) { syntheticInterfaceMethodBridges.put( methodToKeep.getDefinition().getReference(), methodToKeep); - assert null - == methodToKeep.getHolder().lookupMethod(methodToKeep.getDefinition().getReference()); - if (singleTargetMethod.isLibraryMethodOverride().isTrue()) { - methodToKeep.getDefinition().setLibraryMethodOverride(OptionalBool.TRUE); - } - DexProgramClass singleTargetHolder = singleTarget.getHolder(); - assert singleTargetHolder.isInterface(); - markVirtualMethodAsReachable( - singleTargetMethod.getReference(), - singleTargetHolder.isInterface(), - singleTarget, - graphReporter.fakeReportShouldNotBeUsed()); - workList.enqueueMarkMethodLiveAction( - singleTarget, singleTarget, graphReporter.fakeReportShouldNotBeUsed()); + } + } + + private void handleInterfaceMethodSyntheticBridgeAction( + InterfaceMethodSyntheticBridgeAction action, RootSetBuilder builder) { + ProgramMethod methodToKeep = action.getMethodToKeep(); + ProgramMethod singleTarget = action.getSingleTarget(); + DexEncodedMethod singleTargetMethod = singleTarget.getDefinition(); + if (rootSet.isShrinkingDisallowedUnconditionally(singleTarget, options)) { + return; + } + if (singleTargetMethod.isLibraryMethodOverride().isTrue()) { + methodToKeep.getDefinition().setLibraryMethodOverride(OptionalBool.TRUE); } action.getAction().accept(builder); } @@ -3922,33 +4049,23 @@ DexEncodedMethod definition = target.getDefinition(); DexProgramClass holder = target.getHolder(); DexMethod reference = target.getReference(); + markMethodAsTargeted(target, reason); if (definition.isVirtualMethod()) { // A virtual method. Mark it as reachable so that subclasses, if instantiated, keep // their overrides. However, we don't mark it live, as a keep rule might not imply that // the corresponding class is live. markVirtualMethodAsReachable(reference, holder.isInterface(), target, reason); - if (holder.isInterface()) { - // Reachability for default methods is based on live subtypes in general. For keep rules, - // we need special handling as we essentially might have live subtypes that are outside of - // the current compilation unit. Keep either the default-method or its implementation - // method. + // When generating interface bridges the method may be inserted into a live hierarchy. + // If so we need to also mark it as live as the reachable check above will not reprocess the + // hierarchy. + // TODO(b/183998768): The check for isInterface here should be possible to remove now. + if (definition.isNonAbstractVirtualMethod() + && (objectAllocationInfoCollection.isInstantiatedDirectlyOrHasInstantiatedSubtype(holder) + || holder.isInterface())) { // TODO(b/120959039): Codify the kept-graph expectations for these cases in tests. - if (definition.isNonAbstractVirtualMethod()) { - markVirtualMethodAsLive(target, reason); - } else { - DexEncodedMethod implementation = definition.getDefaultInterfaceMethodImplementation(); - if (implementation != null) { - DexProgramClass companion = - asProgramClassOrNull(appInfo().definitionFor(implementation.getHolderType())); - markTypeAsLive(companion, graphReporter.reportCompanionClass(holder, companion)); - markVirtualMethodAsLive( - new ProgramMethod(companion, implementation), - graphReporter.reportCompanionMethod(definition, implementation)); - } - } + markVirtualMethodAsLive(target, reason); } } else { - markMethodAsTargeted(target, reason); markDirectStaticOrConstructorMethodAsLive(target, reason); } } @@ -4090,9 +4207,10 @@ } private void traceNonDesugaredCode(ProgramMethod method) { - if (getMode().isInitialTreeShaking() && desugaring.needsDesugaring(method)) { - pendingDesugaring.add(method); - return; + if (getMode().isInitialTreeShaking()) { + if (addToPendingDesugaring(method)) { + return; + } } traceCode(method);
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 cd52fd7..ad3d4d1 100644 --- a/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java +++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerWorklist.java
@@ -501,7 +501,8 @@ @Override boolean enqueueAssertAction(Action assertion) { - throw attemptToEnqueue(); + assertion.execute(); + return true; } @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java index 0dcddda..c260f3e 100644 --- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java +++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
@@ -244,10 +244,20 @@ this.methodRuleInstances = methodRuleInstances; } - public void removeKeepInfoForPrunedItems(Set<DexType> removedClasses) { - keepClassInfo.keySet().removeIf(removedClasses::contains); - keepFieldInfo.keySet().removeIf(field -> removedClasses.contains(field.getHolderType())); - keepMethodInfo.keySet().removeIf(method -> removedClasses.contains(method.getHolderType())); + public void removeKeepInfoForPrunedItems(Set<? extends DexReference> removedReferences) { + keepClassInfo.keySet().removeIf(removedReferences::contains); + keepFieldInfo + .keySet() + .removeIf( + field -> + (removedReferences.contains(field) + || removedReferences.contains(field.getHolderType()))); + keepMethodInfo + .keySet() + .removeIf( + method -> + (removedReferences.contains(method) + || removedReferences.contains(method.getHolderType()))); } @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java index 73f5c94..72a1510 100644 --- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java +++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -42,6 +42,8 @@ import com.android.tools.r8.graph.PrunedItems; import com.android.tools.r8.graph.SubtypingInfo; import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteBuilderShrinker; +import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper; +import com.android.tools.r8.ir.desugar.itf.InterfaceMethodDesugaringBaseEventConsumer; import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple; import com.android.tools.r8.logging.Log; import com.android.tools.r8.shaking.AnnotationMatchResult.AnnotationsIgnoredMatchResult; @@ -58,6 +60,7 @@ import com.android.tools.r8.utils.PredicateSet; import com.android.tools.r8.utils.StringDiagnostic; import com.android.tools.r8.utils.ThreadUtils; +import com.android.tools.r8.utils.collections.ProgramMethodMap; import com.google.common.base.Equivalence.Wrapper; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -137,6 +140,10 @@ private final OptimizationFeedbackSimple feedback = OptimizationFeedbackSimple.getInstance(); + private final InterfaceDesugaringSyntheticHelper interfaceDesugaringSyntheticHelper; + private final ProgramMethodMap<ProgramMethod> pendingMethodMoveInverse = + ProgramMethodMap.create(); + private RootSetBuilder( AppView<? extends AppInfoWithClassHierarchy> appView, SubtypingInfo subtypingInfo, @@ -146,6 +153,10 @@ this.application = appView.appInfo().app().asDirect(); this.rules = rules; this.options = appView.options(); + interfaceDesugaringSyntheticHelper = + options.isInterfaceMethodDesugaringEnabled() + ? new InterfaceDesugaringSyntheticHelper(appView) + : null; } private RootSetBuilder( @@ -383,7 +394,8 @@ dependentKeepClassCompatRule, identifierNameStrings, ifRules, - Lists.newArrayList(delayedRootSetActionItems)); + Lists.newArrayList(delayedRootSetActionItems), + pendingMethodMoveInverse); } private void propagateAssumeRules(DexClass clazz) { @@ -453,7 +465,8 @@ neverClassInline, dependentMinimumKeepInfo, dependentKeepClassCompatRule, - Lists.newArrayList(delayedRootSetActionItems)); + Lists.newArrayList(delayedRootSetActionItems), + pendingMethodMoveInverse); } private static DexProgramClass testAndGetPrecondition( @@ -1333,25 +1346,6 @@ // Don't keep lambda deserialization methods. return; } - // If desugaring is enabled, private and static interface methods will be moved to a - // companion class. So we don't need to add them to the root set in the beginning. - if (options.isInterfaceMethodDesugaringEnabled() - && method.getDefinition().hasCode() - && (method.getAccessFlags().isPrivate() || method.getAccessFlags().isStatic())) { - DexClass holder = appView.definitionFor(method.getHolderType()); - if (holder != null && holder.isInterface()) { - if (rule.isSpecific()) { - options.reporter.warning( - new StringDiagnostic( - "The rule `" - + rule - + "` is ignored because the targeting interface method `" - + method.getReference().toSourceString() - + "` will be desugared.")); - } - return; - } - } } // The reason for keeping should link to the conditional rule as a whole, if present. @@ -1388,6 +1382,44 @@ preconditionEvent = UnconditionalKeepInfoEvent.get(); } + if (isInterfaceMethodNeedingDesugaring(item)) { + ProgramMethod method = item.asMethod(); + ProgramMethod companion = + interfaceDesugaringSyntheticHelper.ensureMethodOfProgramCompanionClassStub( + method, + new InterfaceMethodDesugaringBaseEventConsumer() { + @Override + public void acceptCompanionClassClinit(ProgramMethod method) { + // No processing of synthesized CC.<clinit>. They will be picked up by tracing. + } + + @Override + public void acceptCompanionMethod(ProgramMethod method, ProgramMethod companion) { + // The move will be included in the pending-inverse map below. + } + }); + // Add the method to the inverse map as tracing will now directly target the CC method. + pendingMethodMoveInverse.put(companion, method); + // Only shrinking and optimization are transferred for interface companion methods. + if (appView.options().isOptimizationEnabled() && !modifiers.allowsOptimization) { + dependentMinimumKeepInfo + .getOrCreateMinimumKeepInfoFor(preconditionEvent, companion.getReference()) + .disallowOptimization(); + context.markAsUsed(); + } + if (appView.options().isShrinking() && !modifiers.allowsShrinking) { + dependentMinimumKeepInfo + .getOrCreateMinimumKeepInfoFor(preconditionEvent, companion.getReference()) + .addRule(keepRule) + .disallowShrinking(); + context.markAsUsed(); + } + if (!item.asMethod().isDefaultMethod()) { + // Static and private methods do not apply to the original item. + return; + } + } + if (appView.options().isAccessModificationEnabled() && !modifiers.allowsAccessModification) { dependentMinimumKeepInfo .getOrCreateMinimumKeepInfoFor(preconditionEvent, item.getReference()) @@ -1436,6 +1468,14 @@ } } + private boolean isInterfaceMethodNeedingDesugaring(ProgramDefinition item) { + return options.isInterfaceMethodDesugaringEnabled() + && item.isMethod() + && item.asMethod().getHolder().isInterface() + && !item.asMethod().getDefinition().isClassInitializer() + && item.asMethod().getDefinition().hasCode(); + } + private void reportAssumeNoSideEffectsWarningForJavaLangClassMethod( DexClassAndMethod method, ProguardAssumeNoSideEffectRule context) { assert method.getHolderType() == options.dexItemFactory().objectType; @@ -1486,6 +1526,7 @@ private final DependentMinimumKeepInfoCollection dependentMinimumKeepInfo; final Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule; final List<DelayedRootSetActionItem> delayedRootSetActionItems; + public final ProgramMethodMap<ProgramMethod> pendingMethodMoveInverse; RootSetBase( Set<DexMethod> neverInline, @@ -1493,13 +1534,15 @@ Set<DexType> neverClassInline, DependentMinimumKeepInfoCollection dependentMinimumKeepInfo, Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule, - List<DelayedRootSetActionItem> delayedRootSetActionItems) { + List<DelayedRootSetActionItem> delayedRootSetActionItems, + ProgramMethodMap<ProgramMethod> pendingMethodMoveInverse) { this.neverInline = neverInline; this.neverInlineDueToSingleCaller = neverInlineDueToSingleCaller; this.neverClassInline = neverClassInline; this.dependentMinimumKeepInfo = dependentMinimumKeepInfo; this.dependentKeepClassCompatRule = dependentKeepClassCompatRule; this.delayedRootSetActionItems = delayedRootSetActionItems; + this.pendingMethodMoveInverse = pendingMethodMoveInverse; } Set<ProguardKeepRuleBase> getDependentKeepClassCompatRule(DexType type) { @@ -1560,14 +1603,16 @@ Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule, Set<DexReference> identifierNameStrings, Set<ProguardIfRule> ifRules, - List<DelayedRootSetActionItem> delayedRootSetActionItems) { + List<DelayedRootSetActionItem> delayedRootSetActionItems, + ProgramMethodMap<ProgramMethod> pendingMethodMoveInverse) { super( neverInline, neverInlineDueToSingleCaller, neverClassInline, dependentMinimumKeepInfo, dependentKeepClassCompatRule, - delayedRootSetActionItems); + delayedRootSetActionItems, + pendingMethodMoveInverse); this.reasonAsked = reasonAsked; this.checkDiscarded = checkDiscarded; this.alwaysInline = alwaysInline; @@ -1880,14 +1925,16 @@ Set<DexType> neverClassInline, DependentMinimumKeepInfoCollection dependentMinimumKeepInfo, Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule, - List<DelayedRootSetActionItem> delayedRootSetActionItems) { + List<DelayedRootSetActionItem> delayedRootSetActionItems, + ProgramMethodMap<ProgramMethod> pendingMethodMoveInverse) { super( neverInline, neverInlineDueToSingleCaller, neverClassInline, dependentMinimumKeepInfo, dependentKeepClassCompatRule, - delayedRootSetActionItems); + delayedRootSetActionItems, + pendingMethodMoveInverse); } static ConsequentRootSetBuilder builder( @@ -1959,7 +2006,8 @@ emptyMap(), Collections.emptySet(), ifRules, - delayedRootSetActionItems); + delayedRootSetActionItems, + ProgramMethodMap.empty()); } public static MainDexRootSetBuilder builder(
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 71ae785..474d1c3 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -731,6 +731,28 @@ return internalEnsureDexProgramClass(kind, fn, onCreationConsumer, outerContext, type, appView); } + public DexClasspathClass ensureFixedClasspathClassFromType( + SyntheticKind kind, + DexType contextType, + AppView<?> appView, + Consumer<SyntheticClasspathClassBuilder> fn) { + SynthesizingContext outerContext = SynthesizingContext.fromType(contextType); + DexType type = SyntheticNaming.createFixedType(kind, outerContext, appView.dexItemFactory()); + synchronized (contextType) { + DexClass clazz = appView.definitionFor(type); + if (clazz != null) { + assert clazz.isClasspathClass(); + return clazz.asClasspathClass(); + } + SyntheticClasspathClassBuilder classBuilder = + new SyntheticClasspathClassBuilder(type, kind, outerContext, appView.dexItemFactory()); + fn.accept(classBuilder); + DexClasspathClass definition = classBuilder.build(); + addPendingDefinition(new SyntheticClasspathClassDefinition(kind, outerContext, definition)); + return definition; + } + } + /** Create a single synthetic method item. */ public ProgramMethod createMethod( SyntheticKind kind,
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 216fcfc..0a05c0f 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticNaming.java
@@ -39,6 +39,7 @@ HORIZONTAL_INIT_TYPE_ARGUMENT_1(SYNTHETIC_CLASS_SEPARATOR + "IA$1", 6, false, true), HORIZONTAL_INIT_TYPE_ARGUMENT_2(SYNTHETIC_CLASS_SEPARATOR + "IA$2", 7, false, true), HORIZONTAL_INIT_TYPE_ARGUMENT_3(SYNTHETIC_CLASS_SEPARATOR + "IA$3", 8, false, true), + CONST_DYNAMIC("$Condy", 30, false), // Method synthetics. ENUM_UNBOXING_CHECK_NOT_ZERO_METHOD("CheckNotZero", 27, true), RECORD_HELPER("Record", 9, true), @@ -53,7 +54,8 @@ SERVICE_LOADER("ServiceLoad", 18, true), OUTLINE("Outline", 19, true), API_CONVERSION("APIConversion", 26, true), - API_CONVERSION_PARAMETERS("APIConversionParameters", 28, true); + API_CONVERSION_PARAMETERS("APIConversionParameters", 28, true), + EMULATED_INTERFACE_MARKER_CLASS("", 29, false, true, true); static { assert verifyNoOverlappingIds();
diff --git a/src/main/java/com/android/tools/r8/utils/DesugarUtils.java b/src/main/java/com/android/tools/r8/utils/DesugarUtils.java new file mode 100644 index 0000000..7b8fba2 --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/DesugarUtils.java
@@ -0,0 +1,17 @@ +// 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.utils; + +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.DexString; + +public class DesugarUtils { + public static DexString appendFullyQualifiedHolderToMethodName( + DexMethod method, DexItemFactory factory) { + return factory.createString( + method.name.toString() + "$" + method.holder.getTypeName().replace('.', '-')); + } +}
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 f391fb1..e0a5e76 100644 --- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java +++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -514,15 +514,7 @@ return !canUseNestBasedAccess(); } - public boolean enableExperimentalRecordDesugaring() { - // TODO(b/169645628): Remove when records are supported. - return testing.enableExperimentalRecordDesugaring; - } - public boolean shouldDesugarRecords() { - if (!enableExperimentalRecordDesugaring()) { - return false; - } return desugarState.isOn() && !canUseRecords(); } @@ -1222,15 +1214,15 @@ // TODO(b/69963623): enable if everything is ready, including signature rewriting at call sites. private boolean enableConstantPropagation = false; private boolean enableExperimentalArgumentPropagation = false; - private boolean enableTypePropagation = true; + private boolean enableDynamicTypePropagation = true; public void disableOptimization() { enableConstantPropagation = false; - enableTypePropagation = false; + enableDynamicTypePropagation = false; } - public void disableTypePropagationForTesting() { - enableTypePropagation = false; + public void disableDynamicTypePropagationForTesting() { + enableDynamicTypePropagation = false; } public int getMaxNumberOfDispatchTargetsBeforeAbandoning() { @@ -1241,7 +1233,7 @@ if (!isOptimizing()) { return false; } - return enableConstantPropagation || enableTypePropagation; + return enableConstantPropagation || enableDynamicTypePropagation; } public boolean isExperimentalArgumentPropagationEnabled() { @@ -1252,8 +1244,8 @@ return enableConstantPropagation; } - public boolean isTypePropagationEnabled() { - return enableTypePropagation; + public boolean isDynamicTypePropagationEnabled() { + return enableDynamicTypePropagation; } public void setEnableConstantPropagation() { @@ -1261,9 +1253,10 @@ enableConstantPropagation = true; } - public void setEnableExperimentalArgumentPropagation( + public CallSiteOptimizationOptions setEnableExperimentalArgumentPropagation( boolean enableExperimentalArgumentPropagation) { this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; + return this; } } @@ -1552,7 +1545,6 @@ public boolean enableEnumUnboxingDebugLogs = false; public boolean forceRedundantConstNumberRemoval = false; public boolean enableExperimentalDesugaredLibraryKeepRuleGenerator = false; - public boolean enableExperimentalRecordDesugaring = false; public boolean invertConditionals = false; public boolean placeExceptionalBlocksLast = false; public boolean dontCreateMarkerInD8 = false; @@ -1639,7 +1631,8 @@ public int numberOfProguardIfRuleMemberEvaluations = 0; } - public Consumer<ProgramMethod> callSiteOptimizationInfoInspector = null; + public Consumer<ProgramMethod> callSiteOptimizationInfoInspector = + ConsumerUtils.emptyConsumer(); public Predicate<DexMethod> cfByteCodePassThrough = null;
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java index 089154e..891f1c2 100644 --- a/src/main/java/com/android/tools/r8/utils/ListUtils.java +++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -25,7 +25,7 @@ * as the singleton list containing {@code v} (i.e., no changes should be made to the given * element). */ - public static <T> List<T> flatMap( + public static <T> List<T> flatMapSameType( List<T> list, Function<T, Collection<T>> fn, List<T> defaultValue) { List<T> result = null; for (int i = 0; i < list.size(); i++) { @@ -48,6 +48,12 @@ return result != null ? result : defaultValue; } + public static <S, T> List<T> flatMap(List<S> list, Function<S, Collection<T>> fn) { + List<T> result = new ArrayList<>(); + list.forEach(element -> result.addAll(fn.apply(element))); + return result; + } + public static <T> List<T> filter(List<T> list, Predicate<? super T> predicate) { ArrayList<T> filtered = new ArrayList<>(list.size()); list.forEach(
diff --git a/src/main/java/com/android/tools/r8/utils/ZipUtils.java b/src/main/java/com/android/tools/r8/utils/ZipUtils.java index e7b9b00..da688a8 100644 --- a/src/main/java/com/android/tools/r8/utils/ZipUtils.java +++ b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
@@ -102,19 +102,24 @@ throws IOException { try (ZipOutputStream stream = new ZipOutputStream(new BufferedOutputStream(Files.newOutputStream(zipFile)))) { - for (Path path : filesToZip) { - ZipEntry zipEntry = - new ZipEntry( - StreamSupport.stream( - Spliterators.spliteratorUnknownSize( - basePath.relativize(path).iterator(), Spliterator.ORDERED), - false) - .map(Path::toString) - .collect(Collectors.joining("/"))); - stream.putNextEntry(zipEntry); - Files.copy(path, stream); - stream.closeEntry(); - } + zip(stream, basePath, filesToZip); + } + } + + public static void zip(ZipOutputStream stream, Path basePath, Collection<Path> filesToZip) + throws IOException { + for (Path path : filesToZip) { + ZipEntry zipEntry = + new ZipEntry( + StreamSupport.stream( + Spliterators.spliteratorUnknownSize( + basePath.relativize(path).iterator(), Spliterator.ORDERED), + false) + .map(Path::toString) + .collect(Collectors.joining("/"))); + stream.putNextEntry(zipEntry); + Files.copy(path, stream); + stream.closeEntry(); } }
diff --git a/src/test/examplesAndroidO/invokecustom/TestGenerator.java b/src/test/examplesAndroidO/invokecustom/TestGenerator.java index 9964ed0..a4cd89a 100644 --- a/src/test/examplesAndroidO/invokecustom/TestGenerator.java +++ b/src/test/examplesAndroidO/invokecustom/TestGenerator.java
@@ -21,6 +21,8 @@ import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; +// TODO(b/167145686): Migrate these tests to the new setup ala +// InvokeDynamicVirtualDispatchToDefaultInterfaceMethodTest public class TestGenerator { private final Path classNamePath; @@ -52,7 +54,6 @@ generateMethodTest6(cw); generateMethodTest7(cw); generateMethodTest8(cw); - generateMethodTest9(cw); generateMethodTest10(cw); generateMethodTest11(cw); generateMethodTest12(cw); @@ -89,8 +90,6 @@ mv.visitMethodInsn( Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test8", "()V", false); mv.visitMethodInsn( - Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test9", "()V", false); - mv.visitMethodInsn( Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test10", "()V", false); mv.visitMethodInsn( Opcodes.INVOKESTATIC, Type.getInternalName(InvokeCustom.class), "test11", "()V", false); @@ -280,29 +279,6 @@ /** * Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a - * MethodHandle of kind invoke virtual. The target method is a method into a class implementing - * an abstract method and that shadows a default method from an interface. - */ - private void generateMethodTest9(ClassVisitor cv) { - MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test9", "()V", - null, null); - MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, - MethodType.class, MethodHandle.class); - Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class), - "bsmCreateCallSite", mt.toMethodDescriptorString(), false); - mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class)); - mv.visitInsn(Opcodes.DUP); - mv.visitMethodInsn( - Opcodes.INVOKESPECIAL, Type.getInternalName(InvokeCustom.class), "<init>", "()V", false); - mv.visitInvokeDynamicInsn("targetMethodTest10", "(Linvokecustom/InvokeCustom;)V", bootstrap, - new Handle(Opcodes.H_INVOKEVIRTUAL, Type.getInternalName(InvokeCustom.class), - "targetMethodTest10", "()V", false)); - mv.visitInsn(Opcodes.RETURN); - mv.visitMaxs(-1, -1); - } - - /** - * Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a * MethodHandle of kind get static. The method handle read a static field from a class. */ private void generateMethodTest10(ClassVisitor cv) {
diff --git a/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java b/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java index 848c566..0637a0b 100644 --- a/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java +++ b/src/test/java/com/android/tools/r8/CompileWithJdkClassFileProviderTest.java
@@ -1,8 +1,8 @@ package com.android.tools.r8; +import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage; import static org.hamcrest.CoreMatchers.anyOf; import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; @@ -10,7 +10,6 @@ import com.android.tools.r8.TestRuntime.CfVm; import com.android.tools.r8.utils.AndroidApiLevel; import java.util.List; -import org.hamcrest.core.StringContains; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -81,6 +80,7 @@ TestShrinkerBuilder<?, ?, ?, ?, ?> testBuilder = testForR8(parameters.getBackend()) + .setMinApi(AndroidApiLevel.B) .addLibraryProvider(provider) .addProgramClassFileData(dumpClassWhichUseJava9Flow()) .addKeepMainRule("MySubscriber"); @@ -90,11 +90,18 @@ // java.util.concurrent.Flow$Subscriber is not present in JDK8 rt.jar. testBuilder.compileWithExpectedDiagnostics( diagnostics -> { - diagnostics.assertOnlyErrors(); - diagnostics.assertErrorsCount(1); - assertThat( - diagnostics.getErrors().get(0).getDiagnosticMessage(), - StringContains.containsString("java.util.concurrent.Flow$Subscriber")); + diagnostics.assertErrorsMatch( + diagnosticMessage(containsString("java.util.concurrent.Flow$Subscriber"))); + if (parameters.isCfRuntime()) { + diagnostics.assertOnlyErrors(); + } else { + // TODO(b/198368663): R8 will double report missing classes in itf desugaring. + diagnostics.assertWarningsMatch( + diagnosticMessage(containsString("java.util.concurrent.Flow$Subscriber"))); + diagnostics.assertErrorsCount(1); + diagnostics.assertWarningsCount(1); + diagnostics.assertInfosCount(0); + } }); } catch (CompilationFailedException e) { return;
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java index f58f1fd..fbf3878 100644 --- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java +++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -644,6 +644,15 @@ // Tests where the output of R8 fails when run with Art. private static final Multimap<String, TestCondition> failingRunWithArt = new ImmutableListMultimap.Builder<String, TestCondition>() + // The itf cache issue is hit on dx inputs on the runtime with the issue as we no longer + // desugar. + // TODO(b/198306901): Investigate this behavior change fully. Do we need a workaround? + .put( + "666-dex-cache-itf", + TestCondition.match( + TestCondition.tools(DexTool.DX), + TestCondition.compilers(CompilerUnderTest.R8), + TestCondition.runtimes(DexVm.Version.DEFAULT))) // The growth limit test fails after processing by R8 because R8 will eliminate an // "unneeded" const store. The following reflective call to the VM's GC will then see the // large array as still live and the subsequent allocations will fail to reach the desired
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java index a92437f..11b41cc 100644 --- a/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java +++ b/src/test/java/com/android/tools/r8/R8RunExamplesAndroidOTest.java
@@ -200,7 +200,7 @@ b -> b.addProguardConfiguration( getProguardOptionsNPlus(enableProguardCompatibilityMode), Origin.unknown())) - .withDexCheck(inspector -> checkLambdaCount(inspector, 3, "lambdadesugaringnplus")) + .withDexCheck(inspector -> checkLambdaCount(inspector, 2, "lambdadesugaringnplus")) .run(); }
diff --git a/src/test/java/com/android/tools/r8/TestAppViewBuilder.java b/src/test/java/com/android/tools/r8/TestAppViewBuilder.java new file mode 100644 index 0000000..d5ef230 --- /dev/null +++ b/src/test/java/com/android/tools/r8/TestAppViewBuilder.java
@@ -0,0 +1,129 @@ +// 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; + +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.shaking.ProguardConfigurationRule; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.AndroidApp; +import com.android.tools.r8.utils.InternalOptions; +import com.android.tools.r8.utils.ListUtils; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; + +public class TestAppViewBuilder { + + private AndroidApp.Builder builder = AndroidApp.builder(); + private List<Function<DexItemFactory, List<ProguardConfigurationRule>>> rules = new ArrayList<>(); + private List<Consumer<InternalOptions>> optionModifications = new ArrayList<>(); + + public static TestAppViewBuilder builder() { + return new TestAppViewBuilder(); + } + + private TestAppViewBuilder() {} + + public TestAppViewBuilder addProgramClasses(Class<?>... classes) { + return addProgramClasses(Arrays.asList(classes)); + } + + public TestAppViewBuilder addProgramClasses(Collection<Class<?>> classes) { + classes.forEach(clazz -> builder.addProgramFile(ToolHelper.getClassFileForTestClass(clazz))); + return this; + } + + public TestAppViewBuilder addProgramClassFileData(byte[]... classes) { + return addProgramClassFileData(Arrays.asList(classes)); + } + + public TestAppViewBuilder addProgramClassFileData(Collection<byte[]> classes) { + builder.addClassProgramData(classes); + return this; + } + + public TestAppViewBuilder addAndroidApp(AndroidApp app) { + app.getProgramResourceProviders().forEach(builder::addProgramResourceProvider); + app.getClasspathResourceProviders().forEach(builder::addClasspathResourceProvider); + app.getLibraryResourceProviders().forEach(builder::addLibraryResourceProvider); + assert !app.hasMainDexList() : "todo"; + return this; + } + + public TestAppViewBuilder addKeepAllRule() { + rules = null; + return this; + } + + public TestAppViewBuilder addKeepMainRule(Class<?> mainClass) { + return addKeepRuleBuilder( + factory -> TestBase.buildKeepRuleForClassAndMethods(mainClass, factory)); + } + + public TestAppViewBuilder addKeepRuleBuilder( + Function<DexItemFactory, List<ProguardConfigurationRule>> ruleBuilder) { + if (rules != null) { + rules.add(ruleBuilder); + } + return this; + } + + public TestAppViewBuilder addOptionsModification(Consumer<InternalOptions> optionsConsumer) { + optionModifications.add(optionsConsumer); + return this; + } + + public AppView<AppInfoWithLiveness> buildWithLiveness() throws Exception { + return TestBase.computeAppViewWithLiveness( + builder.build(), + (rules == null + ? null + : factory -> + TestBase.buildConfigForRules( + factory, ListUtils.flatMap(rules, r -> r.apply(factory)))), + options -> optionModifications.forEach(consumer -> consumer.accept(options))); + } + + public TestAppViewBuilder setMinApi(AndroidApiLevel minApi) { + optionModifications.add(options -> options.minApiLevel = minApi); + return this; + } + + public TestAppViewBuilder addClasspathClasses(Class<?>... classes) { + return addClasspathClasses(Arrays.asList(classes)); + } + + public TestAppViewBuilder addClasspathClasses(Collection<Class<?>> classes) { + classes.forEach(clazz -> addClasspathFiles(ToolHelper.getClassFileForTestClass(clazz))); + return this; + } + + public TestAppViewBuilder addClasspathFiles(Path... files) { + return addClasspathFiles(Arrays.asList(files)); + } + + public TestAppViewBuilder addClasspathFiles(List<Path> files) { + builder.addClasspathFiles(files); + return this; + } + + public TestAppViewBuilder addLibraryFiles(Path... files) { + return addLibraryFiles(Arrays.asList(files)); + } + + public TestAppViewBuilder addLibraryFiles(List<Path> files) { + builder.addLibraryFiles(files); + return this; + } + + public TestAppViewBuilder addTestingAnnotations() { + return addProgramClasses(TestBuilder.getTestingAnnotations()); + } +}
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java index f93398c..2ccb378 100644 --- a/src/test/java/com/android/tools/r8/TestBase.java +++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -8,7 +8,9 @@ import static com.android.tools.r8.ToolHelper.R8_TEST_BUCKET; import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION; import static com.google.common.collect.Lists.cartesianProduct; +import static com.google.common.io.ByteStreams.toByteArray; import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -93,6 +95,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.common.io.ByteStreams; +import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -775,17 +778,18 @@ protected static AppView<AppInfoWithLiveness> computeAppViewWithLiveness(AndroidApp app) throws Exception { - return computeAppViewWithLiveness(app, null, null); + return TestAppViewBuilder.builder().addAndroidApp(app).addKeepAllRule().buildWithLiveness(); } protected static AppView<AppInfoWithLiveness> computeAppViewWithLiveness( AndroidApp app, Class<?> mainClass) throws Exception { - return computeAppViewWithLiveness( - app, - factory -> - buildConfigForRules(factory, buildKeepRuleForClassAndMethods(mainClass, factory))); + return TestAppViewBuilder.builder() + .addAndroidApp(app) + .addKeepMainRule(mainClass) + .buildWithLiveness(); } + // We should try to get rid of this usage of keep rule building which is very internal. protected static AppView<AppInfoWithLiveness> computeAppViewWithLiveness( AndroidApp app, Function<DexItemFactory, ProguardConfiguration> keepConfig) throws Exception { return computeAppViewWithLiveness(app, keepConfig, null); @@ -1912,4 +1916,54 @@ } return false; } + + public static boolean assertProgramsEqual(Path expectedJar, Path actualJar) throws Exception { + if (filesAreEqual(expectedJar, actualJar)) { + return true; + } + ArchiveClassFileProvider expected = new ArchiveClassFileProvider(expectedJar); + ArchiveClassFileProvider actual = new ArchiveClassFileProvider(actualJar); + assertEquals(getSortedDescriptorList(expected), getSortedDescriptorList(actual)); + for (String descriptor : expected.getClassDescriptors()) { + assertArrayEquals( + "Class " + descriptor + " differs", + getClassAsBytes(expected, descriptor), + getClassAsBytes(actual, descriptor)); + } + return false; + } + + public static boolean filesAreEqual(Path file1, Path file2) throws IOException { + long size = Files.size(file1); + long sizeOther = Files.size(file2); + if (size != sizeOther) { + return false; + } + if (size < 4096) { + return Arrays.equals(Files.readAllBytes(file1), Files.readAllBytes(file2)); + } + int byteRead1 = 0; + int byteRead2 = 0; + try (FileInputStream fs1 = new FileInputStream(file1.toString()); + FileInputStream fs2 = new FileInputStream(file2.toString())) { + BufferedInputStream bs1 = new BufferedInputStream(fs1); + BufferedInputStream bs2 = new BufferedInputStream(fs2); + while (byteRead1 == byteRead2 && byteRead1 != -1) { + byteRead1 = bs1.read(); + byteRead2 = bs2.read(); + } + } + return byteRead1 == byteRead2; + } + + private static List<String> getSortedDescriptorList(ArchiveClassFileProvider inputJar) { + ArrayList<String> descriptorList = new ArrayList<>(inputJar.getClassDescriptors()); + Collections.sort(descriptorList); + return descriptorList; + } + + private static byte[] getClassAsBytes(ArchiveClassFileProvider inputJar, String descriptor) + throws Exception { + return toByteArray(inputJar.getProgramResource(descriptor).getByteStream()); + } }
diff --git a/src/test/java/com/android/tools/r8/TestParameters.java b/src/test/java/com/android/tools/r8/TestParameters.java index 1df89b1..7e230ef 100644 --- a/src/test/java/com/android/tools/r8/TestParameters.java +++ b/src/test/java/com/android/tools/r8/TestParameters.java
@@ -31,6 +31,8 @@ public boolean canUseDefaultAndStaticInterfaceMethods() { assert isCfRuntime() || isDexRuntime(); + assert !isCfRuntime() || apiLevel == null + : "Use canUseDefaultAndStaticInterfaceMethodsWhenDesugaring when using CF api levels."; return isCfRuntime() || getApiLevel() .isGreaterThanOrEqualTo(TestBase.apiLevelWithDefaultInterfaceMethodsSupport());
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java index 88738f0..fe6cc31 100644 --- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java +++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -106,6 +106,10 @@ return addKeepRules("-dontoptimize"); } + public T addDontShrink() { + return addKeepRules("-dontshrink"); + } + public T addDontWarn(Class<?>... classes) { for (Class<?> clazz : classes) { addDontWarn(clazz.getTypeName());
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java index 8b3571a..91accca 100644 --- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiDatabaseBuilderGeneratorTest.java
@@ -18,7 +18,6 @@ import com.android.tools.r8.TestState; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.apimodel.AndroidApiVersionsXmlParser.ParsedApiClass; -import com.android.tools.r8.cf.bootstrap.BootstrapCurrentEqualityTest; import com.android.tools.r8.references.ClassReference; import com.android.tools.r8.references.Reference; import com.android.tools.r8.utils.AndroidApiLevel; @@ -116,7 +115,7 @@ @Test public void testDatabaseGenerationUpToDate() throws Exception { - BootstrapCurrentEqualityTest.filesAreEqual(generateJar(), API_DATABASE_JAR); + TestBase.filesAreEqual(generateJar(), API_DATABASE_JAR); } /**
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java index e997894..a580fb1 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfDefaultInterfaceMethodsTest.java
@@ -6,7 +6,7 @@ import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod; import static com.android.tools.r8.utils.AndroidApiLevel.L_MR1; -import static com.android.tools.r8.utils.codeinspector.Matchers.isAbstract; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static junit.framework.TestCase.assertEquals; import static org.hamcrest.MatcherAssert.assertThat; @@ -59,22 +59,22 @@ } else { ClassSubject aSubject = inspector.clazz(A.class); ClassSubject apiCaller = inspector.clazz(ApiCaller.class); - assertThat(apiCaller, isPresent()); - MethodSubject callApiLevel = apiCaller.uniqueMethodWithName("callApiLevel"); if (parameters.isCfRuntime()) { + assert parameters.canUseDefaultAndStaticInterfaceMethods(); + assertThat(apiCaller, isPresent()); assertThat(aSubject, isPresent()); + MethodSubject callApiLevel = apiCaller.uniqueMethodWithName("callApiLevel"); assertThat(callApiLevel, CodeMatchers.invokesMethodWithName("apiLevel22")); } else { assert parameters.isDexRuntime(); - // TODO(b/191013385): A has a virtual method that calls callApiLevel on $CC, but - // that call should be inlined. - assertThat(aSubject, isPresent()); - assertThat(callApiLevel, isAbstract()); - ClassSubject classSubject = apiCaller.toCompanionClass(); - assertThat(classSubject, isPresent()); - assertEquals(1, classSubject.allMethods().size()); + assert !parameters.canUseDefaultAndStaticInterfaceMethods(); + assertThat(apiCaller, isAbsent()); + assertThat(aSubject, isAbsent()); + ClassSubject companionClass = apiCaller.toCompanionClass(); + assertThat(companionClass, isPresent()); + assertEquals(1, companionClass.allMethods().size()); assertThat( - classSubject.allMethods().get(0), + companionClass.allMethods().get(0), CodeMatchers.invokesMethodWithName("apiLevel22")); } }
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfLambdaTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfLambdaTest.java index 0525cd5..04dc573 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfLambdaTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfLambdaTest.java
@@ -18,6 +18,7 @@ import com.android.tools.r8.utils.codeinspector.CodeMatchers; import com.android.tools.r8.utils.codeinspector.MethodSubject; import java.lang.reflect.Method; +import org.junit.Assume; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -47,6 +48,7 @@ @Test public void testR8() throws Exception { + Assume.assumeTrue("b/197494749", parameters.canUseDefaultAndStaticInterfaceMethods()); Method apiMethod = Api.class.getDeclaredMethod("apiLevel22"); testForR8(parameters.getBackend()) .addProgramClasses(ApiCaller.class, Action.class, Main.class) @@ -57,7 +59,6 @@ .apply(setMockApiLevelForMethod(apiMethod, L_MR1)) .apply(ApiModelingTestHelper::enableApiCallerIdentification) .allowAccessModification() - .noMinification() .compile() .inspect( inspector -> {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java index 1e2fd44..086cfa4 100644 --- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java +++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfStaticInterfaceMethodsTest.java
@@ -30,7 +30,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { - return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(); + return getTestParameters().withAllRuntimesAndApiLevels().build(); } public ApiModelNoInliningOfStaticInterfaceMethodsTest(TestParameters parameters) {
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java index e41bf8d..1a6717f 100644 --- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java +++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapCurrentEqualityTest.java
@@ -5,15 +5,12 @@ import static com.android.tools.r8.graph.GenericSignatureIdentityTest.testParseSignaturesInJar; import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION; -import static com.google.common.io.ByteStreams.toByteArray; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; -import com.android.tools.r8.ArchiveClassFileProvider; import com.android.tools.r8.CompilationMode; import com.android.tools.r8.ExternalR8TestCompileResult; import com.android.tools.r8.TestBase; @@ -29,15 +26,9 @@ import com.android.tools.r8.utils.Pair; import com.android.tools.r8.utils.StringUtils; import com.google.common.collect.Lists; -import java.io.BufferedInputStream; -import java.io.FileInputStream; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.List; import org.junit.BeforeClass; import org.junit.ClassRule; @@ -183,8 +174,7 @@ .setMode(CompilationMode.RELEASE) .compile() .outputJar(); - assert uploadJarsToCloudStorageIfTestFails( - BootstrapCurrentEqualityTest::filesAreEqual, runR81, runR82); + assert uploadJarsToCloudStorageIfTestFails(TestBase::filesAreEqual, runR81, runR82); } @Test @@ -247,56 +237,6 @@ BootstrapCurrentEqualityTest::assertProgramsEqual, result.outputJar(), runR8R8.outputJar()); } - public static boolean assertProgramsEqual(Path expectedJar, Path actualJar) throws Exception { - if (filesAreEqual(expectedJar, actualJar)) { - return true; - } - ArchiveClassFileProvider expected = new ArchiveClassFileProvider(expectedJar); - ArchiveClassFileProvider actual = new ArchiveClassFileProvider(actualJar); - assertEquals(getSortedDescriptorList(expected), getSortedDescriptorList(actual)); - for (String descriptor : expected.getClassDescriptors()) { - assertArrayEquals( - "Class " + descriptor + " differs", - getClassAsBytes(expected, descriptor), - getClassAsBytes(actual, descriptor)); - } - return false; - } - - public static boolean filesAreEqual(Path file1, Path file2) throws IOException { - long size = Files.size(file1); - long sizeOther = Files.size(file2); - if (size != sizeOther) { - return false; - } - if (size < 4096) { - return Arrays.equals(Files.readAllBytes(file1), Files.readAllBytes(file2)); - } - int byteRead1 = 0; - int byteRead2 = 0; - try (FileInputStream fs1 = new FileInputStream(file1.toString()); - FileInputStream fs2 = new FileInputStream(file2.toString())) { - BufferedInputStream bs1 = new BufferedInputStream(fs1); - BufferedInputStream bs2 = new BufferedInputStream(fs2); - while (byteRead1 == byteRead2 && byteRead1 != -1) { - byteRead1 = bs1.read(); - byteRead2 = bs2.read(); - } - } - return byteRead1 == byteRead2; - } - - private static List<String> getSortedDescriptorList(ArchiveClassFileProvider inputJar) { - ArrayList<String> descriptorList = new ArrayList<>(inputJar.getClassDescriptors()); - Collections.sort(descriptorList); - return descriptorList; - } - - private static byte[] getClassAsBytes(ArchiveClassFileProvider inputJar, String descriptor) - throws Exception { - return toByteArray(inputJar.getProgramResource(descriptor).getByteStream()); - } - private static TemporaryFolder newTempFolder() throws IOException { TemporaryFolder tempFolder = new TemporaryFolder(testFolder.newFolder()); tempFolder.create();
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java index 7dd698b..409236d 100644 --- a/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java +++ b/src/test/java/com/android/tools/r8/cf/bootstrap/BootstrapTest.java
@@ -4,11 +4,8 @@ package com.android.tools.r8.cf.bootstrap; import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION; -import static com.google.common.io.ByteStreams.toByteArray; -import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; -import com.android.tools.r8.ArchiveClassFileProvider; import com.android.tools.r8.ClassFileConsumer; import com.android.tools.r8.CompilationMode; import com.android.tools.r8.R8Command; @@ -21,9 +18,6 @@ import com.google.common.base.Charsets; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -153,27 +147,4 @@ String pgMap = FileUtils.readTextFile(pgMapFile, Charsets.UTF_8); return new R8Result(processResult, outputJar, pgMap); } - - private static void assertProgramsEqual(Path expectedJar, Path actualJar) throws Exception { - ArchiveClassFileProvider expected = new ArchiveClassFileProvider(expectedJar); - ArchiveClassFileProvider actual = new ArchiveClassFileProvider(actualJar); - assertEquals(getSortedDescriptorList(expected), getSortedDescriptorList(actual)); - for (String descriptor : expected.getClassDescriptors()) { - assertArrayEquals( - "Class " + descriptor + " differs", - getClassAsBytes(expected, descriptor), - getClassAsBytes(actual, descriptor)); - } - } - - private static List<String> getSortedDescriptorList(ArchiveClassFileProvider inputJar) { - ArrayList<String> descriptorList = new ArrayList<>(inputJar.getClassDescriptors()); - Collections.sort(descriptorList); - return descriptorList; - } - - private static byte[] getClassAsBytes(ArchiveClassFileProvider inputJar, String descriptor) - throws Exception { - return toByteArray(inputJar.getProgramResource(descriptor).getByteStream()); - } }
diff --git a/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java b/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java index e7c5ce1..51e4d41 100644 --- a/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/KeptTargetsIncompleteDiamondTest.java
@@ -7,6 +7,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.TestAppViewBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; @@ -20,6 +21,7 @@ import com.android.tools.r8.graph.MethodResolutionResult; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.shaking.ProguardConfigurationRule; +import com.android.tools.r8.utils.AndroidApiLevel; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import java.util.HashSet; @@ -43,18 +45,18 @@ private AppView<AppInfoWithLiveness> computeAppViewWithLiveness( Class<?> methodToBeKept, Class<?> classToBeKept) throws Exception { - return computeAppViewWithLiveness( - buildClasses(I.class, J.class, K.class, L.class, A.class, Main.class) - .addLibraryFile(ToolHelper.getJava8RuntimeJar()) - .build(), - factory -> - buildConfigForRules( - factory, + return TestAppViewBuilder.builder() + .addProgramClasses(I.class, J.class, K.class, L.class, A.class, Main.class) + .addLibraryFiles(ToolHelper.getJava8RuntimeJar()) + .addKeepRuleBuilder( + factory -> ImmutableList.<ProguardConfigurationRule>builder() .addAll(buildKeepRuleForClassAndMethods(methodToBeKept, factory)) .addAll(buildKeepRuleForClass(classToBeKept, factory)) .addAll(buildKeepRuleForClassAndMethods(Main.class, factory)) - .build())); + .build()) + .setMinApi(AndroidApiLevel.N) + .buildWithLiveness(); } @Test
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PrivateAndInterfaceMethodCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PrivateAndInterfaceMethodCollisionTest.java index f440689..837f374 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PrivateAndInterfaceMethodCollisionTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PrivateAndInterfaceMethodCollisionTest.java
@@ -9,7 +9,6 @@ import com.android.tools.r8.NoUnusedInterfaceRemoval; import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector; import org.junit.Test; public class PrivateAndInterfaceMethodCollisionTest extends HorizontalClassMergingTestBase { @@ -24,7 +23,15 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addHorizontallyMergedClassesInspector( - HorizontallyMergedClassesInspector::assertNoClassesMerged) + i -> { + if (parameters.canUseDefaultAndStaticInterfaceMethods()) { + i.assertClassesMerged(); + } else { + // With default method desugaring all uses of J::foo are eliminated so A and B + // merge. + i.assertIsCompleteMergeGroup(A.class, B.class); + } + }) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoUnusedInterfaceRemovalAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/StaticAndInterfaceMethodCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/StaticAndInterfaceMethodCollisionTest.java index 48844ff..c21469c 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/StaticAndInterfaceMethodCollisionTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/StaticAndInterfaceMethodCollisionTest.java
@@ -9,7 +9,6 @@ import com.android.tools.r8.NoUnusedInterfaceRemoval; import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector; import org.junit.Test; public class StaticAndInterfaceMethodCollisionTest extends HorizontalClassMergingTestBase { @@ -24,7 +23,15 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addHorizontallyMergedClassesInspector( - HorizontallyMergedClassesInspector::assertNoClassesMerged) + i -> { + if (parameters.canUseDefaultAndStaticInterfaceMethods()) { + i.assertNoClassesMerged(); + } else { + // When desugaring the call to C.foo in main will be inlined to target the CC. + // With J.foo now unused the classes A and B are safe to merge. + i.assertIsCompleteMergeGroup(A.class, B.class); + } + }) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoUnusedInterfaceRemovalAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java index e2eef5f..4e38b33 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java
@@ -30,7 +30,9 @@ .addHorizontallyMergedClassesInspector( inspector -> inspector - .assertIsCompleteMergeGroup(I.class, J.class) + .applyIf( + parameters.canUseDefaultAndStaticInterfaceMethods(), + i -> i.assertIsCompleteMergeGroup(I.class, J.class)) .applyIf( !parameters.canUseDefaultAndStaticInterfaceMethods(), i -> i.assertIsCompleteMergeGroup(B1.class, B2.class))
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java index 0af1e85..f7f2e0d 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java
@@ -14,6 +14,7 @@ import com.android.tools.r8.TestParameters; import com.android.tools.r8.classmerging.horizontal.HorizontalClassMergingTestBase; import com.android.tools.r8.synthesis.SyntheticItemsTestUtils; +import org.junit.Assume; import org.junit.Test; public class OverrideDefaultMethodTest extends HorizontalClassMergingTestBase { @@ -23,6 +24,7 @@ @Test public void testR8() throws Exception { + Assume.assumeTrue("b/197494749", parameters.canUseDefaultAndStaticInterfaceMethods()); testForR8(parameters.getBackend()) .addInnerClasses(getClass()) .addKeepMainRule(Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java index 0ea8597..313dec7 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java
@@ -38,11 +38,17 @@ inspector.assertNoClassesMerged(); } else { inspector - .assertClassesNotMerged(A.class, B.class) .assertIsCompleteMergeGroup(I.class, J.class) - .assertIsCompleteMergeGroup( - SyntheticItemsTestUtils.syntheticCompanionClass(I.class), - SyntheticItemsTestUtils.syntheticCompanionClass(J.class)) + .applyIf( + parameters.canUseDefaultAndStaticInterfaceMethods(), + i -> + i.assertClassesNotMerged(A.class, B.class) + .assertIsCompleteMergeGroup( + SyntheticItemsTestUtils.syntheticCompanionClass(I.class), + SyntheticItemsTestUtils.syntheticCompanionClass(J.class))) + .applyIf( + !parameters.canUseDefaultAndStaticInterfaceMethods(), + i -> i.assertClassesMerged(A.class, B.class)) .assertNoOtherClassesMerged(); } }) @@ -56,7 +62,9 @@ onlyIf(parameters.canUseDefaultAndStaticInterfaceMethods(), isPresent())); assertThat(codeInspector.clazz(Parent.class), isPresent()); assertThat(codeInspector.clazz(A.class), isPresent()); - assertThat(codeInspector.clazz(B.class), isPresent()); + assertThat( + codeInspector.clazz(B.class), + onlyIf(parameters.canUseDefaultAndStaticInterfaceMethods(), isPresent())); assertThat(codeInspector.clazz(C.class), isPresent()); }); }
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java index 6f5e761..1ec170d 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.classmerging.horizontal.dispatch; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.MatcherAssert.assertThat; @@ -12,7 +13,6 @@ import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestParameters; import com.android.tools.r8.classmerging.horizontal.HorizontalClassMergingTestBase; -import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector; import org.junit.Test; public class OverrideMergeAbsentTest extends HorizontalClassMergingTestBase { @@ -30,14 +30,23 @@ .enableNoVerticalClassMergingAnnotations() .setMinApi(parameters.getApiLevel()) .addHorizontallyMergedClassesInspector( - HorizontallyMergedClassesInspector::assertNoClassesMerged) + inspector -> { + if (parameters.canUseDefaultAndStaticInterfaceMethods()) { + inspector.assertNoClassesMerged(); + } else { + // When desugaring B.m is moved and A and B can be merged. + inspector.assertIsCompleteMergeGroup(A.class, B.class); + } + }) .run(parameters.getRuntime(), Main.class) .assertSuccessWithOutputLines("A", "B", "A", "J") .inspect( codeInspector -> { assertThat(codeInspector.clazz(J.class), isPresent()); assertThat(codeInspector.clazz(A.class), isPresent()); - assertThat(codeInspector.clazz(B.class), isPresent()); + assertThat( + codeInspector.clazz(B.class), + parameters.canUseDefaultAndStaticInterfaceMethods() ? isPresent() : isAbsent()); assertThat(codeInspector.clazz(C.class), isPresent()); }); }
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest.java index cc19dd2..6529d20 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupAfterSubclassMergingTest.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.classmerging.horizontal.interfaces; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; import static com.android.tools.r8.utils.codeinspector.Matchers.isImplementing; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.MatcherAssert.assertThat; @@ -55,11 +56,10 @@ .assertMergedInto(B.class, A.class); if (parameters.canUseDefaultAndStaticInterfaceMethods()) { inspector.assertClassesNotMerged(I.class, J.class, K.class); - } else { - inspector - .assertIsCompleteMergeGroup(I.class, J.class) - .assertClassesNotMerged(K.class); + } else if (enableInterfaceMergingInInitial) { + inspector.assertIsCompleteMergeGroup(I.class, J.class); } + inspector.assertNoOtherClassesMerged(); }) .addOptionsModification( options -> { @@ -82,14 +82,12 @@ assertThat(aClassSubject, isImplementing(inspector.clazz(K.class))); ClassSubject cClassSubject = inspector.clazz(C.class); - assertThat(cClassSubject, isPresent()); - assertThat( - cClassSubject, - isImplementing( - inspector.clazz( - parameters.canUseDefaultAndStaticInterfaceMethods() - ? J.class - : I.class))); + if (parameters.canUseDefaultAndStaticInterfaceMethods()) { + assertThat(cClassSubject, isPresent()); + assertThat(cClassSubject, isImplementing(inspector.clazz(J.class))); + } else { + assertThat(cClassSubject, isAbsent()); + } }) .run(parameters.getRuntime(), Main.class) .assertSuccessWithOutputLines("A", "K", "J");
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupClassTest.java index e18ce93..5ee0365 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupClassTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupClassTest.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.classmerging.horizontal.interfaces; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; import static com.android.tools.r8.utils.codeinspector.Matchers.isImplementing; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.MatcherAssert.assertThat; @@ -16,7 +17,9 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.synthesis.SyntheticItemsTestUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; +import org.junit.Assume; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -37,6 +40,7 @@ @Test public void test() throws Exception { + Assume.assumeTrue("b/197494749", parameters.canUseDefaultAndStaticInterfaceMethods()); testForR8(parameters.getBackend()) .addInnerClasses(getClass()) .addKeepMainRule(Main.class) @@ -47,7 +51,10 @@ if (parameters.canUseDefaultAndStaticInterfaceMethods()) { inspector.assertNoClassesMerged(); } else { - inspector.assertIsCompleteMergeGroup(I.class, J.class); + // J is removed as part of desugaring. This enables merging of its CC class. + inspector.assertIsCompleteMergeGroup( + SyntheticItemsTestUtils.syntheticCompanionClass(J.class), + SyntheticItemsTestUtils.syntheticCompanionClass(K.class)); } }) .enableInliningAnnotations() @@ -60,19 +67,18 @@ .inspect( inspector -> { ClassSubject aClassSubject = inspector.clazz(A.class); - assertThat(aClassSubject, isPresent()); - assertThat(aClassSubject, isImplementing(inspector.clazz(I.class))); - assertThat(aClassSubject, isImplementing(inspector.clazz(K.class))); - ClassSubject bClassSubject = inspector.clazz(B.class); - assertThat(bClassSubject, isPresent()); - assertThat( - bClassSubject, - isImplementing( - inspector.clazz( - parameters.canUseDefaultAndStaticInterfaceMethods() - ? J.class - : I.class))); + if (parameters.canUseDefaultAndStaticInterfaceMethods()) { + assertThat(aClassSubject, isPresent()); + assertThat(aClassSubject, isImplementing(inspector.clazz(I.class))); + assertThat(aClassSubject, isImplementing(inspector.clazz(K.class))); + assertThat(bClassSubject, isPresent()); + assertThat(bClassSubject, isImplementing(inspector.clazz(J.class))); + } else { + // When desugaring the calls in main will directly target the CC classes. + assertThat(aClassSubject, isAbsent()); + assertThat(bClassSubject, isAbsent()); + } }) .run(parameters.getRuntime(), Main.class) .assertSuccessWithOutputLines("K", "J");
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java index 2324f03..a1dbbaf 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.classmerging.horizontal.interfaces; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; import static com.android.tools.r8.utils.codeinspector.Matchers.isImplementing; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.MatcherAssert.assertThat; @@ -61,11 +62,15 @@ .inspect( inspector -> { ClassSubject aClassSubject = inspector.clazz(A.class); - assertThat(aClassSubject, isPresent()); - if (parameters.isCfRuntime()) { - assertThat(aClassSubject, isImplementing(inspector.clazz(I.class))); + if (parameters.canUseDefaultAndStaticInterfaceMethods()) { + assertThat(aClassSubject, isPresent()); + if (parameters.isCfRuntime()) { + assertThat(aClassSubject, isImplementing(inspector.clazz(I.class))); + } else { + assertThat(aClassSubject, isImplementing(inspector.clazz(J.class))); + } } else { - assertThat(aClassSubject, isImplementing(inspector.clazz(J.class))); + assertThat(aClassSubject, isAbsent()); } }) .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java index 5a656d9..2c40a58 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/DisjointInterfacesWithDefaultMethodsMergingTest.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.classmerging.horizontal.interfaces; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; import static com.android.tools.r8.utils.codeinspector.Matchers.isImplementing; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.MatcherAssert.assertThat; @@ -40,7 +41,13 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addHorizontallyMergedClassesInspector( - inspector -> inspector.assertIsCompleteMergeGroup(I.class, J.class)) + inspector -> { + if (parameters.canUseDefaultAndStaticInterfaceMethods()) { + inspector.assertIsCompleteMergeGroup(I.class, J.class); + } else { + inspector.assertNoClassesMerged(); + } + }) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoUnusedInterfaceRemovalAnnotations() @@ -50,8 +57,12 @@ .inspect( inspector -> { ClassSubject aClassSubject = inspector.clazz(A.class); - assertThat(aClassSubject, isPresent()); - assertThat(aClassSubject, isImplementing(inspector.clazz(I.class))); + if (parameters.canUseDefaultAndStaticInterfaceMethods()) { + assertThat(aClassSubject, isPresent()); + assertThat(aClassSubject, isImplementing(inspector.clazz(I.class))); + } else { + assertThat(aClassSubject, isAbsent()); + } }) .run(parameters.getRuntime(), Main.class) .assertSuccessWithOutputLines("I", "J");
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java index a84dbac..f68dd90 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/NoDefaultMethodMergingTest.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.classmerging.horizontal.interfaces; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; import static com.android.tools.r8.utils.codeinspector.Matchers.isImplementing; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.MatcherAssert.assertThat; @@ -17,6 +18,7 @@ import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -42,13 +44,7 @@ .addKeepMainRule(Main.class) // I and J are not eligible for merging, since they declare the same default method. .addHorizontallyMergedClassesInspector( - inspector -> { - if (parameters.canUseDefaultAndStaticInterfaceMethods()) { - inspector.assertNoClassesMerged(); - } else { - inspector.assertIsCompleteMergeGroup(I.class, J.class); - } - }) + HorizontallyMergedClassesInspector::assertNoClassesMerged) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoHorizontalClassMergingAnnotations() @@ -59,10 +55,17 @@ .inspect( inspector -> { ClassSubject aClassSubject = inspector.clazz(A.class); + ClassSubject bClassSubject = inspector.clazz(B.class); + if (!parameters.canUseDefaultAndStaticInterfaceMethods()) { + // When desugaring, the forwarding methods to the CC.m methods will be inlined and + // the class instances become dead code. + assertThat(aClassSubject, isAbsent()); + assertThat(bClassSubject, isAbsent()); + return; + } assertThat(aClassSubject, isPresent()); assertThat(aClassSubject, isImplementing(inspector.clazz(I.class))); - ClassSubject bClassSubject = inspector.clazz(B.class); assertThat(bClassSubject, isPresent()); assertThat( bClassSubject,
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java index 2f157d0..3b35a12 100644 --- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -50,6 +50,7 @@ import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; +import org.junit.Assume; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -355,6 +356,7 @@ @Test public void testNestedDefaultInterfaceMethodsTest() throws Throwable { + Assume.assumeTrue("b/197494749", parameters.canUseDefaultAndStaticInterfaceMethods()); String main = "classmerging.NestedDefaultInterfaceMethodsTest"; Path[] programFiles = new Path[] {
diff --git a/src/test/java/com/android/tools/r8/code/invokedynamic/InvokeDynamicVirtualDispatchToDefaultInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/code/invokedynamic/InvokeDynamicVirtualDispatchToDefaultInterfaceMethodTest.java new file mode 100644 index 0000000..7494b13 --- /dev/null +++ b/src/test/java/com/android/tools/r8/code/invokedynamic/InvokeDynamicVirtualDispatchToDefaultInterfaceMethodTest.java
@@ -0,0 +1,144 @@ +// 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.code.invokedynamic; + +import static org.junit.Assume.assumeTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.references.ClassReference; +import com.android.tools.r8.references.MethodReference; +import com.android.tools.r8.references.Reference; +import com.android.tools.r8.utils.StringUtils; +import java.lang.invoke.CallSite; +import java.lang.invoke.ConstantCallSite; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.MethodType; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.objectweb.asm.Handle; +import org.objectweb.asm.Opcodes; + +/** + * Test invokedynamic with a static bootstrap method with an extra arg that is a MethodHandle of + * kind invoke virtual. The target method is a method into a class implementing an abstract method + * and that shadows a default method from an interface. + */ +// TODO(b/167145686): Copy this test and implement all of the variants in +// ...AndroidOTest.invokeCustom... and then delete those tests. +@RunWith(Parameterized.class) +public class InvokeDynamicVirtualDispatchToDefaultInterfaceMethodTest extends TestBase { + + static final String EXPECTED = StringUtils.lines("Called I.foo"); + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters() + .withAllRuntimes() + .withApiLevelsStartingAtIncluding(apiLevelWithInvokeCustomSupport()) + .enableApiLevelsForCf() + .build(); + } + + public InvokeDynamicVirtualDispatchToDefaultInterfaceMethodTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testReference() throws Exception { + testForDesugaring(parameters) + .addProgramClasses(I.class, A.class) + .addProgramClassFileData(getTransformedTestClass()) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); + } + + @Test + public void testR8() throws Exception { + assumeTrue( + "Only test one R8/CF build.", + parameters.isDexRuntime() || parameters.getApiLevel() == apiLevelWithInvokeCustomSupport()); + testForR8(parameters.getBackend()) + .allowAccessModification() + .addProgramClasses(I.class, A.class) + .addProgramClassFileData(getTransformedTestClass()) + .setMinApi(parameters.getApiLevel()) + .addKeepMainRule(TestClass.class) + .addKeepRules("-keepclassmembers class * { *** foo(...); }") + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); + } + + private byte[] getTransformedTestClass() throws Exception { + ClassReference aClass = Reference.classFromClass(A.class); + MethodReference iFoo = Reference.methodFromMethod(I.class.getDeclaredMethod("foo")); + MethodReference bsm = + Reference.methodFromMethod( + TestClass.class.getDeclaredMethod( + "bsmCreateCallSite", + Lookup.class, + String.class, + MethodType.class, + MethodHandle.class)); + return transformer(TestClass.class) + .transformMethodInsnInMethod( + "main", + (opcode, owner, name, descriptor, isInterface, visitor) -> { + if (name.equals("replaced")) { + visitor.visitInvokeDynamicInsn( + iFoo.getMethodName(), + "(" + aClass.getDescriptor() + ")V", + new Handle( + Opcodes.H_INVOKESTATIC, + bsm.getHolderClass().getBinaryName(), + bsm.getMethodName(), + bsm.getMethodDescriptor(), + false), + new Handle( + Opcodes.H_INVOKEVIRTUAL, + aClass.getBinaryName(), + iFoo.getMethodName(), + iFoo.getMethodDescriptor(), + false)); + } else { + visitor.visitMethodInsn(opcode, owner, name, descriptor, isInterface); + } + }) + .transform(); + } + + public interface I { + + default void foo() { + System.out.println("Called I.foo"); + } + } + + public static class A implements I { + // Instantiation with default from I. + } + + static class TestClass { + + public static CallSite bsmCreateCallSite( + MethodHandles.Lookup caller, String name, MethodType type, MethodHandle handle) + throws Throwable { + return new ConstantCallSite(handle); + } + + public static void replaced(Object o) { + throw new RuntimeException("unreachable!"); + } + + public static void main(String[] args) { + replaced(new A()); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java b/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java index 7cde792..2e9abfb 100644 --- a/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java +++ b/src/test/java/com/android/tools/r8/desugar/DefaultLambdaWithUnderscoreThisTestRunner.java
@@ -100,9 +100,9 @@ testForD8() .addProgramClassesAndInnerClasses(CLASS) .setMinApiThreshold(AndroidApiLevel.K) - .compile(); + .compile() + .assertNoMessages(); compileResult - // TODO(b/123506120): Add .assertNoMessages() .run(parameters.getRuntime(), CLASS) .assertSuccessWithOutput(EXPECTED); runDebugger(compileResult.debugConfig(), true);
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarInnerClassesInInterfaces.java b/src/test/java/com/android/tools/r8/desugar/DesugarInnerClassesInInterfaces.java index 1c8d5b9..8902cd2 100644 --- a/src/test/java/com/android/tools/r8/desugar/DesugarInnerClassesInInterfaces.java +++ b/src/test/java/com/android/tools/r8/desugar/DesugarInnerClassesInInterfaces.java
@@ -4,11 +4,13 @@ package com.android.tools.r8.desugar; import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringForTesting.getCompanionClassNameSuffix; +import static org.junit.Assume.assumeTrue; import com.android.tools.r8.DesugarTestConfiguration; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.AndroidApiLevel; import com.google.common.collect.ImmutableList; import java.util.List; import java.util.concurrent.Callable; @@ -30,13 +32,6 @@ WithLocalInner.class.getName() + getCompanionClassNameSuffix(), "true"); - private final List<String> EXPECTED_RESULT_WITH_DESUGARING_B168697955 = - ImmutableList.of( - WithAnonymousInner.class.getName() + getCompanionClassNameSuffix(), - "false", - WithLocalInner.class.getName() + getCompanionClassNameSuffix(), - "false"); - @Parameterized.Parameters(name = "{0}") public static TestParametersCollection data() { return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(); @@ -69,6 +64,7 @@ @Test public void testR8Compat() throws Exception { + assumeTrue(parameters.isDexRuntime() || parameters.getApiLevel() == AndroidApiLevel.B); testForR8Compat(parameters.getBackend()) .addInnerClasses(DesugarInnerClassesInInterfaces.class) .setMinApi(parameters.getApiLevel()) @@ -78,37 +74,14 @@ .compile() .run(parameters.getRuntime(), TestClass.class) .applyIf( - parameters.canUseDefaultAndStaticInterfaceMethods(), + parameters.isCfRuntime() || parameters.canUseDefaultAndStaticInterfaceMethods(), result -> result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITHOUT_DESUGARING), - // The static method which is moved to the companion class is inlined and causing - // this different output. The rule "-keep class * { *; }" does not keep the static - // method from being inlined after it has moved. Turning off inlining produces the - // expected result. The inlining cause the getEnclosingClass() to return null. - // See b/168697955. - result -> - result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITH_DESUGARING_B168697955)); - } - - @Test - public void testR8_B168697955() throws Exception { - testForR8(parameters.getBackend()) - .addInnerClasses(DesugarInnerClassesInInterfaces.class) - .setMinApi(parameters.getApiLevel()) - .addKeepAllClassesRule() - .addKeepAttributeInnerClassesAndEnclosingMethod() - // With inlining turned off we get the expected result. - .addOptionsModification(options -> options.enableInlining = false) - .compile() - .run(parameters.getRuntime(), TestClass.class) - .applyIf( - parameters.canUseDefaultAndStaticInterfaceMethods(), - result -> result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITHOUT_DESUGARING), - // TODO(b/187377562): We remove the attribute due to not pinning the moved methods. - result -> result.assertFailureWithErrorThatThrows(NullPointerException.class)); + result -> result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITH_DESUGARING)); } @Test public void testR8Full() throws Exception { + assumeTrue(parameters.isDexRuntime() || parameters.getApiLevel() == AndroidApiLevel.B); testForR8(parameters.getBackend()) .addInnerClasses(DesugarInnerClassesInInterfaces.class) .setMinApi(parameters.getApiLevel()) @@ -117,10 +90,9 @@ .compile() .run(parameters.getRuntime(), TestClass.class) .applyIf( - parameters.canUseDefaultAndStaticInterfaceMethods(), + parameters.isCfRuntime() || parameters.canUseDefaultAndStaticInterfaceMethods(), result -> result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITHOUT_DESUGARING), - // TODO(b/187377562): We remove the attribute due to not pinning the moved methods. - result -> result.assertFailureWithErrorThatThrows(NullPointerException.class)); + result -> result.assertSuccessWithOutputLines(EXPECTED_RESULT_WITH_DESUGARING)); } interface WithAnonymousInner {
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithAnonymousClass.java b/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithAnonymousClass.java index 9691b07..23436d3 100644 --- a/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithAnonymousClass.java +++ b/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithAnonymousClass.java
@@ -34,7 +34,8 @@ private final List<String> EXPECTED_D8_DESUGARED_RESULT = ImmutableList.of( - "Hello from inside lambda$test$0$DesugarLambdaWithAnonymousClass$TestClass", + "Hello from inside" + + " lambda$test$0$com-android-tools-r8-desugar-DesugarLambdaWithAnonymousClass$TestClass", "Hello from inside lambda$testStatic$1"); @Parameterized.Parameters(name = "{0}")
diff --git a/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithLocalClass.java b/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithLocalClass.java index 62c36fd..60fde2f 100644 --- a/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithLocalClass.java +++ b/src/test/java/com/android/tools/r8/desugar/DesugarLambdaWithLocalClass.java
@@ -33,7 +33,8 @@ private List<String> EXPECTED_D8_DESUGARED_RESULT = ImmutableList.of( - "Hello from inside lambda$test$0$DesugarLambdaWithLocalClass$TestClass", + "Hello from inside" + + " lambda$test$0$com-android-tools-r8-desugar-DesugarLambdaWithLocalClass$TestClass", "Hello from inside lambda$testStatic$1"); @Parameterized.Parameters(name = "{0}")
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/BasicConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/BasicConstantDynamicTest.java index b0d261e..aede694 100644 --- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/BasicConstantDynamicTest.java +++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/BasicConstantDynamicTest.java
@@ -4,19 +4,16 @@ package com.android.tools.r8.desugar.constantdynamic; import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage; -import static com.android.tools.r8.DiagnosticsMatcher.diagnosticOrigin; -import static com.android.tools.r8.OriginMatcher.hasParent; -import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertThrows; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationFailedException; +import com.android.tools.r8.DesugarTestConfiguration; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestRuntime.CfVm; import com.android.tools.r8.cf.CfVersion; -import com.android.tools.r8.origin.Origin; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.StringUtils; import java.io.IOException; @@ -40,6 +37,7 @@ } private static final String EXPECTED_OUTPUT = StringUtils.lines("true", "true"); + private static final Class<?> MAIN_CLASS = A.class; @Test public void testReference() throws Exception { @@ -49,49 +47,73 @@ testForJvm() .addProgramClassFileData(getTransformedClasses()) - .run(parameters.getRuntime(), A.class) + .run(parameters.getRuntime(), MAIN_CLASS) .assertSuccessWithOutput(EXPECTED_OUTPUT); } @Test + public void TestD8Cf() throws Exception { + testForDesugaring(parameters) + .addProgramClassFileData(getTransformedClasses()) + .run(parameters.getRuntime(), MAIN_CLASS) + .applyIf( + // When not desugaring the CF code requires JDK 11. + DesugarTestConfiguration::isNotDesugared, + r -> { + if (parameters.isCfRuntime() + && parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11)) { + r.assertSuccessWithOutput(EXPECTED_OUTPUT); + } else { + r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class); + } + }) + .applyIf( + DesugarTestConfiguration::isDesugared, r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT)); + } + + @Test public void testD8() throws Exception { assumeTrue(parameters.getRuntime().isDex()); - assertThrows( - CompilationFailedException.class, - () -> - testForD8(parameters.getBackend()) - .addProgramClassFileData(getTransformedClasses()) - .setMinApi(parameters.getApiLevel()) - .compileWithExpectedDiagnostics( - diagnostics -> { - diagnostics.assertOnlyErrors(); - diagnostics.assertErrorsMatch( - allOf( - diagnosticMessage(containsString("Unsupported dynamic constant")), - diagnosticOrigin(hasParent(Origin.unknown())))); - })); + testForD8(parameters.getBackend()) + .addProgramClassFileData(getTransformedClasses()) + .setMinApi(parameters.getApiLevel()) + .run(parameters.getRuntime(), MAIN_CLASS) + .assertSuccessWithOutput(EXPECTED_OUTPUT); } @Test public void testR8() throws Exception { assumeTrue(parameters.isDexRuntime() || parameters.getApiLevel().isEqualTo(AndroidApiLevel.B)); - assertThrows( - CompilationFailedException.class, - () -> - testForR8(parameters.getBackend()) - .addProgramClassFileData(getTransformedClasses()) - .setMinApi(parameters.getApiLevel()) - .addKeepMainRule(A.class) - .compileWithExpectedDiagnostics( - diagnostics -> { - diagnostics.assertOnlyErrors(); - diagnostics.assertErrorsMatch( - allOf( - diagnosticMessage(containsString("Unsupported dynamic constant")), - diagnosticOrigin(hasParent(Origin.unknown())))); - })); + testForR8(parameters.getBackend()) + .addProgramClassFileData(getTransformedClasses()) + .setMinApi(parameters.getApiLevel()) + .addKeepMainRule(MAIN_CLASS) + // TODO(b/198142613): There should not be a warnings on class references which are + // desugared away. + .applyIf( + parameters.getApiLevel().isLessThan(AndroidApiLevel.O), + b -> b.addDontWarn("java.lang.invoke.MethodHandles$Lookup")) + // TODO(b/198142625): Support CONSTANT_Dynamic output for class files. + .applyIf( + parameters.isCfRuntime(), + r -> { + assertThrows( + CompilationFailedException.class, + () -> + r.compileWithExpectedDiagnostics( + diagnostics -> { + diagnostics.assertOnlyErrors(); + diagnostics.assertErrorsMatch( + diagnosticMessage( + containsString( + "Unsupported dynamic constant (not desugaring)"))); + })); + }, + r -> + r.run(parameters.getRuntime(), MAIN_CLASS) + .assertSuccessWithOutput(EXPECTED_OUTPUT)); } private byte[] getTransformedClasses() throws IOException {
diff --git a/src/test/java/com/android/tools/r8/cf/ConstantDynamicHolderTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicHolderTest.java similarity index 79% rename from src/test/java/com/android/tools/r8/cf/ConstantDynamicHolderTest.java rename to src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicHolderTest.java index 1ec23ca..48cbeaf 100644 --- a/src/test/java/com/android/tools/r8/cf/ConstantDynamicHolderTest.java +++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicHolderTest.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.cf; +package com.android.tools.r8.desugar.constantdynamic; import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertEquals; @@ -14,10 +14,12 @@ import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.TestRuntime.CfVm; +import com.android.tools.r8.utils.AndroidApiLevel; import java.io.IOException; 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; import org.objectweb.asm.ConstantDynamic; import org.objectweb.asm.Handle; @@ -25,24 +27,19 @@ @RunWith(Parameterized.class) public class ConstantDynamicHolderTest extends TestBase { + @Parameter() public TestParameters parameters; + @Parameters(name = "{0}") public static TestParametersCollection data() { - return getTestParameters() - .withCfRuntimesStartingFromIncluding(CfVm.JDK11) - .withDexRuntimes() - .withAllApiLevels() - .build(); - } - - final TestParameters parameters; - - public ConstantDynamicHolderTest(TestParameters parameters) { - this.parameters = parameters; + return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(); } @Test public void testReference() throws Exception { assumeTrue(parameters.isCfRuntime()); + assumeTrue(parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11)); + assumeTrue(parameters.getApiLevel().isEqualTo(AndroidApiLevel.B)); + testForJvm() .addProgramClassFileData(getTransformedMain()) .run(parameters.getRuntime(), Main.class) @@ -52,17 +49,21 @@ @Test(expected = CompilationFailedException.class) public void testD8() throws Exception { assumeTrue(parameters.isDexRuntime()); + testForD8() .addProgramClassFileData(getTransformedMain()) .setMinApi(parameters.getApiLevel()) .compileWithExpectedDiagnostics( diagnostics -> diagnostics.assertErrorMessageThatMatches( - containsString("Unsupported dynamic constant"))); + containsString( + "Unsupported dynamic constant (runtime provided bootstrap method)"))); } @Test(expected = CompilationFailedException.class) public void testR8() throws Exception { + assumeTrue(parameters.isDexRuntime() || parameters.getApiLevel().isEqualTo(AndroidApiLevel.B)); + testForR8(parameters.getBackend()) .addProgramClassFileData(getTransformedMain()) .setMinApi(parameters.getApiLevel()) @@ -70,7 +71,8 @@ .compileWithExpectedDiagnostics( diagnostics -> diagnostics.assertErrorMessageThatMatches( - containsString("Unsupported dynamic constant"))); + containsString( + "Unsupported dynamic constant (runtime provided bootstrap method)"))); } private byte[] getTransformedMain() throws IOException {
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/HierarchyConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/HierarchyConstantDynamicTest.java new file mode 100644 index 0000000..edc3cab --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/HierarchyConstantDynamicTest.java
@@ -0,0 +1,142 @@ +// 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.constantdynamic; + +import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThrows; +import static org.junit.Assume.assumeTrue; + +import com.android.tools.r8.CompilationFailedException; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestRuntime.CfVm; +import com.android.tools.r8.cf.CfVersion; +import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.utils.StringUtils; +import com.google.common.collect.ImmutableList; +import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.util.Collection; +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 HierarchyConstantDynamicTest extends TestBase { + + @Parameter() public TestParameters parameters; + + @Parameters(name = "{0}") + public static List<Object[]> data() { + return buildParameters( + getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build()); + } + + private static final String EXPECTED_OUTPUT = StringUtils.lines("true", "true", "true"); + private static final Class<?> MAIN_CLASS = A.class; + + @Test + public void testReference() throws Exception { + assumeTrue(parameters.isCfRuntime()); + assumeTrue(parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11)); + assumeTrue(parameters.getApiLevel().isEqualTo(AndroidApiLevel.B)); + + testForJvm() + .addProgramClassFileData(getTransformedClasses()) + .run(parameters.getRuntime(), A.class) + .assertSuccessWithOutput(EXPECTED_OUTPUT); + } + + @Test + public void testD8() throws Exception { + assumeTrue(parameters.isDexRuntime()); + + testForD8(parameters.getBackend()) + .addProgramClassFileData(getTransformedClasses()) + .setMinApi(parameters.getApiLevel()) + .run(parameters.getRuntime(), MAIN_CLASS) + .assertSuccessWithOutput(EXPECTED_OUTPUT); + } + + @Test + public void testR8() throws Exception { + assumeTrue(parameters.isDexRuntime() || parameters.getApiLevel().isEqualTo(AndroidApiLevel.B)); + + testForR8(parameters.getBackend()) + .addProgramClassFileData(getTransformedClasses()) + .setMinApi(parameters.getApiLevel()) + .addKeepMainRule(MAIN_CLASS) + // TODO(b/198142613): There should not be a warnings on class references which are + // desugared away. + .applyIf( + parameters.getApiLevel().isLessThan(AndroidApiLevel.O), + b -> b.addDontWarn("java.lang.invoke.MethodHandles$Lookup")) + // TODO(b/198142625): Support CONSTANT_Dynamic output for class files. + .applyIf( + parameters.isCfRuntime(), + r -> { + assertThrows( + CompilationFailedException.class, + () -> + r.compileWithExpectedDiagnostics( + diagnostics -> { + diagnostics.assertOnlyErrors(); + diagnostics.assertErrorsMatch( + diagnosticMessage( + containsString( + "Unsupported dynamic constant (not desugaring)"))); + })); + }, + r -> + r.run(parameters.getRuntime(), MAIN_CLASS) + .assertSuccessWithOutput(EXPECTED_OUTPUT)); + } + + private Collection<byte[]> getTransformedClasses() throws IOException { + return ImmutableList.of( + transformer(A.class) + .setVersion(CfVersion.V11) + .transformConstStringToConstantDynamic( + "condy1", A.class, "myConstant", "constantName", Object.class) + .transform(), + transformer(B.class) + .setVersion(CfVersion.V11) + .transformConstStringToConstantDynamic( + "condy1", B.class, "myConstant", "constantName", Object.class) + .transform()); + } + + // The name of the bootstrap method is the same in both A and B. + public static class A { + + public static Object f() { + return "condy1"; // Will be transformed to Constant_DYNAMIC. + } + + public static void main(String[] args) { + System.out.println(f() != null); + System.out.println(B.f() != null); + System.out.println(f() != B.f()); + } + + private static Object myConstant(MethodHandles.Lookup lookup, String name, Class<?> type) { + return new Object(); + } + } + + public static class B extends A { + + public static Object f() { + return "condy1"; // Will be transformed to Constant_DYNAMIC. + } + + private static Object myConstant(MethodHandles.Lookup lookup, String name, Class<?> type) { + return new Object(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicTest.java index d1a4d35..86fad6d 100644 --- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicTest.java +++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/JacocoConstantDynamicTest.java
@@ -3,20 +3,13 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.desugar.constantdynamic; -import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage; -import static com.android.tools.r8.DiagnosticsMatcher.diagnosticOrigin; -import static com.android.tools.r8.OriginMatcher.hasParent; import static com.android.tools.r8.utils.DescriptorUtils.JAVA_PACKAGE_SEPARATOR; import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION; import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION; -import static org.hamcrest.CoreMatchers.allOf; -import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThrows; import static org.junit.Assume.assumeTrue; -import com.android.tools.r8.CompilationFailedException; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestRuntime.CfVm; @@ -24,7 +17,6 @@ import com.android.tools.r8.ToolHelper.DexVm; import com.android.tools.r8.ToolHelper.ProcessResult; import com.android.tools.r8.cf.CfVersion; -import com.android.tools.r8.utils.ArchiveResourceProvider; import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.ZipUtils; @@ -45,8 +37,7 @@ @RunWith(Parameterized.class) public class JacocoConstantDynamicTest extends TestBase { - @Parameter(0) - public TestParameters parameters; + @Parameter() public TestParameters parameters; @Parameter(1) public boolean useConstantDynamic; @@ -140,25 +131,11 @@ assertFalse(Files.exists(agentOutput)); } } else { - assertThrows( - CompilationFailedException.class, - () -> { - ArchiveResourceProvider provider = - ArchiveResourceProvider.fromArchive(testClasses.getInstrumented(), true); - testForD8(parameters.getBackend()) - .addProgramResourceProviders(provider) - .addProgramFiles(ToolHelper.JACOCO_AGENT) - .setMinApi(parameters.getApiLevel()) - .compileWithExpectedDiagnostics( - diagnostics -> { - // Check that the error is reported as an error to the diagnostics handler. - diagnostics.assertOnlyErrors(); - diagnostics.assertErrorsMatch( - allOf( - diagnosticMessage(containsString("Unsupported dynamic constant")), - diagnosticOrigin(hasParent(provider.getOrigin())))); - }); - }); + testForD8(parameters.getBackend()) + .addProgramFiles(testClasses.getInstrumented()) + .addProgramFiles(ToolHelper.JACOCO_AGENT) + .setMinApi(parameters.getApiLevel()) + .compile(); } }
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleNamedConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleNamedConstantDynamicTest.java index 69cd883..e1574f1 100644 --- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleNamedConstantDynamicTest.java +++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleNamedConstantDynamicTest.java
@@ -4,19 +4,16 @@ package com.android.tools.r8.desugar.constantdynamic; import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage; -import static com.android.tools.r8.DiagnosticsMatcher.diagnosticOrigin; -import static com.android.tools.r8.OriginMatcher.hasParent; -import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertThrows; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationFailedException; +import com.android.tools.r8.DesugarTestConfiguration; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestRuntime.CfVm; import com.android.tools.r8.cf.CfVersion; -import com.android.tools.r8.origin.Origin; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.StringUtils; import java.io.IOException; @@ -42,6 +39,7 @@ private static final String EXPECTED_OUTPUT = StringUtils.lines("true", "true", "true", "true", "true"); + private static final Class<?> MAIN_CLASS = A.class; @Test public void testReference() throws Exception { @@ -51,28 +49,39 @@ testForJvm() .addProgramClassFileData(getTransformedClasses()) - .run(parameters.getRuntime(), A.class) + .run(parameters.getRuntime(), MAIN_CLASS) .assertSuccessWithOutput(EXPECTED_OUTPUT); } @Test + public void TestD8Cf() throws Exception { + testForDesugaring(parameters) + .addProgramClassFileData(getTransformedClasses()) + .run(parameters.getRuntime(), MAIN_CLASS) + .applyIf( + // When not desugaring the CF code requires JDK 11. + DesugarTestConfiguration::isNotDesugared, + r -> { + if (parameters.isCfRuntime() + && parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11)) { + r.assertSuccessWithOutput(EXPECTED_OUTPUT); + } else { + r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class); + } + }) + .applyIf( + DesugarTestConfiguration::isDesugared, r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT)); + } + + @Test public void testD8() throws Exception { assumeTrue(parameters.isDexRuntime()); - assertThrows( - CompilationFailedException.class, - () -> - testForD8(parameters.getBackend()) - .addProgramClassFileData(getTransformedClasses()) - .setMinApi(parameters.getApiLevel()) - .compileWithExpectedDiagnostics( - diagnostics -> { - diagnostics.assertOnlyErrors(); - diagnostics.assertErrorsMatch( - allOf( - diagnosticMessage(containsString("Unsupported dynamic constant")), - diagnosticOrigin(hasParent(Origin.unknown())))); - })); + testForD8(parameters.getBackend()) + .addProgramClassFileData(getTransformedClasses()) + .setMinApi(parameters.getApiLevel()) + .run(parameters.getRuntime(), MAIN_CLASS) + .assertSuccessWithOutput(EXPECTED_OUTPUT); } @Test @@ -80,21 +89,34 @@ assumeTrue( parameters.getRuntime().isDex() || parameters.getApiLevel().isEqualTo(AndroidApiLevel.B)); - assertThrows( - CompilationFailedException.class, - () -> - testForR8(parameters.getBackend()) - .addProgramClassFileData(getTransformedClasses()) - .setMinApi(parameters.getApiLevel()) - .addKeepMainRule(A.class) - .compileWithExpectedDiagnostics( - diagnostics -> { - diagnostics.assertOnlyErrors(); - diagnostics.assertErrorsMatch( - allOf( - diagnosticMessage(containsString("Unsupported dynamic constant")), - diagnosticOrigin(hasParent(Origin.unknown())))); - })); + testForR8(parameters.getBackend()) + .addProgramClassFileData(getTransformedClasses()) + .setMinApi(parameters.getApiLevel()) + .addKeepMainRule(A.class) + // TODO(b/198142613): There should not be a warnings on class references which are + // desugared away. + .applyIf( + parameters.getApiLevel().isLessThan(AndroidApiLevel.O), + b -> b.addDontWarn("java.lang.invoke.MethodHandles$Lookup")) + // TODO(b/198142625): Support CONSTANT_Dynamic output for class files. + .applyIf( + parameters.isCfRuntime(), + r -> { + assertThrows( + CompilationFailedException.class, + () -> + r.compileWithExpectedDiagnostics( + diagnostics -> { + diagnostics.assertOnlyErrors(); + diagnostics.assertErrorsMatch( + diagnosticMessage( + containsString( + "Unsupported dynamic constant (not desugaring)"))); + })); + }, + r -> + r.run(parameters.getRuntime(), MAIN_CLASS) + .assertSuccessWithOutput(EXPECTED_OUTPUT)); } private byte[] getTransformedClasses() throws IOException { @@ -138,6 +160,9 @@ } private static Object myConstant(MethodHandles.Lookup lookup, String name, Class<?> type) { + // TODO(b/178172809): Use a un-moddeled library method to prevent R8 from seeing that name + // is unused. + name.toCharArray(); return new Object(); } }
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleTypesConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleTypesConstantDynamicTest.java index b04792f..e0082a0 100644 --- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleTypesConstantDynamicTest.java +++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleTypesConstantDynamicTest.java
@@ -4,19 +4,16 @@ package com.android.tools.r8.desugar.constantdynamic; import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage; -import static com.android.tools.r8.DiagnosticsMatcher.diagnosticOrigin; -import static com.android.tools.r8.OriginMatcher.hasParent; -import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertThrows; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationFailedException; +import com.android.tools.r8.DesugarTestConfiguration; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestRuntime.CfVm; import com.android.tools.r8.cf.CfVersion; -import com.android.tools.r8.origin.Origin; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.StringUtils; import java.io.IOException; @@ -41,6 +38,7 @@ private static final String EXPECTED_OUTPUT = StringUtils.lines("true", "true", "true", "true", "true"); + private static final Class<?> MAIN_CLASS = A.class; @Test public void testReference() throws Exception { @@ -55,23 +53,34 @@ } @Test + public void TestD8Cf() throws Exception { + testForDesugaring(parameters) + .addProgramClassFileData(getTransformedClasses()) + .run(parameters.getRuntime(), MAIN_CLASS) + .applyIf( + // When not desugaring the CF code requires JDK 11. + DesugarTestConfiguration::isNotDesugared, + r -> { + if (parameters.isCfRuntime() + && parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11)) { + r.assertSuccessWithOutput(EXPECTED_OUTPUT); + } else { + r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class); + } + }) + .applyIf( + DesugarTestConfiguration::isDesugared, r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT)); + } + + @Test public void testD8() throws Exception { assumeTrue(parameters.isDexRuntime()); - assertThrows( - CompilationFailedException.class, - () -> - testForD8(parameters.getBackend()) - .addProgramClassFileData(getTransformedClasses()) - .setMinApi(parameters.getApiLevel()) - .compileWithExpectedDiagnostics( - diagnostics -> { - diagnostics.assertOnlyErrors(); - diagnostics.assertErrorsMatch( - allOf( - diagnosticMessage(containsString("Unsupported dynamic constant")), - diagnosticOrigin(hasParent(Origin.unknown())))); - })); + testForD8(parameters.getBackend()) + .addProgramClassFileData(getTransformedClasses()) + .setMinApi(parameters.getApiLevel()) + .run(parameters.getRuntime(), MAIN_CLASS) + .assertSuccessWithOutput(EXPECTED_OUTPUT); } @Test @@ -79,21 +88,34 @@ assumeTrue( parameters.getRuntime().isDex() || parameters.getApiLevel().isEqualTo(AndroidApiLevel.B)); - assertThrows( - CompilationFailedException.class, - () -> - testForR8(parameters.getBackend()) - .addProgramClassFileData(getTransformedClasses()) - .setMinApi(parameters.getApiLevel()) - .addKeepMainRule(A.class) - .compileWithExpectedDiagnostics( - diagnostics -> { - diagnostics.assertOnlyErrors(); - diagnostics.assertErrorsMatch( - allOf( - diagnosticMessage(containsString("Unsupported dynamic constant")), - diagnosticOrigin(hasParent(Origin.unknown())))); - })); + testForR8(parameters.getBackend()) + .addProgramClassFileData(getTransformedClasses()) + .setMinApi(parameters.getApiLevel()) + .addKeepMainRule(MAIN_CLASS) + // TODO(b/198142613): There should not be a warnings on class references which are + // desugared away. + .applyIf( + parameters.getApiLevel().isLessThan(AndroidApiLevel.O), + b -> b.addDontWarn("java.lang.invoke.MethodHandles$Lookup")) + // TODO(b/198142625): Support CONSTANT_Dynamic output for class files. + .applyIf( + parameters.isCfRuntime(), + r -> { + assertThrows( + CompilationFailedException.class, + () -> + r.compileWithExpectedDiagnostics( + diagnostics -> { + diagnostics.assertOnlyErrors(); + diagnostics.assertErrorsMatch( + diagnosticMessage( + containsString( + "Unsupported dynamic constant (not desugaring)"))); + })); + }, + r -> + r.run(parameters.getRuntime(), MAIN_CLASS) + .assertSuccessWithOutput(EXPECTED_OUTPUT)); } private byte[] getTransformedClasses() throws IOException {
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/SharedBootstrapMethodConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/SharedBootstrapMethodConstantDynamicTest.java index 24c9ed8..355a5bd 100644 --- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/SharedBootstrapMethodConstantDynamicTest.java +++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/SharedBootstrapMethodConstantDynamicTest.java
@@ -44,6 +44,7 @@ private static final String EXPECTED_OUTPUT = StringUtils.lines("true", "true", "true", "true", "true"); + private static final Class<?> MAIN_CLASS = A.class; @Test public void testReference() throws Exception { @@ -53,11 +54,28 @@ testForJvm() .addProgramClassFileData(getTransformedClasses()) - .run(parameters.getRuntime(), A.class) + .run(parameters.getRuntime(), MAIN_CLASS) .assertSuccessWithOutput(EXPECTED_OUTPUT); } @Test + public void TestD8Cf() throws Exception { + assertThrows( + CompilationFailedException.class, + () -> + testForD8(Backend.CF) + .addProgramClassFileData(getTransformedClasses()) + .setMinApi(parameters.getApiLevel()) + .compileWithExpectedDiagnostics( + diagnostics -> { + diagnostics.assertOnlyErrors(); + diagnostics.assertErrorsMatch( + diagnosticMessage( + containsString("Unsupported dynamic constant (different owner)"))); + })); + } + + @Test public void testD8() throws Exception { assumeTrue(parameters.isDexRuntime()); @@ -72,7 +90,8 @@ diagnostics.assertOnlyErrors(); diagnostics.assertErrorsMatch( allOf( - diagnosticMessage(containsString("Unsupported dynamic constant")), + diagnosticMessage( + containsString("Unsupported dynamic constant (different owner)")), diagnosticOrigin(hasParent(Origin.unknown())))); })); } @@ -87,13 +106,14 @@ testForR8(parameters.getBackend()) .addProgramClassFileData(getTransformedClasses()) .setMinApi(parameters.getApiLevel()) - .addKeepMainRule(A.class) + .addKeepMainRule(MAIN_CLASS) .compileWithExpectedDiagnostics( diagnostics -> { diagnostics.assertOnlyErrors(); diagnostics.assertErrorsMatch( allOf( - diagnosticMessage(containsString("Unsupported dynamic constant")), + diagnosticMessage( + containsString("Unsupported dynamic constant (different owner)")), diagnosticOrigin(hasParent(Origin.unknown())))); })); }
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 f253398..5e1b8c0 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
@@ -26,7 +26,6 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import org.junit.Assume; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -113,10 +112,6 @@ @Test public void testFileContent() throws Exception { - // Test require r8.jar not r8lib.jar, as the class com.android.tools.r8.GenerateLintFiles in - // not kept. - Assume.assumeTrue(!ToolHelper.isTestingR8Lib()); - Path directory = temp.newFolder().toPath(); GenerateLintFiles.main( new String[] { @@ -131,6 +126,9 @@ .parse(StringResource.fromFile(ToolHelper.getDesugarLibJsonForTesting())); for (AndroidApiLevel apiLevel : AndroidApiLevel.values()) { + if (apiLevel == AndroidApiLevel.UNKNOWN) { + continue; + } Path compileApiLevelDirectory = directory.resolve("compile_api_level_" + apiLevel.getLevel()); if (apiLevel.getLevel() < desugaredLibraryConfiguration.getRequiredCompilationApiLevel().getLevel()) {
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java index bb66f2e..01eb3b8 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/r8ondex/R8CompiledThroughDexTest.java
@@ -12,13 +12,13 @@ import com.android.tools.r8.R8; import com.android.tools.r8.R8Command; import com.android.tools.r8.R8Command.Builder; +import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.TestRuntime; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.ToolHelper.ProcessResult; -import com.android.tools.r8.cf.bootstrap.BootstrapCurrentEqualityTest; import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.Pair; @@ -166,7 +166,7 @@ + " differ from the external run which uses " + r8jar + ". If up-to-date, the likely cause of this error is that R8 is non-deterministic.", - BootstrapCurrentEqualityTest.filesAreEqual(outputThroughCf, outputThroughCfExternal)); + TestBase.filesAreEqual(outputThroughCf, outputThroughCfExternal)); } // Finally compile R8 on the ART runtime using the already compiled DEX version of R8. @@ -194,7 +194,7 @@ assertEquals(0, artProcessResult.exitCode); assertTrue( "The output of R8/JVM in-process and R8/ART external differ.", - BootstrapCurrentEqualityTest.filesAreEqual(outputThroughCf, outputThroughDex)); + TestBase.filesAreEqual(outputThroughCf, outputThroughDex)); } } }
diff --git a/src/test/java/com/android/tools/r8/desugar/enclosingmethod/EnclosingMethodRewriteTest.java b/src/test/java/com/android/tools/r8/desugar/enclosingmethod/EnclosingMethodRewriteTest.java index e7cc9c9..9a47de9 100644 --- a/src/test/java/com/android/tools/r8/desugar/enclosingmethod/EnclosingMethodRewriteTest.java +++ b/src/test/java/com/android/tools/r8/desugar/enclosingmethod/EnclosingMethodRewriteTest.java
@@ -6,7 +6,6 @@ import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.TestBase; @@ -73,8 +72,6 @@ "interface " + A.class.getTypeName(), "public int " + A.class.getTypeName() + ".def()", "42" }; - private final String[] EXPECTED_FULL = new String[] {"null", "null", "42"}; - @Parameterized.Parameters(name = "{0}") public static TestParametersCollection data() { return getTestParameters().withAllRuntimesAndApiLevels().build(); @@ -102,7 +99,7 @@ .addKeepAttributeInnerClassesAndEnclosingMethod() .setMinApi(parameters.getApiLevel()) .compile() - .inspect(inspector -> inspect(inspector, true)) + .inspect(this::inspect) .run(parameters.getRuntime(), MAIN) .applyIf( parameters.canUseDefaultAndStaticInterfaceMethods(), @@ -126,8 +123,7 @@ .addKeepAttributeInnerClassesAndEnclosingMethod() .setMinApi(parameters.getApiLevel()) .compile() - .inspect( - inspector -> inspect(inspector, parameters.canUseDefaultAndStaticInterfaceMethods())) + .inspect(this::inspect) .run(parameters.getRuntime(), MAIN) .applyIf( parameters.canUseDefaultAndStaticInterfaceMethods(), @@ -139,25 +135,21 @@ result.assertSuccessWithOutputLines(EXPECTED); } }, - result -> result.assertSuccessWithOutputLines(EXPECTED_FULL)); + result -> result.assertSuccessWithOutputLines(EXPECTED_CC)); } - private void inspect(CodeInspector inspector, boolean hasEnclosingMethod) { + private void inspect(CodeInspector inspector) { ClassSubject cImplSubject = inspector.clazz(A.class.getTypeName() + "$1"); assertThat(cImplSubject, isPresent()); ClassSubject enclosingClassSubject = parameters.canUseDefaultAndStaticInterfaceMethods() ? inspector.clazz(A.class.getTypeName()) - : inspector.clazz(A.class.getTypeName() + "$-CC"); + : inspector.clazz(A.class.getTypeName()).toCompanionClass(); assertThat(enclosingClassSubject, isPresent()); EnclosingMethodAttribute enclosingMethodAttribute = cImplSubject.getDexProgramClass().getEnclosingMethodAttribute(); - if (hasEnclosingMethod) { - assertEquals( - enclosingClassSubject.getDexProgramClass().getType(), - enclosingMethodAttribute.getEnclosingMethod().getHolderType()); - } else { - assertNull(enclosingMethodAttribute); - } + assertEquals( + enclosingClassSubject.getDexProgramClass().getType(), + enclosingMethodAttribute.getEnclosingMethod().getHolderType()); } }
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/OverlappingLambdaMethodInSubclassWithSameNameTest.java b/src/test/java/com/android/tools/r8/desugar/lambdas/OverlappingLambdaMethodInSubclassWithSameNameTest.java new file mode 100644 index 0000000..9f5fa37 --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/lambdas/OverlappingLambdaMethodInSubclassWithSameNameTest.java
@@ -0,0 +1,73 @@ +// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.desugar.lambdas; + +import static org.junit.Assume.assumeTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.desugar.lambdas.b197625454.OverlappingLambdaMethodInSubclassWithSameNameTestA; +import com.android.tools.r8.desugar.lambdas.b197625454.OverlappingLambdaMethodInSubclassWithSameNameTestB; +import com.android.tools.r8.utils.StringUtils; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; + +// See b/197625454 for details. +@RunWith(Parameterized.class) +public class OverlappingLambdaMethodInSubclassWithSameNameTest extends TestBase { + + static final String EXPECTED = + StringUtils.lines("Superclass lambda: Hello!", "Superclass lambda: Hello!"); + + private static final Class<?> MAIN_CLASS = + com.android.tools.r8.desugar.lambdas.b197625454.subpackage + .OverlappingLambdaMethodInSubclassWithSameNameTestA.class; + + @Parameter() public TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build(); + } + + @Test + public void testJvm() throws Exception { + assumeTrue(parameters.isCfRuntime()); + testForRuntime(parameters) + .addProgramClasses( + MAIN_CLASS, + OverlappingLambdaMethodInSubclassWithSameNameTestA.class, + OverlappingLambdaMethodInSubclassWithSameNameTestB.class) + .run(parameters.getRuntime(), MAIN_CLASS) + .assertSuccessWithOutput(EXPECTED); + } + + @Test + public void testD8() throws Exception { + testForD8(parameters.getBackend()) + .addProgramClasses( + MAIN_CLASS, + OverlappingLambdaMethodInSubclassWithSameNameTestA.class, + OverlappingLambdaMethodInSubclassWithSameNameTestB.class) + .setMinApi(parameters.getApiLevel()) + .run(parameters.getRuntime(), MAIN_CLASS) + .assertSuccessWithOutput(EXPECTED); + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addProgramClasses( + MAIN_CLASS, + OverlappingLambdaMethodInSubclassWithSameNameTestA.class, + OverlappingLambdaMethodInSubclassWithSameNameTestB.class) + .setMinApi(parameters.getApiLevel()) + .addKeepMainRule(MAIN_CLASS) + .run(parameters.getRuntime(), MAIN_CLASS) + .assertSuccessWithOutput(EXPECTED); + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/b197625454/OverlappingLambdaMethodInSubclassWithSameNameTestA.java b/src/test/java/com/android/tools/r8/desugar/lambdas/b197625454/OverlappingLambdaMethodInSubclassWithSameNameTestA.java new file mode 100644 index 0000000..25be7d6 --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/lambdas/b197625454/OverlappingLambdaMethodInSubclassWithSameNameTestA.java
@@ -0,0 +1,24 @@ +// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.desugar.lambdas.b197625454; + +// Same base class name as the class it extends. See b/197625454 for details. +public class OverlappingLambdaMethodInSubclassWithSameNameTestA + extends com.android.tools.r8.desugar.lambdas.b197625454.subpackage + .OverlappingLambdaMethodInSubclassWithSameNameTestA { + + @Override + public void myMethod() { + super.myMethod(); + if (Math.random() < 0) { // always false + Runnable local = () -> System.out.println("Subclass lambda: " + getString()); + } + r.run(); + } + + @Override + public String getString() { + return "Hello!"; + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/b197625454/OverlappingLambdaMethodInSubclassWithSameNameTestB.java b/src/test/java/com/android/tools/r8/desugar/lambdas/b197625454/OverlappingLambdaMethodInSubclassWithSameNameTestB.java new file mode 100644 index 0000000..45193ef --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/lambdas/b197625454/OverlappingLambdaMethodInSubclassWithSameNameTestB.java
@@ -0,0 +1,24 @@ +// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.desugar.lambdas.b197625454; + +import com.android.tools.r8.desugar.lambdas.b197625454.subpackage.OverlappingLambdaMethodInSubclassWithSameNameTestA; + +public class OverlappingLambdaMethodInSubclassWithSameNameTestB + extends OverlappingLambdaMethodInSubclassWithSameNameTestA { + + @Override + public void myMethod() { + super.myMethod(); + if (Math.random() < 0) { // always false + Runnable local = () -> System.out.println("Subclass lambda: " + getString()); + } + r.run(); + } + + @Override + public String getString() { + return "Hello!"; + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/lambdas/b197625454/subpackage/OverlappingLambdaMethodInSubclassWithSameNameTestA.java b/src/test/java/com/android/tools/r8/desugar/lambdas/b197625454/subpackage/OverlappingLambdaMethodInSubclassWithSameNameTestA.java new file mode 100644 index 0000000..3f8bdcd --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/lambdas/b197625454/subpackage/OverlappingLambdaMethodInSubclassWithSameNameTestA.java
@@ -0,0 +1,24 @@ +// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.desugar.lambdas.b197625454.subpackage; + +import com.android.tools.r8.desugar.lambdas.b197625454.OverlappingLambdaMethodInSubclassWithSameNameTestB; + +public abstract class OverlappingLambdaMethodInSubclassWithSameNameTestA { + + public Runnable r; + + public void myMethod() { + r = () -> System.out.println("Superclass lambda: " + getString()); + } + + public abstract String getString(); + + public static void main(String[] args) { + new com.android.tools.r8.desugar.lambdas.b197625454 + .OverlappingLambdaMethodInSubclassWithSameNameTestA() + .myMethod(); + new OverlappingLambdaMethodInSubclassWithSameNameTestB().myMethod(); + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordTest.java b/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordTest.java index 389744e..10e2d10 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordTest.java +++ b/src/test/java/com/android/tools/r8/desugar/records/EmptyRecordTest.java
@@ -55,7 +55,6 @@ .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) - .addOptionsModification(opt -> opt.testing.enableExperimentalRecordDesugaring = true) .compile() .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(EXPECTED_RESULT); @@ -72,7 +71,6 @@ .addKeepMainRule(MAIN_TYPE) .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) - .addOptionsModification(opt -> opt.testing.enableExperimentalRecordDesugaring = true) .compile() .writeToZip(); RecordTestUtils.assertRecordsAreRecords(output); @@ -89,7 +87,6 @@ .addKeepRules(RECORD_KEEP_RULE) .addKeepMainRule(MAIN_TYPE) .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) - .addOptionsModification(opt -> opt.testing.enableExperimentalRecordDesugaring = true) .compile() .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(EXPECTED_RESULT);
diff --git a/src/test/java/com/android/tools/r8/desugar/records/InvalidRecordAttributeTest.java b/src/test/java/com/android/tools/r8/desugar/records/InvalidRecordAttributeTest.java deleted file mode 100644 index 7064af6..0000000 --- a/src/test/java/com/android/tools/r8/desugar/records/InvalidRecordAttributeTest.java +++ /dev/null
@@ -1,111 +0,0 @@ -// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -package com.android.tools.r8.desugar.records; - -import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage; -import static org.hamcrest.CoreMatchers.containsString; -import static org.junit.Assert.assertThrows; - -import com.android.tools.r8.CompilationFailedException; -import com.android.tools.r8.TestBase; -import com.android.tools.r8.TestDiagnosticMessages; -import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestRuntime.CfRuntime; -import com.android.tools.r8.utils.AndroidApiLevel; -import java.util.List; -import org.junit.Assume; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -/** Remove this test when Records are supported by default. */ -@RunWith(Parameterized.class) -public class InvalidRecordAttributeTest extends TestBase { - - private final TestParameters parameters; - private final Backend backend; - - private static final String EMPTY_RECORD = "EmptyRecord"; - private static final byte[][] EMPTY_RECORD_PROGRAM_DATA = - RecordTestUtils.getProgramData(EMPTY_RECORD); - private static final String SIMPLE_RECORD = "SimpleRecord"; - private static final byte[][] SIMPLE_RECORD_PROGRAM_DATA = - RecordTestUtils.getProgramData(SIMPLE_RECORD); - - @Parameters(name = "{0} back: {1}") - public static List<Object[]> data() { - // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk16). - return buildParameters( - getTestParameters().withCustomRuntime(CfRuntime.getCheckedInJdk16()).build(), - Backend.values()); - } - - public InvalidRecordAttributeTest(TestParameters parameters, Backend backend) { - this.parameters = parameters; - this.backend = backend; - } - - @Test - public void testD8EmptyRecord() throws Exception { - Assume.assumeTrue(backend.isDex()); - assertThrows( - CompilationFailedException.class, - () -> { - testForD8(backend) - .addProgramClassFileData(EMPTY_RECORD_PROGRAM_DATA) - .setMinApi(AndroidApiLevel.B) - .compileWithExpectedDiagnostics( - InvalidRecordAttributeTest::assertUnsupportedRecordError); - }); - } - - @Test - public void testD8SimpleRecord() throws Exception { - Assume.assumeTrue(backend.isDex()); - assertThrows( - CompilationFailedException.class, - () -> { - testForD8(backend) - .addProgramClassFileData(RecordTestUtils.getProgramData(SIMPLE_RECORD)) - .setMinApi(AndroidApiLevel.B) - .compileWithExpectedDiagnostics( - InvalidRecordAttributeTest::assertUnsupportedRecordError); - }); - } - - @Test - public void testR8EmptyRecord() throws Exception { - assertThrows( - CompilationFailedException.class, - () -> { - testForR8(backend) - .addProgramClassFileData(EMPTY_RECORD_PROGRAM_DATA) - .setMinApi(AndroidApiLevel.B) - .addKeepMainRule(RecordTestUtils.getMainType(EMPTY_RECORD)) - .compileWithExpectedDiagnostics( - InvalidRecordAttributeTest::assertUnsupportedRecordError); - }); - } - - @Test - public void testR8SimpleRecord() throws Exception { - assertThrows( - CompilationFailedException.class, - () -> { - testForR8(backend) - .addProgramClassFileData(SIMPLE_RECORD_PROGRAM_DATA) - .setMinApi(AndroidApiLevel.B) - .addKeepMainRule(RecordTestUtils.getMainType(SIMPLE_RECORD)) - .compileWithExpectedDiagnostics( - InvalidRecordAttributeTest::assertUnsupportedRecordError); - }); - } - - private static void assertUnsupportedRecordError(TestDiagnosticMessages diagnostics) { - diagnostics.assertErrorThatMatches( - diagnosticMessage(containsString("Records are not supported"))); - } -}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordInstanceOfTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordInstanceOfTest.java index 08b0e85..d6639de 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordInstanceOfTest.java +++ b/src/test/java/com/android/tools/r8/desugar/records/RecordInstanceOfTest.java
@@ -55,7 +55,7 @@ .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) - .addOptionsModification(opt -> opt.testing.enableExperimentalRecordDesugaring = true) + .compile() .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(EXPECTED_RESULT); @@ -72,7 +72,6 @@ .addKeepMainRule(MAIN_TYPE) .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) - .addOptionsModification(opt -> opt.testing.enableExperimentalRecordDesugaring = true) .compile() .writeToZip(); RecordTestUtils.assertRecordsAreRecords(output); @@ -89,7 +88,6 @@ .addKeepRules(RECORD_KEEP_RULE) .addKeepMainRule(MAIN_TYPE) .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) - .addOptionsModification(opt -> opt.testing.enableExperimentalRecordDesugaring = true) .compile() .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(EXPECTED_RESULT);
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomSplitDesugaringTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomSplitDesugaringTest.java new file mode 100644 index 0000000..0383285 --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomSplitDesugaringTest.java
@@ -0,0 +1,89 @@ +// 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.records; + +import static com.android.tools.r8.desugar.records.RecordTestUtils.RECORD_KEEP_RULE; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.utils.InternalOptions.TestingOptions; +import com.android.tools.r8.utils.StringUtils; +import java.nio.file.Path; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class RecordInvokeCustomSplitDesugaringTest extends TestBase { + + private static final String RECORD_NAME = "RecordInvokeCustom"; + private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME); + private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME); + private static final String EXPECTED_RESULT = + StringUtils.lines( + "Empty[]", + "true", + "true", + "true", + "true", + "true", + "false", + "true", + "true", + "false", + "false", + "Person[name=Jane Doe, age=42]"); + + private final TestParameters parameters; + + public RecordInvokeCustomSplitDesugaringTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Parameterized.Parameters(name = "{0}") + public static List<Object[]> data() { + return buildParameters( + getTestParameters().withDexRuntimes().withAllApiLevelsAlsoForCf().build()); + } + + @Test + public void testD8() throws Exception { + Path desugared = + testForD8(Backend.CF) + .addProgramClassFileData(PROGRAM_DATA) + .setMinApi(parameters.getApiLevel()) + .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) + .compile() + .writeToZip(); + testForD8(parameters.getBackend()) + .addProgramFiles(desugared) + .setMinApi(parameters.getApiLevel()) + .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) + .compile() + .run(parameters.getRuntime(), MAIN_TYPE) + .assertSuccessWithOutput(EXPECTED_RESULT); + } + + @Test + public void testR8() throws Exception { + Path desugared = + testForD8(Backend.CF) + .addProgramClassFileData(PROGRAM_DATA) + .setMinApi(parameters.getApiLevel()) + .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) + .compile() + .writeToZip(); + testForR8(parameters.getBackend()) + .addProgramFiles(desugared) + .setMinApi(parameters.getApiLevel()) + .addKeepRules(RECORD_KEEP_RULE) + .addKeepMainRule(MAIN_TYPE) + .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) + .compile() + .run(parameters.getRuntime(), MAIN_TYPE) + .assertSuccessWithOutput(EXPECTED_RESULT); + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java index 9bf3327..dac4ff8 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java +++ b/src/test/java/com/android/tools/r8/desugar/records/RecordInvokeCustomTest.java
@@ -68,7 +68,7 @@ .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) - .addOptionsModification(opt -> opt.testing.enableExperimentalRecordDesugaring = true) + .compile() .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(EXPECTED_RESULT); @@ -85,7 +85,6 @@ .addKeepMainRule(MAIN_TYPE) .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) - .addOptionsModification(opt -> opt.testing.enableExperimentalRecordDesugaring = true) .compile() .writeToZip(); RecordTestUtils.assertRecordsAreRecords(output); @@ -102,7 +101,6 @@ .addKeepRules(RECORD_KEEP_RULE) .addKeepMainRule(MAIN_TYPE) .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) - .addOptionsModification(opt -> opt.testing.enableExperimentalRecordDesugaring = true) .compile() .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(EXPECTED_RESULT);
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java index 240e128..ef11ef4 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java +++ b/src/test/java/com/android/tools/r8/desugar/records/RecordMergeTest.java
@@ -56,7 +56,6 @@ .addProgramClassFileData(PROGRAM_DATA_1) .setMinApi(parameters.getApiLevel()) .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) - .addOptionsModification(opt -> opt.testing.enableExperimentalRecordDesugaring = true) .compile() .writeToZip(); Path output2 = @@ -64,7 +63,6 @@ .addProgramClassFileData(PROGRAM_DATA_2) .setMinApi(parameters.getApiLevel()) .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) - .addOptionsModification(opt -> opt.testing.enableExperimentalRecordDesugaring = true) .compile() .writeToZip(); D8TestCompileResult result = @@ -84,7 +82,6 @@ .addProgramClassFileData(PROGRAM_DATA_1) .setMinApi(parameters.getApiLevel()) .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) - .addOptionsModification(opt -> opt.testing.enableExperimentalRecordDesugaring = true) .compile() .writeToZip(); D8TestCompileResult result = @@ -93,7 +90,6 @@ .addProgramClassFileData(PROGRAM_DATA_2) .setMinApi(parameters.getApiLevel()) .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) - .addOptionsModification(opt -> opt.testing.enableExperimentalRecordDesugaring = true) .compile(); result.run(parameters.getRuntime(), MAIN_TYPE_1).assertSuccessWithOutput(EXPECTED_RESULT_1); result.run(parameters.getRuntime(), MAIN_TYPE_2).assertSuccessWithOutput(EXPECTED_RESULT_2);
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java index 9663f43..e67ccd5 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java +++ b/src/test/java/com/android/tools/r8/desugar/records/RecordReflectionTest.java
@@ -67,7 +67,6 @@ .addKeepMainRule(MAIN_TYPE) .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) - .addOptionsModification(opt -> opt.testing.enableExperimentalRecordDesugaring = true) .compile() .writeToZip(); RecordTestUtils.assertRecordsAreRecords(output);
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java b/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java index 86e30a6..bf74698 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java +++ b/src/test/java/com/android/tools/r8/desugar/records/RecordTestUtils.java
@@ -99,8 +99,7 @@ } public static void assertRecordsAreRecords(Path output) throws IOException { - CodeInspector inspector = - new CodeInspector(output, opt -> opt.testing.enableExperimentalRecordDesugaring = true); + CodeInspector inspector = new CodeInspector(output); for (FoundClassSubject clazz : inspector.allClasses()) { if (clazz.getDexProgramClass().superType.toString().equals("java.lang.Record")) { assertTrue(clazz.getDexProgramClass().isRecord());
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java index 36b2377..cff0cab 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java +++ b/src/test/java/com/android/tools/r8/desugar/records/RecordWithMembersTest.java
@@ -57,7 +57,6 @@ .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) - .addOptionsModification(opt -> opt.testing.enableExperimentalRecordDesugaring = true) .compile() .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(EXPECTED_RESULT); @@ -74,7 +73,6 @@ .addKeepMainRule(MAIN_TYPE) .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) - .addOptionsModification(opt -> opt.testing.enableExperimentalRecordDesugaring = true) .compile() .writeToZip(); RecordTestUtils.assertRecordsAreRecords(output); @@ -91,7 +89,6 @@ .addKeepRules(RECORD_KEEP_RULE) .addKeepMainRule(MAIN_TYPE) .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) - .addOptionsModification(opt -> opt.testing.enableExperimentalRecordDesugaring = true) .compile() .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(EXPECTED_RESULT);
diff --git a/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java b/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java index 08c9468..40f8af0 100644 --- a/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java +++ b/src/test/java/com/android/tools/r8/desugar/records/SimpleRecordTest.java
@@ -56,7 +56,6 @@ .addProgramClassFileData(PROGRAM_DATA) .setMinApi(parameters.getApiLevel()) .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) - .addOptionsModification(opt -> opt.testing.enableExperimentalRecordDesugaring = true) .compile() .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(EXPECTED_RESULT); @@ -73,7 +72,6 @@ .addKeepMainRule(MAIN_TYPE) .addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)) .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) - .addOptionsModification(opt -> opt.testing.enableExperimentalRecordDesugaring = true) .compile() .writeToZip(); RecordTestUtils.assertRecordsAreRecords(output); @@ -90,7 +88,6 @@ .addKeepRules(RECORD_KEEP_RULE) .addKeepMainRule(MAIN_TYPE) .addOptionsModification(TestingOptions::allowExperimentClassFileVersion) - .addOptionsModification(opt -> opt.testing.enableExperimentalRecordDesugaring = true) .compile() .run(parameters.getRuntime(), MAIN_TYPE) .assertSuccessWithOutput(EXPECTED_RESULT);
diff --git a/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/InvokeStaticDesugarTest.java b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/InvokeStaticDesugarTest.java index 35e52b4..8cd35aa 100644 --- a/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/InvokeStaticDesugarTest.java +++ b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/InvokeStaticDesugarTest.java
@@ -7,6 +7,7 @@ import static com.android.tools.r8.desugar.staticinterfacemethod.InvokeStaticDesugarTest.Library.foo; import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.Assume.assumeFalse; @@ -151,12 +152,14 @@ private Set<FoundMethodSubject> getSyntheticMethods(CodeInspector inspector) { Set<FoundMethodSubject> methods = new HashSet<>(); - assert inspector.allClasses().stream() - .allMatch( + inspector + .allClasses() + .forEach( c -> - !SyntheticItemsTestUtils.isExternalSynthetic(c.getFinalReference()) - || SyntheticItemsTestUtils.isExternalStaticInterfaceCall( - c.getFinalReference())); + assertTrue( + !SyntheticItemsTestUtils.isExternalSynthetic(c.getFinalReference()) + || SyntheticItemsTestUtils.isExternalStaticInterfaceCall( + c.getFinalReference()))); inspector.allClasses().stream() .filter(c -> SyntheticItemsTestUtils.isExternalStaticInterfaceCall(c.getFinalReference())) .forEach(c -> methods.addAll(c.allMethods(m -> !m.isInstanceInitializer())));
diff --git a/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/MutuallyRecursiveMethodsTest.java b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/MutuallyRecursiveMethodsTest.java index ee911d3..dfbbd6e 100644 --- a/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/MutuallyRecursiveMethodsTest.java +++ b/src/test/java/com/android/tools/r8/desugar/staticinterfacemethod/MutuallyRecursiveMethodsTest.java
@@ -35,6 +35,16 @@ .assertSuccessWithOutput(EXPECTED); } + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(MutuallyRecursiveMethodsTest.class) + .setMinApi(parameters.getApiLevel()) + .addKeepMainRule(TestClass.class) + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED); + } + interface I { static boolean isEven(int i) { return i == 0 || isOdd(i - 1);
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithPublicStaticResolutionInvokeVirtualTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithPublicStaticResolutionInvokeVirtualTest.java index 12869e2..c960b33 100644 --- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithPublicStaticResolutionInvokeVirtualTest.java +++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithPublicStaticResolutionInvokeVirtualTest.java
@@ -102,22 +102,21 @@ private void checkResult(TestRunResult<?> result, boolean isR8) { // Invalid invoke case is where the invoke-virtual targets C.m. if (invalidInvoke) { - if (parameters.isCfRuntime()) { - result.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class); - return; - } - if (parameters.getDexRuntimeVersion().isInRangeInclusive(Version.V5_1_1, Version.V7_0_0)) { + if (parameters.isDexRuntimeVersion(Version.V7_0_0) + && parameters.canUseDefaultAndStaticInterfaceMethodsWhenDesugaring()) { + // The v7 VM incorrectly fails to throw. result.assertSuccessWithOutput(EXPECTED); - return; + } else { + result.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class); } - result.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class); return; } if (isR8 && parameters.isDexRuntime() && parameters.getDexRuntimeVersion().isNewerThan(Version.V6_0_1) - && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0)) { + && parameters.getDexRuntimeVersion().isOlderThan(Version.V12_0_0) + && parameters.canUseDefaultAndStaticInterfaceMethodsWhenDesugaring()) { // TODO(b/182255398): This should be EXPECTED. result.assertSuccessWithOutput(EXPECTED_R8); return;
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithPublicStaticResolutionOnClassTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithPublicStaticResolutionOnClassTest.java index 1684c5a..b5949f7 100644 --- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithPublicStaticResolutionOnClassTest.java +++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/DefaultInterfaceMethodDesugaringWithPublicStaticResolutionOnClassTest.java
@@ -70,10 +70,9 @@ .setMinApi(parameters.getApiLevel()) .compile() .run(parameters.getRuntime(), TestClass.class) - // TODO(b/182335909): Ideally, this should also throw ICCE when desugaring. .applyIf( - !parameters.canUseDefaultAndStaticInterfaceMethodsWhenDesugaring() - || parameters.isDexRuntimeVersion(Version.V7_0_0), + parameters.canUseDefaultAndStaticInterfaceMethodsWhenDesugaring() + && parameters.isDexRuntimeVersion(Version.V7_0_0), r -> r.assertSuccessWithOutput(EXPECTED_INVALID), r -> r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class)); }
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultInterfaceMethodToNonImmediateInterfaceTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultInterfaceMethodToNonImmediateInterfaceTest.java index 698cf50..f4fec42 100644 --- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultInterfaceMethodToNonImmediateInterfaceTest.java +++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InvokeSuperInDefaultInterfaceMethodToNonImmediateInterfaceTest.java
@@ -16,6 +16,7 @@ import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.StringUtils; import com.google.common.collect.ImmutableList; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -36,6 +37,11 @@ } @Test + @Ignore("b/197494749 and b/120130831") + // TODO(b/197494749): Update this test now that desugaring is moved up. In particular this must + // be rewritten with CF based transformers as R8 does not support interface desugaring on DEX. + // TODO(b/120130831): With the move of desugaring this issue should be resolved. The bug indicates + // that a workaround for issues related to the IR implementation can now be reverted. public void test() throws Exception { // Note that the expected output is independent of the presence of J.m(). String expectedOutput = StringUtils.lines("I.m()", "JImpl.m()");
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/InterfaceEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/InterfaceEnumUnboxingTest.java index 2f27855..3bc5c02 100644 --- a/src/test/java/com/android/tools/r8/enumunboxing/InterfaceEnumUnboxingTest.java +++ b/src/test/java/com/android/tools/r8/enumunboxing/InterfaceEnumUnboxingTest.java
@@ -59,9 +59,12 @@ SuccessUnusedDefaultMethod.EnumInterface.class, SuccessUnusedDefaultMethodOverride.EnumInterface.class, SuccessUnusedDefaultMethodOverrideEnum.EnumInterface.class) - .assertNotUnboxed( - FailureDefaultMethodUsed.EnumInterface.class, - FailureUsedAsInterface.EnumInterface.class)) + .assertNotUnboxed(FailureUsedAsInterface.EnumInterface.class) + // When desugaring interfaces the dispatch will inline the forwarding method + // to the CC method allowing unboxing. + .assertUnboxedIf( + !parameters.canUseDefaultAndStaticInterfaceMethods(), + FailureDefaultMethodUsed.EnumInterface.class)) .noMinification() .enableNoVerticalClassMergingAnnotations() .enableInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnOtherInterfaceTest.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnOtherInterfaceTest.java index 509bfa2..99ab7b3 100644 --- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnOtherInterfaceTest.java +++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialOnOtherInterfaceTest.java
@@ -38,9 +38,9 @@ .addProgramClasses(I.class) .addProgramClassFileData(getClassWithTransformedInvoked()) .run(parameters.getRuntime(), Main.class) + .assertFailureWithErrorThatThrowsIf(parameters.isCfRuntime(), VerifyError.class) // TODO(b/144410139): Consider making this a compilation failure when generating DEX. - .assertSuccessWithOutputLinesIf(parameters.isDexRuntime(), "Hello World!") - .assertFailureWithErrorThatThrowsIf(parameters.isCfRuntime(), VerifyError.class); + .assertSuccessWithOutputLinesIf(parameters.isDexRuntime(), "Hello World!"); } @Test @@ -52,11 +52,14 @@ .addKeepMainRule(Main.class) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class) - .assertFailureWithErrorThatThrows( - parameters.isCfRuntime() - ? VerifyError.class - // TODO(b/144410139): Consider making this a compilation failure. - : NullPointerException.class); + .assertFailureWithErrorThatThrowsIf(parameters.isCfRuntime(), VerifyError.class) + // TODO(b/144410139): Consider making this a compilation failure when generating DEX. + .assertSuccessWithOutputLinesIf( + parameters.isDexRuntime() && !parameters.canUseDefaultAndStaticInterfaceMethods(), + "Hello World!") + .assertFailureWithErrorThatThrowsIf( + parameters.isDexRuntime() && parameters.canUseDefaultAndStaticInterfaceMethods(), + NullPointerException.class); } private byte[] getClassWithTransformedInvoked() throws IOException {
diff --git a/src/test/java/com/android/tools/r8/graph/invokevirtual/InvokeVirtualPrivateBaseWithDefaultDirectInvokeTest.java b/src/test/java/com/android/tools/r8/graph/invokevirtual/InvokeVirtualPrivateBaseWithDefaultDirectInvokeTest.java index f989728..a8a222d 100644 --- a/src/test/java/com/android/tools/r8/graph/invokevirtual/InvokeVirtualPrivateBaseWithDefaultDirectInvokeTest.java +++ b/src/test/java/com/android/tools/r8/graph/invokevirtual/InvokeVirtualPrivateBaseWithDefaultDirectInvokeTest.java
@@ -12,7 +12,6 @@ import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.TestRuntime.CfVm; -import com.android.tools.r8.utils.AndroidApiLevel; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -38,10 +37,7 @@ testForJvm() .addInnerClasses(getClass()) .run(parameters.getRuntime(), Main.class) - .applyIf( - parameters.isCfRuntime(CfVm.JDK11), - r -> r.assertSuccessWithOutputLines("I::foo"), - r -> r.assertFailureWithErrorThatThrows(IllegalAccessError.class)); + .apply(r -> assertResultIsCorrect(r, true)); } @Test @@ -50,7 +46,7 @@ .addInnerClasses(getClass()) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class) - .apply(this::assertResultIsCorrect); + .apply(r -> assertResultIsCorrect(r, false)); } @Test @@ -63,17 +59,23 @@ .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class) // TODO(b/182189123): This should have the same behavior as D8. - .assertSuccessWithOutputLines("I::foo"); + .applyIf( + parameters.isCfRuntime(), + r -> r.assertSuccessWithOutputLines("I::foo"), + r -> assertResultIsCorrect(r, true)); } - public void assertResultIsCorrect(SingleTestRunResult<?> result) { - if (parameters.isCfRuntime(CfVm.JDK11) - && parameters.getApiLevel().isGreaterThan(AndroidApiLevel.M)) { + public void assertResultIsCorrect(SingleTestRunResult<?> result, boolean nonDesugaredCf) { + boolean isNotDesugared = + (nonDesugaredCf && parameters.isCfRuntime()) + || parameters.canUseDefaultAndStaticInterfaceMethodsWhenDesugaring(); + // JDK 11 allows this incorrect dispatch for some reason. + if (parameters.isCfRuntime(CfVm.JDK11) && isNotDesugared) { result.assertSuccessWithOutputLines("I::foo"); return; } - // TODO(b/152199517): Should be illegal access for DEX. - if (parameters.isDexRuntime() && parameters.getApiLevel().isGreaterThan(AndroidApiLevel.M)) { + // TODO(b/152199517): Should become an illegal access on future DEX VM. + if (parameters.isDexRuntime() && isNotDesugared) { result.assertSuccessWithOutputLines("I::foo"); return; }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java index fd13bbf..b60f6c9 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
@@ -125,7 +125,7 @@ // Tests depend on nullability of receiver and argument in general. Learning very // accurate // nullability from actual usage in tests bothers what we want to test. - o.callSiteOptimizationOptions().disableTypePropagationForTesting(); + o.callSiteOptimizationOptions().disableDynamicTypePropagationForTesting(); o.testing.horizontallyMergedClassesConsumer = this::fixInliningNullabilityClass; o.testing.horizontalClassMergingTarget = (appView, candidates, target) -> {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/HashCodeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/HashCodeTest.java index a36a63c..509465b 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/HashCodeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/HashCodeTest.java
@@ -6,24 +6,30 @@ import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; +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.Parameters; @RunWith(Parameterized.class) public class HashCodeTest extends TestBase { + private static final Class<?> MAIN = TestClass.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public HashCodeTest(TestParameters parameters) { + public HashCodeTest(boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -36,6 +42,8 @@ .addOptionsModification( o -> { o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); }) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithRefinedReceiverTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithRefinedReceiverTest.java index b53f23c..2bf775f 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithRefinedReceiverTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeInterfaceWithRefinedReceiverTest.java
@@ -14,29 +14,36 @@ import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import 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 InvokeInterfaceWithRefinedReceiverTest extends TestBase { + private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeInterfaceWithRefinedReceiverTest(TestParameters parameters) { + public InvokeInterfaceWithRefinedReceiverTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -55,6 +62,8 @@ // target. o.enableDevirtualization = false; o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); }) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java index 3ac7e29..2556bf4 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/InvokeVirtualWithRefinedReceiverTest.java
@@ -14,29 +14,35 @@ import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import 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 InvokeVirtualWithRefinedReceiverTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeVirtualWithRefinedReceiverTest(TestParameters parameters) { + public InvokeVirtualWithRefinedReceiverTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -52,6 +58,8 @@ .addOptionsModification( o -> { o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); }) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/PropagationFromSiblingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/PropagationFromSiblingTest.java index 429b5a2..0c04377 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/PropagationFromSiblingTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/PropagationFromSiblingTest.java
@@ -9,7 +9,8 @@ import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; +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; @@ -18,14 +19,18 @@ @RunWith(Parameterized.class) public class PropagationFromSiblingTest extends TestBase { + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - @Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } - public PropagationFromSiblingTest(TestParameters parameters) { + public PropagationFromSiblingTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -34,7 +39,13 @@ testForR8(parameters.getBackend()) .addInnerClasses(PropagationFromSiblingTest.class) .addKeepMainRule(TestClass.class) - .addOptionsModification(options -> options.enableUnusedInterfaceRemoval = false) + .addOptionsModification( + options -> { + options.enableUnusedInterfaceRemoval = false; + options + .callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); + }) .enableInliningAnnotations() .enableNoVerticalClassMergingAnnotations() .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/WithStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/WithStaticizerTest.java index 04cbc5f..3c7307c 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/WithStaticizerTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/WithStaticizerTest.java
@@ -13,28 +13,35 @@ import com.android.tools.r8.NoHorizontalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import 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 WithStaticizerTest extends TestBase { + private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { // TODO(b/112831361): support for class staticizer in CF backend. - return getTestParameters().withDexRuntimes().withAllApiLevels().build(); + return buildParameters( + BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public WithStaticizerTest(TestParameters parameters) { + public WithStaticizerTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -43,6 +50,12 @@ testForR8(parameters.getBackend()) .addInnerClasses(WithStaticizerTest.class) .addKeepMainRule(MAIN) + .addOptionsModification( + options -> + options + .callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation( + enableExperimentalArgumentPropagation)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoHorizontalClassMergingAnnotations() @@ -62,8 +75,12 @@ assertThat(host, isPresent()); MethodSubject foo = host.uniqueMethodWithName("foo"); assertThat(foo, isPresent()); - // TODO(b/139246447): Can optimize branches since `arg` is definitely not null. - assertTrue(foo.streamInstructions().anyMatch(InstructionSubject::isIf)); + if (enableExperimentalArgumentPropagation) { + assertTrue(foo.streamInstructions().noneMatch(InstructionSubject::isIf)); + } else { + // TODO(b/139246447): Can optimize branches since `arg` is definitely not null. + assertTrue(foo.streamInstructions().anyMatch(InstructionSubject::isIf)); + } } @NeverClassInline
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectNegativeTest.java index 2e76fa2..3716b6d 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectNegativeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectNegativeTest.java
@@ -11,30 +11,36 @@ import com.android.tools.r8.NeverInline; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import 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 InvokeDirectNegativeTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeDirectNegativeTest(TestParameters parameters) { + public InvokeDirectNegativeTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -48,6 +54,8 @@ .addOptionsModification( o -> { o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); }) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectPositiveTest.java index e3fa7d7..4b220b6 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectPositiveTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeDirectPositiveTest.java
@@ -11,32 +11,38 @@ import com.android.tools.r8.NeverInline; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.value.AbstractValue; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +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 InvokeDirectPositiveTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeDirectPositiveTest(TestParameters parameters) { + public InvokeDirectPositiveTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -50,6 +56,8 @@ .addOptionsModification( o -> { o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); }) .setMinApi(parameters.getApiLevel()) .addOptionsModification(InternalOptions::enableConstantArgumentPropagationForTesting)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfaceNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfaceNegativeTest.java index 7c2eeee..896d548 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfaceNegativeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfaceNegativeTest.java
@@ -12,30 +12,36 @@ import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import 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 InvokeInterfaceNegativeTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeInterfaceNegativeTest(TestParameters parameters) { + public InvokeInterfaceNegativeTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -53,6 +59,8 @@ // target. o.enableDevirtualization = false; o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); }) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java index 93594ff..553ee4b 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeInterfacePositiveTest.java
@@ -12,32 +12,38 @@ import com.android.tools.r8.NoHorizontalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.value.AbstractValue; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +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 InvokeInterfacePositiveTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeInterfacePositiveTest(TestParameters parameters) { + public InvokeInterfacePositiveTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -56,6 +62,8 @@ // target. o.enableDevirtualization = false; o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); }) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticNegativeTest.java index df1fb98..8457c5c 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticNegativeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticNegativeTest.java
@@ -10,30 +10,36 @@ import com.android.tools.r8.NeverInline; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import 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 InvokeStaticNegativeTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeStaticNegativeTest(TestParameters parameters) { + public InvokeStaticNegativeTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -43,9 +49,12 @@ .addInnerClasses(InvokeStaticNegativeTest.class) .addKeepMainRule(MAIN) .enableInliningAnnotations() - .addOptionsModification(o -> { - o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; - }) + .addOptionsModification( + o -> { + o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); + }) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN) .assertSuccessWithOutputLines("null", "non-null")
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticPositiveTest.java index 096362a..fe38d71 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticPositiveTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeStaticPositiveTest.java
@@ -10,32 +10,38 @@ import com.android.tools.r8.NeverInline; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.value.AbstractValue; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +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 InvokeStaticPositiveTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeStaticPositiveTest(TestParameters parameters) { + public InvokeStaticPositiveTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -48,6 +54,8 @@ .addOptionsModification( o -> { o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); }) .setMinApi(parameters.getApiLevel()) .addOptionsModification(InternalOptions::enableConstantArgumentPropagationForTesting)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualNegativeTest.java index 7be71b2..7464d26 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualNegativeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualNegativeTest.java
@@ -12,30 +12,36 @@ import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import 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 InvokeVirtualNegativeTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeVirtualNegativeTest(TestParameters parameters) { + public InvokeVirtualNegativeTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -50,6 +56,8 @@ .addOptionsModification( o -> { o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); }) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java index e254dc6..9853a4c 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/constants/InvokeVirtualPositiveTest.java
@@ -12,31 +12,37 @@ import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.value.AbstractValue; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +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 InvokeVirtualPositiveTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeVirtualPositiveTest(TestParameters parameters) { + public InvokeVirtualPositiveTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -51,6 +57,8 @@ .addOptionsModification( o -> { o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); }) .setMinApi(parameters.getApiLevel()) .addOptionsModification(InternalOptions::enableConstantArgumentPropagationForTesting)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectNegativeTest.java index 0cc9bb1..4a6d03d 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectNegativeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectNegativeTest.java
@@ -11,30 +11,36 @@ import com.android.tools.r8.NeverInline; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import 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 InvokeDirectNegativeTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeDirectNegativeTest(TestParameters parameters) { + public InvokeDirectNegativeTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -48,6 +54,8 @@ .addOptionsModification( o -> { o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); }) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java index 51175f8..d23ed49 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
@@ -12,30 +12,36 @@ import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import 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 InvokeDirectPositiveTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeDirectPositiveTest(TestParameters parameters) { + public InvokeDirectPositiveTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -50,6 +56,8 @@ .addOptionsModification( o -> { o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); }) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfaceNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfaceNegativeTest.java index 31e6779..20b0d1f 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfaceNegativeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfaceNegativeTest.java
@@ -12,30 +12,36 @@ import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import 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 InvokeInterfaceNegativeTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeInterfaceNegativeTest(TestParameters parameters) { + public InvokeInterfaceNegativeTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -53,6 +59,8 @@ // target. o.enableDevirtualization = false; o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); }) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java index 5a06d91..b39540b 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeInterfacePositiveTest.java
@@ -13,30 +13,36 @@ import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import 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 InvokeInterfacePositiveTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeInterfacePositiveTest(TestParameters parameters) { + public InvokeInterfacePositiveTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -55,6 +61,8 @@ // target. o.enableDevirtualization = false; o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); }) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticNegativeTest.java index 3c94778..6ae9004 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticNegativeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticNegativeTest.java
@@ -10,30 +10,36 @@ import com.android.tools.r8.NeverInline; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import 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 InvokeStaticNegativeTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeStaticNegativeTest(TestParameters parameters) { + public InvokeStaticNegativeTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -43,9 +49,12 @@ .addInnerClasses(InvokeStaticNegativeTest.class) .addKeepMainRule(MAIN) .enableInliningAnnotations() - .addOptionsModification(o -> { - o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; - }) + .addOptionsModification( + o -> { + o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); + }) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN) .assertSuccessWithOutputLines("Sub1", "Sub2")
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java index ff19f58..410d528 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java
@@ -11,30 +11,36 @@ import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import 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 InvokeStaticPositiveTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeStaticPositiveTest(TestParameters parameters) { + public InvokeStaticPositiveTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -48,6 +54,8 @@ .addOptionsModification( o -> { o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); }) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java index 39bfa4c..1db7baa 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualNegativeTest.java
@@ -12,30 +12,36 @@ import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import 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 InvokeVirtualNegativeTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeVirtualNegativeTest(TestParameters parameters) { + public InvokeVirtualNegativeTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -50,6 +56,8 @@ .addOptionsModification( o -> { o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); }) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java index 2595df5..adce8cc 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
@@ -12,30 +12,36 @@ import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import 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 InvokeVirtualPositiveTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeVirtualPositiveTest(TestParameters parameters) { + public InvokeVirtualPositiveTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -50,6 +56,8 @@ .addOptionsModification( o -> { o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); }) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectNegativeTest.java index 96614da..05d1329 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectNegativeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectNegativeTest.java
@@ -11,28 +11,34 @@ import com.android.tools.r8.NeverInline; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import 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 InvokeDirectNegativeTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeDirectNegativeTest(TestParameters parameters) { + public InvokeDirectNegativeTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -46,6 +52,8 @@ .addOptionsModification( o -> { o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); }) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java index 0b7ff7d..3a6fd02 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java
@@ -11,29 +11,35 @@ import com.android.tools.r8.NeverInline; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import 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 InvokeDirectPositiveTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeDirectPositiveTest(TestParameters parameters) { + public InvokeDirectPositiveTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -47,6 +53,8 @@ .addOptionsModification( o -> { o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); }) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceNegativeTest.java index bc9f10c..421ebf0 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceNegativeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfaceNegativeTest.java
@@ -12,30 +12,36 @@ import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import 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 InvokeInterfaceNegativeTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeInterfaceNegativeTest(TestParameters parameters) { + public InvokeInterfaceNegativeTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -53,6 +59,8 @@ // target. o.enableDevirtualization = false; o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); }) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java index 19fa2a1..6d0aab9 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeInterfacePositiveTest.java
@@ -12,29 +12,35 @@ import com.android.tools.r8.NoHorizontalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import 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 InvokeInterfacePositiveTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeInterfacePositiveTest(TestParameters parameters) { + public InvokeInterfacePositiveTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -52,6 +58,8 @@ // target. o.enableDevirtualization = false; o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); }) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticNegativeTest.java index 0a18b69..a7fff40 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticNegativeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticNegativeTest.java
@@ -10,28 +10,34 @@ import com.android.tools.r8.NeverInline; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import 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 InvokeStaticNegativeTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeStaticNegativeTest(TestParameters parameters) { + public InvokeStaticNegativeTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -41,9 +47,12 @@ .addInnerClasses(InvokeStaticNegativeTest.class) .addKeepMainRule(MAIN) .enableInliningAnnotations() - .addOptionsModification(o -> { - o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; - }) + .addOptionsModification( + o -> { + o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); + }) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN) .assertSuccessWithOutputLines("null", "non-null")
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java index 700ec34..0b27105 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java
@@ -10,29 +10,35 @@ import com.android.tools.r8.NeverInline; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import 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 InvokeStaticPositiveTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeStaticPositiveTest(TestParameters parameters) { + public InvokeStaticPositiveTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -42,9 +48,12 @@ .addInnerClasses(InvokeStaticPositiveTest.class) .addKeepMainRule(MAIN) .enableInliningAnnotations() - .addOptionsModification(o -> { - o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; - }) + .addOptionsModification( + o -> { + o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); + }) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN) .assertSuccessWithOutputLines("non-null")
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualCascadeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualCascadeTest.java index f46869d..627171a 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualCascadeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualCascadeTest.java
@@ -12,27 +12,33 @@ import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import 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 InvokeVirtualCascadeTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeVirtualCascadeTest(TestParameters parameters) { + public InvokeVirtualCascadeTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -41,6 +47,12 @@ testForR8(parameters.getBackend()) .addInnerClasses(InvokeVirtualCascadeTest.class) .addKeepMainRule(MAIN) + .addOptionsModification( + options -> + options + .callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation( + enableExperimentalArgumentPropagation)) .enableNoVerticalClassMergingAnnotations() .enableNeverClassInliningAnnotations() .enableInliningAnnotations() @@ -56,16 +68,24 @@ MethodSubject a_m = a.uniqueMethodWithName("m"); assertThat(a_m, isPresent()); - // TODO(b/139246447): Can optimize branches since `arg` is definitely not null. - assertTrue(a_m.streamInstructions().anyMatch(InstructionSubject::isIf)); + if (enableExperimentalArgumentPropagation) { + assertTrue(a_m.streamInstructions().noneMatch(InstructionSubject::isIf)); + } else { + // TODO(b/139246447): Can optimize branches since `arg` is definitely not null. + assertTrue(a_m.streamInstructions().anyMatch(InstructionSubject::isIf)); + } ClassSubject b = inspector.clazz(B.class); assertThat(b, isPresent()); MethodSubject b_m = b.uniqueMethodWithName("m"); assertThat(b_m, isPresent()); - // TODO(b/139246447): Can optimize branches since `arg` is definitely not null. - assertTrue(b_m.streamInstructions().anyMatch(InstructionSubject::isIf)); + if (enableExperimentalArgumentPropagation) { + assertTrue(b_m.streamInstructions().noneMatch(InstructionSubject::isIf)); + } else { + // TODO(b/139246447): Can optimize branches since `arg` is definitely not null. + assertTrue(b_m.streamInstructions().anyMatch(InstructionSubject::isIf)); + } } @NoVerticalClassMerging
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java index 544cf86..b9b1f2f 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualNegativeTest.java
@@ -12,30 +12,36 @@ import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import 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 InvokeVirtualNegativeTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeVirtualNegativeTest(TestParameters parameters) { + public InvokeVirtualNegativeTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -50,6 +56,8 @@ .addOptionsModification( o -> { o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); }) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java index 2cff135..8e1031a 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeVirtualPositiveTest.java
@@ -12,30 +12,36 @@ import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo; +import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import 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 InvokeVirtualPositiveTest extends TestBase { private static final Class<?> MAIN = Main.class; - @Parameterized.Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{1}, experimental: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build()); } + private final boolean enableExperimentalArgumentPropagation; private final TestParameters parameters; - public InvokeVirtualPositiveTest(TestParameters parameters) { + public InvokeVirtualPositiveTest( + boolean enableExperimentalArgumentPropagation, TestParameters parameters) { + this.enableExperimentalArgumentPropagation = enableExperimentalArgumentPropagation; this.parameters = parameters; } @@ -50,6 +56,8 @@ .addOptionsModification( o -> { o.testing.callSiteOptimizationInfoInspector = this::callSiteOptimizationInfoInspect; + o.callSiteOptimizationOptions() + .setEnableExperimentalArgumentPropagation(enableExperimentalArgumentPropagation); }) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningIntoVisibilityBridgeTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningIntoVisibilityBridgeTest.java index 46727f2..75679e5 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningIntoVisibilityBridgeTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InliningIntoVisibilityBridgeTest.java
@@ -9,16 +9,19 @@ import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import com.android.tools.r8.R8TestBuilder; import com.android.tools.r8.R8TestRunResult; import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; import com.android.tools.r8.ir.optimize.inliner.testclasses.InliningIntoVisibilityBridgeTestClasses; import com.android.tools.r8.ir.optimize.inliner.testclasses.InliningIntoVisibilityBridgeTestClasses.InliningIntoVisibilityBridgeTestClassB; import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -28,14 +31,17 @@ @RunWith(Parameterized.class) public class InliningIntoVisibilityBridgeTest extends TestBase { + private final TestParameters parameters; private final boolean neverInline; - @Parameters(name = "Never inline: {0}") - public static Boolean[] data() { - return BooleanUtils.values(); + @Parameters(name = "{0}, never inline: {1}") + public static List<Object[]> data() { + return buildParameters( + getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values()); } - public InliningIntoVisibilityBridgeTest(boolean neverInline) { + public InliningIntoVisibilityBridgeTest(TestParameters parameters, boolean neverInline) { + this.parameters = parameters; this.neverInline = neverInline; } @@ -44,7 +50,7 @@ String expectedOutput = StringUtils.lines("Hello world", "Hello world", "Hello world"); R8TestRunResult result = - testForR8(Backend.DEX) + testForR8(parameters.getBackend()) .addInnerClasses(InliningIntoVisibilityBridgeTest.class) .addInnerClasses(InliningIntoVisibilityBridgeTestClasses.class) .addKeepMainRule(TestClass.class) @@ -54,8 +60,9 @@ .applyIf(!neverInline, R8TestBuilder::enableForceInliningAnnotations) .enableNoVerticalClassMergingAnnotations() .enableProguardTestOptions() + .setMinApi(parameters.getApiLevel()) .compile() - .run(TestClass.class) + .run(parameters.getRuntime(), TestClass.class) .assertSuccessWithOutput(expectedOutput); // Verify that A.method() is only there if there is an explicit -neverinline rule. @@ -75,9 +82,11 @@ assertThat(classSubject, isPresent()); MethodSubject methodSubject = classSubject.uniqueMethodWithName("method"); - assertThat(methodSubject, isPresentAndRenamed()); - assertEquals(neverInline, methodSubject.isBridge()); - assertEquals(neverInline, methodSubject.isSynthetic()); + if (!neverInline) { + assertThat(methodSubject, isPresentAndRenamed()); + assertFalse(methodSubject.isBridge()); + assertFalse(methodSubject.isSynthetic()); + } } }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromDefaultInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromDefaultInterfaceMethodTest.java index 9b3dd42..baf0868 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromDefaultInterfaceMethodTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/OutlineFromDefaultInterfaceMethodTest.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.ir.optimize.outliner; import static com.android.tools.r8.references.Reference.methodFromMethod; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; @@ -16,11 +17,9 @@ import com.android.tools.r8.synthesis.SyntheticItemsTestUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; -import com.android.tools.r8.utils.codeinspector.FoundMethodSubject; import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; -import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -65,25 +64,22 @@ } private void inspect(CodeInspector inspector) { - ClassSubject interfaceSubject; - MethodSubject greetMethodSubject; - if (parameters.isCfRuntime() - || parameters - .getApiLevel() - .isGreaterThanOrEqualTo(apiLevelWithDefaultInterfaceMethodsSupport())) { - interfaceSubject = inspector.clazz(I.class); - greetMethodSubject = interfaceSubject.uniqueMethodWithName("greet"); + if (parameters.canUseDefaultAndStaticInterfaceMethods()) { + ClassSubject interfaceSubject = inspector.clazz(I.class); + MethodSubject greetMethodSubject = interfaceSubject.uniqueMethodWithName("greet"); + assertThat(interfaceSubject, isPresent()); + assertThat(greetMethodSubject, isPresent()); + assertEquals( + 1, + greetMethodSubject + .streamInstructions() + .filter(InstructionSubject::isInvokeStatic) + .count()); } else { - interfaceSubject = inspector.clazz(SyntheticItemsTestUtils.syntheticCompanionClass(I.class)); - List<FoundMethodSubject> companionMethods = interfaceSubject.allMethods(); - assertEquals(1, companionMethods.size()); - greetMethodSubject = companionMethods.get(0); + // The companion class method is inlined into main. + assertThat( + inspector.clazz(SyntheticItemsTestUtils.syntheticCompanionClass(I.class)), isAbsent()); } - assertThat(interfaceSubject, isPresent()); - assertThat(greetMethodSubject, isPresent()); - assertEquals( - 1, - greetMethodSubject.streamInstructions().filter(InstructionSubject::isInvokeStatic).count()); } static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java index d1d5f1f..000f5d4 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/reflection/GetClassTest.java
@@ -155,7 +155,7 @@ // In `getMainClass`, a call with `null`, which will throw NPE, is replaced with null throwing // code. Then, remaining call with non-null argument made getClass() replaceable. // Disable the propagation of call site information to separate the tests. - options.callSiteOptimizationOptions().disableTypePropagationForTesting(); + options.callSiteOptimizationOptions().disableDynamicTypePropagationForTesting(); } @Test
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java index 2e1709c..01ac9dc 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringValueOfTest.java
@@ -58,7 +58,7 @@ // Disable the propagation of call site information to test String#valueOf optimization with // nullable argument. Otherwise, e.g., we know that only `null` is used for `hideNPE`, and then // simplify everything in that method. - options.callSiteOptimizationOptions().disableTypePropagationForTesting(); + options.callSiteOptimizationOptions().disableDynamicTypePropagationForTesting(); options.testing.forceNameReflectionOptimization = true; }
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java index 6bc0862..c10d1db 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.ir.optimize.unusedinterfaces; +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; @@ -57,7 +58,9 @@ assertThat(iClassSubject, isPresent()); ClassSubject jClassSubject = inspector.clazz(J.class); - assertThat(jClassSubject, isPresent()); + assertThat( + jClassSubject, + parameters.canUseDefaultAndStaticInterfaceMethods() ? isPresent() : isAbsent()); ClassSubject aClassSubject = inspector.clazz(A.class); assertThat(aClassSubject, isPresent()); @@ -66,7 +69,9 @@ // m() that happens to be used. assertEquals(1, aClassSubject.getDexProgramClass().interfaces.size()); assertEquals( - jClassSubject.getDexProgramClass().type, + parameters.canUseDefaultAndStaticInterfaceMethods() + ? jClassSubject.getDexProgramClass().type + : iClassSubject.getDexProgramClass().type, aClassSubject.getDexProgramClass().interfaces.values[0]); }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java index 717bf12..b175132 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
@@ -75,9 +75,6 @@ testForD8(Backend.DEX) .addProgramFiles(getKotlinStdlibJar(kotlinc), getKotlinAnnotationJar(kotlinc)) .setMinApi(AndroidApiLevel.B) - // Enable record desugaring support to force a non-identity naming lens - .addOptionsModification( - options -> options.testing.enableExperimentalRecordDesugaring = true) .compile() .inspect( inspector ->
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingBridgeRemovalTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingBridgeRemovalTest.java index aa38f06..031440f 100644 --- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingBridgeRemovalTest.java +++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingBridgeRemovalTest.java
@@ -29,7 +29,7 @@ @Parameterized.Parameters(name = "{0}") public static TestParametersCollection data() { - return getTestParameters().withAllRuntimes().build(); + return getTestParameters().withAllRuntimesAndApiLevels().build(); } public MemberRebindingBridgeRemovalTest(TestParameters parameters) { @@ -44,7 +44,7 @@ .addKeepMainRule(TestClass.class) .enableInliningAnnotations() .enableNoVerticalClassMergingAnnotations() - .setMinApi(parameters.getRuntime()) + .setMinApi(parameters.getApiLevel()) .compile() .inspect(this::inspect) .run(parameters.getRuntime(), TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeTest.java new file mode 100644 index 0000000..d569899 --- /dev/null +++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeTest.java
@@ -0,0 +1,106 @@ +// 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.memberrebinding; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static com.android.tools.r8.utils.codeinspector.Matchers.isSynthetic; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.NoHorizontalClassMerging; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.DescriptorUtils; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.FoundMethodSubject; +import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector; +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 MemberRebindingRemoveInterfaceBridgeTest extends TestBase { + + private final TestParameters parameters; + private final String newMainDescriptor = "La/Main;"; + private final String newMainTypeName = DescriptorUtils.descriptorToJavaType(newMainDescriptor); + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public MemberRebindingRemoveInterfaceBridgeTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addProgramClasses(A.class, B.class, I.class, J.class) + .addProgramClassFileData( + transformer(Main.class).setClassDescriptor(newMainDescriptor).transform()) + .setMinApi(parameters.getApiLevel()) + .addKeepMainRule(newMainTypeName) + .addKeepClassAndMembersRules(I.class) + .enableInliningAnnotations() + .enableNoHorizontalClassMergingAnnotations() + .addHorizontallyMergedClassesInspector( + HorizontallyMergedClassesInspector::assertNoClassesMerged) + .run(parameters.getRuntime(), newMainTypeName) + .assertSuccessWithOutputLines("A::foo") + .inspect( + inspector -> { + ClassSubject clazz = inspector.clazz(J.class); + assertThat(clazz, isPresent()); + // TODO(b/197490164): We should remove the bridge inserted here. + assertEquals(1, clazz.allMethods().size()); + FoundMethodSubject foundMethodSubject = clazz.allMethods().get(0); + assertThat(foundMethodSubject, isSynthetic()); + }); + } + + interface I { + + void foo(); + } + + public interface J extends I {} + + @NoHorizontalClassMerging + public static class A implements J { + + @Override + @NeverInline + public void foo() { + System.out.println("A::foo"); + } + } + + @NoHorizontalClassMerging + public static class B implements J { + + @Override + @NeverInline + public void foo() { + System.out.println("B::foo"); + } + } + + public static class /* a.Main */ Main { + + public static void main(String[] args) { + callJ(args.length == 0 ? new A() : new B()); + } + + @NeverInline + private static void callJ(J j) { + j.foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeWithSubTypeTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeWithSubTypeTest.java new file mode 100644 index 0000000..f1fc86d --- /dev/null +++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeWithSubTypeTest.java
@@ -0,0 +1,101 @@ +// 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.memberrebinding; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static com.android.tools.r8.utils.codeinspector.Matchers.isSynthetic; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.NoHorizontalClassMerging; +import com.android.tools.r8.NoVerticalClassMerging; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.DescriptorUtils; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.FoundMethodSubject; +import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector; +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 MemberRebindingRemoveInterfaceBridgeWithSubTypeTest extends TestBase { + + private final TestParameters parameters; + private final String newMainDescriptor = "La/Main;"; + private final String newMainTypeName = DescriptorUtils.descriptorToJavaType(newMainDescriptor); + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public MemberRebindingRemoveInterfaceBridgeWithSubTypeTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addProgramClasses(A.class, B.class, I.class) + .addProgramClassFileData( + transformer(Main.class).setClassDescriptor(newMainDescriptor).transform()) + .setMinApi(parameters.getApiLevel()) + .addKeepMainRule(newMainTypeName) + .addKeepClassAndMembersRules(I.class) + .enableInliningAnnotations() + .enableNoHorizontalClassMergingAnnotations() + .enableNoVerticalClassMergingAnnotations() + .addHorizontallyMergedClassesInspector( + HorizontallyMergedClassesInspector::assertNoClassesMerged) + .noMinification() + .run(parameters.getRuntime(), newMainTypeName) + .assertSuccessWithOutputLines("B::foo") + .inspect( + inspector -> { + ClassSubject clazz = inspector.clazz(A.class); + assertThat(clazz, isPresent()); + // TODO(b/197490164): We should remove the bridge inserted here. + assertEquals(1, clazz.virtualMethods().size()); + FoundMethodSubject foundMethodSubject = clazz.virtualMethods().get(0); + assertThat(foundMethodSubject, isSynthetic()); + }); + } + + interface I { + + void foo(); + } + + @NoHorizontalClassMerging + @NoVerticalClassMerging + public abstract static class A implements I {} + + @NoHorizontalClassMerging + public static class B extends A { + + @Override + @NeverInline + public void foo() { + System.out.println("B::foo"); + } + } + + public static class /* a.Main */ Main { + + public static void main(String[] args) { + callJ(args.length == 0 ? new B() : null); + } + + @NeverInline + private static void callJ(A a) { + a.foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceDefaultBridgeTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceDefaultBridgeTest.java new file mode 100644 index 0000000..e11bdfe --- /dev/null +++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceDefaultBridgeTest.java
@@ -0,0 +1,92 @@ +// 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.memberrebinding; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.NoHorizontalClassMerging; +import com.android.tools.r8.NoVerticalClassMerging; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.DescriptorUtils; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +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 MemberRebindingRemoveInterfaceDefaultBridgeTest extends TestBase { + + private final TestParameters parameters; + private final String newMainDescriptor = "La/Main;"; + private final String newMainTypeName = DescriptorUtils.descriptorToJavaType(newMainDescriptor); + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public MemberRebindingRemoveInterfaceDefaultBridgeTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addProgramClasses(A.class, I.class, J.class) + .addProgramClassFileData( + transformer(Main.class).setClassDescriptor(newMainDescriptor).transform()) + .setMinApi(parameters.getApiLevel()) + .addKeepMainRule(newMainTypeName) + .addKeepClassAndMembersRules(I.class) + .enableInliningAnnotations() + .enableNoHorizontalClassMergingAnnotations() + .enableNoVerticalClassMergingAnnotations() + .run(parameters.getRuntime(), newMainTypeName) + .assertSuccessWithOutputLines("I::foo") + .inspect( + inspector -> { + ClassSubject clazz = inspector.clazz(J.class); + assertThat(clazz, isPresent()); + assertTrue(clazz.allMethods().isEmpty()); + if (!parameters.canUseDefaultAndStaticInterfaceMethods()) { + ClassSubject classSubject = clazz.toCompanionClass(); + assertThat(classSubject, isAbsent()); + } + }); + } + + interface I { + + @NeverInline + default void foo() { + System.out.println("I::foo"); + } + } + + @NoVerticalClassMerging + public interface J extends I {} + + @NoHorizontalClassMerging + public static class A implements J {} + + public static class /* a.Main */ Main { + + public static void main(String[] args) { + callJ(args.length == 0 ? new A() : null); + } + + @NeverInline + private static void callJ(J j) { + j.foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveStaticBridgeTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveStaticBridgeTest.java new file mode 100644 index 0000000..8bdaa09 --- /dev/null +++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveStaticBridgeTest.java
@@ -0,0 +1,79 @@ +// 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.memberrebinding; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.NoVerticalClassMerging; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.DescriptorUtils; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector; +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 MemberRebindingRemoveStaticBridgeTest extends TestBase { + + private final TestParameters parameters; + private final String newMainDescriptor = "La/Main;"; + private final String newMainTypeName = DescriptorUtils.descriptorToJavaType(newMainDescriptor); + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public MemberRebindingRemoveStaticBridgeTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addProgramClasses(A.class, B.class) + .addProgramClassFileData( + transformer(Main.class).setClassDescriptor(newMainDescriptor).transform()) + .setMinApi(parameters.getApiLevel()) + .addKeepMainRule(newMainTypeName) + .enableInliningAnnotations() + .enableNoVerticalClassMergingAnnotations() + .addHorizontallyMergedClassesInspector( + HorizontallyMergedClassesInspector::assertNoClassesMerged) + .run(parameters.getRuntime(), newMainTypeName) + .assertSuccessWithOutputLines("Hello World!") + .inspect( + inspector -> { + ClassSubject clazz = inspector.clazz(B.class); + assertThat(clazz, isPresent()); + assertTrue(clazz.allMethods().isEmpty()); + }); + } + + @NoVerticalClassMerging + static class A { + + @NeverInline + public static void foo() { + System.out.println("Hello World!"); + } + } + + public static class B extends A {} + + public static class /* a.Main */ Main { + + public static void main(String[] args) { + B.foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveVirtualBridgeDontShrinkTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveVirtualBridgeDontShrinkTest.java new file mode 100644 index 0000000..c23c295 --- /dev/null +++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveVirtualBridgeDontShrinkTest.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.memberrebinding; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertFalse; + +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.DescriptorUtils; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.FoundMethodSubject; +import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector; +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 MemberRebindingRemoveVirtualBridgeDontShrinkTest extends TestBase { + + private final TestParameters parameters; + private final String newMainDescriptor = "La/Main;"; + private final String newMainTypeName = DescriptorUtils.descriptorToJavaType(newMainDescriptor); + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public MemberRebindingRemoveVirtualBridgeDontShrinkTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addProgramClasses(A.class, B.class) + .addProgramClassFileData( + transformer(Main.class).setClassDescriptor(newMainDescriptor).transform()) + .setMinApi(parameters.getApiLevel()) + .addKeepMainRule(newMainTypeName) + .enableInliningAnnotations() + .addHorizontallyMergedClassesInspector( + HorizontallyMergedClassesInspector::assertNoClassesMerged) + .addDontShrink() + .addDontOptimize() + .run(parameters.getRuntime(), newMainTypeName) + .assertSuccessWithOutputLines("Hello World!") + .inspect( + inspector -> { + ClassSubject clazz = inspector.clazz(B.class); + assertThat(clazz, isPresent()); + // TODO(b/197491051): We should be able to remove bridges we have inserted. + assertFalse(clazz.allMethods(FoundMethodSubject::isBridge).isEmpty()); + }); + } + + static class A { + + @NeverInline + public void foo() { + System.out.println("Hello World!"); + } + } + + public static class B extends A {} + + public static class /* a.Main */ Main { + + public static void main(String[] args) { + new B().foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveVirtualBridgeTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveVirtualBridgeTest.java new file mode 100644 index 0000000..f38e042 --- /dev/null +++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveVirtualBridgeTest.java
@@ -0,0 +1,86 @@ +// 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.memberrebinding; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isInstanceInitializer; +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.NeverClassInline; +import com.android.tools.r8.NeverInline; +import com.android.tools.r8.NoVerticalClassMerging; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.DescriptorUtils; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.FoundMethodSubject; +import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector; +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 MemberRebindingRemoveVirtualBridgeTest extends TestBase { + + private final TestParameters parameters; + private final String newMainDescriptor = "La/Main;"; + private final String newMainTypeName = DescriptorUtils.descriptorToJavaType(newMainDescriptor); + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public MemberRebindingRemoveVirtualBridgeTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testR8() throws Exception { + testForR8(parameters.getBackend()) + .addProgramClasses(A.class, B.class) + .addProgramClassFileData( + transformer(Main.class).setClassDescriptor(newMainDescriptor).transform()) + .setMinApi(parameters.getApiLevel()) + .addKeepMainRule(newMainTypeName) + .enableInliningAnnotations() + .enableNoVerticalClassMergingAnnotations() + .enableNeverClassInliningAnnotations() + .addHorizontallyMergedClassesInspector( + HorizontallyMergedClassesInspector::assertNoClassesMerged) + .run(parameters.getRuntime(), newMainTypeName) + .assertSuccessWithOutputLines("Hello World!") + .inspect( + inspector -> { + ClassSubject clazz = inspector.clazz(B.class); + assertThat(clazz, isPresent()); + assertEquals(1, clazz.allMethods().size()); + FoundMethodSubject foundMethodSubject = clazz.allMethods().get(0); + assertThat(foundMethodSubject, isInstanceInitializer()); + }); + } + + @NoVerticalClassMerging + static class A { + + @NeverInline + public void foo() { + System.out.println("Hello World!"); + } + } + + @NeverClassInline + public static class B extends A {} + + public static class /* a.Main */ Main { + + public static void main(String[] args) { + new B().foo(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/desugar/DefaultInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/desugar/DefaultInterfaceMethodTest.java index 1868602..18f0c96 100644 --- a/src/test/java/com/android/tools/r8/naming/applymapping/desugar/DefaultInterfaceMethodTest.java +++ b/src/test/java/com/android/tools/r8/naming/applymapping/desugar/DefaultInterfaceMethodTest.java
@@ -3,16 +3,15 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.naming.applymapping.desugar; -import static com.android.tools.r8.references.Reference.classFromClass; -import static org.junit.Assert.assertTrue; +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.R8TestCompileResult; 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.itf.InterfaceDesugaringForTesting; -import com.android.tools.r8.references.Reference; -import com.android.tools.r8.utils.AndroidApiLevel; +import com.android.tools.r8.synthesis.SyntheticItemsTestUtils; import com.android.tools.r8.utils.StringUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; @@ -73,26 +72,21 @@ @Test public void testLibraryLinkedWithProgram() throws Throwable { - String ruleContent = "-keep class " + LibraryInterface.class.getTypeName() + " { *; }"; R8TestCompileResult libraryResult = testForR8(parameters.getBackend()) .addProgramClasses(LibraryInterface.class) - .addKeepRules(ruleContent) + .addKeepClassAndMembersRules(LibraryInterface.class) .setMinApi(parameters.getApiLevel()) .compile(); CodeInspector inspector = libraryResult.inspector(); - assertTrue(inspector.clazz(LibraryInterface.class).isPresent()); - assertTrue(inspector.method(LibraryInterface.class.getMethod("foo")).isPresent()); - if (willDesugarDefaultInterfaceMethods(parameters.getApiLevel())) { + assertThat(inspector.clazz(LibraryInterface.class), isPresent()); + assertThat(inspector.method(LibraryInterface.class.getMethod("foo")), isPresent()); + if (!parameters.canUseDefaultAndStaticInterfaceMethods()) { ClassSubject companion = - inspector.clazz( - Reference.classFromDescriptor( - InterfaceDesugaringForTesting.getCompanionClassDescriptor( - classFromClass(LibraryInterface.class).getDescriptor()))); - // Check that we included the companion class. - assertTrue(companion.isPresent()); - // TODO(b/129223905): Check the method is also present on the companion class. - assertTrue(inspector.method(LibraryInterface.class.getMethod("foo")).isPresent()); + inspector.clazz(SyntheticItemsTestUtils.syntheticCompanionClass(LibraryInterface.class)); + // Check that we included the companion class and method. + assertThat(companion, isPresent()); + assertEquals(1, companion.allMethods().size()); } testForR8(parameters.getBackend()) @@ -107,8 +101,4 @@ .run(parameters.getRuntime(), ProgramClass.class) .assertSuccessWithOutput(EXPECTED); } - - private static boolean willDesugarDefaultInterfaceMethods(AndroidApiLevel apiLevel) { - return apiLevel != null && apiLevel.getLevel() < AndroidApiLevel.N.getLevel(); - } }
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/desugar/StaticInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/desugar/StaticInterfaceMethodTest.java new file mode 100644 index 0000000..1dfd029 --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/applymapping/desugar/StaticInterfaceMethodTest.java
@@ -0,0 +1,110 @@ +// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.naming.applymapping.desugar; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; +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.R8TestCompileResult; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.synthesis.SyntheticItemsTestUtils; +import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import org.junit.Assume; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +// Reproduction for b/196345511. +@RunWith(Parameterized.class) +public class StaticInterfaceMethodTest extends TestBase { + + public static final String OUTPUT = "Called LibraryInterface::foo"; + public static final String EXPECTED = StringUtils.lines(OUTPUT); + + public interface LibraryInterface { + static void foo() { + System.out.println(OUTPUT); + } + } + + public static class ProgramClass implements LibraryInterface { + + public static void main(String[] args) { + LibraryInterface.foo(); + } + } + + @Parameters(name = "{0}") + public static TestParametersCollection params() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + private final TestParameters parameters; + + public StaticInterfaceMethodTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void testJvm() throws Throwable { + Assume.assumeTrue(parameters.isCfRuntime()); + testForJvm() + .addProgramClasses(LibraryInterface.class, ProgramClass.class) + .run(parameters.getRuntime(), ProgramClass.class) + .assertSuccessWithOutput(EXPECTED); + } + + @Test + public void testFullProgram() throws Throwable { + testForR8(parameters.getBackend()) + .addProgramClasses(LibraryInterface.class, ProgramClass.class) + .addKeepMainRule(ProgramClass.class) + .setMinApi(parameters.getApiLevel()) + .run(parameters.getRuntime(), ProgramClass.class) + .assertSuccessWithOutput(EXPECTED); + } + + @Test + public void testLibraryLinkedWithProgram() throws Throwable { + R8TestCompileResult libraryResult = + testForR8(parameters.getBackend()) + .addProgramClasses(LibraryInterface.class) + .addKeepClassAndMembersRules(LibraryInterface.class) + .setMinApi(parameters.getApiLevel()) + .compile(); + CodeInspector inspector = libraryResult.inspector(); + ClassSubject libraryInterface = inspector.clazz(LibraryInterface.class); + assertThat(libraryInterface, isPresent()); + if (parameters.canUseDefaultAndStaticInterfaceMethods()) { + assertThat(libraryInterface.method(LibraryInterface.class.getMethod("foo")), isPresent()); + } else { + // Desugaring must remove the static on the interface. + assertThat(libraryInterface.method(LibraryInterface.class.getMethod("foo")), isAbsent()); + // Check that we included the companion class and method. + ClassSubject companion = + inspector.clazz(SyntheticItemsTestUtils.syntheticCompanionClass(LibraryInterface.class)); + assertThat(companion, isPresent()); + assertEquals(1, companion.allMethods().size()); + } + + testForR8(parameters.getBackend()) + .noTreeShaking() + .addProgramClasses(ProgramClass.class) + .addClasspathClasses(LibraryInterface.class) + .addApplyMapping(libraryResult.getProguardMap()) + .addKeepMainRule(ProgramClass.class) + .setMinApi(parameters.getApiLevel()) + .compile() + .addRunClasspathFiles(libraryResult.writeToZip()) + .run(parameters.getRuntime(), ProgramClass.class) + .assertSuccessWithOutput(EXPECTED); + } +}
diff --git a/src/test/java/com/android/tools/r8/resolution/DefaultMethodShadowedByStaticTest.java b/src/test/java/com/android/tools/r8/resolution/DefaultMethodShadowedByStaticTest.java index 19424d8..b974111 100644 --- a/src/test/java/com/android/tools/r8/resolution/DefaultMethodShadowedByStaticTest.java +++ b/src/test/java/com/android/tools/r8/resolution/DefaultMethodShadowedByStaticTest.java
@@ -6,7 +6,6 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.TestRunResult; import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.resolution.shadowing1.AClassDump; import com.android.tools.r8.resolution.shadowing1.InterfaceDump; @@ -39,22 +38,15 @@ @Test public void testReference() throws Exception { - TestRunResult<?> result = - testForRuntime(parameters) - .addProgramClassFileData(CLASSES) - .run(parameters.getRuntime(), "Main"); - if (parameters.isDexRuntime() - && (parameters.getApiLevel().isLessThan(apiLevelWithStaticInterfaceMethodsSupport()) - || parameters.getDexRuntimeVersion().equals(Version.V7_0_0))) { - // TODO(b/167535447): Desugaring should preserve the error. - result.assertSuccessWithOutputLines("42"); - } else if (parameters.isDexRuntime() - && parameters.getDexRuntimeVersion().equals(Version.V7_0_0)) { - // Note: VM 7.0.0 without desugaring of defaults will incorrectly allow the virtual dispatch. - result.assertSuccessWithOutputLines("42"); - } else { - result.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class); - } + testForRuntime(parameters) + .addProgramClassFileData(CLASSES) + .run(parameters.getRuntime(), "Main") + .applyIf( + // When not desugaring interfaces, the v7 runtime fails to throw the correct error. + parameters.canUseDefaultAndStaticInterfaceMethods() + && parameters.isDexRuntimeVersion(Version.V7_0_0), + r -> r.assertSuccessWithOutputLines("42"), + r -> r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class)); } @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java index b176930..7d18aee 100644 --- a/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java +++ b/src/test/java/com/android/tools/r8/resolution/SingleTargetLookupTest.java
@@ -8,6 +8,7 @@ import static org.junit.Assert.assertTrue; import com.android.tools.r8.AsmTestBase; +import com.android.tools.r8.TestAppViewBuilder; import com.android.tools.r8.graph.AppInfo; import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexEncodedMethod; @@ -89,12 +90,17 @@ @BeforeClass public static void computeAppInfo() throws Exception { appView = - computeAppViewWithLiveness( - buildClassesWithTestingAnnotations(CLASSES) - .addClassProgramData(ASM_CLASSES) - .addLibraryFile(getMostRecentAndroidJar()) - .build(), - Main.class); + TestAppViewBuilder.builder() + .addAndroidApp( + buildClassesWithTestingAnnotations(CLASSES) + .addClassProgramData(ASM_CLASSES) + .addLibraryFile(getMostRecentAndroidJar()) + .build()) + .addKeepMainRule(Main.class) + // Some of these tests resolve default methods. + // If desugared they will hit the forward methods and not the expected defaults. + .setMinApi(apiLevelWithDefaultInterfaceMethodsSupport()) + .buildWithLiveness(); appInfo = appView.appInfo(); }
diff --git a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java index cbad874..846c994 100644 --- a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java +++ b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentInterfaceTest.java
@@ -4,22 +4,18 @@ package com.android.tools.r8.resolution; import static com.android.tools.r8.ToolHelper.getMostRecentAndroidJar; -import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import com.android.tools.r8.AsmTestBase; -import com.android.tools.r8.R8TestRunResult; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.TestRunResult; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.MethodResolutionResult; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.shaking.AppInfoWithLiveness; -import com.android.tools.r8.utils.AndroidApiLevel; import com.google.common.collect.ImmutableList; import java.util.List; import org.junit.Assert; @@ -150,22 +146,11 @@ @Test public void runJvmAndD8() throws Exception { - TestRunResult<?> runResult; - if (parameters.isCfRuntime()) { - runResult = - testForJvm() - .addProgramClasses(CLASSES) - .addProgramClassFileData(DUMP) - .run(parameters.getRuntime(), Main.class); - } else { - runResult = - testForD8() - .addProgramClasses(CLASSES) - .addProgramClassFileData(DUMP) - .setMinApi(parameters.getApiLevel()) - .run(parameters.getRuntime(), Main.class); - } - checkResult(runResult); + testForRuntime(parameters) + .addProgramClasses(CLASSES) + .addProgramClassFileData(DUMP) + .run(parameters.getRuntime(), Main.class) + .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class); } @Test @@ -179,33 +164,13 @@ } public void runR8(boolean enableVerticalClassMerging) throws Exception { - R8TestRunResult runResult = - testForR8(parameters.getBackend()) - .addProgramClasses(CLASSES) - .addProgramClassFileData(DUMP) - .addKeepMainRule(Main.class) - .setMinApi(parameters.getApiLevel()) - .addOptionsModification(o -> o.enableVerticalClassMerging = enableVerticalClassMerging) - .run(parameters.getRuntime(), Main.class); - if (enableVerticalClassMerging) { - // Vertical class merging will merge B and C and change the instruction to invoke-virtual - // causing the legacy ART runtime behavior to match the expected error. - runResult.assertFailureWithErrorThatMatches(containsString("IncompatibleClassChangeError")); - } else { - checkResult(runResult); - } - } - - private void checkResult(TestRunResult<?> runResult) { - runResult.assertFailureWithErrorThatMatches(containsString(expectedRuntimeError())); - } - - private String expectedRuntimeError() { - if (parameters.isDexRuntime() - && parameters.getApiLevel().getLevel() < AndroidApiLevel.N.getLevel()) { - // When desugaring default interface methods the error will be NoSuchMethodError. - return "NoSuchMethodError"; - } - return "IncompatibleClassChangeError"; + testForR8(parameters.getBackend()) + .addProgramClasses(CLASSES) + .addProgramClassFileData(DUMP) + .addKeepMainRule(Main.class) + .setMinApi(parameters.getApiLevel()) + .addOptionsModification(o -> o.enableVerticalClassMerging = enableVerticalClassMerging) + .run(parameters.getRuntime(), Main.class) + .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class); } }
diff --git a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java index c6e9206..ca29588 100644 --- a/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java +++ b/src/test/java/com/android/tools/r8/resolution/VirtualOverrideOfStaticMethodWithVirtualParentTest.java
@@ -4,7 +4,6 @@ package com.android.tools.r8.resolution; import static com.android.tools.r8.ToolHelper.getMostRecentAndroidJar; -import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -13,8 +12,7 @@ import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.TestRunResult; -import com.android.tools.r8.TestRuntime; -import com.android.tools.r8.ToolHelper.DexVm; +import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.graph.DexEncodedMethod; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProgramClass; @@ -226,23 +224,18 @@ } private void checkResult(TestRunResult<?> runResult, boolean isCorrectedByR8) { - if (expectedToIncorrectlyRun(parameters.getRuntime(), isCorrectedByR8)) { - // Do to incorrect resolution, some Art VMs will resolve to Base.f (ignoring A.f) and thus + if (expectedToIncorrectlyRun(isCorrectedByR8)) { + // Do to incorrect resolution, some Art 7 will resolve to Base.f (ignoring A.f) and thus // virtual dispatch to C.f. See b/140013075. runResult.assertSuccessWithOutputLines("Called C.f"); } else { - runResult.assertFailureWithErrorThatMatches(containsString(expectedRuntimeError())); + runResult.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class); } } - private boolean expectedToIncorrectlyRun(TestRuntime runtime, boolean isCorrectedByR8) { + private boolean expectedToIncorrectlyRun(boolean isCorrectedByR8) { return !isCorrectedByR8 - && runtime.isDex() - && runtime.asDex().getVm().isNewerThan(DexVm.ART_4_4_4_HOST) - && runtime.asDex().getVm().isOlderThanOrEqual(DexVm.ART_7_0_0_HOST); - } - - private static String expectedRuntimeError() { - return "IncompatibleClassChangeError"; + && parameters.canUseDefaultAndStaticInterfaceMethods() + && parameters.isDexRuntimeVersion(Version.V7_0_0); } }
diff --git a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java index 4787b97..82c6b63 100644 --- a/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java +++ b/src/test/java/com/android/tools/r8/resolution/access/NestInvokeSpecialInterfaceMethodAccessWithIntermediateTest.java
@@ -10,6 +10,7 @@ import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; +import com.android.tools.r8.TestAppViewBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestRunResult; @@ -20,6 +21,7 @@ import com.android.tools.r8.graph.MethodResolutionResult.NoSuchMethodResult; import com.android.tools.r8.references.Reference; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.synthesis.SyntheticItemsTestUtils; import com.android.tools.r8.transformers.ClassFileTransformer; import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.DescriptorUtils; @@ -52,6 +54,7 @@ .withCfRuntimesStartingFromIncluding(JDK11) .withDexRuntimes() .withAllApiLevels() + .enableApiLevelsForCf() .build(), BooleanUtils.values(), BooleanUtils.values()); @@ -132,7 +135,16 @@ MethodSubject foo = inspector.clazz(callerClass).uniqueMethodWithName("foo"); assertTrue( foo.streamInstructions() - .anyMatch(i -> i.asCfInstruction().isInvokeSpecial() && i.getMethod() == method)); + .anyMatch( + i -> { + if (parameters.canUseDefaultAndStaticInterfaceMethodsWhenDesugaring()) { + return i.asCfInstruction().isInvokeSpecial() && i.getMethod() == method; + } else { + return i.isInvokeStatic() + && SyntheticItemsTestUtils.isInternalThrowNSME( + i.getMethod().asMethodReference()); + } + })); } private DexMethod getTargetMethodSignature(Class<?> declaredClass, AppInfoWithLiveness appInfo) { @@ -148,12 +160,13 @@ } private AppView<AppInfoWithLiveness> getAppView() throws Exception { - return computeAppViewWithLiveness( - buildClasses(getClasses()) - .addClassProgramData(getTransformedClasses()) - .addLibraryFile(TestBase.runtimeJar(parameters.getBackend())) - .build(), - Main.class); + return TestAppViewBuilder.builder() + .addProgramClasses(getClasses()) + .addProgramClassFileData(getTransformedClasses()) + .addLibraryFiles(parameters.getDefaultRuntimeLibrary()) + .addKeepMainRule(Main.class) + .setMinApi(parameters.getApiLevel()) + .buildWithLiveness(); } @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java index d6edf4f..47d664f 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultLeftAbstractRightTest.java
@@ -6,6 +6,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assume.assumeTrue; +import com.android.tools.r8.TestAppViewBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; @@ -15,7 +16,6 @@ import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.google.common.collect.ImmutableList; import java.util.Collection; -import java.util.Collections; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -42,12 +42,13 @@ // The resolution is runtime independent, so just run it on the default CF VM. assumeTrue(parameters.useRuntimeAsNoneRuntime()); AppInfoWithLiveness appInfo = - computeAppViewWithLiveness( - buildClasses(CLASSES) - .addClassProgramData(Collections.singletonList(transformB())) - .addLibraryFile(parameters.getDefaultRuntimeLibrary()) - .build(), - Main.class) + TestAppViewBuilder.builder() + .addProgramClasses(CLASSES) + .addProgramClassFileData(transformB()) + .addLibraryFiles(parameters.getDefaultRuntimeLibrary()) + .addKeepMainRule(Main.class) + .setMinApi(apiLevelWithDefaultInterfaceMethodsSupport()) + .buildWithLiveness() .appInfo(); DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java index 77fad9a..f6b2df5 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultRightAbstractLeftTest.java
@@ -6,6 +6,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assume.assumeTrue; +import com.android.tools.r8.TestAppViewBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; @@ -15,7 +16,6 @@ import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.google.common.collect.ImmutableList; import java.util.Collection; -import java.util.Collections; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -42,12 +42,13 @@ // The resolution is runtime independent, so just run it on the default CF VM. assumeTrue(parameters.useRuntimeAsNoneRuntime()); AppInfoWithLiveness appInfo = - computeAppViewWithLiveness( - buildClasses(CLASSES) - .addClassProgramData(Collections.singletonList(transformB())) - .addLibraryFile(parameters.getDefaultRuntimeLibrary()) - .build(), - Main.class) + TestAppViewBuilder.builder() + .addProgramClasses(CLASSES) + .addProgramClassFileData(transformB()) + .addLibraryFiles(parameters.getDefaultRuntimeLibrary()) + .addKeepMainRule(Main.class) + .setMinApi(apiLevelWithDefaultInterfaceMethodsSupport()) + .buildWithLiveness() .appInfo(); DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java index 41307e8..af70288 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndBothTest.java
@@ -7,6 +7,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assume.assumeTrue; +import com.android.tools.r8.TestAppViewBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; @@ -15,7 +16,6 @@ import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -44,12 +44,13 @@ // The resolution is runtime independent, so just run it on the default CF VM. assumeTrue(parameters.useRuntimeAsNoneRuntime()); AppInfoWithLiveness appInfo = - computeAppViewWithLiveness( - buildClasses(CLASSES) - .addClassProgramData(Collections.singletonList(transformB())) - .addLibraryFile(parameters.getDefaultRuntimeLibrary()) - .build(), - Main.class) + TestAppViewBuilder.builder() + .addProgramClasses(CLASSES) + .addProgramClassFileData(transformB()) + .addLibraryFiles(parameters.getDefaultRuntimeLibrary()) + .addKeepMainRule(Main.class) + .setMinApi(apiLevelWithDefaultInterfaceMethodsSupport()) + .buildWithLiveness() .appInfo(); DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java index 9b9a45d..d2d08a5 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndLeftTest.java
@@ -6,6 +6,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assume.assumeTrue; +import com.android.tools.r8.TestAppViewBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; @@ -41,9 +42,12 @@ // The resolution is runtime independent, so just run it on the default CF VM. assumeTrue(parameters.useRuntimeAsNoneRuntime()); AppInfoWithLiveness appInfo = - computeAppViewWithLiveness( - buildClasses(CLASSES).addLibraryFile(parameters.getDefaultRuntimeLibrary()).build(), - Main.class) + TestAppViewBuilder.builder() + .addProgramClasses(CLASSES) + .addLibraryFiles(parameters.getDefaultRuntimeLibrary()) + .addKeepMainRule(Main.class) + .setMinApi(apiLevelWithDefaultInterfaceMethodsSupport()) + .buildWithLiveness() .appInfo(); DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java index 50dc8d8..1b900b4 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/DefaultTopAndRightTest.java
@@ -6,6 +6,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assume.assumeTrue; +import com.android.tools.r8.TestAppViewBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; @@ -41,9 +42,12 @@ // The resolution is runtime independent, so just run it on the default CF VM. assumeTrue(parameters.useRuntimeAsNoneRuntime()); AppInfoWithLiveness appInfo = - computeAppViewWithLiveness( - buildClasses(CLASSES).addLibraryFile(parameters.getDefaultRuntimeLibrary()).build(), - Main.class) + TestAppViewBuilder.builder() + .addProgramClasses(CLASSES) + .addLibraryFiles(parameters.getDefaultRuntimeLibrary()) + .addKeepMainRule(Main.class) + .setMinApi(apiLevelWithDefaultInterfaceMethodsSupport()) + .buildWithLiveness() .appInfo(); DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java index 0139049..8901542 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacediamonds/TwoDefaultMethodsWithoutTopTest.java
@@ -5,17 +5,19 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; +import com.android.tools.r8.TestAppViewBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.MethodResolutionResult; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.utils.AndroidApiLevel; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -44,21 +46,45 @@ public void testResolution() throws Exception { // The resolution is runtime independent, so just run it on the default CF VM. assumeTrue(parameters.useRuntimeAsNoneRuntime()); - AppInfoWithLiveness appInfo = - computeAppViewWithLiveness( - buildClasses(CLASSES) - .addClassProgramData(Collections.singletonList(transformB())) - .addLibraryFile(parameters.getDefaultRuntimeLibrary()) - .build(), - Main.class) - .appInfo(); - DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory()); - MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method); - Set<String> holders = new HashSet<>(); - resolutionResult - .asFailedResolution() - .forEachFailureDependency(m -> holders.add(m.getHolderType().toSourceString())); - assertEquals(ImmutableSet.of(I.class.getTypeName(), J.class.getTypeName()), holders); + for (AndroidApiLevel minApi : + ImmutableList.of(AndroidApiLevel.B, apiLevelWithDefaultInterfaceMethodsSupport())) { + AppInfoWithLiveness appInfo = + TestAppViewBuilder.builder() + .addProgramClasses(CLASSES) + .addProgramClassFileData(transformB()) + .addLibraryFiles(parameters.getDefaultRuntimeLibrary()) + .addKeepMainRule(Main.class) + .setMinApi(minApi) + .buildWithLiveness() + .appInfo(); + DexMethod method = buildNullaryVoidMethod(B.class, "f", appInfo.dexItemFactory()); + MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method); + if (minApi.isLessThan(apiLevelWithDefaultInterfaceMethodsSupport())) { + // When desugaring a forwarding method throwing ICCE is inserted. + // Check that the resolved method throws such an exception. + assertTrue( + resolutionResult + .asSingleResolution() + .getResolvedMethod() + .getCode() + .asCfCode() + .getInstructions() + .stream() + .anyMatch( + i -> + i.isTypeInstruction() + && i.asTypeInstruction().getType() + == appInfo.dexItemFactory().icceType)); + } else { + // When not desugaring resolution should fail. Check the failure dependencies are the two + // default methods in conflict. + Set<String> holders = new HashSet<>(); + resolutionResult + .asFailedResolution() + .forEachFailureDependency(m -> holders.add(m.getHolderType().toSourceString())); + assertEquals(ImmutableSet.of(I.class.getTypeName(), J.class.getTypeName()), holders); + } + } } @Test
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java index f8a46ca..b3c88c7 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodAsOverrideWithLambdaTest.java
@@ -13,6 +13,7 @@ import com.android.tools.r8.NeverClassInline; import com.android.tools.r8.NeverInline; import com.android.tools.r8.NoVerticalClassMerging; +import com.android.tools.r8.TestAppViewBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; @@ -53,11 +54,13 @@ public void testResolution() throws Exception { assumeTrue(parameters.useRuntimeAsNoneRuntime()); AppView<AppInfoWithLiveness> appView = - computeAppViewWithLiveness( - buildClassesWithTestingAnnotations(I.class, J.class, A.class, Main.class) - .addLibraryFile(parameters.getDefaultRuntimeLibrary()) - .build(), - Main.class); + TestAppViewBuilder.builder() + .addProgramClasses(I.class, J.class, A.class, Main.class) + .addTestingAnnotations() + .addLibraryFiles(parameters.getDefaultRuntimeLibrary()) + .addKeepMainRule(Main.class) + .setMinApi(apiLevelWithDefaultInterfaceMethodsSupport()) + .buildWithLiveness(); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(I.class, "bar", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult =
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodLambdaTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodLambdaTest.java index 4449400..1b3d3d9 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodLambdaTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultMethodLambdaTest.java
@@ -13,6 +13,7 @@ import com.android.tools.r8.NeverClassInline; import com.android.tools.r8.NeverInline; import com.android.tools.r8.NoVerticalClassMerging; +import com.android.tools.r8.TestAppViewBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; @@ -52,11 +53,13 @@ public void testResolution() throws Exception { assumeTrue(parameters.useRuntimeAsNoneRuntime()); AppView<AppInfoWithLiveness> appView = - computeAppViewWithLiveness( - buildClassesWithTestingAnnotations(I.class, A.class, Main.class) - .addLibraryFile(parameters.getDefaultRuntimeLibrary()) - .build(), - Main.class); + TestAppViewBuilder.builder() + .addProgramClasses(I.class, A.class, Main.class) + .addTestingAnnotations() + .addLibraryFiles(parameters.getDefaultRuntimeLibrary()) + .addKeepMainRule(Main.class) + .setMinApi(apiLevelWithDefaultInterfaceMethodsSupport()) + .buildWithLiveness(); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(I.class, "bar", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult =
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java index 6701adc..467ca50 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DefaultWithoutTopTest.java
@@ -13,6 +13,7 @@ import com.android.tools.r8.NeverClassInline; import com.android.tools.r8.NeverInline; import com.android.tools.r8.NoVerticalClassMerging; +import com.android.tools.r8.TestAppViewBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; @@ -52,12 +53,14 @@ public void testDynamicLookupTargets() throws Exception { assumeTrue(parameters.useRuntimeAsNoneRuntime()); AppView<AppInfoWithLiveness> appView = - computeAppViewWithLiveness( - buildClassesWithTestingAnnotations(I.class, J.class, Main.class) - .addClassProgramData(setAImplementsIAndJ()) - .addLibraryFile(parameters.getDefaultRuntimeLibrary()) - .build(), - Main.class); + TestAppViewBuilder.builder() + .addProgramClasses(I.class, J.class, Main.class) + .addTestingAnnotations() + .addProgramClassFileData(setAImplementsIAndJ()) + .addLibraryFiles(parameters.getDefaultRuntimeLibrary()) + .addKeepMainRule(Main.class) + .setMinApi(apiLevelWithDefaultInterfaceMethodsSupport()) + .buildWithLiveness(); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method); @@ -99,12 +102,14 @@ public void testDynamicLookupTargetsWithIndirectDefault() throws Exception { assumeTrue(parameters.useRuntimeAsNoneRuntime()); AppView<AppInfoWithLiveness> appView = - computeAppViewWithLiveness( - buildClassesWithTestingAnnotations(I.class, J.class, K.class, Main.class) - .addClassProgramData(setAimplementsIandK()) - .addLibraryFile(parameters.getDefaultRuntimeLibrary()) - .build(), - Main.class); + TestAppViewBuilder.builder() + .addTestingAnnotations() + .addProgramClasses(I.class, J.class, K.class, Main.class) + .addProgramClassFileData(setAimplementsIandK()) + .addLibraryFiles(parameters.getDefaultRuntimeLibrary()) + .addKeepMainRule(Main.class) + .setMinApi(apiLevelWithDefaultInterfaceMethodsSupport()) + .buildWithLiveness(); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DuplicateImportsTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DuplicateImportsTest.java index ae340f7..af2dde1 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/DuplicateImportsTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/DuplicateImportsTest.java
@@ -13,6 +13,7 @@ import com.android.tools.r8.NeverClassInline; import com.android.tools.r8.NeverInline; import com.android.tools.r8.NoVerticalClassMerging; +import com.android.tools.r8.TestAppViewBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; @@ -53,11 +54,13 @@ public void testResolution() throws Exception { assumeTrue(parameters.useRuntimeAsNoneRuntime()); AppView<AppInfoWithLiveness> appView = - computeAppViewWithLiveness( - buildClassesWithTestingAnnotations(I.class, J.class, A.class, Main.class) - .addLibraryFile(parameters.getDefaultRuntimeLibrary()) - .build(), - Main.class); + TestAppViewBuilder.builder() + .addTestingAnnotations() + .addProgramClasses(I.class, J.class, A.class, Main.class) + .addLibraryFiles(parameters.getDefaultRuntimeLibrary()) + .addKeepMainRule(Main.class) + .setMinApi(apiLevelWithDefaultInterfaceMethodsSupport()) + .buildWithLiveness(); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java index 0ec1ce8..4dcead3 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/InvokeInterfaceWithStaticTargetTest.java
@@ -4,7 +4,6 @@ package com.android.tools.r8.resolution.interfacetargets; -import static org.hamcrest.CoreMatchers.containsString; import static org.junit.Assume.assumeTrue; import static org.objectweb.asm.Opcodes.INVOKEINTERFACE; @@ -17,7 +16,6 @@ import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.shaking.AppInfoWithLiveness; -import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.DescriptorUtils; import java.io.IOException; import java.util.concurrent.ExecutionException; @@ -70,7 +68,7 @@ .addProgramClasses(A.class, I.class) .addProgramClassFileData(transformMain()) .run(parameters.getRuntime(), Main.class) - .assertFailureWithErrorThatMatches(containsString(getExpected())); + .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class); } @Test @@ -81,14 +79,7 @@ .addKeepMainRule(Main.class) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class) - .assertFailureWithErrorThatMatches(containsString(getExpected())); - } - - private String getExpected() { - return parameters.isCfRuntime() - || parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N) - ? "IncompatibleClassChangeError" - : "NoSuchMethodError"; + .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class); } private byte[] transformMain() throws IOException {
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/LambdaMultipleInterfacesTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/LambdaMultipleInterfacesTest.java index 9af861c..0819b97 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/LambdaMultipleInterfacesTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/LambdaMultipleInterfacesTest.java
@@ -13,6 +13,7 @@ import com.android.tools.r8.NeverClassInline; import com.android.tools.r8.NeverInline; import com.android.tools.r8.NoVerticalClassMerging; +import com.android.tools.r8.TestAppViewBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; @@ -52,11 +53,13 @@ public void testResolution() throws Exception { assumeTrue(parameters.useRuntimeAsNoneRuntime()); AppView<AppInfoWithLiveness> appView = - computeAppViewWithLiveness( - buildClassesWithTestingAnnotations(I.class, J.class, A.class, Main.class) - .addLibraryFile(parameters.getDefaultRuntimeLibrary()) - .build(), - Main.class); + TestAppViewBuilder.builder() + .addTestingAnnotations() + .addProgramClasses(I.class, J.class, A.class, Main.class) + .addLibraryFiles(parameters.getDefaultRuntimeLibrary()) + .addKeepMainRule(Main.class) + .setMinApi(apiLevelWithDefaultInterfaceMethodsSupport()) + .buildWithLiveness(); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(J.class, "bar", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult =
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubInterfaceOverridesTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubInterfaceOverridesTest.java index 512e775..4d85bfb 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubInterfaceOverridesTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/SubInterfaceOverridesTest.java
@@ -13,6 +13,7 @@ import com.android.tools.r8.CompilationFailedException; import com.android.tools.r8.NeverClassInline; import com.android.tools.r8.NoVerticalClassMerging; +import com.android.tools.r8.TestAppViewBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; @@ -52,12 +53,13 @@ public void testResolution() throws Exception { assumeTrue(parameters.useRuntimeAsNoneRuntime()); AppView<AppInfoWithLiveness> appView = - computeAppViewWithLiveness( - buildClassesWithTestingAnnotations( - I.class, J.class, A.class, B.class, C.class, Main.class) - .addLibraryFile(parameters.getDefaultRuntimeLibrary()) - .build(), - Main.class); + TestAppViewBuilder.builder() + .addTestingAnnotations() + .addProgramClasses(I.class, J.class, A.class, B.class, C.class, Main.class) + .addLibraryFiles(parameters.getDefaultRuntimeLibrary()) + .addKeepMainRule(Main.class) + .setMinApi(apiLevelWithDefaultInterfaceMethodsSupport()) + .buildWithLiveness(); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult =
diff --git a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateClasspathWidenTest.java b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateClasspathWidenTest.java index fe3a74c..e59ea7a 100644 --- a/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateClasspathWidenTest.java +++ b/src/test/java/com/android/tools/r8/resolution/packageprivate/PackagePrivateClasspathWidenTest.java
@@ -10,6 +10,7 @@ import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationFailedException; +import com.android.tools.r8.TestAppViewBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; @@ -64,12 +65,13 @@ public void testResolution() throws Exception { assumeTrue(parameters.useRuntimeAsNoneRuntime()); AppView<AppInfoWithLiveness> appView = - computeAppViewWithLiveness( - buildClasses(C.class, Main.class) - .addClasspathFiles(classPathJar) - .addLibraryFile(parameters.getDefaultRuntimeLibrary()) - .build(), - Main.class); + TestAppViewBuilder.builder() + .addProgramClasses(C.class, Main.class) + .addClasspathFiles(classPathJar) + .addLibraryFiles(parameters.getDefaultRuntimeLibrary()) + .addKeepMainRule(Main.class) + .setMinApi(apiLevelWithDefaultInterfaceMethodsSupport()) + .buildWithLiveness(); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(Abstract.class, "foo", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java index f6368cb..d91d19f 100644 --- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java +++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceSubTypeTest.java
@@ -13,6 +13,7 @@ import com.android.tools.r8.NeverClassInline; import com.android.tools.r8.NeverInline; import com.android.tools.r8.NoVerticalClassMerging; +import com.android.tools.r8.TestAppViewBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; @@ -52,11 +53,13 @@ public void testResolution() throws Exception { assumeTrue(parameters.useRuntimeAsNoneRuntime()); AppView<AppInfoWithLiveness> appView = - computeAppViewWithLiveness( - buildClassesWithTestingAnnotations(I.class, J.class, A.class, B.class, Main.class) - .addLibraryFile(parameters.getDefaultRuntimeLibrary()) - .build(), - Main.class); + TestAppViewBuilder.builder() + .addTestingAnnotations() + .addProgramClasses(I.class, J.class, A.class, B.class, Main.class) + .addLibraryFiles(parameters.getDefaultRuntimeLibrary()) + .addKeepMainRule(Main.class) + .setMinApi(apiLevelWithDefaultInterfaceMethodsSupport()) + .buildWithLiveness(); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java index 4673e7c..69c8c7a 100644 --- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java +++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultInterfaceMethodInSubInterfaceTest.java
@@ -13,6 +13,7 @@ import com.android.tools.r8.NeverClassInline; import com.android.tools.r8.NeverInline; import com.android.tools.r8.NoVerticalClassMerging; +import com.android.tools.r8.TestAppViewBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; @@ -52,11 +53,13 @@ public void testResolution() throws Exception { assumeTrue(parameters.useRuntimeAsNoneRuntime()); AppView<AppInfoWithLiveness> appView = - computeAppViewWithLiveness( - buildClassesWithTestingAnnotations(I.class, J.class, A.class, Main.class) - .addLibraryFile(parameters.getDefaultRuntimeLibrary()) - .build(), - Main.class); + TestAppViewBuilder.builder() + .addTestingAnnotations() + .addProgramClasses(I.class, J.class, A.class, Main.class) + .addLibraryFiles(parameters.getDefaultRuntimeLibrary()) + .addKeepMainRule(Main.class) + .setMinApi(apiLevelWithDefaultInterfaceMethodsSupport()) + .buildWithLiveness(); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java index d6d5dc8..65d8316 100644 --- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java +++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/DefaultWithoutTopTest.java
@@ -13,6 +13,7 @@ import com.android.tools.r8.NeverClassInline; import com.android.tools.r8.NeverInline; import com.android.tools.r8.NoVerticalClassMerging; +import com.android.tools.r8.TestAppViewBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; @@ -52,12 +53,14 @@ public void testDynamicLookupTargets() throws Exception { assumeTrue(parameters.useRuntimeAsNoneRuntime()); AppView<AppInfoWithLiveness> appView = - computeAppViewWithLiveness( - buildClassesWithTestingAnnotations(I.class, J.class, Main.class) - .addClassProgramData(setAImplementsIAndJ()) - .addLibraryFile(parameters.getDefaultRuntimeLibrary()) - .build(), - Main.class); + TestAppViewBuilder.builder() + .addTestingAnnotations() + .addProgramClasses(I.class, J.class, Main.class) + .addProgramClassFileData(setAImplementsIAndJ()) + .addLibraryFiles(parameters.getDefaultRuntimeLibrary()) + .addKeepMainRule(Main.class) + .setMinApi(apiLevelWithDefaultInterfaceMethodsSupport()) + .buildWithLiveness(); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java index e6e6f6c..14f9d2a 100644 --- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java +++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/InvokeVirtualToInterfaceDefinitionTest.java
@@ -13,6 +13,7 @@ import com.android.tools.r8.NeverClassInline; import com.android.tools.r8.NeverInline; import com.android.tools.r8.NoVerticalClassMerging; +import com.android.tools.r8.TestAppViewBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; @@ -52,11 +53,13 @@ public void testResolution() throws Exception { assumeTrue(parameters.useRuntimeAsNoneRuntime()); AppView<AppInfoWithLiveness> appView = - computeAppViewWithLiveness( - buildClassesWithTestingAnnotations(A.class, I.class, Main.class) - .addLibraryFile(parameters.getDefaultRuntimeLibrary()) - .build(), - Main.class); + TestAppViewBuilder.builder() + .addTestingAnnotations() + .addProgramClasses(A.class, I.class, Main.class) + .addLibraryFiles(parameters.getDefaultRuntimeLibrary()) + .addKeepMainRule(Main.class) + .setMinApi(apiLevelWithDefaultInterfaceMethodsSupport()) + .buildWithLiveness(); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(A.class, "foo", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult = appInfo.resolveMethodOnClass(method);
diff --git a/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java b/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java index 536f420..162d3c4 100644 --- a/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java +++ b/src/test/java/com/android/tools/r8/resolution/virtualtargets/TargetInDefaultMethodTest.java
@@ -14,6 +14,7 @@ import com.android.tools.r8.NeverClassInline; import com.android.tools.r8.NeverInline; import com.android.tools.r8.NoVerticalClassMerging; +import com.android.tools.r8.TestAppViewBuilder; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; @@ -53,11 +54,13 @@ public void testResolution() throws Exception { assumeTrue(parameters.useRuntimeAsNoneRuntime()); AppView<AppInfoWithLiveness> appView = - computeAppViewWithLiveness( - buildClassesWithTestingAnnotations(I.class, A.class, B.class, C.class, Main.class) - .addLibraryFile(parameters.getDefaultRuntimeLibrary()) - .build(), - Main.class); + TestAppViewBuilder.builder() + .addTestingAnnotations() + .addProgramClasses(I.class, A.class, B.class, C.class, Main.class) + .addLibraryFiles(parameters.getDefaultRuntimeLibrary()) + .addKeepMainRule(Main.class) + .setMinApi(apiLevelWithDefaultInterfaceMethodsSupport()) + .buildWithLiveness(); AppInfoWithLiveness appInfo = appView.appInfo(); DexMethod method = buildNullaryVoidMethod(I.class, "foo", appInfo.dexItemFactory()); MethodResolutionResult resolutionResult = appInfo.resolveMethodOnInterface(method);
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiBinaryCompatibilityTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiBinaryCompatibilityTest.java new file mode 100644 index 0000000..df7c599 --- /dev/null +++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiBinaryCompatibilityTest.java
@@ -0,0 +1,95 @@ +// 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.api; + +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +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.TestRuntime.CfRuntime; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.utils.ZipUtils; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class RetraceApiBinaryCompatibilityTest extends TestBase { + + private final TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withNoneRuntime().build(); + } + + public RetraceApiBinaryCompatibilityTest(TestParameters parameters) { + this.parameters = parameters; + } + + private static final Path BINARY_COMPATIBILITY_JAR = + Paths.get(ToolHelper.THIRD_PARTY_DIR, "retrace", "binary_compatibility", "tests.jar"); + + private Path generateJar() throws Exception { + return RetraceApiTestHelper.generateJarForRetraceBinaryTests( + temp, RetraceApiTestHelper.getBinaryCompatibilityTests()); + } + + @Test + public void testBinaryJarIsUpToDate() throws Exception { + Path binaryContents = temp.newFolder().toPath(); + Path generatedContents = temp.newFolder().toPath(); + ZipUtils.unzip(BINARY_COMPATIBILITY_JAR, binaryContents); + ZipUtils.unzip(generateJar(), generatedContents); + try (Stream<Path> existingPaths = Files.walk(binaryContents); + Stream<Path> generatedPaths = Files.walk(generatedContents)) { + List<Path> existing = existingPaths.filter(this::isClassFile).collect(Collectors.toList()); + List<Path> generated = generatedPaths.filter(this::isClassFile).collect(Collectors.toList()); + assertEquals(existing.size(), generated.size()); + assertNotEquals(0, existing.size()); + for (Path classFile : generated) { + Path otherClassFile = binaryContents.resolve(generatedContents.relativize(classFile)); + assertTrue(Files.exists(otherClassFile)); + assertTrue(TestBase.filesAreEqual(classFile, otherClassFile)); + } + } + } + + private boolean isClassFile(Path path) { + return path.toString().endsWith(".class"); + } + + @Test + public void runCheckedInBinaryJar() throws Exception { + for (CfRuntime cfRuntime : CfRuntime.getCheckedInCfRuntimes()) { + RetraceApiTestHelper.runJunitOnTests( + cfRuntime, + ToolHelper.R8_RETRACE_JAR, + BINARY_COMPATIBILITY_JAR, + RetraceApiTestHelper.getBinaryCompatibilityTests()); + } + } + + public static void main(String[] args) throws Exception { + TemporaryFolder temp = new TemporaryFolder(); + temp.create(); + Path generatedJar = + RetraceApiTestHelper.generateJarForRetraceBinaryTests( + temp, RetraceApiTestHelper.getBinaryCompatibilityTests()); + Files.move(generatedJar, BINARY_COMPATIBILITY_JAR, REPLACE_EXISTING); + } +}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiBinaryTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiBinaryTest.java new file mode 100644 index 0000000..eac71c5 --- /dev/null +++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiBinaryTest.java
@@ -0,0 +1,7 @@ +// Copyright (c) 2021, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.retrace.api; + +public interface RetraceApiBinaryTest {}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiEmptyTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiEmptyTest.java new file mode 100644 index 0000000..45df95c --- /dev/null +++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiEmptyTest.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.api; + +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.DiagnosticsHandler; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.references.Reference; +import com.android.tools.r8.retrace.ProguardMapProducer; +import com.android.tools.r8.retrace.RetracedClassReference; +import com.android.tools.r8.retrace.Retracer; +import java.util.ArrayList; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class RetraceApiEmptyTest extends RetraceApiTestBase { + + public RetraceApiEmptyTest(TestParameters parameters) { + super(parameters); + } + + @Override + protected Class<? extends RetraceApiBinaryTest> binaryTestClass() { + return RetraceTest.class; + } + + public static class RetraceTest implements RetraceApiBinaryTest { + + @Test + public void testNone() throws Exception { + String expected = "hello.World"; + List<RetracedClassReference> retracedClasses = new ArrayList<>(); + Retracer.createDefault(ProguardMapProducer.fromString(""), new DiagnosticsHandler() {}) + .retraceClass(Reference.classFromTypeName(expected)) + .stream() + .forEach( + result -> { + retracedClasses.add(result.getRetracedClass()); + }); + assertEquals(1, retracedClasses.size()); + RetracedClassReference retracedClass = retracedClasses.get(0); + assertEquals(retracedClass.getTypeName(), expected); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestBase.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestBase.java new file mode 100644 index 0000000..a58102a --- /dev/null +++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestBase.java
@@ -0,0 +1,53 @@ +// 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.api; + +import static com.android.tools.r8.retrace.api.RetraceApiTestHelper.runJunitOnTests; +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersBuilder; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.ToolHelper; +import java.nio.file.Files; +import org.junit.Assume; +import org.junit.Test; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runner.notification.Failure; +import org.junit.runners.Parameterized.Parameters; + +public abstract class RetraceApiTestBase extends TestBase { + + private final TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return TestParametersBuilder.builder().withSystemRuntime().build(); + } + + public RetraceApiTestBase(TestParameters parameters) { + this.parameters = parameters; + } + + protected abstract Class<? extends RetraceApiBinaryTest> binaryTestClass(); + + @Test + public void testDirect() { + Result result = JUnitCore.runClasses(binaryTestClass()); + for (Failure failure : result.getFailures()) { + System.out.println(failure.toString()); + } + assertTrue(result.wasSuccessful()); + } + + @Test + public void testRetraceLib() throws Exception { + Assume.assumeTrue(Files.exists(ToolHelper.R8_RETRACE_JAR)); + runJunitOnTests( + parameters.getRuntime().asCf(), ToolHelper.R8_RETRACE_JAR, binaryTestClass(), temp); + } +}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestHelper.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestHelper.java new file mode 100644 index 0000000..9b2f67d --- /dev/null +++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestHelper.java
@@ -0,0 +1,117 @@ +// 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.api; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.Collectors; +import com.android.tools.r8.TestRuntime.CfRuntime; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.ToolHelper.ProcessResult; +import com.android.tools.r8.transformers.ClassFileTransformer; +import com.android.tools.r8.transformers.ClassFileTransformer.InnerClassPredicate; +import com.android.tools.r8.utils.DescriptorUtils; +import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.ZipUtils; +import com.android.tools.r8.utils.ZipUtils.ZipBuilder; +import com.google.common.collect.ImmutableList; +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import org.junit.rules.TemporaryFolder; + +public class RetraceApiTestHelper { + + private static final String JUNIT_JAR = "junit-4.13-beta-2.jar"; + private static final String HAMCREST = "hamcrest-core-1.3.jar"; + + public static List<Class<? extends RetraceApiBinaryTest>> CLASSES_FOR_BINARY_COMPATIBILITY = + ImmutableList.of(RetraceApiEmptyTest.RetraceTest.class); + public static List<Class<? extends RetraceApiBinaryTest>> CLASSES_PENDING_BINARY_COMPATIBILITY = + ImmutableList.of(); + + public static void runJunitOnTests( + CfRuntime runtime, + Path r8Jar, + Class<? extends RetraceApiBinaryTest> clazz, + TemporaryFolder temp) + throws Exception { + assertTrue(testIsSpecifiedAsBinaryOrPending(clazz)); + List<Class<? extends RetraceApiBinaryTest>> testClasses = ImmutableList.of(clazz); + runJunitOnTests( + runtime, r8Jar, generateJarForRetraceBinaryTests(temp, testClasses), testClasses); + } + + public static void runJunitOnTests( + CfRuntime runtime, + Path r8Jar, + Path testJar, + Collection<Class<? extends RetraceApiBinaryTest>> tests) + throws Exception { + List<Path> classPaths = ImmutableList.of(getJunitDependency(), getHamcrest(), r8Jar, testJar); + ProcessResult processResult = + ToolHelper.runJava( + runtime, + classPaths, + "org.junit.runner.JUnitCore", + StringUtils.join(" ", tests, Class::getTypeName)); + assertEquals(0, processResult.exitCode); + assertThat(processResult.stdout, containsString("OK (" + tests.size() + " test")); + } + + private static Path getJunitDependency() { + String junitPath = + Arrays.stream(System.getProperty("java.class.path").split(":")) + .filter(cp -> cp.endsWith(JUNIT_JAR)) + .collect(Collectors.toSingle()); + return Paths.get(junitPath); + } + + private static Path getHamcrest() { + String junitPath = + Arrays.stream(System.getProperty("java.class.path").split(":")) + .filter(cp -> cp.endsWith(HAMCREST)) + .collect(Collectors.toSingle()); + return Paths.get(junitPath); + } + + public static Path generateJarForRetraceBinaryTests( + TemporaryFolder temp, Collection<Class<? extends RetraceApiBinaryTest>> classes) + throws Exception { + Path jar = File.createTempFile("retrace_api_tests", ".jar", temp.getRoot()).toPath(); + ZipBuilder zipBuilder = ZipBuilder.builder(jar); + for (Class<? extends RetraceApiBinaryTest> retraceApiTest : classes) { + zipBuilder.addFilesRelative( + ToolHelper.getClassPathForTests(), + ToolHelper.getClassFilesForInnerClasses(retraceApiTest)); + zipBuilder.addBytes( + ZipUtils.zipEntryNameForClass(retraceApiTest), + ClassFileTransformer.create(retraceApiTest) + .removeInnerClasses( + InnerClassPredicate.onName( + DescriptorUtils.getBinaryNameFromJavaType(retraceApiTest.getTypeName()))) + .transform()); + } + zipBuilder.addFilesRelative( + ToolHelper.getClassPathForTests(), + ToolHelper.getClassFileForTestClass(RetraceApiBinaryTest.class)); + return zipBuilder.build(); + } + + public static Collection<Class<? extends RetraceApiBinaryTest>> getBinaryCompatibilityTests() { + return CLASSES_FOR_BINARY_COMPATIBILITY; + } + + private static boolean testIsSpecifiedAsBinaryOrPending(Class<?> clazz) { + return CLASSES_FOR_BINARY_COMPATIBILITY.contains(clazz) + || CLASSES_PENDING_BINARY_COMPATIBILITY.contains(clazz); + } +}
diff --git a/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java b/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java index 4a8306a..801d678 100644 --- a/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java +++ b/src/test/java/com/android/tools/r8/shaking/NonVirtualOverrideTest.java
@@ -4,9 +4,9 @@ package com.android.tools.r8.shaking; +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.isPresentAndRenamed; -import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; @@ -105,9 +105,9 @@ "In C.m3()", "In A.m4()", "In A.m1()", // With Java: Caught IllegalAccessError when calling B.m1() - "In A.m3()", // With Java: Caught IncompatibleClassChangeError when calling B.m3() + "Caught IncompatibleClassChangeError when calling B.m3()", "In C.m1()", // With Java: Caught IllegalAccessError when calling B.m1() - "In C.m3()", // With Java: Caught IncompatibleClassChangeError when calling B.m3() + "Caught IncompatibleClassChangeError when calling B.m3()", "In C.m1()", "In C.m3()", ""); @@ -178,9 +178,11 @@ ClassSubject classSubject = inspector.clazz(B.class.getName()); assertThat(classSubject, isPresentAndRenamed()); assertThat(classSubject.method("void", "m1", ImmutableList.of()), isPresent()); - assertThat(classSubject.method("void", "m2", ImmutableList.of()), not(isPresent())); - assertThat(classSubject.method("void", "m3", ImmutableList.of()), isPresent()); - assertThat(classSubject.method("void", "m4", ImmutableList.of()), not(isPresent())); + assertThat(classSubject.method("void", "m2", ImmutableList.of()), isAbsent()); + assertThat( + classSubject.method("void", "m3", ImmutableList.of()), + parameters.isCfRuntime() ? isPresent() : isAbsent()); + assertThat(classSubject.method("void", "m4", ImmutableList.of()), isAbsent()); } } @@ -263,7 +265,8 @@ System.out.println("In B.m2()"); } - // Made static in the dump below. This method is targeted and can therefore not be removed. + // Made static in the dump below. Ends up dead as the targeting call is replaced by throw ICCE. + // Except in non-desugaring CF the method will remain instead of inserting a stub. @Override public void m3() { System.out.println("In B.m3()");
diff --git a/src/test/java/com/android/tools/r8/shaking/ReturnTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ReturnTypeTest.java index 98b7f80..82e9d69 100644 --- a/src/test/java/com/android/tools/r8/shaking/ReturnTypeTest.java +++ b/src/test/java/com/android/tools/r8/shaking/ReturnTypeTest.java
@@ -85,7 +85,7 @@ // is // always null, and replace the last call with null-throwing instruction. // However, we want to test return type and parameter type are kept in this scenario. - o.callSiteOptimizationOptions().disableTypePropagationForTesting(); + o.callSiteOptimizationOptions().disableDynamicTypePropagationForTesting(); o.enableInlining = false; }) .run(parameters.getRuntime(), MAIN)
diff --git a/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java b/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java index b427736..29960f9 100644 --- a/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java +++ b/src/test/java/com/android/tools/r8/shaking/clinit/ClassInitializationTriggersIndirectInterfaceInitializationTest.java
@@ -4,8 +4,8 @@ package com.android.tools.r8.shaking.clinit; +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.assertEquals; import static org.junit.Assert.assertTrue; @@ -51,22 +51,27 @@ .compile() .inspect( inspector -> { - // Verify that I's class initializer is still present. ClassSubject iClassSubject = inspector.clazz(I.class); - assertThat(iClassSubject, isPresent()); - assertThat( - iClassSubject.clinit(), - onlyIf(hasDefaultInterfaceMethodsSupport(parameters), isPresent())); - - // Verify that J is still there. ClassSubject jClassSubject = inspector.clazz(J.class); - assertThat(jClassSubject, isPresent()); - - // Verify that A still implements J. ClassSubject aClassSubject = inspector.clazz(A.class); - assertThat(aClassSubject, isPresent()); - assertEquals(1, aClassSubject.getDexProgramClass().getInterfaces().size()); - assertTrue(aClassSubject.isImplementing(jClassSubject)); + if (hasDefaultInterfaceMethodsSupport(parameters)) { + // Verify that I's class initializer is still present. + assertThat(iClassSubject, isPresent()); + assertThat(iClassSubject.clinit(), isPresent()); + + // Verify that J is still there. + assertThat(jClassSubject, isPresent()); + + // Verify that A still implements J. + assertThat(aClassSubject, isPresent()); + assertEquals(1, aClassSubject.getDexProgramClass().getInterfaces().size()); + assertTrue(aClassSubject.isImplementing(jClassSubject)); + } else { + // All interfaces are gone and the default methods companion call is inlined. + assertThat(iClassSubject, isAbsent()); + assertThat(jClassSubject, isAbsent()); + assertThat(aClassSubject, isAbsent()); + } }) .run(parameters.getRuntime(), Main.class) .assertSuccessWithOutputLinesIf(
diff --git a/src/test/java/com/android/tools/r8/shaking/desugar/KeepRuleWarningTest.java b/src/test/java/com/android/tools/r8/shaking/desugar/KeepRuleWarningTest.java index c2be7a8..02dfda4 100644 --- a/src/test/java/com/android/tools/r8/shaking/desugar/KeepRuleWarningTest.java +++ b/src/test/java/com/android/tools/r8/shaking/desugar/KeepRuleWarningTest.java
@@ -3,8 +3,6 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.shaking.desugar; -import static org.hamcrest.CoreMatchers.containsString; - import com.android.tools.r8.NoVerticalClassMerging; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestDiagnosticMessages; @@ -106,14 +104,8 @@ .enableNoVerticalClassMergingAnnotations() .addKeepMainRule(MAIN) .addKeepRules("-keep interface **.I { static void foo(); }") - .allowDiagnosticWarningMessages() .compile() - .inspectDiagnosticMessages( - m -> - m.assertWarningsCount(1) - .assertWarningMessageThatMatches(containsString("static void foo()")) - .assertWarningMessageThatMatches(containsString("is ignored")) - .assertWarningMessageThatMatches(containsString("will be desugared"))) + .inspectDiagnosticMessages(TestDiagnosticMessages::assertNoMessages) .run(parameters.getRuntime(), MAIN) .assertSuccessWithOutput(EXPECTED_OUTPUT); }
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/interfacemethoddesugaring/IfRuleWithInterfaceMethodDesugaringTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/interfacemethoddesugaring/IfRuleWithInterfaceMethodDesugaringTest.java index 19b87e4..01d977a 100644 --- a/src/test/java/com/android/tools/r8/shaking/ifrule/interfacemethoddesugaring/IfRuleWithInterfaceMethodDesugaringTest.java +++ b/src/test/java/com/android/tools/r8/shaking/ifrule/interfacemethoddesugaring/IfRuleWithInterfaceMethodDesugaringTest.java
@@ -4,7 +4,6 @@ package com.android.tools.r8.shaking.ifrule.interfacemethoddesugaring; -import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringForTesting.getCompanionClassNameSuffix; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPublic; import static com.android.tools.r8.utils.codeinspector.Matchers.isStatic; @@ -12,6 +11,8 @@ import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; import com.android.tools.r8.NeverClassInline; import com.android.tools.r8.NeverInline; @@ -34,9 +35,17 @@ private final TestParameters parameters; + private static final String STATIC_STR = "In Interface.staticMethod()"; + private static final String VIRTUAL_STR = "In Interface.virtualMethod()"; + private static final String EXPECTED_OUTPUT = StringUtils.lines(STATIC_STR, VIRTUAL_STR); + @Parameters(name = "{0}") public static TestParametersCollection data() { - return getTestParameters().withDexRuntimes().withApiLevel(AndroidApiLevel.M).build(); + return getTestParameters() + .withCfRuntimes() + .withDexRuntimes() + .withApiLevel(AndroidApiLevel.M) + .build(); } public IfRuleWithInterfaceMethodDesugaringTest(TestParameters parameters) { @@ -44,50 +53,64 @@ } @Test + public void testReference() throws Exception { + assumeTrue(parameters.isCfRuntime()); + testForJvm() + .addTestClasspath() + .run(parameters.getRuntime(), TestClass.class) + .assertSuccessWithOutput(EXPECTED_OUTPUT); + } + + @Test public void test() throws Exception { - String expectedOutput = - StringUtils.lines("In Interface.staticMethod()", "In Interface.virtualMethod()"); - - testForJvm().addTestClasspath().run(TestClass.class).assertSuccessWithOutput(expectedOutput); - CodeInspector inspector = testForR8(parameters.getBackend()) .addInnerClasses(IfRuleWithInterfaceMethodDesugaringTest.class) .addKeepMainRule(TestClass.class) .addKeepRules( "-if class " + Interface.class.getTypeName() + " {", - " !public static void staticMethod();", + " static void staticMethod();", "}", "-keep class " + Unused1.class.getTypeName(), "-if class " + Interface.class.getTypeName() + " {", - " !public !static void virtualMethod();", + " !static void virtualMethod();", "}", "-keep class " + Unused2.class.getTypeName()) - .allowUnusedProguardConfigurationRules() + .allowUnusedProguardConfigurationRules(parameters.isDexRuntime()) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoVerticalClassMergingAnnotations() .setMinApi(parameters.getApiLevel()) - .compile() .run(parameters.getRuntime(), TestClass.class) - .assertSuccessWithOutput(expectedOutput) + .assertSuccessWithOutput(EXPECTED_OUTPUT) .inspector(); - ClassSubject classSubject = - inspector.clazz(Interface.class.getTypeName() + getCompanionClassNameSuffix()); - assertThat(classSubject, isPresent()); - assertEquals(2, classSubject.allMethods().size()); + if (parameters.isCfRuntime()) { + ClassSubject itfClass = inspector.clazz(Interface.class.getTypeName()); + assertThat(itfClass, isPresent()); + assertThat(itfClass.uniqueMethodWithName("staticMethod"), isPresent()); + assertThat(itfClass.uniqueMethodWithName("virtualMethod"), isPresent()); + assertThat(inspector.clazz(Unused1.class), isPresent()); + assertThat(inspector.clazz(Unused2.class), isPresent()); + return; + } + ClassSubject classSubject = inspector.clazz(Interface.class.getTypeName()).toCompanionClass(); + assertThat(classSubject, isPresent()); + + // NeverInline is only applicable to the static method at this point (could change). + assertEquals(1, classSubject.allMethods().size()); MethodSubject staticMethodSubject = classSubject.uniqueMethodWithName("staticMethod"); assertThat(staticMethodSubject, allOf(isPresent(), isPublic(), isStatic())); + assertTrue(staticMethodSubject.streamInstructions().anyMatch(i -> i.isConstString(STATIC_STR))); - // TODO(b/120764902): MethodSubject.getOriginalName() not working in presence of desugaring. - MethodSubject virtualMethodSubject = - classSubject.allMethods().stream() - .filter(subject -> subject != staticMethodSubject) - .findFirst() - .get(); - assertThat(virtualMethodSubject, allOf(isPresent(), isPublic(), isStatic())); + // The virtual method is inlined as @NeverInline does not apply at this point (could change). + assertTrue( + inspector + .clazz(TestClass.class) + .mainMethod() + .streamInstructions() + .anyMatch(i -> i.isConstString(VIRTUAL_STR))); // TODO(b/122875545): The Unused class should be present due to the -if rule. assertThat(inspector.clazz(Unused1.class), not(isPresent()));
diff --git a/src/test/java/com/android/tools/r8/shaking/staticinterfacemethods/defaultmethods/StaticInterfaceMethodsTest.java b/src/test/java/com/android/tools/r8/shaking/staticinterfacemethods/defaultmethods/StaticInterfaceMethodsTest.java index ca0aaa5..e9af866 100644 --- a/src/test/java/com/android/tools/r8/shaking/staticinterfacemethods/defaultmethods/StaticInterfaceMethodsTest.java +++ b/src/test/java/com/android/tools/r8/shaking/staticinterfacemethods/defaultmethods/StaticInterfaceMethodsTest.java
@@ -107,7 +107,7 @@ assertFalse(clazz.method("int", "method", ImmutableList.of()).isPresent()); } - private void staticMethodKeptB159987443(CodeInspector inspector) { + private void staticMethodKept(CodeInspector inspector) { ClassSubject clazz = inspector.clazz(InterfaceWithStaticMethods.class); assertThat(clazz, isPresent()); MethodSubject method = clazz.method("int", "method", ImmutableList.of()); @@ -117,13 +117,12 @@ assertThat(companionClass, not(isPresent())); } else { assertThat(method, not(isPresent())); - // TODO(159987443): The companion class should be present. - assertThat(companionClass, not(isPresent())); - // Also check that method exists on companion class. + assertThat(companionClass, isPresent()); + assertThat(companionClass.uniqueMethodWithName("method"), isPresent()); } } - private void staticMethodKept(CodeInspector inspector) { + private void staticMethodKeptB160142903(CodeInspector inspector) { ClassSubject clazz = inspector.clazz(InterfaceWithStaticMethods.class); ClassSubject companionClass = clazz.toCompanionClass(); MethodSubject method = clazz.method("int", "method", ImmutableList.of()); @@ -136,7 +135,7 @@ // after desugaring, only the companion class is left. assertThat(clazz, not(isPresent())); assertThat(method, not(isPresent())); - // TODO(159987443): The companion class should be present. + // TODO(160142903): The companion class should be present. assertThat(companionClass, not(isPresent())); // Also check that method exists on companion class. } @@ -168,7 +167,7 @@ "-keep interface " + InterfaceWithStaticMethods.class.getTypeName() + "{", " <methods>;", "}"), - this::staticMethodKeptB159987443); + this::staticMethodKept); } @Test @@ -196,7 +195,7 @@ "}"); } } - runTest(builder.build(), this::staticMethodKept); + runTest(builder.build(), this::staticMethodKeptB160142903); } public static class TestClass {
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 6ffbce9..92774e1 100644 --- a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java +++ b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -21,9 +21,12 @@ } public static ClassReference syntheticCompanionClass(Class<?> clazz) { + return syntheticCompanionClass(Reference.classFromClass(clazz)); + } + + public static ClassReference syntheticCompanionClass(ClassReference clazz) { return Reference.classFromDescriptor( - InterfaceDesugaringForTesting.getCompanionClassDescriptor( - Reference.classFromClass(clazz).getDescriptor())); + InterfaceDesugaringForTesting.getCompanionClassDescriptor(clazz.getDescriptor())); } private static ClassReference syntheticClass(Class<?> clazz, SyntheticKind kind, int id) { @@ -72,7 +75,8 @@ public static boolean isExternalSynthetic(ClassReference reference) { for (SyntheticKind kind : SyntheticKind.values()) { - if (kind == SyntheticKind.RECORD_TAG) { + if (kind == SyntheticKind.RECORD_TAG + || kind == SyntheticKind.EMULATED_INTERFACE_MARKER_CLASS) { continue; } if (kind.isFixedSuffixSynthetic) { @@ -134,4 +138,9 @@ public static Matcher<String> containsExternalSyntheticReference() { return containsString(SyntheticNaming.getPhaseSeparator(Phase.EXTERNAL)); } + + public static boolean isInternalThrowNSME(MethodReference method) { + return SyntheticNaming.isSynthetic( + method.getHolderClass(), Phase.INTERNAL, SyntheticKind.THROW_NSME); + } }
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java index 2bbcf4a..bc72103 100644 --- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java +++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.transformers; import static com.android.tools.r8.references.Reference.classFromTypeName; +import static com.android.tools.r8.transformers.ClassFileTransformer.InnerClassPredicate.always; import static com.android.tools.r8.utils.DescriptorUtils.getBinaryNameFromDescriptor; import static com.android.tools.r8.utils.StringUtils.replaceAll; import static org.objectweb.asm.Opcodes.ASM7; @@ -31,6 +32,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; @@ -556,12 +558,31 @@ boolean test(String name, String typeDescriptor); } + @FunctionalInterface + public interface InnerClassPredicate { + boolean test(String name, String outerName, String innerName, int access); + + static InnerClassPredicate always() { + return (name, outerName, innerName, access) -> true; + } + + static InnerClassPredicate onName(String name) { + return (otherName, outerName, innerName, access) -> Objects.equals(name, otherName); + } + } + public ClassFileTransformer removeInnerClasses() { + return removeInnerClasses(always()); + } + + public ClassFileTransformer removeInnerClasses(InnerClassPredicate predicate) { return addClassTransformer( new ClassTransformer() { @Override public void visitInnerClass(String name, String outerName, String innerName, int access) { - // Intentionally empty. + if (!predicate.test(name, outerName, innerName, access)) { + super.visitInnerClass(name, outerName, innerName, access); + } } }); }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassOrMemberSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassOrMemberSubject.java index bca691f..2dc0774 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassOrMemberSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassOrMemberSubject.java
@@ -43,6 +43,7 @@ return getAccessFlags().isStatic(); } + @Override public final boolean isSynthetic() { return getAccessFlags().isSynthetic(); }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java index f552a25..b100332 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -4,8 +4,6 @@ package com.android.tools.r8.utils.codeinspector; -import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringForTesting.getCompanionClassNameSuffix; - import com.android.tools.r8.graph.ClassAccessFlags; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProgramClass; @@ -13,11 +11,11 @@ import com.android.tools.r8.naming.MemberNaming.MethodSignature; import com.android.tools.r8.references.ClassReference; import com.android.tools.r8.references.MethodReference; -import com.android.tools.r8.references.Reference; import com.android.tools.r8.references.TypeReference; import com.android.tools.r8.retrace.RetraceClassElement; import com.android.tools.r8.retrace.RetraceClassResult; import com.android.tools.r8.smali.SmaliBuilder; +import com.android.tools.r8.synthesis.SyntheticItemsTestUtils; import com.android.tools.r8.utils.ListUtils; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableList; @@ -231,12 +229,7 @@ public abstract KotlinClassMetadata getKotlinClassMetadata(); public ClassSubject toCompanionClass() { - String descriptor = reference.getDescriptor(); - return codeInspector.clazz( - Reference.classFromDescriptor( - descriptor.substring(0, descriptor.length() - 1) - + getCompanionClassNameSuffix() - + ";")); + return codeInspector.clazz(SyntheticItemsTestUtils.syntheticCompanionClass(reference)); } public abstract RetraceClassResult retrace();
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java index 77c68b3..76d76e5 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -3,8 +3,6 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.utils.codeinspector; -import static com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringForTesting.getCompanionClassNameSuffix; - import com.android.tools.r8.DexIndexedConsumer; import com.android.tools.r8.StringResource; import com.android.tools.r8.TestDiagnosticMessagesImpl; @@ -40,6 +38,7 @@ import com.android.tools.r8.retrace.Retracer; import com.android.tools.r8.retrace.internal.DirectClassNameMapperProguardMapProducer; import com.android.tools.r8.retrace.internal.RetracerImpl; +import com.android.tools.r8.synthesis.SyntheticItemsTestUtils; import com.android.tools.r8.utils.AndroidApp; import com.android.tools.r8.utils.BiMapContainer; import com.android.tools.r8.utils.DescriptorUtils; @@ -328,7 +327,7 @@ } public ClassSubject companionClassFor(Class<?> clazz) { - return clazz(Reference.classFromTypeName(clazz.getTypeName() + getCompanionClassNameSuffix())); + return clazz(SyntheticItemsTestUtils.syntheticCompanionClass(clazz)); } public void forAllClasses(Consumer<FoundClassSubject> inspection) {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java index cbbbb14..edeed5a 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
@@ -25,6 +25,7 @@ import com.android.tools.r8.utils.Visibility; import com.google.common.collect.ImmutableList; import java.util.List; +import java.util.function.Predicate; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; @@ -84,15 +85,24 @@ } public static Matcher<MethodSubject> isBridge() { + return isMethodSatisfying("bridge", FoundMethodSubject::isBridge); + } + + public static Matcher<MethodSubject> isInstanceInitializer() { + return isMethodSatisfying("instance initializer", FoundMethodSubject::isInstanceInitializer); + } + + private static Matcher<MethodSubject> isMethodSatisfying( + String helperText, Predicate<FoundMethodSubject> predicate) { return new TypeSafeMatcher<MethodSubject>() { @Override protected boolean matchesSafely(MethodSubject subject) { - return subject.isPresent() && subject.isBridge(); + return subject.isPresent() && predicate.test(subject.asFoundMethodSubject()); } @Override public void describeTo(Description description) { - description.appendText(" bridge"); + description.appendText(" " + helperText); } @Override
diff --git a/third_party/retrace/binary_compatibility.tar.gz.sha1 b/third_party/retrace/binary_compatibility.tar.gz.sha1 new file mode 100644 index 0000000..3825d93 --- /dev/null +++ b/third_party/retrace/binary_compatibility.tar.gz.sha1
@@ -0,0 +1 @@ +63dd09b086d0d134219a379720c6b68a94afdf48 \ No newline at end of file