2003/05/12 05:10:30
[org.ibex.core.git] / src / org / mozilla / javascript / IRFactory.java
1 /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
2  *\r
3  * The contents of this file are subject to the Netscape Public\r
4  * License Version 1.1 (the "License"); you may not use this file\r
5  * except in compliance with the License. You may obtain a copy of\r
6  * the License at http://www.mozilla.org/NPL/\r
7  *\r
8  * Software distributed under the License is distributed on an "AS\r
9  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
10  * implied. See the License for the specific language governing\r
11  * rights and limitations under the License.\r
12  *\r
13  * The Original Code is Rhino code, released\r
14  * May 6, 1999.\r
15  *\r
16  * The Initial Developer of the Original Code is Netscape\r
17  * Communications Corporation.  Portions created by Netscape are\r
18  * Copyright (C) 1997-1999 Netscape Communications Corporation. All\r
19  * Rights Reserved.\r
20  *\r
21  * Contributor(s): \r
22  * Norris Boyd\r
23  *\r
24  * Alternatively, the contents of this file may be used under the\r
25  * terms of the GNU Public License (the "GPL"), in which case the\r
26  * provisions of the GPL are applicable instead of those above.\r
27  * If you wish to allow use of your version of this file only\r
28  * under the terms of the GPL and not to allow others to use your\r
29  * version of this file under the NPL, indicate your decision by\r
30  * deleting the provisions above and replace them with the notice\r
31  * and other provisions required by the GPL.  If you do not delete\r
32  * the provisions above, a recipient may use your version of this\r
33  * file under either the NPL or the GPL.\r
34  */\r
35 \r
36 package org.mozilla.javascript;\r
37 \r
38 /**\r
39  * This class allows the creation of nodes, and follows the Factory pattern.\r
40  *\r
41  * @see Node\r
42  * @author Mike McCabe\r
43  * @author Norris Boyd\r
44  */\r
45 public class IRFactory {\r
46     \r
47     public IRFactory(TokenStream ts, Scriptable scope) {\r
48         this.ts = ts;\r
49         this.scope = scope;\r
50     }\r
51 \r
52     /**\r
53      * Script (for associating file/url names with toplevel scripts.)\r
54      */\r
55     public Object createScript(Object body, String sourceName, \r
56                                int baseLineno, int endLineno, Object source)\r
57     {\r
58         Node result = new Node(TokenStream.SCRIPT, sourceName);\r
59         Node children = ((Node) body).getFirstChild();\r
60         if (children != null)\r
61             result.addChildrenToBack(children);\r
62         result.putProp(Node.SOURCENAME_PROP, sourceName);\r
63         result.putProp(Node.BASE_LINENO_PROP, new Integer(baseLineno));\r
64         result.putProp(Node.END_LINENO_PROP, new Integer(endLineno));\r
65         if (source != null)\r
66             result.putProp(Node.SOURCE_PROP, source);\r
67         return result;\r
68     }\r
69 \r
70     /**\r
71      * Leaf\r
72      */\r
73     public Object createLeaf(int nodeType) {\r
74             return new Node(nodeType);\r
75     }\r
76 \r
77     public Object createLeaf(int nodeType, String id) {\r
78         return new Node(nodeType, id);\r
79     }\r
80 \r
81     public Object createLeaf(int nodeType, int nodeOp) {\r
82         return new Node(nodeType, new Integer(nodeOp));\r
83     }\r
84 \r
85     /**\r
86      * Statement leaf nodes.\r
87      */\r
88 \r
89     public Object createSwitch(int lineno) {\r
90         return new Node(TokenStream.SWITCH, new Integer(lineno));\r
91     }\r
92 \r
93     public Object createVariables(int lineno) {\r
94         return new Node(TokenStream.VAR, new Integer(lineno));\r
95     }\r
96 \r
97     public Object createExprStatement(Object expr, int lineno) {\r
98         return new Node(TokenStream.EXPRSTMT, (Node) expr, new Integer(lineno));\r
99     }\r
100 \r
101     /**\r
102      * Name\r
103      */\r
104     public Object createName(String name) {\r
105         return new Node(TokenStream.NAME, name);\r
106     }\r
107 \r
108     /**\r
109      * String (for literals)\r
110      */\r
111     public Object createString(String string) {\r
112         return new Node(TokenStream.STRING, string);\r
113     }\r
114 \r
115     /**\r
116      * Number (for literals)\r
117      */\r
118     public Object createNumber(Number number) {\r
119         return new Node(TokenStream.NUMBER, number);\r
120     }\r
121 \r
122     /**\r
123      * Catch clause of try/catch/finally\r
124      * @param varName the name of the variable to bind to the exception\r
125      * @param catchCond the condition under which to catch the exception.\r
126      *                  May be null if no condition is given.\r
127      * @param stmts the statements in the catch clause\r
128      * @param lineno the starting line number of the catch clause\r
129      */\r
130     public Object createCatch(String varName, Object catchCond, Object stmts,\r
131                               int lineno)\r
132     {\r
133         if (catchCond == null)\r
134             catchCond = new Node(TokenStream.PRIMARY, \r
135                                  new Integer(TokenStream.TRUE));\r
136         Node result = new Node(TokenStream.CATCH, (Node)createName(varName), \r
137                                (Node)catchCond, (Node)stmts);\r
138         result.setDatum(new Integer(lineno));\r
139         return result;\r
140     }\r
141     \r
142     /**\r
143      * Throw\r
144      */\r
145     public Object createThrow(Object expr, int lineno) {\r
146         return new Node(TokenStream.THROW, (Node)expr, new Integer(lineno));\r
147     }\r
148 \r
149     /**\r
150      * Return\r
151      */\r
152     public Object createReturn(Object expr, int lineno) {\r
153         return expr == null\r
154             ? new Node(TokenStream.RETURN, new Integer(lineno))\r
155             : new Node(TokenStream.RETURN, (Node)expr, new Integer(lineno));\r
156     }\r
157 \r
158     /**\r
159      * Assert\r
160      */\r
161     public Object createAssert(Object expr, int lineno) {\r
162         return expr == null\r
163             ? new Node(TokenStream.ASSERT, new Integer(lineno))\r
164             : new Node(TokenStream.ASSERT, (Node)expr, new Integer(lineno));\r
165     }\r
166 \r
167     /**\r
168      * Label\r
169      */\r
170     public Object createLabel(String label, int lineno) {\r
171         Node result = new Node(TokenStream.LABEL, new Integer(lineno));\r
172         Node name = new Node(TokenStream.NAME, label);\r
173         result.addChildToBack(name);\r
174         return result;\r
175     }\r
176 \r
177     /**\r
178      * Break (possibly labeled)\r
179      */\r
180     public Object createBreak(String label, int lineno) {\r
181         Node result = new Node(TokenStream.BREAK, new Integer(lineno));\r
182         if (label == null) {\r
183             return result;\r
184         } else {\r
185             Node name = new Node(TokenStream.NAME, label);\r
186             result.addChildToBack(name);\r
187             return result;\r
188         }\r
189     }\r
190 \r
191     /**\r
192      * Continue (possibly labeled)\r
193      */\r
194     public Object createContinue(String label, int lineno) {\r
195         Node result = new Node(TokenStream.CONTINUE, new Integer(lineno));\r
196         if (label == null) {\r
197             return result;\r
198         } else {\r
199             Node name = new Node(TokenStream.NAME, label);\r
200             result.addChildToBack(name);\r
201             return result;\r
202         }\r
203     }\r
204 \r
205     /**\r
206      * Statement block\r
207      * Creates the empty statement block\r
208      * Must make subsequent calls to add statements to the node\r
209      */\r
210     public Object createBlock(int lineno) {\r
211         return new Node(TokenStream.BLOCK, new Integer(lineno));\r
212     }\r
213     \r
214     public Object createFunctionNode(String name, Object args, \r
215                                      Object statements) \r
216     {\r
217         if (name == null)\r
218             name = "";\r
219         return new FunctionNode(name, (Node) args, (Node) statements);\r
220     }\r
221 \r
222     public Object createFunction(String name, Object args, Object statements,\r
223                                  String sourceName, int baseLineno, \r
224                                  int endLineno, Object source,\r
225                                  boolean isExpr)\r
226     {\r
227         FunctionNode f = (FunctionNode) createFunctionNode(name, args, \r
228                                                            statements);\r
229         f.setFunctionType(isExpr ? FunctionNode.FUNCTION_EXPRESSION\r
230                                  : FunctionNode.FUNCTION_STATEMENT);\r
231         f.putProp(Node.SOURCENAME_PROP, sourceName);\r
232         f.putProp(Node.BASE_LINENO_PROP, new Integer(baseLineno));\r
233         f.putProp(Node.END_LINENO_PROP, new Integer(endLineno));\r
234         if (source != null)\r
235             f.putProp(Node.SOURCE_PROP, source);\r
236         Node result = new Node(TokenStream.FUNCTION, name);\r
237         result.putProp(Node.FUNCTION_PROP, f);\r
238         return result;\r
239     }\r
240     \r
241     public void setFunctionExpressionStatement(Object o) {\r
242         Node n = (Node) o;\r
243         FunctionNode f = (FunctionNode) n.getProp(Node.FUNCTION_PROP);\r
244         f.setFunctionType(FunctionNode.FUNCTION_EXPRESSION_STATEMENT);\r
245     }\r
246 \r
247     /**\r
248      * Add a child to the back of the given node.  This function\r
249      * breaks the Factory abstraction, but it removes a requirement\r
250      * from implementors of Node.\r
251      */\r
252     public void addChildToBack(Object parent, Object child) {\r
253         ((Node)parent).addChildToBack((Node)child);\r
254     }\r
255 \r
256     /**\r
257      * While\r
258      */\r
259     public Object createWhile(Object cond, Object body, int lineno) {\r
260         // Just add a GOTO to the condition in the do..while\r
261         Node result = (Node) createDoWhile(body, cond, lineno);\r
262         Node condTarget = (Node) result.getProp(Node.CONTINUE_PROP);\r
263         Node GOTO = new Node(TokenStream.GOTO);\r
264         GOTO.putProp(Node.TARGET_PROP, condTarget);\r
265         result.addChildToFront(GOTO);\r
266         return result;\r
267     }\r
268 \r
269     /**\r
270      * DoWhile\r
271      */\r
272     public Object createDoWhile(Object body, Object cond, int lineno) {\r
273         Node result = new Node(TokenStream.LOOP, new Integer(lineno));\r
274         Node bodyTarget = new Node(TokenStream.TARGET);\r
275         Node condTarget = new Node(TokenStream.TARGET);\r
276         Node IFEQ = new Node(TokenStream.IFEQ, (Node)cond);\r
277         IFEQ.putProp(Node.TARGET_PROP, bodyTarget);\r
278         Node breakTarget = new Node(TokenStream.TARGET);\r
279 \r
280         result.addChildToBack(bodyTarget);\r
281         result.addChildrenToBack((Node)body);\r
282         result.addChildToBack(condTarget);\r
283         result.addChildToBack(IFEQ);\r
284         result.addChildToBack(breakTarget);\r
285 \r
286         result.putProp(Node.BREAK_PROP, breakTarget);\r
287         result.putProp(Node.CONTINUE_PROP, condTarget);\r
288 \r
289         return result;\r
290     }\r
291 \r
292     /**\r
293      * For\r
294      */\r
295     public Object createFor(Object init, Object test, Object incr,\r
296                             Object body, int lineno)\r
297     {\r
298         if (((Node) test).getType() == TokenStream.VOID) {\r
299             test = new Node(TokenStream.PRIMARY, \r
300                             new Integer(TokenStream.TRUE));\r
301         }\r
302         Node result = (Node)createWhile(test, body, lineno);\r
303         Node initNode = (Node) init;\r
304         if (initNode.getType() != TokenStream.VOID) {\r
305             if (initNode.getType() != TokenStream.VAR)\r
306                 initNode = new Node(TokenStream.POP, initNode);\r
307             result.addChildToFront(initNode);\r
308         }\r
309         Node condTarget = (Node)result.getProp(Node.CONTINUE_PROP);\r
310         Node incrTarget = new Node(TokenStream.TARGET);\r
311         result.addChildBefore(incrTarget, condTarget);\r
312         if (((Node) incr).getType() != TokenStream.VOID) {\r
313             incr = createUnary(TokenStream.POP, incr);\r
314             result.addChildAfter((Node)incr, incrTarget);\r
315         }\r
316         result.putProp(Node.CONTINUE_PROP, incrTarget);\r
317         return result;\r
318     }\r
319 \r
320     /**\r
321      * For .. In\r
322      *\r
323      */\r
324     public Object createForIn(Object lhs, Object obj, Object body, int lineno) {\r
325         String name;\r
326         Node lhsNode = (Node) lhs;\r
327         Node objNode = (Node) obj;\r
328         int type = lhsNode.getType();\r
329 \r
330         Node lvalue = lhsNode;\r
331         switch (type) {\r
332 \r
333           case TokenStream.NAME:\r
334           case TokenStream.GETPROP:\r
335           case TokenStream.GETELEM:\r
336             break;\r
337 \r
338           case TokenStream.VAR:\r
339             /*\r
340              * check that there was only one variable given.\r
341              * we can't do this in the parser, because then the\r
342              * parser would have to know something about the\r
343              * 'init' node of the for-in loop.\r
344              */\r
345             Node lastChild = lhsNode.getLastChild();\r
346             if (lhsNode.getFirstChild() != lastChild) {\r
347                 reportError("msg.mult.index");\r
348             }\r
349             lvalue = new Node(TokenStream.NAME, lastChild.getString());\r
350             break;\r
351 \r
352           default:\r
353             reportError("msg.bad.for.in.lhs");\r
354             return objNode;\r
355         }\r
356 \r
357         Node init = new Node(TokenStream.ENUMINIT, objNode);\r
358         Node next = new Node(TokenStream.ENUMNEXT);\r
359         next.putProp(Node.ENUM_PROP, init);\r
360         Node temp = createNewTemp(next);\r
361         Node cond = new Node(TokenStream.EQOP, new Integer(TokenStream.NE));\r
362         cond.addChildToBack(temp);\r
363         cond.addChildToBack(new Node(TokenStream.PRIMARY,\r
364                                      new Integer(TokenStream.NULL)));\r
365         Node newBody = new Node(TokenStream.BLOCK);\r
366         Node assign = (Node) createAssignment(TokenStream.NOP, lvalue,\r
367                                               createUseTemp(temp), null,\r
368                                               false);\r
369         newBody.addChildToBack(new Node(TokenStream.POP, assign));\r
370         newBody.addChildToBack((Node) body);\r
371         Node result = (Node) createWhile(cond, newBody, lineno);\r
372 \r
373         result.addChildToFront(init);\r
374         if (type == TokenStream.VAR)\r
375             result.addChildToFront(lhsNode);\r
376 \r
377         Node done = new Node(TokenStream.ENUMDONE);\r
378         done.putProp(Node.ENUM_PROP, init);\r
379         result.addChildToBack(done);\r
380 \r
381         return result;\r
382     }\r
383 \r
384     /**\r
385      * Try/Catch/Finally\r
386      *\r
387      * The IRFactory tries to express as much as possible in the tree;\r
388      * the responsibilities remaining for Codegen are to add the Java\r
389      * handlers: (Either (but not both) of TARGET and FINALLY might not\r
390      * be defined)\r
391 \r
392      * - a catch handler for javascript exceptions that unwraps the\r
393      * exception onto the stack and GOTOes to the catch target -\r
394      * TARGET_PROP in the try node.\r
395 \r
396      * - a finally handler that catches any exception, stores it to a\r
397      * temporary, and JSRs to the finally target - FINALLY_PROP in the\r
398      * try node - before re-throwing the exception.\r
399 \r
400      * ... and a goto to GOTO around these handlers.\r
401      */\r
402     public Object createTryCatchFinally(Object tryblock, Object catchblocks,\r
403                                         Object finallyblock, int lineno)\r
404     {\r
405         Node trynode = (Node)tryblock;\r
406 \r
407         // short circuit\r
408         if (trynode.getType() == TokenStream.BLOCK && !trynode.hasChildren())\r
409             return trynode;\r
410 \r
411         Node pn = new Node(TokenStream.TRY, trynode, new Integer(lineno));\r
412         Node catchNodes = (Node)catchblocks;\r
413         boolean hasCatch = catchNodes.hasChildren();\r
414         boolean hasFinally = false;\r
415         Node finallyNode = null;\r
416         Node finallyTarget = null;\r
417         if (finallyblock != null) {\r
418             finallyNode = (Node)finallyblock;\r
419             hasFinally = (finallyNode.getType() != TokenStream.BLOCK\r
420                           || finallyNode.hasChildren());\r
421             if (hasFinally) {\r
422                 // make a TARGET for the finally that the tcf node knows about\r
423                 finallyTarget = new Node(TokenStream.TARGET);\r
424                 pn.putProp(Node.FINALLY_PROP, finallyTarget);\r
425 \r
426                 // add jsr finally to the try block\r
427                 Node jsrFinally = new Node(TokenStream.JSR);\r
428                 jsrFinally.putProp(Node.TARGET_PROP, finallyTarget);\r
429                 pn.addChildToBack(jsrFinally);\r
430             }\r
431         }\r
432 \r
433         // short circuit\r
434         if (!hasFinally && !hasCatch)  // bc finally might be an empty block...\r
435             return trynode;\r
436 \r
437         Node endTarget = new Node(TokenStream.TARGET);\r
438         Node GOTOToEnd = new Node(TokenStream.GOTO);\r
439         GOTOToEnd.putProp(Node.TARGET_PROP, endTarget);\r
440         pn.addChildToBack(GOTOToEnd);\r
441        \r
442         if (hasCatch) {\r
443             /*\r
444              *\r
445                Given\r
446                \r
447                 try {\r
448                         throw 3;\r
449                 } catch (e: e instanceof Object) {\r
450                         print("object");\r
451                 } catch (e2) {\r
452                         print(e2);\r
453                 }\r
454 \r
455                rewrite as\r
456 \r
457                 try {\r
458                         throw 3;\r
459                 } catch (x) {\r
460                         o = newScope();\r
461                         o.e = x;\r
462                         with (o) {\r
463                                 if (e instanceof Object) {\r
464                                         print("object");\r
465                                 }\r
466                         }\r
467                         o2 = newScope();\r
468                         o2.e2 = x;\r
469                         with (o2) {\r
470                                 if (true) {\r
471                                         print(e2);\r
472                                 }\r
473                         }\r
474                 }\r
475             */\r
476             // make a TARGET for the catch that the tcf node knows about\r
477             Node catchTarget = new Node(TokenStream.TARGET);\r
478             pn.putProp(Node.TARGET_PROP, catchTarget);\r
479             // mark it\r
480             pn.addChildToBack(catchTarget);\r
481             \r
482             // get the exception object and store it in a temp\r
483             Node exn = createNewLocal(new Node(TokenStream.VOID));\r
484             pn.addChildToBack(new Node(TokenStream.POP, exn));\r
485             \r
486             Node endCatch = new Node(TokenStream.TARGET);\r
487 \r
488             // add [jsr finally?] goto end to each catch block\r
489             // expects catchNode children to be (cond block) pairs.\r
490             Node cb = catchNodes.getFirstChild();\r
491             while (cb != null) {\r
492                 Node catchStmt = new Node(TokenStream.BLOCK);\r
493                 int catchLineNo = ((Integer)cb.getDatum()).intValue();\r
494                 \r
495                 Node name = cb.getFirstChild();\r
496                 Node cond = name.getNextSibling();\r
497                 Node catchBlock = cond.getNextSibling();\r
498                 cb.removeChild(name);\r
499                 cb.removeChild(cond);\r
500                 cb.removeChild(catchBlock);\r
501                 \r
502                 Node newScope = createNewLocal(new Node(TokenStream.NEWSCOPE));\r
503                 Node initScope = new Node(TokenStream.SETPROP, newScope, \r
504                                           new Node(TokenStream.STRING, \r
505                                                    name.getString()), \r
506                                           createUseLocal(exn));\r
507                 catchStmt.addChildToBack(new Node(TokenStream.POP, initScope));\r
508                 \r
509                 catchBlock.addChildToBack(new Node(TokenStream.LEAVEWITH));\r
510                 Node GOTOToEndCatch = new Node(TokenStream.GOTO);\r
511                 GOTOToEndCatch.putProp(Node.TARGET_PROP, endCatch);\r
512                 catchBlock.addChildToBack(GOTOToEndCatch);\r
513                 \r
514                 Node ifStmt = (Node) createIf(cond, catchBlock, null, catchLineNo);\r
515                 // Try..catch produces "with" code in order to limit \r
516                 // the scope of the exception object.\r
517                 // OPT: We should be able to figure out the correct\r
518                 //      scoping at compile-time and avoid the\r
519                 //      runtime overhead.\r
520                 Node withStmt = (Node) createWith(createUseLocal(newScope), \r
521                                                   ifStmt, catchLineNo);\r
522                 catchStmt.addChildToBack(withStmt);\r
523                 \r
524                 pn.addChildToBack(catchStmt);\r
525                 \r
526                 // move to next cb \r
527                 cb = cb.getNextSibling();\r
528             }\r
529             \r
530             // Generate code to rethrow if no catch clause was executed\r
531             Node rethrow = new Node(TokenStream.THROW, createUseLocal(exn));\r
532             pn.addChildToBack(rethrow);\r
533 \r
534             pn.addChildToBack(endCatch);\r
535             // add a JSR finally if needed\r
536             if (hasFinally) {\r
537                 Node jsrFinally = new Node(TokenStream.JSR);\r
538                 jsrFinally.putProp(Node.TARGET_PROP, finallyTarget);\r
539                 pn.addChildToBack(jsrFinally);\r
540                 Node GOTO = new Node(TokenStream.GOTO);\r
541                 GOTO.putProp(Node.TARGET_PROP, endTarget);\r
542                 pn.addChildToBack(GOTO);\r
543             }\r
544         }\r
545 \r
546         if (hasFinally) {\r
547             pn.addChildToBack(finallyTarget);\r
548             Node returnTemp = createNewLocal(new Node(TokenStream.VOID));\r
549             Node popAndMake = new Node(TokenStream.POP, returnTemp);\r
550             pn.addChildToBack(popAndMake);\r
551             pn.addChildToBack(finallyNode);\r
552             Node ret = createUseLocal(returnTemp);\r
553 \r
554             // add the magic prop that makes it output a RET\r
555             ret.putProp(Node.TARGET_PROP, Boolean.TRUE);\r
556             pn.addChildToBack(ret);\r
557         }\r
558         pn.addChildToBack(endTarget);\r
559         return pn;\r
560     }\r
561 \r
562     /**\r
563      * Throw, Return, Label, Break and Continue are defined in ASTFactory.\r
564      */\r
565 \r
566     /**\r
567      * With\r
568      */\r
569     public Object createWith(Object obj, Object body, int lineno) {\r
570         Node result = new Node(TokenStream.BLOCK, new Integer(lineno));\r
571         result.addChildToBack(new Node(TokenStream.ENTERWITH, (Node)obj));\r
572         Node bodyNode = new Node(TokenStream.WITH, (Node) body,\r
573                                  new Integer(lineno));\r
574         result.addChildrenToBack(bodyNode);\r
575         result.addChildToBack(new Node(TokenStream.LEAVEWITH));\r
576         return result;\r
577     }\r
578 \r
579     /**\r
580      * Array Literal\r
581      * <BR>createArrayLiteral rewrites its argument as array creation\r
582      * plus a series of array element entries, so later compiler\r
583      * stages don't need to know about array literals.\r
584      */\r
585     public Object createArrayLiteral(Object obj) {\r
586         Node array;\r
587         Node result;\r
588         array = result = new Node(TokenStream.NEW,\r
589                                   new Node(TokenStream.NAME, "Array"));\r
590         Node temp = createNewTemp(result);\r
591         result = temp;\r
592 \r
593         java.util.Enumeration children = ((Node) obj).getChildIterator();\r
594 \r
595         Node elem = null;\r
596         int i = 0;\r
597         while (children.hasMoreElements()) {\r
598             elem = (Node) children.nextElement();\r
599             if (elem.getType() == TokenStream.PRIMARY &&\r
600                 elem.getInt() == TokenStream.UNDEFINED)\r
601             {\r
602                 i++;\r
603                 continue;\r
604             }\r
605             Node addelem = new Node(TokenStream.SETELEM, createUseTemp(temp),\r
606                                     new Node(TokenStream.NUMBER,\r
607                                              new Integer(i)),\r
608                                     elem);\r
609             i++;\r
610             result = new Node(TokenStream.COMMA, result, addelem);\r
611         }\r
612 \r
613         /*\r
614          * If the version is 120, then new Array(4) means create a new\r
615          * array with 4 as the first element.  In this case, we might\r
616          * need to explicitly check against trailing undefined\r
617          * elements in the array literal, and set the length manually\r
618          * if these occur.  Otherwise, we can add an argument to the\r
619          * node specifying new Array() to provide the array length.\r
620          * (Which will make Array optimizations involving allocating a\r
621          * Java array to back the javascript array work better.)\r
622          */\r
623         if (Context.getContext().getLanguageVersion() == Context.VERSION_1_2) {\r
624             /* When last array element is empty, we need to set the\r
625              * length explicitly, because we can't depend on SETELEM\r
626              * to do it for us - because empty [,,] array elements\r
627              * never set anything at all. */\r
628             if (elem != null &&\r
629                 elem.getType() == TokenStream.PRIMARY &&\r
630                 elem.getInt() == TokenStream.UNDEFINED)\r
631             {\r
632                 Node setlength = new Node(TokenStream.SETPROP,\r
633                                           createUseTemp(temp),\r
634                                           new Node(TokenStream.STRING,\r
635                                                    "length"),\r
636                                           new Node(TokenStream.NUMBER,\r
637                                                    new Integer(i)));\r
638                 result = new Node(TokenStream.COMMA, result, setlength);\r
639             }\r
640         } else {\r
641             array.addChildToBack(new Node(TokenStream.NUMBER,\r
642                                           new Integer(i)));\r
643         }\r
644         return new Node(TokenStream.COMMA, result, createUseTemp(temp));\r
645     }\r
646 \r
647     /**\r
648      * Object Literals\r
649      * <BR> createObjectLiteral rewrites its argument as object\r
650      * creation plus object property entries, so later compiler\r
651      * stages don't need to know about object literals.\r
652      */\r
653     public Object createObjectLiteral(Object obj) {\r
654         Node result = new Node(TokenStream.NEW, new Node(TokenStream.NAME,\r
655                                                          "Object"));\r
656         Node temp = createNewTemp(result);\r
657         result = temp;\r
658 \r
659         java.util.Enumeration children = ((Node) obj).getChildIterator();\r
660 \r
661         while (children.hasMoreElements()) {\r
662             Node elem = (Node)children.nextElement();\r
663 \r
664             int op = (elem.getType() == TokenStream.NAME)\r
665                    ? TokenStream.SETPROP\r
666                    : TokenStream.SETELEM;\r
667             Node addelem = new Node(op, createUseTemp(temp),\r
668                                     elem, (Node)children.nextElement());\r
669             result = new Node(TokenStream.COMMA, result, addelem);\r
670         }\r
671         return new Node(TokenStream.COMMA, result, createUseTemp(temp));\r
672     }\r
673 \r
674     /**\r
675      * Regular expressions\r
676      */\r
677     public Object createRegExp(String string, String flags) {\r
678         return flags.length() == 0\r
679                ? new Node(TokenStream.OBJECT,\r
680                           new Node(TokenStream.STRING, string))\r
681                : new Node(TokenStream.OBJECT,\r
682                           new Node(TokenStream.STRING, string),\r
683                           new Node(TokenStream.STRING, flags));\r
684     }\r
685 \r
686     /**\r
687      * If statement\r
688      */\r
689     public Object createIf(Object cond, Object ifTrue, Object ifFalse,\r
690                            int lineno)\r
691     {\r
692         Node result = new Node(TokenStream.BLOCK, new Integer(lineno));\r
693         Node ifNotTarget = new Node(TokenStream.TARGET);\r
694         Node IFNE = new Node(TokenStream.IFNE, (Node) cond);\r
695         IFNE.putProp(Node.TARGET_PROP, ifNotTarget);\r
696 \r
697         result.addChildToBack(IFNE);\r
698         result.addChildrenToBack((Node)ifTrue);\r
699 \r
700         if (ifFalse != null) {\r
701             Node GOTOToEnd = new Node(TokenStream.GOTO);\r
702             Node endTarget = new Node(TokenStream.TARGET);\r
703             GOTOToEnd.putProp(Node.TARGET_PROP, endTarget);\r
704             result.addChildToBack(GOTOToEnd);\r
705             result.addChildToBack(ifNotTarget);\r
706             result.addChildrenToBack((Node)ifFalse);\r
707             result.addChildToBack(endTarget);\r
708         } else {\r
709             result.addChildToBack(ifNotTarget);\r
710         }\r
711 \r
712         return result;\r
713     }\r
714 \r
715     public Object createTernary(Object cond, Object ifTrue, Object ifFalse) {\r
716         return createIf(cond, ifTrue, ifFalse, -1);\r
717     }\r
718 \r
719     /**\r
720      * Unary\r
721      */\r
722     public Object createUnary(int nodeType, Object child) {\r
723         Node childNode = (Node) child;\r
724         if (nodeType == TokenStream.DELPROP) {\r
725             int childType = childNode.getType();\r
726             Node left;\r
727             Node right;\r
728             if (childType == TokenStream.NAME) {\r
729                 // Transform Delete(Name "a")\r
730                 //  to Delete(Bind("a"), String("a"))\r
731                 childNode.setType(TokenStream.BINDNAME);\r
732                 left = childNode;\r
733                 right = childNode.cloneNode();\r
734                 right.setType(TokenStream.STRING);\r
735             } else if (childType == TokenStream.GETPROP ||\r
736                        childType == TokenStream.GETELEM)\r
737             {\r
738                 left = childNode.getFirstChild();\r
739                 right = childNode.getLastChild();\r
740                 childNode.removeChild(left);\r
741                 childNode.removeChild(right);\r
742             } else {\r
743                 return new Node(TokenStream.PRIMARY,\r
744                                 new Integer(TokenStream.TRUE));\r
745             }\r
746             return new Node(nodeType, left, right);\r
747         }\r
748         return new Node(nodeType, childNode);\r
749     }\r
750 \r
751     public Object createUnary(int nodeType, int nodeOp, Object child) {\r
752         Node childNode = (Node) child;\r
753         int childType = childNode.getType();\r
754         if (nodeOp == TokenStream.TYPEOF &&\r
755             childType == TokenStream.NAME)\r
756         {\r
757             childNode.setType(TokenStream.TYPEOF);\r
758             return childNode;\r
759         }\r
760 \r
761         if (nodeType == TokenStream.INC || nodeType == TokenStream.DEC) {\r
762 \r
763             if (!hasSideEffects(childNode)\r
764                 && (nodeOp == TokenStream.POST)\r
765                 && (childType == TokenStream.NAME \r
766                     || childType == TokenStream.GETPROP\r
767                     || childType == TokenStream.GETELEM))\r
768             {\r
769                 // if it's not a LHS type, createAssignment (below) will throw\r
770                 // an exception.\r
771                 return new Node(nodeType, childNode);\r
772             }\r
773 \r
774             /*\r
775              * Transform INC/DEC ops to +=1, -=1,\r
776              * expecting later optimization of all +/-=1 cases to INC, DEC.\r
777              */\r
778             // we have to use Double for now, because\r
779             // 0.0 and 1.0 are stored as dconst_[01],\r
780             // and using a Float creates a stack mismatch.\r
781             Node rhs = (Node) createNumber(new Double(1.0));\r
782 \r
783             return createAssignment(nodeType == TokenStream.INC\r
784                                         ? TokenStream.ADD\r
785                                         : TokenStream.SUB,\r
786                                     childNode,\r
787                                     rhs,\r
788                                     ScriptRuntime.NumberClass,\r
789                                     nodeOp == TokenStream.POST);\r
790         }\r
791 \r
792         Node result = new Node(nodeType, new Integer(nodeOp));\r
793         result.addChildToBack((Node)child);\r
794         return result;\r
795     }\r
796 \r
797     /**\r
798      * Binary\r
799      */\r
800     public Object createBinary(int nodeType, Object left, Object right) {\r
801         Node temp;\r
802         switch (nodeType) {\r
803 \r
804           case TokenStream.DOT:\r
805             nodeType = TokenStream.GETPROP;\r
806             Node idNode = (Node) right;\r
807             idNode.setType(TokenStream.STRING);\r
808             String id = idNode.getString();\r
809             if (id.equals("__proto__") || id.equals("__parent__")) {\r
810                 Node result = new Node(nodeType, (Node) left);\r
811                 result.putProp(Node.SPECIAL_PROP_PROP, id);\r
812                 return result;\r
813             }\r
814             break;\r
815 \r
816           case TokenStream.LB:\r
817             // OPT: could optimize to GETPROP iff string can't be a number\r
818             nodeType = TokenStream.GETELEM;\r
819             break;\r
820 /*\r
821           case TokenStream.AND:\r
822             temp = createNewTemp((Node) left);\r
823             return createTernary(temp, right, createUseTemp(temp));\r
824 \r
825           case TokenStream.OR:\r
826             temp = createNewTemp((Node) left);\r
827             return createTernary(temp, createUseTemp(temp), right);\r
828 */            \r
829         }\r
830         return new Node(nodeType, (Node)left, (Node)right);\r
831     }\r
832 \r
833     public Object createBinary(int nodeType, int nodeOp, Object left,\r
834                                Object right)\r
835     {\r
836         if (nodeType == TokenStream.ASSIGN) {\r
837             return createAssignment(nodeOp, (Node) left, (Node) right,\r
838                                     null, false);\r
839         }\r
840         return new Node(nodeType, (Node) left, (Node) right,\r
841                         new Integer(nodeOp));\r
842     }\r
843 \r
844     public Object createAssignment(int nodeOp, Node left, Node right,\r
845                                    Class convert, boolean postfix)\r
846     {\r
847         int nodeType = left.getType();\r
848         String idString;\r
849         Node id = null;\r
850         switch (nodeType) {\r
851           case TokenStream.NAME:\r
852             return createSetName(nodeOp, left, right, convert, postfix);\r
853 \r
854           case TokenStream.GETPROP:\r
855             idString = (String) left.getProp(Node.SPECIAL_PROP_PROP);\r
856             if (idString != null)\r
857                 id = new Node(TokenStream.STRING, idString);\r
858             /* fall through */\r
859           case TokenStream.GETELEM:\r
860             if (id == null)\r
861                 id = left.getLastChild();\r
862             return createSetProp(nodeType, nodeOp, left.getFirstChild(),\r
863                                  id, right, convert, postfix);\r
864           default:\r
865             // TODO: This should be a ReferenceError--but that's a runtime \r
866             //  exception. Should we compile an exception into the code?\r
867             reportError("msg.bad.lhs.assign");\r
868             return left;\r
869         }\r
870     }\r
871 \r
872     private Node createConvert(Class toType, Node expr) {\r
873         if (toType == null)\r
874             return expr;\r
875         Node result = new Node(TokenStream.CONVERT, expr);\r
876         result.putProp(Node.TYPE_PROP, ScriptRuntime.NumberClass);\r
877         return result;\r
878     }\r
879 \r
880     private Object createSetName(int nodeOp, Node left, Node right,\r
881                                  Class convert, boolean postfix)\r
882     {\r
883         if (nodeOp == TokenStream.NOP) {\r
884             left.setType(TokenStream.BINDNAME);\r
885             return new Node(TokenStream.SETNAME, left, right);\r
886         }\r
887 \r
888         String s = left.getString();\r
889 \r
890         if (s.equals("__proto__") || s.equals("__parent__")) {\r
891             Node result = new Node(TokenStream.SETPROP, left, right);\r
892             result.putProp(Node.SPECIAL_PROP_PROP, s);\r
893             return result;\r
894         }\r
895 \r
896         Node opLeft = new Node(TokenStream.NAME, s);\r
897         if (convert != null)\r
898             opLeft = createConvert(convert, opLeft);\r
899         if (postfix)\r
900             opLeft = createNewTemp(opLeft);\r
901         Node op = new Node(nodeOp, opLeft, right);\r
902 \r
903         Node lvalueLeft = new Node(TokenStream.BINDNAME, s);\r
904         Node result = new Node(TokenStream.SETNAME, lvalueLeft, op);\r
905         if (postfix) {\r
906             result = new Node(TokenStream.COMMA, result,\r
907                               createUseTemp(opLeft));\r
908         }\r
909         return result;\r
910     }\r
911 \r
912     public Node createNewTemp(Node n) {\r
913         int type = n.getType();\r
914         if (type == TokenStream.STRING || type == TokenStream.NUMBER) {\r
915             // Optimization: clone these values rather than storing\r
916             // and loading from a temp\r
917             return n;\r
918         }\r
919         Node result = new Node(TokenStream.NEWTEMP, n);\r
920         return result;\r
921     }\r
922 \r
923     public Node createUseTemp(Node newTemp) {\r
924         int type = newTemp.getType();\r
925         if (type == TokenStream.NEWTEMP) {\r
926             Node result = new Node(TokenStream.USETEMP);\r
927             result.putProp(Node.TEMP_PROP, newTemp);\r
928             Integer n = (Integer) newTemp.getProp(Node.USES_PROP);\r
929             if (n == null) {\r
930                 n = new Integer(1);\r
931             } else {\r
932                 if (n.intValue() < Integer.MAX_VALUE)\r
933                     n = new Integer(n.intValue() + 1);\r
934             }\r
935             newTemp.putProp(Node.USES_PROP, n);\r
936             return result;\r
937         }\r
938         return newTemp.cloneNode();\r
939     }\r
940 \r
941     public Node createNewLocal(Node n) {\r
942         Node result = new Node(TokenStream.NEWLOCAL, n);\r
943         return result;\r
944     }\r
945 \r
946     public Node createUseLocal(Node newLocal) {\r
947         int type = newLocal.getType();\r
948         if (type == TokenStream.NEWLOCAL) {\r
949             Node result = new Node(TokenStream.USELOCAL);\r
950             result.putProp(Node.LOCAL_PROP, newLocal);\r
951             return result;\r
952         }\r
953         return newLocal.cloneNode();    // what's this path for ?\r
954     }\r
955 \r
956     public static boolean hasSideEffects(Node exprTree) {\r
957         switch (exprTree.getType()) {\r
958             case TokenStream.INC:\r
959             case TokenStream.DEC:\r
960             case TokenStream.SETPROP:\r
961             case TokenStream.SETELEM:\r
962             case TokenStream.SETNAME:\r
963             case TokenStream.CALL:\r
964             case TokenStream.NEW:\r
965                 return true;\r
966             default:\r
967                 Node child = exprTree.getFirstChild();\r
968                 while (child != null) {\r
969                     if (hasSideEffects(child))\r
970                         return true;\r
971                     else\r
972                         child = child.getNextSibling();\r
973                 }\r
974                 break;\r
975         }\r
976         return false;\r
977     }\r
978 \r
979     private Node createSetProp(int nodeType, int nodeOp, Node obj, Node id,\r
980                                Node expr, Class convert, boolean postfix)\r
981     {\r
982         int type = nodeType == TokenStream.GETPROP\r
983                    ? TokenStream.SETPROP\r
984                    : TokenStream.SETELEM;\r
985 \r
986         Object datum = id.getDatum();\r
987         if (type == TokenStream.SETPROP && datum != null &&\r
988             datum instanceof String)\r
989         {\r
990             String s = (String) datum;\r
991             if (s.equals("__proto__") || s.equals("__parent__")) {\r
992                 Node result = new Node(type, obj, expr);\r
993                 result.putProp(Node.SPECIAL_PROP_PROP, s);\r
994                 return result;\r
995             }\r
996         }\r
997 \r
998         if (nodeOp == TokenStream.NOP)\r
999             return new Node(type, obj, id, expr);\r
1000 /*\r
1001 *    If the RHS expression could modify the LHS we have\r
1002 *    to construct a temporary to hold the LHS context\r
1003 *    prior to running the expression. Ditto, if the id\r
1004 *    expression has side-effects.\r
1005 *\r
1006 *    XXX If the hasSideEffects tests take too long, we\r
1007 *       could make this an optimizer-only transform\r
1008 *       and always do the temp assignment otherwise.\r
1009 *\r
1010 */\r
1011         Node tmp1, tmp2, opLeft;\r
1012         if (hasSideEffects(expr)\r
1013                 || hasSideEffects(id)\r
1014                 || (obj.getType() != TokenStream.NAME))  {\r
1015             tmp1 = createNewTemp(obj);\r
1016             Node useTmp1 = createUseTemp(tmp1);\r
1017 \r
1018             tmp2 = createNewTemp(id);\r
1019             Node useTmp2 = createUseTemp(tmp2);\r
1020 \r
1021             opLeft = new Node(nodeType, useTmp1, useTmp2);\r
1022         } else {\r
1023             tmp1 = obj.cloneNode();\r
1024             tmp2 = id.cloneNode();\r
1025             opLeft = new Node(nodeType, obj, id);\r
1026         }\r
1027 \r
1028         if (convert != null)\r
1029             opLeft = createConvert(convert, opLeft);\r
1030         if (postfix)\r
1031             opLeft = createNewTemp(opLeft);\r
1032         Node op = new Node(nodeOp, opLeft, expr);\r
1033 \r
1034         Node result = new Node(type, tmp1, tmp2, op);\r
1035         if (postfix) {\r
1036             result = new Node(TokenStream.COMMA, result,\r
1037                               createUseTemp(opLeft));\r
1038         }\r
1039 \r
1040         return result;\r
1041     }\r
1042     \r
1043     private void reportError(String msgResource) {\r
1044 \r
1045         if (scope != null)\r
1046             throw NativeGlobal.constructError(\r
1047                         Context.getContext(), "SyntaxError",\r
1048                         ScriptRuntime.getMessage0(msgResource),\r
1049                         scope);\r
1050         else {\r
1051             String message = Context.getMessage0(msgResource);\r
1052             Context.reportError(message, ts.getSourceName(), ts.getLineno(), \r
1053                                 ts.getLine(), ts.getOffset());\r
1054         }\r
1055     }\r
1056     \r
1057     // Only needed to get file/line information. Could create an interface\r
1058     // that TokenStream implements if we want to make the connection less\r
1059     // direct.\r
1060     private TokenStream ts;\r
1061     \r
1062     // Only needed to pass to the Erorr exception constructors\r
1063     private Scriptable scope;\r
1064 }\r
1065 \r