~ubuntu-branches/ubuntu/karmic/rhino/karmic

« back to all changes in this revision

Viewing changes to src/org/mozilla/javascript/IRFactory.java

  • Committer: Bazaar Package Importer
  • Author(s): Jerry Haltom
  • Date: 2005-03-19 16:56:07 UTC
  • mto: (11.1.1 squeeze)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20050319165607-geu3j3fnqlkpqkh1
Tags: upstream-1.6.R1
ImportĀ upstreamĀ versionĀ 1.6.R1

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2
 
 *
3
 
 * The contents of this file are subject to the Netscape Public
4
 
 * License Version 1.1 (the "License"); you may not use this file
5
 
 * except in compliance with the License. You may obtain a copy of
6
 
 * the License at http://www.mozilla.org/NPL/
7
 
 *
8
 
 * Software distributed under the License is distributed on an "AS
9
 
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
10
 
 * implied. See the License for the specific language governing
11
 
 * rights and limitations under the License.
12
 
 *
13
 
 * The Original Code is Rhino code, released
14
 
 * May 6, 1999.
15
 
 *
16
 
 * The Initial Developer of the Original Code is Netscape
17
 
 * Communications Corporation.  Portions created by Netscape are
18
 
 * Copyright (C) 1997-1999 Netscape Communications Corporation. All
19
 
 * Rights Reserved.
20
 
 *
21
 
 * Contributor(s): 
22
 
 * Norris Boyd
23
 
 *
24
 
 * Alternatively, the contents of this file may be used under the
25
 
 * terms of the GNU Public License (the "GPL"), in which case the
26
 
 * provisions of the GPL are applicable instead of those above.
27
 
 * If you wish to allow use of your version of this file only
28
 
 * under the terms of the GPL and not to allow others to use your
29
 
 * version of this file under the NPL, indicate your decision by
30
 
 * deleting the provisions above and replace them with the notice
31
 
 * and other provisions required by the GPL.  If you do not delete
32
 
 * the provisions above, a recipient may use your version of this
33
 
 * file under either the NPL or the GPL.
34
 
 */
35
 
 
36
 
package org.mozilla.javascript;
37
 
 
38
 
/**
39
 
 * This class allows the creation of nodes, and follows the Factory pattern.
40
 
 *
41
 
 * @see Node
42
 
 * @author Mike McCabe
43
 
 * @author Norris Boyd
44
 
 */
45
 
public class IRFactory {
46
 
    
47
 
    public IRFactory(TokenStream ts, Scriptable scope) {
48
 
        this.ts = ts;
49
 
        this.scope = scope;
50
 
    }
51
 
 
52
 
    /**
53
 
     * Script (for associating file/url names with toplevel scripts.)
54
 
     */
55
 
    public Object createScript(Object body, String sourceName, 
56
 
                               int baseLineno, int endLineno, Object source)
57
 
    {
58
 
        Node result = new Node(TokenStream.SCRIPT, sourceName);
59
 
        Node children = ((Node) body).getFirstChild();
60
 
        if (children != null)
61
 
            result.addChildrenToBack(children);
62
 
        result.putProp(Node.SOURCENAME_PROP, sourceName);
63
 
        result.putProp(Node.BASE_LINENO_PROP, new Integer(baseLineno));
64
 
        result.putProp(Node.END_LINENO_PROP, new Integer(endLineno));
65
 
        if (source != null)
66
 
            result.putProp(Node.SOURCE_PROP, source);
67
 
        return result;
68
 
    }
69
 
 
70
 
    /**
71
 
     * Leaf
72
 
     */
73
 
    public Object createLeaf(int nodeType) {
74
 
            return new Node(nodeType);
75
 
    }
76
 
 
77
 
    public Object createLeaf(int nodeType, String id) {
78
 
        return new Node(nodeType, id);
79
 
    }
80
 
 
81
 
    public Object createLeaf(int nodeType, int nodeOp) {
82
 
        return new Node(nodeType, new Integer(nodeOp));
83
 
    }
84
 
 
85
 
    /**
86
 
     * Statement leaf nodes.
87
 
     */
88
 
 
89
 
    public Object createSwitch(int lineno) {
90
 
        return new Node(TokenStream.SWITCH, new Integer(lineno));
91
 
    }
92
 
 
93
 
    public Object createVariables(int lineno) {
94
 
        return new Node(TokenStream.VAR, new Integer(lineno));
95
 
    }
96
 
 
97
 
    public Object createExprStatement(Object expr, int lineno) {
98
 
        return new Node(TokenStream.EXPRSTMT, (Node) expr, new Integer(lineno));
99
 
    }
100
 
 
101
 
    /**
102
 
     * Name
103
 
     */
104
 
    public Object createName(String name) {
105
 
        return new Node(TokenStream.NAME, name);
106
 
    }
107
 
 
108
 
    /**
109
 
     * String (for literals)
110
 
     */
111
 
    public Object createString(String string) {
112
 
        return new Node(TokenStream.STRING, string);
113
 
    }
114
 
 
115
 
    /**
116
 
     * Number (for literals)
117
 
     */
118
 
    public Object createNumber(Number number) {
119
 
        return new Node(TokenStream.NUMBER, number);
120
 
    }
121
 
 
122
 
    /**
123
 
     * Catch clause of try/catch/finally
124
 
     * @param varName the name of the variable to bind to the exception
125
 
     * @param catchCond the condition under which to catch the exception.
126
 
     *                  May be null if no condition is given.
127
 
     * @param stmts the statements in the catch clause
128
 
     * @param lineno the starting line number of the catch clause
129
 
     */
130
 
    public Object createCatch(String varName, Object catchCond, Object stmts,
131
 
                              int lineno)
132
 
    {
133
 
        if (catchCond == null)
134
 
            catchCond = new Node(TokenStream.PRIMARY, 
135
 
                                 new Integer(TokenStream.TRUE));
136
 
        Node result = new Node(TokenStream.CATCH, (Node)createName(varName), 
137
 
                               (Node)catchCond, (Node)stmts);
138
 
        result.setDatum(new Integer(lineno));
139
 
        return result;
140
 
    }
141
 
    
142
 
    /**
143
 
     * Throw
144
 
     */
145
 
    public Object createThrow(Object expr, int lineno) {
146
 
        return new Node(TokenStream.THROW, (Node)expr, new Integer(lineno));
147
 
    }
148
 
 
149
 
    /**
150
 
     * Return
151
 
     */
152
 
    public Object createReturn(Object expr, int lineno) {
153
 
        return expr == null
154
 
            ? new Node(TokenStream.RETURN, new Integer(lineno))
155
 
            : new Node(TokenStream.RETURN, (Node)expr, new Integer(lineno));
156
 
    }
157
 
 
158
 
    /**
159
 
     * Label
160
 
     */
161
 
    public Object createLabel(String label, int lineno) {
162
 
        Node result = new Node(TokenStream.LABEL, new Integer(lineno));
163
 
        Node name = new Node(TokenStream.NAME, label);
164
 
        result.addChildToBack(name);
165
 
        return result;
166
 
    }
167
 
 
168
 
    /**
169
 
     * Break (possibly labeled)
170
 
     */
171
 
    public Object createBreak(String label, int lineno) {
172
 
        Node result = new Node(TokenStream.BREAK, new Integer(lineno));
173
 
        if (label == null) {
174
 
            return result;
175
 
        } else {
176
 
            Node name = new Node(TokenStream.NAME, label);
177
 
            result.addChildToBack(name);
178
 
            return result;
179
 
        }
180
 
    }
181
 
 
182
 
    /**
183
 
     * Continue (possibly labeled)
184
 
     */
185
 
    public Object createContinue(String label, int lineno) {
186
 
        Node result = new Node(TokenStream.CONTINUE, new Integer(lineno));
187
 
        if (label == null) {
188
 
            return result;
189
 
        } else {
190
 
            Node name = new Node(TokenStream.NAME, label);
191
 
            result.addChildToBack(name);
192
 
            return result;
193
 
        }
194
 
    }
195
 
 
196
 
    /**
197
 
     * Statement block
198
 
     * Creates the empty statement block
199
 
     * Must make subsequent calls to add statements to the node
200
 
     */
201
 
    public Object createBlock(int lineno) {
202
 
        return new Node(TokenStream.BLOCK, new Integer(lineno));
203
 
    }
204
 
    
205
 
    public Object createFunctionNode(String name, Object args, 
206
 
                                     Object statements) 
207
 
    {
208
 
        if (name == null)
209
 
            name = "";
210
 
        return new FunctionNode(name, (Node) args, (Node) statements);
211
 
    }
212
 
 
213
 
    public Object createFunction(String name, Object args, Object statements,
214
 
                                 String sourceName, int baseLineno, 
215
 
                                 int endLineno, Object source,
216
 
                                 boolean isExpr)
217
 
    {
218
 
        FunctionNode f = (FunctionNode) createFunctionNode(name, args, 
219
 
                                                           statements);
220
 
        f.setFunctionType(isExpr ? FunctionNode.FUNCTION_EXPRESSION
221
 
                                 : FunctionNode.FUNCTION_STATEMENT);
222
 
        f.putProp(Node.SOURCENAME_PROP, sourceName);
223
 
        f.putProp(Node.BASE_LINENO_PROP, new Integer(baseLineno));
224
 
        f.putProp(Node.END_LINENO_PROP, new Integer(endLineno));
225
 
        if (source != null)
226
 
            f.putProp(Node.SOURCE_PROP, source);
227
 
        Node result = new Node(TokenStream.FUNCTION, name);
228
 
        result.putProp(Node.FUNCTION_PROP, f);
229
 
        return result;
230
 
    }
231
 
    
232
 
    public void setFunctionExpressionStatement(Object o) {
233
 
        Node n = (Node) o;
234
 
        FunctionNode f = (FunctionNode) n.getProp(Node.FUNCTION_PROP);
235
 
        f.setFunctionType(FunctionNode.FUNCTION_EXPRESSION_STATEMENT);
236
 
    }
237
 
 
238
 
    /**
239
 
     * Add a child to the back of the given node.  This function
240
 
     * breaks the Factory abstraction, but it removes a requirement
241
 
     * from implementors of Node.
242
 
     */
243
 
    public void addChildToBack(Object parent, Object child) {
244
 
        ((Node)parent).addChildToBack((Node)child);
245
 
    }
246
 
 
247
 
    /**
248
 
     * While
249
 
     */
250
 
    public Object createWhile(Object cond, Object body, int lineno) {
251
 
        // Just add a GOTO to the condition in the do..while
252
 
        Node result = (Node) createDoWhile(body, cond, lineno);
253
 
        Node condTarget = (Node) result.getProp(Node.CONTINUE_PROP);
254
 
        Node GOTO = new Node(TokenStream.GOTO);
255
 
        GOTO.putProp(Node.TARGET_PROP, condTarget);
256
 
        result.addChildToFront(GOTO);
257
 
        return result;
258
 
    }
259
 
 
260
 
    /**
261
 
     * DoWhile
262
 
     */
263
 
    public Object createDoWhile(Object body, Object cond, int lineno) {
264
 
        Node result = new Node(TokenStream.LOOP, new Integer(lineno));
265
 
        Node bodyTarget = new Node(TokenStream.TARGET);
266
 
        Node condTarget = new Node(TokenStream.TARGET);
267
 
        Node IFEQ = new Node(TokenStream.IFEQ, (Node)cond);
268
 
        IFEQ.putProp(Node.TARGET_PROP, bodyTarget);
269
 
        Node breakTarget = new Node(TokenStream.TARGET);
270
 
 
271
 
        result.addChildToBack(bodyTarget);
272
 
        result.addChildrenToBack((Node)body);
273
 
        result.addChildToBack(condTarget);
274
 
        result.addChildToBack(IFEQ);
275
 
        result.addChildToBack(breakTarget);
276
 
 
277
 
        result.putProp(Node.BREAK_PROP, breakTarget);
278
 
        result.putProp(Node.CONTINUE_PROP, condTarget);
279
 
 
280
 
        return result;
281
 
    }
282
 
 
283
 
    /**
284
 
     * For
285
 
     */
286
 
    public Object createFor(Object init, Object test, Object incr,
287
 
                            Object body, int lineno)
288
 
    {
289
 
        if (((Node) test).getType() == TokenStream.VOID) {
290
 
            test = new Node(TokenStream.PRIMARY, 
291
 
                            new Integer(TokenStream.TRUE));
292
 
        }
293
 
        Node result = (Node)createWhile(test, body, lineno);
294
 
        Node initNode = (Node) init;
295
 
        if (initNode.getType() != TokenStream.VOID) {
296
 
            if (initNode.getType() != TokenStream.VAR)
297
 
                initNode = new Node(TokenStream.POP, initNode);
298
 
            result.addChildToFront(initNode);
299
 
        }
300
 
        Node condTarget = (Node)result.getProp(Node.CONTINUE_PROP);
301
 
        Node incrTarget = new Node(TokenStream.TARGET);
302
 
        result.addChildBefore(incrTarget, condTarget);
303
 
        if (((Node) incr).getType() != TokenStream.VOID) {
304
 
            incr = createUnary(TokenStream.POP, incr);
305
 
            result.addChildAfter((Node)incr, incrTarget);
306
 
        }
307
 
        result.putProp(Node.CONTINUE_PROP, incrTarget);
308
 
        return result;
309
 
    }
310
 
 
311
 
