Java Code Examples for com.google.javascript.rhino.Node#isThis()

The following examples show how to use com.google.javascript.rhino.Node#isThis() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: 1_FunctionInjector.java    From SimFix with GNU General Public License v2.0 6 votes vote down vote up
/**
 * Only ".call" calls and direct calls to functions are supported.
 * @param callNode The call evaluate.
 * @return Whether the call is of a type that is supported.
 */
private boolean isSupportedCallType(Node callNode) {
  if (!callNode.getFirstChild().isName()) {
    if (NodeUtil.isFunctionObjectCall(callNode)) {
      if (!assumeStrictThis) {
        Node thisValue = callNode.getFirstChild().getNext();
        if (thisValue == null || !thisValue.isThis()) {
          return false;
        }
      }
    } else if (NodeUtil.isFunctionObjectApply(callNode)) {
      return false;
    }
  }

  return true;
}
 
Example 2
Source File: ExpandJqueryAliases.java    From astor with GNU General Public License v2.0 6 votes vote down vote up
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
  // In the top scope, "this" is a reference to "value"
  boolean isThis = false;
  if (t.getScope() == this.startingScope) {
    isThis = n.isThis();
  }

  if (isThis || n.isName() && !isShadowed(n.getString(), t.getScope())) {
    String nodeValue = isThis ? null : n.getString();
    if (!isThis && keyName != null && nodeValue.equals(keyName)) {
      keyReferences.add(n);
    } else if (isThis || (valueName != null &&
        nodeValue.equals(valueName))) {
      valueReferences.add(n);
    }
  }
}
 
Example 3
Source File: Closure_116_FunctionInjector_s.java    From coming with MIT License 6 votes vote down vote up
/**
 * Only ".call" calls and direct calls to functions are supported.
 * @param callNode The call evaluate.
 * @return Whether the call is of a type that is supported.
 */
private boolean isSupportedCallType(Node callNode) {
  if (!callNode.getFirstChild().isName()) {
    if (NodeUtil.isFunctionObjectCall(callNode)) {
      if (!assumeStrictThis) {
        Node thisValue = callNode.getFirstChild().getNext();
        if (thisValue == null || !thisValue.isThis()) {
          return false;
        }
      }
    } else if (NodeUtil.isFunctionObjectApply(callNode)) {
      return false;
    }
  }

  return true;
}
 
Example 4
Source File: FunctionInjector.java    From astor with GNU General Public License v2.0 6 votes vote down vote up
/**
 * Only ".call" calls and direct calls to functions are supported.
 * @param callNode The call evaluate.
 * @return Whether the call is of a type that is supported.
 */
private boolean isSupportedCallType(Node callNode) {
  if (!callNode.getFirstChild().isName()) {
    if (NodeUtil.isFunctionObjectCall(callNode)) {
      if (!assumeStrictThis) {
        Node thisValue = callNode.getFirstChild().getNext();
        if (thisValue == null || !thisValue.isThis()) {
          return false;
        }
      }
    } else if (NodeUtil.isFunctionObjectApply(callNode)) {
      return false;
    }
  }

  return true;
}
 
Example 5
Source File: Closure_115_FunctionInjector_t.java    From coming with MIT License 6 votes vote down vote up
/**
 * Only ".call" calls and direct calls to functions are supported.
 * @param callNode The call evaluate.
 * @return Whether the call is of a type that is supported.
 */
private boolean isSupportedCallType(Node callNode) {
  if (!callNode.getFirstChild().isName()) {
    if (NodeUtil.isFunctionObjectCall(callNode)) {
      if (!assumeStrictThis) {
        Node thisValue = callNode.getFirstChild().getNext();
        if (thisValue == null || !thisValue.isThis()) {
          return false;
        }
      }
    } else if (NodeUtil.isFunctionObjectApply(callNode)) {
      return false;
    }
  }

  return true;
}
 
Example 6
Source File: FunctionArgumentInjector.java    From astor with GNU General Public License v2.0 5 votes vote down vote up
@Override
public void visit(Node n) {
  // If we are exiting a loop.
  if (NodeUtil.isLoopStructure(n)) {
    loopsEntered--;
    if (!inLoop() && !sideEffectSeen) {
      // Now that the loops has been fully traversed and
      // no side-effects have been seen, throw away
      // the references seen in them.
      parametersReferenced.clear();
    }
  }

  if (!sideEffectSeen) {
    // Look for side-effects.
    if (hasNonLocalSideEffect(n)) {
      sideEffectSeen = true;
    }
  }

  // If traversing the nodes of a loop save any references
  // that are seen.
  if (inLoop() || sideEffectSeen) {
    // Record references to parameters.
    if (n.isName()) {
      String name = n.getString();
      if (parameters.contains(name)) {
        parametersReferenced.add(name);
      }
    } else if (n.isThis()) {
      parametersReferenced.add(THIS_MARKER);
    }
  }
}
 
Example 7
Source File: CheckGlobalThis.java    From astor with GNU General Public License v2.0 5 votes vote down vote up
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
  if (n.isThis() && shouldReportThis(n, parent)) {
    compiler.report(t.makeError(n, GLOBAL_THIS));
  }
  if (n == assignLhsChild) {
    assignLhsChild = null;
  }
}
 
Example 8
Source File: Nopol2017_0029_t.java    From coming with MIT License 5 votes vote down vote up
/** Check that we don't create new properties on structs. */
private void checkPropCreation(NodeTraversal t, Node lvalue) {
  if (lvalue.isGetProp()) {
    Node obj = lvalue.getFirstChild();
    Node prop = lvalue.getLastChild();
    JSType objType = getJSType(obj);
    String pname = prop.getString();
    if (objType.isStruct() && !objType.hasProperty(pname)) {
      if (!(obj.isThis() &&
            getJSType(t.getScope().getRootNode()).isConstructor())) {
        report(t, prop, ILLEGAL_PROPERTY_CREATION);
      }
    }
  }
}
 
Example 9
Source File: Closure_2_TypeCheck_s.java    From coming with MIT License 5 votes vote down vote up
/** Check that we don't create new properties on structs. */
private void checkPropCreation(NodeTraversal t, Node lvalue) {
  if (lvalue.isGetProp()) {
    Node obj = lvalue.getFirstChild();
    Node prop = lvalue.getLastChild();
    JSType objType = getJSType(obj);
    String pname = prop.getString();
    if (objType.isStruct() && !objType.hasProperty(pname)) {
      if (!(obj.isThis() &&
            getJSType(t.getScope().getRootNode()).isConstructor())) {
        report(t, prop, ILLEGAL_PROPERTY_CREATION);
      }
    }
  }
}
 
Example 10
Source File: TypeCheck.java    From astor with GNU General Public License v2.0 5 votes vote down vote up
/** Check that we don't create new properties on structs. */
private void checkPropCreation(NodeTraversal t, Node lvalue) {
  if (lvalue.isGetProp()) {
    Node obj = lvalue.getFirstChild();
    Node prop = lvalue.getLastChild();
    JSType objType = getJSType(obj);
    String pname = prop.getString();
    if (objType.isStruct() && !objType.hasProperty(pname)) {
      if (!(obj.isThis() &&
            getJSType(t.getScope().getRootNode()).isConstructor())) {
        report(t, prop, ILLEGAL_PROPERTY_CREATION);
      }
    }
  }
}
 
Example 11
Source File: TypeInference.java    From astor with GNU General Public License v2.0 5 votes vote down vote up
private FlowScope narrowScope(FlowScope scope, Node node, JSType narrowed) {
  if (node.isThis()) {
    // "this" references don't need to be modeled in the control flow graph.
    return scope;
  }

  scope = scope.createChildFlowScope();
  if (node.isGetProp()) {
    scope.inferQualifiedSlot(
        node, node.getQualifiedName(), getJSType(node), narrowed);
  } else {
    redeclareSimpleVar(scope, node, narrowed);
  }
  return scope;
}
 
Example 12
Source File: Closure_125_TypeCheck_t.java    From coming with MIT License 5 votes vote down vote up
/**
 * After a struct object is created, we can't add new properties to it, with
 * one exception. We allow creation of "static" properties like
 * Foo.prototype.bar = baz;
 * where Foo.prototype is a struct, if the assignment happens at the top level
 * and the constructor Foo is defined in the same file.
 */
