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