    /**
312
 
     * For .. In
313
 
     *
314
 
     */
315
 
    public Object createForIn(Object lhs, Object obj, Object body, int lineno) {
316
 
        String name;
317
 
        Node lhsNode = (Node) lhs;
318
 
        Node objNode = (Node) obj;
319
 
        int type = lhsNode.getType();
320
 
 
321
 
        Node lvalue = lhsNode;
322
 
        switch (type) {
323
 
 
324
 
          case TokenStream.NAME:
325
 
          case TokenStream.GETPROP:
326
 
          case TokenStream.GETELEM:
327
 
            break;
328
 
 
329
 
          case TokenStream.VAR:
330
 
            /*
331
 
             * check that there was only one variable given.
332
 
             * we can't do this in the parser, because then the
333
 
             * parser would have to know something about the
334
 
             * 'init' node of the for-in loop.
335
 
             */
336
 
            Node lastChild = lhsNode.getLastChild();
337
 
            if (lhsNode.getFirstChild() != lastChild) {
338
 
                reportError("msg.mult.index");
339
 
            }
340
 
            lvalue = new Node(TokenStream.NAME, lastChild.getString());
341
 
            break;
342
 
 
343
 
          default:
344
 
            reportError("msg.bad.for.in.lhs");
345
 
            return objNode;
346
 
        }
347
 
 
348
 
        Node init = new Node(TokenStream.ENUMINIT, objNode);
349
 
        Node next = new Node(TokenStream.ENUMNEXT);
350
 
        next.putProp(Node.ENUM_PROP, init);
351
 
        Node temp = createNewTemp(next);
352
 
        Node cond = new Node(TokenStream.EQOP, new Integer(TokenStream.NE));
353
 
        cond.addChildToBack(temp);
354
 
        cond.addChildToBack(new Node(TokenStream.PRIMARY,
355
 
                                     new Integer(TokenStream.NULL)));
356
 
        Node newBody = new Node(TokenStream.BLOCK);
357
 
        Node assign = (Node) createAssignment(TokenStream.NOP, lvalue,
358
 
                                              createUseTemp(temp), null,
359
 
                                              false);
360
 
        newBody.addChildToBack(new Node(TokenStream.POP, assign));
361
 
        newBody.addChildToBack((Node) body);
362
 
        Node result = (Node) createWhile(cond, newBody, lineno);
363
 
 
364
 
        result.addChildToFront(init);
365
 
        if (type == TokenStream.VAR)
366
 
            result.addChildToFront(lhsNode);
367
 
 
368
 
        Node done = new Node(TokenStream.ENUMDONE);
369
 
        done.putProp(Node.ENUM_PROP, init);
370
 
        result.addChildToBack(done);
371
 
 
372
 
        return result;
373
 
    }
374
 
 
375
 
    /**
376
 
     * Try/Catch/Finally
377
 
     *
378
 
     * The IRFactory tries to express as much as possible in the tree;
379
 
     * the responsibilities remaining for Codegen are to add the Java
380
 
     * handlers: (Either (but not both) of TARGET and FINALLY might not
381
 
     * be defined)
382
 
 
383
 
     * - a catch handler for javascript exceptions that unwraps the
384
 
     * exception onto the stack and GOTOes to the catch target -
385
 
     * TARGET_PROP in the try node.
386
 
 
387
 
     * - a finally handler that catches any exception, stores it to a
388
 
     * temporary, and JSRs to the finally target - FINALLY_PROP in the
389
 
     * try node - before re-throwing the exception.
390
 
 
391
 
     * ... and a goto to GOTO around these handlers.
392
 
     */
393
 
    public Object createTryCatchFinally(Object tryblock, Object catchblocks,
394
 
                                        Object finallyblock, int lineno)
395
 
    {
396
 
        Node trynode = (Node)tryblock;
397
 
 
398
 
        // short circuit
399
 
        if (trynode.getType() == TokenStream.BLOCK && !trynode.hasChildren())
400
 
            return trynode;
401
 
 
402
 
        Node pn = new Node(TokenStream.TRY, trynode, new Integer(lineno));
403
 
        Node catchNodes = (Node)catchblocks;
404
 
        boolean hasCatch = catchNodes.hasChildren();
405
 
        boolean hasFinally = false;
406
 
        Node finallyNode = null;
407
 
        Node finallyTarget = null;
408
 
        if (finallyblock != null) {
409
 
            finallyNode = (Node)finallyblock;
410
 
            hasFinally = (finallyNode.getType() != TokenStream.BLOCK
411
 
                          || finallyNode.hasChildren());
412
 
            if (hasFinally) {
413
 
                // make a TARGET for the finally that the tcf node knows about
414
 
                finallyTarget = new Node(TokenStream.TARGET);
415
 
                pn.putProp(Node.FINALLY_PROP, finallyTarget);
416
 
 
417
 
                // add jsr finally to the try block
418
 
                Node jsrFinally = new Node(TokenStream.JSR);
419
 
                jsrFinally.putProp(Node.TARGET_PROP, finallyTarget);
420
 
                pn.addChildToBack(jsrFinally);
421
 
            }
422
 
        }
423
 
 
424
 
        // short circuit
425
 
        if (!hasFinally && !hasCatch)  // bc finally might be an empty block...
426
 
            return trynode;
427
 
 
428
 
        Node endTarget = new Node(TokenStream.TARGET);
429
 
        Node GOTOToEnd = new Node(TokenStream.GOTO);
430
 
        GOTOToEnd.putProp(Node.TARGET_PROP, endTarget);
431
 
        pn.addChildToBack(GOTOToEnd);
432
 
       
433
 
        if (hasCatch) {
434
 
            /*
435
 
             *
436
 
               Given
437
 
               
438
 
                try {
439
 
                        throw 3;
440
 
                } catch (e: e instanceof Object) {
441
 
                        print("object");
442
 
                } catch (e2) {
443
 
                        print(e2);
444
 
                }
445
 
 
446
 
               rewrite as
447
 
 
448
 
                try {
449
 
                        throw 3;
450
 
                } catch (x) {
451
 
                        o = newScope();
452
 
                        o.e = x;
453
 
                        with (o) {
454
 
                                if (e instanceof Object) {
455
 
                                        print("object");
456
 
                                }
457
 
                        }
458
 
                        o2 = newScope();
459
 
                        o2.e2 = x;
460
 
                        with (o2) {
461
 
                                if (true) {
462
 
                                        print(e2);
463
 
                                }
464
 
                        }
465
 
                }
466
 
            */
467
 
            // make a TARGET for the catch that the tcf node knows about
468
 
            Node catchTarget = new Node(TokenStream.TARGET);
469
 
            pn.putProp(Node.TARGET_PROP, catchTarget);
470
 
            // mark it
471
 
            pn.addChildToBack(catchTarget);
472
 
            
473
 
            // get the exception object and store it in a temp
474
 
            Node exn = createNewLocal(new Node(TokenStream.VOID));
475
 
            pn.addChildToBack(new Node(TokenStream.POP, exn));
476
 
            
477
 
            Node endCatch = new Node(TokenStream.TARGET);
478
 
 
479
 
            // add [jsr finally?] goto end to each catch block
480
 
            // expects catchNode children to be (cond block) pairs.
481
 
            Node cb = catchNodes.getFirstChild();
482
 
            while (cb != null) {
483
 
                Node catchStmt = new Node(TokenStream.BLOCK);
484
 
                int catchLineNo = ((Integer)cb.getDatum()).intValue();
485
 
                
486
 
                Node name = cb.getFirstChild();
487
 
                Node cond = name.getNextSibling();
488
 
                Node catchBlock = cond.getNextSibling();
489
 
                cb.removeChild(name);
490
 
                cb.removeChild(cond);
491
 
                cb.removeChild(catchBlock);
492
 
                
493
 
                Node newScope = createNewLocal(new Node(TokenStream.NEWSCOPE));
494
 
                Node initScope = new Node(TokenStream.SETPROP, newScope, 
495
 
                                          new Node(TokenStream.STRING, 
496
 
                                                   name.getString()), 
497
 
                                          createUseLocal(exn));
498
 
                catchStmt.addChildToBack(new Node(TokenStream.POP, initScope));
499
 
                
500
 
                catchBlock.addChildToBack(new Node(TokenStream.LEAVEWITH));
501
 
                Node GOTOToEndCatch = new Node(TokenStream.GOTO);
502
 
                GOTOToEndCatch.putProp(Node.TARGET_PROP, endCatch);
503
 
                catchBlock.addChildToBack(GOTOToEndCatch);
504
 
                
505
 
                Node ifStmt = (Node) createIf(cond, catchBlock, null, catchLineNo);
506
 
                // Try..catch produces "with" code in order to limit 
507
 
                // the scope of the exception object.
508
 
                // OPT: We should be able to figure out the correct
509
 
                //      scoping at compile-time and avoid the
510
 
                //      runtime overhead.
511
 
                Node withStmt = (Node) createWith(createUseLocal(newScope), 
512
 
                                                  ifStmt, catchLineNo);
513
 
                catchStmt.addChildToBack(withStmt);
514
 
                
515
 
                pn.addChildToBack(catchStmt);
516
 
                
517
 
                // move to next cb 
518
 
                cb = cb.getNextSibling();
519
 
            }
520
 
            
521
 
            // Generate code to rethrow if no catch clause was executed
522
 
            Node rethrow = new Node(TokenStream.THROW, createUseLocal(exn));
523
 
            pn.addChildToBack(rethrow);
524
 
 
525
 
            pn.addChildToBack(endCatch);
526
 
            // add a JSR finally if needed
527
 
            if (hasFinally) {
528
 
                Node jsrFinally = new Node(TokenStream.JSR);
529
 
                jsrFinally.putProp(Node.TARGET_PROP, finallyTarget);
530
 
                pn.addChildToBack(jsrFinally);
531
 
                Node GOTO = new Node(TokenStream.GOTO);
532
 
                GOTO.putProp(Node.TARGET_PROP, endTarget);
533
 
                pn.addChildToBack(GOTO);
534
 
            }
535
 
        }
536
 
 
537
 
        if (hasFinally) {
538
 
            pn.addChildToBack(finallyTarget);
539
 
            Node returnTemp = createNewLocal(new Node(TokenStream.VOID));
540
 
            Node popAndMake = new Node(TokenStream.POP, returnTemp);
541
 
            pn.addChildToBack(popAndMake);
542
 
            pn.addChildToBack(finallyNode);
543
 
            Node ret = createUseLocal(returnTemp);
544
 
 
545
 
            // add the magic prop that makes it output a RET
546
 
            ret.putProp(Node.TARGET_PROP, Boolean.TRUE);
547
 
            pn.addChildToBack(ret);
548
 
        }
549
 
        pn.addChildToBack(endTarget);
550
 
        return pn;
551
 
    }
552
 
 
553
 
    /**
554
 
     * Throw, Return, Label, Break and Continue are defined in ASTFactory.
555
 
     */
556
 
 
557
 
    /**
558
 
     * With
559
 
     */
560
 
    public Object createWith(Object obj, Object body, int lineno) {
561
 
        Node result = new Node(TokenStream.BLOCK, new Integer(lineno));
562
 
        result.addChildToBack(new Node(TokenStream.ENTERWITH, (Node)obj));
563
 
        Node bodyNode = new Node(TokenStream.WITH, (Node) body,
564
 
                                 new Integer(lineno));
565
 
        result.addChildrenToBack(bodyNode);
566
 
        result.addChildToBack(new Node(TokenStream.LEAVEWITH));
567
 
        return result;
568
 
    }
569
 
 
570
 
    /**
571
 
     * Array Literal
572
 
     * <BR>createArrayLiteral rewrites its argument as array creation
573
 
     * plus a series of array element entries, so later compiler
574
 
     * stages don't need to know about array literals.
575
 
     */
576
 
