blob: 6ded2e5ca5dba19d8cdd0b578cb53d9617095f81 [file] [log] [blame]
// Copyright (c) 2020, 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.analysis.type;
import static com.android.tools.r8.ir.code.Opcodes.ASSUME;
import static com.android.tools.r8.ir.code.Opcodes.CHECK_CAST;
import static com.android.tools.r8.ir.code.Opcodes.IF;
import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_GET;
import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_PUT;
import static com.android.tools.r8.ir.code.Opcodes.INVOKE_DIRECT;
import static com.android.tools.r8.ir.code.Opcodes.INVOKE_INTERFACE;
import static com.android.tools.r8.ir.code.Opcodes.INVOKE_STATIC;
import static com.android.tools.r8.ir.code.Opcodes.INVOKE_SUPER;
import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL;
import static com.android.tools.r8.ir.code.Opcodes.RETURN;
import static com.android.tools.r8.ir.code.Opcodes.STATIC_PUT;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.Assume;
import com.android.tools.r8.ir.code.InstanceGet;
import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionOrPhi;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.WorkList;
import java.util.Objects;
public class TypeUtils {
private static class UserAndValuePair {
final InstructionOrPhi user;
final Value value;
UserAndValuePair(InstructionOrPhi user, Value value) {
this.user = user;
this.value = value;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
UserAndValuePair pair = (UserAndValuePair) obj;
return user == pair.user && value == pair.value;
}
@Override
public int hashCode() {
return Objects.hash(user, value);
}
}
/**
* Returns the "use type" of a given value {@link Value}, i.e., the weakest static type that this
* value must have in order for the program to type check.
*/
public static TypeElement computeUseType(
AppView<AppInfoWithLiveness> appView, ProgramMethod method, Value value) {
TypeElement staticType = value.getType();
TypeElement useType = TypeElement.getBottom();
WorkList<UserAndValuePair> users = WorkList.newEqualityWorkList();
enqueueUsers(value, users);
while (users.hasNext()) {
UserAndValuePair item = users.next();
InstructionOrPhi user = item.user;
if (user.isPhi()) {
enqueueUsers(user.asPhi(), users);
} else {
Instruction instruction = user.asInstruction();
TypeElement instructionUseType =
computeUseTypeForInstruction(appView, method, instruction, item.value, users);
useType = useType.join(instructionUseType, appView);
if (useType.isTop() || useType.equalUpToNullability(staticType)) {
// Bail-out.
return staticType;
}
}
}
return useType;
}
private static void enqueueUsers(Value value, WorkList<UserAndValuePair> users) {
for (Instruction user : value.uniqueUsers()) {
users.addIfNotSeen(new UserAndValuePair(user, value));
}
for (Phi user : value.uniquePhiUsers()) {
users.addIfNotSeen(new UserAndValuePair(user, value));
}
}
private static TypeElement computeUseTypeForInstruction(
AppView<AppInfoWithLiveness> appView,
ProgramMethod method,
Instruction instruction,
Value value,
WorkList<UserAndValuePair> users) {
switch (instruction.opcode()) {
case ASSUME:
return computeUseTypeForAssume(instruction.asAssume(), users);
case CHECK_CAST:
case IF:
return TypeElement.getBottom();
case INSTANCE_GET:
return computeUseTypeForInstanceGet(appView, instruction.asInstanceGet());
case INSTANCE_PUT:
return computeUseTypeForInstancePut(appView, instruction.asInstancePut(), value);
case INVOKE_DIRECT:
case INVOKE_INTERFACE:
case INVOKE_STATIC:
case INVOKE_SUPER:
case INVOKE_VIRTUAL:
return computeUseTypeForInvoke(appView, instruction.asInvokeMethod(), value);
case RETURN:
return computeUseTypeForReturn(appView, method);
case STATIC_PUT:
return computeUseTypeForStaticPut(appView, instruction.asStaticPut());
default:
// Bail out for unhandled instructions.
return TypeElement.getTop();
}
}
private static TypeElement computeUseTypeForAssume(
Assume assume, WorkList<UserAndValuePair> users) {
enqueueUsers(assume.outValue(), users);
return TypeElement.getBottom();
}
private static TypeElement computeUseTypeForInstanceGet(
AppView<AppInfoWithLiveness> appView, InstanceGet instanceGet) {
return instanceGet.getField().getHolderType().toTypeElement(appView);
}
private static TypeElement computeUseTypeForInstancePut(
AppView<AppInfoWithLiveness> appView, InstancePut instancePut, Value value) {
DexField field = instancePut.getField();
TypeElement useType = TypeElement.getBottom();
if (instancePut.object() == value) {
useType = useType.join(field.getHolderType().toTypeElement(appView), appView);
}
if (instancePut.value() == value) {
useType = useType.join(field.getType().toTypeElement(appView), appView);
}
return useType;
}
private static TypeElement computeUseTypeForInvoke(
AppView<AppInfoWithLiveness> appView, InvokeMethod invoke, Value value) {
TypeElement useType = TypeElement.getBottom();
for (int argumentIndex = 0; argumentIndex < invoke.arguments().size(); argumentIndex++) {
Value argument = invoke.getArgument(argumentIndex);
if (argument != value) {
continue;
}
TypeElement useTypeForArgument =
invoke
.getInvokedMethod()
.getArgumentType(argumentIndex, invoke.isInvokeStatic())
.toTypeElement(appView);
useType = useType.join(useTypeForArgument, appView);
}
assert !useType.isBottom();
return useType;
}
private static TypeElement computeUseTypeForReturn(
AppView<AppInfoWithLiveness> appView, ProgramMethod method) {
return method.getReturnType().toTypeElement(appView);
}
private static TypeElement computeUseTypeForStaticPut(
AppView<AppInfoWithLiveness> appView, StaticPut staticPut) {
return staticPut.getField().getType().toTypeElement(appView);
}
public static boolean isNullPointerException(TypeElement type, AppView<?> appView) {
return type.isClassType()
&& type.asClassType().getClassType() == appView.dexItemFactory().npeType;
}
}