blob: 5ffee378608c3997fda2ec0c6051d80195dada7b [file] [log] [blame]
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1997-1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Norris Boyd
* Roger Lawrence
* Mike McCabe
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the NPL or the GPL.
*/
// Modified by Google
package com.google.gwt.dev.js.rhino;
/**
* This class implements the root of the intermediate representation.
*
* @author Norris Boyd
* @author Mike McCabe
*/
public class Node implements Cloneable {
private static class NumberNode extends Node {
NumberNode(double number) {
super(TokenStream.NUMBER);
this.number = number;
}
public double getDouble() {
return this.number;
}
public boolean equals(Object o) {
return o instanceof NumberNode
&& getDouble() == ((NumberNode) o).getDouble();
}
private double number;
}
private static class StringNode extends Node {
StringNode(int type, String str) {
super(type);
if (null == str) {
throw new IllegalArgumentException("StringNode: str is null");
}
this.str = str;
}
/** returns the string content.
* @return non null.
*/
public String getString() {
return this.str;
}
/** sets the string content.
* @param str the new value. Non null.
*/
public void setString(String str) {
if (null == str) {
throw new IllegalArgumentException("StringNode: str is null");
}
this.str = str;
}
public boolean equals(Object o) {
if (!(o instanceof StringNode)) { return false; }
return o instanceof StringNode
&& this.str.equals(((StringNode) o).str);
}
private String str;
}
public Node(int nodeType) {
type = nodeType;
}
public Node(int nodeType, Node child) {
type = nodeType;
first = last = child;
child.next = null;
}
public Node(int nodeType, Node left, Node right) {
type = nodeType;
first = left;
last = right;
left.next = right;
right.next = null;
}
public Node(int nodeType, Node left, Node mid, Node right) {
type = nodeType;
first = left;
last = right;
left.next = mid;
mid.next = right;
right.next = null;
}
public Node(int nodeType, Node left, Node mid, Node mid2, Node right) {
type = nodeType;
first = left;
last = right;
left.next = mid;
mid.next = mid2;
mid2.next = right;
right.next = null;
}
public Node(int nodeType, Node[] children) {
this.type = nodeType;
if (children.length != 0) {
this.first = children[0];
this.last = children[children.length - 1];
for (int i = 1; i < children.length; i++) {
if (null != children[i - 1].next) {
// fail early on loops. implies same node in array twice
throw new IllegalArgumentException("duplicate child");
}
children[i - 1].next = children[i];
}
if (null != this.last.next) {
// fail early on loops. implies same node in array twice
throw new IllegalArgumentException("duplicate child");
}
}
}
public Node(int nodeType, int value) {
type = nodeType;
intDatum = value;
}
public Node(int nodeType, Node child, int value) {
this(nodeType, child);
intDatum = value;
}
public Node(int nodeType, Node left, Node right, int value) {
this(nodeType, left, right);
intDatum = value;
}
public Node(int nodeType, Node left, Node mid, Node right, int value) {
this(nodeType, left, mid, right);
intDatum = value;
}
public static Node newNumber(double number) {
return new NumberNode(number);
}
public static Node newString(String str) {
return new StringNode(TokenStream.STRING, str);
}
public static Node newString(int type, String str) {
return new StringNode(type, str);
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public int getIntDatum() {
return this.intDatum;
}
public boolean hasChildren() {
return first != null;
}
public Node getFirstChild() {
return first;
}
public Node getLastChild() {
return last;
}
public Node getNext() {
return next;
}
public int getChildCount() {
int c = 0;
for (Node n = first; n != null; n = n.next)
c++;
return c;
}
public Node getChildBefore(Node child) {
if (child == first)
return null;
Node n = first;
while (n.next != child) {
n = n.next;
if (n == null)
throw new RuntimeException("node is not a child");
}
return n;
}
public Node getLastSibling() {
Node n = this;
while (n.next != null) {
n = n.next;
}
return n;
}
public void addChildToFront(Node child) {
child.next = first;
first = child;
if (last == null) {
last = child;
}
}
public void addChildToBack(Node child) {
child.next = null;
if (last == null) {
first = last = child;
return;
}
last.next = child;
last = child;
}
public void addChildrenToFront(Node children) {
Node lastSib = children.getLastSibling();
lastSib.next = first;
first = children;
if (last == null) {
last = lastSib;
}
}
public void addChildrenToBack(Node children) {
if (last != null) {
last.next = children;
}
last = children.getLastSibling();
if (first == null) {
first = children;
}
}
/**
* Add 'child' before 'node'.
*/
public void addChildBefore(Node newChild, Node node) {
if (newChild.next != null)
throw new RuntimeException(
"newChild had siblings in addChildBefore");
if (first == node) {
newChild.next = first;
first = newChild;
return;
}
Node prev = getChildBefore(node);
addChildAfter(newChild, prev);
}
/**
* Add 'child' after 'node'.
*/
public void addChildAfter(Node newChild, Node node) {
if (newChild.next != null)
throw new RuntimeException(
"newChild had siblings in addChildAfter");
newChild.next = node.next;
node.next = newChild;
if (last == node)
last = newChild;
}
public void removeChild(Node child) {
Node prev = getChildBefore(child);
if (prev == null)
first = first.next;
else
prev.next = child.next;
if (child == last) last = prev;
child.next = null;
}
public void replaceChild(Node child, Node newChild) {
newChild.next = child.next;
if (child == first) {
first = newChild;
} else {
Node prev = getChildBefore(child);
prev.next = newChild;
}
if (child == last)
last = newChild;
child.next = null;
}
public void replaceChildAfter(Node prevChild, Node newChild) {
Node child = prevChild.next;
newChild.next = child.next;
prevChild.next = newChild;
if (child == last)
last = newChild;
child.next = null;
}
public static final int
TARGET_PROP = 1,
BREAK_PROP = 2,
CONTINUE_PROP = 3,
ENUM_PROP = 4,
FUNCTION_PROP = 5,
TEMP_PROP = 6,
LOCAL_PROP = 7,
CODEOFFSET_PROP = 8,
FIXUPS_PROP = 9,
VARS_PROP = 10,
USES_PROP = 11,
REGEXP_PROP = 12,
CASES_PROP = 13,
DEFAULT_PROP = 14,
CASEARRAY_PROP = 15,
SOURCENAME_PROP = 16,
SOURCE_PROP = 17,
TYPE_PROP = 18,
SPECIAL_PROP_PROP = 19,
LABEL_PROP = 20,
FINALLY_PROP = 21,
LOCALCOUNT_PROP = 22,
/*
the following properties are defined and manipulated by the
optimizer -
TARGETBLOCK_PROP - the block referenced by a branch node
VARIABLE_PROP - the variable referenced by a BIND or NAME node
LASTUSE_PROP - that variable node is the last reference before
a new def or the end of the block
ISNUMBER_PROP - this node generates code on Number children and
delivers a Number result (as opposed to Objects)
DIRECTCALL_PROP - this call node should emit code to test the function
object against the known class and call diret if it
matches.
*/
TARGETBLOCK_PROP = 23,
VARIABLE_PROP = 24,
LASTUSE_PROP = 25,
ISNUMBER_PROP = 26,
DIRECTCALL_PROP = 27,
BASE_LINENO_PROP = 28,
END_LINENO_PROP = 29,
SPECIALCALL_PROP = 30,
DEBUGSOURCE_PROP = 31;
public static final int // this value of the ISNUMBER_PROP specifies
BOTH = 0, // which of the children are Number types
LEFT = 1,
RIGHT = 2;
private static final String propToString(int propType) {
switch (propType) {
case TARGET_PROP: return "target";
case BREAK_PROP: return "break";
case CONTINUE_PROP: return "continue";
case ENUM_PROP: return "enum";
case FUNCTION_PROP: return "function";
case TEMP_PROP: return "temp";
case LOCAL_PROP: return "local";
case CODEOFFSET_PROP: return "codeoffset";
case FIXUPS_PROP: return "fixups";
case VARS_PROP: return "vars";
case USES_PROP: return "uses";
case REGEXP_PROP: return "regexp";
case CASES_PROP: return "cases";
case DEFAULT_PROP: return "default";
case CASEARRAY_PROP: return "casearray";
case SOURCENAME_PROP: return "sourcename";
case SOURCE_PROP: return "source";
case TYPE_PROP: return "type";
case SPECIAL_PROP_PROP: return "special_prop";
case LABEL_PROP: return "label";
case FINALLY_PROP: return "finally";
case LOCALCOUNT_PROP: return "localcount";
case TARGETBLOCK_PROP: return "targetblock";
case VARIABLE_PROP: return "variable";
case LASTUSE_PROP: return "lastuse";
case ISNUMBER_PROP: return "isnumber";
case DIRECTCALL_PROP: return "directcall";
case BASE_LINENO_PROP: return "base_lineno";
case END_LINENO_PROP: return "end_lineno";
case SPECIALCALL_PROP: return "specialcall";
case DEBUGSOURCE_PROP: return "debugsource";
default: Context.codeBug();
}
return null;
}
public Object getProp(int propType) {
if (props == null)
return null;
return props.getObject(propType);
}
public int getIntProp(int propType, int defaultValue) {
if (props == null)
return defaultValue;
return props.getInt(propType, defaultValue);
}
public int getExistingIntProp(int propType) {
return props.getExistingInt(propType);
}
public void putProp(int propType, Object prop) {
if (prop == null) {
removeProp(propType);
}
else {
if (props == null) {
props = new UintMap(2);
}
props.put(propType, prop);
}
}
public void putIntProp(int propType, int prop) {
if (props == null)
props = new UintMap(2);
props.put(propType, prop);
}
public void removeProp(int propType) {
if (props != null) {
props.remove(propType);
}
}
public int getOperation() {
switch (type) {
case TokenStream.EQOP:
case TokenStream.RELOP:
case TokenStream.UNARYOP:
case TokenStream.PRIMARY:
return intDatum;
}
Context.codeBug();
return 0;
}
public int getLineno() {
if (hasLineno()) {
return intDatum;
}
return -1;
}
private boolean hasLineno() {
switch (type) {
case TokenStream.EXPRSTMT:
case TokenStream.BLOCK:
case TokenStream.VAR:
case TokenStream.WHILE:
case TokenStream.DO:
case TokenStream.SWITCH:
case TokenStream.CATCH:
case TokenStream.THROW:
case TokenStream.RETURN:
case TokenStream.BREAK:
case TokenStream.CONTINUE:
case TokenStream.WITH:
return true;
}
return false;
}
/** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */
public double getDouble() throws UnsupportedOperationException {
throw new UnsupportedOperationException(this + " is not a number node");
}
/** Can only be called when node has String context. */
public String getString() throws UnsupportedOperationException {
throw new UnsupportedOperationException(this + " is not a string node");
}
/** Can only be called when node has String context. */
public void setString(String s) throws UnsupportedOperationException {
throw new UnsupportedOperationException(this + " is not a string node");
}
/**
* Not usefully implemented.
*/
public final int hashCode() {
assert false : "hashCode not designed";
return 42; // any arbitrary constant will do
}
public boolean equals(Object o) {
if (!(o instanceof Node)) { return false; }
return hasLineno()
|| this.getIntDatum() == ((Node) o).getIntDatum();
}
public Node cloneNode() {
Node result;
try {
result = (Node) super.clone();
result.next = null;
result.first = null;
result.last = null;
}
catch (CloneNotSupportedException e) {
throw new RuntimeException(e.getMessage());
}
return result;
}
public Node cloneTree() {
Node result = cloneNode();
for (Node n2 = getFirstChild(); n2 != null; n2 = n2.getNext()) {
Node n2clone = n2.cloneTree();
if (result.last != null) {
result.last.next = n2clone;
}
if (result.first == null) {
result.first = n2clone;
}
result.last = n2clone;
}
return result;
}
public String toString() {
if (Context.printTrees) {
StringBuffer sb = new StringBuffer(TokenStream.tokenToName(type));
if (this instanceof StringNode) {
sb.append(' ');
sb.append(getString());
} else {
switch (type) {
case TokenStream.TARGET:
sb.append(' ');
sb.append(hashCode());
break;
case TokenStream.NUMBER:
sb.append(' ');
sb.append(getDouble());
break;
case TokenStream.FUNCTION:
sb.append(' ');
sb.append(first.getString());
break;
}
}
if (intDatum != -1) {
sb.append(' ');
sb.append(intDatum);
}
if (props == null)
return sb.toString();
int[] keys = props.getKeys();
for (int i = 0; i != keys.length; ++i) {
int key = keys[i];
sb.append(" [");
sb.append(propToString(key));
sb.append(": ");
switch (key) {
case FIXUPS_PROP : // can't add this as it recurses
sb.append("fixups property");
break;
case SOURCE_PROP : // can't add this as it has unprintables
sb.append("source property");
break;
case TARGETBLOCK_PROP : // can't add this as it recurses
sb.append("target block property");
break;
case LASTUSE_PROP : // can't add this as it is dull
sb.append("last use property");
break;
default :
Object obj = props.getObject(key);
if (obj != null) {
sb.append(obj.toString());
} else {
sb.append(props.getExistingInt(key));
}
break;
}
sb.append(']');
}
return sb.toString();
}
return null;
}
public String toStringTree() {
return toStringTreeHelper(0);
}
private String toStringTreeHelper(int level) {
if (Context.printTrees) {
StringBuffer s = new StringBuffer();
for (int i=0; i < level; i++) {
s.append(" ");
}
s.append(toString());
s.append('\n');
for (Node cursor = getFirstChild(); cursor != null;
cursor = cursor.getNext())
{
Node n = cursor;
s.append(n.toStringTreeHelper(level+1));
}
return s.toString();
}
return "";
}
/**
* Checks if the subtree under this node is the same as another subtree.
* Returns null if it's equal, or a message describing the differences.
*/
public String checkTreeEquals(Node node2) {
boolean eq = false;
if (type == node2.getType() &&
getChildCount() == node2.getChildCount() &&
getClass() == node2.getClass()) {
eq = this.equals(node2);
}
if (!eq) {
return "Node tree inequality:\nTree1:\n" + toStringTreeHelper(1) +
"\n\nTree2:\n" + node2.toStringTreeHelper(1);
}
String res = null;
Node n, n2;
for (n = first, n2 = node2.first;
res == null && n != null;
n = n.next, n2 = n2.next) {
res = n.checkTreeEquals(n2);
if (res != null) {
return res;
}
}
return res;
}
public void setIsSyntheticBlock(boolean val) {
isSyntheticBlock = val;
}
public boolean isSyntheticBlock() {
return isSyntheticBlock;
}
int type; // type of the node; TokenStream.NAME for example
Node next; // next sibling
private Node first; // first element of a linked list of children
private Node last; // last element of a linked list of children
private int intDatum = -1; // encapsulated int data; depends on type
private UintMap props;
private boolean isSyntheticBlock = false;
}