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