blob: fb04da0501f85764ae6152fe3e38670fa1ce6384 [file] [log] [blame]
// 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.apiconverter;
import com.android.tools.r8.cf.code.CfArithmeticBinop;
import com.android.tools.r8.cf.code.CfArithmeticBinop.Opcode;
import com.android.tools.r8.cf.code.CfArrayLength;
import com.android.tools.r8.cf.code.CfArrayLoad;
import com.android.tools.r8.cf.code.CfArrayStore;
import com.android.tools.r8.cf.code.CfCheckCast;
import com.android.tools.r8.cf.code.CfConstNull;
import com.android.tools.r8.cf.code.CfConstNumber;
import com.android.tools.r8.cf.code.CfFrame;
import com.android.tools.r8.cf.code.CfGoto;
import com.android.tools.r8.cf.code.CfIf;
import com.android.tools.r8.cf.code.CfIfCmp;
import com.android.tools.r8.cf.code.CfInstanceFieldRead;
import com.android.tools.r8.cf.code.CfInstanceOf;
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.CfNew;
import com.android.tools.r8.cf.code.CfNewArray;
import com.android.tools.r8.cf.code.CfReturn;
import com.android.tools.r8.cf.code.CfStackInstruction;
import com.android.tools.r8.cf.code.CfStaticFieldRead;
import com.android.tools.r8.cf.code.CfStore;
import com.android.tools.r8.cf.code.frame.FrameType;
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.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.synthetic.SyntheticCfCodeProvider;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.objectweb.asm.Opcodes;
public abstract class NullableConversionCfCodeProvider extends SyntheticCfCodeProvider {
protected NullableConversionCfCodeProvider(AppView<?> appView, DexType holder) {
super(appView, holder);
}
void generateNullCheck(List<CfInstruction> instructions) {
CfLabel nullDest = new CfLabel();
instructions.add(new CfLoad(ValueType.OBJECT, 0));
instructions.add(new CfIf(If.Type.NE, ValueType.OBJECT, nullDest));
instructions.add(new CfConstNull());
instructions.add(new CfReturn(ValueType.OBJECT));
instructions.add(nullDest);
}
public static class ArrayConversionCfCodeProvider extends NullableConversionCfCodeProvider {
private final DexType typeArray;
private final DexType convertedTypeArray;
private final DexMethod conversion;
public ArrayConversionCfCodeProvider(
AppView<?> appView,
DexType holder,
DexType typeArray,
DexType convertedTypeArray,
DexMethod conversion) {
super(appView, holder);
this.typeArray = typeArray;
this.convertedTypeArray = convertedTypeArray;
this.conversion = conversion;
}
@Override
public CfCode generateCfCode() {
DexItemFactory factory = appView.dexItemFactory();
List<CfInstruction> instructions = new ArrayList<>();
// if (arg == null) { return null; }
generateNullCheck(instructions);
instructions.add(CfFrame.builder().appendLocal(FrameType.initialized(typeArray)).build());
CfFrame frame =
CfFrame.builder()
.appendLocal(FrameType.initialized(typeArray))
.appendLocal(FrameType.initialized(factory.intType))
.appendLocal(FrameType.initialized(convertedTypeArray))
.appendLocal(FrameType.initialized(factory.intType))
.build();
// int t1 = arg.length;
instructions.add(new CfLoad(ValueType.fromDexType(typeArray), 0));
instructions.add(new CfArrayLength());
instructions.add(new CfStore(ValueType.INT, 1));
// ConvertedType[] t2 = new ConvertedType[t1];
instructions.add(new CfLoad(ValueType.INT, 1));
instructions.add(new CfNewArray(convertedTypeArray));
instructions.add(new CfStore(ValueType.fromDexType(convertedTypeArray), 2));
// int t3 = 0;
instructions.add(new CfConstNumber(0, ValueType.INT));
instructions.add(new CfStore(ValueType.INT, 3));
// while (t3 < t1) {
CfLabel returnLabel = new CfLabel();
CfLabel loopLabel = new CfLabel();
instructions.add(loopLabel);
instructions.add(frame);
instructions.add(new CfLoad(ValueType.INT, 3));
instructions.add(new CfLoad(ValueType.INT, 1));
instructions.add(new CfIfCmp(If.Type.GE, ValueType.INT, returnLabel));
// t2[t3] = convert(arg[t3]);
instructions.add(new CfLoad(ValueType.fromDexType(convertedTypeArray), 2));
instructions.add(new CfLoad(ValueType.INT, 3));
instructions.add(new CfLoad(ValueType.fromDexType(typeArray), 0));
instructions.add(new CfLoad(ValueType.INT, 3));
instructions.add(new CfArrayLoad(MemberType.OBJECT));
instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, conversion, false));
instructions.add(new CfArrayStore(MemberType.OBJECT));
// t3 = t3 + 1; }
instructions.add(new CfLoad(ValueType.INT, 3));
instructions.add(new CfConstNumber(1, ValueType.INT));
instructions.add(new CfArithmeticBinop(Opcode.Add, NumericType.INT));
instructions.add(new CfStore(ValueType.INT, 3));
instructions.add(new CfGoto(loopLabel));
// return t2;
instructions.add(returnLabel);
instructions.add(frame.clone());
instructions.add(new CfLoad(ValueType.fromDexType(convertedTypeArray), 2));
instructions.add(new CfReturn(ValueType.fromDexType(convertedTypeArray)));
return standardCfCodeFromInstructions(instructions);
}
}
public static class EnumConversionCfCodeProvider extends NullableConversionCfCodeProvider {
private final Iterable<DexEncodedField> enumFields;
private final DexType enumType;
private final DexType convertedType;
public EnumConversionCfCodeProvider(
AppView<?> appView,
DexType holder,
Iterable<DexEncodedField> enumFields,
DexType enumType,
DexType convertedType) {
super(appView, holder);
this.enumFields = enumFields;
this.enumType = enumType;
this.convertedType = convertedType;
}
@Override
public CfCode generateCfCode() {
DexItemFactory factory = appView.dexItemFactory();
List<CfInstruction> instructions = new ArrayList<>();
CfFrame frame = CfFrame.builder().appendLocal(FrameType.initialized(enumType)).build();
// if (arg == null) { return null; }
generateNullCheck(instructions);
instructions.add(frame);
// if (arg == enumType.enumField1) { return convertedType.enumField1; }
Iterator<DexEncodedField> iterator = enumFields.iterator();
while (iterator.hasNext()) {
DexEncodedField enumField = iterator.next();
CfLabel notEqual = new CfLabel();
if (iterator.hasNext()) {
instructions.add(new CfLoad(ValueType.fromDexType(enumType), 0));
instructions.add(
new CfStaticFieldRead(factory.createField(enumType, enumType, enumField.getName())));
instructions.add(new CfIfCmp(If.Type.NE, ValueType.OBJECT, notEqual));
}
instructions.add(
new CfStaticFieldRead(
factory.createField(convertedType, convertedType, enumField.getName())));
instructions.add(new CfReturn(ValueType.fromDexType(convertedType)));
if (iterator.hasNext()) {
instructions.add(notEqual);
instructions.add(frame.clone());
}
}
return standardCfCodeFromInstructions(instructions);
}
}
public static class WrapperConversionCfCodeProvider extends NullableConversionCfCodeProvider {
DexField reverseWrapperField;
DexField wrapperField;
public WrapperConversionCfCodeProvider(
AppView<?> appView, DexField reverseWrapperField, DexField wrapperField) {
super(appView, wrapperField.holder);
this.reverseWrapperField = reverseWrapperField;
this.wrapperField = wrapperField;
}
@Override
public CfCode generateCfCode() {
DexItemFactory factory = appView.dexItemFactory();
List<CfInstruction> instructions = new ArrayList<>();
DexType argType = wrapperField.type;
CfFrame frame = CfFrame.builder().appendLocal(FrameType.initialized(argType)).build();
// if (arg == null) { return null };
generateNullCheck(instructions);
instructions.add(frame);
// if (arg instanceOf ReverseWrapper) { return ((ReverseWrapper) arg).wrapperField};
assert reverseWrapperField != null;
CfLabel unwrapDest = new CfLabel();
instructions.add(new CfLoad(ValueType.fromDexType(argType), 0));
instructions.add(new CfInstanceOf(reverseWrapperField.holder));
instructions.add(new CfIf(If.Type.EQ, ValueType.INT, unwrapDest));
instructions.add(new CfLoad(ValueType.fromDexType(argType), 0));
instructions.add(new CfCheckCast(reverseWrapperField.holder));
instructions.add(new CfInstanceFieldRead(reverseWrapperField));
instructions.add(new CfReturn(ValueType.fromDexType(reverseWrapperField.type)));
instructions.add(unwrapDest);
instructions.add(frame.clone());
// return new Wrapper(wrappedValue);
instructions.add(new CfNew(wrapperField.holder));
instructions.add(CfStackInstruction.fromAsm(Opcodes.DUP));
instructions.add(new CfLoad(ValueType.fromDexType(argType), 0));
instructions.add(
new CfInvoke(
Opcodes.INVOKESPECIAL,
factory.createMethod(
wrapperField.holder,
factory.createProto(factory.voidType, argType),
factory.constructorMethodName),
false));
instructions.add(new CfReturn(ValueType.fromDexType(wrapperField.holder)));
return standardCfCodeFromInstructions(instructions);
}
}
}