/* -*- 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; }