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