8218994: Consolidate indy and condy JVM information within a BootstrapInfo data structure

Introduce BootstrapInfo data structure and merge invocation of a bootstrap method for condy and indy into invoke_bootstrap_method.

Co-authored-by: John Rose <john.r.rose@oracle.com>
Reviewed-by: acorn, coleenp
This commit is contained in:
Lois Foltan 2019-04-23 14:09:54 -04:00
parent fd3378a73e
commit 57aaf7a8cd
8 changed files with 534 additions and 325 deletions

View File

@ -2750,106 +2750,61 @@ Handle SystemDictionary::link_method_handle_constant(Klass* caller,
return Handle(THREAD, (oop) result.get_jobject());
}
// Ask Java to compute a constant by invoking a BSM given a Dynamic_info CP entry
Handle SystemDictionary::link_dynamic_constant(Klass* caller,
int condy_index,
Handle bootstrap_specifier,
Symbol* name,
Symbol* type,
TRAPS) {
Handle empty;
Handle bsm, info;
if (java_lang_invoke_MethodHandle::is_instance(bootstrap_specifier())) {
bsm = bootstrap_specifier;
} else {
assert(bootstrap_specifier->is_objArray(), "");
objArrayOop args = (objArrayOop) bootstrap_specifier();
assert(args->length() == 2, "");
bsm = Handle(THREAD, args->obj_at(0));
info = Handle(THREAD, args->obj_at(1));
}
guarantee(java_lang_invoke_MethodHandle::is_instance(bsm()),
"caller must supply a valid BSM");
// Ask Java to run a bootstrap method, in order to create a dynamic call site
// while linking an invokedynamic op, or compute a constant for Dynamic_info CP entry
// with linkage results being stored back into the bootstrap specifier.
void SystemDictionary::invoke_bootstrap_method(BootstrapInfo& bootstrap_specifier, TRAPS) {
// Resolve the bootstrap specifier, its name, type, and static arguments
bootstrap_specifier.resolve_bsm(CHECK);
// This should not happen. JDK code should take care of that.
if (caller == NULL) {
THROW_MSG_(vmSymbols::java_lang_InternalError(), "bad dynamic constant", empty);
if (bootstrap_specifier.caller() == NULL || bootstrap_specifier.type_arg().is_null()) {
THROW_MSG(vmSymbols::java_lang_InternalError(), "Invalid bootstrap method invocation with no caller or type argument");
}
Handle constant_name = java_lang_String::create_from_symbol(name, CHECK_(empty));
bool is_indy = bootstrap_specifier.is_method_call();
objArrayHandle appendix_box;
if (is_indy) {
// Some method calls may require an appendix argument. Arrange to receive it.
appendix_box = oopFactory::new_objArray_handle(SystemDictionary::Object_klass(), 1, CHECK);
assert(appendix_box->obj_at(0) == NULL, "");
}
// Resolve the constant type in the context of the caller class
Handle type_mirror = find_java_mirror_for_type(type, caller, SignatureStream::NCDFError,
CHECK_(empty));
// call java.lang.invoke.MethodHandleNatives::linkConstantDyanmic(caller, condy_index, bsm, type, info)
// call condy: java.lang.invoke.MethodHandleNatives::linkDynamicConstant(caller, condy_index, bsm, type, info)
// indy: java.lang.invoke.MethodHandleNatives::linkCallSite(caller, indy_index, bsm, name, mtype, info, &appendix)
JavaCallArguments args;
args.push_oop(Handle(THREAD, caller->java_mirror()));
args.push_int(condy_index);
args.push_oop(bsm);
args.push_oop(constant_name);
args.push_oop(type_mirror);
args.push_oop(info);
args.push_oop(Handle(THREAD, bootstrap_specifier.caller_mirror()));
args.push_int(bootstrap_specifier.bss_index());
args.push_oop(bootstrap_specifier.bsm());
args.push_oop(bootstrap_specifier.name_arg());
args.push_oop(bootstrap_specifier.type_arg());
args.push_oop(bootstrap_specifier.arg_values());
if (is_indy) {
args.push_oop(appendix_box);
}
JavaValue result(T_OBJECT);
JavaCalls::call_static(&result,
SystemDictionary::MethodHandleNatives_klass(),
vmSymbols::linkDynamicConstant_name(),
vmSymbols::linkDynamicConstant_signature(),
&args, CHECK_(empty));
is_indy ? vmSymbols::linkCallSite_name() : vmSymbols::linkDynamicConstant_name(),
is_indy ? vmSymbols::linkCallSite_signature() : vmSymbols::linkDynamicConstant_signature(),
&args, CHECK);
return Handle(THREAD, (oop) result.get_jobject());
}
// Ask Java code to find or construct a java.lang.invoke.CallSite for the given
// name and signature, as interpreted relative to the given class loader.
methodHandle SystemDictionary::find_dynamic_call_site_invoker(Klass* caller,
int indy_index,
Handle bootstrap_specifier,
Symbol* name,
Symbol* type,
Handle *appendix_result,
TRAPS) {
methodHandle empty;
Handle bsm, info;
if (java_lang_invoke_MethodHandle::is_instance(bootstrap_specifier())) {
bsm = bootstrap_specifier;
Handle value(THREAD, (oop) result.get_jobject());
if (is_indy) {
Handle appendix;
methodHandle method = unpack_method_and_appendix(value,
bootstrap_specifier.caller(),
appendix_box,
&appendix, CHECK);
bootstrap_specifier.set_resolved_method(method, appendix);
} else {
objArrayOop args = (objArrayOop) bootstrap_specifier();
assert(args->length() == 2, "");
bsm = Handle(THREAD, args->obj_at(0));
info = Handle(THREAD, args->obj_at(1));
}
guarantee(java_lang_invoke_MethodHandle::is_instance(bsm()),
"caller must supply a valid BSM");
Handle method_name = java_lang_String::create_from_symbol(name, CHECK_(empty));
Handle method_type = find_method_handle_type(type, caller, CHECK_(empty));
// This should not happen. JDK code should take care of that.
if (caller == NULL || method_type.is_null()) {
THROW_MSG_(vmSymbols::java_lang_InternalError(), "bad invokedynamic", empty);
bootstrap_specifier.set_resolved_value(value);
}
objArrayHandle appendix_box = oopFactory::new_objArray_handle(SystemDictionary::Object_klass(), 1, CHECK_(empty));
assert(appendix_box->obj_at(0) == NULL, "");
// call java.lang.invoke.MethodHandleNatives::linkCallSite(caller, indy_index, bsm, name, mtype, info, &appendix)
JavaCallArguments args;
args.push_oop(Handle(THREAD, caller->java_mirror()));
args.push_int(indy_index);
args.push_oop(bsm);
args.push_oop(method_name);
args.push_oop(method_type);
args.push_oop(info);
args.push_oop(appendix_box);
JavaValue result(T_OBJECT);
JavaCalls::call_static(&result,
SystemDictionary::MethodHandleNatives_klass(),
vmSymbols::linkCallSite_name(),
vmSymbols::linkCallSite_signature(),
&args, CHECK_(empty));
Handle mname(THREAD, (oop) result.get_jobject());
return unpack_method_and_appendix(mname, caller, appendix_box, appendix_result, THREAD);
// sanity check
assert(bootstrap_specifier.is_resolved() ||
(bootstrap_specifier.is_method_call() &&
bootstrap_specifier.resolved_method().not_null()), "bootstrap method call failed");
}
// Protection domain cache table handling

View File

@ -74,6 +74,7 @@
// of placeholders must hold the SystemDictionary_lock.
//
class BootstrapInfo;
class ClassFileStream;
class Dictionary;
class PlaceholderTable;
@ -221,6 +222,7 @@ class OopStorage;
class SystemDictionary : AllStatic {
friend class BootstrapInfo;
friend class VMStructs;
friend class SystemDictionaryHandles;
@ -531,21 +533,7 @@ public:
TRAPS);
// ask Java to compute a constant by invoking a BSM given a Dynamic_info CP entry
static Handle link_dynamic_constant(Klass* caller,
int condy_index,
Handle bootstrap_specifier,
Symbol* name,
Symbol* type,
TRAPS);
// ask Java to create a dynamic call site, while linking an invokedynamic op
static methodHandle find_dynamic_call_site_invoker(Klass* caller,
int indy_index,
Handle bootstrap_method,
Symbol* name,
Symbol* type,
Handle *appendix_result,
TRAPS);
static void invoke_bootstrap_method(BootstrapInfo& bootstrap_specifier, TRAPS);
// Record the error when the first attempt to resolve a reference from a constant
// pool entry to a class fails.

View File

@ -0,0 +1,287 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#include "precompiled.hpp"
#include "jvm.h"
#include "classfile/javaClasses.inline.hpp"
#include "classfile/resolutionErrors.hpp"
#include "interpreter/bootstrapInfo.hpp"
#include "interpreter/linkResolver.hpp"
#include "logging/log.hpp"
#include "logging/logStream.hpp"
#include "memory/oopFactory.hpp"
#include "oops/cpCache.inline.hpp"
#include "oops/objArrayOop.inline.hpp"
#include "oops/typeArrayOop.inline.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/thread.inline.hpp"
#include "runtime/vmThread.hpp"
//------------------------------------------------------------------------------------------------------------------------
// Implementation of BootstrapInfo
BootstrapInfo::BootstrapInfo(const constantPoolHandle& pool, int bss_index, int indy_index)
: _pool(pool),
_bss_index(bss_index),
_indy_index(indy_index),
// 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 == -1 || pool->invokedynamic_bootstrap_ref_index_at(indy_index) == bss_index, "invalid bootstrap specifier index");
}
// If there is evidence this call site was already linked, set the
// existing linkage data into result, or throw previous exception.
// Return true if either action is taken, else false.
bool BootstrapInfo::resolve_previously_linked_invokedynamic(CallInfo& result, TRAPS) {
assert(_indy_index != -1, "");
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, 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;
}
}
// Resolve the bootstrap specifier in 3 steps:
// - unpack the BSM by resolving the MH constant
// - obtain the NameAndType description for the condy/indy
// - prepare the BSM's static arguments
Handle BootstrapInfo::resolve_bsm(TRAPS) {
if (_bsm.not_null()) return _bsm;
// The tag at the bootstrap method index must be a valid method handle or a method handle in error.
// If it is a MethodHandleInError, a resolution error will be thrown which will be wrapped if necessary
// with a BootstrapMethodError.
assert(_pool->tag_at(bsm_index()).is_method_handle() ||
_pool->tag_at(bsm_index()).is_method_handle_in_error(), "MH not present, classfile structural constraint");
oop bsm_oop = _pool->resolve_possibly_cached_constant_at(bsm_index(), THREAD);
Exceptions::wrap_dynamic_exception(CHECK_NH);
guarantee(java_lang_invoke_MethodHandle::is_instance(bsm_oop), "classfile must supply a valid BSM");
_bsm = Handle(THREAD, bsm_oop);
// Obtain NameAndType information
resolve_bss_name_and_type(THREAD);
Exceptions::wrap_dynamic_exception(CHECK_NH);
// Prepare static arguments
resolve_args(THREAD);
Exceptions::wrap_dynamic_exception(CHECK_NH);
return _bsm;
}
// Resolve metadata from the JVM_Dynamic_info or JVM_InvokeDynamic_info's name and type information.
void BootstrapInfo::resolve_bss_name_and_type(TRAPS) {
assert(_bsm.not_null(), "resolve_bsm first");
Symbol* name = this->name();
Symbol* type = this->signature();
_name_arg = java_lang_String::create_from_symbol(name, CHECK);
if (type->char_at(0) == '(') {
_type_arg = SystemDictionary::find_method_handle_type(type, caller(), CHECK);
} else {
_type_arg = SystemDictionary::find_java_mirror_for_type(type, caller(), SignatureStream::NCDFError, CHECK);
}
}
// Resolve the bootstrap method's static arguments and store the result in _arg_values.
void BootstrapInfo::resolve_args(TRAPS) {
assert(_bsm.not_null(), "resolve_bsm first");
// if there are no static arguments, return leaving _arg_values as null
if (_argc == 0 && UseBootstrapCallInfo < 2) return;
bool use_BSCI;
switch (UseBootstrapCallInfo) {
default: use_BSCI = true; break; // stress mode
case 0: use_BSCI = false; break; // stress mode
case 1: // normal mode
// If we were to support an alternative mode of BSM invocation,
// we'd convert to pull mode here if the BSM could be a candidate
// for that alternative mode. We can't easily test for things
// like varargs here, but we can get away with approximate testing,
// since the JDK runtime will make up the difference either way.
// For now, exercise the pull-mode path if the BSM is of arity 2,
// or if there is a potential condy loop (see below).
oop mt_oop = java_lang_invoke_MethodHandle::type(_bsm());
use_BSCI = (java_lang_invoke_MethodType::ptype_count(mt_oop) == 2);
break;
}
// Here's a reason to use BSCI even if it wasn't requested:
// If a condy uses a condy argument, we want to avoid infinite
// recursion (condy loops) in the C code. It's OK in Java,
// because Java has stack overflow checking, so we punt
// potentially cyclic cases from C to Java.
if (!use_BSCI && _pool->tag_at(_bss_index).is_dynamic_constant()) {
bool found_unresolved_condy = false;
for (int i = 0; i < _argc; i++) {
int arg_index = _pool->bootstrap_argument_index_at(_bss_index, i);
if (_pool->tag_at(arg_index).is_dynamic_constant()) {
// potential recursion point condy -> condy
bool found_it = false;
_pool->find_cached_constant_at(arg_index, found_it, CHECK);
if (!found_it) { found_unresolved_condy = true; break; }
}
}
if (found_unresolved_condy)
use_BSCI = true;
}
const int SMALL_ARITY = 5;
if (use_BSCI && _argc <= SMALL_ARITY && UseBootstrapCallInfo <= 2) {
// If there are only a few arguments, and none of them need linking,
// push them, instead of asking the JDK runtime to turn around and
// pull them, saving a JVM/JDK transition in some simple cases.
bool all_resolved = true;
for (int i = 0; i < _argc; i++) {
bool found_it = false;
int arg_index = _pool->bootstrap_argument_index_at(_bss_index, i);
_pool->find_cached_constant_at(arg_index, found_it, CHECK);
if (!found_it) { all_resolved = false; break; }
}
if (all_resolved)
use_BSCI = false;
}
if (!use_BSCI) {
// return {arg...}; resolution of arguments is done immediately, before JDK code is called
objArrayOop args_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(), _argc, CHECK);
objArrayHandle args(THREAD, args_oop);
_pool->copy_bootstrap_arguments_at(_bss_index, 0, _argc, args, 0, true, Handle(), CHECK);
oop arg_oop = ((_argc == 1) ? args->obj_at(0) : (oop)NULL);
// try to discard the singleton array
if (arg_oop != NULL && !arg_oop->is_array()) {
// JVM treats arrays and nulls specially in this position,
// but other things are just single arguments
_arg_values = Handle(THREAD, arg_oop);
} else {
_arg_values = args;
}
} else {
// return {arg_count, pool_index}; JDK code must pull the arguments as needed
typeArrayOop ints_oop = oopFactory::new_typeArray(T_INT, 2, CHECK);
ints_oop->int_at_put(0, _argc);
ints_oop->int_at_put(1, _bss_index);
_arg_values = Handle(THREAD, ints_oop);
}
}
// 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 != -1, "");
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(), CHECK);
}
void BootstrapInfo::print_msg_on(outputStream* st, const char* msg) {
ResourceMark rm;
char what[20];
st = st ? st : tty;
if (_indy_index != -1)
sprintf(what, "indy#%d", decode_indy_index());
else
sprintf(what, "condy");
bool have_msg = (msg != NULL && strlen(msg) > 0);
st->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 = (int) strlen(argbuf);
if (pos + 20 > (int)sizeof(argbuf)) {
sprintf(argbuf + pos, "...");
break;
}
if (i > 0) argbuf[pos++] = ',';
sprintf(argbuf+pos, "%d", arg_index(i));
}
st->print_cr(" argument indexes: {%s}", argbuf);
}
if (_bsm.not_null()) {
st->print(" resolved BSM: "); _bsm->print();
}
// How the array of resolved arguments is printed depends highly
// on how BootstrapInfo::resolve_args structures the array based on
// the use_BSCI setting.
if (_arg_values.not_null()) {
// Find the static arguments within the first element of _arg_values.
objArrayOop static_args = (objArrayOop)_arg_values();
if (!static_args->is_array()) {
assert(_argc == 1, "Invalid BSM _arg_values for non-array");
st->print(" resolved arg[0]: "); static_args->print();
} else if (static_args->is_objArray()) {
int lines = 0;
for (int i = 0; i < _argc; i++) {
oop x = static_args->obj_at(i);
if (x != NULL) {
if (++lines > 6) {
st->print_cr(" resolved arg[%d]: ...", i);
break;
}
st->print(" resolved arg[%d]: ", i); x->print();
}
}
} else if (static_args->is_typeArray()) {
typeArrayOop tmp_array = (typeArrayOop) static_args;
assert(tmp_array->length() == 2, "Invalid BSM _arg_values type array");
st->print_cr(" resolved arg[0]: %d", tmp_array->int_at(0));
st->print_cr(" resolved arg[1]: %d", tmp_array->int_at(1));
}
}
}

