--- old/src/hotspot/share/interpreter/linkResolver.cpp 2018-09-28 11:53:19.000000000 -0700 +++ new/src/hotspot/share/interpreter/linkResolver.cpp 2018-09-28 11:53:18.000000000 -0700 @@ -25,7 +25,7 @@ #include "precompiled.hpp" #include "jvm.h" #include "classfile/defaultMethods.hpp" -#include "classfile/javaClasses.hpp" +#include "classfile/javaClasses.inline.hpp" #include "classfile/resolutionErrors.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" @@ -37,6 +37,7 @@ #include "interpreter/linkResolver.hpp" #include "logging/log.hpp" #include "logging/logStream.hpp" +#include "memory/oopFactory.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/cpCache.inline.hpp" @@ -95,25 +96,24 @@ void CallInfo::set_handle(const methodHandle& resolved_method, Handle resolved_appendix, - Handle resolved_method_type, TRAPS) { - set_handle(SystemDictionary::MethodHandle_klass(), resolved_method, resolved_appendix, resolved_method_type, CHECK); + bool has_local_sig, TRAPS) { + set_handle(SystemDictionary::MethodHandle_klass(), resolved_method, resolved_appendix, has_local_sig, CHECK); } void CallInfo::set_handle(Klass* resolved_klass, const methodHandle& resolved_method, Handle resolved_appendix, - Handle resolved_method_type, TRAPS) { - if (resolved_method.is_null()) { - THROW_MSG(vmSymbols::java_lang_InternalError(), "resolved method is null"); - } + bool has_local_sig, TRAPS) { + guarantee(resolved_method.not_null(), "resolved method is null"); assert(resolved_method->intrinsic_id() == vmIntrinsics::_invokeBasic || resolved_method->is_compiled_lambda_form(), "linkMethod must return one of these"); int vtable_index = Method::nonvirtual_vtable_index; assert(!resolved_method->has_vtable_index(), ""); set_common(resolved_klass, resolved_klass, resolved_method, resolved_method, CallInfo::direct_call, vtable_index, CHECK); - _resolved_appendix = resolved_appendix; - _resolved_method_type = resolved_method_type; + _resolved_appendix = resolved_appendix; + _has_local_sig = has_local_sig; + assert(has_local_sig, "MHs and indy are always sig-poly"); } void CallInfo::set_common(Klass* resolved_klass, @@ -131,6 +131,7 @@ _call_kind = kind; _call_index = index; _resolved_appendix = Handle(); + _has_local_sig = false; DEBUG_ONLY(verify()); // verify before making side effects CompilationPolicy::compile_if_required(selected_method, THREAD); @@ -186,6 +187,7 @@ _call_kind = kind; _call_index = index; _resolved_appendix = Handle(); + _has_local_sig = false; // Find or create a ResolvedMethod instance for this Method* set_resolved_method_name(CHECK); @@ -236,6 +238,196 @@ #endif //------------------------------------------------------------------------------------------------------------------------ +// Implementation of BootstrapInfo + +BootstrapInfo::BootstrapInfo(const constantPoolHandle& pool, int bss_index, int indy_index) + : _pool(pool), + _bss_index(bss_index), + _indy_index(indy_index), + _is_method_call(indy_index != 0), + // derived and eagerly cached: + _argc( pool->bootstrap_argument_count_at(bss_index) ), + _name( pool->uncached_name_ref_at(bss_index) ), + _signature( pool->uncached_signature_ref_at(bss_index) ) +{ + _is_resolved = false; + assert(pool->tag_at(bss_index).has_bootstrap(), ""); + assert(indy_index == 0 || pool->invokedynamic_bootstrap_ref_index_at(indy_index) == bss_index, ""); +} + +oop BootstrapInfo::arg_value(int i) const { + // safely pull oop from _arg_values + if (_arg_values.is_null()) return NULL; + if (i < 0 || i >= _arg_values->length()) return NULL; + return _arg_values->obj_at(i); +} + +bool BootstrapInfo::resolve_previously_linked_invokedynamic(CallInfo& result, TRAPS) { + assert(_indy_index != 0, ""); + ConstantPoolCacheEntry* cpce = invokedynamic_cp_cache_entry(); + if (!cpce->is_f1_null()) { + methodHandle method( THREAD, cpce->f1_as_method()); + Handle appendix( THREAD, cpce->appendix_if_resolved(_pool)); + result.set_handle(method, appendix, true, THREAD); + Exceptions::wrap_dynamic_exception(CHECK_false); + return true; + } else if (cpce->indy_resolution_failed()) { + int encoded_index = ResolutionErrorTable::encode_cpcache_index(_indy_index); + ConstantPool::throw_resolution_error(_pool, encoded_index, CHECK_false); + return true; + } else { + return false; + } +} + +Handle BootstrapInfo::resolve_bsm(TRAPS) { + if (_bsm.not_null()) return _bsm; + // Unpack the BSM by resolving the MH constant. + assert(_pool->tag_at(bsm_index()).is_method_handle(), "classfile structural constraint"); + oop bsm_oop = _pool->resolve_possibly_cached_constant_at(bsm_index(), THREAD); + Exceptions::wrap_dynamic_exception(CHECK_(_bsm)); + guarantee(java_lang_invoke_MethodHandle::is_instance(bsm_oop), + "classfile must supply a valid BSM"); + _bsm = Handle(THREAD, bsm_oop); + return _bsm; +} + +// What kind of BSM is it? +// - If the BSM takes exactly two arguments (second non-array), then mode=bsci. +// - Otherwise, if it's indy or there's a leading Lookup argument, then mode=metadata. +// - Otherwise, mode=expression. +enum BootstrapInfo::BSMMode BootstrapInfo::bsm_mode() const { + assert(_bsm.not_null(), "resolve_bsm first"); + if (_bsm.is_null()) return _unknown; + oop bsm_type = java_lang_invoke_MethodHandle::type(_bsm()); + int arity = java_lang_invoke_MethodType::ptype_count(bsm_type); + Klass* ptype0 = NULL; + Klass* ptype1 = NULL; + if (arity > 0) { + ptype0 = java_lang_Class::as_Klass(java_lang_invoke_MethodType::ptype(bsm_type, 0)); + if (arity > 1) { + ptype1 = java_lang_Class::as_Klass(java_lang_invoke_MethodType::ptype(bsm_type, 1)); + } + } + if (arity == 2 && ptype1 != NULL && !ptype1->is_array_klass()) + return _bsci; + if (_indy_index != 0 || (ptype0 != NULL && ptype0->name() == vmSymbols::java_lang_invoke_MethodHandles_Lookup())) + return _metadata; + return _expression; +} + +void BootstrapInfo::resolve_metadata(TRAPS) { + resolve_bsm(CHECK); + Symbol* name = this->name(); + Symbol* type = this->signature(); + _name_arg = java_lang_String::create_from_symbol(name, CHECK); + if (type->byte_at(0) == '(') { + _type_arg = SystemDictionary::find_method_handle_type(type, caller(), THREAD); + } else { + _type_arg = SystemDictionary::find_java_mirror_for_type(type, caller(), SignatureStream::NCDFError, true, THREAD); + } + if (HAS_PENDING_EXCEPTION) { + bool continue_after_type_resolution_exception = false; + if (PENDING_EXCEPTION->is_a(SystemDictionary::LinkageError_klass()) && bsm_mode() == _bsci) + continue_after_type_resolution_exception = true; + // The type argument can fail to resolve, if the BSM is ready to handle it. + if (!continue_after_type_resolution_exception) { + return; // keep throwing + } + CLEAR_PENDING_EXCEPTION; + _type_arg = java_lang_String::create_from_symbol(type, CHECK); + } + assert(_type_arg.not_null(), ""); +} +void BootstrapInfo::resolve_args(ConstantPool::BootstrapArgumentReferenceMode resolving, TRAPS) { + arrayHandle buf; + if (resolving == ConstantPool::R_SYMREF) { + assert(_arg_indexes.is_null(), ""); + _arg_indexes = oopFactory::new_intArray_handle(_argc, CHECK); + buf = arrayHandle(THREAD, _arg_indexes()); + } else { + if (_arg_values.is_null()) { + _arg_values = oopFactory::new_objArray_handle(SystemDictionary::Object_klass(), _argc, CHECK); + } + buf = arrayHandle(THREAD, _arg_values()); + } + bool skip_recursion = (indy_index() == 0); + // if it's a condy call, leave it to the BSM to resolve sub-BSM calls + _pool->copy_bootstrap_arguments_at(_bss_index, 0, _argc, buf, 0, + resolving, Handle(), Handle(), + false, skip_recursion, CHECK); +} + +// there must be a LinkageError pending; try to save it and then throw +bool BootstrapInfo::save_and_throw_indy_exc(TRAPS) { + assert(HAS_PENDING_EXCEPTION, ""); + assert(_indy_index != 0, ""); + ConstantPoolCacheEntry* cpce = invokedynamic_cp_cache_entry(); + int encoded_index = ResolutionErrorTable::encode_cpcache_index(_indy_index); + bool recorded_res_status = cpce->save_and_throw_indy_exc(_pool, _bss_index, + encoded_index, + pool()->tag_at(_bss_index), + CHECK_false); + return recorded_res_status; +} + +void BootstrapInfo::resolve_newly_linked_invokedynamic(CallInfo& result, TRAPS) { + assert(is_resolved(), ""); + result.set_handle(resolved_method(), resolved_appendix(), true, THREAD); +} + +void BootstrapInfo::print_msg_on(outputStream* st, const char* msg) { + ResourceMark rm; + char what[20]; + if (_indy_index != 0) + sprintf(what, "indy#%d", decode_indy_index()); + else + sprintf(what, "condy"); + bool have_msg = (msg != NULL && strlen(msg) > 0); + tty->print_cr("%s%sBootstrap in %s %s@CP[%d] %s:%s%s BSMS[%d] BSM@CP[%d]%s argc=%d%s", + (have_msg ? msg : ""), (have_msg ? " " : ""), + caller()->name()->as_C_string(), + what, // "indy#42" or "condy" + _bss_index, + _name->as_C_string(), + _signature->as_C_string(), + (_type_arg.is_null() ? "" : "(resolved)"), + bsms_attr_index(), + bsm_index(), (_bsm.is_null() ? "" : "(resolved)"), + _argc, (_arg_values.is_null() ? "" : "(resolved)")); + if (_argc > 0) { + char argbuf[80]; + argbuf[0] = 0; + for (int i = 0; i < _argc; i++) { + int pos = strlen(argbuf); + if (pos + 20 > (int)sizeof(argbuf)) { + sprintf(argbuf + pos, "..."); + break; + } + if (i > 0) argbuf[pos++] = ','; + sprintf(argbuf+pos, "%d", arg_index(i)); + } + tty->print_cr(" argument indexes: {%s}", argbuf); + } + if (_bsm.not_null()) { + tty->print(" resolved BSM: "); _bsm->print(); + } + if (_arg_values.not_null()) { + int lines = 0; + for (int i = 0; i < _argc; i++) { + oop x = arg_value(i); + if (x != NULL) { + if (++lines > 6) { + tty->print_cr(" resolved arg[%d]: ...", i); + break; + } + tty->print(" resolved arg[%d]: ", i); x->print(); + } + } + } +} + +//------------------------------------------------------------------------------------------------------------------------ // Implementation of LinkInfo LinkInfo::LinkInfo(const constantPoolHandle& pool, int index, const methodHandle& current_method, TRAPS) { @@ -452,7 +644,6 @@ methodHandle LinkResolver::lookup_polymorphic_method( const LinkInfo& link_info, Handle *appendix_result_or_null, - Handle *method_type_result, TRAPS) { Klass* klass = link_info.resolved_klass(); Symbol* name = link_info.name(); @@ -520,7 +711,6 @@ full_signature, link_info.current_klass(), &appendix, - &method_type, CHECK_NULL); if (TraceMethodHandles) { ttyLocker ttyl; @@ -552,7 +742,6 @@ assert(appendix_result_or_null != NULL, ""); (*appendix_result_or_null) = appendix; - (*method_type_result) = method_type; } return result; } @@ -759,7 +948,7 @@ if (resolved_method.is_null()) { // JSR 292: see if this is an implicitly generated method MethodHandle.linkToVirtual(*...), etc - resolved_method = lookup_polymorphic_method(link_info, (Handle*)NULL, (Handle*)NULL, THREAD); + resolved_method = lookup_polymorphic_method(link_info, (Handle*)NULL, THREAD); if (HAS_PENDING_EXCEPTION) { nested_exception = Handle(THREAD, PENDING_EXCEPTION); CLEAR_PENDING_EXCEPTION; @@ -1714,109 +1903,82 @@ resolved_klass == SystemDictionary::VarHandle_klass(), ""); assert(MethodHandles::is_signature_polymorphic_name(link_info.name()), ""); Handle resolved_appendix; - Handle resolved_method_type; - methodHandle resolved_method = lookup_polymorphic_method(link_info, - &resolved_appendix, &resolved_method_type, CHECK); - result.set_handle(resolved_klass, resolved_method, resolved_appendix, resolved_method_type, CHECK); + methodHandle resolved_method = lookup_polymorphic_method(link_info, &resolved_appendix, CHECK); + result.set_handle(resolved_klass, resolved_method, resolved_appendix, true, CHECK); } -void LinkResolver::resolve_invokedynamic(CallInfo& result, const constantPoolHandle& pool, int index, TRAPS) { - Symbol* method_name = pool->name_ref_at(index); - Symbol* method_signature = pool->signature_ref_at(index); - Klass* current_klass = pool->pool_holder(); +void LinkResolver::resolve_invokedynamic(CallInfo& result, const constantPoolHandle& pool, int indy_index, TRAPS) { + ConstantPoolCacheEntry* cpce = pool->invokedynamic_cp_cache_entry_at(indy_index); + int pool_index = cpce->constant_pool_index(); // Resolve the bootstrap specifier (BSM + optional arguments). - Handle bootstrap_specifier; - // Check if CallSite has been bound already: - ConstantPoolCacheEntry* cpce = pool->invokedynamic_cp_cache_entry_at(index); - int pool_index = cpce->constant_pool_index(); + BootstrapInfo bootstrap_specifier(pool, pool_index, indy_index); - if (cpce->is_f1_null()) { - if (cpce->indy_resolution_failed()) { - ConstantPool::throw_resolution_error(pool, - ResolutionErrorTable::encode_cpcache_index(index), - CHECK); - } - - // The initial step in Call Site Specifier Resolution is to resolve the symbolic - // reference to a method handle which will be the bootstrap method for a dynamic - // call site. If resolution for the java.lang.invoke.MethodHandle for the bootstrap - // method fails, then a MethodHandleInError is stored at the corresponding bootstrap - // method's CP index for the CONSTANT_MethodHandle_info. So, there is no need to - // set the indy_rf flag since any subsequent invokedynamic instruction which shares - // this bootstrap method will encounter the resolution of MethodHandleInError. - oop bsm_info = pool->resolve_bootstrap_specifier_at(pool_index, THREAD); - Exceptions::wrap_dynamic_exception(CHECK); - assert(bsm_info != NULL, ""); - // FIXME: Cache this once per BootstrapMethods entry, not once per CONSTANT_InvokeDynamic. - bootstrap_specifier = Handle(THREAD, bsm_info); - } - if (!cpce->is_f1_null()) { - methodHandle method( THREAD, cpce->f1_as_method()); - Handle appendix( THREAD, cpce->appendix_if_resolved(pool)); - Handle method_type(THREAD, cpce->method_type_if_resolved(pool)); - result.set_handle(method, appendix, method_type, THREAD); - Exceptions::wrap_dynamic_exception(CHECK); - return; + // Check if CallSite has been bound already or failed already, and short circuit: + { + bool is_done = bootstrap_specifier.resolve_previously_linked_invokedynamic(result, CHECK); + if (is_done) return; } + // The initial step in Call Site Specifier Resolution is to resolve the symbolic + // reference to a method handle which will be the bootstrap method for a dynamic + // call site. If resolution for the java.lang.invoke.MethodHandle for the bootstrap + // method falis, then a MethodHandleInError is stored at the corresponding bootstrap + // method's CP index for the CONSTANT_MethodHandle_info. So, there is no need to + // set the indy_rf flag since any subsequent invokedynamic instruction which shares + // this bootstrap method will encounter the resolution of MethodHandleInError. if (TraceMethodHandles) { - ResourceMark rm(THREAD); - tty->print_cr("resolve_invokedynamic #%d %s %s in %s", - ConstantPool::decode_invokedynamic_index(index), - method_name->as_C_string(), method_signature->as_C_string(), - current_klass->name()->as_C_string()); - tty->print(" BSM info: "); bootstrap_specifier->print(); - } - - resolve_dynamic_call(result, pool_index, bootstrap_specifier, method_name, - method_signature, current_klass, THREAD); - if (HAS_PENDING_EXCEPTION && PENDING_EXCEPTION->is_a(SystemDictionary::LinkageError_klass())) { - int encoded_index = ResolutionErrorTable::encode_cpcache_index(index); - bool recorded_res_status = cpce->save_and_throw_indy_exc(pool, pool_index, - encoded_index, - pool()->tag_at(pool_index), - CHECK); - if (!recorded_res_status) { - // Another thread got here just before we did. So, either use the method - // that it resolved or throw the LinkageError exception that it threw. - if (!cpce->is_f1_null()) { - methodHandle method( THREAD, cpce->f1_as_method()); - Handle appendix( THREAD, cpce->appendix_if_resolved(pool)); - Handle method_type(THREAD, cpce->method_type_if_resolved(pool)); - result.set_handle(method, appendix, method_type, THREAD); - Exceptions::wrap_dynamic_exception(CHECK); - } else { - assert(cpce->indy_resolution_failed(), "Resolution failure flag not set"); - ConstantPool::throw_resolution_error(pool, encoded_index, CHECK); - } - return; - } - assert(cpce->indy_resolution_failed(), "Resolution failure flag wasn't set"); + bootstrap_specifier.print_msg_on(tty, "resolve_invokedynamic"); } + + resolve_dynamic_call(result, bootstrap_specifier, CHECK); + + // The returned linkage result is provisional up to the moment + // the interpreter or runtime performs a serialized check of + // the relevent CPCE::f1 field. This is done by the caller + // of this method, via CPCE::set_dynamic_call, which uses + // an ObjectLocker to do the final serialization of updates + // to CPCE state, including f1. } void LinkResolver::resolve_dynamic_call(CallInfo& result, - int pool_index, - Handle bootstrap_specifier, - Symbol* method_name, Symbol* method_signature, - Klass* current_klass, + BootstrapInfo& bootstrap_specifier, TRAPS) { - // JSR 292: this must resolve to an implicitly generated method MH.linkToCallSite(*...) + // JSR 292: this must resolve to an implicitly generated method + // such as MH.linkToCallSite(*...) or some other call-site shape. // The appendix argument is likely to be a freshly-created CallSite. - Handle resolved_appendix; - Handle resolved_method_type; - methodHandle resolved_method = - SystemDictionary::find_dynamic_call_site_invoker(current_klass, - pool_index, - bootstrap_specifier, - method_name, method_signature, - &resolved_appendix, - &resolved_method_type, - THREAD); - Exceptions::wrap_dynamic_exception(CHECK); - result.set_handle(resolved_method, resolved_appendix, resolved_method_type, THREAD); - Exceptions::wrap_dynamic_exception(CHECK); + // It may also be a MethodHandle from an unwrapped ConstantCallSite, + // or any other reference. The resolved_method DTRT with the appendix. + SystemDictionary::invoke_bootstrap_method(bootstrap_specifier, THREAD); + Exceptions::wrap_dynamic_exception(THREAD); + + if (HAS_PENDING_EXCEPTION) { + if (!PENDING_EXCEPTION->is_a(SystemDictionary::LinkageError_klass())) { + // Let any random low-level IE or SOE or OOME just bleed through. + // Basically we pretend that the bootstrap method was never called, + // if it fails this way: We neither record a successful linkage, + // nor do we memoize a LE for posterity. + return; + } + // JVMS 5.4.3 says: If an attempt by the Java Virtual Machine to resolve + // a symbolic reference fails because an error is thrown that is an + // instance of LinkageError (or a subclass), then subsequent attempts to + // resolve the reference always fail with the same error that was thrown + // as a result of the initial resolution attempt. + bool recorded_res_status = bootstrap_specifier.save_and_throw_indy_exc(CHECK); + if (!recorded_res_status) { + // Another thread got here just before we did. So, either use the method + // that it resolved or throw the LinkageError exception that it threw. + bool is_done = bootstrap_specifier.resolve_previously_linked_invokedynamic(result, CHECK); + if (is_done) return; + } + assert(bootstrap_specifier.invokedynamic_cp_cache_entry()->indy_resolution_failed(), + "Resolution failure flag wasn't set"); + } + + bootstrap_specifier.resolve_newly_linked_invokedynamic(result, CHECK); + // Exceptions::wrap_dynamic_exception not used because + // set_handle doesn't throw linkage errors } // Selected method is abstract.