--- old/make/hotspot/symbols/symbols-unix 2018-09-28 11:52:46.000000000 -0700 +++ new/make/hotspot/symbols/symbols-unix 2018-09-28 11:52:45.000000000 -0700 @@ -28,24 +28,18 @@ JVM_BeforeHalt JVM_CallStackWalk JVM_Clone -JVM_ConstantPoolGetClassAt -JVM_ConstantPoolGetClassAtIfLoaded -JVM_ConstantPoolGetClassRefIndexAt -JVM_ConstantPoolGetDoubleAt -JVM_ConstantPoolGetFieldAt -JVM_ConstantPoolGetFieldAtIfLoaded -JVM_ConstantPoolGetFloatAt -JVM_ConstantPoolGetIntAt -JVM_ConstantPoolGetLongAt -JVM_ConstantPoolGetMemberRefInfoAt -JVM_ConstantPoolGetMethodAt -JVM_ConstantPoolGetMethodAtIfLoaded -JVM_ConstantPoolGetNameAndTypeRefIndexAt -JVM_ConstantPoolGetNameAndTypeRefInfoAt -JVM_ConstantPoolGetSize -JVM_ConstantPoolGetStringAt -JVM_ConstantPoolGetTagAt -JVM_ConstantPoolGetUTF8At +JVM_ConstantPool1CopyOutRefsAt +JVM_ConstantPool1GetDoubleAt +JVM_ConstantPool1GetFloatAt +JVM_ConstantPool1GetHolder +JVM_ConstantPool1GetIntAt +JVM_ConstantPool1GetLongAt +JVM_ConstantPool1GetRefAt +JVM_ConstantPool1GetSize +JVM_ConstantPool1GetTagAt +JVM_ConstantPool1GetTags +JVM_ConstantPool1GetWordAt +JVM_ConstantPool1GetWordCountAt JVM_CountStackFrames JVM_CurrentThread JVM_CurrentTimeMillis --- old/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp 2018-09-28 11:52:48.000000000 -0700 +++ new/src/hotspot/cpu/aarch64/templateTable_aarch64.cpp 2018-09-28 11:52:47.000000000 -0700 @@ -3228,7 +3228,6 @@ // since the parameter_size includes it. __ push(r19); __ mov(r19, index); - assert(ConstantPoolCacheEntry::_indy_resolved_references_appendix_offset == 0, "appendix expected at index+0"); __ load_resolved_reference_at_index(index, r19); __ pop(r19); __ push(index); // push appendix (MethodType, CallSite, etc.) --- old/src/hotspot/cpu/arm/templateTable_arm.cpp 2018-09-28 11:52:50.000000000 -0700 +++ new/src/hotspot/cpu/arm/templateTable_arm.cpp 2018-09-28 11:52:49.000000000 -0700 @@ -4159,7 +4159,6 @@ Label L_no_push; __ tbz(flags, ConstantPoolCacheEntry::has_appendix_shift, L_no_push); __ mov(temp, index); - assert(ConstantPoolCacheEntry::_indy_resolved_references_appendix_offset == 0, "appendix expected at index+0"); __ load_resolved_reference_at_index(index, temp); __ verify_oop(index); __ push_ptr(index); // push appendix (MethodType, CallSite, etc.) --- old/src/hotspot/cpu/sparc/templateTable_sparc.cpp 2018-09-28 11:52:52.000000000 -0700 +++ new/src/hotspot/cpu/sparc/templateTable_sparc.cpp 2018-09-28 11:52:51.000000000 -0700 @@ -2992,7 +2992,6 @@ // Push the appendix as a trailing parameter. // This must be done before we get the receiver, // since the parameter_size includes it. - assert(ConstantPoolCacheEntry::_indy_resolved_references_appendix_offset == 0, "appendix expected at index+0"); __ load_resolved_reference_at_index(temp, index, /*tmp*/recv); __ verify_oop(temp); __ push_ptr(temp); // push appendix (MethodType, CallSite, etc.) --- old/src/hotspot/cpu/x86/templateTable_x86.cpp 2018-09-28 11:52:54.000000000 -0700 +++ new/src/hotspot/cpu/x86/templateTable_x86.cpp 2018-09-28 11:52:53.000000000 -0700 @@ -3648,7 +3648,6 @@ // since the parameter_size includes it. __ push(rbx); __ mov(rbx, index); - assert(ConstantPoolCacheEntry::_indy_resolved_references_appendix_offset == 0, "appendix expected at index+0"); __ load_resolved_reference_at_index(index, rbx); __ pop(rbx); __ push(index); // push appendix (MethodType, CallSite, etc.) --- old/src/hotspot/share/ci/ciStreams.cpp 2018-09-28 11:52:56.000000000 -0700 +++ new/src/hotspot/share/ci/ciStreams.cpp 2018-09-28 11:52:56.000000000 -0700 @@ -365,16 +365,92 @@ constantPoolHandle cpool(THREAD, _method->get_Method()->constants()); ciMethod* m = env->get_method_by_index(cpool, get_method_index(), cur_bc(), _holder); will_link = m->is_loaded(); + Symbol* local_signature = cpool->symbol_at(get_method_signature_index(cpool)); - // Use the MethodType stored in the CP cache to create a signature + // Use the signature stored in the CP cache to create a signature // with correct types (in respect to class loaders). - if (has_method_type()) { - ciSymbol* sig_sym = env->get_symbol(cpool->symbol_at(get_method_signature_index(cpool))); + // + // In classic Java (before Java 7) there is never the slightest + // difference between the signature at the call site and that of the + // method. Such a difference would have been a type error in the + // JVM. + // + // Now there are a few circumstances where the signature of a call + // site (which controls the outgoing stacked arguments) can differ + // from the signature of the method (which controls the receipt of + // those arguments at the method entry point). + // + // A. The signatures can differ if the callee is a static method and + // the caller thinks it is calling a non-static method (VH.get). + // This requires the method signature to have an explicit leading + // argument for the implicit 'this', not present at the call site. + // + // B. The call site can have less specific parameter types than the + // method, allowing loosely-typed code to handle strongly-typed + // methods. This happens with linkToStatic and related linker + // commands. Obviously the loosely-typed code has to ensure that + // the strongly typed method's invariants are respected, and this is + // done by issuing dynamic casts. + // + // C. The call site can have more specific parameter types than the + // method, allowing loosely-typed methods to handle strongly-typed + // requests. + // + // D. There is are corresponding effects with return values, such as + // boolean method returning an int to an int-receiving call site, + // even though the method thought it returned just a boolean. + // + // E. The calling sequence at a particular call site may add an + // "appendix" argument not mentioned in the call site signature. It + // is expected by the method signature, though, and this adds to the + // method's arity, even after 'this' parameter effects (A) are + // discounted. Appendixes are used by invokehandle and + // invokedynamic instructions. + // + // F. A linker method (linkToStatic, etc.) can also take an extra + // argument, a MemberName which routes the call to a concrete + // strongly-typed method. In this case the linker method may also + // differ in any of the ways A-D. The eventual method will ignore + // the presence of the extra argument. + // + // None of these changes to calling sequences requires an argument + // to be moved or reformatted in any way. This works because all + // references look alike to the JVM, as do all primitives (except + // float/long/double). Another required property of the JVM is + // that, if a trailing argument is added or dropped, the placement + // of other arguments does not change. This allows cases E and F to + // work smoothly, against without any moving or reformatting, + // despite the arity change. + // + if (has_local_signature()) { + ciSymbol* sig_sym = env->get_symbol(local_signature); ciKlass* pool_holder = env->get_klass(cpool->pool_holder()); - ciMethodType* method_type = get_method_type(); - ciSignature* declared_signature = new (env->arena()) ciSignature(pool_holder, sig_sym, method_type); - (*declared_signature_result) = declared_signature; + ciSignature* call_site_sig = new (env->arena()) ciSignature(pool_holder, cpool, sig_sym); + // Examples of how the call site signature can differ from the method's own signature: + // + // meth = static jboolean java.lang.invoke.VarHandleGuards.guard_LII_Z(jobject, jobject, jint, jint, jobject) + // msig = (Ljava/lang/invoke/VarHandle;Ljava/lang/Object;IILjava/lang/invoke/VarHandle$AccessDescriptor;)Z + // call = (Ljava/util/concurrent/locks/AbstractQueuedSynchronizer;II)Z + // + // meth = static jobject java.lang.invoke.LambdaForm$MH/0x0000000800066840.linkToTargetMethod(jobject, jobject) + // msig = (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; + // call = (Ljava/lang/String;)Ljava/util/function/Predicate; + // + (*declared_signature_result) = call_site_sig; + } else { + // We can just use the method's own signature. It may differ from the call site, but not by much. + // + // Examples of how the call site signature can differ from the method's signature: + // + // meth = static final native jint java.lang.invoke.MethodHandle.linkToStatic(jobject, jobject, jint, jint, jobject) + // msig = (Ljava/lang/Object;Ljava/lang/Object;IILjava/lang/invoke/MemberName;)I + // call = (Ljava/lang/invoke/VarHandle;Ljava/lang/Object;IILjava/lang/invoke/MemberName;)Z + // + // meth = final native jint java.lang.invoke.MethodHandle.invokeBasic(jobject, jobject, jint, jint) + // msig = (Ljava/lang/Object;Ljava/lang/Object;II)I + // call = (Ljava/lang/invoke/VarHandle;Ljava/lang/Object;II)Z + // (*declared_signature_result) = m->signature(); } return m; @@ -408,23 +484,10 @@ // // Returns true if there is a MethodType argument stored in the // constant pool cache at the current bci. -bool ciBytecodeStream::has_method_type() { - GUARDED_VM_ENTRY( - constantPoolHandle cpool(_method->get_Method()->constants()); - return ConstantPool::has_method_type_at_if_loaded(cpool, get_method_index()); - ) -} - -// ------------------------------------------------------------------ -// ciBytecodeStream::get_method_type -// -// Return the MethodType stored in the constant pool cache at -// the current bci. -ciMethodType* ciBytecodeStream::get_method_type() { +bool ciBytecodeStream::has_local_signature() { GUARDED_VM_ENTRY( constantPoolHandle cpool(_method->get_Method()->constants()); - oop method_type_oop = ConstantPool::method_type_at_if_loaded(cpool, get_method_index()); - return CURRENT_ENV->get_object(method_type_oop)->as_method_type(); + return ConstantPool::has_local_signature_at_if_loaded(cpool, get_method_index()); ) } --- old/src/hotspot/share/ci/ciStreams.hpp 2018-09-28 11:52:58.000000000 -0700 +++ new/src/hotspot/share/ci/ciStreams.hpp 2018-09-28 11:52:58.000000000 -0700 @@ -260,8 +260,7 @@ ciMethod* get_method(bool& will_link, ciSignature* *declared_signature_result); bool has_appendix(); ciObject* get_appendix(); - bool has_method_type(); - ciMethodType* get_method_type(); + bool has_local_signature(); ciKlass* get_declared_method_holder(); int get_method_holder_index(); int get_method_signature_index(const constantPoolHandle& cpool); --- old/src/hotspot/share/classfile/classFileParser.cpp 2018-09-28 11:53:00.000000000 -0700 +++ new/src/hotspot/share/classfile/classFileParser.cpp 2018-09-28 11:53:00.000000000 -0700 @@ -562,7 +562,7 @@ } case JVM_CONSTANT_Dynamic: { const int name_and_type_ref_index = - cp->invoke_dynamic_name_and_type_ref_index_at(index); + cp->bootstrap_name_and_type_ref_index_at(index); check_property(valid_cp_range(name_and_type_ref_index, length) && cp->tag_at(name_and_type_ref_index).is_name_and_type(), @@ -577,7 +577,7 @@ } case JVM_CONSTANT_InvokeDynamic: { const int name_and_type_ref_index = - cp->invoke_dynamic_name_and_type_ref_index_at(index); + cp->bootstrap_name_and_type_ref_index_at(index); check_property(valid_cp_range(name_and_type_ref_index, length) && cp->tag_at(name_and_type_ref_index).is_name_and_type(), --- old/src/hotspot/share/classfile/javaClasses.cpp 2018-09-28 11:53:02.000000000 -0700 +++ new/src/hotspot/share/classfile/javaClasses.cpp 2018-09-28 11:53:02.000000000 -0700 @@ -2992,18 +2992,15 @@ field->obj_field_put(annotations_offset, value); } -#define CONSTANTPOOL_FIELDS_DO(macro) \ - macro(_oop_offset, k, "constantPoolOop", object_signature, false) void reflect_ConstantPool::compute_offsets() { InstanceKlass* k = SystemDictionary::reflect_ConstantPool_klass(); - // The field is called ConstantPool* in the sun.reflect.ConstantPool class. - CONSTANTPOOL_FIELDS_DO(FIELD_COMPUTE_OFFSET); + CONSTANTPOOL_INJECTED_FIELDS(INJECTED_FIELD_COMPUTE_OFFSET); } #if INCLUDE_CDS void reflect_ConstantPool::serialize_offsets(SerializeClosure* f) { - CONSTANTPOOL_FIELDS_DO(FIELD_SERIALIZE_OFFSET); + CONSTANTPOOL_INJECTED_FIELDS(INJECTED_FIELD_SERIALIZE_OFFSET); } #endif @@ -3147,26 +3144,22 @@ module->address_field_put(_module_entry_offset, (address)module_entry); } -Handle reflect_ConstantPool::create(TRAPS) { +Handle reflect_ConstantPool::create_from_pool(ConstantPool* value, TRAPS) { assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); InstanceKlass* k = SystemDictionary::reflect_ConstantPool_klass(); // Ensure it is initialized k->initialize(CHECK_NH); - return k->allocate_instance_handle(THREAD); -} - - -void reflect_ConstantPool::set_cp(oop reflect, ConstantPool* value) { - assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); + Handle reflect = k->allocate_instance_handle(THREAD); oop mirror = value->pool_holder()->java_mirror(); // Save the mirror to get back the constant pool. - reflect->obj_field_put(_oop_offset, mirror); + reflect->obj_field_put(_constantPoolOop_offset, mirror); + return reflect; } ConstantPool* reflect_ConstantPool::get_cp(oop reflect) { assert(Universe::is_fully_initialized(), "Need to find another solution to the reflection problem"); - oop mirror = reflect->obj_field(_oop_offset); + oop mirror = reflect->obj_field(_constantPoolOop_offset); Klass* k = java_lang_Class::as_Klass(mirror); assert(k->is_instance_klass(), "Must be"); @@ -4063,7 +4056,7 @@ int java_lang_AssertionStatusDirectives::deflt_offset; int java_nio_Buffer::_limit_offset; int java_util_concurrent_locks_AbstractOwnableSynchronizer::_owner_offset; -int reflect_ConstantPool::_oop_offset; +int reflect_ConstantPool::_constantPoolOop_offset; int reflect_UnsafeStaticFieldAccessorImpl::_base_offset; --- old/src/hotspot/share/classfile/javaClasses.hpp 2018-09-28 11:53:04.000000000 -0700 +++ new/src/hotspot/share/classfile/javaClasses.hpp 2018-09-28 11:53:04.000000000 -0700 @@ -792,12 +792,15 @@ friend class JavaClasses; }; +#define CONSTANTPOOL_INJECTED_FIELDS(macro) \ + macro(reflect_ConstantPool, constantPoolOop, object_signature, false) + // Interface to jdk.internal.reflect.ConstantPool objects class reflect_ConstantPool { private: // Note that to reduce dependencies on the JDK we compute these // offsets at run-time. - static int _oop_offset; + static int _constantPoolOop_offset; static void compute_offsets(); @@ -805,12 +808,11 @@ static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN; // Allocation - static Handle create(TRAPS); + static Handle create_from_pool(ConstantPool* value, TRAPS); // Accessors - static void set_cp(oop reflect, ConstantPool* value); static int oop_offset() { - return _oop_offset; + return _constantPoolOop_offset; } static ConstantPool* get_cp(oop reflect); @@ -1507,6 +1509,7 @@ #define ALL_INJECTED_FIELDS(macro) \ CLASS_INJECTED_FIELDS(macro) \ + CONSTANTPOOL_INJECTED_FIELDS(macro) \ CLASSLOADER_INJECTED_FIELDS(macro) \ RESOLVEDMETHOD_INJECTED_FIELDS(macro) \ MEMBERNAME_INJECTED_FIELDS(macro) \ --- old/src/hotspot/share/classfile/systemDictionary.cpp 2018-09-28 11:53:06.000000000 -0700 +++ new/src/hotspot/share/classfile/systemDictionary.cpp 2018-09-28 11:53:06.000000000 -0700 @@ -2512,7 +2512,6 @@ Symbol* signature, Klass* accessing_klass, Handle *appendix_result, - Handle *method_type_result, TRAPS) { methodHandle empty; assert(THREAD->can_call_java() ,""); @@ -2545,7 +2544,6 @@ vmSymbols::linkMethod_signature(), &args, CHECK_(empty)); Handle mname(THREAD, (oop) result.get_jobject()); - (*method_type_result) = method_type; return unpack_method_and_appendix(mname, accessing_klass, appendix_box, appendix_result, THREAD); } @@ -2586,6 +2584,7 @@ Handle class_loader, Handle protection_domain, SignatureStream::FailureMode failure_mode, + bool check_access, TRAPS) { Handle empty; @@ -2626,7 +2625,7 @@ Handle mirror(THREAD, constant_type_klass->java_mirror()); // Check accessibility, emulating ConstantPool::verify_constant_pool_resolve. - if (accessing_klass != NULL) { + if (check_access && accessing_klass != NULL) { Klass* sel_klass = constant_type_klass; bool fold_type_to_class = true; LinkResolver::check_klass_accessability(accessing_klass, sel_klass, @@ -2734,27 +2733,6 @@ return method_type; } -Handle SystemDictionary::find_field_handle_type(Symbol* signature, - Klass* accessing_klass, - TRAPS) { - Handle empty; - ResourceMark rm(THREAD); - SignatureStream ss(signature, /*is_method=*/ false); - if (!ss.is_done()) { - Handle class_loader, protection_domain; - if (accessing_klass != NULL) { - class_loader = Handle(THREAD, accessing_klass->class_loader()); - protection_domain = Handle(THREAD, accessing_klass->protection_domain()); - } - oop mirror = ss.as_java_mirror(class_loader, protection_domain, SignatureStream::NCDFError, CHECK_(empty)); - ss.next(); - if (ss.is_done()) { - return Handle(THREAD, mirror); - } - } - return empty; -} - // Ask Java code to find or construct a method handle constant. Handle SystemDictionary::link_method_handle_constant(Klass* caller, int ref_kind, //e.g., JVM_REF_invokeVirtual @@ -2806,108 +2784,90 @@ 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"); - - // This should not happen. JDK code should take care of that. - if (caller == NULL) { - THROW_MSG_(vmSymbols::java_lang_InternalError(), "bad dynamic constant", empty); - } - - 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) - 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); +// 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) { + ResourceMark rm; + bootstrap_specifier.resolve_bsm(CHECK); + bootstrap_specifier.resolve_metadata(CHECK); + BootstrapInfo::BSMMode bsm_mode = bootstrap_specifier.bsm_mode(); + ConstantPool::BootstrapArgumentReferenceMode arg_mode = ConstantPool::R_IFPRESENT; + if (bsm_mode == BootstrapInfo::_metadata || + (bsm_mode == BootstrapInfo::_expression && + bootstrap_specifier.name() == vmSymbols::invoke_name())) + // We know the BSM is going to immediately use all the arguments. + arg_mode = ConstantPool::R_FORCE; + // Otherwise, it can't hurt to pre-pack the object array with present arguments. + bootstrap_specifier.resolve_args(arg_mode, CHECK); + if (arg_mode != ConstantPool::R_FORCE) { + // If we are going tentative, also grab the symbolic reference data. + bootstrap_specifier.resolve_args(ConstantPool::R_SYMREF, CHECK); + } + + Handle caller(THREAD, bootstrap_specifier.caller_mirror()); + typeArrayOop index_info_oop = oopFactory::new_intArray(2, CHECK); + typeArrayHandle index_info(THREAD, index_info_oop); + index_info->int_at_put(0, bootstrap_specifier.argc()); + index_info->int_at_put(1, bootstrap_specifier.bss_index()); + + bool has_appendix = bootstrap_specifier.is_method_call(); + objArrayHandle appendix_box; + if (has_appendix) { + // Some method calls may require an appendix argument. Arrange to receive it. + objArrayOop appendix_box_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(), 1, CHECK); + appendix_box = objArrayHandle(THREAD, appendix_box_oop); + assert(appendix_box->obj_at(0) == NULL, ""); + } + Handle request; + if (bootstrap_specifier.indy_index() != 0) { + // request is call-site specific + request = Handle(THREAD, SystemDictionary::CallSite_klass()->java_mirror()); + } + + Handle cpool = reflect_ConstantPool::create_from_pool(bootstrap_specifier.pool()(), CHECK); + Handle arg_indexes(THREAD, bootstrap_specifier.arg_indexes()()); + Handle arg_values(THREAD, bootstrap_specifier.arg_values()()); + + // call java.lang.invoke.MethodHandleNatives::bootstrapMethodHelper(cpool, bss_index, indy_index, argc, bsm, name, type, argIndexes, argValues, request, &appendix) + JavaCallArguments args(11); + args.push_oop(cpool); + args.push_int(bootstrap_specifier.bss_index()); + args.push_int(bootstrap_specifier.indy_index()); + args.push_int(bootstrap_specifier.argc()); + 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_indexes()); + args.push_oop(bootstrap_specifier.arg_values()); + args.push_oop(request); + 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()); -} - -// 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, - Handle *method_type_result, - TRAPS) { - methodHandle empty; - Handle bsm, info; - if (java_lang_invoke_MethodHandle::is_instance(bootstrap_specifier())) { - bsm = bootstrap_specifier; + vmSymbols::bootstrapMethodHelper_name(), + vmSymbols::bootstrapMethodHelper_signature(), + &args, CHECK); + if (has_appendix) { + Handle appendix; + Handle mname(THREAD, (oop) result.get_jobject()); + methodHandle method = unpack_method_and_appendix(mname, + 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)); + Handle value(THREAD, (oop) result.get_jobject()); + bootstrap_specifier.set_resolved_value(value); } - 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); + // sanity check + if (!bootstrap_specifier.is_resolved() || + (bootstrap_specifier.is_method_call() && + bootstrap_specifier.resolved_method().is_null())) { + // Could be a guarantee but throw IE instead, in case it is JDK bug. + THROW_MSG(vmSymbols::java_lang_InternalError(), "bootstrap method call failed"); } - - 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()); - (*method_type_result) = method_type; - return unpack_method_and_appendix(mname, caller, appendix_box, appendix_result, THREAD); } // Protection domain cache table handling --- old/src/hotspot/share/classfile/systemDictionary.hpp 2018-09-28 11:53:08.000000000 -0700 +++ new/src/hotspot/share/classfile/systemDictionary.hpp 2018-09-28 11:53:08.000000000 -0700 @@ -73,6 +73,7 @@ // of placeholders must hold the SystemDictionary_lock. // +class BootstrapInfo; class ClassFileStream; class Dictionary; class PlaceholderTable; @@ -507,7 +508,6 @@ Symbol* signature, Klass* accessing_klass, Handle *appendix_result, - Handle *method_type_result, TRAPS); // for a given signature, find the internal MethodHandle method (linkTo* or invokeBasic) // (does not ask Java, since this is a low-level intrinsic defined by the JVM) @@ -522,14 +522,16 @@ Handle class_loader, Handle protection_domain, SignatureStream::FailureMode failure_mode, + bool check_access, TRAPS); static Handle find_java_mirror_for_type(Symbol* signature, Klass* accessing_klass, SignatureStream::FailureMode failure_mode, + bool check_access, TRAPS) { // callee will fill in CL/PD from AK, if they are needed return find_java_mirror_for_type(signature, accessing_klass, Handle(), Handle(), - failure_mode, THREAD); + failure_mode, check_access, THREAD); } @@ -542,11 +544,6 @@ Klass* accessing_klass, TRAPS); - // find a java.lang.Class object for a given signature - static Handle find_field_handle_type(Symbol* signature, - Klass* accessing_klass, - TRAPS); - // ask Java to compute a java.lang.invoke.MethodHandle object for a given CP entry static Handle link_method_handle_constant(Klass* caller, int ref_kind, //e.g., JVM_REF_invokeVirtual @@ -555,23 +552,10 @@ Symbol* signature, 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, - Handle *method_type_result, - TRAPS); + // 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 + 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. --- old/src/hotspot/share/classfile/vmSymbols.hpp 2018-09-28 11:53:10.000000000 -0700 +++ new/src/hotspot/share/classfile/vmSymbols.hpp 2018-09-28 11:53:10.000000000 -0700 @@ -268,6 +268,7 @@ template(parameter_annotations_name, "parameterAnnotations") \ template(annotation_default_name, "annotationDefault") \ template(reflect_ConstantPool, "jdk/internal/reflect/ConstantPool") \ + template(constantPoolOop_name, "constantPoolOop") \ template(reflect_UnsafeStaticFieldAccessorImpl, "jdk/internal/reflect/UnsafeStaticFieldAccessorImpl")\ template(base_name, "base") \ /* Type Annotations (JDK 8 and above) */ \ @@ -299,6 +300,7 @@ template(java_lang_invoke_MethodHandleNatives, "java/lang/invoke/MethodHandleNatives") \ template(java_lang_invoke_MethodHandleNatives_CallSiteContext, "java/lang/invoke/MethodHandleNatives$CallSiteContext") \ template(java_lang_invoke_LambdaForm, "java/lang/invoke/LambdaForm") \ + template(java_lang_invoke_MethodHandles_Lookup, "java/lang/invoke/MethodHandles$Lookup") \ template(java_lang_invoke_InjectedProfile_signature, "Ljava/lang/invoke/InjectedProfile;") \ template(java_lang_invoke_LambdaForm_Compiled_signature, "Ljava/lang/invoke/LambdaForm$Compiled;") \ template(java_lang_invoke_LambdaForm_Hidden_signature, "Ljava/lang/invoke/LambdaForm$Hidden;") \ @@ -311,10 +313,8 @@ template(linkMethodHandleConstant_signature, "(Ljava/lang/Class;ILjava/lang/Class;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;") \ template(linkMethod_name, "linkMethod") \ template(linkMethod_signature, "(Ljava/lang/Class;ILjava/lang/Class;Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/invoke/MemberName;") \ - template(linkDynamicConstant_name, "linkDynamicConstant") \ - template(linkDynamicConstant_signature, "(Ljava/lang/Object;ILjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;") \ - template(linkCallSite_name, "linkCallSite") \ - template(linkCallSite_signature, "(Ljava/lang/Object;ILjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/invoke/MemberName;") \ + template(bootstrapMethodHelper_name, "bootstrapMethodHelper") \ + template(bootstrapMethodHelper_signature, "(Ljdk/internal/reflect/ConstantPool;IIILjava/lang/invoke/MethodHandle;Ljava/lang/String;Ljava/lang/Object;[I[Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;") \ template(setTargetNormal_name, "setTargetNormal") \ template(setTargetVolatile_name, "setTargetVolatile") \ template(setTarget_signature, "(Ljava/lang/invoke/MethodHandle;)V") \ --- old/src/hotspot/share/include/jvm.h 2018-09-28 11:53:13.000000000 -0700 +++ new/src/hotspot/share/include/jvm.h 2018-09-28 11:53:12.000000000 -0700 @@ -555,59 +555,43 @@ JNIEXPORT jobject JNICALL JVM_GetClassConstantPool(JNIEnv *env, jclass cls); -JNIEXPORT jint JNICALL JVM_ConstantPoolGetSize -(JNIEnv *env, jobject unused, jobject jcpool); +JNIEXPORT jclass JNICALL JVM_ConstantPool1GetHolder +(JNIEnv *env, jobject jcpool); -JNIEXPORT jclass JNICALL JVM_ConstantPoolGetClassAt -(JNIEnv *env, jobject unused, jobject jcpool, jint index); +JNIEXPORT jint JNICALL JVM_ConstantPool1GetSize +(JNIEnv *env, jobject jcpool); -JNIEXPORT jclass JNICALL JVM_ConstantPoolGetClassAtIfLoaded -(JNIEnv *env, jobject unused, jobject jcpool, jint index); +JNIEXPORT jint JNICALL JVM_ConstantPool1GetWordCountAt +(JNIEnv *env, jobject jcpool, jint index); -JNIEXPORT jint JNICALL JVM_ConstantPoolGetClassRefIndexAt -(JNIEnv *env, jobject obj, jobject unused, jint index); +JNIEXPORT jobject JNICALL JVM_ConstantPool1GetRefAt +(JNIEnv *env, jobject jcpool, jint index, jint word, jbyte resolving, jobject if_not_present); -JNIEXPORT jobject JNICALL JVM_ConstantPoolGetMethodAt -(JNIEnv *env, jobject unused, jobject jcpool, jint index); +JNIEXPORT void JNICALL JVM_ConstantPool1CopyOutRefsAt +(JNIEnv *env, jobject jcpool, jint index, jint start_word, jint end_word, + jobject buf, jint buf_pos, jbyte resolving, + jobject if_not_present, jobject if_null_constant, jboolean skip_non_null); -JNIEXPORT jobject JNICALL JVM_ConstantPoolGetMethodAtIfLoaded -(JNIEnv *env, jobject unused, jobject jcpool, jint index); +JNIEXPORT jint JNICALL JVM_ConstantPool1GetWordAt +(JNIEnv *env, jobject jcpool, jint index, jint word); -JNIEXPORT jobject JNICALL JVM_ConstantPoolGetFieldAt -(JNIEnv *env, jobject unused, jobject jcpool, jint index); +JNIEXPORT jint JNICALL JVM_ConstantPool1GetIntAt +(JNIEnv *env, jobject jcpool, jint index); -JNIEXPORT jobject JNICALL JVM_ConstantPoolGetFieldAtIfLoaded -(JNIEnv *env, jobject unused, jobject jcpool, jint index); +JNIEXPORT jlong JNICALL JVM_ConstantPool1GetLongAt +(JNIEnv *env, jobject jcpool, jint index); -JNIEXPORT jobjectArray JNICALL JVM_ConstantPoolGetMemberRefInfoAt -(JNIEnv *env, jobject unused, jobject jcpool, jint index); +JNIEXPORT jfloat JNICALL JVM_ConstantPool1GetFloatAt +(JNIEnv *env, jobject jcpool, jint index); -JNIEXPORT jint JNICALL JVM_ConstantPoolGetNameAndTypeRefIndexAt -(JNIEnv *env, jobject obj, jobject unused, jint index); +JNIEXPORT jdouble JNICALL JVM_ConstantPool1GetDoubleAt +(JNIEnv *env, jobject jcpool, jint index); -JNIEXPORT jobjectArray JNICALL JVM_ConstantPoolGetNameAndTypeRefInfoAt -(JNIEnv *env, jobject obj, jobject unused, jint index); +JNIEXPORT jbyte JNICALL JVM_ConstantPool1GetTagAt +(JNIEnv *env, jobject jcpool, jint index); -JNIEXPORT jint JNICALL JVM_ConstantPoolGetIntAt -(JNIEnv *env, jobject unused, jobject jcpool, jint index); - -JNIEXPORT jlong JNICALL JVM_ConstantPoolGetLongAt -(JNIEnv *env, jobject unused, jobject jcpool, jint index); - -JNIEXPORT jfloat JNICALL JVM_ConstantPoolGetFloatAt -(JNIEnv *env, jobject unused, jobject jcpool, jint index); - -JNIEXPORT jdouble JNICALL JVM_ConstantPoolGetDoubleAt -(JNIEnv *env, jobject unused, jobject jcpool, jint index); - -JNIEXPORT jstring JNICALL JVM_ConstantPoolGetStringAt -(JNIEnv *env, jobject unused, jobject jcpool, jint index); - -JNIEXPORT jstring JNICALL JVM_ConstantPoolGetUTF8At -(JNIEnv *env, jobject unused, jobject jcpool, jint index); - -JNIEXPORT jbyte JNICALL JVM_ConstantPoolGetTagAt -(JNIEnv *env, jobject unused, jobject jcpool, jint index); +JNIEXPORT jbyteArray JNICALL JVM_ConstantPool1GetTags +(JNIEnv *env, jobject jcpool); /* * Parameter reflection --- old/src/hotspot/share/interpreter/bytecodeTracer.cpp 2018-09-28 11:53:15.000000000 -0700 +++ new/src/hotspot/share/interpreter/bytecodeTracer.cpp 2018-09-28 11:53:14.000000000 -0700 @@ -385,7 +385,7 @@ st->print_cr(" %d <%s.%s%s%s> ", i, klass->as_C_string(), name->as_C_string(), sep, signature->as_C_string()); } else { if (tag.is_dynamic_constant() || tag.is_invoke_dynamic()) { - int bsm = constants->invoke_dynamic_bootstrap_method_ref_index_at(i); + int bsm = constants->bootstrap_method_ref_index_at(i); st->print(" bsm=%d", bsm); } st->print_cr(" %d <%s%s%s>", i, name->as_C_string(), sep, signature->as_C_string()); --- old/src/hotspot/share/interpreter/interpreterRuntime.cpp 2018-09-28 11:53:17.000000000 -0700 +++ new/src/hotspot/share/interpreter/interpreterRuntime.cpp 2018-09-28 11:53:16.000000000 -0700 @@ -981,9 +981,6 @@ LastFrameAccessor last_frame(thread); const Bytecodes::Code bytecode = Bytecodes::_invokedynamic; - //TO DO: consider passing BCI to Java. - // int caller_bci = last_frame.method()->bci_from(last_frame.bcp()); - // resolve method CallInfo info; constantPoolHandle pool(thread, last_frame.method()->constants()); --- 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. --- old/src/hotspot/share/interpreter/linkResolver.hpp 2018-09-28 11:53:21.000000000 -0700 +++ new/src/hotspot/share/interpreter/linkResolver.hpp 2018-09-28 11:53:20.000000000 -0700 @@ -55,7 +55,7 @@ // others inferred), vtable, itable) int _call_index; // vtable or itable index of selected class method (if any) Handle _resolved_appendix; // extra argument in constant pool (if CPCE::has_appendix) - Handle _resolved_method_type; // MethodType (for invokedynamic and invokehandle call sites) + bool _has_local_sig; // use signature from call site, which differs from that of the method itself Handle _resolved_method_name; // Object holding the ResolvedMethodName void set_static(Klass* resolved_klass, const methodHandle& resolved_method, TRAPS); @@ -68,10 +68,10 @@ const methodHandle& selected_method, int vtable_index, TRAPS); void set_handle(const methodHandle& resolved_method, - Handle resolved_appendix, Handle resolved_method_type, TRAPS); + Handle resolved_appendix, bool has_local_sig, TRAPS); void set_handle(Klass* resolved_klass, const methodHandle& resolved_method, - Handle resolved_appendix, Handle resolved_method_type, TRAPS); + Handle resolved_appendix, bool has_local_sig, TRAPS); void set_common(Klass* resolved_klass, Klass* selected_klass, const methodHandle& resolved_method, const methodHandle& selected_method, @@ -79,6 +79,7 @@ int index, TRAPS); friend class LinkResolver; + friend class BootstrapInfo; public: CallInfo() { @@ -98,7 +99,7 @@ methodHandle resolved_method() const { return _resolved_method; } methodHandle selected_method() const { return _selected_method; } Handle resolved_appendix() const { return _resolved_appendix; } - Handle resolved_method_type() const { return _resolved_method_type; } + bool has_local_signature() const { return _has_local_sig; } Handle resolved_method_name() const { return _resolved_method_name; } // Materialize a java.lang.invoke.ResolvedMethodName for this resolved_method void set_resolved_method_name(TRAPS); @@ -131,6 +132,117 @@ void print() PRODUCT_RETURN; }; +// Condensed information from constant pool to use 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 bool _is_method_call; // are we resolving a method (indy) or a value (condy)? + 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 + typeArrayHandle _arg_indexes; // int[] array of static arguments, as CP indexes + objArrayHandle _arg_values; // Object[] array of static arguments; null for unresolved + + // 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 + + int nat_ref_index() const { return _pool->bootstrap_name_and_type_ref_index_at(_bss_index); } + + public: + BootstrapInfo(const constantPoolHandle& pool, int bss_index, int indy_index = 0); + + // 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 _is_method_call; } + 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; } + typeArrayHandle arg_indexes() const { return _arg_indexes; } + objArrayHandle 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 name_index() const { return _pool->name_ref_index_at(nat_ref_index()); } + int signature_index() const { return _pool->signature_ref_index_at(nat_ref_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); } + oop arg_value(int i) const; // pull oop from _arg_values, safely, NULL if not present + + enum BSMMode { _unknown, _metadata, _bsci, _expression }; + BSMMode bsm_mode() const; + + // 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 previouos 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); + + // argument location and resolution + // Null returns from these are real, actual null condy constants. + // They never produce Universe::the_null_sentinel. + oop find_arg_oop(int i, Handle if_not_present, TRAPS) { + // Don't pass if_not_present as null unless you are OK with confusing + // "not present" with "present but null". + bool found_it = false; + oop result = _pool->find_cached_constant_at(arg_index(i), found_it, THREAD); + return (found_it ? result : if_not_present()); // OK to execute during exception + } + oop resolve_arg_oop(int i, TRAPS) { + return _pool->resolve_possibly_cached_constant_at(arg_index(i), THREAD); + } + + // pre-bootstrap resolution actions: + Handle resolve_bsm(TRAPS); // lazily compute _bsm and return it + void resolve_metadata(TRAPS); // lazily compute _name/_type + void resolve_args(ConstantPool::BootstrapArgumentReferenceMode resolving, 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); +}; // Condensed information from constant pool to use to resolve the method or field. // resolved_klass = specified class (i.e., static receiver class) @@ -207,8 +319,7 @@ static Method* lookup_method_in_interfaces(const LinkInfo& link_info); static methodHandle lookup_polymorphic_method(const LinkInfo& link_info, - Handle *appendix_result_or_null, - Handle *method_type_result, TRAPS); + Handle *appendix_result_or_null, TRAPS); JVMCI_ONLY(public:) // Needed for CompilerToVM.resolveMethod() // Not Linktime so doesn't take LinkInfo static methodHandle lookup_instance_method_in_klasses (Klass* klass, Symbol* name, Symbol* signature, @@ -315,9 +426,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) --- old/src/hotspot/share/interpreter/rewriter.cpp 2018-09-28 11:53:23.000000000 -0700 +++ new/src/hotspot/share/interpreter/rewriter.cpp 2018-09-28 11:53:22.000000000 -0700 @@ -96,7 +96,7 @@ ConstantPoolCache* cache = ConstantPoolCache::allocate(loader_data, _cp_cache_map, _invokedynamic_cp_cache_map, - _invokedynamic_references_map, CHECK); + _appendix_references_map, CHECK); // initialize object cache in constant pool _pool->set_cache(cache); @@ -221,13 +221,13 @@ MethodHandles::is_signature_polymorphic_name(SystemDictionary::MethodHandle_klass(), _pool->name_ref_at(cp_index))) { // we may need a resolved_refs entry for the appendix - add_invokedynamic_resolved_references_entries(cp_index, cache_index); + add_appendix_references_entry(cp_index, cache_index); status = +1; } else if (_pool->klass_ref_at_noresolve(cp_index) == vmSymbols::java_lang_invoke_VarHandle() && MethodHandles::is_signature_polymorphic_name(SystemDictionary::VarHandle_klass(), _pool->name_ref_at(cp_index))) { // we may need a resolved_refs entry for the appendix - add_invokedynamic_resolved_references_entries(cp_index, cache_index); + add_appendix_references_entry(cp_index, cache_index); status = +1; } else { status = -1; @@ -259,7 +259,7 @@ if (!reverse) { int cp_index = Bytes::get_Java_u2(p); int cache_index = add_invokedynamic_cp_cache_entry(cp_index); - int resolved_index = add_invokedynamic_resolved_references_entries(cp_index, cache_index); + int resolved_index = add_appendix_references_entry(cp_index, cache_index); // Replace the trailing four bytes with a CPC index for the dynamic // call site. Unlike other CPC entries, there is one per bytecode, // not just one per distinct CP entry. In other words, the @@ -307,12 +307,9 @@ // invokedynamic resolved references map also points to cp cache and must // add delta to each. int resolved_index = _patch_invokedynamic_refs->at(i); - for (int entry = 0; entry < ConstantPoolCacheEntry::_indy_resolved_references_entries; entry++) { - assert(_invokedynamic_references_map.at(resolved_index + entry) == cache_index, + assert(_appendix_references_map.at(resolved_index) == cache_index, "should be the same index"); - _invokedynamic_references_map.at_put(resolved_index+entry, - cache_index + delta); - } + _appendix_references_map.at_put(resolved_index, cache_index + delta); } } } @@ -584,7 +581,7 @@ _cp_cache_map(cpool->length() / 2), _reference_map(cpool->length()), _resolved_references_map(cpool->length() / 2), - _invokedynamic_references_map(cpool->length() / 2), + _appendix_references_map(cpool->length() / 2), _method_handle_invokers(cpool->length()), _invokedynamic_cp_cache_map(cpool->length() / 4) { --- old/src/hotspot/share/interpreter/rewriter.hpp 2018-09-28 11:53:25.000000000 -0700 +++ new/src/hotspot/share/interpreter/rewriter.hpp 2018-09-28 11:53:24.000000000 -0700 @@ -41,7 +41,7 @@ // InterfaceMethodref and InvokeDynamic GrowableArray _reference_map; // maps from cp index to resolved_refs index (or -1) GrowableArray _resolved_references_map; // for strings, methodHandle, methodType - GrowableArray _invokedynamic_references_map; // for invokedynamic resolved refs + GrowableArray _appendix_references_map; // for invokedynamic and invokehandle resolved refs GrowableArray _method_handle_invokers; int _resolved_reference_limit; @@ -67,7 +67,7 @@ _method_handle_invokers.trunc_to(0); _resolved_references_map.trunc_to(0); - _invokedynamic_references_map.trunc_to(0); + _appendix_references_map.trunc_to(0); _resolved_reference_limit = -1; _first_iteration_cp_cache_limit = -1; @@ -160,18 +160,11 @@ } // add a new entries to the resolved_references map (for invokedynamic and invokehandle only) - int add_invokedynamic_resolved_references_entries(int cp_index, int cache_index) { + int add_appendix_references_entry(int cp_index, int cache_index) { assert(_resolved_reference_limit >= 0, "must add indy refs after first iteration"); - int ref_index = -1; - for (int entry = 0; entry < ConstantPoolCacheEntry::_indy_resolved_references_entries; entry++) { - const int index = _resolved_references_map.append(cp_index); // many-to-one - assert(index >= _resolved_reference_limit, ""); - if (entry == 0) { - ref_index = index; - } - assert((index - entry) == ref_index, "entries must be consecutive"); - _invokedynamic_references_map.at_put_grow(index, cache_index, -1); - } + const int ref_index = _resolved_references_map.append(cp_index); // many-to-one + assert(ref_index >= _resolved_reference_limit, ""); + _appendix_references_map.at_put_grow(ref_index, cache_index, -1); return ref_index; } --- old/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp 2018-09-28 11:53:27.000000000 -0700 +++ new/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp 2018-09-28 11:53:26.000000000 -0700 @@ -1225,7 +1225,6 @@ vmassert(MethodHandles::is_signature_polymorphic_method(resolved_method()),"!"); vmassert(!MethodHandles::is_signature_polymorphic_static(resolved_method->intrinsic_id()), "!"); vmassert(cp_cache_entry->appendix_if_resolved(cp) == NULL, "!"); - vmassert(cp_cache_entry->method_type_if_resolved(cp) == NULL, "!"); methodHandle m(LinkResolver::linktime_resolve_virtual_method_or_null(link_info)); vmassert(m == resolved_method, "!!"); --- old/src/hotspot/share/memory/oopFactory.cpp 2018-09-28 11:53:29.000000000 -0700 +++ new/src/hotspot/share/memory/oopFactory.cpp 2018-09-28 11:53:28.000000000 -0700 @@ -97,3 +97,13 @@ typeArrayOop obj = new_byteArray(length, CHECK_(typeArrayHandle())); return typeArrayHandle(THREAD, obj); } + +typeArrayHandle oopFactory::new_intArray_handle(int length, TRAPS) { + typeArrayOop obj = new_intArray(length, CHECK_(typeArrayHandle())); + return typeArrayHandle(THREAD, obj); +} + +typeArrayHandle oopFactory::new_typeArray_handle(BasicType type, int length, TRAPS) { + typeArrayOop obj = new_typeArray(type, length, CHECK_(typeArrayHandle())); + return typeArrayHandle(THREAD, obj); +} --- old/src/hotspot/share/memory/oopFactory.hpp 2018-09-28 11:53:31.000000000 -0700 +++ new/src/hotspot/share/memory/oopFactory.hpp 2018-09-28 11:53:30.000000000 -0700 @@ -70,6 +70,8 @@ // Helpers that return handles static objArrayHandle new_objArray_handle(Klass* klass, int length, TRAPS); static typeArrayHandle new_byteArray_handle(int length, TRAPS); + static typeArrayHandle new_intArray_handle(int length, TRAPS); + static typeArrayHandle new_typeArray_handle(BasicType type, int length, TRAPS); }; #endif // SHARE_VM_MEMORY_OOPFACTORY_HPP --- old/src/hotspot/share/oops/constantPool.cpp 2018-09-28 11:53:33.000000000 -0700 +++ new/src/hotspot/share/oops/constantPool.cpp 2018-09-28 11:53:32.000000000 -0700 @@ -588,21 +588,13 @@ } -bool ConstantPool::has_method_type_at_if_loaded(const constantPoolHandle& cpool, int which) { +bool ConstantPool::has_local_signature_at_if_loaded(const constantPoolHandle& cpool, int which) { if (cpool->cache() == NULL) return false; // nothing to load yet int cache_index = decode_cpcache_index(which, true); ConstantPoolCacheEntry* e = cpool->cache()->entry_at(cache_index); - return e->has_method_type(); + return e->has_local_signature(); } -oop ConstantPool::method_type_at_if_loaded(const constantPoolHandle& cpool, int which) { - if (cpool->cache() == NULL) return NULL; // nothing to load yet - int cache_index = decode_cpcache_index(which, true); - ConstantPoolCacheEntry* e = cpool->cache()->entry_at(cache_index); - return e->method_type_if_resolved(cpool); -} - - Symbol* ConstantPool::impl_name_ref_at(int which, bool uncached) { int name_index = name_ref_index_at(impl_name_and_type_ref_index_at(which, uncached)); return symbol_at(name_index); @@ -619,26 +611,22 @@ if (!uncached && cache() != NULL) { if (ConstantPool::is_invokedynamic_index(which)) { // Invokedynamic index is index into the constant pool cache - int pool_index = invokedynamic_cp_cache_entry_at(which)->constant_pool_index(); - pool_index = invoke_dynamic_name_and_type_ref_index_at(pool_index); + int pool_index = invokedynamic_bootstrap_ref_index_at(which); + pool_index = bootstrap_name_and_type_ref_index_at(pool_index); assert(tag_at(pool_index).is_name_and_type(), ""); return pool_index; } // change byte-ordering and go via cache i = remap_instruction_operand_from_cache(which); } else { - if (tag_at(which).is_invoke_dynamic() || - tag_at(which).is_dynamic_constant() || - tag_at(which).is_dynamic_constant_in_error()) { - int pool_index = invoke_dynamic_name_and_type_ref_index_at(which); + if (tag_at(which).has_bootstrap()) { + int pool_index = bootstrap_name_and_type_ref_index_at(which); assert(tag_at(pool_index).is_name_and_type(), ""); return pool_index; } } assert(tag_at(i).is_field_or_method(), "Corrupted constant pool"); - assert(!tag_at(i).is_invoke_dynamic() && - !tag_at(i).is_dynamic_constant() && - !tag_at(i).is_dynamic_constant_in_error(), "Must be handled above"); + assert(!tag_at(i).has_bootstrap(), "Must be handled above"); jint ref_index = *int_at_addr(i); return extract_high_short_from_int(ref_index); } @@ -648,7 +636,7 @@ if (!uncached && cache() != NULL) { if (ConstantPool::is_invokedynamic_index(which)) { // Invokedynamic index is index into resolved_references - pool_index = invokedynamic_cp_cache_entry_at(which)->constant_pool_index(); + pool_index = invokedynamic_bootstrap_ref_index_at(which); } else { // change byte-ordering and go via cache pool_index = remap_instruction_operand_from_cache(which); @@ -853,7 +841,10 @@ // FIXME: If bootstrap specifiers stress this code, consider putting in // a reverse index. Binary search over a short array should do it. assert(index > 0, "valid index"); - cache_index = this_cp->cp_to_object_index(index); + if (this_cp->reference_map() == NULL) + cache_index = _no_index_sentinel; + else + cache_index = this_cp->cp_to_object_index(index); } assert(cache_index == _no_index_sentinel || cache_index >= 0, ""); assert(index == _no_index_sentinel || index >= 0, ""); @@ -921,10 +912,6 @@ 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); - // The initial step in resolving an unresolved symbolic reference to a // dynamically-computed constant is to resolve the symbolic reference to a // method handle which will be the bootstrap method for the dynamically-computed @@ -933,27 +920,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); + BootstrapInfo bootstrap_specifier(this_cp, index); // 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(); + 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. @@ -1111,154 +1089,84 @@ 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->invoke_dynamic_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->invoke_dynamic_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->invoke_dynamic_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->invoke_dynamic_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, - bool must_resolve, Handle if_not_available, + arrayHandle buf, int pos, + BootstrapArgumentReferenceMode resolving, + Handle if_not_available, + Handle if_null_constant, + bool skip_non_null, + bool skip_recursion, TRAPS) { + objArrayHandle obj_buf; // set to buf if type Object[] + typeArrayHandle int_buf; // set to buf if type int[] + if (buf.not_null()) { + switch (resolving) { + case R_IFPRESENT: // mode = do not resolve new values + case R_FORCE: // mode = resolve any requested values (cf. skip_non_null) + if (buf->klass() == Universe::objectArrayKlassObj()) + obj_buf = objArrayHandle(THREAD, (objArrayOop) buf()); + break; + case R_SYMREF: // mode = return only CP indexes (symbolic refs) + if (buf->klass() == Universe::intArrayKlassObj()) + int_buf = typeArrayHandle(THREAD, (typeArrayOop) buf()); + break; + } + if (obj_buf.is_null() && int_buf.is_null()) + buf = arrayHandle(); // buf matches neither case; nullify + } int argc; int limit = pos + end_arg - start_arg; // checks: index in range [0..this_cp->length), // tag at index, start..end in range [0..argc], - // info array non-null, pos..limit in [0..info.length] + // buf array non-null, pos..limit in [0..buf.length] if ((0 >= index || index >= this_cp->length()) || - !(this_cp->tag_at(index).is_invoke_dynamic() || - this_cp->tag_at(index).is_dynamic_constant()) || + !this_cp->tag_at(index).has_bootstrap() || (0 > start_arg || start_arg > end_arg) || - (end_arg > (argc = this_cp->invoke_dynamic_argument_count_at(index))) || + (end_arg > (argc = this_cp->bootstrap_argument_count_at(index))) || (0 > pos || pos > limit) || - (info.is_null() || limit > info->length())) { + (buf.is_null() || limit > buf->length())) { // An index or something else went wrong; throw an error. // Since this is an internal API, we don't expect this, // so we don't bother to craft a nice message. THROW_MSG(vmSymbols::java_lang_LinkageError(), "bad BSM argument access"); } // now we can loop safely - int info_i = pos; + int buf_i = pos; for (int i = start_arg; i < end_arg; i++) { - int arg_index = this_cp->invoke_dynamic_argument_index_at(index, i); - oop arg_oop; - if (must_resolve) { + if (skip_non_null && obj_buf.not_null() && obj_buf->obj_at(buf_i) != NULL) { + // buf is somebody's cache; don't disturb non-null entries + buf_i++; + continue; + } + int arg_index = this_cp->bootstrap_argument_index_at(index, i); + BootstrapArgumentReferenceMode mode = resolving; + if (mode == R_FORCE && skip_recursion && this_cp->tag_at(arg_index).has_bootstrap()) { + // don't force recursive execution of BSMs (via condy) + mode = R_IFPRESENT; // but *do* return a value if it is available + } + oop arg_oop = NULL; + int arg_int = 0; + bool found_it; + if (mode == R_FORCE) { arg_oop = this_cp->resolve_possibly_cached_constant_at(arg_index, CHECK); - } else { - bool found_it = false; + found_it = true; + } else if (mode == R_IFPRESENT) { + found_it = false; arg_oop = this_cp->find_cached_constant_at(arg_index, found_it, CHECK); - if (!found_it) arg_oop = if_not_available(); - } - info->obj_at_put(info_i++, arg_oop); + } else { + assert(mode == R_SYMREF, ""); + arg_int = arg_index; + found_it = true; + } + if (!found_it) + arg_oop = if_not_available(); // might be NULL! + else if (arg_oop == NULL) + arg_oop = if_null_constant(); + if (obj_buf.not_null()) + obj_buf->obj_at_put(buf_i++, arg_oop); + else + int_buf->int_at_put(buf_i++, arg_int); } } @@ -1448,10 +1356,10 @@ case JVM_CONSTANT_Dynamic: { - int k1 = invoke_dynamic_name_and_type_ref_index_at(index1); - int k2 = cp2->invoke_dynamic_name_and_type_ref_index_at(index2); - int i1 = invoke_dynamic_bootstrap_specifier_index(index1); - int i2 = cp2->invoke_dynamic_bootstrap_specifier_index(index2); + int k1 = bootstrap_name_and_type_ref_index_at(index1); + int k2 = cp2->bootstrap_name_and_type_ref_index_at(index2); + int i1 = bootstrap_methods_attribute_index(index1); + int i2 = cp2->bootstrap_methods_attribute_index(index2); // separate statements and variables because CHECK_false is used bool match_entry = compare_entry_to(k1, cp2, k2, CHECK_false); bool match_operand = compare_operand_to(i1, cp2, i2, CHECK_false); @@ -1460,10 +1368,10 @@ case JVM_CONSTANT_InvokeDynamic: { - int k1 = invoke_dynamic_name_and_type_ref_index_at(index1); - int k2 = cp2->invoke_dynamic_name_and_type_ref_index_at(index2); - int i1 = invoke_dynamic_bootstrap_specifier_index(index1); - int i2 = cp2->invoke_dynamic_bootstrap_specifier_index(index2); + int k1 = bootstrap_name_and_type_ref_index_at(index1); + int k2 = cp2->bootstrap_name_and_type_ref_index_at(index2); + int i1 = bootstrap_methods_attribute_index(index1); + int i2 = cp2->bootstrap_methods_attribute_index(index2); // separate statements and variables because CHECK_false is used bool match_entry = compare_entry_to(k1, cp2, k2, CHECK_false); bool match_operand = compare_operand_to(i1, cp2, i2, CHECK_false); @@ -1787,16 +1695,16 @@ case JVM_CONSTANT_Dynamic: case JVM_CONSTANT_DynamicInError: { - int k1 = from_cp->invoke_dynamic_bootstrap_specifier_index(from_i); - int k2 = from_cp->invoke_dynamic_name_and_type_ref_index_at(from_i); + int k1 = from_cp->bootstrap_methods_attribute_index(from_i); + int k2 = from_cp->bootstrap_name_and_type_ref_index_at(from_i); k1 += operand_array_length(to_cp->operands()); // to_cp might already have operands to_cp->dynamic_constant_at_put(to_i, k1, k2); } break; case JVM_CONSTANT_InvokeDynamic: { - int k1 = from_cp->invoke_dynamic_bootstrap_specifier_index(from_i); - int k2 = from_cp->invoke_dynamic_name_and_type_ref_index_at(from_i); + int k1 = from_cp->bootstrap_methods_attribute_index(from_i); + int k2 = from_cp->bootstrap_name_and_type_ref_index_at(from_i); k1 += operand_array_length(to_cp->operands()); // to_cp might already have operands to_cp->invoke_dynamic_at_put(to_i, k1, k2); } break; @@ -1857,8 +1765,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++) { @@ -1867,7 +1775,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() @@ -2246,7 +2154,7 @@ *bytes = tag; idx1 = extract_low_short_from_int(*int_at_addr(idx)); idx2 = extract_high_short_from_int(*int_at_addr(idx)); - assert(idx2 == invoke_dynamic_name_and_type_ref_index_at(idx), "correct half of u4"); + assert(idx2 == bootstrap_name_and_type_ref_index_at(idx), "correct half of u4"); Bytes::put_Java_u2((address) (bytes+1), idx1); Bytes::put_Java_u2((address) (bytes+3), idx2); DBG(printf("JVM_CONSTANT_Dynamic: %hd %hd", idx1, idx2)); @@ -2256,7 +2164,7 @@ *bytes = tag; idx1 = extract_low_short_from_int(*int_at_addr(idx)); idx2 = extract_high_short_from_int(*int_at_addr(idx)); - assert(idx2 == invoke_dynamic_name_and_type_ref_index_at(idx), "correct half of u4"); + assert(idx2 == bootstrap_name_and_type_ref_index_at(idx), "correct half of u4"); Bytes::put_Java_u2((address) (bytes+1), idx1); Bytes::put_Java_u2((address) (bytes+3), idx2); DBG(printf("JVM_CONSTANT_InvokeDynamic: %hd %hd", idx1, idx2)); @@ -2460,12 +2368,12 @@ case JVM_CONSTANT_Dynamic : case JVM_CONSTANT_DynamicInError : { - st->print("bootstrap_method_index=%d", invoke_dynamic_bootstrap_method_ref_index_at(index)); - st->print(" type_index=%d", invoke_dynamic_name_and_type_ref_index_at(index)); - int argc = invoke_dynamic_argument_count_at(index); + st->print("bootstrap_method_index=%d", bootstrap_method_ref_index_at(index)); + st->print(" type_index=%d", bootstrap_name_and_type_ref_index_at(index)); + int argc = bootstrap_argument_count_at(index); if (argc > 0) { for (int arg_i = 0; arg_i < argc; arg_i++) { - int arg = invoke_dynamic_argument_index_at(index, arg_i); + int arg = bootstrap_argument_index_at(index, arg_i); st->print((arg_i == 0 ? " arguments={%d" : ", %d"), arg); } st->print("}"); @@ -2474,12 +2382,12 @@ break; case JVM_CONSTANT_InvokeDynamic : { - st->print("bootstrap_method_index=%d", invoke_dynamic_bootstrap_method_ref_index_at(index)); - st->print(" name_and_type_index=%d", invoke_dynamic_name_and_type_ref_index_at(index)); - int argc = invoke_dynamic_argument_count_at(index); + st->print("bootstrap_method_index=%d", bootstrap_method_ref_index_at(index)); + st->print(" name_and_type_index=%d", bootstrap_name_and_type_ref_index_at(index)); + int argc = bootstrap_argument_count_at(index); if (argc > 0) { for (int arg_i = 0; arg_i < argc; arg_i++) { - int arg = invoke_dynamic_argument_index_at(index, arg_i); + int arg = bootstrap_argument_index_at(index, arg_i); st->print((arg_i == 0 ? " arguments={%d" : ", %d"), arg); } st->print("}"); --- old/src/hotspot/share/oops/constantPool.hpp 2018-09-28 11:53:35.000000000 -0700 +++ new/src/hotspot/share/oops/constantPool.hpp 2018-09-28 11:53:34.000000000 -0700 @@ -246,16 +246,22 @@ // The invokedynamic points at a CP cache entry. This entry points back // at the original CP entry (CONSTANT_InvokeDynamic) and also (via f2) at an entry // in the resolved_references array (which provides the appendix argument). - int invokedynamic_cp_cache_index(int index) const { - assert (is_invokedynamic_index(index), "should be a invokedynamic index"); - int cache_index = decode_invokedynamic_index(index); + int invokedynamic_cp_cache_index(int indy_index) const { + assert(is_invokedynamic_index(indy_index), "should be a invokedynamic index"); + int cache_index = decode_invokedynamic_index(indy_index); return cache_index; } - ConstantPoolCacheEntry* invokedynamic_cp_cache_entry_at(int index) const { + ConstantPoolCacheEntry* invokedynamic_cp_cache_entry_at(int indy_index) const { // decode index that invokedynamic points to. - int cp_cache_index = invokedynamic_cp_cache_index(index); + int cp_cache_index = invokedynamic_cp_cache_index(indy_index); return cache()->entry_at(cp_cache_index); } + // Given the per-instruction index of an indy instruction, report the + // main constant pool entry for its bootstrap specifier. + // From there, uncached_name/signature_ref_at will get the name/type. + int invokedynamic_bootstrap_ref_index_at(int indy_index) const { + return invokedynamic_cp_cache_entry_at(indy_index)->constant_pool_index(); + } // Assembly code support static int tags_offset_in_bytes() { return offset_of(ConstantPool, _tags); } @@ -294,14 +300,14 @@ *int_at_addr(which) = ref_index; } - void dynamic_constant_at_put(int which, int bootstrap_specifier_index, int name_and_type_index) { + void dynamic_constant_at_put(int which, int bsms_attribute_index, int name_and_type_index) { tag_at_put(which, JVM_CONSTANT_Dynamic); - *int_at_addr(which) = ((jint) name_and_type_index<<16) | bootstrap_specifier_index; + *int_at_addr(which) = ((jint) name_and_type_index<<16) | bsms_attribute_index; } - void invoke_dynamic_at_put(int which, int bootstrap_specifier_index, int name_and_type_index) { + void invoke_dynamic_at_put(int which, int bsms_attribute_index, int name_and_type_index) { tag_at_put(which, JVM_CONSTANT_InvokeDynamic); - *int_at_addr(which) = ((jint) name_and_type_index<<16) | bootstrap_specifier_index; + *int_at_addr(which) = ((jint) name_and_type_index<<16) | bsms_attribute_index; } void unresolved_string_at_put(int which, Symbol* s) { @@ -534,26 +540,22 @@ return symbol_at(sym); } - int invoke_dynamic_name_and_type_ref_index_at(int which) { - assert(tag_at(which).is_invoke_dynamic() || - tag_at(which).is_dynamic_constant() || - tag_at(which).is_dynamic_constant_in_error(), "Corrupted constant pool"); + int bootstrap_name_and_type_ref_index_at(int which) { + assert(tag_at(which).has_bootstrap(), "Corrupted constant pool"); return extract_high_short_from_int(*int_at_addr(which)); } - int invoke_dynamic_bootstrap_specifier_index(int which) { - assert(tag_at(which).is_invoke_dynamic() || - tag_at(which).is_dynamic_constant() || - tag_at(which).is_dynamic_constant_in_error(), "Corrupted constant pool"); + int bootstrap_methods_attribute_index(int which) { + assert(tag_at(which).has_bootstrap(), "Corrupted constant pool"); return extract_low_short_from_int(*int_at_addr(which)); } - int invoke_dynamic_operand_base(int which) { - int bootstrap_specifier_index = invoke_dynamic_bootstrap_specifier_index(which); - return operand_offset_at(operands(), bootstrap_specifier_index); + int bootstrap_operand_base(int which) { + int bsms_attribute_index = bootstrap_methods_attribute_index(which); + return operand_offset_at(operands(), bsms_attribute_index); } // The first part of the operands array consists of an index into the second part. // Extract a 32-bit index value from the first part. - static int operand_offset_at(Array* operands, int bootstrap_specifier_index) { - int n = (bootstrap_specifier_index * 2); + static int operand_offset_at(Array* operands, int bsms_attribute_index) { + int n = (bsms_attribute_index * 2); assert(n >= 0 && n+2 <= operands->length(), "oob"); // The first 32-bit index points to the beginning of the second part // of the operands array. Make sure this index is in the first part. @@ -566,8 +568,8 @@ assert(offset == 0 || offset >= second_part && offset <= operands->length(), "oob (3)"); return offset; } - static void operand_offset_at_put(Array* operands, int bootstrap_specifier_index, int offset) { - int n = bootstrap_specifier_index * 2; + static void operand_offset_at_put(Array* operands, int bsms_attribute_index, int offset) { + int n = bsms_attribute_index * 2; assert(n >= 0 && n+2 <= operands->length(), "oob"); operands->at_put(n+0, extract_low_short_from_int(offset)); operands->at_put(n+1, extract_high_short_from_int(offset)); @@ -580,20 +582,23 @@ #ifdef ASSERT // operand tuples fit together exactly, end to end - static int operand_limit_at(Array* operands, int bootstrap_specifier_index) { - int nextidx = bootstrap_specifier_index + 1; + static int operand_limit_at(Array* operands, int bsms_attribute_index) { + int nextidx = bsms_attribute_index + 1; if (nextidx == operand_array_length(operands)) return operands->length(); else return operand_offset_at(operands, nextidx); } - int invoke_dynamic_operand_limit(int which) { - int bootstrap_specifier_index = invoke_dynamic_bootstrap_specifier_index(which); - return operand_limit_at(operands(), bootstrap_specifier_index); + int bootstrap_operand_limit(int which) { + int bsms_attribute_index = bootstrap_methods_attribute_index(which); + return operand_limit_at(operands(), bsms_attribute_index); } #endif //ASSERT - // layout of InvokeDynamic and Dynamic bootstrap method specifier (in second part of operands array): + // Layout of InvokeDynamic and Dynamic bootstrap method specifier + // data in second part of operands array. This encodes one record in + // the BootstrapMethods attribute. The whole specifier also includes + // the name and type information from the main constant pool entry. enum { _indy_bsm_offset = 0, // CONSTANT_MethodHandle bsm _indy_argc_offset = 1, // u2 argc @@ -602,35 +607,35 @@ // These functions are used in RedefineClasses for CP merge - int operand_offset_at(int bootstrap_specifier_index) { - assert(0 <= bootstrap_specifier_index && - bootstrap_specifier_index < operand_array_length(operands()), + int operand_offset_at(int bsms_attribute_index) { + assert(0 <= bsms_attribute_index && + bsms_attribute_index < operand_array_length(operands()), "Corrupted CP operands"); - return operand_offset_at(operands(), bootstrap_specifier_index); + return operand_offset_at(operands(), bsms_attribute_index); } - int operand_bootstrap_method_ref_index_at(int bootstrap_specifier_index) { - int offset = operand_offset_at(bootstrap_specifier_index); + int operand_bootstrap_method_ref_index_at(int bsms_attribute_index) { + int offset = operand_offset_at(bsms_attribute_index); return operands()->at(offset + _indy_bsm_offset); } - int operand_argument_count_at(int bootstrap_specifier_index) { - int offset = operand_offset_at(bootstrap_specifier_index); + int operand_argument_count_at(int bsms_attribute_index) { + int offset = operand_offset_at(bsms_attribute_index); int argc = operands()->at(offset + _indy_argc_offset); return argc; } - int operand_argument_index_at(int bootstrap_specifier_index, int j) { - int offset = operand_offset_at(bootstrap_specifier_index); + int operand_argument_index_at(int bsms_attribute_index, int j) { + int offset = operand_offset_at(bsms_attribute_index); return operands()->at(offset + _indy_argv_offset + j); } - int operand_next_offset_at(int bootstrap_specifier_index) { - int offset = operand_offset_at(bootstrap_specifier_index) + _indy_argv_offset - + operand_argument_count_at(bootstrap_specifier_index); + int operand_next_offset_at(int bsms_attribute_index) { + int offset = operand_offset_at(bsms_attribute_index) + _indy_argv_offset + + operand_argument_count_at(bsms_attribute_index); return offset; } - // Compare a bootsrap specifier in the operands arrays - bool compare_operand_to(int bootstrap_specifier_index1, const constantPoolHandle& cp2, - int bootstrap_specifier_index2, TRAPS); - // Find a bootsrap specifier in the operands array - int find_matching_operand(int bootstrap_specifier_index, const constantPoolHandle& search_cp, + // Compare a bootstrap specifier data in the operands arrays + bool compare_operand_to(int bsms_attribute_index1, const constantPoolHandle& cp2, + int bsms_attribute_index2, TRAPS); + // Find a bootstrap specifier data in the operands array + int find_matching_operand(int bsms_attribute_index, const constantPoolHandle& search_cp, int operands_cur_len, TRAPS); // Resize the operands array with delta_len and delta_size void resize_operands(int delta_len, int delta_size, TRAPS); @@ -639,26 +644,22 @@ // Shrink the operands array to a smaller array with new_len length void shrink_operands(int new_len, TRAPS); - int invoke_dynamic_bootstrap_method_ref_index_at(int which) { - assert(tag_at(which).is_invoke_dynamic() || - tag_at(which).is_dynamic_constant() || - tag_at(which).is_dynamic_constant_in_error(), "Corrupted constant pool"); - int op_base = invoke_dynamic_operand_base(which); + int bootstrap_method_ref_index_at(int which) { + assert(tag_at(which).has_bootstrap(), "Corrupted constant pool"); + int op_base = bootstrap_operand_base(which); return operands()->at(op_base + _indy_bsm_offset); } - int invoke_dynamic_argument_count_at(int which) { - assert(tag_at(which).is_invoke_dynamic() || - tag_at(which).is_dynamic_constant() || - tag_at(which).is_dynamic_constant_in_error(), "Corrupted constant pool"); - int op_base = invoke_dynamic_operand_base(which); + int bootstrap_argument_count_at(int which) { + assert(tag_at(which).has_bootstrap(), "Corrupted constant pool"); + int op_base = bootstrap_operand_base(which); int argc = operands()->at(op_base + _indy_argc_offset); DEBUG_ONLY(int end_offset = op_base + _indy_argv_offset + argc; - int next_offset = invoke_dynamic_operand_limit(which)); + int next_offset = bootstrap_operand_limit(which)); assert(end_offset == next_offset, "matched ending"); return argc; } - int invoke_dynamic_argument_index_at(int which, int j) { - int op_base = invoke_dynamic_operand_base(which); + int bootstrap_argument_index_at(int which, int j) { + int op_base = bootstrap_operand_base(which); DEBUG_ONLY(int argc = operands()->at(op_base + _indy_argc_offset)); assert((uint)j < (uint)argc, "oob"); return operands()->at(op_base + _indy_argv_offset + j); @@ -745,18 +746,21 @@ 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); - } + enum BootstrapArgumentReferenceMode { R_IFPRESENT = 0, R_FORCE = 1, R_SYMREF = 2 }; void copy_bootstrap_arguments_at(int index, int start_arg, int end_arg, - objArrayHandle info, int pos, - bool must_resolve, Handle if_not_available, TRAPS) { + arrayHandle buf, int pos, + BootstrapArgumentReferenceMode resolving, + Handle if_not_available, Handle if_null_constant, + bool skip_non_null, // don't overwrite previous stuff in buf + bool skip_recursion, // don't resolve CONSTANT_Dynamic args + TRAPS) { constantPoolHandle h_this(THREAD, this); copy_bootstrap_arguments_at_impl(h_this, index, start_arg, end_arg, - info, pos, must_resolve, if_not_available, THREAD); + buf, pos, + resolving, if_not_available, if_null_constant, + skip_non_null, skip_recursion, THREAD); } // Klass name matches name at offset @@ -796,8 +800,7 @@ static Method* method_at_if_loaded (const constantPoolHandle& this_cp, int which); static bool has_appendix_at_if_loaded (const constantPoolHandle& this_cp, int which); static oop appendix_at_if_loaded (const constantPoolHandle& this_cp, int which); - static bool has_method_type_at_if_loaded (const constantPoolHandle& this_cp, int which); - static oop method_type_at_if_loaded (const constantPoolHandle& this_cp, int which); + static bool has_local_signature_at_if_loaded (const constantPoolHandle& this_cp, int which); static Klass* klass_at_if_loaded (const constantPoolHandle& this_cp, int which); // Routines currently used for annotations (only called by jvm.cpp) but which might be used in the @@ -871,11 +874,15 @@ 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, - bool must_resolve, Handle if_not_available, TRAPS); + arrayHandle buf, int pos, + BootstrapArgumentReferenceMode resolving, + Handle if_not_available, + Handle if_null_constant, + bool skip_non_null, + bool skip_recursion, + TRAPS); // Exception handling static Symbol* exception_message(const constantPoolHandle& this_cp, int which, constantTag tag, oop pending_exception); --- old/src/hotspot/share/oops/cpCache.cpp 2018-09-28 11:53:37.000000000 -0700 +++ new/src/hotspot/share/oops/cpCache.cpp 2018-09-28 11:53:36.000000000 -0700 @@ -381,24 +381,23 @@ const methodHandle adapter = call_info.resolved_method(); const Handle appendix = call_info.resolved_appendix(); - const Handle method_type = call_info.resolved_method_type(); const bool has_appendix = appendix.not_null(); - const bool has_method_type = method_type.not_null(); + const bool has_local_sig = call_info.has_local_signature(); + assert(has_local_sig, "MHs and indy are always sig-poly"); // Write the flags. set_method_flags(as_TosState(adapter->result_type()), - ((has_appendix ? 1 : 0) << has_appendix_shift ) | - ((has_method_type ? 1 : 0) << has_method_type_shift) | - ( 1 << is_final_shift ), + ((has_appendix ? 1 : 0) << has_appendix_shift ) | + ((has_local_sig ? 1 : 0) << has_local_signature_shift ) | + ( 1 << is_final_shift ), adapter->size_of_parameters()); if (TraceInvokeDynamic) { ttyLocker ttyl; - tty->print_cr("set_method_handle bc=%d appendix=" PTR_FORMAT "%s method_type=" PTR_FORMAT "%s method=" PTR_FORMAT " ", + tty->print_cr("set_method_handle bc=%d appendix=" PTR_FORMAT "%s method=" PTR_FORMAT "%s ", invoke_code, p2i(appendix()), (has_appendix ? "" : " (unused)"), - p2i(method_type()), (has_method_type ? "" : " (unused)"), - p2i(adapter())); + p2i(adapter()), (has_local_sig ? " (local signature)" : "")); adapter->print(); if (has_appendix) appendix()->print(); } @@ -424,20 +423,12 @@ // Store appendix, if any. if (has_appendix) { - const int appendix_index = f2_as_index() + _indy_resolved_references_appendix_offset; + const int appendix_index = f2_as_index(); assert(appendix_index >= 0 && appendix_index < resolved_references->length(), "oob"); assert(resolved_references->obj_at(appendix_index) == NULL, "init just once"); resolved_references->obj_at_put(appendix_index, appendix()); } - // Store MethodType, if any. - if (has_method_type) { - const int method_type_index = f2_as_index() + _indy_resolved_references_method_type_offset; - assert(method_type_index >= 0 && method_type_index < resolved_references->length(), "oob"); - assert(resolved_references->obj_at(method_type_index) == NULL, "init just once"); - resolved_references->obj_at_put(method_type_index, method_type()); - } - release_set_f1(adapter()); // This must be the last one to set (see NOTE above)! // The interpreter assembly code does not check byte_2, @@ -448,6 +439,9 @@ ttyLocker ttyl; this->print(tty, 0); } + + assert(has_appendix == this->has_appendix(), "proper storage of appendix flag"); + assert(has_local_sig == this->has_local_signature(), "proper storage of signature flag"); } bool ConstantPoolCacheEntry::save_and_throw_indy_exc( @@ -533,16 +527,7 @@ oop ConstantPoolCacheEntry::appendix_if_resolved(const constantPoolHandle& cpool) { if (!has_appendix()) return NULL; - const int ref_index = f2_as_index() + _indy_resolved_references_appendix_offset; - objArrayOop resolved_references = cpool->resolved_references(); - return resolved_references->obj_at(ref_index); -} - - -oop ConstantPoolCacheEntry::method_type_if_resolved(const constantPoolHandle& cpool) { - if (!has_method_type()) - return NULL; - const int ref_index = f2_as_index() + _indy_resolved_references_method_type_offset; + const int ref_index = f2_as_index(); objArrayOop resolved_references = cpool->resolved_references(); return resolved_references->obj_at(ref_index); } @@ -669,7 +654,7 @@ void ConstantPoolCache::initialize(const intArray& inverse_index_map, const intArray& invokedynamic_inverse_index_map, - const intArray& invokedynamic_references_map) { + const intArray& appendix_references_map) { for (int i = 0; i < inverse_index_map.length(); i++) { ConstantPoolCacheEntry* e = entry_at(i); int original_index = inverse_index_map.at(i); @@ -687,19 +672,10 @@ assert(entry_at(offset) == e, "sanity"); } - for (int ref = 0; ref < invokedynamic_references_map.length(); ref++) { - const int cpci = invokedynamic_references_map.at(ref); + for (int ref = 0; ref < appendix_references_map.length(); ref++) { + const int cpci = appendix_references_map.at(ref); if (cpci >= 0) { -#ifdef ASSERT - // invokedynamic and invokehandle have more entries; check if they - // all point to the same constant pool cache entry. - for (int entry = 1; entry < ConstantPoolCacheEntry::_indy_resolved_references_entries; entry++) { - const int cpci_next = invokedynamic_references_map.at(ref + entry); - assert(cpci == cpci_next, "%d == %d", cpci, cpci_next); - } -#endif entry_at(cpci)->initialize_resolved_reference_index(ref); - ref += ConstantPoolCacheEntry::_indy_resolved_references_entries - 1; // skip extra entries } } } --- old/src/hotspot/share/oops/cpCache.hpp 2018-09-28 11:53:39.000000000 -0700 +++ new/src/hotspot/share/oops/cpCache.hpp 2018-09-28 11:53:38.000000000 -0700 @@ -180,7 +180,7 @@ tos_state_shift = BitsPerInt - tos_state_bits, // see verify_tos_state_shift below // misc. option bits; can be any bit position in [16..27] is_field_entry_shift = 26, // (F) is it a field or a method? - has_method_type_shift = 25, // (M) does the call site have a MethodType? + has_local_signature_shift = 25, // (M) does the call site have a per-site signature (sig-poly methods)? has_appendix_shift = 24, // (A) does the call site have an appendix argument? is_forced_virtual_shift = 23, // (I) is the interface reference forced to virtual mode? is_final_shift = 22, // (f) is the field or method final? @@ -291,19 +291,10 @@ bool save_and_throw_indy_exc(const constantPoolHandle& cpool, int cpool_index, int index, constantTag tag, TRAPS); - // invokedynamic and invokehandle call sites have two entries in the + // invokedynamic and invokehandle call sites have an "appendix" item in the // resolved references array: - // appendix (at index+0) - // MethodType (at index+1) - enum { - _indy_resolved_references_appendix_offset = 0, - _indy_resolved_references_method_type_offset = 1, - _indy_resolved_references_entries - }; - Method* method_if_resolved(const constantPoolHandle& cpool); oop appendix_if_resolved(const constantPoolHandle& cpool); - oop method_type_if_resolved(const constantPoolHandle& cpool); void set_parameter_size(int value); @@ -356,7 +347,7 @@ bool is_vfinal() const { return (_flags & (1 << is_vfinal_shift)) != 0; } bool indy_resolution_failed() const; bool has_appendix() const; - bool has_method_type() const; + bool has_local_signature() const; bool is_method_entry() const { return (_flags & (1 << is_field_entry_shift)) == 0; } bool is_field_entry() const { return (_flags & (1 << is_field_entry_shift)) != 0; } bool is_long() const { return flag_state() == ltos; } @@ -443,7 +434,7 @@ static ConstantPoolCache* allocate(ClassLoaderData* loader_data, const intStack& cp_cache_map, const intStack& invokedynamic_cp_cache_map, - const intStack& invokedynamic_references_map, TRAPS); + const intStack& appendix_references_map, TRAPS); bool is_constantPoolCache() const { return true; } int length() const { return _length; } --- old/src/hotspot/share/oops/cpCache.inline.hpp 2018-09-28 11:53:41.000000000 -0700 +++ new/src/hotspot/share/oops/cpCache.inline.hpp 2018-09-28 11:53:40.000000000 -0700 @@ -71,8 +71,8 @@ return (!is_f1_null()) && (_flags & (1 << has_appendix_shift)) != 0; } -inline bool ConstantPoolCacheEntry::has_method_type() const { - return (!is_f1_null()) && (_flags & (1 << has_method_type_shift)) != 0; +inline bool ConstantPoolCacheEntry::has_local_signature() const { + return (!is_f1_null()) && (_flags & (1 << has_local_signature_shift)) != 0; } inline intx ConstantPoolCacheEntry::flags_ord() const { return (intx)OrderAccess::load_acquire(&_flags); } --- old/src/hotspot/share/prims/jvm.cpp 2018-09-28 11:53:43.000000000 -0700 +++ new/src/hotspot/share/prims/jvm.cpp 2018-09-28 11:53:42.000000000 -0700 @@ -45,6 +45,7 @@ #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/access.inline.hpp" +#include "oops/constantPool.inline.hpp" #include "oops/fieldStreams.hpp" #include "oops/instanceKlass.hpp" #include "oops/method.hpp" @@ -2000,9 +2001,8 @@ Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(cls)); if (k->is_instance_klass()) { InstanceKlass* k_h = InstanceKlass::cast(k); - Handle jcp = reflect_ConstantPool::create(CHECK_NULL); - reflect_ConstantPool::set_cp(jcp(), k_h->constants()); - return JNIHandles::make_local(jcp()); + Handle jcp = reflect_ConstantPool::create_from_pool(k_h->constants(), CHECK_NULL); + return JNIHandles::make_local(THREAD, jcp()); } } return NULL; @@ -2010,43 +2010,193 @@ JVM_END -JVM_ENTRY(jint, JVM_ConstantPoolGetSize(JNIEnv *env, jobject obj, jobject unused)) +// The name "ConstantPool1" means the successor to the original JVM_ConstantPool* API. +// That API had a confusing pair of arguments to specify the constant pool. +// This API has a single normal 'this' argument. + +JVM_ENTRY(jclass, JVM_ConstantPool1GetHolder(JNIEnv *env, jobject obj)) { - JVMWrapper("JVM_ConstantPoolGetSize"); + JVMWrapper("JVM_ConstantPoolGetHolder"); constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); - return cp->length(); + return (jclass) JNIHandles::make_local(THREAD, cp->pool_holder()->java_mirror()); } JVM_END -JVM_ENTRY(jclass, JVM_ConstantPoolGetClassAt(JNIEnv *env, jobject obj, jobject unused, jint index)) +JVM_ENTRY(jint, JVM_ConstantPool1GetSize(JNIEnv *env, jobject obj)) { - JVMWrapper("JVM_ConstantPoolGetClassAt"); + JVMWrapper("JVM_ConstantPoolGetSize"); constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); - bounds_check(cp, index, CHECK_NULL); - constantTag tag = cp->tag_at(index); - if (!tag.is_klass() && !tag.is_unresolved_klass()) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); - } - Klass* k = cp->klass_at(index, CHECK_NULL); - return (jclass) JNIHandles::make_local(k->java_mirror()); + return cp->length(); } JVM_END -JVM_ENTRY(jclass, JVM_ConstantPoolGetClassAtIfLoaded(JNIEnv *env, jobject obj, jobject unused, jint index)) -{ - JVMWrapper("JVM_ConstantPoolGetClassAtIfLoaded"); - constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); - bounds_check(cp, index, CHECK_NULL); - constantTag tag = cp->tag_at(index); - if (!tag.is_klass() && !tag.is_unresolved_klass()) { + +static jint ref_word_count_at_helper(constantPoolHandle cp, jint index) { + if (!cp->is_within_bounds(index)) return 0; + switch (cp->tag_at(index).classfile_value()) { + case JVM_CONSTANT_Utf8: + case JVM_CONSTANT_Class: + case JVM_CONSTANT_String: + case JVM_CONSTANT_MethodType: + return 1; + + case JVM_CONSTANT_Fieldref: + case JVM_CONSTANT_Methodref: + case JVM_CONSTANT_InterfaceMethodref: + return 5; // { klass, nat, klass_sym, name_sym, type_sym } + + case JVM_CONSTANT_NameAndType: + return 2; // { name_sym, type_sym } + + case JVM_CONSTANT_MethodHandle: + return 2; // { ref_kind, ref_index } + + case JVM_CONSTANT_InvokeDynamic: + case JVM_CONSTANT_Dynamic: + int word_count = 6; // { bsms_attr, nat, bsm, name_sym, type_sym, argc, arg* } + word_count += cp->bootstrap_argument_count_at(index); // arg* + return word_count; + } + + return 0; +} + +static void word_bounds_check(const constantPoolHandle& cp, jint index, jint word, jint word_count, TRAPS) { + assert(word_count == ref_word_count_at_helper(cp, index), ""); + if (word < 0 || word >= word_count) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "Constant pool field request out of bounds"); + } +} + +static jint get_ref_index_at_helper(constantPoolHandle cp, jint index, jint word, + jint* non_ref_word, TRAPS) { + int ref_index = -1; + int word_limit = 1; + switch (cp->tag_at(index).classfile_value()) { + case JVM_CONSTANT_Utf8: + // A Utf8 string is its own reference index, but return zero + // If you ask for the string itself, you'll see that string. + ref_index = 0; + break; + case JVM_CONSTANT_Class: + // Unwrap the Class and return the underlying name + ref_index = cp->klass_name_index_at(index); + break; + case JVM_CONSTANT_String: + // The CP does not record Utf8 indexes for strings, so just return zero. + ref_index = 0; + break; + + case JVM_CONSTANT_Fieldref: + case JVM_CONSTANT_Methodref: + case JVM_CONSTANT_InterfaceMethodref: + { + // Unwrap the field or method to reveal three Utf8 strings. + word_limit = 5; + int klass_ref = cp->uncached_klass_ref_index_at(index); + int nat_ref = cp->uncached_name_and_type_ref_index_at(index); + switch (word) { + case 0: return klass_ref; + case 1: return nat_ref; + // Flatten out the structure to reveal Utf8 references. + case 2: return cp->klass_name_index_at(klass_ref); + case 3: return cp->name_ref_index_at(nat_ref); + case 4: return cp->signature_ref_index_at(nat_ref); + } + } + ref_index = 0; // trigger the correct error + break; + + case JVM_CONSTANT_NameAndType: + // Unwrap the NameAndType to reveal two Utf8 strings. + { + word_limit = 2; + ref_index = (word == 0 + ? cp->name_ref_index_at(index) + : cp->signature_ref_index_at(index)); + } + break; + + case JVM_CONSTANT_MethodType: + // Unwrap the MethodType and return the underlying descriptor. + ref_index = cp->method_type_index_at(index); + break; + + case JVM_CONSTANT_MethodHandle: + // Unwrap the MH and return the member kind. + if (word == 0) { + int ref_kind = cp->method_handle_ref_kind_at(index); + assert(ref_kind > 0 && ref_kind <= JVM_REF_invokeInterface, ""); + (*non_ref_word) = ref_kind; + return 0; + } + ref_index = cp->method_handle_index_at(index); + word_limit = 2; + break; + + case JVM_CONSTANT_InvokeDynamic: + case JVM_CONSTANT_Dynamic: + // Unwrap { bsms_attr, nat, bsm, name_sym, type_sym, argc, arg* } + switch (word) { + case 0: (*non_ref_word) = cp->bootstrap_methods_attribute_index(index); return 0; + case 1: return cp->bootstrap_name_and_type_ref_index_at(index); + case 2: return cp->bootstrap_method_ref_index_at(index); + case 3: return cp->name_ref_index_at(cp->bootstrap_name_and_type_ref_index_at(index)); + case 4: return cp->signature_ref_index_at(cp->bootstrap_name_and_type_ref_index_at(index)); + case 5: (*non_ref_word) = cp->bootstrap_argument_count_at(index); return 0; + } + word_limit = 6 + cp->bootstrap_argument_count_at(index); + if (word >= 0 && word < word_limit) { + return cp->bootstrap_argument_index_at(index, word - 6); + } + ref_index = 0; // trigger the correct error + break; + } + + // Perform final checks and return result. + if (ref_index == -1) { THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); } - Klass* k = ConstantPool::klass_at_if_loaded(cp, index); - if (k == NULL) return NULL; - return (jclass) JNIHandles::make_local(k->java_mirror()); + word_bounds_check(cp, index, word, word_limit, CHECK_0); + return ref_index; +} + +static jint get_word_at_helper(constantPoolHandle cp, jint index, jint word, TRAPS) { + jint non_ref_word = 0; + jint ref_index = get_ref_index_at_helper(cp, index, word, &non_ref_word, CHECK_0); + return (ref_index != 0) ? ref_index : non_ref_word; } -JVM_END + +static jstring get_utf8_at_helper(constantPoolHandle cp, jint index, jint word, TRAPS) { + Symbol* sym = NULL; + if (cp->tag_at(index).is_symbol() && word == 0) { + // Just report the Utf8 string that's here. + sym = cp->symbol_at(index); + } else if (cp->tag_at(index).is_string() && word == 0) { + // Report the String, as a resolved value if possible. + if (cp->resolved_references_or_null() != NULL + && cp->cp_to_object_index(index) >= 0) { + oop str = cp->resolved_string_at(index); + if (java_lang_String::is_instance(str)) { + // In this one case, return the actual interned string. + return (jstring) JNIHandles::make_local(THREAD, str); + } + } + sym = cp->unresolved_string_at(index); + } else { + jint ignore_non_ref_word = 0; + jint ref_index = get_ref_index_at_helper(cp, index, word, &ignore_non_ref_word, CHECK_NULL); + if (ref_index != 0 && !cp->tag_at(ref_index).is_symbol()) { + return NULL; // there is a word here, but it is not a Utf8 index + } + sym = cp->symbol_at(ref_index); + } + // Build a java.lang.String to hold the Utf8 spelling. + Handle str = java_lang_String::create_from_symbol(sym, CHECK_NULL); + return (jstring) JNIHandles::make_local(THREAD, str()); +} + static jobject get_method_at_helper(const constantPoolHandle& cp, jint index, bool force_resolution, TRAPS) { constantTag tag = cp->tag_at(index); @@ -2074,30 +2224,8 @@ } else { method = Reflection::new_constructor(m, CHECK_NULL); } - return JNIHandles::make_local(method); -} - -JVM_ENTRY(jobject, JVM_ConstantPoolGetMethodAt(JNIEnv *env, jobject obj, jobject unused, jint index)) -{ - JVMWrapper("JVM_ConstantPoolGetMethodAt"); - JvmtiVMObjectAllocEventCollector oam; - constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); - bounds_check(cp, index, CHECK_NULL); - jobject res = get_method_at_helper(cp, index, true, CHECK_NULL); - return res; -} -JVM_END - -JVM_ENTRY(jobject, JVM_ConstantPoolGetMethodAtIfLoaded(JNIEnv *env, jobject obj, jobject unused, jint index)) -{ - JVMWrapper("JVM_ConstantPoolGetMethodAtIfLoaded"); - JvmtiVMObjectAllocEventCollector oam; - constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); - bounds_check(cp, index, CHECK_NULL); - jobject res = get_method_at_helper(cp, index, false, CHECK_NULL); - return res; + return JNIHandles::make_local(THREAD, method); } -JVM_END static jobject get_field_at_helper(constantPoolHandle cp, jint index, bool force_resolution, TRAPS) { constantTag tag = cp->tag_at(index); @@ -2121,108 +2249,200 @@ THROW_MSG_0(vmSymbols::java_lang_RuntimeException(), "Unable to look up field in target class"); } oop field = Reflection::new_field(&fd, CHECK_NULL); - return JNIHandles::make_local(field); + return JNIHandles::make_local(THREAD, field); } -JVM_ENTRY(jobject, JVM_ConstantPoolGetFieldAt(JNIEnv *env, jobject obj, jobject unusedl, jint index)) -{ - JVMWrapper("JVM_ConstantPoolGetFieldAt"); - JvmtiVMObjectAllocEventCollector oam; - constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); - bounds_check(cp, index, CHECK_NULL); - jobject res = get_field_at_helper(cp, index, true, CHECK_NULL); - return res; +// when get_ref is pointed at word#0 we just fetch the value: +static jobject get_value_at_helper(constantPoolHandle cp, jint index, + bool force_resolution, jobject if_not_present, + TRAPS) { + constantTag tag = cp->tag_at(index); + jobject result = NULL; + switch (tag.classfile_value()) { + case JVM_CONSTANT_Utf8: + case JVM_CONSTANT_String: + return get_utf8_at_helper(cp, index, 0, CHECK_NULL); + + case JVM_CONSTANT_Class: + { + if (!tag.is_klass() && !tag.is_unresolved_klass()) break; + Klass* k = NULL; + if (force_resolution) { + k = cp->klass_at(index, CHECK_NULL); + } else { + k = ConstantPool::klass_at_if_loaded(cp, index); + if (k == NULL) return if_not_present; + } + assert(k != NULL, ""); + return JNIHandles::make_local(THREAD, k->java_mirror()); + } + + case JVM_CONSTANT_Integer: + case JVM_CONSTANT_Long: + case JVM_CONSTANT_Float: + case JVM_CONSTANT_Double: + case JVM_CONSTANT_MethodType: + case JVM_CONSTANT_MethodHandle: + case JVM_CONSTANT_Dynamic: + //case JVM_CONSTANT_InvokeDynamic: + { + oop ref_oop = NULL; + if (force_resolution) { + ref_oop = cp->resolve_possibly_cached_constant_at(index, CHECK_NULL); + } else { + bool found_it = false; + ref_oop = cp->find_cached_constant_at(index, found_it, CHECK_NULL); + if (!found_it) return if_not_present; + } + return JNIHandles::make_local(THREAD, ref_oop); + } + + case JVM_CONSTANT_Methodref: + case JVM_CONSTANT_InterfaceMethodref: + result = get_method_at_helper(cp, index, force_resolution, CHECK_NULL); + if (result == NULL) result = if_not_present; + return result; + + case JVM_CONSTANT_Fieldref: + result = get_field_at_helper(cp, index, force_resolution, CHECK_NULL); + if (result == NULL) result = if_not_present; + return result; + + case JVM_CONSTANT_NameAndType: + // This one is lame. Just make up a 2-array of the component strings. + { + objArrayOop result_oop = oopFactory::new_objArray(SystemDictionary::String_klass(), 2, CHECK_NULL); + objArrayHandle result(THREAD, result_oop); + for (int word = 0; word < 2; word++) { + int ignore_non_ref_word = 0; + jobject ref = get_utf8_at_helper(cp, index, word, CHECK_NULL); + result->obj_at_put(word, JNIHandles::resolve(ref)); + } + return JNIHandles::make_local(THREAD, result()); + } + } + + return NULL; } -JVM_END -JVM_ENTRY(jobject, JVM_ConstantPoolGetFieldAtIfLoaded(JNIEnv *env, jobject obj, jobject unused, jint index)) -{ - JVMWrapper("JVM_ConstantPoolGetFieldAtIfLoaded"); - JvmtiVMObjectAllocEventCollector oam; - constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); - bounds_check(cp, index, CHECK_NULL); - jobject res = get_field_at_helper(cp, index, false, CHECK_NULL); - return res; + +// Extract an underlying reference object (or Utf8 string) for a CP entry +// of type Class, Fieldref, [Interface]Methodref, [Invoke]Dynamic, etc. +// If word is -1, extract the value of the CP entry itself. +static jobject get_ref_at_helper(const constantPoolHandle& cp, jint index, jint word, + bool force_resolution, jobject if_not_present, + TRAPS) { + if (word != -1) { + jint non_ref_word = 0; + jint ref_index = get_ref_index_at_helper(cp, index, word, &non_ref_word, CHECK_NULL); + if (ref_index == 0) return NULL; + index = ref_index; // and fall through: + } + return get_value_at_helper(cp, index, force_resolution, if_not_present, THREAD); } -JVM_END -JVM_ENTRY(jobjectArray, JVM_ConstantPoolGetMemberRefInfoAt(JNIEnv *env, jobject obj, jobject unused, jint index)) +JVM_ENTRY(jint, JVM_ConstantPool1GetWordCountAt(JNIEnv *env, jobject obj, jint index)) { - JVMWrapper("JVM_ConstantPoolGetMemberRefInfoAt"); - JvmtiVMObjectAllocEventCollector oam; constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); - bounds_check(cp, index, CHECK_NULL); - constantTag tag = cp->tag_at(index); - if (!tag.is_field_or_method()) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); - } - int klass_ref = cp->uncached_klass_ref_index_at(index); - Symbol* klass_name = cp->klass_name_at(klass_ref); - Symbol* member_name = cp->uncached_name_ref_at(index); - Symbol* member_sig = cp->uncached_signature_ref_at(index); - objArrayOop dest_o = oopFactory::new_objArray(SystemDictionary::String_klass(), 3, CHECK_NULL); - objArrayHandle dest(THREAD, dest_o); - Handle str = java_lang_String::create_from_symbol(klass_name, CHECK_NULL); - dest->obj_at_put(0, str()); - str = java_lang_String::create_from_symbol(member_name, CHECK_NULL); - dest->obj_at_put(1, str()); - str = java_lang_String::create_from_symbol(member_sig, CHECK_NULL); - dest->obj_at_put(2, str()); - return (jobjectArray) JNIHandles::make_local(dest()); + bounds_check(cp, index, CHECK_0); + return ref_word_count_at_helper(cp, index); } JVM_END -JVM_ENTRY(jint, JVM_ConstantPoolGetClassRefIndexAt(JNIEnv *env, jobject obj, jobject unused, jint index)) +JVM_ENTRY(jint, JVM_ConstantPool1GetWordAt(JNIEnv *env, jobject obj, jint index, jint word)) { - JVMWrapper("JVM_ConstantPoolGetClassRefIndexAt"); - JvmtiVMObjectAllocEventCollector oam; - constantPoolHandle cp(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); + JVMWrapper("JVM_ConstantPoolGetWordAt"); + constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); bounds_check(cp, index, CHECK_0); - constantTag tag = cp->tag_at(index); - if (!tag.is_field_or_method()) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); - } - return (jint) cp->uncached_klass_ref_index_at(index); + return get_word_at_helper(cp, index, word, CHECK_0); } JVM_END -JVM_ENTRY(jint, JVM_ConstantPoolGetNameAndTypeRefIndexAt(JNIEnv *env, jobject obj, jobject unused, jint index)) +JVM_ENTRY(jobject, JVM_ConstantPool1GetRefAt(JNIEnv *env, jobject obj, jint index, jint word, + jbyte resolving, jobject if_not_present)) { - JVMWrapper("JVM_ConstantPoolGetNameAndTypeRefIndexAt"); + JVMWrapper("JVM_ConstantPoolGetRefAt"); JvmtiVMObjectAllocEventCollector oam; - constantPoolHandle cp(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); - bounds_check(cp, index, CHECK_0); - constantTag tag = cp->tag_at(index); - if (!tag.is_invoke_dynamic() && !tag.is_field_or_method()) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); + constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); + bounds_check(cp, index, CHECK_NULL); + switch (resolving) { + case ConstantPool::R_SYMREF: + // Caller must handle symbolic refs other than Utf8. + return get_utf8_at_helper(cp, index, word, THREAD); + case ConstantPool::R_FORCE: + return get_ref_at_helper(cp, index, word, true, NULL, THREAD); + case ConstantPool::R_IFPRESENT: + return get_ref_at_helper(cp, index, word, false, if_not_present, THREAD); } - return (jint) cp->uncached_name_and_type_ref_index_at(index); + THROW_MSG_NULL(vmSymbols::java_lang_IllegalArgumentException(), "resolving mode must be 0, 1, or 2"); + return NULL; } JVM_END -JVM_ENTRY(jobjectArray, JVM_ConstantPoolGetNameAndTypeRefInfoAt(JNIEnv *env, jobject obj, jobject unused, jint index)) + +JVM_ENTRY(void, JVM_ConstantPool1CopyOutRefsAt(JNIEnv *env, jobject obj, jint index, jint start_word, jint end_word, + jobject buf_jobject, jint buf_pos, jbyte resolving, + jobject if_not_present, jobject if_null_constant, jboolean skip_non_null)) { - JVMWrapper("JVM_ConstantPoolGetNameAndTypeRefInfoAt"); + JVMWrapper("JVM_ConstantPoolCopyOutRefsAt"); JvmtiVMObjectAllocEventCollector oam; - constantPoolHandle cp(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); - bounds_check(cp, index, CHECK_NULL); - constantTag tag = cp->tag_at(index); - if (!tag.is_name_and_type()) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); + constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); + bounds_check(cp, index, CHECK); + objArrayHandle obj_buf; // set to buf if type Object[] + typeArrayHandle int_buf; // set to buf if type int[] + bool force_resolution = (resolving == ConstantPool::R_FORCE); + jint buf_len = 0; + if (buf_jobject != NULL) { + oop buf = JNIHandles::resolve_non_null(buf_jobject); + switch (resolving) { + case ConstantPool::R_IFPRESENT: // mode = do not resolve new values + case ConstantPool::R_FORCE: // mode = resolve any requested values (cf. skip_non_null) + if (buf->klass() == Universe::objectArrayKlassObj()) + obj_buf = objArrayHandle(THREAD, (objArrayOop) buf); + break; + case ConstantPool::R_SYMREF: // mode = return only CP indexes (symbolic refs) + if (buf->klass() == Universe::intArrayKlassObj()) + int_buf = typeArrayHandle(THREAD, (typeArrayOop) buf); + break; + default: + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "resolving mode must be 0, 1, or 2"); + } + if (obj_buf.not_null()) + buf_len = obj_buf->length(); + else if (int_buf.not_null()) + buf_len = int_buf->length(); + } + jint word_limit = ref_word_count_at_helper(cp, index); + jint word_count = (end_word - start_word); + if ((obj_buf.is_null() && int_buf.is_null()) || + 0 > start_word || start_word > end_word || end_word > word_limit || + 0 > buf_pos || buf_pos + word_count > buf_len) { + THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "bad buffer index"); + } + if (int_buf.not_null()) { + for (int word = start_word; word < end_word; word++) { + int result = get_word_at_helper(cp, index, word, CHECK); + int_buf->int_at_put(buf_pos++, result); + } + } else { + for (int word = start_word; word < end_word; word++) { + if (skip_non_null && obj_buf->obj_at(buf_pos) != NULL) { + ++buf_pos; continue; + } + jobject sentinel = obj; // safely unique: pool doesn't contain itself + jobject result = get_ref_at_helper(cp, index, word, force_resolution, sentinel, CHECK); + if (result == sentinel) + result = if_not_present; + else if (result == NULL) // note that if_not_present might also be NULL + result = if_null_constant; + obj_buf->obj_at_put(buf_pos++, JNIHandles::resolve(result)); + } } - Symbol* member_name = cp->symbol_at(cp->name_ref_index_at(index)); - Symbol* member_sig = cp->symbol_at(cp->signature_ref_index_at(index)); - objArrayOop dest_o = oopFactory::new_objArray(SystemDictionary::String_klass(), 2, CHECK_NULL); - objArrayHandle dest(THREAD, dest_o); - Handle str = java_lang_String::create_from_symbol(member_name, CHECK_NULL); - dest->obj_at_put(0, str()); - str = java_lang_String::create_from_symbol(member_sig, CHECK_NULL); - dest->obj_at_put(1, str()); - return (jobjectArray) JNIHandles::make_local(dest()); } JVM_END -JVM_ENTRY(jint, JVM_ConstantPoolGetIntAt(JNIEnv *env, jobject obj, jobject unused, jint index)) + +JVM_ENTRY(jint, JVM_ConstantPool1GetIntAt(JNIEnv *env, jobject obj, jint index)) { JVMWrapper("JVM_ConstantPoolGetIntAt"); constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); @@ -2235,7 +2455,7 @@ } JVM_END -JVM_ENTRY(jlong, JVM_ConstantPoolGetLongAt(JNIEnv *env, jobject obj, jobject unused, jint index)) +JVM_ENTRY(jlong, JVM_ConstantPool1GetLongAt(JNIEnv *env, jobject obj, jint index)) { JVMWrapper("JVM_ConstantPoolGetLongAt"); constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); @@ -2248,7 +2468,7 @@ } JVM_END -JVM_ENTRY(jfloat, JVM_ConstantPoolGetFloatAt(JNIEnv *env, jobject obj, jobject unused, jint index)) +JVM_ENTRY(jfloat, JVM_ConstantPool1GetFloatAt(JNIEnv *env, jobject obj, jint index)) { JVMWrapper("JVM_ConstantPoolGetFloatAt"); constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); @@ -2261,7 +2481,7 @@ } JVM_END -JVM_ENTRY(jdouble, JVM_ConstantPoolGetDoubleAt(JNIEnv *env, jobject obj, jobject unused, jint index)) +JVM_ENTRY(jdouble, JVM_ConstantPool1GetDoubleAt(JNIEnv *env, jobject obj, jint index)) { JVMWrapper("JVM_ConstantPoolGetDoubleAt"); constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); @@ -2274,61 +2494,34 @@ } JVM_END -JVM_ENTRY(jstring, JVM_ConstantPoolGetStringAt(JNIEnv *env, jobject obj, jobject unused, jint index)) +JVM_ENTRY(jbyte, JVM_ConstantPool1GetTagAt(JNIEnv *env, jobject obj, jint index)) { - JVMWrapper("JVM_ConstantPoolGetStringAt"); + JVMWrapper("JVM_ConstantPoolGetTagAt"); constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); - bounds_check(cp, index, CHECK_NULL); + bounds_check(cp, index, CHECK_0); constantTag tag = cp->tag_at(index); - if (!tag.is_string()) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); - } - oop str = cp->string_at(index, CHECK_NULL); - return (jstring) JNIHandles::make_local(str); + // If returned tag values are not from the JVM spec, e.g. tags from 100 to 105, + // they are changed to the corresponding tags from the JVM spec, so that java code in + // sun.reflect.ConstantPool will return only tags from the JVM spec, not internal ones. + return tag.classfile_value(); } JVM_END -JVM_ENTRY(jstring, JVM_ConstantPoolGetUTF8At(JNIEnv *env, jobject obj, jobject unused, jint index)) -{ - JVMWrapper("JVM_ConstantPoolGetUTF8At"); - JvmtiVMObjectAllocEventCollector oam; - constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); - bounds_check(cp, index, CHECK_NULL); - constantTag tag = cp->tag_at(index); - if (!tag.is_symbol()) { - THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "Wrong type at constant pool index"); - } - Symbol* sym = cp->symbol_at(index); - Handle str = java_lang_String::create_from_symbol(sym, CHECK_NULL); - return (jstring) JNIHandles::make_local(str()); -} -JVM_END -JVM_ENTRY(jbyte, JVM_ConstantPoolGetTagAt(JNIEnv *env, jobject obj, jobject unused, jint index)) +JVM_ENTRY(jbyteArray, JVM_ConstantPool1GetTags(JNIEnv *env, jobject obj)) { - JVMWrapper("JVM_ConstantPoolGetTagAt"); + JVMWrapper("JVM_ConstantPoolGetTags"); constantPoolHandle cp = constantPoolHandle(THREAD, reflect_ConstantPool::get_cp(JNIHandles::resolve_non_null(obj))); - bounds_check(cp, index, CHECK_0); - constantTag tag = cp->tag_at(index); - jbyte result = tag.value(); - // If returned tag values are not from the JVM spec, e.g. tags from 100 to 105, - // they are changed to the corresponding tags from the JVM spec, so that java code in - // sun.reflect.ConstantPool will return only tags from the JVM spec, not internal ones. - if (tag.is_klass_or_reference()) { - result = JVM_CONSTANT_Class; - } else if (tag.is_string_index()) { - result = JVM_CONSTANT_String; - } else if (tag.is_method_type_in_error()) { - result = JVM_CONSTANT_MethodType; - } else if (tag.is_method_handle_in_error()) { - result = JVM_CONSTANT_MethodHandle; - } else if (tag.is_dynamic_constant_in_error()) { - result = JVM_CONSTANT_Dynamic; + jint length = cp->length(); + typeArrayOop result_oop = oopFactory::new_typeArray(T_BYTE, length, CHECK_NULL); + for (jint index = 0; index < length; index++) { + result_oop->byte_at_put(index, cp->tag_at(index).classfile_value()); } - return result; + return (jbyteArray) JNIHandles::make_local(THREAD, result_oop); } JVM_END + // Assertion support. ////////////////////////////////////////////////////////// JVM_ENTRY(jboolean, JVM_DesiredAssertionStatus(JNIEnv *env, jclass unused, jclass cls)) --- old/src/hotspot/share/prims/jvmtiRedefineClasses.cpp 2018-09-28 11:53:45.000000000 -0700 +++ new/src/hotspot/share/prims/jvmtiRedefineClasses.cpp 2018-09-28 11:53:45.000000000 -0700 @@ -506,11 +506,11 @@ case JVM_CONSTANT_InvokeDynamic: { // Index of the bootstrap specifier in the operands array - int old_bs_i = scratch_cp->invoke_dynamic_bootstrap_specifier_index(scratch_i); + int old_bs_i = scratch_cp->bootstrap_methods_attribute_index(scratch_i); int new_bs_i = find_or_append_operand(scratch_cp, old_bs_i, merge_cp_p, merge_cp_length_p, THREAD); // The bootstrap method NameAndType_info index - int old_ref_i = scratch_cp->invoke_dynamic_name_and_type_ref_index_at(scratch_i); + int old_ref_i = scratch_cp->bootstrap_name_and_type_ref_index_at(scratch_i); int new_ref_i = find_or_append_indirect_entry(scratch_cp, old_ref_i, merge_cp_p, merge_cp_length_p, THREAD); if (new_bs_i != old_bs_i) { --- old/src/hotspot/share/prims/methodComparator.cpp 2018-09-28 11:53:47.000000000 -0700 +++ new/src/hotspot/share/prims/methodComparator.cpp 2018-09-28 11:53:47.000000000 -0700 @@ -123,17 +123,17 @@ int cpi_old = _old_cp->cache()->entry_at(cpci_old)->constant_pool_index(); int cpi_new = _new_cp->cache()->entry_at(cpci_new)->constant_pool_index(); - int bsm_old = _old_cp->invoke_dynamic_bootstrap_method_ref_index_at(cpi_old); - int bsm_new = _new_cp->invoke_dynamic_bootstrap_method_ref_index_at(cpi_new); + int bsm_old = _old_cp->bootstrap_method_ref_index_at(cpi_old); + int bsm_new = _new_cp->bootstrap_method_ref_index_at(cpi_new); if (!pool_constants_same(bsm_old, bsm_new)) return false; - int cnt_old = _old_cp->invoke_dynamic_argument_count_at(cpi_old); - int cnt_new = _new_cp->invoke_dynamic_argument_count_at(cpi_new); + int cnt_old = _old_cp->bootstrap_argument_count_at(cpi_old); + int cnt_new = _new_cp->bootstrap_argument_count_at(cpi_new); if (cnt_old != cnt_new) return false; for (int arg_i = 0; arg_i < cnt_old; arg_i++) { - int idx_old = _old_cp->invoke_dynamic_argument_index_at(cpi_old, arg_i); - int idx_new = _new_cp->invoke_dynamic_argument_index_at(cpi_new, arg_i); + int idx_old = _old_cp->bootstrap_argument_index_at(cpi_old, arg_i); + int idx_new = _new_cp->bootstrap_argument_index_at(cpi_new, arg_i); if (!pool_constants_same(idx_old, idx_new)) return false; } --- old/src/hotspot/share/prims/methodHandles.cpp 2018-09-28 11:53:49.000000000 -0700 +++ new/src/hotspot/share/prims/methodHandles.cpp 2018-09-28 11:53:49.000000000 -0700 @@ -152,13 +152,14 @@ } Handle resolved; int flags = java_lang_invoke_MemberName::flags(mname()); + bool check_access = false; // is this right? switch (flags & ALL_KINDS) { case IS_METHOD: case IS_CONSTRUCTOR: resolved = SystemDictionary::find_method_handle_type(signature, caller, CHECK_(empty)); break; case IS_FIELD: - resolved = SystemDictionary::find_field_handle_type(signature, caller, CHECK_(empty)); + resolved = SystemDictionary::find_java_mirror_for_type(signature, caller, SignatureStream::NCDFError, check_access, CHECK_(empty)); break; default: THROW_MSG_(vmSymbols::java_lang_InternalError(), "unrecognized MemberName format", empty); @@ -1143,6 +1144,17 @@ // #ifndef PRODUCT +# ifndef ADVERTISE_CON_VALUES +# define ADVERTISE_CON_VALUES +# endif +#endif + +#ifdef ADVERTISE_CON_VALUES +// The following macrology pushes a list of symbolic definitions, +// all of integral type, up through getNamedCon, into the +// verifyConstants method of jli.MethodHandleNatives. +// In a debug build, and if the java -esa flag is thrown, +// we make sure all of these names are kept in sync. #define EACH_NAMED_CON(template, requirement) \ template(java_lang_invoke_MemberName,MN_IS_METHOD) \ template(java_lang_invoke_MemberName,MN_IS_CONSTRUCTOR) \ @@ -1153,8 +1165,39 @@ template(java_lang_invoke_MemberName,MN_SEARCH_INTERFACES) \ template(java_lang_invoke_MemberName,MN_REFERENCE_KIND_SHIFT) \ template(java_lang_invoke_MemberName,MN_REFERENCE_KIND_MASK) \ + template(RefKind,REF_NONE) \ + template(RefKind,REF_getField) \ + template(RefKind,REF_getStatic) \ + template(RefKind,REF_putField) \ + template(RefKind,REF_putStatic) \ + template(RefKind,REF_invokeVirtual) \ + template(RefKind,REF_invokeStatic) \ + template(RefKind,REF_invokeSpecial) \ + template(RefKind,REF_newInvokeSpecial) \ + template(RefKind,REF_invokeInterface) \ + template(RefKind,REF_LIMIT) \ + template(ConstantPool,R_IFPRESENT) \ + template(ConstantPool,R_FORCE) \ + template(ConstantPool,R_SYMREF) \ /*end*/ +class RefKind { + public: + enum { + REF_NONE = 0, // null value + REF_getField = 1, + REF_getStatic = 2, + REF_putField = 3, + REF_putStatic = 4, + REF_invokeVirtual = 5, + REF_invokeStatic = 6, + REF_invokeSpecial = 7, + REF_newInvokeSpecial = 8, + REF_invokeInterface = 9, + REF_LIMIT = 10 + }; +}; + #define IGNORE_REQ(req_expr) /* req_expr */ #define ONE_PLUS(scope,value) 1+ static const int con_value_count = EACH_NAMED_CON(ONE_PLUS, IGNORE_REQ) 0; @@ -1184,10 +1227,11 @@ #undef VALUE_COMMA #undef STRING_NULL #undef EACH_NAMED_CON -#endif // PRODUCT + +#endif // ADVERTISE_CON_VALUES JVM_ENTRY(jint, MHN_getNamedCon(JNIEnv *env, jobject igcls, jint which, jobjectArray box_jh)) { -#ifndef PRODUCT +#ifdef ADVERTISE_CON_VALUES if (advertise_con_value(which)) { assert(which >= 0 && which < con_value_count, ""); int con = con_values[which]; @@ -1407,87 +1451,6 @@ } JVM_END -JVM_ENTRY(void, MHN_copyOutBootstrapArguments(JNIEnv* env, jobject igcls, - jobject caller_jh, jintArray index_info_jh, - jint start, jint end, - jobjectArray buf_jh, jint pos, - jboolean resolve, jobject ifna_jh)) { - Klass* caller_k = java_lang_Class::as_Klass(JNIHandles::resolve(caller_jh)); - if (caller_k == NULL || !caller_k->is_instance_klass()) { - THROW_MSG(vmSymbols::java_lang_InternalError(), "bad caller"); - } - InstanceKlass* caller = InstanceKlass::cast(caller_k); - typeArrayOop index_info_oop = (typeArrayOop) JNIHandles::resolve(index_info_jh); - if (index_info_oop == NULL || - index_info_oop->klass() != Universe::intArrayKlassObj() || - typeArrayOop(index_info_oop)->length() < 2) { - THROW_MSG(vmSymbols::java_lang_InternalError(), "bad index info (0)"); - } - typeArrayHandle index_info(THREAD, index_info_oop); - int bss_index_in_pool = index_info->int_at(1); - // While we are here, take a quick look at the index info: - if (bss_index_in_pool <= 0 || - bss_index_in_pool >= caller->constants()->length() || - index_info->int_at(0) - != caller->constants()->invoke_dynamic_argument_count_at(bss_index_in_pool)) { - THROW_MSG(vmSymbols::java_lang_InternalError(), "bad index info (1)"); - } - objArrayHandle buf(THREAD, (objArrayOop) JNIHandles::resolve(buf_jh)); - if (start < 0) { - for (int pseudo_index = -4; pseudo_index < 0; pseudo_index++) { - if (start == pseudo_index) { - if (start >= end || 0 > pos || pos >= buf->length()) break; - oop pseudo_arg = NULL; - switch (pseudo_index) { - case -4: // bootstrap method - { - int bsm_index = caller->constants()->invoke_dynamic_bootstrap_method_ref_index_at(bss_index_in_pool); - pseudo_arg = caller->constants()->resolve_possibly_cached_constant_at(bsm_index, CHECK); - break; - } - case -3: // name - { - Symbol* name = caller->constants()->name_ref_at(bss_index_in_pool); - Handle str = java_lang_String::create_from_symbol(name, CHECK); - pseudo_arg = str(); - break; - } - case -2: // type - { - Symbol* type = caller->constants()->signature_ref_at(bss_index_in_pool); - Handle th; - if (type->byte_at(0) == '(') { - th = SystemDictionary::find_method_handle_type(type, caller, CHECK); - } else { - th = SystemDictionary::find_java_mirror_for_type(type, caller, SignatureStream::NCDFError, CHECK); - } - pseudo_arg = th(); - break; - } - case -1: // argument count - { - int argc = caller->constants()->invoke_dynamic_argument_count_at(bss_index_in_pool); - jvalue argc_value; argc_value.i = (jint)argc; - pseudo_arg = java_lang_boxing_object::create(T_INT, &argc_value, CHECK); - break; - } - } - - // Store the pseudo-argument, and advance the pointers. - buf->obj_at_put(pos++, pseudo_arg); - ++start; - } - } - // When we are done with this there may be regular arguments to process too. - } - Handle ifna(THREAD, JNIHandles::resolve(ifna_jh)); - caller->constants()-> - copy_bootstrap_arguments_at(bss_index_in_pool, - start, end, buf, pos, - (resolve == JNI_TRUE), ifna, CHECK); -} -JVM_END - // It is called by a Cleaner object which ensures that dropped CallSites properly // deallocate their dependency information. JVM_ENTRY(void, MHN_clearCallSiteContext(JNIEnv* env, jobject igcls, jobject context_jh)) { @@ -1567,7 +1530,6 @@ {CC "objectFieldOffset", CC "(" MEM ")J", FN_PTR(MHN_objectFieldOffset)}, {CC "setCallSiteTargetNormal", CC "(" CS "" MH ")V", FN_PTR(MHN_setCallSiteTargetNormal)}, {CC "setCallSiteTargetVolatile", CC "(" CS "" MH ")V", FN_PTR(MHN_setCallSiteTargetVolatile)}, - {CC "copyOutBootstrapArguments", CC "(" CLS "[III[" OBJ "IZ" OBJ ")V", FN_PTR(MHN_copyOutBootstrapArguments)}, {CC "clearCallSiteContext", CC "(" CTX ")V", FN_PTR(MHN_clearCallSiteContext)}, {CC "staticFieldOffset", CC "(" MEM ")J", FN_PTR(MHN_staticFieldOffset)}, {CC "staticFieldBase", CC "(" MEM ")" OBJ, FN_PTR(MHN_staticFieldBase)}, --- old/src/hotspot/share/runtime/arguments.cpp 2018-09-28 11:53:51.000000000 -0700 +++ new/src/hotspot/share/runtime/arguments.cpp 2018-09-28 11:53:51.000000000 -0700 @@ -579,6 +579,7 @@ { "SyncVerbose", JDK_Version::undefined(), JDK_Version::jdk(12), JDK_Version::jdk(13) }, { "SyncFlags", JDK_Version::undefined(), JDK_Version::jdk(12), JDK_Version::jdk(13) }, { "SyncKnobs", JDK_Version::undefined(), JDK_Version::jdk(12), JDK_Version::jdk(13) }, + { "UseBootstrapCallInfo", JDK_Version::undefined(), JDK_Version::jdk(12), JDK_Version::jdk(13) }, #ifdef TEST_VERIFY_SPECIAL_JVM_FLAGS { "dep > obs", JDK_Version::jdk(9), JDK_Version::jdk(8), JDK_Version::undefined() }, --- old/src/hotspot/share/runtime/globals.hpp 2018-09-28 11:53:53.000000000 -0700 +++ new/src/hotspot/share/runtime/globals.hpp 2018-09-28 11:53:53.000000000 -0700 @@ -2466,14 +2466,6 @@ develop(bool, TraceInvokeDynamic, false, \ "trace internal invoke dynamic operations") \ \ - diagnostic(int, UseBootstrapCallInfo, 1, \ - "0: when resolving InDy or ConDy, force all BSM arguments to be " \ - "resolved before the bootstrap method is called; 1: when a BSM " \ - "that may accept a BootstrapCallInfo is detected, use that API " \ - "to pass BSM arguments, which allows the BSM to delay their " \ - "resolution; 2+: stress test the BCI API by calling more BSMs " \ - "via that API, instead of with the eagerly-resolved array.") \ - \ diagnostic(bool, PauseAtStartup, false, \ "Causes the VM to pause at startup time and wait for the pause " \ "file to be removed (default: ./vm.paused.)") \ --- old/src/hotspot/share/runtime/javaCalls.hpp 2018-09-28 11:53:55.000000000 -0700 +++ new/src/hotspot/share/runtime/javaCalls.hpp 2018-09-28 11:53:55.000000000 -0700 @@ -163,34 +163,44 @@ value_state_limit }; + void size_check(int words = 1) { + assert(_size + (words-1) < _max_size, "oob: increase max_size argument to JavaCallArguments"); + } + inline void push_oop(Handle h) { + size_check(); _value_state[_size] = value_state_handle; _size = push_oop_impl(h.raw_value(), _size); } inline void push_jobject(jobject h) { + size_check(); _value_state[_size] = value_state_jobject; _size = push_oop_impl(h, _size); } inline void push_int(int i) { + size_check(); _value_state[_size] = value_state_primitive; JNITypes::put_int(i, _value, _size); } inline void push_double(double d) { + size_check(2); _value_state[_size] = value_state_primitive; _value_state[_size + 1] = value_state_primitive; JNITypes::put_double(d, _value, _size); } inline void push_long(jlong l) { + size_check(2); _value_state[_size] = value_state_primitive; _value_state[_size + 1] = value_state_primitive; JNITypes::put_long(l, _value, _size); } inline void push_float(float f) { + size_check(); _value_state[_size] = value_state_primitive; JNITypes::put_float(f, _value, _size); } @@ -210,6 +220,7 @@ _value_state--; _value--; _size++; + DEBUG_ONLY(_max_size++); _value_state[0] = value_state_handle; push_oop_impl(h.raw_value(), 0); } --- old/src/hotspot/share/utilities/constantTag.cpp 2018-09-28 11:53:57.000000000 -0700 +++ new/src/hotspot/share/utilities/constantTag.cpp 2018-09-28 11:53:57.000000000 -0700 @@ -68,6 +68,31 @@ } +// value as specified in the classfile format +jbyte constantTag::classfile_value() const { + switch (_tag) { + case JVM_CONSTANT_ClassIndex: + case JVM_CONSTANT_UnresolvedClass: + case JVM_CONSTANT_UnresolvedClassInError: + return JVM_CONSTANT_Class; + case JVM_CONSTANT_StringIndex: + return JVM_CONSTANT_String; + case JVM_CONSTANT_MethodHandleInError: + return JVM_CONSTANT_MethodHandle; + case JVM_CONSTANT_MethodTypeInError: + return JVM_CONSTANT_MethodType; + case JVM_CONSTANT_DynamicInError: + return JVM_CONSTANT_Dynamic; + default: + // Return a zero tag for a gap after Long or Double, + // even though that doesn't exactly appear in a classfile. + assert(_tag >= 0 && _tag <= JVM_CONSTANT_ExternalMax, + "cannot interpret tag %d as a standard classfile tag", _tag); + return _tag; + } +} + + jbyte constantTag::non_error_value() const { switch (_tag) { case JVM_CONSTANT_UnresolvedClassInError: --- old/src/hotspot/share/utilities/constantTag.hpp 2018-09-28 11:53:59.000000000 -0700 +++ new/src/hotspot/share/utilities/constantTag.hpp 2018-09-28 11:53:59.000000000 -0700 @@ -98,6 +98,12 @@ bool is_dynamic_constant() const { return _tag == JVM_CONSTANT_Dynamic; } bool is_invoke_dynamic() const { return _tag == JVM_CONSTANT_InvokeDynamic; } + bool has_bootstrap() const { + return (_tag == JVM_CONSTANT_Dynamic || + _tag == JVM_CONSTANT_DynamicInError || + _tag == JVM_CONSTANT_InvokeDynamic); + } + bool is_loadable_constant() const { return ((_tag >= JVM_CONSTANT_Integer && _tag <= JVM_CONSTANT_String) || is_method_type() || is_method_handle() || is_dynamic_constant() || @@ -129,6 +135,7 @@ } jbyte value() const { return _tag; } + jbyte classfile_value() const; jbyte error_value() const; jbyte non_error_value() const; --- old/src/hotspot/share/utilities/exceptions.cpp 2018-09-28 11:54:01.000000000 -0700 +++ new/src/hotspot/share/utilities/exceptions.cpp 2018-09-28 11:54:00.000000000 -0700 @@ -419,7 +419,7 @@ // Otherwise wrap the exception in a BootstrapMethodError if (TraceMethodHandles) { - tty->print_cr("[constant/invoke]dynamic throws BSME for " INTPTR_FORMAT, p2i((void *)exception)); + tty->print_cr("bootstrap method invocation wraps BSME around " INTPTR_FORMAT, p2i((void *)exception)); exception->print(); } Handle nested_exception(THREAD, exception); --- old/src/java.base/share/classes/java/lang/constant/AsTypeMethodHandleDesc.java 2018-09-28 11:54:03.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/constant/AsTypeMethodHandleDesc.java 2018-09-28 11:54:02.000000000 -0700 @@ -29,7 +29,6 @@ import java.lang.invoke.MethodType; import java.util.Optional; -import static java.lang.constant.ConstantDescs.BSM_INVOKE; import static java.lang.constant.ConstantDescs.CR_MethodHandle; import static java.util.Objects.requireNonNull; @@ -45,8 +44,8 @@ private final MethodTypeDesc type; AsTypeMethodHandleDesc(MethodHandleDesc underlying, MethodTypeDesc type) { - super(BSM_INVOKE, ConstantDescs.DEFAULT_NAME, CR_MethodHandle, - ConstantDescs.MHR_METHODHANDLE_ASTYPE, underlying, type); + super(ConstantDescs.MHR_METHODHANDLE_ASTYPE, ConstantDescs.INVOKE_NAME, CR_MethodHandle, + underlying, type); this.underlying = requireNonNull(underlying); this.type = requireNonNull(type); } @@ -66,8 +65,8 @@ @Override public Optional>> describeConstable() { - return ConstantUtils.symbolizeHelper(ConstantDescs.MHR_METHODHANDLEDESC_ASTYPE, ConstantDescs.CR_MethodHandleDesc, - underlying, type); + DirectMethodHandleDesc bootstrap = ConstantDescs.MHR_METHODHANDLEDESC_ASTYPE; + return Optional.of(DynamicConstantDesc.ofSymbolicExpression(bootstrap, underlying, type)); } @Override --- old/src/java.base/share/classes/java/lang/constant/ConstantDescs.java 2018-09-28 11:54:05.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/constant/ConstantDescs.java 2018-09-28 11:54:04.000000000 -0700 @@ -58,6 +58,22 @@ */ public static final String DEFAULT_NAME = "_"; + /** Invocation name to use with a no-metadata expression mode bootstrap method. + * The effect of resolving a dynamic constant with this name is to call + * the bootstrap method on the arguments, but not on the lookup, name, or type. + * The arguments are resolved. + */ + public static final String INVOKE_NAME = "invoke"; + + /** Invocation name to use with a no-metadata expression mode bootstrap method. + * The effect of resolving a dynamic constant with this name is to call + * the bootstrap method on the arguments, but not on the lookup, name, or type. + * In addition, formal parameters which are of a supertype of {@link ConstantDesc} + * are not resolved, but are passed as unresolved {@link ConstantDesc} values + * derived directly from the constant pool. + */ + public static final String SYMBOLIC_NAME = "symbolic"; + // Don't change the order of these declarations! /** {@link ClassDesc} representing {@link Object} */ @@ -249,18 +265,10 @@ DEFAULT_NAME, ConstantDescs.CR_Object); // Used by XxxDesc classes, but need to be here to avoid bootstrap cycles - static final DirectMethodHandleDesc MHR_METHODTYPEDESC_FACTORY - = MethodHandleDesc.of(DirectMethodHandleDesc.Kind.STATIC, CR_MethodTypeDesc, "ofDescriptor", - CR_MethodTypeDesc, CR_String); - static final DirectMethodHandleDesc MHR_CLASSDESC_FACTORY = MethodHandleDesc.of(DirectMethodHandleDesc.Kind.STATIC, CR_ClassDesc, "ofDescriptor", CR_ClassDesc, CR_String); - static final DirectMethodHandleDesc MHR_METHODHANDLEDESC_FACTORY - = MethodHandleDesc.of(DirectMethodHandleDesc.Kind.STATIC, CR_DirectMethodHandleDesc, "ofDescriptor", - CR_DirectMethodHandleDesc, CR_String, CR_String, CR_String, CR_String); - static final DirectMethodHandleDesc MHR_METHODHANDLE_ASTYPE = MethodHandleDesc.of(DirectMethodHandleDesc.Kind.VIRTUAL, CR_MethodHandle, "asType", CR_MethodHandle, CR_MethodType); @@ -269,13 +277,9 @@ = MethodHandleDesc.of(DirectMethodHandleDesc.Kind.VIRTUAL, CR_MethodHandleDesc, "asType", CR_MethodHandleDesc, CR_MethodTypeDesc); - static final DirectMethodHandleDesc MHR_DYNAMICCONSTANTDESC_FACTORY - = MethodHandleDesc.of(DirectMethodHandleDesc.Kind.STATIC, CR_DynamicConstantDesc, "of", - CR_DynamicConstantDesc, CR_DirectMethodHandleDesc, CR_ConstantDesc.arrayType()); - - static final DirectMethodHandleDesc MHR_DYNAMICCONSTANTDESC_NAMED_FACTORY - = MethodHandleDesc.of(DirectMethodHandleDesc.Kind.STATIC, CR_DynamicConstantDesc, "ofNamed", - CR_DynamicConstantDesc, CR_DirectMethodHandleDesc, CR_String, CR_String, CR_ConstantDesc.arrayType()); + static final DirectMethodHandleDesc MHR_CONSTANTDESC_IDENTITY + = MethodHandleDesc.of(DirectMethodHandleDesc.Kind.STATIC, CR_ConstantBootstraps, "constantDesc", + CR_ConstantDesc, CR_ConstantDesc); /** {@link MethodHandleDesc} representing {@link EnumDesc#ofDescriptor(String, String)} */ public static final DirectMethodHandleDesc MHR_ENUMDESC_FACTORY @@ -296,17 +300,6 @@ = MethodHandleDesc.of(DirectMethodHandleDesc.Kind.STATIC, CR_VarHandleDesc, "ofArray", CR_VarHandleDesc, CR_ClassDesc); - static final DirectMethodHandleDesc BSM_METHODHANDLEDESC - = ConstantDescs.ofConstantBootstrap(CR_DirectMethodHandleDesc, - "constantBootstrap", CR_DirectMethodHandleDesc, - CR_String, CR_String, CR_String, CR_String); - - static final DirectMethodHandleDesc BSM_DYNAMICCONSTANTDESC - = ConstantDescs.ofConstantBootstrap(CR_DynamicConstantDesc, - "constantBootstrap", - CR_DynamicConstantDesc, - CR_DirectMethodHandleDesc, CR_String, CR_String, CR_ConstantDesc.arrayType()); - /** * Return a {@link MethodHandleDesc} corresponding to a bootstrap method for * an {@code invokedynamic} callsite, which is a static method whose leading --- old/src/java.base/share/classes/java/lang/constant/ConstantUtils.java 2018-09-28 11:54:07.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/constant/ConstantUtils.java 2018-09-28 11:54:06.000000000 -0700 @@ -72,6 +72,40 @@ return name; } + /** + * validates a bootstrap method: must have a leading Lookup parameter, or else be a valid expression-mode BSM + * @param bootstrapMethod the method to validate + * @param constantName the name associated with the proposed {@code CONSTANT_Dynamic} constant + * @return the method passed if valid + */ + public static DirectMethodHandleDesc validateBootstrapMethod(DirectMethodHandleDesc bootstrapMethod, String constantName) { + if (hasLeadingLookupParameter(bootstrapMethod)) return bootstrapMethod; + switch (constantName) { + case ConstantDescs.INVOKE_NAME: + case ConstantDescs.SYMBOLIC_NAME: + return bootstrapMethod; + } + throw new IllegalArgumentException("Invalid bootstrap method: " + bootstrapMethod + " with reserved invocation name " + constantName); + } + + /** + * validates an expression-mode bootstrap method: must not have a leading Lookup parameter + * @param bootstrapMethod the method to validate + * @param constantName the name associated with the proposed {@code CONSTANT_Dynamic} constant + * @return the method passed if valid + */ + public static DirectMethodHandleDesc validateExpressionModeBootstrapMethod(DirectMethodHandleDesc bootstrapMethod, String constantName) { + if (hasLeadingLookupParameter(bootstrapMethod)) { + throw new IllegalArgumentException("Bootstrap method has leading Lookup parameter: " + bootstrapMethod); + } + return validateBootstrapMethod(bootstrapMethod, constantName); + } + + private static boolean hasLeadingLookupParameter(DirectMethodHandleDesc bootstrapMethod) { + MethodTypeDesc type = bootstrapMethod.methodType(); + return (type.parameterCount() > 0 && type.parameterType(0).equals(ConstantDescs.CR_MethodHandles_Lookup)); + } + static void validateClassOrInterface(ClassDesc clazz) { if (!clazz.isClassOrInterface()) throw new IllegalArgumentException("not a class or interface type: " + clazz); @@ -101,38 +135,6 @@ } /** - * Produce an {@code Optional>} describing the invocation - * of the specified bootstrap with the specified arguments. The arguments will - * be converted to nominal descriptors using the provided lookup. Helper - * method for implementing {@link Constable#describeConstable()}. - * - * @param the type of the resulting constant - * @param bootstrap nominal descriptor for the bootstrap method - * @param type nominal descriptor for the type of the resulting constant - * @param args nominal descriptors for the bootstrap arguments - * @return the nominal descriptor for the dynamic constant - */ - public static Optional> symbolizeHelper(MethodHandleDesc bootstrap, - ClassDesc type, - Constable... args) { - requireNonNull(bootstrap); - requireNonNull(type); - requireNonNull(args); - try { - ConstantDesc[] quotedArgs = new ConstantDesc[args.length + 1]; - quotedArgs[0] = bootstrap; - for (int i=0; i= 0 && i < TABLE.length) { + Kind kind = TABLE[i]; + if (kind.refKind == refKind && kind.isInterface == isInterface) { + return kind; + } + } + if (isInterface) return valueOf(refKind); + if (refKind == REF_invokeInterface) return INTERFACE_VIRTUAL; + throw new IllegalArgumentException("refKind="+refKind+(isInterface?"&isInterface":"")); + } + private static int tableIndex(int refKind, boolean isInterface) { + if (refKind < 0) return refKind; + return (refKind * 2) + (isInterface ? 1 : 0); + } + private static final @Stable Kind[] TABLE = new Kind[20]; + static { + // Pack the static table. + for (Kind kind : values()) { + int i = tableIndex(kind.refKind, kind.isInterface); + if (i >= TABLE.length || TABLE[i] != null) + throw new AssertionError("TABLE entry for "+kind); + TABLE[i] = kind; + } + } } /** * Return the {@code kind} of the method handle described by this nominal @@ -135,30 +188,4 @@ * @return the method type */ MethodTypeDesc methodType(); - - /** - * Create a {@linkplain DirectMethodHandleDesc} given descriptor strings - * for its components. Suitable for use as a constant bootstrap method - * for representing a {@linkplain DirectMethodHandleDesc} in the constant - * pool of a classfile. - * - * @param bsmKindName The name of an {@code enum} constant from {@link Kind} - * @param memberOwner A field type descriptor for the class declaring the - * method, field, or constructor, as per JVMS 4.3.2 - * @param memberName The name of the method or field, as per JVMS 4.2.2 - * @param memberType A method type descriptor for the method handle being - * described, as per JVMS 4.3.3 - * @return the {@linkplain MethodHandleDesc} - * @jvms 4.2.2 Unqualified Names - * @jvms 4.3.2 Field Descriptors - * @jvms 4.3.3 Method Descriptors - */ - static DirectMethodHandleDesc ofDescriptor(String bsmKindName, - String memberOwner, - String memberName, - String memberType) { - return MethodHandleDesc.of(Kind.valueOf(bsmKindName), - ClassDesc.ofDescriptor(memberOwner), memberName, - MethodTypeDesc.ofDescriptor(memberType)); - } } --- old/src/java.base/share/classes/java/lang/constant/DirectMethodHandleDescImpl.java 2018-09-28 11:54:10.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/constant/DirectMethodHandleDescImpl.java 2018-09-28 11:54:10.000000000 -0700 @@ -30,12 +30,8 @@ import java.util.Objects; import java.util.Optional; -import static java.lang.constant.ConstantDescs.BSM_INVOKE; -import static java.lang.constant.ConstantDescs.BSM_METHODHANDLEDESC; import static java.lang.constant.ConstantDescs.CR_DirectMethodHandleDesc; import static java.lang.constant.ConstantDescs.DEFAULT_NAME; -import static java.lang.constant.ConstantDescs.MHR_METHODHANDLEDESC_FACTORY; -import static java.lang.constant.ConstantDescs.MHR_METHODTYPEDESC_FACTORY; import static java.lang.constant.ConstantUtils.validateClassOrInterface; import static java.lang.constant.ConstantUtils.validateMemberName; import static java.lang.constant.DirectMethodHandleDesc.Kind.CONSTRUCTOR; @@ -165,11 +161,7 @@ @Override public Optional>> describeConstable() { - return Optional.of(DynamicConstantDesc.of(BSM_INVOKE, MHR_METHODHANDLEDESC_FACTORY, - kind.toString(), - owner.descriptorString(), - name, - type.descriptorString())); + return Optional.of(DynamicConstantDesc.ofSymbolic(this)); } @Override --- old/src/java.base/share/classes/java/lang/constant/DynamicConstantDesc.java 2018-09-28 11:54:12.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/constant/DynamicConstantDesc.java 2018-09-28 11:54:12.000000000 -0700 @@ -24,11 +24,17 @@ */ package java.lang.constant; +import jdk.internal.vm.annotation.Stable; + import java.lang.Enum.EnumDesc; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.VarHandle; import java.lang.invoke.VarHandle.VarHandleDesc; +import java.lang.reflect.InvocationTargetException; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -37,15 +43,11 @@ import java.util.function.Function; import java.util.stream.Stream; -import static java.lang.constant.ConstantDescs.BSM_DYNAMICCONSTANTDESC; -import static java.lang.constant.ConstantDescs.BSM_INVOKE; import static java.lang.constant.ConstantDescs.CR_Class; import static java.lang.constant.ConstantDescs.CR_VarHandle; import static java.lang.constant.ConstantDescs.DEFAULT_NAME; -import static java.lang.constant.ConstantDescs.MHR_DYNAMICCONSTANTDESC_FACTORY; -import static java.lang.constant.ConstantDescs.MHR_DYNAMICCONSTANTDESC_NAMED_FACTORY; -import static java.lang.constant.ConstantUtils.EMPTY_CONSTANTDESC; -import static java.lang.constant.ConstantUtils.validateMemberName; +import static java.lang.constant.ConstantUtils.*; +import static java.lang.invoke.MethodHandles.lookup; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; @@ -99,7 +101,7 @@ String constantName, ClassDesc constantType, ConstantDesc... bootstrapArgs) { - this.bootstrapMethod = requireNonNull(bootstrapMethod); + this.bootstrapMethod = validateBootstrapMethod(requireNonNull(bootstrapMethod), constantName); this.constantName = validateMemberName(requireNonNull(constantName)); this.constantType = requireNonNull(constantType); this.bootstrapArgs = requireNonNull(bootstrapArgs).clone(); @@ -154,7 +156,7 @@ * Return a nominal descriptor for a dynamic constant. * * @param the type of the dynamic constant - * @param bootstrapMethod a {@link DirectMethodHandleDescImpl} describing the + * @param bootstrapMethod a {@link DirectMethodHandleDesc} describing the * bootstrap method for the constant * @param constantName The name that would appear in the {@code NameAndType} * operand of the {@code LDC} for this constant, as per @@ -231,6 +233,80 @@ } /** + * Return a nominal descriptor for a dynamic constant whose name parameter + * is {@link ConstantDescs#INVOKE_NAME}, and whose type parameter is always + * the same as the bootstrap method return type. + * + * @param the type of the dynamic constant + * @param bootstrapMethod a {@link DirectMethodHandleDescImpl} describing the + * bootstrap method for the constant + * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments + * to the bootstrap, that would appear in the + * {@code BootstrapMethods} attribute + * @return the nominal descriptor + * @throws NullPointerException if any argument is null + * @throws IllegalArgumentException if the {@code name} has the incorrect + * format + * @jvms 4.2.2 Unqualified Names + */ + public static DynamicConstantDesc ofInvoke(DirectMethodHandleDesc bootstrapMethod, + ConstantDesc... bootstrapArgs) { + String constantName = ConstantDescs.INVOKE_NAME; + validateExpressionModeBootstrapMethod(bootstrapMethod, constantName); + return ofNamed(bootstrapMethod, constantName, bootstrapMethod.methodType().returnType(), bootstrapArgs); + } + + /** + * Return a nominal descriptor for a dynamic constant whose name parameter + * is {@link ConstantDescs#SYMBOLIC_NAME}, and whose type parameter is always + * the same as the bootstrap method return type. The effect of resolving + * this dynamic constant will be to invoke the bootstrap method, without + * metadata, and without resolving any static argument whose corresponding + * bootstrap method parameter type is either {@code Object}, {@code ConstantDesc}, + * or a subtype of {@code ConstantDesc}. Other bootstrap method parameter + * types, such as {@code String} or {@code MethodHandle}, receive resolved + * static arguments, as usual. + * + * @param the type of the dynamic constant + * @param bootstrapMethod a {@link DirectMethodHandleDescImpl} describing the + * bootstrap method which will produce for the constant + * @param bootstrapArgs {@link ConstantDesc}s describing the static arguments + * to the bootstrap, that would appear in the + * {@code BootstrapMethods} attribute + * @return the nominal descriptor + * @throws NullPointerException if any argument is null + * @throws IllegalArgumentException if the {@code name} has the incorrect + * format + * @jvms 4.2.2 Unqualified Names + */ + public static DynamicConstantDesc ofSymbolicExpression(DirectMethodHandleDesc bootstrapMethod, + ConstantDesc... bootstrapArgs) { + String constantName = ConstantDescs.SYMBOLIC_NAME; + validateExpressionModeBootstrapMethod(bootstrapMethod, constantName); + return ofNamed(bootstrapMethod, constantName, bootstrapMethod.methodType().returnType(), bootstrapArgs); + } + + /** + * Return a nominal descriptor for a dynamic constant whose name parameter + * is {@link ConstantDescs#SYMBOLIC_NAME}, whose type parameter is + * {@code ConstantDesc}, and whose bootstrap method is + * {@link ConstantBootstraps#constantDesc}. The effect of resolving + * this dynamic constant will be to return a {@code ConstantDesc} + * that represents the sole static argument, without resolving it. + * + * @param the type of the constant to be described + * @param constantDesc the constant to be described + * @return the nominal descriptor, which when resolved produces the constant, as a {@code ConstantDesc} + * @throws NullPointerException if any argument is null + * @throws IllegalArgumentException if the {@code name} has the incorrect + * format + * @jvms 4.2.2 Unqualified Names + */ + public static> DynamicConstantDesc ofSymbolic(T constantDesc) { + return ofSymbolicExpression(ConstantDescs.MHR_CONSTANTDESC_IDENTITY, constantDesc); + } + + /** * Return a nominal descriptor for a dynamic constant whose bootstrap has * no static arguments, whose name parameter is {@link ConstantDescs#DEFAULT_NAME}, * and whose type parameter is always the same as the bootstrap method return type. @@ -294,51 +370,44 @@ return List.of(bootstrapArgs); } - @SuppressWarnings("unchecked") - public T resolveConstantDesc(MethodHandles.Lookup lookup) throws ReflectiveOperationException { - // TODO replace with public supported method + public T resolveConstantDesc(Lookup lookup) throws ReflectiveOperationException { try { - MethodHandle bsm = bootstrapMethod.resolveConstantDesc(lookup); - if (bsm.type().parameterCount() < 2 || - !MethodHandles.Lookup.class.isAssignableFrom(bsm.type().parameterType(0))) { - throw new BootstrapMethodError( - "Invalid bootstrap method declared for resolving a dynamic constant: " + bootstrapMethod); - } - Object[] bsmArgs = new Object[3 + bootstrapArgs.length]; - bsmArgs[0] = lookup; - bsmArgs[1] = constantName; - bsmArgs[2] = constantType.resolveConstantDesc(lookup); - for (int i = 0; i < bootstrapArgs.length; i++) - bsmArgs[3 + i] = bootstrapArgs[i].resolveConstantDesc(lookup); - - return (T) bsm.invokeWithArguments(bsmArgs); - } catch (Error e) { - throw e; - } catch (Throwable t) { - throw new BootstrapMethodError(t); + Object rawResult = MH_resolveDynamicConstant().invokeExact(this, lookup); + @SuppressWarnings("unchecked") + T result = (T) rawResult; + return result; + } catch (ReflectiveOperationException|RuntimeException ex) { + throw ex; + } catch (Throwable ex) { + throw new InvocationTargetException(ex); } } + private static @Stable MethodHandle MH_resolveDynamicConstant; + private static MethodHandle MH_resolveDynamicConstant() { + MethodHandle mh = MH_resolveDynamicConstant; + if (mh != null) return mh; + // tunnel through access controls to get to BSCI::ofConstantDesc + mh = AccessController.doPrivileged(new PrivilegedAction() { + @Override + public MethodHandle run() { + try { + var bsci = Class.forName("java.lang.invoke.BootstrapMethodInvoker"); + var meth = bsci.getDeclaredMethod("resolveDynamicConstant", + DynamicConstantDesc.class, Lookup.class); + meth.setAccessible(true); + return lookup().unreflect(meth); + } catch (ReflectiveOperationException ex) { + throw new InternalError(ex); + } + } + }); + return mh; + } + @Override public Optional>> describeConstable() { - ConstantDesc[] args; - if (constantName.equals(DEFAULT_NAME) && constantType.equals(bootstrapMethod.methodType().returnType())) { - args = new ConstantDesc[bootstrapArgs.length + 2]; - args[0] = MHR_DYNAMICCONSTANTDESC_FACTORY; - args[1] = bootstrapMethod.describeConstable().orElseThrow(); - for (int i = 0; i < bootstrapArgs.length; i++) - args[i + 2] = (ConstantDesc) ((Constable) bootstrapArgs[i]).describeConstable().orElseThrow(); - } - else { - args = new ConstantDesc[bootstrapArgs.length + 4]; - args[0] = MHR_DYNAMICCONSTANTDESC_NAMED_FACTORY; - args[1] = bootstrapMethod.describeConstable().orElseThrow(); - args[2] = constantName; - args[3] = constantType().descriptorString(); - for (int i = 0; i < bootstrapArgs.length; i++) - args[i + 4] = (ConstantDesc) ((Constable) bootstrapArgs[i]).describeConstable().orElseThrow(); - } - return Optional.of(DynamicConstantDesc.of(BSM_INVOKE, args)); + return Optional.of(DynamicConstantDesc.ofSymbolic(this)); } private ConstantDesc tryCanonicalize() { --- old/src/java.base/share/classes/java/lang/constant/MethodTypeDescImpl.java 2018-09-28 11:54:14.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/constant/MethodTypeDescImpl.java 2018-09-28 11:54:14.000000000 -0700 @@ -30,8 +30,6 @@ import java.util.List; import java.util.Optional; -import static java.lang.constant.ConstantDescs.BSM_INVOKE; -import static java.lang.constant.ConstantDescs.MHR_METHODTYPEDESC_FACTORY; import static java.util.Objects.requireNonNull; /** @@ -142,7 +140,7 @@ @Override public Optional>> describeConstable() { - return Optional.of(DynamicConstantDesc.of(BSM_INVOKE, MHR_METHODTYPEDESC_FACTORY, descriptorString())); + return Optional.of(DynamicConstantDesc.ofSymbolic(this)); } @Override --- old/src/java.base/share/classes/java/lang/constant/PrimitiveClassDescImpl.java 2018-09-28 11:54:16.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/constant/PrimitiveClassDescImpl.java 2018-09-28 11:54:16.000000000 -0700 @@ -29,7 +29,6 @@ import sun.invoke.util.Wrapper; -import static java.lang.constant.ConstantDescs.BSM_INVOKE; import static java.lang.constant.ConstantDescs.MHR_CLASSDESC_FACTORY; import static java.util.Objects.requireNonNull; @@ -72,7 +71,7 @@ @Override public Optional>>> describeConstable() { - return Optional.of(DynamicConstantDesc.of(BSM_INVOKE, MHR_CLASSDESC_FACTORY, descriptor)); + return Optional.of(DynamicConstantDesc.ofInvoke(MHR_CLASSDESC_FACTORY, descriptor)); } @Override --- old/src/java.base/share/classes/java/lang/constant/ReferenceClassDescImpl.java 2018-09-28 11:54:18.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/constant/ReferenceClassDescImpl.java 2018-09-28 11:54:18.000000000 -0700 @@ -27,8 +27,6 @@ import java.lang.invoke.MethodHandles; import java.util.Optional; -import static java.lang.constant.ConstantDescs.BSM_INVOKE; -import static java.lang.constant.ConstantDescs.MHR_CLASSDESC_FACTORY; import static java.lang.constant.ConstantUtils.dropFirstAndLastChar; import static java.lang.constant.ConstantUtils.internalToBinary; import static java.util.Objects.requireNonNull; @@ -85,7 +83,7 @@ @Override public Optional>>> describeConstable() { - return Optional.of(DynamicConstantDesc.of(BSM_INVOKE, MHR_CLASSDESC_FACTORY, descriptor)); + return Optional.of(DynamicConstantDesc.ofSymbolic(this)); } @Override --- old/src/java.base/share/classes/java/lang/invoke/BootstrapCallInfo.java 2018-09-28 11:54:20.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/BootstrapCallInfo.java 2018-09-28 11:54:20.000000000 -0700 @@ -25,14 +25,22 @@ package java.lang.invoke; +import java.lang.constant.Constable; +import java.lang.constant.ConstantDesc; +import java.lang.invoke.AbstractBootstrapCallInfo.*; import java.lang.invoke.MethodHandles.Lookup; +import java.util.List; +import java.util.Objects; +import sun.invoke.util.Wrapper; + +import static java.lang.invoke.BootstrapMethodInvoker.invokeCommon; /** * An interface providing full static information about a particular * call to a * bootstrap method of an * dynamic call site or dynamic constant. - * This information includes the method itself, the associated + * This information includes the bootstrap method itself, the associated * name and type, and any associated static arguments. *

