eclipse-openj9 / openj9

Eclipse OpenJ9: A Java Virtual Machine for OpenJDK that's optimized for small footprint, fast start-up, and high throughput. Builds on Eclipse OMR (https://github.com/eclipse/omr) and combines with the Extensions for OpenJDK for OpenJ9 repo.
Other
3.28k stars 721 forks source link

Allow non-vTable methods to be added/removed during class replacement #4872

Open gacholio opened 5 years ago

gacholio commented 5 years ago

The addition or removal of methods which do not affect the shape of the vTable should be allowed in fast HCR.

Methods which never appear in the vTable are:

Methods which do not need to appear in the vTable but currently do:

New final methods should follow the invokeprivate path. This is likely a matter of removing their vTable modifier and modifying the restrictions on which methods can use invokeprivate during resolution.

DanHeidinga commented 5 years ago

Related to https://github.com/eclipse/openj9/issues/771

gacholio commented 5 years ago

This will eventually require some JIT work to handle resolved methods which get deleted.

gacholio commented 5 years ago

Experiments show that the the JVMTI can_maintain_original_method_order sorts the methods in the class according to the original class file (i.e. if the capability is acquired, and a redefinition reorders the methods in a class, the JVMTI GetClassMethods calls returns the original order [returns the same both before and after the reordering]).

It's not clear what this will mean in the presence of added and removed methods.

gacholio commented 5 years ago

Some questions we need answered:

Obvious alternatives are: use placeholder method to throw, continue running the deleted method, re-resolve.

If a method is resolved, and a new method which would have been resolved is added, do we keep running the resolved method, or "correct" the invoke?

Perhaps if this capability is acquired, we cannot add/remove private.

Certainly not interested in trying to maintain ordering for deletion and re-addition of the same method.

Perhaps more than one, if we are to allow final/static.

gacholio commented 5 years ago

Testing on Oracle openjdk10, the VM allows the addition (but not deletion) of private final methods.

Based on one testcase, the newly-added method does not cause re-resolution. The testcase is:

A public m() B extends A C extends B public m() { super.m() }

Where the super call targets A (not B as the compiler would generate).

The super call resolves to A.m(). If B is replaced adding "private final m()", the super call continues to run A.m(). If B.m() were present during resolution, it would be targetted.

gacholio commented 5 years ago

Also, as expected, "resolve failed" state is not saved for method resolutions, which means that adding methods can cause previous failures to succeed the next time around.

A B extends A C extends B public m() { super.m() }

The super targets B in this case.

The super call fails with NoSuchMethodError (as it should). If B is replaced adding "private final m()", the super call then fails with IllegalAccessError, meaning the resolution was attempted again.

gacholio commented 5 years ago

Also worth noting that the newly-added method breaks the ordering enforcement for can_maintain_original_method_order:

methods before:
    <init>()V
<end>
methods after:
    m()V
    <init>()V
<end>

So m() was not added to the end as would be expected.

gacholio commented 5 years ago

Looks like the order of the new class file is obeyed in the addition case:

methods before:
    <init>()V
<end>
methods after:
    m2()V
    <init>()V
    m()V
<end>
gacholio commented 5 years ago

-Xfuture has no effect on the ability to add private final methods.

gacholio commented 5 years ago

Also verified Oracle only allows addition of: