| // 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 applyIf( |
| boolean condition, |
| Consumer<FieldAccessorBuilder> thenConsumer, |
| Consumer<FieldAccessorBuilder> elseConsumer) { |
| return apply(condition ? thenConsumer : elseConsumer); |
| } |
| |
| 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; |
| } |
| } |