aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/tools/android
diff options
context:
space:
mode:
authorGravatar cnsun <cnsun@google.com>2017-08-30 20:41:37 +0200
committerGravatar Vladimir Moskva <vladmos@google.com>2017-08-31 13:44:17 +0200
commite51e9924aebb48931224ba6da807c924fba240ae (patch)
tree681c3b4366c607adf3ddbd6bf0e6e3fd55c45e18 /src/tools/android
parent11073bdd65429d9548ab94df8071c849e65224ba (diff)
Add a closeResource(Throwable throwable, Object resource) in the runtime
library. Javac9 generates a helper method $closeResource(Throwable, AutoCloseable) sometimes for try-with-resources. Now we rewrite the call to call our version to avoid the dependency on AutoCloseable. RELNOTES: None PiperOrigin-RevId: 167025276
Diffstat (limited to 'src/tools/android')
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/desugar/TryWithResourcesRewriter.java57
-rw-r--r--src/tools/android/java/com/google/devtools/build/android/desugar/runtime/ThrowableExtension.java48
2 files changed, 101 insertions, 4 deletions
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/TryWithResourcesRewriter.java b/src/tools/android/java/com/google/devtools/build/android/desugar/TryWithResourcesRewriter.java
index 2cdcae1564..f7a3a7d156 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/TryWithResourcesRewriter.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/TryWithResourcesRewriter.java
@@ -14,6 +14,8 @@
package com.google.devtools.build.android.desugar;
import static com.google.common.base.Preconditions.checkNotNull;
+import static org.objectweb.asm.Opcodes.ACC_STATIC;
+import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
import static org.objectweb.asm.Opcodes.ASM5;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
@@ -88,6 +90,10 @@ public class TryWithResourcesRewriter extends ClassVisitor {
.put("(Ljava/io/PrintWriter;)V", "(Ljava/lang/Throwable;Ljava/io/PrintWriter;)V")
.build();
+ static final String CLOSE_RESOURCE_METHOD_NAME = "$closeResource";
+ static final String CLOSE_RESOURCE_METHOD_DESC =
+ "(Ljava/lang/Throwable;Ljava/lang/AutoCloseable;)V";
+
private final ClassLoader classLoader;
private final Set<String> visitedExceptionTypes;
private final AtomicInteger numOfTryWithResourcesInvoked;
@@ -129,22 +135,38 @@ public class TryWithResourcesRewriter extends ClassVisitor {
// collect exception types.
Collections.addAll(visitedExceptionTypes, exceptions);
}
+ if (isSyntheticCloseResourceMethod(access, name, desc)) {
+ return null; // Discard this method.
+ }
+
MethodVisitor visitor = super.cv.visitMethod(access, name, desc, signature, exceptions);
return visitor == null || shouldCurrentClassBeIgnored
? visitor
- : new TryWithResourceVisitor(internalName + "." + name + desc, visitor, classLoader);
+ : new TryWithResourceVisitor(internalName, name + desc, visitor, classLoader);
+ }
+
+ private boolean isSyntheticCloseResourceMethod(int access, String name, String desc) {
+ return BitFlags.isSet(access, ACC_SYNTHETIC | ACC_STATIC)
+ && CLOSE_RESOURCE_METHOD_NAME.equals(name)
+ && CLOSE_RESOURCE_METHOD_DESC.equals(desc);
}
private class TryWithResourceVisitor extends MethodVisitor {
private final ClassLoader classLoader;
/** For debugging purpose. Enrich exception information. */
+ private final String internalName;
+
private final String methodSignature;
public TryWithResourceVisitor(
- String methodSignature, MethodVisitor methodVisitor, ClassLoader classLoader) {
+ String internalName,
+ String methodSignature,
+ MethodVisitor methodVisitor,
+ ClassLoader classLoader) {
super(ASM5, methodVisitor);
this.classLoader = classLoader;
+ this.internalName = internalName;
this.methodSignature = methodSignature;
}
@@ -158,6 +180,17 @@ public class TryWithResourcesRewriter extends ClassVisitor {
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
+ if (isCallToSyntheticCloseResource(opcode, owner, name, desc)) {
+ // Rewrite the call to the runtime library.
+ super.visitMethodInsn(
+ opcode,
+ THROWABLE_EXTENSION_INTERNAL_NAME,
+ "closeResource",
+ "(Ljava/lang/Throwable;Ljava/lang/Object;)V",
+ itf);
+ return;
+ }
+
if (!isMethodCallTargeted(opcode, owner, name, desc)) {
super.visitMethodInsn(opcode, owner, name, desc, itf);
return;
@@ -168,6 +201,23 @@ public class TryWithResourcesRewriter extends ClassVisitor {
INVOKESTATIC, THROWABLE_EXTENSION_INTERNAL_NAME, name, METHOD_DESC_MAP.get(desc), false);
}
+ private boolean isCallToSyntheticCloseResource(
+ int opcode, String owner, String name, String desc) {
+ if (opcode != INVOKESTATIC) {
+ return false;
+ }
+ if (!internalName.equals(owner)) {
+ return false;
+ }
+ if (!CLOSE_RESOURCE_METHOD_NAME.equals(name)) {
+ return false;
+ }
+ if (!CLOSE_RESOURCE_METHOD_DESC.equals(desc)) {
+ return false;
+ }
+ return true;
+ }
+
private boolean isMethodCallTargeted(int opcode, String owner, String name, String desc) {
if (opcode != INVOKEVIRTUAL) {
return false;
@@ -184,7 +234,8 @@ public class TryWithResourcesRewriter extends ClassVisitor {
return throwableClass.isAssignableFrom(klass);
} catch (ClassNotFoundException e) {
throw new AssertionError(
- "Failed to load class when desugaring method " + methodSignature, e);
+ "Failed to load class when desugaring method " + internalName + "." + methodSignature,
+ e);
}
}
}
diff --git a/src/tools/android/java/com/google/devtools/build/android/desugar/runtime/ThrowableExtension.java b/src/tools/android/java/com/google/devtools/build/android/desugar/runtime/ThrowableExtension.java
index 365884b690..070363a0b8 100644
--- a/src/tools/android/java/com/google/devtools/build/android/desugar/runtime/ThrowableExtension.java
+++ b/src/tools/android/java/com/google/devtools/build/android/desugar/runtime/ThrowableExtension.java
@@ -13,12 +13,15 @@
// limitations under the License.
package com.google.devtools.build.android.desugar.runtime;
+import java.io.Closeable;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
@@ -43,10 +46,14 @@ public final class ThrowableExtension {
public static final String SYSTEM_PROPERTY_TWR_DISABLE_MIMIC =
"com.google.devtools.build.android.desugar.runtime.twr_disable_mimic";
+ // Visible for testing.
+ static final int API_LEVEL;
+
static {
AbstractDesugaringStrategy strategy;
+ Integer apiLevel = null;
try {
- Integer apiLevel = readApiLevelFromBuildVersion();
+ apiLevel = readApiLevelFromBuildVersion();
if (apiLevel != null && apiLevel.intValue() >= 19) {
strategy = new ReuseDesugaringStrategy();
} else if (useMimicStrategy()) {
@@ -66,6 +73,7 @@ public final class ThrowableExtension {
strategy = new NullDesugaringStrategy();
}
STRATEGY = strategy;
+ API_LEVEL = apiLevel == null ? 1 : apiLevel.intValue();
}
public static AbstractDesugaringStrategy getStrategy() {
@@ -92,6 +100,44 @@ public final class ThrowableExtension {
STRATEGY.printStackTrace(receiver, stream);
}
+ public static void closeResource(Throwable throwable, Object resource) throws Throwable {
+ if (resource == null) {
+ return;
+ }
+ try {
+ if (API_LEVEL >= 19) {
+ ((AutoCloseable) resource).close();
+ } else {
+ if (resource instanceof Closeable) {
+ ((Closeable) resource).close();
+ } else {
+ try {
+ Method method = resource.getClass().getMethod("close");
+ method.invoke(resource);
+ } catch (NoSuchMethodException | SecurityException e) {
+ throw new AssertionError(resource.getClass() + " does not have a close() method.", e);
+ } catch (IllegalAccessException
+ | IllegalArgumentException
+ | ExceptionInInitializerError e) {
+ throw new AssertionError("Fail to call close() on " + resource.getClass(), e);
+ } catch (InvocationTargetException e) {
+ // Exception occurs during the invocation to the close method. The cause is the real
+ // exception.
+ Throwable cause = e.getCause();
+ throw cause;
+ }
+ }
+ }
+ } catch (Throwable e) {
+ if (throwable != null) {
+ addSuppressed(throwable, e);
+ throw throwable;
+ } else {
+ throw e;
+ }
+ }
+ }
+
private static boolean useMimicStrategy() {
return !Boolean.getBoolean(SYSTEM_PROPERTY_TWR_DISABLE_MIMIC);
}