Fri, 17 Oct 2014 14:24:26 +0200
8059843: Make AST serializable
Reviewed-by: hannesw, lagergren
1 /*
2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
26 package jdk.nashorn.internal.ir;
28 import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
30 import java.util.Arrays;
31 import java.util.Collections;
32 import java.util.HashSet;
33 import java.util.Set;
34 import java.util.function.Function;
35 import jdk.nashorn.internal.codegen.types.Type;
36 import jdk.nashorn.internal.ir.annotations.Ignore;
37 import jdk.nashorn.internal.ir.annotations.Immutable;
38 import jdk.nashorn.internal.ir.visitor.NodeVisitor;
39 import jdk.nashorn.internal.parser.TokenType;
41 /**
42 * BinaryNode nodes represent two operand operations.
43 */
44 @Immutable
45 public final class BinaryNode extends Expression implements Assignment<Expression>, Optimistic {
46 private static final long serialVersionUID = 1L;
48 // Placeholder for "undecided optimistic ADD type". Unfortunately, we can't decide the type of ADD during optimistic
49 // type calculation as it can have local variables as its operands that will decide its ultimate type.
50 private static final Type OPTIMISTIC_UNDECIDED_TYPE = Type.typeFor(new Object(){/*empty*/}.getClass());
52 /** Left hand side argument. */
53 private final Expression lhs;
55 private final Expression rhs;
57 private final int programPoint;
59 private final Type type;
61 private transient Type cachedType;
62 private transient Object cachedTypeFunction;
64 @Ignore
65 private static final Set<TokenType> CAN_OVERFLOW =
66 Collections.unmodifiableSet(new HashSet<>(Arrays.asList(new TokenType[] {
67 TokenType.ADD,
68 TokenType.DIV,
69 TokenType.MOD,
70 TokenType.MUL,
71 TokenType.SUB,
72 TokenType.ASSIGN_ADD,
73 TokenType.ASSIGN_DIV,
74 TokenType.ASSIGN_MOD,
75 TokenType.ASSIGN_MUL,
76 TokenType.ASSIGN_SUB
77 })));
79 /**
80 * Constructor
81 *
82 * @param token token
83 * @param lhs left hand side
84 * @param rhs right hand side
85 */
86 public BinaryNode(final long token, final Expression lhs, final Expression rhs) {
87 super(token, lhs.getStart(), rhs.getFinish());
88 assert !(isTokenType(TokenType.AND) || isTokenType(TokenType.OR)) || lhs instanceof JoinPredecessorExpression;
89 this.lhs = lhs;
90 this.rhs = rhs;
91 this.programPoint = INVALID_PROGRAM_POINT;
92 this.type = null;
93 }
95 private BinaryNode(final BinaryNode binaryNode, final Expression lhs, final Expression rhs, final Type type, final int programPoint) {
96 super(binaryNode);
97 this.lhs = lhs;
98 this.rhs = rhs;
99 this.programPoint = programPoint;
100 this.type = type;
101 }
103 /**
104 * Returns true if the node is a comparison operation.
105 * @return true if the node is a comparison operation.
106 */
107 public boolean isComparison() {
108 switch (tokenType()) {
109 case EQ:
110 case EQ_STRICT:
111 case NE:
112 case NE_STRICT:
113 case LE:
114 case LT:
115 case GE:
116 case GT:
117 return true;
118 default:
119 return false;
120 }
121 }
123 /**
124 * Returns true if the node is a logical operation.
125 * @return true if the node is a logical operation.
126 */
127 public boolean isLogical() {
128 return isLogical(tokenType());
129 }
131 /**
132 * Returns true if the token type represents a logical operation.
133 * @param tokenType the token type
134 * @return true if the token type represents a logical operation.
135 */
136 public static boolean isLogical(final TokenType tokenType) {
137 switch (tokenType) {
138 case AND:
139 case OR:
140 return true;
141 default:
142 return false;
143 }
144 }
146 private static final Function<Symbol, Type> UNKNOWN_LOCALS = new Function<Symbol, Type>() {
147 @Override
148 public Type apply(final Symbol t) {
149 return null;
150 }
151 };
153 /**
154 * Return the widest possible type for this operation. This is used for compile time
155 * static type inference
156 *
157 * @return Type
158 */
159 @Override
160 public Type getWidestOperationType() {
161 return getWidestOperationType(UNKNOWN_LOCALS);
162 }
164 /**
165 * Return the widest possible operand type for this operation.
166 *
167 * @return Type
168 */
169 public Type getWidestOperandType() {
170 switch (tokenType()) {
171 case SHR:
172 case ASSIGN_SHR:
173 return Type.INT;
174 case INSTANCEOF:
175 return Type.OBJECT;
176 default:
177 if (isComparison()) {
178 return Type.OBJECT;
179 }
180 return getWidestOperationType();
181 }
182 }
184 private Type getWidestOperationType(final Function<Symbol, Type> localVariableTypes) {
185 switch (tokenType()) {
186 case ADD:
187 case ASSIGN_ADD: {
188 // Compare this logic to decideType(Type, Type); it's similar, but it handles the optimistic type
189 // calculation case while this handles the conservative case.
190 final Type lhsType = lhs.getType(localVariableTypes);
191 final Type rhsType = rhs.getType(localVariableTypes);
192 if(lhsType == Type.BOOLEAN && rhsType == Type.BOOLEAN) {
193 // Will always fit in an int, as the value range is [0, 1, 2]. If we didn't treat them specially here,
194 // they'd end up being treated as generic INT operands and their sum would be conservatively considered
195 // to be a LONG in the generic case below; we can do better here.
196 return Type.INT;
197 } else if(isString(lhsType) || isString(rhsType)) {
198 // We can statically figure out that this is a string if either operand is a string. In this case, use
199 // CHARSEQUENCE to prevent it from being proactively flattened.
200 return Type.CHARSEQUENCE;
201 }
202 final Type widestOperandType = Type.widest(undefinedToNumber(booleanToInt(lhsType)), undefinedToNumber(booleanToInt(rhsType)));
203 if(widestOperandType == Type.INT) {
204 return Type.LONG;
205 } else if (widestOperandType.isNumeric()) {
206 return Type.NUMBER;
207 }
208 // We pretty much can't know what it will be statically. Must presume OBJECT conservatively, as we can end
209 // up getting either a string or an object when adding something + object, e.g.:
210 // 1 + {} == "1[object Object]", but
211 // 1 + {valueOf: function() { return 2 }} == 3. Also:
212 // 1 + {valueOf: function() { return "2" }} == "12".
213 return Type.OBJECT;
214 }
215 case SHR:
216 case ASSIGN_SHR:
217 return Type.LONG;
218 case ASSIGN_SAR:
219 case ASSIGN_SHL:
220 case BIT_AND:
221 case BIT_OR:
222 case BIT_XOR:
223 case ASSIGN_BIT_AND:
224 case ASSIGN_BIT_OR:
225 case ASSIGN_BIT_XOR:
226 case SAR:
227 case SHL:
228 return Type.INT;
229 case DIV:
230 case MOD:
231 case ASSIGN_DIV:
232 case ASSIGN_MOD: {
233 // Naively, one might think MOD has the same type as the widest of its operands, this is unfortunately not
234 // true when denominator is zero, so even type(int % int) == double.
235 return Type.NUMBER;
236 }
237 case MUL:
238 case SUB:
239 case ASSIGN_MUL:
240 case ASSIGN_SUB: {
241 final Type lhsType = lhs.getType(localVariableTypes);
242 final Type rhsType = rhs.getType(localVariableTypes);
243 if(lhsType == Type.BOOLEAN && rhsType == Type.BOOLEAN) {
244 return Type.INT;
245 }
246 final Type widestOperandType = Type.widest(booleanToInt(lhsType), booleanToInt(rhsType));
247 if(widestOperandType == Type.INT) {
248 return Type.LONG;
249 }
250 return Type.NUMBER;
251 }
252 case VOID: {
253 return Type.UNDEFINED;
254 }
255 case ASSIGN: {
256 return rhs.getType(localVariableTypes);
257 }
258 case INSTANCEOF: {
259 return Type.BOOLEAN;
260 }
261 case COMMALEFT: {
262 return lhs.getType(localVariableTypes);
263 }
264 case COMMARIGHT: {
265 return rhs.getType(localVariableTypes);
266 }
267 default:
268 if (isComparison()) {
269 return Type.BOOLEAN;
270 }
271 return Type.OBJECT;
272 }
273 }
275 private static boolean isString(final Type type) {
276 return type == Type.STRING || type == Type.CHARSEQUENCE;
277 }
279 private static Type booleanToInt(final Type type) {
280 return type == Type.BOOLEAN ? Type.INT : type;
281 }
283 private static Type undefinedToNumber(final Type type) {
284 return type == Type.UNDEFINED ? Type.NUMBER : type;
285 }
287 /**
288 * Check if this node is an assignment
289 *
290 * @return true if this node assigns a value
291 */
292 @Override
293 public boolean isAssignment() {
294 switch (tokenType()) {
295 case ASSIGN:
296 case ASSIGN_ADD:
297 case ASSIGN_BIT_AND:
298 case ASSIGN_BIT_OR:
299 case ASSIGN_BIT_XOR:
300 case ASSIGN_DIV:
301 case ASSIGN_MOD:
302 case ASSIGN_MUL:
303 case ASSIGN_SAR:
304 case ASSIGN_SHL:
305 case ASSIGN_SHR:
306 case ASSIGN_SUB:
307 return true;
308 default:
309 return false;
310 }
311 }
313 @Override
314 public boolean isSelfModifying() {
315 return isAssignment() && tokenType() != TokenType.ASSIGN;
316 }
318 @Override
319 public Expression getAssignmentDest() {
320 return isAssignment() ? lhs() : null;
321 }
323 @Override
324 public BinaryNode setAssignmentDest(final Expression n) {
325 return setLHS(n);
326 }
328 @Override
329 public Expression getAssignmentSource() {
330 return rhs();
331 }
333 /**
334 * Assist in IR navigation.
335 * @param visitor IR navigating visitor.
336 */
337 @Override
338 public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
339 if (visitor.enterBinaryNode(this)) {
340 if(tokenType().isLeftAssociative()) {
341 return visitor.leaveBinaryNode(setLHS((Expression)lhs.accept(visitor)).setRHS((Expression)rhs.accept(visitor)));
342 }
343 return visitor.leaveBinaryNode(setRHS((Expression)rhs.accept(visitor)).setLHS((Expression)lhs.accept(visitor)));
344 }
346 return this;
347 }
349 @Override
350 public boolean isLocal() {
351 switch (tokenType()) {
352 case SAR:
353 case SHL:
354 case SHR:
355 case BIT_AND:
356 case BIT_OR:
357 case BIT_XOR:
358 case ADD:
359 case DIV:
360 case MOD:
361 case MUL:
362 case SUB:
363 return lhs.isLocal() && lhs.getType().isJSPrimitive()
364 && rhs.isLocal() && rhs.getType().isJSPrimitive();
365 case ASSIGN_ADD:
366 case ASSIGN_BIT_AND:
367 case ASSIGN_BIT_OR:
368 case ASSIGN_BIT_XOR:
369 case ASSIGN_DIV:
370 case ASSIGN_MOD:
371 case ASSIGN_MUL:
372 case ASSIGN_SAR:
373 case ASSIGN_SHL:
374 case ASSIGN_SHR:
375 case ASSIGN_SUB:
376 return lhs instanceof IdentNode && lhs.isLocal() && lhs.getType().isJSPrimitive()
377 && rhs.isLocal() && rhs.getType().isJSPrimitive();
378 case ASSIGN:
379 return lhs instanceof IdentNode && lhs.isLocal() && rhs.isLocal();
380 default:
381 return false;
382 }
383 }
385 @Override
386 public boolean isAlwaysFalse() {
387 switch (tokenType()) {
388 case COMMALEFT:
389 return lhs.isAlwaysFalse();
390 case COMMARIGHT:
391 return rhs.isAlwaysFalse();
392 default:
393 return false;
394 }
395 }
397 @Override
398 public boolean isAlwaysTrue() {
399 switch (tokenType()) {
400 case COMMALEFT:
401 return lhs.isAlwaysTrue();
402 case COMMARIGHT:
403 return rhs.isAlwaysTrue();
404 default:
405 return false;
406 }
407 }
409 @Override
410 public void toString(final StringBuilder sb, final boolean printType) {
411 final TokenType tokenType = tokenType();
413 final boolean lhsParen = tokenType.needsParens(lhs().tokenType(), true);
414 final boolean rhsParen = tokenType.needsParens(rhs().tokenType(), false);
416 if (lhsParen) {
417 sb.append('(');
418 }
420 lhs().toString(sb, printType);
422 if (lhsParen) {
423 sb.append(')');
424 }
426 sb.append(' ');
428 switch (tokenType) {
429 case COMMALEFT:
430 sb.append(",<");
431 break;
432 case COMMARIGHT:
433 sb.append(",>");
434 break;
435 case INCPREFIX:
436 case DECPREFIX:
437 sb.append("++");
438 break;
439 default:
440 sb.append(tokenType.getName());
441 break;
442 }
444 if (isOptimistic()) {
445 sb.append(Expression.OPT_IDENTIFIER);
446 }
448 sb.append(' ');
450 if (rhsParen) {
451 sb.append('(');
452 }
453 rhs().toString(sb, printType);
454 if (rhsParen) {
455 sb.append(')');
456 }
457 }
459 /**
460 * Get the left hand side expression for this node
461 * @return the left hand side expression
462 */
463 public Expression lhs() {
464 return lhs;
465 }
467 /**
468 * Get the right hand side expression for this node
469 * @return the left hand side expression
470 */
471 public Expression rhs() {
472 return rhs;
473 }
475 /**
476 * Set the left hand side expression for this node
477 * @param lhs new left hand side expression
478 * @return a node equivalent to this one except for the requested change.
479 */
480 public BinaryNode setLHS(final Expression lhs) {
481 if (this.lhs == lhs) {
482 return this;
483 }
484 return new BinaryNode(this, lhs, rhs, type, programPoint);
485 }
487 /**
488 * Set the right hand side expression for this node
489 * @param rhs new left hand side expression
490 * @return a node equivalent to this one except for the requested change.
491 */
492 public BinaryNode setRHS(final Expression rhs) {
493 if (this.rhs == rhs) {
494 return this;
495 }
496 return new BinaryNode(this, lhs, rhs, type, programPoint);
497 }
499 @Override
500 public int getProgramPoint() {
501 return programPoint;
502 }
504 @Override
505 public boolean canBeOptimistic() {
506 return isTokenType(TokenType.ADD) || (getMostOptimisticType() != getMostPessimisticType());
507 }
509 @Override
510 public BinaryNode setProgramPoint(final int programPoint) {
511 if (this.programPoint == programPoint) {
512 return this;
513 }
514 return new BinaryNode(this, lhs, rhs, type, programPoint);
515 }
517 @Override
518 public Type getMostOptimisticType() {
519 final TokenType tokenType = tokenType();
520 if(tokenType == TokenType.ADD || tokenType == TokenType.ASSIGN_ADD) {
521 return OPTIMISTIC_UNDECIDED_TYPE;
522 } else if (CAN_OVERFLOW.contains(tokenType())) {
523 return Type.INT;
524 }
525 return getMostPessimisticType();
526 }
528 @Override
529 public Type getMostPessimisticType() {
530 return getWidestOperationType();
531 }
533 /**
534 * Returns true if the node has the optimistic type of the node is not yet decided. Optimistic ADD nodes start out
535 * as undecided until we can figure out if they're numeric or not.
536 * @return true if the node has the optimistic type of the node is not yet decided.
537 */
538 public boolean isOptimisticUndecidedType() {
539 return type == OPTIMISTIC_UNDECIDED_TYPE;
540 }
542 @Override
543 public Type getType(final Function<Symbol, Type> localVariableTypes) {
544 if(localVariableTypes == cachedTypeFunction) {
545 return cachedType;
546 }
547 cachedType = getTypeUncached(localVariableTypes);
548 cachedTypeFunction = localVariableTypes;
549 return cachedType;
550 }
552 private Type getTypeUncached(final Function<Symbol, Type> localVariableTypes) {
553 if(type == OPTIMISTIC_UNDECIDED_TYPE) {
554 return decideType(lhs.getType(localVariableTypes), rhs.getType(localVariableTypes));
555 }
556 final Type widest = getWidestOperationType(localVariableTypes);
557 if(type == null) {
558 return widest;
559 }
560 return Type.narrowest(widest, Type.widest(type, Type.widest(lhs.getType(localVariableTypes), rhs.getType(localVariableTypes))));
561 }
563 private static Type decideType(final Type lhsType, final Type rhsType) {
564 // Compare this to getWidestOperationType() for ADD and ASSIGN_ADD cases. There's some similar logic, but these
565 // are optimistic decisions, meaning that we don't have to treat boolean addition separately (as it'll become
566 // int addition in the general case anyway), and that we also don't conservatively widen sums of ints to
567 // longs, or sums of longs to doubles.
568 if(isString(lhsType) || isString(rhsType)) {
569 return Type.CHARSEQUENCE;
570 }
571 // NOTE: We don't have optimistic object-to-(int, long) conversions. Therefore, if any operand is an Object, we
572 // bail out of optimism here and presume a conservative Object return value, as the object's ToPrimitive() can
573 // end up returning either a number or a string, and their common supertype is Object, for better or worse.
574 final Type widest = Type.widest(undefinedToNumber(booleanToInt(lhsType)), undefinedToNumber(booleanToInt(rhsType)));
575 return widest.isObject() ? Type.OBJECT : widest;
576 }
578 /**
579 * If the node is a node representing an add operation and has {@link #isOptimisticUndecidedType() optimistic
580 * undecided type}, decides its type. Should be invoked after its operands types have been finalized.
581 * @return returns a new node similar to this node, but with its type set to the type decided from the type of its
582 * operands.
583 */
584 public BinaryNode decideType() {
585 assert type == OPTIMISTIC_UNDECIDED_TYPE;
586 return setType(decideType(lhs.getType(), rhs.getType()));
587 }
589 @Override
590 public BinaryNode setType(final Type type) {
591 if (this.type == type) {
592 return this;
593 }
594 return new BinaryNode(this, lhs, rhs, type, programPoint);
595 }
596 }