com.android.dx.cf.direct.StdAttributeFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of builder Show documentation
Show all versions of builder Show documentation
Library to build Android applications.
/*
* Copyright (C) 2007 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.dx.cf.direct;
import com.android.dx.cf.attrib.AttAnnotationDefault;
import com.android.dx.cf.attrib.AttCode;
import com.android.dx.cf.attrib.AttConstantValue;
import com.android.dx.cf.attrib.AttDeprecated;
import com.android.dx.cf.attrib.AttEnclosingMethod;
import com.android.dx.cf.attrib.AttExceptions;
import com.android.dx.cf.attrib.AttInnerClasses;
import com.android.dx.cf.attrib.AttLineNumberTable;
import com.android.dx.cf.attrib.AttLocalVariableTable;
import com.android.dx.cf.attrib.AttLocalVariableTypeTable;
import com.android.dx.cf.attrib.AttRuntimeInvisibleAnnotations;
import com.android.dx.cf.attrib.AttRuntimeInvisibleParameterAnnotations;
import com.android.dx.cf.attrib.AttRuntimeVisibleAnnotations;
import com.android.dx.cf.attrib.AttRuntimeVisibleParameterAnnotations;
import com.android.dx.cf.attrib.AttSignature;
import com.android.dx.cf.attrib.AttSourceFile;
import com.android.dx.cf.attrib.AttSynthetic;
import com.android.dx.cf.attrib.InnerClassList;
import com.android.dx.cf.code.ByteCatchList;
import com.android.dx.cf.code.BytecodeArray;
import com.android.dx.cf.code.LineNumberList;
import com.android.dx.cf.code.LocalVariableList;
import com.android.dx.cf.iface.Attribute;
import com.android.dx.cf.iface.ParseException;
import com.android.dx.cf.iface.ParseObserver;
import com.android.dx.cf.iface.StdAttributeList;
import com.android.dx.rop.annotation.AnnotationVisibility;
import com.android.dx.rop.annotation.Annotations;
import com.android.dx.rop.annotation.AnnotationsList;
import com.android.dx.rop.code.AccessFlags;
import com.android.dx.rop.cst.Constant;
import com.android.dx.rop.cst.ConstantPool;
import com.android.dx.rop.cst.CstNat;
import com.android.dx.rop.cst.CstString;
import com.android.dx.rop.cst.CstType;
import com.android.dx.rop.cst.TypedConstant;
import com.android.dx.rop.type.TypeList;
import com.android.dx.util.ByteArray;
import com.android.dx.util.Hex;
import java.io.IOException;
/**
* Standard subclass of {@link AttributeFactory}, which knows how to parse
* all the standard attribute types.
*/
public class StdAttributeFactory
extends AttributeFactory {
/** {@code non-null;} shared instance of this class */
public static final StdAttributeFactory THE_ONE =
new StdAttributeFactory();
/**
* Constructs an instance.
*/
public StdAttributeFactory() {
// This space intentionally left blank.
}
/** {@inheritDoc} */
@Override
protected Attribute parse0(DirectClassFile cf, int context, String name,
int offset, int length, ParseObserver observer) {
switch (context) {
case CTX_CLASS: {
if (name == AttDeprecated.ATTRIBUTE_NAME) {
return deprecated(cf, offset, length, observer);
}
if (name == AttEnclosingMethod.ATTRIBUTE_NAME) {
return enclosingMethod(cf, offset, length, observer);
}
if (name == AttInnerClasses.ATTRIBUTE_NAME) {
return innerClasses(cf, offset, length, observer);
}
if (name == AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME) {
return runtimeInvisibleAnnotations(cf, offset, length,
observer);
}
if (name == AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME) {
return runtimeVisibleAnnotations(cf, offset, length,
observer);
}
if (name == AttSynthetic.ATTRIBUTE_NAME) {
return synthetic(cf, offset, length, observer);
}
if (name == AttSignature.ATTRIBUTE_NAME) {
return signature(cf, offset, length, observer);
}
if (name == AttSourceFile.ATTRIBUTE_NAME) {
return sourceFile(cf, offset, length, observer);
}
break;
}
case CTX_FIELD: {
if (name == AttConstantValue.ATTRIBUTE_NAME) {
return constantValue(cf, offset, length, observer);
}
if (name == AttDeprecated.ATTRIBUTE_NAME) {
return deprecated(cf, offset, length, observer);
}
if (name == AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME) {
return runtimeInvisibleAnnotations(cf, offset, length,
observer);
}
if (name == AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME) {
return runtimeVisibleAnnotations(cf, offset, length,
observer);
}
if (name == AttSignature.ATTRIBUTE_NAME) {
return signature(cf, offset, length, observer);
}
if (name == AttSynthetic.ATTRIBUTE_NAME) {
return synthetic(cf, offset, length, observer);
}
break;
}
case CTX_METHOD: {
if (name == AttAnnotationDefault.ATTRIBUTE_NAME) {
return annotationDefault(cf, offset, length, observer);
}
if (name == AttCode.ATTRIBUTE_NAME) {
return code(cf, offset, length, observer);
}
if (name == AttDeprecated.ATTRIBUTE_NAME) {
return deprecated(cf, offset, length, observer);
}
if (name == AttExceptions.ATTRIBUTE_NAME) {
return exceptions(cf, offset, length, observer);
}
if (name == AttRuntimeInvisibleAnnotations.ATTRIBUTE_NAME) {
return runtimeInvisibleAnnotations(cf, offset, length,
observer);
}
if (name == AttRuntimeVisibleAnnotations.ATTRIBUTE_NAME) {
return runtimeVisibleAnnotations(cf, offset, length,
observer);
}
if (name == AttRuntimeInvisibleParameterAnnotations.
ATTRIBUTE_NAME) {
return runtimeInvisibleParameterAnnotations(
cf, offset, length, observer);
}
if (name == AttRuntimeVisibleParameterAnnotations.
ATTRIBUTE_NAME) {
return runtimeVisibleParameterAnnotations(
cf, offset, length, observer);
}
if (name == AttSignature.ATTRIBUTE_NAME) {
return signature(cf, offset, length, observer);
}
if (name == AttSynthetic.ATTRIBUTE_NAME) {
return synthetic(cf, offset, length, observer);
}
break;
}
case CTX_CODE: {
if (name == AttLineNumberTable.ATTRIBUTE_NAME) {
return lineNumberTable(cf, offset, length, observer);
}
if (name == AttLocalVariableTable.ATTRIBUTE_NAME) {
return localVariableTable(cf, offset, length, observer);
}
if (name == AttLocalVariableTypeTable.ATTRIBUTE_NAME) {
return localVariableTypeTable(cf, offset, length,
observer);
}
break;
}
}
return super.parse0(cf, context, name, offset, length, observer);
}
/**
* Parses an {@code AnnotationDefault} attribute.
*/
private Attribute annotationDefault(DirectClassFile cf,
int offset, int length, ParseObserver observer) {
if (length < 2) {
throwSeverelyTruncated();
}
AnnotationParser ap =
new AnnotationParser(cf, offset, length, observer);
Constant cst = ap.parseValueAttribute();
return new AttAnnotationDefault(cst, length);
}
/**
* Parses a {@code Code} attribute.
*/
private Attribute code(DirectClassFile cf, int offset, int length,
ParseObserver observer) {
if (length < 12) {
return throwSeverelyTruncated();
}
ByteArray bytes = cf.getBytes();
ConstantPool pool = cf.getConstantPool();
int maxStack = bytes.getUnsignedShort(offset); // u2 max_stack
int maxLocals = bytes.getUnsignedShort(offset + 2); // u2 max_locals
int codeLength = bytes.getInt(offset + 4); // u4 code_length
int origOffset = offset;
if (observer != null) {
observer.parsed(bytes, offset, 2,
"max_stack: " + Hex.u2(maxStack));
observer.parsed(bytes, offset + 2, 2,
"max_locals: " + Hex.u2(maxLocals));
observer.parsed(bytes, offset + 4, 4,
"code_length: " + Hex.u4(codeLength));
}
offset += 8;
length -= 8;
if (length < (codeLength + 4)) {
return throwTruncated();
}
int codeOffset = offset;
offset += codeLength;
length -= codeLength;
BytecodeArray code =
new BytecodeArray(bytes.slice(codeOffset, codeOffset + codeLength),
pool);
if (observer != null) {
code.forEach(new CodeObserver(code.getBytes(), observer));
}
// u2 exception_table_length
int exceptionTableLength = bytes.getUnsignedShort(offset);
ByteCatchList catches = (exceptionTableLength == 0) ?
ByteCatchList.EMPTY :
new ByteCatchList(exceptionTableLength);
if (observer != null) {
observer.parsed(bytes, offset, 2,
"exception_table_length: " +
Hex.u2(exceptionTableLength));
}
offset += 2;
length -= 2;
if (length < (exceptionTableLength * 8 + 2)) {
return throwTruncated();
}
for (int i = 0; i < exceptionTableLength; i++) {
if (observer != null) {
observer.changeIndent(1);
}
int startPc = bytes.getUnsignedShort(offset);
int endPc = bytes.getUnsignedShort(offset + 2);
int handlerPc = bytes.getUnsignedShort(offset + 4);
int catchTypeIdx = bytes.getUnsignedShort(offset + 6);
CstType catchType = (CstType) pool.get0Ok(catchTypeIdx);
catches.set(i, startPc, endPc, handlerPc, catchType);
if (observer != null) {
observer.parsed(bytes, offset, 8,
Hex.u2(startPc) + ".." + Hex.u2(endPc) +
" -> " + Hex.u2(handlerPc) + " " +
((catchType == null) ? "" :
catchType.toHuman()));
}
offset += 8;
length -= 8;
if (observer != null) {
observer.changeIndent(-1);
}
}
catches.setImmutable();
AttributeListParser parser =
new AttributeListParser(cf, CTX_CODE, offset, this);
parser.setObserver(observer);
StdAttributeList attributes = parser.getList();
attributes.setImmutable();
int attributeByteCount = parser.getEndOffset() - offset;
if (attributeByteCount != length) {
return throwBadLength(attributeByteCount + (offset - origOffset));
}
return new AttCode(maxStack, maxLocals, code, catches, attributes);
}
/**
* Parses a {@code ConstantValue} attribute.
*/
private Attribute constantValue(DirectClassFile cf, int offset, int length,
ParseObserver observer) {
if (length != 2) {
return throwBadLength(2);
}
ByteArray bytes = cf.getBytes();
ConstantPool pool = cf.getConstantPool();
int idx = bytes.getUnsignedShort(offset);
TypedConstant cst = (TypedConstant) pool.get(idx);
Attribute result = new AttConstantValue(cst);
if (observer != null) {
observer.parsed(bytes, offset, 2, "value: " + cst);
}
return result;
}
/**
* Parses a {@code Deprecated} attribute.
*/
private Attribute deprecated(DirectClassFile cf, int offset, int length,
ParseObserver observer) {
if (length != 0) {
return throwBadLength(0);
}
return new AttDeprecated();
}
/**
* Parses an {@code EnclosingMethod} attribute.
*/
private Attribute enclosingMethod(DirectClassFile cf, int offset,
int length, ParseObserver observer) {
if (length != 4) {
throwBadLength(4);
}
ByteArray bytes = cf.getBytes();
ConstantPool pool = cf.getConstantPool();
int idx = bytes.getUnsignedShort(offset);
CstType type = (CstType) pool.get(idx);
idx = bytes.getUnsignedShort(offset + 2);
CstNat method = (CstNat) pool.get0Ok(idx);
Attribute result = new AttEnclosingMethod(type, method);
if (observer != null) {
observer.parsed(bytes, offset, 2, "class: " + type);
observer.parsed(bytes, offset + 2, 2, "method: " +
DirectClassFile.stringOrNone(method));
}
return result;
}
/**
* Parses an {@code Exceptions} attribute.
*/
private Attribute exceptions(DirectClassFile cf, int offset, int length,
ParseObserver observer) {
if (length < 2) {
return throwSeverelyTruncated();
}
ByteArray bytes = cf.getBytes();
int count = bytes.getUnsignedShort(offset); // number_of_exceptions
if (observer != null) {
observer.parsed(bytes, offset, 2,
"number_of_exceptions: " + Hex.u2(count));
}
offset += 2;
length -= 2;
if (length != (count * 2)) {
throwBadLength((count * 2) + 2);
}
TypeList list = cf.makeTypeList(offset, count);
return new AttExceptions(list);
}
/**
* Parses an {@code InnerClasses} attribute.
*/
private Attribute innerClasses(DirectClassFile cf, int offset, int length,
ParseObserver observer) {
if (length < 2) {
return throwSeverelyTruncated();
}
ByteArray bytes = cf.getBytes();
ConstantPool pool = cf.getConstantPool();
int count = bytes.getUnsignedShort(offset); // number_of_classes
if (observer != null) {
observer.parsed(bytes, offset, 2,
"number_of_classes: " + Hex.u2(count));
}
offset += 2;
length -= 2;
if (length != (count * 8)) {
throwBadLength((count * 8) + 2);
}
InnerClassList list = new InnerClassList(count);
for (int i = 0; i < count; i++) {
int innerClassIdx = bytes.getUnsignedShort(offset);
int outerClassIdx = bytes.getUnsignedShort(offset + 2);
int nameIdx = bytes.getUnsignedShort(offset + 4);
int accessFlags = bytes.getUnsignedShort(offset + 6);
CstType innerClass = (CstType) pool.get(innerClassIdx);
CstType outerClass = (CstType) pool.get0Ok(outerClassIdx);
CstString name = (CstString) pool.get0Ok(nameIdx);
list.set(i, innerClass, outerClass, name, accessFlags);
if (observer != null) {
observer.parsed(bytes, offset, 2,
"inner_class: " +
DirectClassFile.stringOrNone(innerClass));
observer.parsed(bytes, offset + 2, 2,
" outer_class: " +
DirectClassFile.stringOrNone(outerClass));
observer.parsed(bytes, offset + 4, 2,
" name: " +
DirectClassFile.stringOrNone(name));
observer.parsed(bytes, offset + 6, 2,
" access_flags: " +
AccessFlags.innerClassString(accessFlags));
}
offset += 8;
}
list.setImmutable();
return new AttInnerClasses(list);
}
/**
* Parses a {@code LineNumberTable} attribute.
*/
private Attribute lineNumberTable(DirectClassFile cf, int offset,
int length, ParseObserver observer) {
if (length < 2) {
return throwSeverelyTruncated();
}
ByteArray bytes = cf.getBytes();
int count = bytes.getUnsignedShort(offset); // line_number_table_length
if (observer != null) {
observer.parsed(bytes, offset, 2,
"line_number_table_length: " + Hex.u2(count));
}
offset += 2;
length -= 2;
if (length != (count * 4)) {
throwBadLength((count * 4) + 2);
}
LineNumberList list = new LineNumberList(count);
for (int i = 0; i < count; i++) {
int startPc = bytes.getUnsignedShort(offset);
int lineNumber = bytes.getUnsignedShort(offset + 2);
list.set(i, startPc, lineNumber);
if (observer != null) {
observer.parsed(bytes, offset, 4,
Hex.u2(startPc) + " " + lineNumber);
}
offset += 4;
}
list.setImmutable();
return new AttLineNumberTable(list);
}
/**
* Parses a {@code LocalVariableTable} attribute.
*/
private Attribute localVariableTable(DirectClassFile cf, int offset,
int length, ParseObserver observer) {
if (length < 2) {
return throwSeverelyTruncated();
}
ByteArray bytes = cf.getBytes();
int count = bytes.getUnsignedShort(offset);
if (observer != null) {
observer.parsed(bytes, offset, 2,
"local_variable_table_length: " + Hex.u2(count));
}
LocalVariableList list = parseLocalVariables(
bytes.slice(offset + 2, offset + length), cf.getConstantPool(),
observer, count, false);
return new AttLocalVariableTable(list);
}
/**
* Parses a {@code LocalVariableTypeTable} attribute.
*/
private Attribute localVariableTypeTable(DirectClassFile cf, int offset,
int length, ParseObserver observer) {
if (length < 2) {
return throwSeverelyTruncated();
}
ByteArray bytes = cf.getBytes();
int count = bytes.getUnsignedShort(offset);
if (observer != null) {
observer.parsed(bytes, offset, 2,
"local_variable_type_table_length: " + Hex.u2(count));
}
LocalVariableList list = parseLocalVariables(
bytes.slice(offset + 2, offset + length), cf.getConstantPool(),
observer, count, true);
return new AttLocalVariableTypeTable(list);
}
/**
* Parse the table part of either a {@code LocalVariableTable}
* or a {@code LocalVariableTypeTable}.
*
* @param bytes {@code non-null;} bytes to parse, which should only
* contain the table data (no header)
* @param pool {@code non-null;} constant pool to use
* @param count {@code >= 0;} the number of entries
* @param typeTable {@code true} iff this is for a type table
* @return {@code non-null;} the constructed list
*/
private LocalVariableList parseLocalVariables(ByteArray bytes,
ConstantPool pool, ParseObserver observer, int count,
boolean typeTable) {
if (bytes.size() != (count * 10)) {
// "+ 2" is for the count.
throwBadLength((count * 10) + 2);
}
ByteArray.MyDataInputStream in = bytes.makeDataInputStream();
LocalVariableList list = new LocalVariableList(count);
try {
for (int i = 0; i < count; i++) {
int startPc = in.readUnsignedShort();
int length = in.readUnsignedShort();
int nameIdx = in.readUnsignedShort();
int typeIdx = in.readUnsignedShort();
int index = in.readUnsignedShort();
CstString name = (CstString) pool.get(nameIdx);
CstString type = (CstString) pool.get(typeIdx);
CstString descriptor = null;
CstString signature = null;
if (typeTable) {
signature = type;
} else {
descriptor = type;
}
list.set(i, startPc, length, name,
descriptor, signature, index);
if (observer != null) {
observer.parsed(bytes, i * 10, 10, Hex.u2(startPc) +
".." + Hex.u2(startPc + length) + " " +
Hex.u2(index) + " " + name.toHuman() + " " +
type.toHuman());
}
}
} catch (IOException ex) {
throw new RuntimeException("shouldn't happen", ex);
}
list.setImmutable();
return list;
}
/**
* Parses a {@code RuntimeInvisibleAnnotations} attribute.
*/
private Attribute runtimeInvisibleAnnotations(DirectClassFile cf,
int offset, int length, ParseObserver observer) {
if (length < 2) {
throwSeverelyTruncated();
}
AnnotationParser ap =
new AnnotationParser(cf, offset, length, observer);
Annotations annotations =
ap.parseAnnotationAttribute(AnnotationVisibility.BUILD);
return new AttRuntimeInvisibleAnnotations(annotations, length);
}
/**
* Parses a {@code RuntimeVisibleAnnotations} attribute.
*/
private Attribute runtimeVisibleAnnotations(DirectClassFile cf,
int offset, int length, ParseObserver observer) {
if (length < 2) {
throwSeverelyTruncated();
}
AnnotationParser ap =
new AnnotationParser(cf, offset, length, observer);
Annotations annotations =
ap.parseAnnotationAttribute(AnnotationVisibility.RUNTIME);
return new AttRuntimeVisibleAnnotations(annotations, length);
}
/**
* Parses a {@code RuntimeInvisibleParameterAnnotations} attribute.
*/
private Attribute runtimeInvisibleParameterAnnotations(DirectClassFile cf,
int offset, int length, ParseObserver observer) {
if (length < 2) {
throwSeverelyTruncated();
}
AnnotationParser ap =
new AnnotationParser(cf, offset, length, observer);
AnnotationsList list =
ap.parseParameterAttribute(AnnotationVisibility.BUILD);
return new AttRuntimeInvisibleParameterAnnotations(list, length);
}
/**
* Parses a {@code RuntimeVisibleParameterAnnotations} attribute.
*/
private Attribute runtimeVisibleParameterAnnotations(DirectClassFile cf,
int offset, int length, ParseObserver observer) {
if (length < 2) {
throwSeverelyTruncated();
}
AnnotationParser ap =
new AnnotationParser(cf, offset, length, observer);
AnnotationsList list =
ap.parseParameterAttribute(AnnotationVisibility.RUNTIME);
return new AttRuntimeVisibleParameterAnnotations(list, length);
}
/**
* Parses a {@code Signature} attribute.
*/
private Attribute signature(DirectClassFile cf, int offset, int length,
ParseObserver observer) {
if (length != 2) {
throwBadLength(2);
}
ByteArray bytes = cf.getBytes();
ConstantPool pool = cf.getConstantPool();
int idx = bytes.getUnsignedShort(offset);
CstString cst = (CstString) pool.get(idx);
Attribute result = new AttSignature(cst);
if (observer != null) {
observer.parsed(bytes, offset, 2, "signature: " + cst);
}
return result;
}
/**
* Parses a {@code SourceFile} attribute.
*/
private Attribute sourceFile(DirectClassFile cf, int offset, int length,
ParseObserver observer) {
if (length != 2) {
throwBadLength(2);
}
ByteArray bytes = cf.getBytes();
ConstantPool pool = cf.getConstantPool();
int idx = bytes.getUnsignedShort(offset);
CstString cst = (CstString) pool.get(idx);
Attribute result = new AttSourceFile(cst);
if (observer != null) {
observer.parsed(bytes, offset, 2, "source: " + cst);
}
return result;
}
/**
* Parses a {@code Synthetic} attribute.
*/
private Attribute synthetic(DirectClassFile cf, int offset, int length,
ParseObserver observer) {
if (length != 0) {
return throwBadLength(0);
}
return new AttSynthetic();
}
/**
* Throws the right exception when a known attribute has a way too short
* length.
*
* @return never
* @throws ParseException always thrown
*/
private static Attribute throwSeverelyTruncated() {
throw new ParseException("severely truncated attribute");
}
/**
* Throws the right exception when a known attribute has a too short
* length.
*
* @return never
* @throws ParseException always thrown
*/
private static Attribute throwTruncated() {
throw new ParseException("truncated attribute");
}
/**
* Throws the right exception when an attribute has an unexpected length
* (given its contents).
*
* @param expected expected length
* @return never
* @throws ParseException always thrown
*/
private static Attribute throwBadLength(int expected) {
throw new ParseException("bad attribute length; expected length " +
Hex.u4(expected));
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy