Search in sources :

Example 16 with Tri

use of com.google.javascript.jscomp.base.Tri in project closure-compiler by google.

the class CheckSuspiciousCode method getBooleanValueWithTypes.

/**
 * Returns the possible boolean values of a node. This is a combination of {@link
 * NodeUtil#getBooleanValue} and {@link JSType#getPossibleToBooleanOutcomes}, with some additional
 * backoff for situations that are known to be less accurate (to wit, qualified names and truthy
 * return types, which return UNKNOWN even if the type information seems conclusive). Another
 * difference from the NodeUtil and JSTyoe methods is that we include some specific optimization
 * to avoid quadratic behavior of large nested logical operations.
 *
 * <p>The specifics of when this method returns UNKNOWN instead of TRUE or FALSE is as follows:
 *
 * <ul>
 *   <li>We should not determine that a qualified name (expanded slightly to include computed
 *       properties) is always truthy or always falsy. There are many cases where an extern
 *       defines a name to be truthy, but it still makes sense to feature-test in the browser.
 *   <li>We should not determine that a getelem or a function call is always truthy (though we
 *       expand it to simply never complain about always-truthy based only on types) since the
 *       standard externs lie about the return type of {@code Map.prototype.get} and array
 *       accesses, which can both return undefined despite what the externs say.
 * </ul>
 *
 * We do not back off from boolean literals (e.g. "{@code true &&}"), though they appear to be
 * common in generated code. Instead, such code should suppress "suspiciousCode". We also do not
 * back off from always-falsy function call results, since it provides a valuable check and lies
 * in this direction are much less common.
 */
private Tri getBooleanValueWithTypes(Node n) {
    switch(n.getToken()) {
        case ASSIGN:
        case COMMA:
            return getBooleanValueWithTypes(n.getLastChild());
        case NOT:
            return getBooleanValueWithTypes(n.getLastChild()).not();
        case AND:
            // prevents revisiting deeper nodes repeatedly, which would result in O(n^2) performance.
            return Tri.UNKNOWN.and(getBooleanValueWithTypes(n.getLastChild()));
        case OR:
            // prevents revisiting deeper nodes repeatedly, which would result in O(n^2) performance.
            return Tri.UNKNOWN.or(getBooleanValueWithTypes(n.getLastChild()));
        case HOOK:
            {
                Tri trueValue = getBooleanValueWithTypes(n.getSecondChild());
                Tri falseValue = getBooleanValueWithTypes(n.getLastChild());
                return trueValue.equals(falseValue) ? trueValue : Tri.UNKNOWN;
            }
        case FUNCTION:
        case CLASS:
        case NEW:
        case ARRAYLIT:
        case OBJECTLIT:
            return Tri.TRUE;
        case VOID:
            return Tri.FALSE;
        case GETPROP:
        case GETELEM:
        case OPTCHAIN_GETELEM:
        case OPTCHAIN_GETPROP:
            // initialization of globals ({@code x.y = x.y || {}}).
            return Tri.UNKNOWN;
        default:
    }
    // If we reach this point then all the composite structures that we can decompose have
    // already been handled, leaving only qualified names and type-aware checks to handle below.
    // Note that much of the switch above in fact duplicates the logic in getImpureBooleanValue,
    // though with some subtle differences.  Important differences include (1) avoiding recursion
    // into the left-hand-side of nested logical operators, instead treating them as unknown since
    // they would have already been reported elsewhere in the traversal had they been otherwise
    // (this guarantees we visit each node once, rather than quadratically repeating work);
    // (2) it propagates our unique amalgam of syntax-based and type-based checks to work when more
    // deeply nested (i.e. recursively).  These differences rely on assumptions that are very
    // specific to this use case, so it does not make sense to upstream them.
    Tri literalValue = NodeUtil.getBooleanValue(n);
    if (literalValue != Tri.UNKNOWN || n.isName()) {
        // Alternatively, NAME nodes also get a pass since we don't trust the type information.
        return literalValue;
    }
    JSType type = n.getJSType();
    if (type != null) {
        // those type annotations to be lies.  ANDing with UNKNOWN ensures we never return TRUE.
        return Tri.UNKNOWN.and(type.getPossibleToBooleanOutcomes().toTri());
    }
    return Tri.UNKNOWN;
}
Also used : JSType(com.google.javascript.rhino.jstype.JSType) Tri(com.google.javascript.jscomp.base.Tri)

Example 17 with Tri

use of com.google.javascript.jscomp.base.Tri in project closure-compiler by google.

the class UnionType method testForEquality.

@Override
public Tri testForEquality(JSType that) {
    Tri result = null;
    for (int i = 0; i < alternates.size(); i++) {
        JSType t = alternates.get(i);
        Tri test = t.testForEquality(that);
        if (result == null) {
            result = test;
        } else if (!result.equals(test)) {
            return Tri.UNKNOWN;
        }
    }
    return result;
}
Also used : Tri(com.google.javascript.jscomp.base.Tri)

Aggregations

Tri (com.google.javascript.jscomp.base.Tri)17 Node (com.google.javascript.rhino.Node)12 Token (com.google.javascript.rhino.Token)3 BigInteger (java.math.BigInteger)3 JSType (com.google.javascript.rhino.jstype.JSType)2 MeasuredNode (com.google.javascript.jscomp.MinimizedCondition.MeasuredNode)1 ValueType (com.google.javascript.jscomp.NodeUtil.ValueType)1 EnumType (com.google.javascript.rhino.jstype.EnumType)1 Test (org.junit.Test)1