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