private void checkPropCreation(NodeTraversal t, Node lvalue) {
  if (lvalue.isGetProp()) {
    Node obj = lvalue.getFirstChild();
    Node prop = lvalue.getLastChild();
    JSType objType = getJSType(obj);
    String pname = prop.getString();

    if (!objType.isStruct() || objType.hasProperty(pname)) {
      return;
    }
    Scope s = t.getScope();
    if (obj.isThis() && getJSType(s.getRootNode()).isConstructor()) {
      return;
    }
    // Prop created outside ctor, check that it's a static prop
    Node assgnExp = lvalue.getParent();
    Node assgnStm = assgnExp.getParent();
    if (objType instanceof ObjectType &&
        s.isGlobal() &&
        NodeUtil.isPrototypePropertyDeclaration(assgnStm)) {
      ObjectType instance =
          objType.toObjectType().getOwnerFunction().getInstanceType();
      String file = lvalue.getSourceFileName();
      Node ctor = instance.getConstructor().getSource();
      if (ctor != null && ctor.getSourceFileName().equals(file)) {
        JSType rvalueType = assgnExp.getLastChild().getJSType();
        instance.defineInferredProperty(pname, rvalueType, lvalue);
        return;
      }
    }
    report(t, prop, ILLEGAL_PROPERTY_CREATION);
  }
}
 
Example 13
Source File: Closure_36_InlineVariables_t.java    From coming with MIT License 4 votes vote down vote up
/**
 * Determines whether the reference collection describes a variable that
 * is initialized to an immutable value, never modified, and defined before
 * every reference.
 */
private boolean isImmutableAndWellDefinedVariable(Var v,
    ReferenceCollection refInfo) {
  List<Reference> refSet = refInfo.references;
  int startingReadRef = 1;
  Reference refDecl = refSet.get(0);
  if (!isValidDeclaration(refDecl)) {
    return false;
  }

  boolean isNeverAssigned = refInfo.isNeverAssigned();
  // For values that are never assigned, only the references need to be
  // checked.
  if (!isNeverAssigned) {
    Reference refInit = refInfo.getInitializingReference();
    if (!isValidInitialization(refInit)) {
      return false;
    }

    if (refDecl != refInit) {
      Preconditions.checkState(refInit == refSet.get(1));
      startingReadRef = 2;
    }

    if (!refInfo.isWellDefined()) {
      return false;
    }

    Node value = refInit.getAssignedValue();
    Preconditions.checkNotNull(value);

    boolean isImmutableValueWorthInlining =
        NodeUtil.isImmutableValue(value) &&
        (!value.isString() ||
            isStringWorthInlining(v, refInfo.references));
    boolean isInlinableThisAlias =
        value.isThis() &&
        !refInfo.isEscaped();
    if (!isImmutableValueWorthInlining && !isInlinableThisAlias) {
      return false;
    }
  }

  for (int i = startingReadRef; i < refSet.size(); i++) {
    Reference ref = refSet.get(i);
    if (!isValidReference(ref)) {
      return false;
    }
  }

  return true;
}
 
Example 14
Source File: FunctionInjector.java    From astor with GNU General Public License v2.0 4 votes vote down vote up
/**
 * Determines whether a function can be inlined at a particular call site.
 * There are several criteria that the function and reference must hold in
 * order for the functions to be inlined:
 * 1) If a call's arguments have side effects,
 * the corresponding argument in the function must only be referenced once.
 * For instance, this will not be inlined:
 * <pre>
 *     function foo(a) { return a + a }
 *     x = foo(i++);
 * </pre>
 */
