package com.sebastian_daschner.jaxrs_analyzer.analysis.bytecode.collection;
import com.sebastian_daschner.jaxrs_analyzer.LogProvider;
import com.sebastian_daschner.jaxrs_analyzer.model.instructions.*;
import com.sebastian_daschner.jaxrs_analyzer.model.methods.MethodIdentifier;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import java.lang.reflect.Field;
import static com.sebastian_daschner.jaxrs_analyzer.model.Types.*;
import static com.sebastian_daschner.jaxrs_analyzer.model.methods.MethodIdentifier.of;
import static org.objectweb.asm.Opcodes.*;
import static org.objectweb.asm.util.Printer.OPCODES;
/**
* @author Sebastian Daschner
*/
public final class InstructionBuilder {
private InstructionBuilder() {
throw new UnsupportedOperationException();
}
public static Instruction buildFieldInstruction(final int opcode, final String ownerClass, final String name, final String desc, final Label label) {
// TODO remove
if (org.objectweb.asm.Type.getObjectType(ownerClass).getClassName().equals(ownerClass.replace('.', '/')))
throw new AssertionError("!");
final String opcodeName = OPCODES[opcode];
switch (opcode) {
case GETSTATIC:
final Object value = getStaticValue(name, ownerClass);
return new GetStaticInstruction(ownerClass, name, desc, value, label);
case PUTSTATIC:
return new SizeChangingInstruction(opcodeName, 0, 1, label);
case GETFIELD:
return new GetFieldInstruction(ownerClass, name, desc, label);
case PUTFIELD:
return new SizeChangingInstruction(opcodeName, 0, 2, label);
default:
throw new IllegalArgumentException("Opcode " + opcode + " not a field instruction");
}
}
public static Instruction buildInstruction(final int opcode, final Label label) {
final String opcodeName = OPCODES[opcode];
switch (opcode) {
case ICONST_0:
return new PushInstruction(0, PRIMITIVE_INT, label);
case ICONST_1:
return new PushInstruction(1, PRIMITIVE_INT, label);
case ICONST_2:
return new PushInstruction(2, PRIMITIVE_INT, label);
case ICONST_3:
return new PushInstruction(3, PRIMITIVE_INT, label);
case ICONST_4:
return new PushInstruction(4, PRIMITIVE_INT, label);
case ICONST_5:
return new PushInstruction(5, PRIMITIVE_INT, label);
case ICONST_M1:
return new PushInstruction(-1, PRIMITIVE_INT, label);
case DCONST_0:
return new PushInstruction(0d, PRIMITIVE_DOUBLE, label);
case DCONST_1:
return new PushInstruction(1d, PRIMITIVE_DOUBLE, label);
case FCONST_0:
return new PushInstruction(1f, PRIMITIVE_FLOAT, label);
case FCONST_1:
return new PushInstruction(1f, PRIMITIVE_FLOAT, label);
case FCONST_2:
return new PushInstruction(2f, PRIMITIVE_FLOAT, label);
case LCONST_0:
return new PushInstruction(0L, PRIMITIVE_LONG, label);
case LCONST_1:
return new PushInstruction(1L, PRIMITIVE_LONG, label);
case IALOAD:
case LALOAD:
case FALOAD:
case DALOAD:
case AALOAD:
case BALOAD:
case CALOAD:
case SALOAD:
return new SizeChangingInstruction(opcodeName, 1, 2, label);
case IASTORE:
case LASTORE:
case FASTORE:
case DASTORE:
case AASTORE:
case BASTORE:
case CASTORE:
case SASTORE:
return new SizeChangingInstruction(opcodeName, 0, 3, label);
case DUP_X1:
case DUP2_X1:
return new SizeChangingInstruction(opcodeName, 3, 2, label);
case DUP_X2:
case DUP2_X2:
return new SizeChangingInstruction(opcodeName, 4, 3, label);
case ARRAYLENGTH:
case I2L:
case I2F:
case I2D:
case L2I:
case L2F:
case L2D:
case F2I:
case F2L:
case F2D:
case D2I:
case D2L:
case D2F:
case I2B:
case I2C:
case I2S:
case INEG:
case LNEG:
case FNEG:
case DNEG:
case SWAP:
return new SizeChangingInstruction(opcodeName, 1, 1, label);
case IADD:
case LADD:
case FADD:
case DADD:
case ISUB:
case LSUB:
case FSUB:
case DSUB:
case IMUL:
case LMUL:
case FMUL:
case DMUL:
case IDIV:
case LDIV:
case FDIV:
case DDIV:
case IREM:
case LREM:
case FREM:
case DREM:
case ISHL:
case LSHL:
case ISHR:
case LSHR:
case IUSHR:
case LUSHR:
case IAND:
case LAND:
case IOR:
case LOR:
case IXOR:
case LXOR:
case LCMP:
case FCMPL:
case FCMPG:
case DCMPL:
case DCMPG:
return new SizeChangingInstruction(opcodeName, 1, 2, label);
case IRETURN:
case LRETURN:
case FRETURN:
case DRETURN:
case ARETURN:
return new ReturnInstruction(label);
case ATHROW:
return new ThrowInstruction(label);
case RETURN:
case NOP:
return new DefaultInstruction(opcodeName, label);
case POP:
case POP2:
case MONITORENTER:
case MONITOREXIT:
return new SizeChangingInstruction(opcodeName, 0, 1, label);
case ACONST_NULL:
return new SizeChangingInstruction(opcodeName, 1, 0, label);
case DUP:
case DUP2:
return new DupInstruction(label);
default:
throw new IllegalArgumentException("Unexpected opcode " + opcode);
}
}
public static Instruction buildLoadStoreInstruction(int opcode, int index, Label label) {
switch (opcode) {
case ILOAD:
case LLOAD:
case FLOAD:
case DLOAD:
case ALOAD:
return new LoadStoreInstructionPlaceholder(Instruction.InstructionType.LOAD_PLACEHOLDER, index, label);
case ISTORE:
case LSTORE:
case FSTORE:
case DSTORE:
case ASTORE:
return new LoadStoreInstructionPlaceholder(Instruction.InstructionType.STORE_PLACEHOLDER, index, label);
case RET:
return new DefaultInstruction(OPCODES[opcode], label);
default:
throw new IllegalArgumentException("Unexpected opcode " + opcode);
}
}
public static Instruction buildTypeInstruction(int opcode, String className, final Label label) {
final String opcodeName = OPCODES[opcode];
switch (opcode) {
case NEW:
return new NewInstruction(className, label);
case ANEWARRAY:
case INSTANCEOF:
return new SizeChangingInstruction(opcodeName, 1, 1, label);
case CHECKCAST:
return new DefaultInstruction(opcodeName, label);
default:
throw new IllegalArgumentException("Unexpected opcode " + opcode);
}
}
public static InvokeInstruction buildInvokeInstruction(final int opcode, String containingClass, String name, String desc, final Label label) {
switch (opcode) {
case INVOKEINTERFACE:
case INVOKEVIRTUAL:
case INVOKESPECIAL:
return new InvokeInstruction(of(containingClass, name, desc, false), label);
case INVOKESTATIC:
return new InvokeInstruction(of(containingClass, name, desc, true), label);
default:
throw new IllegalArgumentException("Unexpected opcode " + opcode);
}
}
public static Instruction buildInvokeDynamic(final String className, final String name, final String desc, final Handle handle, final Label label) {
final MethodIdentifier actualIdentifier = of(handle.getOwner(), handle.getName(), handle.getDesc(), handle.getTag() == Opcodes.H_INVOKESTATIC);
final MethodIdentifier dynamicIdentifier = of(className, name, desc, true);
return new InvokeDynamicInstruction(actualIdentifier, dynamicIdentifier, label);
}
public static Instruction buildJumpInstruction(int opcode, final Label label) {
final String opcodeName = OPCODES[opcode];
switch (opcode) {
case IFEQ:
case IFNE:
case IFLT:
case IFGE:
case IFGT:
case IFLE:
case IFNULL:
case IFNONNULL:
return new SizeChangingInstruction(opcodeName, 0, 1, label);
case JSR:
return new SizeChangingInstruction(opcodeName, 1, 0, label);
case GOTO:
return new DefaultInstruction(opcodeName, label);
case IF_ICMPEQ:
case IF_ICMPNE:
case IF_ICMPLT:
case IF_ICMPGE:
case IF_ICMPGT:
case IF_ICMPLE:
case IF_ACMPEQ:
case IF_ACMPNE:
return new SizeChangingInstruction(opcodeName, 0, 2, label);
default:
throw new IllegalArgumentException("Unexpected opcode " + opcode);
}
}
public static Instruction buildIntInstruction(int opcode, int operand, final Label label) {
switch (opcode) {
case BIPUSH:
case SIPUSH:
return new PushInstruction(operand, PRIMITIVE_INT, label);
case NEWARRAY:
return new SizeChangingInstruction(OPCODES[NEWARRAY], 1, 1, label);
default:
throw new IllegalArgumentException("Unexpected opcode " + opcode);
}
}
private static Object getStaticValue(String name, String containingClass) {
final Field field;
try {
field = Class.forName(containingClass.replace('/', '.')).getDeclaredField(name);
field.setAccessible(true);
return field.get(null);
} catch (Exception e) {
LogProvider.error("Could not access static property, reason: " + e.getMessage());
LogProvider.debug(e);
return null;
}
}
}