Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HHH-13682 Generate Java 11/13/14 bytecode for tests when building with JDK11/13/14 #3329

Merged
merged 6 commits into from Apr 14, 2020

Conversation

yrodiere
Copy link
Member

@yrodiere yrodiere commented Apr 3, 2020

https://hibernate.atlassian.net/browse/HHH-13682

This PR is based on #3328 (HHH-13925 - upgrade to Gradle 6.3) . #3328 should be merged first. This PR includes commits from #3328, so it should be rebased after #3328 has been merged. => Done

The goal here is simply to compile tests to Java 11/13/14 when we build with JDK 11/13/14. That way, not only do we check that Hibernate ORM can be run within JDK 11/13/14, but we also check that it will correctly interact with Java 11/13/14 bytecode, which is especially important for bytecode enhancement.

Hopefully this will allow us to avoid the kind of problems we've had before, where bytebuddy needed an update and we didn't notice it because we were only ever manipulating Java 8 bytecode in our tests.

Build against JDK13: https://ci.hibernate.org/view/Personal%20jobs/job/hibernate-orm-personal-yoann-jdk13/8/
Build against JDK14: https://ci.hibernate.org/view/Personal%20jobs/job/hibernate-orm-personal-yoann-jdk14/4/

How to test that Java 11/13/14 is actually generated for tests, and not for the main code:

# Build with JDK13
$ ...
# Check main code: bytecode version 52, i.e. Java 8
$ od -A d -t 'u2' -N 8 --endian big hibernate-core/target/classes/java/main/org/hibernate/Criteria.class
0000000 51966 47806     0    52
0000008
# Check test code: bytecode version 57, i.e. Java 13
$ od -A d -t 'u2' -N 8 --endian big hibernate-core/target/classes/java/test/org/hibernate/jpa/test/callbacks/PreUpdateBytecodeEnhancementTest.class
0000000 51966 47806     0    57
0000008

The build requires JDK8+, so we're alwways Java 8 compatible.
It's no longer necessary since we upgraded to byte-buddy 1.10.2,
and it causes bytecode to be converted from Java 14 to Java 12 in some
cases (I don't know why).
…lizedAssociationTest

This test accesses a field of an entity directly and expects it to be
automatically initialized; this cannot work without extended bytecode
enhancement.

This used to work with Java 8 bytecode, but only by chance. It seems
that Java 8 bytecode relies on "synthetic", static access methods
inserted by the compiler to access the fields of entities in this test:
any access to the field is done through this access method instead of
through a direct field access. Since we apply bytecode enhancement to
all methods of entities, this means that access to fields triggers
initialization, without any bytecode enhancement in the caller class.

I believe this is specific to nested classes, but couldn't find a
source. For reference, the bytecode of access methods looks like this:

  static int access$002(org.hibernate.test.bytecode.enhancement.lazy.NaturalIdInUninitializedAssociationTest$AnEntity, int);
    Code:
       0: aload_0
       1: iload_1
       2: dup_x1
       3: putfield      hibernate#3                  // Field id:I
       6: ireturn

  static org.hibernate.test.bytecode.enhancement.lazy.NaturalIdInUninitializedAssociationTest$EntityImmutableNaturalId access$102(org.hibernate.test.bytecode.enhancement.lazy.NaturalIdInUninitializedAssociationTest$AnEntity, org.hibernate.test.bytecode.enhancement.lazy.NaturalIdInUninitializedAssociationTest$EntityImmutableNaturalId);
    Code:
       0: aload_0
       1: aload_1
       2: dup_x1
       3: putfield      hibernate#2                  // Field entityImmutableNaturalId:Lorg/hibernate/test/bytecode/enhancement/lazy/NaturalIdInUninitializedAssociationTest$EntityImmutableNaturalId;
       6: areturn

With Java 11, however, access to fields of entities is done directly,
even for nested classes. So the access methods no longer exist, and we
don't get automatic initialization upon field access. We need extended
bytecode enhancement, like we would in any other case of field access
(in particular accessing fields of non-nested classes).
... just in case we need that for some cutting-edge JDK, for example 15,
that would not be supported by Gradle yet.
So that we can feed it Java 13/14 bytecode
@yrodiere yrodiere requested a review from Sanne April 7, 2020 06:23
@yrodiere yrodiere changed the title HHH-13682 Generate Java 13/14 bytecode for tests when building with JDK13/14 HHH-13682 Generate Java 11/13/14 bytecode for tests when building with JDK11/13/14 Apr 7, 2020
int majorJVMVersionInt = Integer.valueOf(JavaVersion.current().toString());
//Set the -Dnet.bytebuddy.experimental=true property only when we need it:
if (majorJVMVersionInt >= 12) {
systemProperty 'net.bytebuddy.experimental', true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We no longer need this? Perhaps the check should be bumped to >= 15 ? It's kinda a pattern for ByteBuddy to only support the "work in progress" JDKs when this flag is on. I didn't check if the strategy changed.

Copy link
Member

@Sanne Sanne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes look good, but I have a conceptual question:

are we still able to check that Hibernate ORM - as built with JDK8 - runs fine in JDK11 ?

Because that's what matters, since we release with JDK8. Clearly we could have tests (usercode) compiled with JDK11.

@yrodiere
Copy link
Member Author

are we still able to check that Hibernate ORM - as built with JDK8 - runs fine in JDK11 ?

We never had that ability in the first place. We were always building with JDK11 when testing in JDK11. It's just that we were setting the target to Java 8, so we were generating Java 8 bytecode... through JDK11.

Also, this patch does not change the bytecode generated for ORM in any way. It's just about the tests.

To sum up:

  • Before, we were checking that ORM built in JDK11 with a Java 8 target (Java 8 bytecode) runs fine in JDK11 with tests built in JDK11 with a Java 8 target (Java 8 bytecode)
  • Now, we are checking that ORM built in JDK11 with a Java 8 target (Java 8 bytecode) runs fine in JDK11 with tests built in JDK11 with a Java 11 target (Java 11 bytecode)
  • Optionally, by setting project properties on the command line, we can run a build checking that ORM built in JDK11 with a Java 8 target (Java 8 bytecode) runs fine in JDK11 with tests built in JDK11 with a Java 8 target (Java 8 bytecode)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants