docs: private static int findExpressionType(OptFunctionNode fn, Node n,
                                            int[] varTypes)
      {
          switch (n.getType()) {
              case Token.NUMBER:
                  return Optimizer.NumberType;
  
              case Token.CALL:
              case Token.NEW:
              case Token.REF_CALL:
                  return Optimizer.AnyType;
  
              case Token.GETELEM:
              case Token.GETPROP:
              case Token.NAME:
              case Token.THIS:
                  return Optimizer.AnyType;
  
              case Token.GETVAR:
                  return varTypes[fn.getVarIndex(n)];
  
              case Token.INC:
              case Token.DEC:
              case Token.MUL:
              case Token.DIV:
              case Token.MOD:
              case Token.BITOR:
              case Token.BITXOR:
              case Token.BITAND:
              case Token.BITNOT:
              case Token.LSH:
              case Token.RSH:
              case Token.URSH:
              case Token.SUB:
              case Token.POS:
              case Token.NEG:
                  return Optimizer.NumberType;
  
              case Token.VOID:
                  // NYI: undefined type
                  return Optimizer.AnyType;
  
              case Token.FALSE:
              case Token.TRUE:
              case Token.EQ:
              case Token.NE:
              case Token.LT:
              case Token.LE:
              case Token.GT:
              case Token.GE:
              case Token.SHEQ:
              case Token.SHNE:
              case Token.NOT:
              case Token.INSTANCEOF:
              case Token.IN:
              case Token.DEL_REF:
              case Token.DELPROP:
                  // NYI: boolean type
                  return Optimizer.AnyType;
  
              case Token.STRING:
              case Token.TYPEOF:
              case Token.TYPEOFNAME:
                  // NYI: string type
                  return Optimizer.AnyType;
  
              case Token.NULL:
              case Token.REGEXP:
              case Token.ARRAYCOMP:
              case Token.ARRAYLIT:
              case Token.OBJECTLIT:
                  return Optimizer.AnyType; // XXX: actually, we know it's not
              // number, but no type yet for that
  
              case Token.ADD: {
                  // if the lhs & rhs are known to be numbers, we can be sure that's
                  // the result, otherwise it could be a string.
                  Node child = n.getFirstChild();
                  int lType = findExpressionType(fn, child, varTypes);
                  int rType = findExpressionType(fn, child.getNext(), varTypes);
                  return lType | rType;    // we're not distinguishing strings yet
              }
  
              case Token.HOOK: {
                  Node ifTrue = n.getFirstChild().getNext();
                  Node ifFalse = ifTrue.getNext();
                  int ifTrueType = findExpressionType(fn, ifTrue, varTypes);
                  int ifFalseType = findExpressionType(fn, ifFalse, varTypes);
                  return ifTrueType | ifFalseType;
              }
  
              case Token.COMMA:
              case Token.SETVAR:
              case Token.SETCONSTVAR:
              case Token.SETNAME:
              case Token.SETPROP:
              case Token.SETELEM:
                  return findExpressionType(fn, n.getLastChild(), varTypes);
  
              case Token.AND:
              case Token.OR: {
                  Node child = n.getFirstChild();
                  int lType = findExpressionType(fn, child, varTypes);
                  int rType = findExpressionType(fn, child.getNext(), varTypes);
                  return lType | rType;
              }
          }
  
          return Optimizer.AnyType;
      }