--- old/src/share/vm/classfile/classFileParser.cpp 2015-12-05 15:20:02.658452214 +0100 +++ new/src/share/vm/classfile/classFileParser.cpp 2015-12-05 15:20:01.602452184 +0100 @@ -1717,6 +1717,21 @@ } coll->set_contended_group(group_index); } + // The Accessor-Annotation specifies the field name it + // belongs to. The index of the symbol is later stored in + // the method. + if (id == AnnotationCollector::_method_Accessor_Method) { + u2 group_index = 0; + if (count == 1 + && s_size == (index - index0) // match size + && s_tag_val == *(abase + tag_off) + && member == vmSymbols::value_name()) { + group_index = Bytes::get_Java_u2(abase + s_con_off); + if (_cp->symbol_at(group_index)->utf8_length() != 0) { + coll->set_accessor_field_name(group_index); + } + } + } } } @@ -1757,6 +1772,9 @@ if (_location != _in_method) break; // only allow for methods if (!privileged) break; // only allow in privileged code return _method_HotSpotIntrinsicCandidate; + case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_reflect_Accessor_signature): + if (_location != _in_method) break; // only allow for methods + return _method_Accessor_Method; #if INCLUDE_JVMCI case vmSymbols::VM_SYMBOL_ENUM_NAME(jdk_vm_ci_hotspot_Stable_signature): if (_location != _in_field) break; // only allow for fields @@ -1804,6 +1822,9 @@ m->set_hidden(true); if (has_annotation(_method_HotSpotIntrinsicCandidate) && !m->is_synthetic()) m->set_intrinsic_candidate(true); + if (is_accessor()) { + m->set_accessor_field_name(accessor_field_name()); + } } void ClassFileParser::ClassAnnotationCollector::apply_to(instanceKlassHandle k) { --- old/src/share/vm/classfile/classFileParser.hpp 2015-12-05 15:20:03.418452235 +0100 +++ new/src/share/vm/classfile/classFileParser.hpp 2015-12-05 15:20:03.286452231 +0100 @@ -131,13 +131,15 @@ _method_LambdaForm_Compiled, _method_LambdaForm_Hidden, _method_HotSpotIntrinsicCandidate, + _method_Accessor_Method, _sun_misc_Contended, _field_Stable, _annotation_LIMIT }; const Location _location; - int _annotations_present; - u2 _contended_group; + int _annotations_present; + u2 _contended_group; + u2 _accessor_field_name; AnnotationCollector(Location location) : _location(location), _annotations_present(0) @@ -168,6 +170,12 @@ void set_stable(bool stable) { set_annotation(_field_Stable); } bool is_stable() const { return has_annotation(_field_Stable); } + + void set_accessor_field_name(u2 fname) { _accessor_field_name = fname; } + u2 accessor_field_name() { return _accessor_field_name; } + + bool is_accessor() const { return has_annotation(_method_Accessor_Method); } + }; // This class also doubles as a holder for metadata cleanup. --- old/src/share/vm/classfile/vmSymbols.hpp 2015-12-05 15:20:03.826452246 +0100 +++ new/src/share/vm/classfile/vmSymbols.hpp 2015-12-05 15:20:03.698452243 +0100 @@ -267,6 +267,8 @@ \ /* Intrinsic Annotation (JDK 9 and above) */ \ template(jdk_internal_HotSpotIntrinsicCandidate_signature, "Ljdk/internal/HotSpotIntrinsicCandidate;") \ + /* Accessor Annotation (Prototype) */ \ + template(java_lang_reflect_Accessor_signature, "Ljava/lang/reflect/Accessor;") \ \ /* Support for JSR 292 & invokedynamic (JDK 1.7 and above) */ \ template(java_lang_invoke_CallSite, "java/lang/invoke/CallSite") \ --- old/src/share/vm/interpreter/bytecodeInterpreter.cpp 2015-12-05 15:20:04.482452265 +0100 +++ new/src/share/vm/interpreter/bytecodeInterpreter.cpp 2015-12-05 15:20:04.218452257 +0100 @@ -1947,93 +1947,143 @@ cache = cp->entry_at(index); } + if (cache->is_field_entry()) { #ifdef VM_JVMTI - if (_jvmti_interp_events) { - int *count_addr; - oop obj; - // Check to see if a field modification watch has been set - // before we take the time to call into the VM. - count_addr = (int *)JvmtiExport::get_field_access_count_addr(); - if ( *count_addr > 0 ) { - if ((Bytecodes::Code)opcode == Bytecodes::_getstatic) { - obj = (oop)NULL; - } else { - obj = (oop) STACK_OBJECT(-1); - VERIFY_OOP(obj); + if (_jvmti_interp_events) { + int *count_addr; + oop obj; + // Check to see if a field modification watch has been set + // before we take the time to call into the VM. + count_addr = (int *)JvmtiExport::get_field_access_count_addr(); + if ( *count_addr > 0 ) { + if ((Bytecodes::Code)opcode == Bytecodes::_getstatic) { + obj = (oop)NULL; + } else { + obj = (oop) STACK_OBJECT(-1); + VERIFY_OOP(obj); + } + CALL_VM(InterpreterRuntime::post_field_access(THREAD, + obj, + cache), + handle_exception); } - CALL_VM(InterpreterRuntime::post_field_access(THREAD, - obj, - cache), - handle_exception); } - } #endif /* VM_JVMTI */ - oop obj; - if ((Bytecodes::Code)opcode == Bytecodes::_getstatic) { - Klass* k = cache->f1_as_klass(); - obj = k->java_mirror(); - MORE_STACK(1); // Assume single slot push - } else { - obj = (oop) STACK_OBJECT(-1); - CHECK_NULL(obj); - } - - // - // Now store the result on the stack - // - TosState tos_type = cache->flag_state(); - int field_offset = cache->f2_as_index(); - if (cache->is_volatile()) { - if (support_IRIW_for_not_multiple_copy_atomic_cpu) { - OrderAccess::fence(); - } - if (tos_type == atos) { - VERIFY_OOP(obj->obj_field_acquire(field_offset)); - SET_STACK_OBJECT(obj->obj_field_acquire(field_offset), -1); - } else if (tos_type == itos) { - SET_STACK_INT(obj->int_field_acquire(field_offset), -1); - } else if (tos_type == ltos) { - SET_STACK_LONG(obj->long_field_acquire(field_offset), 0); - MORE_STACK(1); - } else if (tos_type == btos) { - SET_STACK_INT(obj->byte_field_acquire(field_offset), -1); - } else if (tos_type == ctos) { - SET_STACK_INT(obj->char_field_acquire(field_offset), -1); - } else if (tos_type == stos) { - SET_STACK_INT(obj->short_field_acquire(field_offset), -1); - } else if (tos_type == ftos) { - SET_STACK_FLOAT(obj->float_field_acquire(field_offset), -1); + oop obj; + if ((Bytecodes::Code)opcode == Bytecodes::_getstatic) { + Klass* k = cache->f1_as_klass(); + obj = k->java_mirror(); + MORE_STACK(1); // Assume single slot push } else { - SET_STACK_DOUBLE(obj->double_field_acquire(field_offset), 0); - MORE_STACK(1); + obj = (oop) STACK_OBJECT(-1); + CHECK_NULL(obj); } - } else { - if (tos_type == atos) { - VERIFY_OOP(obj->obj_field(field_offset)); - SET_STACK_OBJECT(obj->obj_field(field_offset), -1); - } else if (tos_type == itos) { - SET_STACK_INT(obj->int_field(field_offset), -1); - } else if (tos_type == ltos) { - SET_STACK_LONG(obj->long_field(field_offset), 0); - MORE_STACK(1); - } else if (tos_type == btos) { - SET_STACK_INT(obj->byte_field(field_offset), -1); - } else if (tos_type == ctos) { - SET_STACK_INT(obj->char_field(field_offset), -1); - } else if (tos_type == stos) { - SET_STACK_INT(obj->short_field(field_offset), -1); - } else if (tos_type == ftos) { - SET_STACK_FLOAT(obj->float_field(field_offset), -1); + + // + // Now store the result on the stack + // + TosState tos_type = cache->flag_state(); + int field_offset = cache->f2_as_index(); + if (cache->is_volatile()) { + if (support_IRIW_for_not_multiple_copy_atomic_cpu) { + OrderAccess::fence(); + } + if (tos_type == atos) { + VERIFY_OOP(obj->obj_field_acquire(field_offset)); + SET_STACK_OBJECT(obj->obj_field_acquire(field_offset), -1); + } else if (tos_type == itos) { + SET_STACK_INT(obj->int_field_acquire(field_offset), -1); + } else if (tos_type == ltos) { + SET_STACK_LONG(obj->long_field_acquire(field_offset), 0); + MORE_STACK(1); + } else if (tos_type == btos) { + SET_STACK_INT(obj->byte_field_acquire(field_offset), -1); + } else if (tos_type == ctos) { + SET_STACK_INT(obj->char_field_acquire(field_offset), -1); + } else if (tos_type == stos) { + SET_STACK_INT(obj->short_field_acquire(field_offset), -1); + } else if (tos_type == ftos) { + SET_STACK_FLOAT(obj->float_field_acquire(field_offset), -1); + } else { + SET_STACK_DOUBLE(obj->double_field_acquire(field_offset), 0); + MORE_STACK(1); + } } else { - SET_STACK_DOUBLE(obj->double_field(field_offset), 0); - MORE_STACK(1); + if (tos_type == atos) { + VERIFY_OOP(obj->obj_field(field_offset)); + SET_STACK_OBJECT(obj->obj_field(field_offset), -1); + } else if (tos_type == itos) { + SET_STACK_INT(obj->int_field(field_offset), -1); + } else if (tos_type == ltos) { + SET_STACK_LONG(obj->long_field(field_offset), 0); + MORE_STACK(1); + } else if (tos_type == btos) { + SET_STACK_INT(obj->byte_field(field_offset), -1); + } else if (tos_type == ctos) { + SET_STACK_INT(obj->char_field(field_offset), -1); + } else if (tos_type == stos) { + SET_STACK_INT(obj->short_field(field_offset), -1); + } else if (tos_type == ftos) { + SET_STACK_FLOAT(obj->float_field(field_offset), -1); + } else { + SET_STACK_DOUBLE(obj->double_field(field_offset), 0); + MORE_STACK(1); + } } - } + + UPDATE_PC_AND_CONTINUE(3); + }else { + // mostly copied from _invokevirtual and _invokestatic + istate->set_msg(call_method); + Method* callee; + if ((Bytecodes::Code)opcode == Bytecodes::_getstatic) { + callee = cache->f1_as_method(); - UPDATE_PC_AND_CONTINUE(3); - } + // Profile call. + BI_PROFILE_UPDATE_CALL(); + }else { + // get receiver + int parms = cache->parameter_size(); + // this works but needs a resourcemark and seems to create a vtable on every call: + // Method* callee = rcvr->klass()->vtable()->method_at(cache->f2_as_index()); + // + // this fails with an assert + // InstanceKlass* rcvrKlass = InstanceKlass::cast(STACK_OBJECT(-parms)->klass()); + // but this works + oop rcvr = STACK_OBJECT(-parms); + VERIFY_OOP(rcvr); + InstanceKlass* rcvrKlass = (InstanceKlass*)rcvr->klass(); + /* + Executing this code in java.lang.String: + public String(char value[]) { + this.count = value.length; + this.value = (char[])value.clone(); + } + a find on rcvr->klass() reports: + {type array char}{type array class} + - klass: {other class} + but using InstanceKlass::cast(STACK_OBJECT(-parms)->klass()) causes in assertion failure + because rcvr->klass()->is_instance_klass() == 0 + However it seems to have a vtable in the right location. Huh? + Because vtables have the same offset for ArrayKlass and InstanceKlass. + */ + callee = (Method*) rcvrKlass->start_of_vtable()[ cache->f2_as_index()]; + // Profile virtual call. + BI_PROFILE_UPDATE_VIRTUALCALL(rcvr->klass()); + } + istate->set_callee(callee); + istate->set_callee_entry_point(callee->from_interpreted_entry()); +#ifdef VM_JVMTI + if (JvmtiExport::can_post_interpreter_events() && THREAD->is_interp_only_mode()) { + istate->set_callee_entry_point(callee->interpreter_entry()); + } +#endif /* VM_JVMTI */ + istate->set_bcp_advance(3); + UPDATE_PC_AND_RETURN(0); // I'll be back... + } + } CASE(_putfield): CASE(_putstatic): { --- old/src/share/vm/interpreter/interpreterRuntime.cpp 2015-12-05 15:20:04.826452274 +0100 +++ new/src/share/vm/interpreter/interpreterRuntime.cpp 2015-12-05 15:20:04.706452271 +0100 @@ -588,24 +588,55 @@ !klass->is_initialized()); Bytecodes::Code get_code = (Bytecodes::Code)0; - if (!uninitialized_static) { - get_code = ((is_static) ? Bytecodes::_getstatic : Bytecodes::_getfield); - if (is_put || !info.access_flags().is_final()) { - put_code = ((is_static) ? Bytecodes::_putstatic : Bytecodes::_putfield); + + if (info.is_accessor()) { + // If it is an accessor (not a real field) change bytecode + // semantic to be a call to the accessor-method) + InstanceKlass* current_klass = method(thread)->method_holder(); + // find method based on get/put + Method* m = klass->method_with_idnum(is_put + ? info.get_put_accessor() + : info.get_get_accessor() + ); + + // Create a linkinfo to resolve the method + LinkInfo linfo(klass,m->name(),m->signature(),current_klass,true); + + // Resolve static/non-static method and initialize cp_cache_entry + // accordingly + if (is_static) { + methodHandle mh = LinkResolver::resolve_static_call_or_null(linfo); + cp_cache_entry->set_direct_call( + Bytecodes::_invokestatic, + mh); + }else { + methodHandle mh = LinkResolver::resolve_virtual_call_or_null(klass,linfo); + cp_cache_entry->set_vtable_call( + Bytecodes::_invokevirtual, + mh, + m->vtable_index()); + } + + }else { + if (!uninitialized_static) { + get_code = ((is_static) ? Bytecodes::_getstatic : Bytecodes::_getfield); + if (is_put || !info.access_flags().is_final()) { + put_code = ((is_static) ? Bytecodes::_putstatic : Bytecodes::_putfield); + } } - } - cp_cache_entry->set_field( - get_code, - put_code, - info.field_holder(), - info.index(), - info.offset(), - state, - info.access_flags().is_final(), - info.access_flags().is_volatile(), - pool->pool_holder() - ); + cp_cache_entry->set_field( + get_code, + put_code, + info.field_holder(), + info.index(), + info.offset(), + state, + info.access_flags().is_final(), + info.access_flags().is_volatile(), + pool->pool_holder() + ); + } } --- old/src/share/vm/oops/instanceKlass.cpp 2015-12-05 15:20:05.438452291 +0100 +++ new/src/share/vm/oops/instanceKlass.cpp 2015-12-05 15:20:05.202452285 +0100 @@ -1169,6 +1169,57 @@ return true; } } + // field not found. Try to search for an accessor-method-pair + bool found = false; + int length = sig->utf8_length(); + if (length > 0) { + // extract original signature+ one \0 char + char fSig[length+1]; + sig->as_C_string(fSig,length+1); + + // the signature of the get-Method is two chars longer + \0 + char get_sig[length+3]; + sprintf(get_sig,"()%s",fSig); + + // the signature of the put-Method is three chars longer + \0 + char put_sig[length+4]; + sprintf(put_sig,"(%s)V",fSig); + + // Look through all methods in the class. + Array* methods = this->methods(); + for (int i = 0; i < methods->length(); i++) { + Method* m = methods->at(i); + + // extract name of accessor-field + u2 af = m->accessor_field_name(); + // Is zero an valid index? + if (af != 0) { + Symbol* fn = m->constMethod()->constants()->symbol_at(af); + char mname[name->utf8_length()+1]; + name->as_C_string(mname,name->utf8_length()+1); + // if fieldname matches. record match and store name and + // signature in fielddescriptor + if (fn->equals(mname)) { + found = true; + fd->set_field_name_from_accessor(af); + fd->set_sig_for_accessor(sig); + if (m->signature()->equals(get_sig)) { + // remember get method + fd->set_get_accessor(m->method_idnum()); + }else if (m->signature()->equals(put_sig)) { + // remember put method + fd->set_put_accessor(m->method_idnum()); + } + } + } + } + } + if (found) { + // initialize accesor-values in fielddescriptor + fd->reinitialize_accessor(const_cast(this)); + // is the fielddescriptor a valid accessor-fielddescriptor? + return fd->is_accessor(); + } return false; } --- old/src/share/vm/oops/method.cpp 2015-12-05 15:20:05.822452302 +0100 +++ new/src/share/vm/oops/method.cpp 2015-12-05 15:20:05.682452298 +0100 @@ -103,6 +103,7 @@ set_signature_handler(NULL); } + set_accessor_field_name(0); NOT_PRODUCT(set_compiled_invocation_count(0);) } --- old/src/share/vm/oops/method.hpp 2015-12-05 15:20:06.222452313 +0100 +++ new/src/share/vm/oops/method.hpp 2015-12-05 15:20:06.078452309 +0100 @@ -72,6 +72,7 @@ int _result_index; // C++ interpreter needs for converting results to/from stack #endif u2 _intrinsic_id; // vmSymbols::intrinsic_id (0 == _none) + u2 _accessor_field_name; // Flags enum Flags { @@ -775,6 +776,10 @@ // Support for inlining of intrinsic methods vmIntrinsics::ID intrinsic_id() const { return (vmIntrinsics::ID) _intrinsic_id; } void set_intrinsic_id(vmIntrinsics::ID id) { _intrinsic_id = (u2) id; } + + // Accessor-Method + u2 accessor_field_name() { return _accessor_field_name; } + void set_accessor_field_name(u2 field_name) { _accessor_field_name = field_name; } // Helper routines for intrinsic_id() and vmIntrinsics::method(). void init_intrinsic_id(); // updates from _none if a match --- old/src/share/vm/runtime/fieldDescriptor.cpp 2015-12-05 15:20:06.630452325 +0100 +++ new/src/share/vm/runtime/fieldDescriptor.cpp 2015-12-05 15:20:06.426452319 +0100 @@ -98,7 +98,31 @@ return constants()->uncached_string_at(initial_value_index(), THREAD); } +void fieldDescriptor::reinitialize_accessor(InstanceKlass* ik) { + //assert(_getAccessor != -1 && _putAccessor != -1, "asymetric accesor configuration"); + if (_cp.is_null()) { + _cp = constantPoolHandle(Thread::current(), ik->constants()); + assert(!_cp.is_null(), "must be initialized"); + } + Method* getM = ik->method_with_idnum(_getAccessor); + Method* putM = ik->method_with_idnum(_putAccessor); + + assert(getM->access_flags().get_flags() == putM->access_flags().get_flags(), "asymetic accessiblity in accessor configuration"); + _access_flags = accessFlags_from(getM->access_flags().get_flags()); + + // Somehow the memory has to be freed. Maybe create a destructor for fieldDescriptor + _accessor_info = NEW_C_HEAP_ARRAY(FieldInfo,1,mtInternal); + // How do I get an index for the signature. There maybe no signature in constant-pool of this class. + // Fortunatly the signature is not used for this prototype. + _accessor_info->initialize(/*_access_flags*/_access_flags.as_short(),_field_name_from_accessor,/*sig_index*/0,/*initval*/0); + // fieldDescriptor for accessor initialized completely + // maybe more checks are needed in future. + _is_accessor = true; +} + void fieldDescriptor::reinitialize(InstanceKlass* ik, int index) { + _is_accessor = false; + _accessor_info = NULL; if (_cp.is_null() || field_holder() != ik) { _cp = constantPoolHandle(Thread::current(), ik->constants()); // _cp should now reference ik's constant pool; i.e., ik is now field_holder. --- old/src/share/vm/runtime/fieldDescriptor.hpp 2015-12-05 15:20:06.998452335 +0100 +++ new/src/share/vm/runtime/fieldDescriptor.hpp 2015-12-05 15:20:06.850452331 +0100 @@ -40,7 +40,14 @@ AccessFlags _access_flags; int _index; // the field index constantPoolHandle _cp; - + u2 _putAccessor; + u2 _getAccessor; + bool _is_accessor; + u2 _field_name_from_accessor; + FieldInfo* _accessor_info; + Symbol* _sig_for_accessor; + + // update the access_flags for the field in the klass void update_klass_field_access_flag() { InstanceKlass* ik = field_holder(); @@ -48,23 +55,59 @@ } FieldInfo* field() const { - InstanceKlass* ik = field_holder(); - return ik->field(_index); + if (_is_accessor) { + return _accessor_info; + }else { + InstanceKlass* ik = field_holder(); + return ik->field(_index); + } } public: fieldDescriptor() { DEBUG_ONLY(_index = badInt); + _is_accessor = false; + _index = -1; + _accessor_info = NULL; } fieldDescriptor(InstanceKlass* ik, int index) { DEBUG_ONLY(_index = badInt); reinitialize(ik, index); } + + void set_sig_for_accessor(Symbol* sig) { + this->_sig_for_accessor = sig; + } + + void set_field_name_from_accessor(u2 field_name_from_accessor) { + this->_field_name_from_accessor = field_name_from_accessor; + } + + bool is_accessor() { + return _is_accessor; + } + + u2 get_get_accessor() { + return _getAccessor; + } + + u2 get_put_accessor() { + return _putAccessor; + } + + void set_get_accessor(u2 index) { + _getAccessor = index; + } + + void set_put_accessor(u2 index) { + _putAccessor = index; + } + Symbol* name() const { - return field()->name(_cp); + return _is_accessor ? constants()->symbol_at(_field_name_from_accessor) : field()->name(_cp); } Symbol* signature() const { - return field()->signature(_cp); + return _is_accessor ? _sig_for_accessor : field()->signature(_cp); } InstanceKlass* field_holder() const { return _cp->pool_holder(); } ConstantPool* constants() const { return _cp(); } @@ -120,6 +163,7 @@ // Initialization void reinitialize(InstanceKlass* ik, int index); + void reinitialize_accessor(InstanceKlass* ik); // Print void print() { print_on(tty); }