private CanInlineResult canInlineReferenceDirectly(
    Node callNode, Node fnNode) {
  if (!isDirectCallNodeReplacementPossible(fnNode)) {
    return CanInlineResult.NO;
  }

  Node block = fnNode.getLastChild();

  // CALL NODE: [ NAME, ARG1, ARG2, ... ]
  Node cArg = callNode.getFirstChild().getNext();

  // Functions called via 'call' and 'apply' have a this-object as
  // the first parameter, but this is not part of the called function's
  // parameter list.
  if (!callNode.getFirstChild().isName()) {
    if (NodeUtil.isFunctionObjectCall(callNode)) {
      // TODO(johnlenz): Support replace this with a value.
      if (cArg == null || !cArg.isThis()) {
        return CanInlineResult.NO;
      }
      cArg = cArg.getNext();
    } else {
      // ".apply" call should be filtered before this.
      Preconditions.checkState(!NodeUtil.isFunctionObjectApply(callNode));
    }
  }

  // FUNCTION NODE -> LP NODE: [ ARG1, ARG2, ... ]
  Node fnParam = NodeUtil.getFunctionParameters(fnNode).getFirstChild();
  while (cArg != null || fnParam != null) {
    // For each named parameter check if a mutable argument use more than one.
    if (fnParam != null) {
      if (cArg != null) {
        // Check for arguments that are evaluated more than once.
        // Note: Unlike block inlining, there it is not possible that a
        // parameter reference will be in a loop.
        if (NodeUtil.mayEffectMutableState(cArg, compiler)
            && NodeUtil.getNameReferenceCount(
                block, fnParam.getString()) > 1) {
          return CanInlineResult.NO;
        }
      }

      // Move to the next name.
      fnParam = fnParam.getNext();
    }

    // For every call argument check for side-effects, even if there
    // isn't a named parameter to match.
    if (cArg != null) {
      if (NodeUtil.mayHaveSideEffects(cArg, compiler)) {
        return CanInlineResult.NO;
      }
      cArg = cArg.getNext();
    }
  }

  return CanInlineResult.YES;
}
 
Example 15
Source File: Closure_115_FunctionInjector_t.java    From coming with MIT License 4 votes vote down vote up
/**
 * Determines whether a function can be inlined at a particular call site.
 * There are several criteria that the function and reference must hold in
 * order for the functions to be inlined:
 * 1) If a call's arguments have side effects,
 * the corresponding argument in the function must only be referenced once.
 * For instance, this will not be inlined:
 * <pre>
 *     function foo(a) { return a + a }
 *     x = foo(i++);
 * </pre>
 */
private CanInlineResult canInlineReferenceDirectly(
    Node callNode, Node fnNode) {
  if (!isDirectCallNodeReplacementPossible(fnNode)) {
    return CanInlineResult.NO;
  }

  Node block = fnNode.getLastChild();

  // CALL NODE: [ NAME, ARG1, ARG2, ... ]
  Node cArg = callNode.getFirstChild().getNext();

  // Functions called via 'call' and 'apply' have a this-object as
  // the first parameter, but this is not part of the called function's
  // parameter list.
  if (!callNode.getFirstChild().isName()) {
    if (NodeUtil.isFunctionObjectCall(callNode)) {
      // TODO(johnlenz): Support replace this with a value.
      if (cArg == null || !cArg.isThis()) {
        return CanInlineResult.NO;
      }
      cArg = cArg.getNext();
    } else {
      // ".apply" call should be filtered before this.
      Preconditions.checkState(!NodeUtil.isFunctionObjectApply(callNode));
    }
  }

  // FUNCTION NODE -> LP NODE: [ ARG1, ARG2, ... ]
  Node fnParam = NodeUtil.getFunctionParameters(fnNode).getFirstChild();
  while (cArg != null || fnParam != null) {
    // For each named parameter check if a mutable argument use more than one.
    if (fnParam != null) {
      if (cArg != null) {
        // Check for arguments that are evaluated more than once.
        // Note: Unlike block inlining, there it is not possible that a
        // parameter reference will be in a loop.
        if (NodeUtil.mayEffectMutableState(cArg, compiler)
            && NodeUtil.getNameReferenceCount(
                block, fnParam.getString()) > 1) {
          return CanInlineResult.NO;
        }
      }

      // Move to the next name.
      fnParam = fnParam.getNext();
    }

    // For every call argument check for side-effects, even if there
    // isn't a named parameter to match.
    if (cArg != null) {
      if (NodeUtil.mayHaveSideEffects(cArg, compiler)) {
        return CanInlineResult.NO;
      }
      cArg = cArg.getNext();
    }
  }

  return CanInlineResult.YES;
}
 
Example 16
Source File: Closure_36_InlineVariables_s.java    From coming with MIT License 4 votes vote down vote up
/**
 * Determines whether the reference collection describes a variable that
 * is initialized to an immutable value, never modified, and defined before
 * every reference.
 */
