[9] RFR (S): 8157181: Compilers accept modification of final fields outside initializer methods
Zoltán Majó
zoltan.majo at oracle.com
Fri Jun 10 14:50:40 UTC 2016
Hi Vladimir,
thank you for the feedback!
On 06/10/2016 02:56 PM, Vladimir Ivanov wrote:
> http://cr.openjdk.java.net/~zmajo/8157181/webrev.08
>
> I'm fine with enabling the checks only for 9 for now and handle 7 & 8
> class files separately.
OK, thanks!
>
>>> src/share/vm/classfile/verifier.cpp:
>>>
>>> + void
>>> Verifier::detect_invalid_final_field_accesses(instanceKlassHandle
>>> klass) {
>>>
>>> The name is misleading: such accesses are legal according to JVMS-6
>>> (and earlier). Maybe something like
>>> detect_initialized_final_field_updates?
>>>
>>> Same for:
>>> + bool has_invalid_final_access() const { return
>>> access_flags().has_field_invalid_final_access(); }
>>> + void set_has_invalid_final_access(const bool value) {
>>> + JVM_ACC_FIELD_INVALID_FINAL_ACCESS = 0x00000100,
>>>
>>
>> Yes, that name sounds better and I've updated the code accordingly.
>
> A leftover:
>
> src/share/vm/runtime/fieldDescriptor.hpp:
>
> + void set_has_invalid_final_access(const bool value) {
Thanks for catching that, corrected.
>
>>> ---
>>> I don't see much benefit in tracking the problematic updates
>>> per-methods and not per-class. So, you could do the checks in
>>> ClassVerifier::verify_field_instructions [1] and mark the whole class
>>> (ClassVerifier::_klass) as containing problematic instructions.
>>
>> The problematic updates are tracked *per-field* (and not *per-method* or
>> * per-class*).
>>
>> Initially, I had a version that tracked problematic instructions on a
>> per-class basis. With that version, once a problematic instruction was
>> detected, the whole class was marked as containing problematic
>> instructions. As a result, constant folding was disabled for accesses to
>> *all* final fields of that class.
>>
>> Tracking problematic updates on a per-field basis seems to be less
>> intrusive. That way we disable folding *only for fields to which
>> problematic accesses are made* (and leave constant folding enabled for
>> accesses to all other fields). So we can fold some accesses even for
>> "misbehaving" classes. I hope that is fine.
>>
>> Regarding doing checks in verify_field_instructions() [2]: It turns out
>> that we have two verifiers, one that is called for
>>
>> major_version >= STACKMAP_ATTRIBUTE_MAJOR_VERSION
>>
>> and a second one that is called for
>>
>> major_version < STACKMAP_ATTRIBUTE_MAJOR_VERSION.
>>
>> ClassVerifier::verify_field_instructions() is called only for class
>> files >= STACKMAP_ATTRIBUTE_MAJOR_VERSION so we need to call
>> detect_initialized_final_field_updates() after verification.
>>
>> But I think it is a good idea to move the check to the linkage phase (as
>> you've suggested above).
>
> While browsing the code, I got another idea: what about piggybacking
> on Rewriter pass (see Rewriter::scan_method)? It already sets some
> per-method properties (e.g., Method::has_monitor_bytecodes()).
>
> I'd like to avoid additional pass over the bytecode, because it can
> cause slow downs during class loading (just a precaution - I haven't
> measured the impact).
I understand your concern and have moved the detection of initialized
final field updates to Rewriter::scan_method().
>
>>> ---
>>> src/share/vm/interpreter/linkResolver.cpp:
>>>
>>> THROW(vmSymbols::java_lang_IllegalAccessError());
>>>
>>> + THROW(vmSymbols::java_lang_IllegalAccessError());
>>>
>>> Please, add an error message for diagnostic purposes.
>>
>> OK, I did that.
>
> src/share/vm/interpreter/linkResolver.cpp:
>
> + if (is_put && fd.access_flags().is_final()) {
> + ResourceMark rm(THREAD);
> + char msg[200];
>
> There's much more convenient way now - stringStream. It takes care of
> memory management in resource area for you and provides convenient
> utility functions:
>
> ResourceMark rm(THREAD);
> stringStream ss;
> ss.printf("...", ...);
> THROW_MSG(vmSymbols::java_lang_IllegalAccessError(), ss.as_string());
OK, I changed that part.
>
> ---
> + if ((byte == Bytecodes::_putstatic && fd.is_static() &&
> !m()->is_static_initializer()) ||
> + ((byte == Bytecodes::_putfield || byte ==
> Bytecodes::_nofast_putfield) && !fd.is_static() &&
> !m->is_object_initializer())) {
>
> Maybe refactor it to make more readable?
>
> bool is_static_final_update = ...;
> bool is_instance_final_update = ...;
> if (is_static_final_update || is_instance_final_update) {
OK, I did that.
> ...
>
> Otherwise, looks good.
Thank you! Please let me know if you spot anything with the newest changes.
Here is the updated webrev:
http://cr.openjdk.java.net/~zmajo/8157181/webrev.09/
JPRT is running.
Best regards,
Zoltan
>
> Best regards,
> Vladimir Ivanov
More information about the hotspot-dev
mailing list