Package org.jboss.byteman.rule.expression

Source Code of org.jboss.byteman.rule.expression.DollarExpression

/*
* JBoss, Home of Professional Open Source
* Copyright 2008, Red Hat Middleware LLC, 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.expression;

import org.jboss.byteman.rule.binding.Binding;
import org.jboss.byteman.rule.binding.Bindings;
import org.jboss.byteman.rule.compiler.CompileContext;
import org.jboss.byteman.rule.type.Type;
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.Rule;
import org.jboss.byteman.rule.helper.HelperAdapter;
import org.jboss.byteman.rule.grammar.ParseNode;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import java.io.StringWriter;

/**
* an expression which refers either to a builtin variable or to a bound parameter of the
* triggering method for an ECA rule. builtin variables are written as a dollar sign followed
* by a leading alpha-underscore, trailing alpha-numeric-underscore string. bound parameters are
* written as a dollar sign followed by a non-negativeinteger parameter index
*
* e.g. if the rule applies to method foo.bar(int baz, Mumble mumble) then an occurrence of $2
* appearing as an expression in a rule would have type Mumble and evaluate to the value of mumble
* at the point when the rule was triggered.
*
* At present there are no special variables but we may need to add some later
*/
public class DollarExpression extends AssignableExpression
{
    /**
     * constructor for param bindings or special bindings
     * @param rule
     * @param type
     * @param token
     * @param index
     */
    public DollarExpression(Rule rule, Type type, ParseNode token, int index)
    {
        super(rule, type, token);
        if (index == HELPER_IDX) {
            name = "$$";
        } else if (index == RETURN_VALUE_IDX){
            name = "$!";
        } else if (index == THROWABLE_VALUE_IDX){
            name = "$^";
        } else if (index == PARAM_COUNT_IDX){
            name = "$#";
        } else if (index == PARAM_ARRAY_IDX){
            name = "$*";
        } else if (index == INVOKE_PARAM_ARRAY_IDX){
            name = "$@";
        } else {
            name = "$" + Integer.toString(index);
        }
        this.index = index;
        this.binding = null;
    }

    /**
     * constructor for local var bindings
     * @param rule
     * @param type
     * @param token
     * @param name
     */
    public DollarExpression(Rule rule, Type type, ParseNode token, String name)
    {
        super(rule, type, token);
        this.index = LOCAL_IDX;
        this.name = "$" + name;
        this.binding = null;
    }

    /**
     * verify that variables mentioned in this expression are actually available in the supplied
     * bindings list and infer/validate the type of this expression or its subexpressions
     * where possible

     * @return true if all variables in this expression are bound and no type mismatches have
     * been detected during inference/validation.
     */

    public void  bind() throws TypeException
    {
        bind(false);
    }

    /**
     * verify that variables mentioned in this expression are actually available in the supplied
     * bindings list. infer/validate the type of this expression or its subexpressions
     * where possible

     * @return true if all variables in this expression are bound and non-final and no type mismatches have
     * been detected during inference/validation.
     */

    public void bindAssign() throws TypeException
    {
        if (name.equals("$0") || name.equals("$this")){
            throw new TypeException("invalid assignment to final variable " + name + getPos());
        }
        if (name.equals("$^")){
            // TODO -- see if it is possible to allow update to the throwable variable
            throw new TypeException("invalid assignment to throwable variable " + name + getPos());
        }
        if (name.equals("$#")){
            throw new TypeException("invalid assignment to param count variable " + name + getPos());
        }
        if (name.equals("$*")){
            throw new TypeException("invalid assignment to param array variable " + name + getPos());
        }
        if (name.equals("$@")){
            throw new TypeException("invalid assignment to invoke param array variable " + name + getPos());
        }
        bind(true);
    }

    public void bind(boolean isUpdateable) throws TypeException
    {
        // ensure that there is a binding in the bindings set for this parameter
        // we will type check the binding later

        Bindings bindings = getBindings();

        binding = bindings.lookup(name);

        if (binding == null) {
            binding = new Binding(rule, name, null);
            bindings.append(binding);
        }
       
        if (isUpdateable) {
            binding.setUpdated();
        }
    }

