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