BinaryNode.java revision 1263:044a0fe3944f
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 */
25
26package jdk.nashorn.internal.ir;
27
28import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
29
30import java.util.Arrays;
31import java.util.Collections;
32import java.util.HashSet;
33import java.util.Set;
34import jdk.nashorn.internal.codegen.types.Type;
35import jdk.nashorn.internal.ir.annotations.Ignore;
36import jdk.nashorn.internal.ir.annotations.Immutable;
37import jdk.nashorn.internal.ir.visitor.NodeVisitor;
38import jdk.nashorn.internal.parser.TokenType;
39
40/**
41 * BinaryNode nodes represent two operand operations.
42 */
43@Immutable
44public final class BinaryNode extends Expression implements Assignment<Expression>, Optimistic {
45    private static final long serialVersionUID = 1L;
46
47    // Placeholder for "undecided optimistic ADD type". Unfortunately, we can't decide the type of ADD during optimistic
48    // type calculation as it can have local variables as its operands that will decide its ultimate type.
49    private static final Type OPTIMISTIC_UNDECIDED_TYPE = Type.typeFor(new Object(){/*empty*/}.getClass());
50
51    /** Left hand side argument. */
52    private final Expression lhs;
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);
94        this.lhs = lhs;
95        this.rhs = rhs;
96        this.programPoint = programPoint;
97        this.type = type;
98    }
99
100    /**
101     * Returns true if the node is a comparison operation (either equality, inequality, or relational).
102     * @return true if the node is a comparison operation.
103     */
104    public boolean isComparison() {
105        switch (tokenType()) {
106        case EQ:
107        case EQ_STRICT:
108        case NE:
109        case NE_STRICT:
110        case LE:
111        case LT:
112        case GE:
113        case GT:
114            return true;
115        default:
116            return false;
117        }
118    }
119
120    /**
121     * Returns true if the node is a relational operation (less than (or equals), greater than (or equals)).
122     * @return true if the node is a relational operation.
123     */
124    public boolean isRelational() {
125        switch (tokenType()) {
126        case LT:
127        case GT:
128        case LE:
129        case GE:
130            return true;
131        default:
132            return false;
133        }
134    }
135
136    /**
137     * Returns true if the node is a logical operation.
138     * @return true if the node is a logical operation.
139     */
140    public boolean isLogical() {
141        return isLogical(tokenType());
142    }
143
144    /**
145     * Returns true if the token type represents a logical operation.
146     * @param tokenType the token type
147     * @return true if the token type represents a logical operation.
148     */
149    public static boolean isLogical(final TokenType tokenType) {
150        switch (tokenType) {
151        case AND:
152        case OR:
153            return true;
154        default:
155            return false;
156        }
157    }
158
159    /**
160     * Return the widest possible operand type for this operation.
161     *
162     * @return Type
163     */
164    public Type getWidestOperandType() {
165        switch (tokenType()) {
166        case SHR:
167        case ASSIGN_SHR:
168            return Type.INT;
169        case INSTANCEOF:
170            return Type.OBJECT;
171        default:
172            if (isComparison()) {
173                return Type.OBJECT;
174            }
175            return getWidestOperationType();
176        }
177    }
178
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());
266        }
267        default:
268            if (isComparison()) {
269                return Type.BOOLEAN;
270            }
271            return Type.OBJECT;
272        }
273    }
274
275    private static boolean isString(final Type type) {
276        return type == Type.STRING || type == Type.CHARSEQUENCE;
277    }
278
279    private static Type booleanToInt(final Type type) {
280        return type == Type.BOOLEAN ? Type.INT : type;
281    }
282
283    private static Type undefinedToNumber(final Type type) {
284        return type == Type.UNDEFINED ? Type.NUMBER : type;
285    }
286
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    }
312
313    @Override
314    public boolean isSelfModifying() {
315        return isAssignment() && !isTokenType(TokenType.ASSIGN);
316    }
317
318    @Override
319    public Expression getAssignmentDest() {
320        return isAssignment() ? lhs() : null;
321    }
322
323    @Override
324    public BinaryNode setAssignmentDest(final Expression n) {
325        return setLHS(n);
326    }
327
328    @Override
329    public Expression getAssignmentSource() {
330        return rhs();
331    }
332
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            return visitor.leaveBinaryNode(setLHS((Expression)lhs.accept(visitor)).setRHS((Expression)rhs.accept(visitor)));
341        }
342
343        return this;
344    }
345
346    @Override
347    public boolean isLocal() {
348        switch (tokenType()) {
349        case SAR:
350        case SHL:
351        case SHR:
352        case BIT_AND:
353        case BIT_OR:
354        case BIT_XOR:
355        case ADD:
356        case DIV:
357        case MOD:
358        case MUL:
359        case SUB:
360            return lhs.isLocal() && lhs.getType().isJSPrimitive()
361                && rhs.isLocal() && rhs.getType().isJSPrimitive();
362        case ASSIGN_ADD:
363        case ASSIGN_BIT_AND:
364        case ASSIGN_BIT_OR:
365        case ASSIGN_BIT_XOR:
366        case ASSIGN_DIV:
367        case ASSIGN_MOD:
368        case ASSIGN_MUL:
369        case ASSIGN_SAR:
370        case ASSIGN_SHL:
371        case ASSIGN_SHR:
372        case ASSIGN_SUB:
373            return lhs instanceof IdentNode && lhs.isLocal() && lhs.getType().isJSPrimitive()
374                    && rhs.isLocal() && rhs.getType().isJSPrimitive();
375        case ASSIGN:
376            return lhs instanceof IdentNode && lhs.isLocal() && rhs.isLocal();
377        default:
378            return false;
379        }
380    }
381
382    @Override
383    public boolean isAlwaysFalse() {
384        switch (tokenType()) {
385        case COMMALEFT:
386            return lhs.isAlwaysFalse();
387        case COMMARIGHT:
388            return rhs.isAlwaysFalse();
389        default:
390            return false;
391        }
392    }
393
394    @Override
395    public boolean isAlwaysTrue() {
396        switch (tokenType()) {
397        case COMMALEFT:
398            return lhs.isAlwaysTrue();
399        case COMMARIGHT:
400            return rhs.isAlwaysTrue();
401        default:
402            return false;
403        }
404    }
405
406    @Override
407    public void toString(final StringBuilder sb, final boolean printType) {
408        final TokenType tokenType = tokenType();
409
410        final boolean lhsParen = tokenType.needsParens(lhs().tokenType(), true);
411        final boolean rhsParen = tokenType.needsParens(rhs().tokenType(), false);
412
413        if (lhsParen) {
414            sb.append('(');
415        }
416
417        lhs().toString(sb, printType);
418
419        if (lhsParen) {
420            sb.append(')');
421        }
422
423        sb.append(' ');
424
425        switch (tokenType) {
426        case COMMALEFT:
427            sb.append(",<");
428            break;
429        case COMMARIGHT:
430            sb.append(",>");
431            break;
432        case INCPREFIX:
433        case DECPREFIX:
434            sb.append("++");
435            break;
436        default:
437            sb.append(tokenType.getName());
438            break;
439        }
440
441        if (isOptimistic()) {
442            sb.append(Expression.OPT_IDENTIFIER);
443        }
444
445        sb.append(' ');
446
447        if (rhsParen) {
448            sb.append('(');
449        }
450        rhs().toString(sb, printType);
451        if (rhsParen) {
452            sb.append(')');
453        }
454    }
455
456    /**
457     * Get the left hand side expression for this node
458     * @return the left hand side expression
459     */
460    public Expression lhs() {
461        return lhs;
462    }
463
464    /**
465     * Get the right hand side expression for this node
466     * @return the left hand side expression
467     */
468    public Expression rhs() {
469        return rhs;
470    }
471
472    /**
473     * Set the left hand side expression for this node
474     * @param lhs new left hand side expression
475     * @return a node equivalent to this one except for the requested change.
476     */
477    public BinaryNode setLHS(final Expression lhs) {
478        if (this.lhs == lhs) {
479            return this;
480        }
481        return new BinaryNode(this, lhs, rhs, type, programPoint);
482    }
483
484    /**
485     * Set the right hand side expression for this node
486     * @param rhs new right hand side expression
487     * @return a node equivalent to this one except for the requested change.
488     */
489    public BinaryNode setRHS(final Expression rhs) {
490        if (this.rhs == rhs) {
491            return this;
492        }
493        return new BinaryNode(this, lhs, rhs, type, programPoint);
494    }
495
496    /**
497     * Set both the left and the right hand side expression for this node
498     * @param lhs new left hand side expression
499     * @param rhs new left hand side expression
500     * @return a node equivalent to this one except for the requested change.
501     */
502    public BinaryNode setOperands(final Expression lhs, final Expression rhs) {
503        if (this.lhs == lhs && this.rhs == rhs) {
504            return this;
505        }
506        return new BinaryNode(this, lhs, rhs, type, programPoint);
507    }
508
509    @Override
510    public int getProgramPoint() {
511        return programPoint;
512    }
513
514    @Override
515    public boolean canBeOptimistic() {
516        return isTokenType(TokenType.ADD) || (getMostOptimisticType() != getMostPessimisticType());
517    }
518
519    @Override
520    public BinaryNode setProgramPoint(final int programPoint) {
521        if (this.programPoint == programPoint) {
522            return this;
523        }
524        return new BinaryNode(this, lhs, rhs, type, programPoint);
525    }
526
527    @Override
528    public Type getMostOptimisticType() {
529        final TokenType tokenType = tokenType();
530        if(tokenType == TokenType.ADD || tokenType == TokenType.ASSIGN_ADD) {
531            return OPTIMISTIC_UNDECIDED_TYPE;
532        } else if (CAN_OVERFLOW.contains(tokenType)) {
533            return Type.INT;
534        }
535        return getMostPessimisticType();
536    }
537
538    @Override
539    public Type getMostPessimisticType() {
540        return getWidestOperationType();
541    }
542
543    /**
544     * Returns true if the node has the optimistic type of the node is not yet decided. Optimistic ADD nodes start out
545     * as undecided until we can figure out if they're numeric or not.
546     * @return true if the node has the optimistic type of the node is not yet decided.
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    /**
587     * If the node is a node representing an add operation and has {@link #isOptimisticUndecidedType() optimistic
588     * undecided type}, decides its type. Should be invoked after its operands types have been finalized.
589     * @return returns a new node similar to this node, but with its type set to the type decided from the type of its
590     * operands.
591     */
592    public BinaryNode decideType() {
593        assert type == OPTIMISTIC_UNDECIDED_TYPE;
594        return setType(decideType(lhs.getType(), rhs.getType()));
595    }
596
597    @Override
598    public BinaryNode setType(final Type type) {
599        if (this.type == type) {
600            return this;
601        }
602        return new BinaryNode(this, lhs, rhs, type, programPoint);
603    }
604}
605