    public Object createArrayLiteral(Object obj) {
577
 
        Node array;
578
 
        Node result;
579
 
        array = result = new Node(TokenStream.NEW,
580
 
                                  new Node(TokenStream.NAME, "Array"));
581
 
        Node temp = createNewTemp(result);
582
 
        result = temp;
583
 
 
584
 
        java.util.Enumeration children = ((Node) obj).getChildIterator();
585
 
 
586
 
        Node elem = null;
587
 
        int i = 0;
588
 
        while (children.hasMoreElements()) {
589
 
            elem = (Node) children.nextElement();
590
 
            if (elem.getType() == TokenStream.PRIMARY &&
591
 
                elem.getInt() == TokenStream.UNDEFINED)
592
 
            {
593
 
                i++;
594
 
                continue;
595
 
            }
596
 
            Node addelem = new Node(TokenStream.SETELEM, createUseTemp(temp),
597
 
                                    new Node(TokenStream.NUMBER,
598
 
                                             new Integer(i)),
599
 
                                    elem);
600
 
            i++;
601
 
            result = new Node(TokenStream.COMMA, result, addelem);
602
 
        }
603
 
 
604
 
        /*
605
 
         * If the version is 120, then new Array(4) means create a new
606
 
         * array with 4 as the first element.  In this case, we might
607
 
         * need to explicitly check against trailing undefined
608
 
         * elements in the array literal, and set the length manually
609
 
         * if these occur.  Otherwise, we can add an argument to the
610
 
         * node specifying new Array() to provide the array length.
611
 
         * (Which will make Array optimizations involving allocating a
612
 
         * Java array to back the javascript array work better.)
613
 
         */
614
 
        if (Context.getContext().getLanguageVersion() == Context.VERSION_1_2) {
615
 
            /* When last array element is empty, we need to set the
616
 
             * length explicitly, because we can't depend on SETELEM
617
 
             * to do it for us - because empty [,,] array elements
618
 
             * never set anything at all. */
619
 
            if (elem != null &&
620
 
                elem.getType() == TokenStream.PRIMARY &&
621
 
                elem.getInt() == TokenStream.UNDEFINED)
622
 
            {
623
 
                Node setlength = new Node(TokenStream.SETPROP,
624
 
                                          createUseTemp(temp),
625
 
                                          new Node(TokenStream.STRING,
626
 
                                                   "length"),
627
 
                                          new Node(TokenStream.NUMBER,
628
 
                                                   new Integer(i)));
629
 
                result = new Node(TokenStream.COMMA, result, setlength);
630
 
            }
631
 
        } else {
632
 
            array.addChildToBack(new Node(TokenStream.NUMBER,
633
 
                                          new Integer(i)));
634
 
        }
635
 
        return new Node(TokenStream.COMMA, result, createUseTemp(temp));
636
 
    }
637
 
 
638
 
    /**
639
 
     * Object Literals
640
 
     * <BR> createObjectLiteral rewrites its argument as object
641
 
     * creation plus object property entries, so later compiler
642
 
     * stages don't need to know about object literals.
643
 
     */
644
 
    public Object createObjectLiteral(Object obj) {
645
 
        Node result = new Node(TokenStream.NEW, new Node(TokenStream.NAME,
646
 
                                                         "Object"));
647
 
        Node temp = createNewTemp(result);
648
 
        result = temp;
649
 
 
650
 
        java.util.Enumeration children = ((Node) obj).getChildIterator();
651
 
 
652
 
        while (children.hasMoreElements()) {
653
 
            Node elem = (Node)children.nextElement();
654
 
 
655
 
            int op = (elem.getType() == TokenStream.NAME)
656
 
                   ? TokenStream.SETPROP
657
 
                   : TokenStream.SETELEM;
658
 
            Node addelem = new Node(op, createUseTemp(temp),
659
 
                                    elem, (Node)children.nextElement());
660
 
            result = new Node(TokenStream.COMMA, result, addelem);
661
 
        }
662
 
        return new Node(TokenStream.COMMA, result, createUseTemp(temp));
663
 
    }
664
 
 
665
 
    /**
666
 
     * Regular expressions
667
 
     */
668
 
    public Object createRegExp(String string, String flags) {
669
 
        return flags.length() == 0
670
 
               ? new Node(TokenStream.OBJECT,
671
 
                          new Node(TokenStream.STRING, string))
672
 
               : new Node(TokenStream.OBJECT,
673
 
                          new Node(TokenStream.STRING, string),
674
 
                          new Node(TokenStream.STRING, flags));
675
 
    }
676
 
 
677
 
    /**
678
 
     * If statement
679
 
     */
680
 
    public Object createIf(Object cond, Object ifTrue, Object ifFalse,
681
 
                           int lineno)
682
 
    {
683
 
        Node result = new Node(TokenStream.BLOCK, new Integer(lineno));
684
 
        Node ifNotTarget = new Node(TokenStream.TARGET);
685
 
        Node IFNE = new Node(TokenStream.IFNE, (Node) cond);
686
 
        IFNE.putProp(Node.TARGET_PROP, ifNotTarget);
687
 
 
688
 
        result.addChildToBack(IFNE);
689
 
        result.addChildrenToBack((Node)ifTrue);
690
 
 
691
 
        if (ifFalse != null) {
692
 
            Node GOTOToEnd = new Node(TokenStream.GOTO);
693
 
            Node endTarget = new Node(TokenStream.TARGET);
694
 
            GOTOToEnd.putProp(Node.TARGET_PROP, endTarget);
695
 
            result.addChildToBack(GOTOToEnd);
696
 
            result.addChildToBack(ifNotTarget);
697
 
            result.addChildrenToBack((Node)ifFalse);
698
 
            result.addChildToBack(endTarget);
699
 
        } else {
700
 
            result.addChildToBack(ifNotTarget);
701
 
        }
702
 
 
703
 
        return result;
704
 
    }
705
 
 
706
 
    public Object createTernary(Object cond, Object ifTrue, Object ifFalse) {
707
 
        return createIf(cond, ifTrue, ifFalse, -1);
708
 
    }
709
 
 
710
 
    /**
711
 
     * Unary
712
 
     */
713
 
    public Object createUnary(int nodeType, Object child) {
714
 
        Node childNode = (Node) child;
715
 
        if (nodeType == TokenStream.DELPROP) {
716
 
            int childType = childNode.getType();
717
 
            Node left;
718
 
            Node right;
719
 
            if (childType == TokenStream.NAME) {
720
 
                // Transform Delete(Name "a")
721
 
                //  to Delete(Bind("a"), String("a"))
722
 
                childNode.setType(TokenStream.BINDNAME);
723
 
                left = childNode;
724
 
                right = childNode.cloneNode();
725
 
                right.setType(TokenStream.STRING);
726
 
            } else if (childType == TokenStream.GETPROP ||
727
 
                       childType == TokenStream.GETELEM)
728
 
            {
729
 
                left = childNode.getFirstChild();
730
 
                right = childNode.getLastChild();
731
 
                childNode.removeChild(left);
732
 
                childNode.removeChild(right);
733
 
            } else {
734
 
                return new Node(TokenStream.PRIMARY,
735
 
                                new Integer(TokenStream.TRUE));
736
 
            }
737
 
            return new Node(nodeType, left, right);
738
 
        }
739
 
        return new Node(nodeType, childNode);
740
 
    }
741
 
 
742
 
    public Object createUnary(int nodeType, int nodeOp, Object child) {
743
 
        Node childNode = (Node) child;
744
 
        int childType = childNode.getType();
745
 
        if (nodeOp == TokenStream.TYPEOF &&
746
 
            childType == TokenStream.NAME)
747
 
        {
748
 
            childNode.setType(TokenStream.TYPEOF);
749
 
            return childNode;
750
 
        }
751
 
 
752
 
        if (nodeType == TokenStream.INC || nodeType == TokenStream.DEC) {
753
 
 
754
 
            if (!hasSideEffects(childNode)
755
 
                && (nodeOp == TokenStream.POST)
756
 
                && (childType == TokenStream.NAME 
757
 
                    || childType == TokenStream.GETPROP
758
 
                    || childType == TokenStream.GETELEM))
759
 
            {
760
 
                // if it's not a LHS type, createAssignment (below) will throw
761
 
                // an exception.
762
 
                return new Node(nodeType, childNode);
763
 
            }
764
 
 
765
 
            /*
766
 
             * Transform INC/DEC ops to +=1, -=1,
767
 
             * expecting later optimization of all +/-=1 cases to INC, DEC.
768
 
             */
769
 
            // we have to use Double for now, because
770
 
            // 0.0 and 1.0 are stored as dconst_[01],
771
 
            // and using a Float creates a stack mismatch.
772
 
            Node rhs = (Node) createNumber(new Double(1.0));
773
 
 
774
 
            return createAssignment(nodeType == TokenStream.INC
775
 
                                        ? TokenStream.ADD
776
 
                                        : TokenStream.SUB,
777
 
                                    childNode,
778
 
                                    rhs,
779
 
                                    ScriptRuntime.NumberClass,
780
 
                                    nodeOp == TokenStream.POST);
781
 
        }
782
 
 
783
 
        Node result = new Node(nodeType, new Integer(nodeOp));
784
 
        result.addChildToBack((Node)child);
785
 
        return result;
786
 
    }
787
 
 
788
 
    /**
789
 
     * Binary
790
 
     */
791
 
    public Object createBinary(int nodeType, Object left, Object right) {
792
 
        Node temp;
793
 
        switch (nodeType) {
794
 
 
795
 
          case TokenStream.DOT:
796
 
            nodeType = TokenStream.GETPROP;
797
 
            Node idNode = (Node) right;
798
 
            idNode.setType(TokenStream.STRING);
799
 
            String id = idNode.getString();
800
 
            if (id.equals("__proto__") || id.equals("__parent__")) {
801
 
                Node result = new Node(nodeType, (Node) left);
802
 
                result.putProp(Node.SPECIAL_PROP_PROP, id);
803
 
                return result;
804
 
            }
805
 
            break;
806
 
 
807
 
          case TokenStream.LB:
808
 
            // OPT: could optimize to GETPROP iff string can't be a number
809
 
            nodeType = TokenStream.GETELEM;
810
 
            break;
811
 
/*
812
 
          case TokenStream.AND:
813
 
            temp = createNewTemp((Node) left);
814
 
            return createTernary(temp, right, createUseTemp(temp));
815
 
 
816
 
          case TokenStream.OR:
817
 
            temp = createNewTemp((Node) left);
818
 
            return createTernary(temp, createUseTemp(temp), right);
819
 
*/            
820
 
        }
821
 
        return new Node(nodeType, (Node)left, (Node)right);
822
 
    }
823
 
 
824
 
    public Object createBinary(int nodeType, int nodeOp, Object left,
825
 
                               Object right)
826
 
    {
827
 
        if (nodeType == TokenStream.ASSIGN) {
828
 
            return createAssignment(nodeOp, (Node) left, (Node) right,
829
 
                                    null, false);
830
 
        }
831
 
        return new Node(nodeType, (Node) left, (Node) right,
832
 
                        new Integer(nodeOp));
833
 
    }
834
 
 
835
 
    public Object createAssignment(int nodeOp, Node left, Node right,
836
 
                                   Class convert, boolean postfix)
837
 
    {
838
 
        int nodeType = left.getType();
839
 
        String idString;
840
 
        Node id = null;
841
 
        switch (nodeType) {
842
 
          case TokenStream.NAME:
843
 
            return createSetName(nodeOp, left, right, convert, postfix);
844
 
 
845
 
          case TokenStream.GETPROP:
846
 
            idString = (String) left.getProp(Node.SPECIAL_PROP_PROP);
847
 
            if (idString != null)
848
 
                id = new Node(TokenStream.STRING, idString);
849
 
            /* fall through */
850
 
          case TokenStream.GETELEM:
851
 
            if (id == null)
852
 
                id = left.getLastChild();
853
 
            return createSetProp(nodeType, nodeOp, left.getFirstChild(),
854
 
                                 id, right, convert, postfix);
855
 
          default:
856
 
            // TODO: This should be a ReferenceError--but that's a runtime 
857
 
            //  exception. Should we compile an exception into the code?
858
 
            reportError("msg.bad.lhs.assign");
859
 
            return left;
860
 
        }
861
 
    }
862
 
 
863
 
    private Node createConvert(Class toType, Node expr) {
864
 
        if (toType == null)
865
 
            return expr;
866
 
        Node result = new Node(TokenStream.CONVERT, expr);
867
 
        result.putProp(Node.TYPE_PROP, ScriptRuntime.NumberClass);
868
 
        return result;
869
 
    }
870
 
 
871
 
    private Object createSetName(int nodeOp, Node left, Node right,
872
 
                                 Class convert, boolean postfix)
873
 
    {
874
 
        if (nodeOp == TokenStream.NOP) {
875
 
            left.setType(TokenStream.BINDNAME);
876
 
            return new Node(TokenStream.SETNAME, left, right);
877
 
        }
878
 
 
879
 
        String s = left.getString();
880
 
 
881
 
        if (s.equals("__proto__") || s.equals("__parent__")) {
882
 
            Node result = new Node(TokenStream.SETPROP, left, right);
883
 
            result.putProp(Node.SPECIAL_PROP_PROP, s);
884
 
            return result;
885
 
        }
886
 
 
887
 
        Node opLeft = new Node(TokenStream.NAME, s);
888
 
        if (convert != null)
889
 
            opLeft = createConvert(convert, opLeft);
890
 
        if (postfix)
891
 
            opLeft = createNewTemp(opLeft);
892
 
        Node op = new Node(nodeOp, opLeft, right);
893
 
 
894
 
        Node lvalueLeft = new Node(TokenStream.BINDNAME, s);
895
 
        Node result = new Node(TokenStream.SETNAME, lvalueLeft, op);
896
 
        if (postfix) {
897
 
            result = new Node(TokenStream.COMMA, result,
898
 
                              createUseTemp(opLeft));
899
 
        }
900
 
        return result;
901
 
    }
902
 
 
903
 
