5 Replies Latest reply on Apr 26, 2013 9:20 AM by Andrew Dinn

    Compiling anonymous type

    Radim Vansa Master

      Hi,

       

      I have a working set of rules which is not compiled. One of these rules hooks to method call in anonymous type - foo.bar.Foo$4. In the rule I reference a field of this class - actually it is injected final local variable foo from the method which declares the anonymous class, therefore, I reference it as $0.val$foo.

       

      My problem is that as I want to get this compiled, I get NPE

      at org.jboss.byteman.rule.type.Type.getInternalName(Type.java:130)

      at org.jboss.byteman.rule.RuleElement.compileObjectConversion(RuleElement.java:392)

      at org.jboss.byteman.rule.expression.DollarExpression.compile(DollarExpression.java:229)

      at org.jboss.byteman.rule.expression.FieldExpression.compile(FieldExpression.java:282)

       

      because calling getCanonicalName() on anonymous class returns null.

       

      Is there any workaround for that? I use Byteman 2.1.2. At least if I could compile part of these rules and leave the one problematic not compiled.

        • 1. Re: Compiling anonymous type
          Andrew Dinn Master

          Hi Radim,

           

           

          Radim Vansa wrote:

           

          Hi,

           

          I have a working set of rules which is not compiled. One of these rules hooks to method call in anonymous type - foo.bar.Foo$4. In the rule I reference a field of this class - actually it is injected final local variable foo from the method which declares the anonymous class, therefore, I reference it as $0.val$foo

           

          Could you post the full rule and the class definitions (outer and inner)? This would make it a lot easier to reproduce the problem and provide a fix. If you cannot reveal the source code then could you come up with a small test case which reproduces the problem?

           

          So, I assume $0 is an instance of class 'foo.bar.Foo$4' and the anonymous class includes a field with name 'val$foo'.

           

          Radim Vansa wrote:

           

          My problem is that as I want to get this compiled, I get NPE

          at org.jboss.byteman.rule.type.Type.getInternalName(Type.java:130)

          at org.jboss.byteman.rule.RuleElement.compileObjectConversion(RuleElement.java:392)

          at org.jboss.byteman.rule.expression.DollarExpression.compile(DollarExpression.java:229)

          at org.jboss.byteman.rule.expression.FieldExpression.compile(FieldExpression.java:282)

           

          because calling getCanonicalName() on anonymous class returns null.

           

          Is there any workaround for that? I use Byteman 2.1.2. At least if I could compile part of these rules and leave the one problematic not compiled.

           

          So, the problem here appears to be compiling the reference to local variable $0 which has a class with name 'foo.bar.Foo$4'. I think I have a patch for this but a test case would help me ensure it is what you need. Here is the relevant JIRA:

           

            https://issues.jboss.org/browse/BYTEMAN-235

           

          I cannot think of a workaround for this other than continuing to run inrepreted. At the moment it is not possible to enable compilation just for a single rule. Currently it is either enabled or disabled globally according to the value of system property 'org.jboss.byteman.compile.to.bytecode'. I have planned for some time to allow this flag to be reset per rule file or per rule using a COMPILED keyword. However, I have not had time to implement this yet. If you realy want it please raise a feature JIRA for it so I keep it in mind.

          • 2. Re: Compiling anonymous type
            Radim Vansa Master

            No problem, I just wanted to simplify the case. I am getting that for

             

            RULE loopbackCopyProcessing

            CLASS org.jgroups.protocols.TP$4

            METHOD run

            HELPER org.my.MessageFlowTracer

            AT LINE 1181

            IF TRUE

            DO

              threadHandoverSuccess($0.val$copy);

            ENDRULE

             

            where I use JGroups version 3.2.7 - see this line on github https://github.com/belaban/JGroups/blob/Branch_JGroups_3_2/src/org/jgroups/protocols/TP.java#L1181

             

            OK, thanks for your help.

            • 3. Re: Compiling anonymous type
              Andrew Dinn Master

              Hi Radim,

               

               

              Radim Vansa wrote:

               

              No problem, I just wanted to simplify the case. I am getting that for

               

              RULE loopbackCopyProcessing

              CLASS org.jgroups.protocols.TP$4

              METHOD run

              HELPER org.my.MessageFlowTracer

              AT LINE 1181

              IF TRUE

              DO

                threadHandoverSuccess($0.val$copy);

              ENDRULE

               

              where I use JGroups version 3.2.7 - see this line on github https://github.com/belaban/JGroups/blob/Branch_JGroups_3_2/src/org/jgroups/protocols/TP.java#L1181

               

              Hmm, it is not so simple as I thought to fix this. The name problem is easy to fix. The harder problem is that the access to field 'val$copy' is failing with an IllegalAccessError. Here is an example I coooked up which manifests the problem once the NPE has been avoided.

              java.lang.IllegalAccessError: tried to access class org.jboss.byteman.tests.bugfixes.TestAnonClassCompile$1 from class org.jboss.byteman.tests.helpers.Default_HelperAdapter_Compiled_1

              The same problem occurs if you try to execute a method of the anonymous class in your rule. It seems access to fields/methods of the anonymous class is only allowed from its containing class.

               

              This is a stronger restriction than the usual one which applies to private fields -- those restrictions only seem to apply to direct bytecode operations i.e. if a getfield or invokeXXX occurs in the bytecode. Byteman gets round this by using reflection to access private members from the compiled code; it caches a Member instance in the rule and the compiled code looks it when it wants to do the access/execute operation and then calls the  get/set/execute method of the Member. However, what is interestign is that in this case the anonymous class accesses are failing even when the compiled code performs them via reflection.

               

              This is a little surprising to me because the member accesses succeed when the rule is interpreted. I think this may be because the compiled code belongs to the helper adapter class which is defined in the class loader of the helper class I am using for this test (well, actually, in a subloader of that class). It may not have the same access rights as the rule interpreter code which is implemented by Byteman classes located in the bootstrap class loader. I will play with this a little more to see if that is what is at stake and, if so, will see if there is a way to do the member access from the relevant class context (where the compiled code etches the member and the accesses/executes it I can probably delegate to the execute/access to the rule instead). I'll report findings back on this forum thread.

               

              regards,

               

               

              Andrew Dinn

              • 4. Re: Compiling anonymous type
                Radim Vansa Master

                Hmm, I hate class loader issues :-/ Wouldn't loading the helper directly into boot loader help? Nevertheless, I still wouldn't be able to use that because JGroups fail if loaded into boot class loader (in org.jgroups.util.Util static constructor the Util.class.getClassLoader() returns null and the code is not prepared to that and fails with NPE).

                 

                I have also tried to adapt the code a bit for the test (rewrite the anonymous class as private inner one) but now I see that with compiled rules I cannot even access inner class (and its private field) - when I dump generated classes for (failing) rule

                 

                import org.jgroups.protocols.TP.LoopbackRunnable;

                 

                and in the execute0 method

                 

                threadHandoverSuccess((Message)getAccessibleField((LoopbackRunnable)getBinding("$0"), 0));

                 

                Why is it explicitely casting the returned object to the inner class type? If just to get the exception sooner than later, it would be great if it can be switched off/checked via reflection instead of direct call (for performance reasons I'd prefer the former as an option).

                And do I understand the code correctly that only public fields may be accessed here, not the private ones?

                • 5. Re: Compiling anonymous type
                  Andrew Dinn Master

                  Hi Radim,

                   

                   

                  Radim Vansa wrote:

                   

                  Hmm, I hate class loader issues :-/ Wouldn't loading the helper directly into boot loader help? Nevertheless, I still wouldn't be able to use that because JGroups fail if loaded into boot class loader (in org.jgroups.util.Util static constructor the Util.class.getClassLoader() returns null and the code is not prepared to that and fails with NPE).

                   

                  Yes class loader probolmes are very difficult to manage. The default helper is in the boot loader because it lives in the Byteman jar. However, a test-specific helper may need to be picked up from the class path or an app-specific class loader in order to allow it to refer to classes/code supplied with the app being tested.

                   

                   

                  Radim Vansa wrote:

                   

                  I have also tried to adapt the code a bit for the test (rewrite the anonymous class as private inner one) but now I see that with compiled rules I cannot even access inner class (and its private field) - when I dump generated classes for (failing) rule

                   

                  import org.jgroups.protocols.TP.LoopbackRunnable;

                   

                  and in the execute0 method

                   

                  threadHandoverSuccess((Message)getAccessibleField((LoopbackRunnable)getBinding("$0"), 0));

                   

                  Why is it explicitely casting the returned object to the inner class type? If just to get the exception sooner than later, it would be great if it can be switched off/checked via reflection instead of direct call (for performance reasons I'd prefer the former as an option).

                  And do I understand the code correctly that only public fields may be accessed here, not the private ones?

                   

                  I have discovered that the problem in the compiled version of the rule is a checkcast used when the binding for $0 is looked up. The checkcast is not validated by the bytecode verifier because the helper class does not have valid access to the anonymous class. Of course, this doesnot happen when executing the code interpreted because the instance is handled as an Object.

                   

                  Nomrally Byteman must plant a checkcast in the compiled code  when it retrieves a bound value for a variable or else the bytecode verifier will object when it tries to operate on the retrieved object. So, for example if it looks up a bidning for foo : String then it has to plant a checkcast "java/lang/String" before it can then plant an invokeVirtual "java/lang/String.length()I". It might be possible to avoid the cast when retrieving an anonymous class instance because field accesses and method accesses via the reflection API only require an Object. Of course, the object could also be passed to a method in which case the cast would not be optional.

                   

                  The JIRA for this related problem https://issues.jboss.org/browse/BYTEMAN-236