diff --git a/src/cpu/sparc/vm/templateTable_sparc.cpp b/src/cpu/sparc/vm/templateTable_sparc.cpp index 2a65487b25c16ad50d539670f44604699b578692..82acec5c22c470a34c89b00a71529aadcba2e547 100644 --- a/src/cpu/sparc/vm/templateTable_sparc.cpp +++ b/src/cpu/sparc/vm/templateTable_sparc.cpp @@ -266,7 +266,7 @@ void TemplateTable::sipush() { void TemplateTable::ldc(bool wide) { transition(vtos, vtos); - Label call_ldc, notInt, notString, notClass, exit; + Label call_ldc, notInt, isString, notString, notClass, exit; if (wide) { __ get_2_byte_integer_at_bcp(1, G3_scratch, O1, InterpreterMacroAssembler::Unsigned); @@ -317,8 +317,11 @@ void TemplateTable::ldc(bool wide) { __ bind(notInt); // __ cmp(O2, JVM_CONSTANT_String); + __ brx(Assembler::equal, true, Assembler::pt, isString); + __ delayed()->cmp(O2, JVM_CONSTANT_Object); __ brx(Assembler::notEqual, true, Assembler::pt, notString); __ delayed()->ldf(FloatRegisterImpl::S, O0, O1, Ftos_f); + __ bind(isString); __ ld_ptr(O0, O1, Otos_i); __ verify_oop(Otos_i); __ push(atos); diff --git a/src/cpu/x86/vm/templateTable_x86_32.cpp b/src/cpu/x86/vm/templateTable_x86_32.cpp index b423fe17055f0dcdbff9c5439a4ef1b6c0318f5c..01bd4728bedd573c818763a71014aab050cf6b4c 100644 --- a/src/cpu/x86/vm/templateTable_x86_32.cpp +++ b/src/cpu/x86/vm/templateTable_x86_32.cpp @@ -373,15 +373,17 @@ void TemplateTable::ldc(bool wide) { __ jcc(Assembler::equal, L); __ cmpl(rdx, JVM_CONSTANT_String); __ jcc(Assembler::equal, L); + __ cmpl(rdx, JVM_CONSTANT_Object); + __ jcc(Assembler::equal, L); __ stop("unexpected tag type in ldc"); __ bind(L); } #endif Label isOop; // atos and itos - // String is only oop type we will see here - __ cmpl(rdx, JVM_CONSTANT_String); - __ jccb(Assembler::equal, isOop); + // Integer is only non-oop type we will see here + __ cmpl(rdx, JVM_CONSTANT_Integer); + __ jccb(Assembler::notEqual, isOop); __ movl(rax, Address(rcx, rbx, Address::times_ptr, base_offset)); __ push(itos); __ jmp(Done); diff --git a/src/cpu/x86/vm/templateTable_x86_64.cpp b/src/cpu/x86/vm/templateTable_x86_64.cpp index db37cd68e0f3b2b309302abb3ef13ef36f66cfab..a88bcb71ed7e7721c9d2750ba1547cbe6edd1f72 100644 --- a/src/cpu/x86/vm/templateTable_x86_64.cpp +++ b/src/cpu/x86/vm/templateTable_x86_64.cpp @@ -385,6 +385,8 @@ void TemplateTable::ldc(bool wide) { __ jcc(Assembler::equal, L); __ cmpl(rdx, JVM_CONSTANT_String); __ jcc(Assembler::equal, L); + __ cmpl(rdx, JVM_CONSTANT_Object); + __ jcc(Assembler::equal, L); __ stop("unexpected tag type in ldc"); __ bind(L); } diff --git a/src/share/vm/ci/ciEnv.cpp b/src/share/vm/ci/ciEnv.cpp index 96ff5a0e8e34e60ae0c0fff0b97b33a2d730f676..58f1aa03f47d6a98e5ddc0d989d33c7f59475f31 100644 --- a/src/share/vm/ci/ciEnv.cpp +++ b/src/share/vm/ci/ciEnv.cpp @@ -50,6 +50,7 @@ #include "oops/oop.inline.hpp" #include "oops/oop.inline2.hpp" #include "prims/jvmtiExport.hpp" +#include "prims/methodHandleWalk.hpp" #include "runtime/init.hpp" #include "runtime/reflection.hpp" #include "runtime/sharedRuntime.hpp" @@ -371,6 +372,7 @@ bool ciEnv::check_klass_accessibility(ciKlass* accessing_klass, // ------------------------------------------------------------------ // ciEnv::get_klass_by_name_impl ciKlass* ciEnv::get_klass_by_name_impl(ciKlass* accessing_klass, + constantPoolHandle cpool, ciSymbol* name, bool require_local) { ASSERT_IN_VM; @@ -386,7 +388,7 @@ ciKlass* ciEnv::get_klass_by_name_impl(ciKlass* accessing_klass, sym->utf8_length()-2, KILL_COMPILE_ON_FATAL_(_unloaded_ciinstance_klass)); ciSymbol* strippedname = get_symbol(strippedsym); - return get_klass_by_name_impl(accessing_klass, strippedname, require_local); + return get_klass_by_name_impl(accessing_klass, cpool, strippedname, require_local); } // Check for prior unloaded klass. The SystemDictionary's answers @@ -443,6 +445,7 @@ ciKlass* ciEnv::get_klass_by_name_impl(ciKlass* accessing_klass, // Get element ciKlass recursively. ciKlass* elem_klass = get_klass_by_name_impl(accessing_klass, + cpool, get_symbol(elem_sym), require_local); if (elem_klass != NULL && elem_klass->is_loaded()) { @@ -451,6 +454,19 @@ ciKlass* ciEnv::get_klass_by_name_impl(ciKlass* accessing_klass, } } + if (found_klass() == NULL && !cpool.is_null() && cpool->has_preresolution()) { + // Look inside the constant pool for pre-resolved class entries. + for (int i = cpool->length() - 1; i >= 1; i--) { + if (cpool->tag_at(i).is_klass()) { + klassOop kls = cpool->resolved_klass_at(i); + if (Klass::cast(kls)->name() == sym) { + found_klass = KlassHandle(THREAD, kls); + break; + } + } + } + } + if (found_klass() != NULL) { // Found it. Build a CI handle. return get_object(found_klass())->as_klass(); @@ -468,6 +484,7 @@ ciKlass* ciEnv::get_klass_by_name(ciKlass* accessing_klass, ciSymbol* klass_name, bool require_local) { GUARDED_VM_ENTRY(return get_klass_by_name_impl(accessing_klass, + constantPoolHandle(), klass_name, require_local);) } @@ -508,13 +525,14 @@ ciKlass* ciEnv::get_klass_by_index_impl(constantPoolHandle cpool, if (klass.is_null()) { // Not found in constant pool. Use the name to do the lookup. ciKlass* k = get_klass_by_name_impl(accessor, + cpool, get_symbol(klass_name), false); // Calculate accessibility the hard way. if (!k->is_loaded()) { is_accessible = false; } else if (k->loader() != accessor->loader() && - get_klass_by_name_impl(accessor, k->name(), true) == NULL) { + get_klass_by_name_impl(accessor, cpool, k->name(), true) == NULL) { // Loaded only remotely. Not linked yet. is_accessible = false; } else { @@ -565,7 +583,7 @@ ciConstant ciEnv::get_constant_by_index_impl(constantPoolHandle cpool, index = cpc_entry->constant_pool_index(); oop obj = cpc_entry->f1(); if (obj != NULL) { - assert(obj->is_instance(), "must be an instance"); + assert(obj->is_instance() || obj->is_array(), "must be a Java reference"); ciObject* ciobj = get_object(obj); return ciConstant(T_OBJECT, ciobj); } @@ -607,7 +625,7 @@ ciConstant ciEnv::get_constant_by_index_impl(constantPoolHandle cpool, return ciConstant(T_OBJECT, klass->java_mirror()); } else if (tag.is_object()) { oop obj = cpool->object_at(index); - assert(obj->is_instance(), "must be an instance"); + assert(obj->is_instance() || obj->is_array(), "must be a Java reference"); ciObject* ciobj = get_object(obj); return ciConstant(T_OBJECT, ciobj); } else if (tag.is_method_type()) { @@ -729,9 +747,35 @@ ciMethod* ciEnv::get_method_by_index_impl(constantPoolHandle cpool, Symbol* name_sym = cpool->name_ref_at(index); Symbol* sig_sym = cpool->signature_ref_at(index); + if (cpool->has_preresolution() + || (holder == ciEnv::MethodHandle_klass() && + methodOopDesc::is_method_handle_invoke_name(name_sym))) { + // Short-circuit lookups for JSR 292-related call sites. + // That is, do not rely only on name-based lookups, because they may fail + // if the names are not resolvable in the boot class loader (7056328). + switch (bc) { + case Bytecodes::_invokevirtual: + case Bytecodes::_invokeinterface: + case Bytecodes::_invokespecial: + case Bytecodes::_invokestatic: + { + methodOop m = constantPoolOopDesc::method_at_if_loaded(cpool, index, bc); + if (m != NULL) { + return get_object(m)->as_method(); + } + } + } + } + if (holder_is_accessible) { // Our declared holder is loaded. instanceKlass* lookup = declared_holder->get_instanceKlass(); methodOop m = lookup_method(accessor->get_instanceKlass(), lookup, name_sym, sig_sym, bc); + if (m != NULL && + (bc == Bytecodes::_invokestatic + ? instanceKlass::cast(m->method_holder())->is_not_initialized() + : !instanceKlass::cast(m->method_holder())->is_loaded())) { + m = NULL; + } if (m != NULL) { // We found the method. return get_object(m)->as_method(); @@ -1046,7 +1090,7 @@ void ciEnv::register_method(ciMethod* target, // ciEnv::find_system_klass ciKlass* ciEnv::find_system_klass(ciSymbol* klass_name) { VM_ENTRY_MARK; - return get_klass_by_name_impl(NULL, klass_name, false); + return get_klass_by_name_impl(NULL, constantPoolHandle(), klass_name, false); } // ------------------------------------------------------------------ diff --git a/src/share/vm/ci/ciEnv.hpp b/src/share/vm/ci/ciEnv.hpp index 17b556fc4b5716f443d74c3f94287c05f65725d0..fb6647ccfe182141c28aa1e8e8549c7460ed230a 100644 --- a/src/share/vm/ci/ciEnv.hpp +++ b/src/share/vm/ci/ciEnv.hpp @@ -137,6 +137,7 @@ private: // Implementation methods for loading and constant pool access. ciKlass* get_klass_by_name_impl(ciKlass* accessing_klass, + constantPoolHandle cpool, ciSymbol* klass_name, bool require_local); ciKlass* get_klass_by_index_impl(constantPoolHandle cpool, diff --git a/src/share/vm/ci/ciField.cpp b/src/share/vm/ci/ciField.cpp index 0655b6e55459e95dde5c678aa4871f1b6ee16dad..ab7bed4aa3e73d8a32de636aa1c7c544f66fbe72 100644 --- a/src/share/vm/ci/ciField.cpp +++ b/src/share/vm/ci/ciField.cpp @@ -287,7 +287,7 @@ ciType* ciField::compute_type() { } ciType* ciField::compute_type_impl() { - ciKlass* type = CURRENT_ENV->get_klass_by_name_impl(_holder, _signature, false); + ciKlass* type = CURRENT_ENV->get_klass_by_name_impl(_holder, constantPoolHandle(), _signature, false); if (!type->is_primitive_type() && is_shared()) { // We must not cache a pointer to an unshared type, in a shared field. bool type_is_also_shared = false; diff --git a/src/share/vm/ci/ciMethod.cpp b/src/share/vm/ci/ciMethod.cpp index d8de8d5277bb600227655517d714e105d8626306..38ab7ad0cee15665e3aeeb1cf8959642ecf31600 100644 --- a/src/share/vm/ci/ciMethod.cpp +++ b/src/share/vm/ci/ciMethod.cpp @@ -125,7 +125,8 @@ ciMethod::ciMethod(methodHandle h_m) : ciObject(h_m) { _name = env->get_symbol(h_m()->name()); _holder = env->get_object(h_m()->method_holder())->as_instance_klass(); ciSymbol* sig_symbol = env->get_symbol(h_m()->signature()); - _signature = new (env->arena()) ciSignature(_holder, sig_symbol); + constantPoolHandle cpool = h_m()->constants(); + _signature = new (env->arena()) ciSignature(_holder, cpool, sig_symbol); _method_data = NULL; // Take a snapshot of these values, so they will be commensurate with the MDO. if (ProfileInterpreter || TieredCompilation) { @@ -152,7 +153,7 @@ ciMethod::ciMethod(ciInstanceKlass* holder, // These fields are always filled in. _name = name; _holder = holder; - _signature = new (CURRENT_ENV->arena()) ciSignature(_holder, signature); + _signature = new (CURRENT_ENV->arena()) ciSignature(_holder, constantPoolHandle(), signature); _intrinsic_id = vmIntrinsics::_none; _liveness = NULL; _can_be_statically_bound = false; diff --git a/src/share/vm/ci/ciMethodHandle.cpp b/src/share/vm/ci/ciMethodHandle.cpp index faa23a91a092ed289d3b709676d44a13ceb6c4d8..0e73abca0474708d9385305c9dc084311ee5fd01 100644 --- a/src/share/vm/ci/ciMethodHandle.cpp +++ b/src/share/vm/ci/ciMethodHandle.cpp @@ -41,6 +41,16 @@ ciMethod* ciMethodHandle::get_adapter_impl(bool is_invokedynamic) const { VM_ENTRY_MARK; Handle h(get_oop()); methodHandle callee(_callee->get_methodOop()); + assert(callee->is_method_handle_invoke(), ""); + oop mt1 = callee->method_handle_type(); + oop mt2 = java_lang_invoke_MethodHandle::type(h()); + if (!java_lang_invoke_MethodType::equals(mt1, mt2)) { + if (PrintMiscellaneous && (Verbose || WizardMode)) { + tty->print_cr("ciMethodHandle::get_adapter: types not equal"); + mt1->print(); mt2->print(); + } + return NULL; + } // We catch all exceptions here that could happen in the method // handle compiler and stop the VM. MethodHandleCompiler mhc(h, callee->name(), callee->signature(), _profile.count(), is_invokedynamic, THREAD); @@ -53,7 +63,7 @@ ciMethod* ciMethodHandle::get_adapter_impl(bool is_invokedynamic) const { if (PrintMiscellaneous && (Verbose || WizardMode)) { tty->print("*** ciMethodHandle::get_adapter => "); PENDING_EXCEPTION->print(); - tty->print("*** get_adapter (%s): ", is_invokedynamic ? "indy" : "mh"); ((ciObject*)this)->print(); //@@ + tty->print("*** get_adapter (%s): ", is_invokedynamic ? "indy" : "mh"); ((ciObject*)this)->print(); } CLEAR_PENDING_EXCEPTION; return NULL; diff --git a/src/share/vm/ci/ciObjArrayKlass.cpp b/src/share/vm/ci/ciObjArrayKlass.cpp index 2fcf2c9be6d0305cbdcbcb499799c2fd84ffcf7a..d918983093ba58c3e69e39f260d7c3a21a3a5569 100644 --- a/src/share/vm/ci/ciObjArrayKlass.cpp +++ b/src/share/vm/ci/ciObjArrayKlass.cpp @@ -93,6 +93,7 @@ ciKlass* ciObjArrayKlass::element_klass() { // element klass by name. _element_klass = CURRENT_THREAD_ENV->get_klass_by_name_impl( this, + constantPoolHandle(), construct_array_name(base_element_klass()->name(), dimension() - 1), false); diff --git a/src/share/vm/ci/ciSignature.cpp b/src/share/vm/ci/ciSignature.cpp index 037ba8a8fef11eabc93436ba05fa5d0421f9bb5a..8754fb4e8937dc85eebb246a86dc4296e68a8077 100644 --- a/src/share/vm/ci/ciSignature.cpp +++ b/src/share/vm/ci/ciSignature.cpp @@ -35,7 +35,7 @@ // ------------------------------------------------------------------ // ciSignature::ciSignature -ciSignature::ciSignature(ciKlass* accessing_klass, ciSymbol* symbol) { +ciSignature::ciSignature(ciKlass* accessing_klass, constantPoolHandle cpool, ciSymbol* symbol) { ASSERT_IN_VM; EXCEPTION_CONTEXT; _accessing_klass = accessing_klass; @@ -64,7 +64,7 @@ ciSignature::ciSignature(ciKlass* accessing_klass, ciSymbol* symbol) { CLEAR_PENDING_EXCEPTION; } else { ciSymbol* klass_name = env->get_symbol(name); - type = env->get_klass_by_name_impl(_accessing_klass, klass_name, false); + type = env->get_klass_by_name_impl(_accessing_klass, cpool, klass_name, false); } } _types->append(type); diff --git a/src/share/vm/ci/ciSignature.hpp b/src/share/vm/ci/ciSignature.hpp index f455600e7ac8712a5227d97fa0c69097efa3cfef..aaeac416c817807a690cb2f40e3be43230e35ba8 100644 --- a/src/share/vm/ci/ciSignature.hpp +++ b/src/share/vm/ci/ciSignature.hpp @@ -44,7 +44,7 @@ private: friend class ciMethod; - ciSignature(ciKlass* accessing_klass, ciSymbol* signature); + ciSignature(ciKlass* accessing_klass, constantPoolHandle cpool, ciSymbol* signature); void get_all_klasses(); diff --git a/src/share/vm/classfile/javaClasses.cpp b/src/share/vm/classfile/javaClasses.cpp index eb512306f381b549d0ac807a006fdf4770b63b42..218476fa0d90aac60ed46d4adb1cd39035f32305 100644 --- a/src/share/vm/classfile/javaClasses.cpp +++ b/src/share/vm/classfile/javaClasses.cpp @@ -2574,6 +2574,18 @@ Symbol* java_lang_invoke_MethodType::as_signature(oop mt, bool intern_if_not_fou return name; } +bool java_lang_invoke_MethodType::equals(oop mt1, oop mt2) { + if (rtype(mt1) != rtype(mt2)) + return false; + if (ptype_count(mt1) != ptype_count(mt2)) + return false; + for (int i = ptype_count(mt1) - 1; i >= 0; i--) { + if (ptype(mt1, i) != ptype(mt2, i)) + return false; + } + return true; +} + oop java_lang_invoke_MethodType::rtype(oop mt) { assert(is_instance(mt), "must be a MethodType"); return mt->obj_field(_rtype_offset); diff --git a/src/share/vm/classfile/javaClasses.hpp b/src/share/vm/classfile/javaClasses.hpp index eb104b84361cbbbca3dcd42b868705ac98f19a0c..02c0ce90b7bc78e6280acda2aee1165042ecb2d4 100644 --- a/src/share/vm/classfile/javaClasses.hpp +++ b/src/share/vm/classfile/javaClasses.hpp @@ -1079,6 +1079,8 @@ class java_lang_invoke_MethodType: AllStatic { return obj != NULL && obj->klass() == SystemDictionary::MethodType_klass(); } + static bool equals(oop mt1, oop mt2); + // Accessors for code generation: static int rtype_offset_in_bytes() { return _rtype_offset; } static int ptypes_offset_in_bytes() { return _ptypes_offset; } diff --git a/src/share/vm/classfile/systemDictionary.cpp b/src/share/vm/classfile/systemDictionary.cpp index 9d870ba1b9d62d996f1b41451a800d968742a3e2..892adb1d60af0ba83b0055e9183d54fc9a56feae 100644 --- a/src/share/vm/classfile/systemDictionary.cpp +++ b/src/share/vm/classfile/systemDictionary.cpp @@ -2367,6 +2367,8 @@ methodOop SystemDictionary::find_method_handle_invoke(Symbol* name, // Link m to his method type, if it is suitably generic. oop mtform = java_lang_invoke_MethodType::form(mt()); if (mtform != NULL && mt() == java_lang_invoke_MethodTypeForm::erasedType(mtform) + // vmlayout must be an invokeExact: + && name_id == vmSymbols::VM_SYMBOL_ENUM_NAME(invokeExact_name) && java_lang_invoke_MethodTypeForm::vmlayout_offset_in_bytes() > 0) { java_lang_invoke_MethodTypeForm::init_vmlayout(mtform, m()); } diff --git a/src/share/vm/interpreter/linkResolver.cpp b/src/share/vm/interpreter/linkResolver.cpp index a75de92cace43aa5c5fd21eb52a0b056b9a48c45..7b42fe34f332524bcc512a6b9210e8e686715b4f 100644 --- a/src/share/vm/interpreter/linkResolver.cpp +++ b/src/share/vm/interpreter/linkResolver.cpp @@ -294,6 +294,16 @@ void LinkResolver::resolve_method(methodHandle& resolved_method, KlassHandle& re Symbol* method_signature = pool->signature_ref_at(index); KlassHandle current_klass(THREAD, pool->pool_holder()); + if (pool->has_preresolution() + || (resolved_klass() == SystemDictionary::MethodHandle_klass() && + methodOopDesc::is_method_handle_invoke_name(method_name))) { + methodOop result_oop = constantPoolOopDesc::method_at_if_loaded(pool, index); + if (result_oop != NULL) { + resolved_method = methodHandle(THREAD, result_oop); + return; + } + } + resolve_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, true, CHECK); } diff --git a/src/share/vm/oops/constantPoolKlass.cpp b/src/share/vm/oops/constantPoolKlass.cpp index a5ca5cdfc7f0fefb838aa33df12d1658ab870c78..362e275c2c2bb01801602d066bac37f55b087c73 100644 --- a/src/share/vm/oops/constantPoolKlass.cpp +++ b/src/share/vm/oops/constantPoolKlass.cpp @@ -310,10 +310,14 @@ void constantPoolKlass::oop_print_on(oop obj, outputStream* st) { st->print(" - flags: 0x%x", cp->flags()); if (cp->has_pseudo_string()) st->print(" has_pseudo_string"); if (cp->has_invokedynamic()) st->print(" has_invokedynamic"); + if (cp->has_preresolution()) st->print(" has_preresolution"); st->cr(); } + if (cp->pool_holder() != NULL) { + bool extra = (instanceKlass::cast(cp->pool_holder())->constants() != cp); + st->print_cr(" - holder: " INTPTR_FORMAT "%s", cp->pool_holder(), (extra? " (extra)" : "")); + } st->print_cr(" - cache: " INTPTR_FORMAT, cp->cache()); - for (int index = 1; index < cp->length(); index++) { // Index 0 is unused st->print(" - %3d : ", index); cp->tag_at(index).print_on(st); @@ -414,10 +418,15 @@ void constantPoolKlass::oop_print_value_on(oop obj, outputStream* st) { st->print("constant pool [%d]", cp->length()); if (cp->has_pseudo_string()) st->print("/pseudo_string"); if (cp->has_invokedynamic()) st->print("/invokedynamic"); + if (cp->has_preresolution()) st->print("/preresolution"); if (cp->operands() != NULL) st->print("/operands[%d]", cp->operands()->length()); cp->print_address_on(st); st->print(" for "); cp->pool_holder()->print_value_on(st); + if (cp->pool_holder() != NULL) { + bool extra = (instanceKlass::cast(cp->pool_holder())->constants() != cp); + if (extra) st->print(" (extra)"); + } if (cp->cache() != NULL) { st->print(" cache=" PTR_FORMAT, cp->cache()); } diff --git a/src/share/vm/oops/constantPoolOop.cpp b/src/share/vm/oops/constantPoolOop.cpp index 00aa94f20bd9e69f56be69d5a2ee61f144c9191a..a46f4ebeb02c5537a651847ba94b2eea7ea64619 100644 --- a/src/share/vm/oops/constantPoolOop.cpp +++ b/src/share/vm/oops/constantPoolOop.cpp @@ -266,6 +266,29 @@ klassOop constantPoolOopDesc::klass_ref_at_if_loaded_check(constantPoolHandle th } +methodOop constantPoolOopDesc::method_at_if_loaded(constantPoolHandle cpool, + int which, Bytecodes::Code invoke_code) { + assert(!constantPoolCacheOopDesc::is_secondary_index(which), "no indy instruction here"); + if (cpool->cache() == NULL) return false; // nothing to load yet + int cache_index = which - CPCACHE_INDEX_TAG; + if (!(cache_index >= 0 && cache_index < cpool->cache()->length())) { + if (PrintMiscellaneous && (Verbose||WizardMode)) { + tty->print_cr("bad operand %d for %d in:", which, invoke_code); cpool->print(); + } + return NULL; + } + ConstantPoolCacheEntry* e = cpool->cache()->entry_at(cache_index); + if (invoke_code != Bytecodes::_illegal) + return e->get_method_if_resolved(invoke_code, cpool); + Bytecodes::Code bc; + if ((bc = e->bytecode_1()) != (Bytecodes::Code)0) + return e->get_method_if_resolved(bc, cpool); + if ((bc = e->bytecode_2()) != (Bytecodes::Code)0) + return e->get_method_if_resolved(bc, cpool); + return NULL; +} + + Symbol* constantPoolOopDesc::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); diff --git a/src/share/vm/oops/constantPoolOop.hpp b/src/share/vm/oops/constantPoolOop.hpp index d8dd7844d6ee334f88469e4513238c4364cc644a..c2f985d7750b1b990a620deec0a76b584dbd08a7 100644 --- a/src/share/vm/oops/constantPoolOop.hpp +++ b/src/share/vm/oops/constantPoolOop.hpp @@ -103,7 +103,8 @@ class constantPoolOopDesc : public oopDesc { enum FlagBit { FB_has_invokedynamic = 1, - FB_has_pseudo_string = 2 + FB_has_pseudo_string = 2, + FB_has_preresolution = 3 }; int flags() const { return _flags; } @@ -179,8 +180,10 @@ class constantPoolOopDesc : public oopDesc { bool has_pseudo_string() const { return flag_at(FB_has_pseudo_string); } bool has_invokedynamic() const { return flag_at(FB_has_invokedynamic); } + bool has_preresolution() const { return flag_at(FB_has_preresolution); } void set_pseudo_string() { set_flag_at(FB_has_pseudo_string); } void set_invokedynamic() { set_flag_at(FB_has_invokedynamic); } + void set_preresolution() { set_flag_at(FB_has_preresolution); } // Klass holding pool klassOop pool_holder() const { return _pool_holder; } @@ -663,6 +666,8 @@ class constantPoolOopDesc : public oopDesc { friend class SystemDictionary; // Used by compiler to prevent classloading. + static methodOop method_at_if_loaded (constantPoolHandle this_oop, int which, + Bytecodes::Code bc = Bytecodes::_illegal); static klassOop klass_at_if_loaded (constantPoolHandle this_oop, int which); static klassOop klass_ref_at_if_loaded (constantPoolHandle this_oop, int which); // Same as above - but does LinkResolving. diff --git a/src/share/vm/oops/cpCacheOop.cpp b/src/share/vm/oops/cpCacheOop.cpp index 049247b9d7fa45b5a218c4352d279eb7613ac920..087c8a1f9c5b8d7dadb330bfba04c8be2c0f70d8 100644 --- a/src/share/vm/oops/cpCacheOop.cpp +++ b/src/share/vm/oops/cpCacheOop.cpp @@ -295,6 +295,50 @@ void ConstantPoolCacheEntry::set_dynamic_call(Handle call_site, methodHandle sig } +methodOop ConstantPoolCacheEntry::get_method_if_resolved(Bytecodes::Code invoke_code, constantPoolHandle cpool) { + assert(invoke_code > (Bytecodes::Code)0, "bad query"); + if (is_secondary_entry()) { + return cpool->cache()->entry_at(main_entry_index())->get_method_if_resolved(invoke_code, cpool); + } + // Decode the action of set_method and set_interface_call + if (bytecode_1() == invoke_code) { + oop f1 = _f1; + if (f1 != NULL) { + switch (invoke_code) { + case Bytecodes::_invokeinterface: + assert(f1->is_klass(), ""); + return klassItable::method_for_itable_index(klassOop(f1), (int) f2()); + case Bytecodes::_invokestatic: + case Bytecodes::_invokespecial: + assert(f1->is_method(), ""); + return methodOop(f1); + } + } + } + if (bytecode_2() == invoke_code) { + switch (invoke_code) { + case Bytecodes::_invokevirtual: + if (is_vfinal()) { + // invokevirtual + methodOop m = methodOop((intptr_t) f2()); + assert(m->is_method(), ""); + return m; + } else { + int holder_index = cpool->uncached_klass_ref_index_at(constant_pool_index()); + if (cpool->tag_at(holder_index).is_klass()) { + klassOop klass = cpool->resolved_klass_at(holder_index); + if (!Klass::cast(klass)->oop_is_instance()) + klass = SystemDictionary::Object_klass(); + return instanceKlass::cast(klass)->method_at_vtable((int) f2()); + } + } + } + } + return NULL; +} + + + class LocalOopClosure: public OopClosure { private: void (*_f)(oop*); diff --git a/src/share/vm/oops/cpCacheOop.hpp b/src/share/vm/oops/cpCacheOop.hpp index 75573ab62cf2b90d8598a971a63f818579e6bf44..c3d1847791180c61aed648739c4d47527ac0c999 100644 --- a/src/share/vm/oops/cpCacheOop.hpp +++ b/src/share/vm/oops/cpCacheOop.hpp @@ -194,6 +194,8 @@ class ConstantPoolCacheEntry VALUE_OBJ_CLASS_SPEC { methodHandle signature_invoker // determines signature information ); + methodOop get_method_if_resolved(Bytecodes::Code invoke_code, constantPoolHandle cpool); + // For JVM_CONSTANT_InvokeDynamic cache entries: void initialize_bootstrap_method_index_in_cache(int bsm_cache_index); int bootstrap_method_index_in_cache(); diff --git a/src/share/vm/oops/methodOop.cpp b/src/share/vm/oops/methodOop.cpp index 9fab7ce89d4a60df4d5dd0e2f32249a324074a9e..7b4c1310e8a4a07bc787742cf4c4197c6f1e8117 100644 --- a/src/share/vm/oops/methodOop.cpp +++ b/src/share/vm/oops/methodOop.cpp @@ -928,14 +928,40 @@ methodHandle methodOopDesc::make_invoke_method(KlassHandle holder, name->increment_refcount(); signature->increment_refcount(); + // record non-BCP method types in the constant pool + GrowableArray* extra_klasses = NULL; + for (int i = -1, len = java_lang_invoke_MethodType::ptype_count(method_type()); i < len; i++) { + oop ptype = (i == -1 + ? java_lang_invoke_MethodType::rtype(method_type()) + : java_lang_invoke_MethodType::ptype(method_type(), i)); + klassOop klass = check_non_bcp_klass(java_lang_Class::as_klassOop(ptype)); + if (klass != NULL) { + if (extra_klasses == NULL) + extra_klasses = new GrowableArray(len+1); + bool dup = false; + for (int j = 0; j < extra_klasses->length(); j++) { + if (extra_klasses->at(j) == klass) { dup = true; break; } + } + if (!dup) + extra_klasses->append(KlassHandle(THREAD, klass)); + } + } + + int extra_klass_count = (extra_klasses == NULL ? 0 : extra_klasses->length()); + int cp_length = _imcp_limit + extra_klass_count; constantPoolHandle cp; { - constantPoolOop cp_oop = oopFactory::new_constantPool(_imcp_limit, IsSafeConc, CHECK_(empty)); + constantPoolOop cp_oop = oopFactory::new_constantPool(cp_length, IsSafeConc, CHECK_(empty)); cp = constantPoolHandle(THREAD, cp_oop); } cp->symbol_at_put(_imcp_invoke_name, name); cp->symbol_at_put(_imcp_invoke_signature, signature); cp->string_at_put(_imcp_method_type_value, Universe::the_null_string()); + for (int j = 0; j < extra_klass_count; j++) { + KlassHandle klass = extra_klasses->at(j); + cp->klass_at_put(_imcp_limit + j, klass()); + } + cp->set_preresolution(); cp->set_pool_holder(holder()); // set up the fancy stuff: @@ -984,6 +1010,14 @@ methodHandle methodOopDesc::make_invoke_method(KlassHandle holder, return m; } +klassOop methodOopDesc::check_non_bcp_klass(klassOop klass) { + if (klass != NULL && Klass::cast(klass)->class_loader() != NULL) { + if (Klass::cast(klass)->oop_is_objArray()) + klass = objArrayKlass::cast(klass)->bottom_klass(); + return klass; + } + return NULL; +} methodHandle methodOopDesc:: clone_with_new_data(methodHandle m, u_char* new_code, int new_code_length, diff --git a/src/share/vm/oops/methodOop.hpp b/src/share/vm/oops/methodOop.hpp index bace4defe3c5753b8d65a2293c7363d2dacb7acb..e8e73c63105aaa4d4cc424a0bc68348bee137f8d 100644 --- a/src/share/vm/oops/methodOop.hpp +++ b/src/share/vm/oops/methodOop.hpp @@ -600,6 +600,7 @@ class methodOopDesc : public oopDesc { Symbol* signature, //anything at all Handle method_type, TRAPS); + static klassOop check_non_bcp_klass(klassOop klass); // these operate only on invoke methods: oop method_handle_type() const; static jint* method_type_offsets_chain(); // series of pointer-offsets, terminated by -1 diff --git a/src/share/vm/prims/methodHandleWalk.cpp b/src/share/vm/prims/methodHandleWalk.cpp index bf8a6ca3e3bc5db9aaa7ca488f30efa0ab92d9f5..504b9003b56709ffc221c985f51ea00f42ad3117 100644 --- a/src/share/vm/prims/methodHandleWalk.cpp +++ b/src/share/vm/prims/methodHandleWalk.cpp @@ -425,6 +425,8 @@ MethodHandleWalker::walk(TRAPS) { ArgToken arg = _outgoing.at(arg_slot); assert(dest == arg.basic_type(), ""); arg = make_conversion(T_OBJECT, dest_klass, Bytecodes::_checkcast, arg, CHECK_(empty)); + // replace the object by the result of the cast, to make the compiler happy: + change_argument(T_OBJECT, arg_slot, T_OBJECT, arg); debug_only(dest_klass = (klassOop)badOop); break; } @@ -467,7 +469,7 @@ MethodHandleWalker::walk(TRAPS) { ArgToken arglist[2]; arglist[0] = arg; // outgoing 'this' arglist[1] = ArgToken(); // sentinel - arg = make_invoke(NULL, unboxer, Bytecodes::_invokevirtual, false, 1, &arglist[0], CHECK_(empty)); + arg = make_invoke(methodHandle(), unboxer, Bytecodes::_invokevirtual, false, 1, &arglist[0], CHECK_(empty)); change_argument(T_OBJECT, arg_slot, dest, arg); break; } @@ -483,7 +485,7 @@ MethodHandleWalker::walk(TRAPS) { ArgToken arglist[2]; arglist[0] = arg; // outgoing value arglist[1] = ArgToken(); // sentinel - arg = make_invoke(NULL, boxer, Bytecodes::_invokestatic, false, 1, &arglist[0], CHECK_(empty)); + arg = make_invoke(methodHandle(), boxer, Bytecodes::_invokestatic, false, 1, &arglist[0], CHECK_(empty)); change_argument(src, arg_slot, T_OBJECT, arg); break; } @@ -599,8 +601,9 @@ MethodHandleWalker::walk(TRAPS) { lose("bad vmlayout slot", CHECK_(empty)); } // FIXME: consider inlining the invokee at the bytecode level - ArgToken ret = make_invoke(methodOop(invoker), vmIntrinsics::_none, + ArgToken ret = make_invoke(methodHandle(THREAD, methodOop(invoker)), vmIntrinsics::_invokeGeneric, Bytecodes::_invokevirtual, false, 1+argc, &arglist[0], CHECK_(empty)); + // The iid = _invokeGeneric really means to adjust reference types as needed. DEBUG_ONLY(invoker = NULL); if (rtype == T_OBJECT) { klassOop rklass = java_lang_Class::as_klassOop( java_lang_invoke_MethodType::rtype(recursive_mtype()) ); @@ -657,7 +660,7 @@ MethodHandleWalker::walk(TRAPS) { arglist[0] = array_arg; // value to check arglist[1] = length_arg; // length to check arglist[2] = ArgToken(); // sentinel - make_invoke(NULL, vmIntrinsics::_checkSpreadArgument, + make_invoke(methodHandle(), vmIntrinsics::_checkSpreadArgument, Bytecodes::_invokestatic, false, 2, &arglist[0], CHECK_(empty)); // Spread out the array elements. @@ -680,7 +683,7 @@ MethodHandleWalker::walk(TRAPS) { ArgToken offset_arg = make_prim_constant(T_INT, &offset_jvalue, CHECK_(empty)); ArgToken element_arg = make_fetch(element_type, element_klass(), aload_op, array_arg, offset_arg, CHECK_(empty)); change_argument(T_VOID, ap, element_type, element_arg); - ap += type2size[element_type]; + //ap += type2size[element_type]; // don't do this; insert next arg to *right* of previous } break; } @@ -731,7 +734,7 @@ MethodHandleWalker::walk(TRAPS) { } assert(ap == _outgoing_argc, ""); arglist[ap] = ArgToken(); // add a sentinel, for the sake of asserts - return make_invoke(chain().last_method_oop(), + return make_invoke(chain().last_method(), vmIntrinsics::_none, chain().last_invoke_code(), true, ap, arglist, THREAD); @@ -853,7 +856,6 @@ void MethodHandleWalker::retype_raw_conversion(BasicType src, BasicType dst, boo if (src != dst) { if (MethodHandles::same_basic_type_for_returns(src, dst, /*raw*/ true)) { if (MethodHandles::is_float_fixed_reinterpretation_cast(src, dst)) { - if (for_return) Untested("MHW return raw conversion"); // still untested vmIntrinsics::ID iid = vmIntrinsics::for_raw_conversion(src, dst); if (iid == vmIntrinsics::_none) { lose("no raw conversion method", CHECK); @@ -865,18 +867,24 @@ void MethodHandleWalker::retype_raw_conversion(BasicType src, BasicType dst, boo assert(arg.token_type() >= tt_symbolic || src == arg.basic_type(), "sanity"); arglist[0] = arg; // outgoing 'this' arglist[1] = ArgToken(); // sentinel - arg = make_invoke(NULL, iid, Bytecodes::_invokestatic, false, 1, &arglist[0], CHECK); + arg = make_invoke(methodHandle(), iid, Bytecodes::_invokestatic, false, 1, &arglist[0], CHECK); change_argument(src, slot, dst, arg); } else { // return type conversion - klassOop arg_klass = NULL; - arglist[0] = make_parameter(src, arg_klass, -1, CHECK); // return value - arglist[1] = ArgToken(); // sentinel - (void) make_invoke(NULL, iid, Bytecodes::_invokestatic, false, 1, &arglist[0], CHECK); + if (_return_conv == vmIntrinsics::_none) { + _return_conv = iid; + } else if (_return_conv == vmIntrinsics::for_raw_conversion(dst, src)) { + _return_conv = vmIntrinsics::_none; + } else if (_return_conv != zero_return_conv()) { + lose(err_msg("requested raw return conversion not allowed: %s -> %s (before %s)", type2name(src), type2name(dst), vmIntrinsics::name_at(_return_conv)), CHECK); + } } } else { // Nothing to do. } + } else if (for_return && (!is_subword_type(src) || !is_subword_type(dst))) { + // This can occur in exception-throwing MHs, which have a fictitious return value encoded as Void or Empty. + _return_conv = zero_return_conv(); } else if (src == T_OBJECT && is_java_primitive(dst)) { // ref-to-prim: discard ref, push zero lose("requested ref-to-prim conversion not expected", CHECK); @@ -896,6 +904,7 @@ MethodHandleCompiler::MethodHandleCompiler(Handle root, Symbol* name, Symbol* si _thread(THREAD), _bytecode(THREAD, 50), _constants(THREAD, 10), + _non_bcp_klasses(THREAD, 5), _cur_stack(0), _max_stack(0), _rtype(T_ILLEGAL) @@ -908,6 +917,15 @@ MethodHandleCompiler::MethodHandleCompiler(Handle root, Symbol* name, Symbol* si _name_index = cpool_symbol_put(name); _signature_index = cpool_symbol_put(signature); + // To make the resulting methods more recognizable by + // stack walkers and compiler heuristics, + // we put them in holder class MethodHandle. + // See klass_is_method_handle_adapter_holder + // and methodOopDesc::is_method_handle_adapter. + _target_klass = SystemDictionaryHandles::MethodHandle_klass(); + + check_non_bcp_klasses(java_lang_invoke_MethodHandle::type(root()), CHECK); + // Get return type klass. Handle first_mtype(THREAD, chain().method_type_oop()); // _rklass is NULL for primitives. @@ -929,6 +947,7 @@ methodHandle MethodHandleCompiler::compile(TRAPS) { assert(_thread == THREAD, "must be same thread"); methodHandle nullHandle; (void) walk(CHECK_(nullHandle)); + record_non_bcp_klasses(); return get_method_oop(CHECK_(nullHandle)); } @@ -1197,10 +1216,18 @@ void MethodHandleCompiler::emit_load_constant(ArgToken arg) { } case T_OBJECT: { Handle value = arg.object(); - if (value.is_null()) + if (value.is_null()) { emit_bc(Bytecodes::_aconst_null); - else - emit_bc(Bytecodes::_ldc, cpool_object_put(value)); + break; + } + if (java_lang_Class::is_instance(value())) { + klassOop k = java_lang_Class::as_klassOop(value()); + if (k != NULL) { + emit_bc(Bytecodes::_ldc, cpool_klass_put(k)); + break; + } + } + emit_bc(Bytecodes::_ldc, cpool_object_put(value)); break; } default: @@ -1260,6 +1287,7 @@ MethodHandleCompiler::make_conversion(BasicType type, klassOop tk, Bytecodes::Co index = src.index(); } emit_bc(op, cpool_klass_put(tk)); + check_non_bcp_klass(tk, CHECK_(src)); // Allocate a new local for the type so that we don't hide the // previous type from the verifier. index = new_local_index(type); @@ -1292,15 +1320,15 @@ jvalue MethodHandleCompiler::one_jvalue = { 1 }; // Emit bytecodes for the given invoke instruction. MethodHandleWalker::ArgToken -MethodHandleCompiler::make_invoke(methodOop m, vmIntrinsics::ID iid, +MethodHandleCompiler::make_invoke(methodHandle m, vmIntrinsics::ID iid, Bytecodes::Code op, bool tailcall, int argc, MethodHandleWalker::ArgToken* argv, TRAPS) { ArgToken zero; - if (m == NULL) { + if (m.is_null()) { // Get the intrinsic methodOop. - m = vmIntrinsics::method_for(iid); - if (m == NULL) { + m = methodHandle(THREAD, vmIntrinsics::method_for(iid)); + if (m.is_null()) { lose(vmIntrinsics::name_at(iid), CHECK_(zero)); } } @@ -1309,18 +1337,46 @@ MethodHandleCompiler::make_invoke(methodOop m, vmIntrinsics::ID iid, Symbol* name = m->name(); Symbol* signature = m->signature(); + if (iid == vmIntrinsics::_invokeGeneric && + argc >= 1 && argv[0].token_type() == tt_constant) { + assert(m->intrinsic_id() == vmIntrinsics::_invokeExact, ""); + Handle receiver = argv[0].object(); + Handle rtype(THREAD, java_lang_invoke_MethodHandle::type(receiver())); + Handle mtype(THREAD, m->method_handle_type()); + if (rtype() != mtype()) { + assert(java_lang_invoke_MethodType::form(rtype()) == + java_lang_invoke_MethodType::form(mtype()), + "must be the same shape"); + // customize m to the exact required rtype + bool has_non_bcp_klass = check_non_bcp_klasses(rtype(), CHECK_(zero)); + TempNewSymbol sig2 = java_lang_invoke_MethodType::as_signature(rtype(), true, CHECK_(zero)); + methodHandle m2; + if (!has_non_bcp_klass) { + methodOop m2_oop = SystemDictionary::find_method_handle_invoke(m->name(), sig2, + KlassHandle(), CHECK_(zero)); + m2 = methodHandle(THREAD, m2_oop); + } + if (m2.is_null()) { + // just build it fresh + m2 = methodOopDesc::make_invoke_method(klass, m->name(), sig2, rtype, CHECK_(zero)); + if (m2.is_null()) + lose(err_msg("no customized invoker %s", sig2->as_utf8()), CHECK_(zero)); + } + m = m2; + signature = m->signature(); + } + } + + check_non_bcp_klass(klass, CHECK_(zero)); + if (m->is_method_handle_invoke()) { + check_non_bcp_klasses(m->method_handle_type(), CHECK_(zero)); + } + // Count the number of arguments, not the size ArgumentCount asc(signature); assert(argc == asc.size() + ((op == Bytecodes::_invokestatic || op == Bytecodes::_invokedynamic) ? 0 : 1), "argc mismatch"); - if (tailcall) { - // Actually, in order to make these methods more recognizable, - // let's put them in holder class MethodHandle. That way stack - // walkers and compiler heuristics can recognize them. - _target_klass = SystemDictionary::MethodHandle_klass(); - } - // Inline the method. InvocationCounter* ic = m->invocation_counter(); ic->set_carry_flag(); @@ -1353,7 +1409,7 @@ MethodHandleCompiler::make_invoke(methodOop m, vmIntrinsics::ID iid, int signature_index = cpool_symbol_put(signature); int name_and_type_index = cpool_name_and_type_put(name_index, signature_index); int klass_index = cpool_klass_put(klass); - int methodref_index = cpool_methodref_put(klass_index, name_and_type_index); + int methodref_index = cpool_methodref_put(op, klass_index, name_and_type_index, m); // Generate invoke. switch (op) { @@ -1380,6 +1436,20 @@ MethodHandleCompiler::make_invoke(methodOop m, vmIntrinsics::ID iid, stack_push(rbt); // The return value is already pushed onto the stack. ArgToken ret; if (tailcall) { + if (return_conv() == zero_return_conv()) { + rbt = T_VOID; // discard value + } else if (return_conv() != vmIntrinsics::_none) { + // return value conversion + int index = new_local_index(rbt); + emit_store(rbt, index); + ArgToken arglist[2]; + arglist[0] = ArgToken(tt_temporary, rbt, index); + arglist[1] = ArgToken(); // sentinel + ret = make_invoke(methodHandle(), return_conv(), Bytecodes::_invokestatic, false, 1, &arglist[0], CHECK_(zero)); + set_return_conv(vmIntrinsics::_none); + rbt = ret.basic_type(); + emit_load(rbt, ret.index()); + } if (rbt != _rtype) { if (rbt == T_VOID) { // push a zero of the right sort @@ -1425,6 +1495,7 @@ MethodHandleCompiler::make_invoke(methodOop m, vmIntrinsics::ID iid, case T_OBJECT: if (_rklass.not_null() && _rklass() != SystemDictionary::Object_klass() && !Klass::cast(_rklass())->is_interface()) { emit_bc(Bytecodes::_checkcast, cpool_klass_put(_rklass())); + check_non_bcp_klass(_rklass(), CHECK_(zero)); } emit_bc(Bytecodes::_areturn); break; @@ -1525,6 +1596,52 @@ int MethodHandleCompiler::cpool_primitive_put(BasicType bt, jvalue* con) { return index; } +bool MethodHandleCompiler::check_non_bcp_klasses(Handle method_type, TRAPS) { + bool res = false; + for (int i = -1, len = java_lang_invoke_MethodType::ptype_count(method_type()); i < len; i++) { + oop ptype = (i == -1 + ? java_lang_invoke_MethodType::rtype(method_type()) + : java_lang_invoke_MethodType::ptype(method_type(), i)); + res |= check_non_bcp_klass(java_lang_Class::as_klassOop(ptype), CHECK_(false)); + } + return res; +} + +bool MethodHandleCompiler::check_non_bcp_klass(klassOop klass, TRAPS) { + klass = methodOopDesc::check_non_bcp_klass(klass); + if (klass != NULL) { + Symbol* name = Klass::cast(klass)->name(); + for (int i = _non_bcp_klasses.length() - 1; i >= 0; i--) { + klassOop k2 = _non_bcp_klasses.at(i)(); + if (Klass::cast(k2)->name() == name) { + if (k2 != klass) { + lose(err_msg("unsupported klass name alias %s", name->as_utf8()), THREAD); + } + return true; + } + } + _non_bcp_klasses.append(KlassHandle(THREAD, klass)); + return true; + } + return false; +} + +void MethodHandleCompiler::record_non_bcp_klasses() { + // Append extra klasses to constant pool, to guide klass lookup. + for (int k = 0; k < _non_bcp_klasses.length(); k++) { + klassOop non_bcp_klass = _non_bcp_klasses.at(k)(); + bool add_to_cp = true; + for (int j = 1; j < _constants.length(); j++) { + ConstantValue* cv = _constants.at(j); + if (cv != NULL && cv->tag() == JVM_CONSTANT_Class + && cv->klass_oop() == non_bcp_klass) { + add_to_cp = false; + break; + } + } + if (add_to_cp) cpool_klass_put(non_bcp_klass); + } +} constantPoolHandle MethodHandleCompiler::get_constant_pool(TRAPS) const { constantPoolHandle nullHandle; @@ -1544,6 +1661,8 @@ constantPoolHandle MethodHandleCompiler::get_constant_pool(TRAPS) const { case JVM_CONSTANT_Double: cpool->double_at_put( i, cv->get_jdouble() ); break; case JVM_CONSTANT_Class: cpool->klass_at_put( i, cv->klass_oop() ); break; case JVM_CONSTANT_Methodref: cpool->method_at_put( i, cv->first_index(), cv->second_index()); break; + case JVM_CONSTANT_InterfaceMethodref: + cpool->interface_method_at_put(i, cv->first_index(), cv->second_index()); break; case JVM_CONSTANT_NameAndType: cpool->name_and_type_at_put(i, cv->first_index(), cv->second_index()); break; case JVM_CONSTANT_Object: cpool->object_at_put( i, cv->object_oop() ); break; default: ShouldNotReachHere(); @@ -1558,6 +1677,8 @@ constantPoolHandle MethodHandleCompiler::get_constant_pool(TRAPS) const { } } + cpool->set_preresolution(); + // Set the constant pool holder to the target method's class. cpool->set_pool_holder(_target_klass()); @@ -1606,6 +1727,33 @@ methodHandle MethodHandleCompiler::get_method_oop(TRAPS) const { Rewriter::rewrite(_target_klass(), cpool, methods, CHECK_(empty)); // Use fake class. Rewriter::relocate_and_link(_target_klass(), methods, CHECK_(empty)); // Use fake class. + // Pre-resolve selected CP cache entries, to avoid problems with class loader scoping. + constantPoolCacheHandle cpc(THREAD, cpool->cache()); + for (int i = 0; i < cpc->length(); i++) { + ConstantPoolCacheEntry* e = cpc->entry_at(i); + assert(!e->is_secondary_entry(), "no indy instructions in here, yet"); + int constant_pool_index = e->constant_pool_index(); + ConstantValue* cv = _constants.at(constant_pool_index); + if (!cv->has_linkage()) continue; + methodHandle m = cv->linkage(); + int index; + switch (cv->tag()) { + case JVM_CONSTANT_Methodref: + index = m->vtable_index(); + if (m->is_static()) { + e->set_method(Bytecodes::_invokestatic, m, index); + } else { + e->set_method(Bytecodes::_invokespecial, m, index); + e->set_method(Bytecodes::_invokevirtual, m, index); + } + break; + case JVM_CONSTANT_InterfaceMethodref: + index = klassItable::compute_itable_index(m()); + e->set_interface_call(m, index); + break; + } + } + // Set the invocation counter's count to the invoke count of the // original call site. InvocationCounter* ic = m->invocation_counter(); @@ -1696,6 +1844,9 @@ public: _param_state(0), _temp_num(0) { + out->print("MethodHandle:"); + java_lang_invoke_MethodType::print_signature(java_lang_invoke_MethodHandle::type(root()), out); + out->print(" : #"); start_params(); } virtual ArgToken make_parameter(BasicType type, klassOop tk, int argnum, TRAPS) { @@ -1759,12 +1910,12 @@ public: _strbuf.print(")"); return maybe_make_temp("fetch", type, "x"); } - virtual ArgToken make_invoke(methodOop m, vmIntrinsics::ID iid, + virtual ArgToken make_invoke(methodHandle m, vmIntrinsics::ID iid, Bytecodes::Code op, bool tailcall, int argc, ArgToken* argv, TRAPS) { Symbol* name; Symbol* sig; - if (m != NULL) { + if (m.not_null()) { name = m->name(); sig = m->signature(); } else { diff --git a/src/share/vm/prims/methodHandleWalk.hpp b/src/share/vm/prims/methodHandleWalk.hpp index 78aca302f0ab13d029cb9989374580a841b51e03..783b55b7848d289b0fa6ec6efb9b129c3250e490 100644 --- a/src/share/vm/prims/methodHandleWalk.hpp +++ b/src/share/vm/prims/methodHandleWalk.hpp @@ -98,6 +98,7 @@ public: int bound_arg_slot() { assert(is_bound(), ""); return _arg_slot; } oop bound_arg_oop() { assert(is_bound(), ""); return BoundMethodHandle_argument_oop(); } + methodHandle last_method() { assert(is_last(), ""); return _last_method; } methodOop last_method_oop() { assert(is_last(), ""); return _last_method(); } Bytecodes::Code last_invoke_code() { assert(is_last(), ""); return _last_invoke; } @@ -181,6 +182,8 @@ private: GrowableArray _outgoing; // current outgoing parameter slots int _outgoing_argc; // # non-empty outgoing slots + vmIntrinsics::ID _return_conv; // Return conversion required by raw retypes. + // Replace a value of type old_type at slot (and maybe slot+1) with the new value. // If old_type != T_VOID, remove the old argument at that point. // If new_type != T_VOID, insert the new argument at that point. @@ -219,7 +222,8 @@ public: : _chain(root, THREAD), _for_invokedynamic(for_invokedynamic), _outgoing(THREAD, 10), - _outgoing_argc(0) + _outgoing_argc(0), + _return_conv(vmIntrinsics::_none) { _local_index = for_invokedynamic ? 0 : 1; } @@ -228,6 +232,10 @@ public: bool for_invokedynamic() const { return _for_invokedynamic; } + vmIntrinsics::ID return_conv() const { return _return_conv; } + void set_return_conv(vmIntrinsics::ID c) { _return_conv = c; } + static vmIntrinsics::ID zero_return_conv() { return vmIntrinsics::_min; } + int new_local_index(BasicType bt) { //int index = _for_invokedynamic ? _local_index : _local_index - 1; int index = _local_index; @@ -243,9 +251,9 @@ public: virtual ArgToken make_oop_constant(oop con, TRAPS) = 0; virtual ArgToken make_conversion(BasicType type, klassOop tk, Bytecodes::Code op, const ArgToken& src, TRAPS) = 0; virtual ArgToken make_fetch(BasicType type, klassOop tk, Bytecodes::Code op, const ArgToken& base, const ArgToken& offset, TRAPS) = 0; - virtual ArgToken make_invoke(methodOop m, vmIntrinsics::ID iid, Bytecodes::Code op, bool tailcall, int argc, ArgToken* argv, TRAPS) = 0; + virtual ArgToken make_invoke(methodHandle m, vmIntrinsics::ID iid, Bytecodes::Code op, bool tailcall, int argc, ArgToken* argv, TRAPS) = 0; - // For make_invoke, the methodOop can be NULL if the intrinsic ID + // For make_invoke, the methodHandle can be NULL if the intrinsic ID // is something other than vmIntrinsics::_none. // and in case anyone cares to related the previous actions to the chain: @@ -280,6 +288,7 @@ private: JavaValue _value; Handle _handle; Symbol* _sym; + methodHandle _method; // pre-linkage public: // Constructor for oop types. @@ -328,11 +337,21 @@ private: jlong get_jlong() const { return _value.get_jlong(); } jfloat get_jfloat() const { return _value.get_jfloat(); } jdouble get_jdouble() const { return _value.get_jdouble(); } + + void set_linkage(methodHandle method) { + assert(_method.is_null(), ""); + _method = method; + } + bool has_linkage() const { return _method.not_null(); } + methodHandle linkage() const { return _method; } }; // Fake constant pool. GrowableArray _constants; + // Non-BCP classes that appear in associated MethodTypes (require special handling). + GrowableArray _non_bcp_klasses; + // Accumulated compiler state: GrowableArray _bytecode; @@ -368,15 +387,20 @@ private: return _constants.append(cv); } - int cpool_oop_reference_put(int tag, int first_index, int second_index) { + int cpool_oop_reference_put(int tag, int first_index, int second_index, methodHandle method) { if (first_index == 0 && second_index == 0) return 0; assert(first_index != 0 && second_index != 0, "no zero indexes"); ConstantValue* cv = new ConstantValue(tag, first_index, second_index); + if (method.not_null()) cv->set_linkage(method); return _constants.append(cv); } int cpool_primitive_put(BasicType type, jvalue* con); + bool check_non_bcp_klasses(Handle method_type, TRAPS); + bool check_non_bcp_klass(klassOop klass, TRAPS); + void record_non_bcp_klasses(); + int cpool_int_put(jint value) { jvalue con; con.i = value; return cpool_primitive_put(T_INT, &con); @@ -403,11 +427,12 @@ private: int cpool_klass_put(klassOop klass) { return cpool_oop_put(JVM_CONSTANT_Class, klass); } - int cpool_methodref_put(int class_index, int name_and_type_index) { - return cpool_oop_reference_put(JVM_CONSTANT_Methodref, class_index, name_and_type_index); + int cpool_methodref_put(Bytecodes::Code op, int class_index, int name_and_type_index, methodHandle method) { + int tag = (op == Bytecodes::_invokeinterface ? JVM_CONSTANT_InterfaceMethodref : JVM_CONSTANT_Methodref); + return cpool_oop_reference_put(tag, class_index, name_and_type_index, method); } int cpool_name_and_type_put(int name_index, int signature_index) { - return cpool_oop_reference_put(JVM_CONSTANT_NameAndType, name_index, signature_index); + return cpool_oop_reference_put(JVM_CONSTANT_NameAndType, name_index, signature_index, methodHandle()); } void emit_bc(Bytecodes::Code op, int index = 0, int args_size = -1); @@ -428,7 +453,7 @@ private: virtual ArgToken make_conversion(BasicType type, klassOop tk, Bytecodes::Code op, const ArgToken& src, TRAPS); virtual ArgToken make_fetch(BasicType type, klassOop tk, Bytecodes::Code op, const ArgToken& base, const ArgToken& offset, TRAPS); - virtual ArgToken make_invoke(methodOop m, vmIntrinsics::ID iid, Bytecodes::Code op, bool tailcall, int argc, ArgToken* argv, TRAPS); + virtual ArgToken make_invoke(methodHandle m, vmIntrinsics::ID iid, Bytecodes::Code op, bool tailcall, int argc, ArgToken* argv, TRAPS); // Get a real constant pool. constantPoolHandle get_constant_pool(TRAPS) const; diff --git a/src/share/vm/prims/methodHandles.cpp b/src/share/vm/prims/methodHandles.cpp index eb9804776945c6622cf39f2005f2df46d7aaac0f..6dc40a32b5ceb30b5e7713fc62765284027c9564 100644 --- a/src/share/vm/prims/methodHandles.cpp +++ b/src/share/vm/prims/methodHandles.cpp @@ -24,12 +24,14 @@ #include "precompiled.hpp" #include "classfile/symbolTable.hpp" +#include "compiler/compileBroker.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/oopMapCache.hpp" #include "memory/allocation.inline.hpp" #include "memory/oopFactory.hpp" #include "prims/methodHandles.hpp" #include "prims/methodHandleWalk.hpp" +#include "runtime/compilationPolicy.hpp" #include "runtime/javaCalls.hpp" #include "runtime/reflection.hpp" #include "runtime/signature.hpp" @@ -767,7 +769,9 @@ void MethodHandles::resolve_MemberName(Handle mname, TRAPS) { m = NULL; // try again with a different class loader... } - if (m != NULL) { + if (m != NULL && + m->is_method_handle_invoke() && + java_lang_invoke_MethodType::equals(polymorphic_method_type(), m->method_handle_type())) { int mods = (m->access_flags().as_short() & JVM_RECOGNIZED_METHOD_MODIFIERS); java_lang_invoke_MemberName::set_vmtarget(mname(), m); java_lang_invoke_MemberName::set_vmindex(mname(), m->vtable_index()); @@ -986,6 +990,48 @@ instanceKlassHandle MethodHandles::resolve_instance_klass(oop java_mirror_oop, T // This is for debugging and reflection. oop MethodHandles::encode_target(Handle mh, int format, TRAPS) { assert(java_lang_invoke_MethodHandle::is_instance(mh()), "must be a MH"); + if (format == ETF_FORCE_DIRECT_HANDLE || + format == ETF_COMPILE_DIRECT_HANDLE) { + // Internal function for stress testing. + Handle mt = java_lang_invoke_MethodHandle::type(mh()); + int invocation_count = 10000; + TempNewSymbol signature = java_lang_invoke_MethodType::as_signature(mt(), true, CHECK_NULL); + bool omit_receiver_argument = true; + MethodHandleCompiler mhc(mh, vmSymbols::invoke_name(), signature, invocation_count, omit_receiver_argument, CHECK_NULL); + methodHandle m = mhc.compile(CHECK_NULL); + if (StressMethodHandleWalk && Verbose || PrintMiscellaneous) { + tty->print_cr("MethodHandleNatives.getTarget(%s)", + format == ETF_FORCE_DIRECT_HANDLE ? "FORCE_DIRECT" : "COMPILE_DIRECT"); + if (Verbose) { + m->print_codes(); + } + } + if (StressMethodHandleWalk) { + InterpreterOopMap mask; + OopMapCache::compute_one_oop_map(m, m->code_size() - 1, &mask); + } + if ((format == ETF_COMPILE_DIRECT_HANDLE || + CompilationPolicy::must_be_compiled(m)) + && !instanceKlass::cast(m->method_holder())->is_not_initialized() + && CompilationPolicy::can_be_compiled(m)) { + // Force compilation + CompileBroker::compile_method(m, InvocationEntryBci, + CompLevel_initial_compile, + methodHandle(), 0, "MethodHandleNatives.getTarget", + CHECK_NULL); + } + // Now wrap m in a DirectMethodHandle. + instanceKlassHandle dmh_klass(THREAD, SystemDictionary::DirectMethodHandle_klass()); + Handle dmh = dmh_klass->allocate_instance_handle(CHECK_NULL); + JavaValue ignore_result(T_VOID); + Symbol* init_name = vmSymbols::object_initializer_name(); + Symbol* init_sig = vmSymbols::notifyGenericMethodType_signature(); + JavaCalls::call_special(&ignore_result, dmh, + SystemDictionaryHandles::MethodHandle_klass(), init_name, init_sig, + java_lang_invoke_MethodHandle::type(mh()), CHECK_NULL); + MethodHandles::init_DirectMethodHandle(dmh, m, false, CHECK_NULL); + return dmh(); + } if (format == ETF_HANDLE_OR_METHOD_NAME) { oop target = java_lang_invoke_MethodHandle::vmtarget(mh()); if (target == NULL) { @@ -1221,6 +1267,12 @@ void MethodHandles::verify_method_signature(methodHandle m, klassOop aklass_oop = SystemDictionary::resolve_or_null(name, loader, domain, CHECK); if (aklass_oop != NULL) aklass = KlassHandle(THREAD, aklass_oop); + if (aklass.is_null() && + pklass.not_null() && + loader.is_null() && + pklass->name() == name) + // accept name equivalence here, since that's the best we can do + aklass = pklass; } } else { // for method handle invokers we don't look at the name in the signature @@ -2654,6 +2706,17 @@ static void stress_method_handle_walk_impl(Handle mh, TRAPS) { } InterpreterOopMap mask; OopMapCache::compute_one_oop_map(m, m->code_size() - 1, &mask); + // compile to object code if -Xcomp or WizardMode + if ((WizardMode || + CompilationPolicy::must_be_compiled(m)) + && !instanceKlass::cast(m->method_holder())->is_not_initialized() + && CompilationPolicy::can_be_compiled(m)) { + // Force compilation + CompileBroker::compile_method(m, InvocationEntryBci, + CompLevel_initial_compile, + methodHandle(), 0, "StressMethodHandleWalk", + CHECK); + } } } @@ -2773,7 +2836,12 @@ JVM_ENTRY(void, MHN_init_BMH(JNIEnv *env, jobject igcls, jobject mh_jh, // Build a BMH on top of a DMH or another BMH: MethodHandles::init_BoundMethodHandle(mh, target, argnum, CHECK); } - stress_method_handle_walk(mh, CHECK); + + if (StressMethodHandleWalk) { + if (mh->klass() == SystemDictionary::BoundMethodHandle_klass()) + stress_method_handle_walk(mh, CHECK); + // else don't, since the subclass has not yet initialized its own fields + } } JVM_END diff --git a/src/share/vm/prims/methodHandles.hpp b/src/share/vm/prims/methodHandles.hpp index 5d6813b4697591205532b30166d8a855dac89eb1..dbf6416684944018a0e58bf96a93d7ae55c069ad 100644 --- a/src/share/vm/prims/methodHandles.hpp +++ b/src/share/vm/prims/methodHandles.hpp @@ -588,6 +588,8 @@ class MethodHandles: AllStatic { ETF_DIRECT_HANDLE = 1, // ultimate method handle (will be a DMH, may be self) ETF_METHOD_NAME = 2, // ultimate method as MemberName ETF_REFLECT_METHOD = 3, // ultimate method as java.lang.reflect object (sans refClass) + ETF_FORCE_DIRECT_HANDLE = 64, + ETF_COMPILE_DIRECT_HANDLE = 65, // ad hoc constants OP_ROT_ARGS_DOWN_LIMIT_BIAS = -1