code: protected void joinInputs(DiGraphNode<N, Branch> node) {
      FlowState<L> state = node.getAnnotation();
      if (isForward()) {
        if (cfg.getEntry() == node) {
          state.setIn(createEntryLattice());
        } else {
          List<DiGraphNode<N, Branch>> inNodes = cfg.getDirectedPredNodes(node);
          if (inNodes.size() == 1) {
            FlowState<L> inNodeState = inNodes.get(0).getAnnotation();
            state.setIn(inNodeState.getOut());
          } else if (inNodes.size() > 1) {
            List<L> values = new ArrayList<>(inNodes.size());
            for (DiGraphNode<N, Branch> currentNode : inNodes) {
              FlowState<L> currentNodeState = currentNode.getAnnotation();
              values.add(currentNodeState.getOut());
            }
            state.setIn(joinOp.apply(values));
          }
        }
      } else {
        List<DiGraphNode<N, Branch>> inNodes = cfg.getDirectedSuccNodes(node);
        if (inNodes.size() == 1) {
          DiGraphNode<N, Branch> inNode = inNodes.get(0);
          if (inNode == cfg.getImplicitReturn()) {
            state.setOut(createEntryLattice());
          } else {
            FlowState<L> inNodeState = inNode.getAnnotation();
            state.setOut(inNodeState.getIn());
          }
        } else if (inNodes.size() > 1) {
          List<L> values = new ArrayList<>(inNodes.size());
          for (DiGraphNode<N, Branch> currentNode : inNodes) {
            FlowState<L> currentNodeState = currentNode.getAnnotation();
            values.add(currentNodeState.getIn());
          }
          state.setOut(joinOp.apply(values));
        }
      }
    }
docs: Computes the new flow state at a given node's entry by merging the
  output (input) lattice of the node's predecessor (successor).
  
  @param node Node to compute new join.