View File

@ -0,0 +1,120 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_INTERPRETER_BOOTSTRAPINFO_HPP
#define SHARE_INTERPRETER_BOOTSTRAPINFO_HPP
#include "oops/constantPool.hpp"
#include "oops/instanceKlass.hpp"
// BootstrapInfo provides condensed information from the constant pool
// necessary to invoke a bootstrap method.
class BootstrapInfo : public StackObj {
constantPoolHandle _pool; // constant pool containing the bootstrap specifier
const int _bss_index; // index of bootstrap specifier in CP (condy or indy)
const int _indy_index; // internal index of indy call site, or -1 if a condy call
const int _argc; // number of static arguments
Symbol* _name; // extracted from JVM_CONSTANT_NameAndType
Symbol* _signature;
// pre-bootstrap resolution state:
Handle _bsm; // resolved bootstrap method
Handle _name_arg; // resolved String
Handle _type_arg; // resolved Class or MethodType
Handle _arg_values; // array of static arguments; null implies either
// uresolved or zero static arguments are specified
// post-bootstrap resolution state:
bool _is_resolved; // set true when any of the next fields are set
Handle _resolved_value; // bind this as condy constant
methodHandle _resolved_method; // bind this as indy behavior
Handle _resolved_appendix; // extra opaque static argument for _resolved_method
public:
BootstrapInfo(const constantPoolHandle& pool, int bss_index, int indy_index = -1);
// accessors
const constantPoolHandle& pool() const{ return _pool; }
int bss_index() const { return _bss_index; }
int indy_index() const { return _indy_index; }
int argc() const { return _argc; }
bool is_method_call() const { return (_indy_index != -1); }
Symbol* name() const { return _name; }
Symbol* signature() const { return _signature; }
// accessors to lazy state
Handle bsm() const { return _bsm; }
Handle name_arg() const { return _name_arg; }
Handle type_arg() const { return _type_arg; }
Handle arg_values() const { return _arg_values; }
bool is_resolved() const { return _is_resolved; }
Handle resolved_value() const { assert(!is_method_call(), ""); return _resolved_value; }
methodHandle resolved_method() const { assert(is_method_call(), ""); return _resolved_method; }
Handle resolved_appendix() const { assert(is_method_call(), ""); return _resolved_appendix; }
// derived accessors
InstanceKlass* caller() const { return _pool->pool_holder(); }
oop caller_mirror() const { return caller()->java_mirror(); }
int decode_indy_index() const { return ConstantPool::decode_invokedynamic_index(_indy_index); }
int bsms_attr_index() const { return _pool->bootstrap_methods_attribute_index(_bss_index); }
int bsm_index() const { return _pool->bootstrap_method_ref_index_at(_bss_index); }
//int argc() is eagerly cached in _argc
int arg_index(int i) const { return _pool->bootstrap_argument_index_at(_bss_index, i); }
// CP cache entry for call site (indy only)
ConstantPoolCacheEntry* invokedynamic_cp_cache_entry() const {
assert(is_method_call(), "");
return _pool->invokedynamic_cp_cache_entry_at(_indy_index);
}
// If there is evidence this call site was already linked, set the
// existing linkage data into result, or throw previous exception.
// Return true if either action is taken, else false.
bool resolve_previously_linked_invokedynamic(CallInfo& result, TRAPS);
bool save_and_throw_indy_exc(TRAPS);
void resolve_newly_linked_invokedynamic(CallInfo& result, TRAPS);
// pre-bootstrap resolution actions:
Handle resolve_bsm(TRAPS); // lazily compute _bsm and return it
void resolve_bss_name_and_type(TRAPS); // lazily compute _name/_type
void resolve_args(TRAPS); // compute arguments
// setters for post-bootstrap results:
void set_resolved_value(Handle value) {
assert(!is_resolved() && !is_method_call(), "");
_is_resolved = true;
_resolved_value = value;
}
void set_resolved_method(methodHandle method, Handle appendix) {
assert(!is_resolved() && is_method_call(), "");
_is_resolved = true;
_resolved_method = method;
_resolved_appendix = appendix;
}
void print() { print_msg_on(tty); }
void print_msg_on(outputStream* st, const char* msg = NULL);
};
#endif // SHARE_INTERPRETER_BOOTSTRAPINFO_HPP

