blob: 1f3c6d0c6c91a32986a85b37ba64f3be45506c64 [file] [log] [blame]
// 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.synthetic;
import com.android.tools.r8.cf.code.CfFieldInstruction;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfLoad;
import com.android.tools.r8.cf.code.CfReturn;
import com.android.tools.r8.cf.code.CfReturnVoid;
import com.android.tools.r8.cf.code.CfTryCatch;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.OptionalBool;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import java.util.function.Consumer;
import org.objectweb.asm.Opcodes;
public class FieldAccessorBuilder {
private DexField field;
private OptionalBool isInstanceField = OptionalBool.unknown();
private OptionalBool isSetter = OptionalBool.unknown();
private DexMethod sourceMethod;
private FieldAccessorBuilder() {}
public static FieldAccessorBuilder builder() {
return new FieldAccessorBuilder();
}
public FieldAccessorBuilder apply(Consumer<FieldAccessorBuilder> consumer) {
consumer.accept(this);
return this;
}
public FieldAccessorBuilder setField(DexClassAndField field) {
return field.getAccessFlags().isStatic()
? setStaticField(field.getReference())
: setInstanceField(field.getReference());
}
public FieldAccessorBuilder setGetter() {
isSetter = OptionalBool.FALSE;
return this;
}
public FieldAccessorBuilder setInstanceField(DexField field) {
this.field = field;
this.isInstanceField = OptionalBool.TRUE;
return this;
}
public FieldAccessorBuilder setSetter() {
isSetter = OptionalBool.TRUE;
return this;
}
public FieldAccessorBuilder setSourceMethod(DexMethod sourceMethod) {
this.sourceMethod = sourceMethod;
return this;
}
public FieldAccessorBuilder setStaticField(DexField field) {
this.field = field;
this.isInstanceField = OptionalBool.FALSE;
return this;
}
public CfCode build() {
assert validate();
int maxStack = 0;
int maxLocals = 0;
Builder<CfInstruction> instructions = ImmutableList.builder();
if (isInstanceField()) {
// Load the receiver.
instructions.add(new CfLoad(ValueType.OBJECT, maxLocals));
maxStack += 1;
maxLocals += 1;
}
if (isSetter()) {
// Load the argument.
ValueType fieldType = ValueType.fromDexType(field.getType());
instructions.add(new CfLoad(fieldType, maxLocals));
maxStack += fieldType.requiredRegisters();
maxLocals += fieldType.requiredRegisters();
}
// Get or set the field.
int opcode =
Opcodes.GETSTATIC + BooleanUtils.intValue(isSetter()) + (isInstanceField.ordinal() << 1);
instructions.add(new CfFieldInstruction(opcode, field, field));
// Return.
if (isSetter()) {
instructions.add(new CfReturnVoid());
} else {
ValueType fieldType = ValueType.fromDexType(field.getType());
maxStack = Math.max(fieldType.requiredRegisters(), maxStack);
instructions.add(new CfReturn(fieldType));
}
ImmutableList<CfTryCatch> tryCatchRanges = ImmutableList.of();
ImmutableList<CfCode.LocalVariableInfo> localVariables = ImmutableList.of();
return new CfCode(
sourceMethod.getHolderType(),
maxStack,
maxLocals,
instructions.build(),
tryCatchRanges,
localVariables);
}
private boolean isSetter() {
return isSetter.isTrue();
}
private boolean isInstanceField() {
return isInstanceField.isTrue();
}
private boolean validate() {
assert field != null;
assert !isInstanceField.isUnknown();
assert !isSetter.isUnknown();
assert sourceMethod != null;
return true;
}
}