* If a bootstrap method declares exactly two arguments, and is @@ -72,40 +80,21 @@ args.add(lookup); args.add(bsci.invocationName()); args.add(bsci.invocationType()); - MethodHandle bsm = (MethodHandle) bsci.get(0); - List restOfArgs = bsci.asList().subList(1, bsci.size(); + MethodHandle bsm = (MethodHandle) bsci.argument(0); + List restOfArgs = bsci.argumentList().subList(1, bsci.argumentCount()); // the next line eagerly resolves all remaining static arguments: args.addAll(restOfArgs); return bsm.invokeWithArguments(args); } * } * - *

- * In the other direction, here is a combinator which pops - * a pull-mode bootstrap method from the beginning of a list of - * static argument values (already resolved), reformats all of - * the arguments into a pair of a lookup and a {@code BootstrapCallInfo}, - * and invokes the popped method. Again the callee has no way of - * telling it was not called directly by the JVM, except that - * all of the constant values will appear as resolved. - * Put another way, if any constant fails to resolve, the - * callee will not be able to catch the resulting error, - * since the error will be thrown by the JVM before the - * bootstrap method is entered. - *

{@code
-static Object genericBSM(Lookup lookup, String name, Object type,
-                         MethodHandle bsm, Object... args)
-    throws Throwable {
-  ConstantGroup cons = ConstantGroup.makeConstantGroup(Arrays.asList(args));
-  BootstrapCallInfo bsci = makeBootstrapCallInfo(bsm, name, type, cons);
-  return bsm.invoke(lookup, bsci);
-}
- * }
+ * @param  the type {@code MethodType} or {@code Class}
  *
- * @since 1.10
+ * @since 12
  */
 // public
-interface BootstrapCallInfo extends ConstantGroup {
+interface BootstrapCallInfo> {
+
     /** Returns the bootstrap method for this call.
      * @return the bootstrap method
      */
@@ -121,22 +110,342 @@
      */
     T invocationType();
 
+    /** Returns a ConstantDesc for the method type or constant type for this call.
+     * Does not (by itself) trigger resolution of this type.
+     * @return the method type or constant type
+     */
+    ConstantDesc invocationTypeDesc();
+
+    /**
+     * Returns the number of static arguments.
+     * @return the number of static arguments
+     */
+    int argumentCount();
+
+    /**
+     * Returns the selected static argument, resolving it if necessary.
+     * Throws a linkage error if resolution proves impossible.
+     * @param index which static argument to select
+     * @return the selected static argument
+     * @throws LinkageError if the selected static argument needs resolution
+     * and cannot be resolved
+     */
+    Object argument(int index) throws LinkageError;
+
+    /**
+     * Returns the selected static argument,
+     * or the given sentinel value if there is none available.
+     * If the static argument cannot be resolved, the sentinel will be returned.
+     * If the static argument can (perhaps) be resolved, but has not yet been resolved,
+     * then the sentinel may be returned, at the implementation's discretion.
+     * To force resolution (and a possible exception), call {@link #argument(int)}.
+     * @param index which static argument to select
+     * @param ifNotPresent the sentinel value to return if the static argument is not present
+     * @return the selected static argument, if available, else the sentinel value
+     */
+    default Object argument(int index, Object ifNotPresent) {
+        if (argumentIsPresent(index))  return argument(index);
+        return ifNotPresent;
+    }
+
+    /**
+     * Returns a symbolic reference underlying the selected static argument,
+     * if it is available.
+     * @param index which static argument to select
+     * @return a symbolic reference underlying the selected static argument
+     * @throws IllegalArgumentException if the original symbolic reference is not available,
+     * and a substitute cannot be created on the fly by {@link ConstantDesc}.
+     */
+    ConstantDesc argumentDesc(int index);
+
+    /**
+     * Returns an indication of whether a static argument may be available.
+     * If it returns {@code true}, it will always return true in the future,
+     * and a call to {@link #argument(int)} will never throw an exception.
+     * 

+ * After a normal return from {@link #argument(int)} or a present + * value is reported from {@link #argument(int,Object)}, this method + * must always return true. + *

+ * If this method returns {@code false}, nothing in particular + * can be inferred, since the query only concerns the internal + * logic of the {@code BootstrapCallInfo} object which ensures that a + * successful query to a constant will always remain successful. + * The only way to force a permanent decision about whether + * a static argument is available is to call {@link #argument(int)} and + * be ready for an exception if the constant is unavailable. + * @param index which constant to select + * @return {@code true} if the selected static argument is known by + * this object to be present, {@code false} if it is known + * not to be present or + */ + boolean argumentIsPresent(int index); + + + /// Views + + /** + * Create a view on the static arguments as a {@link List} view. + * Any request for a static argument through this view will + * force resolution, and may therefore cause a {@code LinkageError}. + * @return a {@code List} view on the static arguments which will force resolution + */ + default List argumentList() { + return new ArgList(this, 0, argumentCount()); + } + + /** + * Create a view on the static arguments as a {@link List} view. + * Any request for a static argument through this view will + * return the given sentinel value, if the corresponding + * call to {@link #argument(int,Object)} would do so. + * @param ifNotPresent the sentinel value to return if a static argument is not present + * @return a {@code List} view on the static arguments which will not force resolution + */ + default List argumentList(Object ifNotPresent) { + return new ArgList(this, 0, argumentCount(), ifNotPresent); + } + + /** + * Create a view on the symbolic references of the static arguments as a {@link List} view. + * @return a {@code List} view on this group's symbolic references + */ + default List> argumentDescList() { + List syms = new ArgList(this, true, 0, argumentCount()); + @SuppressWarnings({"unchecked", "rawtypes"}) + List> result = (List) syms; + return result; + } + + /// Factories and helper methods + + /** + * Invoke a bootstrap method handle with arguments obtained by resolving + * the sequence of constants supplied by a given bootstrap call descriptor, + * {@code bci}. + * The first argument to the method will be {@code lookup}. + * The second argument will be the invocation name of {@code bci}. + * The third argument will be the invocation type of {@code bci}. + * The fourth and subsequent arguments (if any) will be the resolved + * constants, in order, supplied by {@code bci}. + *

+ * @apiNote + * This method behaves like the following but may be more optimal: + *

{@code
+     *   ArrayList args = new ArrayList<>();
+     *   args.add(lookup);
+     *   args.add(name);
+     *   args.add(type);
+     *   args.addAll(staticArgs);
+     *   return handle.invokeWithArguments(args);
+     * }
+     *
+     * @param handle the bootstrap method handle to be invoked on the static arguments
+     * @param lookup the lookup
+     * @param name the name associated with the constant or call site being linked
+     * @param type the type associated with the constant or call site being linked
+     * @param staticArgs the static argument list
+     * @return the result of invocation
+     * @throws Throwable if an error occurs when resolving the constants from
+     *         the bootstrap call descriptor or invoking the method handle
+     */
+    static Object invokeWithMetadata(MethodHandle handle,
+                                     MethodHandles.Lookup lookup,
+                                     String name,
+                                     TypeDescriptor type,
+                                     List staticArgs)
+            throws Throwable {
+        Objects.requireNonNull(lookup);
+        return invokeCommon(handle, lookup, name, type, null, staticArgs);
+    }
+
     /**
-     * Make a new bootstrap call descriptor with the given components.
-     * @param bsm bootstrap method
-     * @param name invocation name
-     * @param type invocation type
-     * @param constants the additional static arguments for the bootstrap method
-     * @param  the type of the invocation type, either {@link MethodHandle} or {@link Class}
-     * @return a new bootstrap call descriptor with the given components
-     */
-    static  BootstrapCallInfo makeBootstrapCallInfo(MethodHandle bsm,
-                                                          String name,
-                                                          T type,
-                                                          ConstantGroup constants) {
-        AbstractConstantGroup.BSCIWithCache bsci = new AbstractConstantGroup.BSCIWithCache<>(bsm, name, type, constants.size());
-        final Object NP = AbstractConstantGroup.BSCIWithCache.NOT_PRESENT;
-        bsci.initializeCache(constants.asList(NP), NP);
-        return bsci;
+     * Invoke a bootstrap method handle with arguments obtained by resolving
+     * the sequence of constants supplied by a given bootstrap call descriptor,
+     * {@code bci}.
+     * The first argument to the method will be {@code lookup}.
+     * The second argument will be the invocation name of {@code bci}.
+     * The third argument will be the invocation type of {@code bci}.
+     * The fourth and subsequent arguments (if any) will be the resolved
+     * constants, in order, supplied by {@code bci}.
+     * 

+ * @apiNote + * This method behaves like the following but may be more optimal: + *

{@code
+     *   ArrayList args = new ArrayList<>();
+     *   args.add(lookup);
+     *   args.add(name);
+     *   args.add(type);
+     *   args.addAll(Arrays.asList(staticArgs));
+     *   return handle.invokeWithArguments(args);
+     * }
+     *
+     * @param handle the bootstrap method handle to be invoked on the static arguments
+     * @param lookup the lookup
+     * @param name the name associated with the constant or call site being linked
+     * @param type the type associated with the constant or call site being linked
+     * @param staticArgs the static argument list
+     * @return the result of invocation
+     * @throws Throwable if an error occurs when resolving the constants from
+     *         the bootstrap call descriptor or invoking the method handle
+     */
+    static Object invokeWithMetadata(MethodHandle handle,
+                                     MethodHandles.Lookup lookup,
+                                     String name,
+                                     TypeDescriptor type,
+                                     Object... staticArgs)
+            throws Throwable {
+        Objects.requireNonNull(lookup);
+        Objects.requireNonNull(staticArgs);
+        return invokeCommon(handle, lookup, name, type, staticArgs, null);
+    }
+
+    /**
+     * Invoke a bootstrap method handle with arguments obtained by resolving
+     * the sequence of constants supplied by a given bootstrap call descriptor,
+     * {@code bci}.
+     * The first argument to the method will be {@code lookup}.
+     * The second argument will be the invocation name of {@code bci}.
+     * The third argument will be the invocation type of {@code bci}.
+     * The fourth and subsequent arguments (if any) will be the resolved
+     * constants, in order, supplied by {@code bci}.
+     * 

+ * @apiNote + * This method behaves like the following but may be more optimal: + *

{@code
+     *   ArrayList args = new ArrayList<>();
+     *   args.add(lookup);
+     *   args.add(bci.invocationName());
+     *   args.add(bci.invocationType());
+     *   args.addAll(bci.argumentList());
+     *   return handle.invokeWithArguments(args);
+     * }
+     *
+     * @param handle the bootstrap method handle to be invoked with resolved
+     *        constants supplied by {@code bci}
+     * @param lookup the lookup
+     * @param bsci the bootstrap call descriptor
+     * @return the result of invocation
+     * @throws Throwable if an error occurs when resolving the constants from
+     *         the bootstrap call descriptor or invoking the method handle
+     */
+    static Object invokeWithMetadata(MethodHandle handle,
+                                     MethodHandles.Lookup lookup,
+                                     BootstrapCallInfo bsci)
+            throws Throwable {
+        Objects.requireNonNull(lookup);
+        return invokeCommon(handle, lookup, bsci.invocationName(), bsci.invocationType(), AbstractBootstrapCallInfo.maybeShareArguments(bsci), bsci.argumentList());
+    }
+
+    /**
+     * Convert the result returned by a bootstrap method to the class
+     * required by the bootstrap method's {@code invocationType}.
+     *
+     * @param bsci the bootstrap call descriptor
+     * @param result the result to be converted
+     * @param  the type {@code MethodType} or {@code Class}
+     * @return the converted result
+     * @throws ClassCastException if a value conversion error occurs during conversion
+     * @throws WrongMethodTypeException if a method type mismatch is detected occurs during conversion
+     */
+    static
+    >
+    Object convertResult(BootstrapCallInfo bsci, Object result) {
+        // FIXME:  If invocationType cannot be resolved, some results are still valid.
+        return convertResult(bsci.invocationType(), bsci.bootstrapMethod().type().returnType(), result);
+    }
+
+    /**
+     * Convert the result returned by a bootstrap method to the class
+     * required by the bootstrap method's {@code invocationType}.
+     *
+     * @param type the method type or constant type to be obtained
+     * @param resultType the type of the result (return type of the BSM that returned the result)
+     * @param result the result to be converted
+     * @param  the type {@code MethodType} or {@code Class}
+     * @return the converted result
+     * @throws ClassCastException if a value conversion error occurs during conversion
+     * @throws WrongMethodTypeException if a method type mismatch is detected occurs during conversion
+     */
+    static
+    >
+    Object convertResult(T type, Class resultType, Object result) {
+        Class resultClass;
+        boolean isFieldType;
+        if (type instanceof Class) {
+            isFieldType = true;
+            resultClass = (Class) type;
+        } else {
+            isFieldType = false;
+            resultClass = null;
+        }
+        if (isFieldType && resultClass.isPrimitive()) {
+            Class wrapperClass = Wrapper.asWrapperType(resultClass);
+            if (result.getClass() == wrapperClass) {
+                return result;  // fast path
+            }
+            // Non-reference conversions are more than just plain casts.
+            // By pushing the value through a funnel of the form (R x)->x,
+            // the boxed result can be widened as needed.  See MH::asType.
+            MethodHandle funnel = MethodHandles.identity(resultClass);
+            if (!MethodType.canConvert(resultType, resultClass)) {
+                // Example:  Result type is Integer and resultClass is short.
+                funnel = funnel.asType(MethodType.methodType(resultClass, resultType));
+                assert(false);  // should have thrown WMTE
+            }
+            try {
+                result = funnel.invoke(result);
+            } catch (ClassCastException | WrongMethodTypeException ex) {
+                throw ex;
+            } catch (Throwable ex) {
+                throw new InternalError(ex);
+            }
+            // Now it is the wrapper type for resultType.
+            assert(result.getClass() == Wrapper.asWrapperType(resultClass));
+            return result;
+        } else if (isFieldType) {
+            // A reference type.
+            return resultClass.cast(result);
+        } else {
+            // Check or convert the method-like result.
+            MethodType mt = (MethodType) type;  // must be either Class or MethodType
+            if (result instanceof CallSite) {
+                CallSite cs = (CallSite) result;
+                if (!cs.type().equals(mt))
+                    throw CallSite.wrongTargetType(cs, mt);
+                return cs;  // no conversion, just checking
+            }
+            if (result instanceof MethodHandle) {
+                // Method handles can be converted on the fly:
+                MethodHandle mh = (MethodHandle) result;
+                return mh.asType(mt);
+            }
+            throw new ClassCastException("CallSite bootstrap method failed to produce an instance of CallSite");
+        }
+    }
+
+    /**
+     * Produce a string that briefly reports the BSM, name, type,
+     * and argument list.  For arguments, use a non-resolving list view,
+     * with unresolved elements being presented as asterisks.
+     * @param bsci the object to produce a string for
+     * @return {@code this.asList("*").toString()}
+     */
+    public static String toString(BootstrapCallInfo bsci) {
+        MethodHandle bsm = bsci.bootstrapMethod();
+        MemberName mem = bsm.internalMemberName();
+        Object bsmStr = bsm;
+        if (mem != null)  bsmStr = mem;
+        //bsmStr = bsm.describeConstable()
+        Object typeStr = null;
+        try {
+            typeStr = bsci.invocationType();
+        } catch (LinkageError ex) {
+            typeStr = bsci.invocationTypeDesc();
+        }
+        return (bsmStr
+                + "/" + bsci.invocationName()
+                + ":" + typeStr
+                + bsci.argumentList("*"));
     }
 }
--- old/src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java	2018-09-28 11:54:22.000000000 -0700
+++ new/src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java	2018-09-28 11:54:22.000000000 -0700
@@ -24,162 +24,68 @@
  */
 package java.lang.invoke;
 
-import sun.invoke.util.Wrapper;
+import jdk.internal.reflect.ConstantPool;
+import jdk.internal.vm.annotation.Stable;
 
-import java.lang.invoke.AbstractConstantGroup.BSCIWithCache;
+import java.lang.constant.Constable;
+import java.lang.constant.ConstantDesc;
+import java.lang.constant.DynamicConstantDesc;
+import java.lang.reflect.Array;
 import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
 
-import static java.lang.invoke.BootstrapCallInfo.makeBootstrapCallInfo;
-import static java.lang.invoke.ConstantGroup.makeConstantGroup;
-import static java.lang.invoke.MethodHandleNatives.*;
+import java.lang.invoke.AbstractBootstrapCallInfo.*;
+
+import static java.lang.invoke.AbstractBootstrapCallInfo.maybeShareArguments;
+import static java.lang.invoke.MethodHandleNatives.Constants.*;
 import static java.lang.invoke.MethodHandleStatics.TRACE_METHOD_LINKAGE;
 import static java.lang.invoke.MethodHandles.Lookup;
 import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
+import static java.util.Objects.requireNonNull;
 
 final class BootstrapMethodInvoker {
-
     /**
      * Factored code for invoking a bootstrap method for invokedynamic
      * or a dynamic constant.
-     * @param resultType the expected return type (either CallSite or a constant type)
-     * @param bootstrapMethod the BSM to call
-     * @param name the method name or constant name
-     * @param type the method type or constant type
-     * @param info information passed up from the JVM, to derive static arguments
-     * @param callerClass the class containing the resolved method call or constant load
-     * @param  the expected return type
+     * @param lookup the capability to access the caller class's constants
+     * @param bsci the BootstrapCallInfo provided by the JVM
+     * @param  the kind of the type argument, {@code Class} or {@code MethodType}
      * @return the expected value, either a CallSite or a constant value
      */
-    static  T invoke(Class resultType,
-                        MethodHandle bootstrapMethod,
-                        // Callee information:
-                        String name, Object type,
-                        // Extra arguments for BSM, if any:
-                        Object info,
-                        // Caller information:
-                        Class callerClass) {
-        MethodHandles.Lookup caller = IMPL_LOOKUP.in(callerClass);
+    static >
+    Object invoke(Lookup lookup,
+                  BootstrapCallInfo bsci) {
+        Objects.requireNonNull(lookup);
+        MethodHandle bsm = bsci.bootstrapMethod();
+        MethodType bsmType = bsm.type();
+        boolean bsmIsVarargs = bsm.isVarargsCollector();
         Object result;
-        boolean pullMode = isPullModeBSM(bootstrapMethod);  // default value is false
-        boolean vmIsPushing = !staticArgumentsPulled(info); // default value is true
-        MethodHandle pullModeBSM;
-        // match the VM with the BSM
-        if (vmIsPushing) {
-            // VM is pushing arguments at us
-            pullModeBSM = null;
-            if (pullMode) {
-                bootstrapMethod = pushMePullYou(bootstrapMethod, true);
-            }
-        } else {
-            // VM wants us to pull args from it
-            pullModeBSM = pullMode ? bootstrapMethod :
-                    pushMePullYou(bootstrapMethod, false);
-            bootstrapMethod = null;
-        }
+
         try {
-            // As an optimization we special case various known BSMs,
-            // such as LambdaMetafactory::metafactory and
-            // StringConcatFactory::makeConcatWithConstants.
-            //
-            // By providing static type information or even invoking
-            // exactly, we avoid emitting code to perform runtime
-            // checking.
-            info = maybeReBox(info);
-            if (info == null) {
-                // VM is allowed to pass up a null meaning no BSM args
-                result = invoke(bootstrapMethod, caller, name, type);
-            }
-            else if (!info.getClass().isArray()) {
-                // VM is allowed to pass up a single BSM arg directly
-
-                // Call to StringConcatFactory::makeConcatWithConstants
-                // with empty constant arguments?
-                if (isStringConcatFactoryBSM(bootstrapMethod.type())) {
-                    result = (CallSite)bootstrapMethod
-                            .invokeExact(caller, name, (MethodType)type,
-                                         (String)info, new Object[0]);
-                } else {
-                    result = invoke(bootstrapMethod, caller, name, type, info);
-                }
-            }
-            else if (info.getClass() == int[].class) {
-                // VM is allowed to pass up a pair {argc, index}
-                // referring to 'argc' BSM args at some place 'index'
-                // in the guts of the VM (associated with callerClass).
-                // The format of this index pair is private to the
-                // handshake between the VM and this class only.
-                // This supports "pulling" of arguments.
-                // The VM is allowed to do this for any reason.
-                // The code in this method makes up for any mismatches.
-                BootstrapCallInfo bsci
-                    = new VM_BSCI<>(bootstrapMethod, name, type, caller, (int[])info);
+            // By using maybeShareArguments, we avoid extra copy steps
+            // on the argument list between a BSM created for the JVM
+            // (by MethodHandleNatives) and the invocation logic of
+            // invokeCommon.
+            if (isPullModeBSM(bsm)) {
                 // Pull-mode API is (Lookup, BootstrapCallInfo) -> Object
-                result = pullModeBSM.invoke(caller, bsci);
+                result = bsm.invoke(lookup, bsci);
+            } else if (isExpressionBSM(bsm)) {
+                if (bsci.invocationName().equals("invoke"))
+                    // short circuit in common case
+                    result = invokeCommon(bsm,
+                                          null, null, null,
+                                          maybeShareArguments(bsci),
+                                          bsci.argumentList());
+                else
+                    result = ConstantBootstraps.linkExpression(lookup, bsci);
+            } else {
+                // Push-mode BSM, needs to get (lookup, name, type, arg...).
+                result = invokeCommon(bsm, lookup,
+                                      bsci.invocationName(), bsci.invocationType(),
+                                      maybeShareArguments(bsci), bsci.argumentList());
             }
-            else {
-                // VM is allowed to pass up a full array of resolved BSM args
-                Object[] argv = (Object[]) info;
-                maybeReBoxElements(argv);
-
-                MethodType bsmType = bootstrapMethod.type();
-                if (isLambdaMetafactoryIndyBSM(bsmType) && argv.length == 3) {
-                    result = (CallSite)bootstrapMethod
-                            .invokeExact(caller, name, (MethodType)type, (MethodType)argv[0],
-                                    (MethodHandle)argv[1], (MethodType)argv[2]);
-                } else if (isLambdaMetafactoryCondyBSM(bsmType) && argv.length == 3) {
-                    result = bootstrapMethod
-                            .invokeExact(caller, name, (Class)type, (MethodType)argv[0],
-                                    (MethodHandle)argv[1], (MethodType)argv[2]);
-                } else if (isStringConcatFactoryBSM(bsmType) && argv.length >= 1) {
-                    String recipe = (String)argv[0];
-                    Object[] shiftedArgs = Arrays.copyOfRange(argv, 1, argv.length);
-                    result = (CallSite)bootstrapMethod.invokeExact(caller, name, (MethodType)type, recipe, shiftedArgs);
-                } else if (isLambdaMetafactoryAltMetafactoryBSM(bsmType)) {
-                    result = (CallSite)bootstrapMethod.invokeExact(caller, name, (MethodType)type, argv);
-                } else {
-                    switch (argv.length) {
-                        case 0:
-                            result = invoke(bootstrapMethod, caller, name, type);
-                            break;
-                        case 1:
-                            result = invoke(bootstrapMethod, caller, name, type,
-                                            argv[0]);
-                            break;
-                        case 2:
-                            result = invoke(bootstrapMethod, caller, name, type,
-                                            argv[0], argv[1]);
-                            break;
-                        case 3:
-                            result = invoke(bootstrapMethod, caller, name, type,
-                                            argv[0], argv[1], argv[2]);
-                            break;
-                        case 4:
-                            result = invoke(bootstrapMethod, caller, name, type,
-                                            argv[0], argv[1], argv[2], argv[3]);
-                            break;
-                        case 5:
-                            result = invoke(bootstrapMethod, caller, name, type,
-                                            argv[0], argv[1], argv[2], argv[3], argv[4]);
-                            break;
-                        case 6:
-                            result = invoke(bootstrapMethod, caller, name, type,
-                                            argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
-                            break;
-                        default:
-                            result = invokeWithManyArguments(bootstrapMethod, caller, name, type, argv);
-                    }
-                }
-            }
-            if (resultType.isPrimitive()) {
-                // Non-reference conversions are more than just plain casts.
-                // By pushing the value through a funnel of the form (T x)->x,
-                // the boxed result can be widened as needed.  See MH::asType.
-                MethodHandle funnel = MethodHandles.identity(resultType);
-                result = funnel.invoke(result);
-                // Now it is the wrapper type for resultType.
-                resultType = Wrapper.asWrapperType(resultType);
-            }
-            return resultType.cast(result);
+            return BootstrapCallInfo.convertResult(bsci.invocationType(), bsmType.returnType(), result);
         }
         catch (Error e) {
             // Pass through an Error, including BootstrapMethodError, any other
@@ -195,151 +101,94 @@
         }
     }
 
-    // If we don't provide static type information for type, we'll generate runtime
-    // checks. Let's try not to...
-
-    private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
-                                 String name, Object type) throws Throwable {
-        if (type instanceof Class) {
-            return bootstrapMethod.invoke(caller, name, (Class)type);
-        } else {
-            return bootstrapMethod.invoke(caller, name, (MethodType)type);
-        }
-    }
-
-    private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
-                                 String name, Object type, Object arg0) throws Throwable {
-        if (type instanceof Class) {
-            return bootstrapMethod.invoke(caller, name, (Class)type, arg0);
-        } else {
-            return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0);
-        }
-    }
-
-    private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name,
-                                 Object type, Object arg0, Object arg1) throws Throwable {
-        if (type instanceof Class) {
-            return bootstrapMethod.invoke(caller, name, (Class)type, arg0, arg1);
-        } else {
-            return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1);
-        }
-    }
-
-    private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name,
-                                 Object type, Object arg0, Object arg1,
-                                 Object arg2) throws Throwable {
-        if (type instanceof Class) {
-            return bootstrapMethod.invoke(caller, name, (Class)type, arg0, arg1, arg2);
-        } else {
-            return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2);
-        }
-    }
-
-    private static Object invoke(MethodHandle bootstrapMethod, Lookup caller, String name,
-                                 Object type, Object arg0, Object arg1,
-                                 Object arg2, Object arg3) throws Throwable {
-        if (type instanceof Class) {
-            return bootstrapMethod.invoke(caller, name, (Class)type, arg0, arg1, arg2, arg3);
-        } else {
-            return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3);
-        }
+    /** A BSM runs in pull mode if and only if its sole arguments
+     * are {@code (Lookup, BootstrapCallInfo)}.
+     * Since the trailing argument is not an array type, it cannot be of variable arity.
+     * The return type of the BSM is unconstrained, but it will often be {@code Object}.
+     */
+    public static boolean isPullModeBSM(MethodHandle bsm) {
+        MethodType mtype = bsm.type();
+        return mtype.parameterCount() == 2
+               && mtype.parameterType(0) == Lookup.class
+               && mtype.parameterType(1) == BootstrapCallInfo.class;
+    }
+
+    /** Most BSM calls take a standard set of metadata arguments,
+     *  one of {@code (Lookup, String, Class)}, {@code (Lookup, String, MethodType)},
+     *  or {@code (Lookup, BootstrapCallInfo)}.
+     *  But if the lead argument is not {@code Lookup} and
+     *  the invocation type being linked is a field type ({@code Class}),
+     *  then only the static arguments will be passed.
+     *  Such a BSM is called an expression mode BSM.
+     *  The standard bootstrap method {@link ConstantBootstraps#linkExpression}
+     *  is consulted to correctly invoke such an expression bootstrap method.
+     *  Often, it is just applied directly to the static arguments.
+     */
+    static boolean isExpressionBSM(MethodHandle bsm) {
+        MethodType mtype = bsm.type();
+        return (mtype.parameterCount() == 0
+                || mtype.parameterType(0) != Lookup.class);
     }
 
