UnaryNode.java revision 1068:34ef988d5959
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.parser.TokenType.BIT_NOT;
29import static jdk.nashorn.internal.parser.TokenType.DECPOSTFIX;
30import static jdk.nashorn.internal.parser.TokenType.INCPOSTFIX;
31import static jdk.nashorn.internal.runtime.UnwarrantedOptimismException.INVALID_PROGRAM_POINT;
32
33import java.util.Arrays;
34import java.util.Collections;
35import java.util.List;
36import java.util.function.Function;
37import jdk.nashorn.internal.codegen.types.Type;
38import jdk.nashorn.internal.ir.annotations.Ignore;
39import jdk.nashorn.internal.ir.annotations.Immutable;
40import jdk.nashorn.internal.ir.visitor.NodeVisitor;
41import jdk.nashorn.internal.parser.Token;
42import jdk.nashorn.internal.parser.TokenType;
43
44/**
45 * UnaryNode nodes represent single operand operations.
46 */
47@Immutable
48public final class UnaryNode extends Expression implements Assignment<Expression>, Optimistic {
49    private static final long serialVersionUID = 1L;
50
51    /** Right hand side argument. */
52    private final Expression expression;
53
54    private final int programPoint;
55
56    private final Type type;
57
58    @Ignore
59    private static final List<TokenType> CAN_OVERFLOW =
60            Collections.unmodifiableList(
61                Arrays.asList(new TokenType[] {
62                    TokenType.ADD,
63                    TokenType.SUB, //negate
64                    TokenType.DECPREFIX,
65                    TokenType.DECPOSTFIX,
66                    TokenType.INCPREFIX,
67                    TokenType.INCPOSTFIX,
68                }));
69
70    /**
71     * Constructor
72     *
73     * @param token  token
74     * @param rhs    expression
75     */
76    public UnaryNode(final long token, final Expression rhs) {
77        this(token, Math.min(rhs.getStart(), Token.descPosition(token)), Math.max(Token.descPosition(token) + Token.descLength(token), rhs.getFinish()), rhs);
78    }
79
80    /**
81     * Constructor
82     *
83     * @param token      token
84     * @param start      start
85     * @param finish     finish
86     * @param expression expression
87     */
88    public UnaryNode(final long token, final int start, final int finish, final Expression expression) {
89        super(token, start, finish);
90        this.expression   = expression;
91        this.programPoint = INVALID_PROGRAM_POINT;
92        this.type = null;
93    }
94
95
96    private UnaryNode(final UnaryNode unaryNode, final Expression expression, final Type type, final int programPoint) {
97        super(unaryNode);
98        this.expression   = expression;
99        this.programPoint = programPoint;
100        this.type = type;
101    }
102
103    /**
104     * Is this an assignment - i.e. that mutates something such as a++
105     *
106     * @return true if assignment
107     */
108    @Override
109    public boolean isAssignment() {
110        switch (tokenType()) {
111        case DECPOSTFIX:
112        case DECPREFIX:
113        case INCPOSTFIX:
114        case INCPREFIX:
115            return true;
116        default:
117            return false;
118        }
119    }
120
121    @Override
122    public boolean isSelfModifying() {
123        return isAssignment();
124    }
125
126    private static final Function<Symbol, Type> UNKNOWN_LOCALS = new Function<Symbol, Type>() {
127        @Override
128        public Type apply(final Symbol t) {
129            return null;
130        }
131    };
132
133
134    @Override
135    public Type getWidestOperationType() {
136        return getWidestOperationType(UNKNOWN_LOCALS);
137    }
138
139    private Type getWidestOperationType(final Function<Symbol, Type> localVariableTypes) {
140        switch (tokenType()) {
141        case ADD:
142            final Type operandType = getExpression().getType(localVariableTypes);
143            if(operandType == Type.BOOLEAN) {
144                return Type.INT;
145            } else if(operandType.isObject()) {
146                return Type.NUMBER;
147            }
148            assert operandType.isNumeric();
149            return operandType;
150        case SUB:
151            // This might seems overly conservative until you consider that -0 can only be represented as a double.
152            return Type.NUMBER;
153        case NOT:
154        case DELETE:
155            return Type.BOOLEAN;
156        case BIT_NOT:
157            return Type.INT;
158        case VOID:
159            return Type.UNDEFINED;
160        default:
161            return isAssignment() ? Type.NUMBER : Type.OBJECT;
162        }
163    }
164
165    @Override
166    public Expression getAssignmentDest() {
167        return isAssignment() ? getExpression() : null;
168    }
169
170    @Override
171    public UnaryNode setAssignmentDest(final Expression n) {
172        return setExpression(n);
173    }
174
175    @Override
176    public Expression getAssignmentSource() {
177        return getAssignmentDest();
178    }
179
180    /**
181     * Assist in IR navigation.
182     * @param visitor IR navigating visitor.
183     */
184    @Override
185    public Node accept(final NodeVisitor<? extends LexicalContext> visitor) {
186        if (visitor.enterUnaryNode(this)) {
187            return visitor.leaveUnaryNode(setExpression((Expression)expression.accept(visitor)));
188        }
189
190        return this;
191    }
192
193    @Override
194    public boolean isLocal() {
195        switch (tokenType()) {
196        case NEW:
197            return false;
198        case ADD:
199        case SUB:
200        case NOT:
201        case BIT_NOT:
202            return expression.isLocal() && expression.getType().isJSPrimitive();
203        case DECPOSTFIX:
204        case DECPREFIX:
205        case INCPOSTFIX:
206        case INCPREFIX:
207            return expression instanceof IdentNode && expression.isLocal() && expression.getType().isJSPrimitive();
208        default:
209            return expression.isLocal();
210        }
211    }
212
213    @Override
214    public void toString(final StringBuilder sb, final boolean printType) {
215        toString(sb,
216                new Runnable() {
217                    @Override
218                    public void run() {
219                        getExpression().toString(sb, printType);
220                    }
221                },
222                printType);
223    }
224
225    /**
226     * Creates the string representation of this unary node, delegating the creation of the string representation of its
227     * operand to a specified runnable.
228     * @param sb the string builder to use
229     * @param rhsStringBuilder the runnable that appends the string representation of the operand to the string builder
230     * @param printType should we print type
231     * when invoked.
232     */
233    public void toString(final StringBuilder sb, final Runnable rhsStringBuilder, final boolean printType) {
234        final TokenType tokenType = tokenType();
235        final String    name      = tokenType.getName();
236        final boolean   isPostfix = tokenType == DECPOSTFIX || tokenType == INCPOSTFIX;
237
238        if (isOptimistic()) {
239            sb.append(Expression.OPT_IDENTIFIER);
240        }
241        boolean rhsParen = tokenType.needsParens(getExpression().tokenType(), false);
242
243        if (!isPostfix) {
244            if (name == null) {
245                sb.append(tokenType.name());
246                rhsParen = true;
247            } else {
248                sb.append(name);
249
250                if (tokenType.ordinal() > BIT_NOT.ordinal()) {
251                    sb.append(' ');
252                }
253            }
254        }
255
256        if (rhsParen) {
257            sb.append('(');
258        }
259        rhsStringBuilder.run();
260        if (rhsParen) {
261            sb.append(')');
262        }
263
264        if (isPostfix) {
265            sb.append(tokenType == DECPOSTFIX ? "--" : "++");
266        }
267    }
268
269    /**
270     * Get the right hand side of this if it is inherited by a binary expression,
271     * or just the expression itself if still Unary
272     *
273     * @see BinaryNode
274     *
275     * @return right hand side or expression node
276     */
277    public Expression getExpression() {
278        return expression;
279    }
280
281    /**
282     * Reset the right hand side of this if it is inherited by a binary expression,
283     * or just the expression itself if still Unary
284     *
285     * @see BinaryNode
286     *
287     * @param expression right hand side or expression node
288     * @return a node equivalent to this one except for the requested change.
289     */
290    public UnaryNode setExpression(final Expression expression) {
291        if (this.expression == expression) {
292            return this;
293        }
294        return new UnaryNode(this, expression, type, programPoint);
295    }
296
297    @Override
298    public int getProgramPoint() {
299        return programPoint;
300    }
301
302    @Override
303    public UnaryNode setProgramPoint(final int programPoint) {
304        if (this.programPoint == programPoint) {
305            return this;
306        }
307        return new UnaryNode(this, expression, type, programPoint);
308    }
309
310    @Override
311    public boolean canBeOptimistic() {
312        return getMostOptimisticType() != getMostPessimisticType();
313    }
314
315    @Override
316    public Type getMostOptimisticType() {
317        if (CAN_OVERFLOW.contains(tokenType())) {
318            return Type.INT;
319        }
320        return getMostPessimisticType();
321    }
322
323    @Override
324    public Type getMostPessimisticType() {
325        return getWidestOperationType();
326    }
327
328    @Override
329    public Type getType(final Function<Symbol, Type> localVariableTypes) {
330        final Type widest = getWidestOperationType(localVariableTypes);
331        if(type == null) {
332            return widest;
333        }
334        return Type.narrowest(widest, Type.widest(type, expression.getType(localVariableTypes)));
335    }
336
337    @Override
338    public UnaryNode setType(final Type type) {
339        if (this.type == type) {
340            return this;
341        }
342        return new UnaryNode(this, expression, type, programPoint);
343    }
344
345}
346