• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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