/*
* 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();
}
}