001    /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
002     *
003     * The contents of this file are subject to the Netscape Public
004     * License Version 1.1 (the "License"); you may not use this file
005     * except in compliance with the License. You may obtain a copy of
006     * the License at http://www.mozilla.org/NPL/
007     *
008     * Software distributed under the License is distributed on an "AS
009     * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
010     * implied. See the License for the specific language governing
011     * rights and limitations under the License.
012     *
013     * The Original Code is Rhino code, released
014     * May 6, 1999.
015     *
016     * The Initial Developer of the Original Code is Netscape
017     * Communications Corporation.  Portions created by Netscape are
018     * Copyright (C) 1997-1999 Netscape Communications Corporation. All
019     * Rights Reserved.
020     *
021     * Contributor(s):
022     * Norris Boyd
023     * Roger Lawrence
024     * Mike McCabe
025     *
026     * Alternatively, the contents of this file may be used under the
027     * terms of the GNU Public License (the "GPL"), in which case the
028     * provisions of the GPL are applicable instead of those above.
029     * If you wish to allow use of your version of this file only
030     * under the terms of the GPL and not to allow others to use your
031     * version of this file under the NPL, indicate your decision by
032     * deleting the provisions above and replace them with the notice
033     * and other provisions required by the GPL.  If you do not delete
034     * the provisions above, a recipient may use your version of this
035     * file under either the NPL or the GPL.
036     */
037    // Modified by Google
038    
039    package com.google.gwt.dev.js.rhino;
040    
041    /**
042     * This class implements the root of the intermediate representation.
043     */
044    
045    public class Node implements Cloneable {
046    
047        private static class NumberNode extends Node {
048    
049            NumberNode(int number) {
050                super(TokenStream.NUMBER_INT);
051                this.number = number;
052            }
053    
054            NumberNode(double number) {
055                super(TokenStream.NUMBER);
056                this.number = number;
057            }
058    
059            @Override
060            public double getDouble() {
061              return this.number;
062            }
063    
064            @Override
065            public boolean equals(Object o) {
066                return o instanceof NumberNode
067                    && getDouble() == ((NumberNode) o).getDouble();
068            }
069    
070            private double number;
071        }
072    
073        private static class StringNode extends Node {
074    
075            StringNode(int type, String str) {
076                super(type);
077                if (null == str) {
078                    throw new IllegalArgumentException("StringNode: str is null");
079                }
080                this.str = str;
081            }
082    
083            /** returns the string content.
084              * @return non null.
085              */
086            @Override
087            public String getString() {
088                return this.str;
089            }
090    
091            /** sets the string content.
092              * @param str the new value.  Non null.
093              */
094            @Override
095            public void setString(String str) {
096                if (null == str) {
097                    throw new IllegalArgumentException("StringNode: str is null");
098                }
099                this.str = str;
100            }
101    
102            @Override
103            public boolean equals(Object o) {
104                if (!(o instanceof StringNode)) { return false; }
105                return o instanceof StringNode
106                    && this.str.equals(((StringNode) o).str);
107            }
108    
109            private String str;
110        }
111    
112        public Node(int nodeType) {
113            type = nodeType;
114        }
115    
116        public Node(int nodeType, Node child) {
117            type = nodeType;
118            first = last = child;
119            child.next = null;
120        }
121    
122        public Node(int nodeType, Node left, Node right) {
123            type = nodeType;
124            first = left;
125            last = right;
126            left.next = right;
127            right.next = null;
128        }
129    
130        public Node(int nodeType, Node left, Node mid, Node right) {
131            type = nodeType;
132            first = left;
133            last = right;
134            left.next = mid;
135            mid.next = right;
136            right.next = null;
137        }
138    
139        public Node(int nodeType, Node left, Node mid, Node mid2, Node right) {
140            type = nodeType;
141            first = left;
142            last = right;
143            left.next = mid;
144            mid.next = mid2;
145            mid2.next = right;
146            right.next = null;
147        }
148    
149        public Node(int nodeType, Node[] children) {
150            this.type = nodeType;
151            if (children.length != 0) {
152                this.first = children[0];
153                this.last = children[children.length - 1];
154    
155                for (int i = 1; i < children.length; i++) {
156                    if (null != children[i - 1].next) {
157                        // fail early on loops.  implies same node in array twice
158                        throw new IllegalArgumentException("duplicate child");
159                    }
160                    children[i - 1].next = children[i];
161                }
162                if (null != this.last.next) {
163                    // fail early on loops.  implies same node in array twice
164                    throw new IllegalArgumentException("duplicate child");
165                }
166            }
167        }
168    
169        public Node(int nodeType, int value) {
170            type = nodeType;
171            intDatum = value;
172        }
173    
174        public Node(int nodeType, Node child, int value) {
175            this(nodeType, child);
176            intDatum = value;
177        }
178    
179        public Node(int nodeType, Node left, Node right, int value) {
180            this(nodeType, left, right);
181            intDatum = value;
182        }
183    
184        public Node(int nodeType, Node left, Node mid, Node right, int value) {
185            this(nodeType, left, mid, right);
186            intDatum = value;
187        }
188    
189        public static Node newNumber(int number) {
190            return new NumberNode(number);
191        }
192    
193        public static Node newNumber(double number) {
194            return new NumberNode(number);
195        }
196    
197        public static Node newString(String str) {
198            return new StringNode(TokenStream.STRING, str);
199        }
200    
201        public static Node newString(int type, String str) {
202            return new StringNode(type, str);
203        }
204    
205        public int getType() {
206            return type;
207        }
208    
209        public void setType(int type) {
210            this.type = type;
211        }
212    
213        public int getIntDatum() {
214            return this.intDatum;
215        }
216    
217        public Node getFirstChild() {
218            return first;
219        }
220    
221        public Node getNext() {
222            return next;
223        }
224    
225        public int getChildCount() {
226            int c = 0;
227            for (Node n = first; n != null; n = n.next)
228                c++;
229    
230            return c;
231        }
232    
233        public Node getLastSibling() {
234            Node n = this;
235            while (n.next != null) {
236                n = n.next;
237            }
238            return n;
239        }
240    
241        public void addChildToBack(Node child) {
242            child.next = null;
243            if (last == null) {
244                first = last = child;
245                return;
246            }
247            last.next = child;
248            last = child;
249        }
250    
251        public void addChildrenToBack(Node children) {
252            if (last != null) {
253                last.next = children;
254            }
255            last = children.getLastSibling();
256            if (first == null) {
257                first = children;
258            }
259        }
260    
261        public static final int
262            TARGET_PROP       =  1,
263            BREAK_PROP        =  2,
264            CONTINUE_PROP     =  3,
265            ENUM_PROP         =  4,
266            FUNCTION_PROP     =  5,
267            TEMP_PROP         =  6,
268            LOCAL_PROP        =  7,
269            CODEOFFSET_PROP   =  8,
270            FIXUPS_PROP       =  9,
271            VARS_PROP         = 10,
272            USES_PROP         = 11,
273            REGEXP_PROP       = 12,
274            CASES_PROP        = 13,
275            DEFAULT_PROP      = 14,
276            CASEARRAY_PROP    = 15,
277            SOURCENAME_PROP   = 16,
278            SOURCE_PROP       = 17,
279            TYPE_PROP         = 18,
280            SPECIAL_PROP_PROP = 19,
281            LABEL_PROP        = 20,
282            FINALLY_PROP      = 21,
283            LOCALCOUNT_PROP   = 22,
284        /*
285            the following properties are defined and manipulated by the
286            optimizer -
287            TARGETBLOCK_PROP - the block referenced by a branch node
288            VARIABLE_PROP - the variable referenced by a BIND or NAME node
289            LASTUSE_PROP - that variable node is the last reference before
290                            a new def or the end of the block
291            ISNUMBER_PROP - this node generates code on Number children and
292                            delivers a Number result (as opposed to Objects)
293            DIRECTCALL_PROP - this call node should emit code to test the function
294                              object against the known class and call diret if it
295                              matches.
296        */
297    
298            TARGETBLOCK_PROP  = 23,
299            VARIABLE_PROP     = 24,
300            LASTUSE_PROP      = 25,
301            ISNUMBER_PROP     = 26,
302            DIRECTCALL_PROP   = 27,
303    
304            BASE_LINENO_PROP  = 28,
305            END_LINENO_PROP   = 29,
306            SPECIALCALL_PROP  = 30,
307            DEBUGSOURCE_PROP  = 31;
308    
309        public static final int    // this value of the ISNUMBER_PROP specifies
310            BOTH = 0,               // which of the children are Number types
311            LEFT = 1,
312            RIGHT = 2;
313    
314        private static final String propToString(int propType) {
315            switch (propType) {
316                case TARGET_PROP:        return "target";
317                case BREAK_PROP:         return "break";
318                case CONTINUE_PROP:      return "continue";
319                case ENUM_PROP:          return "enum";
320                case FUNCTION_PROP:      return "function";
321                case TEMP_PROP:          return "temp";
322                case LOCAL_PROP:         return "local";
323                case CODEOFFSET_PROP:    return "codeoffset";
324                case FIXUPS_PROP:        return "fixups";
325                case VARS_PROP:          return "vars";
326                case USES_PROP:          return "uses";
327                case REGEXP_PROP:        return "regexp";
328                case CASES_PROP:         return "cases";
329                case DEFAULT_PROP:       return "default";
330                case CASEARRAY_PROP:     return "casearray";
331                case SOURCENAME_PROP:    return "sourcename";
332                case SOURCE_PROP:        return "source";
333                case TYPE_PROP:          return "type";
334                case SPECIAL_PROP_PROP:  return "special_prop";
335                case LABEL_PROP:         return "label";
336                case FINALLY_PROP:       return "finally";
337                case LOCALCOUNT_PROP:    return "localcount";
338    
339                case TARGETBLOCK_PROP:   return "targetblock";
340                case VARIABLE_PROP:      return "variable";
341                case LASTUSE_PROP:       return "lastuse";
342                case ISNUMBER_PROP:      return "isnumber";
343                case DIRECTCALL_PROP:    return "directcall";
344    
345                case BASE_LINENO_PROP:   return "base_lineno";
346                case END_LINENO_PROP:    return "end_lineno";
347                case SPECIALCALL_PROP:   return "specialcall";
348                case DEBUGSOURCE_PROP:   return "debugsource";
349    
350                default: Context.codeBug();
351    
352            }
353            return null;
354        }
355    
356        public Object getProp(int propType) {
357            if (props == null)
358                return null;
359            return props.getObject(propType);
360        }
361    
362        public void putProp(int propType, Object prop) {
363            if (prop == null) {
364                removeProp(propType);
365            }
366            else {
367                if (props == null) {
368                    props = new UintMap(2);
369                }
370                props.put(propType, prop);
371            }
372        }
373    
374        public void putIntProp(int propType, int prop) {
375            if (props == null)
376                props = new UintMap(2);
377            props.put(propType, prop);
378        }
379    
380        public void removeProp(int propType) {
381            if (props != null) {
382                props.remove(propType);
383            }
384        }
385    
386        public int getOperation() {
387            switch (type) {
388                case TokenStream.EQOP:
389                case TokenStream.RELOP:
390                case TokenStream.UNARYOP:
391                case TokenStream.PRIMARY:
392                    return intDatum;
393            }
394            Context.codeBug();
395            return 0;
396        }
397    
398        public int getLineno() {
399            if (hasLineno()) {
400                return intDatum;
401            }
402            return -1;
403        }
404    
405        private boolean hasLineno() {
406            switch (type) {
407                case TokenStream.EXPRSTMT:
408                case TokenStream.BLOCK:
409                case TokenStream.VAR:
410                case TokenStream.WHILE:
411                case TokenStream.DO:
412                case TokenStream.SWITCH:
413                case TokenStream.CATCH:
414                case TokenStream.THROW:
415                case TokenStream.RETURN:
416                case TokenStream.BREAK:
417                case TokenStream.CONTINUE:
418                case TokenStream.WITH:
419                case TokenStream.IF:
420                    return true;
421            }
422            return false;
423        }
424    
425        /** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */
426        public double getDouble() throws UnsupportedOperationException {
427            throw new UnsupportedOperationException(this + " is not a number node");
428        }
429    
430        /** Can only be called when node has String context. */
431        public String getString() throws UnsupportedOperationException {
432            throw new UnsupportedOperationException(this + " is not a string node");
433        }
434    
435        /** Can only be called when node has String context. */
436        public void setString(String s) throws UnsupportedOperationException {
437            throw new UnsupportedOperationException(this + " is not a string node");
438        }
439    
440        /**
441         * Not usefully implemented.
442         */
443        @Override
444        public final int hashCode() {
445            assert false : "hashCode not designed";
446            return 42; // any arbitrary constant will do
447        }
448    
449        @Override
450        public boolean equals(Object o) {
451            if (!(o instanceof Node)) { return false; }
452            return hasLineno()
453                || this.getIntDatum() == ((Node) o).getIntDatum();
454        }
455    
456        @Override
457        public String toString() {
458            if (Context.printTrees) {
459                StringBuffer sb = new StringBuffer(TokenStream.tokenToName(type));
460                if (this instanceof StringNode) {
461                    sb.append(' ');
462                    sb.append(getString());
463                } else {
464                    switch (type) {
465                        case TokenStream.TARGET:
466                            sb.append(' ');
467                            sb.append(hashCode());
468                            break;
469                        case TokenStream.NUMBER_INT:
470                            sb.append(' ');
471                            sb.append((int) getDouble());
472                            break;
473                        case TokenStream.NUMBER:
474                            sb.append(' ');
475                            sb.append(getDouble());
476                            break;
477                        case TokenStream.FUNCTION:
478                            sb.append(' ');
479                            sb.append(first.getString());
480                            break;
481                    }
482                }
483                if (intDatum != -1) {
484                    sb.append(' ');
485                    sb.append(intDatum);
486                }
487                if (props == null)
488                    return sb.toString();
489    
490                int[] keys = props.getKeys();
491                for (int i = 0; i != keys.length; ++i) {
492                    int key = keys[i];
493                    sb.append(" [");
494                    sb.append(propToString(key));
495                    sb.append(": ");
496                    switch (key) {
497                        case FIXUPS_PROP : // can't add this as it recurses
498                            sb.append("fixups property");
499                            break;
500                        case SOURCE_PROP : // can't add this as it has unprintables
501                            sb.append("source property");
502                            break;
503                        case TARGETBLOCK_PROP : // can't add this as it recurses
504                            sb.append("target block property");
505                            break;
506                        case LASTUSE_PROP :     // can't add this as it is dull
507                            sb.append("last use property");
508                            break;
509                        default :
510                            Object obj = props.getObject(key);
511                            if (obj != null) {
512                                sb.append(obj.toString());
513                            } else {
514                                sb.append(props.getExistingInt(key));
515                            }
516                            break;
517                    }
518                    sb.append(']');
519                }
520                return sb.toString();
521            }
522            return null;
523        }
524    
525        public void setIsSyntheticBlock(boolean val) {
526            isSyntheticBlock = val;
527        }
528    
529        int type;              // type of the node; TokenStream.NAME for example
530        Node next;             // next sibling
531        private Node first;    // first element of a linked list of children
532        private Node last;     // last element of a linked list of children
533        private int intDatum = -1;    // encapsulated int data; depends on type
534        private UintMap props;
535        private boolean isSyntheticBlock = false;
536    }