diff options
Diffstat (limited to 'java/com/google/devtools/build/android/desugar/CloseResourceMethodScanner.java')
-rw-r--r-- | java/com/google/devtools/build/android/desugar/CloseResourceMethodScanner.java | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/java/com/google/devtools/build/android/desugar/CloseResourceMethodScanner.java b/java/com/google/devtools/build/android/desugar/CloseResourceMethodScanner.java new file mode 100644 index 0000000..a390d72 --- /dev/null +++ b/java/com/google/devtools/build/android/desugar/CloseResourceMethodScanner.java @@ -0,0 +1,116 @@ +// Copyright 2017 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.google.devtools.build.android.desugar; + +import com.google.common.base.Preconditions; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +/** + * A class scanner to check whether the class has the synthetic method $closeResource(Throwable, + * AutoCloseable). + */ +public class CloseResourceMethodScanner extends ClassVisitor { + + private boolean hasCloseResourceMethod; + private String internalName; + private int classFileVersion; + + public CloseResourceMethodScanner() { + super(Opcodes.ASM5); + } + + @Override + public void visit( + int version, + int access, + String name, + String signature, + String superName, + String[] interfaces) { + Preconditions.checkState(internalName == null, "This scanner has been used."); + this.internalName = name; + this.classFileVersion = version; + super.visit(version, access, name, signature, superName, interfaces); + } + + public boolean hasCloseResourceMethod() { + return hasCloseResourceMethod; + } + + @Override + public MethodVisitor visitMethod( + int access, String name, String desc, String signature, String[] exceptions) { + if (classFileVersion <= 50) { + // A Java 6 or below class file should not have $closeResource method. + return null; + } + if (!hasCloseResourceMethod) { + hasCloseResourceMethod = + TryWithResourcesRewriter.isSyntheticCloseResourceMethod(access, name, desc); + } + return new StackMapFrameCollector(name, desc); + } + + private class StackMapFrameCollector extends MethodVisitor { + + private final String methodSignature; + private boolean hasCallToCloseResourceMethod; + private boolean hasJumpInstructions; + private boolean hasStackMapFrame; + + public StackMapFrameCollector(String name, String desc) { + super(Opcodes.ASM5); + methodSignature = internalName + '.' + name + desc; + } + + @Override + public void visitEnd() { + if (!hasCallToCloseResourceMethod) { + return; + } + if (hasJumpInstructions && !hasStackMapFrame) { + throw new UnsupportedOperationException( + "The method " + + methodSignature + + " calls $closeResource(Throwable, AutoCloseable), " + + "and Desugar thus needs to perform type inference for it " + + "to rewrite $closeResourceMethod. " + + "However, this method has jump instructions, but does not have stack map frames. " + + "Please recompile this class with stack map frames."); + } + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { + if (!hasCallToCloseResourceMethod + && TryWithResourcesRewriter.isCallToSyntheticCloseResource( + internalName, opcode, owner, name, desc)) { + hasCallToCloseResourceMethod = true; + } + } + + @Override + public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) { + hasStackMapFrame = true; + } + + @Override + public void visitJumpInsn(int opcode, Label label) { + hasJumpInstructions = true; + } + } +} |