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                    return true;
420            }
421            return false;
422        }
423    
424        /** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */
425        public double getDouble() throws UnsupportedOperationException {
426            throw new UnsupportedOperationException(this + " is not a number node");
427        }
428    
429        /** Can only be called when node has String context. */
430        public String getString() throws UnsupportedOperationException {
431            throw new UnsupportedOperationException(this + " is not a string node");
432        }
433    
434        /** Can only be called when node has String context. */
435        public void setString(String s) throws UnsupportedOperationException {
436            throw new UnsupportedOperationException(this + " is not a string node");
437        }
438    
439        /**
440         * Not usefully implemented.
441         */
442        @Override
443        public final int hashCode() {
444            assert false : "hashCode not designed";
445            return 42; // any arbitrary constant will do
446        }
447    
448        @Override
449        public boolean equals(Object o) {
450            if (!(o instanceof Node)) { return false; }
451            return hasLineno()
452                || this.getIntDatum() == ((Node) o).getIntDatum();
453        }
454    
455        @Override
456        public String toString() {
457            if (Context.printTrees) {
458                StringBuffer sb = new StringBuffer(TokenStream.tokenToName(type));
459                if (this instanceof StringNode) {
460                    sb.append(' ');
461                    sb.append(getString());
462                } else {
463                    switch (type) {
464                        case TokenStream.TARGET:
465                            sb.append(' ');
466                            sb.append(hashCode());
467                            break;
468                        case TokenStream.NUMBER_INT:
469                            sb.append(' ');
470                            sb.append((int) getDouble());
471                            break;
472                        case TokenStream.NUMBER:
473                            sb.append(' ');
474                            sb.append(getDouble());
475                            break;
476                        case TokenStream.FUNCTION:
477                            sb.append(' ');
478                            sb.append(first.getString());
479                            break;
480                    }
481                }
482                if (intDatum != -1) {
483                    sb.append(' ');
484                    sb.append(intDatum);
485                }
486                if (props == null)
487                    return sb.toString();
488    
489                int[] keys = props.getKeys();
490                for (int i = 0; i != keys.length; ++i) {
491                    int key = keys[i];
492                    sb.append(" [");
493                    sb.append(propToString(key));
494                    sb.append(": ");
495                    switch (key) {
496                        case FIXUPS_PROP : // can't add this as it recurses
497                            sb.append("fixups property");
498                            break;
499                        case SOURCE_PROP : // can't add this as it has unprintables
500                            sb.append("source property");
501                            break;
502                        case TARGETBLOCK_PROP : // can't add this as it recurses
503                            sb.append("target block property");
504                            break;
505                        case LASTUSE_PROP :     // can't add this as it is dull
506                            sb.append("last use property");
507                            break;
508                        default :
509                            Object obj = props.getObject(key);
510                            if (obj != null) {
511                                sb.append(obj.toString());
512                            } else {
513                                sb.append(props.getExistingInt(key));
514                            }
515                            break;
516                    }
517                    sb.append(']');
518                }
519                return sb.toString();
520            }
521            return null;
522        }
523    
524        public void setIsSyntheticBlock(boolean val) {
525            isSyntheticBlock = val;
526        }
527    
528        int type;              // type of the node; TokenStream.NAME for example
529        Node next;             // next sibling
530        private Node first;    // first element of a linked list of children
531        private Node last;     // last element of a linked list of children
532        private int intDatum = -1;    // encapsulated int data; depends on type
533        private UintMap props;
534        private boolean isSyntheticBlock = false;
535    }