/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is Rhino code, released * May 6, 1999. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1997-1999 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * Norris Boyd * * Alternatively, the contents of this file may be used under the * terms of the GNU Public License (the "GPL"), in which case the * provisions of the GPL are applicable instead of those above. * If you wish to allow use of your version of this file only * under the terms of the GPL and not to allow others to use your * version of this file under the NPL, indicate your decision by * deleting the provisions above and replace them with the notice * and other provisions required by the GPL. If you do not delete * the provisions above, a recipient may use your version of this * file under either the NPL or the GPL. */ package com.google.gwt.dev.js.rhino; /** * This class allows the creation of nodes, and follows the Factory pattern. * * @see Node * @author Mike McCabe * @author Norris Boyd */ public class IRFactory { public IRFactory(TokenStream ts) { this.ts = ts; } /** * Script (for associating file/url names with toplevel scripts.) */ public Object createScript(Object body, String sourceName, int baseLineno, int endLineno, Object source) { Node result = new Node(TokenStream.SCRIPT); Node children = ((Node) body).getFirstChild(); if (children != null) result.addChildrenToBack(children); result.putProp(Node.SOURCENAME_PROP, sourceName); result.putIntProp(Node.BASE_LINENO_PROP, baseLineno); result.putIntProp(Node.END_LINENO_PROP, endLineno); if (source != null) result.putProp(Node.SOURCE_PROP, source); return result; } /** * Leaf */ public Object createLeaf(int nodeType) { return new Node(nodeType); } public Object createLeaf(int nodeType, int nodeOp) { return new Node(nodeType, nodeOp); } public int getLeafType(Object leaf) { Node n = (Node) leaf; return n.getType(); } /** * Statement leaf nodes. */ public Object createSwitch(int lineno) { return new Node(TokenStream.SWITCH, lineno); } public Object createVariables(int lineno) { return new Node(TokenStream.VAR, lineno); } public Object createExprStatement(Object expr, int lineno) { return new Node(TokenStream.EXPRSTMT, (Node) expr, lineno); } /** * Name */ public Object createName(String name) { return Node.newString(TokenStream.NAME, name); } /** * String (for literals) */ public Object createString(String string) { return Node.newString(string); } /** * Number (for literals) */ public Object createNumber(double number) { return Node.newNumber(number); } /** * Catch clause of try/catch/finally * @param varName the name of the variable to bind to the exception * @param catchCond the condition under which to catch the exception. * May be null if no condition is given. * @param stmts the statements in the catch clause * @param lineno the starting line number of the catch clause */ public Object createCatch(String varName, Object catchCond, Object stmts, int lineno) { if (catchCond == null) { catchCond = new Node(TokenStream.PRIMARY, TokenStream.TRUE); } return new Node(TokenStream.CATCH, (Node)createName(varName), (Node)catchCond, (Node)stmts, lineno); } /** * Throw */ public Object createThrow(Object expr, int lineno) { return new Node(TokenStream.THROW, (Node)expr, lineno); } /** * Return */ public Object createReturn(Object expr, int lineno) { return expr == null ? new Node(TokenStream.RETURN, lineno) : new Node(TokenStream.RETURN, (Node)expr, lineno); } /** * Label */ public Object createLabel(String label, int lineno) { Node result = new Node(TokenStream.LABEL, lineno); Node name = Node.newString(TokenStream.NAME, label); result.addChildToBack(name); return result; } /** * Break (possibly labeled) */ public Object createBreak(String label, int lineno) { Node result = new Node(TokenStream.BREAK, lineno); if (label == null) { return result; } else { Node name = Node.newString(TokenStream.NAME, label); result.addChildToBack(name); return result; } } /** * Continue (possibly labeled) */ public Object createContinue(String label, int lineno) { Node result = new Node(TokenStream.CONTINUE, lineno); if (label == null) { return result; } else { Node name = Node.newString(TokenStream.NAME, label); result.addChildToBack(name); return result; } } /** * debugger */ public Object createDebugger(int lineno) { Node result = new Node(TokenStream.DEBUGGER, lineno); return result; } /** * Statement block * Creates the empty statement block * Must make subsequent calls to add statements to the node */ public Object createBlock(int lineno) { return new Node(TokenStream.BLOCK, lineno); } public Object createFunction(String name, Object args, Object statements, String sourceName, int baseLineno, int endLineno, Object source, boolean isExpr) { Node f = new Node(TokenStream.FUNCTION, Node.newString(TokenStream.NAME, name == null ? "" : name), (Node)args, (Node)statements, baseLineno); f.putProp(Node.SOURCENAME_PROP, sourceName); f.putIntProp(Node.BASE_LINENO_PROP, baseLineno); f.putIntProp(Node.END_LINENO_PROP, endLineno); if (source != null) f.putProp(Node.SOURCE_PROP, source); return f; } /** * Add a child to the back of the given node. This function * breaks the Factory abstraction, but it removes a requirement * from implementors of Node. */ public void addChildToBack(Object parent, Object child) { ((Node)parent).addChildToBack((Node)child); } /** * While */ public Object createWhile(Object cond, Object body, int lineno) { return new Node(TokenStream.WHILE, (Node)cond, (Node)body, lineno); } /** * DoWhile */ public Object createDoWhile(Object body, Object cond, int lineno) { return new Node(TokenStream.DO, (Node)body, (Node)cond, lineno); } /** * For */ public Object createFor(Object init, Object test, Object incr, Object body, int lineno) { return new Node(TokenStream.FOR, (Node)init, (Node)test, (Node)incr, (Node)body); } /** * For .. In * */ public Object createForIn(Object lhs, Object obj, Object body, int lineno) { return new Node(TokenStream.FOR, (Node)lhs, (Node)obj, (Node)body); } /** * Try/Catch/Finally */ public Object createTryCatchFinally(Object tryblock, Object catchblocks, Object finallyblock, int lineno) { if (finallyblock == null) { return new Node(TokenStream.TRY, (Node)tryblock, (Node)catchblocks); } return new Node(TokenStream.TRY, (Node)tryblock, (Node)catchblocks, (Node)finallyblock); } /** * Throw, Return, Label, Break and Continue are defined in ASTFactory. */ /** * With */ public Object createWith(Object obj, Object body, int lineno) { return new Node(TokenStream.WITH, (Node)obj, (Node)body, lineno); } /** * Array Literal */ public Object createArrayLiteral(Object obj) { return obj; } /** * Object Literals */ public Object createObjectLiteral(Object obj) { return obj; } /** * Regular expressions */ public Object createRegExp(String string, String flags) { return flags.length() == 0 ? new Node(TokenStream.REGEXP, Node.newString(string)) : new Node(TokenStream.REGEXP, Node.newString(string), Node.newString(flags)); } /** * If statement */ public Object createIf(Object cond, Object ifTrue, Object ifFalse, int lineno) { if (ifFalse == null) return new Node(TokenStream.IF, (Node)cond, (Node)ifTrue); return new Node(TokenStream.IF, (Node)cond, (Node)ifTrue, (Node)ifFalse); } public Object createTernary(Object cond, Object ifTrue, Object ifFalse) { return new Node(TokenStream.HOOK, (Node)cond, (Node)ifTrue, (Node)ifFalse); } /** * Unary */ public Object createUnary(int nodeType, Object child) { Node childNode = (Node) child; return new Node(nodeType, childNode); } public Object createUnary(int nodeType, int nodeOp, Object child) { return new Node(nodeType, (Node)child, nodeOp); } /** * Binary */ public Object createBinary(int nodeType, Object left, Object right) { Node temp; switch (nodeType) { case TokenStream.DOT: nodeType = TokenStream.GETPROP; Node idNode = (Node) right; idNode.setType(TokenStream.STRING); String id = idNode.getString(); if (id.equals("__proto__") || id.equals("__parent__")) { Node result = new Node(nodeType, (Node) left); result.putProp(Node.SPECIAL_PROP_PROP, id); return result; } break; case TokenStream.LB: // OPT: could optimize to GETPROP iff string can't be a number nodeType = TokenStream.GETELEM; break; } return new Node(nodeType, (Node)left, (Node)right); } public Object createBinary(int nodeType, int nodeOp, Object left, Object right) { if (nodeType == TokenStream.ASSIGN) { return createAssignment(nodeOp, (Node) left, (Node) right, null, false); } return new Node(nodeType, (Node) left, (Node) right, nodeOp); } public Object createAssignment(int nodeOp, Node left, Node right, Class convert, boolean postfix) { int nodeType = left.getType(); switch (nodeType) { case TokenStream.NAME: case TokenStream.GETPROP: case TokenStream.GETELEM: break; default: // TODO: This should be a ReferenceError--but that's a runtime // exception. Should we compile an exception into the code? reportError("msg.bad.lhs.assign"); } return new Node(TokenStream.ASSIGN, left, right, nodeOp); } private Node createConvert(Class toType, Node expr) { if (toType == null) return expr; Node result = new Node(TokenStream.CONVERT, expr); result.putProp(Node.TYPE_PROP, Number.class); return result; } public static boolean hasSideEffects(Node exprTree) { switch (exprTree.getType()) { case TokenStream.INC: case TokenStream.DEC: case TokenStream.SETPROP: case TokenStream.SETELEM: case TokenStream.SETNAME: case TokenStream.CALL: case TokenStream.NEW: return true; default: Node child = exprTree.getFirstChild(); while (child != null) { if (hasSideEffects(child)) return true; else child = child.getNext(); } break; } return false; } private void reportError(String msgResource) { String message = Context.getMessage0(msgResource); Context.reportError( message, ts.getSourceName(), ts.getLineno(), ts.getLine(), ts.getOffset()); } // Only needed to get file/line information. Could create an interface // that TokenStream implements if we want to make the connection less // direct. private TokenStream ts; }