Fri, 21 Dec 2012 16:36:24 -0400
8005403: Open-source Nashorn
Reviewed-by: attila, hannesw, lagergren, sundar
Contributed-by: james.laskey@oracle.com, akhil.arora@oracle.com, andreas.woess@jku.at, attila.szegedi@oracle.com, hannes.wallnoefer@oracle.com, henry.jen@oracle.com, marcus.lagergren@oracle.com, pavel.semenov@oracle.com, pavel.stepanov@oracle.com, petr.hejl@oracle.com, petr.pisl@oracle.com, sundararajan.athijegannathan@oracle.com
1 /*
2 * Copyright (c) 2010, 2012, 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 java.util.IdentityHashMap;
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.runtime.Source;
35 /**
36 * Nodes are used to compose Abstract Syntax Trees.
37 *
38 */
39 public abstract class Node extends Location {
40 /** Node symbol. */
41 private Symbol nodeSymbol;
43 /** Start of source range. */
44 protected int start;
46 /** End of source range. */
47 protected int finish;
49 /** Has this node been resolved - i.e. emitted code already */
50 private boolean isResolved;
52 /** Is this node terminal */
53 private boolean isTerminal;
55 /** Is this a goto node */
56 private boolean hasGoto;
58 /** Is this a discard */
59 private boolean shouldDiscard;
61 /**
62 * Constructor
63 *
64 * @param source the source
65 * @param token token
66 * @param finish finish
67 */
68 public Node(final Source source, final long token, final int finish) {
69 super(source, token);
71 start = Token.descPosition(token);
72 this.finish = finish;
73 }
75 /**
76 * Copy constructor
77 *
78 * @param node source node
79 */
80 protected Node(final Node node) {
81 super(node);
83 this.nodeSymbol = node.nodeSymbol;
84 this.isResolved = node.isResolved;
85 this.isTerminal = node.isTerminal;
86 this.hasGoto = node.hasGoto;
87 this.shouldDiscard = node.shouldDiscard;
88 this.start = node.start;
89 this.finish = node.finish;
90 }
92 /**
93 * Check if the node has a type. The default behavior is to go into the symbol
94 * and check the symbol type, but there may be overrides, for example in
95 * getters that require a different type than the internal representation
96 *
97 * @return true if a type exists
98 */
99 public boolean hasType() {
100 return getSymbol() != null;
101 }
103 /**
104 * Returns the type of the node. Typically this is the symbol type. No types
105 * are stored in the node itself, unless it implements TypeOverride
106 *
107 * @return the type of the node.
108 */
109 public Type getType() {
110 assert hasType();
111 return nodeSymbol.getSymbolType();
112 }
114 /**
115 * Is this an atom node - for example a literal or an identity
116 *
117 * @return true if atom
118 */
119 public boolean isAtom() {
120 return false;
121 }
123 /**
124 * Is this a loop node?
125 *
126 * @return true if atom
127 */
128 public boolean isLoop() {
129 return false;
130 }
132 /**
133 * Is this an assignment node - for example a var node with an init
134 * or a binary node that writes to a destination
135 *
136 * @return true if assignment
137 */
138 public boolean isAssignment() {
139 return false;
140 }
142 /**
143 * Is this a self modifying assignment?
144 * @return true if self modifying, e.g. a++, or a*= 17
145 */
146 public boolean isSelfModifying() {
147 return false;
148 }
150 /**
151 * Returns widest operation type of this operation.
152 *
153 * @return the widest type for this operation
154 */
155 public Type getWidestOperationType() {
156 return Type.OBJECT;
157 }
159 /**
160 * Test to see if code been generated for this node. Set isResolved if not.
161 *
162 * @return True if node has already been resolved.
163 */
164 public boolean testResolved() {
165 if (isResolved()) {
166 return true;
167 }
169 setIsResolved();
171 return false;
172 }
174 /**
175 * Is this a debug info node like LineNumberNode etc?
176 *
177 * @return true if this is a debug node
178 */
179 public boolean isDebug() {
180 return false;
181 }
183 /**
184 * Helper class used for node cloning
185 */
186 public static final class CopyState {
187 private final IdentityHashMap<Node, Node> cloneMap = new IdentityHashMap<>();
189 /**
190 * Find existing or create new copy of the node.
191 *
192 * @param node Node to copy.
193 *
194 * @return New object.
195 */
196 public Node existingOrCopy(final Node node) {
197 if (node != null) {
198 Node copy = cloneMap.get(node);
200 if (copy == null) {
201 copy = node.copy(this);
202 cloneMap.put(node, copy);
203 }
205 return copy;
206 }
208 return node;
209 }
211 /**
212 * Find existing or use old copy of the node.
213 *
214 * @param node Node to copy.
215 *
216 * @return new object.
217 */
218 public Node existingOrSame(final Node node) {
219 if (node != null) {
220 Node copy = cloneMap.get(node);
222 if (copy == null) {
223 copy = node;
224 }
226 return copy;
227 }
229 return node;
230 }
231 }
233 /**
234 * Deep copy the node.
235 *
236 * @return Deep copy of the Node.
237 */
238 @Override
239 public final Node clone() {
240 return copy(new CopyState());
241 }
243 /**
244 * Deep copy the node.
245 *
246 * @param cs CopyState passed around to re-use certain nodes.
247 * @return Deep copy of the Node.
248 */
249 protected Node copy(final CopyState cs) {
250 return cs.existingOrCopy(this);
251 }
253 /**
254 * Provides a means to navigate the IR.
255 * @param visitor Node visitor.
256 * @return node the node or its replacement after visitation, null if no further visitations are required
257 */
258 public abstract Node accept(NodeVisitor visitor);
260 @Override
261 public String toString() {
262 final StringBuilder sb = new StringBuilder();
263 toString(sb);
264 return sb.toString();
265 }
267 /**
268 * String conversion helper. Fills a {@link StringBuilder} with the
269 * string version of this node
270 *
271 * @param sb a StringBuilder
272 */
273 public abstract void toString(StringBuilder sb);
275 /**
276 * Check if this node has terminal flags, i.e. ends or breaks control flow
277 *
278 * @return true if terminal
279 */
280 public boolean hasTerminalFlags() {
281 return isTerminal || hasGoto;
282 }
284 /**
285 * Copy the terminal flags state of a node to another node
286 *
287 * @param other source node
288 */
289 public void copyTerminalFlags(final Node other) {
290 isTerminal = other.isTerminal;
291 hasGoto = other.hasGoto;
292 }
294 /**
295 * Check if the return value of this expression should be discarded
296 * @return true if return value is discarded
297 */
298 public boolean shouldDiscard() {
299 return shouldDiscard;
300 }
302 /**
303 * Setter that determines whether this node's return value should be discarded
304 * or not
305 *
306 * @param shouldDiscard true if return value is discarded, false otherwise
307 */
308 public void setDiscard(final boolean shouldDiscard) {
309 this.shouldDiscard = shouldDiscard;
310 }
312 /**
313 * Get the finish position for this node in the source string
314 * @return finish
315 */
316 public int getFinish() {
317 return finish;
318 }
320 /**
321 * Set finish position for this node in the source string
322 * @param finish finish
323 */
324 public void setFinish(final int finish) {
325 this.finish = finish;
326 }
328 /**
329 * Check if this function repositions control flow with goto like
330 * semantics, for example {@link BreakNode} or a {@link ForNode} with no test
331 * @return true if node has goto semantics
332 */
333 public boolean hasGoto() {
334 return hasGoto;
335 }
337 /**
338 * Flag this node as having goto semantics as described in {@link Node#hasGoto()}
339 */
340 public void setHasGoto() {
341 this.hasGoto = true;
342 }
344 /**
345 * Check whether this node is resolved, i.e. code has been generated for it
346 * @return true if node is resolved
347 */
348 public boolean isResolved() {
349 return isResolved;
350 }
352 /**
353 * Flag this node as resolved, i.e. code has been generated for it
354 */
355 public void setIsResolved() {
356 this.isResolved = true;
357 }
359 /**
360 * Get start position for node
361 * @return start position
362 */
363 public int getStart() {
364 return start;
365 }
367 /**
368 * Set start position for node
369 * @param start start position
370 */
371 public void setStart(final int start) {
372 this.start = start;
373 }
375 /**
376 * Return the Symbol the compiler has assigned to this Node. The symbol
377 * is the place where it's expression value is stored after evaluation
378 *
379 * @return the symbol
380 */
381 public Symbol getSymbol() {
382 return nodeSymbol;
383 }
385 /**
386 * Assign a symbol to this node. See {@link Node#getSymbol()} for explanation
387 * of what a symbol is
388 *
389 * @param symbol the symbol
390 */
391 public void setSymbol(final Symbol symbol) {
392 nodeSymbol = symbol;
393 }
395 /**
396 * Is this a terminal Node, i.e. does it end control flow like a throw or return
397 * expression does?
398 *
399 * @return true if this node is terminal
400 */
401 public boolean isTerminal() {
402 return isTerminal;
403 }
405 /**
406 * Set this to be a terminal node, i.e. it terminates control flow as described
407 * in {@link Node#isTerminal()}
408 *
409 * @param isTerminal true if this is a terminal node, false otherwise
410 */
411 public void setIsTerminal(final boolean isTerminal) {
412 this.isTerminal = isTerminal;
413 }
415 /**
416 * Return last node in a statement list.
417 *
418 * @param statements Statement list.
419 *
420 * @return Last (non-debug) statement or null if empty block.
421 */
422 public static Node lastStatement(final List<Node> statements) {
423 for (int lastIndex = statements.size() - 1; lastIndex >= 0; lastIndex--) {
424 final Node node = statements.get(lastIndex);
425 if (!node.isDebug()) {
426 return node;
427 }
428 }
430 return null;
431 }
432 }