1 /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
\r
3 * The contents of this file are subject to the Netscape Public
\r
4 * License Version 1.1 (the "License"); you may not use this file
\r
5 * except in compliance with the License. You may obtain a copy of
\r
6 * the License at http://www.mozilla.org/NPL/
\r
8 * Software distributed under the License is distributed on an "AS
\r
9 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
\r
10 * implied. See the License for the specific language governing
\r
11 * rights and limitations under the License.
\r
13 * The Original Code is Rhino code, released
\r
16 * The Initial Developer of the Original Code is Netscape
\r
17 * Communications Corporation. Portions created by Netscape are
\r
18 * Copyright (C) 1997-1999 Netscape Communications Corporation. All
\r
24 * Alternatively, the contents of this file may be used under the
\r
25 * terms of the GNU Public License (the "GPL"), in which case the
\r
26 * provisions of the GPL are applicable instead of those above.
\r
27 * If you wish to allow use of your version of this file only
\r
28 * under the terms of the GPL and not to allow others to use your
\r
29 * version of this file under the NPL, indicate your decision by
\r
30 * deleting the provisions above and replace them with the notice
\r
31 * and other provisions required by the GPL. If you do not delete
\r
32 * the provisions above, a recipient may use your version of this
\r
33 * file under either the NPL or the GPL.
\r
36 package org.mozilla.javascript;
\r
39 * This class allows the creation of nodes, and follows the Factory pattern.
\r
42 * @author Mike McCabe
\r
43 * @author Norris Boyd
\r
45 public class IRFactory {
\r
47 public IRFactory(TokenStream ts, Scriptable scope) {
\r
53 * Script (for associating file/url names with toplevel scripts.)
\r
55 public Object createScript(Object body, String sourceName,
\r
56 int baseLineno, int endLineno, Object source)
\r
58 Node result = new Node(TokenStream.SCRIPT, sourceName);
\r
59 Node children = ((Node) body).getFirstChild();
\r
60 if (children != null)
\r
61 result.addChildrenToBack(children);
\r
62 result.putProp(Node.SOURCENAME_PROP, sourceName);
\r
63 result.putProp(Node.BASE_LINENO_PROP, new Integer(baseLineno));
\r
64 result.putProp(Node.END_LINENO_PROP, new Integer(endLineno));
\r
66 result.putProp(Node.SOURCE_PROP, source);
\r
73 public Object createLeaf(int nodeType) {
\r
74 return new Node(nodeType);
\r
77 public Object createLeaf(int nodeType, String id) {
\r
78 return new Node(nodeType, id);
\r
81 public Object createLeaf(int nodeType, int nodeOp) {
\r
82 return new Node(nodeType, new Integer(nodeOp));
\r
86 * Statement leaf nodes.
\r
89 public Object createSwitch(int lineno) {
\r
90 return new Node(TokenStream.SWITCH, new Integer(lineno));
\r
93 public Object createVariables(int lineno) {
\r
94 return new Node(TokenStream.VAR, new Integer(lineno));
\r
97 public Object createExprStatement(Object expr, int lineno) {
\r
98 return new Node(TokenStream.EXPRSTMT, (Node) expr, new Integer(lineno));
\r
104 public Object createName(String name) {
\r
105 return new Node(TokenStream.NAME, name);
\r
109 * String (for literals)
\r
111 public Object createString(String string) {
\r
112 return new Node(TokenStream.STRING, string);
\r
116 * Number (for literals)
\r
118 public Object createNumber(Number number) {
\r
119 return new Node(TokenStream.NUMBER, number);
\r
123 * Catch clause of try/catch/finally
\r
124 * @param varName the name of the variable to bind to the exception
\r
125 * @param catchCond the condition under which to catch the exception.
\r
126 * May be null if no condition is given.
\r
127 * @param stmts the statements in the catch clause
\r
128 * @param lineno the starting line number of the catch clause
\r
130 public Object createCatch(String varName, Object catchCond, Object stmts,
\r
133 if (catchCond == null)
\r
134 catchCond = new Node(TokenStream.PRIMARY,
\r
135 new Integer(TokenStream.TRUE));
\r
136 Node result = new Node(TokenStream.CATCH, (Node)createName(varName),
\r
137 (Node)catchCond, (Node)stmts);
\r
138 result.setDatum(new Integer(lineno));
\r
145 public Object createThrow(Object expr, int lineno) {
\r
146 return new Node(TokenStream.THROW, (Node)expr, new Integer(lineno));
\r
152 public Object createReturn(Object expr, int lineno) {
\r
153 return expr == null
\r
154 ? new Node(TokenStream.RETURN, new Integer(lineno))
\r
155 : new Node(TokenStream.RETURN, (Node)expr, new Integer(lineno));
\r
161 public Object createAssert(Object expr, int lineno) {
\r
162 return expr == null
\r
163 ? new Node(TokenStream.ASSERT, new Integer(lineno))
\r
164 : new Node(TokenStream.ASSERT, (Node)expr, new Integer(lineno));
\r
170 public Object createLabel(String label, int lineno) {
\r
171 Node result = new Node(TokenStream.LABEL, new Integer(lineno));
\r
172 Node name = new Node(TokenStream.NAME, label);
\r
173 result.addChildToBack(name);
\r
178 * Break (possibly labeled)
\r
180 public Object createBreak(String label, int lineno) {
\r
181 Node result = new Node(TokenStream.BREAK, new Integer(lineno));
\r
182 if (label == null) {
\r
185 Node name = new Node(TokenStream.NAME, label);
\r
186 result.addChildToBack(name);
\r
192 * Continue (possibly labeled)
\r
194 public Object createContinue(String label, int lineno) {
\r
195 Node result = new Node(TokenStream.CONTINUE, new Integer(lineno));
\r
196 if (label == null) {
\r
199 Node name = new Node(TokenStream.NAME, label);
\r
200 result.addChildToBack(name);
\r
207 * Creates the empty statement block
\r
208 * Must make subsequent calls to add statements to the node
\r
210 public Object createBlock(int lineno) {
\r
211 return new Node(TokenStream.BLOCK, new Integer(lineno));
\r
214 public Object createFunctionNode(String name, Object args,
\r
215 Object statements)
\r
219 return new FunctionNode(name, (Node) args, (Node) statements);
\r
222 public Object createFunction(String name, Object args, Object statements,
\r
223 String sourceName, int baseLineno,
\r
224 int endLineno, Object source,
\r
227 FunctionNode f = (FunctionNode) createFunctionNode(name, args,
\r
229 f.setFunctionType(isExpr ? FunctionNode.FUNCTION_EXPRESSION
\r
230 : FunctionNode.FUNCTION_STATEMENT);
\r
231 f.putProp(Node.SOURCENAME_PROP, sourceName);
\r
232 f.putProp(Node.BASE_LINENO_PROP, new Integer(baseLineno));
\r
233 f.putProp(Node.END_LINENO_PROP, new Integer(endLineno));
\r
234 if (source != null)
\r
235 f.putProp(Node.SOURCE_PROP, source);
\r
236 Node result = new Node(TokenStream.FUNCTION, name);
\r
237 result.putProp(Node.FUNCTION_PROP, f);
\r
241 public void setFunctionExpressionStatement(Object o) {
\r
243 FunctionNode f = (FunctionNode) n.getProp(Node.FUNCTION_PROP);
\r
244 f.setFunctionType(FunctionNode.FUNCTION_EXPRESSION_STATEMENT);
\r
248 * Add a child to the back of the given node. This function
\r
249 * breaks the Factory abstraction, but it removes a requirement
\r
250 * from implementors of Node.
\r
252 public void addChildToBack(Object parent, Object child) {
\r
253 ((Node)parent).addChildToBack((Node)child);
\r
259 public Object createWhile(Object cond, Object body, int lineno) {
\r
260 // Just add a GOTO to the condition in the do..while
\r
261 Node result = (Node) createDoWhile(body, cond, lineno);
\r
262 Node condTarget = (Node) result.getProp(Node.CONTINUE_PROP);
\r
263 Node GOTO = new Node(TokenStream.GOTO);
\r
264 GOTO.putProp(Node.TARGET_PROP, condTarget);
\r
265 result.addChildToFront(GOTO);
\r
272 public Object createDoWhile(Object body, Object cond, int lineno) {
\r
273 Node result = new Node(TokenStream.LOOP, new Integer(lineno));
\r
274 Node bodyTarget = new Node(TokenStream.TARGET);
\r
275 Node condTarget = new Node(TokenStream.TARGET);
\r
276 Node IFEQ = new Node(TokenStream.IFEQ, (Node)cond);
\r
277 IFEQ.putProp(Node.TARGET_PROP, bodyTarget);
\r
278 Node breakTarget = new Node(TokenStream.TARGET);
\r
280 result.addChildToBack(bodyTarget);
\r
281 result.addChildrenToBack((Node)body);
\r
282 result.addChildToBack(condTarget);
\r
283 result.addChildToBack(IFEQ);
\r
284 result.addChildToBack(breakTarget);
\r
286 result.putProp(Node.BREAK_PROP, breakTarget);
\r
287 result.putProp(Node.CONTINUE_PROP, condTarget);
\r
295 public Object createFor(Object init, Object test, Object incr,
\r
296 Object body, int lineno)
\r
298 if (((Node) test).getType() == TokenStream.VOID) {
\r
299 test = new Node(TokenStream.PRIMARY,
\r
300 new Integer(TokenStream.TRUE));
\r
302 Node result = (Node)createWhile(test, body, lineno);
\r
303 Node initNode = (Node) init;
\r
304 if (initNode.getType() != TokenStream.VOID) {
\r
305 if (initNode.getType() != TokenStream.VAR)
\r
306 initNode = new Node(TokenStream.POP, initNode);
\r
307 result.addChildToFront(initNode);
\r
309 Node condTarget = (Node)result.getProp(Node.CONTINUE_PROP);
\r
310 Node incrTarget = new Node(TokenStream.TARGET);
\r
311 result.addChildBefore(incrTarget, condTarget);
\r
312 if (((Node) incr).getType() != TokenStream.VOID) {
\r
313 incr = createUnary(TokenStream.POP, incr);
\r
314 result.addChildAfter((Node)incr, incrTarget);
\r
316 result.putProp(Node.CONTINUE_PROP, incrTarget);
\r
324 public Object createForIn(Object lhs, Object obj, Object body, int lineno) {
\r
326 Node lhsNode = (Node) lhs;
\r
327 Node objNode = (Node) obj;
\r
328 int type = lhsNode.getType();
\r
330 Node lvalue = lhsNode;
\r
333 case TokenStream.NAME:
\r
334 case TokenStream.GETPROP:
\r
335 case TokenStream.GETELEM:
\r
338 case TokenStream.VAR:
\r
340 * check that there was only one variable given.
\r
341 * we can't do this in the parser, because then the
\r
342 * parser would have to know something about the
\r
343 * 'init' node of the for-in loop.
\r
345 Node lastChild = lhsNode.getLastChild();
\r
346 if (lhsNode.getFirstChild() != lastChild) {
\r
347 reportError("msg.mult.index");
\r
349 lvalue = new Node(TokenStream.NAME, lastChild.getString());
\r
353 reportError("msg.bad.for.in.lhs");
\r
357 Node init = new Node(TokenStream.ENUMINIT, objNode);
\r
358 Node next = new Node(TokenStream.ENUMNEXT);
\r
359 next.putProp(Node.ENUM_PROP, init);
\r
360 Node temp = createNewTemp(next);
\r
361 Node cond = new Node(TokenStream.EQOP, new Integer(TokenStream.NE));
\r
362 cond.addChildToBack(temp);
\r
363 cond.addChildToBack(new Node(TokenStream.PRIMARY,
\r
364 new Integer(TokenStream.NULL)));
\r
365 Node newBody = new Node(TokenStream.BLOCK);
\r
366 Node assign = (Node) createAssignment(TokenStream.NOP, lvalue,
\r
367 createUseTemp(temp), null,
\r
369 newBody.addChildToBack(new Node(TokenStream.POP, assign));
\r
370 newBody.addChildToBack((Node) body);
\r
371 Node result = (Node) createWhile(cond, newBody, lineno);
\r
373 result.addChildToFront(init);
\r
374 if (type == TokenStream.VAR)
\r
375 result.addChildToFront(lhsNode);
\r
377 Node done = new Node(TokenStream.ENUMDONE);
\r
378 done.putProp(Node.ENUM_PROP, init);
\r
379 result.addChildToBack(done);
\r
385 * Try/Catch/Finally
\r
387 * The IRFactory tries to express as much as possible in the tree;
\r
388 * the responsibilities remaining for Codegen are to add the Java
\r
389 * handlers: (Either (but not both) of TARGET and FINALLY might not
\r
392 * - a catch handler for javascript exceptions that unwraps the
\r
393 * exception onto the stack and GOTOes to the catch target -
\r
394 * TARGET_PROP in the try node.
\r
396 * - a finally handler that catches any exception, stores it to a
\r
397 * temporary, and JSRs to the finally target - FINALLY_PROP in the
\r
398 * try node - before re-throwing the exception.
\r
400 * ... and a goto to GOTO around these handlers.
\r
402 public Object createTryCatchFinally(Object tryblock, Object catchblocks,
\r
403 Object finallyblock, int lineno)
\r
405 Node trynode = (Node)tryblock;
\r
408 if (trynode.getType() == TokenStream.BLOCK && !trynode.hasChildren())
\r
411 Node pn = new Node(TokenStream.TRY, trynode, new Integer(lineno));
\r
412 Node catchNodes = (Node)catchblocks;
\r
413 boolean hasCatch = catchNodes.hasChildren();
\r
414 boolean hasFinally = false;
\r
415 Node finallyNode = null;
\r
416 Node finallyTarget = null;
\r
417 if (finallyblock != null) {
\r
418 finallyNode = (Node)finallyblock;
\r
419 hasFinally = (finallyNode.getType() != TokenStream.BLOCK
\r
420 || finallyNode.hasChildren());
\r
422 // make a TARGET for the finally that the tcf node knows about
\r
423 finallyTarget = new Node(TokenStream.TARGET);
\r
424 pn.putProp(Node.FINALLY_PROP, finallyTarget);
\r
426 // add jsr finally to the try block
\r
427 Node jsrFinally = new Node(TokenStream.JSR);
\r
428 jsrFinally.putProp(Node.TARGET_PROP, finallyTarget);
\r
429 pn.addChildToBack(jsrFinally);
\r
434 if (!hasFinally && !hasCatch) // bc finally might be an empty block...
\r
437 Node endTarget = new Node(TokenStream.TARGET);
\r
438 Node GOTOToEnd = new Node(TokenStream.GOTO);
\r
439 GOTOToEnd.putProp(Node.TARGET_PROP, endTarget);
\r
440 pn.addChildToBack(GOTOToEnd);
\r
449 } catch (e: e instanceof Object) {
\r
463 if (e instanceof Object) {
\r
476 // make a TARGET for the catch that the tcf node knows about
\r
477 Node catchTarget = new Node(TokenStream.TARGET);
\r
478 pn.putProp(Node.TARGET_PROP, catchTarget);
\r
480 pn.addChildToBack(catchTarget);
\r
482 // get the exception object and store it in a temp
\r
483 Node exn = createNewLocal(new Node(TokenStream.VOID));
\r
484 pn.addChildToBack(new Node(TokenStream.POP, exn));
\r
486 Node endCatch = new Node(TokenStream.TARGET);
\r
488 // add [jsr finally?] goto end to each catch block
\r
489 // expects catchNode children to be (cond block) pairs.
\r
490 Node cb = catchNodes.getFirstChild();
\r
491 while (cb != null) {
\r
492 Node catchStmt = new Node(TokenStream.BLOCK);
\r
493 int catchLineNo = ((Integer)cb.getDatum()).intValue();
\r
495 Node name = cb.getFirstChild();
\r
496 Node cond = name.getNextSibling();
\r
497 Node catchBlock = cond.getNextSibling();
\r
498 cb.removeChild(name);
\r
499 cb.removeChild(cond);
\r
500 cb.removeChild(catchBlock);
\r
502 Node newScope = createNewLocal(new Node(TokenStream.NEWSCOPE));
\r
503 Node initScope = new Node(TokenStream.SETPROP, newScope,
\r
504 new Node(TokenStream.STRING,
\r
505 name.getString()),
\r
506 createUseLocal(exn));
\r
507 catchStmt.addChildToBack(new Node(TokenStream.POP, initScope));
\r
509 catchBlock.addChildToBack(new Node(TokenStream.LEAVEWITH));
\r
510 Node GOTOToEndCatch = new Node(TokenStream.GOTO);
\r
511 GOTOToEndCatch.putProp(Node.TARGET_PROP, endCatch);
\r
512 catchBlock.addChildToBack(GOTOToEndCatch);
\r
514 Node ifStmt = (Node) createIf(cond, catchBlock, null, catchLineNo);
\r
515 // Try..catch produces "with" code in order to limit
\r
516 // the scope of the exception object.
\r
517 // OPT: We should be able to figure out the correct
\r
518 // scoping at compile-time and avoid the
\r
519 // runtime overhead.
\r
520 Node withStmt = (Node) createWith(createUseLocal(newScope),
\r
521 ifStmt, catchLineNo);
\r
522 catchStmt.addChildToBack(withStmt);
\r
524 pn.addChildToBack(catchStmt);
\r
526 // move to next cb
\r
527 cb = cb.getNextSibling();
\r
530 // Generate code to rethrow if no catch clause was executed
\r
531 Node rethrow = new Node(TokenStream.THROW, createUseLocal(exn));
\r
532 pn.addChildToBack(rethrow);
\r
534 pn.addChildToBack(endCatch);
\r
535 // add a JSR finally if needed
\r
537 Node jsrFinally = new Node(TokenStream.JSR);
\r
538 jsrFinally.putProp(Node.TARGET_PROP, finallyTarget);
\r
539 pn.addChildToBack(jsrFinally);
\r
540 Node GOTO = new Node(TokenStream.GOTO);
\r
541 GOTO.putProp(Node.TARGET_PROP, endTarget);
\r
542 pn.addChildToBack(GOTO);
\r
547 pn.addChildToBack(finallyTarget);
\r
548 Node returnTemp = createNewLocal(new Node(TokenStream.VOID));
\r
549 Node popAndMake = new Node(TokenStream.POP, returnTemp);
\r
550 pn.addChildToBack(popAndMake);
\r
551 pn.addChildToBack(finallyNode);
\r
552 Node ret = createUseLocal(returnTemp);
\r
554 // add the magic prop that makes it output a RET
\r
555 ret.putProp(Node.TARGET_PROP, Boolean.TRUE);
\r
556 pn.addChildToBack(ret);
\r
558 pn.addChildToBack(endTarget);
\r
563 * Throw, Return, Label, Break and Continue are defined in ASTFactory.
\r
569 public Object createWith(Object obj, Object body, int lineno) {
\r
570 Node result = new Node(TokenStream.BLOCK, new Integer(lineno));
\r
571 result.addChildToBack(new Node(TokenStream.ENTERWITH, (Node)obj));
\r
572 Node bodyNode = new Node(TokenStream.WITH, (Node) body,
\r
573 new Integer(lineno));
\r
574 result.addChildrenToBack(bodyNode);
\r
575 result.addChildToBack(new Node(TokenStream.LEAVEWITH));
\r
581 * <BR>createArrayLiteral rewrites its argument as array creation
\r
582 * plus a series of array element entries, so later compiler
\r
583 * stages don't need to know about array literals.
\r
585 public Object createArrayLiteral(Object obj) {
\r
588 array = result = new Node(TokenStream.NEW,
\r
589 new Node(TokenStream.NAME, "Array"));
\r
590 Node temp = createNewTemp(result);
\r
593 java.util.Enumeration children = ((Node) obj).getChildIterator();
\r
597 while (children.hasMoreElements()) {
\r
598 elem = (Node) children.nextElement();
\r
599 if (elem.getType() == TokenStream.PRIMARY &&
\r
600 elem.getInt() == TokenStream.UNDEFINED)
\r
605 Node addelem = new Node(TokenStream.SETELEM, createUseTemp(temp),
\r
606 new Node(TokenStream.NUMBER,
\r
610 result = new Node(TokenStream.COMMA, result, addelem);
\r
614 * If the version is 120, then new Array(4) means create a new
\r
615 * array with 4 as the first element. In this case, we might
\r
616 * need to explicitly check against trailing undefined
\r
617 * elements in the array literal, and set the length manually
\r
618 * if these occur. Otherwise, we can add an argument to the
\r
619 * node specifying new Array() to provide the array length.
\r
620 * (Which will make Array optimizations involving allocating a
\r
621 * Java array to back the javascript array work better.)
\r
623 if (Context.getContext().getLanguageVersion() == Context.VERSION_1_2) {
\r
624 /* When last array element is empty, we need to set the
\r
625 * length explicitly, because we can't depend on SETELEM
\r
626 * to do it for us - because empty [,,] array elements
\r
627 * never set anything at all. */
\r
628 if (elem != null &&
\r
629 elem.getType() == TokenStream.PRIMARY &&
\r
630 elem.getInt() == TokenStream.UNDEFINED)
\r
632 Node setlength = new Node(TokenStream.SETPROP,
\r
633 createUseTemp(temp),
\r
634 new Node(TokenStream.STRING,
\r
636 new Node(TokenStream.NUMBER,
\r
638 result = new Node(TokenStream.COMMA, result, setlength);
\r
641 array.addChildToBack(new Node(TokenStream.NUMBER,
\r
644 return new Node(TokenStream.COMMA, result, createUseTemp(temp));
\r
649 * <BR> createObjectLiteral rewrites its argument as object
\r
650 * creation plus object property entries, so later compiler
\r
651 * stages don't need to know about object literals.
\r
653 public Object createObjectLiteral(Object obj) {
\r
654 Node result = new Node(TokenStream.NEW, new Node(TokenStream.NAME,
\r
656 Node temp = createNewTemp(result);
\r
659 java.util.Enumeration children = ((Node) obj).getChildIterator();
\r
661 while (children.hasMoreElements()) {
\r
662 Node elem = (Node)children.nextElement();
\r
664 int op = (elem.getType() == TokenStream.NAME)
\r
665 ? TokenStream.SETPROP
\r
666 : TokenStream.SETELEM;
\r
667 Node addelem = new Node(op, createUseTemp(temp),
\r
668 elem, (Node)children.nextElement());
\r
669 result = new Node(TokenStream.COMMA, result, addelem);
\r
671 return new Node(TokenStream.COMMA, result, createUseTemp(temp));
\r
675 * Regular expressions
\r
677 public Object createRegExp(String string, String flags) {
\r
678 return flags.length() == 0
\r
679 ? new Node(TokenStream.OBJECT,
\r
680 new Node(TokenStream.STRING, string))
\r
681 : new Node(TokenStream.OBJECT,
\r
682 new Node(TokenStream.STRING, string),
\r
683 new Node(TokenStream.STRING, flags));
\r
689 public Object createIf(Object cond, Object ifTrue, Object ifFalse,
\r
692 Node result = new Node(TokenStream.BLOCK, new Integer(lineno));
\r
693 Node ifNotTarget = new Node(TokenStream.TARGET);
\r
694 Node IFNE = new Node(TokenStream.IFNE, (Node) cond);
\r
695 IFNE.putProp(Node.TARGET_PROP, ifNotTarget);
\r
697 result.addChildToBack(IFNE);
\r
698 result.addChildrenToBack((Node)ifTrue);
\r
700 if (ifFalse != null) {
\r
701 Node GOTOToEnd = new Node(TokenStream.GOTO);
\r
702 Node endTarget = new Node(TokenStream.TARGET);
\r
703 GOTOToEnd.putProp(Node.TARGET_PROP, endTarget);
\r
704 result.addChildToBack(GOTOToEnd);
\r
705 result.addChildToBack(ifNotTarget);
\r
706 result.addChildrenToBack((Node)ifFalse);
\r
707 result.addChildToBack(endTarget);
\r
709 result.addChildToBack(ifNotTarget);
\r
715 public Object createTernary(Object cond, Object ifTrue, Object ifFalse) {
\r
716 return createIf(cond, ifTrue, ifFalse, -1);
\r
722 public Object createUnary(int nodeType, Object child) {
\r
723 Node childNode = (Node) child;
\r
724 if (nodeType == TokenStream.DELPROP) {
\r
725 int childType = childNode.getType();
\r
728 if (childType == TokenStream.NAME) {
\r
729 // Transform Delete(Name "a")
\r
730 // to Delete(Bind("a"), String("a"))
\r
731 childNode.setType(TokenStream.BINDNAME);
\r
733 right = childNode.cloneNode();
\r
734 right.setType(TokenStream.STRING);
\r
735 } else if (childType == TokenStream.GETPROP ||
\r
736 childType == TokenStream.GETELEM)
\r
738 left = childNode.getFirstChild();
\r
739 right = childNode.getLastChild();
\r
740 childNode.removeChild(left);
\r
741 childNode.removeChild(right);
\r
743 return new Node(TokenStream.PRIMARY,
\r
744 new Integer(TokenStream.TRUE));
\r
746 return new Node(nodeType, left, right);
\r
748 return new Node(nodeType, childNode);
\r
751 public Object createUnary(int nodeType, int nodeOp, Object child) {
\r
752 Node childNode = (Node) child;
\r
753 int childType = childNode.getType();
\r
754 if (nodeOp == TokenStream.TYPEOF &&
\r
755 childType == TokenStream.NAME)
\r
757 childNode.setType(TokenStream.TYPEOF);
\r
761 if (nodeType == TokenStream.INC || nodeType == TokenStream.DEC) {
\r
763 if (!hasSideEffects(childNode)
\r
764 && (nodeOp == TokenStream.POST)
\r
765 && (childType == TokenStream.NAME
\r
766 || childType == TokenStream.GETPROP
\r
767 || childType == TokenStream.GETELEM))
\r
769 // if it's not a LHS type, createAssignment (below) will throw
\r
771 return new Node(nodeType, childNode);
\r
775 * Transform INC/DEC ops to +=1, -=1,
\r
776 * expecting later optimization of all +/-=1 cases to INC, DEC.
\r
778 // we have to use Double for now, because
\r
779 // 0.0 and 1.0 are stored as dconst_[01],
\r
780 // and using a Float creates a stack mismatch.
\r
781 Node rhs = (Node) createNumber(new Double(1.0));
\r
783 return createAssignment(nodeType == TokenStream.INC
\r
788 ScriptRuntime.NumberClass,
\r
789 nodeOp == TokenStream.POST);
\r
792 Node result = new Node(nodeType, new Integer(nodeOp));
\r
793 result.addChildToBack((Node)child);
\r
800 public Object createBinary(int nodeType, Object left, Object right) {
\r
802 switch (nodeType) {
\r
804 case TokenStream.DOT:
\r
805 nodeType = TokenStream.GETPROP;
\r
806 Node idNode = (Node) right;
\r
807 idNode.setType(TokenStream.STRING);
\r
808 String id = idNode.getString();
\r
809 if (id.equals("__proto__") || id.equals("__parent__")) {
\r
810 Node result = new Node(nodeType, (Node) left);
\r
811 result.putProp(Node.SPECIAL_PROP_PROP, id);
\r
816 case TokenStream.LB:
\r
817 // OPT: could optimize to GETPROP iff string can't be a number
\r
818 nodeType = TokenStream.GETELEM;
\r
821 case TokenStream.AND:
\r
822 temp = createNewTemp((Node) left);
\r
823 return createTernary(temp, right, createUseTemp(temp));
\r
825 case TokenStream.OR:
\r
826 temp = createNewTemp((Node) left);
\r
827 return createTernary(temp, createUseTemp(temp), right);
\r
830 return new Node(nodeType, (Node)left, (Node)right);
\r
833 public Object createBinary(int nodeType, int nodeOp, Object left,
\r
836 if (nodeType == TokenStream.ASSIGN) {
\r
837 return createAssignment(nodeOp, (Node) left, (Node) right,
\r
840 return new Node(nodeType, (Node) left, (Node) right,
\r
841 new Integer(nodeOp));
\r
844 public Object createAssignment(int nodeOp, Node left, Node right,
\r
845 Class convert, boolean postfix)
\r
847 int nodeType = left.getType();
\r
850 switch (nodeType) {
\r
851 case TokenStream.NAME:
\r
852 return createSetName(nodeOp, left, right, convert, postfix);
\r
854 case TokenStream.GETPROP:
\r
855 idString = (String) left.getProp(Node.SPECIAL_PROP_PROP);
\r
856 if (idString != null)
\r
857 id = new Node(TokenStream.STRING, idString);
\r
859 case TokenStream.GETELEM:
\r
861 id = left.getLastChild();
\r
862 return createSetProp(nodeType, nodeOp, left.getFirstChild(),
\r
863 id, right, convert, postfix);
\r
865 // TODO: This should be a ReferenceError--but that's a runtime
\r
866 // exception. Should we compile an exception into the code?
\r
867 reportError("msg.bad.lhs.assign");
\r
872 private Node createConvert(Class toType, Node expr) {
\r
873 if (toType == null)
\r
875 Node result = new Node(TokenStream.CONVERT, expr);
\r
876 result.putProp(Node.TYPE_PROP, ScriptRuntime.NumberClass);
\r
880 private Object createSetName(int nodeOp, Node left, Node right,
\r
881 Class convert, boolean postfix)
\r
883 if (nodeOp == TokenStream.NOP) {
\r
884 left.setType(TokenStream.BINDNAME);
\r
885 return new Node(TokenStream.SETNAME, left, right);
\r
888 String s = left.getString();
\r
890 if (s.equals("__proto__") || s.equals("__parent__")) {
\r
891 Node result = new Node(TokenStream.SETPROP, left, right);
\r
892 result.putProp(Node.SPECIAL_PROP_PROP, s);
\r
896 Node opLeft = new Node(TokenStream.NAME, s);
\r
897 if (convert != null)
\r
898 opLeft = createConvert(convert, opLeft);
\r
900 opLeft = createNewTemp(opLeft);
\r
901 Node op = new Node(nodeOp, opLeft, right);
\r
903 Node lvalueLeft = new Node(TokenStream.BINDNAME, s);
\r
904 Node result = new Node(TokenStream.SETNAME, lvalueLeft, op);
\r
906 result = new Node(TokenStream.COMMA, result,
\r
907 createUseTemp(opLeft));
\r
912 public Node createNewTemp(Node n) {
\r
913 int type = n.getType();
\r
914 if (type == TokenStream.STRING || type == TokenStream.NUMBER) {
\r
915 // Optimization: clone these values rather than storing
\r
916 // and loading from a temp
\r
919 Node result = new Node(TokenStream.NEWTEMP, n);
\r
923 public Node createUseTemp(Node newTemp) {
\r
924 int type = newTemp.getType();
\r
925 if (type == TokenStream.NEWTEMP) {
\r
926 Node result = new Node(TokenStream.USETEMP);
\r
927 result.putProp(Node.TEMP_PROP, newTemp);
\r
928 Integer n = (Integer) newTemp.getProp(Node.USES_PROP);
\r
930 n = new Integer(1);
\r
932 if (n.intValue() < Integer.MAX_VALUE)
\r
933 n = new Integer(n.intValue() + 1);
\r
935 newTemp.putProp(Node.USES_PROP, n);
\r
938 return newTemp.cloneNode();
\r
941 public Node createNewLocal(Node n) {
\r
942 Node result = new Node(TokenStream.NEWLOCAL, n);
\r
946 public Node createUseLocal(Node newLocal) {
\r
947 int type = newLocal.getType();
\r
948 if (type == TokenStream.NEWLOCAL) {
\r
949 Node result = new Node(TokenStream.USELOCAL);
\r
950 result.putProp(Node.LOCAL_PROP, newLocal);
\r
953 return newLocal.cloneNode(); // what's this path for ?
\r
956 public static boolean hasSideEffects(Node exprTree) {
\r
957 switch (exprTree.getType()) {
\r
958 case TokenStream.INC:
\r
959 case TokenStream.DEC:
\r
960 case TokenStream.SETPROP:
\r
961 case TokenStream.SETELEM:
\r
962 case TokenStream.SETNAME:
\r
963 case TokenStream.CALL:
\r
964 case TokenStream.NEW:
\r
967 Node child = exprTree.getFirstChild();
\r
968 while (child != null) {
\r
969 if (hasSideEffects(child))
\r
972 child = child.getNextSibling();
\r
979 private Node createSetProp(int nodeType, int nodeOp, Node obj, Node id,
\r
980 Node expr, Class convert, boolean postfix)
\r
982 int type = nodeType == TokenStream.GETPROP
\r
983 ? TokenStream.SETPROP
\r
984 : TokenStream.SETELEM;
\r
986 Object datum = id.getDatum();
\r
987 if (type == TokenStream.SETPROP && datum != null &&
\r
988 datum instanceof String)
\r
990 String s = (String) datum;
\r
991 if (s.equals("__proto__") || s.equals("__parent__")) {
\r
992 Node result = new Node(type, obj, expr);
\r
993 result.putProp(Node.SPECIAL_PROP_PROP, s);
\r
998 if (nodeOp == TokenStream.NOP)
\r
999 return new Node(type, obj, id, expr);
\r
1001 * If the RHS expression could modify the LHS we have
\r
1002 * to construct a temporary to hold the LHS context
\r
1003 * prior to running the expression. Ditto, if the id
\r
1004 * expression has side-effects.
\r
1006 * XXX If the hasSideEffects tests take too long, we
\r
1007 * could make this an optimizer-only transform
\r
1008 * and always do the temp assignment otherwise.
\r
1011 Node tmp1, tmp2, opLeft;
\r
1012 if (hasSideEffects(expr)
\r
1013 || hasSideEffects(id)
\r
1014 || (obj.getType() != TokenStream.NAME)) {
\r
1015 tmp1 = createNewTemp(obj);
\r
1016 Node useTmp1 = createUseTemp(tmp1);
\r
1018 tmp2 = createNewTemp(id);
\r
1019 Node useTmp2 = createUseTemp(tmp2);
\r
1021 opLeft = new Node(nodeType, useTmp1, useTmp2);
\r
1023 tmp1 = obj.cloneNode();
\r
1024 tmp2 = id.cloneNode();
\r
1025 opLeft = new Node(nodeType, obj, id);
\r
1028 if (convert != null)
\r
1029 opLeft = createConvert(convert, opLeft);
\r
1031 opLeft = createNewTemp(opLeft);
\r
1032 Node op = new Node(nodeOp, opLeft, expr);
\r
1034 Node result = new Node(type, tmp1, tmp2, op);
\r
1036 result = new Node(TokenStream.COMMA, result,
\r
1037 createUseTemp(opLeft));
\r
1043 private void reportError(String msgResource) {
\r
1045 if (scope != null)
\r
1046 throw NativeGlobal.constructError(
\r
1047 Context.getContext(), "SyntaxError",
\r
1048 ScriptRuntime.getMessage0(msgResource),
\r
1051 String message = Context.getMessage0(msgResource);
\r
1052 Context.reportError(message, ts.getSourceName(), ts.getLineno(),
\r
1053 ts.getLine(), ts.getOffset());
\r
1057 // Only needed to get file/line information. Could create an interface
\r
1058 // that TokenStream implements if we want to make the connection less
\r
1060 private TokenStream ts;
\r
1062 // Only needed to pass to the Erorr exception constructors
\r
1063 private Scriptable scope;
\r