8218994: Consolidate indy and condy JVM information within a BootstrapInfo data structure
Summary: Introduce BootstrapInfo data structure and merge invocation of a bootstrap method for condy and indy into invoke_bootstrap_method.
Reviewed-by: acorn, coleenp
Contributed-by: john.r.rose@oracle.com, lois.foltan@oracle.com
--- a/src/hotspot/share/classfile/systemDictionary.cpp Tue Apr 23 08:11:38 2019 -0700
+++ b/src/hotspot/share/classfile/systemDictionary.cpp Tue Apr 23 14:09:54 2019 -0400
@@ -2750,106 +2750,61 @@
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");
+ }
+
+ 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, "");
}
- Handle constant_name = java_lang_String::create_from_symbol(name, CHECK_(empty));
-
- // 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));
-
- return Handle(THREAD, (oop) result.get_jobject());
-}
+ is_indy ? vmSymbols::linkCallSite_name() : vmSymbols::linkDynamicConstant_name(),
+ is_indy ? vmSymbols::linkCallSite_signature() : vmSymbols::linkDynamicConstant_signature(),
+ &args, CHECK);
-// 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
--- a/src/hotspot/share/classfile/systemDictionary.hpp Tue Apr 23 08:11:38 2019 -0700
+++ b/src/hotspot/share/classfile/systemDictionary.hpp Tue Apr 23 14:09:54 2019 -0400
@@ -74,6 +74,7 @@
// of placeholders must hold the SystemDictionary_lock.
//
+class BootstrapInfo;
class ClassFileStream;
class Dictionary;
class PlaceholderTable;
@@ -221,6 +222,7 @@
class SystemDictionary : AllStatic {
+ friend class BootstrapInfo;
friend class VMStructs;
friend class SystemDictionaryHandles;
@@ -531,21 +533,7 @@
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.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/interpreter/bootstrapInfo.cpp Tue Apr 23 14:09:54 2019 -0400
@@ -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));
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/interpreter/bootstrapInfo.hpp Tue Apr 23 14:09:54 2019 -0400
@@ -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
--- a/src/hotspot/share/interpreter/linkResolver.cpp Tue Apr 23 08:11:38 2019 -0700
+++ b/src/hotspot/share/interpreter/linkResolver.cpp Tue Apr 23 14:09:54 2019 -0400
@@ -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 @@
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);
+
+ // 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.
- 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;
- }
+ // 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.
--- a/src/hotspot/share/interpreter/linkResolver.hpp Tue Apr 23 08:11:38 2019 -0700
+++ b/src/hotspot/share/interpreter/linkResolver.hpp Tue Apr 23 14:09:54 2019 -0400
@@ -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 @@
CallKind kind,
int index, TRAPS);
+ friend class BootstrapInfo;
friend class LinkResolver;
public:
@@ -311,9 +313,8 @@
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)
--- a/src/hotspot/share/oops/constantPool.cpp Tue Apr 23 08:11:38 2019 -0700
+++ b/src/hotspot/share/oops/constantPool.cpp Tue Apr 23 14:09:54 2019 -0400
@@ -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 @@
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 @@
// 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 @@
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 @@
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 @@
} // 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 @@
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()
--- a/src/hotspot/share/oops/constantPool.hpp Tue Apr 23 08:11:38 2019 -0700
+++ b/src/hotspot/share/oops/constantPool.hpp Tue Apr 23 14:09:54 2019 -0400
@@ -746,11 +746,6 @@
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 @@
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,