blob: 59be80b202b5c59aae3b155235250992f9a5aa2a [file] [log] [blame]
/* -*- 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;
}