/* Software Name : AsmDex
* Version : 1.0
*
* Copyright © 2012 France Télécom
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.ow2.asmdex.structureWriter;
import org.ow2.asmdex.Opcodes;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
/**
* Constant Pool of the Application. It has a link to every element the Application holds.
*
* Dalvik requires some structures to be sorted. It is faster to sort them once the Application is parsed
* than adding elements one after one, so we manage first simple collections, and then the
* prepareGeneration method builds the sorted ones.
*
* Also, we use Symbolic elements from Strings, Fields, Types and Methods so that we can generate the bytecode
* right after each method is parsed. This is useful to be able to destroy the elements related the Methods
* when their parsing is over. The indexes are then remapped correctly after the Application is fully parsed
* (see {@link CodeItem#mapResolvedIndexes()}).
*
* NOTES :
* - Empty Strings can be added to the Constant Pool, but only if useful. Empty parameters aren't encoded
* in the Constant Pool (yet they are declared in the Debug Item). This is tested by the Debug Item
* encoder though, not the Constant Pool itself.
*
* - a "this" String is added by dx, I don't know exactly where it comes from. I add it when a
* non-static/non-constructor Method is found, it seems logical.
*
* @author Julien Névo
*/
public class ConstantPool {
/**
* Indicates whether the searches must be performed on Symbolic elements (where available) or not.
* It matters only for Strings, Fields, Types and Methods.
*/
private boolean useSymbolicElements = true;
/* Here follows the symbolic maps. They are useful to instructions, because the opcode of methods
* is generated at the end of each of them, and the list of instructions are erased in order to
* reduce memory consumption. But indexes to Strings, Fields, Types and Methods, which are used by
* instructions, are only definitely known when the whole application has been parsed, as they must
* be ordered within the Dex file.
* The solution consists in using symbolic Strings/Fields/Types/Methods which refers an index
* that will later be mapped into the "real" index, once the full application is parsed.
*/
/**
* Map linking a symbolic String to its index.
*/
private HashMap<String, Integer> symbolicStringsToIndexes = new HashMap<String, Integer>();
/**
* Map linking a symbolic Types to its index.
*/
private HashMap<String, Integer> symbolicTypesToIndexes = new HashMap<String, Integer>();
/**
* Map linking a symbolic Methods to its index.
*/
private HashMap<Method, Integer> symbolicMethodsToIndexes = new HashMap<Method, Integer>();
/**
* Map linking a symbolic Fields to its index.
*/
private HashMap<Field, Integer> symbolicFieldsToIndexes = new HashMap<Field, Integer>();
/**
* Array linking a symbolic String index to a resolved String index.
*/
private int[] symbolicStringsIndexToResolvedStringsIndex;
/**
* Array linking a symbolic Field index to a resolved Field index.
*/
private int[] symbolicFieldsIndexToResolvedFieldsIndex;
/**
* Array linking a symbolic Field index to a resolved Field index.
*/
private int[] symbolicMethodsIndexToResolvedMethodsIndex;
/**
* Array linking a symbolic Field index to a resolved Field index.
*/
private int[] symbolicTypesIndexToResolvedTypesIndex;
/*
* The following maps links the structures to an index. It is only filled once the application has been
* fully parsed.
*/
private HashMap<String, Integer> resolvedStringsToIndexes = new HashMap<String, Integer>();
private HashMap<String, Integer> resolvedTypesToIndexes = new HashMap<String, Integer>();
private HashMap<Method, Integer> resolvedMethodsToIndexes = new HashMap<Method, Integer>();
private HashMap<Field, Integer> resolvedFieldsToIndexes = new HashMap<Field, Integer>();
private HashMap<Prototype, Integer> prototypesToIndexes = new HashMap<Prototype, Integer>();
//private HashMap<ClassDefinitionItem, Integer> classesToIndexes = new HashMap<ClassDefinitionItem, Integer>();
/**
* Map linking a TypeList structure to its offset from the beginning of the file.
*/
private HashMap<TypeList, Integer> typeListsToOffsets = new HashMap<TypeList, Integer>();
/**
* Ordered Set in which the newly discovered Strings are added. This is useful to order the
* Strings when assigning them indexes, as requested by the Dex format.
*/
private TreeSet<String> strings = new TreeSet<String>();
/**
* Unordered Set in which the newly discovered prototypes are added. The Prototypes will be sorted
* only when the Application has been fully parsed. The ordered collection is just below.
*/
private HashMap<Prototype,Prototype> prototypes = new HashMap<Prototype,Prototype>();
/**
* List of Ordered Prototypes. It is only completed when everything has been parsed.
*/
private TreeSet<Prototype> orderedPrototypes;
/**
* Ordered Set in which the newly discovered Types are added. A Type added here must also
* be added in the strings structure above.
*/
private TreeSet<String> types = new TreeSet<String>();
/**
* Ordered Set in which the newly discovered Fields are added. The Fields are sorted by the
* class owning name, their own name, and their Type.
*/
private HashMap<Field,Field> fields = new HashMap<Field,Field>();
/**
* Unordered Set containing Methods. They will be sorted when all the Application is parsed.
* This is done to speed up the addition of new Methods. The ordered collection is just below.
*/
private HashMap<Method,Method> methods = new HashMap<Method,Method>();
/**
* List of Ordered Fields. It is only completed when everything has been parsed.
*/
private TreeSet<Field> orderedFields;
/**
* List of Ordered Methods. It is only completed when everything has been parsed.
*/
private TreeSet<Method> orderedMethods;
/**
* List of Ordered Classes. It is only completed when everything has been parsed.
*/
private ArrayList<ClassDefinitionItem> orderedClasses;
/**
* Unordered Set containing TypeList, from to the type_list structure.
* The ordered list is below, constructed after all has been parsed.
*/
private HashSet<TypeList> typeList = new HashSet<TypeList>();
/**
* Set of Ordered TypeLists. It is only completed when everything has been parsed.
*/
private TreeSet<TypeList> orderedTypeList;
/**
* Map linking a String from the Type_ids list (as encoded in the future Dex file) to the
* String index in the string_ids. This map is created only when the header is being built.
*/
//private HashMap<String, Integer> stringsToTypeIdIndexes = new HashMap<String, Integer>();
/**
* Map linking a ClassDefinitionItem to its static_value_encoded_array_item offset. Only Classes
* with an actual array are encoded. If a Class doesn't found its array, it means it has none.
* This map is created when the value_encoded_array_items are built.
*/
private HashMap<ClassDefinitionItem, Integer> staticValuesEncodedArrayItemOffsets = new HashMap<ClassDefinitionItem, Integer>();
/**
* Set of annotation_items. They are ordered, and unique.
*/
//private HashSet<AnnotationItem> annotationItems = new HashSet<AnnotationItem>();
private TreeSet<AnnotationItem> annotationItems = new TreeSet<AnnotationItem>();
/**
* Map of annotation_items linking an annotation_item to an Offset.
*/
private HashMap<AnnotationItem, Integer> annotationItemsToOffsets = new HashMap<AnnotationItem, Integer>();
/**
* Set of annotation_set_items. They are ordered, and unique.
*/
//private HashSet<AnnotationSetItem> annotationSetItems = new HashSet<AnnotationSetItem>();
private TreeSet<AnnotationSetItem> annotationSetItems = new TreeSet<AnnotationSetItem>();
/**
* Map of annotation_set_items linking an annotation_set_item to an Offset.
*/
private HashMap<AnnotationSetItem, Integer> annotationSetItemsToOffsets = new HashMap<AnnotationSetItem, Integer>();
/**
* Set of annotation_set_ref_list. They are not ordered, but we need them to be unique.
*/
private HashSet<AnnotationSetRefList> annotationSetRefLists = new HashSet<AnnotationSetRefList>();
/**
* Map of annotation_set_ref_lists linking an annotation_set_ref_list to an Offset.
*/
private HashMap<AnnotationSetRefList, Integer> annotationSetRefListsToOffsets = new HashMap<AnnotationSetRefList, Integer>();
/**
* Map linking a Class to the offset of its annotation_directory_item, if any.
*/
//private HashMap<ClassDefinitionItem, Integer> classesToAnnotationDirectoryItemOffsets = new HashMap<ClassDefinitionItem, Integer>();
private HashMap<AnnotationDirectoryItem, Integer> classesToAnnotationDirectoryItemOffsets = new HashMap<AnnotationDirectoryItem, Integer>();
/**
* Map linking a ClassName (fully qualified) to its Class Definition Item. It is useful to sort the
* Class at the end of the parsing.
*/
// We use a sorted map instead of a regular map so that names of classes are roughly ordered although some
// items may be introduced to respect the type hierarchy (required constraint imposed by sortClasses().
// This rough ordering is the one used by dx. [PC - 7/1/13]
private Map<String, ClassDefinitionItem> classNameToClassDefinitionItem = new TreeMap<String, ClassDefinitionItem>();
/**
* HashSet of annotation_directory_items. They are unique, but we don't care about their order.
*/
private HashSet<AnnotationDirectoryItem> annotationDirectoryItems = new HashSet<AnnotationDirectoryItem>();
// ---------------------------------------
// Getters and Setters.
// ---------------------------------------
/**
* Returns the number of Strings currently parsed.
* @return the number of Strings currently parsed.
*/
public int getStringCount() {
return strings.size();
}
/**
* Returns the number of Types currently parsed.
* @return the number of Types currently parsed.
*/
public int getTypeCount() {
return types.size();
}
/**
* Returns the number of Prototypes currently parsed.
* @return the number of Prototypes currently parsed.
*/
public int getPrototypeCount() {
return prototypes.size();
}
/**
* Returns the number of Fields currently parsed.
* @return the number of Fields currently parsed.
*/
public int getFieldCount() {
return fields.size();
}
/**
* Returns the number of Methods currently parsed.
* @return the number of Methods currently parsed.
*/
public int getMethodCount() {
return methods.size();
}
/**
* Returns the number of Classes currently parsed.
* @return the number of Classes currently parsed.
*/
public int getClassDefinitionCount() {
return classNameToClassDefinitionItem.size();
}
/**
* Returns the number of TypeList currently parsed.
* @return the number of TypeList currently parsed.
*/
public int getTypeListCount() {
return typeList.size();
}
/**
* Returns the ordered Strings of the Application. It is only set when the Application has been fully parsed.
* @return the ordered Strings of the Application.
*/
public TreeSet<String> getStrings() {
return strings;
}
/**
* Returns the ordered Types of the Application. It is only set when the Application has been fully parsed.
* @return the ordered Types of the Application.
*/
public TreeSet<String> getTypes() {
return types;
}
/**
* Returns the ordered Fields of the Application. It is only set when the Application has been fully parsed.
* @return the ordered Fields of the Application.
*/
public TreeSet<Field> getFields() {
return orderedFields;
}
/**
* Returns the ordered Prototypes of the Application. It is only set when the Application has been fully parsed.
* @return the ordered Prototypes of the Application.
*/
public TreeSet<Prototype> getPrototypes() {
return orderedPrototypes;
}
/**
* Returns the ordered Methods of the Application. It is only set when the Application has been fully parsed.
* @return the ordered Methods of the Application.
*/
public TreeSet<Method> getMethods() {
return orderedMethods;
}
/**
* Returns the ordered TypeLists of the Application. It is only set when the Application has been fully parsed.
* @return the ordered TypeLists of the Application.
*/
public TreeSet<TypeList> getTypeList() {
return orderedTypeList;
}
/**
* Returns the index of a String. The Symbolic index is used as long as the application hasn't been
* fully parsed.
* @param string the String we want the index of.
* @return the index of the String.
*/
public int getStringIndex(String string) {
if (useSymbolicElements) {
return symbolicStringsToIndexes.get(string);
} else {
return resolvedStringsToIndexes.get(string);
}
}
/**
* Returns the Offset of a TypeList. <i>The Dex file must have been fully parsed before.</i>
* @param typeList the TypeList to find.
* @return the offset of the TypeList, from the beginning of the file.
*/
public int getTypeListOffset(TypeList typeList) {
return typeListsToOffsets.get(typeList);
}
/**
* Sets an Offset to a TypeList.
* @param typeList the TypeList to give the Offset.
* @param offset the Offset to set.
*/
public void setTypeListOffset(TypeList typeList, int offset) {
typeListsToOffsets.put(typeList, offset);
}
/**
* Returns the Index of a Type. The Symbolic index is used as long as the application hasn't been
* fully parsed.
* @param type name of the Type.
* @return the index of the Type.
*/
public int getTypeIndex(String type) {
if (useSymbolicElements) {
return symbolicTypesToIndexes.get(type);
} else {
return resolvedTypesToIndexes.get(type);
}
}
/**
* Returns the Index of a Prototype. <i>The Dex file must have been fully parsed before.</i>
* @param prototype the Prototype.
* @return the index of the Prototype.
*/
public int getPrototypeIndex(Prototype prototype) {
return prototypesToIndexes.get(prototype);
}
/**
* Returns the Index of a Field. The Symbolic index is used as long as the application hasn't been
* fully parsed.
* @param field the Field.
* @return the index of the Field.
*/
public int getFieldIndex(Field field) {
if (useSymbolicElements) {
return symbolicFieldsToIndexes.get(field);
} else {
return resolvedFieldsToIndexes.get(field);
}
}
/**
* Returns the Index of a Method. The Symbolic index is used as long as the application hasn't been
* fully parsed.
* @param method the Method.
* @return the index of the Method.
*/
public int getMethodIndex(Method method) {
if (useSymbolicElements) {
return symbolicMethodsToIndexes.get(method);
} else {
return resolvedMethodsToIndexes.get(method);
}
}
/**
* Returns the offset of an annotation_item.
* @param annotationItem the offset of the annotation_item.
* @return the offset of the annotation_item.
*/
public int getAnnotationItemOffset(AnnotationItem annotationItem) {
return annotationItemsToOffsets.get(annotationItem);
}
/**
* Sets the offset of an annotation_item.
* @param annotationItem the annotation_item.
* @param offset the Offset of the annotation_item.
*/
public void setAnnotationItemOffset(AnnotationItem annotationItem, int offset) {
annotationItemsToOffsets.put(annotationItem, offset);
}
/**
* Returns the offset of an annotation_set_item.
* @param annotationSetItem the annotation_set_item.
* @return the offset of the annotation_set_item.
*/
public int getAnnotationSetItemOffset(AnnotationSetItem annotationSetItem) {
return annotationSetItemsToOffsets.get(annotationSetItem);
}
/**
* Sets the offset of an annotation_set_item.
* @param annotationSetItem the annotation_set_item.
* @param offset the offset of the annotation_set_item.
*/
public void setAnnotationSetItemOffset(AnnotationSetItem annotationSetItem, int offset) {
annotationSetItemsToOffsets.put(annotationSetItem, offset);
}
/**
* Returns the offset of an annotation_set_ref_list.
* @param annotationSetRefList the annotation_set_ref_list.
* @return offset the offset of the annotation_set_ref_list.
*/
public int getAnnotationSetRefListOffset(AnnotationSetRefList annotationSetRefList) {
return annotationSetRefListsToOffsets.get(annotationSetRefList);
}
/**
* Sets the offset of an annotation_set_ref_list.
* @param annotationSetRefList the annotation_set_ref_list.
* @param offset the offset of the annotation_set_ref_list.
*/
public void setAnnotationSetRefListOffset(AnnotationSetRefList annotationSetRefList, int offset) {
annotationSetRefListsToOffsets.put(annotationSetRefList, offset);
}
/**
* Returns the offset of an annotation_directory_item, or 0 if the Class doesn't have an
* annotation_directory_item.
* @param annotationDirectoryItem the annotation_directory_item.
* @return the offset of the annotation_directory_item, or 0.
*/
public int getAnnotationDirectoryItemOffset(AnnotationDirectoryItem annotationDirectoryItem) {
if (classesToAnnotationDirectoryItemOffsets.containsKey(annotationDirectoryItem)) {
return classesToAnnotationDirectoryItemOffsets.get(annotationDirectoryItem);
} else {
return 0;
}
}
/**
* Sets the offset of an annotation_directory_item.
* @param annotationDirectoryItem the annotation_directory_item belongs to.
* @param offset the offset of the annotation_directory_item.
*/
public void setAnnotationDirectoryItemOffset(AnnotationDirectoryItem annotationDirectoryItem, int offset) {
classesToAnnotationDirectoryItemOffsets.put(annotationDirectoryItem, offset);
}
/**
* Returns the Set of annotation_items.
* @return the Set of annotation_items.
*/
public TreeSet<AnnotationItem> getAnnotationItems() {
return annotationItems;
}
/**
* Returns the set of annotation_set_item.
* @return the set of annotation_set_item.
*/
public TreeSet<AnnotationSetItem> getAnnotationSetItems() {
return annotationSetItems;
}
/**
* Returns the set of annotation_set_ref_list.
* @return the set of annotation_set_ref_list.
*/
public HashSet<AnnotationSetRefList> getAnnotationSetRefLists() {
return annotationSetRefLists;
}
/**
* Returns the count of annotation_items.
* @return the count of annotation_items.
*/
public int getAnnotationItemCount() {
return annotationItems.size();
}
/**
* Returns the count of annotation_set_items.
* @return the count of annotation_set_items.
*/
public int getAnnotationSetItemCount() {
return annotationSetItems.size();
}
/**
* Returns the count of annotation_set_ref_lists.
* @return the count of annotation_set_ref_lists.
*/
public int getAnnotationSetRefListsCount() {
return annotationSetRefLists.size();
}
/**
* Returns the count of the annotation_directory_item.
* @return the count of the annotation_directory_item.
*/
public int getAnnotationDirectoryItemCount() {
return classesToAnnotationDirectoryItemOffsets.size();
}
/**
* Returns the offset of the encoded_array_item of a static_values_offset for one Class if it exists.
* @param cdi the Class.
* @return the offset of the encoded_array_item of a static_values_offset for one Class, or 0 if no
* offset has been found.
*/
public int getOffsetOfStaticValuesEncodedArrayItemOfClass(ClassDefinitionItem cdi) {
int result = 0;
if (staticValuesEncodedArrayItemOffsets.containsKey(cdi)) {
result = staticValuesEncodedArrayItemOffsets.get(cdi);
}
return result;
}
/**
* Returns an ordered list of the classes of the Application. It is only completed when the
* Application has been fully parsed.
* @return an ordered list of the classes of the Application.
*/
public ArrayList<ClassDefinitionItem> getClasses() {
return orderedClasses;
}
/**
* Returns the annotation_directory_items, unique but non-ordered.
* @return the annotation_directory_items.
*/
public HashSet<AnnotationDirectoryItem> getAnnotationDirectoryItems() {
return annotationDirectoryItems;
}
/**
* Returns the resolved method index from the symbolic index. The application must have been
* fully parsed for the conversion to be possible.
* @param methodIndex the symbolic method index.
* @return the resolved method index.
*/
public int getResolvedMethodIndexFromSymbolicMethodIndex(int methodIndex) {
return symbolicMethodsIndexToResolvedMethodsIndex[methodIndex];
}
/**
* Returns the resolved Field index from the symbolic index. The application must have been
* fully parsed for the conversion to be possible.
* @param fieldIndex the symbolic Field index.
* @return the resolved Field index.
*/
public int getResolvedFieldIndexFromSymbolicFieldIndex(int fieldIndex) {
return symbolicFieldsIndexToResolvedFieldsIndex[fieldIndex];
}
/**
* Returns the resolved Type index from the symbolic index. The application must have been
* fully parsed for the conversion to be possible.
* @param typeIndex the symbolic Type index.
* @return the resolved Type index.
*/
public int getResolvedTypeIndexFromSymbolicTypeIndex(int typeIndex) {
return symbolicTypesIndexToResolvedTypesIndex[typeIndex];
}
/**
* Returns the resolved String index from the symbolic index. The application must have been
* fully parsed for the conversion to be possible.
* @param stringIndex the symbolic String index.
* @return the resolved String index.
*/
public int getResolvedStringIndexFromSymbolicStringIndex(int stringIndex) {
return symbolicStringsIndexToResolvedStringsIndex[stringIndex];
}
// ---------------------------------------
// Public methods.
// ---------------------------------------
/**
* Adds a String to the Constant Pool. Nothing happens if the String already exists or is null.
* Empty strings are authorized.
* @param string the String to add.
*/
public void addStringToConstantPool(String string) {
if (string != null) {
if (useSymbolicElements) {
if (!symbolicStringsToIndexes.containsKey(string)) {
symbolicStringsToIndexes.put(string, symbolicStringsToIndexes.size());
}
}
strings.add(string);
}
}
/**
* Adds Strings to the Constant pool. Nothing happens if the String already exists or is null.
* Empty strings are authorized.
* @param strings the Strings to add.
*/
public void addStringsToConstantPool(String[] strings) {
if (strings != null) {
for (String string : strings) {
addStringToConstantPool(string);
}
}
}
/**
* Adds a Type to the Constant Pool. Nothing happens if the Type already exists, is null
* or empty. The Type is added to both the Types and Strings sets.
* @param type the Type to add.
*/
public void addTypeToConstantPool(String type) {
if ((type != null) && (!"".equals(type))) {
addStringToConstantPool(type);
if (useSymbolicElements) {
if (!symbolicTypesToIndexes.containsKey(type)) {
symbolicTypesToIndexes.put(type, symbolicTypesToIndexes.size());
}
}
types.add(type);
}
}
/**
* Adds Types to the Constant Pool. Nothing happens if the Types already exists, are null
* or empty. The Type is added to both the Types and Strings sets.
* @param types the Types to add.
*/
public void addTypesToConstantPool(String[] types) {
if (types != null) {
for (String type : types) {
addTypeToConstantPool(type);
}
}
}
/**
* Adds a Prototype to the Constant Pool. Adds the Strings inside, but also the Prototype to the
* Prototype Pool.
* @param prototype Prototype to add.
*/
public void addPrototypeToConstantPool(Prototype prototype) {
Prototype existing = prototypes.get(prototype);
if (existing == null) {
prototype.initialize();
addStringToConstantPool(prototype.getShortyDescriptor());
addTypeToConstantPool(prototype.getReturnType());
addTypeListToConstantPool(prototype.getParameterTypes());
prototypes.put(prototype,prototype);
}
}
/**
* Creates a Prototype from the descriptor and adds it to the Constant Pool, as well as the
* Strings inside. Returns the Prototype created.
* @param descriptor full descriptor in the TypeDescriptor format.
* @return the Prototype created.
*/
public Prototype addPrototypeToConstantPool(String descriptor) {
Prototype prototype = new Prototype(descriptor);
Prototype existing = prototypes.get(prototype);
if (existing == null) {
addPrototypeToConstantPool(prototype);
} else {
prototype = existing;
}
return prototype;
}
/**
* Adds a TypeList to the Constant Pool, as well as the Strings inside. If the TypeList is empty, it is
* not added.
* @param typeList the TypeList to add to the Constant Pool.
*/
public void addTypeListToConstantPool(TypeList typeList) {
if (typeList.size() > 0) {
for (String type : typeList.getTypeList()) {
addTypeToConstantPool(type);
}
this.typeList.add(typeList);
}
}
/**
* Adds a Method to the Constant Pool being built. Does nothing if the Constant Pool already
* contains a similar item <i>unless the stored Method was tagged Unknown</i>, in which case it means
* that it was just referred by an instruction, but not actually visited, and so lacks information.
* This Method can be called when declaring a Method without knowing what is inside, for example
* when a reference to this Method is found. In that case, the information are added.
* The method name, descriptor, and class name are added to the constant pool if necessary.
* In return is received the Method related to the given parameters.
*
* @param methodName the method name.
* @param className the class name of the method.
* @param methodDescriptor the method descriptor.
* @param access the access flags of the method.
* @param signature the signature of the method. May be Null.
* @param exceptionNames the exceptions of the method. May be null.
* @return the Method related to the given parameters.
*/
public Method addMethodToConstantPool(String methodName, String className,
String methodDescriptor, int access, String[] signature, String[] exceptionNames) {
// int prototypeHashCode = Prototype.calculateHashCode(methodDescriptor);
// int methodHashCode = Method.calculateHashCode(methodName, className, prototypeHashCode);
Prototype prototype = addPrototypeToConstantPool(methodDescriptor);
Method method = new Method(methodName, className, prototype);
Method foundMethod = methods.get(method);
if (foundMethod == null) {
method.init(access, signature, exceptionNames, this);
methods.put(method,method);
symbolicMethodsToIndexes.put(method, symbolicMethodsToIndexes.size());
// Add the name, descriptor, className and exceptions to the Constant Pool.
addStringToConstantPool(methodName);
addTypeToConstantPool(className);
addStringsToConstantPool(signature);
addTypesToConstantPool(exceptionNames);
return method;
} else {
// The Method to add has already been added before, probably when it has been declared.
// Maybe the current instance is the declaration. One of the two has incomplete information.
// We must give them these information without replacing the object, because Instructions are
// referring the incomplete Method.
// If the current method is unknown, it is useless to get information from it.
if (((access & Opcodes.ACC_UNKNOWN) == 0) && foundMethod.isUnknown()) {
foundMethod.completeInformation(access, exceptionNames, signature, this);
}
return foundMethod; // In the end, we return the previously added Method.
}
}
/**
* Adds a Field in the Constant Pool. Does nothing if the constant pool already
* contains a similar item. The field name, type, and class owning name are added to the constant
* pool. In return is received the Field related to the given parameters.
* This method can be called when declaring a Field without knowing what is inside, for example
* when a reference to this Field is found. In that case, the Field access should be ACC_UNKNOWN.
* @param fieldName name of the Field
* @param desc type of the field in TypeDescriptor format.
* @param classOwningName name of the class owning the Field.
* @param access the access flags of the Field.
* @param signature the Signature of the field. May be Null.
* @param value the value of the Field. Null for non-final static Fields.
* @return the Field related to the given parameters.
*/
public Field addFieldToConstantPool(String fieldName, String desc, String classOwningName,
int access, String[] signature, Object value) {
// int fieldHashCode = Field.calculateHashCode(fieldName, desc, classOwningName);
Field field = new Field(fieldName, desc, classOwningName);
Field foundField = fields.get(field);
if (foundField == null) {
field.init(access, signature, value, this);
fields.put(field,field);
symbolicFieldsToIndexes.put(field, symbolicFieldsToIndexes.size());
// Adds the name, type and class owning name to the constant pool.
addStringToConstantPool(fieldName);
addTypeToConstantPool(desc);
addTypeToConstantPool(classOwningName);
addStringsToConstantPool(signature);
return field;
} else {
// The Field to add has already been added before, probably when it has been declared.
// Or maybe the current instance is the declaration. One of the two has incomplete information.
// We must give them these information without replacing the object, because Instructions are
// referring the incomplete Field.
// If the current Field is unknown, it is useless to get information from it.
if (((access & Opcodes.ACC_UNKNOWN) == 0) && foundField.isUnknown()) {
foundField.completeInformation(access, signature, value, this);
}
return foundField; // In the end, we use the already added Field.
}
}
/**
* Adds a class to the constant pool. Nothing happens if the Class already exists.
* The class name, superClass name, signature and interfaces name are added to the constant pool.
* In return is received the ClassDefinitionItem related to the class.
* @param className the name of the class.
* @param superName the name of the super class.
* @param access the access flags of the class.
* @param interfaceNames the name of the interfaces of the class.
* @param signature the signature of the class. Used only for generics. May be null.
* @return the ClassDefinitionItem related to the class.
*/
public ClassDefinitionItem addClassToConstantPool(String className, String superName,
int access, String[] interfaceNames, String[] signature) {
// We build a Class and add it to the Class constant pool.
if (!classNameToClassDefinitionItem.containsKey(className)) {
TypeList interfaceTypeList = new TypeList(interfaceNames);
ClassDefinitionItem cdi = new ClassDefinitionItem(className, superName,
access, interfaceTypeList, signature, this);
classNameToClassDefinitionItem.put(className, cdi);
// Adds the name, superName, signature and interfaces to the constant pool.
addTypeToConstantPool(className);
addTypeToConstantPool(superName);
addStringsToConstantPool(signature);
addTypeListToConstantPool(interfaceTypeList);
return cdi;
} else {
return classNameToClassDefinitionItem.get(className);
}
}
/**
* Adds an annotation_item to the Constant Pool. It must have its full content known.
* @param annotationItem the annotation_item to add.
*/
public void addAnnotationItemToConstantPool(AnnotationItem annotationItem) {
annotationItems.add(annotationItem);
}
/**
* Adds an annotation_set_item to the Constant Pool. It must have its full content known.
* Doesn't add the annotation_set_item if it doesn't contain any annotation_item.
* @param annotationSetItem the annotation_set_item to add.
*/
public void addAnnotationSetItemToConstantPool(AnnotationSetItem annotationSetItem) {
if (annotationSetItem.getNbAnnotationItems() > 0) {
annotationSetItems.add(annotationSetItem);
}
}
/**
* Adds an annotation_set_item to the Constant Pool. It must have its full content known.
* Even annotation_set_item with a size of 0 are added. This is especially useful for the
* Annotated Parameter, as annotation_set_ref_list will probably contain empty annotation_set_items.
* @param annotationSetItem the annotation_set_item to add.
*/
public void addAnnotationSetItemNoSizeCheck(AnnotationSetItem annotationSetItem) {
annotationSetItems.add(annotationSetItem);
}
/**
* Adds an annotation_set_ref_list to the Constant Pool. It also registers all the annotation_set_items
* it contains to the Constant Pool. It must have its full content known.
* @param annotationSetRefList the annotation_set_ref_list to add.
*/
public void addAnnotationSetRefListToConstantPool(AnnotationSetRefList annotationSetRefList) {
if (annotationSetRefList.getNbAnnotationSetItemsUsed() > 0) {
annotationSetRefLists.add(annotationSetRefList);
// Registers all the annotation_set_items it contains.
for (AnnotationSetItem annotationSetItem : annotationSetRefList.getAnnotationSetItems()) {
addAnnotationSetItemNoSizeCheck(annotationSetItem);
}
}
}
/**
* Adds an offset for the encoded_array_item of a static_values_offset for one Class.
* @param cdi the class.
* @param offset the offset to add.
*/
public void addOffsetForStaticValuesEncodedArrayItemOfClass(ClassDefinitionItem cdi, int offset) {
staticValuesEncodedArrayItemOffsets.put(cdi, offset);
}
/**
* Adds an annotation_directory_item to the Constant Pool.
* @param annotationDirectoryItem the annotation_directory_item to add.
*/
public void addAnnotationDirectoryItem(AnnotationDirectoryItem annotationDirectoryItem) {
annotationDirectoryItems.add(annotationDirectoryItem);
}
/**
* Prepares the various elements to be generated. This must be done only once, after the whole
* Application has been parsed. It consists in generating the sorted collections, and building the
* Index Maps.
*/
public void prepareGeneration() {
generateSortedCollections();
buildIndexMaps();
}
/**
* Generates the sorted collections from the unsorted ones (it is faster to sort them once the
* Application is parsed than adding elements one after one).
*/
private void generateSortedCollections() {
orderedTypeList = new TreeSet<TypeList>(typeList);
orderedFields = new TreeSet<Field>(fields.values());
orderedPrototypes = new TreeSet<Prototype>(prototypes.values());
orderedMethods = new TreeSet<Method>(methods.values());
sortClasses();
}
/**
* Builds the Index Maps, allowing to link Strings, Types, Fields... to an Index, sorted in the order
* requested by the Dex format.
* Also creates the link between the symbolic elements and the resolved elements.
* The application must have been fully parsed, and the sorted collections must have been generated
* (generateSortedCollections method).
*/
private void buildIndexMaps() {
useSymbolicElements = false;
int symbolicIndex;
// Builds the Strings index map, and links the symbolic Strings indexes to the resolved Strings indexes.
resolvedStringsToIndexes.clear();
symbolicStringsIndexToResolvedStringsIndex = new int[strings.size()];
int index = 0;
for (String string : strings) {
resolvedStringsToIndexes.put(string, index);
symbolicIndex = symbolicStringsToIndexes.get(string);
symbolicStringsIndexToResolvedStringsIndex[symbolicIndex] = index;
index++;
}
// Builds the Types index map, and links the symbolic Types indexes to the resolved Types indexes.
resolvedTypesToIndexes.clear();
symbolicTypesIndexToResolvedTypesIndex = new int[types.size()];
index = 0;
for (String type : types) {
resolvedTypesToIndexes.put(type, index);
symbolicIndex = symbolicTypesToIndexes.get(type);
symbolicTypesIndexToResolvedTypesIndex[symbolicIndex] = index;
index++;
}
// Builds the Prototypes index map. The collection must be sorted first.
prototypesToIndexes.clear();
index = 0;
for (Prototype prototype : orderedPrototypes) {
prototypesToIndexes.put(prototype, index);
index++;
}
// Builds the Fields index map, and links the symbolic Fields indexes to the resolved Types indexes.
resolvedFieldsToIndexes.clear();
symbolicFieldsIndexToResolvedFieldsIndex = new int[fields.size()];
index = 0;
for (Field field : orderedFields) {
resolvedFieldsToIndexes.put(field, index);
symbolicIndex = symbolicFieldsToIndexes.get(field);
symbolicFieldsIndexToResolvedFieldsIndex[symbolicIndex] = index;
index++;
}
// Builds the Methods index map, and links the symbolic Methods indexes to the resolved Types indexes.
// The collection must be sorted first.
resolvedMethodsToIndexes.clear();
symbolicMethodsIndexToResolvedMethodsIndex = new int[orderedMethods.size()];
index = 0;
for (Method method : orderedMethods) {
resolvedMethodsToIndexes.put(method, index);
symbolicIndex = symbolicMethodsToIndexes.get(method);
symbolicMethodsIndexToResolvedMethodsIndex[symbolicIndex] = index;
index++;
}
}
/**
* Sorts the Classes into a new List. Each Class must be written after its possible Parent.
*/
private void sortClasses() {
orderedClasses = new ArrayList<ClassDefinitionItem>();
Collection<ClassDefinitionItem> classes = classNameToClassDefinitionItem.values();
for (ClassDefinitionItem cdi : classes) {
sortClassRec(cdi);
}
if (classNameToClassDefinitionItem.size() != orderedClasses.size()) {
try { throw new Exception("Sorted Classes list doesn't match the original list !");
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void sortClassRec(ClassDefinitionItem cdi) {
// We make sure the SuperClass hasn't already been encoded.
if (!orderedClasses.contains(cdi)) {
String superClass = cdi.getSuperClassName();
// Checks the super class, if any.
if (superClass != null) {
sortClassRecString(superClass);
}
// Checks the interfaces, if any.
TypeList tl = cdi.getInterfaces();
for (String className : tl.getTypeList()) {
sortClassRecString(className);
}
orderedClasses.add(cdi);
}
}
private void sortClassRecString(String className) {
ClassDefinitionItem ncdi = classNameToClassDefinitionItem.get(className);
// The SuperClass may not be found if it's a Java SuperClass like Object, Enum etc.
// We also make sure the SuperClass hasn't already been encoded.
if ((ncdi != null) && (!orderedClasses.contains(ncdi))) {
sortClassRec(ncdi);
}
}
}