private boolean isImmutableAndWellDefinedVariable(Var v,
    ReferenceCollection refInfo) {
  List<Reference> refSet = refInfo.references;
  int startingReadRef = 1;
  Reference refDecl = refSet.get(0);
  if (!isValidDeclaration(refDecl)) {
    return false;
  }

  boolean isNeverAssigned = refInfo.isNeverAssigned();
  // For values that are never assigned, only the references need to be
  // checked.
  if (!isNeverAssigned) {
    Reference refInit = refInfo.getInitializingReference();
    if (!isValidInitialization(refInit)) {
      return false;
    }

    if (refDecl != refInit) {
      Preconditions.checkState(refInit == refSet.get(1));
      startingReadRef = 2;
    }

    if (!refInfo.isWellDefined()) {
      return false;
    }

    Node value = refInit.getAssignedValue();
    Preconditions.checkNotNull(value);

    boolean isImmutableValueWorthInlining =
        NodeUtil.isImmutableValue(value) &&
        (!value.isString() ||
            isStringWorthInlining(v, refInfo.references));
    boolean isInlinableThisAlias =
        value.isThis() &&
        !refInfo.isEscaped();
    if (!isImmutableValueWorthInlining && !isInlinableThisAlias) {
      return false;
    }
  }

  for (int i = startingReadRef; i < refSet.size(); i++) {
    Reference ref = refSet.get(i);
    if (!isValidReference(ref)) {
      return false;
    }
  }

  return true;
}
 
Example 17
Source File: Closure_112_TypeInference_t.java    From coming with MIT License 4 votes vote down vote up
/**
 * Defines a property if the property has not been defined yet.
 */
private void ensurePropertyDefined(Node getprop, JSType rightType) {
  String propName = getprop.getLastChild().getString();
  Node obj = getprop.getFirstChild();
  JSType nodeType = getJSType(obj);
  ObjectType objectType = ObjectType.cast(
      nodeType.restrictByNotNullOrUndefined());
  boolean propCreationInConstructor = obj.isThis() &&
      getJSType(syntacticScope.getRootNode()).isConstructor();

  if (objectType == null) {
    registry.registerPropertyOnType(propName, nodeType);
  } else {
    if (nodeType.isStruct() && !objectType.hasProperty(propName)) {
      // In general, we don't want to define a property on a struct object,
      // b/c TypeCheck will later check for improper property creation on
      // structs. There are two exceptions.
      // 1) If it's a property created inside the constructor, on the newly
      //    created instance, allow it.
      // 2) If it's a prototype property, allow it. For example:
      //    Foo.prototype.bar = baz;
      //    where Foo.prototype is a struct and the assignment happens at the
      //    top level and the constructor Foo is defined in the same file.
      boolean staticPropCreation = false;
      Node maybeAssignStm = getprop.getParent().getParent();
      if (syntacticScope.isGlobal() &&
          NodeUtil.isPrototypePropertyDeclaration(maybeAssignStm)) {
        String propCreationFilename = maybeAssignStm.getSourceFileName();
        Node ctor = objectType.getOwnerFunction().getSource();
        if (ctor != null &&
            ctor.getSourceFileName().equals(propCreationFilename)) {
          staticPropCreation = true;
        }
      }
      if (!propCreationInConstructor && !staticPropCreation) {
        return; // Early return to avoid creating the property below.
      }
    }

    if (ensurePropertyDeclaredHelper(getprop, objectType)) {
      return;
    }

    if (!objectType.isPropertyTypeDeclared(propName)) {
      // We do not want a "stray" assign to define an inferred property
      // for every object of this type in the program. So we use a heuristic
      // approach to determine whether to infer the property.
      //
      // 1) If the property is already defined, join it with the previously
      //    inferred type.
      // 2) If this isn't an instance object, define it.
      // 3) If the property of an object is being assigned in the constructor,
      //    define it.
      // 4) If this is a stub, define it.
      // 5) Otherwise, do not define the type, but declare it in the registry
      //    so that we can use it for missing property checks.
      if (objectType.hasProperty(propName) || !objectType.isInstanceType()) {
        if ("prototype".equals(propName)) {
          objectType.defineDeclaredProperty(propName, rightType, getprop);
        } else {
          objectType.defineInferredProperty(propName, rightType, getprop);
        }
      } else if (propCreationInConstructor) {
        objectType.defineInferredProperty(propName, rightType, getprop);
      } else {
        registry.registerPropertyOnType(propName, objectType);
      }
    }
  }
}
 
