blob: ce5fde6bdf3aa7ffb079eef2bf7d03a17a7d262d [file] [log] [blame]
// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.desugar;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
import com.android.tools.r8.cf.code.CfLoad;
import com.android.tools.r8.cf.code.CfReturn;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.ValueType;
import com.google.common.collect.ImmutableList;
import java.util.Collections;
import java.util.List;
import org.objectweb.asm.Opcodes;
/** This outlines calls to array clone from within interface methods. See b/342802978 */
public class OutlineArrayCloneFromInterfaceMethodDesugaring implements CfInstructionDesugaring {
private final AppView<?> appView;
public OutlineArrayCloneFromInterfaceMethodDesugaring(AppView<?> appView) {
this.appView = appView;
}
@Override
public DesugarDescription compute(CfInstruction instruction, ProgramMethod context) {
// This workaround only applies within default or static interface method.
if (!context.getHolder().isInterface()) {
return DesugarDescription.nothing();
}
DexEncodedMethod contextDefinition = context.getDefinition();
if (!contextDefinition.isStatic() && !contextDefinition.isNonPrivateVirtualMethod()) {
return DesugarDescription.nothing();
}
// The target method must be virtual clone on an array type.
if (!instruction.isInvokeVirtual()) {
return DesugarDescription.nothing();
}
CfInvoke invoke = instruction.asInvoke();
if (!appView.dexItemFactory().isArrayClone(invoke.getMethod())) {
return DesugarDescription.nothing();
}
return DesugarDescription.builder()
.setDesugarRewrite(
(position, locals, stack, info, eventConsumer, ctx, pCtx, collection, factory) -> {
ProgramMethod newProgramMethod =
appView
.getSyntheticItems()
.createMethod(
kind -> kind.OBJECT_CLONE_OUTLINE,
pCtx.createUniqueContext(),
appView,
methodBuilder ->
methodBuilder
.setProto(
factory.createProto(factory.objectType, factory.objectType))
.setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
.setCode(this::createCode));
eventConsumer.acceptInvokeObjectCloneOutliningMethod(newProgramMethod, ctx);
return createStaticInvoke(newProgramMethod);
})
.build();
}
private static List<CfInstruction> createStaticInvoke(ProgramMethod newProgramMethod) {
return Collections.singletonList(
new CfInvoke(Opcodes.INVOKESTATIC, newProgramMethod.getReference(), false));
}
private Code createCode(DexMethod method) {
return new CfCode(
method.getHolderType(),
1,
1,
ImmutableList.of(
new CfLoad(ValueType.OBJECT, 0),
new CfInvoke(
Opcodes.INVOKEVIRTUAL, appView.dexItemFactory().objectMembers.clone, false),
new CfReturn(ValueType.OBJECT)));
}
}