blob: 24dc0e37456bed92305409fcd17e8a552dc80eed [file] [log] [blame]
/*
* Copyright (C) 2011 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 com.android.dex;
import com.android.dex.util.ByteInput;
/**
* Pull parser for encoded values.
*/
public final class EncodedValueReader {
public static final int ENCODED_BYTE = 0x00;
public static final int ENCODED_SHORT = 0x02;
public static final int ENCODED_CHAR = 0x03;
public static final int ENCODED_INT = 0x04;
public static final int ENCODED_LONG = 0x06;
public static final int ENCODED_FLOAT = 0x10;
public static final int ENCODED_DOUBLE = 0x11;
public static final int ENCODED_METHOD_TYPE = 0x15;
public static final int ENCODED_METHOD_HANDLE = 0x16;
public static final int ENCODED_STRING = 0x17;
public static final int ENCODED_TYPE = 0x18;
public static final int ENCODED_FIELD = 0x19;
public static final int ENCODED_ENUM = 0x1b;
public static final int ENCODED_METHOD = 0x1a;
public static final int ENCODED_ARRAY = 0x1c;
public static final int ENCODED_ANNOTATION = 0x1d;
public static final int ENCODED_NULL = 0x1e;
public static final int ENCODED_BOOLEAN = 0x1f;
/** placeholder type if the type is not yet known */
private static final int MUST_READ = -1;
protected final ByteInput in;
private int type = MUST_READ;
private int annotationType;
private int arg;
public EncodedValueReader(ByteInput in) {
this.in = in;
}
public EncodedValueReader(EncodedValue in) {
this(in.asByteInput());
}
/**
* Creates a new encoded value reader whose only value is the specified
* known type. This is useful for encoded values without a type prefix,
* such as class_def_item's encoded_array or annotation_item's
* encoded_annotation.
*/
public EncodedValueReader(ByteInput in, int knownType) {
this.in = in;
this.type = knownType;
}
public EncodedValueReader(EncodedValue in, int knownType) {
this(in.asByteInput(), knownType);
}
/**
* Returns the type of the next value to read.
*/
public int peek() {
if (type == MUST_READ) {
int argAndType = in.readByte() & 0xff;
type = argAndType & 0x1f;
arg = (argAndType & 0xe0) >> 5;
}
return type;
}
/**
* Begins reading the elements of an array, returning the array's size. The
* caller must follow up by calling a read method for each element in the
* array. For example, this reads a byte array: <pre> {@code
* int arraySize = readArray();
* for (int i = 0, i < arraySize; i++) {
* readByte();
* }
* }</pre>
*/
public int readArray() {
checkType(ENCODED_ARRAY);
type = MUST_READ;
return Leb128.readUnsignedLeb128(in);
}
/**
* Begins reading the fields of an annotation, returning the number of
* fields. The caller must follow up by making alternating calls to {@link
* #readAnnotationName()} and another read method. For example, this reads
* an annotation whose fields are all bytes: <pre> {@code
* int fieldCount = readAnnotation();
* int annotationType = getAnnotationType();
* for (int i = 0; i < fieldCount; i++) {
* readAnnotationName();
* readByte();
* }
* }</pre>
*/
public int readAnnotation() {
checkType(ENCODED_ANNOTATION);
type = MUST_READ;
annotationType = Leb128.readUnsignedLeb128(in);
return Leb128.readUnsignedLeb128(in);
}
/**
* Returns the type of the annotation just returned by {@link
* #readAnnotation()}. This method's value is undefined unless the most
* recent call was to {@link #readAnnotation()}.
*/
public int getAnnotationType() {
return annotationType;
}
public int readAnnotationName() {
return Leb128.readUnsignedLeb128(in);
}
public byte readByte() {
checkType(ENCODED_BYTE);
type = MUST_READ;
return (byte) EncodedValueCodec.readSignedInt(in, arg);
}
public short readShort() {
checkType(ENCODED_SHORT);
type = MUST_READ;
return (short) EncodedValueCodec.readSignedInt(in, arg);
}
public char readChar() {
checkType(ENCODED_CHAR);
type = MUST_READ;
return (char) EncodedValueCodec.readUnsignedInt(in, arg, false);
}
public int readInt() {
checkType(ENCODED_INT);
type = MUST_READ;
return EncodedValueCodec.readSignedInt(in, arg);
}
public long readLong() {
checkType(ENCODED_LONG);
type = MUST_READ;
return EncodedValueCodec.readSignedLong(in, arg);
}
public float readFloat() {
checkType(ENCODED_FLOAT);
type = MUST_READ;
return Float.intBitsToFloat(EncodedValueCodec.readUnsignedInt(in, arg, true));
}
public double readDouble() {
checkType(ENCODED_DOUBLE);
type = MUST_READ;
return Double.longBitsToDouble(EncodedValueCodec.readUnsignedLong(in, arg, true));
}
public int readMethodType() {
checkType(ENCODED_METHOD_TYPE);
type = MUST_READ;
return EncodedValueCodec.readUnsignedInt(in, arg, false);
}
public int readMethodHandle() {
checkType(ENCODED_METHOD_HANDLE);
type = MUST_READ;
return EncodedValueCodec.readUnsignedInt(in, arg, false);
}
public int readString() {
checkType(ENCODED_STRING);
type = MUST_READ;
return EncodedValueCodec.readUnsignedInt(in, arg, false);
}
public int readType() {
checkType(ENCODED_TYPE);
type = MUST_READ;
return EncodedValueCodec.readUnsignedInt(in, arg, false);
}
public int readField() {
checkType(ENCODED_FIELD);
type = MUST_READ;
return EncodedValueCodec.readUnsignedInt(in, arg, false);
}
public int readEnum() {
checkType(ENCODED_ENUM);
type = MUST_READ;
return EncodedValueCodec.readUnsignedInt(in, arg, false);
}
public int readMethod() {
checkType(ENCODED_METHOD);
type = MUST_READ;
return EncodedValueCodec.readUnsignedInt(in, arg, false);
}
public void readNull() {
checkType(ENCODED_NULL);
type = MUST_READ;
}
public boolean readBoolean() {
checkType(ENCODED_BOOLEAN);
type = MUST_READ;
return arg != 0;
}
/**
* Skips a single value, including its nested values if it is an array or
* annotation.
*/
public void skipValue() {
switch (peek()) {
case ENCODED_BYTE:
readByte();
break;
case ENCODED_SHORT:
readShort();
break;
case ENCODED_CHAR:
readChar();
break;
case ENCODED_INT:
readInt();
break;
case ENCODED_LONG:
readLong();
break;
case ENCODED_FLOAT:
readFloat();
break;
case ENCODED_DOUBLE:
readDouble();
break;
case ENCODED_METHOD_TYPE:
readMethodType();
break;
case ENCODED_METHOD_HANDLE:
readMethodHandle();
break;
case ENCODED_STRING:
readString();
break;
case ENCODED_TYPE:
readType();
break;
case ENCODED_FIELD:
readField();
break;
case ENCODED_ENUM:
readEnum();
break;
case ENCODED_METHOD:
readMethod();
break;
case ENCODED_ARRAY:
for (int i = 0, size = readArray(); i < size; i++) {
skipValue();
}
break;
case ENCODED_ANNOTATION:
for (int i = 0, size = readAnnotation(); i < size; i++) {
readAnnotationName();
skipValue();
}
break;
case ENCODED_NULL:
readNull();
break;
case ENCODED_BOOLEAN:
readBoolean();
break;
default:
throw new DexException("Unexpected type: " + Integer.toHexString(type));
}
}
private void checkType(int expected) {
if (peek() != expected) {
throw new IllegalStateException(
String.format("Expected %x but was %x", expected, peek()));
}
}
}