View Javadoc
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 java.util.ArrayList;
29  import java.util.List;
30  import jdk.nashorn.internal.codegen.types.Type;
31  import jdk.nashorn.internal.ir.visitor.NodeVisitor;
32  import jdk.nashorn.internal.parser.Token;
33  import jdk.nashorn.internal.parser.TokenType;
34  
35  /**
36   * Nodes are used to compose Abstract Syntax Trees.
37   */
38  public abstract class Node implements Cloneable {
39      /** Start of source range. */
40      protected final int start;
41  
42      /** End of source range. */
43      protected int finish;
44  
45      /** Token descriptor. */
46      private final long token;
47  
48      /**
49       * Constructor
50       *
51       * @param token  token
52       * @param finish finish
53       */
54      public Node(final long token, final int finish) {
55          this.token  = token;
56          this.start  = Token.descPosition(token);
57          this.finish = finish;
58      }
59  
60      /**
61       * Constructor
62       *
63       * @param token   token
64       * @param start   start
65       * @param finish  finish
66       */
67      protected Node(final long token, final int start, final int finish) {
68          this.start = start;
69          this.finish = finish;
70          this.token = token;
71      }
72  
73      /**
74       * Copy constructor
75       *
76       * @param node source node
77       */
78      protected Node(final Node node) {
79          this.token  = node.token;
80          this.start  = node.start;
81          this.finish = node.finish;
82      }
83  
84      /**
85       * Is this an atom node - for example a literal or an identity
86       *
87       * @return true if atom
88       */
89      public boolean isAtom() {
90          return false;
91      }
92  
93      /**
94       * Is this a loop node?
95       *
96       * @return true if atom
97       */
98      public boolean isLoop() {
99          return false;
100     }
101 
102     /**
103      * Is this an assignment node - for example a var node with an init
104      * or a binary node that writes to a destination
105      *
106      * @return true if assignment
107      */
108     public boolean isAssignment() {
109         return false;
110     }
111 
112     /**
113      * Is this a self modifying assignment?
114      * @return true if self modifying, e.g. a++, or a*= 17
115      */
116     public boolean isSelfModifying() {
117         return false;
118     }
119 
120     /**
121      * Returns widest operation type of this operation.
122      *
123      * @return the widest type for this operation
124      */
125     public Type getWidestOperationType() {
126         return Type.OBJECT;
127     }
128 
129     /**
130      * Returns true if this node represents a comparison operator
131      * @return true if comparison
132      */
133     public boolean isComparison() {
134         return false;
135     }
136 
137     /**
138      * For reference copies - ensure that labels in the copy node are unique
139      * using an appropriate copy constructor
140      * @param lc lexical context
141      * @return new node or same of no labels
142      */
143     public Node ensureUniqueLabels(final LexicalContext lc) {
144         return this;
145     }
146 
147     /**
148      * Provides a means to navigate the IR.
149      * @param visitor Node visitor.
150      * @return node the node or its replacement after visitation, null if no further visitations are required
151      */
152     public abstract Node accept(NodeVisitor<? extends LexicalContext> visitor);
153 
154     @Override
155     public String toString() {
156         final StringBuilder sb = new StringBuilder();
157         toString(sb);
158         return sb.toString();
159     }
160 
161     /**
162      * String conversion helper. Fills a {@link StringBuilder} with the
163      * string version of this node
164      *
165      * @param sb a StringBuilder
166      */
167     public abstract void toString(StringBuilder sb);
168 
169     /**
170      * Check if this node has terminal flags, i.e. ends or breaks control flow
171      *
172      * @return true if terminal
173      */
174     public boolean hasTerminalFlags() {
175         return isTerminal() || hasGoto();
176     }
177 
178     /**
179      * Get the finish position for this node in the source string
180      * @return finish
181      */
182     public int getFinish() {
183         return finish;
184     }
185 
186     /**
187      * Set finish position for this node in the source string
188      * @param finish finish
189      */
190     public void setFinish(final int finish) {
191         this.finish = finish;
192     }
193 
194     /**
195      * Check if this function repositions control flow with goto like
196      * semantics, for example {@link BreakNode} or a {@link ForNode} with no test
197      * @return true if node has goto semantics
198      */
199     public boolean hasGoto() {
200         return false;
201     }
202 
203     /**
204      * Get start position for node
205      * @return start position
206      */
207     public int getStart() {
208         return start;
209     }
210 
211     @Override
212     protected Object clone() {
213         try {
214             return super.clone();
215         } catch (final CloneNotSupportedException e) {
216             throw new AssertionError(e);
217         }
218     }
219 
220     @Override
221     public final boolean equals(final Object other) {
222         return super.equals(other);
223     }
224 
225     @Override
226     public final int hashCode() {
227         return super.hashCode();
228     }
229 
230     /**
231      * Return token position from a token descriptor.
232      *
233      * @return Start position of the token in the source.
234      */
235     public int position() {
236         return Token.descPosition(token);
237     }
238 
239     /**
240      * Return token length from a token descriptor.
241      *
242      * @return Length of the token.
243      */
244     public int length() {
245         return Token.descLength(token);
246     }
247 
248     /**
249      * Return token tokenType from a token descriptor.
250      *
251      * @return Type of token.
252      */
253     public TokenType tokenType() {
254         return Token.descType(token);
255     }
256 
257     /**
258      * Test token tokenType.
259      *
260      * @param type a type to check this token against
261      * @return true if token types match.
262      */
263     public boolean isTokenType(final TokenType type) {
264         return Token.descType(token) == type;
265     }
266 
267     /**
268      * Get the token for this location
269      * @return the token
270      */
271     public long getToken() {
272         return token;
273     }
274 
275     /**
276      * Is this a terminal Node, i.e. does it end control flow like a throw or return
277      * expression does?
278      *
279      * @return true if this node is terminal
280      */
281     public boolean isTerminal() {
282         return false;
283     }
284 
285     //on change, we have to replace the entire list, that's we can't simple do ListIterator.set
286     static <T extends Node> List<T> accept(final NodeVisitor<? extends LexicalContext> visitor, final Class<T> clazz, final List<T> list) {
287         boolean changed = false;
288         final List<T> newList = new ArrayList<>();
289 
290         for (final Node node : list) {
291             final T newNode = node == null ? null : clazz.cast(node.accept(visitor));
292             if (newNode != node) {
293                 changed = true;
294             }
295             newList.add(newNode);
296         }
297 
298         return changed ? newList : list;
299     }
300 
301     static <T extends LexicalContextNode> T replaceInLexicalContext(final LexicalContext lc, final T oldNode, final T newNode) {
302         if (lc != null) {
303             lc.replace(oldNode, newNode);
304         }
305         return newNode;
306     }
307 }