• 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.io;
15 
16 import java.io.IOException;
17 import java.io.InputStream;
18 import javax.annotation.Nullable;
19 import org.objectweb.asm.Attribute;
20 import org.objectweb.asm.ClassReader;
21 import org.objectweb.asm.ClassVisitor;
22 import org.objectweb.asm.ClassWriter;
23 import org.objectweb.asm.Opcodes;
24 import org.objectweb.asm.commons.ClassRemapper;
25 import org.objectweb.asm.commons.Remapper;
26 
27 /** Utility class to prefix or unprefix class names of core library classes */
28 public class CoreLibraryRewriter {
29   private final String prefix;
30 
CoreLibraryRewriter(String prefix)31   public CoreLibraryRewriter(String prefix) {
32     this.prefix = prefix;
33   }
34 
35   /**
36    * Factory method that returns either a normal ClassReader if prefix is empty, or a ClassReader
37    * with a ClassRemapper that prefixes class names of core library classes if prefix is not empty.
38    */
reader(InputStream content)39   public ClassReader reader(InputStream content) throws IOException {
40     if (prefix.isEmpty()) {
41       return new ClassReader(content);
42     } else {
43       return new PrefixingClassReader(content, prefix);
44     }
45   }
46 
47   /**
48    * Factory method that returns a ClassVisitor that delegates to a ClassWriter, removing prefix
49    * from core library class names if it is not empty.
50    */
writer(int flags)51   public UnprefixingClassWriter writer(int flags) {
52     return new UnprefixingClassWriter(flags);
53   }
54 
shouldPrefix(String typeName)55   static boolean shouldPrefix(String typeName) {
56     return (typeName.startsWith("java/") || typeName.startsWith("sun/")) && !except(typeName);
57   }
58 
except(String typeName)59   private static boolean except(String typeName) {
60     if (typeName.startsWith("java/lang/invoke/")) {
61       return true;
62     }
63 
64     switch (typeName) {
65         // Autoboxed types
66       case "java/lang/Boolean":
67       case "java/lang/Byte":
68       case "java/lang/Character":
69       case "java/lang/Double":
70       case "java/lang/Float":
71       case "java/lang/Integer":
72       case "java/lang/Long":
73       case "java/lang/Number":
74       case "java/lang/Short":
75 
76         // Special types
77       case "java/lang/Class":
78       case "java/lang/Object":
79       case "java/lang/String":
80       case "java/lang/Throwable":
81         return true;
82 
83       default: // fall out
84     }
85 
86     return false;
87   }
88 
getPrefix()89   public String getPrefix() {
90     return prefix;
91   }
92 
93   /** Removes prefix from class names */
unprefix(String typeName)94   public String unprefix(String typeName) {
95     if (prefix.isEmpty() || !typeName.startsWith(prefix)) {
96       return typeName;
97     }
98     return typeName.substring(prefix.length());
99   }
100 
101   /** ClassReader that prefixes core library class names as they are read */
102   private static class PrefixingClassReader extends ClassReader {
103     private final String prefix;
104 
PrefixingClassReader(InputStream content, String prefix)105     PrefixingClassReader(InputStream content, String prefix) throws IOException {
106       super(content);
107       this.prefix = prefix;
108     }
109 
110     @Override
accept(ClassVisitor cv, Attribute[] attrs, int flags)111     public void accept(ClassVisitor cv, Attribute[] attrs, int flags) {
112       cv =
113           new ClassRemapper(
114               cv,
115               new Remapper() {
116                 @Override
117                 public String map(String typeName) {
118                   return prefix(typeName);
119                 }
120               });
121       super.accept(cv, attrs, flags);
122     }
123 
124     @Override
getClassName()125     public String getClassName() {
126       return prefix(super.getClassName());
127     }
128 
129     @Override
getSuperName()130     public String getSuperName() {
131       String result = super.getSuperName();
132       return result != null ? prefix(result) : null;
133     }
134 
135     @Override
getInterfaces()136     public String[] getInterfaces() {
137       String[] result = super.getInterfaces();
138       for (int i = 0, len = result.length; i < len; ++i) {
139         result[i] = prefix(result[i]);
140       }
141       return result;
142     }
143 
144     /** Prefixes core library class names with prefix. */
prefix(String typeName)145     private String prefix(String typeName) {
146       if (shouldPrefix(typeName)) {
147         return prefix + typeName;
148       }
149       return typeName;
150     }
151   }
152 
153   /**
154    * ClassVisitor that delegates to a ClassWriter, but removes a prefix as each class is written.
155    * The unprefixing is optimized out if prefix is empty.
156    */
157   public class UnprefixingClassWriter extends ClassVisitor {
158     private final ClassWriter writer;
159 
160     private String finalClassName;
161 
UnprefixingClassWriter(int flags)162     UnprefixingClassWriter(int flags) {
163       super(Opcodes.ASM6);
164       this.writer = new ClassWriter(flags);
165       this.cv = this.writer;
166       if (!prefix.isEmpty()) {
167         this.cv =
168             new ClassRemapper(
169                 this.writer,
170                 new Remapper() {
171                   @Override
172                   public String map(String typeName) {
173                     return unprefix(typeName);
174                   }
175                 });
176       }
177     }
178 
179     /** Returns the (unprefixed) name of the class once written. */
180     @Nullable
getClassName()181     public String getClassName() {
182       return finalClassName;
183     }
184 
toByteArray()185     public byte[] toByteArray() {
186       return writer.toByteArray();
187     }
188 
189     @Override
visit( int version, int access, String name, String signature, String superName, String[] interfaces)190     public void visit(
191         int version,
192         int access,
193         String name,
194         String signature,
195         String superName,
196         String[] interfaces) {
197       finalClassName = unprefix(name);
198       super.visit(version, access, name, signature, superName, interfaces);
199     }
200   }
201 }
202