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.Buffer;
23  import java.nio.ByteBuffer;
24  import java.nio.ByteOrder;
25  import java.util.Collections;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.Map;
29  import java.util.Set;
30  
31  /**
32   * A small parser to read the constant pool directly, in case it contains references
33   * ASM does not support.
34   *
35   * Adapted from http://stackoverflow.com/a/32278587/23691
36   * 
37   * Constant pool types:
38   * 
39   * @see <a href="https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.4">JVM 9 Sepc</a>
40   * @see <a href="https://docs.oracle.com/javase/specs/jvms/se10/html/jvms-4.html#jvms-4.4">JVM 10 Sepc</a>
41   * 
42   */
43  public class ConstantPoolParser
44  {
45      public static final int HEAD = 0xcafebabe;
46  
47      // Constant pool types
48      public static final byte CONSTANT_UTF8 = 1;
49  
50      public static final byte CONSTANT_INTEGER = 3;
51  
52      public static final byte CONSTANT_FLOAT = 4;
53  
54      public static final byte CONSTANT_LONG = 5;
55  
56      public static final byte CONSTANT_DOUBLE = 6;
57  
58      public static final byte CONSTANT_CLASS = 7;
59  
60      public static final byte CONSTANT_STRING = 8;
61  
62      public static final byte CONSTANT_FIELDREF = 9;
63  
64      public static final byte CONSTANT_METHODREF = 10;
65  
66      public static final byte CONSTANT_INTERFACEMETHODREF = 11;
67  
68      public static final byte CONSTANT_NAME_AND_TYPE = 12;
69  
70      public static final byte CONSTANT_METHODHANDLE = 15;
71  
72      public static final byte CONSTANT_METHOD_TYPE = 16;
73  
74      public static final byte CONSTANT_INVOKE_DYNAMIC = 18;
75      
76      public static final byte CONSTANT_MODULE = 19;
77  
78      public static final byte CONSTANT_PACKAGE = 20;
79  
80      private static final int OXF0 = 0xf0;
81  
82      private static final int OXE0 = 0xe0;
83  
84      private static final int OX3F = 0x3F;
85  
86      static Set<String> getConstantPoolClassReferences( byte[] b )
87      {
88          return parseConstantPoolClassReferences( ByteBuffer.wrap( b ) );
89      }
90  
91      static Set<String> parseConstantPoolClassReferences( ByteBuffer buf )
92      {
93          if ( buf.order( ByteOrder.BIG_ENDIAN )
94                  .getInt() != HEAD )
95          {
96              return Collections.emptySet();
97          }
98          buf.getChar() ; buf.getChar(); // minor + ver
99          Set<Integer> classes = new HashSet<Integer>();
100         Map<Integer, String> stringConstants = new HashMap<Integer, String>();
101         for ( int ix = 1, num = buf.getChar(); ix < num; ix++ )
102         {
103             byte tag = buf.get();
104             switch ( tag )
105             {
106                 default:
107                     throw new RuntimeException( "Unknown constant pool type '" + tag + "'" );
108                 case CONSTANT_UTF8:
109                     stringConstants.put( ix, decodeString( buf ) );
110                     continue;
111                 case CONSTANT_CLASS:
112                 case CONSTANT_STRING:
113                 case CONSTANT_METHOD_TYPE:
114                     classes.add( (int) buf.getChar() );
115                     break;
116                 case CONSTANT_FIELDREF:
117                 case CONSTANT_METHODREF:
118                 case CONSTANT_INTERFACEMETHODREF:
119                 case CONSTANT_NAME_AND_TYPE:
120                     buf.getChar();
121                     buf.getChar();
122                     break;
123                 case CONSTANT_INTEGER:
124                     buf.getInt();
125                     break;
126                 case CONSTANT_FLOAT:
127                     buf.getFloat();
128                     break;
129                 case CONSTANT_DOUBLE:
130                     buf.getDouble();
131                     ix++;
132                     break;
133                 case CONSTANT_LONG:
134                     buf.getLong();
135                     ix++;
136                     break;
137                 case CONSTANT_METHODHANDLE:
138                     buf.get();
139                     buf.getChar();
140                     break;
141                 case CONSTANT_INVOKE_DYNAMIC:
142                     buf.getChar();
143                     buf.getChar();
144                     break;
145                 case CONSTANT_MODULE:
146                     buf.getChar();
147                     break;
148                 case CONSTANT_PACKAGE:
149                     buf.getChar();
150                     break;  
151             }
152         }
153         Set<String> result = new HashSet<String>();
154         for ( Integer aClass : classes )
155         {
156             result.add( stringConstants.get( aClass ) );
157         }
158         return result;
159     }
160 
161     private static String decodeString( ByteBuffer buf )
162     {
163         int size = buf.getChar();
164         // Explicit cast for compatibility with covariant return type on JDK 9's ByteBuffer
165         int oldLimit = ( (Buffer) buf ).limit();
166         ( (Buffer) buf ).limit( buf.position() + size );
167         StringBuilder sb = new StringBuilder( size + ( size >> 1 ) + 16 );
168         while ( buf.hasRemaining() )
169         {
170             byte b = buf.get();
171             if ( b > 0 )
172             {
173                 sb.append( (char) b );
174             }
175             else
176             {
177                 int b2 = buf.get();
178                 if ( ( b & OXF0 ) != OXE0 )
179                 {
180                     sb.append( (char) ( ( b & 0x1F ) << 6 | b2 & OX3F ) );
181                 }
182                 else
183                 {
184                     int b3 = buf.get();
185                     sb.append( (char) ( ( b & 0x0F ) << 12 | ( b2 & OX3F ) << 6 | b3 & OX3F ) );
186                 }
187             }
188         }
189         ( (Buffer) buf ).limit( oldLimit );
190         return sb.toString();
191     }
192 }