    public Node createNewTemp(Node n) {
904
 
        int type = n.getType();
905
 
        if (type == TokenStream.STRING || type == TokenStream.NUMBER) {
906
 
            // Optimization: clone these values rather than storing
907
 
            // and loading from a temp
908
 
            return n;
909
 
        }
910
 
        Node result = new Node(TokenStream.NEWTEMP, n);
911
 
        return result;
912
 
    }
913
 
 
914
 
    public Node createUseTemp(Node newTemp) {
915
 
        int type = newTemp.getType();
916
 
        if (type == TokenStream.NEWTEMP) {
917
 
            Node result = new Node(TokenStream.USETEMP);
918
 
            result.putProp(Node.TEMP_PROP, newTemp);
919
 
            Integer n = (Integer) newTemp.getProp(Node.USES_PROP);
920
 
            if (n == null) {
921
 
                n = new Integer(1);
922
 
            } else {
923
 
                if (n.intValue() < Integer.MAX_VALUE)
924
 
                    n = new Integer(n.intValue() + 1);
925
 
            }
926
 
            newTemp.putProp(Node.USES_PROP, n);
927
 
            return result;
928
 
        }
929
 
        return newTemp.cloneNode();
930
 
    }
931
 
 
932
 
    public Node createNewLocal(Node n) {
933
 
        Node result = new Node(TokenStream.NEWLOCAL, n);
934
 
        return result;
935
 
    }
936
 
 
937
 
    public Node createUseLocal(Node newLocal) {
938
 
        int type = newLocal.getType();
939
 
        if (type == TokenStream.NEWLOCAL) {
940
 
            Node result = new Node(TokenStream.USELOCAL);
941
 
            result.putProp(Node.LOCAL_PROP, newLocal);
942
 
            return result;
943
 
        }
944
 
        return newLocal.cloneNode();    // what's this path for ?
945
 
    }
946
 
 
947
 
    public static boolean hasSideEffects(Node exprTree) {
948
 
        switch (exprTree.getType()) {
949
 
            case TokenStream.INC:
950
 
            case TokenStream.DEC:
951
 
            case TokenStream.SETPROP:
952
 
            case TokenStream.SETELEM:
953
 
            case TokenStream.SETNAME:
954
 
            case TokenStream.CALL:
955
 
            case TokenStream.NEW:
956
 
                return true;
957
 
            default:
958
 
                Node child = exprTree.getFirstChild();
959
 
                while (child != null) {
960
 
                    if (hasSideEffects(child))
961
 
                        return true;
962
 
                    else
963
 
                        child = child.getNextSibling();
964
 
                }
965
 
                break;
966
 
        }
967
 
        return false;
968
 
    }
969
 
 
970
 
    private Node createSetProp(int nodeType, int nodeOp, Node obj, Node id,
971
 
                               Node expr, Class convert, boolean postfix)
972
 
    {
973
 
        int type = nodeType == TokenStream.GETPROP
974
 
                   ? TokenStream.SETPROP
975
 
                   : TokenStream.SETELEM;
976
 
 
977
 
        Object datum = id.getDatum();
978
 
        if (type == TokenStream.SETPROP && datum != null &&
979
 
            datum instanceof String)
980
 
        {
981
 
            String s = (String) datum;
982
 
            if (s.equals("__proto__") || s.equals("__parent__")) {
983
 
                Node result = new Node(type, obj, expr);
984
 
                result.putProp(Node.SPECIAL_PROP_PROP, s);
985
 
                return result;
986
 
            }
987
 
        }
988
 
 
989
 
        if (nodeOp == TokenStream.NOP)
990
 
            return new Node(type, obj, id, expr);
991
 
/*
992
 
*    If the RHS expression could modify the LHS we have
993
 
*    to construct a temporary to hold the LHS context
994
 
*    prior to running the expression. Ditto, if the id
995
 
*    expression has side-effects.
996
 
*
997
 
*    XXX If the hasSideEffects tests take too long, we
998
 
*       could make this an optimizer-only transform
999
 
*       and always do the temp assignment otherwise.
1000
 
*
1001
 
*/
1002
 
        Node tmp1, tmp2, opLeft;
1003
 
        if (hasSideEffects(expr)
1004
 
                || hasSideEffects(id)
1005
 
                || (obj.getType() != TokenStream.NAME))  {
1006
 
            tmp1 = createNewTemp(obj);
1007
 
            Node useTmp1 = createUseTemp(tmp1);
1008
 
 
1009
 
            tmp2 = createNewTemp(id);
1010
 
            Node useTmp2 = createUseTemp(tmp2);
1011
 
 
1012
 
            opLeft = new Node(nodeType, useTmp1, useTmp2);
1013
 
        } else {
1014
 
            tmp1 = obj.cloneNode();
1015
 
            tmp2 = id.cloneNode();
1016
 
            opLeft = new Node(nodeType, obj, id);
1017
 
        }
1018
 
 
1019
 
        if (convert != null)
1020
 
            opLeft = createConvert(convert, opLeft);
1021
 
        if (postfix)
1022
 
            opLeft = createNewTemp(opLeft);
1023
 
        Node op = new Node(nodeOp, opLeft, expr);
1024
 
 
1025
 
        Node result = new Node(type, tmp1, tmp2, op);
1026
 
        if (postfix) {
1027
 
            result = new Node(TokenStream.COMMA, result,
1028
 
                              createUseTemp(opLeft));
1029
 
        }
1030
 
 
1031
 
        return result;
1032
 
    }
1033
 
    
1034
 
    private void reportError(String msgResource) {
1035
 
 
1036
 
        if (scope != null)
1037
 
            throw NativeGlobal.constructError(
1038
 
                        Context.getContext(), "SyntaxError",
1039
 
                        ScriptRuntime.getMessage0(msgResource),
1040
 
                        scope);
1041
 
        else {
1042
 
            String message = Context.getMessage0(msgResource);
1043
 
            Context.reportError(message, ts.getSourceName(), ts.getLineno(), 
1044
 
                                ts.getLine(), ts.getOffset());
1045
 
        }
1046
 
    }
1047
 
    
1048
 
    // Only needed to get file/line information. Could create an interface
1049
 
    // that TokenStream implements if we want to make the connection less
1050
 
    // direct.
1051
 
    private TokenStream ts;
1052
 
    
1053
 
    // Only needed to pass to the Erorr exception constructors
1054
 
    private Scriptable scope;
1055
 
}
1056
 
 
 
1
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 
2
 *
 
3
 * The contents of this file are subject to the Netscape Public
 
4
 * License Version 1.1 (the "License"); you may not use this file
 
5
 * except in compliance with the License. You may obtain a copy of
 
6
 * the License at http://www.mozilla.org/NPL/
 
7
 *
 
8
 * Software distributed under the License is distributed on an "AS
 
9
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 
10
 * implied. See the License for the specific language governing
 
11
 * rights and limitations under the License.
 
12
 *
 
13
 * The Original Code is Rhino code, released
 
14
 * May 6, 1999.
 
15
 *
 
16
 * The Initial Developer of the Original Code is Netscape
 
17
 * Communications Corporation.  Portions created by Netscape are
 
18
 * Copyright (C) 1997-1999 Netscape Communications Corporation. All
 
19
 * Rights Reserved.
 
20
 *
 
21
 * Contributor(s):
 
22
 * Norris Boyd
 
23
 * Igor Bukanov
 
24
 * Ethan Hugg
 
25
 * Terry Lucas
 
26
 * Milen Nankov
 
27
 *
 
28
 * Alternatively, the contents of this file may be used under the
 
29
 * terms of the GNU Public License (the "GPL"), in which case the
 
30
 * provisions of the GPL are applicable instead of those above.
 
31
 * If you wish to allow use of your version of this file only
 
32
 * under the terms of the GPL and not to allow others to use your
 
33
 * version of this file under the NPL, indicate your decision by
 
34
 * deleting the provisions above and replace them with the notice
 
35
 * and other provisions required by the GPL.  If you do not delete
 
36
 * the provisions above, a recipient may use your version of this
 
37
 * file under either the NPL or the GPL.
 
38
 */
 
39
 
 
40
package org.mozilla.javascript;
 
41
 
 
42
/**
 
43
 * This class allows the creation of nodes, and follows the Factory pattern.
 
44
 *
 
45
 * @see Node
 
46
 * @author Mike McCabe
 
47
 * @author Norris Boyd
 
48
 */
 
49
final class IRFactory
 
