-
Notifications
You must be signed in to change notification settings - Fork 112
8349945: Implement strict static fields (proposed JVM feature) #1465
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
8349945: Implement strict static fields (proposed JVM feature) #1465
Conversation
👋 Welcome back matsaave! A progress list of the required criteria for merging this PR into |
@matias9927 This change now passes all automated pre-integration checks. ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details. 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 1 new commit pushed to the
Please see this link for an up-to-date comparison between the source branch of this pull request and the ➡️ To integrate this PR with the above commit message to the |
Webrevs
|
* questions. | ||
*/ | ||
|
||
identity class Brbefore_BAD version 69:65535 |
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.
Can't this be compiled with javac? I don't think we are going to add any checks for strict initialization on the javac side as pre-write reads cannot be detected because they can easily happen in nested method calls.
Even if we decide to use jasm, I think the best practice seems to leave the source to generate the jasm in a .test file, and comment in the jasm what was changed.
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.
Javac can catch this because the static field hasn't been initialized yet:
error: variable F2__STRICT might not have been initialized int x = F2__STRICT; //FAIL
It may be possible to write a java program with nested calls that does this but I think it's best to keep the test cases simple.
I agree with the last comment, I'll add the equivalent java code to the jasm files.
@@ -1165,6 +1166,9 @@ JRT_ENTRY(void, Runtime1::patch_code(JavaThread* current, C1StubId stub_id )) | |||
// field because it was unknown at compile time. | |||
deoptimize_for_flat = result.is_flat(); | |||
|
|||
// Strict statics may require tracking if their class is not fully initialized. | |||
// For now we can bail out of the compiler and let the interpreter handle it. | |||
deoptimize_for_strict_static = result.is_strict_static_unset(); |
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.
Deoptimizing on strict fields makes sense when the field is unset as an initial implementation.
As a possible future optimization, we could make some additional checks here to decide whether we need to deopt or not and instead simulate the strict static is_set bits here. current
Thread is either the thread initializing the class and therefore able to read/update the set/unset bits or it is some other thread that would need to wait.
If current
is the initializing thread, and this is a strict static field write, we know we're currently executing the field set or we wouldn't be patching the code. Given that, it would be possible to avoid the deopt by calling a helper from this code to toggle the bit.
Something to think about for the future when strict statics are more common to avoid spurious deopts
src/hotspot/share/oops/fieldInfo.hpp
Outdated
@@ -313,6 +313,8 @@ class FieldStatus { | |||
enum FieldStatusBitPosition { | |||
_fs_access_watched, // field access is watched by JVMTI | |||
_fs_modification_watched, // field modification is watched by JVMTI | |||
_fs_strict_static_unset, // JVM_ACC_STRICT static field has not yet been set | |||
_fs_strict_static_unread, // SS field has not yet been read (EnforceStrictStatics=2 only) |
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.
_fs_strict_static_unread, // SS field has not yet been read (EnforceStrictStatics=2 only) | |
_fs_strict_static_unread, // SS field has not yet been read |
Minor comment update - I think we removed the various modes so we don't need to mention them in the comments
throw_strict_static_exception(bad_strict_static, "is unset before first read in", CHECK); | ||
} | ||
} else { | ||
// Experimentally, enforce additional proposed conditions after the first write. |
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.
// Experimentally, enforce additional proposed conditions after the first write. | |
// Ensure no write after read for final strict statics |
I think this comment may be left over from the multi-option initial prototype. It might be worth going over all the comments and making sure they still apply now that we've settled on a single approach
Symbol* bad_strict_static = fi.name(constants()); | ||
throw_strict_static_exception(bad_strict_static, "is set after read (as final) in", CHECK); | ||
} else if (!is_writing && fs.is_strict_static_unread()) { | ||
// log the read (this requires an extra status bit, with extra tests on it) |
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.
// log the read (this requires an extra status bit, with extra tests on it) |
This comment also seems like a holdover from the prototype
@@ -59,6 +59,8 @@ class InstanceKlassFlags { | |||
flag(is_naturally_atomic , 1 << 16) /* loaded/stored in one instruction*/ \ | |||
flag(must_be_atomic , 1 << 17) /* doesn't allow tearing */ \ | |||
flag(has_loosely_consistent_annotation , 1 << 18) /* the class has the LooselyConsistentValue annotation WARNING: it doesn't automatically mean that the class allows tearing */ \ | |||
flag(is_implicitly_constructible , 1 << 19) /* the class has the ImplicitlyConstrutible annotation */ \ |
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.
Did this get accidentally propogated into this patch from somewhere else?
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.
Weird, thanks for catching that!
src/hotspot/share/prims/jni.cpp
Outdated
!InstanceKlass::cast(k)->find_field(fieldname, signame, true, &fd)) { | ||
!InstanceKlass::cast(k)->find_field(fieldname, signame, true, &fd) || | ||
// strict fields need special tracking during <clinit>; do not hand them out so early | ||
(fd.access_flags().is_strict() && !InstanceKlass::cast(k)->is_initialized())) { |
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.
Should this be an assert rather than a runtime check given that we force the class to be initialized a few lines above?
@matias9927 this pull request can not be integrated into git checkout implement_strict_statics_8349945
git fetch https://git.openjdk.org/valhalla.git lworld
git merge FETCH_HEAD
# resolve conflicts and follow the instructions given by git merge
git commit -m "Merge lworld"
git push |
src/hotspot/share/prims/jni.cpp
Outdated
@@ -2057,6 +2057,7 @@ JNI_ENTRY(jfieldID, jni_GetStaticFieldID(JNIEnv *env, jclass clazz, | |||
fieldDescriptor fd; | |||
if (!k->is_instance_klass() || | |||
!InstanceKlass::cast(k)->find_field(fieldname, signame, true, &fd)) { | |||
assert(InstanceKlass::cast(k)->is_initialized(), "must be"); |
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.
The assert is right before throwing a NoSuchFieldError. If we're going to assert, we want to do it on the path that can return the jfieldID so it needs to be moved out of the if
block
There are a couple of other corner cases for dealing with reflection we should probably test. Assume a single
This ensures we've covered the mix of reflective vs bytecode reads and writes and ensures all paths correctly set and check the bits. |
I think reflective write for static final is already impossible - at least that is what I know about Method Handles and core reflection. So we probably just need to care about reflective reads. |
Let's confirm that. It used to be possible (and maybe still is) to use |
/contributor add @rose00 |
@matias9927 |
It looks like Chen is correct here, reflective writing of static finals doesn't seem to be possible. At the very least, setAccessible() won't prevent an exception like this: With regards to your suggested test cases, the best I can do at the moment is add the first one on that list. |
That's great! It removes a bunch cases we'll never have to worry about |
A subtle change got swept up in the merge: |
if (vererrs != null && !vererrs.isEmpty()) { | ||
System.out.println(vererrs); | ||
var cm = ClassFile.of().parse(classBytes); | ||
System.out.println("" + cm + cm.fields() + cm.methods() + cm.methods().get(0).code().orElseThrow().elementList()); |
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.
System.out.println("" + cm + cm.fields() + cm.methods() + cm.methods().get(0).code().orElseThrow().elementList()); | |
System.out.println(cm.toDebugString()); |
Note we can't fail here: ClassFile.verify is not up-to-date with strict fields. I don't know who originally wrote the VerificationTable
code that is supposed to be on parity with stackMapTable.cpp
, but that code is complex and is bound to diverge from whatever we have in the C++ code there.
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. Thanks Matias
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.
I can keep an eye on the verification part of classfile and investigate that from the language and tooling side. Please simplify the debug output of a ClassModel as I suggested above. Otherwise, changes to java.lang.invoke looks good.
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 to me.
Thank you for this thorough work on strict fields.
test/hotspot/jtreg/ProblemList.txt
Outdated
@@ -131,6 +131,7 @@ containers/docker/TestJcmdWithSideCar.java 8341518 linux-x64 | |||
runtime/AccModule/ConstModule.java 8294051 generic-all | |||
runtime/valhalla/inlinetypes/CircularityTest.java 8349037 generic-all | |||
runtime/valhalla/inlinetypes/verifier/StrictInstanceFieldsTest.java 8357141 generic-all | |||
runtime/valhalla/inlinetypes/verifier/StrictStaticFieldsTest.java 8357141 generic-all |
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.
8357141 is now closed. We might need a dedicated new issue to re-enable these tests.
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.
Thanks for letting me know! I've updated the bug number to CODETOOLS-7904031 for both StrictStaticFieldsTest and StrictInstanceFieldsTest.
I think this is ready for integration; we can fine tune the test and other things later. |
Thank you for the reviews @DanHeidinga @fparain and @liach and thank you @rose00 for the prototype! |
@matias9927 Pushed as commit f4bd868. 💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored. |
This patch enables and implements verification for fields with the ACC_STATIC and ACC_STRICT modifiers. To enforce strictness on static fields, the reads and writes on the field are tracked dynamically to ensure that the field is written before being read and written to before.
Progress
Issue
Reviewers
Contributors
<[email protected]>
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/valhalla.git pull/1465/head:pull/1465
$ git checkout pull/1465
Update a local copy of the PR:
$ git checkout pull/1465
$ git pull https://git.openjdk.org/valhalla.git pull/1465/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 1465
View PR using the GUI difftool:
$ git pr show -t 1465
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/valhalla/pull/1465.diff
Using Webrev
Link to Webrev Comment