-    private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
-                                 String name, Object type, Object arg0, Object arg1,
-                                 Object arg2, Object arg3, Object arg4) throws Throwable {
-        if (type instanceof Class) {
-            return bootstrapMethod.invoke(caller, name, (Class)type, arg0, arg1, arg2, arg3, arg4);
-        } else {
-            return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3, arg4);
+    /** Given a poorly typed BSM, wrap it in a BSM whose
+     * type starts with Lookup.  This will prevent it from
+     * being accidentally treated as an expression-mode BSM.
+     * We only do this, of course, if we know that the BSM
+     * is not supposed to have expression mode.  This is the
+     * case when the BSM links an invokedynamic instruction.
+     */
+    static MethodHandle forceLookupParameter(MethodHandle bsm) {
+        if (TRACE_METHOD_LINKAGE)
+            System.out.println("[TRACE_METHOD_LINKAGE] forceLookupParameter "+bsm);
+        MethodHandle mh = MH_forceLookupParameter;
+        if (mh == null) {
+            try {
+                mh = IMPL_LOOKUP.findStatic(BootstrapMethodInvoker.class, "forceLookupParameter",
+                                            MethodType.methodType(Object.class, MethodHandle.class,
+                                                                  Lookup.class, Object[].class));
+            } catch (ReflectiveOperationException ex) {
+                throw new InternalError(ex);
+            }
+            MH_forceLookupParameter = mh;
         }
+        return mh.bindTo(bsm).withVarargs(true);
     }
-
-    private static Object invoke(MethodHandle bootstrapMethod, Lookup caller,
-                                 String name, Object type, Object arg0, Object arg1,
-                                 Object arg2, Object arg3, Object arg4, Object arg5) throws Throwable {
-        if (type instanceof Class) {
-            return bootstrapMethod.invoke(caller, name, (Class)type, arg0, arg1, arg2, arg3, arg4, arg5);
-        } else {
-            return bootstrapMethod.invoke(caller, name, (MethodType)type, arg0, arg1, arg2, arg3, arg4, arg5);
-        }
+    private static @Stable MethodHandle MH_forceLookupParameter;
+    private static Object forceLookupParameter(MethodHandle bsm,
+                                               Lookup lookup,
+                                               Object... otherArgs) throws Throwable {
+        // There are often faster ways to get this done, but this formulation
+        // covers all the corner cases.  Poorly typed BSMs are rare, anyway.
+        Object[] args = new Object[1 + otherArgs.length];
+        args[0] = lookup; System.arraycopy(otherArgs, 0, args, 1, otherArgs.length);
+        return bsm.invokeWithArguments(args);
     }
 
-    private static Object invokeWithManyArguments(MethodHandle bootstrapMethod, Lookup caller,
-                                                  String name, Object type, Object[] argv) throws Throwable {
-        final int NON_SPREAD_ARG_COUNT = 3;  // (caller, name, type)
-        final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT;
-        if (argv.length >= MAX_SAFE_SIZE) {
-            // to be on the safe side, use invokeWithArguments which handles jumbo lists
-            Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argv.length];
-            newargv[0] = caller;
-            newargv[1] = name;
-            newargv[2] = type;
-            System.arraycopy(argv, 0, newargv, NON_SPREAD_ARG_COUNT, argv.length);
-            return bootstrapMethod.invokeWithArguments(newargv);
-        } else {
-            MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length);
-            MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
-            MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
-            return spreader.invokeExact(typedBSM, (Object) caller, (Object) name, type, argv);
-        }
-    }
+    /// Signatures which have bespoke invocation paths:
 
