blob: 7762a1facd3e3e16236d282d26a9bb88cccfc072 [file] [log] [blame]
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.ir;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import jdk.nashorn.internal.ir.visitor.NodeVisitor;
import jdk.nashorn.internal.parser.Token;
import jdk.nashorn.internal.parser.TokenType;
/**
* Nodes are used to compose Abstract Syntax Trees.
*/
public abstract class Node implements Cloneable, Serializable {
private static final long serialVersionUID = 1L;
/** Constant used for synthetic AST nodes that have no line number. */
public static final int NO_LINE_NUMBER = -1;
/** Constant used for synthetic AST nodes that have no token. */
public static final long NO_TOKEN = 0L;
/** Constant used for synthetic AST nodes that have no finish. */
public static final int NO_FINISH = 0;
/** Start of source range. */
protected final int start;
/** End of source range. */
protected int finish;
/** Token descriptor. */
private final long token;
/**
* Constructor
*
* @param token token
* @param finish finish
*/
public Node(final long token, final int finish) {
this.token = token;
this.start = Token.descPosition(token);
this.finish = finish;
}
/**
* Constructor
*
* @param token token
* @param start start
* @param finish finish
*/
protected Node(final long token, final int start, final int finish) {
this.start = start;
this.finish = finish;
this.token = token;
}
/**
* Copy constructor
*
* @param node source node
*/
protected Node(final Node node) {
this.token = node.token;
this.start = node.start;
this.finish = node.finish;
}
/**
* Is this a loop node?
*
* @return true if atom
*/
public boolean isLoop() {
return false;
}
/**
* Is this an assignment node - for example a var node with an init
* or a binary node that writes to a destination
*
* @return true if assignment
*/
public boolean isAssignment() {
return false;
}
/**
* For reference copies - ensure that labels in the copy node are unique
* using an appropriate copy constructor
* @param lc lexical context
* @return new node or same of no labels
*/
public Node ensureUniqueLabels(final LexicalContext lc) {
return this;
}
/**
* Provides a means to navigate the IR.
* @param visitor Node visitor.
* @return node the node or its replacement after visitation, null if no further visitations are required
*/
public abstract Node accept(NodeVisitor<? extends LexicalContext> visitor);
@Override
public final String toString() {
return toString(true);
}
/*
* Return String representation of this Node.
* @param includeTypeInfo include type information or not
*/
public final String toString(final boolean includeTypeInfo) {
final StringBuilder sb = new StringBuilder();
toString(sb, includeTypeInfo);
return sb.toString();
}
/**
* String conversion helper. Fills a {@link StringBuilder} with the
* string version of this node
*
* @param sb a StringBuilder
*/
public void toString(final StringBuilder sb) {
toString(sb, true);
}
/**
* Print logic that decides whether to show the optimistic type
* or not - for example it should not be printed after just parse,
* when it hasn't been computed, or has been set to a trivially provable
* value
* @param sb string builder
* @param printType print type?
*/
public abstract void toString(final StringBuilder sb, final boolean printType);
/**
* Get the finish position for this node in the source string
* @return finish
*/
public int getFinish() {
return finish;
}
/**
* Set finish position for this node in the source string
* @param finish finish
*/
public void setFinish(final int finish) {
this.finish = finish;
}
/**
* Get start position for node
* @return start position
*/
public int getStart() {
return start;
}
@Override
protected Object clone() {
try {
return super.clone();
} catch (final CloneNotSupportedException e) {
throw new AssertionError(e);
}
}
@Override
public final boolean equals(final Object other) {
return this == other;
}
@Override
public final int hashCode() {
// NOTE: we aren't delegating to Object.hashCode as it still requires trip to the VM for initializing,
// it touches the object header and/or stores the identity hashcode somewhere, etc. There's several
// places in the compiler pipeline that store nodes in maps, so this can get hot.
return Long.hashCode(token);
}
/**
* Return token position from a token descriptor.
*
* @return Start position of the token in the source.
*/
public int position() {
return Token.descPosition(token);
}
/**
* Return token length from a token descriptor.
*
* @return Length of the token.
*/
public int length() {
return Token.descLength(token);
}
/**
* Returns this node's token's type. If you want to check for the node having a specific token type,
* consider using {@link #isTokenType(TokenType)} instead.
*
* @return type of token.
*/
public TokenType tokenType() {
return Token.descType(token);
}
/**
* Tests if this node has the specific token type.
*
* @param type a token type to check this node's token type against
* @return true if token types match.
*/
public boolean isTokenType(final TokenType type) {
return tokenType() == type;
}
/**
* Get the token for this node. If you want to retrieve the token's type, consider using
* {@link #tokenType()} or {@link #isTokenType(TokenType)} instead.
* @return the token
*/
public long getToken() {
return token;
}
//on change, we have to replace the entire list, that's we can't simple do ListIterator.set
static <T extends Node> List<T> accept(final NodeVisitor<? extends LexicalContext> visitor, final List<T> list) {
final int size = list.size();
if (size == 0) {
return list;
}
List<T> newList = null;
for (int i = 0; i < size; i++) {
final T node = list.get(i);
@SuppressWarnings("unchecked")
final T newNode = node == null ? null : (T)node.accept(visitor);
if (newNode != node) {
if (newList == null) {
newList = new ArrayList<>(size);
for (int j = 0; j < i; j++) {
newList.add(list.get(j));
}
}
newList.add(newNode);
} else {
if (newList != null) {
newList.add(node);
}
}
}
return newList == null ? list : newList;
}
static <T extends LexicalContextNode> T replaceInLexicalContext(final LexicalContext lc, final T oldNode, final T newNode) {
if (lc != null) {
lc.replace(oldNode, newNode);
}
return newNode;
}
}