View Javadoc
1   package org.apache.maven.shared.dependency.analyzer.asm;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
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  /**
31   * A small parser to read the constant pool directly, in case it contains references
32   * ASM does not support.
33   *
34   * Adapted from http://stackoverflow.com/a/32278587/23691
35   * 
36   * Constant pool types:
37   * 
38   * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4">JVM 9 Sepc</a>
39   * @see <a href="https://docs.oracle.com/javase/specs/jvms/se10/html/jvms-4.html#jvms-4.4">JVM 10 Sepc</a>
40   * 
41   */
42  public class ConstantPoolParser
43  {
44      public static final int HEAD = 0xcafebabe;
45  
46      // Constant pool types
47      public static final byte CONSTANT_UTF8 = 1;
48  
49      public static final byte CONSTANT_INTEGER = 3;
50  
51      public static final byte CONSTANT_FLOAT = 4;
52  
53      public static final byte CONSTANT_LONG = 5;
54  
55      public static final byte CONSTANT_DOUBLE = 6;
56  
57      public static final byte CONSTANT_CLASS = 7;
58  
59      public static final byte CONSTANT_STRING = 8;
60  
61      public static final byte CONSTANT_FIELDREF = 9;
62  
63      public static final byte CONSTANT_METHODREF = 10;
64  
65      public static final byte CONSTANT_INTERFACEMETHODREF = 11;
66  
67      public static final byte CONSTANT_NAME_AND_TYPE = 12;
68  
69      public static final byte CONSTANT_METHODHANDLE = 15;
70  
71      public static final byte CONSTANT_METHOD_TYPE = 16;
72  
73      public static final byte CONSTANT_INVOKE_DYNAMIC = 18;
74      
75      public static final byte CONSTANT_MODULE = 19;
76  
77      public static final byte CONSTANT_PACKAGE = 20;
78  
79      private static final int OXF0 = 0xf0;
80  
81      private static final int OXE0 = 0xe0;
82  
83      private static final int OX3F = 0x3F;
84  
85      static Set<String> getConstantPoolClassReferences( byte[] b )
86      {
87          return parseConstantPoolClassReferences( ByteBuffer.wrap( b ) );
88      }
89  
90      static Set<String> parseConstantPoolClassReferences( ByteBuffer buf )
91      {
92          if ( buf.order( ByteOrder.BIG_ENDIAN )
93                  .getInt() != HEAD )
94          {
95              return Collections.emptySet();
96          }
97          buf.getChar() ; buf.getChar(); // minor + ver
98          Set<Integer> classes = new HashSet<Integer>();
99          Map<Integer, String> stringConstants = new HashMap<Integer, String>();
100         for ( int ix = 1, num = buf.getChar(); ix < num; ix++ )
101         {
102             byte tag = buf.get();
103             switch ( tag )
104             {
105                 default:
106                     throw new RuntimeException( "Unknown constant pool type '" + tag + "'" );
107                 case CONSTANT_UTF8:
108                     stringConstants.put( ix, decodeString( buf ) );
109                     continue;
110                 case CONSTANT_CLASS:
111                 case CONSTANT_STRING:
112                 case CONSTANT_METHOD_TYPE:
113                     classes.add( (int) buf.getChar() );
114                     break;
115                 case CONSTANT_FIELDREF:
116                 case CONSTANT_METHODREF:
117                 case CONSTANT_INTERFACEMETHODREF:
118                 case CONSTANT_NAME_AND_TYPE:
119                     buf.getChar();
120                     buf.getChar();
121                     break;
122                 case CONSTANT_INTEGER:
123                     buf.getInt();
124                     break;
125                 case CONSTANT_FLOAT:
126                     buf.getFloat();
127                     break;
128                 case CONSTANT_DOUBLE:
129                     buf.getDouble();
130                     ix++;
131                     break;
132                 case CONSTANT_LONG:
133                     buf.getLong();
134                     ix++;
135                     break;
136                 case CONSTANT_METHODHANDLE:
137                     buf.get();
138                     buf.getChar();
139                     break;
140                 case CONSTANT_INVOKE_DYNAMIC:
141                     buf.getChar();
142                     buf.getChar();
143                     break;
144                 case CONSTANT_MODULE:
145                     buf.getChar();
146                     break;
147                 case CONSTANT_PACKAGE:
148                     buf.getChar();
149                     break;  
150             }
151         }
152         Set<String> result = new HashSet<String>();
153         for ( Integer aClass : classes )
154         {
155             result.add( stringConstants.get( aClass ) );
156         }
157         return result;
158     }
159 
160     private static String decodeString( ByteBuffer buf )
161     {
162         int size = buf.getChar(), oldLimit = buf.limit();
163         buf.limit( buf.position() + size );
164         StringBuilder sb = new StringBuilder( size + ( size >> 1 ) + 16 );
165         while ( buf.hasRemaining() )
166         {
167             byte b = buf.get();
168             if ( b > 0 )
169             {
170                 sb.append( (char) b );
171             }
172             else
173             {
174                 int b2 = buf.get();
175                 if ( ( b & OXF0 ) != OXE0 )
176                 {
177                     sb.append( (char) ( ( b & 0x1F ) << 6 | b2 & OX3F ) );
178                 }
179                 else
180                 {
181                     int b3 = buf.get();
182                     sb.append( (char) ( ( b & 0x0F ) << 12 | ( b2 & OX3F ) << 6 | b3 & OX3F ) );
183                 }
184             }
185         }
186         buf.limit( oldLimit );
187         return sb.toString();
188     }
189 }