| // Copyright (c) 2022, 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.optimize; | |
| import com.android.tools.r8.graph.AppInfoWithClassHierarchy; | |
| import com.android.tools.r8.graph.AppView; | |
| import com.android.tools.r8.graph.DexClassAndMethod; | |
| import com.android.tools.r8.graph.ProgramMethod; | |
| import com.android.tools.r8.ir.code.BasicBlock; | |
| import com.android.tools.r8.ir.code.BasicBlockIterator; | |
| import com.android.tools.r8.ir.code.IRCode; | |
| import com.android.tools.r8.ir.code.Instruction; | |
| import com.android.tools.r8.ir.code.InstructionListIterator; | |
| import com.android.tools.r8.ir.code.InvokeMethod; | |
| import com.android.tools.r8.ir.code.Value; | |
| public class CheckNotNullConverter { | |
| public static void runIfNecessary(AppView<?> appView, IRCode code) { | |
| if (appView.enableWholeProgramOptimizations()) { | |
| run(appView.withClassHierarchy(), code); | |
| } | |
| } | |
| /** | |
| * Replace all calls to methods marked as a check-not-null method by a call to Object.getClass(), | |
| * using the first argument as the receiver for the new call. | |
| * | |
| * <p>If the invoke has an out-value, the out-value is replaced by the first argument to allow | |
| * removing the invoke. | |
| */ | |
| private static void run(AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code) { | |
| BasicBlockIterator blockIterator = code.listIterator(); | |
| while (blockIterator.hasNext()) { | |
| BasicBlock block = blockIterator.next(); | |
| InstructionListIterator instructionIterator = block.listIterator(code); | |
| while (instructionIterator.hasNext()) { | |
| Instruction instruction = instructionIterator.next(); | |
| if (instruction.isInvokeMethod()) { | |
| rewriteInvoke(appView, code, instructionIterator, instruction.asInvokeMethod()); | |
| } | |
| } | |
| } | |
| } | |
| private static void rewriteInvoke( | |
| AppView<? extends AppInfoWithClassHierarchy> appView, | |
| IRCode code, | |
| InstructionListIterator instructionIterator, | |
| InvokeMethod invoke) { | |
| ProgramMethod context = code.context(); | |
| DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, context); | |
| if (singleTarget == null || !singleTarget.getOptimizationInfo().isConvertCheckNotNull()) { | |
| return; | |
| } | |
| Value checkNotNullValue = invoke.getFirstNonReceiverArgument(); | |
| if (invoke.hasUsedOutValue()) { | |
| invoke.outValue().replaceUsers(checkNotNullValue); | |
| } | |
| if (checkNotNullValue.getType().nullability().isDefinitelyNotNull()) { | |
| instructionIterator.removeOrReplaceByDebugLocalRead(); | |
| } else { | |
| instructionIterator.replaceCurrentInstructionWithNullCheck(appView, checkNotNullValue); | |
| } | |
| } | |
| } |