50
{
 
51
    IRFactory(Parser parser)
 
52
    {
 
53
        this.parser = parser;
 
54
    }
 
55
 
 
56
    ScriptOrFnNode createScript()
 
57
    {
 
58
        return new ScriptOrFnNode(Token.SCRIPT);
 
59
    }
 
60
 
 
61
    /**
 
62
     * Script (for associating file/url names with toplevel scripts.)
 
63
     */
 
64
    void initScript(ScriptOrFnNode scriptNode, Node body)
 
65
    {
 
66
        Node children = body.getFirstChild();
 
67
        if (children != null) { scriptNode.addChildrenToBack(children); }
 
68
    }
 
69
 
 
70
    /**
 
71
     * Leaf
 
72
     */
 
73
    Node createLeaf(int nodeType)
 
74
    {
 
75
        return new Node(nodeType);
 
76
    }
 
77
 
 
78
    Node createLeaf(int nodeType, int nodeOp)
 
79
    {
 
80
        return new Node(nodeType, nodeOp);
 
81
    }
 
82
 
 
83
    /**
 
84
     * Statement leaf nodes.
 
85
     */
 
86
 
 
87
    Node createSwitch(Node expr, int lineno)
 
88
    {
 
89
        //
 
90
        // The switch will be rewritten from:
 
91
        //
 
92
        // switch (expr) {
 
93
        //   case test1: statements1;
 
94
        //   ...
 
95
        //   default: statementsDefault;
 
96
        //   ...
 
97
        //   case testN: statementsN;
 
98
        // }
 
99
        //
 
100
        // to:
 
101
        //
 
102
        // {
 
103
        //     switch (expr) {
 
104
        //       case test1: goto label1;
 
105
        //       ...
 
106
        //       case testN: goto labelN;
 
107
        //     }
 
108
        //     goto labelDefault;
 
109
        //   label1:
 
110
        //     statements1;
 
111
        //   ...
 
112
        //   labelDefault:
 
113
        //     statementsDefault;
 
114
        //   ...
 
115
        //   labelN:
 
116
        //     statementsN;
 
117
        //   breakLabel:
 
118
        // }
 
119
        //
 
120
        // where inside switch each "break;" without label will be replaced
 
121
        // by "goto breakLabel".
 
122
        //
 
123
        // If the original switch does not have the default label, then
 
124
        // the transformed code would contain after the switch instead of
 
125
        //     goto labelDefault;
 
126
        // the following goto:
 
127
        //     goto breakLabel;
 
128
        //
 
129
 
 
130
        Node.Jump switchNode = new Node.Jump(Token.SWITCH, expr, lineno);
 
131
        Node block = new Node(Token.BLOCK, switchNode);
 
132
        return block;
 
133
    }
 
134
 
 
135
    /**
 
136
     * If caseExpression argument is null it indicate default label.
 
137
     */
 
138
    void addSwitchCase(Node switchBlock, Node caseExpression, Node statements)
 
139
    {
 
140
        if (switchBlock.getType() != Token.BLOCK) throw Kit.codeBug();
 
141
        Node.Jump switchNode = (Node.Jump)switchBlock.getFirstChild();
 
142
        if (switchNode.getType() != Token.SWITCH) throw Kit.codeBug();
 
143
 
 
144
        Node gotoTarget = Node.newTarget();
 
145
        if (caseExpression != null) {
 
146
            Node.Jump caseNode = new Node.Jump(Token.CASE, caseExpression);
 
147
            caseNode.target = gotoTarget;
 
148
            switchNode.addChildToBack(caseNode);
 
149
        } else {
 
150
            switchNode.setDefault(gotoTarget);
 
151
        }
 
152
        switchBlock.addChildToBack(gotoTarget);
 
153
        switchBlock.addChildToBack(statements);
 
154
    }
 
155
 
 
156
    void closeSwitch(Node switchBlock)
 
157
    {
 
158
        if (switchBlock.getType() != Token.BLOCK) throw Kit.codeBug();
 
159
        Node.Jump switchNode = (Node.Jump)switchBlock.getFirstChild();
 
160
        if (switchNode.getType() != Token.SWITCH) throw Kit.codeBug();
 
161
 
 
162
        Node switchBreakTarget = Node.newTarget();
 
163
        // switchNode.target is only used by NodeTransformer
 
164
        // to detect switch end
 
165
        switchNode.target = switchBreakTarget;
 
166
 
 
167
        Node defaultTarget = switchNode.getDefault();
 
168
        if (defaultTarget == null) {
 
169
            defaultTarget = switchBreakTarget;
 
170
        }
 
171
 
 
172
        switchBlock.addChildAfter(makeJump(Token.GOTO, defaultTarget),
 
173
                                  switchNode);
 
174
        switchBlock.addChildToBack(switchBreakTarget);
 
175
    }
 
176
 
 
177
    Node createVariables(int lineno)
 
178
    {
 
179
        return new Node(Token.VAR, lineno);
 
180
    }
 
181
 
 
182
    Node createExprStatement(Node expr, int lineno)
 
183
    {
 
184
        int type;
 
185
        if (parser.insideFunction()) {
 
186
            type = Token.EXPR_VOID;
 
187
        } else {
 
188
            type = Token.EXPR_RESULT;
 
189
        }
 
190
        return new Node(type, expr, lineno);
 
191
    }
 
192
 
 
193
    Node createExprStatementNoReturn(Node expr, int lineno)
 
194
    {
 
195
        return new Node(Token.EXPR_VOID, expr, lineno);
 
196
    }
 
197
 
 
198
    Node createDefaultNamespace(Node expr, int lineno)
 
199
    {
 
200
        // default xml namespace requires activation
 
201
        setRequiresActivation();
 
202
        Node n = createUnary(Token.DEFAULTNAMESPACE, expr);
 
203
        Node result = createExprStatement(n, lineno);
 
204
        return result;
 
205
    }
 
206
 
 
207
    /**
 
208
     * Name
 
209
     */
 
210
    Node createName(String name)
 
211
    {
 
212
        checkActivationName(name, Token.NAME);
 
213
        return Node.newString(Token.NAME, name);
 
214
    }
 
215
 
 
216
    /**
 
217
     * String (for literals)
 
218
     */
 
219
    Node createString(String string)
 
220
    {
 
221
        return Node.newString(string);
 
222
    }
 
223
 
 
224
    /**
 
225
     * Number (for literals)
 
226
     */
 
227
    Node createNumber(double number)
 
228
    {
 
229
        return Node.newNumber(number);
 
230
    }
 
231
 
 
232
    /**
 
233
     * Catch clause of try/catch/finally
 
234
     * @param varName the name of the variable to bind to the exception
 
235
     * @param catchCond the condition under which to catch the exception.
 
236
     *                  May be null if no condition is given.
 
237
     * @param stmts the statements in the catch clause
 
238
     * @param lineno the starting line number of the catch clause
 
239
     */
 
240
    Node createCatch(String varName, Node catchCond, Node stmts, int lineno)
 
241
    {
 
242
        if (catchCond == null) {
 
243
            catchCond = new Node(Token.EMPTY);
 
244
        }
 
245
        return new Node(Token.CATCH, createName(varName),
 
246
                        catchCond, stmts, lineno);
 
247
    }
 
248
 
 
249
    /**
 
250
     * Throw
 
251
     */
 
252
    Node createThrow(Node expr, int lineno)
 
253
    {
 
254
        return new Node(Token.THROW, expr, lineno);
 
255
    }
 
256
 
 
257
    /**
 
258
     * Return
 
259
     */
 
260
    Node createReturn(Node expr, int lineno)
 
261
    {
 
262
        return expr == null
 
263
            ? new Node(Token.RETURN, lineno)
 
264
            : new Node(Token.RETURN, expr, lineno);
 
265
    }
 
266
 
 
267
    /**
 
268
     * Label
 
269
     */
 
270
    Node createLabel(int lineno)
 
271
    {
 
272
        return new Node.Jump(Token.LABEL, lineno);
 
273
    }
 
274
 
 
275
    Node getLabelLoop(Node label)
 
276
    {
 
277
        return ((Node.Jump)label).getLoop();
 
278
    }
 
279
 
 
280
    /**
 
281
     * Label
 
282
     */
 
283
    Node createLabeledStatement(Node labelArg, Node statement)
 
284
    {
 
285
        Node.Jump label = (Node.Jump)labelArg;
 
286
 
 
287
        // Make a target and put it _after_ the statement
 
288
        // node.  And in the LABEL node, so breaks get the
 
289
        // right target.
 
290
 
 
291
        Node breakTarget = Node.newTarget();
 
292
        Node block = new Node(Token.BLOCK, label, statement, breakTarget);
 
293
        label.target = breakTarget;
 
294
 
 
295
        return block;
 
296
    }
 
297
 
 
298
    /**
 
299
     * Break (possibly labeled)
 
300
     */
 
301
    Node createBreak(Node breakStatement, int lineno)
 
302
    {
 
303
        Node.Jump n = new Node.Jump(Token.BREAK, lineno);
 
304
        Node.Jump jumpStatement;
 
305
        int t = breakStatement.getType();
 
306
        if (t == Token.LOOP || t == Token.LABEL) {
 
307
            jumpStatement = (Node.Jump)breakStatement;
 
308
        } else if (t == Token.BLOCK
 
309
                   && breakStatement.getFirstChild().getType() == Token.SWITCH)
 
310
        {
 
311
            jumpStatement = (Node.Jump)breakStatement.getFirstChild();
 
312
        } else {
 
313
            throw Kit.codeBug();
 
314
        }
 
315
        n.setJumpStatement(jumpStatement);
 
316
        return n;
 
317
    }
 
318
 
 
319
    /**
 
320
     * Continue (possibly labeled)
 
321
     */
 
322
    Node createContinue(Node loop, int lineno)
 
323
    {
 
324
        if (loop.getType() != Token.LOOP) Kit.codeBug();
 
325
        Node.Jump n = new Node.Jump(Token.CONTINUE, lineno);
 
326
        n.setJumpStatement((Node.Jump)loop);
 
327
        return n;
 
328
    }
 
329
 
 
330
    /**
 
331
     * Statement block
 
332
     * Creates the empty statement block
 
333
     * Must make subsequent calls to add statements to the node
 
334
     */
 
335
    Node createBlock(int lineno)
 
336
    {
 
337
        return new Node(Token.BLOCK, lineno);
 
338
    }
 
339
 
 
340
    FunctionNode createFunction(String name)
 
341
    {
 
342
        return new FunctionNode(name);
 
343
    }
 
344
 
 
345
    Node initFunction(FunctionNode fnNode, int functionIndex,
 
346
                      Node statements, int functionType)
 
347
    {
 
348
        fnNode.itsFunctionType = functionType;
 
349
        fnNode.addChildToBack(statements);
 
350
 
 
351
        int functionCount = fnNode.getFunctionCount();
 
352
        if (functionCount != 0) {
 
353
            // Functions containing other functions require activation objects
 
354
            fnNode.itsNeedsActivation = true;
 
355
            for (int i = 0; i != functionCount; ++i) {
 
356
                FunctionNode fn = fnNode.getFunctionNode(i);
 
357
                // nested function expression statements overrides var
 
358
                if (fn.getFunctionType()
 
359
                        == FunctionNode.FUNCTION_EXPRESSION_STATEMENT)
 
360
                {
 
361
                    String name = fn.getFunctionName();
 
362
                    if (name != null && name.length() != 0) {
 
363
                        fnNode.removeParamOrVar(name);
 
364
                    }
 
365
                }
 
366
            }
 
367
        }
 
368
 
 
369
        if (functionType == FunctionNode.FUNCTION_EXPRESSION) {
 
370
            String name = fnNode.getFunctionName();
 
371
            if (name != null && name.length() != 0
 
372
                && !fnNode.hasParamOrVar(name))
 
373
            {
 
374
                // A function expression needs to have its name as a
 
375
                // variable (if it isn't already allocated as a variable).
 
376
                // See ECMA Ch. 13.  We add code to the beginning of the
 
377
                // function to initialize a local variable of the
 
378
                // function's name to the function value.
 
379
                fnNode.addVar(name);
 
380
                Node setFn = new Node(Token.EXPR_VOID,
 
381
                                new Node(Token.SETVAR, Node.newString(name),
 
382
                                         new Node(Token.THISFN)));
 
383
                statements.addChildrenToFront(setFn);
 
384
            }
 
385
        }
 
386
 
 
387
        // Add return to end if needed.
 
388
        Node lastStmt = statements.getLastChild();
 
389
        if (lastStmt == null || lastStmt.getType() != Token.RETURN) {
 
390
            statements.addChildToBack(new Node(Token.RETURN));
 
391
        }
 
392
 
 
393
        Node result = Node.newString(Token.FUNCTION,
 
394
                                     fnNode.getFunctionName());
 
395
        result.putIntProp(Node.FUNCTION_PROP, functionIndex);
 
396
        return result;
 
397
    }
 
398
 
 
399
    /**
 
400
     * Add a child to the back of the given node.  This function
 
401
     * breaks the Factory abstraction, but it removes a requirement
 
402
     * from implementors of Node.
 
403
     */
 
404
    void addChildToBack(Node parent, Node child)
 
405
    {
 
406
        parent.addChildToBack(child);
 
407
    }
 
408
 
 
409
    /**
 
410
     * Create loop node. The parser will later call
 
411
     * createWhile|createDoWhile|createFor|createForIn
 
412
     * to finish loop generation.
 
413
     */
 
414
    Node createLoopNode(Node loopLabel, int lineno)
 
415
    {
 
416
        Node.Jump result = new Node.Jump(Token.LOOP, lineno);
 
417
        if (loopLabel != null) {
 
418
            ((Node.Jump)loopLabel).setLoop(result);
 
419
        }
 
420
        return result;
 
421
    }
 
422
 
 
423
    /**
 
424
     * While
 
425
     */
 
426
    Node createWhile(Node loop, Node cond, Node body)
 
427
    {
 
428
        return createLoop((Node.Jump)loop, LOOP_WHILE, body, cond,
 
429
                          null, null);
 
430
    }
 
431
 
 
432
    /**
 
433
     * DoWhile
 
434
     */
 
435
    Node createDoWhile(Node loop, Node body, Node cond)
 
436
    {
 
437
        return createLoop((Node.Jump)loop, LOOP_DO_WHILE, body, cond,
 
438
                          null, null);
 
439
    }
 
440
 
 
441
    /**
 
442
     * For
 
443
     */
 
444
    Node createFor(Node loop, Node init, Node test, Node incr, Node body)
 
445
    {
 
446
        return createLoop((Node.Jump)loop, LOOP_FOR, body, test,
 
447
                          init, incr);
 
448
    }
 
449
 
 
450
    private Node createLoop(Node.Jump loop, int loopType, Node body, Node cond,
 
451
                            Node init, Node incr)
 
452
    {
 
453
        Node bodyTarget = Node.newTarget();
 
454
        Node condTarget = Node.newTarget();
 
455
        if (loopType == LOOP_FOR && cond.getType() == Token.EMPTY) {
 
456
            cond = new Node(Token.TRUE);
 
457
        }
 
458
        Node.Jump IFEQ = new Node.Jump(Token.IFEQ, cond);
 
459
        IFEQ.target = bodyTarget;
 
460
        Node breakTarget = Node.newTarget();
 
461
 
 
462
        loop.addChildToBack(bodyTarget);
 
463
        loop.addChildrenToBack(body);
 
464
        if (loopType == LOOP_WHILE || loopType == LOOP_FOR) {
 
465
            // propagate lineno to condition
 
466
            loop.addChildrenToBack(new Node(Token.EMPTY, loop.getLineno()));
 
467
        }
 
468
        loop.addChildToBack(condTarget);
 
469
        loop.addChildToBack(IFEQ);
 
470
        loop.addChildToBack(breakTarget);
 
471
 
 
472
        loop.target = breakTarget;
 
473
        Node continueTarget = condTarget;
 
474
 
 
475
        if (loopType == LOOP_WHILE || loopType == LOOP_FOR) {
 
476
            // Just add a GOTO to the condition in the do..while
 
477
            loop.addChildToFront(makeJump(Token.GOTO, condTarget));
 
478
 
 
479
            if (loopType == LOOP_FOR) {
 
480
                if (init.getType() != Token.EMPTY) {
 
481
                    if (init.getType() != Token.VAR) {
 
482
                        init = new Node(Token.EXPR_VOID, init);
 
483
                    }
 
484
                    loop.addChildToFront(init);
 
485
                }
 
486
                Node incrTarget = Node.newTarget();
 
487
                loop.addChildAfter(incrTarget, body);
 
488
                if (incr.getType() != Token.EMPTY) {
 
489
                    incr = new Node(Token.EXPR_VOID, incr);
 
490
                    loop.addChildAfter(incr, incrTarget);
 
491
                }
 
492
                continueTarget = incrTarget;
 
493
            }
 
494
        }
 
495
 
 
496
        loop.setContinue(continueTarget);
 
497
 
 
498
        return loop;
 
499
    }
 
500
 
 
501
    /**
 
502
     * For .. In
 
503
     *
 
504
     */
 
505
    Node createForIn(Node loop, Node lhs, Node obj, Node body,
 
506
                     boolean isForEach)
 
507
    {
 
508
        String name;
 
509
        int type = lhs.getType();
 
510
 
 
511
        Node lvalue;
 
512
        if (type == Token.VAR) {
 
513
            /*
 
514
             * check that there was only one variable given.
 
515
             * we can't do this in the parser, because then the
 
516
             * parser would have to know something about the
 
517
             * 'init' node of the for-in loop.
 
518
             */
 
519
            Node lastChild = lhs.getLastChild();
 
520
            if (lhs.getFirstChild() != lastChild) {
 
521
                parser.reportError("msg.mult.index");
 
522
            }
 
523
            lvalue = Node.newString(Token.NAME, lastChild.getString());
 
524
        } else {
 
525
            lvalue = makeReference(lhs);
 
526
            if (lvalue == null) {
 
527
                parser.reportError("msg.bad.for.in.lhs");
 
528
                return obj;
 
529
            }
 
530
        }
 
531
 
 
532
        Node localBlock = new Node(Token.LOCAL_BLOCK);
 
533
 
 
534
        int initType = (isForEach) ? Token.ENUM_INIT_VALUES
 
535
                                   : Token.ENUM_INIT_KEYS;
 
536
        Node init = new Node(initType, obj);
 
537
        init.putProp(Node.LOCAL_BLOCK_PROP, localBlock);
 
538
        Node cond = new Node(Token.ENUM_NEXT);
 
539
        cond.putProp(Node.LOCAL_BLOCK_PROP, localBlock);
 
540
        Node id = new Node(Token.ENUM_ID);
 
541
        id.putProp(Node.LOCAL_BLOCK_PROP, localBlock);
 
542
 
 
543
        Node newBody = new Node(Token.BLOCK);
 
544
        Node assign = simpleAssignment(lvalue, id);
 
545
        newBody.addChildToBack(new Node(Token.EXPR_VOID, assign));
 
546
        newBody.addChildToBack(body);
 
547
 
 
548
        loop = createWhile(loop, cond, newBody);
 
549
        loop.addChildToFront(init);
 
550
        if (type == Token.VAR)
 
551
            loop.addChildToFront(lhs);
 
552
        localBlock.addChildToBack(loop);
 
553
 
 
554
        return localBlock;
 
555
    }
 
556
 
 
557
    /**
 
558
     * Try/Catch/Finally
 
559
     *
 
560
     * The IRFactory tries to express as much as possible in the tree;
 
561
     * the responsibilities remaining for Codegen are to add the Java
 
562
     * handlers: (Either (but not both) of TARGET and FINALLY might not
 
563
     * be defined)
 
564
 
 
565
     * - a catch handler for javascript exceptions that unwraps the
 
566
     * exception onto the stack and GOTOes to the catch target
 
567
 
 
568
     * - a finally handler
 
569
 
 
570
     * ... and a goto to GOTO around these handlers.
 
571
     */
 
572
    Node createTryCatchFinally(Node tryBlock, Node catchBlocks,
 
573
                               Node finallyBlock, int lineno)
 
574
    {
 
575
        boolean hasFinally = (finallyBlock != null)
 
576
                             && (finallyBlock.getType() != Token.BLOCK
 
577
                                 || finallyBlock.hasChildren());
 
578
 
 
579
        // short circuit
 
580
        if (tryBlock.getType() == Token.BLOCK && !tryBlock.hasChildren()
 
581
            && !hasFinally)
 
582
        {
 
583
            return tryBlock;
 
584
        }
 
585
 
 
586
        boolean hasCatch = catchBlocks.hasChildren();
 
587
 
 
588
        // short circuit
 
589
        if (!hasFinally && !hasCatch)  {
 
590
            // bc finally might be an empty block...
 
591
            return tryBlock;
 
592
        }
 
593
 
 
594
 
 
595
        Node handlerBlock  = new Node(Token.LOCAL_BLOCK);
 
596
        Node.Jump pn = new Node.Jump(Token.TRY, tryBlock, lineno);
 
597
        pn.putProp(Node.LOCAL_BLOCK_PROP, handlerBlock);
 
598
 
 
599
        if (hasCatch) {
 
600
            // jump around catch code
 
601
            Node endCatch = Node.newTarget();
 
602
            pn.addChildToBack(makeJump(Token.GOTO, endCatch));
 
603
 
 
604
            // make a TARGET for the catch that the tcf node knows about
 
605
            Node catchTarget = Node.newTarget();
 
606
            pn.target = catchTarget;
 
607
            // mark it
 
608
            pn.addChildToBack(catchTarget);
 
609
 
 
610
            //
 
611
            //  Given
 
612
            //
 
613
            //   try {
 
614
            //       tryBlock;
 
615
            //   } catch (e if condition1) {
 
616
            //       something1;
 
617
            //   ...
 
618
            //
 
619
            //   } catch (e if conditionN) {
 
620
            //       somethingN;
 
621
            //   } catch (e) {
 
622
            //       somethingDefault;
 
623
            //   }
 
624
            //
 
625
            //  rewrite as
 
626
            //
 
627
            //   try {
 
628
            //       tryBlock;
 
629
            //       goto after_catch:
 
630
            //   } catch (x) {
 
631
            //       with (newCatchScope(e, x)) {
 
632
            //           if (condition1) {
 
633
            //               something1;
 
634
            //               goto after_catch;
 
635
            //           }
 
636
            //       }
 
637
            //   ...
 
638
            //       with (newCatchScope(e, x)) {
 
639
            //           if (conditionN) {
 
640
            //               somethingN;
 
641
            //               goto after_catch;
 
642
            //           }
 
643
            //       }
 
644
            //       with (newCatchScope(e, x)) {
 
645
            //           somethingDefault;
 
646
            //           goto after_catch;
 
647
            //       }
 
648
            //   }
 
649
            // after_catch:
 
650
            //
 
651
            // If there is no default catch, then the last with block
 
652
            // arround  "somethingDefault;" is replaced by "rethrow;"
 
653
 
 
654
            // It is assumed that catch handler generation will store
 
655
            // exeception object in handlerBlock register
 
656
 
 
657
            // Block with local for exception scope objects
 
658
            Node catchScopeBlock = new Node(Token.LOCAL_BLOCK);
 
659
 
 
660
            // expects catchblocks children to be (cond block) pairs.
 
661
            Node cb = catchBlocks.getFirstChild();
 
662
            boolean hasDefault = false;
 
663
            int scopeIndex = 0;
 
664
            while (cb != null) {
 
665
                int catchLineNo = cb.getLineno();
 
666
 
 
667
                Node name = cb.getFirstChild();
 
668
                Node cond = name.getNext();
 
669
                Node catchStatement = cond.getNext();
 
670
                cb.removeChild(name);
 
671
                cb.removeChild(cond);
 
672
                cb.removeChild(catchStatement);
 
673
 
 
674
                // Add goto to the catch statement to jump out of catch
 
675
                // but prefix it with LEAVEWITH since try..catch produces
 
676
                // "with"code in order to limit the scope of the exception
 
677
                // object.
 
678
                catchStatement.addChildToBack(new Node(Token.LEAVEWITH));
 
679
                catchStatement.addChildToBack(makeJump(Token.GOTO, endCatch));
 
680
 
 
681
                // Create condition "if" when present
 
682
                Node condStmt;
 
683
                if (cond.getType() == Token.EMPTY) {
 
684
                    condStmt = catchStatement;
 
685
                    hasDefault = true;
 
686
                } else {
 
687
                    condStmt = createIf(cond, catchStatement, null,
 
688
                                        catchLineNo);
 
689
                }
 
690
 
 
691
                // Generate code to create the scope object and store
 
692
                // it in catchScopeBlock register
 
693
                Node catchScope = new Node(Token.CATCH_SCOPE, name,
 
694
                                           createUseLocal(handlerBlock));
 
695
                catchScope.putProp(Node.LOCAL_BLOCK_PROP, catchScopeBlock);
 
696
                catchScope.putIntProp(Node.CATCH_SCOPE_PROP, scopeIndex);
 
697
                catchScopeBlock.addChildToBack(catchScope);
 
698
 
 
699
                // Add with statement based on catch scope object
 
700
                catchScopeBlock.addChildToBack(
 
701
                    createWith(createUseLocal(catchScopeBlock), condStmt,
 
702
                               catchLineNo));
 
703
 
 
704
                // move to next cb
 
705
                cb = cb.getNext();
 
706
                ++scopeIndex;
 
707
            }
 
708
            pn.addChildToBack(catchScopeBlock);
 
709
            if (!hasDefault) {
 
710
                // Generate code to rethrow if no catch clause was executed
 
711
                Node rethrow = new Node(Token.RETHROW);
 
712
                rethrow.putProp(Node.LOCAL_BLOCK_PROP, handlerBlock);
 
713
                pn.addChildToBack(rethrow);
 
714
            }
 
715
 
 
716
            pn.addChildToBack(endCatch);
 
717
        }
 
718
 
 
719
        if (hasFinally) {
 
720
            Node finallyTarget = Node.newTarget();
 
721
            pn.setFinally(finallyTarget);
 
722
 
 
723
            // add jsr finally to the try block
 
724
            pn.addChildToBack(makeJump(Token.JSR, finallyTarget));
 
725
 
 
726
            // jump around finally code
 
727
            Node finallyEnd = Node.newTarget();
 
728
            pn.addChildToBack(makeJump(Token.GOTO, finallyEnd));
 
729
 
 
730
            pn.addChildToBack(finallyTarget);
 
731
            Node fBlock = new Node(Token.FINALLY, finallyBlock);
 
732
            fBlock.putProp(Node.LOCAL_BLOCK_PROP, handlerBlock);
 
733
            pn.addChildToBack(fBlock);
 
734
 
 
735
            pn.addChildToBack(finallyEnd);
 
736
        }
 
737
        handlerBlock.addChildToBack(pn);
 
738
        return handlerBlock;
 
739
    }
 
740
 
 
741
    /**
 
742
     * Throw, Return, Label, Break and Continue are defined in ASTFactory.
 
743
     */
 
744
 
 
745
    /**
 
746
     * With
 
747
     */
 
748
    Node createWith(Node obj, Node body, int lineno)
 
749
    {
 
750
        setRequiresActivation();
 
751
        Node result = new Node(Token.BLOCK, lineno);
 
752
        result.addChildToBack(new Node(Token.ENTERWITH, obj));
 
753
        Node bodyNode = new Node(Token.WITH, body, lineno);
 
754
        result.addChildrenToBack(bodyNode);
 
755
        result.addChildToBack(new Node(Token.LEAVEWITH));
 
756
        return result;
 
757
    }
 
758
 
 
759
    /**
 
760
     * DOTQUERY
 
761
     */
 
762
    public Node createDotQuery (Node obj, Node body, int lineno)
 
763
    {
 
764
        setRequiresActivation();
 
765
        Node result = new Node(Token.DOTQUERY, obj, body, lineno);
 
766
        return result;
 
767
    }
 
768
 
 
769
    Node createArrayLiteral(ObjArray elems, int skipCount)
 
770
    {
 
771
        int length = elems.size();
 
772
        int[] skipIndexes = null;
 
773
        if (skipCount != 0) {
 
774
            skipIndexes = new int[skipCount];
 
775
        }
 
776
        Node array = new Node(Token.ARRAYLIT);
 
777
        for (int i = 0, j = 0; i != length; ++i) {
 
778
            Node elem = (Node)elems.get(i);
 
779
            if (elem != null) {
 
780
                array.addChildToBack(elem);
 
781
            } else {
 
782
                skipIndexes[j] = i;
 
783
                ++j;
 
784
            }
 
785
        }
 
786
        if (skipCount != 0) {
 
787
            array.putProp(Node.SKIP_INDEXES_PROP, skipIndexes);
 
788
        }
 
789
        return array;
 
790
    }
 
791
 
 
792
    /**
 
793
     * Object Literals
 
794
     * <BR> createObjectLiteral rewrites its argument as object
 
795
     * creation plus object property entries, so later compiler
 
796
     * stages don't need to know about object literals.
 
797
     */
 
798
    Node createObjectLiteral(ObjArray elems)
 
799
    {
 
800
        int size = elems.size() / 2;
 
801
        Node object = new Node(Token.OBJECTLIT);
 
802
        Object[] properties;
 
803
        if (size == 0) {
 
804
            properties = ScriptRuntime.emptyArgs;
 
805
        } else {
 
806
            properties = new Object[size];
 
807
            for (int i = 0; i != size; ++i) {
 
808
                properties[i] = elems.get(2 * i);
 
809
                Node value = (Node)elems.get(2 * i + 1);
 
810
                object.addChildToBack(value);
 
811
            }
 
812
        }
 
813
        object.putProp(Node.OBJECT_IDS_PROP, properties);
 
814
        return object;
 
815
    }
 
816
 
 
817
    /**
 
818
     * Regular expressions
 
819
     */
 
820
    Node createRegExp(int regexpIndex)
 
821
    {
 
822
        Node n = new Node(Token.REGEXP);
 
823
        n.putIntProp(Node.REGEXP_PROP, regexpIndex);
 
824
        return n;
 
825
    }
 
826
 
 
827
    /**
 
828
     * If statement
 
829
     */
 
830
    Node createIf(Node cond, Node ifTrue, Node ifFalse, int lineno)
 
831
    {
 
832
        int condStatus = isAlwaysDefinedBoolean(cond);
 
833
        if (condStatus == ALWAYS_TRUE_BOOLEAN) {
 
834
            return ifTrue;
 
835
        } else if (condStatus == ALWAYS_FALSE_BOOLEAN) {
 
836
            if (ifFalse != null) {
 
837
                return ifFalse;
 
838
            }
 
839
            // Replace if (false) xxx by empty block
 
840
            return new Node(Token.BLOCK, lineno);
 
841
        }
 
842
 
 
843
        Node result = new Node(Token.BLOCK, lineno);
 
844
        Node ifNotTarget = Node.newTarget();
 
845
        Node.Jump IFNE = new Node.Jump(Token.IFNE, cond);
 
846
        IFNE.target = ifNotTarget;
 
847
 
 
848
        result.addChildToBack(IFNE);
 
849
        result.addChildrenToBack(ifTrue);
 
850
 
 
851
        if (ifFalse != null) {
 
852
            Node endTarget = Node.newTarget();
 
853
            result.addChildToBack(makeJump(Token.GOTO, endTarget));
 
854
            result.addChildToBack(ifNotTarget);
 
855
            result.addChildrenToBack(ifFalse);
 
856
            result.addChildToBack(endTarget);
 
857
        } else {
 
858
            result.addChildToBack(ifNotTarget);
 
859
        }
 
860
 
 
861
        return result;
 
862
    }
 
863
 
 
864
    Node createCondExpr(Node cond, Node ifTrue, Node ifFalse)
 
865
    {
 
866
        int condStatus = isAlwaysDefinedBoolean(cond);
 
867
        if (condStatus == ALWAYS_TRUE_BOOLEAN) {
 
868
            return ifTrue;
 
869
        } else if (condStatus == ALWAYS_FALSE_BOOLEAN) {
 
870
            return ifFalse;
 
871
        }
 
872
        return new Node(Token.HOOK, cond, ifTrue, ifFalse);
 
873
    }
 
874
 
 
875
    /**
 
876
     * Unary
 
877
     */
 
878
    Node createUnary(int nodeType, Node child)
 
879
    {
 
880
        int childType = child.getType();
 
881
        switch (nodeType) {
 
882
          case Token.DELPROP: {
 
883
            Node n;
 
884
            if (childType == Token.NAME) {
 
885
                // Transform Delete(Name "a")
 
886
                //  to Delete(Bind("a"), String("a"))
 
887
                child.setType(Token.BINDNAME);
 
888
                Node left = child;
 
889
                Node right = Node.newString(child.getString());
 
890
                n = new Node(nodeType, left, right);
 
891
            } else if (childType == Token.GETPROP ||
 
892
                       childType == Token.GETELEM)
 
893
            {
 
894
                Node left = child.getFirstChild();
 
895
                Node right = child.getLastChild();
 
896
                child.removeChild(left);
 
897
                child.removeChild(right);
 
898
                n = new Node(nodeType, left, right);
 
899
            } else if (childType == Token.GET_REF) {
 
900
                Node ref = child.getFirstChild();
 
901
                child.removeChild(ref);
 
902
                n = new Node(Token.DEL_REF, ref);
 
903
            } else {
 
904
                n = new Node(Token.TRUE);
 
905
            }
 
906
            return n;
 
907
          }
 
908
          case Token.TYPEOF:
 
909
            if (childType == Token.NAME) {
 
910
                child.setType(Token.TYPEOFNAME);
 
911
                return child;
 
912
            }
 
913
            break;
 
914
          case Token.BITNOT:
 
915
            if (childType == Token.NUMBER) {
 
916
                int value = ScriptRuntime.toInt32(child.getDouble());
 
917
                child.setDouble(~value);
 
918
                return child;
 
919
            }
 
920
            break;
 
921
          case Token.NEG:
 
922
            if (childType == Token.NUMBER) {
 
923
                child.setDouble(-child.getDouble());
 
924
                return child;
 
925
            }
 
926
            break;
 
927
          case Token.NOT: {
 
928
            int status = isAlwaysDefinedBoolean(child);
 
929
            if (status != 0) {
 
930
                int type;
 
931
                if (status == ALWAYS_TRUE_BOOLEAN) {
 
932
                    type = Token.FALSE;
 
933
                } else {
 
934
                    type = Token.TRUE;
 
935
                }
 
936
                if (childType == Token.TRUE || childType == Token.FALSE) {
 
937
                    child.setType(type);
 
938
                    return child;
 
939
                }
 
940
                return new Node(type);
 
941
            }
 
942
            break;
 
943
          }
 
944
        }
 
945
        return new Node(nodeType, child);
 
946
    }
 
947
 
 
948
    Node createCallOrNew(int nodeType, Node child)
 
949
    {
 
950
        int type = Node.NON_SPECIALCALL;
 
951
        if (child.getType() == Token.NAME) {
 
952
            String name = child.getString();
 
953
            if (name.equals("eval")) {
 
954
                type = Node.SPECIALCALL_EVAL;
 
955
            } else if (name.equals("With")) {
 
956
                type = Node.SPECIALCALL_WITH;
 
957
            }
 
958
        } else if (child.getType() == Token.GETPROP) {
 
959
            String name = child.getLastChild().getString();
 
960
            if (name.equals("eval")) {
 
961
                type = Node.SPECIALCALL_EVAL;
 
962
            }
 
963
        }
 
964
        Node node = new Node(nodeType, child);
 
965
        if (type != Node.NON_SPECIALCALL) {
 
966
            // Calls to these functions require activation objects.
 
967
            setRequiresActivation();
 
968
            node.putIntProp(Node.SPECIALCALL_PROP, type);
 
969
        }
 
970
        return node;
 
971
    }
 
972
 
 
973
    Node createIncDec(int nodeType, boolean post, Node child)
 
974
    {
 
975
        child = makeReference(child);
 
976
        if (child == null) {
 
977
            String msg;
 
978
            if (nodeType == Token.DEC) {
 
979
                msg = "msg.bad.decr";
 
980
            } else {
 
981
                msg = "msg.bad.incr";
 
982
            }
 
983
            parser.reportError(msg);
 
984
            return null;
 
985
        }
 
986
 
 
987
        int childType = child.getType();
 
988
 
 
989
        switch (childType) {
 
990
          case Token.NAME:
 
991
          case Token.GETPROP:
 
992
          case Token.GETELEM:
 
993
          case Token.GET_REF: {
 
994
            Node n = new Node(nodeType, child);
 
995
            int incrDecrMask = 0;
 
996
            if (nodeType == Token.DEC) {
 
997
                incrDecrMask |= Node.DECR_FLAG;
 
998
            }
 
999
            if (post) {
 
1000
                incrDecrMask |= Node.POST_FLAG;
 
1001
            }
 
1002
            n.putIntProp(Node.INCRDECR_PROP, incrDecrMask);
 
1003
            return n;
 
1004
          }
 
1005
        }
 
1006
        throw Kit.codeBug();
 
1007
    }
 
1008
 
 
1009
    Node createPropertyGet(Node target, String namespace, String name,
 
1010
                           int memberTypeFlags)
 
1011
    {
 
1012
        if (namespace == null && memberTypeFlags == 0) {
 
1013
            if (target == null) {
 
1014
                return createName(name);
 
1015
            }
 
1016
            checkActivationName(name, Token.GETPROP);
 
1017
            if (ScriptRuntime.isSpecialProperty(name)) {
 
1018
                Node ref = new Node(Token.REF_SPECIAL, target);
 
1019
                ref.putProp(Node.NAME_PROP, name);
 
1020
                return new Node(Token.GET_REF, ref);
 
1021
            }
 
1022
            return new Node(Token.GETPROP, target, createString(name));
 
1023
        }
 
1024
        Node elem = createString(name);
 
1025
        memberTypeFlags |= Node.PROPERTY_FLAG;
 
1026
        return createMemberRefGet(target, namespace, elem, memberTypeFlags);
 
1027
    }
 
1028
 
 
1029
    Node createElementGet(Node target, String namespace, Node elem,
 
1030
                          int memberTypeFlags)
 
1031
    {
 
1032
        // OPT: could optimize to createPropertyGet
 
1033
        // iff elem is string that can not be number
 
1034
        if (namespace == null && memberTypeFlags == 0) {
 
1035
            // stand-alone [aaa] as primary expression is array literal
 
1036
            // declaration and should not come here!
 
1037
            if (target == null) throw Kit.codeBug();
 
1038
            return new Node(Token.GETELEM, target, elem);
 
1039
        }
 
1040
        return createMemberRefGet(target, namespace, elem, memberTypeFlags);
 
1041
    }
 
1042
 
 
1043
    private Node createMemberRefGet(Node target, String namespace, Node elem,
 
1044
                                    int memberTypeFlags)
 
1045
    {
 
1046
        Node nsNode = null;
 
1047
        if (namespace != null) {
 
1048
            // See 11.1.2 in ECMA 357
 
1049
            if (namespace.equals("*")) {
 
1050
                nsNode = new Node(Token.NULL);
 
1051
            } else {
 
1052
                nsNode = createName(namespace);
 
1053
            }
 
1054
        }
 
1055
        Node ref;
 
1056
        if (target == null) {
 
1057
            if (namespace == null) {
 
1058
                ref = new Node(Token.REF_NAME, elem);
 
1059
            } else {
 
1060
                ref = new Node(Token.REF_NS_NAME, nsNode, elem);
 
1061
            }
 
1062
        } else {
 
1063
            if (namespace == null) {
 
1064
                ref = new Node(Token.REF_MEMBER, target, elem);
 
1065
            } else {
 
1066
                ref = new Node(Token.REF_NS_MEMBER, target, nsNode, elem);
 
1067
            }
 
1068
        }
 
1069
        if (memberTypeFlags != 0) {
 
1070
            ref.putIntProp(Node.MEMBER_TYPE_PROP, memberTypeFlags);
 
1071
        }
 
1072
        return new Node(Token.GET_REF, ref);
 
1073
    }
 
1074
 
 
1075
    /**
 
1076
     * Binary
 
1077
     */
 
1078
    Node createBinary(int nodeType, Node left, Node right)
 
1079
    {
 
1080
        switch (nodeType) {
 
1081
 
 
1082
          case Token.ADD:
 
1083
            // numerical addition and string concatenation
 
1084
            if (left.type == Token.STRING) {
 
1085
                String s2;
 
1086
                if (right.type == Token.STRING) {
 
1087
                    s2 = right.getString();
 
1088
                } else if (right.type == Token.NUMBER) {
 
1089
                    s2 = ScriptRuntime.numberToString(right.getDouble(), 10);
 
1090
                } else {
 
1091
                    break;
 
1092
                }
 
1093
                String s1 = left.getString();
 
1094
                left.setString(s1.concat(s2));
 
1095
                return left;
 
1096
            } else if (left.type == Token.NUMBER) {
 
1097
                if (right.type == Token.NUMBER) {
 
1098
                    left.setDouble(left.getDouble() + right.getDouble());
 
1099
                    return left;
 
1100
                } else if (right.type == Token.STRING) {
 
1101
                    String s1, s2;
 
1102
                    s1 = ScriptRuntime.numberToString(left.getDouble(), 10);
 
1103
                    s2 = right.getString();
 
1104
                    right.setString(s1.concat(s2));
 
1105
                    return right;
 
1106
                }
 
1107
            }
 
1108
            // can't do anything if we don't know  both types - since
 
1109
            // 0 + object is supposed to call toString on the object and do
 
1110
            // string concantenation rather than addition
 
1111
            break;
 
1112
 
 
1113
          case Token.SUB:
 
1114
            // numerical subtraction
 
1115
            if (left.type == Token.NUMBER) {
 
1116
                double ld = left.getDouble();
 
1117
                if (right.type == Token.NUMBER) {
 
1118
                    //both numbers
 
1119
                    left.setDouble(ld - right.getDouble());
 
1120
                    return left;
 
1121
                } else if (ld == 0.0) {
 
1122
                    // first 0: 0-x -> -x
 
1123
                    return new Node(Token.NEG, right);
 
1124
                }
 
1125
            } else if (right.type == Token.NUMBER) {
 
1126
                if (right.getDouble() == 0.0) {
 
1127
                    //second 0: x - 0 -> +x
 
1128
                    // can not make simply x because x - 0 must be number
 
1129
                    return new Node(Token.POS, left);
 
1130
                }
 
1131
            }
 
1132
            break;
 
1133
 
 
1134
          case Token.MUL:
 
1135
            // numerical multiplication
 
1136
            if (left.type == Token.NUMBER) {
 
1137
                double ld = left.getDouble();
 
1138
                if (right.type == Token.NUMBER) {
 
1139
                    //both numbers
 
1140
                    left.setDouble(ld * right.getDouble());
 
1141
                    return left;
 
1142
                } else if (ld == 1.0) {
 
1143
                    // first 1: 1 *  x -> +x
 
1144
                    return new Node(Token.POS, right);
 
1145
                }
 
1146
            } else if (right.type == Token.NUMBER) {
 
1147
                if (right.getDouble() == 1.0) {
 
1148
                    //second 1: x * 1 -> +x
 
1149
                    // can not make simply x because x - 0 must be number
 
1150
                    return new Node(Token.POS, left);
 
1151
                }
 
1152
            }
 
1153
            // can't do x*0: Infinity * 0 gives NaN, not 0
 
1154
            break;
 
1155
 
 
1156
          case Token.DIV:
 
1157
            // number division
 
1158
            if (right.type == Token.NUMBER) {
 
1159
                double rd = right.getDouble();
 
1160
                if (left.type == Token.NUMBER) {
 
1161
                    // both constants -- just divide, trust Java to handle x/0
 
1162
                    left.setDouble(left.getDouble() / rd);
 
1163
                    return left;
 
1164
               } else if (rd == 1.0) {
 
1165
                    // second 1: x/1 -> +x
 
1166
                    // not simply x to force number convertion
 
1167
                    return new Node(Token.POS, left);
 
1168
                }
 
1169
            }
 
1170
            break;
 
1171
 
 
1172
          case Token.AND: {
 
1173
            int leftStatus = isAlwaysDefinedBoolean(left);
 
1174
            if (leftStatus == ALWAYS_FALSE_BOOLEAN) {
 
1175
                // if the first one is false, replace with FALSE
 
1176
                return new Node(Token.FALSE);
 
1177
            } else if (leftStatus == ALWAYS_TRUE_BOOLEAN) {
 
1178
                // if first is true, set to second
 
1179
                return right;
 
1180
            }
 
1181
            int rightStatus = isAlwaysDefinedBoolean(right);
 
1182
            if (rightStatus == ALWAYS_FALSE_BOOLEAN) {
 
1183
                // if the second one is false, replace with FALSE
 
1184
                if (!hasSideEffects(left)) {
 
1185
                    return new Node(Token.FALSE);
 
1186
                }
 
1187
            } else if (rightStatus == ALWAYS_TRUE_BOOLEAN) {
 
1188
                // if second is true, set to first
 
1189
                return left;
 
1190
            }
 
1191
            break;
 
1192
          }
 
1193
 
 
1194
          case Token.OR: {
 
1195
            int leftStatus = isAlwaysDefinedBoolean(left);
 
1196
            if (leftStatus == ALWAYS_TRUE_BOOLEAN) {
 
1197
                // if the first one is true, replace with TRUE
 
1198
                return new Node(Token.TRUE);
 
1199
            } else if (leftStatus == ALWAYS_FALSE_BOOLEAN) {
 
1200
                // if first is false, set to second
 
1201
                return right;
 
1202
            }
 
1203
            int rightStatus = isAlwaysDefinedBoolean(right);
 
1204
            if (rightStatus == ALWAYS_TRUE_BOOLEAN) {
 
1205
                // if the second one is true, replace with TRUE
 
1206
                if (!hasSideEffects(left)) {
 
1207
                    return new Node(Token.TRUE);
 
1208
                }
 
1209
            } else if (rightStatus == ALWAYS_FALSE_BOOLEAN) {
 
1210
                // if second is false, set to first
 
1211
                return left;
 
1212
            }
 
1213
            break;
 
1214
          }
 
1215
        }
 
1216
 
 
1217
        return new Node(nodeType, left, right);
 
1218
    }
 
1219
 
 
1220
    private Node simpleAssignment(Node left, Node right)
 
1221
    {
 
1222
        int nodeType = left.getType();
 
1223
        switch (nodeType) {
 
1224
          case Token.NAME:
 
1225
            left.setType(Token.BINDNAME);
 
1226
            return new Node(Token.SETNAME, left, right);
 
1227
 
 
1228
          case Token.GETPROP:
 
1229
          case Token.GETELEM: {
 
1230
            Node obj = left.getFirstChild();
 
1231
            Node id = left.getLastChild();
 
1232
            int type;
 
1233
            if (nodeType == Token.GETPROP) {
 
1234
                type = Token.SETPROP;
 
1235
            } else {
 
1236
                type = Token.SETELEM;
 
1237
            }
 
1238
            return new Node(type, obj, id, right);
 
1239
          }
 
1240
          case Token.GET_REF: {
 
1241
            Node ref = left.getFirstChild();
 
1242
            return new Node(Token.SET_REF, ref, right);
 
1243
          }
 
1244
        }
 
1245
 
 
1246
        throw Kit.codeBug();
 
1247
    }
 
1248
 
 
1249
    Node createAssignment(int assignType, Node left, Node right)
 
1250
    {
 
1251
        left = makeReference(left);
 
1252
        if (left == null) {
 
1253
            parser.reportError("msg.bad.assign.left");
 
1254
            return right;
 
1255
        }
 
1256
 
 
1257
        int assignOp;
 
1258
        switch (assignType) {
 
1259
          case Token.ASSIGN:
 
1260
            return simpleAssignment(left, right);
 
1261
          case Token.ASSIGN_BITOR:  assignOp = Token.BITOR;  break;
 
1262
          case Token.ASSIGN_BITXOR: assignOp = Token.BITXOR; break;
 
1263
          case Token.ASSIGN_BITAND: assignOp = Token.BITAND; break;
 
1264
          case Token.ASSIGN_LSH:    assignOp = Token.LSH;    break;
 
1265
          case Token.ASSIGN_RSH:    assignOp = Token.RSH;    break;
 
1266
          case Token.ASSIGN_URSH:   assignOp = Token.URSH;   break;
 
1267
          case Token.ASSIGN_ADD:    assignOp = Token.ADD;    break;
 
1268
          case Token.ASSIGN_SUB:    assignOp = Token.SUB;    break;
 
1269
          case Token.ASSIGN_MUL:    assignOp = Token.MUL;    break;
 
1270
          case Token.ASSIGN_DIV:    assignOp = Token.DIV;    break;
 
1271
          case Token.ASSIGN_MOD:    assignOp = Token.MOD;    break;
 
1272
          default: throw Kit.codeBug();
 
1273
        }
 
1274
 
 
1275
        int nodeType = left.getType();
 
1276
        switch (nodeType) {
 
1277
          case Token.NAME: {
 
1278
            String s = left.getString();
 
1279
 
 
1280
            Node opLeft = Node.newString(Token.NAME, s);
 
1281
            Node op = new Node(assignOp, opLeft, right);
 
1282
            Node lvalueLeft = Node.newString(Token.BINDNAME, s);
 
1283
            return new Node(Token.SETNAME, lvalueLeft, op);
 
1284
          }
 
1285
          case Token.GETPROP:
 
1286
          case Token.GETELEM: {
 
1287
            Node obj = left.getFirstChild();
 
1288
            Node id = left.getLastChild();
 
1289
 
 
1290
            int type = nodeType == Token.GETPROP
 
1291
                       ? Token.SETPROP_OP
 
1292
                       : Token.SETELEM_OP;
 
1293
 
 
1294
            Node opLeft = new Node(Token.USE_STACK);
 
1295
            Node op = new Node(assignOp, opLeft, right);
 
1296
            return new Node(type, obj, id, op);
 
1297
          }
 
1298
          case Token.GET_REF: {
 
1299
            Node ref = left.getFirstChild();
 
1300
            Node opLeft = new Node(Token.USE_STACK);
 
1301
            Node op = new Node(assignOp, opLeft, right);
 
1302
            return new Node(Token.SET_REF_OP, ref, op);
 
1303
          }
 
1304
        }
 
1305
 
 
1306
        throw Kit.codeBug();
 
1307
    }
 
1308
 
 
1309
    Node createUseLocal(Node localBlock)
 
1310
    {
 
1311
        if (Token.LOCAL_BLOCK != localBlock.getType()) throw Kit.codeBug();
 
1312
        Node result = new Node(Token.LOCAL_LOAD);
 
1313
        result.putProp(Node.LOCAL_BLOCK_PROP, localBlock);
 
1314
        return result;
 
1315
    }
 
1316
 
 
1317
    private Node.Jump makeJump(int type, Node target)
 
1318
    {
 
1319
        Node.Jump n = new Node.Jump(type);
 
1320
        n.target = target;
 
1321
        return n;
 
1322
    }
 
1323
 
 
1324
    private Node makeReference(Node node)
 
1325
    {
 
1326
        int type = node.getType();
 
1327
        switch (type) {
 
1328
          case Token.NAME:
 
1329
          case Token.GETPROP:
 
1330
          case Token.GETELEM:
 
1331
          case Token.GET_REF:
 
1332
            return node;
 
1333
          case Token.CALL:
 
1334
            node.setType(Token.REF_CALL);
 
1335
            return new Node(Token.GET_REF, node);
 
1336
        }
 
1337
        // Signal caller to report error
 
1338
        return null;
 
1339
    }
 
1340
 
 
1341
    // Check if Node always mean true or false in boolean context
 
1342
    private static int isAlwaysDefinedBoolean(Node node)
 
1343
    {
 
1344
        switch (node.getType()) {
 
1345
          case Token.FALSE:
 
1346
          case Token.NULL:
 
1347
            return ALWAYS_FALSE_BOOLEAN;
 
1348
          case Token.TRUE:
 
1349
            return ALWAYS_TRUE_BOOLEAN;
 
1350
          case Token.NUMBER: {
 
1351
            double num = node.getDouble();
 
1352
            if (num == num && num != 0.0) {
 
1353
                return ALWAYS_TRUE_BOOLEAN;
 
1354
            } else {
 
1355
                return ALWAYS_FALSE_BOOLEAN;
 
1356
            }
 
1357
          }
 
1358
        }
 
1359
        return 0;
 
1360
    }
 
1361
 
 
1362
    private static boolean hasSideEffects(Node exprTree)
 
1363
    {
 
1364
        switch (exprTree.getType()) {
 
1365
            case Token.INC:
 
1366
            case Token.DEC:
 
1367
            case Token.SETPROP:
 
1368
            case Token.SETELEM:
 
1369
            case Token.SETNAME:
 
1370
            case Token.CALL:
 
1371
            case Token.NEW:
 
1372
                return true;
 
1373
            default:
 
1374
                Node child = exprTree.getFirstChild();
 
1375
                while (child != null) {
 
1376
                    if (hasSideEffects(child))
 
1377
                        return true;
 
1378
                    child = child.getNext();
 
1379
                }
 
1380
                break;
 
1381
        }
 
1382
        return false;
 
1383
    }
 
1384
 
 
1385
    private void checkActivationName(String name, int token)
 
1386
    {
 
1387
        if (parser.insideFunction()) {
 
1388
            boolean activation = false;
 
1389
            if ("arguments".equals(name)
 
1390
                || (parser.compilerEnv.activationNames != null
 
1391
                    && parser.compilerEnv.activationNames.containsKey(name)))
 
1392
            {
 
1393
                activation = true;
 
1394
            } else if ("length".equals(name)) {
 
1395
                if (token == Token.GETPROP
 
1396
                    && parser.compilerEnv.getLanguageVersion()
 
1397
                       == Context.VERSION_1_2)
 
1398
                {
 
1399
                    // Use of "length" in 1.2 requires an activation object.
 
1400
                    activation = true;
 
1401
                }
 
1402
            }
 
1403
            if (activation) {
 
1404
                setRequiresActivation();
 
1405
            }
 
1406
        }
 
1407
    }
 
1408
 
 
1409
    private void setRequiresActivation()
 
1410
    {
 
1411
        if (parser.insideFunction()) {
 
1412
            ((FunctionNode)parser.currentScriptOrFn).itsNeedsActivation = true;
 
1413
        }
 
1414
    }
 
1415
 
 
1416
    private Parser parser;
 
1417
 
 
1418
    private static final int LOOP_DO_WHILE = 0;
 
1419
    private static final int LOOP_WHILE    = 1;
 
1420
    private static final int LOOP_FOR      = 2;
 
1421
 
 
1422
    private static final int ALWAYS_TRUE_BOOLEAN = 1;
 
1423
    private static final int ALWAYS_FALSE_BOOLEAN = -1;
 
1424
}
 
1425