package com.sebastian_daschner.jaxrs_analyzer.analysis.classes; import com.sebastian_daschner.jaxrs_analyzer.model.Types; import com.sebastian_daschner.jaxrs_analyzer.model.instructions.*; import com.sebastian_daschner.jaxrs_analyzer.model.results.MethodResult; import org.objectweb.asm.Handle; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; import java.util.*; import java.util.stream.Stream; import static com.sebastian_daschner.jaxrs_analyzer.analysis.bytecode.collection.InstructionBuilder.*; import static com.sebastian_daschner.jaxrs_analyzer.model.instructions.Instruction.InstructionType.LOAD_PLACEHOLDER; import static com.sebastian_daschner.jaxrs_analyzer.model.instructions.Instruction.InstructionType.STORE_PLACEHOLDER; import static org.objectweb.asm.Opcodes.*; import static org.objectweb.asm.util.Printer.OPCODES; /** * @author Sebastian Daschner */ class ProjectMethodVisitor extends MethodVisitor { private final Set<Label> exceptionHandlers = new HashSet<>(); private final List<Label> visitedLabels = new ArrayList<>(); final MethodResult methodResult; private final String className; ProjectMethodVisitor(MethodResult methodResult, String className) { super(ASM5); // TODO refactor to list of instructions only this.methodResult = methodResult; this.className = className; } @Override public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { exceptionHandlers.add(handler); } @Override public void visitLabel(Label label) { visitedLabels.add(label); if (exceptionHandlers.remove(label)) methodResult.getInstructions().add(new ExceptionHandlerInstruction(label)); } @Override public void visitMaxs(final int maxStack, final int maxLocals) { super.visitMaxs(maxStack, maxLocals); } @Override public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { // resolve LOAD & STORE placeholders ListIterator<Instruction> iterator = methodResult.getInstructions().listIterator(); while (iterator.hasNext()) { final Instruction instruction = iterator.next(); if (instruction.getType() != LOAD_PLACEHOLDER && instruction.getType() != STORE_PLACEHOLDER) continue; final LoadStoreInstructionPlaceholder placeholder = (LoadStoreInstructionPlaceholder) instruction; if (placeholder.getNumber() != index) continue; final Label label = placeholder.getLabel(); if (isLabelActive(label, start, end)) { final String type = signature != null ? signature : desc; iterator.set(placeholder.getType() == LOAD_PLACEHOLDER ? new LoadInstruction(index, type, name, label, end) : new StoreInstruction(index, type, name, label)); } } } private boolean isLabelActive(final Label label, final Label start, final Label end) { boolean startVisited = false; for (final Label current : visitedLabels) { if (current == start) startVisited = true; if (current == label) return startVisited; if (current == end) return false; } return false; } @Override public void visitInsn(int opcode) { methodResult.getInstructions().add(buildInstruction(opcode, getLastLabel())); } @Override public void visitIntInsn(int opcode, int operand) { methodResult.getInstructions().add(buildIntInstruction(opcode, operand, getLastLabel())); } @Override public void visitVarInsn(int opcode, int index) { final Label label = !visitedLabels.isEmpty() ? visitedLabels.get(visitedLabels.size() - 1) : null; methodResult.getInstructions().add(buildLoadStoreInstruction(opcode, index, label)); } @Override public void visitTypeInsn(int opcode, String className) { methodResult.getInstructions().add(buildTypeInstruction(opcode, className, getLastLabel())); } @Override public void visitFieldInsn(int opcode, String owner, String name, String desc) { methodResult.getInstructions().add(buildFieldInstruction(opcode, owner, name, desc, getLastLabel())); } @Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { methodResult.getInstructions().add(buildInvokeInstruction(opcode, owner, name, desc, getLastLabel())); } @Override public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) { final Handle handle = Stream.of(bsmArgs).filter(a -> a instanceof Handle).map(a -> (Handle) a) .findAny().orElseThrow(() -> new IllegalStateException("No invoke dynamic handle found.")); methodResult.getInstructions().add(buildInvokeDynamic(className, name, desc, handle, getLastLabel())); } @Override public void visitJumpInsn(int opcode, Label label) { methodResult.getInstructions().add(buildJumpInstruction(opcode, label)); } @Override public void visitLdcInsn(Object object) { methodResult.getInstructions().add(createLdcInstruction(object, getLastLabel())); } private Label getLastLabel() { if (visitedLabels.isEmpty()) return null; return visitedLabels.get(visitedLabels.size() - 1); } private PushInstruction createLdcInstruction(final Object object, final Label label) { // see MethodVisitor if (object instanceof Integer) { return new PushInstruction(object, Types.INTEGER, label); } if (object instanceof Float) { return new PushInstruction(object, Types.FLOAT, label); } if (object instanceof Long) { return new PushInstruction(object, Types.LONG, label); } if (object instanceof Double) { return new PushInstruction(object, Types.DOUBLE, label); } if (object instanceof String) { return new PushInstruction(object, Types.STRING, label); } if (object instanceof Type) { return new PushInstruction(((Type) object).getDescriptor(), Types.CLASS, label); } return new PushInstruction(object, Type.getDescriptor(object.getClass()), label); } @Override public void visitIincInsn(int var, int increment) { methodResult.getInstructions().add(new DefaultInstruction(OPCODES[IINC], getLastLabel())); } @Override public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) { methodResult.getInstructions().add(new SizeChangingInstruction(OPCODES[TABLESWITCH], 0, 1, getLastLabel())); } @Override public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { methodResult.getInstructions().add(new SizeChangingInstruction(OPCODES[LOOKUPSWITCH], 0, 1, getLastLabel())); } @Override public void visitMultiANewArrayInsn(String desc, int dimensions) { methodResult.getInstructions().add(new SizeChangingInstruction(OPCODES[MULTIANEWARRAY], 1, dimensions, getLastLabel())); } @Override public void visitEnd() { // resolve unresolved placeholders final ListIterator<Instruction> listIterator = methodResult.getInstructions().listIterator(); while (listIterator.hasNext()) { final Instruction instruction = listIterator.next(); if (instruction.getType() == LOAD_PLACEHOLDER) { listIterator.set(new LoadInstruction(((LoadStoreInstructionPlaceholder) instruction).getNumber(), Types.OBJECT, instruction.getLabel(), null)); } else if (instruction.getType() == STORE_PLACEHOLDER) { listIterator.set(new StoreInstruction(((LoadStoreInstructionPlaceholder) instruction).getNumber(), Types.OBJECT, instruction.getLabel())); } } } }