[ApiModel] Outline NewInstance and <init> to new library classes
Bug: b/243883589
Change-Id: Iefe0a9851d546b0a2c16a12fdde3a79c676ddecc
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 c62d786..e323b6b 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
@@ -62,6 +62,7 @@
import com.android.tools.r8.ir.optimize.IdempotentFunctionCallCanonicalizer;
import com.android.tools.r8.ir.optimize.Inliner;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
+import com.android.tools.r8.ir.optimize.InstanceInitializerOutliner;
import com.android.tools.r8.ir.optimize.NaturalIntLoopRemover;
import com.android.tools.r8.ir.optimize.RedundantFieldLoadAndStoreElimination;
import com.android.tools.r8.ir.optimize.ReflectionOptimizer;
@@ -143,6 +144,7 @@
private final ServiceLoaderRewriter serviceLoaderRewriter;
private final EnumValueOptimizer enumValueOptimizer;
private final EnumUnboxer enumUnboxer;
+ private final InstanceInitializerOutliner instanceInitializerOutliner;
public final AssumeInserter assumeInserter;
private final DynamicTypeOptimization dynamicTypeOptimization;
@@ -227,6 +229,7 @@
this.enumValueOptimizer = null;
this.enumUnboxer = EnumUnboxer.empty();
this.assumeInserter = null;
+ this.instanceInitializerOutliner = null;
return;
}
this.instructionDesugaring =
@@ -237,6 +240,12 @@
options.processCovariantReturnTypeAnnotations
? new CovariantReturnTypeAnnotationTransformer(this, appView.dexItemFactory())
: null;
+ if (appView.options().desugarState.isOn()
+ && appView.options().apiModelingOptions().enableOutliningOfMethods) {
+ this.instanceInitializerOutliner = new InstanceInitializerOutliner(appView);
+ } else {
+ this.instanceInitializerOutliner = null;
+ }
if (appView.enableWholeProgramOptimizations()) {
assert appView.appInfo().hasLiveness();
assert appView.rootSet() != null;
@@ -358,6 +367,10 @@
reportNestDesugarDependencies();
clearNestAttributes();
+ if (instanceInitializerOutliner != null) {
+ processSimpleSynthesizeMethods(instanceInitializerOutliner.getSynthesizedMethods(), executor);
+ }
+
application = commitPendingSyntheticItemsD8(appView, application);
postProcessingDesugaringForD8(methodProcessor, interfaceProcessor, executor);
@@ -780,10 +793,15 @@
builder.setHighestSortingString(highestSortingString);
if (serviceLoaderRewriter != null) {
- processSynthesizedServiceLoaderMethods(
+ processSimpleSynthesizeMethods(
serviceLoaderRewriter.getServiceLoadMethods(), executorService);
}
+ if (instanceInitializerOutliner != null) {
+ processSimpleSynthesizeMethods(
+ instanceInitializerOutliner.getSynthesizedMethods(), executorService);
+ }
+
// Update optimization info for all synthesized methods at once.
feedback.updateVisibleOptimizationInfo();
@@ -865,14 +883,14 @@
return onWaveDoneActions != null;
}
- private void processSynthesizedServiceLoaderMethods(
+ private void processSimpleSynthesizeMethods(
List<ProgramMethod> serviceLoadMethods, ExecutorService executorService)
throws ExecutionException {
ThreadUtils.processItems(
- serviceLoadMethods, this::forEachSynthesizedServiceLoaderMethod, executorService);
+ serviceLoadMethods, this::processAndFinalizeSimpleSynthesiedMethod, executorService);
}
- private void forEachSynthesizedServiceLoaderMethod(ProgramMethod method) {
+ private void processAndFinalizeSimpleSynthesiedMethod(ProgramMethod method) {
IRCode code = method.buildIR(appView);
assert code != null;
codeRewriter.rewriteMoveResult(code);
@@ -1208,6 +1226,11 @@
timing.end();
}
+ if (instanceInitializerOutliner != null) {
+ instanceInitializerOutliner.rewriteInstanceInitializers(
+ code, context, methodProcessingContext);
+ }
+
previous = printMethod(code, "IR after disable assertions (SSA)", previous);
// Update the IR code if collected call site optimization info has something useful.
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
index 6137206..67ec80a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
@@ -41,7 +41,7 @@
}
case INVOKE_CONSTRUCTOR:
{
- forwardMethodBuilder.setConstructorTarget(target, appView.dexItemFactory());
+ forwardMethodBuilder.setConstructorTargetWithNewInstance(target);
break;
}
default:
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InstanceInitializerOutliner.java b/src/main/java/com/android/tools/r8/ir/optimize/InstanceInitializerOutliner.java
new file mode 100644
index 0000000..32bff94
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InstanceInitializerOutliner.java
@@ -0,0 +1,216 @@
+// Copyright (c) 2022, 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.optimize;
+
+import com.android.tools.r8.androidapi.ComputedApiLevel;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.InvokeStatic;
+import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
+import com.android.tools.r8.ir.synthetic.NewInstanceSourceCode;
+import com.android.tools.r8.shaking.ComputeApiLevelUseRegistry;
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The InstanceInitializerOutliner will outline instance initializers and their NewInstance source.
+ * Unlike the ApiInvokeOutlinerDesugaring that works on CF, this works on IR to properly replace the
+ * users of the NewInstance call.
+ */
+public class InstanceInitializerOutliner {
+
+ private final AppView<?> appView;
+ private final DexItemFactory factory;
+
+ private final List<ProgramMethod> synthesizedMethods = new ArrayList<>();
+
+ public InstanceInitializerOutliner(AppView<?> appView) {
+ this.appView = appView;
+ this.factory = appView.dexItemFactory();
+ }
+
+ public List<ProgramMethod> getSynthesizedMethods() {
+ return synthesizedMethods;
+ }
+
+ public void rewriteInstanceInitializers(
+ IRCode code, ProgramMethod context, MethodProcessingContext methodProcessingContext) {
+ // Do not outline from already synthesized methods.
+ if (context.getDefinition().isD8R8Synthesized()) {
+ return;
+ }
+ Map<NewInstance, Value> rewrittenNewInstances = new IdentityHashMap<>();
+ ComputedApiLevel minApiLevel = appView.computedMinApiLevel();
+ InstructionListIterator iterator = code.instructionListIterator();
+ // Scan over the code to find <init> calls that needs to be outlined.
+ while (iterator.hasNext()) {
+ Instruction instruction = iterator.next();
+ InvokeDirect invokeDirect = instruction.asInvokeDirect();
+ if (invokeDirect == null) {
+ continue;
+ }
+ DexMethod invokedConstructor = invokeDirect.getInvokedMethod();
+ if (!invokedConstructor.isInstanceInitializer(factory)) {
+ continue;
+ }
+ NewInstance newInstance = invokeDirect.getFirstOperand().getDefinition().asNewInstance();
+ if (newInstance == null) {
+ // We could not find a new instance call associated with the init, this is probably a
+ // constructor call to the super class.
+ continue;
+ }
+ ComputedApiLevel apiReferenceLevel =
+ appView
+ .apiLevelCompute()
+ .computeApiLevelForLibraryReference(invokedConstructor, minApiLevel);
+ if (minApiLevel.isGreaterThanOrEqualTo(apiReferenceLevel)) {
+ continue;
+ }
+ DexEncodedMethod synthesizedInstanceInitializer =
+ createSynthesizedInstanceInitializer(
+ invokeDirect.getInvokedMethod(), apiReferenceLevel, methodProcessingContext);
+ List<Value> arguments = instruction.inValues();
+ InvokeStatic outlinedMethodInvoke =
+ InvokeStatic.builder()
+ .setMethod(synthesizedInstanceInitializer.getReference())
+ .setPosition(instruction)
+ .setFreshOutValue(code, newInstance.getOutType())
+ .setArguments(arguments.subList(1, arguments.size()))
+ .build();
+ iterator.replaceCurrentInstruction(outlinedMethodInvoke);
+ rewrittenNewInstances.put(newInstance, outlinedMethodInvoke.outValue());
+ }
+ if (rewrittenNewInstances.isEmpty()) {
+ return;
+ }
+ // Scan over NewInstance calls that needs to be outlined. We insert a call to a synthetic method
+ // with a NewInstance to preserve class-init semantics.
+ // TODO(b/244284945): If we know that arguments to an init cannot change class initializer
+ // semantics we can avoid inserting the NewInstance outline.
+ iterator = code.instructionListIterator();
+ while (iterator.hasNext()) {
+ Instruction instruction = iterator.next();
+ if (!instruction.isNewInstance()) {
+ continue;
+ }
+ NewInstance newInstance = instruction.asNewInstance();
+ Value newOutlineInstanceValue = rewrittenNewInstances.get(newInstance);
+ if (newOutlineInstanceValue == null) {
+ continue;
+ }
+ ComputedApiLevel classApiLevel =
+ appView
+ .apiLevelCompute()
+ .computeApiLevelForLibraryReference(newInstance.getType(), minApiLevel);
+ assert classApiLevel.isKnownApiLevel();
+ DexEncodedMethod synthesizedNewInstance =
+ createSynthesizedNewInstance(
+ newInstance.getType(), classApiLevel, methodProcessingContext);
+ InvokeStatic outlinedStaticInit =
+ InvokeStatic.builder()
+ .setMethod(synthesizedNewInstance.getReference())
+ .setPosition(instruction)
+ .build();
+ newInstance.outValue().replaceUsers(newOutlineInstanceValue);
+ iterator.replaceCurrentInstruction(outlinedStaticInit);
+ }
+ // Outlining of instance initializers will in most cases change the api level of the context
+ // since all other soft verification issues has been outlined. To ensure that we do not inline
+ // the outline again in R8 - but allow inlining of other calls to min api level methods, we have
+ // to recompute the api level.
+ recomputeApiLevel(context, code);
+ }
+
+ private void recomputeApiLevel(ProgramMethod context, IRCode code) {
+ DexEncodedMethod definition = context.getDefinition();
+ if (!definition.getApiLevelForCode().isKnownApiLevel()) {
+ // This is either D8 or the api level is unknown.
+ return;
+ }
+ ComputeApiLevelUseRegistry registry =
+ new ComputeApiLevelUseRegistry(appView, context, appView.apiLevelCompute());
+ code.registerCodeReferences(context, registry);
+ ComputedApiLevel maxApiReferenceLevel = registry.getMaxApiReferenceLevel();
+ assert maxApiReferenceLevel.isKnownApiLevel();
+ definition.setApiLevelForCode(maxApiReferenceLevel);
+ }
+
+ private DexEncodedMethod createSynthesizedNewInstance(
+ DexType targetType,
+ ComputedApiLevel computedApiLevel,
+ MethodProcessingContext methodProcessingContext) {
+ DexProto proto = appView.dexItemFactory().createProto(factory.voidType);
+ ProgramMethod method =
+ appView
+ .getSyntheticItems()
+ .createMethod(
+ kinds -> kinds.API_MODEL_OUTLINE,
+ methodProcessingContext.createUniqueContext(),
+ appView,
+ builder ->
+ builder
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setProto(proto)
+ .setApiLevelForDefinition(appView.computedMinApiLevel())
+ .setApiLevelForCode(computedApiLevel)
+ .setCode(
+ m ->
+ NewInstanceSourceCode.create(appView, m.getHolderType(), targetType)
+ .generateCfCode()));
+ synchronized (synthesizedMethods) {
+ synthesizedMethods.add(method);
+ }
+ return method.getDefinition();
+ }
+
+ private DexEncodedMethod createSynthesizedInstanceInitializer(
+ DexMethod targetMethod,
+ ComputedApiLevel computedApiLevel,
+ MethodProcessingContext methodProcessingContext) {
+ DexProto proto =
+ appView
+ .dexItemFactory()
+ .createProto(targetMethod.getHolderType(), targetMethod.getParameters());
+ ProgramMethod method =
+ appView
+ .getSyntheticItems()
+ .createMethod(
+ kinds -> kinds.API_MODEL_OUTLINE,
+ methodProcessingContext.createUniqueContext(),
+ appView,
+ builder ->
+ builder
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setProto(proto)
+ .setApiLevelForDefinition(appView.computedMinApiLevel())
+ .setApiLevelForCode(computedApiLevel)
+ .setCode(
+ m ->
+ ForwardMethodBuilder.builder(appView.dexItemFactory())
+ .setConstructorTargetWithNewInstance(targetMethod)
+ .setStaticSource(m)
+ .build()));
+
+ synchronized (synthesizedMethods) {
+ synthesizedMethods.add(method);
+ }
+ return method.getDefinition();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/ExceptionThrowingSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/ExceptionThrowingSourceCode.java
deleted file mode 100644
index 080b519..0000000
--- a/src/main/java/com/android/tools/r8/ir/synthetic/ExceptionThrowingSourceCode.java
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.ir.synthetic;
-
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.ir.code.Invoke.Type;
-import com.android.tools.r8.ir.code.Position;
-import com.android.tools.r8.ir.code.ValueType;
-import com.android.tools.r8.ir.conversion.IRBuilder;
-import java.util.Collections;
-
-// Source code representing simple forwarding method.
-public final class ExceptionThrowingSourceCode extends SyntheticSourceCode {
-
- private static final int register = 0;
- private final DexType exceptionType;
-
- public ExceptionThrowingSourceCode(
- DexType receiver, DexMethod method, Position callerPosition, DexType exceptionType) {
- super(receiver, method, callerPosition);
- this.exceptionType = exceptionType;
- }
-
- @Override
- protected void prepareInstructions() {
- add(builder -> build(builder, exceptionType));
- }
-
- public static void build(IRBuilder builder, DexType exceptionType) {
- DexItemFactory factory = builder.appView.dexItemFactory();
- DexProto initProto = factory.createProto(factory.voidType);
- DexMethod initMethod =
- factory.createMethod(exceptionType, initProto, factory.constructorMethodName);
- builder.addNewInstance(register, exceptionType);
- builder.addInvoke(
- Type.DIRECT,
- initMethod,
- initMethod.proto,
- Collections.singletonList(ValueType.OBJECT),
- Collections.singletonList(register),
- false /* isInterface */);
- builder.addThrow(register);
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
index 90065e6..8d50c17 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/ForwardMethodBuilder.java
@@ -142,7 +142,7 @@
return this;
}
- public ForwardMethodBuilder setConstructorTarget(DexMethod method, DexItemFactory factory) {
+ public ForwardMethodBuilder setConstructorTargetWithNewInstance(DexMethod method) {
assert method.isInstanceInitializer(factory);
targetMethod = method;
isConstructorDelegate = true;
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/NewInstanceSourceCode.java b/src/main/java/com/android/tools/r8/ir/synthetic/NewInstanceSourceCode.java
new file mode 100644
index 0000000..02b32bc
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/NewInstanceSourceCode.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2022, 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.synthetic;
+
+import com.android.tools.r8.cf.code.CfInstruction;
+import com.android.tools.r8.cf.code.CfNew;
+import com.android.tools.r8.cf.code.CfReturnVoid;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
+import com.android.tools.r8.graph.DexType;
+import java.util.ArrayList;
+import java.util.List;
+
+// Source code representing a simple call to NewInstance.
+public final class NewInstanceSourceCode extends SyntheticCfCodeProvider {
+
+ private final DexType newInstanceType;
+
+ private NewInstanceSourceCode(AppView<?> appView, DexType holder, DexType newInstanceType) {
+ super(appView, holder);
+ this.newInstanceType = newInstanceType;
+ }
+
+ public static NewInstanceSourceCode create(
+ AppView<?> appView, DexType holder, DexType newInstanceType) {
+ return new NewInstanceSourceCode(appView, holder, newInstanceType);
+ }
+
+ @Override
+ public CfCode generateCfCode() {
+ List<CfInstruction> instructions = new ArrayList<>();
+ instructions.add(new CfNew(newInstanceType));
+ instructions.add(new CfReturnVoid());
+ return standardCfCodeFromInstructions(instructions);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ComputeApiLevelUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/ComputeApiLevelUseRegistry.java
new file mode 100644
index 0000000..89d124e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/ComputeApiLevelUseRegistry.java
@@ -0,0 +1,157 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
+import com.android.tools.r8.dex.code.CfOrDexInstruction;
+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.DexMethodHandle;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.UseRegistry;
+import java.util.ListIterator;
+
+public class ComputeApiLevelUseRegistry extends UseRegistry<ProgramMethod> {
+
+ protected final AppView<?> appView;
+ private final AndroidApiLevelCompute apiLevelCompute;
+ private ComputedApiLevel maxApiReferenceLevel;
+
+ public ComputeApiLevelUseRegistry(
+ AppView<?> appView, ProgramMethod context, AndroidApiLevelCompute apiLevelCompute) {
+ super(appView, context);
+ this.appView = appView;
+ this.apiLevelCompute = apiLevelCompute;
+ maxApiReferenceLevel = appView.computedMinApiLevel();
+ }
+
+ @Override
+ public void registerInitClass(DexType clazz) {
+ // Intentionally empty since init class as synthesized.
+ }
+
+ @Override
+ public void registerInvokeVirtual(DexMethod invokedMethod) {
+ setMaxApiReferenceLevel(invokedMethod);
+ }
+
+ @Override
+ public void registerInvokeDirect(DexMethod invokedMethod) {
+ setMaxApiReferenceLevel(invokedMethod);
+ }
+
+ @Override
+ public void registerInvokeStatic(DexMethod invokedMethod) {
+ setMaxApiReferenceLevel(invokedMethod);
+ }
+
+ @Override
+ public void registerInvokeInterface(DexMethod invokedMethod) {
+ setMaxApiReferenceLevel(invokedMethod);
+ }
+
+ @Override
+ public void registerInvokeSuper(DexMethod invokedMethod) {
+ setMaxApiReferenceLevel(invokedMethod);
+ }
+
+ @Override
+ public void registerInstanceFieldRead(DexField field) {
+ setMaxApiReferenceLevel(field);
+ }
+
+ @Override
+ public void registerInstanceFieldReadFromMethodHandle(DexField field) {
+ setMaxApiReferenceLevel(field);
+ }
+
+ @Override
+ public void registerInstanceFieldWrite(DexField field) {
+ setMaxApiReferenceLevel(field);
+ }
+
+ @Override
+ public void registerInstanceFieldWriteFromMethodHandle(DexField field) {
+ setMaxApiReferenceLevel(field);
+ }
+
+ @Override
+ public void registerNewInstance(DexType type) {
+ setMaxApiReferenceLevel(type);
+ }
+
+ @Override
+ public void registerStaticFieldRead(DexField field) {
+ setMaxApiReferenceLevel(field);
+ }
+
+ @Override
+ public void registerStaticFieldReadFromMethodHandle(DexField field) {
+ setMaxApiReferenceLevel(field);
+ }
+
+ @Override
+ public void registerStaticFieldWrite(DexField field) {
+ setMaxApiReferenceLevel(field);
+ }
+
+ @Override
+ public void registerStaticFieldWriteFromMethodHandle(DexField field) {
+ setMaxApiReferenceLevel(field);
+ }
+
+ @Override
+ public void registerConstClass(
+ DexType type,
+ ListIterator<? extends CfOrDexInstruction> iterator,
+ boolean ignoreCompatRules) {
+ setMaxApiReferenceLevel(type);
+ }
+
+ @Override
+ public void registerCheckCast(DexType type, boolean ignoreCompatRules) {
+ // CheckCast are not causing soft verification issues
+ }
+
+ @Override
+ public void registerSafeCheckCast(DexType type) {
+ // CheckCast are not causing soft verification issues
+ }
+
+ @Override
+ public void registerTypeReference(DexType type) {
+ // Type references are OK as long as we do not have a use on them
+ }
+
+ @Override
+ public void registerInstanceOf(DexType type) {
+ // InstanceOf are not causing soft verification issues
+ }
+
+ @Override
+ public void registerExceptionGuard(DexType guard) {
+ setMaxApiReferenceLevel(guard);
+ }
+
+ @Override
+ public void registerMethodHandle(DexMethodHandle methodHandle, MethodHandleUse use) {
+ super.registerMethodHandle(methodHandle, use);
+ }
+
+ private void setMaxApiReferenceLevel(DexReference reference) {
+ maxApiReferenceLevel =
+ maxApiReferenceLevel.max(
+ apiLevelCompute.computeApiLevelForLibraryReference(
+ reference, apiLevelCompute.getPlatformApiLevelOrUnknown(appView)));
+ }
+
+ public ComputedApiLevel getMaxApiReferenceLevel() {
+ return maxApiReferenceLevel;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
index 192b16a..f7ffc42 100644
--- a/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/DefaultEnqueuerUseRegistry.java
@@ -7,7 +7,6 @@
import static com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.isInvokeDynamicOnRecord;
import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
-import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.dex.code.CfOrDexInstruction;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
@@ -18,29 +17,23 @@
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.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.UseRegistry;
import java.util.ListIterator;
-public class DefaultEnqueuerUseRegistry extends UseRegistry<ProgramMethod> {
+public class DefaultEnqueuerUseRegistry extends ComputeApiLevelUseRegistry {
protected final AppView<? extends AppInfoWithClassHierarchy> appView;
protected final Enqueuer enqueuer;
- private final AndroidApiLevelCompute apiLevelCompute;
- private ComputedApiLevel maxApiReferenceLevel;
public DefaultEnqueuerUseRegistry(
AppView<? extends AppInfoWithClassHierarchy> appView,
ProgramMethod context,
Enqueuer enqueuer,
AndroidApiLevelCompute apiLevelCompute) {
- super(appView, context);
+ super(appView, context, apiLevelCompute);
this.appView = appView;
this.enqueuer = enqueuer;
- this.apiLevelCompute = apiLevelCompute;
- maxApiReferenceLevel = appView.computedMinApiLevel();
}
public DexProgramClass getContextHolder() {
@@ -53,6 +46,7 @@
@Override
public void registerInitClass(DexType clazz) {
+ super.registerInitClass(clazz);
enqueuer.traceInitClass(clazz, getContext());
}
@@ -64,90 +58,90 @@
@Override
public void registerInvokeVirtual(DexMethod invokedMethod) {
- setMaxApiReferenceLevel(invokedMethod);
+ super.registerInvokeVirtual(invokedMethod);
enqueuer.traceInvokeVirtual(invokedMethod, getContext());
}
@Override
public void registerInvokeDirect(DexMethod invokedMethod) {
- setMaxApiReferenceLevel(invokedMethod);
+ super.registerInvokeDirect(invokedMethod);
enqueuer.traceInvokeDirect(invokedMethod, getContext());
}
@Override
public void registerInvokeStatic(DexMethod invokedMethod) {
- setMaxApiReferenceLevel(invokedMethod);
+ super.registerInvokeStatic(invokedMethod);
enqueuer.traceInvokeStatic(invokedMethod, getContext());
}
@Override
public void registerInvokeInterface(DexMethod invokedMethod) {
- setMaxApiReferenceLevel(invokedMethod);
+ super.registerInvokeInterface(invokedMethod);
enqueuer.traceInvokeInterface(invokedMethod, getContext());
}
@Override
public void registerInvokeSuper(DexMethod invokedMethod) {
- setMaxApiReferenceLevel(invokedMethod);
+ super.registerInvokeSuper(invokedMethod);
enqueuer.traceInvokeSuper(invokedMethod, getContext());
}
@Override
public void registerInstanceFieldRead(DexField field) {
- setMaxApiReferenceLevel(field);
+ super.registerInstanceFieldRead(field);
enqueuer.traceInstanceFieldRead(field, getContext());
}
@Override
public void registerInstanceFieldReadFromMethodHandle(DexField field) {
- setMaxApiReferenceLevel(field);
+ super.registerInstanceFieldReadFromMethodHandle(field);
enqueuer.traceInstanceFieldReadFromMethodHandle(field, getContext());
}
private void registerInstanceFieldReadFromRecordMethodHandle(DexField field) {
- setMaxApiReferenceLevel(field);
+ super.registerInstanceFieldWriteFromMethodHandle(field);
enqueuer.traceInstanceFieldReadFromRecordMethodHandle(field, getContext());
}
@Override
public void registerInstanceFieldWrite(DexField field) {
- setMaxApiReferenceLevel(field);
+ super.registerInstanceFieldWrite(field);
enqueuer.traceInstanceFieldWrite(field, getContext());
}
@Override
public void registerInstanceFieldWriteFromMethodHandle(DexField field) {
- setMaxApiReferenceLevel(field);
+ super.registerInstanceFieldWriteFromMethodHandle(field);
enqueuer.traceInstanceFieldWriteFromMethodHandle(field, getContext());
}
@Override
public void registerNewInstance(DexType type) {
- setMaxApiReferenceLevel(type);
+ super.registerNewInstance(type);
enqueuer.traceNewInstance(type, getContext());
}
@Override
public void registerStaticFieldRead(DexField field) {
- setMaxApiReferenceLevel(field);
+ super.registerStaticFieldRead(field);
enqueuer.traceStaticFieldRead(field, getContext());
}
@Override
public void registerStaticFieldReadFromMethodHandle(DexField field) {
- setMaxApiReferenceLevel(field);
+ super.registerStaticFieldReadFromMethodHandle(field);
enqueuer.traceStaticFieldReadFromMethodHandle(field, getContext());
}
@Override
public void registerStaticFieldWrite(DexField field) {
- setMaxApiReferenceLevel(field);
+ super.registerStaticFieldWrite(field);
enqueuer.traceStaticFieldWrite(field, getContext());
}
@Override
public void registerStaticFieldWriteFromMethodHandle(DexField field) {
- setMaxApiReferenceLevel(field);
+ super.registerStaticFieldWriteFromMethodHandle(field);
enqueuer.traceStaticFieldWriteFromMethodHandle(field, getContext());
}
@@ -156,32 +150,37 @@
DexType type,
ListIterator<? extends CfOrDexInstruction> iterator,
boolean ignoreCompatRules) {
+ super.registerConstClass(type, iterator, ignoreCompatRules);
enqueuer.traceConstClass(type, getContext(), iterator, ignoreCompatRules);
}
@Override
public void registerCheckCast(DexType type, boolean ignoreCompatRules) {
+ super.registerCheckCast(type, ignoreCompatRules);
enqueuer.traceCheckCast(type, getContext(), ignoreCompatRules);
}
@Override
public void registerSafeCheckCast(DexType type) {
+ super.registerSafeCheckCast(type);
enqueuer.traceSafeCheckCast(type, getContext());
}
@Override
public void registerTypeReference(DexType type) {
+ super.registerTypeReference(type);
enqueuer.traceTypeReference(type, getContext());
}
@Override
public void registerInstanceOf(DexType type) {
+ super.registerInstanceOf(type);
enqueuer.traceInstanceOf(type, getContext());
}
@Override
public void registerExceptionGuard(DexType guard) {
- setMaxApiReferenceLevel(guard);
+ super.registerExceptionGuard(guard);
enqueuer.traceExceptionGuard(guard, getContext());
}
@@ -219,15 +218,4 @@
}
}
}
-
- private void setMaxApiReferenceLevel(DexReference reference) {
- maxApiReferenceLevel =
- maxApiReferenceLevel.max(
- apiLevelCompute.computeApiLevelForLibraryReference(
- reference, apiLevelCompute.getPlatformApiLevelOrUnknown(appView)));
- }
-
- public ComputedApiLevel getMaxApiReferenceLevel() {
- return maxApiReferenceLevel;
- }
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
index 57eaa67..e826dd7 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineDuplicateMethodTest.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.apimodel;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethodWithName;
@@ -60,7 +59,6 @@
.setMinApi(parameters.getApiLevel())
.addAndroidBuildVersion()
.apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
- .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, classApiLevel))
.apply(setMockApiLevelForMethod(addedOn23(), methodApiLevel))
// TODO(b/213552119): Remove when enabled by default.
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
@@ -162,7 +160,7 @@
// Only present from api level 19.
public static class LibraryClass {
- public void addedOn23() {
+ public static void addedOn23() {
System.out.println("LibraryClass::addedOn23");
}
}
@@ -171,12 +169,9 @@
@NeverInline
public static void test() {
- if (AndroidBuildVersion.VERSION >= 19) {
- LibraryClass libraryClass = new LibraryClass();
- if (AndroidBuildVersion.VERSION >= 23) {
- libraryClass.addedOn23();
- libraryClass.addedOn23();
- }
+ if (AndroidBuildVersion.VERSION >= 23) {
+ LibraryClass.addedOn23();
+ LibraryClass.addedOn23();
}
System.out.println("Hello World");
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
index 9793b9b..978d068 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineHorizontalMergingTest.java
@@ -35,7 +35,6 @@
public class ApiModelOutlineHorizontalMergingTest extends TestBase {
private final AndroidApiLevel libraryClassApiLevel = AndroidApiLevel.K;
- private final AndroidApiLevel otherLibraryClassApiLevel = AndroidApiLevel.K;
private final AndroidApiLevel firstMethodApiLevel = AndroidApiLevel.M;
private final AndroidApiLevel secondMethodApiLevel = AndroidApiLevel.O_MR1;
@@ -62,10 +61,10 @@
.apply(
setMockApiLevelForMethod(
LibraryClass.class.getMethod("addedOn27"), secondMethodApiLevel))
- .apply(setMockApiLevelForClass(OtherLibraryClass.class, otherLibraryClassApiLevel))
+ .apply(setMockApiLevelForClass(OtherLibraryClass.class, libraryClassApiLevel))
.apply(
setMockApiLevelForDefaultInstanceInitializer(
- OtherLibraryClass.class, otherLibraryClassApiLevel))
+ OtherLibraryClass.class, libraryClassApiLevel))
.apply(
setMockApiLevelForMethod(
OtherLibraryClass.class.getMethod("addedOn23"), firstMethodApiLevel))
@@ -101,7 +100,11 @@
.inspect(
inspector -> {
// TODO(b/187675788): Update when horizontal merging is enabled for D8 for debug mode.
- if (parameters.getApiLevel().isLessThan(firstMethodApiLevel)) {
+ if (parameters.getApiLevel().isLessThan(libraryClassApiLevel)) {
+ // We have generated 4 outlines two having api level 23 and two having api level 27
+ // and 2 outlines for each instance initializer.
+ assertEquals(11, inspector.allClasses().size());
+ } else if (parameters.getApiLevel().isLessThan(firstMethodApiLevel)) {
// We have generated 4 outlines two having api level 23 and two having api level 27.
assertEquals(7, inspector.allClasses().size());
} else if (parameters.getApiLevel().isLessThan(secondMethodApiLevel)) {
@@ -166,8 +169,12 @@
assertEquals(3, inspector.allClasses().size());
} else if (parameters.getApiLevel().isLessThan(firstMethodApiLevel)) {
// We have generated 4 outlines two having api level 23 and two having api level 27.
+ // If less than the library api level then we have synthesized two instance initializer
+ // outlines as well.
// Check that the levels are horizontally merged.
- assertEquals(5, inspector.allClasses().size());
+ assertEquals(
+ parameters.getApiLevel().isLessThan(libraryClassApiLevel) ? 6 : 5,
+ inspector.allClasses().size());
assertEquals(2, outlinedAddedOn23.size());
assertTrue(
outlinedAddedOn23.stream()
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerSuperTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerSuperTest.java
new file mode 100644
index 0000000..a362bd3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerSuperTest.java
@@ -0,0 +1,165 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.testing.AndroidBuildVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+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 ApiModelOutlineInstanceInitializerSuperTest extends TestBase {
+
+ private static final AndroidApiLevel classApiLevel = AndroidApiLevel.M;
+
+ private static final String[] EXPECTED = new String[] {"Hello ", "World!"};
+
+ @Parameter public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private void setupTestBuilder(TestCompilerBuilder<?, ?, ?, ?, ?> testBuilder) throws Exception {
+ testBuilder
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .addProgramClasses(Main.class, ProgramExtendsLibraryClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .addAndroidBuildVersion()
+ .apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
+ .apply(
+ setMockApiLevelForMethod(
+ LibraryClass.class.getDeclaredConstructor(String.class), classApiLevel))
+ .apply(setMockApiLevelForMethod(LibraryClass.class.getMethod("print"), classApiLevel))
+ .apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(ApiModelingTestHelper::enableOutliningOfMethods)
+ .apply(ApiModelingTestHelper::disableStubbingOfClasses);
+ }
+
+ public boolean addToBootClasspath() {
+ return parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(classApiLevel);
+ }
+
+ @Test
+ public void testD8Debug() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8()
+ .setMode(CompilationMode.DEBUG)
+ .apply(this::setupTestBuilder)
+ .compile()
+ .inspect(inspector -> inspect(inspector, false))
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput);
+ }
+
+ @Test
+ public void testD8Release() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ testForD8()
+ .setMode(CompilationMode.RELEASE)
+ .apply(this::setupTestBuilder)
+ .compile()
+ .inspect(inspector -> inspect(inspector, false))
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .apply(this::setupTestBuilder)
+ .addKeepMainRule(Main.class)
+ .addKeepClassAndMembersRules(ProgramExtendsLibraryClass.class)
+ .addDontObfuscate()
+ .compile()
+ .inspect(inspector -> inspect(inspector, true))
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput);
+ }
+
+ private void inspect(CodeInspector inspector, boolean isR8) throws Exception {
+ // Because each of the outline context also have a super call, R8 will inline back the outline
+ // because the super call has the same api level as the outlinee.
+ verifyThat(inspector, parameters, LibraryClass.class.getMethod("print"))
+ .isOutlinedFromUntil(
+ ProgramExtendsLibraryClass.class.getMethod("print"),
+ isR8 ? AndroidApiLevel.B : classApiLevel);
+ verifyThat(inspector, parameters, LibraryClass.class.getDeclaredConstructor(String.class))
+ .isOutlinedFromUntil(
+ ProgramExtendsLibraryClass.class.getDeclaredConstructor(String.class),
+ isR8 ? AndroidApiLevel.B : classApiLevel);
+ }
+
+ private void checkOutput(SingleTestRunResult<?> runResult) {
+ if (parameters.isDexRuntime()
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(classApiLevel)) {
+ runResult.assertSuccessWithOutputLines(EXPECTED);
+ } else {
+ runResult.assertSuccessWithOutputLines("Not calling API");
+ }
+ }
+
+ // Only present from api level 23.
+ public static class LibraryClass {
+
+ private final String arg;
+
+ public LibraryClass(String arg) {
+ this.arg = arg;
+ }
+
+ public void print() {
+ System.out.println(arg);
+ }
+ }
+
+ public static class ProgramExtendsLibraryClass extends LibraryClass {
+
+ private final LibraryClass otherArg;
+
+ public ProgramExtendsLibraryClass(String arg) {
+ super(arg); // <-- this cannot be outlined
+ otherArg = new LibraryClass("World!"); // <-- this should be outlined.
+ }
+
+ @Override
+ public void print() {
+ super.print();
+ otherArg.print();
+ }
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ if (AndroidBuildVersion.VERSION >= 23) {
+ new ProgramExtendsLibraryClass("Hello ").print();
+ } else {
+ System.out.println("Not calling API");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerTest.java
index ea08c59..b0a5fb7 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceInitializerTest.java
@@ -113,11 +113,11 @@
private void inspect(CodeInspector inspector, boolean isR8) throws Exception {
Method mainMethod = Main.class.getMethod("main", String[].class);
verifyThat(inspector, parameters, Argument.class.getDeclaredConstructor(String.class))
- .isOutlinedFromUntil(mainMethod, AndroidApiLevel.B);
+ .isOutlinedFromUntil(mainMethod, classApiLevel);
verifyThat(inspector, parameters, LibraryClass.class.getDeclaredConstructor(Argument.class))
- .isOutlinedFromUntil(mainMethod, AndroidApiLevel.B);
+ .isOutlinedFromUntil(mainMethod, classApiLevel);
verifyThat(inspector, parameters, LibraryClass.class.getMethod("print"))
- .isOutlinedFromUntil(mainMethod, isR8 ? AndroidApiLevel.B : classApiLevel);
+ .isOutlinedFromUntil(mainMethod, classApiLevel);
}
private void checkOutput(SingleTestRunResult<?> runResult) {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java
index 0b2f270..3504527 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.apimodel;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
import static org.junit.Assume.assumeTrue;
@@ -55,8 +54,9 @@
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.apply(ApiModelingTestHelper::enableStubbingOfClasses)
.apply(ApiModelingTestHelper::enableOutliningOfMethods)
+ // We only model the class and not the default initializer, otherwise we outline the new
+ // instance call and remove the last reference in non-outlined code.
.apply(setMockApiLevelForClass(LibraryClass.class, libraryClassLevel))
- .apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, libraryClassLevel))
.apply(setMockApiLevelForMethod(apiMethod(), libraryMethodLevel));
}
@@ -123,7 +123,7 @@
public static class LibraryClass {
// Only present from api level 30
- public void foo() {
+ public static void foo() {
System.out.println("LibraryClass::foo");
}
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
index 21db603..22f79df 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodMissingClassTest.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.apimodel;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
-import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForDefaultInstanceInitializer;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethodWithName;
@@ -64,9 +63,6 @@
.setMinApi(parameters.getApiLevel())
.addAndroidBuildVersion()
.apply(setMockApiLevelForClass(LibraryClass.class, initialLibraryMockLevel))
- .apply(
- setMockApiLevelForDefaultInstanceInitializer(
- LibraryClass.class, initialLibraryMockLevel))
.apply(setMockApiLevelForMethod(addedOn23(), initialLibraryMockLevel))
.apply(setMockApiLevelForMethod(addedOn27(), finalLibraryMethodLevel))
// TODO(b/213552119): Remove when enabled by default.
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
index 178c115..574a8b5 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.utils.codeinspector.CodeMatchers.accessesField;
import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethodWithHolderAndName;
import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethodWithName;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
@@ -28,6 +29,7 @@
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections;
@@ -334,7 +336,7 @@
assertThat(target, not(CodeMatchers.invokesMethod(candidate)));
}
- void isOutlinedFromUntil(Method method, AndroidApiLevel apiLevel) {
+ void isOutlinedFromUntil(Executable method, AndroidApiLevel apiLevel) {
if (parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(apiLevel)) {
isOutlinedFrom(method);
} else {
@@ -342,7 +344,7 @@
}
}
- void isOutlinedFrom(Method method) {
+ void isOutlinedFrom(Executable method) {
// Check that the call is in a synthetic class.
List<FoundMethodSubject> outlinedMethod =
inspector.allClasses().stream()
@@ -350,18 +352,20 @@
.filter(
methodSubject ->
methodSubject.isSynthetic()
- && invokesMethodWithName(methodOfInterest.getMethodName())
+ && invokesMethodWithHolderAndName(
+ methodOfInterest.getHolderClass().getTypeName(),
+ methodOfInterest.getMethodName())
.matches(methodSubject))
.collect(Collectors.toList());
assertEquals(1, outlinedMethod.size());
// Assert that method invokes the outline
- MethodSubject caller = inspector.method(method);
+ MethodSubject caller = inspector.method(Reference.methodFromMethod(method));
assertThat(caller, isPresent());
assertThat(caller, invokesMethod(outlinedMethod.get(0)));
}
- void isNotOutlinedFrom(Method method) {
- MethodSubject caller = inspector.method(method);
+ void isNotOutlinedFrom(Executable method) {
+ MethodSubject caller = inspector.method(Reference.methodFromMethod(method));
assertThat(caller, isPresent());
assertThat(caller, invokesMethodWithName(methodOfInterest.getMethodName()));
}