StandardDataFlowInterpreter (@NotNull DfaListener listener, @NotNull ControlFlow flow) { return new StandardDataFlowInterpreter(flow, listener, true) { @Override protected void beforeInstruction(Instruction instruction) { afterStates.clear(); killedStates.clear(); } @Override protected DfaInstructionState @NotNull [] acceptInstruction(@NotNull DfaInstructionState instructionState) { Instruction instruction = instructionState.getInstruction(); TrackingDfaMemoryState memState = (TrackingDfaMemoryState)instructionState.getMemoryState().createCopy(); DfaInstructionState[] states = super.acceptInstruction(instructionState); for (DfaInstructionState state : states) { afterStates.add(state); ((TrackingDfaMemoryState)state.getMemoryState()).recordChange(instruction, memState); } if (states.length == 0) { killedStates.add(memState); } if (instruction instanceof ExpressionPushingInstruction pushing && pushing.getDfaAnchor() instanceof JavaExpressionAnchor anchor && anchor.getExpression() == myExpression) { for (DfaInstructionState state : states) { MemoryStateChange history = ((TrackingDfaMemoryState)state.getMemoryState()).getHistory(); myHistoryForContext = myHistoryForContext == null ? history : myHistoryForContext.merge(history); } } return states; } @Override protected void afterInstruction(Instruction instruction) { if (afterStates.size() <= 1 && killedStates.isEmpty()) return; Map<Instruction, List<TrackingDfaMemoryState>> instructionToState = StreamEx.of(afterStates).mapToEntry(s -> s.getInstruction(), s -> (TrackingDfaMemoryState)s.getMemoryState()).grouping(); if (instructionToState.size() <= 1 && killedStates.isEmpty()) return; instructionToState.forEach((target, memStates) -> { List<TrackingDfaMemoryState> bridgeChanges = StreamEx.of(afterStates).filter(s -> s.getInstruction() != target) .map(s -> ((TrackingDfaMemoryState)s.getMemoryState())) .append(killedStates) .toList(); for (TrackingDfaMemoryState state : memStates) { state.addBridge(instruction, bridgeChanges); } }); } }; }