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
8254354: Add a withInvokeExactBehavior() VarHandle combinator #843
Conversation
|
@JornVernee The following labels will be automatically applied to this pull request:
When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing lists. If you would like to change these labels, use the /label pull request command. |
/label remove hotspot-compiler |
@JornVernee |
Webrevs
|
src/java.base/share/classes/java/lang/invoke/MemoryAccessVarHandleGenerator.java
Outdated
Show resolved
Hide resolved
Looks good to me. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This approach does not work for reference types, since they are erased to Object
, and then exact checking will be performed on the erased reference types.
For example try this:
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
public class Test {
int x;
public static void main(String[] args) throws Throwable {
VarHandle x = MethodHandles.lookup().findVarHandle(Test.class, "x", int.class);
VarHandle ex = x.asExact();
Test t = new Test();
ex.set(t, 1);
}
}
Which results in:
Exception in thread "main" java.lang.invoke.WrongMethodTypeException: expected (Object,int)void but found (Test,int)void
at Test.main(Test.java:11)
Exact type checking requires that match be performed on the VH access mode method type and the exact symbolic method type, something like:
final static Object guard_L_L(VarHandle handle, Object arg0, VarHandle.AccessDescriptor ad) throws Throwable {
if (handle.vform.exact && handle.accessModeType(ad.type) != ad.symbolicMethodTypeExact) {
throw new WrongMethodTypeException("expected " + handle.vform.methodType_table_exact[ad.type] + " but found "
+ ad.symbolicMethodTypeExact);
}
Then it's precisely the same as MH.invokeExact
, rather than MH.invoke
.
A VarForm
is a resource shared across many instances of the same kind of VarHandle
, so cannot be used for exact matching, except in specific scenarios e.g. access on a primitive array.
@PaulSandoz Thanks. I initially tested this with memory access VarHandles, which don't erase the receiver type. e.g.
Will result in:
Which led me to believe the approach would work for other reference types. But, I suppose the MethodTypes fed to memaccess VarForms are non-erased as an exception rather than a rule. I'll update the patch and sharpen the tests to check that the actual expected type is correct (per the exception message). |
…atter are shared. Use handle.accessModeType to get the exact type of the VarHandle.
@PaulSandoz I've implemented your suggestion, by moving the I've also sharpened the tests to check the exception message. Do you think the testing is sufficient? (Note that I did not add tests to the template files since only a select set of argument type conversions causes the WMTE we're looking for. So, that's why I created a new test file). FWIW, there seems to have been a bug in the implementation of IndirectVarHandle::accessModeTypeUncached, where it was using the VarHandle's type as the receiver argument (unlike all the other impls). I've fixed this by passing |
/csr needed |
@JornVernee has indicated that a compatibility and specification (CSR) request is needed for this pull request. |
- Make isExact() public
I've updated the javadoc, and added two benchmarks that show the existing discrepancy between an exact and a generic use of a VarHandle, as well as showing that an exact VarHandle is as fast as a generic VarHandle for an exact invocation. (1 benchmark for normal Java field access, and 1 benchmark for the foreign memory-access API).
|
The direct use of the enum ordinal is because HotSpot accessing it from the enum value is (or was) not optimal in C2. You can avoid the addition of the stable array by doing the following: public final MethodType accessModeType(AccessMode accessMode) {
return accessModeType(accessMode.at.ordinal());
}
@ForceInline
final MethodType accessModeType(int accessModeOrdinal) {
TypesAndInvokers tis = getTypesAndInvokers();
MethodType mt = tis.methodType_table[accessModeOrdinal];
if (mt == null) {
mt = tis.methodType_table[accessModeOrdinal] =
accessModeTypeUncached(accessModeOrdinal);
}
return mt;
}
final MethodType accessModeTypeUncached(int accessModeOrdinal) {
return accessModeTypeUncached(AccessMode.values()[accessModeOrdinal]);
} It's a little odd going back and forth between the ordinal and the enum value, but it's all on the slow uncached path, and it avoids some duplication of code. I'll take a closer looks at the other areas and respond in another comment. |
We can clarify the new methods and tie them closer to method handle semantics. I suggest the names The
We will need to adjust this section, i can help, but first let us reach agreement on this approach. |
I think If |
…haviour and withInvokeBehaviour
I've uploaded another revision that has the suggested javadoc changes, courtesy of Paul. We also decided to rename asExact() and asGeneric() to withInvokeExactBehaviour() and withInvokeBehaviour() (with links to the relevant javadoc sections). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. There is just one difference between the spec and implementation. The spec states:
If this VarHandle already has invoke{-exact} behaviour this VarHandle is returned.
I prefer this behaviour, but feel free to update the spec if you like e.g. If ... already has XXX then a new VH with exactly the same behaviour as this VH is returned.
I just realized i used "behaviour", the english spelling. We should use the US spelling, "behavior", which is more commonly used throughout the JDK documentation.
- Return same VarHandle instance when instance is already exact/non-exact
@JornVernee This change now passes all automated pre-integration checks. After integration, the commit message for the final commit will be:
You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed. At the time when this comment was updated there had been 40 new commits pushed to the
As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details.
|
- Fix typo - add specification about return value of hasInvokeExactBehaviour
@ChrisHegarty Thanks for the comments. I've implemented them. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
The test could be sprinkled with a number of assertions related to the invocation behaviour, a.k.a hasInvokeExactBehavior
; either the newly specified default behaviour assertFalse(vh. hasInvokeExactBehavior()), or post switching to exact: assertTrue(vh. hasInvokeExactBehavior())
@ChrisHegarty Good suggestion! Added. |
/integrate |
@JornVernee Since your change was applied there have been 43 commits pushed to the
Your commit was automatically rebased without conflicts. Pushed as commit 0a41ca6. |
Hi,
This patch adds an asExact() combinator to VarHandle, that will return a new VarHandle that performs exact type checks, similar to MethodHandle::invokeExact, to help developers catch inexact VarHandle usage, which can lead to performance degradation.
This is implemented using a boolean flag in VarForm. If the flag is set, the exact type of the invocation is checked against the exact type in the VarForm. If there is a mismatch, a WrongMethodTypeException is thrown.
Other than that, there is also an asGeneric() combinator added that does the inverse operation (thanks to Rémi for the suggestion). I've also added The
@Hidden
annotation to the VarHandleGuards methods, as well as a type-checking helper method called from the generic invocation lambda form, so that the stack trace we get points at the location where the VarHandle is being used.Thanks,
Jorn
CSR link: https://bugs.openjdk.java.net/browse/JDK-8255375
Progress
Testing
Issue
Reviewers
Download
$ git fetch https://git.openjdk.java.net/jdk pull/843/head:pull/843
$ git checkout pull/843