org.jboss.byteman.rule.compiler.CompileContext Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of byteman Show documentation
Show all versions of byteman Show documentation
The byteman jar merges the byteman-agent jar contents with those of the
byteman-jigsaw and byteman-layer jars as a mutli-release jar. The contents
of the latter two jars are installed under META-INF/versions/9 ensuring
that they are only linked when Byteman is deployed on a JDK9+ JVM
/*
* JBoss, Home of Professional Open Source
* Copyright 2010 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.compiler;
import org.jboss.byteman.rule.type.Type;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* class which retains compiler state during recursive compilation of rule expressions to bytecode
*/
public class CompileContext
{
private int sourceLine;
private int stackCount;
private int stackMax;
private int localCount;
private int localMax;
private MethodVisitor mv;
CompileContext(MethodVisitor mv)
{
sourceLine = -1;
stackCount = stackMax = localCount = localMax = 0;
this.mv = mv;
}
public int getSourceLine()
{
return sourceLine;
}
public int getStackCount()
{
return stackCount;
}
public int getLocalCount()
{
return localCount;
}
public int getStackMax()
{
return stackMax;
}
public int getLocalMax()
{
return localMax;
}
public void addStackCount(int count)
{
stackCount += count;
if (stackCount > stackMax) {
stackMax = stackCount;
}
}
public void addLocalCount(int count)
{
localCount += count;
if (localCount > localMax) {
localMax = localCount;
}
}
public void notifySourceLine(int line)
{
if (line > sourceLine) {
sourceLine = line;
Label label = new Label();
mv.visitLabel(label);
mv.visitLineNumber(sourceLine, label);
}
}
public void notifySourceEnd()
{
Label label = new Label();
mv.visitLabel(label);
mv.visitLineNumber(sourceLine + 1, label);
}
public void compileTypeConversion(Type fromType, Type toType)
{
// make sure we have some real work to do
if (fromType.equals(toType)) {
return;
}
if (toType.isNumeric()) {
// do number conversion
compileNumericConversion(fromType, toType);
} else if (toType.isString()) {
// do toString conversion
compileStringConversion(fromType, toType);
} else if (toType.isBoolean()) {
// do toString conversion
compileBooleanConversion(fromType, toType);
} else {
compileObjectConversion(fromType, toType);
}
}
public void compileNumericConversion(Type fromType, Type toType)
{
// 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()) {
if (toType == Type.C) {
compileObjectConversion(fromType, Type.CHARACTER);
fromType = Type.CHARACTER;
} else {
compileObjectConversion(fromType, Type.NUMBER);
fromType = Type.NUMBER;
}
}
if (box) {
if (toType == Type.NUMBER) {
// special case! nothing to do
} else {
// convert from one numeric object type to another
Type midType = Type.boxType(toType);
compileUnbox(fromType, midType);
compileBox(toType);
}
} else {
compileUnbox(fromType, toType);
}
} else if (box) {
if (toType == Type.CHARACTER) {
compilePrimitiveConversion(fromType, Type.C);
compileBox(toType);
} else if (toType == Type.NUMBER) {
// special case! convert primitive to it's numeric box type
toType = Type.boxType(fromType);
compileBox(toType);
} else {
Type midType = Type.boxType(toType);
if(fromType != midType) {
compilePrimitiveConversion(fromType, midType);
}
compileBox(toType);
}
} else {
compilePrimitiveConversion(fromType, toType);
}
}
/**
* compile code to convert a value of a boxed type to a primitive type, possibly not the immediately
* related primitive type
*
* @param fromType the type of the value to be unboxed
* @param toType he type required after unboxing
*/
public void compileUnbox(Type fromType, Type toType)
{
// 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);
} 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");
addStackCount(1);
} else if (toType == Type.F){
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Number", "floatValue", "()F");
} else {
assert toType == Type.D;
addStackCount(1);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Number", "doubleValue", "()D");
}
}
}
/**
* box a value belonging to a primitive type
* @param toType the type required after boxing
*/
public void compileBox(Type toType)
{
// 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;");
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;");
addStackCount(-1);
}
}
public void compileStringConversion(Type fromType, Type toType)
{
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);
// the above dup bumps the stack height
addStackCount(1);
mv.visitJumpInsn(Opcodes.IFNONNULL, elseLabel);
addStackCount(-1);
// 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;");
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;");
addStackCount(-1);
}
}
/**
* compile code to convert a numeric or character primitive to a numeric or character primitive
* @param fromType the type of the value to be converted
* @param toType the type required after conversion
*/
public void compilePrimitiveConversion(Type fromType, Type toType)
{
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);
addStackCount(1);
} else if (toType == Type.F) {
mv.visitInsn(Opcodes.I2F);
} else if (toType == Type.D) {
mv.visitInsn(Opcodes.I2D);
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);
addStackCount(1);
} else if (toType == Type.F) {
mv.visitInsn(Opcodes.I2F);
} else if (toType == Type.D) {
mv.visitInsn(Opcodes.I2D);
addStackCount(1);
}
} else if (fromType == Type.J) {
if (toType == Type.B || toType == Type.S || toType == Type.I || toType == Type.C) {
mv.visitInsn(Opcodes.L2I);
addStackCount(-1);
} else if (toType == Type.J) {
// nothing to do
} else if (toType == Type.F) {
mv.visitInsn(Opcodes.L2F);
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);
addStackCount(1);
} else if (toType == Type.F) {
// nothing to do
} else if (toType == Type.D) {
mv.visitInsn(Opcodes.F2D);
addStackCount(1);
}
} else if (fromType == Type.D) {
if (toType == Type.B) {
mv.visitInsn(Opcodes.D2I);
mv.visitInsn(Opcodes.I2B);
addStackCount(-1);
} else if (toType == Type.S) {
mv.visitInsn(Opcodes.D2I);
mv.visitInsn(Opcodes.I2S);
addStackCount(-1);
} else if (toType == Type.C) {
mv.visitInsn(Opcodes.D2I);
mv.visitInsn(Opcodes.I2C);
addStackCount(-1);
} else if (toType == Type.I) {
mv.visitInsn(Opcodes.D2I);
addStackCount(-1);
} else if (toType == Type.J) {
mv.visitInsn(Opcodes.D2L);
} else if (toType == Type.F) {
mv.visitInsn(Opcodes.D2F);
addStackCount(-1);
} else if (toType == Type.D) {
// nothing to do
}
}
}
public void compileBooleanConversion(Type fromType, Type toType)
{
if (toType == Type.Z) {
if (fromType == Type.OBJECT) {
fromType = Type.BOOLEAN;
mv.visitTypeInsn(Opcodes.CHECKCAST, fromType.getInternalName());
}
assert fromType == Type.BOOLEAN;
compileUnbox(fromType, toType);
} else {
assert toType == Type.BOOLEAN;
if (fromType == Type.OBJECT) {
mv.visitTypeInsn(Opcodes.CHECKCAST, toType.getInternalName());
} else {
assert fromType == Type.Z;
compileBox(toType);
}
}
}
public void compileObjectConversion(Type fromType, Type toType)
{
// ensure any primitive type is boxed before we go any further
if (fromType.isPrimitive()) {
Type boxType = Type.boxType(fromType);
compileBox(boxType);
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);
} else {
// nothing more to do
}
} else {
assert fromType.isAssignableFrom(toType);
compileCheckCast(toType);
}
}
public void compileCheckCast(Type toType)
{
mv.visitTypeInsn(Opcodes.CHECKCAST, toType.getInternalName());
}
}