1c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun// Copyright 2017 The Bazel Authors. All rights reserved. 2c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun// 3c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun// Licensed under the Apache License, Version 2.0 (the "License"); 4c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun// you may not use this file except in compliance with the License. 5c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun// You may obtain a copy of the License at 6c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun// 7c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun// http://www.apache.org/licenses/LICENSE-2.0 8c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun// 9c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun// Unless required by applicable law or agreed to in writing, software 10c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun// distributed under the License is distributed on an "AS IS" BASIS, 11c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun// See the License for the specific language governing permissions and 13c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun// limitations under the License. 14c5e345ca89228abdcff50024d8728a4e5bf87d44cnsunpackage com.google.devtools.build.android.desugar; 15c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun 166dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsunimport static com.google.common.base.Preconditions.checkNotNull; 17c5e345ca89228abdcff50024d8728a4e5bf87d44cnsunimport static org.objectweb.asm.Opcodes.ASM5; 18c5e345ca89228abdcff50024d8728a4e5bf87d44cnsunimport static org.objectweb.asm.Opcodes.INVOKESTATIC; 19c5e345ca89228abdcff50024d8728a4e5bf87d44cnsunimport static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; 20c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun 21c5e345ca89228abdcff50024d8728a4e5bf87d44cnsunimport com.google.common.base.Function; 22c5e345ca89228abdcff50024d8728a4e5bf87d44cnsunimport com.google.common.collect.FluentIterable; 23c5e345ca89228abdcff50024d8728a4e5bf87d44cnsunimport com.google.common.collect.ImmutableMap; 24c5e345ca89228abdcff50024d8728a4e5bf87d44cnsunimport com.google.common.collect.ImmutableMultimap; 25c5e345ca89228abdcff50024d8728a4e5bf87d44cnsunimport com.google.common.collect.ImmutableSet; 266dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsunimport java.util.Collections; 276dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsunimport java.util.Set; 28c5e345ca89228abdcff50024d8728a4e5bf87d44cnsunimport java.util.concurrent.atomic.AtomicInteger; 29c5e345ca89228abdcff50024d8728a4e5bf87d44cnsunimport org.objectweb.asm.ClassVisitor; 306dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsunimport org.objectweb.asm.Label; 31c5e345ca89228abdcff50024d8728a4e5bf87d44cnsunimport org.objectweb.asm.MethodVisitor; 32c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun 33c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun/** 34c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun * Desugar try-with-resources. This class visitor intercepts calls to the following methods, and 35c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun * redirect them to ThrowableExtension. 36c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun * <li>{@code Throwable.addSuppressed(Throwable)} 37c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun * <li>{@code Throwable.getSuppressed()} 38c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun * <li>{@code Throwable.printStackTrace()} 39c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun * <li>{@code Throwable.printStackTrace(PrintStream)} 40c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun * <li>{@code Throwable.printStackTrace(PringWriter)} 41c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun */ 42c5e345ca89228abdcff50024d8728a4e5bf87d44cnsunpublic class TryWithResourcesRewriter extends ClassVisitor { 43c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun 44c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun private static final String RUNTIME_PACKAGE_INTERNAL_NAME = 45c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun "com/google/devtools/build/android/desugar/runtime"; 46c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun 47c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun static final String THROWABLE_EXTENSION_INTERNAL_NAME = 48c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun RUNTIME_PACKAGE_INTERNAL_NAME + '/' + "ThrowableExtension"; 49c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun 50c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun /** The extension classes for java.lang.Throwable. */ 51c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun static final ImmutableSet<String> THROWABLE_EXT_CLASS_INTERNAL_NAMES = 52c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun ImmutableSet.of( 53c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun THROWABLE_EXTENSION_INTERNAL_NAME, 54c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun THROWABLE_EXTENSION_INTERNAL_NAME + "$AbstractDesugaringStrategy", 55950d20dcbc7760aa9c107c9e0c3e3e79ddc0d9adcnsun THROWABLE_EXTENSION_INTERNAL_NAME + "$ConcurrentWeakIdentityHashMap", 56950d20dcbc7760aa9c107c9e0c3e3e79ddc0d9adcnsun THROWABLE_EXTENSION_INTERNAL_NAME + "$ConcurrentWeakIdentityHashMap$WeakKey", 57c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun THROWABLE_EXTENSION_INTERNAL_NAME + "$MimicDesugaringStrategy", 58c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun THROWABLE_EXTENSION_INTERNAL_NAME + "$NullDesugaringStrategy", 59c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun THROWABLE_EXTENSION_INTERNAL_NAME + "$ReuseDesugaringStrategy"); 60c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun 61c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun /** The extension classes for java.lang.Throwable. All the names end with ".class" */ 62c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun static final ImmutableSet<String> THROWABLE_EXT_CLASS_INTERNAL_NAMES_WITH_CLASS_EXT = 63c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun FluentIterable.from(THROWABLE_EXT_CLASS_INTERNAL_NAMES) 64c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun .transform( 65c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun new Function<String, String>() { 66c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun @Override 67c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun public String apply(String s) { 68c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun return s + ".class"; 69c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun } 70c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun }) 71c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun .toSet(); 72c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun 73c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun static final ImmutableMultimap<String, String> TARGET_METHODS = 74c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun ImmutableMultimap.<String, String>builder() 75c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun .put("addSuppressed", "(Ljava/lang/Throwable;)V") 76c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun .put("getSuppressed", "()[Ljava/lang/Throwable;") 77c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun .put("printStackTrace", "()V") 78c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun .put("printStackTrace", "(Ljava/io/PrintStream;)V") 79c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun .put("printStackTrace", "(Ljava/io/PrintWriter;)V") 80c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun .build(); 81c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun 82c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun static final ImmutableMap<String, String> METHOD_DESC_MAP = 83c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun ImmutableMap.<String, String>builder() 84c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun .put("(Ljava/lang/Throwable;)V", "(Ljava/lang/Throwable;Ljava/lang/Throwable;)V") 85c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun .put("()[Ljava/lang/Throwable;", "(Ljava/lang/Throwable;)[Ljava/lang/Throwable;") 86c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun .put("()V", "(Ljava/lang/Throwable;)V") 87c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun .put("(Ljava/io/PrintStream;)V", "(Ljava/lang/Throwable;Ljava/io/PrintStream;)V") 88c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun .put("(Ljava/io/PrintWriter;)V", "(Ljava/lang/Throwable;Ljava/io/PrintWriter;)V") 89c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun .build(); 90c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun 91c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun private final ClassLoader classLoader; 926dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun private final Set<String> visitedExceptionTypes; 93c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun private final AtomicInteger numOfTryWithResourcesInvoked; 94885bce7f8d296b091d15c0007a7716f01c3fee33cnsun /** 95885bce7f8d296b091d15c0007a7716f01c3fee33cnsun * Indicate whether the current class being desugared should be ignored. If the current class is 96885bce7f8d296b091d15c0007a7716f01c3fee33cnsun * one of the runtime extension classes, then it should be ignored. 97885bce7f8d296b091d15c0007a7716f01c3fee33cnsun */ 98885bce7f8d296b091d15c0007a7716f01c3fee33cnsun private boolean shouldCurrentClassBeIgnored; 99c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun 100c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun public TryWithResourcesRewriter( 101c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun ClassVisitor classVisitor, 102c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun ClassLoader classLoader, 1036dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun Set<String> visitedExceptionTypes, 104c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun AtomicInteger numOfTryWithResourcesInvoked) { 105c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun super(ASM5, classVisitor); 106c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun this.classLoader = classLoader; 1076dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun this.visitedExceptionTypes = visitedExceptionTypes; 108c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun this.numOfTryWithResourcesInvoked = numOfTryWithResourcesInvoked; 109c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun } 110c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun 111c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun @Override 112885bce7f8d296b091d15c0007a7716f01c3fee33cnsun public void visit( 113885bce7f8d296b091d15c0007a7716f01c3fee33cnsun int version, 114885bce7f8d296b091d15c0007a7716f01c3fee33cnsun int access, 115885bce7f8d296b091d15c0007a7716f01c3fee33cnsun String name, 116885bce7f8d296b091d15c0007a7716f01c3fee33cnsun String signature, 117885bce7f8d296b091d15c0007a7716f01c3fee33cnsun String superName, 118885bce7f8d296b091d15c0007a7716f01c3fee33cnsun String[] interfaces) { 119885bce7f8d296b091d15c0007a7716f01c3fee33cnsun super.visit(version, access, name, signature, superName, interfaces); 120885bce7f8d296b091d15c0007a7716f01c3fee33cnsun shouldCurrentClassBeIgnored = THROWABLE_EXT_CLASS_INTERNAL_NAMES.contains(name); 121885bce7f8d296b091d15c0007a7716f01c3fee33cnsun } 122885bce7f8d296b091d15c0007a7716f01c3fee33cnsun 123885bce7f8d296b091d15c0007a7716f01c3fee33cnsun @Override 124c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun public MethodVisitor visitMethod( 125c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun int access, String name, String desc, String signature, String[] exceptions) { 1266dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun if (exceptions != null && exceptions.length > 0) { 1276dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun // collect exception types. 1286dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun Collections.addAll(visitedExceptionTypes, exceptions); 1296dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun } 130c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun MethodVisitor visitor = super.cv.visitMethod(access, name, desc, signature, exceptions); 131885bce7f8d296b091d15c0007a7716f01c3fee33cnsun return visitor == null || shouldCurrentClassBeIgnored 132c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun ? visitor 1336dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun : new TryWithResourceVisitor(name + desc, visitor, classLoader); 134c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun } 135c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun 136c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun private class TryWithResourceVisitor extends MethodVisitor { 137c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun 138c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun private final ClassLoader classLoader; 1396dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun /** For debugging purpose. Enrich exception information. */ 1406dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun private final String methodSignature; 141c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun 1426dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun public TryWithResourceVisitor( 1436dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun String methodSignature, MethodVisitor methodVisitor, ClassLoader classLoader) { 144c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun super(ASM5, methodVisitor); 145c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun this.classLoader = classLoader; 1466dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun this.methodSignature = methodSignature; 1476dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun } 1486dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun 1496dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun @Override 1506dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { 1516dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun if (type != null) { 1526dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun visitedExceptionTypes.add(type); // type in a try-catch block must extend Throwable. 1536dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun } 1546dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun super.visitTryCatchBlock(start, end, handler, type); 155c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun } 156c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun 157c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun @Override 158c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { 159c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun if (!isMethodCallTargeted(opcode, owner, name, desc)) { 160c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun super.visitMethodInsn(opcode, owner, name, desc, itf); 161c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun return; 162c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun } 163c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun numOfTryWithResourcesInvoked.incrementAndGet(); 1646dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun visitedExceptionTypes.add(checkNotNull(owner)); // owner extends Throwable. 165c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun super.visitMethodInsn( 166c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun INVOKESTATIC, THROWABLE_EXTENSION_INTERNAL_NAME, name, METHOD_DESC_MAP.get(desc), false); 167c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun } 168c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun 169c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun private boolean isMethodCallTargeted(int opcode, String owner, String name, String desc) { 170c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun if (opcode != INVOKEVIRTUAL) { 171c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun return false; 172c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun } 173c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun if (!TARGET_METHODS.containsEntry(name, desc)) { 174c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun return false; 175c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun } 1766dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun if (visitedExceptionTypes.contains(owner)) { 1776dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun return true; // The owner is an exception that has been visited before. 178c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun } 179c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun try { 180c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun Class<?> throwableClass = classLoader.loadClass("java.lang.Throwable"); 181c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun Class<?> klass = classLoader.loadClass(owner.replace('/', '.')); 182c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun return throwableClass.isAssignableFrom(klass); 183c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun } catch (ClassNotFoundException e) { 1846dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun throw new AssertionError( 1856dea9f5fe0a15fd4faf6fe16108b97f6dd299b2dcnsun "Failed to load class when desugaring method " + methodSignature, e); 186c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun } 187c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun } 188c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun } 189c5e345ca89228abdcff50024d8728a4e5bf87d44cnsun} 190