• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.dx.cf.cst;
18 
19 import static com.android.dx.cf.cst.ConstantTags.CONSTANT_Class;
20 import static com.android.dx.cf.cst.ConstantTags.CONSTANT_Double;
21 import static com.android.dx.cf.cst.ConstantTags.CONSTANT_Fieldref;
22 import static com.android.dx.cf.cst.ConstantTags.CONSTANT_Float;
23 import static com.android.dx.cf.cst.ConstantTags.CONSTANT_Integer;
24 import static com.android.dx.cf.cst.ConstantTags.CONSTANT_InterfaceMethodref;
25 import static com.android.dx.cf.cst.ConstantTags.CONSTANT_InvokeDynamic;
26 import static com.android.dx.cf.cst.ConstantTags.CONSTANT_Long;
27 import static com.android.dx.cf.cst.ConstantTags.CONSTANT_MethodHandle;
28 import static com.android.dx.cf.cst.ConstantTags.CONSTANT_MethodType;
29 import static com.android.dx.cf.cst.ConstantTags.CONSTANT_Methodref;
30 import static com.android.dx.cf.cst.ConstantTags.CONSTANT_NameAndType;
31 import static com.android.dx.cf.cst.ConstantTags.CONSTANT_String;
32 import static com.android.dx.cf.cst.ConstantTags.CONSTANT_Utf8;
33 import com.android.dx.cf.iface.ParseException;
34 import com.android.dx.cf.iface.ParseObserver;
35 import com.android.dx.rop.cst.Constant;
36 import com.android.dx.rop.cst.CstDouble;
37 import com.android.dx.rop.cst.CstFieldRef;
38 import com.android.dx.rop.cst.CstFloat;
39 import com.android.dx.rop.cst.CstInteger;
40 import com.android.dx.rop.cst.CstInterfaceMethodRef;
41 import com.android.dx.rop.cst.CstInvokeDynamic;
42 import com.android.dx.rop.cst.CstLong;
43 import com.android.dx.rop.cst.CstMethodHandle;
44 import com.android.dx.rop.cst.CstMethodRef;
45 import com.android.dx.rop.cst.CstNat;
46 import com.android.dx.rop.cst.CstProtoRef;
47 import com.android.dx.rop.cst.CstString;
48 import com.android.dx.rop.cst.CstType;
49 import com.android.dx.rop.cst.StdConstantPool;
50 import com.android.dx.rop.type.Type;
51 import com.android.dx.util.ByteArray;
52 import com.android.dx.util.Hex;
53 import java.util.BitSet;
54 
55 /**
56  * Parser for a constant pool embedded in a class file.
57  */
58 public final class ConstantPoolParser {
59     /** {@code non-null;} the bytes of the constant pool */
60     private final ByteArray bytes;
61 
62     /** {@code non-null;} actual parsed constant pool contents */
63     private final StdConstantPool pool;
64 
65     /** {@code non-null;} byte offsets to each cst */
66     private final int[] offsets;
67 
68     /**
69      * -1 || >= 10; the end offset of this constant pool in the
70      * {@code byte[]} which it came from or {@code -1} if not
71      * yet parsed
72      */
73     private int endOffset;
74 
75     /** {@code null-ok;} parse observer, if any */
76     private ParseObserver observer;
77 
78     /**
79      * Constructs an instance.
80      *
81      * @param bytes {@code non-null;} the bytes of the file
82      */
ConstantPoolParser(ByteArray bytes)83     public ConstantPoolParser(ByteArray bytes) {
84         int size = bytes.getUnsignedShort(8); // constant_pool_count
85 
86         this.bytes = bytes;
87         this.pool = new StdConstantPool(size);
88         this.offsets = new int[size];
89         this.endOffset = -1;
90     }
91 
92     /**
93      * Sets the parse observer for this instance.
94      *
95      * @param observer {@code null-ok;} the observer
96      */
setObserver(ParseObserver observer)97     public void setObserver(ParseObserver observer) {
98         this.observer = observer;
99     }
100 
101     /**
102      * Gets the end offset of this constant pool in the {@code byte[]}
103      * which it came from.
104      *
105      * @return {@code >= 10;} the end offset
106      */
getEndOffset()107     public int getEndOffset() {
108         parseIfNecessary();
109         return endOffset;
110     }
111 
112     /**
113      * Gets the actual constant pool.
114      *
115      * @return {@code non-null;} the constant pool
116      */
getPool()117     public StdConstantPool getPool() {
118         parseIfNecessary();
119         return pool;
120     }
121 
122     /**
123      * Runs {@link #parse} if it has not yet been run successfully.
124      */
parseIfNecessary()125     private void parseIfNecessary() {
126         if (endOffset < 0) {
127             parse();
128         }
129     }
130 
131     /**
132      * Does the actual parsing.
133      */
parse()134     private void parse() {
135         determineOffsets();
136 
137         if (observer != null) {
138             observer.parsed(bytes, 8, 2,
139                             "constant_pool_count: " + Hex.u2(offsets.length));
140             observer.parsed(bytes, 10, 0, "\nconstant_pool:");
141             observer.changeIndent(1);
142         }
143 
144         /*
145          * Track the constant value's original string type. True if constants[i] was
146          * a CONSTANT_Utf8, false for any other type including CONSTANT_string.
147          */
148         BitSet wasUtf8 = new BitSet(offsets.length);
149 
150         for (int i = 1; i < offsets.length; i++) {
151             int offset = offsets[i];
152             if ((offset != 0) && (pool.getOrNull(i) == null)) {
153                 parse0(i, wasUtf8);
154             }
155         }
156 
157         if (observer != null) {
158             for (int i = 1; i < offsets.length; i++) {
159                 Constant cst = pool.getOrNull(i);
160                 if (cst == null) {
161                     continue;
162                 }
163                 int offset = offsets[i];
164                 int nextOffset = endOffset;
165                 for (int j = i + 1; j < offsets.length; j++) {
166                     int off = offsets[j];
167                     if (off != 0) {
168                         nextOffset = off;
169                         break;
170                     }
171                 }
172                 String human = wasUtf8.get(i)
173                         ? Hex.u2(i) + ": utf8{\"" + cst.toHuman() + "\"}"
174                         : Hex.u2(i) + ": " + cst.toString();
175                 observer.parsed(bytes, offset, nextOffset - offset, human);
176             }
177 
178             observer.changeIndent(-1);
179             observer.parsed(bytes, endOffset, 0, "end constant_pool");
180         }
181     }
182 
183     /**
184      * Populates {@link #offsets} and also completely parse utf8 constants.
185      */
determineOffsets()186     private void determineOffsets() {
187         int at = 10; // offset from the start of the file to the first cst
188         int lastCategory;
189 
190         for (int i = 1; i < offsets.length; i += lastCategory) {
191             offsets[i] = at;
192             int tag = bytes.getUnsignedByte(at);
193             try {
194                 switch (tag) {
195                     case CONSTANT_Integer:
196                     case CONSTANT_Float:
197                     case CONSTANT_Fieldref:
198                     case CONSTANT_Methodref:
199                     case CONSTANT_InterfaceMethodref:
200                     case CONSTANT_NameAndType: {
201                         lastCategory = 1;
202                         at += 5;
203                         break;
204                     }
205                     case CONSTANT_Long:
206                     case CONSTANT_Double: {
207                         lastCategory = 2;
208                         at += 9;
209                         break;
210                     }
211                     case CONSTANT_Class:
212                     case CONSTANT_String: {
213                         lastCategory = 1;
214                         at += 3;
215                         break;
216                     }
217                     case CONSTANT_Utf8: {
218                         lastCategory = 1;
219                         at += bytes.getUnsignedShort(at + 1) + 3;
220                         break;
221                     }
222                     case CONSTANT_MethodHandle: {
223                         lastCategory = 1;
224                         at += 4;
225                         break;
226                     }
227                     case CONSTANT_MethodType: {
228                         lastCategory = 1;
229                         at += 3;
230                         break;
231                     }
232                     case CONSTANT_InvokeDynamic: {
233                         lastCategory = 1;
234                         at += 5;
235                         break;
236                     }
237                     default: {
238                         throw new ParseException("unknown tag byte: " + Hex.u1(tag));
239                     }
240                 }
241             } catch (ParseException ex) {
242                 ex.addContext("...while preparsing cst " + Hex.u2(i) + " at offset " + Hex.u4(at));
243                 throw ex;
244             }
245         }
246 
247         endOffset = at;
248     }
249 
250     /**
251      * Parses the constant for the given index if it hasn't already been
252      * parsed, also storing it in the constant pool. This will also
253      * have the side effect of parsing any entries the indicated one
254      * depends on.
255      *
256      * @param idx which constant
257      * @return {@code non-null;} the parsed constant
258      */
parse0(int idx, BitSet wasUtf8)259     private Constant parse0(int idx, BitSet wasUtf8) {
260         Constant cst = pool.getOrNull(idx);
261         if (cst != null) {
262             return cst;
263         }
264 
265         int at = offsets[idx];
266 
267         try {
268             int tag = bytes.getUnsignedByte(at);
269             switch (tag) {
270                 case CONSTANT_Utf8: {
271                     cst = parseUtf8(at);
272                     wasUtf8.set(idx);
273                     break;
274                 }
275                 case CONSTANT_Integer: {
276                     int value = bytes.getInt(at + 1);
277                     cst = CstInteger.make(value);
278                     break;
279                 }
280                 case CONSTANT_Float: {
281                     int bits = bytes.getInt(at + 1);
282                     cst = CstFloat.make(bits);
283                     break;
284                 }
285                 case CONSTANT_Long: {
286                     long value = bytes.getLong(at + 1);
287                     cst = CstLong.make(value);
288                     break;
289                 }
290                 case CONSTANT_Double: {
291                     long bits = bytes.getLong(at + 1);
292                     cst = CstDouble.make(bits);
293                     break;
294                 }
295                 case CONSTANT_Class: {
296                     int nameIndex = bytes.getUnsignedShort(at + 1);
297                     CstString name = (CstString) parse0(nameIndex, wasUtf8);
298                     cst = new CstType(Type.internClassName(name.getString()));
299                     break;
300                 }
301                 case CONSTANT_String: {
302                     int stringIndex = bytes.getUnsignedShort(at + 1);
303                     cst = parse0(stringIndex, wasUtf8);
304                     break;
305                 }
306                 case CONSTANT_Fieldref: {
307                     int classIndex = bytes.getUnsignedShort(at + 1);
308                     CstType type = (CstType) parse0(classIndex, wasUtf8);
309                     int natIndex = bytes.getUnsignedShort(at + 3);
310                     CstNat nat = (CstNat) parse0(natIndex, wasUtf8);
311                     cst = new CstFieldRef(type, nat);
312                     break;
313                 }
314                 case CONSTANT_Methodref: {
315                     int classIndex = bytes.getUnsignedShort(at + 1);
316                     CstType type = (CstType) parse0(classIndex, wasUtf8);
317                     int natIndex = bytes.getUnsignedShort(at + 3);
318                     CstNat nat = (CstNat) parse0(natIndex, wasUtf8);
319                     cst = new CstMethodRef(type, nat);
320                     break;
321                 }
322                 case CONSTANT_InterfaceMethodref: {
323                     int classIndex = bytes.getUnsignedShort(at + 1);
324                     CstType type = (CstType) parse0(classIndex, wasUtf8);
325                     int natIndex = bytes.getUnsignedShort(at + 3);
326                     CstNat nat = (CstNat) parse0(natIndex, wasUtf8);
327                     cst = new CstInterfaceMethodRef(type, nat);
328                     break;
329                 }
330                 case CONSTANT_NameAndType: {
331                     int nameIndex = bytes.getUnsignedShort(at + 1);
332                     CstString name = (CstString) parse0(nameIndex, wasUtf8);
333                     int descriptorIndex = bytes.getUnsignedShort(at + 3);
334                     CstString descriptor = (CstString) parse0(descriptorIndex, wasUtf8);
335                     cst = new CstNat(name, descriptor);
336                     break;
337                 }
338                 case CONSTANT_MethodHandle: {
339                     final int kind = bytes.getUnsignedByte(at + 1);
340                     final int constantIndex = bytes.getUnsignedShort(at + 2);
341                     final Constant ref;
342                     switch (kind) {
343                         case MethodHandleKind.REF_getField:
344                         case MethodHandleKind.REF_getStatic:
345                         case MethodHandleKind.REF_putField:
346                         case MethodHandleKind.REF_putStatic:
347                             ref = (CstFieldRef) parse0(constantIndex, wasUtf8);
348                             break;
349                         case MethodHandleKind.REF_invokeVirtual:
350                         case MethodHandleKind.REF_newInvokeSpecial:
351                             ref = (CstMethodRef) parse0(constantIndex, wasUtf8);
352                             break;
353                         case MethodHandleKind.REF_invokeStatic:
354                         case MethodHandleKind.REF_invokeSpecial:
355                             ref = parse0(constantIndex, wasUtf8);
356                             if (!(ref instanceof CstMethodRef
357                                 || ref instanceof CstInterfaceMethodRef)) {
358                               throw new ParseException(
359                                   "Unsupported ref constant type for MethodHandle "
360                                   + ref.getClass());
361                             }
362                             break;
363                         case MethodHandleKind.REF_invokeInterface:
364                             ref = (CstInterfaceMethodRef) parse0(constantIndex, wasUtf8);
365                             break;
366                         default:
367                             throw new ParseException("Unsupported MethodHandle kind: " + kind);
368                     }
369 
370                     final int methodHandleType = getMethodHandleTypeForKind(kind);
371                     cst = CstMethodHandle.make(methodHandleType, ref);
372                     break;
373                 }
374                 case CONSTANT_MethodType: {
375                     int descriptorIndex = bytes.getUnsignedShort(at + 1);
376                     CstString descriptor = (CstString) parse0(descriptorIndex, wasUtf8);
377                     cst = CstProtoRef.make(descriptor);
378                     break;
379                 }
380                 case CONSTANT_InvokeDynamic: {
381                     int bootstrapMethodIndex = bytes.getUnsignedShort(at + 1);
382                     int natIndex = bytes.getUnsignedShort(at + 3);
383                     CstNat nat = (CstNat) parse0(natIndex, wasUtf8);
384                     cst = CstInvokeDynamic.make(bootstrapMethodIndex, nat);
385                     break;
386                 }
387                 default: {
388                     throw new ParseException("unknown tag byte: " + Hex.u1(tag));
389                 }
390             }
391         } catch (ParseException ex) {
392             ex.addContext("...while parsing cst " + Hex.u2(idx) +
393                           " at offset " + Hex.u4(at));
394             throw ex;
395         } catch (RuntimeException ex) {
396             ParseException pe = new ParseException(ex);
397             pe.addContext("...while parsing cst " + Hex.u2(idx) +
398                           " at offset " + Hex.u4(at));
399             throw pe;
400         }
401 
402         pool.set(idx, cst);
403         return cst;
404     }
405 
406     /**
407      * Parses a utf8 constant.
408      *
409      * @param at offset to the start of the constant (where the tag byte is)
410      * @return {@code non-null;} the parsed value
411      */
parseUtf8(int at)412     private CstString parseUtf8(int at) {
413         int length = bytes.getUnsignedShort(at + 1);
414 
415         at += 3; // Skip to the data.
416 
417         ByteArray ubytes = bytes.slice(at, at + length);
418 
419         try {
420             return new CstString(ubytes);
421         } catch (IllegalArgumentException ex) {
422             // Translate the exception
423             throw new ParseException(ex);
424         }
425     }
426 
getMethodHandleTypeForKind(int kind)427     private static int getMethodHandleTypeForKind(int kind) {
428         switch (kind) {
429             case MethodHandleKind.REF_getField:
430                 return CstMethodHandle.METHOD_HANDLE_TYPE_INSTANCE_GET;
431             case MethodHandleKind.REF_getStatic:
432                 return CstMethodHandle.METHOD_HANDLE_TYPE_STATIC_GET;
433             case MethodHandleKind.REF_putField:
434                 return CstMethodHandle.METHOD_HANDLE_TYPE_INSTANCE_PUT;
435             case MethodHandleKind.REF_putStatic:
436                 return CstMethodHandle.METHOD_HANDLE_TYPE_STATIC_PUT;
437             case MethodHandleKind.REF_invokeVirtual:
438                 return CstMethodHandle.METHOD_HANDLE_TYPE_INVOKE_INSTANCE;
439             case MethodHandleKind.REF_invokeStatic:
440                 return CstMethodHandle.METHOD_HANDLE_TYPE_INVOKE_STATIC;
441             case MethodHandleKind.REF_invokeSpecial:
442                 return CstMethodHandle.METHOD_HANDLE_TYPE_INVOKE_DIRECT;
443             case MethodHandleKind.REF_newInvokeSpecial:
444                 return CstMethodHandle.METHOD_HANDLE_TYPE_INVOKE_CONSTRUCTOR;
445             case MethodHandleKind.REF_invokeInterface:
446                 return CstMethodHandle.METHOD_HANDLE_TYPE_INVOKE_INTERFACE;
447         }
448         throw new IllegalArgumentException("invalid kind: " + kind);
449     }
450 }
451