| // Copyright (c) 2018, 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.desugar; | 
 |  | 
 | import com.android.tools.r8.dex.Constants; | 
 | import com.android.tools.r8.graph.AppInfo; | 
 | import com.android.tools.r8.graph.AppView; | 
 | import com.android.tools.r8.graph.CfCode; | 
 | import com.android.tools.r8.graph.ClassAccessFlags; | 
 | import com.android.tools.r8.graph.DexAnnotationSet; | 
 | import com.android.tools.r8.graph.DexApplication.Builder; | 
 | import com.android.tools.r8.graph.DexEncodedField; | 
 | import com.android.tools.r8.graph.DexEncodedMethod; | 
 | import com.android.tools.r8.graph.DexItemFactory; | 
 | import com.android.tools.r8.graph.DexMethod; | 
 | import com.android.tools.r8.graph.DexProgramClass; | 
 | import com.android.tools.r8.graph.DexProto; | 
 | import com.android.tools.r8.graph.DexType; | 
 | import com.android.tools.r8.graph.DexTypeList; | 
 | import com.android.tools.r8.graph.MethodAccessFlags; | 
 | import com.android.tools.r8.graph.ParameterAnnotationsList; | 
 | 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.InvokeStatic; | 
 | import com.android.tools.r8.ir.conversion.IRConverter; | 
 | import com.android.tools.r8.ir.desugar.backports.BackportedMethods; | 
 | import com.android.tools.r8.origin.SynthesizedOrigin; | 
 | import com.android.tools.r8.utils.InternalOptions; | 
 | import com.google.common.collect.Sets; | 
 | import java.util.Collections; | 
 | import java.util.Set; | 
 | import java.util.concurrent.ExecutionException; | 
 | import java.util.concurrent.ExecutorService; | 
 |  | 
 | // Try with resources outlining processor. Handles $closeResource methods | 
 | // synthesized by java 9 compiler. | 
 | // | 
 | // Works in two phases: | 
 | //   a. during method code processing finds all references to $closeResource(...) synthesized | 
 | //      by java compiler, and replaces them with references to a special utility class method. | 
 | //   b. after all methods are processed and if there was at least one method referencing | 
 | //      $closeResource(...), it synthesizes utility class with appropriate methods. | 
 | // | 
 | // Note that we don't remove $closeResource(...) synthesized by java compiler, relying on | 
 | // tree shaking to remove them since now they should not be referenced. | 
 | // | 
 | public final class TwrCloseResourceRewriter { | 
 |  | 
 |   public static final String UTILITY_CLASS_NAME = "$r8$twr$utility"; | 
 |   public static final String UTILITY_CLASS_DESCRIPTOR = "L$r8$twr$utility;"; | 
 |  | 
 |   private final AppView<?> appView; | 
 |   private final IRConverter converter; | 
 |  | 
 |   private final DexMethod twrCloseResourceMethod; | 
 |  | 
 |   private final Set<DexProgramClass> referencingClasses = Sets.newConcurrentHashSet(); | 
 |  | 
 |   public TwrCloseResourceRewriter(AppView<?> appView, IRConverter converter) { | 
 |     this.appView = appView; | 
 |     this.converter = converter; | 
 |  | 
 |     DexItemFactory dexItemFactory = appView.dexItemFactory(); | 
 |     DexType twrUtilityClass = dexItemFactory.createType(UTILITY_CLASS_DESCRIPTOR); | 
 |     DexProto twrCloseResourceProto = | 
 |         dexItemFactory.createProto( | 
 |             dexItemFactory.voidType, dexItemFactory.throwableType, dexItemFactory.objectType); | 
 |     this.twrCloseResourceMethod = | 
 |         dexItemFactory.createMethod( | 
 |             twrUtilityClass, twrCloseResourceProto, dexItemFactory.twrCloseResourceMethodName); | 
 |   } | 
 |  | 
 |   public static boolean isUtilityClassDescriptor(DexType type) { | 
 |     return type.descriptor.toString().equals(UTILITY_CLASS_DESCRIPTOR); | 
 |   } | 
 |  | 
 |   // Rewrites calls to $closeResource() method. Can be invoked concurrently. | 
 |   public void rewriteMethodCode(IRCode code) { | 
 |     InstructionListIterator iterator = code.instructionListIterator(); | 
 |     AppInfo appInfo = appView.appInfo(); | 
 |     while (iterator.hasNext()) { | 
 |       Instruction instruction = iterator.next(); | 
 |       if (!instruction.isInvokeStatic()) { | 
 |         continue; | 
 |       } | 
 |       InvokeStatic invoke = instruction.asInvokeStatic(); | 
 |       if (!isSynthesizedCloseResourceMethod(invoke.getInvokedMethod(), appView)) { | 
 |         continue; | 
 |       } | 
 |  | 
 |       // Replace with a call to a synthetic utility with the only | 
 |       // implementation of the method $closeResource. | 
 |       assert invoke.outValue() == null; | 
 |       assert invoke.inValues().size() == 2; | 
 |       iterator.replaceCurrentInstruction( | 
 |           new InvokeStatic(twrCloseResourceMethod, null, invoke.inValues())); | 
 |  | 
 |       // Mark as a class referencing utility class. | 
 |       referencingClasses.add(appInfo.definitionFor(code.method().holder()).asProgramClass()); | 
 |     } | 
 |   } | 
 |  | 
 |   public static boolean isSynthesizedCloseResourceMethod(DexMethod method, AppView<?> appView) { | 
 |     DexMethod original = appView.graphLens().getOriginalMethodSignature(method); | 
 |     assert original != null; | 
 |     // We consider all methods of *any* class with expected name and signature | 
 |     // to be synthesized by java 9 compiler for try-with-resources, reasoning: | 
 |     // | 
 |     //  * we need to look to all potential classes because the calls might be | 
 |     //    moved by inlining. | 
 |     //  * theoretically we could check appropriate encoded method for having | 
 |     //    right attributes, but it still does not guarantee much since we also | 
 |     //    need to look into code and doing this seems an overkill | 
 |     // | 
 |     DexItemFactory dexItemFactory = appView.dexItemFactory(); | 
 |     return original.name == dexItemFactory.twrCloseResourceMethodName | 
 |         && original.proto == dexItemFactory.twrCloseResourceMethodProto; | 
 |   } | 
 |  | 
 |   public void synthesizeUtilityClass( | 
 |       Builder<?> builder, ExecutorService executorService, InternalOptions options) | 
 |       throws ExecutionException { | 
 |     if (referencingClasses.isEmpty()) { | 
 |       return; | 
 |     } | 
 |  | 
 |     // The only encoded method. | 
 |     CfCode code = | 
 |         BackportedMethods.CloseResourceMethod_closeResourceImpl( | 
 |             options, twrCloseResourceMethod); | 
 |     MethodAccessFlags flags = MethodAccessFlags.fromSharedAccessFlags( | 
 |         Constants.ACC_PUBLIC | Constants.ACC_STATIC | Constants.ACC_SYNTHETIC, false); | 
 |     DexEncodedMethod method = new DexEncodedMethod(twrCloseResourceMethod, | 
 |         flags, DexAnnotationSet.empty(), ParameterAnnotationsList.empty(), code, true); | 
 |  | 
 |     // Create utility class. | 
 |     DexProgramClass utilityClass = | 
 |         new DexProgramClass( | 
 |             twrCloseResourceMethod.holder, | 
 |             null, | 
 |             new SynthesizedOrigin("twr utility class", getClass()), | 
 |             ClassAccessFlags.fromSharedAccessFlags(Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC), | 
 |             appView.dexItemFactory().objectType, | 
 |             DexTypeList.empty(), | 
 |             null, | 
 |             null, | 
 |             Collections.emptyList(), | 
 |             null, | 
 |             Collections.emptyList(), | 
 |             DexAnnotationSet.empty(), | 
 |             DexEncodedField.EMPTY_ARRAY, | 
 |             DexEncodedField.EMPTY_ARRAY, | 
 |             new DexEncodedMethod[] {method}, | 
 |             DexEncodedMethod.EMPTY_ARRAY, | 
 |             appView.dexItemFactory().getSkipNameValidationForTesting(), | 
 |             DexProgramClass::checksumFromType, | 
 |             referencingClasses); | 
 |  | 
 |     // Process created class and method. | 
 |     AppInfo appInfo = appView.appInfo(); | 
 |     boolean addToMainDexList = | 
 |         referencingClasses.stream().anyMatch(clazz -> appInfo.isInMainDexList(clazz.type)); | 
 |     appInfo.addSynthesizedClass(utilityClass); | 
 |     converter.optimizeSynthesizedClass(utilityClass, executorService); | 
 |     builder.addSynthesizedClass(utilityClass, addToMainDexList); | 
 |   } | 
 | } |