View File

@ -32,6 +32,7 @@
#include "classfile/vmSymbols.hpp"
#include "compiler/compileBroker.hpp"
#include "gc/shared/collectedHeap.inline.hpp"
#include "interpreter/bootstrapInfo.hpp"
#include "interpreter/bytecode.hpp"
#include "interpreter/interpreterRuntime.hpp"
#include "interpreter/linkResolver.hpp"
@ -1694,99 +1695,80 @@ void LinkResolver::resolve_handle_call(CallInfo& result,
result.set_handle(resolved_klass, resolved_method, resolved_appendix, 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();
// 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);
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();
if (cpce->is_f1_null()) {
if (cpce->indy_resolution_failed()) {
ConstantPool::throw_resolution_error(pool,
ResolutionErrorTable::encode_cpcache_index(index),
CHECK);
}
// Resolve the bootstrap specifier (BSM + optional arguments).
BootstrapInfo bootstrap_specifier(pool, pool_index, indy_index);
// 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));
result.set_handle(method, appendix, 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 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.
resolve_dynamic_call(result, bootstrap_specifier, CHECK);
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();
bootstrap_specifier.print_msg_on(tty, "resolve_invokedynamic");
}
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));
result.set_handle(method, appendix, 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");
}
// The returned linkage result is provisional up to the moment
// the interpreter or runtime performs a serialized check of
// the relevant 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;
methodHandle resolved_method =
SystemDictionary::find_dynamic_call_site_invoker(current_klass,
pool_index,
bootstrap_specifier,
method_name, method_signature,
&resolved_appendix,
THREAD);
Exceptions::wrap_dynamic_exception(CHECK);
result.set_handle(resolved_method, resolved_appendix, THREAD);
Exceptions::wrap_dynamic_exception(CHECK);
// It may also be a MethodHandle from an unwrapped ConstantCallSite,
// or any other reference. The resolved_method as well as the appendix
// are both recorded together via CallInfo::set_handle.
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 memorize 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.

View File

@ -25,6 +25,7 @@
#ifndef SHARE_INTERPRETER_LINKRESOLVER_HPP
#define SHARE_INTERPRETER_LINKRESOLVER_HPP
#include "interpreter/bootstrapInfo.hpp"
#include "oops/method.hpp"
// All the necessary definitions for run-time link resolution.
@ -77,6 +78,7 @@ class CallInfo : public StackObj {
CallKind kind,
int index, TRAPS);
friend class BootstrapInfo;
friend class LinkResolver;
public:
@ -311,9 +313,8 @@ class LinkResolver: AllStatic {
bool check_null_and_abstract, TRAPS);
static void resolve_handle_call (CallInfo& result,
const LinkInfo& link_info, TRAPS);
static void resolve_dynamic_call (CallInfo& result, int pool_index, Handle bootstrap_specifier,
Symbol* method_name, Symbol* method_signature,
Klass* current_klass, TRAPS);
static void resolve_dynamic_call (CallInfo& result,
BootstrapInfo& bootstrap_specifier, TRAPS);
// same as above for compile-time resolution; but returns null handle instead of throwing
// an exception on error also, does not initialize klass (i.e., no side effects)

View File

@ -30,6 +30,7 @@
#include "classfile/stringTable.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/vmSymbols.hpp"
#include "interpreter/bootstrapInfo.hpp"
#include "interpreter/linkResolver.hpp"
#include "memory/allocation.inline.hpp"
#include "memory/heapInspection.hpp"
@ -909,9 +910,8 @@ oop ConstantPool::resolve_constant_at_impl(const constantPoolHandle& this_cp,
case JVM_CONSTANT_Dynamic:
{
Klass* current_klass = this_cp->pool_holder();
Symbol* constant_name = this_cp->uncached_name_ref_at(index);
Symbol* constant_type = this_cp->uncached_signature_ref_at(index);
// Resolve the Dynamically-Computed constant to invoke the BSM in order to obtain the resulting oop.
BootstrapInfo bootstrap_specifier(this_cp, index);
// The initial step in resolving an unresolved symbolic reference to a
// dynamically-computed constant is to resolve the symbolic reference to a
@ -921,27 +921,18 @@ oop ConstantPool::resolve_constant_at_impl(const constantPoolHandle& this_cp,
// bootstrap method's CP index for the CONSTANT_MethodHandle_info. No need to
// set a DynamicConstantInError here since any subsequent use of this
// bootstrap method will encounter the resolution of MethodHandleInError.
oop bsm_info = this_cp->resolve_bootstrap_specifier_at(index, THREAD);
Exceptions::wrap_dynamic_exception(CHECK_NULL);
assert(bsm_info != NULL, "");
// FIXME: Cache this once per BootstrapMethods entry, not once per CONSTANT_Dynamic.
Handle bootstrap_specifier = Handle(THREAD, bsm_info);
// Resolve the Dynamically-Computed constant to invoke the BSM in order to obtain the resulting oop.
Handle value = SystemDictionary::link_dynamic_constant(current_klass,
index,
bootstrap_specifier,
constant_name,
constant_type,
THREAD);
result_oop = value();
// Both the first, (resolution of the BSM and its static arguments), and the second tasks,
// (invocation of the BSM), of JVMS Section 5.4.3.6 occur within invoke_bootstrap_method()
// for the bootstrap_specifier created above.
SystemDictionary::invoke_bootstrap_method(bootstrap_specifier, THREAD);
Exceptions::wrap_dynamic_exception(THREAD);
if (HAS_PENDING_EXCEPTION) {
// Resolution failure of the dynamically-computed constant, save_and_throw_exception
// will check for a LinkageError and store a DynamicConstantInError.
save_and_throw_exception(this_cp, index, tag, CHECK_NULL);
}
BasicType type = FieldType::basic_type(constant_type);
result_oop = bootstrap_specifier.resolved_value()();
BasicType type = FieldType::basic_type(bootstrap_specifier.signature());
if (!is_reference_type(type)) {
// Make sure the primitive value is properly boxed.
// This is a JDK responsibility.
@ -961,6 +952,10 @@ oop ConstantPool::resolve_constant_at_impl(const constantPoolHandle& this_cp,
THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), fail);
}
}
if (TraceMethodHandles) {
bootstrap_specifier.print_msg_on(tty, "resolve_constant_at_impl");
}
break;
}
@ -1102,119 +1097,6 @@ oop ConstantPool::uncached_string_at(int which, TRAPS) {
return str;
}
oop ConstantPool::resolve_bootstrap_specifier_at_impl(const constantPoolHandle& this_cp, int index, TRAPS) {
assert((this_cp->tag_at(index).is_invoke_dynamic() ||
this_cp->tag_at(index).is_dynamic_constant()), "Corrupted constant pool");
Handle bsm;
int argc;
{
// JVM_CONSTANT_InvokeDynamic is an ordered pair of [bootm, name&mtype], plus optional arguments
// JVM_CONSTANT_Dynamic is an ordered pair of [bootm, name&ftype], plus optional arguments
// In both cases, the bootm, being a JVM_CONSTANT_MethodHandle, has its own cache entry.
// It is accompanied by the optional arguments.
int bsm_index = this_cp->bootstrap_method_ref_index_at(index);
oop bsm_oop = this_cp->resolve_possibly_cached_constant_at(bsm_index, CHECK_NULL);
if (!java_lang_invoke_MethodHandle::is_instance(bsm_oop)) {
THROW_MSG_NULL(vmSymbols::java_lang_LinkageError(), "BSM not an MethodHandle");
}
// Extract the optional static arguments.
argc = this_cp->bootstrap_argument_count_at(index);
// if there are no static arguments, return the bsm by itself:
if (argc == 0 && UseBootstrapCallInfo < 2) return bsm_oop;
bsm = Handle(THREAD, bsm_oop);
}
// We are going to return an ordered pair of {bsm, info}, using a 2-array.
objArrayHandle info;
{
objArrayOop info_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(), 2, CHECK_NULL);
info = objArrayHandle(THREAD, info_oop);
}
info->obj_at_put(0, bsm());
bool use_BSCI;
switch (UseBootstrapCallInfo) {
default: use_BSCI = true; break; // stress mode
case 0: use_BSCI = false; break; // stress mode
case 1: // normal mode
// If we were to support an alternative mode of BSM invocation,
// we'd convert to pull mode here if the BSM could be a candidate
// for that alternative mode. We can't easily test for things
// like varargs here, but we can get away with approximate testing,
// since the JDK runtime will make up the difference either way.
// For now, exercise the pull-mode path if the BSM is of arity 2,
// or if there is a potential condy loop (see below).
oop mt_oop = java_lang_invoke_MethodHandle::type(bsm());
use_BSCI = (java_lang_invoke_MethodType::ptype_count(mt_oop) == 2);
break;
}
// Here's a reason to use BSCI even if it wasn't requested:
// If a condy uses a condy argument, we want to avoid infinite
// recursion (condy loops) in the C code. It's OK in Java,
// because Java has stack overflow checking, so we punt
// potentially cyclic cases from C to Java.
if (!use_BSCI && this_cp->tag_at(index).is_dynamic_constant()) {
bool found_unresolved_condy = false;
for (int i = 0; i < argc; i++) {
int arg_index = this_cp->bootstrap_argument_index_at(index, i);
if (this_cp->tag_at(arg_index).is_dynamic_constant()) {
// potential recursion point condy -> condy
bool found_it = false;
this_cp->find_cached_constant_at(arg_index, found_it, CHECK_NULL);
if (!found_it) { found_unresolved_condy = true; break; }
}
}
if (found_unresolved_condy)
use_BSCI = true;
}
const int SMALL_ARITY = 5;
if (use_BSCI && argc <= SMALL_ARITY && UseBootstrapCallInfo <= 2) {
// If there are only a few arguments, and none of them need linking,
// push them, instead of asking the JDK runtime to turn around and
// pull them, saving a JVM/JDK transition in some simple cases.
bool all_resolved = true;
for (int i = 0; i < argc; i++) {
bool found_it = false;
int arg_index = this_cp->bootstrap_argument_index_at(index, i);
this_cp->find_cached_constant_at(arg_index, found_it, CHECK_NULL);
if (!found_it) { all_resolved = false; break; }
}
if (all_resolved)
use_BSCI = false;
}
if (!use_BSCI) {
// return {bsm, {arg...}}; resolution of arguments is done immediately, before JDK code is called
objArrayOop args_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(), argc, CHECK_NULL);
info->obj_at_put(1, args_oop); // may overwrite with args[0] below
objArrayHandle args(THREAD, args_oop);
copy_bootstrap_arguments_at_impl(this_cp, index, 0, argc, args, 0, true, Handle(), CHECK_NULL);
if (argc == 1) {
// try to discard the singleton array
oop arg_oop = args->obj_at(0);
if (arg_oop != NULL && !arg_oop->is_array()) {
// JVM treats arrays and nulls specially in this position,
// but other things are just single arguments
info->obj_at_put(1, arg_oop);
}
}
} else {
// return {bsm, {arg_count, pool_index}}; JDK code must pull the arguments as needed
typeArrayOop ints_oop = oopFactory::new_typeArray(T_INT, 2, CHECK_NULL);
ints_oop->int_at_put(0, argc);
ints_oop->int_at_put(1, index);
info->obj_at_put(1, ints_oop);
}
return info();
}
void ConstantPool::copy_bootstrap_arguments_at_impl(const constantPoolHandle& this_cp, int index,
int start_arg, int end_arg,
objArrayHandle info, int pos,
@ -1848,8 +1730,8 @@ bool ConstantPool::compare_operand_to(int idx1, const constantPoolHandle& cp2, i
} // end compare_operand_to()
// Search constant pool search_cp for a bootstrap specifier that matches
// this constant pool's bootstrap specifier at pattern_i index.
// Return the index of a matching bootstrap specifier or (-1) if there is no match.
// this constant pool's bootstrap specifier data at pattern_i index.
// Return the index of a matching bootstrap attribute record or (-1) if there is no match.
int ConstantPool::find_matching_operand(int pattern_i,
const constantPoolHandle& search_cp, int search_len, TRAPS) {
for (int i = 0; i < search_len; i++) {
@ -1858,7 +1740,7 @@ int ConstantPool::find_matching_operand(int pattern_i,
return i;
}
}
return -1; // bootstrap specifier not found; return unused index (-1)
return -1; // bootstrap specifier data not found; return unused index (-1)
} // end find_matching_operand()

View File

@ -746,11 +746,6 @@ class ConstantPool : public Metadata {
return resolve_constant_at_impl(h_this, pool_index, _possible_index_sentinel, &found_it, THREAD);
}
oop resolve_bootstrap_specifier_at(int index, TRAPS) {
constantPoolHandle h_this(THREAD, this);
return resolve_bootstrap_specifier_at_impl(h_this, index, THREAD);
}
void copy_bootstrap_arguments_at(int index,
int start_arg, int end_arg,
objArrayHandle info, int pos,
@ -871,7 +866,6 @@ class ConstantPool : public Metadata {
static oop resolve_constant_at_impl(const constantPoolHandle& this_cp, int index, int cache_index,
bool* status_return, TRAPS);
static oop resolve_bootstrap_specifier_at_impl(const constantPoolHandle& this_cp, int index, TRAPS);
static void copy_bootstrap_arguments_at_impl(const constantPoolHandle& this_cp, int index,
int start_arg, int end_arg,
objArrayHandle info, int pos,