All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jboss.byteman.rule.expression.ArrayExpression Maven / Gradle / Ivy

Go to download

The Byteman agent jar contains the implementation of the Byteman java agent, including the bytecode transformer, rule parser, type checker and execution engine and the agent listener.

There is a newer version: 4.0.24
Show newest version
/*
* 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.expression;

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.util.List;
import java.util.Iterator;
import java.io.StringWriter;
import java.lang.reflect.Array;

/**
 * an expression which identifies an array reference.
 */

public class ArrayExpression extends AssignableExpression
{

    public ArrayExpression(Rule rule, Type type, ParseNode token, Expression arrayRef, List idxList)
    {
        super(rule, type, token);
        this.arrayRef = arrayRef;
        this.idxList = idxList;
    }

    /**
     * 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
     *
     * @throws TypeException if any variable is missing or has the wrong type
     */
    public void bind() throws TypeException
    {
        // we  have to make sure that any names occuring in the array reference are bound
        // and that the index expressions contain valid bindings
        arrayRef.bind();

        Iterator iterator = idxList.iterator();

        while (iterator.hasNext()) {
            iterator.next().bind();
        }
    }

    public Type typeCheck(Type expected) throws TypeException {
        typeCheckAny();
        if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
            throw new TypeException("ArrayExpression.typeCheck : invalid expected result type " + expected.getName() + getPos());
        }

        return type;
    }

    public Type typeCheckAssign(Type expected) throws TypeException
    {
        typeCheckAny();
        if (Type.dereference(expected).isDefined() && !type.isAssignableFrom(expected)) {
            throw new TypeException("ArrayExpression.typeCheckAssign : invalid value type " + expected.getName() + " for array assignment " + getPos());
        }

        return type;
    }

    private void typeCheckAny() throws TypeException
    {
        Type arrayType = arrayRef.typeCheck(Type.UNDEFINED);
        Type nextType = arrayType;
        for (Expression expr : idxList) {
            if (!nextType.isArray()) {
                throw new TypeException("ArrayExpression.typeCheck : invalid type for array dereference " + nextType.getName() + getPos());
            }
            nextType = nextType.getBaseType();
            expr.typeCheck(Type.N);
        }
        type = nextType;
    }

    public Object interpret(HelperAdapter helper) throws ExecuteException {
        // evaluate the array expression then evaluate each index expression in turn and
        // dereference to access the array element

        try {
            Object value = arrayRef.interpret(helper);
            Type nextType = arrayRef.getType();
            for (Expression expr : idxList) {
                int idx = ((Number) expr.interpret(helper)).intValue();
                if (value == null) {
                    throw new ExecuteException("ArrayExpression.interpret : attempted array indirection through null value " + arrayRef.token.getText() + getPos());
                }
                value = Array.get(value, idx);
                nextType = nextType.getBaseType();
            }

            return value;
        } catch (ExecuteException e) {
            throw e;
        } catch (IllegalArgumentException e) {
            throw new ExecuteException("ArrayExpression.interpret : failed to evaluate expression " + arrayRef.token.getText() + getPos(), e);
        } catch (ArrayIndexOutOfBoundsException e) {
            throw new ExecuteException("ArrayExpression.interpret : invalid index for array " + arrayRef.token.getText() + getPos(), e);
        } catch (ClassCastException e) {
            throw new ExecuteException("ArrayExpression.interpret : invalid index dereferencing array " + arrayRef.token.getText() + getPos(), e);
        } catch (Exception e) {
            throw new ExecuteException("ArrayExpression.interpret : unexpected exception dereferencing array " + arrayRef.token.getText() + getPos(), e);
        }
    }

    public void compile(MethodVisitor mv, CompileContext compileContext) throws CompileException
    {
        // make sure we are at the right source line
        compileContext.notifySourceLine(line);

        Type valueType = arrayRef.getType().getBaseType();
        int currentStack = compileContext.getStackCount();
        int expected = 0;

        // compile load of array reference -- adds 1 to stack height
        arrayRef.compile(mv, compileContext);
        // for each index expression compile the expression and the do an array load
        Iterator iterator = idxList.iterator();

        while (iterator.hasNext()) {
            Expression idxExpr = iterator.next();
            // compile expression index -- adds 1 to height
            idxExpr.compile(mv, compileContext);
            // make sure the index is an integer
            compileTypeConversion(idxExpr.getType(), Type.I, mv, compileContext);

            if (valueType.isObject() || valueType.isArray()) {
                // compile load object - pops 2 and adds 1
                mv.visitInsn(Opcodes.AALOAD);
                expected = 1;
            } else if (valueType == Type.Z || valueType == Type.B) {
                // compile load byte - pops 2 and adds 1
                mv.visitInsn(Opcodes.BALOAD);
                expected = 1;
            } else if (valueType == Type.S) {
                // compile load short - pops 2 and adds 1
                mv.visitInsn(Opcodes.SALOAD);
                expected = 1;
            } else if (valueType == Type.C) {
                // compile load char - pops 2 and adds 1
                mv.visitInsn(Opcodes.CALOAD);
                expected = 1;
            } else if (valueType == Type.I) {
                // compile load int - pops 2 and adds 1
                mv.visitInsn(Opcodes.IALOAD);
                expected = 1;
            } else if (valueType == Type.J) {
                // compile load long - pops 2 and adds 2
                mv.visitInsn(Opcodes.LALOAD);
                expected = 2;
            } else if (valueType == Type.F) {
                // compile load float - pops 2 and adds 1
                mv.visitInsn(Opcodes.FALOAD);
                expected = 1;
            } else if (valueType == Type.D) {
                // compile load double - pops 2 and adds 2
                mv.visitInsn(Opcodes.DALOAD);
                expected = 2;
            }
            compileContext.addStackCount(expected - 2);
            if (iterator.hasNext()) {
                assert valueType.isArray();
                valueType =valueType.getBaseType();
            }
        }

        // check stack height
        if (compileContext.getStackCount() != currentStack + expected) {
            throw new CompileException("ArrayExpression.compile : invalid stack height " + compileContext.getStackCount() + " expecting " + (currentStack + expected));
        }

        // we needed room for an aray and an index or for a one or two word result
        // but the recursive evaluations will have made sure the max stack is big enough
        // so there is no need to update the maximum stack height
    }

    public void writeTo(StringWriter stringWriter) {
        arrayRef.writeTo(stringWriter);
        for (Expression expr : idxList) {
            stringWriter.write("[");
            expr.writeTo(stringWriter);
            stringWriter.write("]");
        }
    }

    Expression arrayRef;
    List idxList;

    @Override
    public Object interpretAssign(HelperAdapter helper, Object value) throws ExecuteException {
        // evaluate the array expression then evaluate each index expression in turn up to the last one
        // and dereference to access the array at that index. finally evaluate the final index expression
        // and use it as the position at which to install the supplied value in the dereferenced array

        try {
            Object array = arrayRef.interpret(helper);
            Type nextType = arrayRef.getType();
            int count = idxList.size() - 1;
            for (Expression expr : idxList) {
                int idx = ((Number) expr.interpret(helper)).intValue();
                if (array == null) {
                    throw new ExecuteException("ArrayExpression.interpret : attempted array indirection through null value " + arrayRef.token.getText() + getPos());
                }
                if (count-- >  0)  {
                    array = Array.get(array, idx);
                    nextType = nextType.getBaseType();
                } else {
                    Array.set(array, idx, value);
                }
            }
            return value;
        } catch (ExecuteException e) {
            throw e;
        } catch (IllegalArgumentException e) {
            throw new ExecuteException("ArrayExpression.interpret : failed to evaluate expression " + arrayRef.token.getText() + getPos(), e);
        } catch (ArrayIndexOutOfBoundsException e) {
            throw new ExecuteException("ArrayExpression.interpret : invalid index for array " + arrayRef.token.getText() + getPos(), e);
        } catch (ClassCastException e) {
            throw new ExecuteException("ArrayExpression.interpret : invalid index dereferencing array " + arrayRef.token.getText() + getPos(), e);
        } catch (Exception e) {
            throw new ExecuteException("ArrayExpression.interpret : unexpected exception dereferencing array " + arrayRef.token.getText() + getPos(), e);
        }
    }

    @Override
    public void compileAssign(MethodVisitor mv, CompileContext compileContext) throws CompileException {
        // make sure we are at the right source line
        compileContext.notifySourceLine(line);

        Type valueType = arrayRef.getType().getBaseType();
        int currentStack = compileContext.getStackCount();
        boolean isTwoWords = (valueType.getNBytes() > 4);
        int toPop = 0;
        int size = (isTwoWords ? 2 : 1);

        // value to be assigned is TOS and will already be coerced to the correct value type
        // copy it so we can install the copy and leave the original as a a return value on the stack
        if (isTwoWords) {
            // [... val1 val2 ==> ... val1 val2 val1 val2]
            mv.visitInsn(Opcodes.DUP2);
        } else {
            // [... val ==> ... val val]
            mv.visitInsn(Opcodes.DUP);
        }
        compileContext.addStackCount(size);

        // compile load of array reference -- adds 1 to stack height
        arrayRef.compile(mv, compileContext);
        // for each index expression compile the expression and the do an array load
        Iterator iterator = idxList.iterator();

        while (iterator.hasNext()) {
            Expression idxExpr = iterator.next();
            if (iterator.hasNext()) {
                // dereference the array to get an embedded array
                // compile expression index -- adds 1 to height
                idxExpr.compile(mv, compileContext);
                // make sure the index is an integer
                compileTypeConversion(idxExpr.getType(), Type.I, mv, compileContext);
                // fetch embedded array pop 2 and add 1
                mv.visitInsn(Opcodes.AALOAD);
                compileContext.addStackCount(-1);
                valueType = valueType.getBaseType();
            } else {
                if (isTwoWords) {
                    // stack is [..., val1, val2, val1, val2, aref ] and we want [..., val1, val2, aref, val1, val2 ]
                    mv.visitInsn(Opcodes.DUP_X2);     // ==>  [..., val1, val2, aref. val1, val2, aref ]
                    compileContext.addStackCount(1);
                    mv.visitInsn(Opcodes.POP);        // ==> [..., val1, val2, aref. val1, val2 ]
                    compileContext.addStackCount(-1);
                } else {
                    // stack is [..., val, val, aref ] and we want [..., val, aref, val ]
                    mv.visitInsn(Opcodes.SWAP);
                }
                // compile expression index -- adds 1 to height
                idxExpr.compile(mv, compileContext);
                // make sure the index is an integer
                compileTypeConversion(idxExpr.getType(), Type.I, mv, compileContext);
                if (isTwoWords) {
                    // stack is [..., val1, val2, aref, val1, val2, idx] and we want [..., val1, val2, aref, idx, val1, val2 ]
                    mv.visitInsn(Opcodes.DUP_X2);     // ==> [..., val1, val2, aref, idx, val1, val2, idx]
                    compileContext.addStackCount(1);
                    mv.visitInsn(Opcodes.POP);        // ==> [..., val1, val2, aref, idx, val1, val2 ]
                    compileContext.addStackCount(-1);
                } else {
                    // stack is [..., val, aref, val, idx] and we want [..., val, aref, idx, val ]
                    mv.visitInsn(Opcodes.SWAP);
                }
                // now we can do the array store
                if (valueType.isObject() || valueType.isArray()) {
                    // compile load object - pops 3
                    mv.visitInsn(Opcodes.AASTORE);
                    toPop =- 3;
                } else if (valueType == Type.Z || valueType == Type.B) {
                    // compile load byte - pops 3
                    mv.visitInsn(Opcodes.BASTORE);
                    toPop = -3;
                } else if (valueType == Type.S) {
                    // compile load short - pops 3
                    mv.visitInsn(Opcodes.SASTORE);
                    toPop = -3;
                } else if (valueType == Type.C) {
                    // compile load char - pops 3
                    mv.visitInsn(Opcodes.CASTORE);
                    toPop = -3;
                } else if (valueType == Type.I) {
                    // compile load int - pops 3
                    mv.visitInsn(Opcodes.IASTORE);
                    toPop = -3;
                } else if (valueType == Type.J) {
                    // compile load long - pops 4
                    mv.visitInsn(Opcodes.LASTORE);
                    toPop = -4;
                } else if (valueType == Type.F) {
                    // compile load float - pops 3
                    mv.visitInsn(Opcodes.FASTORE);
                    toPop = -3;
                } else if (valueType == Type.D) {
                    // compile load double - pops 4
                    mv.visitInsn(Opcodes.DASTORE);
                    toPop = -4;
                }
                compileContext.addStackCount(toPop);
                if (iterator.hasNext()) {
                    assert valueType.isArray();
                    valueType =valueType.getBaseType();
                }
            }
        }

        // check stack height
        if (compileContext.getStackCount() != currentStack) {
            throw new CompileException("ArrayExpression.compile : invalid stack height " + compileContext.getStackCount() + " expecting " + currentStack);
        }

        // we needed room for an aray and an index or for a one or two word result
        // but the recursive evaluations will have made sure the max stack is big enough
        // so there is no need to update the maximum stack height
    }

    @Override
    public void bindAssign() throws TypeException {
        bind();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy