code: package de.hub.se.jqf.bedivfuzz.examples.bcel;
  
  import com.pholser.junit.quickcheck.generator.GenerationStatus;
  import de.hub.se.jqf.bedivfuzz.junit.quickcheck.SplitGenerator;
  import de.hub.se.jqf.bedivfuzz.junit.quickcheck.SplitSourceOfRandomness;
  import org.apache.bcel.Const;
  import org.apache.bcel.classfile.Method;
  import org.apache.bcel.generic.*;
  import org.junit.AssumptionViolatedException;
  
  import java.util.function.Supplier;
  import java.util.stream.IntStream;
  import java.util.stream.Stream;
  
  public class SplitMethodGenerator {
      private static final int MIN_ARGUMENTS = 0;
      private static final int MAX_ARGUMENTS = 10;
      private static final int MAX_INSTRUCTIONS = 100;
      private static final int MIN_INSTRUCTIONS = 0;
      private static final int MAX_SWITCH_KEYS = 10;
      private final Supplier<String> classNameSupplier;
      private final Supplier<String> identifierSupplier;
      private final SplitTypeGenerator typeGenerator;
      private final SplitSourceOfRandomness random;
      private final ClassGen clazz;
  
      SplitMethodGenerator(SplitSourceOfRandomness random, GenerationStatus status, ClassGen clazz) {
          this.random = random;
          this.clazz = clazz;
          SplitGenerator<String> classNameGenerator = new SplitJavaClassNameGenerator();
          SplitGenerator<String> identifierGenerator = new SplitJavaIdentifierGenerator();
          this.typeGenerator = new SplitTypeGenerator(random, status);
          this.classNameSupplier = () -> classNameGenerator.generate(random, status);
          this.identifierSupplier = () -> identifierGenerator.generate(random, status);
      }
  
      public Method generate() {
          String methodName = identifierSupplier.get();
          Type returnType = random.structure.nextBoolean() ? Type.VOID : typeGenerator.generate();
          int numberOfArguments = random.structure.nextInt(MIN_ARGUMENTS, MAX_ARGUMENTS);
          Type[] argumentTypes = Stream.generate(typeGenerator::generate).limit(numberOfArguments).toArray(Type[]::new);
          String[] argumentNames = Stream.generate(identifierSupplier).limit(numberOfArguments).toArray(String[]::new);
          InstructionFactory factory = new InstructionFactory(clazz);
          InstructionList code = generateInstructions(factory, argumentTypes);
          short accessFlags = random.value.nextShort((short) 0, Short.MAX_VALUE);
          MethodGen method = new MethodGen(accessFlags,
                  returnType,
                  argumentTypes,
                  argumentNames,
                  methodName,
                  clazz.getClassName(),
                  code,
                  clazz.getConstantPool());
          return method.getMethod();
      }
  
      private InstructionList generateInstructions(InstructionFactory factory, Type[] argumentTypes) {
          InstructionList instructionList = new InstructionList();
          int numberOfInstructions = random.structure.nextInt(MIN_INSTRUCTIONS, MAX_INSTRUCTIONS);
          int maxLocals = random.structure.nextInt(argumentTypes.length + 1, 2 * argumentTypes.length + 1);
          while (instructionList.size() < numberOfInstructions) {
              Instruction ins = generateInstruction(factory, maxLocals, instructionList);
              if (ins instanceof BranchInstruction) {
                  instructionList.append((BranchInstruction) ins);
              } else {
                  instructionList.append(ins);
              }
          }
          return instructionList;
      }
  
      private Instruction generateInstruction(InstructionFactory factory, int maxLocals,
                                              InstructionList instructionList) {
          short opcode = random.structure.nextShort(Const.NOP, Const.BREAKPOINT);
          Instruction ins = InstructionConst.getInstruction(opcode);
          if (ins != null) {
              return ins; // Used predefined immutable object, if available
          }
          switch (opcode) {
              case Const.BIPUSH:
                  return factory.createConstant(random.value.nextByte(Byte.MIN_VALUE, Byte.MAX_VALUE));
              case Const.SIPUSH:
                  return factory.createConstant(random.value.nextShort(Short.MIN_VALUE, Short.MAX_VALUE));
              case Const.LDC:
              case Const.LDC_W:
                  return factory.createConstant("?");
              case Const.LDC2_W:
                  return factory.createConstant(random.value.nextDouble(Double.MIN_VALUE, Double.MAX_VALUE));
              case Const.ILOAD:
                  return new ILOAD(random.value.nextInt(maxLocals));
              case Const.LLOAD:
                  return new LLOAD(random.value.nextInt(maxLocals));
              case Const.FLOAD:
                  return new FLOAD(random.value.nextInt(maxLocals));
              case Const.DLOAD:
                  return new DLOAD(random.value.nextInt(maxLocals));
              case Const.ALOAD:
                  return new ALOAD(random.value.nextInt(maxLocals));
              case Const.ILOAD_0:
              case Const.ILOAD_1:
              case Const.ILOAD_2:
              case Const.ILOAD_3:
                  return new ILOAD(opcode - Const.ILOAD_0);
              case Const.LLOAD_0:
              case Const.LLOAD_1:
              case Const.LLOAD_2:
              case Const.LLOAD_3:
                  return new LLOAD(opcode - Const.LLOAD_0);
              case Const.FLOAD_0:
              case Const.FLOAD_1:
              case Const.FLOAD_2:
              case Const.FLOAD_3:
                  return new FLOAD(opcode - Const.FLOAD_0);
              case Const.DLOAD_0:
              case Const.DLOAD_1:
              case Const.DLOAD_2:
              case Const.DLOAD_3:
                  return new DLOAD(opcode - Const.DLOAD_0);
              case Const.ALOAD_0:
              case Const.ALOAD_1:
              case Const.ALOAD_2:
              case Const.ALOAD_3:
                  return new ALOAD(opcode - Const.ALOAD_0);
              case Const.ISTORE:
                  return new ISTORE(random.value.nextInt(maxLocals));
              case Const.LSTORE:
                  return new LSTORE(random.value.nextInt(maxLocals));
              case Const.FSTORE:
                  return new FSTORE(random.value.nextInt(maxLocals));
              case Const.DSTORE:
                  return new DSTORE(random.value.nextInt(maxLocals));
              case Const.ASTORE:
                  return new ASTORE(random.value.nextInt(maxLocals));
              case Const.ISTORE_0:
              case Const.ISTORE_1:
              case Const.ISTORE_2:
              case Const.ISTORE_3:
                  return new ISTORE(opcode - Const.ISTORE_0);
              case Const.LSTORE_0:
              case Const.LSTORE_1:
              case Const.LSTORE_2:
              case Const.LSTORE_3:
                  return new LSTORE(opcode - Const.LSTORE_0);
              case Const.FSTORE_0:
              case Const.FSTORE_1:
              case Const.FSTORE_2:
              case Const.FSTORE_3:
                  return new FSTORE(opcode - Const.FSTORE_0);
              case Const.DSTORE_0:
              case Const.DSTORE_1:
              case Const.DSTORE_2:
              case Const.DSTORE_3:
                  return new DSTORE(opcode - Const.DSTORE_0);
              case Const.ASTORE_0:
              case Const.ASTORE_1:
              case Const.ASTORE_2:
              case Const.ASTORE_3:
                  return new ASTORE(opcode - Const.ASTORE_0);
              case Const.IINC:
                  return new IINC(random.value.nextInt(maxLocals), random.value.nextInt(-128, 128));
              case Const.GETSTATIC:
              case Const.PUTSTATIC:
              case Const.GETFIELD:
              case Const.PUTFIELD:
                  return generateFieldAccess(factory, opcode);
              case Const.INVOKEVIRTUAL:
              case Const.INVOKESPECIAL:
              case Const.INVOKESTATIC:
              case Const.INVOKEINTERFACE:
              case Const.INVOKEDYNAMIC:
                  return generateInvoke(factory, opcode);
              case Const.NEW:
                  return factory.createNew(typeGenerator.generateObjectType());
              case Const.CHECKCAST:
                  return factory.createCast(typeGenerator.generateReferenceType(), typeGenerator.generateReferenceType());
              case Const.INSTANCEOF:
                  return factory.createInstanceOf(typeGenerator.generateReferenceType());
              case Const.NEWARRAY:
              case Const.ANEWARRAY:
              case Const.MULTIANEWARRAY:
                  return factory.createNewArray(typeGenerator.generate(), random.value.nextShort((short) 1, (short) 20));
              case Const.IFEQ:
              case Const.IFNE:
              case Const.IFLT:
              case Const.IFGE:
              case Const.IFGT:
              case Const.IFLE:
              case Const.IF_ICMPEQ:
              case Const.IF_ICMPNE:
              case Const.IF_ICMPLT:
              case Const.IF_ICMPGE:
              case Const.IF_ICMPGT:
              case Const.IF_ICMPLE:
              case Const.IF_ACMPEQ:
              case Const.IF_ACMPNE:
              case Const.GOTO:
              case Const.JSR:
              case Const.IFNULL:
              case Const.IFNONNULL:
              case Const.GOTO_W:
              case Const.JSR_W:
                  return InstructionFactory.createBranchInstruction(opcode, chooseTarget(instructionList));
              case Const.RET:
                  return new RET(random.value.nextInt(maxLocals));
              case Const.TABLESWITCH:
              case Const.LOOKUPSWITCH:
                  return generateSwitch(opcode, instructionList);
              case Const.WIDE:
              case Const.BREAKPOINT:
                  return new BREAKPOINT();
              default:
                  throw new AssumptionViolatedException("Invalid opcode");
          }
      }
  
      private InvokeInstruction generateInvoke(InstructionFactory factory, short opcode) {
          String className = classNameSupplier.get();
          String name = identifierSupplier.get();
          Type returnType = random.value.nextBoolean() ? Type.VOID : typeGenerator.generate();
          int numberOfArguments = random.value.nextInt(MIN_ARGUMENTS, MAX_ARGUMENTS);
          Type[] argumentTypes = Stream.generate(typeGenerator::generate).limit(numberOfArguments).toArray(Type[]::new);
          return factory.createInvoke(className, name, returnType, argumentTypes, opcode);
      }
  
      private InstructionHandle chooseTarget(InstructionList instructionList) {
          InstructionHandle[] handles = instructionList.getInstructionHandles();
          // If no instructions generated so far, emit a NOP to get some label
          if (handles.length == 0) {
              handles = new InstructionHandle[]{instructionList.append(new NOP())};
          }
          return random.value.choose(handles);
      }
  
      private Select generateSwitch(int opcode, InstructionList instructionList) {
          int[] matches = IntStream.generate(random.structure::nextInt)
                  .limit(random.structure.nextInt(0, MAX_SWITCH_KEYS))
                  .distinct()
                  .sorted()
                  .toArray();
          InstructionHandle[] targets = Stream.generate(() -> chooseTarget(instructionList))
                  .limit(matches.length)
                  .toArray(InstructionHandle[]::new);
          InstructionHandle defaultTarget = chooseTarget(instructionList);
          return opcode == Const.TABLESWITCH ? new TABLESWITCH(matches, targets, defaultTarget) :
                  new LOOKUPSWITCH(matches, targets, defaultTarget);
      }
  
      private FieldInstruction generateFieldAccess(InstructionFactory factory, short opcode) {
          return factory.createFieldAccess(classNameSupplier.get(),
                  identifierSupplier.get(),
                  typeGenerator.generate(),
                  opcode);
      }
  }
  