1 // Copyright 2017 The Bazel Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package com.google.devtools.build.android.desugar; 15 16 import com.google.common.base.Preconditions; 17 import org.objectweb.asm.ClassVisitor; 18 import org.objectweb.asm.Label; 19 import org.objectweb.asm.MethodVisitor; 20 import org.objectweb.asm.Opcodes; 21 22 /** 23 * A class scanner to check whether the class has the synthetic method $closeResource(Throwable, 24 * AutoCloseable). 25 */ 26 public class CloseResourceMethodScanner extends ClassVisitor { 27 28 private boolean hasCloseResourceMethod; 29 private String internalName; 30 private int classFileVersion; 31 CloseResourceMethodScanner()32 public CloseResourceMethodScanner() { 33 super(Opcodes.ASM6); 34 } 35 36 @Override visit( int version, int access, String name, String signature, String superName, String[] interfaces)37 public void visit( 38 int version, 39 int access, 40 String name, 41 String signature, 42 String superName, 43 String[] interfaces) { 44 Preconditions.checkState(internalName == null, "This scanner has been used."); 45 this.internalName = name; 46 this.classFileVersion = version; 47 super.visit(version, access, name, signature, superName, interfaces); 48 } 49 hasCloseResourceMethod()50 public boolean hasCloseResourceMethod() { 51 return hasCloseResourceMethod; 52 } 53 54 @Override visitMethod( int access, String name, String desc, String signature, String[] exceptions)55 public MethodVisitor visitMethod( 56 int access, String name, String desc, String signature, String[] exceptions) { 57 if (classFileVersion <= 50) { 58 // A Java 6 or below class file should not have $closeResource method. 59 return null; 60 } 61 if (!hasCloseResourceMethod) { 62 hasCloseResourceMethod = 63 TryWithResourcesRewriter.isSyntheticCloseResourceMethod(access, name, desc); 64 } 65 return new StackMapFrameCollector(name, desc); 66 } 67 68 private class StackMapFrameCollector extends MethodVisitor { 69 70 private final String methodSignature; 71 private boolean hasCallToCloseResourceMethod; 72 private boolean hasJumpInstructions; 73 private boolean hasStackMapFrame; 74 StackMapFrameCollector(String name, String desc)75 public StackMapFrameCollector(String name, String desc) { 76 super(Opcodes.ASM6); 77 methodSignature = internalName + '.' + name + desc; 78 } 79 80 @Override visitEnd()81 public void visitEnd() { 82 if (!hasCallToCloseResourceMethod) { 83 return; 84 } 85 if (hasJumpInstructions && !hasStackMapFrame) { 86 throw new UnsupportedOperationException( 87 "The method " 88 + methodSignature 89 + " calls $closeResource(Throwable, AutoCloseable), " 90 + "and Desugar thus needs to perform type inference for it " 91 + "to rewrite $closeResourceMethod. " 92 + "However, this method has jump instructions, but does not have stack map frames. " 93 + "Please recompile this class with stack map frames."); 94 } 95 } 96 97 @Override visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf)98 public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { 99 if (!hasCallToCloseResourceMethod 100 && TryWithResourcesRewriter.isCallToSyntheticCloseResource( 101 internalName, opcode, owner, name, desc)) { 102 hasCallToCloseResourceMethod = true; 103 } 104 } 105 106 @Override visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack)107 public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) { 108 hasStackMapFrame = true; 109 } 110 111 @Override visitJumpInsn(int opcode, Label label)112 public void visitJumpInsn(int opcode, Label label) { 113 hasJumpInstructions = true; 114 } 115 } 116 } 117