blob: 617714503418ba7d9d01b61de7ead27dcb3fc654 [file] [log] [blame]
/*
* 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.dexgen.dex.file;
import com.android.dexgen.rop.annotation.Annotations;
import com.android.dexgen.rop.annotation.AnnotationsList;
import com.android.dexgen.rop.code.AccessFlags;
import com.android.dexgen.rop.cst.Constant;
import com.android.dexgen.rop.cst.CstArray;
import com.android.dexgen.rop.cst.CstFieldRef;
import com.android.dexgen.rop.cst.CstMethodRef;
import com.android.dexgen.rop.cst.CstType;
import com.android.dexgen.rop.cst.CstUtf8;
import com.android.dexgen.rop.type.StdTypeList;
import com.android.dexgen.rop.type.TypeList;
import com.android.dexgen.util.AnnotatedOutput;
import com.android.dexgen.util.Hex;
import com.android.dexgen.util.Writers;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.TreeSet;
/**
* Representation of a Dalvik class, which is basically a set of
* members (fields or methods) along with a few more pieces of
* information.
*/
public final class ClassDefItem extends IndexedItem {
/** size of instances when written out to a file, in bytes */
public static final int WRITE_SIZE = 32;
/** {@code non-null;} type constant for this class */
private final CstType thisClass;
/** access flags */
private final int accessFlags;
/**
* {@code null-ok;} superclass or {@code null} if this class is a/the
* root class
*/
private final CstType superclass;
/** {@code null-ok;} list of implemented interfaces */
private TypeListItem interfaces;
/** {@code null-ok;} source file name or {@code null} if unknown */
private final CstUtf8 sourceFile;
/** {@code non-null;} associated class data object */
private final ClassDataItem classData;
/**
* {@code null-ok;} item wrapper for the static values, initialized
* in {@link #addContents}
*/
private EncodedArrayItem staticValuesItem;
/** {@code non-null;} annotations directory */
private AnnotationsDirectoryItem annotationsDirectory;
/**
* Constructs an instance. Its sets of members and annotations are
* initially empty.
*
* @param thisClass {@code non-null;} type constant for this class
* @param accessFlags access flags
* @param superclass {@code null-ok;} superclass or {@code null} if
* this class is a/the root class
* @param interfaces {@code non-null;} list of implemented interfaces
* @param sourceFile {@code null-ok;} source file name or
* {@code null} if unknown
*/
public ClassDefItem(CstType thisClass, int accessFlags,
CstType superclass, TypeList interfaces, CstUtf8 sourceFile) {
if (thisClass == null) {
throw new NullPointerException("thisClass == null");
}
/*
* TODO: Maybe check accessFlags and superclass, at
* least for easily-checked stuff?
*/
if (interfaces == null) {
throw new NullPointerException("interfaces == null");
}
this.thisClass = thisClass;
this.accessFlags = accessFlags;
this.superclass = superclass;
this.interfaces =
(interfaces.size() == 0) ? null : new TypeListItem(interfaces);
this.sourceFile = sourceFile;
this.classData = new ClassDataItem(thisClass);
this.staticValuesItem = null;
this.annotationsDirectory = new AnnotationsDirectoryItem();
}
/** {@inheritDoc} */
@Override
public ItemType itemType() {
return ItemType.TYPE_CLASS_DEF_ITEM;
}
/** {@inheritDoc} */
@Override
public int writeSize() {
return WRITE_SIZE;
}
/** {@inheritDoc} */
@Override
public void addContents(DexFile file) {
TypeIdsSection typeIds = file.getTypeIds();
MixedItemSection byteData = file.getByteData();
MixedItemSection wordData = file.getWordData();
MixedItemSection typeLists = file.getTypeLists();
StringIdsSection stringIds = file.getStringIds();
typeIds.intern(thisClass);
if (!classData.isEmpty()) {
MixedItemSection classDataSection = file.getClassData();
classDataSection.add(classData);
CstArray staticValues = classData.getStaticValuesConstant();
if (staticValues != null) {
staticValuesItem =
byteData.intern(new EncodedArrayItem(staticValues));
}
}
if (superclass != null) {
typeIds.intern(superclass);
}
if (interfaces != null) {
interfaces = typeLists.intern(interfaces);
}
if (sourceFile != null) {
stringIds.intern(sourceFile);
}
if (! annotationsDirectory.isEmpty()) {
if (annotationsDirectory.isInternable()) {
annotationsDirectory = wordData.intern(annotationsDirectory);
} else {
wordData.add(annotationsDirectory);
}
}
}
/** {@inheritDoc} */
@Override
public void writeTo(DexFile file, AnnotatedOutput out) {
boolean annotates = out.annotates();
TypeIdsSection typeIds = file.getTypeIds();
int classIdx = typeIds.indexOf(thisClass);
int superIdx = (superclass == null) ? -1 :
typeIds.indexOf(superclass);
int interOff = OffsettedItem.getAbsoluteOffsetOr0(interfaces);
int annoOff = annotationsDirectory.isEmpty() ? 0 :
annotationsDirectory.getAbsoluteOffset();
int sourceFileIdx = (sourceFile == null) ? -1 :
file.getStringIds().indexOf(sourceFile);
int dataOff = classData.isEmpty()? 0 : classData.getAbsoluteOffset();
int staticValuesOff =
OffsettedItem.getAbsoluteOffsetOr0(staticValuesItem);
if (annotates) {
out.annotate(0, indexString() + ' ' + thisClass.toHuman());
out.annotate(4, " class_idx: " + Hex.u4(classIdx));
out.annotate(4, " access_flags: " +
AccessFlags.classString(accessFlags));
out.annotate(4, " superclass_idx: " + Hex.u4(superIdx) +
" // " + ((superclass == null) ? "<none>" :
superclass.toHuman()));
out.annotate(4, " interfaces_off: " + Hex.u4(interOff));
if (interOff != 0) {
TypeList list = interfaces.getList();
int sz = list.size();
for (int i = 0; i < sz; i++) {
out.annotate(0, " " + list.getType(i).toHuman());
}
}
out.annotate(4, " source_file_idx: " + Hex.u4(sourceFileIdx) +
" // " + ((sourceFile == null) ? "<none>" :
sourceFile.toHuman()));
out.annotate(4, " annotations_off: " + Hex.u4(annoOff));
out.annotate(4, " class_data_off: " + Hex.u4(dataOff));
out.annotate(4, " static_values_off: " +
Hex.u4(staticValuesOff));
}
out.writeInt(classIdx);
out.writeInt(accessFlags);
out.writeInt(superIdx);
out.writeInt(interOff);
out.writeInt(sourceFileIdx);
out.writeInt(annoOff);
out.writeInt(dataOff);
out.writeInt(staticValuesOff);
}
/**
* Gets the constant corresponding to this class.
*
* @return {@code non-null;} the constant
*/
public CstType getThisClass() {
return thisClass;
}
/**
* Gets the access flags.
*
* @return the access flags
*/
public int getAccessFlags() {
return accessFlags;
}
/**
* Gets the superclass.
*
* @return {@code null-ok;} the superclass or {@code null} if
* this class is a/the root class
*/
public CstType getSuperclass() {
return superclass;
}
/**
* Gets the list of interfaces implemented.
*
* @return {@code non-null;} the interfaces list
*/
public TypeList getInterfaces() {
if (interfaces == null) {
return StdTypeList.EMPTY;
}
return interfaces.getList();
}
/**
* Gets the source file name.
*
* @return {@code null-ok;} the source file name or {@code null} if unknown
*/
public CstUtf8 getSourceFile() {
return sourceFile;
}
/**
* Adds a static field.
*
* @param field {@code non-null;} the field to add
* @param value {@code null-ok;} initial value for the field, if any
*/
public void addStaticField(EncodedField field, Constant value) {
classData.addStaticField(field, value);
}
/**
* Adds an instance field.
*
* @param field {@code non-null;} the field to add
*/
public void addInstanceField(EncodedField field) {
classData.addInstanceField(field);
}
/**
* Adds a direct ({@code static} and/or {@code private}) method.
*
* @param method {@code non-null;} the method to add
*/
public void addDirectMethod(EncodedMethod method) {
classData.addDirectMethod(method);
}
/**
* Adds a virtual method.
*
* @param method {@code non-null;} the method to add
*/
public void addVirtualMethod(EncodedMethod method) {
classData.addVirtualMethod(method);
}
/**
* Gets all the methods in this class. The returned list is not linked
* in any way to the underlying lists contained in this instance, but
* the objects contained in the list are shared.
*
* @return {@code non-null;} list of all methods
*/
public ArrayList<EncodedMethod> getMethods() {
return classData.getMethods();
}
/**
* Sets the direct annotations on this class. These are annotations
* made on the class, per se, as opposed to on one of its members.
* It is only valid to call this method at most once per instance.
*
* @param annotations {@code non-null;} annotations to set for this class
*/
public void setClassAnnotations(Annotations annotations) {
annotationsDirectory.setClassAnnotations(annotations);
}
/**
* Adds a field annotations item to this class.
*
* @param field {@code non-null;} field in question
* @param annotations {@code non-null;} associated annotations to add
*/
public void addFieldAnnotations(CstFieldRef field,
Annotations annotations) {
annotationsDirectory.addFieldAnnotations(field, annotations);
}
/**
* Adds a method annotations item to this class.
*
* @param method {@code non-null;} method in question
* @param annotations {@code non-null;} associated annotations to add
*/
public void addMethodAnnotations(CstMethodRef method,
Annotations annotations) {
annotationsDirectory.addMethodAnnotations(method, annotations);
}
/**
* Adds a parameter annotations item to this class.
*
* @param method {@code non-null;} method in question
* @param list {@code non-null;} associated list of annotation sets to add
*/
public void addParameterAnnotations(CstMethodRef method,
AnnotationsList list) {
annotationsDirectory.addParameterAnnotations(method, list);
}
/**
* Gets the method annotations for a given method, if any. This is
* meant for use by debugging / dumping code.
*
* @param method {@code non-null;} the method
* @return {@code null-ok;} the method annotations, if any
*/
public Annotations getMethodAnnotations(CstMethodRef method) {
return annotationsDirectory.getMethodAnnotations(method);
}
/**
* Gets the parameter annotations for a given method, if any. This is
* meant for use by debugging / dumping code.
*
* @param method {@code non-null;} the method
* @return {@code null-ok;} the parameter annotations, if any
*/
public AnnotationsList getParameterAnnotations(CstMethodRef method) {
return annotationsDirectory.getParameterAnnotations(method);
}
/**
* Prints out the contents of this instance, in a debugging-friendly
* way.
*
* @param out {@code non-null;} where to output to
* @param verbose whether to be verbose with the output
*/
public void debugPrint(Writer out, boolean verbose) {
PrintWriter pw = Writers.printWriterFor(out);
pw.println(getClass().getName() + " {");
pw.println(" accessFlags: " + Hex.u2(accessFlags));
pw.println(" superclass: " + superclass);
pw.println(" interfaces: " +
((interfaces == null) ? "<none>" : interfaces));
pw.println(" sourceFile: " +
((sourceFile == null) ? "<none>" : sourceFile.toQuoted()));
classData.debugPrint(out, verbose);
annotationsDirectory.debugPrint(pw);
pw.println("}");
}
}