@Override
	public void transform(@Nonnull JvmTransformerContext context, @Nonnull Workspace workspace,
	                      @Nonnull WorkspaceResource resource, @Nonnull JvmClassBundle bundle,
	                      @Nonnull JvmClassInfo initialClassState) throws TransformationException {
		boolean dirty = false;
		String className = initialClassState.getName();
		ClassNode node = context.getNode(bundle, initialClassState);
		for (MethodNode method : node.methods) {
			InsnList instructions = method.instructions;
			if (instructions == null)
				continue;
			try {
				boolean localDirty = false;
				Frame<ReValue>[] frames = context.analyze(inheritanceGraph, node, method);
				for (int i = 1; i < method.instructions.size() - 1; i++) {
					// We must know the contents of the next frame, and it must have 1 or more values on the stack.
					Frame<ReValue> nextFrame = frames[i + 1];
					if (nextFrame == null || nextFrame.getStackSize() == 0)
						continue;

					// The next frame's stack top is the result of the operation.
					ReValue nextFrameStackTop = nextFrame.getStack(nextFrame.getStackSize() - 1);
					if (!nextFrameStackTop.hasKnownValue())
						continue;

					// Handle folding for specific instructions.
					AbstractInsnNode instruction = method.instructions.get(i);
					int opcode = instruction.getOpcode();
					switch (opcode) {
						case IADD:
						case FADD:
						case ISUB:
						case FSUB:
						case IMUL:
						case FMUL:
						case IDIV:
						case FDIV:
						case IREM:
						case FREM:
						case ISHL:
						case ISHR:
						case IUSHR:
						case IAND:
						case IXOR:
						case IOR:
						case DREM:
						case DDIV:
						case DMUL:
						case DSUB:
						case DADD:
						case LUSHR:
						case LSHR:
						case LSHL:
						case LREM:
						case LDIV:
						case LMUL:
						case LSUB:
						case LADD:
						case LAND:
						case LOR:
						case LXOR:
						case FCMPL:
						case FCMPG:
						case LCMP:
						case DCMPL:
						case DCMPG: {
							// Get instruction of the top stack's contributing instruction.
							AbstractInsnNode argument2 = method.instructions.get(i - 1);
							while (argument2 != null && argument2.getOpcode() == NOP)
								argument2 = argument2.getPrevious();
							if (argument2 == null)
								continue;

							// Get instruction of the 2nd-to-top stack's contributing instruction.
							AbstractInsnNode argument1 = argument2.getPrevious();
							while (argument1 != null && argument1.getOpcode() == NOP)
								argument1 = argument1.getPrevious();
							if (argument1 == null)
								continue;

							// Both argument instructions must be value producers.
							if (!isSupportedValueProducer(argument1) || !isSupportedValueProducer(argument2))
								continue;

							// We must have a viable replacement to offer.
							AbstractInsnNode replacement = toInsn(nextFrameStackTop);
							if (replacement == null)
								continue;

							// Replace the arguments and operation instructions with the replacement const value.
							method.instructions.set(instruction, replacement);
							method.instructions.set(argument2, new InsnNode(NOP));
							method.instructions.set(argument1, new InsnNode(NOP));
							localDirty = true;
							break;
						}
						case INEG:
						case FNEG:
						case DNEG:
						case LNEG:
						case I2L:
						case I2F:
						case I2D:
						case F2I:
						case F2L:
						case F2D:
						case I2B:
						case I2C:
						case I2S:
						case D2I:
						case D2L:
						case D2F:
						case L2I:
						case L2F:
						case L2D: {
							// Get instruction of the top stack's contributing instruction.
							// It must also be a value producing instruction.
							AbstractInsnNode argument = method.instructions.get(i - 1);
							while (argument != null && argument.getOpcode() == NOP)
								argument = argument.getPrevious();
							if (argument == null || !isSupportedValueProducer(argument))
								continue;

							// We must have a viable replacement to offer.
							AbstractInsnNode replacement = toInsn(nextFrameStackTop);
							if (replacement == null)
								continue;

							// Replace the argument and operation instructions with the replacement const value.
							method.instructions.set(instruction, replacement);
							method.instructions.set(argument, new InsnNode(NOP));
							localDirty = true;
							break;
						}
						case INVOKESPECIAL:
						case INVOKEINTERFACE:
						case INVOKEVIRTUAL:
						case INVOKESTATIC: {
							// We'll have some loops further below that will set this flag to indicate to skip this
							// instruction after the loop completes.
							boolean skip = false;

							// Get the contributing instructions.
							MethodInsnNode min = (MethodInsnNode) instruction;
							Type methodType = Type.getMethodType(min.desc);
							List<AbstractInsnNode> argumentInstructions = new ArrayList<>(methodType.getArgumentCount());
							int start = opcode == INVOKESTATIC ? 1 : 0; // non-static methods start at zero to include the method instance host.
							for (int arg = methodType.getArgumentCount(); arg >= start; arg--) {
								// Get the contributing instruction for this argument (or method instance host for non-static methods when arg == 0)
								AbstractInsnNode lastArgumentInsn = argumentInstructions.isEmpty() ? null : argumentInstructions.getLast();
								AbstractInsnNode argument = lastArgumentInsn == null ?
										method.instructions.get(i - 1) : lastArgumentInsn.getPrevious();

								// Argument must be a value producing instruction.
								while (argument != null && argument.getOpcode() == NOP)
									argument = argument.getPrevious();
								if (argument == null || !isSupportedValueProducer(argument)) {
									skip = true;
									break;
								}
								argumentInstructions.add(argument);
							}
							if (skip)
								continue;

							// We must have a viable replacement to offer.
							AbstractInsnNode replacement = toInsn(nextFrameStackTop);
							if (replacement == null)
								continue;

							// Replace the arguments and invoke instructions with the replacement const value.
							method.instructions.set(instruction, replacement);
							for (AbstractInsnNode argument : argumentInstructions)
								method.instructions.set(argument, new InsnNode(NOP));
							localDirty = true;
							break;
						}
						case GETSTATIC:
						case GETFIELD: {
							// We'll have some loops further below that will set this flag to indicate to skip this
							// instruction after the loop completes.
							boolean skip = false;

							// Get the contributing instructions.
							FieldInsnNode fin = (FieldInsnNode) instruction;
							if (opcode == GETFIELD) {
								// Get instruction of the top stack's contributing instruction.
								AbstractInsnNode argumentValue = method.instructions.get(i - 1);
								while (argumentValue != null && argumentValue.getOpcode() == NOP)
									argumentValue = argumentValue.getPrevious();
								if (argumentValue == null)
									continue;

								// Get instruction of the 2nd-to-top stack's contributing instruction.
								AbstractInsnNode argumentContext = argumentValue.getPrevious();
								while (argumentContext != null && argumentContext.getOpcode() == NOP)
									argumentContext = argumentContext.getPrevious();
								if (argumentContext == null)
									continue;

								// Both argument instructions must be value producers.
								if (!isSupportedValueProducer(argumentValue) || !isSupportedValueProducer(argumentContext))
									continue;

								// We must have a viable replacement to offer.
								AbstractInsnNode replacement = toInsn(nextFrameStackTop);
								if (replacement == null)
									continue;

								// Replace the arguments and field instructions with the replacement const value.
								method.instructions.set(instruction, replacement);
								method.instructions.set(argumentValue, new InsnNode(NOP));
								method.instructions.set(argumentContext, new InsnNode(NOP));
								localDirty = true;
							} else {
								// Get instruction of the top stack's contributing instruction.
								// It must also be a value producing instruction.
								AbstractInsnNode argumentValue = method.instructions.get(i - 1);
								while (argumentValue != null && argumentValue.getOpcode() == NOP)
									argumentValue = argumentValue.getPrevious();
								if (argumentValue == null || !isSupportedValueProducer(argumentValue))
									continue;

								// We must have a viable replacement to offer.
								AbstractInsnNode replacement = toInsn(nextFrameStackTop);
								if (replacement == null)
									continue;

								// Replace the argument and field instructions with the replacement const value.
								method.instructions.set(instruction, replacement);
								method.instructions.set(argumentValue, new InsnNode(NOP));
								localDirty = true;
								break;
							}
							break;
						}
					}
				}

				// We replace instructions with NOP in the code above because it reduces the headache of managing
				// the proper index of instructions. Now that we are done, we'll prune any NOP instructions.
				if (localDirty) {
					dirty = true;
					for (AbstractInsnNode insn : method.instructions.toArray()) {
						if (insn.getOpcode() == NOP)
							method.instructions.remove(insn);
					}
				}
			} catch (Throwable t) {
				throw new TransformationException("Error encountered when folding constants", t);
			}
		}
		if (dirty)
			context.setNode(bundle, initialClassState, node);
	}
A transformer that folds <i>basic</i> linear constant usages.

@author Matt Coley
recaf-core/src/main/java/software/coley/recaf/services/deobfuscation/transform/generic/LinearOpaqueConstantFoldingTransformer.java