Program: 36399
Project: 36399
Buggy source:
/** 
 * If types have changed, we can have failed to update vars. For example var x = 17; //x is int x = "apa";  //x is object. This will be converted fine
 * @param functionNode
 */
private FunctionNode finalizeTypes(final FunctionNode functionNode){
  final Set<Node> changed=new HashSet<>();
  FunctionNode currentFunctionNode=functionNode;
  do {
    changed.clear();
    final FunctionNode newFunctionNode=(FunctionNode)currentFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()){
      private Expression widen(      final Expression node,      final Type to){
        if (node instanceof LiteralNode) {
          return node;
        }
        Type from=node.getType();
        if (!Type.areEquivalent(from,to) && Type.widest(from,to) == to) {
          LOG.fine("String_Node_Str",node,"String_Node_Str",Debug.id(node),"String_Node_Str",node.getType(),"String_Node_Str",to);
          Symbol symbol=node.getSymbol();
          if (symbol.isShared() && symbol.wouldChangeType(to)) {
            symbol=temporarySymbols.getTypedTemporarySymbol(to);
          }
          newType(symbol,to);
          final Expression newNode=node.setSymbol(lc,symbol);
          changed.add(newNode);
          return newNode;
        }
        return node;
      }
      @Override public boolean enterFunctionNode(      final FunctionNode node){
        return !node.isLazy();
      }
      /** 
 * Eg. var d = 17; var e; e = d; //initially typed as int for node type, should retype as double e = object; var d = 17; var e; e -= d; //initially type number, should number remain with a final conversion supplied by Store. ugly, but the computation result of the sub is numeric e = object;
 */
      @SuppressWarnings("String_Node_Str") @Override public Node leaveBinaryNode(      final BinaryNode binaryNode){
        final Type widest=Type.widest(binaryNode.lhs().getType(),binaryNode.rhs().getType());
        BinaryNode newBinaryNode=binaryNode;
switch (binaryNode.tokenType()) {
default :
          if (!binaryNode.isAssignment() || binaryNode.isSelfModifying()) {
            break;
          }
        newBinaryNode=newBinaryNode.setLHS(widen(newBinaryNode.lhs(),widest));
case ADD:
      newBinaryNode=(BinaryNode)widen(newBinaryNode,widest);
  }
  return newBinaryNode;
}
}
);
lc.replace(currentFunctionNode,newFunctionNode);
currentFunctionNode=newFunctionNode;
}
 while (!changed.isEmpty());
return currentFunctionNode;
}

Fixed version (ground truth):
/** 
 * If types have changed, we can have failed to update vars. For example var x = 17; //x is int x = "apa";  //x is object. This will be converted fine
 * @param functionNode
 */
private FunctionNode finalizeTypes(final FunctionNode functionNode){
  final Set<Node> changed=new HashSet<>();
  FunctionNode currentFunctionNode=functionNode;
  do {
    changed.clear();
    final FunctionNode newFunctionNode=(FunctionNode)currentFunctionNode.accept(new NodeVisitor<LexicalContext>(new LexicalContext()){
      private Expression widen(      final Expression node,      final Type to){
        if (node instanceof LiteralNode) {
          return node;
        }
        Type from=node.getType();
        if (!Type.areEquivalent(from,to) && Type.widest(from,to) == to) {
          LOG.fine("String_Node_Str",node,"String_Node_Str",Debug.id(node),"String_Node_Str",node.getType(),"String_Node_Str",to);
          Symbol symbol=node.getSymbol();
          if (symbol.isShared() && symbol.wouldChangeType(to)) {
            symbol=temporarySymbols.getTypedTemporarySymbol(to);
          }
          newType(symbol,to);
          final Expression newNode=node.setSymbol(lc,symbol);
          changed.add(newNode);
          return newNode;
        }
        return node;
      }
      @Override public boolean enterFunctionNode(      final FunctionNode node){
        return !node.isLazy();
      }
      @SuppressWarnings("String_Node_Str") @Override public Node leaveBinaryNode(      final BinaryNode binaryNode){
        final Type widest=Type.widest(binaryNode.lhs().getType(),binaryNode.rhs().getType());
        BinaryNode newBinaryNode=binaryNode;
        if (isAdd(binaryNode)) {
          newBinaryNode=(BinaryNode)widen(newBinaryNode,widest);
          if (newBinaryNode.getType().isObject() && !isAddString(newBinaryNode)) {
            return new RuntimeNode(newBinaryNode,Request.ADD);
          }
        }
 else         if (binaryNode.isComparison()) {
          final Expression lhs=newBinaryNode.lhs();
          final Expression rhs=newBinaryNode.rhs();
          Type cmpWidest=Type.widest(lhs.getType(),rhs.getType());
          boolean newRuntimeNode=false, finalized=false;
switch (newBinaryNode.tokenType()) {
case EQ_STRICT:
case NE_STRICT:
            if (lhs.getType().isBoolean() != rhs.getType().isBoolean()) {
              newRuntimeNode=true;
              cmpWidest=Type.OBJECT;
              finalized=true;
            }
default :
          if (newRuntimeNode || cmpWidest.isObject()) {
            return new RuntimeNode(newBinaryNode,Request.requestFor(binaryNode)).setIsFinal(finalized);
          }
        break;
    }
    return newBinaryNode;
  }
 else {
    if (!binaryNode.isAssignment() || binaryNode.isSelfModifying()) {
      return newBinaryNode;
    }
    checkThisAssignment(binaryNode);
    newBinaryNode=newBinaryNode.setLHS(widen(newBinaryNode.lhs(),widest));
    newBinaryNode=(BinaryNode)widen(newBinaryNode,widest);
  }
  return newBinaryNode;
}
private boolean isAdd(final Node node){
  return node.isTokenType(TokenType.ADD);
}
/** 
 * Determine if the outcome of + operator is a string.
 * @param node  Node to test.
 * @return true if a string result.
 */
private boolean isAddString(final Node node){
  if (node instanceof BinaryNode && isAdd(node)) {
    final BinaryNode binaryNode=(BinaryNode)node;
    final Node lhs=binaryNode.lhs();
    final Node rhs=binaryNode.rhs();
    return isAddString(lhs) || isAddString(rhs);
  }
  return node instanceof LiteralNode<?> && ((LiteralNode<?>)node).isString();
}
private void checkThisAssignment(final BinaryNode binaryNode){
  if (binaryNode.isAssignment()) {
    if (binaryNode.lhs() instanceof AccessNode) {
      final AccessNode accessNode=(AccessNode)binaryNode.lhs();
      if (accessNode.getBase().getSymbol().isThis()) {
        lc.getCurrentFunction().addThisProperty(accessNode.getProperty().getName());
      }
    }
  }
}
}
);
lc.replace(currentFunctionNode,newFunctionNode);
currentFunctionNode=newFunctionNode;
}
 while (!changed.isEmpty());
return currentFunctionNode;
}