Skip to content

Commit

Permalink
added support for downcasting in local var initialisation in BBIND cl…
Browse files Browse the repository at this point in the history
…auses - fixes BYTEMAN-225
  • Loading branch information
adinn committed Dec 5, 2012
1 parent 40a6298 commit 33b0755
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 9 deletions.
32 changes: 32 additions & 0 deletions agent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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>
Expand Down
35 changes: 35 additions & 0 deletions agent/src/main/java/org/jboss/byteman/agent/Transformer.java
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,11 @@ public byte[] transform(ClassLoader originalLoader,
*/
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
Expand Down Expand Up @@ -592,6 +597,20 @@ public static boolean isCompileToBytecode()
return compileToBytecode;
}

/**
* 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
Expand Down Expand Up @@ -1045,6 +1064,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
*/
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 */
Expand Down
33 changes: 28 additions & 5 deletions agent/src/main/java/org/jboss/byteman/rule/binding/Binding.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -96,24 +97,38 @@ 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
if (value != null) {
// 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);
Expand All @@ -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;
}
Expand All @@ -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");
Expand Down Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
72 changes: 72 additions & 0 deletions agent/src/test/java/org/jboss/byteman/tests/misc/TestDowncast.java
Original file line number Diff line number Diff line change
@@ -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();
}
}
38 changes: 38 additions & 0 deletions agent/src/test/resources/scripts/misc/TestDowncast.btm
Original file line number Diff line number Diff line change
@@ -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.