src/com/google/javascript/jscomp/NodeUtil.java
/**
   * Returns true if some node in n's subtree changes application state.
   * If {@code checkForNewObjects} is true, we assume that newly created
   * mutable objects (like object literals) change state. Otherwise, we assume
   * that they have no side effects.
   */
Returns true if some node in n's subtree changes application state.
If  checkForNewObjects is true, we assume that newly created
mutable objects (like object literals) change state. Otherwise, we assume
that they have no side effects.
Returns true if some node in n ' s subtree changes application state . If checkForNewObjects is true we assume that newly created mutable objects ( like object literals ) change state . Otherwise we assume that they have no side effects .
private static boolean checkForStateChangeHelper(
      Node n, boolean checkForNewObjects, AbstractCompiler compiler) {
    switch (n.getToken()) {
      case THROW:
      case YIELD:
      case EXPORT:
      case VAR:
      case LET:
      case CONST:
        return true;
      case OBJECTLIT:
        if (checkForNewObjects) {
          return true;
        }
        for (Node key = n.getFirstChild(); key != null; key = key.getNext()) {
          for (Node c = key.getFirstChild(); c != null; c = c.getNext()) {
            if (checkForStateChangeHelper(c, checkForNewObjects, compiler)) {
              return true;
            }
          }
        }
        return false;
      case ARRAYLIT:
      case REGEXP:
        if (checkForNewObjects) {
          return true;
        }
        break;
      case SPREAD:
        Node expr = n.getOnlyChild();
        if (!expr.isArrayLit()) {
          return true;
        }
        break;
      case NAME:
        if (n.hasChildren()) {
          return true;
        }
        break;
      case FUNCTION:
        return checkForNewObjects || isFunctionDeclaration(n);
      case CLASS:
        return checkForNewObjects || isClassDeclaration(n)
            || checkForStateChangeHelper(n.getSecondChild(), checkForNewObjects, compiler)
            || checkForStateChangeHelper(n.getLastChild(), checkForNewObjects, compiler);
      case CLASS_MEMBERS:
        for (Node member = n.getFirstChild(); member != null; member = member.getNext()) {
          if (member.isComputedProp()
              && checkForStateChangeHelper(member.getFirstChild(), checkForNewObjects, compiler)) {
            return true;
          }
        }
        return false;
      case NEW:
        if (checkForNewObjects) {
          return true;
        }
        if (!constructorCallHasSideEffects(n)) {
          break;
        }
        return true;
      case CALL:
        if (!functionCallHasSideEffects(n, compiler)) {
          break;
        }
        return true;
      case TAGGED_TEMPLATELIT:
        return functionCallHasSideEffects(n, compiler);
      case CAST:
      case AND:
      case BLOCK:
      case ROOT:
      case EXPR_RESULT:
      case HOOK:
      case IF:
      case PARAM_LIST:
      case NUMBER:
      case OR:
      case THIS:
      case TRUE:
      case FALSE:
      case NULL:
      case STRING:
      case STRING_KEY:
      case SWITCH:
      case TEMPLATELIT_SUB:
      case TRY:
      case EMPTY:
      case TEMPLATELIT:
      case TEMPLATELIT_STRING:
      case COMPUTED_PROP:
        break;
      default:
        if (isSimpleOperator(n)) {
          break;
        }
        if (isAssignmentOp(n)) {
          Node assignTarget = n.getFirstChild();
          if (assignTarget.isName()) {
            return true;
          }
          if (checkForStateChangeHelper(n.getFirstChild(), checkForNewObjects, compiler)
              || checkForStateChangeHelper(n.getLastChild(), checkForNewObjects, compiler)) {
            return true;
          }
          if (isGet(assignTarget)) {
            Node current = assignTarget.getFirstChild();
            if (evaluatesToLocalValue(current)) {
              return false;
            }
            while (isGet(current)) {
              current = current.getFirstChild();
            }
            return !isLiteralValue(current, true);
          } else {
            return !isLiteralValue(assignTarget, true);
          }
        }
        return true;
    }
    for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
      if (checkForStateChangeHelper(c, checkForNewObjects, compiler)) {
        return true;
      }
    }
    return false;
  }