+    // Lambda metafactory, standard.
     private static final MethodType LMF_INDY_MT = MethodType.methodType(CallSite.class,
             Lookup.class, String.class, MethodType.class, MethodType.class, MethodHandle.class, MethodType.class);
 
+    // Lambda metafactory, alternate.
     private static final MethodType LMF_ALT_MT = MethodType.methodType(CallSite.class,
             Lookup.class, String.class, MethodType.class, Object[].class);
 
+    // Lambda metafactory, condy.
     private static final MethodType LMF_CONDY_MT = MethodType.methodType(Object.class,
             Lookup.class, String.class, Class.class, MethodType.class, MethodHandle.class, MethodType.class);
 
+    // String concat metafactory.
     private static final MethodType SCF_MT = MethodType.methodType(CallSite.class,
             Lookup.class, String.class, MethodType.class, String.class, Object[].class);
 
-    /**
-     * @return true iff the BSM method type exactly matches
-     *         {@see java.lang.invoke.StringConcatFactory#makeConcatWithConstants(MethodHandles.Lookup,
-     *                 String,MethodType,String,Object...))}
-     */
-    private static boolean isStringConcatFactoryBSM(MethodType bsmType) {
-        return bsmType == SCF_MT;
-    }
-
-    /**
-     * @return true iff the BSM method type exactly matches
-     *         {@see java.lang.invoke.LambdaMetafactory#metafactory(
-     *          MethodHandles.Lookup,String,Class,MethodType,MethodHandle,MethodType)}
-     */
-    private static boolean isLambdaMetafactoryCondyBSM(MethodType bsmType) {
-        return bsmType == LMF_CONDY_MT;
-    }
-
-    /**
-     * @return true iff the BSM method type exactly matches
-     *         {@see java.lang.invoke.LambdaMetafactory#metafactory(
-     *          MethodHandles.Lookup,String,MethodType,MethodType,MethodHandle,MethodType)}
-     */
-    private static boolean isLambdaMetafactoryIndyBSM(MethodType bsmType) {
-        return bsmType == LMF_INDY_MT;
-    }
-
-    /**
-     * @return true iff the BSM method type exactly matches
-     *         {@see java.lang.invoke.LambdaMetafactory#altMetafactory(
-     *          MethodHandles.Lookup,String,MethodType,Object[])}
-     */
-    private static boolean isLambdaMetafactoryAltMetafactoryBSM(MethodType bsmType) {
-        return bsmType == LMF_ALT_MT;
-    }
-
     /** The JVM produces java.lang.Integer values to box
      *  CONSTANT_Integer boxes but does not intern them.
      *  Let's intern them.  This is slightly wrong for
      *  a {@code CONSTANT_Dynamic} which produces an
      *  un-interned integer (e.g., {@code new Integer(0)}).
      */
