Package org.jboss.byteman.rule

Source Code of org.jboss.byteman.rule.RuleElement

/*
* 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;

import org.jboss.byteman.rule.binding.Bindings;
import org.jboss.byteman.rule.compiler.CompileContext;
import org.jboss.byteman.rule.type.TypeGroup;
import org.jboss.byteman.rule.type.Type;
import org.jboss.byteman.rule.exception.TypeException;
import org.jboss.byteman.rule.exception.CompileException;
import org.jboss.byteman.rule.exception.ExecuteException;
import org.jboss.byteman.rule.helper.HelperAdapter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Label;

import java.io.StringWriter;

/**
* generic class implemented by rule events, conditions and actions which gives them
* access to the rule context and provides them with common behaviours
*/
public abstract class RuleElement {
    protected RuleElement(Rule rule)
    {
        this.rule = rule;
    }

    protected Rule rule;

    protected TypeGroup getTypeGroup()
    {
        return rule.getTypeGroup();
    }

    protected Bindings getBindings()
    {
        return rule.getBindings();
    }

    public abstract Type typeCheck(Type expected) throws TypeException;

    public abstract Object interpret(HelperAdapter helper) throws ExecuteException;

    public abstract void compile(MethodVisitor mv, CompileContext compileContext) throws CompileException;

    protected void compileTypeConversion(Type fromType, Type toType, MethodVisitor mv, CompileContext compileContext)
            throws CompileException
    {
        // make sure we have some real work to do

        if (fromType.equals(toType)) {
            return;
        }

        if (toType.isNumeric()) {
            // do number conversion
            compileNumericConversion(fromType, toType, mv, compileContext);
        } else if (toType.isString()) {
            // do toString conversion
            compileStringConversion(fromType, toType, mv, compileContext);
        } else if (toType.isBoolean()) {
            // do toString conversion
            compileBooleanConversion(fromType, toType, mv, compileContext);
        } else {
            compileObjectConversion(fromType, toType, mv, compileContext);
        }
    }

    protected void compileNumericConversion(Type fromType, Type toType, MethodVisitor mv, CompileContext compileContext)
            throws CompileException
    {
        // fromType != toType
        boolean unbox = fromType.isObject();
        boolean box = toType.isObject();

        if (unbox) {
            // if this is not already a numeric type then generate a cast
            if (!fromType.isNumeric()) {
                compileObjectConversion(fromType, Type.NUMBER, mv, compileContext);
            }
            if (box) {
                Type midType = Type.boxType(toType);
                compileUnbox(fromType, midType, mv, compileContext);
                compileBox(midType, mv, compileContext);
            } else {
                compileUnbox(fromType, toType, mv, compileContext);
            }
        } else if (box) {
            Type midType = Type.boxType(toType);
            if (fromType != midType) {
                compilePrimitiveConversion(fromType, midType, mv, compileContext);
            }
            compileBox(toType, mv, compileContext);
        } else {
            compilePrimitiveConversion(fromType, toType, mv, compileContext);
        }
    }

    /**
     * compile code to convert a value of a boxed type to a primitive type, possibly not the immediately
     * related primitive type
     *
     * @param fromType
     * @param toType
     * @param mv
     * @param compileContext
     * @throws CompileException
     */
   protected void compileUnbox(Type fromType, Type toType, MethodVisitor mv, CompileContext compileContext)
            throws CompileException
    {
        // we either have a Boolean, a Character or a Number for fromType
        if (fromType == Type.BOOLEAN) {
            assert toType == Type.Z;
            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z");
        } else if (fromType == Type.CHARACTER) {
            // obtain the underlying char then massage it to the correct bit format
            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C");
            compilePrimitiveConversion(Type.C, toType, mv, compileContext);
        } else {
            // we have a numeric type so call the relevant conversion method
            if (toType == Type.B) {
                mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Number", "byteValue", "()B");
            } else if (toType == Type.S){
                mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Number", "shortValue", "()S");
            } else if (toType == Type.C){
                mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Number", "intValue", "()I");
                // now convert to char, dropping any sign extension
                mv.visitIntInsn(Opcodes.ISHL, 16);
                mv.visitIntInsn(Opcodes.LSHR, 16);
            } else if (toType == Type.I){
                mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Number", "intValue", "()I");
            } else if (toType == Type.J){
                mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Number", "longValue", "()J");
                compileContext.addStackCount(1);
            } else if (toType == Type.F){
                mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Number", "floatValue", "()F");
            } else {
                assert toType == Type.D;
                compileContext.addStackCount(1);
                mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Number", "doubleValue", "()D");
            }
        }
    }