Example 18
Source File: TypeConversionPass.java    From clutz with MIT License 4 votes vote down vote up
/** Returns whether a name starts with "this." */
static boolean containsThis(Node fullName) {
  return fullName.isThis() || (fullName.isGetProp() && containsThis(fullName.getFirstChild()));
}
 
Example 19
Source File: TypeInference.java    From astor with GNU General Public License v2.0 4 votes vote down vote up
/**
 * Defines a property if the property has not been defined yet.
 */
private void ensurePropertyDefined(Node getprop, JSType rightType) {
  String propName = getprop.getLastChild().getString();
  Node obj = getprop.getFirstChild();
  JSType nodeType = getJSType(obj);
  ObjectType objectType = ObjectType.cast(
      nodeType.restrictByNotNullOrUndefined());
  if (objectType == null) {
    registry.registerPropertyOnType(propName, nodeType);
  } else {
    // Don't add the property to @struct objects outside a constructor
    if (nodeType.isStruct() && !objectType.hasProperty(propName)) {
      if (!(obj.isThis() &&
            getJSType(syntacticScope.getRootNode()).isConstructor())) {
        return;
      }
    }

    if (ensurePropertyDeclaredHelper(getprop, objectType)) {
      return;
    }

    if (!objectType.isPropertyTypeDeclared(propName)) {
      // We do not want a "stray" assign to define an inferred property
      // for every object of this type in the program. So we use a heuristic
      // approach to determine whether to infer the property.
      //
      // 1) If the property is already defined, join it with the previously
      //    inferred type.
      // 2) If this isn't an instance object, define it.
      // 3) If the property of an object is being assigned in the constructor,
      //    define it.
      // 4) If this is a stub, define it.
      // 5) Otherwise, do not define the type, but declare it in the registry
      //    so that we can use it for missing property checks.
      if (objectType.hasProperty(propName) || !objectType.isInstanceType()) {
        if ("prototype".equals(propName)) {
          objectType.defineDeclaredProperty(propName, rightType, getprop);
        } else {
          objectType.defineInferredProperty(propName, rightType, getprop);
        }
      } else if (obj.isThis() &&
                 getJSType(syntacticScope.getRootNode()).isConstructor()) {
        objectType.defineInferredProperty(propName, rightType, getprop);
      } else {
        registry.registerPropertyOnType(propName, objectType);
      }
    }
  }
}
 
Example 20
Source File: PureFunctionIdentifier.java    From astor with GNU General Public License v2.0 4 votes vote down vote up
@Override
public boolean traverseEdge(FunctionInformation callee,
                            Node callSite,
                            FunctionInformation caller) {
  Preconditions.checkArgument(callSite.isCall() ||
                              callSite.isNew());

  boolean changed = false;
  if (!caller.mutatesGlobalState() && callee.mutatesGlobalState()) {
    caller.setTaintsGlobalState();
    changed = true;
  }

  if (!caller.functionThrows() && callee.functionThrows()) {
    caller.setFunctionThrows();
    changed = true;
  }

  if (callee.mutatesThis()) {
    // Side effects only propagate via regular calls.
    // Calling a constructor that modifies "this" has no side effects.
    if (!callSite.isNew()) {
      Node objectNode = getCallThisObject(callSite);
      if (objectNode != null && objectNode.isName()
          && !isCallOrApply(callSite)) {
        // Exclude ".call" and ".apply" as the value may still be
        // null or undefined. We don't need to worry about this with a
        // direct method call because null and undefined don't have any
        // properties.
        String name = objectNode.getString();

        // TODO(nicksantos): Turn this back on when locals-tracking
        // is fixed. See testLocalizedSideEffects11.
        //if (!caller.knownLocals.contains(name)) {
          if (!caller.mutatesGlobalState()) {
            caller.setTaintsGlobalState();
            changed = true;
          }
        //}
      } else if (objectNode != null && objectNode.isThis()) {
        if (!caller.mutatesThis()) {
          caller.setTaintsThis();
          changed = true;
        }
      } else if (objectNode != null
          && NodeUtil.evaluatesToLocalValue(objectNode)
          && !isCallOrApply(callSite)) {
        // Modifying 'this' on a known local object doesn't change any
        // significant state.
        // TODO(johnlenz): We can improve this by including literal values
        // that we know for sure are not null.
      } else if (!caller.mutatesGlobalState()) {
        caller.setTaintsGlobalState();
        changed = true;
      }
    }
  }

  return changed;
}