/* 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 java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import org.ow2.asmdex.Opcodes;
import org.ow2.asmdex.instruction.Instruction;
import org.ow2.asmdex.lowLevelUtils.ByteVector;
import org.ow2.asmdex.lowLevelUtils.DalvikValueReader;
import org.ow2.asmdex.lowLevelUtils.IDalvikValueReader;
import org.ow2.asmdex.structureCommon.Label;
import org.ow2.asmdex.structureCommon.LocalVariable;
/**
* Class representing the code instructions and debug informations of one method.
* The instructions can link to debug instructions (the ones that don't concern lines/PC management).
* Debug line/PC advances are managed directly by this class.
*
* @author Julien Névo
*/
public class CodeItem {
/**
* The generated code_item code. Includes the bytecode as well as the header of the code_item,
* minus the reference to the debug_info_item as we don't know yet its offset.
* <i>CAN</i> include the padding at the end needed by the Try/catch.
* It is generated through the generateBytecode method.
* It uses <i>symbolic</i> references, so must be parsed again to link them to the "real"
* elements.
*/
private ByteVector codeItemCode;
/**
* The generated code_item try/catch section, just after the instructions, which should be stuck
* right after the codeItemCode. <i>Does NOT</i> include the padding right at the beginning. This ByteVector
* had to be separated from the code because it is composed of ULeb128 fields, which can change size.
* Instead of copying all the code if that happens, it is better to work on this part only.
*/
private ByteVector codeItemTryCatch;
/**
* Offset to the tries_size field.
*/
public static final int TRIES_SIZE_FIELD_OFFSET = 6;
/**
* Size in bytes of a try_item structure.
*/
private static final int TRY_ITEM_STRUCTURE_SIZE = 8;
/**
* Offset to the debug_info_offset field.
*/
private static final int DEBUG_INFO_OFFSET_OFFSET = 4 * 2;
/**
* Offset in bytes of the handler in a try_item structure.
*/
private static final int HANDLER_OFFSET_IN_TRY_ITEM_STRUCTURE = 6;
/**
* Size in bytes of the header of the code_item structure (till insns_size included).
*/
public static final int HEADER_SIZE = 4 * 2 + 2 * 4;
/**
* Offset in bytes of the insns_size structure.
*/
public static final int INSNS_SIZE_OFFSET = 12;
/**
* The offset of the Code Item from the beginning of the Dex file. It is only known when producing the
* output file, after the input has been fully parsed.
*/
private int offset;
/**
* Current size in bytes of the total instructions of this item (and by extension, the method).
* Does NOT take in account the header of the Code Item.
*/
private int size = 0;
/**
* List of the Labels for this method.
*/
private List<Label> labels = new ArrayList<Label>();
/**
* The number of words of incoming arguments used by this code.
*/
private int incomingArgumentsSizeInWord = 0;
/**
* The number of words of outgoing arguments used by this code.
*/
private int outgoingArgumentsSizeInWord = 0;
/**
* The number of words used by the register of the frame of this code.
* (= temporary registers + "this" (if non-static and non-constructor) + parameters).
*/
private int registerSize = 0;
/**
* The first line number found. It may be 0 if none were found.
*/
private int firstLineNumber = 0;
/**
* List of the Try/Catch of this Code Item.
*/
private ArrayList<TryCatch> tryCatches = new ArrayList<TryCatch>();
/**
* List of the instructions of this code item.
*/
private ArrayList<Instruction> instructions = new ArrayList<Instruction>();
/**
* The Method this Code Item belongs to.
*/
private Method method;
/**
* The Debug Info Item of this Code Item.
*/
private DebugInfoItem debugInfoItem;
/**
* The Constant Pool of the Application.
*/
final private ConstantPool constantPool;
/**
* Sets of encoded_catch_handlers, so that they are unique.
*/
private HashSet<EncodedCatchHandler> encodedCatchHandlers = new HashSet<EncodedCatchHandler>();
/**
* Map linking a TryCatch structure to a unique encoded_catch_handlers.
*/
private HashMap<TryCatch, EncodedCatchHandler> tryCatchToEncodedCatchHandler = new HashMap<TryCatch, EncodedCatchHandler>();
/**
* Map linking a unique EncodedCatchHandler to its offset relative to the encoded_catch_handler_list,
* that is from where the try_items need it.
*/
private HashMap<EncodedCatchHandler, Integer> encodedCatchHandlerToRelativeOffset = new HashMap<EncodedCatchHandler, Integer>();
/**
* Constructor of the Code Item.
* @param method the Method this Code Item belongs to.
*/
public CodeItem(Method method, ConstantPool constantPool) {
this.method = method;
this.constantPool = constantPool;
debugInfoItem = new DebugInfoItem(constantPool);
}
// ------------------------------------
// Public methods.
// ------------------------------------
/**
* Frees all the structures (list of instructions, try/catch...) of this element and the debug_info_item
* so that they don't consume memory. This <i>MUST</i> be done after having generated the bytecode, once
* the method has been parsed and its end visited.
*/
public void free() {
labels = null;
instructions = null;
encodedCatchHandlers = null;
tryCatchToEncodedCatchHandler = null;
encodedCatchHandlerToRelativeOffset = null;
debugInfoItem.free();
}
/**
* Adds a label to the set of used labels. If Null, nothing is done.
* @param label the label to add.
*/
public void addLabel(Label label) {
if ((label != null) && label.isResolved()) {
// We must be sure the Label isn't already in the list.
// The Object must not be here, not is content (that's why "contains" isn't used).
boolean found = false;
Iterator<Label> it = labels.iterator();
while (!found && it.hasNext()) {
found = (it.next() == label);
}
if (!found) {
labels.add(label);
}
}
}
/**
* Adds an instruction to the code item.
* @param instruction instruction to add to the code item.
*/
public void addInstruction(Instruction instruction) {
instructions.add(instruction);
size += instruction.getSize();
}
/**
* Adds a Try/Catch structure to the list of Try/Catch.
* @param tryCatch Try/Catch structure to add.
*/
public void addTryCatch(TryCatch tryCatch) {
// First we need to know if a similar Try (same Start) already exists.
// If yes, we add the Handler and Type to the Try.
// Else, we add the structure directly.
TryCatch foundTc = null;
Iterator<TryCatch> iterator = tryCatches.iterator();
while ((foundTc == null) && (iterator.hasNext())) {
TryCatch tc = iterator.next();
if (tc.getStart() == tryCatch.getStart()) {
foundTc = tc;
}
}
if (foundTc != null) {
foundTc.addExceptionHandlers(tryCatch.getExceptionHandlers());
} else {
tryCatches.add(tryCatch);
}
}
/**
* Generates the Instructions code, as Dalvik bytecode, in the codeItemCode buffer, as well as its
* header. It uses <i>symbolic</i> references, so must be parsed again later to link them to the "real"
* elements.
* Also fills the debug_code_item in this instance. Note however that the debug_info_offset field
* in the code_item header is not set, because we don't know where the debug_info_item is encoded
* for now. Note that the debug_info_item is not written here, only built.
* The alignment is not managed here, but must be by the calling method.
*/
public void generateCodeItemCode() {
codeItemCode = new ByteVector();
List<LocalVariable> localVariables = method.getLocalVariables();
// If the method has no instruction (can it happen in non-generated code ?), we skip all the
// coding of the instructions and the debug info.
boolean mustEncodeMethod = (instructions != null) && (instructions.size() > 0);
// Initializes the debug_info_item by writing its header.
if (mustEncodeMethod) {
debugInfoItem.initializeDebugInfoItem(method.getParameters(), this, localVariables);
}
// Generates the header of the Code Item.
codeItemCode.putShort(getRegistersSize());
codeItemCode.putShort(incomingArgumentsSizeInWord);
codeItemCode.putShort(outgoingArgumentsSizeInWord);
codeItemCode.putShort(getTriesSize());
codeItemCode.putInt(0); // The Debug Info Offset is not defined for now, as we don't know its offset yet.
int sizeWord = size / 2;
codeItemCode.putInt(sizeWord);
int offsetByteCode = codeItemCode.getLength();
if (mustEncodeMethod) {
for (Instruction instruction : instructions) {
debugInfoItem.parseDebugInformation(instruction, codeItemCode.getLength() - offsetByteCode);
instruction.write(codeItemCode, constantPool);
}
// Padding may be needed if Try/Catch structures are present.
if ((getTriesSize() != 0) && ((sizeWord % 2) != 0)) {
codeItemCode.putShort(0);
}
// Encodes the Try/Catch structures in the second ByteVector.
writeTryCatches(constantPool);
// Ends the debug_info_item.
debugInfoItem.closeDebugInfoItem();
}
}
/**
* Replaces one Instruction with a new given one.
* @param oldInsn the replaced Instruction.
* @param newInsn the new Instruction.
*/
public void replaceInstructions(Instruction oldInsn, Instruction newInsn) {
int indexInsnToReplace = instructions.indexOf(oldInsn);
if (indexInsnToReplace >= 0) {
instructions.remove(indexInsnToReplace);
instructions.add(indexInsnToReplace, newInsn);
// Updates the size of the Instructions.
size += newInsn.getSize() - oldInsn.getSize();
}
}
/**
* Parses the debug bytecode and maps the resolved indexes (Strings, Types) from the
* symbolic ones. The mapping between the two, done through two tables in the Constant Pool,
* must have been performed before.
* @param in input buffer to parse.
* @param offsetInputBuffer offset inside the input buffer from where to start the parsing.
* @return the parsed debug bytecode.
*/
public ByteVector mapResolvedIndexesForDebug(ByteVector in, int offsetInputBuffer) {
return debugInfoItem.mapResolvedIndexes(in, offsetInputBuffer);
}
/**
* Parses the bytecode and tryCatch and maps the resolved indexes (Strings, Fields, Types, Methods) from
* the symbolic ones. The mapping between the two, done through four tables in the Constant Pool,
* must have been performed before.
* This method must be called when the method has its own bytecode i.e. when the "ConstantPool"
* optimization isn't performed.
*/
public void mapResolvedIndexes() {
mapResolvedIndexesByteCode(codeItemCode, 0); // Maps the bytecode indexes.
codeItemTryCatch = mapResolvedIndexesTryCatch(codeItemTryCatch, 0, getTriesSize()); // Maps the try/catch indexes.
}
final static private byte INDEX_STRING = 1;
final static private byte INDEX_FIELD = 2;
final static private byte INDEX_TYPE = 3;
final static private byte INDEX_METHOD = 4;
/**
* Look-up table containing the Types of index every instructions uses. Includes 256 opcodes.
* 0 = no index.
*/
final private static byte[] typeOfIndexInInstructions = new byte[] {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x00
0,0,0,0,0,0,0,0,0,0,INDEX_STRING,INDEX_STRING,INDEX_TYPE,0,0,INDEX_TYPE, // 0x10
INDEX_TYPE,0,INDEX_TYPE,INDEX_TYPE,INDEX_TYPE,INDEX_TYPE,0,0,0,0,0,0,0,0,0,0, // 0x20
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x30
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x40
0,0,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,
INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD, // 0x50
INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,
INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_METHOD,INDEX_METHOD, // 0x60
INDEX_METHOD,INDEX_METHOD,INDEX_METHOD,0,INDEX_METHOD,INDEX_METHOD,INDEX_METHOD,INDEX_METHOD,INDEX_METHOD,0,0,0,0,0,0,0, // 0x70
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x80
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0x90
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xa0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xb0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xc0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xd0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xe0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 // 0xf0
};
/**
* Parses the bytecode and maps the resolved indexes (Strings, Fields, Types, Methods) from the
* symbolic ones. The given bytecode is <i>modified</i>.
* The mapping between the two, done through four tables in the Constant Pool,
* must have been performed before.
* @param out ByteVector to be read and modified.
* @param offsetByteCode offset in bytes where the parsing must begin.
*/
public void mapResolvedIndexesByteCode(ByteVector out, int offsetByteCode) {
// The same byte array is shared by the reader and writer.
IDalvikValueReader reader = new DalvikValueReader(out.getBuffer());
// Looks for the size of the instructions. We don't want to parse the possible try/catch
// structures after the code.
reader.seek(INSNS_SIZE_OFFSET + offsetByteCode);
int insnsSizeInBytes = reader.uint() * 2; // The read size is in words, hence the * 2.
int endPos = insnsSizeInBytes + reader.getPos();
int indexFoundType = 0;
// Parses all the opcodes.
while (reader.getPos() < endPos) {
int opcode = reader.ubyte();
int secondByte = reader.ubyte();
int indexSize = 2; // By default, we read short index, but it can raise to 4.
if (opcode == Opcodes.INSN_NOP) {
// If we find an array, we have to skip it.
int bytesToSkip = 0;
switch (secondByte) {
case 0x1: { // Packed-switch format.
int size = reader.ushort();
reader.skipInt(); // Skip first_key.
bytesToSkip = size * 4;
break;
}
case 0x2: { // Sparse-switch format.
int size = reader.ushort();
bytesToSkip = size * 4 * 2;
break;
}
case 0x3: { // Fill-array-data format.
int width = reader.ushort();
int size = reader.uint();
bytesToSkip = (size * width + 1) / 2 * 2;
break;
}
}
if (bytesToSkip > 0) {
reader.relativeSeek(bytesToSkip);
}
} else {
// Searches the type of the index inside a look-up table.
indexFoundType = typeOfIndexInInstructions[opcode];
if (opcode == Opcodes.INSN_CONST_STRING_JUMBO) {
indexSize = 4;
}
// If we didn't find any index, then skip to the next Instruction.
if (indexFoundType == 0) {
// Skips the instruction according to its size, - 2 because we already read the 16-bit opcode.
int instructionSize = Instruction.getInstructionSizeInByte(opcode) - 2;
if (instructionSize > 0) {
reader.relativeSeek(instructionSize);
}
} else {
int savedReaderOffset = reader.getPos();
// We get to the encoded index.
int indexOffset = savedReaderOffset; // + 2 - 2 : +2 to reach the index, -2 because we already read the 16-bit opcode.
reader.seek(indexOffset);
// Reads the symbolic index according to its size.
int symbolicIndex = (indexSize == 2 ? reader.ushort() :
reader.ushort() + (reader.ushort() << 16));
int resolvedIndex = -1;
switch (indexFoundType) {
case INDEX_STRING:
resolvedIndex = constantPool.getResolvedStringIndexFromSymbolicStringIndex(symbolicIndex);
break;
case INDEX_FIELD:
resolvedIndex = constantPool.getResolvedFieldIndexFromSymbolicFieldIndex(symbolicIndex);
break;
case INDEX_TYPE:
resolvedIndex = constantPool.getResolvedTypeIndexFromSymbolicTypeIndex(symbolicIndex);
break;
case INDEX_METHOD:
resolvedIndex = constantPool.getResolvedMethodIndexFromSymbolicMethodIndex(symbolicIndex);
break;
default:
try { throw new Exception("Unknown Index type.");
} catch (Exception e) { e.printStackTrace(); }
}
// Overwrites the symbolic index with the resolved index.
if (indexSize == 2) {
out.putShort(resolvedIndex, indexOffset);
} else {
out.putShort(resolvedIndex & 0xffff, indexOffset);
out.putShort((resolvedIndex >> 16) & 0xffff, indexOffset + 2);
}
// Skips the instruction according to its size, - 2 because we already read the 16-bit opcode.
int instructionSize = Instruction.getInstructionSizeInByte(opcode) - 2;
if (instructionSize > 0) {
reader.seek(savedReaderOffset + instructionSize);
}
}
}
}
}
/**
* Parses the tryCatch and maps the resolved indexes (Strings, Fields, Types, Methods) from the
* symbolic ones. The input vector is not modified.
* The mapping between the two, done through four tables in the Constant Pool,
* must have been performed before.
* @param in a ByteVector containing the try_catch bytecode to modify.
* @param offsetInInputArray offset in bytes where the parsing must begin.
* @param nbTries count of Try/Catch.
* @return a ByteVector containing the try_catch bytecode.
*/
public ByteVector mapResolvedIndexesTryCatch(ByteVector in, int offsetInInputArray, int nbTries) {
ByteVector out = new ByteVector();
if (nbTries == 0) {
return out;
}
// The reader and writer are not shared, as ULeb128 encoded field may differ in size.
IDalvikValueReader reader = new DalvikValueReader(in.getBuffer());
reader.seek(offsetInInputArray);
// Duplicates the try_item elements.
for (int i = 0; i < nbTries; i++) {
out.putInt(reader.uint());
out.putShort(reader.ushort());
out.putShort(reader.ushort()); // It may move will be patched later.
}
// Get reference position of the encoded_catch_handler
int absoluteOldPos = reader.getPos();
int absoluteNewPos = out.getLength();
HashMap<Integer,Integer> oldToNewHandlerOffsets = new HashMap<Integer,Integer>();
// Encodes the encoded_catch_handler elements. First the list.
int nbEncodedCatchHandler = reader.uleb128();
out.putUleb128(nbEncodedCatchHandler);
// Encodes each of them.
for (int i = 0; i < nbEncodedCatchHandler; i++) {
// Detects shifts in handler position
int oldOffset = reader.getPos() - absoluteOldPos;
int newOffset = out.getLength() - absoluteNewPos;
if (oldOffset != newOffset) {
oldToNewHandlerOffsets.put(oldOffset, newOffset);
}
int readSize = (int)reader.sleb128(); // May be negative if catch_all present.
int size = (readSize >= 0 ? readSize : -readSize);
out.putSleb128(readSize);
// Encodes each encoded_type_addr_pair.
for (int j = 0; j < size; j++) {
int symbolicIndex = reader.uleb128(); // Encodes the type_idx.
int resolvedIndex = constantPool.getResolvedTypeIndexFromSymbolicTypeIndex(symbolicIndex);
out.putUleb128(resolvedIndex);
out.putUleb128(reader.uleb128()); // Encodes the addr.
}
if (readSize <= 0) {
out.putUleb128(reader.uleb128()); // Encodes the catch_all_addr.
}
}
if (oldToNewHandlerOffsets.size() != 0) {
// Handlers were shifted
int offset = HANDLER_OFFSET_IN_TRY_ITEM_STRUCTURE;
for(int i = 0; i < nbTries; i++) {
reader.seek(offsetInInputArray + offset);
int oldHandlerOffset = reader.ushort();
Integer newHandlerOffset = oldToNewHandlerOffsets.get(oldHandlerOffset);
if (newHandlerOffset != null) out.putShort(newHandlerOffset, offset);
offset += TRY_ITEM_STRUCTURE_SIZE;
}
}
return out;
}
// ------------------------------------
// Getters and setters.
// ------------------------------------
/**
* Returns the size in bytes of all the instructions of this item (and by extension, the method).
* Does NOT take in account the header of the Code Item.
* @return the size in bytes of all the instructions of this item, without the header.
*/
public int getSize() {
return size;
}
/**
* Returns the number of Try structure in this Code Item.
* @return the number of Try structure in this Code Item.
*/
public int getTriesSize() {
return tryCatches.size();
}
/**
* Sets the Register Size of this code.
* @param registerSize the Register Size of this code.
*/
public void setRegisterSize(int registerSize) {
this.registerSize = registerSize;
}
/**
* Gets the number of registers used by this code.
* @return the number of registers used by this code.
*/
public int getRegistersSize() {
return registerSize;
}
/**
* Returns the list of the Labels of this code item.
* @return the list of the Labels of this code item.
*/
public List<Label> getLabels() {
return labels;
}
/**
* Gets the size in word of the Incoming Arguments.
* @return the size in word of the Incoming Arguments.
*/
public int getIncomingArgumentsSizeInWord() {
return incomingArgumentsSizeInWord;
}
/**
* Sets the size in word of the Incoming Arguments.
* @param incomingArgumentsSizeInWord the size in word of the Incoming Arguments.
*/
public void setIncomingArgumentsSizeInWord(int incomingArgumentsSizeInWord) {
this.incomingArgumentsSizeInWord = incomingArgumentsSizeInWord;
}
/**
* Gets the size in word of the Outgoing Arguments.
* @return the size in word of the Outgoing Arguments.
*/
public int getOutgoingArgumentsSizeInWord() {
return outgoingArgumentsSizeInWord;
}
/**
* Sets the size in word of the Outgoing Arguments.
* @param outgoingArgumentsSizeInWord the size in word of the Outgoing Arguments.
*/
public void setOutgoingArgumentsSizeInWord(int outgoingArgumentsSizeInWord) {
this.outgoingArgumentsSizeInWord = outgoingArgumentsSizeInWord;
}
/**
* Returns the offset of the Code Item from the beginning of the file. It is only known when
* producing the output file, after the input has been fully parsed.
* @return the offset of the Code Item from the beginning of the file.
*/
public int getOffset() {
return offset;
}
/**
* Sets the offset of the Code Item from the beginning of the file.
* @param offset the offset of the Code Item from the beginning of the file.
*/
public void setOffset(int offset) {
this.offset = offset;
}
/**
* Sets the Method this Code Item is linked to.
* @param method the Method this Code Item is linked to.
*/
public void setMethod(Method method) {
this.method = method;
}
/**
* Sets the offset of the Debug Info Item inside the already encoded Code Item.
* @param out Vector of the Dex file. It already contains the current Code Item.
* @param debugInfoItemOffset the offset of the debug_info_item, encoded or soon to be.
*/
public void setDebugInfoItemOffset(ByteVector out, int debugInfoItemOffset) {
out.putInt(debugInfoItemOffset, offset + DEBUG_INFO_OFFSET_OFFSET); // Reaches the debug_info_off field.
}
/**
* Sets the first line number of this code_item, but only if none has been found before.
* @param firstLineNumber the first line number of this code_item.
*/
public void setFirstLineNumber(int firstLineNumber) {
if (this.firstLineNumber == 0) {
this.firstLineNumber = firstLineNumber;
}
}
/**
* Returns the first line number found. It may be 0 if none were.
* @return the first line number found, or 0.
*/
public int getFirstLineNumber() {
return firstLineNumber;
}
/**
* Returns the code_item code (including the code_item header and the bytecode), <i>without</i> the
* try/catch fields after the insns field, but with the padding if needed.
* It uses <i>symbolic</i> references, so must be parsed again to link them to the "real"
* elements.
* @return the code_item code, without the try/catch fields.
*/
public ByteVector getCodeItemCode() {
if (codeItemCode == null) {
generateCodeItemCode();
}
return codeItemCode;
}
/**
* Returns the try/catch section of the code_item, beginning by the possible padding after the insns field,
* or Null if no try/catch is present.
* It uses <i>symbolic</i> references, so must be parsed again to link them to the "real"
* elements.
* @return the try/catch code, or Null.
*/
public ByteVector getCodeItemTryCatch() {
if (codeItemCode == null) { // Generating the code will generate the try/catch
generateCodeItemCode(); // (or not, if there is none).
}
return codeItemTryCatch;
}
/**
* Returns the debug_info_item code, still using symbolic indexes.
* It is available only after this method bytecode has been generated.
* @return the debug_info_item code still using symbolic indexes.
*/
public ByteVector getDebugInfoItemCode() {
return debugInfoItem.getDebugInfoItemCode();
}
/**
* Indicates if at least one index is encoded in the debug code. We use it to optimize the mapping
* with the resolved indexes : it doesn't need do be done if no index was found.
* @return true if at least one index is encoded in the debug code.
*/
public boolean areSymbolicIndexesUsedInDebugCodeItem() {
return debugInfoItem.areSymbolicIndexesUsed();
}
// ------------------------------------
// Private methods.
// ------------------------------------
/**
* Encodes the Try/Catch/Handler structures of the code_item, inside a new codeItemTryCatch buffer.
* @param constantPool the Constant Pool.
*/
private void writeTryCatches(ConstantPool constantPool) {
if (getTriesSize() == 0) {
return;
}
codeItemTryCatch = new ByteVector();
// Encodes the "tries" field. For now, the handler is not encoded.
for (TryCatch tc : tryCatches) {
int startAddr = tc.getStart().getOffset();
int endAddr = tc.getEnd().getOffset();
codeItemTryCatch.putInt(startAddr / 2); // / 2 because based on 16-bit units.
codeItemTryCatch.putShort((endAddr - startAddr) / 2); // / 2 because based on 16-bit units.
codeItemTryCatch.putShort(0); // Not encoded for now.
// Builds an EncodedCatchHandler to provide uniqueness of these structures, and link it to
// the TryCatch.
EncodedCatchHandler ech = new EncodedCatchHandler(constantPool);
for (ExceptionHandler eh : tc.getExceptionHandlers()) {
ech.addTypeAddrPair(eh.getType(), eh.getHandler());
}
encodedCatchHandlers.add(ech);
// Links the TryCatch to the EncodedCatchHandler.
tryCatchToEncodedCatchHandler.put(tc, ech);
}
// Encodes the encoded_catch_handler_list.
int absoluteOffsetEncodedCatchHandlerList = codeItemTryCatch.getLength();
// Encodes total count of Catch Handlers to be encoded. They may be shared.
codeItemTryCatch.putUleb128(encodedCatchHandlers.size());
for (EncodedCatchHandler ech : encodedCatchHandlers) {
int offsetEncodedCatchHandlerCurrentItem = codeItemTryCatch.getLength() - absoluteOffsetEncodedCatchHandlerList;
encodedCatchHandlerToRelativeOffset.put(ech, offsetEncodedCatchHandlerCurrentItem);
ech.write(codeItemTryCatch);
}
// Links the TryCatches with the EncodedCatchHandler offsets.
int offset = HANDLER_OFFSET_IN_TRY_ITEM_STRUCTURE;
for (TryCatch tc : tryCatches) {
codeItemTryCatch.putShort(encodedCatchHandlerToRelativeOffset.get(tryCatchToEncodedCatchHandler.get(tc)), offset);
offset += TRY_ITEM_STRUCTURE_SIZE;
}
}
}