7056328: JSR 292 invocation sometimes fails in adapters for types not on boot class path
Reviewed-by: never
This commit is contained in:
parent
15161b8cd1
commit
8df44305c7
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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; }
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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*);
|
||||
|
@ -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();
|
||||
|
@ -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<KlassHandle>* 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<KlassHandle>(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,
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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<ArgToken> _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<ConstantValue*> _constants;
|
||||
|
||||
// Non-BCP classes that appear in associated MethodTypes (require special handling).
|
||||
GrowableArray<KlassHandle> _non_bcp_klasses;
|
||||
|
||||
// Accumulated compiler state:
|
||||
GrowableArray<unsigned char> _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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user