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   * @see <a href="https://docs.oracle.com/javase/specs/jvms/se20/html/jvms-4.html#jvms-4.4">JVM 20 Sepc</a>
43   */
44  public class ConstantPoolParser {
45      /** Constant <code>HEAD=0xcafebabe</code> */
46      public static final int HEAD = 0xcafebabe;
47  
48      // Constant pool types
49  
50      /** Constant <code>CONSTANT_UTF8=1</code> */
51      public static final byte CONSTANT_UTF8 = 1;
52  
53      /** Constant <code>CONSTANT_INTEGER=3</code> */
54      public static final byte CONSTANT_INTEGER = 3;
55  
56      /** Constant <code>CONSTANT_FLOAT=4</code> */
57      public static final byte CONSTANT_FLOAT = 4;
58  
59      /** Constant <code>CONSTANT_LONG=5</code> */
60      public static final byte CONSTANT_LONG = 5;
61  
62      /** Constant <code>CONSTANT_DOUBLE=6</code> */
63      public static final byte CONSTANT_DOUBLE = 6;
64  
65      /** Constant <code>CONSTANT_CLASS=7</code> */
66      public static final byte CONSTANT_CLASS = 7;
67  
68      /** Constant <code>CONSTANT_STRING=8</code> */
69      public static final byte CONSTANT_STRING = 8;
70  
71      /** Constant <code>CONSTANT_FIELDREF=9</code> */
72      public static final byte CONSTANT_FIELDREF = 9;
73  
74      /** Constant <code>CONSTANT_METHODREF=10</code> */
75      public static final byte CONSTANT_METHODREF = 10;
76  
77      /** Constant <code>CONSTANT_INTERFACEMETHODREF=11</code> */
78      public static final byte CONSTANT_INTERFACEMETHODREF = 11;
79  
80      /** Constant <code>CONSTANT_NAME_AND_TYPE=12</code> */
81      public static final byte CONSTANT_NAME_AND_TYPE = 12;
82  
83      /** Constant <code>CONSTANT_METHODHANDLE=15</code> */
84      public static final byte CONSTANT_METHODHANDLE = 15;
85  
86      /** Constant <code>CONSTANT_METHOD_TYPE=16</code> */
87      public static final byte CONSTANT_METHOD_TYPE = 16;
88  
89      /** Constant <code>CONSTANT_INVOKE=17</code> */
90      public static final byte CONSTANT_INVOKE = 17;
91  
92      /** Constant <code>CONSTANT_INVOKE_DYNAMIC=18</code> */
93      public static final byte CONSTANT_INVOKE_DYNAMIC = 18;
94  
95      /** Constant <code>CONSTANT_MODULE=19</code> */
96      public static final byte CONSTANT_MODULE = 19;
97  
98      /** Constant <code>CONSTANT_PACKAGE=20</code> */
99      public static final byte CONSTANT_PACKAGE = 20;
100 
101     private static final int OXF0 = 0xf0;
102 
103     private static final int OXE0 = 0xe0;
104 
105     private static final int OX3F = 0x3F;
106 
107     static Set<String> getConstantPoolClassReferences(byte[] b) {
108         return parseConstantPoolClassReferences(ByteBuffer.wrap(b));
109     }
110 
111     static Set<String> parseConstantPoolClassReferences(ByteBuffer buf) {
112         if (buf.order(ByteOrder.BIG_ENDIAN).getInt() != HEAD) {
113             return Collections.emptySet();
114         }
115         buf.getChar();
116         buf.getChar(); // minor + ver
117         Set<Integer> classReferences = new HashSet<>();
118         Set<Integer> typeReferences = new HashSet<>();
119         Map<Integer, String> stringConstants = new HashMap<>();
120         for (int ix = 1, num = buf.getChar(); ix < num; ix++) {
121             byte tag = buf.get();
122             switch (tag) {
123                 default:
124                     throw new RuntimeException("Unknown constant pool type '" + tag + "'");
125                 case CONSTANT_UTF8:
126                     stringConstants.put(ix, decodeString(buf));
127                     break;
128                 case CONSTANT_CLASS:
129                     classReferences.add((int) buf.getChar());
130                     break;
131                 case CONSTANT_METHOD_TYPE:
132                     consumeMethodType(buf);
133                     break;
134                 case CONSTANT_FIELDREF:
135                 case CONSTANT_METHODREF:
136                 case CONSTANT_INTERFACEMETHODREF:
137                     consumeReference(buf);
138                     break;
139                 case CONSTANT_NAME_AND_TYPE:
140                     buf.getChar();
141                     typeReferences.add((int) buf.getChar());
142                     break;
143                 case CONSTANT_INTEGER:
144                     consumeInt(buf);
145                     break;
146                 case CONSTANT_FLOAT:
147                     consumeFloat(buf);
148                     break;
149                 case CONSTANT_DOUBLE:
150                     consumeDouble(buf);
151                     ix++;
152                     break;
153                 case CONSTANT_LONG:
154                     consumeLong(buf);
155                     ix++;
156                     break;
157                 case CONSTANT_STRING:
158                     consumeString(buf);
159                     break;
160                 case CONSTANT_METHODHANDLE:
161                     consumeMethodHandle(buf);
162                     break;
163                 case CONSTANT_INVOKE:
164                     consumeDynamic(buf);
165                     break;
166                 case CONSTANT_INVOKE_DYNAMIC:
167                     consumeInvokeDynamic(buf);
168                     break;
169                 case CONSTANT_MODULE:
170                     consumeModule(buf);
171                     break;
172                 case CONSTANT_PACKAGE:
173                     consumePackage(buf);
174                     break;
175             }
176         }
177 
178         Set<String> result = new HashSet<>();
179 
180         for (Integer classRef : classReferences) {
181             addClassToResult(result, stringConstants.get(classRef));
182         }
183 
184         for (Integer typeRef : typeReferences) {
185             String typeName = stringConstants.get(typeRef);
186 
187             if (Type.getType(typeName).getSort() == Type.METHOD) {
188                 addClassToResult(result, Type.getReturnType(typeName).getInternalName());
189                 Type[] argumentTypes = Type.getArgumentTypes(typeName);
190                 for (Type argumentType : argumentTypes) {
191                     addClassToResult(result, argumentType.getInternalName());
192                 }
193             }
194         }
195 
196         return result;
197     }
198 
199     private static void addClassToResult(Set<String> result, String className) {
200         // filter out things from unnamed package, probably a false-positive
201         if (isImportableClass(className)) {
202             result.add(className);
203         }
204     }
205 
206     private static String decodeString(ByteBuffer buf) {
207         int size = buf.getChar();
208         // Explicit cast for compatibility with covariant return type on JDK 9's ByteBuffer
209         @SuppressWarnings("RedundantCast")
210         int oldLimit = ((Buffer) buf).limit();
211         ((Buffer) buf).limit(buf.position() + size);
212         StringBuilder sb = new StringBuilder(size + (size >> 1) + 16);
213         while (buf.hasRemaining()) {
214             byte b = buf.get();
215             if (b > 0) {
216                 sb.append((char) b);
217             } else {
218                 int b2 = buf.get();
219                 if ((b & OXF0) != OXE0) {
220                     sb.append((char) ((b & 0x1F) << 6 | b2 & OX3F));
221                 } else {
222                     int b3 = buf.get();
223                     sb.append((char) ((b & 0x0F) << 12 | (b2 & OX3F) << 6 | b3 & OX3F));
224                 }
225             }
226         }
227         ((Buffer) buf).limit(oldLimit);
228         return sb.toString();
229     }
230 
231     private static boolean isImportableClass(String className) {
232         // without a slash, class must be in unnamed package, which can't be imported
233         return className.indexOf('/') != -1;
234     }
235 
236     private static void consumeMethodType(ByteBuffer buf) {
237         buf.getChar();
238     }
239 
240     private static void consumeReference(ByteBuffer buf) {
241         buf.getChar();
242         buf.getChar();
243     }
244 
245     private static void consumeInt(ByteBuffer buf) {
246         buf.getInt();
247     }
248 
249     private static void consumeFloat(ByteBuffer buf) {
250         buf.getFloat();
251     }
252 
253     private static void consumeDouble(ByteBuffer buf) {
254         buf.getDouble();
255     }
256 
257     private static void consumeLong(ByteBuffer buf) {
258         buf.getLong();
259     }
260 
261     private static void consumeString(ByteBuffer buf) {
262         buf.getChar();
263     }
264 
265     private static void consumeMethodHandle(ByteBuffer buf) {
266         buf.get();
267         buf.getChar();
268     }
269 
270     private static void consumeDynamic(ByteBuffer buf) {
271         buf.getChar(); // u2 bootstrap_method_attr_index;
272         buf.getChar(); // u2 name_and_type_index;
273     }
274 
275     private static void consumeInvokeDynamic(ByteBuffer buf) {
276         buf.getChar();
277         buf.getChar();
278     }
279 
280     private static void consumeModule(ByteBuffer buf) {
281         buf.getChar();
282     }
283 
284     private static void consumePackage(ByteBuffer buf) {
285         buf.getChar();
286     }
287 }