| // Copyright (c) 2017, 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.errors.Unimplemented; |
| import com.android.tools.r8.graph.DexMethod; |
| import com.android.tools.r8.graph.DexType; |
| import com.android.tools.r8.graph.ProgramMethod; |
| import com.android.tools.r8.ir.code.Invoke; |
| 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 com.android.tools.r8.utils.BooleanUtils; |
| import com.google.common.collect.Lists; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| // Source code representing simple forwarding method. |
| public final class ForwardMethodSourceCode extends SyntheticSourceCode { |
| |
| public static Builder builder(DexMethod method) { |
| return new Builder(method); |
| } |
| |
| public static class Builder { |
| |
| private DexType receiver; |
| private DexMethod method; |
| private DexMethod originalMethod; |
| private DexType targetReceiver; |
| private DexMethod target; |
| private Invoke.Type invokeType; |
| private boolean castResult; |
| private boolean isInterface; |
| private boolean extraNullParameter; |
| |
| public Builder(DexMethod method) { |
| this.method = method; |
| this.originalMethod = method; |
| } |
| |
| public Builder setReceiver(DexType receiver) { |
| this.receiver = receiver; |
| return this; |
| } |
| |
| public Builder setMethod(DexMethod method) { |
| this.method = method; |
| return this; |
| } |
| |
| public Builder setOriginalMethod(DexMethod originalMethod) { |
| this.originalMethod = originalMethod; |
| return this; |
| } |
| |
| public Builder setTargetReceiver(DexType targetReceiver) { |
| this.targetReceiver = targetReceiver; |
| return this; |
| } |
| |
| public Builder setTarget(DexMethod target) { |
| this.target = target; |
| return this; |
| } |
| |
| public Builder setInvokeType(Type invokeType) { |
| this.invokeType = invokeType; |
| return this; |
| } |
| |
| public Builder setCastResult() { |
| this.castResult = true; |
| return this; |
| } |
| |
| public Builder setIsInterface(boolean isInterface) { |
| this.isInterface = isInterface; |
| return this; |
| } |
| |
| public Builder setExtraNullParameter() { |
| this.extraNullParameter = true; |
| return this; |
| } |
| |
| public ForwardMethodSourceCode build(ProgramMethod context, Position callerPosition) { |
| return new ForwardMethodSourceCode( |
| receiver, |
| method, |
| originalMethod, |
| targetReceiver, |
| target, |
| invokeType, |
| callerPosition, |
| isInterface, |
| castResult, |
| extraNullParameter); |
| } |
| } |
| |
| private final DexType targetReceiver; |
| private final DexMethod target; |
| private final Invoke.Type invokeType; |
| private final boolean castResult; |
| private final boolean isInterface; |
| private final boolean extraNullParameter; |
| |
| protected ForwardMethodSourceCode( |
| DexType receiver, |
| DexMethod method, |
| DexMethod originalMethod, |
| DexType targetReceiver, |
| DexMethod target, |
| Type invokeType, |
| Position callerPosition, |
| boolean isInterface, |
| boolean castResult, |
| boolean extraNullParameter) { |
| super(receiver, method, callerPosition, originalMethod); |
| assert (targetReceiver == null) == (invokeType == Invoke.Type.STATIC); |
| |
| this.target = target; |
| this.targetReceiver = targetReceiver; |
| this.invokeType = invokeType; |
| this.isInterface = isInterface; |
| this.castResult = castResult; |
| this.extraNullParameter = extraNullParameter; |
| assert checkSignatures(); |
| |
| switch (invokeType) { |
| case DIRECT: |
| case STATIC: |
| case SUPER: |
| case INTERFACE: |
| case VIRTUAL: |
| break; |
| default: |
| throw new Unimplemented("Invoke type " + invokeType + " is not yet supported."); |
| } |
| } |
| |
| private boolean checkSignatures() { |
| List<DexType> sourceParams = new ArrayList<>(); |
| if (receiver != null) { |
| sourceParams.add(receiver); |
| } |
| sourceParams.addAll(Lists.newArrayList(proto.parameters.values)); |
| if (extraNullParameter) { |
| sourceParams.remove(sourceParams.size() - 1); |
| } |
| |
| List<DexType> targetParams = new ArrayList<>(); |
| if (targetReceiver != null) { |
| targetParams.add(targetReceiver); |
| } |
| targetParams.addAll(Lists.newArrayList(target.proto.parameters.values)); |
| |
| assert sourceParams.size() == targetParams.size(); |
| for (int i = 0; i < sourceParams.size(); i++) { |
| DexType source = sourceParams.get(i); |
| DexType target = targetParams.get(i); |
| |
| // We assume source is compatible with target if they both are classes. |
| // This check takes care of receiver widening conversion but does not |
| // many others, like conversion from an array to Object. |
| assert (source.isClassType() && target.isClassType()) || source == target; |
| } |
| |
| assert this.proto.returnType == target.proto.returnType || castResult; |
| return true; |
| } |
| |
| @Override |
| protected void prepareInstructions() { |
| // Prepare call arguments. |
| List<ValueType> argValueTypes = new ArrayList<>(); |
| List<Integer> argRegisters = new ArrayList<>(); |
| |
| if (receiver != null) { |
| argValueTypes.add(ValueType.OBJECT); |
| argRegisters.add(getReceiverRegister()); |
| } |
| |
| DexType[] accessorParams = proto.parameters.values; |
| for (int i = 0; i < accessorParams.length - BooleanUtils.intValue(extraNullParameter); i++) { |
| argValueTypes.add(ValueType.fromDexType(accessorParams[i])); |
| argRegisters.add(getParamRegister(i)); |
| } |
| |
| // Method call to the target method. |
| add( |
| builder -> |
| builder.addInvoke( |
| this.invokeType, |
| this.target, |
| this.target.proto, |
| argValueTypes, |
| argRegisters, |
| this.isInterface)); |
| |
| // Does the method return value? |
| if (proto.returnType.isVoidType()) { |
| add(IRBuilder::addReturn); |
| } else { |
| ValueType valueType = ValueType.fromDexType(proto.returnType); |
| int tempValue = nextRegister(valueType); |
| add(builder -> builder.addMoveResult(tempValue)); |
| if (this.proto.returnType != target.proto.returnType) { |
| add(builder -> builder.addCheckCast(tempValue, this.proto.returnType)); |
| } |
| add(builder -> builder.addReturn(tempValue)); |
| } |
| } |
| } |