Java tutorial
/* * 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.binding; import org.jboss.byteman.agent.Transformer; import org.jboss.byteman.rule.compiler.CompileContext; import org.jboss.byteman.rule.expression.DollarExpression; import org.jboss.byteman.rule.type.Type; import org.jboss.byteman.rule.expression.Expression; 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.RuleElement; import org.jboss.byteman.rule.helper.HelperAdapter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import java.io.StringWriter; /** * Class used to store a binding of a named variable to a value of some given type */ public class Binding extends RuleElement { public Binding(Rule rule, String name) { this(rule, name, Type.UNDEFINED, null); } public Binding(Rule rule, String name, Type type) { this(rule, name, type, null); } public Binding(Rule rule, String name, Type type, Expression value) { super(rule); this.name = name; this.type = (type != null ? type : Type.UNDEFINED); this.value = value; this.alias = null; // ok, check the name to see what type of binding we have if (name.matches("\\$[0-9].*")) { // $NNN references the method target or a parameter from 0 upwards index = Integer.valueOf(name.substring(1)); } else if (name.equals("$$")) { // $$ references the helper implicitly associated with a builtin call index = DollarExpression.HELPER_IDX; } else if (name.equals("$!")) { // $! refers to the current return value for the trigger method and is only valid when // the rule is triggered AT EXIT index = DollarExpression.RETURN_VALUE_IDX; } else if (name.equals("$^")) { // $^ refers to the current throwable value for the trigger method and is only valid when // the rule is triggered AT THROW index = DollarExpression.THROWABLE_VALUE_IDX; } else if (name.equals("$#")) { // $# refers to the parameter count for the trigger method index = DollarExpression.PARAM_COUNT_IDX; } else if (name.equals("$*")) { // $* refers to the parameters for the trigger method supplied as an Object array index = DollarExpression.PARAM_ARRAY_IDX; } else if (name.equals("$@")) { // $* refers to the parameters for the trigger method supplied as an Object array index = DollarExpression.INVOKE_PARAM_ARRAY_IDX; } else if (name.equals("$CLASS")) { // $* refers to the parameters for the trigger method supplied as an Object array index = DollarExpression.TRIGGER_CLASS_IDX; } else if (name.equals("$METHOD")) { // $* refers to the parameters for the trigger method supplied as an Object array index = DollarExpression.TRIGGER_METHOD_IDX; } else if (name.matches("\\$[A-Za-z].*")) { // $AAAAA refers to a local variable in the trigger method index = DollarExpression.LOCAL_IDX; } else { // anything else must be a variable introduced in the BINDS clause index = DollarExpression.BIND_IDX; } this.callArrayIndex = 0; this.updated = false; this.doCheckCast = false; } public Type typeCheck(Type expected) throws TypeException { if (alias != null) { type = alias.typeCheck(expected); doCheckCast = alias.doCheckCast; return type; } // value can be null if this is a rule method parameter if (value != null) { // type check the binding expression, using the bound variable's expected if it is known if (type.isDefined()) { if (Transformer.disallowDowncast()) { // compatibility behaviour -- use declared type to help infer expression type value.typeCheck(type); } else { // allow downcasts in the binding // typecheck the value first and then check for assignability in either direction // modulo assigning void Type valueType = value.typeCheck(expected); if (!type.isAssignableFrom(valueType)) { // if this is a downcast we need to check whether downcasts are disabled if (valueType == Type.VOID || !valueType.isAssignableFrom(type)) { throw new TypeException("Binding.typecheck : incompatible type for binding expression " + valueType + value.getPos()); } } } } else { Type valueType = value.typeCheck(expected); type = valueType; } } else if (type.isUndefined()) { // can we have no expected for a method parameter? throw new TypeException("Binding.typecheck unknown type for binding " + name); } return type; } public Object interpret(HelperAdapter helper) throws ExecuteException { if (isBindVar()) { Object result = value.interpret(helper); if (doCheckCast) { if (type.getTargetClass().isInstance(result)) { throw new ClassCastException("Cannot cast " + result + " to class " + type); } } helper.setBinding(getName(), result); return result; } return null; } public void compile(MethodVisitor mv, CompileContext compileContext) throws CompileException { if (alias != null) { alias.compile(mv, compileContext); } else if (isBindVar()) { // push the current helper instance i.e. this -- adds 1 to stack height mv.visitVarInsn(Opcodes.ALOAD, 0); // push the variable name -- adds 1 to stack height mv.visitLdcInsn(name); // increment stack count compileContext.addStackCount(2); // compile the rhs expression for the binding -- adds 1 to stack height value.compile(mv, compileContext); // make sure value is boxed if necessary if (type.isPrimitive()) { compileBox(Type.boxType(type), mv, compileContext); } else if (doCheckCast) { mv.visitTypeInsn(Opcodes.CHECKCAST, type.getInternalName()); } // compile a setBinding call pops 3 from stack height mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type.internalName(HelperAdapter.class), "setBinding", "(Ljava/lang/String;Ljava/lang/Object;)V"); compileContext.addStackCount(-3); } } public String getName() { return name; } public Expression getValue() { if (alias != null) { return alias.getValue(); } return value; } public Expression setValue(Expression value) { Expression oldValue = this.value; this.value = value; return oldValue; } public Type getType() { if (alias != null) { return alias.getType(); } return type; } public void setType(Type type) { this.type = type; } public int getCallArrayIndex() { if (alias != null) { return alias.getCallArrayIndex(); } return callArrayIndex; } public void setCallArrayIndex(int callArrayIndex) { this.callArrayIndex = callArrayIndex; } public int getLocalIndex() { if (alias != null) { return alias.getLocalIndex(); } return localIndex; } public void setLocalIndex(int localIndex) { this.localIndex = localIndex; } public boolean isParam() { return index > 0; } public boolean isRecipient() { return index == 0; } public boolean isHelper() { return index == DollarExpression.HELPER_IDX; } public boolean isBindVar() { return index == DollarExpression.BIND_IDX; } public boolean isLocalVar() { return index == DollarExpression.LOCAL_IDX; } public boolean isReturn() { return index == DollarExpression.RETURN_VALUE_IDX; } public boolean isThrowable() { return index == DollarExpression.THROWABLE_VALUE_IDX; } public boolean isParamCount() { return index == DollarExpression.PARAM_COUNT_IDX; } public boolean isParamArray() { return index == DollarExpression.PARAM_ARRAY_IDX; } public boolean isInvokeParamArray() { return index == DollarExpression.INVOKE_PARAM_ARRAY_IDX; } public boolean isTriggerClass() { return index == DollarExpression.TRIGGER_CLASS_IDX; } public boolean isTriggerMethod() { return index == DollarExpression.TRIGGER_METHOD_IDX; } public int getIndex() { return index; } public String getDescriptor() { return descriptor; } public void setDescriptor(String desc) { this.descriptor = desc; } /** * record that this binding occurs on the LHS of an assignment */ public void setUpdated() { updated = true; if (alias != null) { alias.setUpdated(); } } /** * record that this binding occurs on the LHS of an assignment */ public boolean isUpdated() { return updated; } public void writeTo(StringWriter stringWriter) { if (isHelper()) { stringWriter.write(name); } else if (isParam() || isRecipient()) { stringWriter.write(name); if (type != null && (type.isDefined() || type.isObject())) { stringWriter.write(" : "); stringWriter.write(type.getName()); } } else { stringWriter.write(name); if (type != null && (type.isDefined() || type.isObject())) { stringWriter.write(" : "); stringWriter.write(type.getName()); } } if (value != null) { stringWriter.write(" = "); value.writeTo(stringWriter); } } public void aliasTo(Binding alias) { if (this.isLocalVar()) { this.alias = alias; if (this.updated) { alias.updated = true; } } else { System.out.println("Binding : attempt to alias non-local var " + getName() + " to " + alias.getName()); } } public boolean isAlias() { return (alias != null); } public Binding getAlias() { return alias; } // special index values for non-positional parameters private final static int HELPER = -1; private final static int BIND_VAR = -2; private final static int LOCAL_VAR = -3; private final static int RETURN_VAR = -4; private final static int THROWABLE_VAR = -5; private final static int PARAM_COUNT_VAR = -6; private final static int PARAM_ARRAY_VAR = -7; private final static int INVOKE_PARAM_ARRAY_VAR = -8; private String name; private String descriptor; // supplied when the binding is for a local var private Type type; private Expression value; // the position index of the trigger method recipient or a trigger method parameter or one of the special index // values for other types of parameters. private int index; // the offset into the trigger method Object array of the initial value for this parameter private int callArrayIndex; // the offset into the stack at which a local var is located private int localIndex; private Binding alias; // aliases $x to $n where x is a method parameter name and n its index in the parameter list boolean updated; // records whether this binding occurs on the lhs of an assignment boolean doCheckCast; }