    /**
     * box a value belonging to a primitive type
     * @param toType
     * @param mv
     * @param compileContext
     * @throws CompileException
     */
    protected void compileBox(Type toType, MethodVisitor mv, CompileContext compileContext)
            throws CompileException
    {
        // use the static methods on the class  to do conversions -- that means the class gets a chance
        // to reuse cached values
        if (toType == Type.BOOLEAN) {
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;");
        } else if (toType == Type.BYTE) {
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
        } else if (toType == Type.SHORT){
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
        } else if (toType == Type.CHARACTER){
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;");
        } else if (toType == Type.INTEGER) {
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
        } else if (toType == Type.LONG) {
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
            compileContext.addStackCount(-1);
        } else if (toType == Type.FLOAT) {
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
        } else if (toType == Type.DOUBLE) {
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
            compileContext.addStackCount(-1);
        }
    }

    protected void compileStringConversion(Type fromType, Type toType, MethodVisitor mv, CompileContext compileContext)
            throws CompileException
    {
        assert toType == Type.STRING;
        if (fromType.isObject() || fromType.isArray() || (fromType.isNumeric() && !fromType.isPrimitive())) {
            // use the toString method if the object is non null otherwise just replace it with null
            Label elseLabel = new Label();
            Label endLabel = new Label();
            // if (object == null)
            mv.visitInsn(Opcodes.DUP);
            mv.visitJumpInsn(Opcodes.IFNONNULL, elseLabel);
            // then string = "null"
            mv.visitInsn(Opcodes.POP);
            mv.visitInsn(Opcodes.ACONST_NULL);
            mv.visitJumpInsn(Opcodes.GOTO, endLabel);
            // else string = object.toString()
            mv.visitLabel(elseLabel);
            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "toString", "()Ljava/lang/String;");
            mv.visitLabel(endLabel);
        } else if (fromType == Type.Z) {
            // use the toString method
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Boolean", "toString", "(Z)Ljava/lang/String;");
        } else if (fromType == Type.B) {
            // use the toString method
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Byte", "toString", "(B)Ljava/lang/String;");
        } else if (fromType == Type.S) {
            // use the toString method
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Short", "toString", "(S)Ljava/lang/String;");
        } else if (fromType == Type.C) {
            // use the toString method
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Character", "toString", "(C)Ljava/lang/String;");
        } else if (fromType == Type.I) {
            // use the toString method
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Integer", "toString", "(I)Ljava/lang/String;");
        } else if (fromType == Type.J) {
            // use the toString method
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Long", "toString", "(J)Ljava/lang/String;");
            compileContext.addStackCount(-1);
        } else if (fromType == Type.F) {
            // use the toString method
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Float", "toString", "(F)Ljava/lang/String;");
        } else if (fromType == Type.D) {
            // use the toString method
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Double", "toString", "(D)Ljava/lang/String;");
            compileContext.addStackCount(-1);
        }
    }

    /**
     * compile code to convert a numeric or character primitive to a numeric or character primitive
     * @param fromType
     * @param toType
     * @param mv
     * @param compileContext
     * @throws CompileException
     */
    protected void compilePrimitiveConversion(Type fromType, Type toType, MethodVisitor mv, CompileContext compileContext)
            throws CompileException
    {
        if (fromType == Type.B || fromType == Type.S || fromType == Type.I) {
            if (toType == Type.B) {
                mv.visitInsn(Opcodes.I2B);
            } else if (toType == Type.S) {
                mv.visitInsn(Opcodes.I2S);
            } else if (toType == Type.C) {
                mv.visitInsn(Opcodes.I2C);
            } else if (toType == Type.I) {
                // nothing to do
            } else if (toType == Type.J) {
                mv.visitInsn(Opcodes.I2L);
                compileContext.addStackCount(1);
            } else if (toType == Type.F) {
                mv.visitInsn(Opcodes.I2F);
            } else if (toType == Type.D) {
                mv.visitInsn(Opcodes.I2D);
                compileContext.addStackCount(1);
            }
        } else if (fromType == Type.C) {
            // convert to the relevant numeric size
            if (toType == Type.B) {
                mv.visitInsn(Opcodes.I2B);
            } else if (toType == Type.S) {
                mv.visitInsn(Opcodes.I2S);
            } else if (toType == Type.C) {
                // nothing to do
            } else if (toType == Type.I) {
                // nothing to do
            } else  if (toType == Type.J) {
                mv.visitInsn(Opcodes.I2L);
                compileContext.addStackCount(1);
            } else if (toType == Type.F) {
                mv.visitInsn(Opcodes.I2F);
            } else if (toType == Type.D) {
                mv.visitInsn(Opcodes.I2D);
                compileContext.addStackCount(1);
            }
        } else if (fromType == Type.J) {
            if (toType == Type.B || toType ==  Type.S || toType == Type.I || toType == Type.C) {
                mv.visitInsn(Opcodes.L2I);
                compileContext.addStackCount(-1);
            } else if (toType == Type.J) {
                // nothing to do
            } else if (toType == Type.F) {
                mv.visitInsn(Opcodes.L2F);
                compileContext.addStackCount(-1);
            } else if (toType == Type.D) {
                mv.visitInsn(Opcodes.L2D);
            }
        } else if (fromType == Type.F) {
            if (toType == Type.B) {
                mv.visitInsn(Opcodes.F2I);
                mv.visitInsn(Opcodes.I2B);
            } else if (toType == Type.S) {
                mv.visitInsn(Opcodes.F2I);
                mv.visitInsn(Opcodes.I2S);
            } else if (toType == Type.C) {
                mv.visitInsn(Opcodes.F2I);
                mv.visitInsn(Opcodes.I2C);
            } else if (toType == Type.I) {
                mv.visitInsn(Opcodes.F2I);
            } else if (toType == Type.J) {
                mv.visitInsn(Opcodes.F2L);
                compileContext.addStackCount(1);
            } else if (toType == Type.F) {
                // nothing to do
            } else if (toType == Type.D) {
                mv.visitInsn(Opcodes.F2D);
                compileContext.addStackCount(1);
            }
        } else if (fromType == Type.D) {
            if (toType == Type.B) {
                mv.visitInsn(Opcodes.D2I);
                mv.visitInsn(Opcodes.I2B);
                compileContext.addStackCount(-1);
            } else if (toType == Type.S) {
                mv.visitInsn(Opcodes.D2I);
                mv.visitInsn(Opcodes.I2S);
                compileContext.addStackCount(-1);
            } else if (toType == Type.C) {
                mv.visitInsn(Opcodes.D2I);
                mv.visitInsn(Opcodes.I2C);
                compileContext.addStackCount(-1);
            } else if (toType == Type.I) {
                mv.visitInsn(Opcodes.D2I);
                compileContext.addStackCount(-1);
            } else if (toType == Type.J) {
                mv.visitInsn(Opcodes.D2L);
            } else if (toType == Type.F) {
                mv.visitInsn(Opcodes.D2F);
                compileContext.addStackCount(-1);
            } else if (toType == Type.D) {
                // nothing to do
            }
        }
    }

    protected void compileBooleanConversion(Type fromType, Type toType, MethodVisitor mv, CompileContext compileContext)
            throws CompileException
    {
        if (toType == Type.Z) {
            assert fromType == Type.BOOLEAN;
            compileUnbox(fromType, toType, mv, compileContext);
        } else {
            assert toType == Type.BOOLEAN;
            assert fromType == Type.Z;
            compileBox(toType, mv, compileContext);
        }
    }

    protected void compileObjectConversion(Type fromType, Type toType, MethodVisitor mv, CompileContext compileContext)
            throws CompileException
    {
        // ensure any primitive type is boxed before we go any further

        if (fromType.isPrimitive()) {
            Type boxType = Type.boxType(fromType);
            compileBox(boxType, mv, compileContext);
            fromType = boxType;
        }

        if (toType.isAssignableFrom(fromType)) {
            // special case -- isAssignableFrom says yes if we are trying to assign to a String but
            // we may still need to do a toString cobversion all the same
            if (toType == Type.STRING && fromType != Type.STRING) {
                compileStringConversion(fromType, toType, mv, compileContext);
            } else {
                // nothing more to do
            }
        } else {
            // this happens when we downcast a bound variable from Object to the variable's type
            assert fromType.isAssignableFrom(toType);
            mv.visitTypeInsn(Opcodes.CHECKCAST, toType.getInternalName(false, true));
        }
    }

    public String toString()
    {
        StringWriter stringWriter = new StringWriter();
        writeTo(stringWriter);
        return stringWriter.toString();
    }

    public abstract void writeTo(StringWriter stringWriter);
}
TOP

Related Classes of org.jboss.byteman.rule.RuleElement

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.