blob: 9e708ccb546a7521dc4049f3044c5193478318d6 [file] [log] [blame]
// 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));
}
}
}