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     *
024     * Alternatively, the contents of this file may be used under the
025     * terms of the GNU Public License (the "GPL"), in which case the
026     * provisions of the GPL are applicable instead of those above.
027     * If you wish to allow use of your version of this file only
028     * under the terms of the GPL and not to allow others to use your
029     * version of this file under the NPL, indicate your decision by
030     * deleting the provisions above and replace them with the notice
031     * and other provisions required by the GPL.  If you do not delete
032     * the provisions above, a recipient may use your version of this
033     * file under either the NPL or the GPL.
034     */
035    
036    package com.google.gwt.dev.js.rhino;
037    
038    /**
039     * This class allows the creation of nodes, and follows the Factory pattern.
040     *
041     * @see Node
042     */
043    public class IRFactory {
044    
045        public IRFactory(TokenStream ts) {
046            this.ts = ts;
047        }
048    
049        /**
050         * Script (for associating file/url names with toplevel scripts.)
051         */
052        public Object createScript(Object body, String sourceName,
053                                   int baseLineno, int endLineno, Object source)
054        {
055            Node result = new Node(TokenStream.SCRIPT);
056            Node children = ((Node) body).getFirstChild();
057            if (children != null)
058                result.addChildrenToBack(children);
059            
060            result.putProp(Node.SOURCENAME_PROP, sourceName);
061            result.putIntProp(Node.BASE_LINENO_PROP, baseLineno);
062            result.putIntProp(Node.END_LINENO_PROP, endLineno);
063            if (source != null)
064                result.putProp(Node.SOURCE_PROP, source);
065            return result;
066        }
067    
068        /**
069         * Leaf
070         */
071        public Object createLeaf(int nodeType) {
072                return new Node(nodeType);
073        }
074    
075        public Object createLeaf(int nodeType, int nodeOp) {
076            return new Node(nodeType, nodeOp);
077        }
078    
079        public int getLeafType(Object leaf) {
080            Node n = (Node) leaf;
081            return n.getType();
082        }
083    
084        /**
085         * Statement leaf nodes.
086         */
087    
088        public Object createSwitch(int lineno) {
089            return new Node(TokenStream.SWITCH, lineno);
090        }
091    
092        public Object createVariables(int lineno) {
093            return new Node(TokenStream.VAR, lineno);
094        }
095    
096        public Object createExprStatement(Object expr, int lineno) {
097            return new Node(TokenStream.EXPRSTMT, (Node) expr, lineno);
098        }
099    
100        /**
101         * Name
102         */
103        public Object createName(String name) {
104            return Node.newString(TokenStream.NAME, name);
105        }
106    
107        /**
108         * String (for literals)
109         */
110        public Object createString(String string) {
111            return Node.newString(string);
112        }
113    
114        /**
115         * Number (for literals)
116         */
117        public Object createNumber(int number) {
118            return Node.newNumber(number);
119        }
120    
121        public Object createNumber(double number) {
122            return Node.newNumber(number);
123        }
124    
125        /**
126         * Catch clause of try/catch/finally
127         * @param varName the name of the variable to bind to the exception
128         * @param catchCond the condition under which to catch the exception.
129         *                  May be null if no condition is given.
130         * @param stmts the statements in the catch clause
131         * @param lineno the starting line number of the catch clause
132         */
133        public Object createCatch(String varName, Object catchCond, Object stmts,
134                                  int lineno)
135        {
136            if (catchCond == null) {
137                catchCond = new Node(TokenStream.PRIMARY, TokenStream.TRUE);
138            }
139            return new Node(TokenStream.CATCH, (Node)createName(varName),
140                                   (Node)catchCond, (Node)stmts, lineno);
141        }
142    
143        /**
144         * Throw
145         */
146        public Object createThrow(Object expr, int lineno) {
147            return new Node(TokenStream.THROW, (Node)expr, lineno);
148        }
149    
150        /**
151         * Return
152         */
153        public Object createReturn(Object expr, int lineno) {
154            return expr == null
155                ? new Node(TokenStream.RETURN, lineno)
156                : new Node(TokenStream.RETURN, (Node)expr, lineno);
157        }
158    
159        /**
160         * Label
161         */
162        public Object createLabel(String label, int lineno) {
163            Node result = new Node(TokenStream.LABEL, lineno);
164            Node name = Node.newString(TokenStream.NAME, label);
165            result.addChildToBack(name);
166            return result;
167        }
168    
169        /**
170         * Break (possibly labeled)
171         */
172        public Object createBreak(String label, int lineno) {
173            Node result = new Node(TokenStream.BREAK, lineno);
174            if (label == null) {
175                return result;
176            } else {
177                Node name = Node.newString(TokenStream.NAME, label);
178                result.addChildToBack(name);
179                return result;
180            }
181        }
182    
183        /**
184         * Continue (possibly labeled)
185         */
186        public Object createContinue(String label, int lineno) {
187            Node result = new Node(TokenStream.CONTINUE, lineno);
188            if (label == null) {
189                return result;
190            } else {
191                Node name = Node.newString(TokenStream.NAME, label);
192                result.addChildToBack(name);
193                return result;
194            }
195        }
196    
197        /**
198         * debugger
199         */
200        public Object createDebugger(int lineno) {
201            Node result = new Node(TokenStream.DEBUGGER, lineno);
202            return result;
203        }
204    
205        /**
206         * Statement block
207         * Creates the empty statement block
208         * Must make subsequent calls to add statements to the node
209         */
210        public Object createBlock(int lineno) {
211            return new Node(TokenStream.BLOCK, lineno);
212        }
213    
214        public Object createFunction(String name, Object args, Object statements,
215                                     String sourceName, int baseLineno,
216                                     int endLineno, Object source,
217                                     boolean isExpr)
218        {
219            Node f = new Node(TokenStream.FUNCTION, 
220                              Node.newString(TokenStream.NAME, 
221                                             name == null ? "" : name),
222                              (Node)args, (Node)statements, baseLineno);
223    
224            f.putProp(Node.SOURCENAME_PROP, sourceName);
225            f.putIntProp(Node.BASE_LINENO_PROP, baseLineno);
226            f.putIntProp(Node.END_LINENO_PROP, endLineno);
227            if (source != null)
228                f.putProp(Node.SOURCE_PROP, source);
229    
230            return f;
231        }
232    
233        /**
234         * Add a child to the back of the given node.  This function
235         * breaks the Factory abstraction, but it removes a requirement
236         * from implementors of Node.
237         */
238        public void addChildToBack(Object parent, Object child) {
239            ((Node)parent).addChildToBack((Node)child);
240        }
241    
242        /**
243         * While
244         */
245        public Object createWhile(Object cond, Object body, int lineno) {
246            return new Node(TokenStream.WHILE, (Node)cond, (Node)body, lineno);
247        }
248    
249        /**
250         * DoWhile
251         */
252        public Object createDoWhile(Object body, Object cond, int lineno) {
253            return new Node(TokenStream.DO, (Node)body, (Node)cond, lineno);
254        }
255    
256        /**
257         * For
258         */
259        public Object createFor(Object init, Object test, Object incr,
260                                Object body, int lineno)
261        {
262            return new Node(TokenStream.FOR, (Node)init, (Node)test, (Node)incr,
263                            (Node)body);
264        }
265    
266        /**
267         * For .. In
268         *
269         */
270        public Object createForIn(Object lhs, Object obj, Object body, int lineno) {
271            return new Node(TokenStream.FOR, (Node)lhs, (Node)obj, (Node)body);
272        }
273    
274        /**
275         * Try/Catch/Finally
276         */
277        public Object createTryCatchFinally(Object tryblock, Object catchblocks,
278                                            Object finallyblock, int lineno)
279        {
280            if (finallyblock == null) {
281                return new Node(TokenStream.TRY, (Node)tryblock, (Node)catchblocks);
282            }
283            return new Node(TokenStream.TRY, (Node)tryblock,
284                            (Node)catchblocks, (Node)finallyblock);
285        }
286    
287        /**
288         * Throw, Return, Label, Break and Continue are defined in ASTFactory.
289         */
290    
291        /**
292         * With
293         */
294        public Object createWith(Object obj, Object body, int lineno) {
295            return new Node(TokenStream.WITH, (Node)obj, (Node)body, lineno);
296        }
297    
298        /**
299         * Array Literal
300         */
301        public Object createArrayLiteral(Object obj) {
302            return obj;
303        }
304    
305        /**
306         * Object Literals
307         */
308        public Object createObjectLiteral(Object obj) {
309            return obj;
310        }
311    
312        /**
313         * Regular expressions
314         */
315        public Object createRegExp(String string, String flags) {
316            return flags.length() == 0
317                   ? new Node(TokenStream.REGEXP,
318                              Node.newString(string))
319                   : new Node(TokenStream.REGEXP,
320                              Node.newString(string),
321                              Node.newString(flags));
322        }
323    
324        /**
325         * If statement
326         */
327        public Object createIf(Object cond, Object ifTrue, Object ifFalse,
328                               int lineno)
329        {
330            if (ifFalse == null)
331                return new Node(TokenStream.IF, (Node)cond, (Node)ifTrue);
332            return new Node(TokenStream.IF, (Node)cond, (Node)ifTrue, (Node)ifFalse);
333        }
334    
335        public Object createTernary(Object cond, Object ifTrue, Object ifFalse) {
336            return new Node(TokenStream.HOOK,
337                            (Node)cond, (Node)ifTrue, (Node)ifFalse);
338        }
339    
340        /**
341         * Unary
342         */
343        public Object createUnary(int nodeType, Object child) {
344            Node childNode = (Node) child;
345            return new Node(nodeType, childNode);
346        }
347    
348        public Object createUnary(int nodeType, int nodeOp, Object child) {
349            return new Node(nodeType, (Node)child, nodeOp);
350        }
351    
352        /**
353         * Binary
354         */
355        public Object createBinary(int nodeType, Object left, Object right) {
356            Node temp;
357            switch (nodeType) {
358    
359              case TokenStream.DOT:
360                nodeType = TokenStream.GETPROP;
361                Node idNode = (Node) right;
362                idNode.setType(TokenStream.STRING);
363                String id = idNode.getString();
364                if (id.equals("__proto__") || id.equals("__parent__")) {
365                    Node result = new Node(nodeType, (Node) left);
366                    result.putProp(Node.SPECIAL_PROP_PROP, id);
367                    return result;
368                }
369                break;
370    
371              case TokenStream.LB:
372                // OPT: could optimize to GETPROP iff string can't be a number
373                nodeType = TokenStream.GETELEM;
374                break;
375            }
376            return new Node(nodeType, (Node)left, (Node)right);
377        }
378    
379        public Object createBinary(int nodeType, int nodeOp, Object left,
380                                   Object right)
381        {
382            if (nodeType == TokenStream.ASSIGN) {
383                return createAssignment(nodeOp, (Node) left, (Node) right,
384                                        null, false);
385            }
386            return new Node(nodeType, (Node) left, (Node) right, nodeOp);
387        }
388    
389        public Object createAssignment(int nodeOp, Node left, Node right,
390                                       Class convert, boolean postfix)
391        {
392            int nodeType = left.getType();
393            switch (nodeType) {
394                case TokenStream.NAME:
395                case TokenStream.GETPROP:
396                case TokenStream.GETELEM:
397                    break;
398                default:
399                    // TODO: This should be a ReferenceError--but that's a runtime 
400                    //  exception. Should we compile an exception into the code?
401                    reportError("msg.bad.lhs.assign");
402            }
403            
404            return new Node(TokenStream.ASSIGN, left, right, nodeOp);
405        }
406    
407        private void reportError(String msgResource) {
408    
409            String message = Context.getMessage0(msgResource);
410            Context.reportError(
411                message, ts.getSourceName(), ts.getLineno(),
412                ts.getLine(), ts.getOffset());
413        }
414    
415        // Only needed to get file/line information. Could create an interface
416        // that TokenStream implements if we want to make the connection less
417        // direct.
418        private TokenStream ts;
419    }