private static boolean checkForStateChangeHelper ( Node n , boolean checkForNewObjects , AbstractCompiler compiler ) { switch ( n . getToken ( ) ) { case THROW : case YIELD : case EXPORT : case VAR : case LET : case CONST : return true ; case OBJECTLIT : if ( checkForNewObjects ) { return true ; } for ( Node key = n . getFirstChild ( ) ; key != null ; key = key . getNext ( ) ) { for ( Node c = key . getFirstChild ( ) ; c != null ; c = c . getNext ( ) ) { if ( checkForStateChangeHelper ( c , checkForNewObjects , compiler ) ) { return true ; } } } return false ; case ARRAYLIT : case REGEXP : if ( checkForNewObjects ) { return true ; } break ; case SPREAD : Node expr = n . getOnlyChild ( ) ; if ( ! expr . isArrayLit ( ) ) { return true ; } break ; case NAME : if ( n . hasChildren ( ) ) { return true ; } break ; case FUNCTION : return checkForNewObjects || isFunctionDeclaration ( n ) ; case CLASS : return checkForNewObjects || isClassDeclaration ( n ) || checkForStateChangeHelper ( n . getSecondChild ( ) , checkForNewObjects , compiler ) || checkForStateChangeHelper ( n . getLastChild ( ) , checkForNewObjects , compiler ) ; case CLASS_MEMBERS : for ( Node member = n . getFirstChild ( ) ; member != null ; member = member . getNext ( ) ) { if ( member . isComputedProp ( ) && checkForStateChangeHelper ( member . getFirstChild ( ) , checkForNewObjects , compiler ) ) { return true ; } } return false ; case NEW : if ( checkForNewObjects ) { return true ; } if ( ! constructorCallHasSideEffects ( n ) ) { break ; } return true ; case CALL : if ( ! functionCallHasSideEffects ( n , compiler ) ) { break ; } return true ; case TAGGED_TEMPLATELIT : return functionCallHasSideEffects ( n , compiler ) ; case CAST : case AND : case BLOCK : case ROOT : case EXPR_RESULT : case HOOK : case IF : case PARAM_LIST : case NUMBER : case OR : case THIS : case TRUE : case FALSE : case NULL : case STRING : case STRING_KEY : case SWITCH : case TEMPLATELIT_SUB : case TRY : case EMPTY : case TEMPLATELIT : case TEMPLATELIT_STRING : case COMPUTED_PROP : break ; default : if ( isSimpleOperator ( n ) ) { break ; } if ( isAssignmentOp ( n ) ) { Node assignTarget = n . getFirstChild ( ) ; if ( assignTarget . isName ( ) ) { return true ; } if ( checkForStateChangeHelper ( n . getFirstChild ( ) , checkForNewObjects , compiler ) || checkForStateChangeHelper ( n . getLastChild ( ) , checkForNewObjects , compiler ) ) { return true ; } if ( isGet ( assignTarget ) ) { Node current = assignTarget . getFirstChild ( ) ; if ( evaluatesToLocalValue ( current ) ) { return false ; } while ( isGet ( current ) ) { current = current . getFirstChild ( ) ; } return ! isLiteralValue ( current , true ) ; } else { return ! isLiteralValue ( assignTarget , true ) ; } } return true ; } for ( Node c = n . getFirstChild ( ) ; c != null ; c = c . getNext ( ) ) { if ( checkForStateChangeHelper ( c , checkForNewObjects , compiler ) ) { return true ; } } return false ; }
Returns true if some node in n's subtree changes application state.
Returns true if some node in n ' s subtree changes application state .
// Rather than id which ops may have side effects, id the ones // that we know to be safe // Throws are by definition side effects, and yield and export are similar. // Anything other than an array, in the absense of any other information, // we have to assume is going to invoke invoke a stateful generator or the like. // This is the left side of a var/let/const // Function expressions don't have side-effects, but function // declarations change the namespace. Either way, we don't need to // check the children, since they aren't executed at declaration time. // Check the extends clause for side effects. // Check for class members that are computed properties with side effects. // loop below will see if the constructor parameters have // side-effects // calls to functions that have no side effects have the no // side effect property set. // loop below will see if the function parameters have // side-effects // Assignments will have side effects if // a) The RHS has side effects, or // b) The LHS has side effects, or // c) A name on the LHS will exist beyond the life of this statement. // If the object being assigned to is a local object, don't // consider this a side-effect as it can't be referenced // elsewhere.  Don't do this recursively as the property might // be an alias of another object, unlike a literal below. // A literal value as defined by "isLiteralValue" is guaranteed // not to be an alias, or any components which are aliases of // other objects. // If the root object is a literal don't consider this a // side-effect. // TODO(johnlenz): remove this code and make this an exception. This // is here only for legacy reasons, the AST is not valid but // preserve existing behavior.