-
1. Re: Accessing classes behind field interfaces
adinn Feb 16, 2016 10:55 AM (in response to rachmato)Richard Achmatowicz wrote:
initialized with a class C which implements B, I would also like to have
access to the implementation class C, possibly like this:- RULE test
- CLASS A
- METHOD <init>
- AT EXIT
- BIND
- c:C = (C)$0.b;
- ...
- ENDRULE
However, when I try this, I get the error: script.btm line 9 : invalid expression
I would have thought that the rule has enough information available to process this cast and provide access to the class C.
Questions:
- is this sort of casting allowed in bind expressions?
- if it is not allowed, what are the preferred methods for accessing classes behind interfaces in bind expressions?
What you are trying to do is referred to in the programmer's guide as a downcast. This is only pemissible when attempting to initialise a variable at the point of declaration in the BIND clause (i.e. it is not permissible if you try subsequently to assign the BIND variable).
If you have a variable c of type C an initialise it with an expression e
c : C = e
then Byteman accepts that initialization so long as the lexically derived type E of e is either a super of C or is an interface implemented (directly or indirecty) by C.
You don't have to insert a cast -- indeed Byteman does not recognise a cast and will regard it as an invalid expression. When Byteman executes the BIND code to initialize c it includes a type check (impemented using the checkcast bytecode when the rule is compiled) to ensure that the result from evaluating e is actually an instance of C. If the check fails it throws a ClassCastException.
n.b. If you retry this without the cast expression note that you may well need to provide the fully package qualified name of C. If you just write
f: FooBar = fooExpression;
where the expression fooExpression has type Foo then Byteman may well be able to infer that fooExpression has type org.my.Foo but it cannot know that Foobar refers to subclass org.my.FooBar of Foo.
Please provide me with more details if you cannot get your example to work.
regards,
Andrew Dinn
-
2. Re: Accessing classes behind field interfaces
rachmato Feb 16, 2016 7:46 PM (in response to adinn)Hi Andrew
Thank you for the clarification. I did write up a small scale example of the problem I was having (it's easier to debug toy examples than sift through the logs of the application server) and the example worked out exactly as you described. With the addition of the package name, that problem has now gone away.
I may have a follow up instance: same situation, but this time an ExecutionException when I try to invoke on the class behind the interface. But before posting, i'll try to do a little more investigating myself.
-
3. Re: Accessing classes behind field interfaces
rachmato Feb 17, 2016 3:39 PM (in response to rachmato)Hi Andrew
As I said in a previous posting, I made up a toy example of the scenario I was having problems with: accessing the class behind an interface field. I thought the problem went away with making use of the package name, but when I ran the test program again today, it failed with the error I used to see regularly:
Rule.execute called for nested class example_0 Rule.ensureTypeCheckedCompiled : error type checking rule nested class example org.jboss.byteman.rule.exception.TypeException: FieldExpresssion.typeCheck : invalid field reference B .d file /home/nrla/java/byteman/accessNestedInstance/script.btm line 13 at org.jboss.byteman.rule.expression.FieldExpression.typeCheckAny(FieldExpression.java:203) at org.jboss.byteman.rule.expression.FieldExpression.typeCheck(FieldExpression.java:110) at org.jboss.byteman.rule.binding.Binding.typeCheck(Binding.java:155) at org.jboss.byteman.rule.Event.typeCheck(Event.java:114) at org.jboss.byteman.rule.Event.typeCheck(Event.java:106) at org.jboss.byteman.rule.Rule.typeCheck(Rule.java:548) at org.jboss.byteman.rule.Rule.ensureTypeCheckedCompiled(Rule.java:487) at org.jboss.byteman.rule.Rule.execute(Rule.java:705) at org.jboss.byteman.rule.Rule.execute(Rule.java:686) at Client.displayNest(Client.java) at Client.main(Client.java:14)
I am expecting to be able to get access to the field d of class C within the Byteman script by following the trail of field assignments starting from A.
Is this a legitimate use case?
-
downcast.example.zip 2.2 KB
-
-
4. Re: Accessing classes behind field interfaces
adinn Feb 18, 2016 6:07 AM (in response to rachmato)Hi Richard,
The error notified by the type checker occurs when it tries to type check the assignment of BIND variable d at line 13.This error is actually valid but the reason you cannot see that is because a prior error is not detected and notified.
The prior error arises because of a problem in your example which Byteman is not using to derail the type check process. You have declared c1 and c2 with type org.byteman.C
. . . # access C instance by accessor via downcast c1:org.byteman.C = b ; # access C instance by accessor via downcast c1:org.byteman.C = b ; # access C instance by field c2:org.byteman.C = a.b; # access D instance by field d:D = c1.d ; . . .
However, your class is located in the default package
public class C implements B { String id ; D d ; . . .
Byteman should throw an error when it detects that the type instance created to represent declared type org.byteman.C cannot be resolved. However it is conflating this UNKNOWN type instance with the special type instance Type.UNDEFINED and so ends up using the derived type of expressions b and a.b to type the variables c1 and c2. Later when it tries to type check c1.d the attempt to lookup a field d in interface B fails.
-
5. Re: Accessing classes behind field interfaces
adinn Feb 18, 2016 8:36 AM (in response to adinn)Hmm, fixing this has been quite 'interesting' :-)
The problem arises in the following circumstance. You declare a binding with declaration type org.my.Foo but omit the package qualifier and then initalize it with an expression of type org.my.FooBar which may or may not be related to Foo
BIND foo : Foo = returnMeFooBar() . . .
The bug is that the type checker fails to throw an exception for Foo being undefined and instead uses type org.my.FooBar for foo.
Initially I added a simple fix which said that when the declaration type Foo is undefined you type check the expression and then check whether the type of the expression has Foo as its name when the package is stripped away. So, in the case where returnMeFoo() has return type x.y.Foo this will work
BIND foo : org.my.Foo = returnMeFoo() . . .
bit in the case above where it returns org.my.FooBar it will fail.
That's ok in most cases. But what about when org.my.FooBar is a subclass of Foo or implements Foo as an interface? In that case it would be sensible to check the class + interface hierarchy above org.my.FooBar to see if any of them can be used to resolve Foo. It turns out that lots of the Byteman unit tests require using the derived type to validate a declared supertype. Typically I was declaring
BIND test : Test = $0 . . .
where $0 is a test class like TestArithmetic that subclasses org.jboss.byteman.tests.Test. So, I couldn't actually build a patched Byteman without either changing all these tests to use the full name
BIND test : org.jboss.byteman.tests.Test = $0 . . .
or adding the extra functionality. Of course I fixed the type checker rather than change the test scripts.
Of course, it doesn't work the other way
BIND foobar : FooBar = returnMeFoo() . . .
If the initializer returns an instance of org.my.Foo and we don't independently have some way of deriving the package name for FooBar then I can look up the class + interface hierarchy to find a proper name for FooBar but I can't search down the hierarchy (well not using the reflection API at least). So, unfortunately, when you want to downcast you will either have to use a type which has been identified independently as the type of some expression or method parameter or else you will need to use the package qualified name in the declaration e.g.
BIND foobar : oeg.my.FooBar = returnMeFoo() . . .
I raised JIRA BYTEMAN-315 for this problem and will push a patch asap.