private Node tryFoldUnaryOperator(Node n) { Preconditions.checkState(n.hasOneChild(), n); Node left = n.getFirstChild(); Node parent = n.getParent(); if (left == null) { return n; } TernaryValue leftVal = NodeUtil.getPureBooleanValue(left); if (leftVal == TernaryValue.UNKNOWN) { return n; } switch (n.getType()) { case NOT: // Don't fold !0 and !1 back to false. if (late && left.isNumber()) { double numValue = left.getDouble(); if (numValue == 0 || numValue == 1) { return n; } } Node replacementNode = NodeUtil.booleanNode(!leftVal.toBoolean(true)); parent.replaceChild(n, replacementNode); reportCodeChange(); return replacementNode; case POS: if (NodeUtil.isNumericResult(left)) { // POS does nothing to numeric values. parent.replaceChild(n, left.detachFromParent()); reportCodeChange(); return left; } return n; case NEG: if (left.isName()) { if (left.getString().equals("Infinity")) { // "-Infinity" is valid and a literal, don't modify it. return n; } else if (left.getString().equals("NaN")) { // "-NaN" is "NaN". n.removeChild(left); parent.replaceChild(n, left); reportCodeChange(); return left; } } if (left.isNumber()) { double negNum = -left.getDouble(); Node negNumNode = IR.number(negNum); parent.replaceChild(n, negNumNode); reportCodeChange(); return negNumNode; } else { // left is not a number node, so do not replace, but warn the // user because they can't be doing anything good report(NEGATING_A_NON_NUMBER_ERROR, left); return n; } case BITNOT: try { double val = left.getDouble(); if (val >= Integer.MIN_VALUE && val <= Integer.MAX_VALUE) { int intVal = (int) val; if (intVal == val) { Node notIntValNode = IR.number(~intVal); parent.replaceChild(n, notIntValNode); reportCodeChange(); return notIntValNode; } else { report(FRACTIONAL_BITWISE_OPERAND, left); return n; } } else { report(BITWISE_OPERAND_OUT_OF_RANGE, left); return n; } } catch (UnsupportedOperationException ex) { // left is not a number node, so do not replace, but warn the // user because they can't be doing anything good report(NEGATING_A_NON_NUMBER_ERROR, left); return n; } default: return n; } }
/** http://www.ecma-international.org/ecma-262/6.0/#sec-strict-equality-comparison */ private static TernaryValue tryStrictEqualityComparison(Node left, Node right, boolean useTypes) { // First, try to evaluate based on the general type. ValueType leftValueType = NodeUtil.getKnownValueType(left); ValueType rightValueType = NodeUtil.getKnownValueType(right); if (leftValueType != ValueType.UNDETERMINED && rightValueType != ValueType.UNDETERMINED) { // Strict equality can only be true for values of the same type. if (leftValueType != rightValueType) { return TernaryValue.FALSE; } switch (leftValueType) { case VOID: case NULL: return TernaryValue.TRUE; case NUMBER: { if (NodeUtil.isNaN(left)) { return TernaryValue.FALSE; } if (NodeUtil.isNaN(right)) { return TernaryValue.FALSE; } Double lv = NodeUtil.getNumberValue(left, useTypes); Double rv = NodeUtil.getNumberValue(right, useTypes); if (lv != null && rv != null) { return TernaryValue.forBoolean(lv.doubleValue() == rv.doubleValue()); } break; } case STRING: { String lv = NodeUtil.getStringValue(left); String rv = NodeUtil.getStringValue(right); if (lv != null && rv != null) { // In JS, browsers parse \v differently. So do not consider strings // equal if one contains \v. if (lv.indexOf('\u000B') != -1 || rv.indexOf('\u000B') != -1) { return TernaryValue.UNKNOWN; } else { return lv.equals(rv) ? TernaryValue.TRUE : TernaryValue.FALSE; } } else if (left.isTypeOf() && right.isTypeOf() && left.getFirstChild().isName() && right.getFirstChild().isName() && left.getFirstChild().getString().equals(right.getFirstChild().getString())) { // Special case, typeof a == typeof a is always true. return TernaryValue.TRUE; } break; } case BOOLEAN: { TernaryValue lv = NodeUtil.getPureBooleanValue(left); TernaryValue rv = NodeUtil.getPureBooleanValue(right); return lv.and(rv).or(lv.not().and(rv.not())); } default: // Symbol and Object cannot be folded in the general case. return TernaryValue.UNKNOWN; } } // Then, try to evaluate based on the value of the node. There's only one special case: // Any strict equality comparison against NaN returns false. if (NodeUtil.isNaN(left) || NodeUtil.isNaN(right)) { return TernaryValue.FALSE; } return TernaryValue.UNKNOWN; }