308 lines
8.9 KiB
Java
308 lines
8.9 KiB
Java
/*
|
|
* 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 external.com.android.dex;
|
|
|
|
import external.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()));
|
|
}
|
|
}
|
|
}
|