    public Type typeCheck(Type expected) throws TypeException {
        // if the associated binding is an alias then dereference it

        if (binding.isAlias()) {
            binding = binding.getAlias();
        }

        type = binding.getType();
       
        if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
            throw new TypeException("DollarExpression.typeCheck : invalid expected type " + expected.getName() + " for bound parameter " + name + getPos());           
        }
        return type;
    }

    public Object interpret(HelperAdapter helper) throws ExecuteException
    {
        return helper.getBinding(binding.getName());
    }

    public void compile(MethodVisitor mv, CompileContext compileContext) throws CompileException
    {
        String targetName = binding.getName();

        int currentStack = compileContext.getStackCount();
        int expected = (type.getNBytes() > 4 ? 2 : 1);

        if (index == HELPER_IDX) {
            // reference to the current helper so just stack this
            mv.visitVarInsn(Opcodes.ALOAD, 0);
            compileContext.addStackCount(1);
        } else {
            // stack the current helper
            // stack the name for the variable
            // call the getBinding method
            mv.visitVarInsn(Opcodes.ALOAD, 0);
            mv.visitLdcInsn(targetName);
            compileContext.addStackCount(2);
            mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type.internalName(HelperAdapter.class), "getBinding", "(Ljava/lang/String;)Ljava/lang/Object;");
            compileContext.addStackCount(-1);
            // perform any necessary type conversion
            if (type.isPrimitive()) {
                // cast down to the boxed type then do an unbox
                Type boxType = Type.boxType(type);
                compileObjectConversion(Type.OBJECT, boxType, mv, compileContext);
                compileUnbox(boxType, type,  mv, compileContext);
            } else {
                // cast down to the required type
                compileObjectConversion(Type.OBJECT, type, mv, compileContext);
            }
        }

        // ensure we have only increased the stack by the return value size
        if (compileContext.getStackCount() != currentStack + expected) {
            throw new CompileException("DollarExpression.compile : invalid stack height " + compileContext.getStackCount() + " expecting " + (currentStack + expected));
        }

    }

    @Override
    public Object interpretAssign(HelperAdapter helperAdapter, Object value) throws ExecuteException
    {
        helperAdapter.setBinding(binding.getName(), value);
        return value;
    }

    @Override
    public void compileAssign(MethodVisitor mv, CompileContext compileContext) throws CompileException
    {
        String targetName = binding.getName();

        int currentStack = compileContext.getStackCount();
        int size = ((type.getNBytes() > 4) ? 2 : 1);

        if (index == HELPER_IDX) {
            // not allowed to reassign the helper binding
            throw new CompileException("DollarExpression.compileAssign : invalid assignment to helper binding $$");
        } else {
            // value to be assigned is TOS and will already be coerced to the correct value type
            // copy it so we leave it as a a return value on the stack
            if (size == 2) {
                mv.visitInsn(Opcodes.DUP2);
            } else {
                mv.visitInsn(Opcodes.DUP);
            }
            // stack the current helper then insert it below the value
            mv.visitVarInsn(Opcodes.ALOAD, 0);
            if (size == 2) {
                // use a DUP_X2 to push a copy below the value then pop the redundant value
                mv.visitInsn(Opcodes.DUP_X2);
                mv.visitInsn(Opcodes.POP);
            } else {
                // we can just swap the two values
                mv.visitInsn(Opcodes.SWAP);
            }
            // stack the name for the variable and swap below the value
            mv.visitLdcInsn(targetName);
            if (size == 2) {
                // use a DUP_X2 to push a copy below the value then pop the redundant value
                mv.visitInsn(Opcodes.DUP_X2);
                // this is the high water mark
                // at this point the stack has gone from [ .. val1 val2]  to [.. val1 val2 helper name val1 val2 name]
                compileContext.addStackCount(5);
                mv.visitInsn(Opcodes.POP);
                compileContext.addStackCount(-1);
            } else {
                // this is the high water mark
                // at this point the stack has gone from [ .. val]  to [.. val helper val name]
                compileContext.addStackCount(3);
                // we can just swap the two values
                mv.visitInsn(Opcodes.SWAP);
            }
            // ensure we have an object
            compileObjectConversion(type, Type.OBJECT, mv, compileContext);

            // call the setBinding method
            mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type.internalName(HelperAdapter.class), "setBinding", "(Ljava/lang/String;Ljava/lang/Object;)V");

            // the call will remove 3 from the stack height
            compileContext.addStackCount(-3);

            // ok, the stack height should be as it was
            if (compileContext.getStackCount() != currentStack) {
                throw new CompileException("variable.compileAssignment : invalid stack height " + compileContext.getStackCount() + " expecting " + currentStack);
            }
        }
    }

    public void writeTo(StringWriter stringWriter) {
        stringWriter.write(name);
    }

    private String name;
    /**
     * index is positive or zero if this is a reference to a method param and negative if this is a reference to
     * the current helper, the return value on the stack in an AT EXIT rule or a local or BIND variable
     */
    private int index;

    private Binding binding;

    /**
     * index of $$ variable which is bound to the current helper instance
     */
    public final static int HELPER_IDX = -1;
    /**
     * index for any variable introduced in a BINDS clause
     */
    public final static int BIND_IDX = -2;
    /**
     * index for any local variable which must be further identified via its name
     */
    public final static int LOCAL_IDX = -3;
    /**
     * index of $! variable which is bound to the current return value on stack in AT RETURN rule
     */
    public final static int RETURN_VALUE_IDX = -4;
    /**
     * index of $@ variable which is bound to the current throwable on stack in AT THROW rule
     */
    public final static int THROWABLE_VALUE_IDX = -5;
    /**
     * index of $# variable which is bound to the count of number of trigger method params
     */
    public final static int PARAM_COUNT_IDX = -6;
    /**
     * index of $* variable which is bound to an array of the trigger method params
     */
    public final static int PARAM_ARRAY_IDX = -7;
    /**
     * index of $@ variable which is bound to an array of the invoked method params in an AT INVOKE rule
     */
    public final static int INVOKE_PARAM_ARRAY_IDX = -8;
}
TOP

Related Classes of org.jboss.byteman.rule.expression.DollarExpression

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.