• Home
  • 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_Long;
26 import static com.android.dx.cf.cst.ConstantTags.CONSTANT_Methodref;
27 import static com.android.dx.cf.cst.ConstantTags.CONSTANT_NameAndType;
28 import static com.android.dx.cf.cst.ConstantTags.CONSTANT_String;
29 import static com.android.dx.cf.cst.ConstantTags.CONSTANT_Utf8;
30 import com.android.dx.cf.iface.ParseException;
31 import com.android.dx.cf.iface.ParseObserver;
32 import com.android.dx.rop.cst.Constant;
33 import com.android.dx.rop.cst.CstDouble;
34 import com.android.dx.rop.cst.CstFieldRef;
35 import com.android.dx.rop.cst.CstFloat;
36 import com.android.dx.rop.cst.CstInteger;
37 import com.android.dx.rop.cst.CstInterfaceMethodRef;
38 import com.android.dx.rop.cst.CstLong;
39 import com.android.dx.rop.cst.CstMethodRef;
40 import com.android.dx.rop.cst.CstNat;
41 import com.android.dx.rop.cst.CstString;
42 import com.android.dx.rop.cst.CstType;
43 import com.android.dx.rop.cst.StdConstantPool;
44 import com.android.dx.rop.type.Type;
45 import com.android.dx.util.ByteArray;
46 import com.android.dx.util.Hex;
47 import java.util.BitSet;
48 
49 /**
50  * Parser for a constant pool embedded in a class file.
51  */
52 public final class ConstantPoolParser {
53     /** {@code non-null;} the bytes of the constant pool */
54     private final ByteArray bytes;
55 
56     /** {@code non-null;} actual parsed constant pool contents */
57     private final StdConstantPool pool;
58 
59     /** {@code non-null;} byte offsets to each cst */
60     private final int[] offsets;
61 
62     /**
63      * -1 || >= 10; the end offset of this constant pool in the
64      * {@code byte[]} which it came from or {@code -1} if not
65      * yet parsed
66      */
67     private int endOffset;
68 
69     /** {@code null-ok;} parse observer, if any */
70     private ParseObserver observer;
71 
72     /**
73      * Constructs an instance.
74      *
75      * @param bytes {@code non-null;} the bytes of the file
76      */
ConstantPoolParser(ByteArray bytes)77     public ConstantPoolParser(ByteArray bytes) {
78         int size = bytes.getUnsignedShort(8); // constant_pool_count
79 
80         this.bytes = bytes;
81         this.pool = new StdConstantPool(size);
82         this.offsets = new int[size];
83         this.endOffset = -1;
84     }
85 
86     /**
87      * Sets the parse observer for this instance.
88      *
89      * @param observer {@code null-ok;} the observer
90      */
setObserver(ParseObserver observer)91     public void setObserver(ParseObserver observer) {
92         this.observer = observer;
93     }
94 
95     /**
96      * Gets the end offset of this constant pool in the {@code byte[]}
97      * which it came from.
98      *
99      * @return {@code >= 10;} the end offset
100      */
getEndOffset()101     public int getEndOffset() {
102         parseIfNecessary();
103         return endOffset;
104     }
105 
106     /**
107      * Gets the actual constant pool.
108      *
109      * @return {@code non-null;} the constant pool
110      */
getPool()111     public StdConstantPool getPool() {
112         parseIfNecessary();
113         return pool;
114     }
115 
116     /**
117      * Runs {@link #parse} if it has not yet been run successfully.
118      */
parseIfNecessary()119     private void parseIfNecessary() {
120         if (endOffset < 0) {
121             parse();
122         }
123     }
124 
125     /**
126      * Does the actual parsing.
127      */
parse()128     private void parse() {
129         determineOffsets();
130 
131         if (observer != null) {
132             observer.parsed(bytes, 8, 2,
133                             "constant_pool_count: " + Hex.u2(offsets.length));
134             observer.parsed(bytes, 10, 0, "\nconstant_pool:");
135             observer.changeIndent(1);
136         }
137 
138         /*
139          * Track the constant value's original string type. True if constants[i] was
140          * a CONSTANT_Utf8, false for any other type including CONSTANT_string.
141          */
142         BitSet wasUtf8 = new BitSet(offsets.length);
143 
144         for (int i = 1; i < offsets.length; i++) {
145             int offset = offsets[i];
146             if ((offset != 0) && (pool.getOrNull(i) == null)) {
147                 parse0(i, wasUtf8);
148             }
149         }
150 
151         if (observer != null) {
152             for (int i = 1; i < offsets.length; i++) {
153                 Constant cst = pool.getOrNull(i);
154                 if (cst == null) {
155                     continue;
156                 }
157                 int offset = offsets[i];
158                 int nextOffset = endOffset;
159                 for (int j = i + 1; j < offsets.length; j++) {
160                     int off = offsets[j];
161                     if (off != 0) {
162                         nextOffset = off;
163                         break;
164                     }
165                 }
166                 String human = wasUtf8.get(i)
167                         ? Hex.u2(i) + ": utf8{\"" + cst.toHuman() + "\"}"
168                         : Hex.u2(i) + ": " + cst.toString();
169                 observer.parsed(bytes, offset, nextOffset - offset, human);
170             }
171 
172             observer.changeIndent(-1);
173             observer.parsed(bytes, endOffset, 0, "end constant_pool");
174         }
175     }
176 
177     /**
178      * Populates {@link #offsets} and also completely parse utf8 constants.
179      */
determineOffsets()180     private void determineOffsets() {
181         int at = 10; // offset from the start of the file to the first cst
182         int lastCategory;
183 
184         for (int i = 1; i < offsets.length; i += lastCategory) {
185             offsets[i] = at;
186             int tag = bytes.getUnsignedByte(at);
187             switch (tag) {
188                 case CONSTANT_Integer:
189                 case CONSTANT_Float:
190                 case CONSTANT_Fieldref:
191                 case CONSTANT_Methodref:
192                 case CONSTANT_InterfaceMethodref:
193                 case CONSTANT_NameAndType: {
194                     lastCategory = 1;
195                     at += 5;
196                     break;
197                 }
198                 case CONSTANT_Long:
199                 case CONSTANT_Double: {
200                     lastCategory = 2;
201                     at += 9;
202                     break;
203                 }
204                 case CONSTANT_Class:
205                 case CONSTANT_String: {
206                     lastCategory = 1;
207                     at += 3;
208                     break;
209                 }
210                 case CONSTANT_Utf8: {
211                     lastCategory = 1;
212                     at += bytes.getUnsignedShort(at + 1) + 3;
213                     break;
214                 }
215                 default: {
216                     ParseException ex =
217                         new ParseException("unknown tag byte: " + Hex.u1(tag));
218                     ex.addContext("...while preparsing cst " + Hex.u2(i) +
219                                   " at offset " + Hex.u4(at));
220                     throw ex;
221                 }
222             }
223         }
224 
225         endOffset = at;
226     }
227 
228     /**
229      * Parses the constant for the given index if it hasn't already been
230      * parsed, also storing it in the constant pool. This will also
231      * have the side effect of parsing any entries the indicated one
232      * depends on.
233      *
234      * @param idx which constant
235      * @return {@code non-null;} the parsed constant
236      */
parse0(int idx, BitSet wasUtf8)237     private Constant parse0(int idx, BitSet wasUtf8) {
238         Constant cst = pool.getOrNull(idx);
239         if (cst != null) {
240             return cst;
241         }
242 
243         int at = offsets[idx];
244 
245         try {
246             int tag = bytes.getUnsignedByte(at);
247             switch (tag) {
248                 case CONSTANT_Utf8: {
249                     cst = parseUtf8(at);
250                     wasUtf8.set(idx);
251                     break;
252                 }
253                 case CONSTANT_Integer: {
254                     int value = bytes.getInt(at + 1);
255                     cst = CstInteger.make(value);
256                     break;
257                 }
258                 case CONSTANT_Float: {
259                     int bits = bytes.getInt(at + 1);
260                     cst = CstFloat.make(bits);
261                     break;
262                 }
263                 case CONSTANT_Long: {
264                     long value = bytes.getLong(at + 1);
265                     cst = CstLong.make(value);
266                     break;
267                 }
268                 case CONSTANT_Double: {
269                     long bits = bytes.getLong(at + 1);
270                     cst = CstDouble.make(bits);
271                     break;
272                 }
273                 case CONSTANT_Class: {
274                     int nameIndex = bytes.getUnsignedShort(at + 1);
275                     CstString name = (CstString) parse0(nameIndex, wasUtf8);
276                     cst = new CstType(Type.internClassName(name.getString()));
277                     break;
278                 }
279                 case CONSTANT_String: {
280                     int stringIndex = bytes.getUnsignedShort(at + 1);
281                     cst = parse0(stringIndex, wasUtf8);
282                     break;
283                 }
284                 case CONSTANT_Fieldref: {
285                     int classIndex = bytes.getUnsignedShort(at + 1);
286                     CstType type = (CstType) parse0(classIndex, wasUtf8);
287                     int natIndex = bytes.getUnsignedShort(at + 3);
288                     CstNat nat = (CstNat) parse0(natIndex, wasUtf8);
289                     cst = new CstFieldRef(type, nat);
290                     break;
291                 }
292                 case CONSTANT_Methodref: {
293                     int classIndex = bytes.getUnsignedShort(at + 1);
294                     CstType type = (CstType) parse0(classIndex, wasUtf8);
295                     int natIndex = bytes.getUnsignedShort(at + 3);
296                     CstNat nat = (CstNat) parse0(natIndex, wasUtf8);
297                     cst = new CstMethodRef(type, nat);
298                     break;
299                 }
300                 case CONSTANT_InterfaceMethodref: {
301                     int classIndex = bytes.getUnsignedShort(at + 1);
302                     CstType type = (CstType) parse0(classIndex, wasUtf8);
303                     int natIndex = bytes.getUnsignedShort(at + 3);
304                     CstNat nat = (CstNat) parse0(natIndex, wasUtf8);
305                     cst = new CstInterfaceMethodRef(type, nat);
306                     break;
307                 }
308                 case CONSTANT_NameAndType: {
309                     int nameIndex = bytes.getUnsignedShort(at + 1);
310                     CstString name = (CstString) parse0(nameIndex, wasUtf8);
311                     int descriptorIndex = bytes.getUnsignedShort(at + 3);
312                     CstString descriptor = (CstString) parse0(descriptorIndex, wasUtf8);
313                     cst = new CstNat(name, descriptor);
314                     break;
315                 }
316             }
317         } catch (ParseException ex) {
318             ex.addContext("...while parsing cst " + Hex.u2(idx) +
319                           " at offset " + Hex.u4(at));
320             throw ex;
321         } catch (RuntimeException ex) {
322             ParseException pe = new ParseException(ex);
323             pe.addContext("...while parsing cst " + Hex.u2(idx) +
324                           " at offset " + Hex.u4(at));
325             throw pe;
326         }
327 
328         pool.set(idx, cst);
329         return cst;
330     }
331 
332     /**
333      * Parses a utf8 constant.
334      *
335      * @param at offset to the start of the constant (where the tag byte is)
336      * @return {@code non-null;} the parsed value
337      */
parseUtf8(int at)338     private CstString parseUtf8(int at) {
339         int length = bytes.getUnsignedShort(at + 1);
340 
341         at += 3; // Skip to the data.
342 
343         ByteArray ubytes = bytes.slice(at, at + length);
344 
345         try {
346             return new CstString(ubytes);
347         } catch (IllegalArgumentException ex) {
348             // Translate the exception
349             throw new ParseException(ex);
350         }
351     }
352 }
353