53
54 private final Expression rhs;
55
56 private final int programPoint;
57
58 private final Type type;
59 private transient Type cachedType;
60
61 @Ignore
62 private static final Set<TokenType> CAN_OVERFLOW =
63 Collections.unmodifiableSet(new HashSet<>(Arrays.asList(new TokenType[] {
64 TokenType.ADD,
65 TokenType.DIV,
66 TokenType.MOD,
67 TokenType.MUL,
68 TokenType.SUB,
69 TokenType.ASSIGN_ADD,
70 TokenType.ASSIGN_DIV,
71 TokenType.ASSIGN_MOD,
72 TokenType.ASSIGN_MUL,
73 TokenType.ASSIGN_SUB
74 })));
75
76 /**
77 * Constructor
78 *
79 * @param token token
80 * @param lhs left hand side
81 * @param rhs right hand side
82 */
83 public BinaryNode(final long token, final Expression lhs, final Expression rhs) {
84 super(token, lhs.getStart(), rhs.getFinish());
85 assert !(isTokenType(TokenType.AND) || isTokenType(TokenType.OR)) || lhs instanceof JoinPredecessorExpression;
86 this.lhs = lhs;
87 this.rhs = rhs;
88 this.programPoint = INVALID_PROGRAM_POINT;
89 this.type = null;
90 }
91
92 private BinaryNode(final BinaryNode binaryNode, final Expression lhs, final Expression rhs, final Type type, final int programPoint) {
93 super(binaryNode);
179 @Override
180 public Type getWidestOperationType() {
181 switch (tokenType()) {
182 case ADD:
183 case ASSIGN_ADD: {
184 // Compare this logic to decideType(Type, Type); it's similar, but it handles the optimistic type
185 // calculation case while this handles the conservative case.
186 final Type lhsType = lhs.getType();
187 final Type rhsType = rhs.getType();
188 if(lhsType == Type.BOOLEAN && rhsType == Type.BOOLEAN) {
189 // Will always fit in an int, as the value range is [0, 1, 2]. If we didn't treat them specially here,
190 // they'd end up being treated as generic INT operands and their sum would be conservatively considered
191 // to be a LONG in the generic case below; we can do better here.
192 return Type.INT;
193 } else if(isString(lhsType) || isString(rhsType)) {
194 // We can statically figure out that this is a string if either operand is a string. In this case, use
195 // CHARSEQUENCE to prevent it from being proactively flattened.
196 return Type.CHARSEQUENCE;
197 }
198 final Type widestOperandType = Type.widest(undefinedToNumber(booleanToInt(lhsType)), undefinedToNumber(booleanToInt(rhsType)));
199 if(widestOperandType == Type.INT) {
200 return Type.LONG;
201 } else if (widestOperandType.isNumeric()) {
202 return Type.NUMBER;
203 }
204 // We pretty much can't know what it will be statically. Must presume OBJECT conservatively, as we can end
205 // up getting either a string or an object when adding something + object, e.g.:
206 // 1 + {} == "1[object Object]", but
207 // 1 + {valueOf: function() { return 2 }} == 3. Also:
208 // 1 + {valueOf: function() { return "2" }} == "12".
209 return Type.OBJECT;
210 }
211 case SHR:
212 case ASSIGN_SHR:
213 return Type.LONG;
214 case ASSIGN_SAR:
215 case ASSIGN_SHL:
216 case BIT_AND:
217 case BIT_OR:
218 case BIT_XOR:
219 case ASSIGN_BIT_AND:
220 case ASSIGN_BIT_OR:
221 case ASSIGN_BIT_XOR:
222 case SAR:
223 case SHL:
224 return Type.INT;
225 case DIV:
226 case MOD:
227 case ASSIGN_DIV:
228 case ASSIGN_MOD: {
229 // Naively, one might think MOD has the same type as the widest of its operands, this is unfortunately not
230 // true when denominator is zero, so even type(int % int) == double.
231 return Type.NUMBER;
232 }
233 case MUL:
234 case SUB:
235 case ASSIGN_MUL:
236 case ASSIGN_SUB: {
237 final Type lhsType = lhs.getType();
238 final Type rhsType = rhs.getType();
239 if(lhsType == Type.BOOLEAN && rhsType == Type.BOOLEAN) {
240 return Type.INT;
241 }
242 final Type widestOperandType = Type.widest(booleanToInt(lhsType), booleanToInt(rhsType));
243 if(widestOperandType == Type.INT) {
244 return Type.LONG;
245 }
246 return Type.NUMBER;
247 }
248 case VOID: {
249 return Type.UNDEFINED;
250 }
251 case ASSIGN: {
252 return rhs.getType();
253 }
254 case INSTANCEOF: {
255 return Type.BOOLEAN;
256 }
257 case COMMALEFT: {
258 return lhs.getType();
259 }
260 case COMMARIGHT: {
261 return rhs.getType();
262 }
263 case AND:
264 case OR:{
265 return Type.widestReturnType(lhs.getType(), rhs.getType());
547 */
548 public boolean isOptimisticUndecidedType() {
549 return type == OPTIMISTIC_UNDECIDED_TYPE;
550 }
551
552 @Override
553 public Type getType() {
554 if (cachedType == null) {
555 cachedType = getTypeUncached();
556 }
557 return cachedType;
558 }
559
560 private Type getTypeUncached() {
561 if(type == OPTIMISTIC_UNDECIDED_TYPE) {
562 return decideType(lhs.getType(), rhs.getType());
563 }
564 final Type widest = getWidestOperationType();
565 if(type == null) {
566 return widest;
567 }
568 return Type.narrowest(widest, Type.widest(type, Type.widest(lhs.getType(), rhs.getType())));
569 }
570
571 private static Type decideType(final Type lhsType, final Type rhsType) {
572 // Compare this to getWidestOperationType() for ADD and ASSIGN_ADD cases. There's some similar logic, but these
573 // are optimistic decisions, meaning that we don't have to treat boolean addition separately (as it'll become
574 // int addition in the general case anyway), and that we also don't conservatively widen sums of ints to
575 // longs, or sums of longs to doubles.
576 if(isString(lhsType) || isString(rhsType)) {
577 return Type.CHARSEQUENCE;
578 }
579 // NOTE: We don't have optimistic object-to-(int, long) conversions. Therefore, if any operand is an Object, we
580 // bail out of optimism here and presume a conservative Object return value, as the object's ToPrimitive() can
581 // end up returning either a number or a string, and their common supertype is Object, for better or worse.
582 final Type widest = Type.widest(undefinedToNumber(booleanToInt(lhsType)), undefinedToNumber(booleanToInt(rhsType)));
583 return widest.isObject() ? Type.OBJECT : widest;
584 }
585
586 /**
|
53
54 private final Expression rhs;
55
56 private final int programPoint;
57
58 private final Type type;
59 private transient Type cachedType;
60
61 @Ignore
62 private static final Set<TokenType> CAN_OVERFLOW =
63 Collections.unmodifiableSet(new HashSet<>(Arrays.asList(new TokenType[] {
64 TokenType.ADD,
65 TokenType.DIV,
66 TokenType.MOD,
67 TokenType.MUL,
68 TokenType.SUB,
69 TokenType.ASSIGN_ADD,
70 TokenType.ASSIGN_DIV,
71 TokenType.ASSIGN_MOD,
72 TokenType.ASSIGN_MUL,
73 TokenType.ASSIGN_SUB,
74 TokenType.SHR,
75 TokenType.ASSIGN_SHR
76 })));
77
78 /**
79 * Constructor
80 *
81 * @param token token
82 * @param lhs left hand side
83 * @param rhs right hand side
84 */
85 public BinaryNode(final long token, final Expression lhs, final Expression rhs) {
86 super(token, lhs.getStart(), rhs.getFinish());
87 assert !(isTokenType(TokenType.AND) || isTokenType(TokenType.OR)) || lhs instanceof JoinPredecessorExpression;
88 this.lhs = lhs;
89 this.rhs = rhs;
90 this.programPoint = INVALID_PROGRAM_POINT;
91 this.type = null;
92 }
93
94 private BinaryNode(final BinaryNode binaryNode, final Expression lhs, final Expression rhs, final Type type, final int programPoint) {
95 super(binaryNode);
181 @Override
182 public Type getWidestOperationType() {
183 switch (tokenType()) {
184 case ADD:
185 case ASSIGN_ADD: {
186 // Compare this logic to decideType(Type, Type); it's similar, but it handles the optimistic type
187 // calculation case while this handles the conservative case.
188 final Type lhsType = lhs.getType();
189 final Type rhsType = rhs.getType();
190 if(lhsType == Type.BOOLEAN && rhsType == Type.BOOLEAN) {
191 // Will always fit in an int, as the value range is [0, 1, 2]. If we didn't treat them specially here,
192 // they'd end up being treated as generic INT operands and their sum would be conservatively considered
193 // to be a LONG in the generic case below; we can do better here.
194 return Type.INT;
195 } else if(isString(lhsType) || isString(rhsType)) {
196 // We can statically figure out that this is a string if either operand is a string. In this case, use
197 // CHARSEQUENCE to prevent it from being proactively flattened.
198 return Type.CHARSEQUENCE;
199 }
200 final Type widestOperandType = Type.widest(undefinedToNumber(booleanToInt(lhsType)), undefinedToNumber(booleanToInt(rhsType)));
201 if (widestOperandType.isNumeric()) {
202 return Type.NUMBER;
203 }
204 // We pretty much can't know what it will be statically. Must presume OBJECT conservatively, as we can end
205 // up getting either a string or an object when adding something + object, e.g.:
206 // 1 + {} == "1[object Object]", but
207 // 1 + {valueOf: function() { return 2 }} == 3. Also:
208 // 1 + {valueOf: function() { return "2" }} == "12".
209 return Type.OBJECT;
210 }
211 case SHR:
212 case ASSIGN_SHR:
213 return Type.NUMBER;
214 case ASSIGN_SAR:
215 case ASSIGN_SHL:
216 case BIT_AND:
217 case BIT_OR:
218 case BIT_XOR:
219 case ASSIGN_BIT_AND:
220 case ASSIGN_BIT_OR:
221 case ASSIGN_BIT_XOR:
222 case SAR:
223 case SHL:
224 return Type.INT;
225 case DIV:
226 case MOD:
227 case ASSIGN_DIV:
228 case ASSIGN_MOD: {
229 // Naively, one might think MOD has the same type as the widest of its operands, this is unfortunately not
230 // true when denominator is zero, so even type(int % int) == double.
231 return Type.NUMBER;
232 }
233 case MUL:
234 case SUB:
235 case ASSIGN_MUL:
236 case ASSIGN_SUB: {
237 final Type lhsType = lhs.getType();
238 final Type rhsType = rhs.getType();
239 if(lhsType == Type.BOOLEAN && rhsType == Type.BOOLEAN) {
240 return Type.INT;
241 }
242 return Type.NUMBER;
243 }
244 case VOID: {
245 return Type.UNDEFINED;
246 }
247 case ASSIGN: {
248 return rhs.getType();
249 }
250 case INSTANCEOF: {
251 return Type.BOOLEAN;
252 }
253 case COMMALEFT: {
254 return lhs.getType();
255 }
256 case COMMARIGHT: {
257 return rhs.getType();
258 }
259 case AND:
260 case OR:{
261 return Type.widestReturnType(lhs.getType(), rhs.getType());
543 */
544 public boolean isOptimisticUndecidedType() {
545 return type == OPTIMISTIC_UNDECIDED_TYPE;
546 }
547
548 @Override
549 public Type getType() {
550 if (cachedType == null) {
551 cachedType = getTypeUncached();
552 }
553 return cachedType;
554 }
555
556 private Type getTypeUncached() {
557 if(type == OPTIMISTIC_UNDECIDED_TYPE) {
558 return decideType(lhs.getType(), rhs.getType());
559 }
560 final Type widest = getWidestOperationType();
561 if(type == null) {
562 return widest;
563 }
564 if (tokenType() == TokenType.ASSIGN_SHR || tokenType() == TokenType.SHR) {
565 return type;
566 }
567 return Type.narrowest(widest, Type.widest(type, Type.widest(lhs.getType(), rhs.getType())));
568 }
569
570 private static Type decideType(final Type lhsType, final Type rhsType) {
571 // Compare this to getWidestOperationType() for ADD and ASSIGN_ADD cases. There's some similar logic, but these
572 // are optimistic decisions, meaning that we don't have to treat boolean addition separately (as it'll become
573 // int addition in the general case anyway), and that we also don't conservatively widen sums of ints to
574 // longs, or sums of longs to doubles.
575 if(isString(lhsType) || isString(rhsType)) {
576 return Type.CHARSEQUENCE;
577 }
578 // NOTE: We don't have optimistic object-to-(int, long) conversions. Therefore, if any operand is an Object, we
579 // bail out of optimism here and presume a conservative Object return value, as the object's ToPrimitive() can
580 // end up returning either a number or a string, and their common supertype is Object, for better or worse.
581 final Type widest = Type.widest(undefinedToNumber(booleanToInt(lhsType)), undefinedToNumber(booleanToInt(rhsType)));
582 return widest.isObject() ? Type.OBJECT : widest;
583 }
584
585 /**
|