View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.shared.dependency.analyzer.asm;
20  
21  import java.nio.Buffer;
22  import java.nio.ByteBuffer;
23  import java.nio.ByteOrder;
24  import java.util.Collections;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.Map;
28  import java.util.Set;
29  
30  import org.objectweb.asm.Type;
31  
32  /**
33   * A small parser to read the constant pool directly, in case it contains references
34   * ASM does not support.
35   *
36   * Adapted from http://stackoverflow.com/a/32278587/23691
37   *
38   * Constant pool types:
39   *
40   * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4">JVM 9 Sepc</a>
41   * @see <a href="https://docs.oracle.com/javase/specs/jvms/se10/html/jvms-4.html#jvms-4.4">JVM 10 Sepc</a>
42   */
43  public class ConstantPoolParser {
44      /** Constant <code>HEAD=0xcafebabe</code> */
45      public static final int HEAD = 0xcafebabe;
46  
47      // Constant pool types
48  
49      /** Constant <code>CONSTANT_UTF8=1</code> */
50      public static final byte CONSTANT_UTF8 = 1;
51  
52      /** Constant <code>CONSTANT_INTEGER=3</code> */
53      public static final byte CONSTANT_INTEGER = 3;
54  
55      /** Constant <code>CONSTANT_FLOAT=4</code> */
56      public static final byte CONSTANT_FLOAT = 4;
57  
58      /** Constant <code>CONSTANT_LONG=5</code> */
59      public static final byte CONSTANT_LONG = 5;
60  
61      /** Constant <code>CONSTANT_DOUBLE=6</code> */
62      public static final byte CONSTANT_DOUBLE = 6;
63  
64      /** Constant <code>CONSTANT_CLASS=7</code> */
65      public static final byte CONSTANT_CLASS = 7;
66  
67      /** Constant <code>CONSTANT_STRING=8</code> */
68      public static final byte CONSTANT_STRING = 8;
69  
70      /** Constant <code>CONSTANT_FIELDREF=9</code> */
71      public static final byte CONSTANT_FIELDREF = 9;
72  
73      /** Constant <code>CONSTANT_METHODREF=10</code> */
74      public static final byte CONSTANT_METHODREF = 10;
75  
76      /** Constant <code>CONSTANT_INTERFACEMETHODREF=11</code> */
77      public static final byte CONSTANT_INTERFACEMETHODREF = 11;
78  
79      /** Constant <code>CONSTANT_NAME_AND_TYPE=12</code> */
80      public static final byte CONSTANT_NAME_AND_TYPE = 12;
81  
82      /** Constant <code>CONSTANT_METHODHANDLE=15</code> */
83      public static final byte CONSTANT_METHODHANDLE = 15;
84  
85      /** Constant <code>CONSTANT_METHOD_TYPE=16</code> */
86      public static final byte CONSTANT_METHOD_TYPE = 16;
87  
88      /** Constant <code>CONSTANT_INVOKE_DYNAMIC=18</code> */
89      public static final byte CONSTANT_INVOKE_DYNAMIC = 18;
90  
91      /** Constant <code>CONSTANT_MODULE=19</code> */
92      public static final byte CONSTANT_MODULE = 19;
93  
94      /** Constant <code>CONSTANT_PACKAGE=20</code> */
95      public static final byte CONSTANT_PACKAGE = 20;
96  
97      private static final int OXF0 = 0xf0;
98  
99      private static final int OXE0 = 0xe0;
100 
101     private static final int OX3F = 0x3F;
102 
103     static Set<String> getConstantPoolClassReferences(byte[] b) {
104         return parseConstantPoolClassReferences(ByteBuffer.wrap(b));
105     }
106 
107     static Set<String> parseConstantPoolClassReferences(ByteBuffer buf) {
108         if (buf.order(ByteOrder.BIG_ENDIAN).getInt() != HEAD) {
109             return Collections.emptySet();
110         }
111         buf.getChar();
112         buf.getChar(); // minor + ver
113         Set<Integer> classReferences = new HashSet<>();
114         Set<Integer> typeReferences = new HashSet<>();
115         Map<Integer, String> stringConstants = new HashMap<>();
116         for (int ix = 1, num = buf.getChar(); ix < num; ix++) {
117             byte tag = buf.get();
118             switch (tag) {
119                 default:
120                     throw new RuntimeException("Unknown constant pool type '" + tag + "'");
121                 case CONSTANT_UTF8:
122                     stringConstants.put(ix, decodeString(buf));
123                     break;
124                 case CONSTANT_CLASS:
125                     classReferences.add((int) buf.getChar());
126                     break;
127                 case CONSTANT_METHOD_TYPE:
128                     consumeMethodType(buf);
129                     break;
130                 case CONSTANT_FIELDREF:
131                 case CONSTANT_METHODREF:
132                 case CONSTANT_INTERFACEMETHODREF:
133                     consumeReference(buf);
134                     break;
135                 case CONSTANT_NAME_AND_TYPE:
136                     buf.getChar();
137                     typeReferences.add((int) buf.getChar());
138                     break;
139                 case CONSTANT_INTEGER:
140                     consumeInt(buf);
141                     break;
142                 case CONSTANT_FLOAT:
143                     consumeFloat(buf);
144                     break;
145                 case CONSTANT_DOUBLE:
146                     consumeDouble(buf);
147                     ix++;
148                     break;
149                 case CONSTANT_LONG:
150                     consumeLong(buf);
151                     ix++;
152                     break;
153                 case CONSTANT_STRING:
154                     consumeString(buf);
155                     break;
156                 case CONSTANT_METHODHANDLE:
157                     consumeMethodHandle(buf);
158                     break;
159                 case CONSTANT_INVOKE_DYNAMIC:
160                     consumeInvokeDynamic(buf);
161                     break;
162                 case CONSTANT_MODULE:
163                     consumeModule(buf);
164                     break;
165                 case CONSTANT_PACKAGE:
166                     consumePackage(buf);
167                     break;
168             }
169         }
170 
171         Set<String> result = new HashSet<>();
172 
173         for (Integer classRef : classReferences) {
174             addClassToResult(result, stringConstants.get(classRef));
175         }
176 
177         for (Integer typeRef : typeReferences) {
178             String typeName = stringConstants.get(typeRef);
179 
180             if (Type.getType(typeName).getSort() == Type.METHOD) {
181                 addClassToResult(result, Type.getReturnType(typeName).getInternalName());
182                 Type[] argumentTypes = Type.getArgumentTypes(typeName);
183                 for (Type argumentType : argumentTypes) {
184                     addClassToResult(result, argumentType.getInternalName());
185                 }
186             }
187         }
188 
189         return result;
190     }
191 
192     private static void addClassToResult(Set<String> result, String className) {
193         // filter out things from unnamed package, probably a false-positive
194         if (isImportableClass(className)) {
195             result.add(className);
196         }
197     }
198 
199     private static String decodeString(ByteBuffer buf) {
200         int size = buf.getChar();
201         // Explicit cast for compatibility with covariant return type on JDK 9's ByteBuffer
202         @SuppressWarnings("RedundantCast")
203         int oldLimit = ((Buffer) buf).limit();
204         ((Buffer) buf).limit(buf.position() + size);
205         StringBuilder sb = new StringBuilder(size + (size >> 1) + 16);
206         while (buf.hasRemaining()) {
207             byte b = buf.get();
208             if (b > 0) {
209                 sb.append((char) b);
210             } else {
211                 int b2 = buf.get();
212                 if ((b & OXF0) != OXE0) {
213                     sb.append((char) ((b & 0x1F) << 6 | b2 & OX3F));
214                 } else {
215                     int b3 = buf.get();
216                     sb.append((char) ((b & 0x0F) << 12 | (b2 & OX3F) << 6 | b3 & OX3F));
217                 }
218             }
219         }
220         ((Buffer) buf).limit(oldLimit);
221         return sb.toString();
222     }
223 
224     private static boolean isImportableClass(String className) {
225         // without a slash, class must be in unnamed package, which can't be imported
226         return className.indexOf('/') != -1;
227     }
228 
229     private static void consumeMethodType(ByteBuffer buf) {
230         buf.getChar();
231     }
232 
233     private static void consumeReference(ByteBuffer buf) {
234         buf.getChar();
235         buf.getChar();
236     }
237 
238     private static void consumeInt(ByteBuffer buf) {
239         buf.getInt();
240     }
241 
242     private static void consumeFloat(ByteBuffer buf) {
243         buf.getFloat();
244     }
245 
246     private static void consumeDouble(ByteBuffer buf) {
247         buf.getDouble();
248     }
249 
250     private static void consumeLong(ByteBuffer buf) {
251         buf.getLong();
252     }
253 
254     private static void consumeString(ByteBuffer buf) {
255         buf.getChar();
256     }
257 
258     private static void consumeMethodHandle(ByteBuffer buf) {
259         buf.get();
260         buf.getChar();
261     }
262 
263     private static void consumeInvokeDynamic(ByteBuffer buf) {
264         buf.getChar();
265         buf.getChar();
266     }
267 
268     private static void consumeModule(ByteBuffer buf) {
269         buf.getChar();
270     }
271 
272     private static void consumePackage(ByteBuffer buf) {
273         buf.getChar();
274     }
275 }