/*
* JBoss, Home of Professional Open Source
* Copyright 2008-10 Red Hat and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*
* @authors Andrew Dinn
*/
package org.jboss.byteman.rule;
import org.jboss.byteman.rule.compiler.CompileContext;
import org.jboss.byteman.rule.type.Type;
import org.jboss.byteman.rule.expression.ExpressionHelper;
import org.jboss.byteman.rule.expression.Expression;
import org.jboss.byteman.rule.expression.ReturnExpression;
import org.jboss.byteman.rule.expression.ThrowExpression;
import org.jboss.byteman.rule.grammar.ECATokenLexer;
import org.jboss.byteman.rule.grammar.ECAGrammarParser;
import org.jboss.byteman.rule.grammar.ParseNode;
import static org.jboss.byteman.rule.grammar.ParseNode.*;
import org.jboss.byteman.rule.exception.ParseException;
import org.jboss.byteman.rule.exception.TypeException;
import org.jboss.byteman.rule.exception.ExecuteException;
import org.jboss.byteman.rule.exception.CompileException;
import org.jboss.byteman.rule.helper.HelperAdapter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.util.List;
import java.util.ArrayList;
import java.io.StringWriter;
import java.io.StringReader;
import java_cup.runtime.Symbol;
/**
* class which represents a rule action comprising a void expression
*/
public class Action extends RuleElement
{
public static Action create(Rule rule, ParseNode actionTree)
throws TypeException
{
Action action = new Action(rule, actionTree);
return action;
}
public static Action create(Rule rule, String text)
throws ParseException, TypeException
{
if ("".equals(text)) {
return new Action(rule);
}
String fullText = "BIND NOTHING IF TRUE DO \n" + text;
try {
ECATokenLexer lexer = new ECATokenLexer(new StringReader(text));
ECAGrammarParser parser = new ECAGrammarParser(lexer);
Symbol parse = parser.parse();
ParseNode parseTree = (ParseNode)parse.value;
ParseNode actionTree = (ParseNode)parseTree.getChild(3);
Action action = new Action(rule, actionTree);
return action;
} catch (Exception e) {
throw new ParseException("org.jboss.byteman.rule.Action : error parsing action\n" + text);
}
}
protected Action(Rule rule, ParseNode actionTree) throws TypeException
{
super(rule);
if (actionTree.getTag() == NOTHING) {
this.action = new ArrayList<Expression>();
} else {
this.action = ExpressionHelper.createExpressionList(rule, this.getBindings(), actionTree, Type.VOID);
for (Expression expr : action) {
// check bindings
expr.bind();
}
}
}
protected Action(Rule rule)
{
super(rule);
this.action = null;
}
public Type typeCheck(Type expected) throws TypeException {
// expected must be Type.VOID
if (action != null) {
for (Expression expr : action) {
expr.typeCheck(Type.VOID);
}
}
return Type.VOID;
}
public void compile(MethodVisitor mv, CompileContext compileContext) throws CompileException {
int currentStack = compileContext.getStackCount();
for (Expression expr : action) {
expr.compile(mv, compileContext);
Type resultType = expr.getType();
// return and throw expressions don't actually leave a value on the stack even
// though they may have a non-VOID value type
boolean maybePop = !(expr instanceof ReturnExpression || expr instanceof ThrowExpression);
if (maybePop && resultType != Type.VOID) {
int expected = (resultType.getNBytes() > 4 ? 2 : 1);
if (expected == 1) {
mv.visitInsn(Opcodes.POP);
compileContext.addStackCount(-1);
} else if (expected == 2) {
mv.visitInsn(Opcodes.POP2);
compileContext.addStackCount(-2);
}
}
}
// check original stack height has been restored
if (compileContext.getStackCount() != currentStack) {
throw new CompileException("Action.compile : invalid stack height " + compileContext.getStackCount() + " expecting " + currentStack);
}
}
public Object interpret(HelperAdapter helper)
throws ExecuteException
{
if (action != null) {
for (Expression expr : action) {
expr.interpret(helper);
}
}
return null;
}
public void writeTo(StringWriter stringWriter)
{
if (action == null || action.size() == 0) {
stringWriter.write("DO NOTHING");
} else {
String prefix = "DO ";
for (Expression expr : action) {
stringWriter.write(prefix);
expr.writeTo(stringWriter);
prefix = ";\n ";
}
}
stringWriter.write("\n");
}
private List<Expression> action;
}