/* 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.lowLevelUtils; import org.ow2.asmdex.Opcodes; import org.ow2.asmdex.instruction.DebugInstruction; import org.ow2.asmdex.instruction.DebugInstructionStartLocal; import org.ow2.asmdex.instruction.DebugInstructionStartLocalExtended; import org.ow2.asmdex.instruction.Instruction; import org.ow2.asmdex.instruction.InstructionFormat10T; import org.ow2.asmdex.instruction.InstructionFormat10X; import org.ow2.asmdex.instruction.InstructionFormat11N; import org.ow2.asmdex.instruction.InstructionFormat11X; import org.ow2.asmdex.instruction.InstructionFormat12X; import org.ow2.asmdex.instruction.InstructionFormat20T; import org.ow2.asmdex.instruction.InstructionFormat21C; import org.ow2.asmdex.instruction.InstructionFormat21H; import org.ow2.asmdex.instruction.InstructionFormat21S; import org.ow2.asmdex.instruction.InstructionFormat21T; import org.ow2.asmdex.instruction.InstructionFormat22B; import org.ow2.asmdex.instruction.InstructionFormat22C; import org.ow2.asmdex.instruction.InstructionFormat22S; import org.ow2.asmdex.instruction.InstructionFormat22T; import org.ow2.asmdex.instruction.InstructionFormat22X; import org.ow2.asmdex.instruction.InstructionFormat23X; import org.ow2.asmdex.instruction.InstructionFormat30T; import org.ow2.asmdex.instruction.InstructionFormat31C; import org.ow2.asmdex.instruction.InstructionFormat31I; import org.ow2.asmdex.instruction.InstructionFormat31T; import org.ow2.asmdex.instruction.InstructionFormat32X; import org.ow2.asmdex.instruction.InstructionFormat35C; import org.ow2.asmdex.instruction.InstructionFormat3RC; import org.ow2.asmdex.instruction.InstructionFormat51L; import org.ow2.asmdex.structureCommon.Label; import org.ow2.asmdex.structureWriter.Field; import org.ow2.asmdex.structureWriter.Method; /** * Utility class that encodes Instructions in the most optimized possible form. * * Each static method builds an Instruction according to its given parameters, * and returns it. * * @author Julien Névo */ public class InstructionEncoder { private final static int MIN_VALUE_4BITS_SIGNED = -8; private final static int MAX_VALUE_4BITS_SIGNED = 7; private final static int MAX_VALUE_4BITS_UNSIGNED = 0xf; private final static int MAX_VALUE_8BITS_UNSIGNED = 0xff; private final static int MIN_VALUE_16BITS_SIGNED = -32768; private final static int MAX_VALUE_16BITS_SIGNED = 32767; /** * Method invocation encoder * * @param opcode * @param method * @param arguments * @return abstract instruction */ public static Instruction encodeMethodInsn(int opcode, Method method, int[] arguments) { // The opcode could be about an invoke Instruction, or an invoke/range. if (opcode < Opcodes.INSN_INVOKE_VIRTUAL_RANGE) { return new InstructionFormat35C(opcode, method, arguments); } else { return new InstructionFormat3RC(opcode, method, arguments); } } /** * Move variable encoder * * @param opcode * @param destinationRegister * @param var * @return instruction encoded */ public static Instruction encodeVarInsn(int opcode, int destinationRegister, int var) { Instruction insn = null; switch (opcode) { // Testing all the move instructions to know which one is the best // adapted according to // the needs (4, 8, 16 bits...). case 0x01: // Move. case 0x02: case 0x03: case 0x04: // Move Wide. case 0x05: case 0x06: case 0x07: // Move Object. case 0x08: case 0x09: insn = createBest12X22X32XInstruction(opcode, destinationRegister, var); break; case 0x12: // Const/4, Const/16, Const, Const/high 16. case 0x13: case 0x14: case 0x15: { if ((destinationRegister <= MAX_VALUE_4BITS_UNSIGNED) && ((var <= MAX_VALUE_4BITS_SIGNED) && (var >= MIN_VALUE_4BITS_SIGNED))) { // 4 bits ? insn = new InstructionFormat11N(Opcodes.INSN_CONST_4, destinationRegister, var); } else if ((destinationRegister <= MAX_VALUE_8BITS_UNSIGNED) && ((var <= MAX_VALUE_16BITS_SIGNED) && (var >= MIN_VALUE_16BITS_SIGNED))) { // dest = 8 bits, var = 16 bits ? insn = new InstructionFormat21S(Opcodes.INSN_CONST_16, destinationRegister, var); } else { // dest = 8 bits, var = 32 bits, or var = 16 bits in the format // BBBB0000 ? // Is var full 16-bits, or in the format BBBB0000 ? insn = ((var & 0x0000ffff) == 0) ? new InstructionFormat21H(Opcodes.INSN_CONST_HIGH16, destinationRegister, var >> 16) : new InstructionFormat31I(Opcodes.INSN_CONST, destinationRegister, var); } break; } case 0x16: // Const-wide/16, Const-wide/32, Const-wide, const-wide/high // 16. case 0x17: { if ((var <= MAX_VALUE_16BITS_SIGNED) && (var >= MIN_VALUE_16BITS_SIGNED)) { // var = 16 bits ? insn = new InstructionFormat21S(Opcodes.INSN_CONST_WIDE_16, destinationRegister, var); } else { // var = 32 bits. insn = new InstructionFormat31I(Opcodes.INSN_CONST_WIDE_32, destinationRegister, var); } break; } default: throw new RuntimeException("Unexpected opcode : 0x" + Integer.toHexString(opcode)); } return insn; } /** * Move variable encoder for long constant source * * @param opcode * @param destinationRegister * @param var * @return abstract instruction */ public static Instruction encodeVarInsn(int opcode, int destinationRegister, long var) { Instruction insn = null; switch (opcode) { case 0x18: case 0x19: { // Is var full 64-bits, or in the format BBBB000000000000 ? insn = ((var & 0xffffffffffffL) == 0) ? new InstructionFormat21H(Opcodes.INSN_CONST_WIDE_HIGH16, destinationRegister, (int) (var >> 48)) : new InstructionFormat51L(Opcodes.INSN_CONST_WIDE, destinationRegister, var); break; } default: throw new RuntimeException("Unexpected opcode : 0x" + Integer.toHexString(opcode)); } return insn; } /** * Creates the best (4, 8, 16 bits) Instruction (12X, 22X, 32X) according to * the given parameters. * * @param opcode * the opcode of the Instruction (1-3, 4-6, 7-9). * @param destinationRegister * the destination register. * @param sourceRegister * the source register. * @return the most optimized Instruction. */ private static Instruction createBest12X22X32XInstruction(int opcode, int destinationRegister, int sourceRegister) { if (opcode >= Opcodes.INSN_MOVE_OBJECT) { opcode = Opcodes.INSN_MOVE_OBJECT; } else if (opcode >= Opcodes.INSN_MOVE_WIDE) { opcode = Opcodes.INSN_MOVE_WIDE; } else if (opcode >= Opcodes.INSN_MOVE) { opcode = Opcodes.INSN_MOVE; } Instruction insn; if ((destinationRegister <= MAX_VALUE_4BITS_UNSIGNED) && (sourceRegister <= MAX_VALUE_4BITS_UNSIGNED)) { // 4 bits ? insn = new InstructionFormat12X(opcode, destinationRegister, sourceRegister); } else if (destinationRegister <= MAX_VALUE_8BITS_UNSIGNED) { // dest = 8 bits ? insn = new InstructionFormat22X(opcode + 1, destinationRegister, sourceRegister); } else { insn = new InstructionFormat32X(opcode + 2, destinationRegister, sourceRegister); // all // is // 16 // bits. } return insn; } /** * Encode instructions without args * * @param opcode * @return abstract instruction */ public static Instruction encodeInsn(int opcode) { return new InstructionFormat10X(opcode); } /** * Encode three arguments operations * * @param opcode * @param destinationRegister * @param firstSourceRegister * @param secondSourceRegister * @param value * @return abstract instruction */ public static Instruction encodeOperationInsn(int opcode, int destinationRegister, int firstSourceRegister, int secondSourceRegister, int value) { Instruction insn = null; // Creates an instruction according to the opcode. if ((opcode >= Opcodes.INSN_ADD_INT) && (opcode <= Opcodes.INSN_REM_DOUBLE)) { insn = new InstructionFormat23X(opcode, destinationRegister, firstSourceRegister, secondSourceRegister); } else if ((opcode >= Opcodes.INSN_CMPL_FLOAT) && (opcode <= Opcodes.INSN_CMP_LONG)) { insn = new InstructionFormat23X(opcode, destinationRegister, firstSourceRegister, secondSourceRegister); } else if ((opcode >= Opcodes.INSN_NEG_INT) && (opcode <= Opcodes.INSN_INT_TO_SHORT)) { insn = new InstructionFormat12X(opcode, destinationRegister, firstSourceRegister); } else if ((opcode >= Opcodes.INSN_ADD_INT) && (opcode <= Opcodes.INSN_REM_DOUBLE)) { insn = new InstructionFormat23X(opcode, destinationRegister, firstSourceRegister, secondSourceRegister); } else if ((opcode >= Opcodes.INSN_ADD_INT_2ADDR) && (opcode <= Opcodes.INSN_REM_DOUBLE_2ADDR)) { insn = new InstructionFormat12X(opcode, destinationRegister, secondSourceRegister); } else if ((opcode >= Opcodes.INSN_ADD_INT_LIT16) && (opcode <= Opcodes.INSN_XOR_INT_LIT16)) { insn = new InstructionFormat22S(opcode, destinationRegister, firstSourceRegister, value); } else if ((opcode >= Opcodes.INSN_ADD_INT_LIT8) && (opcode <= Opcodes.INSN_USHR_INT_LIT8)) { insn = new InstructionFormat22B(opcode, destinationRegister, firstSourceRegister, value); } else { throw new RuntimeException("Unexpected opcode : 0x" + Integer.toHexString(opcode)); } return insn; } /** * Unary instruction * * @param opcode * @param operand * @return abstract instruction */ public static Instruction encodeIntInsn(int opcode, int operand) { return new InstructionFormat11X(opcode, operand); } /** * Encode jumps (conditional or not) * * @param opcode * @param label * @param registerA * @param registerB * @param instructionOffset * @return abstract instruction */ public static Instruction encodeJumpInsn(int opcode, Label label, int registerA, int registerB, int instructionOffset) { Instruction insn = null; // Creates an instruction according to the opcode. We also give the // current offset of the // instruction of the method, because Jump Instructions need it to // resolve Labels. if (opcode == Opcodes.INSN_GOTO) { //insn = InstructionFormat10T.obtain(opcode, label, instructionOffset); insn = new InstructionFormat10T(opcode, label, instructionOffset); } else if (opcode == Opcodes.INSN_GOTO_16) { insn = new InstructionFormat20T(opcode, label, instructionOffset); } else if (opcode == Opcodes.INSN_GOTO_32) { insn = new InstructionFormat30T(opcode, label, instructionOffset); } else if ((opcode >= Opcodes.INSN_IF_EQ) && (opcode <= Opcodes.INSN_IF_LE)) { insn = new InstructionFormat22T(opcode, label, registerA, registerB, instructionOffset); } else if ((opcode >= Opcodes.INSN_IF_EQZ) && (opcode <= Opcodes.INSN_IF_LEZ)) { insn = new InstructionFormat21T(opcode, label, registerA, instructionOffset); } else { throw new RuntimeException("Unexpected opcode : 0x" + Integer.toHexString(opcode)); } return insn; } /** * Fill array * * @param opcode * @param arrayReference * @param arrayLabel * @param instructionOffset * @return abstract instruction */ public static Instruction encodeFillArrayDataInsn(int opcode, int arrayReference, Label arrayLabel, int instructionOffset) { return new InstructionFormat31T(opcode, arrayLabel, arrayReference, instructionOffset); } /** * Instructions handling type representation * * @param opcode * @param destinationRegister * @param referenceBearingRegister * @param sizeRegister * @param type * @return abstract instruction */ public static Instruction encodeTypeInsn(int opcode, int destinationRegister, int referenceBearingRegister, int sizeRegister, String type) { Instruction insn = null; // Creates an instruction according to the opcode. if (opcode == Opcodes.INSN_CONST_CLASS) { insn = new InstructionFormat21C(opcode, type, destinationRegister); } else if (opcode == Opcodes.INSN_CHECK_CAST) { insn = new InstructionFormat21C(opcode, type, referenceBearingRegister); } else if (opcode == Opcodes.INSN_INSTANCE_OF) { insn = new InstructionFormat22C(opcode, type, destinationRegister, referenceBearingRegister); } else if (opcode == Opcodes.INSN_NEW_INSTANCE) { insn = new InstructionFormat21C(opcode, type, destinationRegister); } else if (opcode == Opcodes.INSN_NEW_ARRAY) { insn = new InstructionFormat22C(opcode, type, destinationRegister, sizeRegister); } else { throw new RuntimeException("Unexpected opcode : 0x" + Integer.toHexString(opcode)); } return insn; } /** * Multi new array * * @param type * @param registers * @return abstract instruction */ public static Instruction encodeMultiANewArrayInsn(String type, int[] registers) { Instruction insn = null; int length = registers.length; // The instruction to create varies according to the number of // registers. // Bug316405: we have to use a range if register indexes are beyond 16. // In that case we assume it is a range and let // instruction generation catch errors. if (length <= 5 && length > 0 && registers[length - 1] < 16) { insn = new InstructionFormat35C(Opcodes.INSN_FILLED_NEW_ARRAY, type, registers); } else { insn = new InstructionFormat3RC(Opcodes.INSN_FILLED_NEW_ARRAY_RANGE, type, registers); } return insn; } /** * Table switch * * @param register * @param switchTableLabel * @param instructionOffset * @return abstract instruction */ public static Instruction encodeTableSwitchInsn(int register, Label switchTableLabel, int instructionOffset) { return new InstructionFormat31T(Opcodes.INSN_PACKED_SWITCH_INSN, switchTableLabel, register, instructionOffset); } /** * Sparse switch * * @param register * @param switchTableLabel * @param instructionOffset * @return abstract instruction */ public static Instruction encodeSparseSwitchInsn(int register, Label switchTableLabel, int instructionOffset) { return new InstructionFormat31T(Opcodes.INSN_SPARSE_SWITCH_INSN, switchTableLabel, register, instructionOffset); } /** * @param destinationRegister * @param arrayReferenceBearing * @return abstract instruction */ public static Instruction encodeArrayLength(int destinationRegister, int arrayReferenceBearing) { return new InstructionFormat12X(Opcodes.INSN_ARRAY_LENGTH, destinationRegister, arrayReferenceBearing); } /** * Operation on array * * @param opcode * @param valueRegister * @param arrayRegister * @param indexRegister * @return abstract instruction */ public static Instruction encodeArrayOperation(int opcode, int valueRegister, int arrayRegister, int indexRegister) { return new InstructionFormat23X(opcode, valueRegister, arrayRegister, indexRegister); } /** * Operation with strings * * @param opcode * @param destinationRegister * @param string * @return abstract instruction */ public static Instruction encodeStringOperation(int opcode, int destinationRegister, String string) { Instruction insn = null; if (opcode == Opcodes.INSN_CONST_STRING) { insn = new InstructionFormat21C(opcode, string, destinationRegister); } else if (opcode == Opcodes.INSN_CONST_STRING_JUMBO) { insn = new InstructionFormat31C(opcode, string, destinationRegister); } else { throw new RuntimeException("Unexpected opcode : 0x" + Integer.toHexString(opcode)); } return insn; } /** * Field getter or setter * * @param opcode * @param valueRegister * @param objectRegister * @param field * @return abstract instruction */ public static Instruction encodeFieldInsn(int opcode, int valueRegister, int objectRegister, Field field) { Instruction insn = null; if ((opcode >= Opcodes.INSN_IGET) && (opcode <= Opcodes.INSN_IPUT_SHORT)) { insn = new InstructionFormat22C(opcode, field, valueRegister, objectRegister); } else if ((opcode >= Opcodes.INSN_SGET) && (opcode <= Opcodes.INSN_SPUT_SHORT)) { insn = new InstructionFormat21C(opcode, field, valueRegister); } return insn; } /** * Pseudo debug instruction * * @param name * @param desc * @param signature * @param index * @param start * @return abstract instruction */ public static DebugInstruction encodeDebugStartLocal(String name, String desc, String signature, int index, Label start) { DebugInstruction insn = null; if (signature == null) { insn = new DebugInstructionStartLocal(index, name, desc, start); } else { insn = new DebugInstructionStartLocalExtended(index, name, desc, signature, start); } return insn; } }