Byte code manipulation of descriptor classes?

11 views
Skip to first unread message

Ullrich Hafner

unread,
Jan 5, 2021, 11:04:07 AM1/5/21
to Jenkins Developers
I’m currently struggling with an architecture test for my descriptor classes.
Does the Jenkins maven HPI plugin somehow manipulates the byte code of descriptor classes?

Given the following setup:

BaseDescriptor {
public void method() {}
}

ConcreteDescriptor extends BaseDescriptor {

}

In my architecture tests it looks like that the ConcreteDescriptor contains a byte code method that simply invokes the base method. I.e., the code seems to be

BaseDescriptor {
public void method() {}
}

ConcreteDescriptor extends BaseDescriptor {
public void method() {super.method()}
}

Jesse Glick

unread,
Jan 5, 2021, 3:39:02 PM1/5/21
to Jenkins Dev

On Tue, Jan 5, 2021 at 11:04 AM Ullrich Hafner ullrich...@gmail.com wrote:

Does the Jenkins maven HPI plugin somehow manipulates the byte code of descriptor classes?

Not in this respect Sounds like something being done by javac, though I am not sure what offhand. Did you confirm via javap?

John Patrick

unread,
Jan 5, 2021, 4:14:09 PM1/5/21
to jenkin...@googlegroups.com
That sounds like standard java to me.

If ConcreteDescriptor does not contain method() then the 1st method in
the inheritance hierarchy will be executes and from your code above
that would be BaseDescriptor.method. That is how java is designed to
work.

But if you are saying you have a method in both BaseDescriptor and
ConcreteDescriptor which have the exact same method signature, then
the version in BaseDescriptor could only be called if super.method()
is called. So that could be from within ConcreteDescriptor.method() of
another method in ConcreteDescriptor could also do that call as super
would override the the version in the same class.

BaseDescriptor {
public void method() { // A }
}
ConcreteDescriptor extends BaseDescriptor {
public void method() { // B }
public void qwerty() { super.method(); }
}

Calling new ConcreteDescriptor().method() would trigger code B and not code A.
Calling new ConcreteDescriptor().qwerty() would trigger code A.

That is how inheritance, super and this work.

Yeah some class processing tool or byte code manipulator could be
injecting the code super.method(). Using javap will show you what that
code is doing but is hard to understand the raw byte code translation.

Simpler option, put a debug point into method() of BaseDescriptor and
run your test in debug mode, you'll quickly the call hierarchy and
what triggered the call.

John
> --
> You received this message because you are subscribed to the Google Groups "Jenkins Developers" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to jenkinsci-de...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/jenkinsci-dev/CANfRfr03TcOgvzXtXU_XvAg7GRjd1Ck%3D%3D0JX%3DEy7mYq%2Bp93S2w%40mail.gmail.com.

Ullrich Hafner

unread,
Jan 6, 2021, 6:53:30 AM1/6/21
to Jenkins Developers
Ok, thanks for the tip with javap. After analyzing the problem it seems that javac creates a new method in ConcreteDescriptor (that simply calls the base class method) as soon I mark the base class as package private:

abstract class A {
public void method() {

}
}
public class B extends A {

}

Output of javap:

Compiled from "B.java"
public class io.jenkins.plugins.analysis.warnings.B extends io.jenkins.plugins.analysis.warnings.A {
  public io.jenkins.plugins.analysis.warnings.B();
  public void method();
}

public abstract class A {
public void method() {

}
}
public class B extends A {

}

Output of javap:

Compiled from "B.java"
public class io.jenkins.plugins.analysis.warnings.B extends io.jenkins.plugins.analysis.warnings.A {
  public io.jenkins.plugins.analysis.warnings.B();
}



John Patrick

unread,
Jan 6, 2021, 8:27:28 AM1/6/21
to jenkin...@googlegroups.com
But both version of what you show above, if I execute 'new
B().method();' will trigger method in class A to be executed. Using
'javap' might change between the two versions.

That is how the language is designed to work regarding inheritance.
It's similar to how the compiler added a no-args constructor into your
class which calls the super no args constructor if you have not
defined a constructor yourself

If you don't want method in class A to be executed you'll need to
explicitly override it in class B. As not defining method in class B
will effectively make a method like 'public void method()
{super.method();}' as you have highlighted, but that is as expected
and isn't any bytecode manipulation tool, it's how the language is
designed to work.

John
> To view this discussion on the web visit https://groups.google.com/d/msgid/jenkinsci-dev/4E35D2CF-860A-4D73-8C70-7DB2CF2F2367%40gmail.com.

Ullrich Hafner

unread,
Jan 6, 2021, 3:44:18 PM1/6/21
to Jenkins Developers

Am 06.01.2021 um 14:27 schrieb John Patrick <nhoj.p...@gmail.com>:

But both version of what you show above, if I execute 'new
B().method();' will trigger method in class A to be executed. Using
'javap' might change between the two versions.

That is how the language is designed to work regarding inheritance.
It's similar to how the compiler added a no-args constructor into your
class which calls the super no args constructor if you have not
defined a constructor yourself

If you don't want method in class A to be executed you'll need to
explicitly override it in class B. As not defining method in class B
will effectively make a method like 'public void method()
{super.method();}' as you have highlighted, but that is as expected
and isn't any bytecode manipulation tool, it's how the language is
designed to work.


Well, the problem I was referring to is not related to the concept of how Java implements calls in inheritance hierarchies from the user perspective. 
Maybe I forgot to add some more context: I want to check the byte code of all methods in a given class. Actually I have an architecture test that verifies that I have a call to Jenkins.hasPermission in any method that is accessible by Stapler. Sometimes that check worked, and sometimes not. As it turned out, javac only sometimes generates a synthetic method (for package private parent classes). And this synthetic method did just call the super method (and obviously does contain the permission check only in an indirect way).
 
But I solved it now, I simply ignore all methods that are marked as synthetic (i.e., generated by javac). 
Solution:


Jesse Glick

unread,
Jan 6, 2021, 3:59:56 PM1/6/21
to Jenkins Dev
On Wed, Jan 6, 2021 at 3:44 PM Ullrich Hafner <ullrich...@gmail.com> wrote:
I have an architecture test that verifies that I have a call to Jenkins.hasPermission in any method that is accessible by Stapler.

Does not suffice to check CodeQL for violations? https://groups.google.com/g/jenkinsci-dev/c/0hw97zAdUMw/m/zt4TeGV7AQAJ

This smells like a generic check which really belongs in `InjectedTest` or something.

Ullrich Hafner

unread,
Jan 6, 2021, 6:24:55 PM1/6/21
to Jenkins Developers
Yes, I already discussed that with Daniel. His check is simple not yet ready (see https://github.com/jenkinsci/jenkins-test-harness/pull/133).

--
You received this message because you are subscribed to the Google Groups "Jenkins Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to jenkinsci-de...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages