/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
* Free SoftwareFoundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.es.parser;
import com.caucho.es.ESBase;
import com.caucho.es.ESException;
import com.caucho.es.ESId;
import com.caucho.util.CharBuffer;
import java.util.ArrayList;
import java.util.HashMap;
/**
* Block is an intermediate form representing an expression.
*/
class Block {
private static HashMap specialNames;
Function function;
private Block parent;
private Expr lastExpr;
boolean isDead;
private boolean hasStatementValue;
private Parser parser;
private boolean isLoop;
private boolean canExit;
private boolean hasDefault;
private int withDepth;
private ESId id;
private Expr mark;
private Object switchTop;
private Object top;
private int topMark;
private Block()
{
}
Block create()
throws ESException
{
evalExpr();
Block block = new Block();
block.function = function;
block.parent = this;
block.lastExpr = null;
block.isDead = false;
block.hasStatementValue = hasStatementValue;
block.parser = parser;
block.isLoop = false;
block.canExit = false;
block.withDepth = withDepth;
block.id = null;
block.mark = null;
block.switchTop = null;
block.top = null;
block.setTop();
return block;
}
static Block create(Parser parser, Function function)
{
Block block = new Block();
block.function = function;
block.parent = null;
block.lastExpr = null;
block.isDead = false;
block.hasStatementValue = function.needsStatementResults();
block.parser = parser;
block.isLoop = false;
block.canExit = false;
block.withDepth = 0;
block.id = null;
block.top = null;
block.topMark = 0;
function.setVars();
return block;
}
ClassLoader getClassLoader()
{
return parser.getClassLoader();
}
void setTop()
{
top = function.getTop();
if (top instanceof CharBuffer) {
CharBuffer cb = (CharBuffer) top;
topMark = cb.length();
}
else
topMark = 0;
}
Block pop()
{
Block parent = this.parent;
function.setVars();
free();
return parent;
}
boolean isGlobal()
{
return function.isGlobal();
}
int getDepth()
{
return withDepth;
}
boolean allowSpecial()
{
return parent == null;
}
String getFilename()
{
String filename = parser.lexer.getLastFilename();
int p = filename.lastIndexOf('/');
if (p > 0)
filename = filename.substring(p + 1);
p = filename.lastIndexOf('\\');
if (p > 0)
filename = filename.substring(p + 1);
return filename;
}
int getLine()
{
int line = parser.lexer.getLastLine();
return line;
}
void setLine(int line)
{
}
/**
* Returns true if the variable is already declared.
*/
boolean hasVar(ESId name)
{
return function.hasVar(name) || specialNames.get(name) != null;
}
/**
* Returns an expression for a new variable
*/
IdExpr newVar(ESId name)
{
return newVar(name, null);
}
IdExpr newVar(ESId name, Expr type)
{
if (withDepth > 0)
return new IdExpr(this, new Variable(this, name, type, false));
IdExpr expr = function.newVar(this, name, type);
// Kill variables inside an if
if (parent != null)
expr.getType();
return expr;
}
/**
* Define a new variable.
*/
void defVar(ESId name)
{
function.addVariable(this, name, null);
}
/**
* Define a new variable with the given type.
*/
void defVar(ESId name, Expr type)
{
function.addVariable(this, name, type);
}
Expr newLiteral(ESBase value)
{
return new LiteralExpr(this, value);
}
Expr newRegexp(ESBase value, String flags)
throws ESException
{
return new RegexpExpr(this, value, flags);
}
Expr newThis()
{
return new SpecialExpr(this, SpecialExpr.THIS);
}
Expr newArray(Expr expr)
{
return new SpecialExpr(this, SpecialExpr.ARRAY, expr);
}
Expr hasNext(String iter)
{
return new SpecialExpr(this, SpecialExpr.HAS_NEXT, iter);
}
Expr newType(ESId name)
{
return TypeExpr.create(this, name);
}
void addExpr(Expr expr)
throws ESException
{
if (isDead)
throw error("Statement is unreachable.");
if (lastExpr != null)
lastExpr.exprStatement(function);
if (hasStatementValue && ! void.class.equals(expr.getJavaClass()))
lastExpr = expr;
else {
lastExpr = null;
expr.exprStatement(function);
}
}
Block startBlock()
throws ESException
{
evalExpr();
function.println("{");
return create();
}
Block startBlock(ESId id)
throws ESException
{
if (findBlock(id) != null)
throw error("duplicate label `" + id + "'");
evalExpr();
Block block = create();
block.id = id;
function.println(id + ": {");
block.setTop();
return block;
}
Block finishBlock()
throws ESException
{
evalExpr();
function.println("}");
this.id = null;
Block old = pop();
if (isDead && ! canExit)
old.isDead = true;
return old;
}
void endBlock()
throws ESException
{
evalExpr();
function.println("}");
this.id = null;
}
void startIf(Expr expr, boolean isElse)
throws ESException
{
evalExpr();
if (isElse)
function.print(" else ");
function.print("if (");
function.addBoolean(expr);
function.println(") {");
setTop();
}
void startElse()
throws ESException
{
evalExpr();
function.println(" else {");
setTop();
}
Block startWhile(ESId id, Expr expr)
throws ESException
{
evalExpr();
if (id != null)
function.println(id + ":");
function.print("while (");
function.addBoolean(expr);
function.println(") {");
Block block = create();
startLoop(id);
if (! (expr instanceof LiteralExpr) ||
! ((LiteralExpr) expr).getLiteral().toBoolean())
canExit = true;
return block;
}
Block startFor(ESId id, Expr test, Expr incr)
throws ESException
{
evalExpr();
if (id != null)
function.println(id + ":");
function.print("for (;");
if (test != null)
function.addBoolean(test);
function.print(";");
if (incr != null)
function.addExpr(incr);
function.println(") {");
function.cl.pushDepth();
Block block = create();
startLoop(id);
if (test == null)
canExit = false;
else if (! (test instanceof LiteralExpr) ||
! ((LiteralExpr) test).getLiteral().toBoolean())
canExit = true;
return block;
}
Block startDo(ESId id)
throws ESException
{
evalExpr();
if (id != null)
function.println(id + ":");
function.print("do {");
Block block = create();
startLoop(id);
return block;
}
Block endDo(Expr expr)
throws ESException
{
evalExpr();
Block old = endLoop();
if (! (expr instanceof LiteralExpr) ||
! ((LiteralExpr) expr).getLiteral().toBoolean())
old.canExit = true;
if (old.canExit)
old.isDead = false;
function.print("while (");
function.addBoolean(expr);
function.println(");");
return old;
}
void startLoop(ESId id)
{
String oldVar = function.getStatementVar();
function.pushStatementLoop();
String newVar = function.getStatementVar();
if (oldVar != null)
function.println(newVar + " = " + oldVar + ";");
this.id = id;
isLoop = true;
canExit = false;
}
Block endLoop()
throws ESException
{
evalExpr();
String newVar = function.getStatementVar();
function.popStatementLoop();
String oldVar = function.getStatementVar();
if (oldVar != null && ! isDead)
function.println(oldVar + " = " + newVar + ";");
function.cl.popDepth();
function.println("}");
Block old = pop();
if (! old.canExit)
old.isDead = true;
return old;
}
Block startSwitch(Expr test)
throws ESException
{
ESId id = ESId.intern("_switchtemp");
function.print("_switchtemp = ");
function.addExpr(test);
function.println(";");
Block block = create();
block.switchTop = function.getSwitch();
block.isLoop = true;
block.hasDefault = false;
function.println("switch (_switchcode) {");
return block;
}
void doCase(int i)
throws ESException
{
isDead = false;
evalExpr();
function.println("case " + i + ":");
}
void doDefault()
throws ESException
{
isDead = false;
hasDefault = true;
evalExpr();
function.println("default:");
}
Block fillSwitch(ArrayList exprs)
throws ESException
{
evalExpr();
if (! hasDefault && ! isDead) {
function.println("default:");
function.println(" break;");
}
else if (! isDead)
function.println("break;");
function.println("}");
int mark = function.mark();
for (int i = 0; i < exprs.size(); i++) {
if (i != 0)
function.print("else ");
Expr test = (Expr) exprs.get(i);
function.print("if (_switchtemp.equals(");
function.addExpr(test);
function.println(")) _switchcode = " + i + ";");
}
if (exprs.size() > 0)
function.print("else ");
function.println("_switchcode = -1;");
function.moveChunk(switchTop, mark);
Block old = pop();
if (isDead && ! canExit && hasDefault)
old.isDead = true;
return old;
}
void doBreak(ESId id)
throws ESException
{
Block block = this;
for (; block != null; block = block.parent) {
if (block.id == id) {
block.canExit = true;
break;
}
}
if (block == null)
throw error("break needs enclosing loop");
function.setVars();
evalExpr();
function.println("break " + id + ";");
isDead = true;
}
void doBreak()
throws ESException
{
Block block = this;
for (; block != null; block = block.parent) {
if (block.isLoop) {
block.canExit = true;
break;
}
}
if (block == null)
throw error("break needs enclosing loop");
function.setVars();
evalExpr();
function.println("break;");
isDead = true;
}
void doContinue(ESId id)
throws ESException
{
Block block = this;
for (; block != null; block = block.parent) {
if (block.id == id && block.isLoop)
break;
/*
else
block.canExit = true;
*/
}
if (block == null)
throw error("continue needs enclosing loop");
function.setVars();
evalExpr();
function.println("continue " + id + ";");
isDead = true;
}
void doContinue()
throws ESException
{
if (findBlock(null) == null)
throw error("continue needs enclosing loop");
function.setVars();
evalExpr();
function.println("continue;");
isDead = true;
}
private Block findBlock(ESId id)
{
for (Block block = this; block != null; block = block.parent) {
if (id != null && block.id == id)
return block;
else if (id == null && block.isLoop)
return block;
}
return null;
}
Block startWith(Expr expr)
throws ESException
{
function.setArguments();
function.setUseAllVariables();
evalExpr();
withDepth++;
function.println("try {");
function.print("_env.pushScope(");
function.addExpr(expr);
function.println(");");
setTop();
return this;
}
Block endWith()
throws ESException
{
evalExpr();
withDepth--;
function.println("} finally {");
function.println("_env.popScope();");
function.println("}");
return this;
}
int getWithDepth()
{
return withDepth;
}
Block startTry()
throws ESException
{
function.setVars();
evalExpr();
function.println("try {");
return this;
}
Block endTry()
throws ESException
{
function.setVars();
evalExpr();
function.println("}");
return this;
}
void doTry()
throws ESException
{
evalExpr();
int i = 0;
for (; i < function.data.size(); i++) {
Object o = function.data.get(i);
if (o != top) {
}
else if (o instanceof CharBuffer) {
CharBuffer cb = (CharBuffer) o;
cb.insert(topMark, " try {\n");
break;
}
else {
function.data.add(i + 1, new CharBuffer(" try {\n"));
break;
}
}
if (i < function.data.size()) {
}
else if (function.tail != null && top == function.tail)
function.tail.insert(topMark, " try {\n");
else
function.data.add(0, new CharBuffer(" try {\n"));
function.println("}");
}
Block startCatch(String exn, Expr var)
throws ESException
{
evalExpr();
String temp = "_e" + function.getTemp();
function.println("catch (" + exn + " " + temp + ") {");
if (var != null) {
Expr expr = new SpecialExpr(this, SpecialExpr.EXCEPTION, temp);
var.assign(expr).exprStatement(function);
}
isDead = false;
setTop();
return this;
}
Block endCatch()
throws ESException
{
evalExpr();
function.println("}");
return this;
}
Block startFinally()
throws ESException
{
evalExpr();
Block block = create();
function.println("finally {");
function.pushStatementLoop();
block.setTop();
return block;
}
Block endFinally()
throws ESException
{
evalExpr();
function.println("}");
function.popStatementLoop();
return pop();
}
Block startSynchronized(Expr expr)
throws ESException
{
evalExpr();
function.print("synchronized (");
function.addExpr(expr);
function.println(".toJavaObject()) {");
return create();
}
Block endSynchronized()
throws ESException
{
evalExpr();
function.println("}");
Block old = pop();
old.isDead = isDead;
return old;
}
void doThrow(Expr expr)
throws ESException
{
function.print("throw (Exception)");
function.addExpr(expr);
function.println(".toJavaObject();");
isDead = true;
}
void doReturn(Expr value)
throws ESException
{
evalExpr();
function.print("return ");
value.setUsed();
if (function.getReturnType() != null)
function.addExpr(new TopExpr(this, value,
function.getReturnType()));
else
function.addExpr(value);
function.println(";");
isDead = true;
/* can't break
for (Block block = this; block != null; block = block.parent)
block.canExit = true;
*/
}
void doReturn()
throws ESException
{
evalExpr();
if (function.getReturnType() != null)
function.print("return 0;");
else
function.print("return ESBase.esUndefined;");
isDead = true;
/*
for (Block block = this; block != null; block = block.parent)
block.canExit = true;
*/
}
void finish()
throws ESException
{
if (isDead)
return;
if (lastExpr != null) {
function.print("return ");
function.addExpr(lastExpr);
function.println(";");
lastExpr = null;
}
else if (hasStatementValue)
function.println("return _val0;");
else
function.println("return ESBase.esUndefined;");
}
String newIterator(ESId id, Expr expr)
throws ESException
{
evalExpr();
String iter = "iter" + function.getIter();
function.print(iter + " = ");
function.addExpr(expr);
function.println(".keys();");
return iter;
}
void evalExpr() throws ESException
{
if (lastExpr == null)
return;
function.print(function.getStatementVar() + " = ");
function.addExpr(lastExpr);
function.println(";");
lastExpr = null;
}
private static Block allocate()
{
Block block = new Block();
return block;
}
ESException error(String message)
{
return parser.lexer.error(message);
}
void free()
{
}
static {
specialNames = new HashMap();
specialNames.put(ESId.intern("Object"), "Object");
specialNames.put(ESId.intern("Date"), "Date");
specialNames.put(ESId.intern("String"), "String");
specialNames.put(ESId.intern("Number"), "Number");
specialNames.put(ESId.intern("Array"), "Array");
specialNames.put(ESId.intern("Boolean"), "Boolean");
specialNames.put(ESId.intern("Math"), "Math");
};
}