code: private static byte[] dumpBytecode(MethodNode methodNode) {
          // Calculate label offsets -- required for hash calculation
            // we only care about where the labels are in relation to the opcode instructions -- we don't care about things like
            // LocalVariableNode or other ancillary data because these can change without the actual logic changing
          List<AbstractInsnNode> onlyInstructionsAndLabels = Arrays.stream(methodNode.instructions.toArray())
                  .filter(x -> x instanceof LabelNode || x.getOpcode() != -1)
                  .collect(Collectors.toList());
          Map<Label, Integer> labelOffsets = onlyInstructionsAndLabels.stream()
                  .filter(x -> x instanceof LabelNode)
                  .map(x -> (LabelNode) x)
                  .collect(Collectors.toMap(x -> x.getLabel(), x -> onlyInstructionsAndLabels.indexOf(x)));
  
  
          // Hash based on overall structures and instructions+operands
          try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
                  DataOutputStream daos = new DataOutputStream(baos);) {
              MethodVisitor daosDumpMethodVisitor = new DumpToDaosMethodVisitor(daos, labelOffsets);
  
              methodNode.accept(daosDumpMethodVisitor);
              daos.flush(); // doesn't really need it -- just incase
              
              return baos.toByteArray();
          } catch (IOException ioe) {
              throw new IllegalStateException(ioe); // should never happen
          }
      }
docs: Takes into account the instructions and operands, as well as the overall structure.