/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dex.reader; import static dex.structure.DexEncodedValueType.*; import dex.reader.DexFileReader.FieldIdItem; import dex.structure.DexAnnotation; import dex.structure.DexEncodedValue; import dex.structure.DexEncodedValueType; import java.util.ArrayList; import java.util.List; /* package */final class DexEncodedValueImpl implements DexEncodedValue { private final DexBuffer buffer; private byte typeAndValueArg; private DexEncodedValueType type; private String[] stringPool; private Object value; private int[] typeIds; private final FieldIdItem[] fieldIdItems; private final DexAnnotation annotation; /** * * @param buffer * the buffer with the correct position * @param annotation * @param stringPool * @param fieldIdItems */ public DexEncodedValueImpl(DexBuffer buffer, DexAnnotation annotation, int[] typeIds, String[] stringPool, FieldIdItem[] fieldIdItems) { this.buffer = buffer; this.annotation = annotation; this.typeIds = typeIds; this.stringPool = stringPool; this.fieldIdItems = fieldIdItems; parseValue(); } private void parseValue() { typeAndValueArg = buffer.readUByte(); type = DexEncodedValueType.get(typeAndValueArg); int valueArg = DexEncodedValueType.valueArg(typeAndValueArg); switch (type) { case VALUE_BYTE: value = getByteValue(valueArg); break; case VALUE_SHORT: value = getShortValue(valueArg); break; case VALUE_CHAR: value = getCharValue(valueArg); break; case VALUE_INT: value = getIntValue(valueArg); break; case VALUE_LONG: value = getLongValue(valueArg); break; case VALUE_FLOAT: value = getFloatValue(valueArg); break; case VALUE_DOUBLE: value = getDoubleValue(valueArg); break; case VALUE_STRING: value = getStringValue(valueArg); break; case VALUE_TYPE: value = getTypeValue(valueArg); break; case VALUE_FIELD: value = getFieldValue(valueArg); break; case VALUE_METHOD: value = getMethodValue(valueArg); break; case VALUE_ENUM: value = getEnumValue(valueArg); break; case VALUE_ARRAY: value = getArrayValue(valueArg); break; case VALUE_ANNOTATION: value = getAnnotationValue(valueArg); break; case VALUE_NULL: value = getNullValue(valueArg); break; case VALUE_BOOLEAN: value = getBooleanValue(valueArg); break; default: throw new IllegalArgumentException("DexEncodedValueType " + type + " not recognized"); } } /** * VALUE_BOOLEAN 0x1f boolean (0...1) (none) one-bit value; 0 for false and * 1 for true. The bit is represented in the value_arg. */ private Boolean getBooleanValue(int valueArg) { return valueArg == 1; } /** VALUE_NULL 0x1e (none; must be 0) (none) null reference value */ private Object getNullValue(int valueArg) { return null; // must be like that! } /** * VALUE_ANNOTATION 0x1d (none; must be 0) encoded_annotation a * sub-annotation, in the format specified by "encoded_annotation Format" * below. The size of the value is implicit in the encoding. */ private Object getAnnotationValue(int valueArg) { // use the buffer directly to get adjusted offset return new DexEncodedAnnotationImpl(buffer, annotation, typeIds, stringPool, fieldIdItems); } /** * VALUE_ARRAY 0x1c (none; must be 0) encoded_array an array of values, in * the format specified by "encoded_array Format" below. The size of the * value is implicit in the encoding. */ private List<DexEncodedValue> getArrayValue(int valueArg) { int size = buffer.readUleb128(); List<DexEncodedValue> values = new ArrayList<DexEncodedValue>(size); for (int i = 0; i < size; i++) { values.add(new DexEncodedValueImpl(buffer, annotation, typeIds, stringPool, fieldIdItems)); } return values; } /** * VALUE_ENUM 0x1b size - 1 (0...3) ubyte[size] unsigned (zero-extended) * four-byte integer value, interpreted as an index into the field_ids * section and representing the value of an enumerated type constant */ private Object getEnumValue(int valueArg) { int fieldOffset = buffer.readInt(valueArg + 1); FieldIdItem fieldIdItem = fieldIdItems[fieldOffset]; // FORMAT La/b/E;!CONSTANT String constantName = stringPool[fieldIdItem.name_idx]; String typeName = stringPool[typeIds[fieldIdItem.type_idx]]; return typeName + "!" + constantName; } /** * VALUE_METHOD 0x1a size - 1 (0...3) ubyte[size] unsigned (zero-extended) * four-byte integer value, interpreted as an index into the method_ids * section and representing a reflective method value */ private Object getMethodValue(int valueArg) { // FIXME lookup value buffer.skip(valueArg + 1); return null; } /** * VALUE_FIELD 0x19 size - 1 (0...3) ubyte[size] unsigned (zero-extended) * four-byte integer value, interpreted as an index into the field_ids * section and representing a reflective field value */ private Object getFieldValue(int valueArg) { int fieldOffset = buffer.readInt(valueArg + 1); FieldIdItem fieldIdItem = fieldIdItems[fieldOffset]; // FORMAT La/b/E;!CONSTANT String fieldName = stringPool[fieldIdItem.name_idx]; String typeName = stringPool[typeIds[fieldIdItem.type_idx]]; return typeName + "!" + fieldName; } /** * VALUE_TYPE 0x18 size - 1 (0...3) ubyte[size] unsigned (zero-extended) * four-byte integer value, interpreted as an index into the type_ids * section and representing a reflective type/class value */ private Object getTypeValue(int valueArg) { valueArg++; // size - 1 (0...3) // FIXME SPEC!! states: unsigned (zero-extended) four-byte integer value return stringPool[typeIds[buffer.readInt(valueArg)]]; } /** * VALUE_STRING 0x17 size - 1 (0...3) ubyte[size] unsigned (zero-extended) * four-byte integer value, interpreted as an index into the string_ids * section and representing a string value */ private Object getStringValue(int valueArg) { valueArg++; return stringPool[buffer.readInt(valueArg)]; } /** * VALUE_DOUBLE 0x11 size - 1 (0...7) ubyte[size] eight-byte bit pattern, * zero-extended to the right, and interpreted as an IEEE754 64-bit floating * point value */ private Object getDoubleValue(int valueArg) { return buffer.readDouble(valueArg + 1); } /** * VALUE_FLOAT 0x10 size - 1 (0...3) ubyte[size] four-byte bit pattern, * zero-extended to the right, and interpreted as an IEEE754 32-bit floating * point value */ private Float getFloatValue(int valueArg) { return buffer.readFloat(valueArg + 1); } /** * VALUE_LONG 0x06 size - 1 (0...7) ubyte[size] signed eight-byte integer * value, sign-extended */ private Long getLongValue(int valueArg) { return buffer.readLong(valueArg + 1); } /** * VALUE_INT 0x04 size - 1 (0...3) ubyte[size] signed four-byte integer * value, sign-extended */ private Integer getIntValue(int valueArg) { return buffer.readInt(valueArg + 1); } /** * VALUE_CHAR 0x03 size - 1 (0...1) ubyte[size] unsigned two-byte integer * value, zero-extended */ private Character getCharValue(int valueArg) { return buffer.readChar(valueArg + 1); } /** * VALUE_SHORT 0x02 size - 1 (0...1) ubyte[size] signed two-byte integer * value, sign-extended */ private Short getShortValue(int valueArg) { return buffer.readShort(valueArg + 1); } /** * VALUE_BYTE 0x00 (none; must be 0) ubyte[1] signed one-byte integer value */ private Byte getByteValue(int valueArg) { assert valueArg == 0 : "Illegal valueArg for VALUE_BYTE: " + valueArg; return null; } public DexEncodedValueType getType() { return type; } public Object getValue() { return value; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("="); if (type == VALUE_ARRAY) { if (getValue() instanceof List<?>) { List<?> values = (List<?>) getValue(); for (Object object : values) { DexEncodedValue val = (DexEncodedValue) object; builder.append(val.getValue()); } } } else { builder.append(getValue()); } return builder.toString(); } }