1/* 2 * Copyright (C) 2008 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.dex.file; 18 19import com.android.dx.rop.annotation.Annotation; 20import com.android.dx.rop.annotation.NameValuePair; 21import com.android.dx.rop.cst.Constant; 22import com.android.dx.rop.cst.CstAnnotation; 23import com.android.dx.rop.cst.CstArray; 24import com.android.dx.rop.cst.CstBoolean; 25import com.android.dx.rop.cst.CstByte; 26import com.android.dx.rop.cst.CstChar; 27import com.android.dx.rop.cst.CstDouble; 28import com.android.dx.rop.cst.CstEnumRef; 29import com.android.dx.rop.cst.CstFieldRef; 30import com.android.dx.rop.cst.CstFloat; 31import com.android.dx.rop.cst.CstInteger; 32import com.android.dx.rop.cst.CstKnownNull; 33import com.android.dx.rop.cst.CstLiteralBits; 34import com.android.dx.rop.cst.CstLong; 35import com.android.dx.rop.cst.CstMethodRef; 36import com.android.dx.rop.cst.CstShort; 37import com.android.dx.rop.cst.CstString; 38import com.android.dx.rop.cst.CstType; 39import com.android.dx.rop.cst.CstUtf8; 40import com.android.dx.util.AnnotatedOutput; 41import com.android.dx.util.Hex; 42 43import java.util.Collection; 44 45/** 46 * Handler for writing out <code>encoded_values</code> and parts 47 * thereof. 48 */ 49public final class ValueEncoder { 50 /** annotation value type constant: <code>byte</code> */ 51 private static final int VALUE_BYTE = 0x00; 52 53 /** annotation value type constant: <code>short</code> */ 54 private static final int VALUE_SHORT = 0x02; 55 56 /** annotation value type constant: <code>char</code> */ 57 private static final int VALUE_CHAR = 0x03; 58 59 /** annotation value type constant: <code>int</code> */ 60 private static final int VALUE_INT = 0x04; 61 62 /** annotation value type constant: <code>long</code> */ 63 private static final int VALUE_LONG = 0x06; 64 65 /** annotation value type constant: <code>float</code> */ 66 private static final int VALUE_FLOAT = 0x10; 67 68 /** annotation value type constant: <code>double</code> */ 69 private static final int VALUE_DOUBLE = 0x11; 70 71 /** annotation value type constant: <code>string</code> */ 72 private static final int VALUE_STRING = 0x17; 73 74 /** annotation value type constant: <code>type</code> */ 75 private static final int VALUE_TYPE = 0x18; 76 77 /** annotation value type constant: <code>field</code> */ 78 private static final int VALUE_FIELD = 0x19; 79 80 /** annotation value type constant: <code>method</code> */ 81 private static final int VALUE_METHOD = 0x1a; 82 83 /** annotation value type constant: <code>enum</code> */ 84 private static final int VALUE_ENUM = 0x1b; 85 86 /** annotation value type constant: <code>array</code> */ 87 private static final int VALUE_ARRAY = 0x1c; 88 89 /** annotation value type constant: <code>annotation</code> */ 90 private static final int VALUE_ANNOTATION = 0x1d; 91 92 /** annotation value type constant: <code>null</code> */ 93 private static final int VALUE_NULL = 0x1e; 94 95 /** annotation value type constant: <code>boolean</code> */ 96 private static final int VALUE_BOOLEAN = 0x1f; 97 98 /** non-null; file being written */ 99 private final DexFile file; 100 101 /** non-null; output stream to write to */ 102 private final AnnotatedOutput out; 103 104 /** 105 * Construct an instance. 106 * 107 * @param file non-null; file being written 108 * @param out non-null; output stream to write to 109 */ 110 public ValueEncoder(DexFile file, AnnotatedOutput out) { 111 if (file == null) { 112 throw new NullPointerException("file == null"); 113 } 114 115 if (out == null) { 116 throw new NullPointerException("out == null"); 117 } 118 119 this.file = file; 120 this.out = out; 121 } 122 123 /** 124 * Writes out the encoded form of the given constant. 125 * 126 * @param cst non-null; the constant to write 127 */ 128 public void writeConstant(Constant cst) { 129 int type = constantToValueType(cst); 130 int arg; 131 132 switch (type) { 133 case VALUE_BYTE: 134 case VALUE_SHORT: 135 case VALUE_INT: 136 case VALUE_LONG: { 137 long value = ((CstLiteralBits) cst).getLongBits(); 138 writeSignedIntegralValue(type, value); 139 break; 140 } 141 case VALUE_CHAR: { 142 long value = ((CstLiteralBits) cst).getLongBits(); 143 writeUnsignedIntegralValue(type, value); 144 break; 145 } 146 case VALUE_FLOAT: { 147 // Shift value left 32 so that right-zero-extension works. 148 long value = ((CstFloat) cst).getLongBits() << 32; 149 writeRightZeroExtendedValue(type, value); 150 break; 151 } 152 case VALUE_DOUBLE: { 153 long value = ((CstDouble) cst).getLongBits(); 154 writeRightZeroExtendedValue(type, value); 155 break; 156 } 157 case VALUE_STRING: { 158 int index = file.getStringIds().indexOf((CstString) cst); 159 writeUnsignedIntegralValue(type, (long) index); 160 break; 161 } 162 case VALUE_TYPE: { 163 int index = file.getTypeIds().indexOf((CstType) cst); 164 writeUnsignedIntegralValue(type, (long) index); 165 break; 166 } 167 case VALUE_FIELD: { 168 int index = file.getFieldIds().indexOf((CstFieldRef) cst); 169 writeUnsignedIntegralValue(type, (long) index); 170 break; 171 } 172 case VALUE_METHOD: { 173 int index = file.getMethodIds().indexOf((CstMethodRef) cst); 174 writeUnsignedIntegralValue(type, (long) index); 175 break; 176 } 177 case VALUE_ENUM: { 178 CstFieldRef fieldRef = ((CstEnumRef) cst).getFieldRef(); 179 int index = file.getFieldIds().indexOf(fieldRef); 180 writeUnsignedIntegralValue(type, (long) index); 181 break; 182 } 183 case VALUE_ARRAY: { 184 out.writeByte(type); 185 writeArray((CstArray) cst, false); 186 break; 187 } 188 case VALUE_ANNOTATION: { 189 out.writeByte(type); 190 writeAnnotation(((CstAnnotation) cst).getAnnotation(), 191 false); 192 break; 193 } 194 case VALUE_NULL: { 195 out.writeByte(type); 196 break; 197 } 198 case VALUE_BOOLEAN: { 199 int value = ((CstBoolean) cst).getIntBits(); 200 out.writeByte(type | (value << 5)); 201 break; 202 } 203 default: { 204 throw new RuntimeException("Shouldn't happen"); 205 } 206 } 207 } 208 209 /** 210 * Gets the value type for the given constant. 211 * 212 * @param cst non-null; the constant 213 * @return the value type; one of the <code>VALUE_*</code> constants 214 * defined by this class 215 */ 216 private static int constantToValueType(Constant cst) { 217 /* 218 * TODO: Constant should probable have an associated enum, so this 219 * can be a switch(). 220 */ 221 if (cst instanceof CstByte) { 222 return VALUE_BYTE; 223 } else if (cst instanceof CstShort) { 224 return VALUE_SHORT; 225 } else if (cst instanceof CstChar) { 226 return VALUE_CHAR; 227 } else if (cst instanceof CstInteger) { 228 return VALUE_INT; 229 } else if (cst instanceof CstLong) { 230 return VALUE_LONG; 231 } else if (cst instanceof CstFloat) { 232 return VALUE_FLOAT; 233 } else if (cst instanceof CstDouble) { 234 return VALUE_DOUBLE; 235 } else if (cst instanceof CstString) { 236 return VALUE_STRING; 237 } else if (cst instanceof CstType) { 238 return VALUE_TYPE; 239 } else if (cst instanceof CstFieldRef) { 240 return VALUE_FIELD; 241 } else if (cst instanceof CstMethodRef) { 242 return VALUE_METHOD; 243 } else if (cst instanceof CstEnumRef) { 244 return VALUE_ENUM; 245 } else if (cst instanceof CstArray) { 246 return VALUE_ARRAY; 247 } else if (cst instanceof CstAnnotation) { 248 return VALUE_ANNOTATION; 249 } else if (cst instanceof CstKnownNull) { 250 return VALUE_NULL; 251 } else if (cst instanceof CstBoolean) { 252 return VALUE_BOOLEAN; 253 } else { 254 throw new RuntimeException("Shouldn't happen"); 255 } 256 } 257 258 /** 259 * Writes out the encoded form of the given array, that is, as 260 * an <code>encoded_array</code> and not including a 261 * <code>value_type</code> prefix. If the output stream keeps 262 * (debugging) annotations and <code>topLevel</code> is 263 * <code>true</code>, then this method will write (debugging) 264 * annotations. 265 * 266 * @param array non-null; array instance to write 267 * @param topLevel <code>true</code> iff the given annotation is the 268 * top-level annotation or <code>false</code> if it is a sub-annotation 269 * of some other annotation 270 */ 271 public void writeArray(CstArray array, boolean topLevel) { 272 boolean annotates = topLevel && out.annotates(); 273 CstArray.List list = ((CstArray) array).getList(); 274 int size = list.size(); 275 276 if (annotates) { 277 out.annotate(" size: " + Hex.u4(size)); 278 } 279 280 out.writeUnsignedLeb128(size); 281 282 for (int i = 0; i < size; i++) { 283 Constant cst = list.get(i); 284 if (annotates) { 285 out.annotate(" [" + Integer.toHexString(i) + "] " + 286 constantToHuman(cst)); 287 } 288 writeConstant(cst); 289 } 290 291 if (annotates) { 292 out.endAnnotation(); 293 } 294 } 295 296 /** 297 * Writes out the encoded form of the given annotation, that is, 298 * as an <code>encoded_annotation</code> and not including a 299 * <code>value_type</code> prefix. If the output stream keeps 300 * (debugging) annotations and <code>topLevel</code> is 301 * <code>true</code>, then this method will write (debugging) 302 * annotations. 303 * 304 * @param annotation non-null; annotation instance to write 305 * @param topLevel <code>true</code> iff the given annotation is the 306 * top-level annotation or <code>false</code> if it is a sub-annotation 307 * of some other annotation 308 */ 309 public void writeAnnotation(Annotation annotation, boolean topLevel) { 310 boolean annotates = topLevel && out.annotates(); 311 StringIdsSection stringIds = file.getStringIds(); 312 TypeIdsSection typeIds = file.getTypeIds(); 313 314 CstType type = annotation.getType(); 315 int typeIdx = typeIds.indexOf(type); 316 317 if (annotates) { 318 out.annotate(" type_idx: " + Hex.u4(typeIdx) + " // " + 319 type.toHuman()); 320 } 321 322 out.writeUnsignedLeb128(typeIds.indexOf(annotation.getType())); 323 324 Collection<NameValuePair> pairs = annotation.getNameValuePairs(); 325 int size = pairs.size(); 326 327 if (annotates) { 328 out.annotate(" size: " + Hex.u4(size)); 329 } 330 331 out.writeUnsignedLeb128(size); 332 333 int at = 0; 334 for (NameValuePair pair : pairs) { 335 CstUtf8 name = pair.getName(); 336 int nameIdx = stringIds.indexOf(name); 337 Constant value = pair.getValue(); 338 339 if (annotates) { 340 out.annotate(0, " elements[" + at + "]:"); 341 at++; 342 out.annotate(" name_idx: " + Hex.u4(nameIdx) + " // " + 343 name.toHuman()); 344 } 345 346 out.writeUnsignedLeb128(nameIdx); 347 348 if (annotates) { 349 out.annotate(" value: " + constantToHuman(value)); 350 } 351 352 writeConstant(value); 353 } 354 355 if (annotates) { 356 out.endAnnotation(); 357 } 358 } 359 360 /** 361 * Gets the colloquial type name and human form of the type of the 362 * given constant, when used as an encoded value. 363 * 364 * @param cst non-null; the constant 365 * @return non-null; its type name and human form 366 */ 367 public static String constantToHuman(Constant cst) { 368 int type = constantToValueType(cst); 369 370 if (type == VALUE_NULL) { 371 return "null"; 372 } 373 374 StringBuilder sb = new StringBuilder(); 375 376 sb.append(cst.typeName()); 377 sb.append(' '); 378 sb.append(cst.toHuman()); 379 380 return sb.toString(); 381 } 382 383 /** 384 * Helper for {@link #writeConstant}, which writes out the value 385 * for any signed integral type. 386 * 387 * @param type the type constant 388 * @param value <code>long</code> bits of the value 389 */ 390 private void writeSignedIntegralValue(int type, long value) { 391 /* 392 * Figure out how many bits are needed to represent the value, 393 * including a sign bit: The bit count is subtracted from 65 394 * and not 64 to account for the sign bit. The xor operation 395 * has the effect of leaving non-negative values alone and 396 * unary complementing negative values (so that a leading zero 397 * count always returns a useful number for our present 398 * purpose). 399 */ 400 int requiredBits = 401 65 - Long.numberOfLeadingZeros(value ^ (value >> 63)); 402 403 // Round up the requiredBits to a number of bytes. 404 int requiredBytes = (requiredBits + 0x07) >> 3; 405 406 /* 407 * Write the header byte, which includes the type and 408 * requiredBytes - 1. 409 */ 410 out.writeByte(type | ((requiredBytes - 1) << 5)); 411 412 // Write the value, per se. 413 while (requiredBytes > 0) { 414 out.writeByte((byte) value); 415 value >>= 8; 416 requiredBytes--; 417 } 418 } 419 420 /** 421 * Helper for {@link #writeConstant}, which writes out the value 422 * for any unsigned integral type. 423 * 424 * @param type the type constant 425 * @param value <code>long</code> bits of the value 426 */ 427 private void writeUnsignedIntegralValue(int type, long value) { 428 // Figure out how many bits are needed to represent the value. 429 int requiredBits = 64 - Long.numberOfLeadingZeros(value); 430 if (requiredBits == 0) { 431 requiredBits = 1; 432 } 433 434 // Round up the requiredBits to a number of bytes. 435 int requiredBytes = (requiredBits + 0x07) >> 3; 436 437 /* 438 * Write the header byte, which includes the type and 439 * requiredBytes - 1. 440 */ 441 out.writeByte(type | ((requiredBytes - 1) << 5)); 442 443 // Write the value, per se. 444 while (requiredBytes > 0) { 445 out.writeByte((byte) value); 446 value >>= 8; 447 requiredBytes--; 448 } 449 } 450 451 /** 452 * Helper for {@link #writeConstant}, which writes out a 453 * right-zero-extended value. 454 * 455 * @param type the type constant 456 * @param value <code>long</code> bits of the value 457 */ 458 private void writeRightZeroExtendedValue(int type, long value) { 459 // Figure out how many bits are needed to represent the value. 460 int requiredBits = 64 - Long.numberOfTrailingZeros(value); 461 if (requiredBits == 0) { 462 requiredBits = 1; 463 } 464 465 // Round up the requiredBits to a number of bytes. 466 int requiredBytes = (requiredBits + 0x07) >> 3; 467 468 // Scootch the first bits to be written down to the low-order bits. 469 value >>= 64 - (requiredBytes * 8); 470 471 /* 472 * Write the header byte, which includes the type and 473 * requiredBytes - 1. 474 */ 475 out.writeByte(type | ((requiredBytes - 1) << 5)); 476 477 // Write the value, per se. 478 while (requiredBytes > 0) { 479 out.writeByte((byte) value); 480 value >>= 8; 481 requiredBytes--; 482 } 483 } 484 485 486 /** 487 * Helper for <code>addContents()</code> methods, which adds 488 * contents for a particular {@link Annotation}, calling itself 489 * recursively should it encounter a nested annotation. 490 * 491 * @param file non-null; the file to add to 492 * @param annotation non-null; the annotation to add contents for 493 */ 494 public static void addContents(DexFile file, Annotation annotation) { 495 TypeIdsSection typeIds = file.getTypeIds(); 496 StringIdsSection stringIds = file.getStringIds(); 497 498 typeIds.intern(annotation.getType()); 499 500 for (NameValuePair pair : annotation.getNameValuePairs()) { 501 stringIds.intern(pair.getName()); 502 addContents(file, pair.getValue()); 503 } 504 } 505 506 /** 507 * Helper for <code>addContents()</code> methods, which adds 508 * contents for a particular constant, calling itself recursively 509 * should it encounter a {@link CstArray} and calling {@link 510 * #addContents(DexFile,Annotation)} recursively should it 511 * encounter a {@link CstAnnotation}. 512 * 513 * @param file non-null; the file to add to 514 * @param cst non-null; the constant to add contents for 515 */ 516 public static void addContents(DexFile file, Constant cst) { 517 TypeIdsSection typeIds = file.getTypeIds(); 518 StringIdsSection stringIds = file.getStringIds(); 519 520 if (cst instanceof CstAnnotation) { 521 addContents(file, ((CstAnnotation) cst).getAnnotation()); 522 } else if (cst instanceof CstArray) { 523 CstArray.List list = ((CstArray) cst).getList(); 524 int size = list.size(); 525 for (int i = 0; i < size; i++) { 526 addContents(file, list.get(i)); 527 } 528 } else { 529 file.internIfAppropriate(cst); 530 } 531 } 532} 533