private int upgrade(int target, @Nonnull DoubleConsumer sink) {
		int classTarget = JavaVersion.VERSION_OFFSET + target;

		List<JvmClassInfo> targets = targetBundle.stream()
				.filter(c -> c.getVersion() < classTarget)
				.collect(Collectors.toCollection(ArrayList::new));
		int total = targets.size();
		AtomicDouble remaining = new AtomicDouble(target);

		ExecutorService service = ThreadPoolFactory.newFixedThreadPool("class-upgrade");
		for (JvmClassInfo classInfo : targets) {
			CompletableFuture.supplyAsync(() -> {
				try {
					int baseClassVersion = classInfo.getVersion();
					JvmClassInfo result;
					if (baseClassVersion <= JavaVersion.VERSION_OFFSET + 7 && !classInfo.hasInterfaceModifier()) {
						// We need to use ASM's JSRInlinerAdapter to ensure JSR/RET instructions are removed from
						// the updated copy of the class file.
						ClassReader reader = classInfo.getClassReader();
						ClassWriter writer = new ClassWriter(reader, 0);
						reader.accept(new ClassVisitor(RecafConstants.getAsmVersion(), writer) {
							@Override
							public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
								super.visit(classTarget, access, name, signature, superName, interfaces);
							}

							@Override
							public MethodVisitor visitMethod(int access, String name, String descriptor,
															 String signature, String[] exceptions) {
								MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
								return new JSRInlinerAdapter(mv, access, name, descriptor, signature, exceptions);
							}
						}, 0);
						result = new JvmClassInfoBuilder(writer.toByteArray()).build();
					} else {
						// No special handling needed, just update the version value.
						byte[] original = classInfo.getBytecode();
						byte[] copy = Arrays.copyOf(original, original.length);
						copy[7] = (byte) classTarget;
						result = classInfo.toJvmClassBuilder().withBytecode(copy).withVersion(classTarget).build();
					}
					sink.accept(1 - (remaining.addAndGet(-1) / total));
					return result;
				} catch (Exception ex) {
					logger.error("Failed upgrading class '{}'", classInfo.getName(), ex);
					return null;
				} finally {
					sink.accept(1 - (remaining.addAndGet(-1) / total));
				}
			}, service).whenComplete((updated, error) -> {
				if (updated != null)
					targetBundle.put(updated);
			});
		}
		try {
			service.shutdown();
			service.awaitTermination(1, TimeUnit.HOURS);
		} catch (InterruptedException ex) {
			logger.info("Upgrade interrupted", ex);
		}
		return total;
	}
Popup for initiating the upgrading or downgrading of all classes.

@author Matt Coley
recaf-ui/src/main/java/software/coley/recaf/ui/control/popup/ChangeClassVersionForAllPopup.java