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