@Override
	public void transform(@Nonnull JvmTransformerContext context, @Nonnull Workspace workspace,
	                      @Nonnull WorkspaceResource resource, @Nonnull JvmClassBundle bundle,
	                      @Nonnull JvmClassInfo initialClassState) throws TransformationException {
		boolean isTransformed = false;
		ClassNode node = context.getNode(bundle, initialClassState);
		for (MethodNode method : node.methods) {
			InsnList instructions = method.instructions;
			if (instructions == null) continue;
			if (method.tryCatchBlocks == null || method.tryCatchBlocks.size() <= 1) continue;

			// Build model of catch block contents.
			Set<AbstractInsnNode> visitedHandlers = Collections.newSetFromMap(new IdentityHashMap<>());
			Map<TryCatchBlockNode, CodeBlock> catchBlocks = new IdentityHashMap<>();
			for (TryCatchBlockNode tryCatchBlock : method.tryCatchBlocks) {
				List<AbstractInsnNode> catchBlockInsns = new ArrayList<>();
				AbstractInsnNode insn = tryCatchBlock.handler;

				// Skip if we've already visited this block.
				if (!visitedHandlers.add(insn))
					continue;

				// Build block contents.
				while (insn != null) {
					// Only include non-metadata/filler instructions.
					if (insn.getType() != AbstractInsnNode.FRAME && insn.getOpcode() != NOP)
						catchBlockInsns.add(insn);

					// Abort when we encounter an exit or control flow.
					if (isReturn(insn) || isFlowControl(insn))
						break;

					insn = insn.getNext();
				}

				// Skip if the block is too small.
				if (catchBlockInsns.size() < MIN_BLOCK_THRESHOLD)
					continue;

				// Skip if there is control flow pointing to a contained label.
				if (hasExternalFlowIntoCatchBlock(method, catchBlockInsns))
					continue;

				catchBlocks.put(tryCatchBlock, new CodeBlock(catchBlockInsns));
			}

			// Sort the blocks by their position in the method code, and then redirect the control flow of earlier
			// catch blocks to the last catch block handler with equivalent code.
			List<CodeBlock> blocks = catchBlocks.values().stream()
					.sorted()
					.toList();
			for (int i = 0; i < blocks.size() - 1; i++) {
				CodeBlock block = blocks.get(i);
				for (int j = blocks.size() - 1; j > i; j--) {
					CodeBlock laterBlock = blocks.get(j);
					if (block.equals(laterBlock)) {
						block.pruneContent(instructions);
						block.redirectTo(instructions, (LabelNode) laterBlock.instructions.getFirst());
						isTransformed = true;
						break;
					}
				}
			}
		}

		if (isTransformed) {
			context.setRecomputeFrames(initialClassState.getName());
			context.setNode(bundle, initialClassState, node);
		}
	}
Allows us to skip blocks that are too simple to bother merging.
For instance:
<ul>
    <li>{@code { throw e; }}</li>
    <li>{@code { e.printStacktrace(); }}</li>
    <li>{@code { no-op }}</li>
</ul>
recaf-core/src/main/java/software/coley/recaf/services/deobfuscation/transform/generic/DuplicateCatchMergingTransformer.java