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.io.Serializable; 29 import java.util.ArrayList; 30 import java.util.List; 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, Serializable { 39 private static final long serialVersionUID = 1L; 40 41 /** Constant used for synthetic AST nodes that have no line number. */ 42 public static final int NO_LINE_NUMBER = -1; 43 44 /** Constant used for synthetic AST nodes that have no token. */ 45 public static final long NO_TOKEN = 0L; 46 47 /** Constant used for synthetic AST nodes that have no finish. */ 48 public static final int NO_FINISH = 0; 49 50 /** Start of source range. */ 51 protected final int start; 52 53 /** End of source range. */ 54 protected final int finish; 55 56 /** Token descriptor. */ 57 private final long token; 58 59 /** 60 * Constructor 61 * 62 * @param token token 63 * @param finish finish 64 */ 65 public Node(final long token, final int finish) { 66 this.token = token; 67 this.start = Token.descPosition(token); 68 this.finish = finish; 69 } 70 71 /** 72 * Constructor 73 * 74 * @param token token 75 * @param start start 76 * @param finish finish 77 */ 78 protected Node(final long token, final int start, final int finish) { 79 this.start = start; 80 this.finish = finish; 81 this.token = token; 82 } 83 84 /** 85 * Copy constructor 86 * 87 * @param node source node 88 */ 89 protected Node(final Node node) { 90 this.token = node.token; 91 this.start = node.start; 92 this.finish = node.finish; 93 } 94 95 /** 96 * Copy constructor that overrides finish 97 * 98 * @param node source node 99 * @param finish Last character 100 */ 101 protected Node(final Node node, final int finish) { 102 this.token = node.token; 103 this.start = node.start; 104 this.finish = finish; 105 } 106 107 /** 108 * Is this a loop node? 109 * 110 * @return true if atom 111 */ 112 public boolean isLoop() { 113 return false; 114 } 115 116 /** 117 * Is this an assignment node - for example a var node with an init 118 * or a binary node that writes to a destination 119 * 120 * @return true if assignment 121 */ 122 public boolean isAssignment() { 123 return false; 124 } 125 126 /** 127 * For reference copies - ensure that labels in the copy node are unique 128 * using an appropriate copy constructor 129 * @param lc lexical context 130 * @return new node or same of no labels 131 */ 132 public Node ensureUniqueLabels(final LexicalContext lc) { 133 return this; 134 } 135 136 /** 137 * Provides a means to navigate the IR. 138 * @param visitor Node visitor. 139 * @return node the node or its replacement after visitation, null if no further visitations are required 140 */ 141 public abstract Node accept(NodeVisitor<? extends LexicalContext> visitor); 142 143 @Override 144 public final String toString() { 145 return toString(true); 146 } 147 148 /* 149 * Return String representation of this Node. 150 * @param includeTypeInfo include type information or not 151 */ 152 public final String toString(final boolean includeTypeInfo) { 153 final StringBuilder sb = new StringBuilder(); 154 toString(sb, includeTypeInfo); 155 return sb.toString(); 156 } 157 158 /** 159 * String conversion helper. Fills a {@link StringBuilder} with the 160 * string version of this node 161 * 162 * @param sb a StringBuilder 163 */ 164 public void toString(final StringBuilder sb) { 165 toString(sb, true); 166 } 167 168 /** 169 * Print logic that decides whether to show the optimistic type 170 * or not - for example it should not be printed after just parse, 171 * when it hasn't been computed, or has been set to a trivially provable 172 * value 173 * @param sb string builder 174 * @param printType print type? 175 */ 176 public abstract void toString(final StringBuilder sb, final boolean printType); 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 * Get start position for node 188 * @return start position 189 */ 190 public int getStart() { 191 return start; 192 } 193 194 /** 195 * Integer to sort nodes in source order. This order is 196 * used by parser API to sort statements in correct order. 197 * By default, this is the start position of this node. 198 * 199 * @return int code to sort this node. 200 */ 201 public int getSourceOrder() { 202 return getStart(); 203 } 204 205 @Override 206 protected Object clone() { 207 try { 208 return super.clone(); 209 } catch (final CloneNotSupportedException e) { 210 throw new AssertionError(e); 211 } 212 } 213 214 @Override 215 public final boolean equals(final Object other) { 216 return this == other; 217 } 218 219 @Override 220 public final int hashCode() { 221 // NOTE: we aren't delegating to Object.hashCode as it still requires trip to the VM for initializing, 222 // it touches the object header and/or stores the identity hashcode somewhere, etc. There's several 223 // places in the compiler pipeline that store nodes in maps, so this can get hot. 224 return Long.hashCode(token); 225 } 226 227 /** 228 * Return token position from a token descriptor. 229 * 230 * @return Start position of the token in the source. 231 */ 232 public int position() { 233 return Token.descPosition(token); 234 } 235 236 /** 237 * Return token length from a token descriptor. 238 * 239 * @return Length of the token. 240 */ 241 public int length() { 242 return Token.descLength(token); 243 } 244 245 /** 246 * Returns this node's token's type. If you want to check for the node having a specific token type, 247 * consider using {@link #isTokenType(TokenType)} instead. 248 * 249 * @return type of token. 250 */ 251 public TokenType tokenType() { 252 return Token.descType(token); 253 } 254 255 /** 256 * Tests if this node has the specific token type. 257 * 258 * @param type a token type to check this node's token type against 259 * @return true if token types match. 260 */ 261 public boolean isTokenType(final TokenType type) { 262 return tokenType() == type; 263 } 264 265 /** 266 * Get the token for this node. If you want to retrieve the token's type, consider using 267 * {@link #tokenType()} or {@link #isTokenType(TokenType)} instead. 268 * @return the token 269 */ 270 public long getToken() { 271 return token; 272 } 273 274 //on change, we have to replace the entire list, that's we can't simple do ListIterator.set 275 static <T extends Node> List<T> accept(final NodeVisitor<? extends LexicalContext> visitor, final List<T> list) { 276 final int size = list.size(); 277 if (size == 0) { 278 return list; 279 } 280 281 List<T> newList = null; 282 283 for (int i = 0; i < size; i++) { 284 final T node = list.get(i); 285 @SuppressWarnings("unchecked") 286 final T newNode = node == null ? null : (T)node.accept(visitor); 287 if (newNode != node) { 288 if (newList == null) { 289 newList = new ArrayList<>(size); 290 for (int j = 0; j < i; j++) { 291 newList.add(list.get(j)); 292 } 293 } 294 newList.add(newNode); 295 } else { 296 if (newList != null) { 297 newList.add(node); 298 } 299 } 300 } 301 302 return newList == null ? list : newList; 303 } 304 305 static <T extends LexicalContextNode> T replaceInLexicalContext(final LexicalContext lc, final T oldNode, final T newNode) { 306 if (lc != null) { 307 lc.replace(oldNode, newNode); 308 } 309 return newNode; 310 } 311 }