Skip to content
Browse files

added support for downcasting in local var initialisation in BBIND cl…

…auses - fixes BYTEMAN-225
  • Loading branch information...
1 parent 40a6298 commit 33b075589bba55bed8cb7c509fe198e4109e91ce @adinn committed
View
32 agent/pom.xml
@@ -538,6 +538,23 @@
<argLine>-javaagent:${project.build.directory}/byteman-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestThrowBinding.btm</argLine>
</configuration>
</execution>
+ <!--
+ <execution>
+ <id>misc.TestDowncast</id>
+ <phase>integration-test</phase>
+ <goals>
+ <goal>integration-test</goal>
+ <goal>verify</goal>
+ </goals>
+ <configuration>
+ <forkMode>once</forkMode>
+ <includes>
+ <include>org/jboss/byteman/tests/misc/TestDowncast.class</include>
+ </includes>
+ <argLine>-javaagent:${project.build.directory}/byteman-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestDowncast.btm</argLine>
+ </configuration>
+ </execution>
+ -->
<!-- bugfixes -->
<execution>
<id>bugfixes.TestEmptySignature</id>
@@ -1238,6 +1255,21 @@
<argLine>-Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestThrowBinding.btm</argLine>
</configuration>
</execution>
+ <execution>
+ <id>misc.TestDowncast.compiled</id>
+ <phase>integration-test</phase>
+ <goals>
+ <goal>integration-test</goal>
+ <goal>verify</goal>
+ </goals>
+ <configuration>
+ <forkMode>once</forkMode>
+ <includes>
+ <include>org/jboss/byteman/tests/misc/TestDowncast.class</include>
+ </includes>
+ <argLine>-Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/misc/TestDowncast.btm</argLine>
+ </configuration>
+ </execution>
<!-- bugfixes compiled -->
<execution>
<id>bugfixes.TestEmptySignature.compiled</id>
View
35 agent/src/main/java/org/jboss/byteman/agent/Transformer.java
@@ -477,6 +477,11 @@ public void installBootScripts() throws Exception
public static final String ALLOW_CONFIG_UPDATE = BYTEMAN_PACKAGE_PREFIX + "allow.config.update";
/**
+ * system property which disables downcasts in bindings
+ */
+ public static final String DISALLOW_DOWNCAST = BYTEMAN_PACKAGE_PREFIX + "disallow.downcast";
+
+ /**
* disable triggering of rules inside the current thread
* @return true if triggering was previously enabled and false if it was already disabled
*/
@@ -593,6 +598,20 @@ public static boolean isCompileToBytecode()
}
/**
+ * check whether downcasts in bindings are disallowed.
+ * @return true if downcasts in bindings are disallowed otherwise false
+ */
+ public static boolean disallowDowncast()
+ {
+ if (allowConfigUpdate()) {
+ synchronized (configLock) {
+ return disallowDowncast;
+ }
+ }
+ return disallowDowncast;
+ }
+
+ /**
* check whether compilation of rules is enabled or disabled
* @return true if compilation of rules is enabled otherwise false
*/
@@ -1046,6 +1065,11 @@ protected boolean isBytemanClass(String className)
private static boolean verifyTransformedBytes = computeVerifyTransformedBytes();
/**
+ * switch which determines whether downcasts in binding initialisations are disallowed
+ */
+ private static boolean disallowDowncast = computeDisallowDowncast();
+
+ /**
* master switch which determines whether or not config values can be updated
*/
private static boolean allowConfigUpdate = (System.getProperty(ALLOW_CONFIG_UPDATE) != null);
@@ -1124,6 +1148,10 @@ private static boolean computeVerifyTransformedBytes()
return System.getProperty(VERIFY_TRANSFORMED_BYTES) != null;
}
+ private static boolean computeDisallowDowncast() {
+ return (System.getProperty(DISALLOW_DOWNCAST) != null);
+ }
+
private void checkConfiguration(String property)
{
// n.b. this needs to be kept up to date with each new config setting that is added
@@ -1207,6 +1235,13 @@ private void checkConfiguration(String property)
transformAll = value;
}
}
+
+ if (DISALLOW_DOWNCAST.equals(property)) {
+ boolean value = computeDisallowDowncast();
+ synchronized (configLock) {
+ disallowDowncast = value;
+ }
+ }
}
/* helper methods to dump class files */
View
33 agent/src/main/java/org/jboss/byteman/rule/binding/Binding.java
@@ -23,6 +23,7 @@
*/
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;
@@ -96,13 +97,16 @@ public Binding(Rule rule, String name, Type type, Expression value)
this.callArrayIndex = 0;
this.updated = false;
+ this.doCheckCast = false;
}
public Type typeCheck(Type expected)
throws TypeException
{
if (alias != null) {
- return alias.typeCheck(expected);
+ type = alias.typeCheck(expected);
+ doCheckCast = alias.doCheckCast;
+ return type;
}
// value can be null if this is a rule method parameter
@@ -110,10 +114,21 @@ public Type typeCheck(Type expected)
// type check the binding expression, using the bound variable's expected if it is known
if (type.isDefined()) {
- value.typeCheck(type);
- // redundant?
- if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
- throw new TypeException("Binding.typecheck : incompatible type binding expression " + type + value.getPos());
+ 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);
@@ -131,6 +146,11 @@ 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;
}
@@ -153,6 +173,8 @@ public void compile(MethodVisitor mv, CompileContext compileContext) throws Comp
// 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");
@@ -372,4 +394,5 @@ public Binding getAlias()
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;
}
View
8 agent/src/main/java/org/jboss/byteman/rule/expression/NullLiteral.java
@@ -60,13 +60,13 @@ public void bind()
}
public Type typeCheck(Type expected) throws TypeException {
- if (expected.isPrimitive()) {
+
+ if (expected == Type.VOID || expected.isUndefined()) {
+ type = Type.OBJECT;
+ } else if (expected.isPrimitive()) {
type = Type.boxType(expected);
} else {
type = expected;
- if (type.isUndefined()) {
- throw new TypeException("NullLiteral.typeCheck : unable to derive type for null from context");
- }
}
return type;
}
View
72 agent/src/test/java/org/jboss/byteman/tests/misc/TestDowncast.java
@@ -0,0 +1,72 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2012, 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.tests.misc;
+import org.jboss.byteman.tests.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * class to test use of downcast in rule binding
+ */
+public class TestDowncast extends Test
+{
+ public TestDowncast()
+ {
+ super(TestDowncast.class.getCanonicalName());
+ }
+
+ public void test()
+ {
+ List<String> names = new ArrayList<String>();
+
+ names.add("Andrew");
+
+ try {
+ log("calling TestDowncast.triggerMethod((Andrew))");
+ triggerMethod(names);
+ log("called TestDowncast.triggerMethod((Andrew))");
+ } catch (Exception e) {
+ log(e);
+ }
+
+ checkOutput(true);
+ }
+
+ public void triggerMethod(List<String> names)
+ {
+ log("inside TestDowncast.triggerMethod()");
+ }
+
+ @Override
+ public String getExpected() {
+ logExpected("calling TestDowncast.triggerMethod((Andrew))");
+ logExpected("inside TestDowncast.triggerMethod()");
+ logExpected("triggerMethod : list.get(0) == Andrew");
+ logExpected("called TestDowncast.triggerMethod((Andrew))");
+
+ return super.getExpected();
+ }
+}
View
38 agent/src/test/resources/scripts/misc/TestDowncast.btm
@@ -0,0 +1,38 @@
+##############################################################################
+# JBoss, Home of Professional Open Source
+# Copyright 2012, 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
+#
+
+##############################################################################
+#
+# test that a downcast can be used in a rule binding
+
+RULE test return value binding
+CLASS org.jboss.byteman.tests.misc.TestDowncast
+METHOD triggerMethod(List)
+HELPER org.jboss.byteman.tests.helpers.Default
+AT EXIT
+BIND name : String = $1.get(0);
+ test = $0
+IF TRUE
+DO test.log("triggerMethod : list.get(0) == " + name);
+ENDRULE

0 comments on commit 33b0755

Please sign in to comment.
Something went wrong with that request. Please try again.