| // 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; |
| } |
| } |