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