-    private static Object maybeReBox(Object x) {
+    static Object maybeReBox(Object x) {
         if (x instanceof Integer) {
             int xi = (int) x;
             if (xi == (byte) xi)
@@ -348,72 +197,387 @@
         return x;
     }
 
-    private static void maybeReBoxElements(Object[] xa) {
+    static void maybeReBoxElements(Object[] xa) {
         for (int i = 0; i < xa.length; i++) {
             xa[i] = maybeReBox(xa[i]);
         }
     }
 
+    static Object invokeCommon(MethodHandle handle,
+                               Lookup lookup,
+                               String name,
+                               TypeDescriptor type,
+                               Object[] argv,
+                               List args)
+            throws Throwable {
+        // Efficiently invoke a method (probably a BSM) on a particular
+        // set of bootstrap arguments, perhaps including the usual
+        // metadata values of (lookup, name, type).
+        // To avoid copying, source the arguments from whatever
+        // the caller has on hand:  A Java array, a List, or both.
+        //
+        // Support fast arity-specific invocation paths for both
+        // method types and field types (Class and MethodType), and
+        // no-metadata calls also.  To get to these paths as often
+        // as possible, executet some varargs processing directly.
+        //
+        // For further optimization we special-case various known BSMs,
+        // including LambdaMetafactory::metafactory and
+        // StringConcatFactory::makeConcatWithConstants.
+        // (Actually, we special-case their *types*, which amounts
+        // to the same thing.)
+        //
+        // By providing static type information or even invoking
+        // exactly, we avoid emitting code to perform runtime
+        // checking.
+        //
+        // Because we usually don't examine the BSM return type,
+        // we can't use the MH.invokeExact mode as much as we would
+        // like, but at least the MH.invoke path has a reasonably
+        // small type mismatch.
+
+        if (TRACE_METHOD_LINKAGE) {
+            System.out.println("invoking " + handle + " on " + (argv == null ? args : Arrays.asList(argv)));
+        }
+        int argc = argv != null ? argv.length : args.size();
+
+        boolean isFieldType  = (type instanceof Class);
+        boolean isMethodType = (type instanceof MethodType);
+
+        boolean passMetadata = (lookup != null);
+        final int MAX_ARITY = 7, FT = MAX_ARITY+1, MT = FT*2, NMD = 0;
+        final int METADATA_ARG_COUNT = 3;  // (lookup, name, type)
+        final int metaArgc = passMetadata ? METADATA_ARG_COUNT : 0;
+        if (passMetadata) {
+            requireNonNull(name);
+            requireNonNull(type);
+        }
+
+        MethodType handleType = handle.type();
+        boolean handleVarargs = handle.isVarargsCollector();
+
+        if (!passMetadata) {
+            // Must not lead with a Lookup formal parameter type.
+            // Lookup leading types are reserved for metadata-using BSMs.
+            assert(handleType.parameterCount() == 0 ||
+                   handleType.parameterType(0) != Lookup.class);
+            assert(name == null);
+            assert(type == null);
+        } else if (isFieldType) {
+            // With condy, we require an explicit leading type of Lookup,
+            // if metadata is to be passed.
+            assert(handleType.parameterCount() == 0 ||
+                   handleType.parameterType(0) == Lookup.class);
+            // Also, the caller should not try to handle a BSCI call here.
+            assert(handleType.parameterCount() != 2 ||
+                   handleType.parameterType(1) != BootstrapCallInfo.class);
+            // Otherwise, we are doing an old-style indy bootstrap,
+            // which tolerates Object in the metadata positions.
+        }
+
+        // Some varargs methods can be handled quickly here,
+        // rather than going through more dynamic invocation paths.
+        // Do the varargs spreading if:  the BSM is varargs, the BSM
+        // directly accepts at least the metadata arguments (if applicable),
+        // the given static arguments fill in the BSM's inital set
+        // of arguments, and the BSM's trailing argument is a
+        // reference array.  In that case, split the static argument
+        // list into head and tail arrays, and link the latter onto the
+        // end of the former.
+        if (handleVarargs) {
+            int headArgc = (handleType.parameterCount() - 1) - metaArgc;
+            Class tailType = handleType.lastParameterType();
+            Class tailElementType = tailType.getComponentType();
+            if (headArgc >= 0 &&
+                headArgc <= argc &&
+                Object[].class.isAssignableFrom(tailType) &&
+                tailElementType != null) {
+                // Make a shortened argument list for a fixed-arity call.
+                Object[] headArgv = new Object[headArgc + 1];
+                if (argv != null) {
+                    System.arraycopy(argv, 0, headArgv, 0, headArgc);
+                } else {
+                    List headArgs = args.subList(0, headArgc);
+                    headArgv = headArgs.toArray(headArgv);
+                }
+                // Collect the trailing arguments into an array of the
+                // required type.  Also, make sure they fit into the array.
+                int tailArgc = argc - headArgc;
+                Object[] tailArgv = (tailElementType == Object.class)
+                        ? new Object[tailArgc]
+                        : (Object[]) Array.newInstance(tailElementType, tailArgc);
+                try {
+                    if (argv != null) {
+                        System.arraycopy(argv, headArgc, tailArgv, 0, tailArgc);
+                    } else {
+                        List tailArgs = args.subList(headArgc, argc);
+                        tailArgv = tailArgs.toArray(tailArgv);
+                    }
+                } catch (ArrayStoreException ex) {
+                    // Oops, one of the actuals doesn't fit in the varargs formal.
+                    // For example, f(String...) is passed ("foo", 42, "bar").
+                    tailArgv = null;  // bail out
+                }
+                if (tailArgv != null) {
+                    // Link the head to the tail:
+                    headArgv[headArgc] = tailArgv;
+                    // Now swap in a new MH and arglist:
+                    handle = handle.asFixedArity();
+                    assert(handleType == handle.type());  // still true
+                    handleVarargs = false;
+                    argv = headArgv;
+                    args = null;
+                    argc = headArgc + 1;
+                }
+            }
+        }
+
+        // Now try to make an inline call:
+        if (argc <= MAX_ARITY && argv == null)
+            argv = args.toArray();
+
+        assert(argv == null || argc == argv.length);
+        assert(args == null || argc == args.size());
+
+        // If we don't provide static type information for the type,
+        // we'll generate runtime checks.  The cases marked FT and MT
+        // avoid that problem.
+
+        switch (argc > MAX_ARITY ? -1 :
+                argc + (isFieldType ? FT : isMethodType ? MT : NMD)) {
+            // Fixed-arity cases for a Class (field) type:
+            case 0+FT:
+                return handle.invoke(lookup, name, (Class)type);
+            case 1+FT:
+                return handle.invoke(lookup, name, (Class)type,
+                                     argv[0]);
+            case 2+FT:
+                return handle.invoke(lookup, name, (Class)type,
+                                     argv[0], argv[1]);
+            case 3+FT:
+                if (handleType == LMF_CONDY_MT && !handleVarargs) {
+                    return handle.invokeExact(lookup, name, (Class)type,
+                                              (MethodType)argv[0],
+                                              (MethodHandle)argv[1],
+                                              (MethodType)argv[2]);
+                }
+                return handle.invoke(lookup, name, (Class)type,
+                                     argv[0], argv[1], argv[2]);
+            case 4+FT:
+                return handle.invoke(lookup, name, (Class)type,
+                                     argv[0], argv[1], argv[2], argv[3]);
+            case 5+FT:
+                return handle.invoke(lookup, name, (Class)type,
+                                     argv[0], argv[1], argv[2], argv[3],
+                                     argv[4]);
+            case 6+FT:
+                return handle.invoke(lookup, name, (Class)type,
+                                     argv[0], argv[1], argv[2], argv[3],
+                                     argv[4], argv[5]);
+            case MAX_ARITY+FT:
+                return handle.invoke(lookup, name, (Class)type,
+                                     argv[0], argv[1], argv[2], argv[3],
+                                     argv[4], argv[5], argv[6]);
+
+            // Same cases, but for a MethodType argument:
+            case 0+MT:
+                return handle.invoke(lookup, name, (MethodType)type);
+            case 1+MT:
+                if (handleType == LMF_ALT_MT && !handleVarargs) {
+                    return (CallSite)
+                        handle.invokeExact(lookup, name, (MethodType)type,
+                                           (Object[])argv[0]);
+                }
+                return handle.invoke(lookup, name, (MethodType)type,
+                                     argv[0]);
+            case 2+MT:
+                if (handleType == SCF_MT && !handleVarargs) {
+                    return (CallSite)
+                        handle.invokeExact(lookup, name, (MethodType)type,
+                                           (String)argv[0],
+                                           (Object[])argv[1]);
+                }
+                return handle.invoke(lookup, name, (MethodType)type,
+                                     argv[0], argv[1]);
+            case 3+MT:
+                if (handleType == LMF_INDY_MT && !handleVarargs) {
+                    return (CallSite)
+                        handle.invokeExact(lookup, name, (MethodType)type,
+                                           (MethodType)argv[0],
+                                           (MethodHandle)argv[1],
+                                           (MethodType)argv[2]);
+                }
+                return handle.invoke(lookup, name, (MethodType)type,
+                                     argv[0], argv[1], argv[2]);
+            case 4+MT:
+                return handle.invoke(lookup, name, (MethodType)type,
+                                     argv[0], argv[1], argv[2], argv[3]);
+            case 5+MT:
+                return handle.invoke(lookup, name, (MethodType)type,
+                                     argv[0], argv[1], argv[2], argv[3],
+                                     argv[4]);
+            case 6+MT:
+                return handle.invoke(lookup, name, (MethodType)type,
+                                     argv[0], argv[1], argv[2], argv[3],
+                                     argv[4], argv[5]);
+            case MAX_ARITY+MT:
+                return handle.invoke(lookup, name, (MethodType)type,
+                                     argv[0], argv[1], argv[2], argv[3],
+                                     argv[4], argv[5], argv[6]);
+
+            // Same cases again, but without metadata:
+            case 0+NMD:
+                return handle.invoke();
+            case 1+NMD:
+                return handle.invoke(argv[0]);
+            case 2+NMD:
+                return handle.invoke(argv[0], argv[1]);
+            case 3+NMD:
+                return handle.invoke(argv[0], argv[1], argv[2]);
+            case 4+NMD:
+                return handle.invoke(argv[0], argv[1], argv[2], argv[3]);
+            case 5+NMD:
+                return handle.invoke(argv[0], argv[1], argv[2], argv[3],
+                                     argv[4]);
+            case 6+NMD:
+                return handle.invoke(argv[0], argv[1], argv[2], argv[3],
+                                     argv[4], argv[5]);
+            case MAX_ARITY+NMD:
+                return handle.invoke(argv[0], argv[1], argv[2], argv[3],
+                                     argv[4], argv[5], argv[6]);
+        }
+
+        // We have to stop at some point and use invokeWithArguments instead.
+        // Note that invokeWithArguments handles jumbo argument lists.
+
+        if (passMetadata) {
+            Object[] allArgv = new Object[metaArgc + argc];
+            int pos = 0;
+            allArgv[pos++] = lookup;
+            allArgv[pos++] = name;
+            allArgv[pos++] = type;
+            assert(pos == metaArgc);
+            if (argv != null) {
+                System.arraycopy(argv, 0, allArgv, pos, argc);
+                pos += argc;
+            } else {
+                for (Object arg : args) {
+                    allArgv[pos++] = arg;
+                }
+            }
+            assert(pos == allArgv.length);
+            return handle.invokeWithArguments(allArgv);
+        } else if (argv != null) {
+            // It's simple if there are no metadata arguments to prepend.
+            return handle.invokeWithArguments(argv);
+        } else {
+            return handle.invokeWithArguments(args);
+        }
+    }
+
     /** Canonical VM-aware implementation of BootstrapCallInfo.
      * Knows how to dig into the JVM for lazily resolved (pull-mode) constants.
      */
-    private static final class VM_BSCI extends BSCIWithCache {
-        private final int[] indexInfo;
-        private final Class caller;  // for index resolution only
-
-        VM_BSCI(MethodHandle bsm, String name, T type,
-                Lookup lookup, int[] indexInfo) {
-            super(bsm, name, type, indexInfo[0]);
-            if (!lookup.hasPrivateAccess())  //D.I.D.
-                throw new AssertionError("bad Lookup object");
-            this.caller = lookup.lookupClass();
-            this.indexInfo = indexInfo;
-            // scoop up all the easy stuff right away:
-            prefetchIntoCache(0, size());
+    static final class VM_BSCI> extends WithCache {
+        private final int bssIndex;
+        private final int indyIndex;
+        private final ConstantPool pool;
+        private int[] argIndexes;  // lazy
+
+        public VM_BSCI(ConstantPool pool, int bssIndex, int indyIndex,
+                       MethodHandle bsm, String name, TypeView type, Object[] arguments) {
+            super(bsm, name, type, arguments);
+            this.pool = pool;
+            this.bssIndex = bssIndex;
+            this.indyIndex = indyIndex;
         }
 
-        @Override Object fillCache(int i) {
+        /** Special VM-specific hook to resolve static arguments. */
+        @Override Object resolveConstant(int i) {
             Object[] buf = { null };
-            copyConstants(i, i+1, buf, 0);
-            Object res = wrapNull(buf[0]);
-            cache[i] = res;
+            copyVMArguments(i, i+1, buf, 0, BAR_FORCE, null);
+            Object res = buf[0];
             int next = i + 1;
             if (next < cache.length && cache[next] == null)
-                maybePrefetchIntoCache(next, false);  // try to prefetch
+                maybePrefetchIntoCache(cache, next, false);  // try to prefetch
             return res;
         }
 
-        @Override public int copyConstants(int start, int end,
-                                           Object[] buf, int pos) {
+        /** Special VM-specific hook to find symbolic references. */
+        @Override ConstantDesc findSymbol(int i) {
+            Object[] buf = { null };
+            copyVMArguments(i, i+1, buf, 0, BAR_SYMREF, null);
+            ConstantDesc res = (ConstantDesc) buf[0];
+            //int next = i + 1;
+            //if (next < symCache.length && symCache[next] == null)
+            //    maybePrefetchIntoCache(symCache, next, false);  // try to prefetch
+            return res;
+        }
+
+        private Class caller() { return pool.getHolder(); }
+
+        /*non-public contract with ArgList*/
+        void copyVMArguments(int start, int end,
+                             Object[] buf, int pos,
+                             byte resolving, Object ifNotPresent) {
+            if (buf.getClass() != Object[].class)  throw new InternalError();
             int i = start, bufi = pos;
-            while (i < end) {
+            if (resolving == BAR_SYMREF) {
+                while (i < end) {
+                    Object x = symCache[i];
+                    if (x == null)  break;
+                    buf[bufi++] = x;
+                    i++;
+                }
+                if (i >= end)  return;
+                // pull in all of the symbols into symCache
+                int[] temp = new int[end - i];
+                int tempi = 0;
+                pool.copyOutBootstrapArgumentsAt(bssIndex,
+                        start, end, temp, 0,
+                        BAR_SYMREF, null, null, false);
+                for (; i < end; i++) {
+                    ConstantDesc x = symCache[i];
+                    int symIndex = temp[tempi++];
+                    if (x == null) {
+                        x = makeConstantFromPool(symIndex);
+                        symCache[i] = x;
+                    }
+                    buf[bufi++] = x;
+                }
+                return;
+            }
+            assert(resolving == BAR_IFPRESENT || resolving == BAR_FORCE);
+            for (; i < end; i++) {
                 Object x = cache[i];
                 if (x == null)  break;
                 buf[bufi++] = unwrapNull(x);
-                i++;
             }
             // give up at first null and grab the rest in one big block
-            if (i >= end)  return i;
-            Object[] temp = new Object[end - i];
+            if (i >= end)  return;
             if (TRACE_METHOD_LINKAGE) {
                 System.out.println("resolving more BSM arguments: " +
-                        Arrays.asList(caller.getSimpleName(), Arrays.toString(indexInfo), i, end));
+                        Arrays.asList(caller().getSimpleName(), bssIndex, indyIndex, i, end));
             }
-            copyOutBootstrapArguments(caller, indexInfo,
-                                      i, end, temp, 0,
-                                      true, null);
-            for (Object x : temp) {
-                x = maybeReBox(x);
-                buf[bufi++] = x;
-                cache[i++] = wrapNull(x);
+            pool.copyOutBootstrapArgumentsAt(bssIndex,
+                    i, end, cache, i,
+                    resolving, null, wrapNull(null), true);
+            for (; i < end; i++) {
+                Object x = cache[i];
+                Object x2 = maybeReBox(x);
+                if (x2 != x)  cache[i] = x = x2;
+                buf[bufi++] = (x == null) ? ifNotPresent : unwrapNull(x);
             }
             if (end < cache.length && cache[end] == null)
-                maybePrefetchIntoCache(end, true);  // try to prefetch
-            return i;
+                maybePrefetchIntoCache(cache, end, true);  // try to prefetch
+        }
+
+        private ConstantDesc makeConstantFromPool(int symIndex) {
+            return pool.getConstantDescAt(symIndex);
         }
 
         private static final int MIN_PF = 4;
-        private void maybePrefetchIntoCache(int i, boolean bulk) {
+        private void maybePrefetchIntoCache(Object[] cache, int i, boolean bulk) {
             int len = cache.length;
             assert(0 <= i && i <= len);
             int pfLimit = i;
@@ -438,137 +602,88 @@
             }
             if (bulk && empty < MIN_PF && pfLimit < len)
                 return;  // not worth the effort
-            prefetchIntoCache(i, pfLimit);
+            prefetchIntoCache(cache, i, pfLimit);
         }
 
-        private void prefetchIntoCache(int i, int pfLimit) {
+        private void prefetchIntoCache(Object[] cache, int i, int pfLimit) {
             if (pfLimit <= i)  return;  // corner case
-            Object[] temp = new Object[pfLimit - i];
             if (TRACE_METHOD_LINKAGE) {
                 System.out.println("prefetching BSM arguments: " +
-                        Arrays.asList(caller.getSimpleName(), Arrays.toString(indexInfo), i, pfLimit));
-            }
-            copyOutBootstrapArguments(caller, indexInfo,
-                                      i, pfLimit, temp, 0,
-                                      false, NOT_PRESENT);
-            for (Object x : temp) {
-                if (x != NOT_PRESENT && cache[i] == null) {
-                    cache[i] = wrapNull(maybeReBox(x));
-                }
-                i++;
+                        Arrays.asList(caller().getSimpleName(), bssIndex, indyIndex, i, pfLimit));
             }
+            pool.copyOutBootstrapArgumentsAt(bssIndex,
+                                             i, pfLimit, cache, i,
+                                             BAR_IFPRESENT, null, wrapNull(null), true);
+        }
+
+        void setArgIndexes(int[] argIndexes) {
+            assert(this.argIndexes == null);
+            this.argIndexes = argIndexes;
         }
     }
 
-    /*non-public*/ static final
-    class PushAdapter {
-        // skeleton for push-mode BSM which wraps a pull-mode BSM:
-        static Object pushToBootstrapMethod(MethodHandle pullModeBSM,
-                                            MethodHandles.Lookup lookup, String name, Object type,
-                                            Object... arguments) throws Throwable {
-            ConstantGroup cons = makeConstantGroup(Arrays.asList(arguments));
-            BootstrapCallInfo bsci = makeBootstrapCallInfo(pullModeBSM, name, type, cons);
-            if (TRACE_METHOD_LINKAGE)
-                System.out.println("pull-mode BSM gets pushed arguments from fake BSCI");
-            return pullModeBSM.invoke(lookup, bsci);
+    /** Canonical DynamicConstantDesc implementation of BootstrapCallInfo.
+     */
+    static final class DCD_BSCI extends WithCache> {
+        private Lookup lookup;
+        private DynamicConstantDesc desc;
+        private final List> args;
+
+        public DCD_BSCI(Lookup lookup, DynamicConstantDesc desc) throws ReflectiveOperationException {
+            super(desc.bootstrapMethod().resolveConstantDesc(lookup),
+                  desc.constantName(),
+                  new TypeView>(desc.constantType(), lookup),
+                  desc.bootstrapArgs());
+            this.lookup = lookup;
+            this.desc = desc;
+            this.args = desc.bootstrapArgsList();
         }
 
-        static final MethodHandle MH_pushToBootstrapMethod;
-        static {
-            final Class THIS_CLASS = PushAdapter.class;
+        @Override Object resolveConstant(int i) throws BootstrapMethodError {
             try {
-                MH_pushToBootstrapMethod = IMPL_LOOKUP
-                    .findStatic(THIS_CLASS, "pushToBootstrapMethod",
-                                MethodType.methodType(Object.class, MethodHandle.class,
-                                        Lookup.class, String.class, Object.class, Object[].class));
-            } catch (Throwable ex) {
-                throw new InternalError(ex);
+                return argumentDesc(i).resolveConstantDesc(lookup);
+            } catch (ReflectiveOperationException ex) {
+                throw new BootstrapMethodError(ex);
             }
         }
-    }
 
-    /*non-public*/ static final
-    class PullAdapter {
-        // skeleton for pull-mode BSM which wraps a push-mode BSM:
-        static Object pullFromBootstrapMethod(MethodHandle pushModeBSM,
-                                              MethodHandles.Lookup lookup,
-                                              BootstrapCallInfo bsci)
-                throws Throwable {
-            int argc = bsci.size();
-            switch (argc) {
-                case 0:
-                    return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType());
-                case 1:
-                    return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
-                            bsci.get(0));
-                case 2:
-                    return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
-                            bsci.get(0), bsci.get(1));
-                case 3:
-                    return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
-                            bsci.get(0), bsci.get(1), bsci.get(2));
-                case 4:
-                    return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
-                            bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3));
-                case 5:
-                    return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
-                            bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3), bsci.get(4));
-                case 6:
-                    return pushModeBSM.invoke(lookup, bsci.invocationName(), bsci.invocationType(),
-                            bsci.get(0), bsci.get(1), bsci.get(2), bsci.get(3), bsci.get(4), bsci.get(5));
-                default:
-                    final int NON_SPREAD_ARG_COUNT = 3;  // (lookup, name, type)
-                    final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT;
-                    if (argc >= MAX_SAFE_SIZE) {
-                        // to be on the safe side, use invokeWithArguments which handles jumbo lists
-                        Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argc];
-                        newargv[0] = lookup;
-                        newargv[1] = bsci.invocationName();
-                        newargv[2] = bsci.invocationType();
-                        bsci.copyConstants(0, argc, newargv, NON_SPREAD_ARG_COUNT);
-                        return pushModeBSM.invokeWithArguments(newargv);
-                    }
-                    MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argc);
-                    MethodHandle typedBSM = pushModeBSM.asType(invocationType);
-                    MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
-                    Object[] argv = new Object[argc];
-                    bsci.copyConstants(0, argc, argv, 0);
-                    return spreader.invokeExact(typedBSM, (Object) lookup, (Object) bsci.invocationName(), bsci.invocationType(), argv);
-                }
+        @Override
+        ConstantDesc findSymbol(int i) {
+            return args.get(i);
         }
+    }
 
-        static final MethodHandle MH_pullFromBootstrapMethod;
-
-        static {
-            final Class THIS_CLASS = PullAdapter.class;
-            try {
-                MH_pullFromBootstrapMethod = IMPL_LOOKUP
-                    .findStatic(THIS_CLASS, "pullFromBootstrapMethod",
-                                MethodType.methodType(Object.class, MethodHandle.class,
-                                        Lookup.class, BootstrapCallInfo.class));
-            } catch (Throwable ex) {
-                throw new InternalError(ex);
-            }
-        }
+    /**
+     * Create a BootstrapCallInfo whose symbolic content is derived from the given
+     * descriptor.  When it resolves constants, it will use the given Lookup object.
+     * @param desc the dynamic constant descriptor
+     * @param lookup a lookup object which is capable of resolving the dynamic constant
+     * @return a BootstrapCallInfo which carries the given
+     * @throws ReflectiveOperationException if the bootstrap method fails to resolve
+     */
+    static BootstrapCallInfo> ofConstantDesc(DynamicConstantDesc desc, Lookup lookup)
+                throws ReflectiveOperationException {
+        return new BootstrapMethodInvoker.DCD_BSCI(lookup, desc);
     }
 
-    /** Given a push-mode BSM (taking one argument) convert it to a
-     *  pull-mode BSM (taking N pre-resolved arguments).
-     *  This method is used when, in fact, the JVM is passing up
-     *  pre-resolved arguments, but the BSM is expecting lazy stuff.
-     *  Or, when goToPushMode is true, do the reverse transform.
-     *  (The two transforms are exactly inverse.)
+    /**
+     * Resolve a dynamic constant, by creating a temporary BootstrapCallInfo for it
+     * and immediately invoking its resolution behavior.  Any cached resolution state
+     * will be lost after the temporary BootstrapCallInfo is discarded.
+     * @param desc the dynamic constant descriptor
+     * @param lookup a lookup object which is capable of resolving the dynamic constant
+     * @return the result of resolving the descriptor in the lookup class
+     * @throws ReflectiveOperationException
      */
-    static MethodHandle pushMePullYou(MethodHandle bsm, boolean goToPushMode) {
-        if (TRACE_METHOD_LINKAGE) {
-            System.out.println("converting BSM of type " + bsm.type() + " to "
-                    + (goToPushMode ? "push mode" : "pull mode"));
-        }
-        assert(isPullModeBSM(bsm) == goToPushMode); // there must be a change
-        if (goToPushMode) {
-            return PushAdapter.MH_pushToBootstrapMethod.bindTo(bsm).withVarargs(true);
-        } else {
-            return PullAdapter.MH_pullFromBootstrapMethod.bindTo(bsm).withVarargs(false);
+    static Object resolveDynamicConstant(DynamicConstantDesc desc, Lookup lookup)
+            throws ReflectiveOperationException {
+        try {
+            return invoke(lookup, ofConstantDesc(desc, lookup));
+        } catch (LinkageError ex) {
+            var roe = ex.getCause();
+            if (roe instanceof ReflectiveOperationException)
+                throw (ReflectiveOperationException) roe;
+            throw ex;
         }
     }
 }
--- old/src/java.base/share/classes/java/lang/invoke/CallSite.java	2018-09-28 11:54:25.000000000 -0700
+++ new/src/java.base/share/classes/java/lang/invoke/CallSite.java	2018-09-28 11:54:24.000000000 -0700
@@ -197,7 +197,7 @@
             throw wrongTargetType(newTarget, oldType);
     }
 
-    private static WrongMethodTypeException wrongTargetType(MethodHandle target, MethodType type) {
+    static WrongMethodTypeException wrongTargetType(Object target, MethodType type) {
         return new WrongMethodTypeException(String.valueOf(target)+" should be of type "+type);
     }
 
@@ -293,48 +293,4 @@
     void setTargetVolatile(MethodHandle newTarget) {
         MethodHandleNatives.setCallSiteTargetVolatile(this, newTarget);
     }
-
-    // this implements the upcall from the JVM, MethodHandleNatives.linkCallSite:
-    static CallSite makeSite(MethodHandle bootstrapMethod,
-                             // Callee information:
-                             String name, MethodType type,
-                             // Extra arguments for BSM, if any:
-                             Object info,
-                             // Caller information:
-                             Class callerClass) {
-        CallSite site;
-        try {
-            Object binding = BootstrapMethodInvoker.invoke(
-                    CallSite.class, bootstrapMethod, name, type, info, callerClass);
-            if (binding instanceof CallSite) {
-                site = (CallSite) binding;
-            } else {
-                // See the "Linking Exceptions" section for the invokedynamic
-                // instruction in JVMS 6.5.
-                // Throws a runtime exception defining the cause that is then
-                // in the "catch (Throwable ex)" a few lines below wrapped in
-                // BootstrapMethodError
-                throw new ClassCastException("CallSite bootstrap method failed to produce an instance of CallSite");
-            }
-            if (!site.getTarget().type().equals(type)) {
-                // See the "Linking Exceptions" section for the invokedynamic
-                // instruction in JVMS 6.5.
-                // Throws a runtime exception defining the cause that is then
-                // in the "catch (Throwable ex)" a few lines below wrapped in
-                // BootstrapMethodError
-                throw wrongTargetType(site.getTarget(), type);
-            }
-        } catch (Error e) {
-            // Pass through an Error, including BootstrapMethodError, any other
-            // form of linkage error, such as IllegalAccessError if the bootstrap
-            // method is inaccessible, or say ThreadDeath/OutOfMemoryError
-            // See the "Linking Exceptions" section for the invokedynamic
-            // instruction in JVMS 6.5.
-            throw e;
-        } catch (Throwable ex) {
-            // Wrap anything else in BootstrapMethodError
-            throw new BootstrapMethodError("CallSite bootstrap method initialization exception", ex);
-        }
-        return site;
-    }
 }
--- old/src/java.base/share/classes/java/lang/invoke/ConstantBootstraps.java	2018-09-28 11:54:27.000000000 -0700
+++ new/src/java.base/share/classes/java/lang/invoke/ConstantBootstraps.java	2018-09-28 11:54:26.000000000 -0700
@@ -26,7 +26,13 @@
 
 import sun.invoke.util.Wrapper;
 
+import java.lang.constant.ConstantDesc;
+import java.util.Arrays;
+
+import static java.lang.invoke.AbstractBootstrapCallInfo.maybeShareArguments;
+import static java.lang.invoke.BootstrapMethodInvoker.invokeCommon;
 import static java.lang.invoke.MethodHandleNatives.mapLookupExceptionToError;
+import static java.lang.invoke.MethodHandles.Lookup;
 import static java.util.Objects.requireNonNull;
 
 /**
@@ -40,34 +46,6 @@
  * @since 11
  */
 public final class ConstantBootstraps {
-    // implements the upcall from the JVM, MethodHandleNatives.linkDynamicConstant:
-    /*non-public*/
-    static Object makeConstant(MethodHandle bootstrapMethod,
-                               // Callee information:
-                               String name, Class type,
-                               // Extra arguments for BSM, if any:
-                               Object info,
-                               // Caller information:
-                               Class callerClass) {
-        // Restrict bootstrap methods to those whose first parameter is Lookup
-        // The motivation here is, in the future, to possibly support BSMs
-        // that do not accept the meta-data of lookup/name/type, thereby
-        // allowing the co-opting of existing methods to be used as BSMs as
-        // long as the static arguments can be passed as method arguments
-        MethodType mt = bootstrapMethod.type();
-        if (mt.parameterCount() < 2 ||
-            !MethodHandles.Lookup.class.isAssignableFrom(mt.parameterType(0))) {
-            throw new BootstrapMethodError(
-                    "Invalid bootstrap method declared for resolving a dynamic constant: " + bootstrapMethod);
-        }
-
-        // BSMI.invoke handles all type checking and exception translation.
-        // If type is not a reference type, the JVM is expecting a boxed
-        // version, and will manage unboxing on the other side.
-        return BootstrapMethodInvoker.invoke(
-                type, bootstrapMethod, name, type, info, callerClass);
-    }
-
     /**
      * Returns a {@code null} object reference for the reference type specified
      * by {@code type}.
@@ -218,12 +196,12 @@
 
 
     /**
-     * Returns the result of invoking a method handle with the provided
-     * arguments.
+     * Returns the result of invoking a bootstrap method handle on static
+     * arguments, but without metadata.
      * 

* This method behaves as if the method handle to be invoked is the result * of adapting the given method handle, via {@link MethodHandle#asType}, to - * adjust the return type to the desired type. + * adjust the return type to {@code invocationType} of the bootstrap call. * * @param lookup unused * @param name unused @@ -247,14 +225,162 @@ requireNonNull(handle); requireNonNull(args); - if (type != handle.type().returnType()) { - // Adjust the return type of the handle to be invoked while - // preserving variable arity if present - handle = handle.asType(handle.type().changeReturnType(type)). - withVarargs(handle.isVarargsCollector()); + Object result = invokeCommon(handle, null, null, null, args, null); + return BootstrapCallInfo.convertResult(type, handle.type().returnType(), result); + } + + /** + * Returns the result of invoking a bootstrap method handle on static + * arguments, but without metadata. + *

+ * This method behaves as if the method handle to be invoked is the result + * of adapting the given method handle, via {@link MethodHandle#asType}, to + * adjust the return type to {@code invocationType} of the bootstrap call. + * + * @param lookup unused + * @param bsci the container of the bootstrap method + * @return the result of invoking the method handle + * @throws WrongMethodTypeException if the handle's method type cannot be + * adjusted to take the given number of arguments, or if the handle's return + * type cannot be adjusted to the desired type + * @throws ClassCastException if an argument or the result produced by + * invoking the handle cannot be converted by reference casting + * @throws Throwable anything thrown by the method handle invocation + */ + // not public until BootstrapCallInfo is public + static Object invoke(MethodHandles.Lookup lookup, BootstrapCallInfo bsci) throws Throwable { + MethodHandle bsm = bsci.bootstrapMethod(); + Object result = invokeCommon(bsm, + null, null, null, + maybeShareArguments(bsci), + bsci.argumentList()); + return BootstrapCallInfo.convertResult((Class) bsci.invocationType(), bsm.type().returnType(), result); + } + + /** + * Returns the result of invoking a bootstrap method handle on static + * arguments, but without metadata. In addition, any arguments of type + * {@code Object} or of any type assignable to {@code ConstantDesc} will + * be provided symbolic references, rather than resolved values, for + * the corresponding static arguments. + *

+ * Finally, any single argument of type {@code Lookup} will be passed the + * lookup object directly, rather than either a symbolic reference or + * resolved argument value. + *

+ * This method behaves as if the method handle to be invoked is the result + * of adapting the given method handle, via {@link MethodHandle#asType}, to + * adjust the return type to {@code invocationType} of the bootstrap call. + * + * @param lookup unused + * @param bsci the container of the bootstrap method + * @return the result of invoking the method handle + * @throws WrongMethodTypeException if the handle's method type cannot be + * adjusted to take the given number of arguments, or if the handle's return + * type cannot be adjusted to the desired type + * @throws ClassCastException if an argument or the result produced by + * invoking the handle cannot be converted by reference casting + * @throws Throwable anything thrown by the method handle invocation + */ + // not public until BootstrapCallInfo is public + static Object symbolic(MethodHandles.Lookup lookup, BootstrapCallInfo bsci) throws Throwable { + MethodHandle bsm = bsci.bootstrapMethod(); + MethodType bsmType = bsm.type(); + boolean passLookup = bsmType.parameterList().contains(Lookup.class); + Object[] argv = new Object[bsci.argumentCount() + (passLookup ? 1 : 0)]; + int pos = 0; + int maxPos = bsmType.parameterCount() - (bsm.isVarargsCollector() ? 1 : 0); + int bargPos = 0; + for (Class ptype : bsmType.parameterList()) { + if (pos == maxPos) break; + if (ptype == Lookup.class && passLookup) { + argv[pos++] = lookup; + passLookup = false; + } else if (isSymbolicArgType(ptype)) { + argv[pos++] = bsci.argumentDesc(bargPos++); + } else { + argv[pos++] = bsci.argument(bargPos++); + } + } + Class ptype = bsmType.lastParameterType().componentType(); + while (pos < argv.length) { + assert(ptype != null); + if (isSymbolicArgType(ptype)) { + argv[pos++] = bsci.argumentDesc(bargPos++); + } else { + argv[pos++] = bsci.argument(bargPos++); + } + } + Object result = invokeCommon(bsm, + null, null, null, + argv, null); + return BootstrapCallInfo.convertResult((Class)bsci.invocationType(), bsmType.returnType(), result); + } + + private static boolean isSymbolicArgType(Class ptype) { + return (ptype == Object.class || ConstantDesc.class.isAssignableFrom(ptype)); + } + + /** + * Trivial method which returns its sole argument, which must be a + * ConstantDesc of some sort. This method is useful as an + * expression-mode bootstrap method with the operation "symbolic". + * + * @param desc the value to be returned + * @param the type of the value to be returned + * @return desc + * @see DynamicConstantDesc#ofSymbolic + */ + public static > T constantDesc(T desc) { + return desc; + } + + /** + * Returns the result of invoking a bootstrap method handle in + * expression mode. The exact behavior of this mode depends on + * the {@code name} string. If the name is {@code "invoke"}, + * then the bootstrap method is applied directly to the arguments, + * as if it were the leading static argument to {@link #invoke}. + * If the name is {@code "symbolic"} then bootstrap arguments + * are extracted as unresolved symbolic references from the + * constant pool and returned. + * Other {@code name} strings are reserved for future use. + *

+ * @apiNote + * This method behaves like the following: + *

{@code
+    String name = bsci.invocationName();
+    switch (name) {
+    case "invoke":          return invoke(lookup, bsci);
+    case "symbolic":        return symbolic(lookup, bsci);
+    }
+     * }
+ *

+ * + * @param lookup must be non-null, otherwise presently unused + * @param bsci bootstrap call information + * @return the result of invoking the bootstrap method handle on appropriate arguments + * @throws WrongMethodTypeException if the handle's method type cannot be + * adjusted to take the given number of arguments, or if the handle's return + * type cannot be adjusted to the desired type + * @throws ClassCastException if an argument or the result produced by + * invoking the handle cannot be converted by reference casting + * @throws IllegalArgumentException if the {@code name} is invalid + * @throws Throwable anything thrown by an action selected by {@code name} + */ + // not public until BootstrapCallInfo is public + static Object linkExpression(MethodHandles.Lookup lookup, + BootstrapCallInfo bsci) throws Throwable { + requireNonNull(lookup); + requireNonNull(bsci); + + String name = bsci.invocationName(); + switch (name) { + case "invoke": return invoke(lookup, bsci); + case "symbolic": return symbolic(lookup, bsci); } - return handle.invokeWithArguments(args); + throw new IllegalArgumentException("invalid name for expression-mode constant: "+bsci); } /** --- old/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java 2018-09-28 11:54:29.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java 2018-09-28 11:54:28.000000000 -0700 @@ -26,13 +26,20 @@ package java.lang.invoke; import jdk.internal.ref.CleanerFactory; +import jdk.internal.reflect.ConstantPool; import sun.invoke.util.Wrapper; +import java.lang.constant.ClassDesc; +import java.lang.constant.MethodTypeDesc; +import java.lang.invoke.AbstractBootstrapCallInfo.TypeView; +import java.lang.invoke.BootstrapMethodInvoker.VM_BSCI; import java.lang.invoke.MethodHandles.Lookup; import java.lang.reflect.Field; +import java.util.Arrays; import static java.lang.invoke.MethodHandleNatives.Constants.*; import static java.lang.invoke.MethodHandleStatics.TRACE_METHOD_LINKAGE; +import static java.lang.invoke.MethodHandleStatics.TRACE_RESOLVE; import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP; /** @@ -66,12 +73,6 @@ static native void setCallSiteTargetNormal(CallSite site, MethodHandle target); static native void setCallSiteTargetVolatile(CallSite site, MethodHandle target); - static native void copyOutBootstrapArguments(Class caller, int[] indexInfo, - int start, int end, - Object[] buf, int pos, - boolean resolve, - Object ifNotAvailable); - /** Represents a context to track nmethod dependencies on CallSite instance target. */ static class CallSiteContext implements Runnable { //@Injected JVM_nmethodBucket* vmdependencies; @@ -136,6 +137,11 @@ REF_newInvokeSpecial = 8, REF_invokeInterface = 9, REF_LIMIT = 10; + + /** + * Flag values which affect the copying of bootstrap static arguments. + */ + static final byte BAR_IFPRESENT = 0, BAR_FORCE = 1, BAR_SYMREF = 2; } static boolean refKindIsValid(int refKind) { @@ -199,6 +205,10 @@ private static native int getNamedCon(int which, Object[] name); static boolean verifyConstants() { + // This code requires ADVERTISE_CON_VALUES to be turned on + // in methodHandles.cpp, which is usually only true in debug builds. + // If that is not the case, then getNamedCon returns nothing and + // the loop body never runs. Object[] box = { null }; for (int i = 0; ; i++) { box[0] = null; @@ -208,12 +218,11 @@ try { Field con = Constants.class.getDeclaredField(name); int jval = con.getInt(null); + if (TRACE_RESOLVE) { + System.out.println("[CON_RESOLVE] " + name + " = " + vmval); + } if (jval == vmval) continue; String err = (name+": JVM has "+vmval+" while Java has "+jval); - if (name.equals("CONV_OP_LIMIT")) { - System.err.println("warning: "+err); - continue; - } throw new InternalError(err); } catch (NoSuchFieldException | IllegalAccessException ex) { String err = (name+": JVM has "+vmval+" which Java does not define"); @@ -225,148 +234,140 @@ return true; } static { - assert(verifyConstants()); + if (TRACE_RESOLVE) { + boolean ok = verifyConstants(); + assert(ok); + } else { + assert(verifyConstants()); + } } // Up-calls from the JVM. // These must NOT be public. /** - * The JVM is linking an invokedynamic instruction. Create a reified call site for it. - */ - static MemberName linkCallSite(Object callerObj, - int indexInCP, - Object bootstrapMethodObj, - Object nameObj, Object typeObj, - Object staticArguments, - Object[] appendixResult) { - MethodHandle bootstrapMethod = (MethodHandle)bootstrapMethodObj; - Class caller = (Class)callerObj; - String name = nameObj.toString().intern(); - MethodType type = (MethodType)typeObj; - if (!TRACE_METHOD_LINKAGE) - return linkCallSiteImpl(caller, bootstrapMethod, name, type, - staticArguments, appendixResult); - return linkCallSiteTracing(caller, bootstrapMethod, name, type, - staticArguments, appendixResult); - } - static MemberName linkCallSiteImpl(Class caller, - MethodHandle bootstrapMethod, - String name, MethodType type, - Object staticArguments, - Object[] appendixResult) { - CallSite callSite = CallSite.makeSite(bootstrapMethod, - name, - type, - staticArguments, - caller); - if (callSite instanceof ConstantCallSite) { - appendixResult[0] = callSite.dynamicInvoker(); - return Invokers.linkToTargetMethod(type); + * All bootstrap method invocations funnel through this single method. + * A large subset of the values of the JVM's BootstrapInfo structure is + * made available here, basically everything that can be easily computed + * without a detectable side effect or exception. The callee is + * invited to call back the JVM for more information. + */ + static Object bootstrapMethodHelper(ConstantPool pool, + int bssIndex, + int indyIndex, + int argc, + MethodHandle bsm, + String name, + Object type, // Class or MethodType or String + int[] argIndexes, + Object[] argValues, + Object request, + Object[] appendixResult) { + assert((appendixResult != null) == (indyIndex != 0)); + assert(request == (indyIndex == 0 ? null : CallSite.class)); + // Special case: If the bsm does not begin with Lookup, *and* + // it is a CallSite request (i.e., legacy from Java 7), then + // forcibly convert the leading type to Lookup. + if (request == CallSite.class && BootstrapMethodInvoker.isExpressionBSM(bsm)) { + bsm = BootstrapMethodInvoker.forceLookupParameter(bsm); + assert(!BootstrapMethodInvoker.isExpressionBSM(bsm)); // properly wrapped + } + // Make a full-power lookup into the CP (a privileged operation). + Lookup lookup = IMPL_LOOKUP.in(pool.getHolder()); + // Process the type: { field, method } x { resolved, string } + TypeView> ftype = null; + TypeView mtype = null; + if (type instanceof MethodType) { + mtype = new TypeView<>((MethodType)type); + } else if (type instanceof Class) { + ftype = new TypeView<>((Class)type); } else { - appendixResult[0] = callSite; - return Invokers.linkToCallSiteMethod(type); - } - } - // Tracing logic: - static MemberName linkCallSiteTracing(Class caller, - MethodHandle bootstrapMethod, - String name, MethodType type, - Object staticArguments, - Object[] appendixResult) { - Object bsmReference = bootstrapMethod.internalMemberName(); - if (bsmReference == null) bsmReference = bootstrapMethod; - String staticArglist = staticArglistForTrace(staticArguments); - System.out.println("linkCallSite "+caller.getName()+" "+ - bsmReference+" "+ - name+type+"/"+staticArglist); - try { - MemberName res = linkCallSiteImpl(caller, bootstrapMethod, name, type, - staticArguments, appendixResult); - System.out.println("linkCallSite => "+res+" + "+appendixResult[0]); - return res; - } catch (Throwable ex) { - ex.printStackTrace(); // print now in case exception is swallowed - System.out.println("linkCallSite => throw "+ex); - throw ex; + // JVM was not able to resolve the type. Keep the ball rolling. + String desc = (String) type; + if (desc.startsWith("(")) + mtype = new TypeView<>(MethodTypeDesc.ofDescriptor(desc), lookup); + else + ftype = new TypeView<>(ClassDesc.ofDescriptor(desc), lookup); + } + // If VM is pushing up argument values, use them. Otherwise make a fresh buffer. + if (argValues == null) { + argValues = new Object[argc]; + } else { + assert(argValues.length == argc); + // The JVM resolves CONSTANT_Integer CP items without calling Integer.valueOf. + BootstrapMethodInvoker.maybeReBoxElements(argValues); + } + // Make a BSCI wrapper for all of this state. + VM_BSCI bsci; + // (The following if/then/else is required to make the generic types match up.) + if (ftype != null) + bsci = new VM_BSCI<>(pool, bssIndex, indyIndex, bsm, name, ftype, argValues); + else + bsci = new VM_BSCI<>(pool, bssIndex, indyIndex, bsm, name, mtype, argValues); + // If VM is passing up argument indexes, accept that also. + if (argIndexes != null) bsci.setArgIndexes(argIndexes); + // Finish the job by invoking the fresh BSCI under the Lookup: + if (!TRACE_METHOD_LINKAGE) { + Object res = BootstrapMethodInvoker.invoke(lookup, bsci); + if (appendixResult == null) { + return res; + } + return encodeBindingViaAppendix(bsci, res, appendixResult); } - } - - // this implements the upcall from the JVM, MethodHandleNatives.linkDynamicConstant: - static Object linkDynamicConstant(Object callerObj, - int indexInCP, - Object bootstrapMethodObj, - Object nameObj, Object typeObj, - Object staticArguments) { - MethodHandle bootstrapMethod = (MethodHandle)bootstrapMethodObj; - Class caller = (Class)callerObj; - String name = nameObj.toString().intern(); - Class type = (Class)typeObj; - if (!TRACE_METHOD_LINKAGE) - return linkDynamicConstantImpl(caller, bootstrapMethod, name, type, staticArguments); - return linkDynamicConstantTracing(caller, bootstrapMethod, name, type, staticArguments); - } - - static Object linkDynamicConstantImpl(Class caller, - MethodHandle bootstrapMethod, - String name, Class type, - Object staticArguments) { - return ConstantBootstraps.makeConstant(bootstrapMethod, name, type, staticArguments, caller); - } - - private static String staticArglistForTrace(Object staticArguments) { - if (staticArguments instanceof Object[]) - return "BSA="+java.util.Arrays.asList((Object[]) staticArguments); - if (staticArguments instanceof int[]) - return "BSA@"+java.util.Arrays.toString((int[]) staticArguments); - if (staticArguments == null) - return "BSA0=null"; - return "BSA1="+staticArguments; - } - // Tracing logic: - static Object linkDynamicConstantTracing(Class caller, - MethodHandle bootstrapMethod, - String name, Class type, - Object staticArguments) { - Object bsmReference = bootstrapMethod.internalMemberName(); - if (bsmReference == null) bsmReference = bootstrapMethod; - String staticArglist = staticArglistForTrace(staticArguments); - System.out.println("linkDynamicConstant "+caller.getName()+" "+ - bsmReference+" "+ - name+type+"/"+staticArglist); + // Tracing logic follows: + String linkWhat = "[TRACE_METHOD_LINKAGE] link" + + ((request == null) ? "DynamicConstant" + : request instanceof Class ? ((Class)request).getSimpleName() + : request); + System.out.println(linkWhat+" "+bsci.toString()); try { - Object res = linkDynamicConstantImpl(caller, bootstrapMethod, name, type, staticArguments); - System.out.println("linkDynamicConstantImpl => "+res); - return res; + Object res = BootstrapMethodInvoker.invoke(lookup, bsci); + if (appendixResult == null) { + System.out.println(linkWhat+" => "+res); return res; + } + MemberName mem = encodeBindingViaAppendix(bsci, res, appendixResult); + System.out.println(linkWhat+" => "+res+" => "+mem+" + "+appendixResult[0]); + return mem; } catch (Throwable ex) { ex.printStackTrace(); // print now in case exception is swallowed - System.out.println("linkDynamicConstant => throw "+ex); + System.out.println(linkWhat+" => throw "+ex); throw ex; } } - /** The JVM is requesting pull-mode bootstrap when it provides - * a tuple of the form int[]{ argc, vmindex }. - * The BSM is expected to call back to the JVM using the caller - * class and vmindex to resolve the static arguments. - */ - static boolean staticArgumentsPulled(Object staticArguments) { - return staticArguments instanceof int[]; - } - - /** A BSM runs in pull-mode if and only if its sole arguments - * are (Lookup, BootstrapCallInfo), or can be converted pairwise - * to those types, and it is not of variable arity. - * Excluding error cases, we can just test that the arity is a constant 2. - * - * NOTE: This method currently returns false, since pulling is not currently - * exposed to a BSM. When pull mode is supported the method block will be - * replaced with currently commented out code. - */ - static boolean isPullModeBSM(MethodHandle bsm) { - return false; -// return bsm.type().parameterCount() == 2 && !bsm.isVarargsCollector(); + // Follow-up step to bootstrapMethodHelper, when an appendix was provided. + // The basic result was a specific behavior; factor it (for the VM's sake) + // into a generic chunk of argument shuffling plus a datum. + static MemberName encodeBindingViaAppendix(BootstrapCallInfo bsci, Object binding, Object[] appendixResult) { + MethodType type = (MethodType) bsci.invocationType(); + MemberName result; + MethodType resultType; + Object appendix; + if (binding instanceof ConstantCallSite) { + ConstantCallSite ccs = (ConstantCallSite) binding; + result = Invokers.linkToTargetMethod(type); + appendix = ccs.getTarget(); + resultType = ((MethodHandle) appendix).type(); + } else if (binding instanceof CallSite) { + result = Invokers.linkToCallSiteMethod(type); + appendix = binding; + resultType = ((CallSite) appendix).type(); + } else if (binding instanceof MethodHandle) { + result = Invokers.linkToTargetMethod(type); + appendix = binding; + resultType = ((MethodHandle) appendix).type(); + } else { + throw new InternalError("should not get here: "+binding+":"+binding.getClass().getName()); + } + // Double-check the function type also. + if (!resultType.equals(type)) { + throw new InternalError("should not get here: "+binding+type); + } + // After type checking, give the JVM the two bits it needs + // to implement the call site: A behavior method, and a cookie. + appendixResult[0] = appendix; + return result; } /** @@ -473,6 +474,7 @@ } throw new LinkageError("no such method "+defc.getName()+"."+name+type); } + private static MethodType fixMethodType(Class callerClass, Object type) { if (type instanceof MethodType) return (MethodType) type; --- old/src/java.base/share/classes/jdk/internal/reflect/ConstantPool.java 2018-09-28 11:54:31.000000000 -0700 +++ new/src/java.base/share/classes/jdk/internal/reflect/ConstantPool.java 2018-09-28 11:54:30.000000000 -0700 @@ -25,49 +25,63 @@ package jdk.internal.reflect; -import java.lang.reflect.*; -import java.util.Set; +import jdk.internal.vm.annotation.Stable; + +import java.lang.constant.*; +import java.lang.constant.DirectMethodHandleDesc.Kind; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.util.Arrays; /** Provides reflective access to the constant pools of classes. Currently this is needed to provide reflective access to annotations - but may be used by other internal subsystems in the future. */ + and bootstrap methods + but may be used by other internal subsystems in the future. +

+ When applying this API to internal subsystems, take special account + of interactions with the JVMTI class redefinition features. + If a class reflects on its constant pool, and then is redefined to + have a different constant pool, the data reported by the reflected + constant pool may become out of date. + */ public class ConstantPool { // Number of entries in this constant pool (= maximum valid constant pool index) - public int getSize() { return getSize0 (constantPoolOop); } - public Class getClassAt (int index) { return getClassAt0 (constantPoolOop, index); } - public Class getClassAtIfLoaded (int index) { return getClassAtIfLoaded0 (constantPoolOop, index); } + public int getSize() { return getSize1(); } + // Class holding this CP + public Class getHolder() { return getHolder1(); } + public Class getClassAt (int index) { return (Class) getRefAt(index, Tag.CLASS, true); } + + public Class getClassAtIfLoaded (int index) { return (Class) getRefAt(index, Tag.CLASS, false); } // Returns a class reference index for a method or a field. - public int getClassRefIndexAt(int index) { - return getClassRefIndexAt0(constantPoolOop, index); - } + public int getClassRefIndexAt(int index) { return getWordAt1(index, 0); } // Returns either a Method or Constructor. // Static initializers are returned as Method objects. - public Member getMethodAt (int index) { return getMethodAt0 (constantPoolOop, index); } - public Member getMethodAtIfLoaded(int index) { return getMethodAtIfLoaded0(constantPoolOop, index); } - public Field getFieldAt (int index) { return getFieldAt0 (constantPoolOop, index); } - public Field getFieldAtIfLoaded (int index) { return getFieldAtIfLoaded0 (constantPoolOop, index); } + public Member getMethodAt (int index) { return (Member) getRefAt(index, Tag.METHODREF, true); } + public Member getMethodAtIfLoaded(int index) { return (Member) getRefAt(index, Tag.METHODREF, false); } + public Field getFieldAt (int index) { return (Field) getRefAt(index, Tag.FIELDREF, true); } + public Field getFieldAtIfLoaded (int index) { return (Field) getRefAt(index, Tag.FIELDREF, false); } // Fetches the class name, member (field, method or interface // method) name, and type descriptor as an array of three Strings - public String[] getMemberRefInfoAt (int index) { return getMemberRefInfoAt0 (constantPoolOop, index); } - // Returns a name and type reference index for a method, a field or an invokedynamic. - public int getNameAndTypeRefIndexAt(int index) { - return getNameAndTypeRefIndexAt0(constantPoolOop, index); + public String[] getMemberRefInfoAt (int index) { + return new String[] { getUtf8At(index, 2), getUtf8At(index, 3), getUtf8At(index, 4) }; } + + // Returns a name and type reference index for a method, a field or an invokedynamic. + public int getNameAndTypeRefIndexAt(int index) { return getWordAt1(index, 1); } // Fetches the name and type from name_and_type index as an array of two Strings public String[] getNameAndTypeRefInfoAt(int index) { - return getNameAndTypeRefInfoAt0(constantPoolOop, index); - } - public int getIntAt (int index) { return getIntAt0 (constantPoolOop, index); } - public long getLongAt (int index) { return getLongAt0 (constantPoolOop, index); } - public float getFloatAt (int index) { return getFloatAt0 (constantPoolOop, index); } - public double getDoubleAt (int index) { return getDoubleAt0 (constantPoolOop, index); } - public String getStringAt (int index) { return getStringAt0 (constantPoolOop, index); } - public String getUTF8At (int index) { return getUTF8At0 (constantPoolOop, index); } - public Tag getTagAt(int index) { - return Tag.valueOf(getTagAt0(constantPoolOop, index)); + return (String[]) getRefAt(index, Tag.NAMEANDTYPE, true); } + public int getIntAt (int index) { return getIntAt1 (index); } + public long getLongAt (int index) { return getLongAt1 (index); } + public float getFloatAt (int index) { return getFloatAt1 (index); } + public double getDoubleAt (int index) { return getDoubleAt1 (index); } + public String getStringAt (int index) { return (String) getRefAt(index, Tag.STRING, true); } + public String getUTF8At (int index) { return (String) getRefAt1(index, 0, BAR_SYMREF, null); } + public Tag getTagAt (int index) { return Tag.valueOf(getCachedTagAt(index)); } + public static enum Tag { UTF8(1), INTEGER(3), @@ -82,7 +96,8 @@ NAMEANDTYPE(12), METHODHANDLE(15), METHODTYPE(16), - INVOKEDYNAMIC(18), + DYNAMIC(17), // condy + INVOKEDYNAMIC(18), // indy INVALID(0); private final int tagCode; @@ -91,42 +106,196 @@ this.tagCode = tagCode; } - private static Tag valueOf(byte v) { - for (Tag tag : Tag.values()) { - if (tag.tagCode == v) { - return tag; - } + private static @Stable final Tag TAG_TABLE[] = new Tag[20]; + static { + for (Tag tag : values()) { + assert(TAG_TABLE[tag.tagCode] == null); // unique + TAG_TABLE[tag.tagCode] = tag; } + } + private static Tag valueOf(byte v) { + Tag tag = (v >= 0 && v < TAG_TABLE.length) ? TAG_TABLE[v] : null; + if (tag != null) return tag; throw new IllegalArgumentException("Unknown constant pool tag code " + v); } } + + public Object getConstantAt(int index) { + return getRefAt1(index, -1, BAR_FORCE, null); + } + public Object getConstantAt(int index, Object ifNotPresent) { + return getRefAt1(index, -1, BAR_IFPRESENT, ifNotPresent); + } + + private static final byte BAR_IFPRESENT = 0, BAR_FORCE = 1, BAR_SYMREF = 2; + + public ConstantDesc getConstantDescAt(int index) { + Tag tag = getTagAt(index); + switch (tag) { + case UTF8: return getUTF8At(index); + case INTEGER: return getIntAt(index); + case LONG: return getLongAt(index); + case FLOAT: return getFloatAt(index); + case DOUBLE: return getDoubleAt(index); + case CLASS: return classDescOf(getUTF8At(index)); + case STRING: return getStringAt(index); + //case FIELDREF: + //case METHODREF: + //case INTERFACEMETHODREF: + //case NAMEANDTYPE: + case METHODHANDLE: + { + int w0 = getWordAt1(index, 0); // refKind + int w1 = getWordAt1(index, 1); // member + boolean isInterface = getTagAt(w1) == Tag.INTERFACEMETHODREF; + Kind kind = Kind.valueOf(w0, isInterface); + // Note: Even if isInterface is true Kind.valueOf may discard it. + ClassDesc clazz = classDescOf(getUtf8At(w1, 2)); + return MethodHandleDesc.of(kind, clazz, getUtf8At(w1, 3), getUtf8At(w1, 4)); + } + case METHODTYPE: + return MethodTypeDesc.ofDescriptor(getUTF8At(index)); + //case INVOKEDYNAMIC: + case DYNAMIC: + return (DynamicConstantDesc) getBootstrappedEntryAt(index); + } + throw new IllegalArgumentException("no ConstantDesc representation for "+tag+" at CP["+index+"]"); + } + + public DynamicCallSiteDesc getDynamicCallSiteDescAt(int index) { + checkTag(index, Tag.INVOKEDYNAMIC); + return (DynamicCallSiteDesc) getBootstrappedEntryAt(index); + } + + /** + * Get bootstrap arguments for the indy or condy at the given index. + * @param index CP index of the selected indy or condy constant + * @param start which bootstrap argument to start copying + * @param end which bootstrap argument (exclusive) to stop copying + * @param buf where to put the copied arguments (must be int[] for BAR_SYMREF, Object[] for other modes) + * @param pos where to store arguments into the buf + * @param resolving resolution mode: 0=BAR_IFPRESENT, 1=BAR_FORCE, 2=BAR_SYMREF + * @param ifNotPresent in BAR_IFPRESENT mode, sentinel to store for values not yet resolved + * @param ifNullConstant in BAR_IFPRESENT or BAR_FORCE mode, sentinel to store for an actual {@code null} + * @param skipNonNull BAR_IFPRESENT or BAR_FORCE mode, do not store over elements of buf which are non-{@code null} + */ + public void copyOutBootstrapArgumentsAt(int index, + int start, int end, + Object buf, int pos, + byte resolving, + Object ifNotPresent, + Object ifNullConstant, + boolean skipNonNull) { + if (start < 0 || end < start) throw new IllegalArgumentException(); + copyOutRefsAt1(index, start + BSS_HEADER_WORDS, end + BSS_HEADER_WORDS, + buf, pos, resolving, ifNotPresent, ifNullConstant, skipNonNull); + } + //--------------------------------------------------------------------------- // Internals only below this point // - static { - Reflection.registerFieldsToFilter(ConstantPool.class, Set.of("constantPoolOop")); + private @Stable byte[] tags; + private byte getCachedTagAt(int index) { + if (tags == null) tags = getTags1(); + if (index >= 0 && index < tags.length) { + return tags[index]; + } + // constant pool can grow over time + return getTagAt1(index); + } + + private static final int + BSS_HEADER_WORDS = 6, // { attrIndex, natIndex, bsm, name, type, argc } + BSS_HEADER_BSM = 2, BSS_HEADER_NAME = 3, BSS_HEADER_TYPE = 4, BSS_HEADER_ARGC = 5; + private Object getBootstrappedEntryAt(int index) { + int argc = getWordAt1(index, BSS_HEADER_ARGC); + int[] argIndexes = copyOutWordsAt(index, BSS_HEADER_WORDS, BSS_HEADER_WORDS+argc); + int bsmIndex = getWordAt1(index, BSS_HEADER_BSM); + DirectMethodHandleDesc bsm = (DirectMethodHandleDesc) getConstantDescAt(bsmIndex); + String name = (String) getRefAt1(index, BSS_HEADER_NAME, BAR_FORCE, null); + String type = (String) getRefAt1(index, BSS_HEADER_TYPE, BAR_FORCE, null); + ConstantDesc[] args = new ConstantDesc[argc]; + for (int i = 0; i < argc; i++) { + args[i] = getConstantDescAt(argIndexes[i]); + } + if (getTagAt(index) == Tag.DYNAMIC) + return DynamicConstantDesc.ofNamed(bsm, name, ClassDesc.ofDescriptor(type), args); + else + return DynamicCallSiteDesc.of(bsm, name, MethodTypeDesc.ofDescriptor(type), args); + } + + + private ClassDesc classDescOf(String name) { + return ClassDesc.of(name.replace('/', '.')); } - // HotSpot-internal constant pool object (set by the VM, name known to the VM) - private Object constantPoolOop; + private Object getRefAt(int index, Tag tag, boolean forceResolution) { + return getRefAt1(index, -1, forceResolution ? BAR_FORCE : BAR_IFPRESENT, null); + } + + private int getWordAt(int index, Tag tag, int word) { + checkTag(index, tag); + return getWordAt1(index, word); + } + + private void checkTag(int index, Tag needTag) { + Tag haveTag = getTagAt(index); + if (haveTag == needTag) return; + switch (needTag) { + case METHODREF: + if (haveTag == Tag.INTERFACEMETHODREF) return; + break; + } + throw new IllegalArgumentException("expected "+needTag+" but found "+haveTag); + } + + private String getUtf8At(int index, int word) { + return (String) getRefAt1(index, word, BAR_SYMREF, null); + } + + + private native Class getHolder1 (); + private native int getSize1 (); + private native int getWordCountAt1 (int index); + private native int getWordAt1 (int index, int word); + private native Object getRefAt1 (int index, int word, byte resolving, Object ifNotPresent); + private native int getIntAt1 (int index); + private native long getLongAt1 (int index); + private native float getFloatAt1 (int index); + private native double getDoubleAt1 (int index); + private native byte getTagAt1 (int index); + private native byte[] getTags1 (); + + /** + * @param index which CP entry to process + * @param startWord which word to start copying + * @param endWord which word (exclusive) to stop copying + * @param buf where to put the copied arguments (must be int[] for BAR_SYMREF, Object[] for other modes) + * @param bufPos where to store arguments into the buf + * @param resolving resolution mode: 0=BAR_IFPRESENT, 1=BAR_FORCE, 2=BAR_SYMREF + * @param ifNotPresent in BAR_IFPRESENT mode, sentinel to store for values not yet resolved + * @param ifNullConstant in BAR_IFPRESENT or BAR_FORCE mode, sentinel to store for an actual {@code null} + * @param skipNonNull BAR_IFPRESENT or BAR_FORCE mode, do not store over elements of buf which are non-{@code null} + */ + private native void copyOutRefsAt1(int index, + int startWord, int endWord, + Object buf, int bufPos, // destination + byte resolving, + Object ifNotPresent, + Object ifNullConstant, + boolean skipNonNull); + + private int[] copyOutWordsAt(int index, int startWord, int endWord) { + int[] buf = new int[endWord - startWord]; + copyOutRefsAt1(index, startWord, endWord, buf, 0, BAR_SYMREF, null, null, false); + return buf; + } + private Object[] copyOutRefsAt(int index, int startWord, int endWord, boolean forceResolution, + Object ifNotPresent, Object ifNullConstant) { + Object[] buf = new Object[endWord - startWord]; + copyOutRefsAt1(index, startWord, endWord, buf, 0, forceResolution ? BAR_FORCE : BAR_IFPRESENT, ifNotPresent, ifNullConstant, false); + return buf; + } - private native int getSize0 (Object constantPoolOop); - private native Class getClassAt0 (Object constantPoolOop, int index); - private native Class getClassAtIfLoaded0 (Object constantPoolOop, int index); - private native int getClassRefIndexAt0 (Object constantPoolOop, int index); - private native Member getMethodAt0 (Object constantPoolOop, int index); - private native Member getMethodAtIfLoaded0(Object constantPoolOop, int index); - private native Field getFieldAt0 (Object constantPoolOop, int index); - private native Field getFieldAtIfLoaded0 (Object constantPoolOop, int index); - private native String[] getMemberRefInfoAt0 (Object constantPoolOop, int index); - private native int getNameAndTypeRefIndexAt0(Object constantPoolOop, int index); - private native String[] getNameAndTypeRefInfoAt0(Object constantPoolOop, int index); - private native int getIntAt0 (Object constantPoolOop, int index); - private native long getLongAt0 (Object constantPoolOop, int index); - private native float getFloatAt0 (Object constantPoolOop, int index); - private native double getDoubleAt0 (Object constantPoolOop, int index); - private native String getStringAt0 (Object constantPoolOop, int index); - private native String getUTF8At0 (Object constantPoolOop, int index); - private native byte getTagAt0 (Object constantPoolOop, int index); } --- old/src/java.base/share/native/libjava/ConstantPool.c 2018-09-28 11:54:33.000000000 -0700 +++ new/src/java.base/share/native/libjava/ConstantPool.c 2018-09-28 11:54:32.000000000 -0700 @@ -26,111 +26,78 @@ #include "jvm.h" #include "jdk_internal_reflect_ConstantPool.h" -JNIEXPORT jint JNICALL Java_jdk_internal_reflect_ConstantPool_getSize0 -(JNIEnv *env, jobject unused, jobject jcpool) +JNIEXPORT jclass JNICALL Java_jdk_internal_reflect_ConstantPool_getHolder1 +(JNIEnv *env, jobject jcpool) { - return JVM_ConstantPoolGetSize(env, unused, jcpool); + return JVM_ConstantPool1GetHolder(env, jcpool); } -JNIEXPORT jclass JNICALL Java_jdk_internal_reflect_ConstantPool_getClassAt0 -(JNIEnv *env, jobject unused, jobject jcpool, jint index) +JNIEXPORT jint JNICALL Java_jdk_internal_reflect_ConstantPool_getSize1 +(JNIEnv *env, jobject jcpool) { - return JVM_ConstantPoolGetClassAt(env, unused, jcpool, index); + return JVM_ConstantPool1GetSize(env, jcpool); } -JNIEXPORT jclass JNICALL Java_jdk_internal_reflect_ConstantPool_getClassAtIfLoaded0 -(JNIEnv *env, jobject unused, jobject jcpool, jint index) +JNIEXPORT jint JNICALL Java_jdk_internal_reflect_ConstantPool_getWordCountAt1 +(JNIEnv *env, jobject jcpool, jint index) { - return JVM_ConstantPoolGetClassAtIfLoaded(env, unused, jcpool, index); + return JVM_ConstantPool1GetWordCountAt(env, jcpool, index); } -JNIEXPORT jint JNICALL Java_jdk_internal_reflect_ConstantPool_getClassRefIndexAt0 -(JNIEnv *env, jobject unused, jobject jcpool, jint index) +JNIEXPORT jint JNICALL Java_jdk_internal_reflect_ConstantPool_getWordAt1 +(JNIEnv *env, jobject jcpool, jint index, jint word) { - return JVM_ConstantPoolGetClassRefIndexAt(env, unused, jcpool, index); + return JVM_ConstantPool1GetWordAt(env, jcpool, index, word); } -JNIEXPORT jobject JNICALL Java_jdk_internal_reflect_ConstantPool_getMethodAt0 -(JNIEnv *env, jobject unused, jobject jcpool, jint index) +JNIEXPORT jobject JNICALL Java_jdk_internal_reflect_ConstantPool_getRefAt1 +(JNIEnv *env, jobject jcpool, jint index, jint word, jbyte resolving, jobject if_not_present) { - return JVM_ConstantPoolGetMethodAt(env, unused, jcpool, index); + return JVM_ConstantPool1GetRefAt(env, jcpool, index, word, resolving, if_not_present); } -JNIEXPORT jobject JNICALL Java_jdk_internal_reflect_ConstantPool_getMethodAtIfLoaded0 -(JNIEnv *env, jobject unused, jobject jcpool, jint index) +JNIEXPORT void JNICALL Java_jdk_internal_reflect_ConstantPool_copyOutRefsAt1 +(JNIEnv *env, jobject jcpool, jint index, jint start_word, jint end_word, + jobject buf, jint buf_pos, jbyte resolving, + jobject if_not_present, jobject if_null_constant, jboolean skip_non_null) { - return JVM_ConstantPoolGetMethodAtIfLoaded(env, unused, jcpool, index); + JVM_ConstantPool1CopyOutRefsAt(env, jcpool, index, start_word, end_word, + buf, buf_pos, resolving, if_not_present, if_null_constant, skip_non_null); } -JNIEXPORT jobject JNICALL Java_jdk_internal_reflect_ConstantPool_getFieldAt0 -(JNIEnv *env, jobject unused, jobject jcpool, jint index) +JNIEXPORT jint JNICALL Java_jdk_internal_reflect_ConstantPool_getIntAt1 +(JNIEnv *env, jobject jcpool, jint index) { - return JVM_ConstantPoolGetFieldAt(env, unused, jcpool, index); + return JVM_ConstantPool1GetIntAt(env, jcpool, index); } -JNIEXPORT jobject JNICALL Java_jdk_internal_reflect_ConstantPool_getFieldAtIfLoaded0 -(JNIEnv *env, jobject unused, jobject jcpool, jint index) +JNIEXPORT jlong JNICALL Java_jdk_internal_reflect_ConstantPool_getLongAt1 +(JNIEnv *env, jobject jcpool, jint index) { - return JVM_ConstantPoolGetFieldAtIfLoaded(env, unused, jcpool, index); + return JVM_ConstantPool1GetLongAt(env, jcpool, index); } -JNIEXPORT jobjectArray JNICALL Java_jdk_internal_reflect_ConstantPool_getMemberRefInfoAt0 -(JNIEnv *env, jobject unused, jobject jcpool, jint index) +JNIEXPORT jfloat JNICALL Java_jdk_internal_reflect_ConstantPool_getFloatAt1 +(JNIEnv *env, jobject jcpool, jint index) { - return JVM_ConstantPoolGetMemberRefInfoAt(env, unused, jcpool, index); + return JVM_ConstantPool1GetFloatAt(env, jcpool, index); } -JNIEXPORT jint JNICALL Java_jdk_internal_reflect_ConstantPool_getNameAndTypeRefIndexAt0 -(JNIEnv *env, jobject unused, jobject jcpool, jint index) +JNIEXPORT jdouble JNICALL Java_jdk_internal_reflect_ConstantPool_getDoubleAt1 +(JNIEnv *env, jobject jcpool, jint index) { - return JVM_ConstantPoolGetNameAndTypeRefIndexAt(env, unused, jcpool, index); + return JVM_ConstantPool1GetDoubleAt(env, jcpool, index); } -JNIEXPORT jobjectArray JNICALL Java_jdk_internal_reflect_ConstantPool_getNameAndTypeRefInfoAt0 -(JNIEnv *env, jobject unused, jobject jcpool, jint index) +JNIEXPORT jbyte JNICALL Java_jdk_internal_reflect_ConstantPool_getTagAt1 +(JNIEnv *env, jobject jcpool, jint index) { - return JVM_ConstantPoolGetNameAndTypeRefInfoAt(env, unused, jcpool, index); + return JVM_ConstantPool1GetTagAt(env, jcpool, index); } -JNIEXPORT jint JNICALL Java_jdk_internal_reflect_ConstantPool_getIntAt0 -(JNIEnv *env, jobject unused, jobject jcpool, jint index) +JNIEXPORT jbyteArray JNICALL Java_jdk_internal_reflect_ConstantPool_getTags1 +(JNIEnv *env, jobject jcpool) { - return JVM_ConstantPoolGetIntAt(env, unused, jcpool, index); -} - -JNIEXPORT jlong JNICALL Java_jdk_internal_reflect_ConstantPool_getLongAt0 -(JNIEnv *env, jobject unused, jobject jcpool, jint index) -{ - return JVM_ConstantPoolGetLongAt(env, unused, jcpool, index); -} - -JNIEXPORT jfloat JNICALL Java_jdk_internal_reflect_ConstantPool_getFloatAt0 -(JNIEnv *env, jobject unused, jobject jcpool, jint index) -{ - return JVM_ConstantPoolGetFloatAt(env, unused, jcpool, index); -} - -JNIEXPORT jdouble JNICALL Java_jdk_internal_reflect_ConstantPool_getDoubleAt0 -(JNIEnv *env, jobject unused, jobject jcpool, jint index) -{ - return JVM_ConstantPoolGetDoubleAt(env, unused, jcpool, index); -} - -JNIEXPORT jstring JNICALL Java_jdk_internal_reflect_ConstantPool_getStringAt0 -(JNIEnv *env, jobject unused, jobject jcpool, jint index) -{ - return JVM_ConstantPoolGetStringAt(env, unused, jcpool, index); -} - -JNIEXPORT jstring JNICALL Java_jdk_internal_reflect_ConstantPool_getUTF8At0 -(JNIEnv *env, jobject unused, jobject jcpool, jint index) -{ - return JVM_ConstantPoolGetUTF8At(env, unused, jcpool, index); -} - -JNIEXPORT jbyte JNICALL Java_jdk_internal_reflect_ConstantPool_getTagAt0 -(JNIEnv *env, jobject unused, jobject jcpool, jint index) -{ - return JVM_ConstantPoolGetTagAt(env, unused, jcpool, index); + return JVM_ConstantPool1GetTags(env, jcpool); } --- old/test/jdk/java/lang/constant/boottest/java.base/java/lang/constant/ConstantUtilsTest.java 2018-09-28 11:54:35.000000000 -0700 +++ new/test/jdk/java/lang/constant/boottest/java.base/java/lang/constant/ConstantUtilsTest.java 2018-09-28 11:54:34.000000000 -0700 @@ -68,28 +68,4 @@ } } } - - public void testSymbolizeHelper() { - DirectMethodHandleDesc mh = MethodHandleDesc.of(DirectMethodHandleDesc.Kind.VIRTUAL, ConstantDescs.CR_String, "isEmpty", "()Z"); - try { - ConstantUtils.symbolizeHelper(mh, null, ""); - fail(""); - } catch (NullPointerException e) { - // good - } - - try { - ConstantUtils.symbolizeHelper(null, ConstantDescs.CR_ClassDesc, ""); - fail(""); - } catch (NullPointerException e) { - // good - } - - try { - ConstantUtils.symbolizeHelper(null, null, ""); - fail(""); - } catch (NullPointerException e) { - // good - } - } } --- old/test/jdk/java/lang/invoke/condy/BootstrapMethodJumboArgsTest.java 2018-09-28 11:54:36.000000000 -0700 +++ new/test/jdk/java/lang/invoke/condy/BootstrapMethodJumboArgsTest.java 2018-09-28 11:54:36.000000000 -0700 @@ -28,7 +28,6 @@ * @library /lib/testlibrary/bytecode /java/lang/invoke/common * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper * @run testng BootstrapMethodJumboArgsTest - * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 BootstrapMethodJumboArgsTest */ import jdk.experimental.bytecode.PoolHelper; @@ -86,6 +85,17 @@ } } + // Expression-mode versions of bsm* + static Object bsmZeroExpr(Object... args) { + return bsmZero(null, null, null, args); + } + static Object bsmOneExpr(Object first, Object... args) { + return bsmOne(null, null, null, first, args); + } + static Object bsmTwoExpr(Object first, Object second, Object... args) { + return bsmTwo(null, null, null, first, second, args); + } + static void manyStaticStrings(String[] args, PoolHelper.StaticArgListBuilder staticArgs) { for (String s : args) { staticArgs.add(s); @@ -128,6 +138,41 @@ } @Test + public void testCondyWithJumboArgsWithoutMetaData() throws Throwable { + String[] expected = IntStream.range(0, 1000).mapToObj(Integer::toString).toArray(String[]::new); + + { + MethodHandle mh = InstructionHelper.ldcDynamicConstant( + L, "invoke", Object[].class, + "bsmZeroExpr", methodType(Object.class, Object[].class), + S -> manyStaticStrings(expected, S)); + + Object[] actual = (Object[]) mh.invoke(); + Assert.assertEquals(actual, expected); + } + + { + MethodHandle mh = InstructionHelper.ldcDynamicConstant( + L, "invoke", Object[].class, + "bsmOneExpr", methodType(Object.class, Object.class, Object[].class), + S -> manyStaticStrings(expected, S)); + + Object[] actual = (Object[]) mh.invoke(); + Assert.assertEquals(actual, expected); + } + + { + MethodHandle mh = InstructionHelper.ldcDynamicConstant( + L, "invoke", Object[].class, + "bsmTwoExpr", methodType(Object.class, Object.class, Object.class, Object[].class), + S -> manyStaticStrings(expected, S)); + + Object[] actual = (Object[]) mh.invoke(); + Assert.assertEquals(actual, expected); + } + } + + @Test public void testIndyWithJumboArgs() throws Throwable { String[] expected = IntStream.range(0, 1000).mapToObj(Integer::toString).toArray(String[]::new); --- old/test/jdk/java/lang/invoke/condy/CondyBSMException.java 2018-09-28 11:54:38.000000000 -0700 +++ new/test/jdk/java/lang/invoke/condy/CondyBSMException.java 2018-09-28 11:54:38.000000000 -0700 @@ -28,7 +28,6 @@ * @library /lib/testlibrary/bytecode /java/lang/invoke/common * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper * @run testng CondyBSMException - * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyBSMException */ import org.testng.Assert; --- old/test/jdk/java/lang/invoke/condy/CondyBSMInvocation.java 2018-09-28 11:54:40.000000000 -0700 +++ new/test/jdk/java/lang/invoke/condy/CondyBSMInvocation.java 2018-09-28 11:54:40.000000000 -0700 @@ -27,8 +27,7 @@ * @summary Test basic invocation of bootstrap methods * @library /lib/testlibrary/bytecode /java/lang/invoke/common * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper - * @run testng CondyBSMInvocation - * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyBSMInvocation + * @run testng/timeout=999999 CondyBSMInvocation */ @@ -54,8 +53,8 @@ @Test public void testNonexistent() throws Throwable { MethodHandle mh = InstructionHelper.ldcDynamicConstant( - L, "name", Object.class, - "bsm", methodType(Object.class), + L, "invoke", Object.class, + "noSuchMethod", methodType(Object.class), S -> {}); try { @@ -65,6 +64,25 @@ } } + @Test + public void testBadExpr() throws Throwable { + MethodHandle mh = InstructionHelper.ldcDynamicConstant( + L, "noSuchExprMode", Object.class, + "bsm", methodType(Object.class), + S -> {}); + + try { + mh.invoke(); + Assert.fail("BootstrapMethodError expected to be thrown"); + } catch (BootstrapMethodError e) { + Assert.assertEquals(e.getCause().getClass(), IllegalArgumentException.class); + // Example: java.lang.IllegalArgumentException: invalid name + // for expression-mode constant: CondyBSMInvocation.bsm()Object + // /invokeStatic/noSuchExprMode:class java.lang.Object[] + + } + } + static MethodHandle[] bsms(String bsmName) { return Stream.of(CondyBSMInvocation.class.getDeclaredMethods()). filter(m -> m.getName().equals(bsmName)). @@ -201,6 +219,36 @@ return Integer.toString(staticArgs.length); } + // expression mode BSMs + public static Object bsm() { + return bsm((MethodHandles.Lookup)null, null, null); + } + public static Object bsm(Object a1) { + return bsm((MethodHandles.Lookup)null, null, null, a1); + } + public static Object bsm(Object a1, Object a2) { + return bsm((MethodHandles.Lookup)null, null, null, a1, a2); + } + public static Object bsm(Object a1, Object a2, Object a3) { + return bsm((MethodHandles.Lookup)null, null, null, a1, a2, a3); + } + public static Object bsm(Object a1, Object a2, Object a3, Object a4) { + return bsm((MethodHandles.Lookup)null, null, null, a1, a2, a3, a4); + } + public static Object bsm(Object a1, Object a2, Object a3, Object a4, Object a5) { + return bsm((MethodHandles.Lookup)null, null, null, a1, a2, a3, a4, a5); + } + public static Object bsm(Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) { + return bsm((MethodHandles.Lookup)null, null, null, a1, a2, a3, a4, a5, a6); + } + public static Object bsm(Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) { + return bsm((MethodHandles.Lookup)null, null, null, a1, a2, a3, a4, a5, a6, a7); + } + public static Object bsm(Object... args) { + assertAll(args); + return Integer.toString(args.length); + } + static void assertAll(Object... as) { for (int i = 0; i < as.length; i++) { Assert.assertEquals(as[i], i); @@ -238,7 +286,64 @@ } @Test - public void testWrongNumberOfStaticArguments() throws Throwable { + public void testArityWithoutMetadata() throws Throwable { + for (int i = 0; i < 8; i++) { + final int n = i; + MethodType mt = methodType(Object.class) + .appendParameterTypes(Collections.nCopies(n, Object.class)); + MethodHandle mh = InstructionHelper.ldcDynamicConstant( + L, "invoke", Object.class, + "bsm", mt, + S -> IntStream.range(0, n).forEach(S::add) + ); + + Object r = mh.invoke(); + Assert.assertEquals(r, Integer.toString(n)); + } + + { + MethodType mt = methodType(Object.class, Object[].class); + MethodHandle mh = InstructionHelper.ldcDynamicConstant( + L, "invoke", Object.class, + "bsm", mt, + S -> IntStream.range(0, 9).forEach(S::add) + ); + + Object r = mh.invoke(); + Assert.assertEquals(r, Integer.toString(9)); + } + } + @Test + public void testArityAsSymbolic() throws Throwable { + for (int i = 0; i < 8; i++) { + final int n = i; + MethodType mt = methodType(Object.class) + .appendParameterTypes(Collections.nCopies(n, Object.class)); + MethodHandle mh = InstructionHelper.ldcDynamicConstant( + L, "symbolic", Object.class, + "bsm", mt, + S -> IntStream.range(0, n).forEach(S::add) + ); + + Object r = mh.invoke(); + Assert.assertEquals(r, Integer.toString(n)); + } + + { + MethodType mt = methodType(Object.class, Object[].class); + MethodHandle mh = InstructionHelper.ldcDynamicConstant( + L, "symbolic", Object.class, + "bsm", mt, + S -> IntStream.range(0, 9).forEach(S::add) + ); + + Object r = mh.invoke(); + Assert.assertEquals(r, Integer.toString(9)); + } + } + + @Test + public void testWrongNumberOfStaticArgumentsWithMetaData() throws Throwable { for (int i = 1; i < 8; i++) { final int n = i; MethodType mt = methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class) @@ -258,4 +363,27 @@ } } } + + @Test + public void testWrongNumberOfStaticArgumentsWithoutMetaData() throws Throwable { + for (int i = 1; i < 8; i++) { + final int n = i; + MethodType mt = methodType(Object.class) + .appendParameterTypes(Collections.nCopies(n, Object.class)); + MethodHandle mh = InstructionHelper.ldcDynamicConstant( + L, "invoke", Object.class, + "bsm", mt, + S -> IntStream.range(0, n - 1).forEach(S::add) + ); + + try { + Object r = mh.invoke(); + Assert.fail("BootstrapMethodError expected to be thrown for arrity " + n); + } catch (BootstrapMethodError e) { + Throwable t = e.getCause(); + Assert.assertTrue(WrongMethodTypeException.class.isAssignableFrom(t.getClass())); + } + } + } + } --- old/test/jdk/java/lang/invoke/condy/CondyBSMValidationTest.java 2018-09-28 11:54:43.000000000 -0700 +++ new/test/jdk/java/lang/invoke/condy/CondyBSMValidationTest.java 2018-09-28 11:54:42.000000000 -0700 @@ -28,7 +28,6 @@ * @library /lib/testlibrary/bytecode /java/lang/invoke/common * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper * @run testng CondyBSMValidationTest - * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyBSMValidationTest */ import org.testng.annotations.DataProvider; --- old/test/jdk/java/lang/invoke/condy/CondyInterfaceWithOverpassMethods.java 2018-09-28 11:54:44.000000000 -0700 +++ new/test/jdk/java/lang/invoke/condy/CondyInterfaceWithOverpassMethods.java 2018-09-28 11:54:44.000000000 -0700 @@ -28,7 +28,6 @@ * @library /lib/testlibrary/bytecode * @build jdk.experimental.bytecode.BasicClassBuilder * @run testng CondyInterfaceWithOverpassMethods - * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyInterfaceWithOverpassMethods */ import jdk.experimental.bytecode.BasicClassBuilder; --- old/test/jdk/java/lang/invoke/condy/CondyNameValidationTest.java 2018-09-28 11:54:46.000000000 -0700 +++ new/test/jdk/java/lang/invoke/condy/CondyNameValidationTest.java 2018-09-28 11:54:46.000000000 -0700 @@ -28,7 +28,6 @@ * @library /lib/testlibrary/bytecode /java/lang/invoke/common * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper * @run testng CondyNameValidationTest - * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyNameValidationTest */ import org.testng.annotations.DataProvider; --- old/test/jdk/java/lang/invoke/condy/CondyNestedTest.java 2018-09-28 11:54:48.000000000 -0700 +++ new/test/jdk/java/lang/invoke/condy/CondyNestedTest.java 2018-09-28 11:54:48.000000000 -0700 @@ -27,7 +27,6 @@ * @summary Test nested dynamic constant declarations that are recursive * @compile CondyNestedTest_Code.jcod * @run testng CondyNestedTest - * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyNestedTest */ import org.testng.Assert; --- old/test/jdk/java/lang/invoke/condy/CondyRepeatFailedResolution.java 2018-09-28 11:54:50.000000000 -0700 +++ new/test/jdk/java/lang/invoke/condy/CondyRepeatFailedResolution.java 2018-09-28 11:54:50.000000000 -0700 @@ -28,7 +28,6 @@ * @library /lib/testlibrary/bytecode /java/lang/invoke/common * @build jdk.experimental.bytecode.BasicClassBuilder * @run testng CondyRepeatFailedResolution - * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyRepeatFailedResolution */ import jdk.experimental.bytecode.BasicClassBuilder; --- old/test/jdk/java/lang/invoke/condy/CondyReturnPrimitiveTest.java 2018-09-28 11:54:52.000000000 -0700 +++ new/test/jdk/java/lang/invoke/condy/CondyReturnPrimitiveTest.java 2018-09-28 11:54:52.000000000 -0700 @@ -28,7 +28,6 @@ * @library /lib/testlibrary/bytecode * @build jdk.experimental.bytecode.BasicClassBuilder * @run testng CondyReturnPrimitiveTest - * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyReturnPrimitiveTest */ import jdk.experimental.bytecode.BasicClassBuilder; --- old/test/jdk/java/lang/invoke/condy/CondyStaticArgumentsTest.java 2018-09-28 11:54:54.000000000 -0700 +++ new/test/jdk/java/lang/invoke/condy/CondyStaticArgumentsTest.java 2018-09-28 11:54:54.000000000 -0700 @@ -28,7 +28,6 @@ * @library /lib/testlibrary/bytecode /java/lang/invoke/common * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper * @run testng CondyStaticArgumentsTest - * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyStaticArgumentsTest */ import jdk.experimental.bytecode.PoolHelper; --- old/test/jdk/java/lang/invoke/condy/CondyTypeValidationTest.java 2018-09-28 11:54:56.000000000 -0700 +++ new/test/jdk/java/lang/invoke/condy/CondyTypeValidationTest.java 2018-09-28 11:54:56.000000000 -0700 @@ -27,7 +27,7 @@ * @summary Test invalid name in name and type * @library /lib/testlibrary/bytecode /java/lang/invoke/common * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper - * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyTypeValidationTest + * @run testng/othervm CondyTypeValidationTest */ import org.testng.annotations.DataProvider; --- old/test/jdk/java/lang/invoke/condy/CondyWithGarbageTest.java 2018-09-28 11:54:58.000000000 -0700 +++ new/test/jdk/java/lang/invoke/condy/CondyWithGarbageTest.java 2018-09-28 11:54:58.000000000 -0700 @@ -28,7 +28,6 @@ * @library /lib/testlibrary/bytecode /java/lang/invoke/common * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper * @run testng CondyWithGarbageTest - * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyWithGarbageTest */ --- old/test/jdk/java/lang/invoke/condy/CondyWrongType.java 2018-09-28 11:55:00.000000000 -0700 +++ new/test/jdk/java/lang/invoke/condy/CondyWrongType.java 2018-09-28 11:55:00.000000000 -0700 @@ -28,7 +28,6 @@ * @library /lib/testlibrary/bytecode /java/lang/invoke/common * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper * @run testng CondyWrongType - * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyWrongType */ import org.testng.Assert; --- old/test/jdk/java/lang/invoke/condy/ConstantBootstrapsTest.java 2018-09-28 11:55:02.000000000 -0700 +++ new/test/jdk/java/lang/invoke/condy/ConstantBootstrapsTest.java 2018-09-28 11:55:02.000000000 -0700 @@ -28,7 +28,6 @@ * @library /lib/testlibrary/bytecode /java/lang/invoke/common * @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper * @run testng ConstantBootstrapsTest - * @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 ConstantBootstrapsTest */ import jdk.experimental.bytecode.PoolHelper; --- old/test/jdk/jdk/internal/reflect/constantPool/ConstantPoolTest.java 2018-09-28 11:55:04.000000000 -0700 +++ new/test/jdk/jdk/internal/reflect/constantPool/ConstantPoolTest.java 2018-09-28 11:55:04.000000000 -0700 @@ -34,7 +34,7 @@ package jdk.internal.reflect.constantPool; -import java.util.HashMap; +import java.util.TreeMap; import java.util.Map; import jdk.internal.misc.SharedSecrets; import jdk.test.lib.Asserts; @@ -46,7 +46,8 @@ private static final ConstantPool CP = SharedSecrets.getJavaLangAccess() .getConstantPool(TEST_CLASS); - public static void main(String[] s) { + public static void main(String[] s) throws Throwable { + TEST_CLASS.newInstance(); for (TestCase testCase : TestCase.values()) { testCase.test(); } @@ -69,6 +70,7 @@ referenceMap.put(30, ConstantPool.Tag.METHODTYPE); referenceMap.put(48, ConstantPool.Tag.FIELDREF); referenceMap.put(52, ConstantPool.Tag.FLOAT); + referenceMap.put(53, ConstantPool.Tag.DYNAMIC); } @Override void testIndex(int cpi, Object reference) { @@ -80,6 +82,95 @@ Asserts.assertEquals(tagToVerify, tagToRefer, msg); } }, + GET_CONSTANT_AT { + { + referenceMap.put(3, Object.class); + referenceMap.put(13, (long)6); + referenceMap.put(15, (int)1); + referenceMap.put(17, Runnable.class); + referenceMap.put(21, (double)1.45); + referenceMap.put(23, "Hello"); + referenceMap.put(31, "[a method handle]"); + referenceMap.put(52, (float)1.34); + //referenceMap.put(53, "[a dynamic constant]"); + } + @Override + void testIndex(int cpi, Object reference) { + Object entryToVerify = CP.getConstantAt(cpi); + Object entryToRefer = reference; + if (cpi == 31 && entryToVerify instanceof java.lang.invoke.MethodHandle) { + // const #31 = MethodHandle 5:#34; // REF_invokeVirtual:java/lang/Object.toString:"()Ljava/lang/String;" + entryToVerify = entryToRefer; // good enough + } + String msg = String.format("Method getConstantAt works not" + + " as expected at CP entry #%d:" + + " got %s, but should be %s", + cpi, entryToVerify, entryToRefer); + Asserts.assertEquals(entryToVerify, entryToRefer, msg); + } + }, + GET_CONSTANTDESC_AT { + { + referenceMap.put(3, Object.class); + referenceMap.put(13, (long)6); + referenceMap.put(15, (int)1); + referenceMap.put(17, Runnable.class); + referenceMap.put(21, (double)1.45); + referenceMap.put(23, "Hello"); + referenceMap.put(31, "[a method handle]"); + referenceMap.put(52, (float)1.34); + referenceMap.put(53, "[a dynamic constant]"); + } + @Override + void testIndex(int cpi, Object reference) { + Object entryToVerify = CP.getConstantDescAt(cpi); + Object entryToRefer = ((java.lang.constant.Constable)reference).describeConstable().orElse(null); + if (cpi == 31 && entryToVerify instanceof java.lang.constant.MethodHandleDesc) { + String str = entryToVerify.toString(); + // MethodHandleDesc[VIRTUAL/Object::toString()String] + if (str.contains("toString") && + str.contains("Object") && + str.contains("()") && + (str.contains("virtual") || str.contains("Virtual") || str.contains("VIRTUAL"))) + // close enough + entryToVerify = entryToRefer; + } + if (cpi == 53 && entryToVerify instanceof java.lang.constant.DynamicConstantDesc) { + // DynamicConstantDesc[LambdaMetafactory::metafactory( + // toString/MethodTypeDesc[(Object)void], + // MethodHandleDesc[VIRTUAL/Object::toString()String], + // MethodTypeDesc[(Object)void] + // )Object], + String str = entryToVerify.toString(); + if (str.contains("toString") && + str.contains("metafactory") && + str.contains("(Object)void")) + // close enough + entryToVerify = entryToRefer; + } + String msg = String.format("Method getConstantDescAt works not" + + " as expected at CP entry #%d:" + + " got %s, but should be %s", + cpi, entryToVerify, entryToRefer); + Asserts.assertEquals(entryToVerify, entryToRefer, msg); + } + }, + GET_CLASS_AT { + { + referenceMap.put(3, Object.class); + referenceMap.put(17, Runnable.class); + } + @Override + void testIndex(int cpi, Object reference) { + Object entryToVerify = CP.getClassAt(cpi); + Object entryToRefer = reference; + String msg = String.format("Method getClassAt works not" + + " as expected at CP entry #%d:" + + " got %s, but should be %s", + cpi, entryToVerify, entryToRefer); + Asserts.assertEquals(entryToVerify, entryToRefer, msg); + } + }, GET_CLASS_REF_INDEX_AT { { referenceMap.put(1, 3); @@ -161,10 +252,12 @@ protected final Map referenceMap; TestCase() { - this.referenceMap = new HashMap<>(); + // Use a TreeMap for deterministic test order. + this.referenceMap = new TreeMap<>(); } abstract void testIndex(int cpi, Object reference); public void test() { + System.out.println(this); referenceMap.forEach(this::testIndex); } } --- old/test/jdk/jdk/internal/reflect/constantPool/ConstantPoolTestDummy.jasm 2018-09-28 11:55:06.000000000 -0700 +++ new/test/jdk/jdk/internal/reflect/constantPool/ConstantPoolTestDummy.jasm 2018-09-28 11:55:06.000000000 -0700 @@ -24,7 +24,7 @@ package jdk/internal/reflect/constantPool; super public #2; //class ConstantPoolTestDummy - version 52:0 + version 55:0 { // Actually, only first 13 constant pool entries are actually used by the class @@ -81,6 +81,10 @@ const #50 = Asciz "myField"; const #51 = Asciz "I"; const #52 = float 1.34f; +const #53 = Dynamic 0:#54; // REF_invokeStatic:java/lang/invoke/LambdaMetafactory.metafactory:"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;":toString:"Ljava/lang/Object;" MethodType "(Ljava/lang/Object;)V", MethodHandle REF_invokeVirtual:java/lang/Object.toString:"()Ljava/lang/String;", MethodType "(Ljava/lang/Object;)V" +const #54 = NameAndType #55:#56; // toString:"Ljava/lang/Object;" +const #55 = Asciz "toString"; +const #56 = Asciz "Ljava/lang/Object;"; public Method #4:#5 // "":"()V" --- old/src/java.base/share/classes/java/lang/invoke/AbstractConstantGroup.java 2018-09-28 11:55:08.000000000 -0700 +++ /dev/null 2018-09-28 11:55:08.000000000 -0700 @@ -1,341 +0,0 @@ -/* - * Copyright (c) 2017, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package java.lang.invoke; - -import java.util.*; -import jdk.internal.vm.annotation.Stable; - -import static java.lang.invoke.MethodHandleStatics.rangeCheck1; -import static java.lang.invoke.MethodHandleStatics.rangeCheck2; - -/** Utility class for implementing ConstantGroup. */ -/*non-public*/ -abstract class AbstractConstantGroup implements ConstantGroup { - /** The size of this constant group, set permanently by the constructor. */ - protected final int size; - - /** The constructor requires the size of the constant group being represented. - * @param size the size of this constant group, set permanently by the constructor - */ - AbstractConstantGroup(int size) { - this.size = size; - } - - @Override public final int size() { - return size; - } - - public abstract Object get(int index) throws LinkageError; - - public abstract Object get(int index, Object ifNotPresent); - - public abstract boolean isPresent(int index); - - // Do not override equals or hashCode, since this type is stateful. - - /** - * Produce a string using the non-resolving list view, - * where unresolved elements are presented as asterisks. - * @return {@code this.asList("*").toString()} - */ - @Override public String toString() { - return asList("*").toString(); - } - - static class AsIterator implements Iterator { - private final ConstantGroup self; - private final int end; - private final boolean resolving; - private final Object ifNotPresent; - - // Mutable state: - private int index; - - private AsIterator(ConstantGroup self, int start, int end, - boolean resolving, Object ifNotPresent) { - this.self = self; - this.end = end; - this.index = start; - this.resolving = resolving; - this.ifNotPresent = ifNotPresent; - } - AsIterator(ConstantGroup self, int start, int end) { - this(self, start, end, true, null); - } - AsIterator(ConstantGroup self, int start, int end, - Object ifNotPresent) { - this(self, start, end, false, ifNotPresent); - } - - @Override - public boolean hasNext() { - return index < end; - } - - @Override - public Object next() { - int i = bumpIndex(); - if (resolving) - return self.get(i); - else - return self.get(i, ifNotPresent); - } - - private int bumpIndex() { - int i = index; - if (i >= end) throw new NoSuchElementException(); - index = i+1; - return i; - } - } - - static class SubGroup extends AbstractConstantGroup { - private final ConstantGroup self; // the real CG - private final int offset; // offset within myself - SubGroup(ConstantGroup self, int start, int end) { - super(end - start); - this.self = self; - this.offset = start; - rangeCheck2(start, end, size); - } - - private int mapIndex(int index) { - return rangeCheck1(index, size) + offset; - } - - @Override - public Object get(int index) { - return self.get(mapIndex(index)); - } - - @Override - public Object get(int index, Object ifNotPresent) { - return self.get(mapIndex(index), ifNotPresent); - } - - @Override - public boolean isPresent(int index) { - return self.isPresent(mapIndex(index)); - } - - @Override - public ConstantGroup subGroup(int start, int end) { - rangeCheck2(start, end, size); - return new SubGroup(self, offset + start, offset + end); - } - - @Override - public List asList() { - return new AsList(self, offset, offset + size); - } - - @Override - public List asList(Object ifNotPresent) { - return new AsList(self, offset, offset + size, ifNotPresent); - } - - @Override - public int copyConstants(int start, int end, - Object[] buf, int pos) throws LinkageError { - rangeCheck2(start, end, size); - return self.copyConstants(offset + start, offset + end, - buf, pos); - } - - @Override - public int copyConstants(int start, int end, - Object[] buf, int pos, - Object ifNotPresent) { - rangeCheck2(start, end, size); - return self.copyConstants(offset + start, offset + end, - buf, pos, ifNotPresent); - } - } - - static class AsList extends AbstractList { - private final ConstantGroup self; - private final int size; - private final int offset; - private final boolean resolving; - private final Object ifNotPresent; - - private AsList(ConstantGroup self, int start, int end, - boolean resolving, Object ifNotPresent) { - this.self = self; - this.size = end - start; - this.offset = start; - this.resolving = resolving; - this.ifNotPresent = ifNotPresent; - rangeCheck2(start, end, self.size()); - } - AsList(ConstantGroup self, int start, int end) { - this(self, start, end, true, null); - } - AsList(ConstantGroup self, int start, int end, - Object ifNotPresent) { - this(self, start, end, false, ifNotPresent); - } - - private int mapIndex(int index) { - return rangeCheck1(index, size) + offset; - } - - @Override public final int size() { - return size; - } - - @Override public Object get(int index) { - if (resolving) - return self.get(mapIndex(index)); - else - return self.get(mapIndex(index), ifNotPresent); - } - - @Override - public Iterator iterator() { - if (resolving) - return new AsIterator(self, offset, offset + size); - else - return new AsIterator(self, offset, offset + size, ifNotPresent); - } - - @Override public List subList(int start, int end) { - rangeCheck2(start, end, size); - return new AsList(self, offset + start, offset + end, - resolving, ifNotPresent); - } - - @Override public Object[] toArray() { - return toArray(new Object[size]); - } - @Override public T[] toArray(T[] a) { - int pad = a.length - size; - if (pad < 0) { - pad = 0; - a = Arrays.copyOf(a, size); - } - if (resolving) - self.copyConstants(offset, offset + size, a, 0); - else - self.copyConstants(offset, offset + size, a, 0, - ifNotPresent); - if (pad > 0) a[size] = null; - return a; - } - } - - static abstract - class WithCache extends AbstractConstantGroup { - @Stable final Object[] cache; - - WithCache(int size) { - super(size); - // It is caller's responsibility to initialize the cache. - // Initial contents are all-null, which means nothing is present. - cache = new Object[size]; - } - - void initializeCache(List cacheContents, Object ifNotPresent) { - // Replace ifNotPresent with NOT_PRESENT, - // and null with RESOLVED_TO_NULL. - // Then forget about the user-provided ifNotPresent. - for (int i = 0; i < cache.length; i++) { - Object x = cacheContents.get(i); - if (x == ifNotPresent) - continue; // leave the null in place - if (x == null) - x = RESOLVED_TO_NULL; - cache[i] = x; - } - } - - @Override public Object get(int i) { - Object x = cache[i]; - // @Stable array must use null for sentinel - if (x == null) x = fillCache(i); - return unwrapNull(x); - } - - @Override public Object get(int i, Object ifNotAvailable) { - Object x = cache[i]; - // @Stable array must use null for sentinel - if (x == null) return ifNotAvailable; - return unwrapNull(x); - } - - @Override - public boolean isPresent(int i) { - return cache[i] != null; - } - - /** hook for local subclasses */ - Object fillCache(int i) { - throw new NoSuchElementException("constant group does not contain element #"+i); - } - - /// routines for mapping between null sentinel and true resolved null - - static Object wrapNull(Object x) { - return x == null ? RESOLVED_TO_NULL : x; - } - - static Object unwrapNull(Object x) { - assert(x != null); - return x == RESOLVED_TO_NULL ? null : x; - } - - // secret sentinel for an actual null resolved value, in the cache - static final Object RESOLVED_TO_NULL = new Object(); - - // secret sentinel for a "hole" in the cache: - static final Object NOT_PRESENT = new Object(); - - } - - /** Skeleton implementation of BootstrapCallInfo. */ - static - class BSCIWithCache extends WithCache implements BootstrapCallInfo { - private final MethodHandle bsm; - private final String name; - private final T type; - - @Override public String toString() { - return bsm+"/"+name+":"+type+super.toString(); - } - - BSCIWithCache(MethodHandle bsm, String name, T type, int size) { - super(size); - this.type = type; - this.bsm = bsm; - this.name = name; - assert(type instanceof Class || type instanceof MethodType); - } - - @Override public MethodHandle bootstrapMethod() { return bsm; } - @Override public String invocationName() { return name; } - @Override public T invocationType() { return type; } - } -} --- /dev/null 2018-09-28 11:55:08.000000000 -0700 +++ new/src/java.base/share/classes/java/lang/invoke/AbstractBootstrapCallInfo.java 2018-09-28 11:55:08.000000000 -0700 @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2017, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package java.lang.invoke; + +import jdk.internal.vm.annotation.Stable; + +import java.lang.constant.ClassDesc; +import java.lang.constant.Constable; +import java.lang.constant.ConstantDesc; +import java.lang.constant.MethodTypeDesc; +import java.lang.invoke.MethodHandles.Lookup; +import java.util.*; +import java.util.function.IntFunction; + +import static java.lang.invoke.BootstrapMethodInvoker.VM_BSCI; +import static java.lang.invoke.MethodHandleNatives.Constants.*; +import static java.lang.invoke.MethodHandleStatics.rangeCheck1; +import static java.lang.invoke.MethodHandleStatics.rangeCheck2; + +/** + * Utility class for implementing BootstrapCallInfo. + * Implements the three list-views on top of the three indexes accessors. + * The {@link WithCache} subclass adds a backing store. + */ +/*non-public*/ +abstract class AbstractBootstrapCallInfo> + implements BootstrapCallInfo { + /** The size of this constant group, set permanently by the constructor. */ + private final MethodHandle bsm; + private final String name; + private final TypeView typeView; + private final IntFunction> symFinder = null; //@@ + protected final int argumentCount; + + // view caches: + private @Stable ArgList argListF; + private ArgList argListI; // can be reassigned for differing ifPresent values + private @Stable List> argListS; + + /** + * Constructor, which takes permanent settings for the properties of the BSCI. + * @param bsm bootstrap method, which must be previously resolved + * @param name name string used for invocation or constant + * @param typeView type used for invocation or constant (wrapped as resolved or unresolved) + * @param argumentCount number of static arguments for bootstrap method + */ + AbstractBootstrapCallInfo(MethodHandle bsm, String name, TypeView typeView, int argumentCount) { + this.bsm = bsm; + this.name = name; + this.typeView = typeView; + this.argumentCount = argumentCount; + } + + @Override public MethodHandle bootstrapMethod() { return bsm; } + + @Override public String invocationName() { return name; } + + @Override public T invocationType() { return typeView.invocationType(); } + + @Override public ConstantDesc invocationTypeDesc() { return typeView.invocationTypeDesc(); } + + @Override public final int argumentCount() { return argumentCount; } + + @Override + public abstract Object argument(int index) throws LinkageError; + + @Override + public abstract Object argument(int index, Object ifNotPresent); + + @Override + public abstract ConstantDesc argumentDesc(int index); + + @Override + public abstract boolean argumentIsPresent(int index); + + /** + * Produce a string that briefly reports the BSM, name, type, + * and argument list. For arguments, use a non-resolving list view, + * with unresolved elements being presented as asterisks. + * @return {@code this.asList("*").toString()} + */ + @Override public String toString() { + return BootstrapCallInfo.toString(this); + } + // (Do not override equals or hashCode, since this type is stateful.) + + /** + * Representative of a resolved type mirror, an unresolved type descriptor, or both. + * If one component is null, the {@code TypeView} can lazily resolve it from the other. + * If the type component is null, a {@code Lookup} object must be supplied to resolve the type descriptor. + * @param the type {@code MethodType} or {@code Class} + */ + static + class TypeView> { + private @Stable T type; + private @Stable ConstantDesc typeDesc; + private final Lookup lookup; // used only to resolve typeDesc + + public T invocationType() { + if (type == null) resolve(); + return type; + } + public ConstantDesc invocationTypeDesc() { + if (typeDesc == null) resolve(); + return typeDesc; + } + + private TypeView(T type, ConstantDesc typeDesc) { + Objects.requireNonNull(type); + // typeDesc can be null, will be lazily reverse-resolved + this.type = type; + if (typeDesc != null) this.typeDesc = typeDesc; + this.lookup = null; + check(); + } + + TypeView(T type) { + this(type, null); + } + + TypeView(ConstantDesc typeDesc, Lookup lookup) { + Objects.requireNonNull(typeDesc); + Objects.requireNonNull(lookup); + this.typeDesc = typeDesc; + this.lookup = lookup; + check(); + } + private void check() { + if (!(type == null || + type instanceof Class || + type instanceof MethodType)) { + throw new IllegalArgumentException("must be class or method type: " + type); + } + if (!(typeDesc == null || + typeDesc instanceof ClassDesc || + typeDesc instanceof MethodTypeDesc)) { + throw new IllegalArgumentException("must be class or method type descriptor: " + typeDesc); + } + } + @Override + public String toString() { + Object res = typeDesc; + if (type != null) { + res = type; + if (res instanceof Class) + res = ((Class)type).getSimpleName(); + } + return res.toString(); + } + + /** Fill in either field if it is null. */ + void resolve() { + if (type != null && typeDesc == null) { + typeDesc = type.describeConstable().orElse(null); + } + if (typeDesc != null && type == null) { + try { + type = typeDesc.resolveConstantDesc(lookup); + } catch (ReflectiveOperationException ex) { + Object what; + if (typeDesc instanceof TypeDescriptor) + what = ((TypeDescriptor) typeDesc).descriptorString(); + else what = typeDesc.toString(); + throw new NoClassDefFoundError("cannot resolve: " + what); + } + } + // check required end-state + if (type == null || typeDesc == null) { + throw new InternalError("cannot resolve type"); + } + } + } + + /// List-view machinery + + @Override + public List argumentList() { + ArgList args = argListF; + if (args == null) + argListF = args = new ArgList(this, 0, argumentCount()); + return args; + } + + @Override + public List argumentList(Object ifNotPresent) { + ArgList args = argListI; + if (args == null || args.ifNotPresent != ifNotPresent) + argListI = args = new ArgList(this, 0, argumentCount(), ifNotPresent); + return args; + + } + + @Override + public List> argumentDescList() { + List> args = argListS; + if (args == null) + argListS = args = argumentDescList(this); + return args; + } + + static List argumentList(AbstractBootstrapCallInfo self) { + return new ArgList(self, 0, self.argumentCount()); + } + static List argumentList(AbstractBootstrapCallInfo self, Object ifPresent) { + return new ArgList(self, 0, self.argumentCount(), ifPresent); + } + static List> argumentDescList(AbstractBootstrapCallInfo self) { + ArgList args = new ArgList(self, true, 0, self.argumentCount()); + @SuppressWarnings({"unchecked", "rawtypes"}) + List> result = (List) args; + return result; + } + + /** Non-public implementation of the three List views for BootstrapCallInfo. */ + static class ArgList extends AbstractList { + private final BootstrapCallInfo self; + private final int size; + private final int offset; + private final byte resolving; + private final Object ifNotPresent; + + private ArgList(BootstrapCallInfo self, int start, int end, + byte resolving, Object ifNotPresent) { + this.self = self; + this.size = end - start; + this.offset = start; + this.resolving = resolving; + this.ifNotPresent = ifNotPresent; + rangeCheck2(start, end, self.argumentCount()); + } + ArgList(BootstrapCallInfo self, int start, int end) { + this(self, start, end, BAR_FORCE, null); + } + ArgList(BootstrapCallInfo self, int start, int end, + Object ifNotPresent) { + this(self, start, end, BAR_IFPRESENT, ifNotPresent); + } + ArgList(BootstrapCallInfo self, boolean symRefs, int start, int end) { + this(self, start, end, BAR_SYMREF, null); + } + + private int mapIndex(int index) { + return rangeCheck1(index, size) + offset; + } + + @Override public final int size() { + return size; + } + + @Override public Object get(int index) { + if (resolving == BAR_FORCE) + return self.argument(mapIndex(index)); + else if (resolving == BAR_SYMREF) + return self.argumentDesc(index); + else + return self.argument(mapIndex(index), ifNotPresent); + } + + @Override public List subList(int start, int end) { + rangeCheck2(start, end, size); + return new ArgList(self, offset + start, offset + end, + resolving, ifNotPresent); + } + + @Override public Object[] toArray() { + return toArray(new Object[size]); + } + @Override public T[] toArray(T[] a) { + if (!(self instanceof VM_BSCI)) + return super.toArray(a); + int pad = a.length - size; + Object[] buf = a; + if (pad < 0) { + pad = 0; + buf = a = Arrays.copyOf(a, size); + } + if (a.getClass() != Object[].class || resolving == BAR_SYMREF) + buf = new Object[size]; // VM might store any kind of Object + ((VM_BSCI) self).copyVMArguments(offset, offset + size, a, 0, resolving, ifNotPresent); + if (buf != a) { + // If a is the wrong array type, and the VM sends us a bad + // object, the arraycopy call is where the error will be reported. + System.arraycopy(buf, 0, a, 0, buf.length); + } + if (pad > 0) a[size] = null; + return a; + } + } + + abstract static + class WithCache> + extends AbstractBootstrapCallInfo { + protected @Stable final Object[] cache; + protected @Stable ConstantDesc[] symCache; + + WithCache(MethodHandle bsm, String name, TypeView typeView, int argumentCount) { + super(bsm, name, typeView, argumentCount); + // It is caller's responsibility to initialize the cache. + // Initial contents are all-null, which means nothing is present. + cache = new Object[argumentCount]; + // create symCache lazily + } + + WithCache(MethodHandle bsm, String name, TypeView typeView, Object[] arguments) { + super(bsm, name, typeView, arguments.length); + cache = arguments; // caller may have pre-loaded stuff into the cache + } + + WithCache(MethodHandle bsm, String name, TypeView typeView, ConstantDesc[] symbolicRefs) { + super(bsm, name, typeView, symbolicRefs.length); + cache = new Object[argumentCount]; + symCache = symbolicRefs; + } + +// WithCache(MethodHandle bsm, String name, TypeView typeView, List allResolved) { +// this(bsm, name, typeView, allResolved.size()); +// initializeCache(allResolved); +// } + + void initializeCache(List cacheContents) { + initializeCache(cacheContents, new Object()); + } + void initializeCache(List cacheContents, Object ifNotPresent) { + // Replace ifNotPresent with null, + // and null with RESOLVED_TO_NULL. + // Then forget about the user-provided ifNotPresent. + for (int i = 0; i < cache.length; i++) { + Object x = cacheContents.get(i); + if (x == ifNotPresent) + continue; // leave the null in place + cache[i] = wrapNull(x); + } + } + + @Override public Object argument(int i) { + Object x = cache[i]; + // @Stable array must use null for sentinel + if (x == null) { + x = wrapNull(resolveConstant(i)); + // racy CAS: + Object x2 = cache[i]; + if (x2 != null) x = x2; + else cache[i] = x; + } + return unwrapNull(x); + } + + @Override public Object argument(int i, Object ifNotAvailable) { + Object x = cache[i]; + // @Stable array must use null for sentinel + if (x == null) return ifNotAvailable; + return unwrapNull(x); + } + + @Override public ConstantDesc argumentDesc(int i) { + ConstantDesc[] symCache = this.symCache; + if (symCache == null) { + this.symCache = symCache = new ConstantDesc[argumentCount()]; + } + ConstantDesc x = symCache[i]; + if (x == null) { + x = findSymbol(i); + // racy CAS: + ConstantDesc x2 = symCache[i]; + if (x2 != null) x = x2; + else symCache[i] = x; + } + return x; + } + + @Override + public boolean argumentIsPresent(int i) { + return cache[i] != null; //NULL_MEANS_NOT_PRESENT + } + + /** hook for local subclasses */ + abstract Object resolveConstant(int i) throws LinkageError; + + /** hook for local subclasses */ + abstract ConstantDesc findSymbol(int i); + + // carefully try to expose the underlying object array of arguments + // return null if this is not possible + Object[] maybeShareArguments() { + // fill in missing cache items, and look for null + for (int i = 0; i < cache.length; i++) { + Object cx = cache[i]; + Object rx = cx; + if (cx == null) { // empty cache entry + rx = argument(i); + cx = cache[i]; // reload filled cache entry + } else if (cx == RESOLVED_TO_NULL) { + rx = null; + } + // Punt if the cache contains a masked null value. + // Also punt if cache doesn't contain a value. + if (cx == null || rx == null) return null; + } + return cache; + } + + /// routines for mapping between null sentinel and true resolved null + + Object wrapNull(Object x) { + return x == null ? RESOLVED_TO_NULL : x; + } + + Object unwrapNull(Object x) { + assert(x != null); + return x == RESOLVED_TO_NULL ? null : x; + } + + // secret sentinel for an actual null resolved value, in the cache + private static final Object RESOLVED_TO_NULL = new Object(); + } + + static Object[] maybeShareArguments(BootstrapCallInfo bsci) { + if (bsci instanceof WithCache) { + WithCache wc = (WithCache) bsci; + return wc.maybeShareArguments(); + } + return null; + } +} --- old/src/java.base/share/classes/java/lang/invoke/ConstantGroup.java 2018-09-28 11:55:10.000000000 -0700 +++ /dev/null 2018-09-28 11:55:10.000000000 -0700 @@ -1,287 +0,0 @@ -/* - * Copyright (c) 2017, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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. - */ - -package java.lang.invoke; - -import java.util.List; -import java.util.NoSuchElementException; -import java.util.function.IntFunction; - -/** - * An ordered sequence of constants, some of which may not yet - * be present. This type is used by {@link BootstrapCallInfo} - * to represent the sequence of bootstrap arguments associated - * with a bootstrap method, without forcing their immediate - * resolution. - *

- * If you use the - * {@linkplain ConstantGroup#get(int) simple get method}, - * the constant will be resolved, if this has not already - * happened. An occasional side effect of resolution is a - * {@code LinkageError}, which happens if the system - * could not resolve the constant in question. - *

- * In order to peek at a constant without necessarily - * resolving it, use the - * {@linkplain ConstantGroup#get(int,Object) - * non-throwing get method}. - * This method will never throw a resolution error. - * Instead, if the resolution would result in an error, - * or if the implementation elects not to attempt - * resolution at this point, then the method will - * return the user-supplied sentinel value. - *

- * To iterate through the constants, resolving as you go, - * use the iterator provided on the {@link List}-typed view. - * If you supply a sentinel, resolution will be suppressed. - *

- * Typically the constant is drawn from a constant pool entry - * in the virtual machine. Constant pool entries undergo a - * one-time state transition from unresolved to resolved, - * with a permanently recorded result. Usually that result - * is the desired constant value, but it may also be an error. - * In any case, the results displayed by a {@code ConstantGroup} - * are stable in the same way. If a query to a particular - * constant in a {@code ConstantGroup} throws an exception once, - * it will throw the same kind of exception forever after. - * If the query returns a constant value once, it will return - * the same value forever after. - *

- * The only possible change in the status of a constant is - * from the unresolved to the resolved state, and that - * happens exactly once. A constant will never revert to - * an unlinked state. However, from the point of view of - * this interface, constants may appear to spontaneously - * resolve. This is so because constant pools are global - * structures shared across threads, and because - * prefetching of some constants may occur, there are no - * strong guarantees when the virtual machine may resolve - * constants. - *

- * When choosing sentinel values, be aware that a constant - * pool which has {@code CONSTANT_Dynamic} entries - * can contain potentially any representable value, - * and arbitrary implementations of {@code ConstantGroup} - * are also free to produce arbitrary values. - * This means some obvious choices for sentinel values, - * such as {@code null}, may sometimes fail to distinguish - * a resolved from an unresolved constant in the group. - * The most reliable sentinel is a privately created object, - * or perhaps the {@code ConstantGroup} itself. - * @since 1.10 - */ -// public -interface ConstantGroup { - /// Access - - /** - * Returns the number of constants in this group. - * This value never changes, for any particular group. - * @return the number of constants in this group - */ - int size(); - - /** - * Returns the selected constant, resolving it if necessary. - * Throws a linkage error if resolution proves impossible. - * @param index which constant to select - * @return the selected constant - * @throws LinkageError if the selected constant needs resolution and cannot be resolved - */ - Object get(int index) throws LinkageError; - - /** - * Returns the selected constant, - * or the given sentinel value if there is none available. - * If the constant cannot be resolved, the sentinel will be returned. - * If the constant can (perhaps) be resolved, but has not yet been resolved, - * then the sentinel may be returned, at the implementation's discretion. - * To force resolution (and a possible exception), call {@link #get(int)}. - * @param index the selected constant - * @param ifNotPresent the sentinel value to return if the constant is not present - * @return the selected constant, if available, else the sentinel value - */ - Object get(int index, Object ifNotPresent); - - /** - * Returns an indication of whether a constant may be available. - * If it returns {@code true}, it will always return true in the future, - * and a call to {@link #get(int)} will never throw an exception. - *

- * After a normal return from {@link #get(int)} or a present - * value is reported from {@link #get(int,Object)}, this method - * must always return true. - *

- * If this method returns {@code false}, nothing in particular - * can be inferred, since the query only concerns the internal - * logic of the {@code ConstantGroup} object which ensures that - a successful * query to a constant will always remain successful. - * The only way to force a permanent decision about whether - * a constant is available is to call {@link #get(int)} and - * be ready for an exception if the constant is unavailable. - * @param index the selected constant - * @return {@code true} if the selected constant is known by - * this object to be present, {@code false} if it is known - * not to be present or - */ - boolean isPresent(int index); - - /// Views - - /** - * Create a view on this group as a {@link List} view. - * Any request for a constant through this view will - * force resolution. - * @return a {@code List} view on this group which will force resolution - */ - default List asList() { - return new AbstractConstantGroup.AsList(this, 0, size()); - } - - /** - * Create a view on this group as a {@link List} view. - * Any request for a constant through this view will - * return the given sentinel value, if the corresponding - * call to {@link #get(int,Object)} would do so. - * @param ifNotPresent the sentinel value to return if a constant is not present - * @return a {@code List} view on this group which will not force resolution - */ - default List asList(Object ifNotPresent) { - return new AbstractConstantGroup.AsList(this, 0, size(), ifNotPresent); - } - - /** - * Create a view on a sub-sequence of this group. - * @param start the index to begin the view - * @param end the index to end the view - * @return a view on the selected sub-group - */ - default ConstantGroup subGroup(int start, int end) { - return new AbstractConstantGroup.SubGroup(this, start, end); - } - - /// Bulk operations - - /** - * Copy a sequence of constant values into a given buffer. - * This is equivalent to {@code end-offset} separate calls to {@code get}, - * for each index in the range from {@code offset} up to but not including {@code end}. - * For the first constant that cannot be resolved, - * a {@code LinkageError} is thrown, but only after - * preceding constant value have been stored. - * @param start index of first constant to retrieve - * @param end limiting index of constants to retrieve - * @param buf array to receive the requested values - * @param pos position in the array to offset storing the values - * @return the limiting index, {@code end} - * @throws LinkageError if a constant cannot be resolved - */ - default int copyConstants(int start, int end, - Object[] buf, int pos) - throws LinkageError - { - int bufBase = pos - start; // buf[bufBase + i] = get(i) - for (int i = start; i < end; i++) { - buf[bufBase + i] = get(i); - } - return end; - } - - /** - * Copy a sequence of constant values into a given buffer. - * This is equivalent to {@code end-offset} separate calls to {@code get}, - * for each index in the range from {@code offset} up to but not including {@code end}. - * Any constants that cannot be resolved are replaced by the - * given sentinel value. - * @param start index of first constant to retrieve - * @param end limiting index of constants to retrieve - * @param buf array to receive the requested values - * @param pos position in the array to offset storing the values - * @param ifNotPresent sentinel value to store if a value is not available - * @return the limiting index, {@code end} - * @throws LinkageError if {@code resolve} is true and a constant cannot be resolved - */ - default int copyConstants(int start, int end, - Object[] buf, int pos, - Object ifNotPresent) { - int bufBase = pos - start; // buf[bufBase + i] = get(i) - for (int i = start; i < end; i++) { - buf[bufBase + i] = get(i, ifNotPresent); - } - return end; - } - - /** - * Make a new constant group with the given constants. - * The value of {@code ifNotPresent} may be any reference. - * If this value is encountered as an element of the - * {@code constants} list, the new constant group will - * regard that element of the list as logically missing. - * If the new constant group is called upon to resolve - * a missing element of the group, it will refer to the - * given {@code constantProvider}, by calling it on the - * index of the missing element. - * The {@code constantProvider} must be stable, in the sense - * that the outcome of calling it on the same index twice - * will produce equivalent results. - * If {@code constantProvider} is the null reference, then - * it will be treated as if it were a function which raises - * {@link NoSuchElementException}. - * @param constants the elements of this constant group - * @param ifNotPresent sentinel value provided instead of a missing constant - * @param constantProvider function to call when a missing constant is resolved - * @return a new constant group with the given constants and resolution behavior - */ - static ConstantGroup makeConstantGroup(List constants, - Object ifNotPresent, - IntFunction constantProvider) { - class Impl extends AbstractConstantGroup.WithCache { - Impl() { - super(constants.size()); - initializeCache(constants, ifNotPresent); - } - @Override - Object fillCache(int index) { - if (constantProvider == null) super.fillCache(index); - return constantProvider.apply(index); - } - } - return new Impl(); - } - - /** - * Make a new constant group with the given constant values. - * The constants will be copied from the given list into the - * new constant group, forcing resolution if any are missing. - * @param constants the constants of this constant group - * @return a new constant group with the given constants - */ - static ConstantGroup makeConstantGroup(List constants) { - final Object NP = AbstractConstantGroup.WithCache.NOT_PRESENT; - assert(!constants.contains(NP)); // secret value - return makeConstantGroup(constants, NP, null); - } - -}