8186209: Tool support for ConstantDynamic
8186046: Minimal ConstantDynamic support 8190972: Ensure that AOT/Graal filters out class files containing CONSTANT_Dynamic ahead of full AOT support Co-authored-by: Lois Foltan <lois.foltan@oracle.com> Co-authored-by: John Rose <john.r.rose@oracle.com> Reviewed-by: acorn, coleenp, kvn
This commit is contained in:
parent
52d3bf29b2
commit
e55a05957d
@ -516,6 +516,8 @@ void InterpreterMacroAssembler::load_resolved_reference_at_index(
|
||||
// Add in the index
|
||||
addptr(result, tmp);
|
||||
load_heap_oop(result, Address(result, arrayOopDesc::base_offset_in_bytes(T_OBJECT)));
|
||||
// The resulting oop is null if the reference is not yet resolved.
|
||||
// It is Universe::the_null_sentinel() if the reference resolved to NULL via condy.
|
||||
}
|
||||
|
||||
// load cpool->resolved_klass_at(index)
|
||||
|
@ -836,7 +836,8 @@ void MacroAssembler::warn(const char* msg) {
|
||||
andq(rsp, -16); // align stack as required by push_CPU_state and call
|
||||
push_CPU_state(); // keeps alignment at 16 bytes
|
||||
lea(c_rarg0, ExternalAddress((address) msg));
|
||||
call_VM_leaf(CAST_FROM_FN_PTR(address, warning), c_rarg0);
|
||||
lea(rax, ExternalAddress(CAST_FROM_FN_PTR(address, warning)));
|
||||
call(rax);
|
||||
pop_CPU_state();
|
||||
mov(rsp, rbp);
|
||||
pop(rbp);
|
||||
|
@ -419,7 +419,7 @@ void TemplateTable::sipush() {
|
||||
void TemplateTable::ldc(bool wide) {
|
||||
transition(vtos, vtos);
|
||||
Register rarg = NOT_LP64(rcx) LP64_ONLY(c_rarg1);
|
||||
Label call_ldc, notFloat, notClass, Done;
|
||||
Label call_ldc, notFloat, notClass, notInt, Done;
|
||||
|
||||
if (wide) {
|
||||
__ get_unsigned_2_byte_index_at_bcp(rbx, 1);
|
||||
@ -465,19 +465,18 @@ void TemplateTable::ldc(bool wide) {
|
||||
__ jmp(Done);
|
||||
|
||||
__ bind(notFloat);
|
||||
#ifdef ASSERT
|
||||
{
|
||||
Label L;
|
||||
__ cmpl(rdx, JVM_CONSTANT_Integer);
|
||||
__ jcc(Assembler::equal, L);
|
||||
// String and Object are rewritten to fast_aldc
|
||||
__ stop("unexpected tag type in ldc");
|
||||
__ bind(L);
|
||||
}
|
||||
#endif
|
||||
// itos JVM_CONSTANT_Integer only
|
||||
__ cmpl(rdx, JVM_CONSTANT_Integer);
|
||||
__ jccb(Assembler::notEqual, notInt);
|
||||
|
||||
// itos
|
||||
__ movl(rax, Address(rcx, rbx, Address::times_ptr, base_offset));
|
||||
__ push(itos);
|
||||
__ jmp(Done);
|
||||
|
||||
// assume the tag is for condy; if not, the VM runtime will tell us
|
||||
__ bind(notInt);
|
||||
condy_helper(Done);
|
||||
|
||||
__ bind(Done);
|
||||
}
|
||||
|
||||
@ -487,6 +486,7 @@ void TemplateTable::fast_aldc(bool wide) {
|
||||
|
||||
Register result = rax;
|
||||
Register tmp = rdx;
|
||||
Register rarg = NOT_LP64(rcx) LP64_ONLY(c_rarg1);
|
||||
int index_size = wide ? sizeof(u2) : sizeof(u1);
|
||||
|
||||
Label resolved;
|
||||
@ -496,17 +496,28 @@ void TemplateTable::fast_aldc(bool wide) {
|
||||
assert_different_registers(result, tmp);
|
||||
__ get_cache_index_at_bcp(tmp, 1, index_size);
|
||||
__ load_resolved_reference_at_index(result, tmp);
|
||||
__ testl(result, result);
|
||||
__ testptr(result, result);
|
||||
__ jcc(Assembler::notZero, resolved);
|
||||
|
||||
address entry = CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_ldc);
|
||||
|
||||
// first time invocation - must resolve first
|
||||
__ movl(tmp, (int)bytecode());
|
||||
__ call_VM(result, entry, tmp);
|
||||
|
||||
__ movl(rarg, (int)bytecode());
|
||||
__ call_VM(result, entry, rarg);
|
||||
__ bind(resolved);
|
||||
|
||||
{ // Check for the null sentinel.
|
||||
// If we just called the VM, that already did the mapping for us,
|
||||
// but it's harmless to retry.
|
||||
Label notNull;
|
||||
ExternalAddress null_sentinel((address)Universe::the_null_sentinel_addr());
|
||||
__ movptr(tmp, null_sentinel);
|
||||
__ cmpptr(tmp, result);
|
||||
__ jccb(Assembler::notEqual, notNull);
|
||||
__ xorptr(result, result); // NULL object reference
|
||||
__ bind(notNull);
|
||||
}
|
||||
|
||||
if (VerifyOops) {
|
||||
__ verify_oop(result);
|
||||
}
|
||||
@ -514,7 +525,7 @@ void TemplateTable::fast_aldc(bool wide) {
|
||||
|
||||
void TemplateTable::ldc2_w() {
|
||||
transition(vtos, vtos);
|
||||
Label Long, Done;
|
||||
Label notDouble, notLong, Done;
|
||||
__ get_unsigned_2_byte_index_at_bcp(rbx, 1);
|
||||
|
||||
__ get_cpool_and_tags(rcx, rax);
|
||||
@ -522,25 +533,143 @@ void TemplateTable::ldc2_w() {
|
||||
const int tags_offset = Array<u1>::base_offset_in_bytes();
|
||||
|
||||
// get type
|
||||
__ cmpb(Address(rax, rbx, Address::times_1, tags_offset),
|
||||
JVM_CONSTANT_Double);
|
||||
__ jccb(Assembler::notEqual, Long);
|
||||
__ movzbl(rdx, Address(rax, rbx, Address::times_1, tags_offset));
|
||||
__ cmpl(rdx, JVM_CONSTANT_Double);
|
||||
__ jccb(Assembler::notEqual, notDouble);
|
||||
|
||||
// dtos
|
||||
__ load_double(Address(rcx, rbx, Address::times_ptr, base_offset));
|
||||
__ push(dtos);
|
||||
|
||||
__ jmpb(Done);
|
||||
__ bind(Long);
|
||||
__ jmp(Done);
|
||||
__ bind(notDouble);
|
||||
__ cmpl(rdx, JVM_CONSTANT_Long);
|
||||
__ jccb(Assembler::notEqual, notLong);
|
||||
|
||||
// ltos
|
||||
__ movptr(rax, Address(rcx, rbx, Address::times_ptr, base_offset + 0 * wordSize));
|
||||
NOT_LP64(__ movptr(rdx, Address(rcx, rbx, Address::times_ptr, base_offset + 1 * wordSize)));
|
||||
__ push(ltos);
|
||||
__ jmp(Done);
|
||||
|
||||
__ bind(notLong);
|
||||
condy_helper(Done);
|
||||
|
||||
__ bind(Done);
|
||||
}
|
||||
|
||||
void TemplateTable::condy_helper(Label& Done) {
|
||||
const Register obj = rax;
|
||||
const Register off = rbx;
|
||||
const Register flags = rcx;
|
||||
const Register rarg = NOT_LP64(rcx) LP64_ONLY(c_rarg1);
|
||||
__ movl(rarg, (int)bytecode());
|
||||
call_VM(obj, CAST_FROM_FN_PTR(address, InterpreterRuntime::resolve_ldc), rarg);
|
||||
#ifndef _LP64
|
||||
// borrow rdi from locals
|
||||
__ get_thread(rdi);
|
||||
__ get_vm_result_2(flags, rdi);
|
||||
__ restore_locals();
|
||||
#else
|
||||
__ get_vm_result_2(flags, r15_thread);
|
||||
#endif
|
||||
// VMr = obj = base address to find primitive value to push
|
||||
// VMr2 = flags = (tos, off) using format of CPCE::_flags
|
||||
__ movl(off, flags);
|
||||
__ andl(off, ConstantPoolCacheEntry::field_index_mask);
|
||||
const Address field(obj, off, Address::times_1, 0*wordSize);
|
||||
|
||||
// What sort of thing are we loading?
|
||||
__ shrl(flags, ConstantPoolCacheEntry::tos_state_shift);
|
||||
__ andl(flags, ConstantPoolCacheEntry::tos_state_mask);
|
||||
|
||||
switch (bytecode()) {
|
||||
case Bytecodes::_ldc:
|
||||
case Bytecodes::_ldc_w:
|
||||
{
|
||||
// tos in (itos, ftos, stos, btos, ctos, ztos)
|
||||
Label notInt, notFloat, notShort, notByte, notChar, notBool;
|
||||
__ cmpl(flags, itos);
|
||||
__ jcc(Assembler::notEqual, notInt);
|
||||
// itos
|
||||
__ movl(rax, field);
|
||||
__ push(itos);
|
||||
__ jmp(Done);
|
||||
|
||||
__ bind(notInt);
|
||||
__ cmpl(flags, ftos);
|
||||
__ jcc(Assembler::notEqual, notFloat);
|
||||
// ftos
|
||||
__ load_float(field);
|
||||
__ push(ftos);
|
||||
__ jmp(Done);
|
||||
|
||||
__ bind(notFloat);
|
||||
__ cmpl(flags, stos);
|
||||
__ jcc(Assembler::notEqual, notShort);
|
||||
// stos
|
||||
__ load_signed_short(rax, field);
|
||||
__ push(stos);
|
||||
__ jmp(Done);
|
||||
|
||||
__ bind(notShort);
|
||||
__ cmpl(flags, btos);
|
||||
__ jcc(Assembler::notEqual, notByte);
|
||||
// btos
|
||||
__ load_signed_byte(rax, field);
|
||||
__ push(btos);
|
||||
__ jmp(Done);
|
||||
|
||||
__ bind(notByte);
|
||||
__ cmpl(flags, ctos);
|
||||
__ jcc(Assembler::notEqual, notChar);
|
||||
// ctos
|
||||
__ load_unsigned_short(rax, field);
|
||||
__ push(ctos);
|
||||
__ jmp(Done);
|
||||
|
||||
__ bind(notChar);
|
||||
__ cmpl(flags, ztos);
|
||||
__ jcc(Assembler::notEqual, notBool);
|
||||
// ztos
|
||||
__ load_signed_byte(rax, field);
|
||||
__ push(ztos);
|
||||
__ jmp(Done);
|
||||
|
||||
__ bind(notBool);
|
||||
break;
|
||||
}
|
||||
|
||||
case Bytecodes::_ldc2_w:
|
||||
{
|
||||
Label notLong, notDouble;
|
||||
__ cmpl(flags, ltos);
|
||||
__ jcc(Assembler::notEqual, notLong);
|
||||
// ltos
|
||||
__ movptr(rax, field);
|
||||
NOT_LP64(__ movptr(rdx, field.plus_disp(4)));
|
||||
__ push(ltos);
|
||||
__ jmp(Done);
|
||||
|
||||
__ bind(notLong);
|
||||
__ cmpl(flags, dtos);
|
||||
__ jcc(Assembler::notEqual, notDouble);
|
||||
// dtos
|
||||
__ load_double(field);
|
||||
__ push(dtos);
|
||||
__ jmp(Done);
|
||||
|
||||
__ bind(notDouble);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
|
||||
__ stop("bad ldc/condy");
|
||||
}
|
||||
|
||||
void TemplateTable::locals_index(Register reg, int offset) {
|
||||
__ load_unsigned_byte(reg, at_bcp(offset));
|
||||
__ negptr(reg);
|
||||
|
@ -874,6 +874,8 @@ void GraphBuilder::ScopeData::incr_num_returns() {
|
||||
void GraphBuilder::load_constant() {
|
||||
ciConstant con = stream()->get_constant();
|
||||
if (con.basic_type() == T_ILLEGAL) {
|
||||
// FIXME: an unresolved Dynamic constant can get here,
|
||||
// and that should not terminate the whole compilation.
|
||||
BAILOUT("could not resolve a constant");
|
||||
} else {
|
||||
ValueType* t = illegalType;
|
||||
@ -893,11 +895,19 @@ void GraphBuilder::load_constant() {
|
||||
ciObject* obj = con.as_object();
|
||||
if (!obj->is_loaded()
|
||||
|| (PatchALot && obj->klass() != ciEnv::current()->String_klass())) {
|
||||
// A Class, MethodType, MethodHandle, or String.
|
||||
// Unloaded condy nodes show up as T_ILLEGAL, above.
|
||||
patch_state = copy_state_before();
|
||||
t = new ObjectConstant(obj);
|
||||
} else {
|
||||
assert(obj->is_instance(), "must be java_mirror of klass");
|
||||
t = new InstanceConstant(obj->as_instance());
|
||||
// Might be a Class, MethodType, MethodHandle, or Dynamic constant
|
||||
// result, which might turn out to be an array.
|
||||
if (obj->is_null_object())
|
||||
t = objectNull;
|
||||
else if (obj->is_array())
|
||||
t = new ArrayConstant(obj->as_array());
|
||||
else
|
||||
t = new InstanceConstant(obj->as_instance());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -584,8 +584,34 @@ ciConstant ciEnv::get_constant_by_index_impl(const constantPoolHandle& cpool,
|
||||
int index = pool_index;
|
||||
if (cache_index >= 0) {
|
||||
assert(index < 0, "only one kind of index at a time");
|
||||
index = cpool->object_to_cp_index(cache_index);
|
||||
oop obj = cpool->resolved_references()->obj_at(cache_index);
|
||||
if (obj != NULL) {
|
||||
if (obj == Universe::the_null_sentinel()) {
|
||||
return ciConstant(T_OBJECT, get_object(NULL));
|
||||
}
|
||||
BasicType bt = T_OBJECT;
|
||||
if (cpool->tag_at(index).is_dynamic_constant())
|
||||
bt = FieldType::basic_type(cpool->uncached_signature_ref_at(index));
|
||||
if (is_reference_type(bt)) {
|
||||
} else {
|
||||
// we have to unbox the primitive value
|
||||
if (!is_java_primitive(bt)) return ciConstant();
|
||||
jvalue value;
|
||||
BasicType bt2 = java_lang_boxing_object::get_value(obj, &value);
|
||||
assert(bt2 == bt, "");
|
||||
switch (bt2) {
|
||||
case T_DOUBLE: return ciConstant(value.d);
|
||||
case T_FLOAT: return ciConstant(value.f);
|
||||
case T_LONG: return ciConstant(value.j);
|
||||
case T_INT: return ciConstant(bt2, value.i);
|
||||
case T_SHORT: return ciConstant(bt2, value.s);
|
||||
case T_BYTE: return ciConstant(bt2, value.b);
|
||||
case T_CHAR: return ciConstant(bt2, value.c);
|
||||
case T_BOOLEAN: return ciConstant(bt2, value.z);
|
||||
default: return ciConstant();
|
||||
}
|
||||
}
|
||||
ciObject* ciobj = get_object(obj);
|
||||
if (ciobj->is_array()) {
|
||||
return ciConstant(T_ARRAY, ciobj);
|
||||
@ -594,7 +620,6 @@ ciConstant ciEnv::get_constant_by_index_impl(const constantPoolHandle& cpool,
|
||||
return ciConstant(T_OBJECT, ciobj);
|
||||
}
|
||||
}
|
||||
index = cpool->object_to_cp_index(cache_index);
|
||||
}
|
||||
constantTag tag = cpool->tag_at(index);
|
||||
if (tag.is_int()) {
|
||||
@ -650,6 +675,8 @@ ciConstant ciEnv::get_constant_by_index_impl(const constantPoolHandle& cpool,
|
||||
ciSymbol* signature = get_symbol(cpool->method_handle_signature_ref_at(index));
|
||||
ciObject* ciobj = get_unloaded_method_handle_constant(callee, name, signature, ref_kind);
|
||||
return ciConstant(T_OBJECT, ciobj);
|
||||
} else if (tag.is_dynamic_constant()) {
|
||||
return ciConstant();
|
||||
} else {
|
||||
ShouldNotReachHere();
|
||||
return ciConstant();
|
||||
|
@ -721,6 +721,7 @@ class CompileReplay : public StackObj {
|
||||
case JVM_CONSTANT_Float:
|
||||
case JVM_CONSTANT_MethodHandle:
|
||||
case JVM_CONSTANT_MethodType:
|
||||
case JVM_CONSTANT_Dynamic:
|
||||
case JVM_CONSTANT_InvokeDynamic:
|
||||
if (tag != cp->tag_at(i).value()) {
|
||||
report_error("tag mismatch: wrong class files?");
|
||||
|
@ -254,7 +254,8 @@ ciConstant ciBytecodeStream::get_constant() {
|
||||
// constant.
|
||||
constantTag ciBytecodeStream::get_constant_pool_tag(int index) const {
|
||||
VM_ENTRY_MARK;
|
||||
return _method->get_Method()->constants()->tag_at(index);
|
||||
BasicType bt = _method->get_Method()->constants()->basic_type_for_constant_at(index);
|
||||
return constantTag::ofBasicType(bt);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
|
@ -204,6 +204,21 @@ void ClassFileParser::parse_constant_pool_entries(const ClassFileStream* const s
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JVM_CONSTANT_Dynamic : {
|
||||
if (_major_version < Verifier::DYNAMICCONSTANT_MAJOR_VERSION) {
|
||||
classfile_parse_error(
|
||||
"Class file version does not support constant tag %u in class file %s",
|
||||
tag, CHECK);
|
||||
}
|
||||
cfs->guarantee_more(5, CHECK); // bsm_index, nt, tag/access_flags
|
||||
const u2 bootstrap_specifier_index = cfs->get_u2_fast();
|
||||
const u2 name_and_type_index = cfs->get_u2_fast();
|
||||
if (_max_bootstrap_specifier_index < (int) bootstrap_specifier_index) {
|
||||
_max_bootstrap_specifier_index = (int) bootstrap_specifier_index; // collect for later
|
||||
}
|
||||
cp->dynamic_constant_at_put(index, bootstrap_specifier_index, name_and_type_index);
|
||||
break;
|
||||
}
|
||||
case JVM_CONSTANT_InvokeDynamic : {
|
||||
if (_major_version < Verifier::INVOKEDYNAMIC_MAJOR_VERSION) {
|
||||
classfile_parse_error(
|
||||
@ -536,6 +551,21 @@ void ClassFileParser::parse_constant_pool(const ClassFileStream* const stream,
|
||||
ref_index, CHECK);
|
||||
break;
|
||||
}
|
||||
case JVM_CONSTANT_Dynamic: {
|
||||
const int name_and_type_ref_index =
|
||||
cp->invoke_dynamic_name_and_type_ref_index_at(index);
|
||||
|
||||
check_property(valid_cp_range(name_and_type_ref_index, length) &&
|
||||
cp->tag_at(name_and_type_ref_index).is_name_and_type(),
|
||||
"Invalid constant pool index %u in class file %s",
|
||||
name_and_type_ref_index, CHECK);
|
||||
// bootstrap specifier index must be checked later,
|
||||
// when BootstrapMethods attr is available
|
||||
|
||||
// Mark the constant pool as having a CONSTANT_Dynamic_info structure
|
||||
cp->set_has_dynamic_constant();
|
||||
break;
|
||||
}
|
||||
case JVM_CONSTANT_InvokeDynamic: {
|
||||
const int name_and_type_ref_index =
|
||||
cp->invoke_dynamic_name_and_type_ref_index_at(index);
|
||||
@ -628,6 +658,27 @@ void ClassFileParser::parse_constant_pool(const ClassFileStream* const stream,
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JVM_CONSTANT_Dynamic: {
|
||||
const int name_and_type_ref_index =
|
||||
cp->name_and_type_ref_index_at(index);
|
||||
// already verified to be utf8
|
||||
const int name_ref_index =
|
||||
cp->name_ref_index_at(name_and_type_ref_index);
|
||||
// already verified to be utf8
|
||||
const int signature_ref_index =
|
||||
cp->signature_ref_index_at(name_and_type_ref_index);
|
||||
const Symbol* const name = cp->symbol_at(name_ref_index);
|
||||
const Symbol* const signature = cp->symbol_at(signature_ref_index);
|
||||
if (_need_verify) {
|
||||
// CONSTANT_Dynamic's name and signature are verified above, when iterating NameAndType_info.
|
||||
// Need only to be sure signature is non-zero length and the right type.
|
||||
if (signature->utf8_length() == 0 ||
|
||||
signature->byte_at(0) == JVM_SIGNATURE_FUNC) {
|
||||
throwIllegalSignature("CONSTANT_Dynamic", name, signature, CHECK);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JVM_CONSTANT_InvokeDynamic:
|
||||
case JVM_CONSTANT_Fieldref:
|
||||
case JVM_CONSTANT_Methodref:
|
||||
|
@ -2641,6 +2641,81 @@ static bool is_always_visible_class(oop mirror) {
|
||||
InstanceKlass::cast(klass)->is_same_class_package(SystemDictionary::MethodHandle_klass())); // java.lang.invoke
|
||||
}
|
||||
|
||||
|
||||
// Return the Java mirror (java.lang.Class instance) for a single-character
|
||||
// descriptor. This result, when available, is the same as produced by the
|
||||
// heavier API point of the same name that takes a Symbol.
|
||||
oop SystemDictionary::find_java_mirror_for_type(char signature_char) {
|
||||
return java_lang_Class::primitive_mirror(char2type(signature_char));
|
||||
}
|
||||
|
||||
// Find or construct the Java mirror (java.lang.Class instance) for a
|
||||
// for the given field type signature, as interpreted relative to the
|
||||
// given class loader. Handles primitives, void, references, arrays,
|
||||
// and all other reflectable types, except method types.
|
||||
// N.B. Code in reflection should use this entry point.
|
||||
Handle SystemDictionary::find_java_mirror_for_type(Symbol* signature,
|
||||
Klass* accessing_klass,
|
||||
Handle class_loader,
|
||||
Handle protection_domain,
|
||||
SignatureStream::FailureMode failure_mode,
|
||||
TRAPS) {
|
||||
Handle empty;
|
||||
|
||||
assert(accessing_klass == NULL || (class_loader.is_null() && protection_domain.is_null()),
|
||||
"one or the other, or perhaps neither");
|
||||
|
||||
Symbol* type = signature;
|
||||
|
||||
// What we have here must be a valid field descriptor,
|
||||
// and all valid field descriptors are supported.
|
||||
// Produce the same java.lang.Class that reflection reports.
|
||||
if (type->utf8_length() == 1) {
|
||||
|
||||
// It's a primitive. (Void has a primitive mirror too.)
|
||||
char ch = (char) type->byte_at(0);
|
||||
assert(is_java_primitive(char2type(ch)) || ch == 'V', "");
|
||||
return Handle(THREAD, find_java_mirror_for_type(ch));
|
||||
|
||||
} else if (FieldType::is_obj(type) || FieldType::is_array(type)) {
|
||||
|
||||
// It's a reference type.
|
||||
if (accessing_klass != NULL) {
|
||||
class_loader = Handle(THREAD, accessing_klass->class_loader());
|
||||
protection_domain = Handle(THREAD, accessing_klass->protection_domain());
|
||||
}
|
||||
Klass* constant_type_klass;
|
||||
if (failure_mode == SignatureStream::ReturnNull) {
|
||||
constant_type_klass = resolve_or_null(type, class_loader, protection_domain,
|
||||
CHECK_(empty));
|
||||
} else {
|
||||
bool throw_error = (failure_mode == SignatureStream::NCDFError);
|
||||
constant_type_klass = resolve_or_fail(type, class_loader, protection_domain,
|
||||
throw_error, CHECK_(empty));
|
||||
}
|
||||
if (constant_type_klass == NULL) {
|
||||
return Handle(); // report failure this way
|
||||
}
|
||||
Handle mirror(THREAD, constant_type_klass->java_mirror());
|
||||
|
||||
// Check accessibility, emulating ConstantPool::verify_constant_pool_resolve.
|
||||
if (accessing_klass != NULL) {
|
||||
Klass* sel_klass = constant_type_klass;
|
||||
bool fold_type_to_class = true;
|
||||
LinkResolver::check_klass_accessability(accessing_klass, sel_klass,
|
||||
fold_type_to_class, CHECK_(empty));
|
||||
}
|
||||
|
||||
return mirror;
|
||||
|
||||
}
|
||||
|
||||
// Fall through to an error.
|
||||
assert(false, "unsupported mirror syntax");
|
||||
THROW_MSG_(vmSymbols::java_lang_InternalError(), "unsupported mirror syntax", empty);
|
||||
}
|
||||
|
||||
|
||||
// Ask Java code to find or construct a java.lang.invoke.MethodType for the given
|
||||
// signature, as interpreted relative to the given class loader.
|
||||
// Because of class loader constraints, all method handle usage must be
|
||||
@ -2695,15 +2770,13 @@ Handle SystemDictionary::find_method_handle_type(Symbol* signature,
|
||||
pts->obj_at_put(arg++, mirror);
|
||||
|
||||
// Check accessibility.
|
||||
if (ss.is_object() && accessing_klass != NULL) {
|
||||
if (!java_lang_Class::is_primitive(mirror) && accessing_klass != NULL) {
|
||||
Klass* sel_klass = java_lang_Class::as_Klass(mirror);
|
||||
mirror = NULL; // safety
|
||||
// Emulate ConstantPool::verify_constant_pool_resolve.
|
||||
if (sel_klass->is_objArray_klass())
|
||||
sel_klass = ObjArrayKlass::cast(sel_klass)->bottom_klass();
|
||||
if (sel_klass->is_instance_klass()) {
|
||||
LinkResolver::check_klass_accessability(accessing_klass, sel_klass, CHECK_(empty));
|
||||
}
|
||||
bool fold_type_to_class = true;
|
||||
LinkResolver::check_klass_accessability(accessing_klass, sel_klass,
|
||||
fold_type_to_class, CHECK_(empty));
|
||||
}
|
||||
}
|
||||
assert(arg == npts, "");
|
||||
@ -2806,9 +2879,60 @@ Handle SystemDictionary::link_method_handle_constant(Klass* caller,
|
||||
return Handle(THREAD, (oop) result.get_jobject());
|
||||
}
|
||||
|
||||
// Ask Java to compute a constant by invoking a BSM given a Dynamic_info CP entry
|
||||
Handle SystemDictionary::link_dynamic_constant(Klass* caller,
|
||||
int condy_index,
|
||||
Handle bootstrap_specifier,
|
||||
Symbol* name,
|
||||
Symbol* type,
|
||||
TRAPS) {
|
||||
Handle empty;
|
||||
Handle bsm, info;
|
||||
if (java_lang_invoke_MethodHandle::is_instance(bootstrap_specifier())) {
|
||||
bsm = bootstrap_specifier;
|
||||
} else {
|
||||
assert(bootstrap_specifier->is_objArray(), "");
|
||||
objArrayOop args = (objArrayOop) bootstrap_specifier();
|
||||
assert(args->length() == 2, "");
|
||||
bsm = Handle(THREAD, args->obj_at(0));
|
||||
info = Handle(THREAD, args->obj_at(1));
|
||||
}
|
||||
guarantee(java_lang_invoke_MethodHandle::is_instance(bsm()),
|
||||
"caller must supply a valid BSM");
|
||||
|
||||
// This should not happen. JDK code should take care of that.
|
||||
if (caller == NULL) {
|
||||
THROW_MSG_(vmSymbols::java_lang_InternalError(), "bad dynamic constant", empty);
|
||||
}
|
||||
|
||||
Handle constant_name = java_lang_String::create_from_symbol(name, CHECK_(empty));
|
||||
|
||||
// Resolve the constant type in the context of the caller class
|
||||
Handle type_mirror = find_java_mirror_for_type(type, caller, SignatureStream::NCDFError,
|
||||
CHECK_(empty));
|
||||
|
||||
// call java.lang.invoke.MethodHandleNatives::linkConstantDyanmic(caller, condy_index, bsm, type, info)
|
||||
JavaCallArguments args;
|
||||
args.push_oop(Handle(THREAD, caller->java_mirror()));
|
||||
args.push_int(condy_index);
|
||||
args.push_oop(bsm);
|
||||
args.push_oop(constant_name);
|
||||
args.push_oop(type_mirror);
|
||||
args.push_oop(info);
|
||||
JavaValue result(T_OBJECT);
|
||||
JavaCalls::call_static(&result,
|
||||
SystemDictionary::MethodHandleNatives_klass(),
|
||||
vmSymbols::linkDynamicConstant_name(),
|
||||
vmSymbols::linkDynamicConstant_signature(),
|
||||
&args, CHECK_(empty));
|
||||
|
||||
return Handle(THREAD, (oop) result.get_jobject());
|
||||
}
|
||||
|
||||
// Ask Java code to find or construct a java.lang.invoke.CallSite for the given
|
||||
// name and signature, as interpreted relative to the given class loader.
|
||||
methodHandle SystemDictionary::find_dynamic_call_site_invoker(Klass* caller,
|
||||
int indy_index,
|
||||
Handle bootstrap_specifier,
|
||||
Symbol* name,
|
||||
Symbol* type,
|
||||
@ -2820,17 +2944,10 @@ methodHandle SystemDictionary::find_dynamic_call_site_invoker(Klass* caller,
|
||||
if (java_lang_invoke_MethodHandle::is_instance(bootstrap_specifier())) {
|
||||
bsm = bootstrap_specifier;
|
||||
} else {
|
||||
assert(bootstrap_specifier->is_objArray(), "");
|
||||
objArrayHandle args(THREAD, (objArrayOop) bootstrap_specifier());
|
||||
int len = args->length();
|
||||
assert(len >= 1, "");
|
||||
bsm = Handle(THREAD, args->obj_at(0));
|
||||
if (len > 1) {
|
||||
objArrayOop args1 = oopFactory::new_objArray(SystemDictionary::Object_klass(), len-1, CHECK_(empty));
|
||||
for (int i = 1; i < len; i++)
|
||||
args1->obj_at_put(i-1, args->obj_at(i));
|
||||
info = Handle(THREAD, args1);
|
||||
}
|
||||
objArrayOop args = (objArrayOop) bootstrap_specifier();
|
||||
assert(args->length() == 2, "");
|
||||
bsm = Handle(THREAD, args->obj_at(0));
|
||||
info = Handle(THREAD, args->obj_at(1));
|
||||
}
|
||||
guarantee(java_lang_invoke_MethodHandle::is_instance(bsm()),
|
||||
"caller must supply a valid BSM");
|
||||
@ -2846,9 +2963,10 @@ methodHandle SystemDictionary::find_dynamic_call_site_invoker(Klass* caller,
|
||||
objArrayHandle appendix_box = oopFactory::new_objArray_handle(SystemDictionary::Object_klass(), 1, CHECK_(empty));
|
||||
assert(appendix_box->obj_at(0) == NULL, "");
|
||||
|
||||
// call java.lang.invoke.MethodHandleNatives::linkCallSite(caller, bsm, name, mtype, info, &appendix)
|
||||
// call java.lang.invoke.MethodHandleNatives::linkCallSite(caller, indy_index, bsm, name, mtype, info, &appendix)
|
||||
JavaCallArguments args;
|
||||
args.push_oop(Handle(THREAD, caller->java_mirror()));
|
||||
args.push_int(indy_index);
|
||||
args.push_oop(bsm);
|
||||
args.push_oop(method_name);
|
||||
args.push_oop(method_type);
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "oops/symbol.hpp"
|
||||
#include "runtime/java.hpp"
|
||||
#include "runtime/reflectionUtils.hpp"
|
||||
#include "runtime/signature.hpp"
|
||||
#include "utilities/hashtable.hpp"
|
||||
#include "utilities/hashtable.inline.hpp"
|
||||
|
||||
@ -527,6 +528,28 @@ public:
|
||||
static methodHandle find_method_handle_intrinsic(vmIntrinsics::ID iid,
|
||||
Symbol* signature,
|
||||
TRAPS);
|
||||
|
||||
// compute java_mirror (java.lang.Class instance) for a type ("I", "[[B", "LFoo;", etc.)
|
||||
// Either the accessing_klass or the CL/PD can be non-null, but not both.
|
||||
static Handle find_java_mirror_for_type(Symbol* signature,
|
||||
Klass* accessing_klass,
|
||||
Handle class_loader,
|
||||
Handle protection_domain,
|
||||
SignatureStream::FailureMode failure_mode,
|
||||
TRAPS);
|
||||
static Handle find_java_mirror_for_type(Symbol* signature,
|
||||
Klass* accessing_klass,
|
||||
SignatureStream::FailureMode failure_mode,
|
||||
TRAPS) {
|
||||
// callee will fill in CL/PD from AK, if they are needed
|
||||
return find_java_mirror_for_type(signature, accessing_klass, Handle(), Handle(),
|
||||
failure_mode, THREAD);
|
||||
}
|
||||
|
||||
|
||||
// fast short-cut for the one-character case:
|
||||
static oop find_java_mirror_for_type(char signature_char);
|
||||
|
||||
// find a java.lang.invoke.MethodType object for a given signature
|
||||
// (asks Java to compute it if necessary, except in a compiler thread)
|
||||
static Handle find_method_handle_type(Symbol* signature,
|
||||
@ -546,8 +569,17 @@ public:
|
||||
Symbol* signature,
|
||||
TRAPS);
|
||||
|
||||
// ask Java to compute a constant by invoking a BSM given a Dynamic_info CP entry
|
||||
static Handle link_dynamic_constant(Klass* caller,
|
||||
int condy_index,
|
||||
Handle bootstrap_specifier,
|
||||
Symbol* name,
|
||||
Symbol* type,
|
||||
TRAPS);
|
||||
|
||||
// ask Java to create a dynamic call site, while linking an invokedynamic op
|
||||
static methodHandle find_dynamic_call_site_invoker(Klass* caller,
|
||||
int indy_index,
|
||||
Handle bootstrap_method,
|
||||
Symbol* name,
|
||||
Symbol* type,
|
||||
|
@ -2054,19 +2054,21 @@ void ClassVerifier::verify_ldc(
|
||||
const constantPoolHandle& cp, u2 bci, TRAPS) {
|
||||
verify_cp_index(bci, cp, index, CHECK_VERIFY(this));
|
||||
constantTag tag = cp->tag_at(index);
|
||||
unsigned int types;
|
||||
unsigned int types = 0;
|
||||
if (opcode == Bytecodes::_ldc || opcode == Bytecodes::_ldc_w) {
|
||||
if (!tag.is_unresolved_klass()) {
|
||||
types = (1 << JVM_CONSTANT_Integer) | (1 << JVM_CONSTANT_Float)
|
||||
| (1 << JVM_CONSTANT_String) | (1 << JVM_CONSTANT_Class)
|
||||
| (1 << JVM_CONSTANT_MethodHandle) | (1 << JVM_CONSTANT_MethodType);
|
||||
| (1 << JVM_CONSTANT_MethodHandle) | (1 << JVM_CONSTANT_MethodType)
|
||||
| (1 << JVM_CONSTANT_Dynamic);
|
||||
// Note: The class file parser already verified the legality of
|
||||
// MethodHandle and MethodType constants.
|
||||
verify_cp_type(bci, index, cp, types, CHECK_VERIFY(this));
|
||||
}
|
||||
} else {
|
||||
assert(opcode == Bytecodes::_ldc2_w, "must be ldc2_w");
|
||||
types = (1 << JVM_CONSTANT_Double) | (1 << JVM_CONSTANT_Long);
|
||||
types = (1 << JVM_CONSTANT_Double) | (1 << JVM_CONSTANT_Long)
|
||||
| (1 << JVM_CONSTANT_Dynamic);
|
||||
verify_cp_type(bci, index, cp, types, CHECK_VERIFY(this));
|
||||
}
|
||||
if (tag.is_string() && cp->is_pseudo_string_at(index)) {
|
||||
@ -2101,6 +2103,30 @@ void ClassVerifier::verify_ldc(
|
||||
current_frame->push_stack(
|
||||
VerificationType::reference_type(
|
||||
vmSymbols::java_lang_invoke_MethodType()), CHECK_VERIFY(this));
|
||||
} else if (tag.is_dynamic_constant()) {
|
||||
Symbol* constant_type = cp->uncached_signature_ref_at(index);
|
||||
if (!SignatureVerifier::is_valid_type_signature(constant_type)) {
|
||||
class_format_error(
|
||||
"Invalid type for dynamic constant in class %s referenced "
|
||||
"from constant pool index %d", _klass->external_name(), index);
|
||||
return;
|
||||
}
|
||||
assert(sizeof(VerificationType) == sizeof(uintptr_t),
|
||||
"buffer type must match VerificationType size");
|
||||
uintptr_t constant_type_buffer[2];
|
||||
VerificationType* v_constant_type = (VerificationType*)constant_type_buffer;
|
||||
SignatureStream sig_stream(constant_type, false);
|
||||
int n = change_sig_to_verificationType(
|
||||
&sig_stream, v_constant_type, CHECK_VERIFY(this));
|
||||
int opcode_n = (opcode == Bytecodes::_ldc2_w ? 2 : 1);
|
||||
if (n != opcode_n) {
|
||||
// wrong kind of ldc; reverify against updated type mask
|
||||
types &= ~(1 << JVM_CONSTANT_Dynamic);
|
||||
verify_cp_type(bci, index, cp, types, CHECK_VERIFY(this));
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
current_frame->push_stack(v_constant_type[i], CHECK_VERIFY(this));
|
||||
}
|
||||
} else {
|
||||
/* Unreachable? verify_cp_type has already validated the cp type. */
|
||||
verify_error(
|
||||
@ -2665,7 +2691,7 @@ void ClassVerifier::verify_invoke_instructions(
|
||||
// Make sure the constant pool item is the right type
|
||||
u2 index = bcs->get_index_u2();
|
||||
Bytecodes::Code opcode = bcs->raw_code();
|
||||
unsigned int types;
|
||||
unsigned int types = 0;
|
||||
switch (opcode) {
|
||||
case Bytecodes::_invokeinterface:
|
||||
types = 1 << JVM_CONSTANT_InterfaceMethodref;
|
||||
|
@ -40,7 +40,8 @@ class Verifier : AllStatic {
|
||||
STRICTER_ACCESS_CTRL_CHECK_VERSION = 49,
|
||||
STACKMAP_ATTRIBUTE_MAJOR_VERSION = 50,
|
||||
INVOKEDYNAMIC_MAJOR_VERSION = 51,
|
||||
NO_RELAX_ACCESS_CTRL_CHECK_VERSION = 52
|
||||
NO_RELAX_ACCESS_CTRL_CHECK_VERSION = 52,
|
||||
DYNAMICCONSTANT_MAJOR_VERSION = 55
|
||||
};
|
||||
typedef enum { ThrowException, NoException } Mode;
|
||||
|
||||
|
@ -98,6 +98,14 @@ void vmSymbols::initialize(TRAPS) {
|
||||
_type_signatures[T_BOOLEAN] = bool_signature();
|
||||
_type_signatures[T_VOID] = void_signature();
|
||||
// no single signatures for T_OBJECT or T_ARRAY
|
||||
#ifdef ASSERT
|
||||
for (int i = (int)T_BOOLEAN; i < (int)T_VOID+1; i++) {
|
||||
Symbol* s = _type_signatures[i];
|
||||
if (s == NULL) continue;
|
||||
BasicType st = signature_type(s);
|
||||
assert(st == i, "");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ASSERT
|
||||
@ -202,9 +210,11 @@ void vmSymbols::serialize(SerializeClosure* soc) {
|
||||
|
||||
BasicType vmSymbols::signature_type(const Symbol* s) {
|
||||
assert(s != NULL, "checking");
|
||||
for (int i = T_BOOLEAN; i < T_VOID+1; i++) {
|
||||
if (s == _type_signatures[i]) {
|
||||
return (BasicType)i;
|
||||
if (s->utf8_length() == 1) {
|
||||
BasicType result = char2type(s->byte_at(0));
|
||||
if (is_java_primitive(result) || result == T_VOID) {
|
||||
assert(s == _type_signatures[result], "");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return T_OBJECT;
|
||||
|
@ -307,8 +307,10 @@
|
||||
template(linkMethodHandleConstant_signature, "(Ljava/lang/Class;ILjava/lang/Class;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;") \
|
||||
template(linkMethod_name, "linkMethod") \
|
||||
template(linkMethod_signature, "(Ljava/lang/Class;ILjava/lang/Class;Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/invoke/MemberName;") \
|
||||
template(linkDynamicConstant_name, "linkDynamicConstant") \
|
||||
template(linkDynamicConstant_signature, "(Ljava/lang/Object;ILjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;") \
|
||||
template(linkCallSite_name, "linkCallSite") \
|
||||
template(linkCallSite_signature, "(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/invoke/MemberName;") \
|
||||
template(linkCallSite_signature, "(Ljava/lang/Object;ILjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/invoke/MemberName;") \
|
||||
template(setTargetNormal_name, "setTargetNormal") \
|
||||
template(setTargetVolatile_name, "setTargetVolatile") \
|
||||
template(setTarget_signature, "(Ljava/lang/invoke/MethodHandle;)V") \
|
||||
|
@ -207,8 +207,7 @@ int Bytecode_loadconstant::pool_index() const {
|
||||
|
||||
BasicType Bytecode_loadconstant::result_type() const {
|
||||
int index = pool_index();
|
||||
constantTag tag = _method->constants()->tag_at(index);
|
||||
return tag.basic_type();
|
||||
return _method->constants()->basic_type_for_constant_at(index);
|
||||
}
|
||||
|
||||
oop Bytecode_loadconstant::resolve_constant(TRAPS) const {
|
||||
@ -217,6 +216,8 @@ oop Bytecode_loadconstant::resolve_constant(TRAPS) const {
|
||||
ConstantPool* constants = _method->constants();
|
||||
if (has_cache_index()) {
|
||||
return constants->resolve_cached_constant_at(index, THREAD);
|
||||
} else if (_method->constants()->tag_at(index).is_dynamic_constant()) {
|
||||
return constants->resolve_possibly_cached_constant_at(index, THREAD);
|
||||
} else {
|
||||
return constants->resolve_constant_at(index, THREAD);
|
||||
}
|
||||
|
@ -2368,6 +2368,30 @@ run:
|
||||
THREAD->set_vm_result(NULL);
|
||||
break;
|
||||
|
||||
case JVM_CONSTANT_Dynamic:
|
||||
{
|
||||
oop result = constants->resolved_references()->obj_at(index);
|
||||
if (result == NULL) {
|
||||
CALL_VM(InterpreterRuntime::resolve_ldc(THREAD, (Bytecodes::Code) opcode), handle_exception);
|
||||
result = THREAD->vm_result();
|
||||
}
|
||||
VERIFY_OOP(result);
|
||||
|
||||
jvalue value;
|
||||
BasicType type = java_lang_boxing_object::get_value(result, &value);
|
||||
switch (type) {
|
||||
case T_FLOAT: SET_STACK_FLOAT(value.f, 0); break;
|
||||
case T_INT: SET_STACK_INT(value.i, 0); break;
|
||||
case T_SHORT: SET_STACK_INT(value.s, 0); break;
|
||||
case T_BYTE: SET_STACK_INT(value.b, 0); break;
|
||||
case T_CHAR: SET_STACK_INT(value.c, 0); break;
|
||||
case T_BOOLEAN: SET_STACK_INT(value.z, 0); break;
|
||||
default: ShouldNotReachHere();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default: ShouldNotReachHere();
|
||||
}
|
||||
UPDATE_PC_AND_TOS_AND_CONTINUE(incr, 1);
|
||||
@ -2387,6 +2411,27 @@ run:
|
||||
case JVM_CONSTANT_Double:
|
||||
SET_STACK_DOUBLE(constants->double_at(index), 1);
|
||||
break;
|
||||
|
||||
case JVM_CONSTANT_Dynamic:
|
||||
{
|
||||
oop result = constants->resolved_references()->obj_at(index);
|
||||
if (result == NULL) {
|
||||
CALL_VM(InterpreterRuntime::resolve_ldc(THREAD, (Bytecodes::Code) opcode), handle_exception);
|
||||
result = THREAD->vm_result();
|
||||
}
|
||||
VERIFY_OOP(result);
|
||||
|
||||
jvalue value;
|
||||
BasicType type = java_lang_boxing_object::get_value(result, &value);
|
||||
switch (type) {
|
||||
case T_DOUBLE: SET_STACK_DOUBLE(value.d, 1); break;
|
||||
case T_LONG: SET_STACK_LONG(value.j, 1); break;
|
||||
default: ShouldNotReachHere();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default: ShouldNotReachHere();
|
||||
}
|
||||
UPDATE_PC_AND_TOS_AND_CONTINUE(3, 2);
|
||||
@ -2404,7 +2449,7 @@ run:
|
||||
incr = 3;
|
||||
}
|
||||
|
||||
// We are resolved if the f1 field contains a non-null object (CallSite, etc.)
|
||||
// We are resolved if the resolved_references array contains a non-null object (CallSite, etc.)
|
||||
// This kind of CP cache entry does not need to match the flags byte, because
|
||||
// there is a 1-1 relation between bytecode type and CP entry type.
|
||||
ConstantPool* constants = METHOD->constants();
|
||||
@ -2414,6 +2459,8 @@ run:
|
||||
handle_exception);
|
||||
result = THREAD->vm_result();
|
||||
}
|
||||
if (result == Universe::the_null_sentinel())
|
||||
result = NULL;
|
||||
|
||||
VERIFY_OOP(result);
|
||||
SET_STACK_OBJECT(result, 0);
|
||||
@ -2425,7 +2472,7 @@ run:
|
||||
u4 index = Bytes::get_native_u4(pc+1);
|
||||
ConstantPoolCacheEntry* cache = cp->constant_pool()->invokedynamic_cp_cache_entry_at(index);
|
||||
|
||||
// We are resolved if the resolved_references field contains a non-null object (CallSite, etc.)
|
||||
// We are resolved if the resolved_references array contains a non-null object (CallSite, etc.)
|
||||
// This kind of CP cache entry does not need to match the flags byte, because
|
||||
// there is a 1-1 relation between bytecode type and CP entry type.
|
||||
if (! cache->is_resolved((Bytecodes::Code) opcode)) {
|
||||
|
@ -367,6 +367,7 @@ void BytecodePrinter::print_field_or_method(int orig_i, int i, outputStream* st)
|
||||
case JVM_CONSTANT_Fieldref:
|
||||
break;
|
||||
case JVM_CONSTANT_NameAndType:
|
||||
case JVM_CONSTANT_Dynamic:
|
||||
case JVM_CONSTANT_InvokeDynamic:
|
||||
has_klass = false;
|
||||
break;
|
||||
@ -382,7 +383,7 @@ void BytecodePrinter::print_field_or_method(int orig_i, int i, outputStream* st)
|
||||
Symbol* klass = constants->klass_name_at(constants->uncached_klass_ref_index_at(i));
|
||||
st->print_cr(" %d <%s.%s%s%s> ", i, klass->as_C_string(), name->as_C_string(), sep, signature->as_C_string());
|
||||
} else {
|
||||
if (tag.is_invoke_dynamic()) {
|
||||
if (tag.is_dynamic_constant() || tag.is_invoke_dynamic()) {
|
||||
int bsm = constants->invoke_dynamic_bootstrap_method_ref_index_at(i);
|
||||
st->print(" bsm=%d", bsm);
|
||||
}
|
||||
|
@ -118,22 +118,54 @@ IRT_ENTRY(void, InterpreterRuntime::ldc(JavaThread* thread, bool wide))
|
||||
IRT_END
|
||||
|
||||
IRT_ENTRY(void, InterpreterRuntime::resolve_ldc(JavaThread* thread, Bytecodes::Code bytecode)) {
|
||||
assert(bytecode == Bytecodes::_fast_aldc ||
|
||||
assert(bytecode == Bytecodes::_ldc ||
|
||||
bytecode == Bytecodes::_ldc_w ||
|
||||
bytecode == Bytecodes::_ldc2_w ||
|
||||
bytecode == Bytecodes::_fast_aldc ||
|
||||
bytecode == Bytecodes::_fast_aldc_w, "wrong bc");
|
||||
ResourceMark rm(thread);
|
||||
const bool is_fast_aldc = (bytecode == Bytecodes::_fast_aldc ||
|
||||
bytecode == Bytecodes::_fast_aldc_w);
|
||||
LastFrameAccessor last_frame(thread);
|
||||
methodHandle m (thread, last_frame.method());
|
||||
Bytecode_loadconstant ldc(m, last_frame.bci());
|
||||
|
||||
// Double-check the size. (Condy can have any type.)
|
||||
BasicType type = ldc.result_type();
|
||||
switch (type2size[type]) {
|
||||
case 2: guarantee(bytecode == Bytecodes::_ldc2_w, ""); break;
|
||||
case 1: guarantee(bytecode != Bytecodes::_ldc2_w, ""); break;
|
||||
default: ShouldNotReachHere();
|
||||
}
|
||||
|
||||
// Resolve the constant. This does not do unboxing.
|
||||
// But it does replace Universe::the_null_sentinel by null.
|
||||
oop result = ldc.resolve_constant(CHECK);
|
||||
assert(result != NULL || is_fast_aldc, "null result only valid for fast_aldc");
|
||||
|
||||
#ifdef ASSERT
|
||||
{
|
||||
// The bytecode wrappers aren't GC-safe so construct a new one
|
||||
Bytecode_loadconstant ldc2(m, last_frame.bci());
|
||||
oop coop = m->constants()->resolved_references()->obj_at(ldc2.cache_index());
|
||||
assert(result == coop, "expected result for assembly code");
|
||||
int rindex = ldc2.cache_index();
|
||||
if (rindex < 0)
|
||||
rindex = m->constants()->cp_to_object_index(ldc2.pool_index());
|
||||
if (rindex >= 0) {
|
||||
oop coop = m->constants()->resolved_references()->obj_at(rindex);
|
||||
oop roop = (result == NULL ? Universe::the_null_sentinel() : result);
|
||||
assert(roop == coop, "expected result for assembly code");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
thread->set_vm_result(result);
|
||||
if (!is_fast_aldc) {
|
||||
// Tell the interpreter how to unbox the primitive.
|
||||
guarantee(java_lang_boxing_object::is_instance(result, type), "");
|
||||
int offset = java_lang_boxing_object::value_offset_in_bytes(type);
|
||||
intptr_t flags = ((as_TosState(type) << ConstantPoolCacheEntry::tos_state_shift)
|
||||
| (offset & ConstantPoolCacheEntry::field_index_mask));
|
||||
thread->set_vm_result_2((Metadata*)flags);
|
||||
}
|
||||
}
|
||||
IRT_END
|
||||
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include "memory/universe.inline.hpp"
|
||||
#include "oops/instanceKlass.hpp"
|
||||
#include "oops/method.hpp"
|
||||
#include "oops/objArrayKlass.hpp"
|
||||
#include "oops/objArrayOop.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "prims/methodHandles.hpp"
|
||||
@ -54,7 +55,6 @@
|
||||
#include "runtime/thread.inline.hpp"
|
||||
#include "runtime/vmThread.hpp"
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------
|
||||
// Implementation of CallInfo
|
||||
|
||||
@ -284,20 +284,32 @@ void LinkInfo::print() {
|
||||
//------------------------------------------------------------------------------------------------------------------------
|
||||
// Klass resolution
|
||||
|
||||
void LinkResolver::check_klass_accessability(Klass* ref_klass, Klass* sel_klass, TRAPS) {
|
||||
void LinkResolver::check_klass_accessability(Klass* ref_klass, Klass* sel_klass,
|
||||
bool fold_type_to_class, TRAPS) {
|
||||
Klass* base_klass = sel_klass;
|
||||
if (fold_type_to_class) {
|
||||
if (sel_klass->is_objArray_klass()) {
|
||||
base_klass = ObjArrayKlass::cast(sel_klass)->bottom_klass();
|
||||
}
|
||||
// The element type could be a typeArray - we only need the access
|
||||
// check if it is an reference to another class.
|
||||
if (!base_klass->is_instance_klass()) {
|
||||
return; // no relevant check to do
|
||||
}
|
||||
}
|
||||
Reflection::VerifyClassAccessResults vca_result =
|
||||
Reflection::verify_class_access(ref_klass, InstanceKlass::cast(sel_klass), true);
|
||||
Reflection::verify_class_access(ref_klass, InstanceKlass::cast(base_klass), true);
|
||||
if (vca_result != Reflection::ACCESS_OK) {
|
||||
ResourceMark rm(THREAD);
|
||||
char* msg = Reflection::verify_class_access_msg(ref_klass,
|
||||
InstanceKlass::cast(sel_klass),
|
||||
InstanceKlass::cast(base_klass),
|
||||
vca_result);
|
||||
if (msg == NULL) {
|
||||
Exceptions::fthrow(
|
||||
THREAD_AND_LOCATION,
|
||||
vmSymbols::java_lang_IllegalAccessError(),
|
||||
"failed to access class %s from class %s",
|
||||
sel_klass->external_name(),
|
||||
base_klass->external_name(),
|
||||
ref_klass->external_name());
|
||||
} else {
|
||||
// Use module specific message returned by verify_class_access_msg().
|
||||
@ -1663,31 +1675,6 @@ void LinkResolver::resolve_handle_call(CallInfo& result,
|
||||
result.set_handle(resolved_klass, resolved_method, resolved_appendix, resolved_method_type, CHECK);
|
||||
}
|
||||
|
||||
static void wrap_invokedynamic_exception(TRAPS) {
|
||||
if (HAS_PENDING_EXCEPTION) {
|
||||
// See the "Linking Exceptions" section for the invokedynamic instruction
|
||||
// in JVMS 6.5.
|
||||
if (PENDING_EXCEPTION->is_a(SystemDictionary::Error_klass())) {
|
||||
// Pass through an Error, including BootstrapMethodError, any other form
|
||||
// of linkage error, or say ThreadDeath/OutOfMemoryError
|
||||
if (TraceMethodHandles) {
|
||||
tty->print_cr("invokedynamic passes through an Error for " INTPTR_FORMAT, p2i((void *)PENDING_EXCEPTION));
|
||||
PENDING_EXCEPTION->print();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise wrap the exception in a BootstrapMethodError
|
||||
if (TraceMethodHandles) {
|
||||
tty->print_cr("invokedynamic throws BSME for " INTPTR_FORMAT, p2i((void *)PENDING_EXCEPTION));
|
||||
PENDING_EXCEPTION->print();
|
||||
}
|
||||
Handle nested_exception(THREAD, PENDING_EXCEPTION);
|
||||
CLEAR_PENDING_EXCEPTION;
|
||||
THROW_CAUSE(vmSymbols::java_lang_BootstrapMethodError(), nested_exception)
|
||||
}
|
||||
}
|
||||
|
||||
void LinkResolver::resolve_invokedynamic(CallInfo& result, const constantPoolHandle& pool, int index, TRAPS) {
|
||||
Symbol* method_name = pool->name_ref_at(index);
|
||||
Symbol* method_signature = pool->signature_ref_at(index);
|
||||
@ -1714,7 +1701,7 @@ void LinkResolver::resolve_invokedynamic(CallInfo& result, const constantPoolHan
|
||||
// set the indy_rf flag since any subsequent invokedynamic instruction which shares
|
||||
// this bootstrap method will encounter the resolution of MethodHandleInError.
|
||||
oop bsm_info = pool->resolve_bootstrap_specifier_at(pool_index, THREAD);
|
||||
wrap_invokedynamic_exception(CHECK);
|
||||
Exceptions::wrap_dynamic_exception(CHECK);
|
||||
assert(bsm_info != NULL, "");
|
||||
// FIXME: Cache this once per BootstrapMethods entry, not once per CONSTANT_InvokeDynamic.
|
||||
bootstrap_specifier = Handle(THREAD, bsm_info);
|
||||
@ -1724,7 +1711,7 @@ void LinkResolver::resolve_invokedynamic(CallInfo& result, const constantPoolHan
|
||||
Handle appendix( THREAD, cpce->appendix_if_resolved(pool));
|
||||
Handle method_type(THREAD, cpce->method_type_if_resolved(pool));
|
||||
result.set_handle(method, appendix, method_type, THREAD);
|
||||
wrap_invokedynamic_exception(CHECK);
|
||||
Exceptions::wrap_dynamic_exception(CHECK);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1737,7 +1724,7 @@ void LinkResolver::resolve_invokedynamic(CallInfo& result, const constantPoolHan
|
||||
tty->print(" BSM info: "); bootstrap_specifier->print();
|
||||
}
|
||||
|
||||
resolve_dynamic_call(result, bootstrap_specifier, method_name,
|
||||
resolve_dynamic_call(result, pool_index, bootstrap_specifier, method_name,
|
||||
method_signature, current_klass, THREAD);
|
||||
if (HAS_PENDING_EXCEPTION && PENDING_EXCEPTION->is_a(SystemDictionary::LinkageError_klass())) {
|
||||
int encoded_index = ResolutionErrorTable::encode_cpcache_index(index);
|
||||
@ -1753,7 +1740,7 @@ void LinkResolver::resolve_invokedynamic(CallInfo& result, const constantPoolHan
|
||||
Handle appendix( THREAD, cpce->appendix_if_resolved(pool));
|
||||
Handle method_type(THREAD, cpce->method_type_if_resolved(pool));
|
||||
result.set_handle(method, appendix, method_type, THREAD);
|
||||
wrap_invokedynamic_exception(CHECK);
|
||||
Exceptions::wrap_dynamic_exception(CHECK);
|
||||
} else {
|
||||
assert(cpce->indy_resolution_failed(), "Resolution failure flag not set");
|
||||
ConstantPool::throw_resolution_error(pool, encoded_index, CHECK);
|
||||
@ -1765,6 +1752,7 @@ void LinkResolver::resolve_invokedynamic(CallInfo& result, const constantPoolHan
|
||||
}
|
||||
|
||||
void LinkResolver::resolve_dynamic_call(CallInfo& result,
|
||||
int pool_index,
|
||||
Handle bootstrap_specifier,
|
||||
Symbol* method_name, Symbol* method_signature,
|
||||
Klass* current_klass,
|
||||
@ -1775,12 +1763,13 @@ void LinkResolver::resolve_dynamic_call(CallInfo& result,
|
||||
Handle resolved_method_type;
|
||||
methodHandle resolved_method =
|
||||
SystemDictionary::find_dynamic_call_site_invoker(current_klass,
|
||||
pool_index,
|
||||
bootstrap_specifier,
|
||||
method_name, method_signature,
|
||||
&resolved_appendix,
|
||||
&resolved_method_type,
|
||||
THREAD);
|
||||
wrap_invokedynamic_exception(CHECK);
|
||||
Exceptions::wrap_dynamic_exception(CHECK);
|
||||
result.set_handle(resolved_method, resolved_appendix, resolved_method_type, THREAD);
|
||||
wrap_invokedynamic_exception(CHECK);
|
||||
Exceptions::wrap_dynamic_exception(CHECK);
|
||||
}
|
||||
|
@ -274,7 +274,16 @@ class LinkResolver: AllStatic {
|
||||
const constantPoolHandle& pool, int index, TRAPS);
|
||||
public:
|
||||
// constant pool resolving
|
||||
static void check_klass_accessability(Klass* ref_klass, Klass* sel_klass, TRAPS);
|
||||
static void check_klass_accessability(Klass* ref_klass, Klass* sel_klass,
|
||||
bool fold_type_to_class, TRAPS);
|
||||
// The optional 'fold_type_to_class' means that a derived type (array)
|
||||
// is first converted to the class it is derived from (element type).
|
||||
// If this element type is not a class, then the check passes quietly.
|
||||
// This is usually what is needed, but a few existing uses might break
|
||||
// if this flag were always turned on. FIXME: See if it can be, always.
|
||||
static void check_klass_accessability(Klass* ref_klass, Klass* sel_klass, TRAPS) {
|
||||
return check_klass_accessability(ref_klass, sel_klass, false, THREAD);
|
||||
}
|
||||
|
||||
// static resolving calls (will not run any Java code);
|
||||
// used only from Bytecode_invoke::static_target
|
||||
@ -306,7 +315,7 @@ class LinkResolver: AllStatic {
|
||||
bool check_null_and_abstract, TRAPS);
|
||||
static void resolve_handle_call (CallInfo& result,
|
||||
const LinkInfo& link_info, TRAPS);
|
||||
static void resolve_dynamic_call (CallInfo& result, Handle bootstrap_specifier,
|
||||
static void resolve_dynamic_call (CallInfo& result, int pool_index, Handle bootstrap_specifier,
|
||||
Symbol* method_name, Symbol* method_signature,
|
||||
Klass* current_klass, TRAPS);
|
||||
|
||||
|
@ -48,7 +48,11 @@ void Rewriter::compute_index_maps() {
|
||||
case JVM_CONSTANT_Methodref : // fall through
|
||||
add_cp_cache_entry(i);
|
||||
break;
|
||||
case JVM_CONSTANT_String:
|
||||
case JVM_CONSTANT_Dynamic:
|
||||
assert(_pool->has_dynamic_constant(), "constant pool's _has_dynamic_constant flag not set");
|
||||
add_resolved_references_entry(i);
|
||||
break;
|
||||
case JVM_CONSTANT_String : // fall through
|
||||
case JVM_CONSTANT_MethodHandle : // fall through
|
||||
case JVM_CONSTANT_MethodType : // fall through
|
||||
add_resolved_references_entry(i);
|
||||
@ -321,7 +325,14 @@ void Rewriter::maybe_rewrite_ldc(address bcp, int offset, bool is_wide,
|
||||
address p = bcp + offset;
|
||||
int cp_index = is_wide ? Bytes::get_Java_u2(p) : (u1)(*p);
|
||||
constantTag tag = _pool->tag_at(cp_index).value();
|
||||
if (tag.is_method_handle() || tag.is_method_type() || tag.is_string()) {
|
||||
|
||||
if (tag.is_method_handle() ||
|
||||
tag.is_method_type() ||
|
||||
tag.is_string() ||
|
||||
(tag.is_dynamic_constant() &&
|
||||
// keep regular ldc interpreter logic for condy primitives
|
||||
is_reference_type(FieldType::basic_type(_pool->uncached_signature_ref_at(cp_index))))
|
||||
) {
|
||||
int ref_index = cp_entry_to_resolved_references(cp_index);
|
||||
if (is_wide) {
|
||||
(*bcp) = Bytecodes::_fast_aldc_w;
|
||||
|
@ -278,7 +278,7 @@ void TemplateTable::initialize() {
|
||||
def(Bytecodes::_sipush , ubcp|____|____|____, vtos, itos, sipush , _ );
|
||||
def(Bytecodes::_ldc , ubcp|____|clvm|____, vtos, vtos, ldc , false );
|
||||
def(Bytecodes::_ldc_w , ubcp|____|clvm|____, vtos, vtos, ldc , true );
|
||||
def(Bytecodes::_ldc2_w , ubcp|____|____|____, vtos, vtos, ldc2_w , _ );
|
||||
def(Bytecodes::_ldc2_w , ubcp|____|clvm|____, vtos, vtos, ldc2_w , _ );
|
||||
def(Bytecodes::_iload , ubcp|____|clvm|____, vtos, itos, iload , _ );
|
||||
def(Bytecodes::_lload , ubcp|____|____|____, vtos, ltos, lload , _ );
|
||||
def(Bytecodes::_fload , ubcp|____|____|____, vtos, ftos, fload , _ );
|
||||
|
@ -295,6 +295,7 @@ class TemplateTable: AllStatic {
|
||||
static void getstatic(int byte_no);
|
||||
static void putstatic(int byte_no);
|
||||
static void pop_and_check_object(Register obj);
|
||||
static void condy_helper(Label& Done); // shared by ldc instances
|
||||
|
||||
static void _new();
|
||||
static void newarray();
|
||||
|
@ -766,11 +766,10 @@ C2V_END
|
||||
|
||||
C2V_VMENTRY(jboolean, isCompilable,(JNIEnv *, jobject, jobject jvmci_method))
|
||||
methodHandle method = CompilerToVM::asMethod(jvmci_method);
|
||||
// Skip redefined methods
|
||||
if (method->is_old()) {
|
||||
return false;
|
||||
}
|
||||
return !method->is_not_compilable(CompLevel_full_optimization);
|
||||
constantPoolHandle cp = method->constMethod()->constants();
|
||||
assert(!cp.is_null(), "npe");
|
||||
// don't inline method when constant pool contains a CONSTANT_Dynamic
|
||||
return !method->is_not_compilable(CompLevel_full_optimization) && !cp->has_dynamic_constant();
|
||||
C2V_END
|
||||
|
||||
C2V_VMENTRY(jboolean, hasNeverInlineDirective,(JNIEnv *, jobject, jobject jvmci_method))
|
||||
|
@ -119,6 +119,7 @@
|
||||
nonstatic_field(ConstantPool, _tags, Array<u1>*) \
|
||||
nonstatic_field(ConstantPool, _pool_holder, InstanceKlass*) \
|
||||
nonstatic_field(ConstantPool, _length, int) \
|
||||
nonstatic_field(ConstantPool, _flags, int) \
|
||||
\
|
||||
nonstatic_field(ConstMethod, _constants, ConstantPool*) \
|
||||
nonstatic_field(ConstMethod, _flags, u2) \
|
||||
@ -415,6 +416,7 @@
|
||||
declare_constant(JVM_CONSTANT_UnresolvedClassInError) \
|
||||
declare_constant(JVM_CONSTANT_MethodHandleInError) \
|
||||
declare_constant(JVM_CONSTANT_MethodTypeInError) \
|
||||
declare_constant(JVM_CONSTANT_DynamicInError) \
|
||||
declare_constant(JVM_CONSTANT_InternalMax) \
|
||||
\
|
||||
declare_constant(ArrayData::array_len_off_set) \
|
||||
@ -452,6 +454,7 @@
|
||||
declare_constant(CodeInstaller::INVOKE_INVALID) \
|
||||
\
|
||||
declare_constant(ConstantPool::CPCACHE_INDEX_TAG) \
|
||||
declare_constant(ConstantPool::_has_dynamic_constant) \
|
||||
\
|
||||
declare_constant(ConstMethod::_has_linenumber_table) \
|
||||
declare_constant(ConstMethod::_has_localvariable_table) \
|
||||
|
@ -111,6 +111,7 @@ oop Universe::_main_thread_group = NULL;
|
||||
oop Universe::_system_thread_group = NULL;
|
||||
objArrayOop Universe::_the_empty_class_klass_array = NULL;
|
||||
Array<Klass*>* Universe::_the_array_interfaces_array = NULL;
|
||||
oop Universe::_the_null_sentinel = NULL;
|
||||
oop Universe::_the_null_string = NULL;
|
||||
oop Universe::_the_min_jint_string = NULL;
|
||||
LatestMethodCache* Universe::_finalizer_register_cache = NULL;
|
||||
@ -195,6 +196,7 @@ void Universe::oops_do(OopClosure* f, bool do_all) {
|
||||
assert(_mirrors[0] == NULL && _mirrors[T_BOOLEAN - 1] == NULL, "checking");
|
||||
|
||||
f->do_oop((oop*)&_the_empty_class_klass_array);
|
||||
f->do_oop((oop*)&_the_null_sentinel);
|
||||
f->do_oop((oop*)&_the_null_string);
|
||||
f->do_oop((oop*)&_the_min_jint_string);
|
||||
f->do_oop((oop*)&_out_of_memory_error_java_heap);
|
||||
@ -381,6 +383,11 @@ void Universe::genesis(TRAPS) {
|
||||
initialize_basic_type_klass(longArrayKlassObj(), CHECK);
|
||||
} // end of core bootstrapping
|
||||
|
||||
{
|
||||
Handle tns = java_lang_String::create_from_str("<null_sentinel>", CHECK);
|
||||
_the_null_sentinel = tns();
|
||||
}
|
||||
|
||||
// Maybe this could be lifted up now that object array can be initialized
|
||||
// during the bootstrapping.
|
||||
|
||||
|
@ -141,6 +141,7 @@ class Universe: AllStatic {
|
||||
static oop _system_thread_group; // Reference to the system thread group object
|
||||
|
||||
static objArrayOop _the_empty_class_klass_array; // Canonicalized obj array of type java.lang.Class
|
||||
static oop _the_null_sentinel; // A unique object pointer unused except as a sentinel for null.
|
||||
static oop _the_null_string; // A cache of "null" as a Java string
|
||||
static oop _the_min_jint_string; // A cache of "-2147483648" as a Java string
|
||||
static LatestMethodCache* _finalizer_register_cache; // static method for registering finalizable objects
|
||||
@ -322,6 +323,9 @@ class Universe: AllStatic {
|
||||
|
||||
static Method* do_stack_walk_method() { return _do_stack_walk_cache->get_method(); }
|
||||
|
||||
static oop the_null_sentinel() { return _the_null_sentinel; }
|
||||
static address the_null_sentinel_addr() { return (address) &_the_null_sentinel; }
|
||||
|
||||
// Function to initialize these
|
||||
static void initialize_known_methods(TRAPS);
|
||||
|
||||
|
@ -607,7 +607,6 @@ Symbol* ConstantPool::impl_signature_ref_at(int which, bool uncached) {
|
||||
return symbol_at(signature_index);
|
||||
}
|
||||
|
||||
|
||||
int ConstantPool::impl_name_and_type_ref_index_at(int which, bool uncached) {
|
||||
int i = which;
|
||||
if (!uncached && cache() != NULL) {
|
||||
@ -621,14 +620,18 @@ int ConstantPool::impl_name_and_type_ref_index_at(int which, bool uncached) {
|
||||
// change byte-ordering and go via cache
|
||||
i = remap_instruction_operand_from_cache(which);
|
||||
} else {
|
||||
if (tag_at(which).is_invoke_dynamic()) {
|
||||
if (tag_at(which).is_invoke_dynamic() ||
|
||||
tag_at(which).is_dynamic_constant() ||
|
||||
tag_at(which).is_dynamic_constant_in_error()) {
|
||||
int pool_index = invoke_dynamic_name_and_type_ref_index_at(which);
|
||||
assert(tag_at(pool_index).is_name_and_type(), "");
|
||||
return pool_index;
|
||||
}
|
||||
}
|
||||
assert(tag_at(i).is_field_or_method(), "Corrupted constant pool");
|
||||
assert(!tag_at(i).is_invoke_dynamic(), "Must be handled above");
|
||||
assert(!tag_at(i).is_invoke_dynamic() &&
|
||||
!tag_at(i).is_dynamic_constant() &&
|
||||
!tag_at(i).is_dynamic_constant_in_error(), "Must be handled above");
|
||||
jint ref_index = *int_at_addr(i);
|
||||
return extract_high_short_from_int(ref_index);
|
||||
}
|
||||
@ -672,16 +675,12 @@ int ConstantPool::remap_instruction_operand_from_cache(int operand) {
|
||||
|
||||
|
||||
void ConstantPool::verify_constant_pool_resolve(const constantPoolHandle& this_cp, Klass* k, TRAPS) {
|
||||
if (k->is_instance_klass() || k->is_objArray_klass()) {
|
||||
InstanceKlass* holder = this_cp->pool_holder();
|
||||
Klass* elem = k->is_instance_klass() ? k : ObjArrayKlass::cast(k)->bottom_klass();
|
||||
|
||||
// The element type could be a typeArray - we only need the access check if it is
|
||||
// an reference to another class
|
||||
if (elem->is_instance_klass()) {
|
||||
LinkResolver::check_klass_accessability(holder, elem, CHECK);
|
||||
}
|
||||
if (!(k->is_instance_klass() || k->is_objArray_klass())) {
|
||||
return; // short cut, typeArray klass is always accessible
|
||||
}
|
||||
Klass* holder = this_cp->pool_holder();
|
||||
bool fold_type_to_class = true;
|
||||
LinkResolver::check_klass_accessability(holder, k, fold_type_to_class, CHECK);
|
||||
}
|
||||
|
||||
|
||||
@ -769,8 +768,8 @@ void ConstantPool::throw_resolution_error(const constantPoolHandle& this_cp, int
|
||||
THROW_MSG(error, message->as_C_string());
|
||||
}
|
||||
|
||||
// If resolution for Class, MethodHandle or MethodType fails, save the exception
|
||||
// in the resolution error table, so that the same exception is thrown again.
|
||||
// If resolution for Class, Dynamic constant, MethodHandle or MethodType fails, save the
|
||||
// exception in the resolution error table, so that the same exception is thrown again.
|
||||
void ConstantPool::save_and_throw_exception(const constantPoolHandle& this_cp, int which,
|
||||
constantTag tag, TRAPS) {
|
||||
Symbol* error = PENDING_EXCEPTION->klass()->name();
|
||||
@ -806,16 +805,31 @@ void ConstantPool::save_and_throw_exception(const constantPoolHandle& this_cp, i
|
||||
}
|
||||
}
|
||||
|
||||
BasicType ConstantPool::basic_type_for_constant_at(int which) {
|
||||
constantTag tag = tag_at(which);
|
||||
if (tag.is_dynamic_constant() ||
|
||||
tag.is_dynamic_constant_in_error()) {
|
||||
// have to look at the signature for this one
|
||||
Symbol* constant_type = uncached_signature_ref_at(which);
|
||||
return FieldType::basic_type(constant_type);
|
||||
}
|
||||
return tag.basic_type();
|
||||
}
|
||||
|
||||
// Called to resolve constants in the constant pool and return an oop.
|
||||
// Some constant pool entries cache their resolved oop. This is also
|
||||
// called to create oops from constants to use in arguments for invokedynamic
|
||||
oop ConstantPool::resolve_constant_at_impl(const constantPoolHandle& this_cp, int index, int cache_index, TRAPS) {
|
||||
oop ConstantPool::resolve_constant_at_impl(const constantPoolHandle& this_cp,
|
||||
int index, int cache_index,
|
||||
bool* status_return, TRAPS) {
|
||||
oop result_oop = NULL;
|
||||
Handle throw_exception;
|
||||
|
||||
if (cache_index == _possible_index_sentinel) {
|
||||
// It is possible that this constant is one which is cached in the objects.
|
||||
// We'll do a linear search. This should be OK because this usage is rare.
|
||||
// FIXME: If bootstrap specifiers stress this code, consider putting in
|
||||
// a reverse index. Binary search over a short array should do it.
|
||||
assert(index > 0, "valid index");
|
||||
cache_index = this_cp->cp_to_object_index(index);
|
||||
}
|
||||
@ -825,6 +839,12 @@ oop ConstantPool::resolve_constant_at_impl(const constantPoolHandle& this_cp, in
|
||||
if (cache_index >= 0) {
|
||||
result_oop = this_cp->resolved_references()->obj_at(cache_index);
|
||||
if (result_oop != NULL) {
|
||||
if (result_oop == Universe::the_null_sentinel()) {
|
||||
DEBUG_ONLY(int temp_index = (index >= 0 ? index : this_cp->object_to_cp_index(cache_index)));
|
||||
assert(this_cp->tag_at(temp_index).is_dynamic_constant(), "only condy uses the null sentinel");
|
||||
result_oop = NULL;
|
||||
}
|
||||
if (status_return != NULL) (*status_return) = true;
|
||||
return result_oop;
|
||||
// That was easy...
|
||||
}
|
||||
@ -835,6 +855,35 @@ oop ConstantPool::resolve_constant_at_impl(const constantPoolHandle& this_cp, in
|
||||
|
||||
constantTag tag = this_cp->tag_at(index);
|
||||
|
||||
if (status_return != NULL) {
|
||||
// don't trigger resolution if the constant might need it
|
||||
switch (tag.value()) {
|
||||
case JVM_CONSTANT_Class:
|
||||
{
|
||||
CPKlassSlot kslot = this_cp->klass_slot_at(index);
|
||||
int resolved_klass_index = kslot.resolved_klass_index();
|
||||
if (this_cp->resolved_klasses()->at(resolved_klass_index) == NULL) {
|
||||
(*status_return) = false;
|
||||
return NULL;
|
||||
}
|
||||
// the klass is waiting in the CP; go get it
|
||||
break;
|
||||
}
|
||||
case JVM_CONSTANT_String:
|
||||
case JVM_CONSTANT_Integer:
|
||||
case JVM_CONSTANT_Float:
|
||||
case JVM_CONSTANT_Long:
|
||||
case JVM_CONSTANT_Double:
|
||||
// these guys trigger OOM at worst
|
||||
break;
|
||||
default:
|
||||
(*status_return) = false;
|
||||
return NULL;
|
||||
}
|
||||
// from now on there is either success or an OOME
|
||||
(*status_return) = true;
|
||||
}
|
||||
|
||||
switch (tag.value()) {
|
||||
|
||||
case JVM_CONSTANT_UnresolvedClass:
|
||||
@ -848,6 +897,63 @@ oop ConstantPool::resolve_constant_at_impl(const constantPoolHandle& this_cp, in
|
||||
break;
|
||||
}
|
||||
|
||||
case JVM_CONSTANT_Dynamic:
|
||||
{
|
||||
Klass* current_klass = this_cp->pool_holder();
|
||||
Symbol* constant_name = this_cp->uncached_name_ref_at(index);
|
||||
Symbol* constant_type = this_cp->uncached_signature_ref_at(index);
|
||||
|
||||
// The initial step in resolving an unresolved symbolic reference to a
|
||||
// dynamically-computed constant is to resolve the symbolic reference to a
|
||||
// method handle which will be the bootstrap method for the dynamically-computed
|
||||
// constant. If resolution of the java.lang.invoke.MethodHandle for the bootstrap
|
||||
// method fails, then a MethodHandleInError is stored at the corresponding
|
||||
// bootstrap method's CP index for the CONSTANT_MethodHandle_info. No need to
|
||||
// set a DynamicConstantInError here since any subsequent use of this
|
||||
// bootstrap method will encounter the resolution of MethodHandleInError.
|
||||
oop bsm_info = this_cp->resolve_bootstrap_specifier_at(index, THREAD);
|
||||
Exceptions::wrap_dynamic_exception(CHECK_NULL);
|
||||
assert(bsm_info != NULL, "");
|
||||
// FIXME: Cache this once per BootstrapMethods entry, not once per CONSTANT_Dynamic.
|
||||
Handle bootstrap_specifier = Handle(THREAD, bsm_info);
|
||||
|
||||
// Resolve the Dynamically-Computed constant to invoke the BSM in order to obtain the resulting oop.
|
||||
Handle value = SystemDictionary::link_dynamic_constant(current_klass,
|
||||
index,
|
||||
bootstrap_specifier,
|
||||
constant_name,
|
||||
constant_type,
|
||||
THREAD);
|
||||
result_oop = value();
|
||||
Exceptions::wrap_dynamic_exception(THREAD);
|
||||
if (HAS_PENDING_EXCEPTION) {
|
||||
// Resolution failure of the dynamically-computed constant, save_and_throw_exception
|
||||
// will check for a LinkageError and store a DynamicConstantInError.
|
||||
save_and_throw_exception(this_cp, index, tag, CHECK_NULL);
|
||||
}
|
||||
BasicType type = FieldType::basic_type(constant_type);
|
||||
if (!is_reference_type(type)) {
|
||||
// Make sure the primitive value is properly boxed.
|
||||
// This is a JDK responsibility.
|
||||
const char* fail = NULL;
|
||||
if (result_oop == NULL) {
|
||||
fail = "null result instead of box";
|
||||
} else if (!is_java_primitive(type)) {
|
||||
// FIXME: support value types via unboxing
|
||||
fail = "can only handle references and primitives";
|
||||
} else if (!java_lang_boxing_object::is_instance(result_oop, type)) {
|
||||
fail = "primitive is not properly boxed";
|
||||
}
|
||||
if (fail != NULL) {
|
||||
// Since this exception is not a LinkageError, throw exception
|
||||
// but do not save a DynamicInError resolution result.
|
||||
// See section 5.4.3 of the VM spec.
|
||||
THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), fail);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case JVM_CONSTANT_String:
|
||||
assert(cache_index != _no_index_sentinel, "should have been set");
|
||||
if (this_cp->is_pseudo_string_at(index)) {
|
||||
@ -857,6 +963,7 @@ oop ConstantPool::resolve_constant_at_impl(const constantPoolHandle& this_cp, in
|
||||
result_oop = string_at_impl(this_cp, index, cache_index, CHECK_NULL);
|
||||
break;
|
||||
|
||||
case JVM_CONSTANT_DynamicInError:
|
||||
case JVM_CONSTANT_MethodHandleInError:
|
||||
case JVM_CONSTANT_MethodTypeInError:
|
||||
{
|
||||
@ -957,15 +1064,20 @@ oop ConstantPool::resolve_constant_at_impl(const constantPoolHandle& this_cp, in
|
||||
// The important thing here is that all threads pick up the same result.
|
||||
// It doesn't matter which racing thread wins, as long as only one
|
||||
// result is used by all threads, and all future queries.
|
||||
oop old_result = this_cp->resolved_references()->atomic_compare_exchange_oop(cache_index, result_oop, NULL);
|
||||
oop new_result = (result_oop == NULL ? Universe::the_null_sentinel() : result_oop);
|
||||
oop old_result = this_cp->resolved_references()
|
||||
->atomic_compare_exchange_oop(cache_index, new_result, NULL);
|
||||
if (old_result == NULL) {
|
||||
return result_oop; // was installed
|
||||
} else {
|
||||
// Return the winning thread's result. This can be different than
|
||||
// the result here for MethodHandles.
|
||||
if (old_result == Universe::the_null_sentinel())
|
||||
old_result = NULL;
|
||||
return old_result;
|
||||
}
|
||||
} else {
|
||||
assert(result_oop != Universe::the_null_sentinel(), "");
|
||||
return result_oop;
|
||||
}
|
||||
}
|
||||
@ -979,13 +1091,14 @@ oop ConstantPool::uncached_string_at(int which, TRAPS) {
|
||||
|
||||
|
||||
oop ConstantPool::resolve_bootstrap_specifier_at_impl(const constantPoolHandle& this_cp, int index, TRAPS) {
|
||||
assert(this_cp->tag_at(index).is_invoke_dynamic(), "Corrupted constant pool");
|
||||
|
||||
assert((this_cp->tag_at(index).is_invoke_dynamic() ||
|
||||
this_cp->tag_at(index).is_dynamic_constant()), "Corrupted constant pool");
|
||||
Handle bsm;
|
||||
int argc;
|
||||
{
|
||||
// JVM_CONSTANT_InvokeDynamic is an ordered pair of [bootm, name&type], plus optional arguments
|
||||
// The bootm, being a JVM_CONSTANT_MethodHandle, has its own cache entry.
|
||||
// JVM_CONSTANT_InvokeDynamic is an ordered pair of [bootm, name&mtype], plus optional arguments
|
||||
// JVM_CONSTANT_Dynamic is an ordered pair of [bootm, name&ftype], plus optional arguments
|
||||
// In both cases, the bootm, being a JVM_CONSTANT_MethodHandle, has its own cache entry.
|
||||
// It is accompanied by the optional arguments.
|
||||
int bsm_index = this_cp->invoke_dynamic_bootstrap_method_ref_index_at(index);
|
||||
oop bsm_oop = this_cp->resolve_possibly_cached_constant_at(bsm_index, CHECK_NULL);
|
||||
@ -995,30 +1108,142 @@ oop ConstantPool::resolve_bootstrap_specifier_at_impl(const constantPoolHandle&
|
||||
|
||||
// Extract the optional static arguments.
|
||||
argc = this_cp->invoke_dynamic_argument_count_at(index);
|
||||
if (argc == 0) return bsm_oop;
|
||||
|
||||
// if there are no static arguments, return the bsm by itself:
|
||||
if (argc == 0 && UseBootstrapCallInfo < 2) return bsm_oop;
|
||||
|
||||
bsm = Handle(THREAD, bsm_oop);
|
||||
}
|
||||
|
||||
// We are going to return an ordered pair of {bsm, info}, using a 2-array.
|
||||
objArrayHandle info;
|
||||
{
|
||||
objArrayOop info_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(), 1+argc, CHECK_NULL);
|
||||
objArrayOop info_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(), 2, CHECK_NULL);
|
||||
info = objArrayHandle(THREAD, info_oop);
|
||||
}
|
||||
|
||||
info->obj_at_put(0, bsm());
|
||||
for (int i = 0; i < argc; i++) {
|
||||
int arg_index = this_cp->invoke_dynamic_argument_index_at(index, i);
|
||||
oop arg_oop = this_cp->resolve_possibly_cached_constant_at(arg_index, CHECK_NULL);
|
||||
info->obj_at_put(1+i, arg_oop);
|
||||
|
||||
bool use_BSCI;
|
||||
switch (UseBootstrapCallInfo) {
|
||||
default: use_BSCI = true; break; // stress mode
|
||||
case 0: use_BSCI = false; break; // stress mode
|
||||
case 1: // normal mode
|
||||
// If we were to support an alternative mode of BSM invocation,
|
||||
// we'd convert to pull mode here if the BSM could be a candidate
|
||||
// for that alternative mode. We can't easily test for things
|
||||
// like varargs here, but we can get away with approximate testing,
|
||||
// since the JDK runtime will make up the difference either way.
|
||||
// For now, exercise the pull-mode path if the BSM is of arity 2,
|
||||
// or if there is a potential condy loop (see below).
|
||||
oop mt_oop = java_lang_invoke_MethodHandle::type(bsm());
|
||||
use_BSCI = (java_lang_invoke_MethodType::ptype_count(mt_oop) == 2);
|
||||
break;
|
||||
}
|
||||
|
||||
// Here's a reason to use BSCI even if it wasn't requested:
|
||||
// If a condy uses a condy argument, we want to avoid infinite
|
||||
// recursion (condy loops) in the C code. It's OK in Java,
|
||||
// because Java has stack overflow checking, so we punt
|
||||
// potentially cyclic cases from C to Java.
|
||||
if (!use_BSCI && this_cp->tag_at(index).is_dynamic_constant()) {
|
||||
bool found_unresolved_condy = false;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
int arg_index = this_cp->invoke_dynamic_argument_index_at(index, i);
|
||||
if (this_cp->tag_at(arg_index).is_dynamic_constant()) {
|
||||
// potential recursion point condy -> condy
|
||||
bool found_it = false;
|
||||
this_cp->find_cached_constant_at(arg_index, found_it, CHECK_NULL);
|
||||
if (!found_it) { found_unresolved_condy = true; break; }
|
||||
}
|
||||
}
|
||||
if (found_unresolved_condy)
|
||||
use_BSCI = true;
|
||||
}
|
||||
|
||||
const int SMALL_ARITY = 5;
|
||||
if (use_BSCI && argc <= SMALL_ARITY && UseBootstrapCallInfo <= 2) {
|
||||
// If there are only a few arguments, and none of them need linking,
|
||||
// push them, instead of asking the JDK runtime to turn around and
|
||||
// pull them, saving a JVM/JDK transition in some simple cases.
|
||||
bool all_resolved = true;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
bool found_it = false;
|
||||
int arg_index = this_cp->invoke_dynamic_argument_index_at(index, i);
|
||||
this_cp->find_cached_constant_at(arg_index, found_it, CHECK_NULL);
|
||||
if (!found_it) { all_resolved = false; break; }
|
||||
}
|
||||
if (all_resolved)
|
||||
use_BSCI = false;
|
||||
}
|
||||
|
||||
if (!use_BSCI) {
|
||||
// return {bsm, {arg...}}; resolution of arguments is done immediately, before JDK code is called
|
||||
objArrayOop args_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(), argc, CHECK_NULL);
|
||||
info->obj_at_put(1, args_oop); // may overwrite with args[0] below
|
||||
objArrayHandle args(THREAD, args_oop);
|
||||
copy_bootstrap_arguments_at_impl(this_cp, index, 0, argc, args, 0, true, Handle(), CHECK_NULL);
|
||||
if (argc == 1) {
|
||||
// try to discard the singleton array
|
||||
oop arg_oop = args->obj_at(0);
|
||||
if (arg_oop != NULL && !arg_oop->is_array()) {
|
||||
// JVM treats arrays and nulls specially in this position,
|
||||
// but other things are just single arguments
|
||||
info->obj_at_put(1, arg_oop);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// return {bsm, {arg_count, pool_index}}; JDK code must pull the arguments as needed
|
||||
typeArrayOop ints_oop = oopFactory::new_typeArray(T_INT, 2, CHECK_NULL);
|
||||
ints_oop->int_at_put(0, argc);
|
||||
ints_oop->int_at_put(1, index);
|
||||
info->obj_at_put(1, ints_oop);
|
||||
}
|
||||
return info();
|
||||
}
|
||||
|
||||
void ConstantPool::copy_bootstrap_arguments_at_impl(const constantPoolHandle& this_cp, int index,
|
||||
int start_arg, int end_arg,
|
||||
objArrayHandle info, int pos,
|
||||
bool must_resolve, Handle if_not_available,
|
||||
TRAPS) {
|
||||
int argc;
|
||||
int limit = pos + end_arg - start_arg;
|
||||
// checks: index in range [0..this_cp->length),
|
||||
// tag at index, start..end in range [0..argc],
|
||||
// info array non-null, pos..limit in [0..info.length]
|
||||
if ((0 >= index || index >= this_cp->length()) ||
|
||||
!(this_cp->tag_at(index).is_invoke_dynamic() ||
|
||||
this_cp->tag_at(index).is_dynamic_constant()) ||
|
||||
(0 > start_arg || start_arg > end_arg) ||
|
||||
(end_arg > (argc = this_cp->invoke_dynamic_argument_count_at(index))) ||
|
||||
(0 > pos || pos > limit) ||
|
||||
(info.is_null() || limit > info->length())) {
|
||||
// An index or something else went wrong; throw an error.
|
||||
// Since this is an internal API, we don't expect this,
|
||||
// so we don't bother to craft a nice message.
|
||||
THROW_MSG(vmSymbols::java_lang_LinkageError(), "bad BSM argument access");
|
||||
}
|
||||
// now we can loop safely
|
||||
int info_i = pos;
|
||||
for (int i = start_arg; i < end_arg; i++) {
|
||||
int arg_index = this_cp->invoke_dynamic_argument_index_at(index, i);
|
||||
oop arg_oop;
|
||||
if (must_resolve) {
|
||||
arg_oop = this_cp->resolve_possibly_cached_constant_at(arg_index, CHECK);
|
||||
} else {
|
||||
bool found_it = false;
|
||||
arg_oop = this_cp->find_cached_constant_at(arg_index, found_it, CHECK);
|
||||
if (!found_it) arg_oop = if_not_available();
|
||||
}
|
||||
info->obj_at_put(info_i++, arg_oop);
|
||||
}
|
||||
}
|
||||
|
||||
oop ConstantPool::string_at_impl(const constantPoolHandle& this_cp, int which, int obj_index, TRAPS) {
|
||||
// If the string has already been interned, this entry will be non-null
|
||||
oop str = this_cp->resolved_references()->obj_at(obj_index);
|
||||
assert(str != Universe::the_null_sentinel(), "");
|
||||
if (str != NULL) return str;
|
||||
Symbol* sym = this_cp->unresolved_string_at(which);
|
||||
str = StringTable::intern(sym, CHECK_(NULL));
|
||||
@ -1199,6 +1424,18 @@ bool ConstantPool::compare_entry_to(int index1, const constantPoolHandle& cp2,
|
||||
}
|
||||
} break;
|
||||
|
||||
case JVM_CONSTANT_Dynamic:
|
||||
{
|
||||
int k1 = invoke_dynamic_name_and_type_ref_index_at(index1);
|
||||
int k2 = cp2->invoke_dynamic_name_and_type_ref_index_at(index2);
|
||||
int i1 = invoke_dynamic_bootstrap_specifier_index(index1);
|
||||
int i2 = cp2->invoke_dynamic_bootstrap_specifier_index(index2);
|
||||
// separate statements and variables because CHECK_false is used
|
||||
bool match_entry = compare_entry_to(k1, cp2, k2, CHECK_false);
|
||||
bool match_operand = compare_operand_to(i1, cp2, i2, CHECK_false);
|
||||
return (match_entry && match_operand);
|
||||
} break;
|
||||
|
||||
case JVM_CONSTANT_InvokeDynamic:
|
||||
{
|
||||
int k1 = invoke_dynamic_name_and_type_ref_index_at(index1);
|
||||
@ -1525,6 +1762,15 @@ void ConstantPool::copy_entry_to(const constantPoolHandle& from_cp, int from_i,
|
||||
to_cp->method_handle_index_at_put(to_i, k1, k2);
|
||||
} break;
|
||||
|
||||
case JVM_CONSTANT_Dynamic:
|
||||
case JVM_CONSTANT_DynamicInError:
|
||||
{
|
||||
int k1 = from_cp->invoke_dynamic_bootstrap_specifier_index(from_i);
|
||||
int k2 = from_cp->invoke_dynamic_name_and_type_ref_index_at(from_i);
|
||||
k1 += operand_array_length(to_cp->operands()); // to_cp might already have operands
|
||||
to_cp->dynamic_constant_at_put(to_i, k1, k2);
|
||||
} break;
|
||||
|
||||
case JVM_CONSTANT_InvokeDynamic:
|
||||
{
|
||||
int k1 = from_cp->invoke_dynamic_bootstrap_specifier_index(from_i);
|
||||
@ -1786,6 +2032,8 @@ jint ConstantPool::cpool_entry_size(jint idx) {
|
||||
case JVM_CONSTANT_NameAndType:
|
||||
return 5;
|
||||
|
||||
case JVM_CONSTANT_Dynamic:
|
||||
case JVM_CONSTANT_DynamicInError:
|
||||
case JVM_CONSTANT_InvokeDynamic:
|
||||
// u1 tag, u2 bsm, u2 nt
|
||||
return 5;
|
||||
@ -1971,6 +2219,17 @@ int ConstantPool::copy_cpool_bytes(int cpool_size,
|
||||
DBG(printf("JVM_CONSTANT_MethodType: %hd", idx1));
|
||||
break;
|
||||
}
|
||||
case JVM_CONSTANT_Dynamic:
|
||||
case JVM_CONSTANT_DynamicInError: {
|
||||
*bytes = tag;
|
||||
idx1 = extract_low_short_from_int(*int_at_addr(idx));
|
||||
idx2 = extract_high_short_from_int(*int_at_addr(idx));
|
||||
assert(idx2 == invoke_dynamic_name_and_type_ref_index_at(idx), "correct half of u4");
|
||||
Bytes::put_Java_u2((address) (bytes+1), idx1);
|
||||
Bytes::put_Java_u2((address) (bytes+3), idx2);
|
||||
DBG(printf("JVM_CONSTANT_Dynamic: %hd %hd", idx1, idx2));
|
||||
break;
|
||||
}
|
||||
case JVM_CONSTANT_InvokeDynamic: {
|
||||
*bytes = tag;
|
||||
idx1 = extract_low_short_from_int(*int_at_addr(idx));
|
||||
@ -2176,6 +2435,21 @@ void ConstantPool::print_entry_on(const int index, outputStream* st) {
|
||||
case JVM_CONSTANT_MethodTypeInError :
|
||||
st->print("signature_index=%d", method_type_index_at(index));
|
||||
break;
|
||||
case JVM_CONSTANT_Dynamic :
|
||||
case JVM_CONSTANT_DynamicInError :
|
||||
{
|
||||
st->print("bootstrap_method_index=%d", invoke_dynamic_bootstrap_method_ref_index_at(index));
|
||||
st->print(" type_index=%d", invoke_dynamic_name_and_type_ref_index_at(index));
|
||||
int argc = invoke_dynamic_argument_count_at(index);
|
||||
if (argc > 0) {
|
||||
for (int arg_i = 0; arg_i < argc; arg_i++) {
|
||||
int arg = invoke_dynamic_argument_index_at(index, arg_i);
|
||||
st->print((arg_i == 0 ? " arguments={%d" : ", %d"), arg);
|
||||
}
|
||||
st->print("}");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JVM_CONSTANT_InvokeDynamic :
|
||||
{
|
||||
st->print("bootstrap_method_index=%d", invoke_dynamic_bootstrap_method_ref_index_at(index));
|
||||
|
@ -113,9 +113,10 @@ class ConstantPool : public Metadata {
|
||||
Array<Klass*>* _resolved_klasses;
|
||||
|
||||
enum {
|
||||
_has_preresolution = 1, // Flags
|
||||
_on_stack = 2,
|
||||
_is_shared = 4
|
||||
_has_preresolution = 1, // Flags
|
||||
_on_stack = 2,
|
||||
_is_shared = 4,
|
||||
_has_dynamic_constant = 8
|
||||
};
|
||||
|
||||
int _flags; // old fashioned bit twiddling
|
||||
@ -207,6 +208,9 @@ class ConstantPool : public Metadata {
|
||||
// Faster than MetaspaceObj::is_shared() - used by set_on_stack()
|
||||
bool is_shared() const { return (_flags & _is_shared) != 0; }
|
||||
|
||||
bool has_dynamic_constant() const { return (_flags & _has_dynamic_constant) != 0; }
|
||||
void set_has_dynamic_constant() { _flags |= _has_dynamic_constant; }
|
||||
|
||||
// Klass holding pool
|
||||
InstanceKlass* pool_holder() const { return _pool_holder; }
|
||||
void set_pool_holder(InstanceKlass* k) { _pool_holder = k; }
|
||||
@ -297,6 +301,11 @@ class ConstantPool : public Metadata {
|
||||
*int_at_addr(which) = ref_index;
|
||||
}
|
||||
|
||||
void dynamic_constant_at_put(int which, int bootstrap_specifier_index, int name_and_type_index) {
|
||||
tag_at_put(which, JVM_CONSTANT_Dynamic);
|
||||
*int_at_addr(which) = ((jint) name_and_type_index<<16) | bootstrap_specifier_index;
|
||||
}
|
||||
|
||||
void invoke_dynamic_at_put(int which, int bootstrap_specifier_index, int name_and_type_index) {
|
||||
tag_at_put(which, JVM_CONSTANT_InvokeDynamic);
|
||||
*int_at_addr(which) = ((jint) name_and_type_index<<16) | bootstrap_specifier_index;
|
||||
@ -554,11 +563,15 @@ class ConstantPool : public Metadata {
|
||||
}
|
||||
|
||||
int invoke_dynamic_name_and_type_ref_index_at(int which) {
|
||||
assert(tag_at(which).is_invoke_dynamic(), "Corrupted constant pool");
|
||||
assert(tag_at(which).is_invoke_dynamic() ||
|
||||
tag_at(which).is_dynamic_constant() ||
|
||||
tag_at(which).is_dynamic_constant_in_error(), "Corrupted constant pool");
|
||||
return extract_high_short_from_int(*int_at_addr(which));
|
||||
}
|
||||
int invoke_dynamic_bootstrap_specifier_index(int which) {
|
||||
assert(tag_at(which).value() == JVM_CONSTANT_InvokeDynamic, "Corrupted constant pool");
|
||||
assert(tag_at(which).is_invoke_dynamic() ||
|
||||
tag_at(which).is_dynamic_constant() ||
|
||||
tag_at(which).is_dynamic_constant_in_error(), "Corrupted constant pool");
|
||||
return extract_low_short_from_int(*int_at_addr(which));
|
||||
}
|
||||
int invoke_dynamic_operand_base(int which) {
|
||||
@ -608,7 +621,7 @@ class ConstantPool : public Metadata {
|
||||
}
|
||||
#endif //ASSERT
|
||||
|
||||
// layout of InvokeDynamic bootstrap method specifier (in second part of operands array):
|
||||
// layout of InvokeDynamic and Dynamic bootstrap method specifier (in second part of operands array):
|
||||
enum {
|
||||
_indy_bsm_offset = 0, // CONSTANT_MethodHandle bsm
|
||||
_indy_argc_offset = 1, // u2 argc
|
||||
@ -654,14 +667,17 @@ class ConstantPool : public Metadata {
|
||||
// Shrink the operands array to a smaller array with new_len length
|
||||
void shrink_operands(int new_len, TRAPS);
|
||||
|
||||
|
||||
int invoke_dynamic_bootstrap_method_ref_index_at(int which) {
|
||||
assert(tag_at(which).is_invoke_dynamic(), "Corrupted constant pool");
|
||||
assert(tag_at(which).is_invoke_dynamic() ||
|
||||
tag_at(which).is_dynamic_constant() ||
|
||||
tag_at(which).is_dynamic_constant_in_error(), "Corrupted constant pool");
|
||||
int op_base = invoke_dynamic_operand_base(which);
|
||||
return operands()->at(op_base + _indy_bsm_offset);
|
||||
}
|
||||
int invoke_dynamic_argument_count_at(int which) {
|
||||
assert(tag_at(which).is_invoke_dynamic(), "Corrupted constant pool");
|
||||
assert(tag_at(which).is_invoke_dynamic() ||
|
||||
tag_at(which).is_dynamic_constant() ||
|
||||
tag_at(which).is_dynamic_constant_in_error(), "Corrupted constant pool");
|
||||
int op_base = invoke_dynamic_operand_base(which);
|
||||
int argc = operands()->at(op_base + _indy_argc_offset);
|
||||
DEBUG_ONLY(int end_offset = op_base + _indy_argv_offset + argc;
|
||||
@ -731,20 +747,27 @@ class ConstantPool : public Metadata {
|
||||
enum { _no_index_sentinel = -1, _possible_index_sentinel = -2 };
|
||||
public:
|
||||
|
||||
BasicType basic_type_for_constant_at(int which);
|
||||
|
||||
// Resolve late bound constants.
|
||||
oop resolve_constant_at(int index, TRAPS) {
|
||||
constantPoolHandle h_this(THREAD, this);
|
||||
return resolve_constant_at_impl(h_this, index, _no_index_sentinel, THREAD);
|
||||
return resolve_constant_at_impl(h_this, index, _no_index_sentinel, NULL, THREAD);
|
||||
}
|
||||
|
||||
oop resolve_cached_constant_at(int cache_index, TRAPS) {
|
||||
constantPoolHandle h_this(THREAD, this);
|
||||
return resolve_constant_at_impl(h_this, _no_index_sentinel, cache_index, THREAD);
|
||||
return resolve_constant_at_impl(h_this, _no_index_sentinel, cache_index, NULL, THREAD);
|
||||
}
|
||||
|
||||
oop resolve_possibly_cached_constant_at(int pool_index, TRAPS) {
|
||||
constantPoolHandle h_this(THREAD, this);
|
||||
return resolve_constant_at_impl(h_this, pool_index, _possible_index_sentinel, THREAD);
|
||||
return resolve_constant_at_impl(h_this, pool_index, _possible_index_sentinel, NULL, THREAD);
|
||||
}
|
||||
|
||||
oop find_cached_constant_at(int pool_index, bool& found_it, TRAPS) {
|
||||
constantPoolHandle h_this(THREAD, this);
|
||||
return resolve_constant_at_impl(h_this, pool_index, _possible_index_sentinel, &found_it, THREAD);
|
||||
}
|
||||
|
||||
oop resolve_bootstrap_specifier_at(int index, TRAPS) {
|
||||
@ -752,6 +775,15 @@ class ConstantPool : public Metadata {
|
||||
return resolve_bootstrap_specifier_at_impl(h_this, index, THREAD);
|
||||
}
|
||||
|
||||
void copy_bootstrap_arguments_at(int index,
|
||||
int start_arg, int end_arg,
|
||||
objArrayHandle info, int pos,
|
||||
bool must_resolve, Handle if_not_available, TRAPS) {
|
||||
constantPoolHandle h_this(THREAD, this);
|
||||
copy_bootstrap_arguments_at_impl(h_this, index, start_arg, end_arg,
|
||||
info, pos, must_resolve, if_not_available, THREAD);
|
||||
}
|
||||
|
||||
// Klass name matches name at offset
|
||||
bool klass_name_at_matches(const InstanceKlass* k, int which);
|
||||
|
||||
@ -833,6 +865,7 @@ class ConstantPool : public Metadata {
|
||||
|
||||
Symbol* impl_name_ref_at(int which, bool uncached);
|
||||
Symbol* impl_signature_ref_at(int which, bool uncached);
|
||||
|
||||
int impl_klass_ref_index_at(int which, bool uncached);
|
||||
int impl_name_and_type_ref_index_at(int which, bool uncached);
|
||||
constantTag impl_tag_ref_at(int which, bool uncached);
|
||||
@ -862,8 +895,13 @@ class ConstantPool : public Metadata {
|
||||
// Resolve string constants (to prevent allocation during compilation)
|
||||
static void resolve_string_constants_impl(const constantPoolHandle& this_cp, TRAPS);
|
||||
|
||||
static oop resolve_constant_at_impl(const constantPoolHandle& this_cp, int index, int cache_index, TRAPS);
|
||||
static oop resolve_constant_at_impl(const constantPoolHandle& this_cp, int index, int cache_index,
|
||||
bool* status_return, TRAPS);
|
||||
static oop resolve_bootstrap_specifier_at_impl(const constantPoolHandle& this_cp, int index, TRAPS);
|
||||
static void copy_bootstrap_arguments_at_impl(const constantPoolHandle& this_cp, int index,
|
||||
int start_arg, int end_arg,
|
||||
objArrayHandle info, int pos,
|
||||
bool must_resolve, Handle if_not_available, TRAPS);
|
||||
|
||||
// Exception handling
|
||||
static Symbol* exception_message(const constantPoolHandle& this_cp, int which, constantTag tag, oop pending_exception);
|
||||
|
@ -1878,13 +1878,15 @@ void GenerateOopMap::do_ldc(int bci) {
|
||||
ConstantPool* cp = method()->constants();
|
||||
constantTag tag = cp->tag_at(ldc.pool_index()); // idx is index in resolved_references
|
||||
BasicType bt = ldc.result_type();
|
||||
#ifdef ASSERT
|
||||
BasicType tag_bt = tag.is_dynamic_constant() ? bt : tag.basic_type();
|
||||
assert(bt == tag_bt, "same result");
|
||||
#endif
|
||||
CellTypeState cts;
|
||||
if (tag.basic_type() == T_OBJECT) {
|
||||
if (is_reference_type(bt)) { // could be T_ARRAY with condy
|
||||
assert(!tag.is_string_index() && !tag.is_klass_index(), "Unexpected index tag");
|
||||
assert(bt == T_OBJECT, "Guard is incorrect");
|
||||
cts = CellTypeState::make_line_ref(bci);
|
||||
} else {
|
||||
assert(bt != T_OBJECT, "Guard is incorrect");
|
||||
cts = valCTS;
|
||||
}
|
||||
ppush1(cts);
|
||||
|
@ -1483,8 +1483,9 @@ void Parse::do_one_bytecode() {
|
||||
// If the constant is unresolved, run this BC once in the interpreter.
|
||||
{
|
||||
ciConstant constant = iter().get_constant();
|
||||
if (constant.basic_type() == T_OBJECT &&
|
||||
!constant.as_object()->is_loaded()) {
|
||||
if (!constant.is_valid() ||
|
||||
(constant.basic_type() == T_OBJECT &&
|
||||
!constant.as_object()->is_loaded())) {
|
||||
int index = iter().get_constant_pool_index();
|
||||
constantTag tag = iter().get_constant_pool_tag(index);
|
||||
uncommon_trap(Deoptimization::make_trap_request
|
||||
|
@ -2212,6 +2212,8 @@ JVM_ENTRY(jbyte, JVM_ConstantPoolGetTagAt(JNIEnv *env, jobject obj, jobject unus
|
||||
result = JVM_CONSTANT_MethodType;
|
||||
} else if (tag.is_method_handle_in_error()) {
|
||||
result = JVM_CONSTANT_MethodHandle;
|
||||
} else if (tag.is_dynamic_constant_in_error()) {
|
||||
result = JVM_CONSTANT_Dynamic;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -497,6 +497,7 @@ void VM_RedefineClasses::append_entry(const constantPoolHandle& scratch_cp,
|
||||
} break;
|
||||
|
||||
// this is an indirect CP entry so it needs special handling
|
||||
case JVM_CONSTANT_Dynamic: // fall through
|
||||
case JVM_CONSTANT_InvokeDynamic:
|
||||
{
|
||||
// Index of the bootstrap specifier in the operands array
|
||||
@ -509,15 +510,18 @@ void VM_RedefineClasses::append_entry(const constantPoolHandle& scratch_cp,
|
||||
merge_cp_length_p, THREAD);
|
||||
if (new_bs_i != old_bs_i) {
|
||||
log_trace(redefine, class, constantpool)
|
||||
("InvokeDynamic entry@%d bootstrap_method_attr_index change: %d to %d",
|
||||
("Dynamic entry@%d bootstrap_method_attr_index change: %d to %d",
|
||||
*merge_cp_length_p, old_bs_i, new_bs_i);
|
||||
}
|
||||
if (new_ref_i != old_ref_i) {
|
||||
log_trace(redefine, class, constantpool)
|
||||
("InvokeDynamic entry@%d name_and_type_index change: %d to %d", *merge_cp_length_p, old_ref_i, new_ref_i);
|
||||
("Dynamic entry@%d name_and_type_index change: %d to %d", *merge_cp_length_p, old_ref_i, new_ref_i);
|
||||
}
|
||||
|
||||
(*merge_cp_p)->invoke_dynamic_at_put(*merge_cp_length_p, new_bs_i, new_ref_i);
|
||||
if (scratch_cp->tag_at(scratch_i).is_dynamic_constant())
|
||||
(*merge_cp_p)->dynamic_constant_at_put(*merge_cp_length_p, new_bs_i, new_ref_i);
|
||||
else
|
||||
(*merge_cp_p)->invoke_dynamic_at_put(*merge_cp_length_p, new_bs_i, new_ref_i);
|
||||
if (scratch_i != *merge_cp_length_p) {
|
||||
// The new entry in *merge_cp_p is at a different index than
|
||||
// the new entry in scratch_cp so we need to map the index values.
|
||||
|
@ -1359,6 +1359,87 @@ JVM_ENTRY(void, MHN_setCallSiteTargetVolatile(JNIEnv* env, jobject igcls, jobjec
|
||||
}
|
||||
JVM_END
|
||||
|
||||
JVM_ENTRY(void, MHN_copyOutBootstrapArguments(JNIEnv* env, jobject igcls,
|
||||
jobject caller_jh, jintArray index_info_jh,
|
||||
jint start, jint end,
|
||||
jobjectArray buf_jh, jint pos,
|
||||
jboolean resolve, jobject ifna_jh)) {
|
||||
Klass* caller_k = java_lang_Class::as_Klass(JNIHandles::resolve(caller_jh));
|
||||
if (caller_k == NULL || !caller_k->is_instance_klass()) {
|
||||
THROW_MSG(vmSymbols::java_lang_InternalError(), "bad caller");
|
||||
}
|
||||
InstanceKlass* caller = InstanceKlass::cast(caller_k);
|
||||
typeArrayOop index_info_oop = (typeArrayOop) JNIHandles::resolve(index_info_jh);
|
||||
if (index_info_oop == NULL ||
|
||||
index_info_oop->klass() != Universe::intArrayKlassObj() ||
|
||||
typeArrayOop(index_info_oop)->length() < 2) {
|
||||
THROW_MSG(vmSymbols::java_lang_InternalError(), "bad index info (0)");
|
||||
}
|
||||
typeArrayHandle index_info(THREAD, index_info_oop);
|
||||
int bss_index_in_pool = index_info->int_at(1);
|
||||
// While we are here, take a quick look at the index info:
|
||||
if (bss_index_in_pool <= 0 ||
|
||||
bss_index_in_pool >= caller->constants()->length() ||
|
||||
index_info->int_at(0)
|
||||
!= caller->constants()->invoke_dynamic_argument_count_at(bss_index_in_pool)) {
|
||||
THROW_MSG(vmSymbols::java_lang_InternalError(), "bad index info (1)");
|
||||
}
|
||||
objArrayHandle buf(THREAD, (objArrayOop) JNIHandles::resolve(buf_jh));
|
||||
if (start < 0) {
|
||||
for (int pseudo_index = -4; pseudo_index < 0; pseudo_index++) {
|
||||
if (start == pseudo_index) {
|
||||
if (start >= end || 0 > pos || pos >= buf->length()) break;
|
||||
oop pseudo_arg = NULL;
|
||||
switch (pseudo_index) {
|
||||
case -4: // bootstrap method
|
||||
{
|
||||
int bsm_index = caller->constants()->invoke_dynamic_bootstrap_method_ref_index_at(bss_index_in_pool);
|
||||
pseudo_arg = caller->constants()->resolve_possibly_cached_constant_at(bsm_index, CHECK);
|
||||
break;
|
||||
}
|
||||
case -3: // name
|
||||
{
|
||||
Symbol* name = caller->constants()->name_ref_at(bss_index_in_pool);
|
||||
Handle str = java_lang_String::create_from_symbol(name, CHECK);
|
||||
pseudo_arg = str();
|
||||
break;
|
||||
}
|
||||
case -2: // type
|
||||
{
|
||||
Symbol* type = caller->constants()->signature_ref_at(bss_index_in_pool);
|
||||
Handle th;
|
||||
if (type->byte_at(0) == '(') {
|
||||
th = SystemDictionary::find_method_handle_type(type, caller, CHECK);
|
||||
} else {
|
||||
th = SystemDictionary::find_java_mirror_for_type(type, caller, SignatureStream::NCDFError, CHECK);
|
||||
}
|
||||
pseudo_arg = th();
|
||||
break;
|
||||
}
|
||||
case -1: // argument count
|
||||
{
|
||||
int argc = caller->constants()->invoke_dynamic_argument_count_at(bss_index_in_pool);
|
||||
jvalue argc_value; argc_value.i = (jint)argc;
|
||||
pseudo_arg = java_lang_boxing_object::create(T_INT, &argc_value, CHECK);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Store the pseudo-argument, and advance the pointers.
|
||||
buf->obj_at_put(pos++, pseudo_arg);
|
||||
++start;
|
||||
}
|
||||
}
|
||||
// When we are done with this there may be regular arguments to process too.
|
||||
}
|
||||
Handle ifna(THREAD, JNIHandles::resolve(ifna_jh));
|
||||
caller->constants()->
|
||||
copy_bootstrap_arguments_at(bss_index_in_pool,
|
||||
start, end, buf, pos,
|
||||
(resolve == JNI_TRUE), ifna, CHECK);
|
||||
}
|
||||
JVM_END
|
||||
|
||||
// It is called by a Cleaner object which ensures that dropped CallSites properly
|
||||
// deallocate their dependency information.
|
||||
JVM_ENTRY(void, MHN_clearCallSiteContext(JNIEnv* env, jobject igcls, jobject context_jh)) {
|
||||
@ -1438,6 +1519,7 @@ static JNINativeMethod MHN_methods[] = {
|
||||
{CC "objectFieldOffset", CC "(" MEM ")J", FN_PTR(MHN_objectFieldOffset)},
|
||||
{CC "setCallSiteTargetNormal", CC "(" CS "" MH ")V", FN_PTR(MHN_setCallSiteTargetNormal)},
|
||||
{CC "setCallSiteTargetVolatile", CC "(" CS "" MH ")V", FN_PTR(MHN_setCallSiteTargetVolatile)},
|
||||
{CC "copyOutBootstrapArguments", CC "(" CLS "[III[" OBJ "IZ" OBJ ")V", FN_PTR(MHN_copyOutBootstrapArguments)},
|
||||
{CC "clearCallSiteContext", CC "(" CTX ")V", FN_PTR(MHN_clearCallSiteContext)},
|
||||
{CC "staticFieldOffset", CC "(" MEM ")J", FN_PTR(MHN_staticFieldOffset)},
|
||||
{CC "staticFieldBase", CC "(" MEM ")" OBJ, FN_PTR(MHN_staticFieldBase)},
|
||||
|
@ -3961,6 +3961,14 @@ public:
|
||||
develop(bool, TraceInvokeDynamic, false, \
|
||||
"trace internal invoke dynamic operations") \
|
||||
\
|
||||
diagnostic(int, UseBootstrapCallInfo, 1, \
|
||||
"0: when resolving InDy or ConDy, force all BSM arguments to be " \
|
||||
"resolved before the bootstrap method is called; 1: when a BSM " \
|
||||
"that may accept a BootstrapCallInfo is detected, use that API " \
|
||||
"to pass BSM arguments, which allows the BSM to delay their " \
|
||||
"resolution; 2+: stress test the BCI API by calling more BSMs " \
|
||||
"via that API, instead of with the eagerly-resolved array.") \
|
||||
\
|
||||
diagnostic(bool, PauseAtStartup, false, \
|
||||
"Causes the VM to pause at startup time and wait for the pause " \
|
||||
"file to be removed (default: ./vm.paused.<pid>)") \
|
||||
|
@ -765,27 +765,26 @@ void Reflection::check_for_inner_class(const InstanceKlass* outer, const Instanc
|
||||
static oop get_mirror_from_signature(const methodHandle& method,
|
||||
SignatureStream* ss,
|
||||
TRAPS) {
|
||||
Klass* accessing_klass = method->method_holder();
|
||||
assert(accessing_klass != NULL, "method has no accessing_klass");
|
||||
|
||||
oop mirror_oop = ss->as_java_mirror(Handle(THREAD, accessing_klass->class_loader()),
|
||||
Handle(THREAD, accessing_klass->protection_domain()),
|
||||
SignatureStream::NCDFError,
|
||||
CHECK_NULL);
|
||||
|
||||
if (T_OBJECT == ss->type() || T_ARRAY == ss->type()) {
|
||||
Symbol* name = ss->as_symbol(CHECK_NULL);
|
||||
oop loader = method->method_holder()->class_loader();
|
||||
oop protection_domain = method->method_holder()->protection_domain();
|
||||
const Klass* k = SystemDictionary::resolve_or_fail(name,
|
||||
Handle(THREAD, loader),
|
||||
Handle(THREAD, protection_domain),
|
||||
true,
|
||||
CHECK_NULL);
|
||||
if (log_is_enabled(Debug, class, resolve)) {
|
||||
trace_class_resolution(k);
|
||||
// Special tracing logic for resolution of class names during reflection.
|
||||
if (log_is_enabled(Debug, class, resolve)) {
|
||||
Klass* result = java_lang_Class::as_Klass(mirror_oop);
|
||||
if (result != NULL) {
|
||||
trace_class_resolution(result);
|
||||
}
|
||||
return k->java_mirror();
|
||||
}
|
||||
|
||||
assert(ss->type() != T_VOID || ss->at_return_type(),
|
||||
"T_VOID should only appear as return type");
|
||||
|
||||
return java_lang_Class::primitive_mirror(ss->type());
|
||||
return mirror_oop;
|
||||
}
|
||||
|
||||
static objArrayHandle get_parameter_types(const methodHandle& method,
|
||||
@ -819,24 +818,17 @@ static objArrayHandle get_exception_types(const methodHandle& method, TRAPS) {
|
||||
}
|
||||
|
||||
static Handle new_type(Symbol* signature, Klass* k, TRAPS) {
|
||||
// Basic types
|
||||
BasicType type = vmSymbols::signature_type(signature);
|
||||
if (type != T_OBJECT) {
|
||||
return Handle(THREAD, Universe::java_mirror(type));
|
||||
}
|
||||
|
||||
Klass* result =
|
||||
SystemDictionary::resolve_or_fail(signature,
|
||||
Handle(THREAD, k->class_loader()),
|
||||
Handle(THREAD, k->protection_domain()),
|
||||
true, CHECK_(Handle()));
|
||||
Handle mirror = SystemDictionary::find_java_mirror_for_type(signature, k, SignatureStream::NCDFError, CHECK_(Handle()));
|
||||
|
||||
// Special tracing logic for resolution of class names during reflection.
|
||||
if (log_is_enabled(Debug, class, resolve)) {
|
||||
trace_class_resolution(result);
|
||||
Klass* result = java_lang_Class::as_Klass(mirror());
|
||||
if (result != NULL) {
|
||||
trace_class_resolution(result);
|
||||
}
|
||||
}
|
||||
|
||||
oop nt = result->java_mirror();
|
||||
return Handle(THREAD, nt);
|
||||
return mirror;
|
||||
}
|
||||
|
||||
|
||||
|
@ -392,11 +392,19 @@ Klass* SignatureStream::as_klass(Handle class_loader, Handle protection_domain,
|
||||
|
||||
oop SignatureStream::as_java_mirror(Handle class_loader, Handle protection_domain,
|
||||
FailureMode failure_mode, TRAPS) {
|
||||
if (!is_object())
|
||||
return Universe::java_mirror(type());
|
||||
Klass* klass = as_klass(class_loader, protection_domain, failure_mode, CHECK_NULL);
|
||||
if (klass == NULL) return NULL;
|
||||
return klass->java_mirror();
|
||||
if (raw_length() == 1) {
|
||||
// short-cut in a common case
|
||||
return SystemDictionary::find_java_mirror_for_type((char) raw_byte_at(0));
|
||||
}
|
||||
TempNewSymbol signature = SymbolTable::new_symbol(_signature, _begin, _end, CHECK_NULL);
|
||||
Klass* no_accessing_klass = NULL;
|
||||
Handle mirror = SystemDictionary::find_java_mirror_for_type(signature,
|
||||
no_accessing_klass,
|
||||
class_loader,
|
||||
protection_domain,
|
||||
failure_mode,
|
||||
CHECK_NULL);
|
||||
return mirror();
|
||||
}
|
||||
|
||||
Symbol* SignatureStream::as_symbol_or_null() {
|
||||
|
@ -409,6 +409,11 @@ class SignatureStream : public StackObj {
|
||||
const jbyte* raw_bytes() { return _signature->bytes() + _begin; }
|
||||
int raw_length() { return _end - _begin; }
|
||||
|
||||
jbyte raw_byte_at(int index) {
|
||||
assert(index >= 0 && index < raw_length(), "index overflow");
|
||||
return _signature->byte_at(_begin + index);
|
||||
}
|
||||
|
||||
// return same as_symbol except allocation of new symbols is avoided.
|
||||
Symbol* as_symbol_or_null();
|
||||
|
||||
|
@ -2339,6 +2339,7 @@ typedef PaddedEnd<ObjectMonitor> PaddedObjectMonitor;
|
||||
declare_constant(JVM_CONSTANT_NameAndType) \
|
||||
declare_constant(JVM_CONSTANT_MethodHandle) \
|
||||
declare_constant(JVM_CONSTANT_MethodType) \
|
||||
declare_constant(JVM_CONSTANT_Dynamic) \
|
||||
declare_constant(JVM_CONSTANT_InvokeDynamic) \
|
||||
declare_constant(JVM_CONSTANT_ExternalMax) \
|
||||
\
|
||||
@ -2350,6 +2351,7 @@ typedef PaddedEnd<ObjectMonitor> PaddedObjectMonitor;
|
||||
declare_constant(JVM_CONSTANT_UnresolvedClassInError) \
|
||||
declare_constant(JVM_CONSTANT_MethodHandleInError) \
|
||||
declare_constant(JVM_CONSTANT_MethodTypeInError) \
|
||||
declare_constant(JVM_CONSTANT_DynamicInError) \
|
||||
declare_constant(JVM_CONSTANT_InternalMax) \
|
||||
\
|
||||
/*****************************/ \
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -56,6 +56,11 @@ BasicType constantTag::basic_type() const {
|
||||
case JVM_CONSTANT_MethodType :
|
||||
case JVM_CONSTANT_MethodTypeInError :
|
||||
return T_OBJECT;
|
||||
|
||||
case JVM_CONSTANT_Dynamic :
|
||||
case JVM_CONSTANT_DynamicInError :
|
||||
assert(false, "Dynamic constant has no fixed basic type");
|
||||
|
||||
default:
|
||||
ShouldNotReachHere();
|
||||
return T_ILLEGAL;
|
||||
@ -71,6 +76,8 @@ jbyte constantTag::non_error_value() const {
|
||||
return JVM_CONSTANT_MethodHandle;
|
||||
case JVM_CONSTANT_MethodTypeInError:
|
||||
return JVM_CONSTANT_MethodType;
|
||||
case JVM_CONSTANT_DynamicInError:
|
||||
return JVM_CONSTANT_Dynamic;
|
||||
default:
|
||||
return _tag;
|
||||
}
|
||||
@ -85,6 +92,8 @@ jbyte constantTag::error_value() const {
|
||||
return JVM_CONSTANT_MethodHandleInError;
|
||||
case JVM_CONSTANT_MethodType:
|
||||
return JVM_CONSTANT_MethodTypeInError;
|
||||
case JVM_CONSTANT_Dynamic:
|
||||
return JVM_CONSTANT_DynamicInError;
|
||||
default:
|
||||
ShouldNotReachHere();
|
||||
return JVM_CONSTANT_Invalid;
|
||||
@ -123,6 +132,10 @@ const char* constantTag::internal_name() const {
|
||||
return "MethodType";
|
||||
case JVM_CONSTANT_MethodTypeInError :
|
||||
return "MethodType Error";
|
||||
case JVM_CONSTANT_Dynamic :
|
||||
return "Dynamic";
|
||||
case JVM_CONSTANT_DynamicInError :
|
||||
return "Dynamic Error";
|
||||
case JVM_CONSTANT_InvokeDynamic :
|
||||
return "InvokeDynamic";
|
||||
case JVM_CONSTANT_Utf8 :
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -43,7 +43,8 @@ enum {
|
||||
JVM_CONSTANT_UnresolvedClassInError = 103, // Error tag due to resolution error
|
||||
JVM_CONSTANT_MethodHandleInError = 104, // Error tag due to resolution error
|
||||
JVM_CONSTANT_MethodTypeInError = 105, // Error tag due to resolution error
|
||||
JVM_CONSTANT_InternalMax = 105 // Last implementation tag
|
||||
JVM_CONSTANT_DynamicInError = 106, // Error tag due to resolution error
|
||||
JVM_CONSTANT_InternalMax = 106 // Last implementation tag
|
||||
};
|
||||
|
||||
|
||||
@ -80,6 +81,10 @@ class constantTag VALUE_OBJ_CLASS_SPEC {
|
||||
return _tag == JVM_CONSTANT_MethodTypeInError;
|
||||
}
|
||||
|
||||
bool is_dynamic_constant_in_error() const {
|
||||
return _tag == JVM_CONSTANT_DynamicInError;
|
||||
}
|
||||
|
||||
bool is_klass_index() const { return _tag == JVM_CONSTANT_ClassIndex; }
|
||||
bool is_string_index() const { return _tag == JVM_CONSTANT_StringIndex; }
|
||||
|
||||
@ -88,13 +93,14 @@ class constantTag VALUE_OBJ_CLASS_SPEC {
|
||||
bool is_field_or_method() const { return is_field() || is_method() || is_interface_method(); }
|
||||
bool is_symbol() const { return is_utf8(); }
|
||||
|
||||
bool is_method_type() const { return _tag == JVM_CONSTANT_MethodType; }
|
||||
bool is_method_handle() const { return _tag == JVM_CONSTANT_MethodHandle; }
|
||||
bool is_invoke_dynamic() const { return _tag == JVM_CONSTANT_InvokeDynamic; }
|
||||
bool is_method_type() const { return _tag == JVM_CONSTANT_MethodType; }
|
||||
bool is_method_handle() const { return _tag == JVM_CONSTANT_MethodHandle; }
|
||||
bool is_dynamic_constant() const { return _tag == JVM_CONSTANT_Dynamic; }
|
||||
bool is_invoke_dynamic() const { return _tag == JVM_CONSTANT_InvokeDynamic; }
|
||||
|
||||
bool is_loadable_constant() const {
|
||||
return ((_tag >= JVM_CONSTANT_Integer && _tag <= JVM_CONSTANT_String) ||
|
||||
is_method_type() || is_method_handle() ||
|
||||
is_method_type() || is_method_handle() || is_dynamic_constant() ||
|
||||
is_unresolved_klass());
|
||||
}
|
||||
|
||||
@ -108,6 +114,20 @@ class constantTag VALUE_OBJ_CLASS_SPEC {
|
||||
_tag = tag;
|
||||
}
|
||||
|
||||
static constantTag ofBasicType(BasicType bt) {
|
||||
if (is_subword_type(bt)) bt = T_INT;
|
||||
switch (bt) {
|
||||
case T_OBJECT: return constantTag(JVM_CONSTANT_String);
|
||||
case T_INT: return constantTag(JVM_CONSTANT_Integer);
|
||||
case T_LONG: return constantTag(JVM_CONSTANT_Long);
|
||||
case T_FLOAT: return constantTag(JVM_CONSTANT_Float);
|
||||
case T_DOUBLE: return constantTag(JVM_CONSTANT_Double);
|
||||
default: break;
|
||||
}
|
||||
assert(false, "bad basic type for tag");
|
||||
return constantTag();
|
||||
}
|
||||
|
||||
jbyte value() const { return _tag; }
|
||||
jbyte error_value() const;
|
||||
jbyte non_error_value() const;
|
||||
|
@ -403,6 +403,37 @@ Handle Exceptions::new_exception(Thread* thread, Symbol* name,
|
||||
h_prot, to_utf8_safe);
|
||||
}
|
||||
|
||||
// invokedynamic uses wrap_dynamic_exception for:
|
||||
// - bootstrap method resolution
|
||||
// - post call to MethodHandleNatives::linkCallSite
|
||||
// dynamically computed constant uses wrap_dynamic_exception for:
|
||||
// - bootstrap method resolution
|
||||
// - post call to MethodHandleNatives::linkDynamicConstant
|
||||
void Exceptions::wrap_dynamic_exception(Thread* THREAD) {
|
||||
if (THREAD->has_pending_exception()) {
|
||||
oop exception = THREAD->pending_exception();
|
||||
// See the "Linking Exceptions" section for the invokedynamic instruction
|
||||
// in JVMS 6.5.
|
||||
if (exception->is_a(SystemDictionary::Error_klass())) {
|
||||
// Pass through an Error, including BootstrapMethodError, any other form
|
||||
// of linkage error, or say ThreadDeath/OutOfMemoryError
|
||||
if (TraceMethodHandles) {
|
||||
tty->print_cr("[constant/invoke]dynamic passes through an Error for " INTPTR_FORMAT, p2i((void *)exception));
|
||||
exception->print();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise wrap the exception in a BootstrapMethodError
|
||||
if (TraceMethodHandles) {
|
||||
tty->print_cr("[constant/invoke]dynamic throws BSME for " INTPTR_FORMAT, p2i((void *)exception));
|
||||
exception->print();
|
||||
}
|
||||
Handle nested_exception(THREAD, exception);
|
||||
THREAD->clear_pending_exception();
|
||||
THROW_CAUSE(vmSymbols::java_lang_BootstrapMethodError(), nested_exception)
|
||||
}
|
||||
}
|
||||
|
||||
// Exception counting for hs_err file
|
||||
volatile int Exceptions::_stack_overflow_errors = 0;
|
||||
|
@ -166,6 +166,8 @@ class Exceptions {
|
||||
|
||||
static void throw_stack_overflow_exception(Thread* thread, const char* file, int line, const methodHandle& method);
|
||||
|
||||
static void wrap_dynamic_exception(Thread* thread);
|
||||
|
||||
// Exception counting for error files of interesting exceptions that may have
|
||||
// caused a problem for the jvm
|
||||
static volatile int _stack_overflow_errors;
|
||||
|
@ -596,6 +596,10 @@ inline bool is_signed_subword_type(BasicType t) {
|
||||
return (t == T_BYTE || t == T_SHORT);
|
||||
}
|
||||
|
||||
inline bool is_reference_type(BasicType t) {
|
||||
return (t == T_OBJECT || t == T_ARRAY);
|
||||
}
|
||||
|
||||
// Convert a char from a classfile signature to a BasicType
|
||||
inline BasicType char2type(char c) {
|
||||
switch( c ) {
|
||||
|
@ -26,11 +26,15 @@
|
||||
package java.lang;
|
||||
|
||||
/**
|
||||
* Thrown to indicate that an {@code invokedynamic} instruction has
|
||||
* failed to find its bootstrap method,
|
||||
* or the bootstrap method has failed to provide a
|
||||
* {@linkplain java.lang.invoke.CallSite call site} with a {@linkplain java.lang.invoke.CallSite#getTarget target}
|
||||
* of the correct {@linkplain java.lang.invoke.MethodHandle#type() method type}.
|
||||
* Thrown to indicate that an {@code invokedynamic} instruction or a dynamic
|
||||
* constant failed to resolve its bootstrap method and arguments,
|
||||
* or for {@code invokedynamic} instruction the bootstrap method has failed to
|
||||
* provide a
|
||||
* {@linkplain java.lang.invoke.CallSite call site} with a
|
||||
* {@linkplain java.lang.invoke.CallSite#getTarget target}
|
||||
* of the correct {@linkplain java.lang.invoke.MethodHandle#type() method type},
|
||||
* or for a dynamic constant the bootstrap method has failed to provide a
|
||||
* constant value of the required type.
|
||||
*
|
||||
* @author John Rose, JSR 292 EG
|
||||
* @since 1.7
|
||||
|
@ -0,0 +1,341 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package java.lang.invoke;
|
||||
|
||||
import java.util.*;
|
||||
import jdk.internal.vm.annotation.Stable;
|
||||
|
||||
import static java.lang.invoke.MethodHandleStatics.rangeCheck1;
|
||||
import static java.lang.invoke.MethodHandleStatics.rangeCheck2;
|
||||
|
||||
/** Utility class for implementing ConstantGroup. */
|
||||
/*non-public*/
|
||||
abstract class AbstractConstantGroup implements ConstantGroup {
|
||||
/** The size of this constant group, set permanently by the constructor. */
|
||||
protected final int size;
|
||||
|
||||
/** The constructor requires the size of the constant group being represented.
|
||||
* @param size the size of this constant group, set permanently by the constructor
|
||||
*/
|
||||
AbstractConstantGroup(int size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
@Override public final int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public abstract Object get(int index) throws LinkageError;
|
||||
|
||||
public abstract Object get(int index, Object ifNotPresent);
|
||||
|
||||
public abstract boolean isPresent(int index);
|
||||
|
||||
// Do not override equals or hashCode, since this type is stateful.
|
||||
|
||||
/**
|
||||
* Produce a string using the non-resolving list view,
|
||||
* where unresolved elements are presented as asterisks.
|
||||
* @return {@code this.asList("*").toString()}
|
||||
*/
|
||||
@Override public String toString() {
|
||||
return asList("*").toString();
|
||||
}
|
||||
|
||||
static class AsIterator implements Iterator<Object> {
|
||||
private final ConstantGroup self;
|
||||
private final int end;
|
||||
private final boolean resolving;
|
||||
private final Object ifNotPresent;
|
||||
|
||||
// Mutable state:
|
||||
private int index;
|
||||
|
||||
private AsIterator(ConstantGroup self, int start, int end,
|
||||
boolean resolving, Object ifNotPresent) {
|
||||
this.self = self;
|
||||
this.end = end;
|
||||
this.index = start;
|
||||
this.resolving = resolving;
|
||||
this.ifNotPresent = ifNotPresent;
|
||||
}
|
||||
AsIterator(ConstantGroup self, int start, int end) {
|
||||
this(self, start, end, true, null);
|
||||
}
|
||||
AsIterator(ConstantGroup self, int start, int end,
|
||||
Object ifNotPresent) {
|
||||
this(self, start, end, false, ifNotPresent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return index < end;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object next() {
|
||||
int i = bumpIndex();
|
||||
if (resolving)
|
||||
return self.get(i);
|
||||
else
|
||||
return self.get(i, ifNotPresent);
|
||||
}
|
||||
|
||||
private int bumpIndex() {
|
||||
int i = index;
|
||||
if (i >= end) throw new NoSuchElementException();
|
||||
index = i+1;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
static class SubGroup extends AbstractConstantGroup {
|
||||
private final ConstantGroup self; // the real CG
|
||||
private final int offset; // offset within myself
|
||||
SubGroup(ConstantGroup self, int start, int end) {
|
||||
super(end - start);
|
||||
this.self = self;
|
||||
this.offset = start;
|
||||
rangeCheck2(start, end, size);
|
||||
}
|
||||
|
||||
private int mapIndex(int index) {
|
||||
return rangeCheck1(index, size) + offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(int index) {
|
||||
return self.get(mapIndex(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(int index, Object ifNotPresent) {
|
||||
return self.get(mapIndex(index), ifNotPresent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPresent(int index) {
|
||||
return self.isPresent(mapIndex(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConstantGroup subGroup(int start, int end) {
|
||||
rangeCheck2(start, end, size);
|
||||
return new SubGroup(self, offset + start, offset + end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> asList() {
|
||||
return new AsList(self, offset, offset + size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> asList(Object ifNotPresent) {
|
||||
return new AsList(self, offset, offset + size, ifNotPresent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int copyConstants(int start, int end,
|
||||
Object[] buf, int pos) throws LinkageError {
|
||||
rangeCheck2(start, end, size);
|
||||
return self.copyConstants(offset + start, offset + end,
|
||||
buf, pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int copyConstants(int start, int end,
|
||||
Object[] buf, int pos,
|
||||
Object ifNotPresent) {
|
||||
rangeCheck2(start, end, size);
|
||||
return self.copyConstants(offset + start, offset + end,
|
||||
buf, pos, ifNotPresent);
|
||||
}
|
||||
}
|
||||
|
||||
static class AsList extends AbstractList<Object> {
|
||||
private final ConstantGroup self;
|
||||
private final int size;
|
||||
private final int offset;
|
||||
private final boolean resolving;
|
||||
private final Object ifNotPresent;
|
||||
|
||||
private AsList(ConstantGroup self, int start, int end,
|
||||
boolean resolving, Object ifNotPresent) {
|
||||
this.self = self;
|
||||
this.size = end - start;
|
||||
this.offset = start;
|
||||
this.resolving = resolving;
|
||||
this.ifNotPresent = ifNotPresent;
|
||||
rangeCheck2(start, end, self.size());
|
||||
}
|
||||
AsList(ConstantGroup self, int start, int end) {
|
||||
this(self, start, end, true, null);
|
||||
}
|
||||
AsList(ConstantGroup self, int start, int end,
|
||||
Object ifNotPresent) {
|
||||
this(self, start, end, false, ifNotPresent);
|
||||
}
|
||||
|
||||
private int mapIndex(int index) {
|
||||
return rangeCheck1(index, size) + offset;
|
||||
}
|
||||
|
||||
@Override public final int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override public Object get(int index) {
|
||||
if (resolving)
|
||||
return self.get(mapIndex(index));
|
||||
else
|
||||
return self.get(mapIndex(index), ifNotPresent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Object> iterator() {
|
||||
if (resolving)
|
||||
return new AsIterator(self, offset, offset + size);
|
||||
else
|
||||
return new AsIterator(self, offset, offset + size, ifNotPresent);
|
||||
}
|
||||
|
||||
@Override public List<Object> subList(int start, int end) {
|
||||
rangeCheck2(start, end, size);
|
||||
return new AsList(self, offset + start, offset + end,
|
||||
resolving, ifNotPresent);
|
||||
}
|
||||
|
||||
@Override public Object[] toArray() {
|
||||
return toArray(new Object[size]);
|
||||
}
|
||||
@Override public <T> T[] toArray(T[] a) {
|
||||
int pad = a.length - size;
|
||||
if (pad < 0) {
|
||||
pad = 0;
|
||||
a = Arrays.copyOf(a, size);
|
||||
}
|
||||
if (resolving)
|
||||
self.copyConstants(offset, offset + size, a, 0);
|
||||
else
|
||||
self.copyConstants(offset, offset + size, a, 0,
|
||||
ifNotPresent);
|
||||
if (pad > 0) a[size] = null;
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
static abstract
|
||||
class WithCache extends AbstractConstantGroup {
|
||||
@Stable final Object[] cache;
|
||||
|
||||
WithCache(int size) {
|
||||
super(size);
|
||||
// It is caller's responsibility to initialize the cache.
|
||||
// Initial contents are all-null, which means nothing is present.
|
||||
cache = new Object[size];
|
||||
}
|
||||
|
||||
void initializeCache(List<Object> cacheContents, Object ifNotPresent) {
|
||||
// Replace ifNotPresent with NOT_PRESENT,
|
||||
// and null with RESOLVED_TO_NULL.
|
||||
// Then forget about the user-provided ifNotPresent.
|
||||
for (int i = 0; i < cache.length; i++) {
|
||||
Object x = cacheContents.get(i);
|
||||
if (x == ifNotPresent)
|
||||
continue; // leave the null in place
|
||||
if (x == null)
|
||||
x = RESOLVED_TO_NULL;
|
||||
cache[i] = x;
|
||||
}
|
||||
}
|
||||
|
||||
@Override public Object get(int i) {
|
||||
Object x = cache[i];
|
||||
// @Stable array must use null for sentinel
|
||||
if (x == null) x = fillCache(i);
|
||||
return unwrapNull(x);
|
||||
}
|
||||
|
||||
@Override public Object get(int i, Object ifNotAvailable) {
|
||||
Object x = cache[i];
|
||||
// @Stable array must use null for sentinel
|
||||
if (x == null) return ifNotAvailable;
|
||||
return unwrapNull(x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPresent(int i) {
|
||||
return cache[i] != null;
|
||||
}
|
||||
|
||||
/** hook for local subclasses */
|
||||
Object fillCache(int i) {
|
||||
throw new NoSuchElementException("constant group does not contain element #"+i);
|
||||
}
|
||||
|
||||
/// routines for mapping between null sentinel and true resolved null
|
||||
|
||||
static Object wrapNull(Object x) {
|
||||
return x == null ? RESOLVED_TO_NULL : x;
|
||||
}
|
||||
|
||||
static Object unwrapNull(Object x) {
|
||||
assert(x != null);
|
||||
return x == RESOLVED_TO_NULL ? null : x;
|
||||
}
|
||||
|
||||
// secret sentinel for an actual null resolved value, in the cache
|
||||
static final Object RESOLVED_TO_NULL = new Object();
|
||||
|
||||
// secret sentinel for a "hole" in the cache:
|
||||
static final Object NOT_PRESENT = new Object();
|
||||
|
||||
}
|
||||
|
||||
/** Skeleton implementation of BootstrapCallInfo. */
|
||||
static
|
||||
class BSCIWithCache<T> extends WithCache implements BootstrapCallInfo<T> {
|
||||
private final MethodHandle bsm;
|
||||
private final String name;
|
||||
private final T type;
|
||||
|
||||
@Override public String toString() {
|
||||
return bsm+"/"+name+":"+type+super.toString();
|
||||
}
|
||||
|
||||
BSCIWithCache(MethodHandle bsm, String name, T type, int size) {
|
||||
super(size);
|
||||
this.type = type;
|
||||
this.bsm = bsm;
|
||||
this.name = name;
|
||||
assert(type instanceof Class || type instanceof MethodType);
|
||||
}
|
||||
|
||||
@Override public MethodHandle bootstrapMethod() { return bsm; }
|
||||
@Override public String invocationName() { return name; }
|
||||
@Override public T invocationType() { return type; }
|
||||
}
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package java.lang.invoke;
|
||||
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
|
||||
/**
|
||||
* An interface providing full static information about a particular
|
||||
* call to a
|
||||
* <a href="package-summary.html#bsm">bootstrap method</a> of an
|
||||
* dynamic call site or dynamic constant.
|
||||
* This information includes the method itself, the associated
|
||||
* name and type, and any associated static arguments.
|
||||
* <p>
|
||||
* If a bootstrap method declares exactly two arguments, and is
|
||||
* not of variable arity, then it is fed only two arguments by
|
||||
* the JVM, the {@linkplain Lookup lookup object} and an instance
|
||||
* of {@code BootstrapCallInfo} which supplies the rest of the
|
||||
* information about the call.
|
||||
* <p>
|
||||
* The API for accessing the static arguments allows the bootstrap
|
||||
* method to reorder the resolution (in the constant pool) of the
|
||||
* static arguments, and to catch errors resulting from the resolution.
|
||||
* This mode of evaluation <em>pulls</em> bootstrap parameters from
|
||||
* the JVM under control of the bootstrap method, as opposed to
|
||||
* the JVM <em>pushing</em> parameters to a bootstrap method
|
||||
* by resolving them all before the bootstrap method is called.
|
||||
* @apiNote
|
||||
* <p>
|
||||
* The {@linkplain Lookup lookup object} is <em>not</em> included in this
|
||||
* bundle of information, so as not to obscure the access control
|
||||
* logic of the program.
|
||||
* In cases where there are many thousands of parameters, it may
|
||||
* be preferable to pull their resolved values, either singly or in
|
||||
* batches, rather than wait until all of them have been resolved
|
||||
* before a constant or call site can be used.
|
||||
* <p>
|
||||
* A push mode bootstrap method can be adapted to a pull mode
|
||||
* bootstrap method, and vice versa. For example, this generic
|
||||
* adapter pops a push-mode bootstrap method from the beginning
|
||||
* of the static argument list, eagerly resolves all the remaining
|
||||
* static arguments, and invokes the popped method in push mode.
|
||||
* The callee has no way of telling that it was not called directly
|
||||
* from the JVM.
|
||||
* <blockquote><pre>{@code
|
||||
static Object genericBSM(Lookup lookup, BootstrapCallInfo<Object> bsci)
|
||||
throws Throwable {
|
||||
ArrayList<Object> args = new ArrayList<>();
|
||||
args.add(lookup);
|
||||
args.add(bsci.invocationName());
|
||||
args.add(bsci.invocationType());
|
||||
MethodHandle bsm = (MethodHandle) bsci.get(0);
|
||||
List<Object> restOfArgs = bsci.asList().subList(1, bsci.size();
|
||||
// the next line eagerly resolves all remaining static arguments:
|
||||
args.addAll(restOfArgs);
|
||||
return bsm.invokeWithArguments(args);
|
||||
}
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* <p>
|
||||
* In the other direction, here is a combinator which pops
|
||||
* a pull-mode bootstrap method from the beginning of a list of
|
||||
* static argument values (already resolved), reformats all of
|
||||
* the arguments into a pair of a lookup and a {@code BootstrapCallInfo},
|
||||
* and invokes the popped method. Again the callee has no way of
|
||||
* telling it was not called directly by the JVM, except that
|
||||
* all of the constant values will appear as resolved.
|
||||
* Put another way, if any constant fails to resolve, the
|
||||
* callee will not be able to catch the resulting error,
|
||||
* since the error will be thrown by the JVM before the
|
||||
* bootstrap method is entered.
|
||||
* <blockquote><pre>{@code
|
||||
static Object genericBSM(Lookup lookup, String name, Object type,
|
||||
MethodHandle bsm, Object... args)
|
||||
throws Throwable {
|
||||
ConstantGroup cons = ConstantGroup.makeConstantGroup(Arrays.asList(args));
|
||||
BootstrapCallInfo<Object> bsci = makeBootstrapCallInfo(bsm, name, type, cons);
|
||||
return bsm.invoke(lookup, bsci);
|
||||
}
|
||||
* }</pre></blockquote>
|
||||
*
|
||||
* @since 1.10
|
||||
*/
|
||||
// public
|
||||
interface BootstrapCallInfo<T> extends ConstantGroup {
|
||||
/** Returns the bootstrap method for this call.
|
||||
* @return the bootstrap method
|
||||
*/
|
||||
MethodHandle bootstrapMethod();
|
||||
|
||||
/** Returns the method name or constant name for this call.
|
||||
* @return the method name or constant name
|
||||
*/
|
||||
String invocationName();
|
||||
|
||||
/** Returns the method type or constant type for this call.
|
||||
* @return the method type or constant type
|
||||
*/
|
||||
T invocationType();
|
||||
|
||||
/**
|
||||
* Make a new bootstrap call descriptor with the given components.
|
||||
* @param bsm bootstrap method
|
||||
* @param name invocation name
|
||||
* @param type invocation type
|
||||
* @param constants the additional static arguments for the bootstrap method
|
||||
* @param <T> the type of the invocation type, either {@link MethodHandle} or {@link Class}
|
||||
* @return a new bootstrap call descriptor with the given components
|
||||
*/
|
||||
static <T> BootstrapCallInfo<T> makeBootstrapCallInfo(MethodHandle bsm,
|
||||
String name,
|
||||
T type,
|
||||
ConstantGroup constants) {
|
||||
AbstractConstantGroup.BSCIWithCache<T> bsci = new AbstractConstantGroup.BSCIWithCache<>(bsm, name, type, constants.size());
|
||||
final Object NP = AbstractConstantGroup.BSCIWithCache.NOT_PRESENT;
|
||||
bsci.initializeCache(constants.asList(NP), NP);
|
||||
return bsci;
|
||||
}
|
||||
}
|
@ -0,0 +1,366 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package java.lang.invoke;
|
||||
|
||||
import sun.invoke.util.Wrapper;
|
||||
|
||||
import java.lang.invoke.AbstractConstantGroup.BSCIWithCache;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static java.lang.invoke.BootstrapCallInfo.makeBootstrapCallInfo;
|
||||
import static java.lang.invoke.ConstantGroup.makeConstantGroup;
|
||||
import static java.lang.invoke.MethodHandleNatives.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.TRACE_METHOD_LINKAGE;
|
||||
import static java.lang.invoke.MethodHandles.Lookup;
|
||||
import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
|
||||
|
||||
final class BootstrapMethodInvoker {
|
||||
/**
|
||||
* Factored code for invoking a bootstrap method for invokedynamic
|
||||
* or a dynamic constant.
|
||||
* @param resultType the expected return type (either CallSite or a constant type)
|
||||
* @param bootstrapMethod the BSM to call
|
||||
* @param name the method name or constant name
|
||||
* @param type the method type or constant type
|
||||
* @param info information passed up from the JVM, to derive static arguments
|
||||
* @param callerClass the class containing the resolved method call or constant load
|
||||
* @param <T> the expected return type
|
||||
* @return the expected value, either a CallSite or a constant value
|
||||
*/
|
||||
static <T> T invoke(Class<T> resultType,
|
||||
MethodHandle bootstrapMethod,
|
||||
// Callee information:
|
||||
String name, Object type,
|
||||
// Extra arguments for BSM, if any:
|
||||
Object info,
|
||||
// Caller information:
|
||||
Class<?> callerClass) {
|
||||
MethodHandles.Lookup caller = IMPL_LOOKUP.in(callerClass);
|
||||
Object result;
|
||||
boolean pullMode = isPullModeBSM(bootstrapMethod); // default value is false
|
||||
boolean vmIsPushing = !staticArgumentsPulled(info); // default value is true
|
||||
MethodHandle pullModeBSM;
|
||||
// match the VM with the BSM
|
||||
if (vmIsPushing) {
|
||||
// VM is pushing arguments at us
|
||||
pullModeBSM = null;
|
||||
if (pullMode) {
|
||||
bootstrapMethod = Adapters.pushMePullYou(bootstrapMethod, true);
|
||||
}
|
||||
} else {
|
||||
// VM wants us to pull args from it
|
||||
pullModeBSM = pullMode ? bootstrapMethod :
|
||||
Adapters.pushMePullYou(bootstrapMethod, false);
|
||||
bootstrapMethod = null;
|
||||
}
|
||||
try {
|
||||
info = maybeReBox(info);
|
||||
if (info == null) {
|
||||
// VM is allowed to pass up a null meaning no BSM args
|
||||
result = bootstrapMethod.invoke(caller, name, type);
|
||||
}
|
||||
else if (!info.getClass().isArray()) {
|
||||
// VM is allowed to pass up a single BSM arg directly
|
||||
result = bootstrapMethod.invoke(caller, name, type, info);
|
||||
}
|
||||
else if (info.getClass() == int[].class) {
|
||||
// VM is allowed to pass up a pair {argc, index}
|
||||
// referring to 'argc' BSM args at some place 'index'
|
||||
// in the guts of the VM (associated with callerClass).
|
||||
// The format of this index pair is private to the
|
||||
// handshake between the VM and this class only.
|
||||
// This supports "pulling" of arguments.
|
||||
// The VM is allowed to do this for any reason.
|
||||
// The code in this method makes up for any mismatches.
|
||||
BootstrapCallInfo<Object> bsci
|
||||
= new VM_BSCI<>(bootstrapMethod, name, type, caller, (int[])info);
|
||||
// Pull-mode API is (Lookup, BootstrapCallInfo) -> Object
|
||||
result = pullModeBSM.invoke(caller, bsci);
|
||||
}
|
||||
else {
|
||||
// VM is allowed to pass up a full array of resolved BSM args
|
||||
Object[] argv = (Object[]) info;
|
||||
maybeReBoxElements(argv);
|
||||
switch (argv.length) {
|
||||
case 0:
|
||||
result = bootstrapMethod.invoke(caller, name, type);
|
||||
break;
|
||||
case 1:
|
||||
result = bootstrapMethod.invoke(caller, name, type,
|
||||
argv[0]);
|
||||
break;
|
||||
case 2:
|
||||
result = bootstrapMethod.invoke(caller, name, type,
|
||||
argv[0], argv[1]);
|
||||
break;
|
||||
case 3:
|
||||
result = bootstrapMethod.invoke(caller, name, type,
|
||||
argv[0], argv[1], argv[2]);
|
||||
break;
|
||||
case 4:
|
||||
result = bootstrapMethod.invoke(caller, name, type,
|
||||
argv[0], argv[1], argv[2], argv[3]);
|
||||
break;
|
||||
case 5:
|
||||
result = bootstrapMethod.invoke(caller, name, type,
|
||||
argv[0], argv[1], argv[2], argv[3], argv[4]);
|
||||
break;
|
||||
case 6:
|
||||
result = bootstrapMethod.invoke(caller, name, type,
|
||||
argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
|
||||
break;
|
||||
default:
|
||||
final int NON_SPREAD_ARG_COUNT = 3; // (caller, name, type)
|
||||
final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT;
|
||||
if (argv.length >= MAX_SAFE_SIZE) {
|
||||
// to be on the safe side, use invokeWithArguments which handles jumbo lists
|
||||
Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argv.length];
|
||||
newargv[0] = caller;
|
||||
newargv[1] = name;
|
||||
newargv[2] = type;
|
||||
System.arraycopy(argv, 0, newargv, NON_SPREAD_ARG_COUNT, argv.length);
|
||||
result = bootstrapMethod.invokeWithArguments(newargv);
|
||||
break;
|
||||
}
|
||||
MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length);
|
||||
MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
|
||||
MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
|
||||
result = spreader.invokeExact(typedBSM, (Object) caller, (Object) name, type, argv);
|
||||
}
|
||||
}
|
||||
if (resultType.isPrimitive()) {
|
||||
// Non-reference conversions are more than just plain casts.
|
||||
// By pushing the value through a funnel of the form (T x)->x,
|
||||
// the boxed result can be widened as needed. See MH::asType.
|
||||
MethodHandle funnel = MethodHandles.identity(resultType);
|
||||
result = funnel.invoke(result);
|
||||
// Now it is the wrapper type for resultType.
|
||||
resultType = Wrapper.asWrapperType(resultType);
|
||||
}
|
||||
return resultType.cast(result);
|
||||
}
|
||||
catch (Error e) {
|
||||
// Pass through an Error, including BootstrapMethodError, any other
|
||||
// form of linkage error, such as IllegalAccessError if the bootstrap
|
||||
// method is inaccessible, or say ThreadDeath/OutOfMemoryError
|
||||
// See the "Linking Exceptions" section for the invokedynamic
|
||||
// instruction in JVMS 6.5.
|
||||
throw e;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// Wrap anything else in BootstrapMethodError
|
||||
throw new BootstrapMethodError("bootstrap method initialization exception", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/** The JVM produces java.lang.Integer values to box
|
||||
* CONSTANT_Integer boxes but does not intern them.
|
||||
* Let's intern them. This is slightly wrong for
|
||||
* a {@code CONSTANT_Dynamic} which produces an
|
||||
* un-interned integer (e.g., {@code new Integer(0)}).
|
||||
*/
|
||||
private static Object maybeReBox(Object x) {
|
||||
if (x instanceof Integer) {
|
||||
int xi = (int) x;
|
||||
if (xi == (byte) xi)
|
||||
x = xi; // must rebox; see JLS 5.1.7
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
private static void maybeReBoxElements(Object[] xa) {
|
||||
for (int i = 0; i < xa.length; i++) {
|
||||
xa[i] = maybeReBox(xa[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Canonical VM-aware implementation of BootstrapCallInfo.
|
||||
* Knows how to dig into the JVM for lazily resolved (pull-mode) constants.
|
||||
*/
|
||||
private static final class VM_BSCI<T> extends BSCIWithCache<T> {
|
||||
private final int[] indexInfo;
|
||||
private final Class<?> caller; // for index resolution only
|
||||
|
||||
VM_BSCI(MethodHandle bsm, String name, T type,
|
||||
Lookup lookup, int[] indexInfo) {
|
||||
super(bsm, name, type, indexInfo[0]);
|
||||
if (!lookup.hasPrivateAccess()) //D.I.D.
|
||||
throw new AssertionError("bad Lookup object");
|
||||
this.caller = lookup.lookupClass();
|
||||
this.indexInfo = indexInfo;
|
||||
// scoop up all the easy stuff right away:
|
||||
prefetchIntoCache(0, size());
|
||||
}
|
||||
|
||||
@Override Object fillCache(int i) {
|
||||
Object[] buf = { null };
|
||||
copyConstants(i, i+1, buf, 0);
|
||||
Object res = wrapNull(buf[0]);
|
||||
cache[i] = res;
|
||||
int next = i + 1;
|
||||
if (next < cache.length && cache[next] == null)
|
||||
maybePrefetchIntoCache(next, false); // try to prefetch
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override public int copyConstants(int start, int end,
|
||||
Object[] buf, int pos) {
|
||||
int i = start, bufi = pos;
|
||||
while (i < end) {
|
||||
Object x = cache[i];
|
||||
if (x == null) break;
|
||||
buf[bufi++] = unwrapNull(x);
|
||||
i++;
|
||||
}
|
||||
// give up at first null and grab the rest in one big block
|
||||
if (i >= end) return i;
|
||||
Object[] temp = new Object[end - i];
|
||||
if (TRACE_METHOD_LINKAGE)
|
||||
System.out.println("resolving more BSM arguments: "+
|
||||
Arrays.asList(caller.getSimpleName(), Arrays.toString(indexInfo), i, end));
|
||||
copyOutBootstrapArguments(caller, indexInfo,
|
||||
i, end, temp, 0,
|
||||
true, null);
|
||||
for (Object x : temp) {
|
||||
x = maybeReBox(x);
|
||||
buf[bufi++] = x;
|
||||
cache[i++] = wrapNull(x);
|
||||
}
|
||||
if (end < cache.length && cache[end] == null)
|
||||
maybePrefetchIntoCache(end, true); // try to prefetch
|
||||
return i;
|
||||
}
|
||||
|
||||
private static final int MIN_PF = 4;
|
||||
private void maybePrefetchIntoCache(int i, boolean bulk) {
|
||||
int len = cache.length;
|
||||
assert(0 <= i && i <= len);
|
||||
int pfLimit = i;
|
||||
if (bulk) pfLimit += i; // exponential prefetch expansion
|
||||
// try to prefetch at least MIN_PF elements
|
||||
if (pfLimit < i + MIN_PF) pfLimit = i + MIN_PF;
|
||||
if (pfLimit > len || pfLimit < 0) pfLimit = len;
|
||||
// stop prefetching where cache is more full than empty
|
||||
int empty = 0, nonEmpty = 0, lastEmpty = i;
|
||||
for (int j = i; j < pfLimit; j++) {
|
||||
if (cache[j] == null) {
|
||||
empty++;
|
||||
lastEmpty = j;
|
||||
} else {
|
||||
nonEmpty++;
|
||||
if (nonEmpty > empty) {
|
||||
pfLimit = lastEmpty + 1;
|
||||
break;
|
||||
}
|
||||
if (pfLimit < len) pfLimit++;
|
||||
}
|
||||
}
|
||||
if (bulk && empty < MIN_PF && pfLimit < len)
|
||||
return; // not worth the effort
|
||||
prefetchIntoCache(i, pfLimit);
|
||||
}
|
||||
|
||||
private void prefetchIntoCache(int i, int pfLimit) {
|
||||
if (pfLimit <= i) return; // corner case
|
||||
Object[] temp = new Object[pfLimit - i];
|
||||
if (TRACE_METHOD_LINKAGE)
|
||||
System.out.println("prefetching BSM arguments: "+
|
||||
Arrays.asList(caller.getSimpleName(), Arrays.toString(indexInfo), i, pfLimit));
|
||||
copyOutBootstrapArguments(caller, indexInfo,
|
||||
i, pfLimit, temp, 0,
|
||||
false, NOT_PRESENT);
|
||||
for (Object x : temp) {
|
||||
if (x != NOT_PRESENT && cache[i] == null) {
|
||||
cache[i] = wrapNull(maybeReBox(x));
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*non-public*/ static final
|
||||
class Adapters {
|
||||
// skeleton for push-mode BSM which wraps a pull-mode BSM:
|
||||
static Object pushToBootstrapMethod(MethodHandle pullModeBSM,
|
||||
MethodHandles.Lookup lookup, String name, Object type,
|
||||
Object... arguments) throws Throwable {
|
||||
ConstantGroup cons = makeConstantGroup(Arrays.asList(arguments));
|
||||
BootstrapCallInfo<?> bsci = makeBootstrapCallInfo(pullModeBSM, name, type, cons);
|
||||
if (TRACE_METHOD_LINKAGE)
|
||||
System.out.println("pull-mode BSM gets pushed arguments from fake BSCI");
|
||||
return pullModeBSM.invoke(lookup, bsci);
|
||||
}
|
||||
|
||||
// skeleton for pull-mode BSM which wraps a push-mode BSM:
|
||||
static Object pullFromBootstrapMethod(MethodHandle pushModeBSM,
|
||||
MethodHandles.Lookup lookup, BootstrapCallInfo<?> bsci)
|
||||
throws Throwable {
|
||||
int argc = bsci.size();
|
||||
Object arguments[] = new Object[3 + argc];
|
||||
arguments[0] = lookup;
|
||||
arguments[1] = bsci.invocationName();
|
||||
arguments[2] = bsci.invocationType();
|
||||
bsci.copyConstants(0, argc, arguments, 3);
|
||||
if (TRACE_METHOD_LINKAGE)
|
||||
System.out.println("pulled arguments from VM for push-mode BSM");
|
||||
return pushModeBSM.invokeWithArguments(arguments);
|
||||
}
|
||||
static final MethodHandle MH_pushToBootstrapMethod;
|
||||
static final MethodHandle MH_pullFromBootstrapMethod;
|
||||
static {
|
||||
final Class<?> THIS_CLASS = Adapters.class;
|
||||
try {
|
||||
MH_pushToBootstrapMethod = IMPL_LOOKUP
|
||||
.findStatic(THIS_CLASS, "pushToBootstrapMethod",
|
||||
MethodType.methodType(Object.class, MethodHandle.class,
|
||||
Lookup.class, String.class, Object.class, Object[].class));
|
||||
MH_pullFromBootstrapMethod = IMPL_LOOKUP
|
||||
.findStatic(THIS_CLASS, "pullFromBootstrapMethod",
|
||||
MethodType.methodType(Object.class, MethodHandle.class,
|
||||
Lookup.class, BootstrapCallInfo.class));
|
||||
} catch (Throwable ex) {
|
||||
throw new InternalError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/** Given a push-mode BSM (taking one argument) convert it to a
|
||||
* pull-mode BSM (taking N pre-resolved arguments).
|
||||
* This method is used when, in fact, the JVM is passing up
|
||||
* pre-resolved arguments, but the BSM is expecting lazy stuff.
|
||||
* Or, when goToPushMode is true, do the reverse transform.
|
||||
* (The two transforms are exactly inverse.)
|
||||
*/
|
||||
static MethodHandle pushMePullYou(MethodHandle bsm, boolean goToPushMode) {
|
||||
if (TRACE_METHOD_LINKAGE)
|
||||
System.out.println("converting BSM to "+(goToPushMode ? "push mode" : "pull mode"));
|
||||
assert(isPullModeBSM(bsm) == goToPushMode); //there must be a change
|
||||
if (goToPushMode) {
|
||||
return Adapters.MH_pushToBootstrapMethod.bindTo(bsm).withVarargs(true);
|
||||
} else {
|
||||
return Adapters.MH_pullFromBootstrapMethod.bindTo(bsm).withVarargs(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -302,65 +302,10 @@ public class CallSite {
|
||||
Object info,
|
||||
// Caller information:
|
||||
Class<?> callerClass) {
|
||||
MethodHandles.Lookup caller = IMPL_LOOKUP.in(callerClass);
|
||||
CallSite site;
|
||||
try {
|
||||
Object binding;
|
||||
info = maybeReBox(info);
|
||||
if (info == null) {
|
||||
binding = bootstrapMethod.invoke(caller, name, type);
|
||||
} else if (!info.getClass().isArray()) {
|
||||
binding = bootstrapMethod.invoke(caller, name, type, info);
|
||||
} else {
|
||||
Object[] argv = (Object[]) info;
|
||||
maybeReBoxElements(argv);
|
||||
switch (argv.length) {
|
||||
case 0:
|
||||
binding = bootstrapMethod.invoke(caller, name, type);
|
||||
break;
|
||||
case 1:
|
||||
binding = bootstrapMethod.invoke(caller, name, type,
|
||||
argv[0]);
|
||||
break;
|
||||
case 2:
|
||||
binding = bootstrapMethod.invoke(caller, name, type,
|
||||
argv[0], argv[1]);
|
||||
break;
|
||||
case 3:
|
||||
binding = bootstrapMethod.invoke(caller, name, type,
|
||||
argv[0], argv[1], argv[2]);
|
||||
break;
|
||||
case 4:
|
||||
binding = bootstrapMethod.invoke(caller, name, type,
|
||||
argv[0], argv[1], argv[2], argv[3]);
|
||||
break;
|
||||
case 5:
|
||||
binding = bootstrapMethod.invoke(caller, name, type,
|
||||
argv[0], argv[1], argv[2], argv[3], argv[4]);
|
||||
break;
|
||||
case 6:
|
||||
binding = bootstrapMethod.invoke(caller, name, type,
|
||||
argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
|
||||
break;
|
||||
default:
|
||||
final int NON_SPREAD_ARG_COUNT = 3; // (caller, name, type)
|
||||
final int MAX_SAFE_SIZE = MethodType.MAX_MH_ARITY / 2 - NON_SPREAD_ARG_COUNT;
|
||||
if (argv.length >= MAX_SAFE_SIZE) {
|
||||
// to be on the safe side, use invokeWithArguments which handles jumbo lists
|
||||
Object[] newargv = new Object[NON_SPREAD_ARG_COUNT + argv.length];
|
||||
newargv[0] = caller;
|
||||
newargv[1] = name;
|
||||
newargv[2] = type;
|
||||
System.arraycopy(argv, 0, newargv, NON_SPREAD_ARG_COUNT, argv.length);
|
||||
binding = bootstrapMethod.invokeWithArguments(newargv);
|
||||
} else {
|
||||
MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length);
|
||||
MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
|
||||
MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
|
||||
binding = spreader.invokeExact(typedBSM, (Object) caller, (Object) name, (Object) type, argv);
|
||||
}
|
||||
}
|
||||
}
|
||||
Object binding = BootstrapMethodInvoker.invoke(
|
||||
CallSite.class, bootstrapMethod, name, type, info, callerClass);
|
||||
if (binding instanceof CallSite) {
|
||||
site = (CallSite) binding;
|
||||
} else {
|
||||
@ -369,7 +314,7 @@ public class CallSite {
|
||||
// Throws a runtime exception defining the cause that is then
|
||||
// in the "catch (Throwable ex)" a few lines below wrapped in
|
||||
// BootstrapMethodError
|
||||
throw new ClassCastException("bootstrap method failed to produce a CallSite");
|
||||
throw new ClassCastException("CallSite bootstrap method failed to produce an instance of CallSite");
|
||||
}
|
||||
if (!site.getTarget().type().equals(type)) {
|
||||
// See the "Linking Exceptions" section for the invokedynamic
|
||||
@ -388,22 +333,8 @@ public class CallSite {
|
||||
throw e;
|
||||
} catch (Throwable ex) {
|
||||
// Wrap anything else in BootstrapMethodError
|
||||
throw new BootstrapMethodError("call site initialization exception", ex);
|
||||
throw new BootstrapMethodError("CallSite bootstrap method initialization exception", ex);
|
||||
}
|
||||
return site;
|
||||
}
|
||||
|
||||
private static Object maybeReBox(Object x) {
|
||||
if (x instanceof Integer) {
|
||||
int xi = (int) x;
|
||||
if (xi == (byte) xi)
|
||||
x = xi; // must rebox; see JLS 5.1.7
|
||||
}
|
||||
return x;
|
||||
}
|
||||
private static void maybeReBoxElements(Object[] xa) {
|
||||
for (int i = 0; i < xa.length; i++) {
|
||||
xa[i] = maybeReBox(xa[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
287
src/java.base/share/classes/java/lang/invoke/ConstantGroup.java
Normal file
287
src/java.base/share/classes/java/lang/invoke/ConstantGroup.java
Normal file
@ -0,0 +1,287 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package java.lang.invoke;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
/**
|
||||
* An ordered sequence of constants, some of which may not yet
|
||||
* be present. This type is used by {@link BootstrapCallInfo}
|
||||
* to represent the sequence of bootstrap arguments associated
|
||||
* with a bootstrap method, without forcing their immediate
|
||||
* resolution.
|
||||
* <p>
|
||||
* If you use the
|
||||
* {@linkplain ConstantGroup#get(int) simple get method},
|
||||
* the constant will be resolved, if this has not already
|
||||
* happened. An occasional side effect of resolution is a
|
||||
* {@code LinkageError}, which happens if the system
|
||||
* could not resolve the constant in question.
|
||||
* <p>
|
||||
* In order to peek at a constant without necessarily
|
||||
* resolving it, use the
|
||||
* {@linkplain ConstantGroup#get(int,Object)
|
||||
* non-throwing get method}.
|
||||
* This method will never throw a resolution error.
|
||||
* Instead, if the resolution would result in an error,
|
||||
* or if the implementation elects not to attempt
|
||||
* resolution at this point, then the method will
|
||||
* return the user-supplied sentinel value.
|
||||
* <p>
|
||||
* To iterate through the constants, resolving as you go,
|
||||
* use the iterator provided on the {@link List}-typed view.
|
||||
* If you supply a sentinel, resolution will be suppressed.
|
||||
* <p>
|
||||
* Typically the constant is drawn from a constant pool entry
|
||||
* in the virtual machine. Constant pool entries undergo a
|
||||
* one-time state transition from unresolved to resolved,
|
||||
* with a permanently recorded result. Usually that result
|
||||
* is the desired constant value, but it may also be an error.
|
||||
* In any case, the results displayed by a {@code ConstantGroup}
|
||||
* are stable in the same way. If a query to a particular
|
||||
* constant in a {@code ConstantGroup} throws an exception once,
|
||||
* it will throw the same kind of exception forever after.
|
||||
* If the query returns a constant value once, it will return
|
||||
* the same value forever after.
|
||||
* <p>
|
||||
* The only possible change in the status of a constant is
|
||||
* from the unresolved to the resolved state, and that
|
||||
* happens exactly once. A constant will never revert to
|
||||
* an unlinked state. However, from the point of view of
|
||||
* this interface, constants may appear to spontaneously
|
||||
* resolve. This is so because constant pools are global
|
||||
* structures shared across threads, and because
|
||||
* prefetching of some constants may occur, there are no
|
||||
* strong guarantees when the virtual machine may resolve
|
||||
* constants.
|
||||
* <p>
|
||||
* When choosing sentinel values, be aware that a constant
|
||||
* pool which has {@code CONSTANT_Dynamic} entries
|
||||
* can contain potentially any representable value,
|
||||
* and arbitrary implementations of {@code ConstantGroup}
|
||||
* are also free to produce arbitrary values.
|
||||
* This means some obvious choices for sentinel values,
|
||||
* such as {@code null}, may sometimes fail to distinguish
|
||||
* a resolved from an unresolved constant in the group.
|
||||
* The most reliable sentinel is a privately created object,
|
||||
* or perhaps the {@code ConstantGroup} itself.
|
||||
* @since 1.10
|
||||
*/
|
||||
// public
|
||||
interface ConstantGroup {
|
||||
/// Access
|
||||
|
||||
/**
|
||||
* Returns the number of constants in this group.
|
||||
* This value never changes, for any particular group.
|
||||
* @return the number of constants in this group
|
||||
*/
|
||||
int size();
|
||||
|
||||
/**
|
||||
* Returns the selected constant, resolving it if necessary.
|
||||
* Throws a linkage error if resolution proves impossible.
|
||||
* @param index which constant to select
|
||||
* @return the selected constant
|
||||
* @throws LinkageError if the selected constant needs resolution and cannot be resolved
|
||||
*/
|
||||
Object get(int index) throws LinkageError;
|
||||
|
||||
/**
|
||||
* Returns the selected constant,
|
||||
* or the given sentinel value if there is none available.
|
||||
* If the constant cannot be resolved, the sentinel will be returned.
|
||||
* If the constant can (perhaps) be resolved, but has not yet been resolved,
|
||||
* then the sentinel <em>may</em> be returned, at the implementation's discretion.
|
||||
* To force resolution (and a possible exception), call {@link #get(int)}.
|
||||
* @param index the selected constant
|
||||
* @param ifNotPresent the sentinel value to return if the constant is not present
|
||||
* @return the selected constant, if available, else the sentinel value
|
||||
*/
|
||||
Object get(int index, Object ifNotPresent);
|
||||
|
||||
/**
|
||||
* Returns an indication of whether a constant may be available.
|
||||
* If it returns {@code true}, it will always return true in the future,
|
||||
* and a call to {@link #get(int)} will never throw an exception.
|
||||
* <p>
|
||||
* After a normal return from {@link #get(int)} or a present
|
||||
* value is reported from {@link #get(int,Object)}, this method
|
||||
* must always return true.
|
||||
* <p>
|
||||
* If this method returns {@code false}, nothing in particular
|
||||
* can be inferred, since the query only concerns the internal
|
||||
* logic of the {@code ConstantGroup} object which ensures that
|
||||
a successful * query to a constant will always remain successful.
|
||||
* The only way to force a permanent decision about whether
|
||||
* a constant is available is to call {@link #get(int)} and
|
||||
* be ready for an exception if the constant is unavailable.
|
||||
* @param index the selected constant
|
||||
* @return {@code true} if the selected constant is known by
|
||||
* this object to be present, {@code false} if it is known
|
||||
* not to be present or
|
||||
*/
|
||||
boolean isPresent(int index);
|
||||
|
||||
/// Views
|
||||
|
||||
/**
|
||||
* Create a view on this group as a {@link List} view.
|
||||
* Any request for a constant through this view will
|
||||
* force resolution.
|
||||
* @return a {@code List} view on this group which will force resolution
|
||||
*/
|
||||
default List<Object> asList() {
|
||||
return new AbstractConstantGroup.AsList(this, 0, size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a view on this group as a {@link List} view.
|
||||
* Any request for a constant through this view will
|
||||
* return the given sentinel value, if the corresponding
|
||||
* call to {@link #get(int,Object)} would do so.
|
||||
* @param ifNotPresent the sentinel value to return if a constant is not present
|
||||
* @return a {@code List} view on this group which will not force resolution
|
||||
*/
|
||||
default List<Object> asList(Object ifNotPresent) {
|
||||
return new AbstractConstantGroup.AsList(this, 0, size(), ifNotPresent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a view on a sub-sequence of this group.
|
||||
* @param start the index to begin the view
|
||||
* @param end the index to end the view
|
||||
* @return a view on the selected sub-group
|
||||
*/
|
||||
default ConstantGroup subGroup(int start, int end) {
|
||||
return new AbstractConstantGroup.SubGroup(this, start, end);
|
||||
}
|
||||
|
||||
/// Bulk operations
|
||||
|
||||
/**
|
||||
* Copy a sequence of constant values into a given buffer.
|
||||
* This is equivalent to {@code end-offset} separate calls to {@code get},
|
||||
* for each index in the range from {@code offset} up to but not including {@code end}.
|
||||
* For the first constant that cannot be resolved,
|
||||
* a {@code LinkageError} is thrown, but only after
|
||||
* preceding constant value have been stored.
|
||||
* @param start index of first constant to retrieve
|
||||
* @param end limiting index of constants to retrieve
|
||||
* @param buf array to receive the requested values
|
||||
* @param pos position in the array to offset storing the values
|
||||
* @return the limiting index, {@code end}
|
||||
* @throws LinkageError if a constant cannot be resolved
|
||||
*/
|
||||
default int copyConstants(int start, int end,
|
||||
Object[] buf, int pos)
|
||||
throws LinkageError
|
||||
{
|
||||
int bufBase = pos - start; // buf[bufBase + i] = get(i)
|
||||
for (int i = start; i < end; i++) {
|
||||
buf[bufBase + i] = get(i);
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a sequence of constant values into a given buffer.
|
||||
* This is equivalent to {@code end-offset} separate calls to {@code get},
|
||||
* for each index in the range from {@code offset} up to but not including {@code end}.
|
||||
* Any constants that cannot be resolved are replaced by the
|
||||
* given sentinel value.
|
||||
* @param start index of first constant to retrieve
|
||||
* @param end limiting index of constants to retrieve
|
||||
* @param buf array to receive the requested values
|
||||
* @param pos position in the array to offset storing the values
|
||||
* @param ifNotPresent sentinel value to store if a value is not available
|
||||
* @return the limiting index, {@code end}
|
||||
* @throws LinkageError if {@code resolve} is true and a constant cannot be resolved
|
||||
*/
|
||||
default int copyConstants(int start, int end,
|
||||
Object[] buf, int pos,
|
||||
Object ifNotPresent) {
|
||||
int bufBase = pos - start; // buf[bufBase + i] = get(i)
|
||||
for (int i = start; i < end; i++) {
|
||||
buf[bufBase + i] = get(i, ifNotPresent);
|
||||
}
|
||||
return end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a new constant group with the given constants.
|
||||
* The value of {@code ifNotPresent} may be any reference.
|
||||
* If this value is encountered as an element of the
|
||||
* {@code constants} list, the new constant group will
|
||||
* regard that element of the list as logically missing.
|
||||
* If the new constant group is called upon to resolve
|
||||
* a missing element of the group, it will refer to the
|
||||
* given {@code constantProvider}, by calling it on the
|
||||
* index of the missing element.
|
||||
* The {@code constantProvider} must be stable, in the sense
|
||||
* that the outcome of calling it on the same index twice
|
||||
* will produce equivalent results.
|
||||
* If {@code constantProvider} is the null reference, then
|
||||
* it will be treated as if it were a function which raises
|
||||
* {@link NoSuchElementException}.
|
||||
* @param constants the elements of this constant group
|
||||
* @param ifNotPresent sentinel value provided instead of a missing constant
|
||||
* @param constantProvider function to call when a missing constant is resolved
|
||||
* @return a new constant group with the given constants and resolution behavior
|
||||
*/
|
||||
static ConstantGroup makeConstantGroup(List<Object> constants,
|
||||
Object ifNotPresent,
|
||||
IntFunction<Object> constantProvider) {
|
||||
class Impl extends AbstractConstantGroup.WithCache {
|
||||
Impl() {
|
||||
super(constants.size());
|
||||
initializeCache(constants, ifNotPresent);
|
||||
}
|
||||
@Override
|
||||
Object fillCache(int index) {
|
||||
if (constantProvider == null) super.fillCache(index);
|
||||
return constantProvider.apply(index);
|
||||
}
|
||||
}
|
||||
return new Impl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a new constant group with the given constant values.
|
||||
* The constants will be copied from the given list into the
|
||||
* new constant group, forcing resolution if any are missing.
|
||||
* @param constants the constants of this constant group
|
||||
* @return a new constant group with the given constants
|
||||
*/
|
||||
static ConstantGroup makeConstantGroup(List<Object> constants) {
|
||||
final Object NP = AbstractConstantGroup.WithCache.NOT_PRESENT;
|
||||
assert(!constants.contains(NP)); // secret value
|
||||
return makeConstantGroup(constants, NP, null);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package java.lang.invoke;
|
||||
|
||||
/**
|
||||
* Bootstrap methods for dynamically-computed constant.
|
||||
*/
|
||||
final class DynamicConstant {
|
||||
// implements the upcall from the JVM, MethodHandleNatives.linkDynamicConstant:
|
||||
/*non-public*/
|
||||
static Object makeConstant(MethodHandle bootstrapMethod,
|
||||
// Callee information:
|
||||
String name, Class<?> type,
|
||||
// Extra arguments for BSM, if any:
|
||||
Object info,
|
||||
// Caller information:
|
||||
Class<?> callerClass) {
|
||||
// BSMI.invoke handles all type checking and exception translation.
|
||||
// If type is not a reference type, the JVM is expecting a boxed
|
||||
// version, and will manage unboxing on the other side.
|
||||
return BootstrapMethodInvoker.invoke(
|
||||
type, bootstrapMethod, name, type, info, callerClass);
|
||||
}
|
||||
}
|
@ -65,6 +65,12 @@ class MethodHandleNatives {
|
||||
static native void setCallSiteTargetNormal(CallSite site, MethodHandle target);
|
||||
static native void setCallSiteTargetVolatile(CallSite site, MethodHandle target);
|
||||
|
||||
static native void copyOutBootstrapArguments(Class<?> caller, int[] indexInfo,
|
||||
int start, int end,
|
||||
Object[] buf, int pos,
|
||||
boolean resolve,
|
||||
Object ifNotAvailable);
|
||||
|
||||
/** Represents a context to track nmethod dependencies on CallSite instance target. */
|
||||
static class CallSiteContext implements Runnable {
|
||||
//@Injected JVM_nmethodBucket* vmdependencies;
|
||||
@ -228,6 +234,7 @@ class MethodHandleNatives {
|
||||
* The JVM is linking an invokedynamic instruction. Create a reified call site for it.
|
||||
*/
|
||||
static MemberName linkCallSite(Object callerObj,
|
||||
int indexInCP,
|
||||
Object bootstrapMethodObj,
|
||||
Object nameObj, Object typeObj,
|
||||
Object staticArguments,
|
||||
@ -268,9 +275,7 @@ class MethodHandleNatives {
|
||||
Object[] appendixResult) {
|
||||
Object bsmReference = bootstrapMethod.internalMemberName();
|
||||
if (bsmReference == null) bsmReference = bootstrapMethod;
|
||||
Object staticArglist = (staticArguments instanceof Object[] ?
|
||||
java.util.Arrays.asList((Object[]) staticArguments) :
|
||||
staticArguments);
|
||||
String staticArglist = staticArglistForTrace(staticArguments);
|
||||
System.out.println("linkCallSite "+caller.getName()+" "+
|
||||
bsmReference+" "+
|
||||
name+type+"/"+staticArglist);
|
||||
@ -280,11 +285,89 @@ class MethodHandleNatives {
|
||||
System.out.println("linkCallSite => "+res+" + "+appendixResult[0]);
|
||||
return res;
|
||||
} catch (Throwable ex) {
|
||||
ex.printStackTrace(); // print now in case exception is swallowed
|
||||
System.out.println("linkCallSite => throw "+ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
// this implements the upcall from the JVM, MethodHandleNatives.linkDynamicConstant:
|
||||
static Object linkDynamicConstant(Object callerObj,
|
||||
int indexInCP,
|
||||
Object bootstrapMethodObj,
|
||||
Object nameObj, Object typeObj,
|
||||
Object staticArguments) {
|
||||
MethodHandle bootstrapMethod = (MethodHandle)bootstrapMethodObj;
|
||||
Class<?> caller = (Class<?>)callerObj;
|
||||
String name = nameObj.toString().intern();
|
||||
Class<?> type = (Class<?>)typeObj;
|
||||
if (!TRACE_METHOD_LINKAGE)
|
||||
return linkDynamicConstantImpl(caller, bootstrapMethod, name, type, staticArguments);
|
||||
return linkDynamicConstantTracing(caller, bootstrapMethod, name, type, staticArguments);
|
||||
}
|
||||
|
||||
static Object linkDynamicConstantImpl(Class<?> caller,
|
||||
MethodHandle bootstrapMethod,
|
||||
String name, Class<?> type,
|
||||
Object staticArguments) {
|
||||
return DynamicConstant.makeConstant(bootstrapMethod, name, type, staticArguments, caller);
|
||||
}
|
||||
|
||||
private static String staticArglistForTrace(Object staticArguments) {
|
||||
if (staticArguments instanceof Object[])
|
||||
return "BSA="+java.util.Arrays.asList((Object[]) staticArguments);
|
||||
if (staticArguments instanceof int[])
|
||||
return "BSA@"+java.util.Arrays.toString((int[]) staticArguments);
|
||||
if (staticArguments == null)
|
||||
return "BSA0=null";
|
||||
return "BSA1="+staticArguments;
|
||||
}
|
||||
|
||||
// Tracing logic:
|
||||
static Object linkDynamicConstantTracing(Class<?> caller,
|
||||
MethodHandle bootstrapMethod,
|
||||
String name, Class<?> type,
|
||||
Object staticArguments) {
|
||||
Object bsmReference = bootstrapMethod.internalMemberName();
|
||||
if (bsmReference == null) bsmReference = bootstrapMethod;
|
||||
String staticArglist = staticArglistForTrace(staticArguments);
|
||||
System.out.println("linkDynamicConstant "+caller.getName()+" "+
|
||||
bsmReference+" "+
|
||||
name+type+"/"+staticArglist);
|
||||
try {
|
||||
Object res = linkDynamicConstantImpl(caller, bootstrapMethod, name, type, staticArguments);
|
||||
System.out.println("linkDynamicConstantImpl => "+res);
|
||||
return res;
|
||||
} catch (Throwable ex) {
|
||||
ex.printStackTrace(); // print now in case exception is swallowed
|
||||
System.out.println("linkDynamicConstant => throw "+ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/** The JVM is requesting pull-mode bootstrap when it provides
|
||||
* a tuple of the form int[]{ argc, vmindex }.
|
||||
* The BSM is expected to call back to the JVM using the caller
|
||||
* class and vmindex to resolve the static arguments.
|
||||
*/
|
||||
static boolean staticArgumentsPulled(Object staticArguments) {
|
||||
return staticArguments instanceof int[];
|
||||
}
|
||||
|
||||
/** A BSM runs in pull-mode if and only if its sole arguments
|
||||
* are (Lookup, BootstrapCallInfo), or can be converted pairwise
|
||||
* to those types, and it is not of variable arity.
|
||||
* Excluding error cases, we can just test that the arity is a constant 2.
|
||||
*
|
||||
* NOTE: This method currently returns false, since pulling is not currently
|
||||
* exposed to a BSM. When pull mode is supported the method block will be
|
||||
* replaced with currently commented out code.
|
||||
*/
|
||||
static boolean isPullModeBSM(MethodHandle bsm) {
|
||||
return false;
|
||||
// return bsm.type().parameterCount() == 2 && !bsm.isVarargsCollector();
|
||||
}
|
||||
|
||||
/**
|
||||
* The JVM wants a pointer to a MethodType. Oblige it by finding or creating one.
|
||||
*/
|
||||
@ -506,34 +589,43 @@ class MethodHandleNatives {
|
||||
Lookup lookup = IMPL_LOOKUP.in(callerClass);
|
||||
assert(refKindIsValid(refKind));
|
||||
return lookup.linkMethodHandleConstant((byte) refKind, defc, name, type);
|
||||
} catch (IllegalAccessException ex) {
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw mapLookupExceptionToError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a reflective exception to a linkage error.
|
||||
*/
|
||||
static LinkageError mapLookupExceptionToError(ReflectiveOperationException ex) {
|
||||
LinkageError err;
|
||||
if (ex instanceof IllegalAccessException) {
|
||||
Throwable cause = ex.getCause();
|
||||
if (cause instanceof AbstractMethodError) {
|
||||
throw (AbstractMethodError) cause;
|
||||
return (AbstractMethodError) cause;
|
||||
} else {
|
||||
Error err = new IllegalAccessError(ex.getMessage());
|
||||
throw initCauseFrom(err, ex);
|
||||
err = new IllegalAccessError(ex.getMessage());
|
||||
}
|
||||
} catch (NoSuchMethodException ex) {
|
||||
Error err = new NoSuchMethodError(ex.getMessage());
|
||||
throw initCauseFrom(err, ex);
|
||||
} catch (NoSuchFieldException ex) {
|
||||
Error err = new NoSuchFieldError(ex.getMessage());
|
||||
throw initCauseFrom(err, ex);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
Error err = new IncompatibleClassChangeError();
|
||||
throw initCauseFrom(err, ex);
|
||||
} else if (ex instanceof NoSuchMethodException) {
|
||||
err = new NoSuchMethodError(ex.getMessage());
|
||||
} else if (ex instanceof NoSuchFieldException) {
|
||||
err = new NoSuchFieldError(ex.getMessage());
|
||||
} else {
|
||||
err = new IncompatibleClassChangeError();
|
||||
}
|
||||
return initCauseFrom(err, ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use best possible cause for err.initCause(), substituting the
|
||||
* cause for err itself if the cause has the same (or better) type.
|
||||
*/
|
||||
private static Error initCauseFrom(Error err, Exception ex) {
|
||||
static <E extends Error> E initCauseFrom(E err, Exception ex) {
|
||||
Throwable th = ex.getCause();
|
||||
if (err.getClass().isInstance(th))
|
||||
return (Error) th;
|
||||
@SuppressWarnings("unchecked")
|
||||
final Class<E> Eclass = (Class<E>) err.getClass();
|
||||
if (Eclass.isInstance(th))
|
||||
return Eclass.cast(th);
|
||||
err.initCause(th == null ? ex : th);
|
||||
return err;
|
||||
}
|
||||
|
@ -142,4 +142,13 @@ import java.util.Properties;
|
||||
if (obj != null || obj2 != null) message = message + ": " + obj + ", " + obj2;
|
||||
return message;
|
||||
}
|
||||
/*non-public*/ static void rangeCheck2(int start, int end, int size) {
|
||||
if (0 > start || start > end || end > size)
|
||||
throw new IndexOutOfBoundsException(start+".."+end);
|
||||
}
|
||||
/*non-public*/ static int rangeCheck1(int index, int size) {
|
||||
if (0 > index || index >= size)
|
||||
throw new IndexOutOfBoundsException(index);
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
@ -98,12 +98,6 @@ public class MethodHandles {
|
||||
* <p>
|
||||
* This method is caller sensitive, which means that it may return different
|
||||
* values to different callers.
|
||||
* <p>
|
||||
* For any given caller class {@code C}, the lookup object returned by this call
|
||||
* has equivalent capabilities to any lookup object
|
||||
* supplied by the JVM to the bootstrap method of an
|
||||
* <a href="package-summary.html#indyinsn">invokedynamic instruction</a>
|
||||
* executing in the same caller class {@code C}.
|
||||
* @return a lookup object for the caller of this method, with private access
|
||||
*/
|
||||
@CallerSensitive
|
||||
|
@ -24,13 +24,12 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* The {@code java.lang.invoke} package contains dynamic language support provided directly by
|
||||
* the Java core class libraries and virtual machine.
|
||||
* The {@code java.lang.invoke} package provides low-level primitives for interacting
|
||||
* with the Java Virtual Machine.
|
||||
*
|
||||
* <p>
|
||||
* As described in the Java Virtual Machine Specification,
|
||||
* certain types in this package have special relations to dynamic
|
||||
* language support in the virtual machine:
|
||||
* As described in the Java Virtual Machine Specification, certain types in this package
|
||||
* are given special treatment by the virtual machine:
|
||||
* <ul>
|
||||
* <li>The classes {@link java.lang.invoke.MethodHandle MethodHandle}
|
||||
* {@link java.lang.invoke.VarHandle VarHandle} contain
|
||||
@ -40,77 +39,106 @@
|
||||
* </li>
|
||||
*
|
||||
* <li>The JVM bytecode format supports immediate constants of
|
||||
* the classes {@link java.lang.invoke.MethodHandle MethodHandle} and {@link java.lang.invoke.MethodType MethodType}.
|
||||
* the classes {@link java.lang.invoke.MethodHandle MethodHandle} and
|
||||
* {@link java.lang.invoke.MethodType MethodType}.
|
||||
* </li>
|
||||
*
|
||||
* <li>The {@code invokedynamic} instruction makes use of bootstrap {@code MethodHandle}
|
||||
* constants to dynamically resolve {@code CallSite} objects for custom method invocation
|
||||
* behavior.
|
||||
* </li>
|
||||
*
|
||||
* <li>The {@code ldc} instruction makes use of bootstrap {@code MethodHandle} constants
|
||||
* to dynamically resolve custom constant values.
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* <h1><a id="jvm_mods"></a>Summary of relevant Java Virtual Machine changes</h1>
|
||||
* <h1><a id="jvm_mods"></a>Dynamic resolution of call sites and constants</h1>
|
||||
* The following low-level information summarizes relevant parts of the
|
||||
* Java Virtual Machine specification. For full details, please see the
|
||||
* current version of that specification.
|
||||
*
|
||||
* Each occurrence of an {@code invokedynamic} instruction is called a <em>dynamic call site</em>.
|
||||
* <h2><a id="indyinsn"></a>{@code invokedynamic} instructions</h2>
|
||||
* A dynamic call site is originally in an unlinked state. In this state, there is
|
||||
* no target method for the call site to invoke.
|
||||
* <h2><a id="indyinsn"></a>Dynamically-computed call sites</h2>
|
||||
* An {@code invokedynamic} instruction is originally in an unlinked state.
|
||||
* In this state, there is no target method for the instruction to invoke.
|
||||
* <p>
|
||||
* Before the JVM can execute a dynamic call site (an {@code invokedynamic} instruction),
|
||||
* the call site must first be <em>linked</em>.
|
||||
* Before the JVM can execute an {@code invokedynamic} instruction,
|
||||
* the instruction must first be <em>linked</em>.
|
||||
* Linking is accomplished by calling a <em>bootstrap method</em>
|
||||
* which is given the static information content of the call site,
|
||||
* and which must produce a {@link java.lang.invoke.MethodHandle method handle}
|
||||
* that gives the behavior of the call site.
|
||||
* which is given the static information content of the call,
|
||||
* and which must produce a {@link java.lang.invoke.CallSite}
|
||||
* that gives the behavior of the invocation.
|
||||
* <p>
|
||||
* Each {@code invokedynamic} instruction statically specifies its own
|
||||
* bootstrap method as a constant pool reference.
|
||||
* The constant pool reference also specifies the call site's name and type descriptor,
|
||||
* just like {@code invokevirtual} and the other invoke instructions.
|
||||
* The constant pool reference also specifies the invocation's name and method type descriptor,
|
||||
* just like {@code invokestatic} and the other invoke instructions.
|
||||
*
|
||||
* <h2><a id="condycon"></a>Dynamically-computed constants</h2>
|
||||
* The constant pool may contain constants tagged {@code CONSTANT_Dynamic},
|
||||
* equipped with bootstrap methods which perform their resolution.
|
||||
* Such a <em>dynamic constant</em> is originally in an unresolved state.
|
||||
* Before the JVM can use a dynamically-computed constant, it must first be <em>resolved</em>.
|
||||
* Dynamically-computed constant resolution is accomplished by calling a <em>bootstrap method</em>
|
||||
* which is given the static information content of the constant,
|
||||
* and which must produce a value of the constant's statically declared type.
|
||||
* <p>
|
||||
* Linking starts with resolving the constant pool entry for the
|
||||
* bootstrap method, and resolving a {@link java.lang.invoke.MethodType MethodType} object for
|
||||
* the type descriptor of the dynamic call site.
|
||||
* This resolution process may trigger class loading.
|
||||
* It may therefore throw an error if a class fails to load.
|
||||
* This error becomes the abnormal termination of the dynamic
|
||||
* call site execution.
|
||||
* Linkage does not trigger class initialization.
|
||||
* <p>
|
||||
* The bootstrap method is invoked on at least three values:
|
||||
* Each dynamically-computed constant statically specifies its own
|
||||
* bootstrap method as a constant pool reference.
|
||||
* The constant pool reference also specifies the constant's name and field type descriptor,
|
||||
* just like {@code getstatic} and the other field reference instructions.
|
||||
* (Roughly speaking, a dynamically-computed constant is to a dynamically-computed call site
|
||||
* as a {@code CONSTANT_Fieldref} is to a {@code CONSTANT_Methodref}.)
|
||||
*
|
||||
* <h2><a id="bsm"></a>Execution of bootstrap methods</h2>
|
||||
* Resolving a dynamically-computed call site or constant
|
||||
* starts with resolving constants from the constant pool for the
|
||||
* following items:
|
||||
* <ul>
|
||||
* <li>a {@code MethodHandles.Lookup}, a lookup object on the <em>caller class</em>
|
||||
* in which dynamic call site occurs </li>
|
||||
* <li>a {@code String}, the method name mentioned in the call site </li>
|
||||
* <li>a {@code MethodType}, the resolved type descriptor of the call </li>
|
||||
* <li>optionally, any number of additional static arguments taken from the constant pool </li>
|
||||
* <li>the bootstrap method, a {@code CONSTANT_MethodHandle}</li>
|
||||
* <li>the {@code Class} or {@code MethodType} derived from
|
||||
* type component of the {@code CONSTANT_NameAndType} descriptor</li>
|
||||
* <li>static arguments, if any (note that static arguments can themselves be
|
||||
* dynamically-computed constants)</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* In all cases, bootstrap method invocation is as if by
|
||||
* {@link java.lang.invoke.MethodHandle#invokeWithArguments MethodHandle.invokeWithArguments},
|
||||
* (This is also equivalent to
|
||||
* {@linkplain java.lang.invoke.MethodHandle#invoke generic invocation}
|
||||
* if the number of arguments is small enough.)
|
||||
* The bootstrap method is then invoked, as if by
|
||||
* {@link java.lang.invoke.MethodHandle#invoke MethodHandle.invoke},
|
||||
* with the following arguments:
|
||||
* <ul>
|
||||
* <li>a {@code MethodHandles.Lookup}, which is a lookup object on the <em>caller class</em>
|
||||
* in which dynamically-computed constant or call site occurs</li>
|
||||
* <li>a {@code String}, the name mentioned in the {@code CONSTANT_NameAndType}</li>
|
||||
* <li>a {@code MethodType} or {@code Class}, the resolved type descriptor of the {@code CONSTANT_NameAndType}</li>
|
||||
* <li>a {@code Class}, the resolved type descriptor of the constant, if it is a dynamic constant </li>
|
||||
* <li>the additional resolved static arguments, if any</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* For an {@code invokedynamic} instruction, the
|
||||
* returned result must be convertible to a non-null reference to a
|
||||
* For a dynamically-computed call site, the returned result must be a non-null reference to a
|
||||
* {@link java.lang.invoke.CallSite CallSite}.
|
||||
* If the returned result cannot be converted to the expected type,
|
||||
* {@link java.lang.BootstrapMethodError BootstrapMethodError} is thrown.
|
||||
* The type of the call site's target must be exactly equal to the type
|
||||
* derived from the dynamic call site's type descriptor and passed to
|
||||
* the bootstrap method, otherwise a {@code BootstrapMethodError} is thrown.
|
||||
* On success the call site then becomes permanently linked to the dynamic call
|
||||
* site.
|
||||
* derived from the invocation's type descriptor and passed to
|
||||
* the bootstrap method. If these conditions are not met, a {@code BootstrapMethodError} is thrown.
|
||||
* On success the call site then becomes permanently linked to the {@code invokedynamic}
|
||||
* instruction.
|
||||
* <p>
|
||||
* If an exception, {@code E} say, occurs when linking the call site then the
|
||||
* linkage fails and terminates abnormally. {@code E} is rethrown if the type of
|
||||
* For a dynamically-computed constant, the result of the bootstrap method is cached
|
||||
* as the resolved constant value.
|
||||
* <p>
|
||||
* If an exception, {@code E} say, occurs during execution of the bootstrap method, then
|
||||
* resolution fails and terminates abnormally. {@code E} is rethrown if the type of
|
||||
* {@code E} is {@code Error} or a subclass, otherwise a
|
||||
* {@code BootstrapMethodError} that wraps {@code E} is thrown.
|
||||
* If this happens, the same {@code Error} or subclass will the thrown for all
|
||||
* subsequent attempts to execute the dynamic call site.
|
||||
* <h2>timing of linkage</h2>
|
||||
* A dynamic call site is linked just before its first execution.
|
||||
* If this happens, the same error will be thrown for all
|
||||
* subsequent attempts to execute the {@code invokedynamic} instruction or load the
|
||||
* dynamically-computed constant.
|
||||
*
|
||||
* <h2>Timing of resolution</h2>
|
||||
* An {@code invokedynamic} instruction is linked just before its first execution.
|
||||
* A dynamically-computed constant is resolved just before the first time it is used
|
||||
* (by pushing it on the stack or linking it as a bootstrap method parameter).
|
||||
* The bootstrap method call implementing the linkage occurs within
|
||||
* a thread that is attempting a first execution.
|
||||
* a thread that is attempting a first execution or first use.
|
||||
* <p>
|
||||
* If there are several such threads, the bootstrap method may be
|
||||
* invoked in several threads concurrently.
|
||||
@ -119,7 +147,7 @@
|
||||
* In any case, every {@code invokedynamic} instruction is either
|
||||
* unlinked or linked to a unique {@code CallSite} object.
|
||||
* <p>
|
||||
* In an application which requires dynamic call sites with individually
|
||||
* In an application which requires {@code invokedynamic} instructions with individually
|
||||
* mutable behaviors, their bootstrap methods should produce distinct
|
||||
* {@link java.lang.invoke.CallSite CallSite} objects, one for each linkage request.
|
||||
* Alternatively, an application can link a single {@code CallSite} object
|
||||
@ -127,53 +155,46 @@
|
||||
* a change to the target method will become visible at each of
|
||||
* the instructions.
|
||||
* <p>
|
||||
* If several threads simultaneously execute a bootstrap method for a single dynamic
|
||||
* call site, the JVM must choose one {@code CallSite} object and install it visibly to
|
||||
* If several threads simultaneously execute a bootstrap method for a single dynamically-computed
|
||||
* call site or constant, the JVM must choose one bootstrap method result and install it visibly to
|
||||
* all threads. Any other bootstrap method calls are allowed to complete, but their
|
||||
* results are ignored, and their dynamic call site invocations proceed with the originally
|
||||
* chosen target object.
|
||||
* results are ignored.
|
||||
|
||||
* <p style="font-size:smaller;">
|
||||
* <em>Discussion:</em>
|
||||
* These rules do not enable the JVM to duplicate dynamic call sites,
|
||||
* These rules do not enable the JVM to share call sites,
|
||||
* or to issue “causeless” bootstrap method calls.
|
||||
* Every dynamic call site transitions at most once from unlinked to linked,
|
||||
* Every {@code invokedynamic} instruction transitions at most once from unlinked to linked,
|
||||
* just before its first invocation.
|
||||
* There is no way to undo the effect of a completed bootstrap method call.
|
||||
*
|
||||
* <h2>types of bootstrap methods</h2>
|
||||
* As long as each bootstrap method can be correctly invoked
|
||||
* by {@code MethodHandle.invoke}, its detailed type is arbitrary.
|
||||
* <h2>Types of bootstrap methods</h2>
|
||||
* For a dynamically-computed call site, the bootstrap method is invoked with parameter
|
||||
* types {@code MethodHandles.Lookup}, {@code String}, {@code MethodType}, and the types
|
||||
* of any static arguments; the return type is {@code CallSite}. For a
|
||||
* dynamically-computed constant, the bootstrap method is invoked with parameter types
|
||||
* {@code MethodHandles.Lookup}, {@code String}, {@code Class}, and the types of any
|
||||
* static arguments; the return type is the type represented by the {@code Class}.
|
||||
*
|
||||
* Because {@link java.lang.invoke.MethodHandle#invoke MethodHandle.invoke} allows for
|
||||
* adaptations between the invoked method type and the method handle's method type,
|
||||
* there is flexibility in the declaration of the bootstrap method.
|
||||
* For example, the first argument could be {@code Object}
|
||||
* instead of {@code MethodHandles.Lookup}, and the return type
|
||||
* could also be {@code Object} instead of {@code CallSite}.
|
||||
* (Note that the types and number of the stacked arguments limit
|
||||
* the legal kinds of bootstrap methods to appropriately typed
|
||||
* static methods and constructors of {@code CallSite} subclasses.)
|
||||
* static methods and constructors.)
|
||||
* <p>
|
||||
* If a given {@code invokedynamic} instruction specifies no static arguments,
|
||||
* the instruction's bootstrap method will be invoked on three arguments,
|
||||
* conveying the instruction's caller class, name, and method type.
|
||||
* If the {@code invokedynamic} instruction specifies one or more static arguments,
|
||||
* those values will be passed as additional arguments to the method handle.
|
||||
* (Note that because there is a limit of 255 arguments to any method,
|
||||
* at most 251 extra arguments can be supplied to a non-varargs bootstrap method,
|
||||
* since the bootstrap method
|
||||
* handle itself and its first three arguments must also be stacked.)
|
||||
* The bootstrap method will be invoked as if by {@code MethodHandle.invokeWithArguments}.
|
||||
* A variable-arity bootstrap method can accept thousands of static arguments,
|
||||
* subject only by limits imposed by the class-file format.
|
||||
* <p>
|
||||
* The normal argument conversion rules for {@code MethodHandle.invoke} apply to all stacked arguments.
|
||||
* For example, if a pushed value is a primitive type, it may be converted to a reference by boxing conversion.
|
||||
* If a pushed value is a primitive type, it may be converted to a reference by boxing conversion.
|
||||
* If the bootstrap method is a variable arity method (its modifier bit {@code 0x0080} is set),
|
||||
* then some or all of the arguments specified here may be collected into a trailing array parameter.
|
||||
* (This is not a special rule, but rather a useful consequence of the interaction
|
||||
* between {@code CONSTANT_MethodHandle} constants, the modifier bit for variable arity methods,
|
||||
* and the {@link java.lang.invoke.MethodHandle#asVarargsCollector asVarargsCollector} transformation.)
|
||||
* <p>
|
||||
* Given these rules, here are examples of legal bootstrap method declarations,
|
||||
* given various numbers {@code N} of extra arguments.
|
||||
* Given these rules, here are examples of legal bootstrap method declarations for
|
||||
* dynamically-computed call sites, given various numbers {@code N} of extra arguments.
|
||||
* The first row (marked {@code *}) will work for any number of extra arguments.
|
||||
* <table class="plain" style="vertical-align:top">
|
||||
* <caption style="display:none">Static argument types</caption>
|
||||
@ -208,28 +229,27 @@
|
||||
* {@code String}.
|
||||
* The other examples work with all types of extra arguments.
|
||||
* <p>
|
||||
* As noted above, the actual method type of the bootstrap method can vary.
|
||||
* For example, the fourth argument could be {@code MethodHandle},
|
||||
* if that is the type of the corresponding constant in
|
||||
* the {@code CONSTANT_InvokeDynamic} entry.
|
||||
* In that case, the {@code MethodHandle.invoke} call will pass the extra method handle
|
||||
* constant as an {@code Object}, but the type matching machinery of {@code MethodHandle.invoke}
|
||||
* will cast the reference back to {@code MethodHandle} before invoking the bootstrap method.
|
||||
* (If a string constant were passed instead, by badly generated code, that cast would then fail,
|
||||
* resulting in a {@code BootstrapMethodError}.)
|
||||
* <p>
|
||||
* Note that, as a consequence of the above rules, the bootstrap method may accept a primitive
|
||||
* argument, if it can be represented by a constant pool entry.
|
||||
* Since dynamically-computed constants can be provided as static arguments to bootstrap
|
||||
* methods, there are no limitations on the types of bootstrap arguments.
|
||||
* However, arguments of type {@code boolean}, {@code byte}, {@code short}, or {@code char}
|
||||
* cannot be created for bootstrap methods, since such constants cannot be directly
|
||||
* represented in the constant pool, and the invocation of the bootstrap method will
|
||||
* cannot be <em>directly</em> supplied by {@code CONSTANT_Integer}
|
||||
* constant pool entries, since the {@code asType} conversions do
|
||||
* not perform the necessary narrowing primitive conversions.
|
||||
* <p>
|
||||
* Extra bootstrap method arguments are intended to allow language implementors
|
||||
* to safely and compactly encode metadata.
|
||||
* In principle, the name and extra arguments are redundant,
|
||||
* since each call site could be given its own unique bootstrap method.
|
||||
* Such a practice would be likely to produce large class files and constant pools.
|
||||
* In the above examples, the return type is always {@code CallSite},
|
||||
* but that is not a necessary feature of bootstrap methods.
|
||||
* In the case of a dynamically-computed call site, the only requirement is that
|
||||
* the return type of the bootstrap method must be convertible
|
||||
* (using the {@code asType} conversions) to {@code CallSite}, which
|
||||
* means the bootstrap method return type might be {@code Object} or
|
||||
* {@code ConstantCallSite}.
|
||||
* In the case of a dynamically-resolved constant, the return type of the bootstrap
|
||||
* method must be convertible to the type of the constant, as
|
||||
* represented by its field type descriptor. For example, if the
|
||||
* dynamic constant has a field type descriptor of {@code "C"}
|
||||
* ({@code char}) then the bootstrap method return type could be
|
||||
* {@code Object}, {@code Character}, or {@code char}, but not
|
||||
* {@code int} or {@code Integer}.
|
||||
*
|
||||
* @author John Rose, JSR 292 EG
|
||||
* @since 1.7
|
||||
|
@ -205,6 +205,10 @@ public class ClassReader {
|
||||
case ClassWriter.FLOAT:
|
||||
case ClassWriter.NAME_TYPE:
|
||||
case ClassWriter.INDY:
|
||||
// @@@ ClassWriter.CONDY
|
||||
// Enables MethodHandles.lookup().defineClass to function correctly
|
||||
// when it reads the class name
|
||||
case 17:
|
||||
size = 5;
|
||||
break;
|
||||
case ClassWriter.LONG:
|
||||
|
@ -107,6 +107,7 @@ enum {
|
||||
JVM_CONSTANT_NameAndType = 12,
|
||||
JVM_CONSTANT_MethodHandle = 15, // JSR 292
|
||||
JVM_CONSTANT_MethodType = 16, // JSR 292
|
||||
JVM_CONSTANT_Dynamic = 17,
|
||||
JVM_CONSTANT_InvokeDynamic = 18,
|
||||
JVM_CONSTANT_ExternalMax = 18
|
||||
};
|
||||
|
@ -30,6 +30,8 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jdk.vm.ci.hotspot.HotSpotConstantPool;
|
||||
import jdk.vm.ci.hotspot.HotSpotResolvedObjectType;
|
||||
import jdk.vm.ci.meta.MetaAccessProvider;
|
||||
import jdk.vm.ci.meta.ResolvedJavaMethod;
|
||||
import jdk.vm.ci.meta.ResolvedJavaType;
|
||||
@ -87,6 +89,10 @@ final class GraalFilters {
|
||||
if (specialClasses.stream().filter(s -> s.isAssignableFrom(klass)).findAny().isPresent()) {
|
||||
return false;
|
||||
}
|
||||
// Skip klass with Condy until Graal is fixed.
|
||||
if (((HotSpotConstantPool)((HotSpotResolvedObjectType) klass).getConstantPool()).hasDynamicConstant()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -84,6 +84,7 @@ public class ClassFile {
|
||||
public final static int CONSTANT_NameandType = 12;
|
||||
public final static int CONSTANT_MethodHandle = 15;
|
||||
public final static int CONSTANT_MethodType = 16;
|
||||
public final static int CONSTANT_Dynamic = 17;
|
||||
public final static int CONSTANT_InvokeDynamic = 18;
|
||||
public final static int CONSTANT_Module = 19;
|
||||
public final static int CONSTANT_Package = 20;
|
||||
|
@ -426,6 +426,7 @@ public class ClassReader {
|
||||
case CONSTANT_NameandType:
|
||||
case CONSTANT_Integer:
|
||||
case CONSTANT_Float:
|
||||
case CONSTANT_Dynamic:
|
||||
case CONSTANT_InvokeDynamic:
|
||||
bp = bp + 4;
|
||||
break;
|
||||
@ -501,6 +502,7 @@ public class ClassReader {
|
||||
case CONSTANT_MethodType:
|
||||
skipBytes(3);
|
||||
break;
|
||||
case CONSTANT_Dynamic:
|
||||
case CONSTANT_InvokeDynamic:
|
||||
skipBytes(5);
|
||||
break;
|
||||
|
@ -296,7 +296,7 @@ public class ConstantPool extends Metadata implements ClassConstants {
|
||||
// change byte-ordering and go via cache
|
||||
i = remapInstructionOperandFromCache(which);
|
||||
} else {
|
||||
if (getTagAt(which).isInvokeDynamic()) {
|
||||
if (getTagAt(which).isInvokeDynamic() || getTagAt(which).isDynamicConstant()) {
|
||||
int poolIndex = invokeDynamicNameAndTypeRefIndexAt(which);
|
||||
Assert.that(getTagAt(poolIndex).isNameAndType(), "");
|
||||
return poolIndex;
|
||||
@ -429,10 +429,10 @@ public class ConstantPool extends Metadata implements ClassConstants {
|
||||
return res;
|
||||
}
|
||||
|
||||
/** Lookup for multi-operand (InvokeDynamic) entries. */
|
||||
/** Lookup for multi-operand (InvokeDynamic, Dynamic) entries. */
|
||||
public short[] getBootstrapSpecifierAt(int i) {
|
||||
if (Assert.ASSERTS_ENABLED) {
|
||||
Assert.that(getTagAt(i).isInvokeDynamic(), "Corrupted constant pool");
|
||||
Assert.that(getTagAt(i).isInvokeDynamic() || getTagAt(i).isDynamicConstant(), "Corrupted constant pool");
|
||||
}
|
||||
int bsmSpec = extractLowShortFromInt(this.getIntAt(i));
|
||||
U2Array operands = getOperands();
|
||||
@ -468,6 +468,7 @@ public class ConstantPool extends Metadata implements ClassConstants {
|
||||
case JVM_CONSTANT_NameAndType: return "JVM_CONSTANT_NameAndType";
|
||||
case JVM_CONSTANT_MethodHandle: return "JVM_CONSTANT_MethodHandle";
|
||||
case JVM_CONSTANT_MethodType: return "JVM_CONSTANT_MethodType";
|
||||
case JVM_CONSTANT_Dynamic: return "JVM_CONSTANT_Dynamic";
|
||||
case JVM_CONSTANT_InvokeDynamic: return "JVM_CONSTANT_InvokeDynamic";
|
||||
case JVM_CONSTANT_Invalid: return "JVM_CONSTANT_Invalid";
|
||||
case JVM_CONSTANT_UnresolvedClass: return "JVM_CONSTANT_UnresolvedClass";
|
||||
@ -524,6 +525,7 @@ public class ConstantPool extends Metadata implements ClassConstants {
|
||||
case JVM_CONSTANT_NameAndType:
|
||||
case JVM_CONSTANT_MethodHandle:
|
||||
case JVM_CONSTANT_MethodType:
|
||||
case JVM_CONSTANT_Dynamic:
|
||||
case JVM_CONSTANT_InvokeDynamic:
|
||||
visitor.doInt(new IntField(new NamedFieldIdentifier(nameForTag(ctag)), indexOffset(index), true), true);
|
||||
break;
|
||||
|
@ -42,7 +42,7 @@ public interface ClassConstants
|
||||
public static final int JVM_CONSTANT_NameAndType = 12;
|
||||
public static final int JVM_CONSTANT_MethodHandle = 15;
|
||||
public static final int JVM_CONSTANT_MethodType = 16;
|
||||
// static final int JVM_CONSTANT_(unused) = 17;
|
||||
public static final int JVM_CONSTANT_Dynamic = 17;
|
||||
public static final int JVM_CONSTANT_InvokeDynamic = 18;
|
||||
|
||||
// JVM_CONSTANT_MethodHandle subtypes
|
||||
|
@ -296,6 +296,18 @@ public class ClassWriter implements /* imports */ ClassConstants
|
||||
break;
|
||||
}
|
||||
|
||||
case JVM_CONSTANT_Dynamic: {
|
||||
dos.writeByte(cpConstType);
|
||||
int value = cpool.getIntAt(ci);
|
||||
short bsmIndex = (short) extractLowShortFromInt(value);
|
||||
short nameAndTypeIndex = (short) extractHighShortFromInt(value);
|
||||
dos.writeShort(bsmIndex);
|
||||
dos.writeShort(nameAndTypeIndex);
|
||||
if (DEBUG) debugMessage("CP[" + ci + "] = CONDY bsm = " +
|
||||
bsmIndex + ", N&T = " + nameAndTypeIndex);
|
||||
break;
|
||||
}
|
||||
|
||||
case JVM_CONSTANT_InvokeDynamic: {
|
||||
dos.writeByte(cpConstType);
|
||||
int value = cpool.getIntAt(ci);
|
||||
|
@ -558,6 +558,12 @@ public class HTMLGenerator implements /* imports */ ClassConstants {
|
||||
buf.cell(Integer.toString(cpool.getIntAt(index)));
|
||||
break;
|
||||
|
||||
case JVM_CONSTANT_Dynamic:
|
||||
buf.cell("JVM_CONSTANT_Dynamic");
|
||||
buf.cell(genLowHighShort(cpool.getIntAt(index)) +
|
||||
genListOfShort(cpool.getBootstrapSpecifierAt(index)));
|
||||
break;
|
||||
|
||||
case JVM_CONSTANT_InvokeDynamic:
|
||||
buf.cell("JVM_CONSTANT_InvokeDynamic");
|
||||
buf.cell(genLowHighShort(cpool.getIntAt(index)) +
|
||||
|
@ -42,7 +42,7 @@ public class ConstantTag {
|
||||
private static final int JVM_CONSTANT_NameAndType = 12;
|
||||
private static final int JVM_CONSTANT_MethodHandle = 15; // JSR 292
|
||||
private static final int JVM_CONSTANT_MethodType = 16; // JSR 292
|
||||
// static final int JVM_CONSTANT_(unused) = 17; // JSR 292 early drafts only
|
||||
private static final int JVM_CONSTANT_Dynamic = 17; // JSR 292 early drafts only
|
||||
private static final int JVM_CONSTANT_InvokeDynamic = 18; // JSR 292
|
||||
private static final int JVM_CONSTANT_Invalid = 0; // For bad value initialization
|
||||
private static final int JVM_CONSTANT_UnresolvedClass = 100; // Temporary tag until actual use
|
||||
@ -84,6 +84,7 @@ public class ConstantTag {
|
||||
public boolean isUtf8() { return tag == JVM_CONSTANT_Utf8; }
|
||||
public boolean isMethodHandle() { return tag == JVM_CONSTANT_MethodHandle; }
|
||||
public boolean isMethodType() { return tag == JVM_CONSTANT_MethodType; }
|
||||
public boolean isDynamicConstant() { return tag == JVM_CONSTANT_Dynamic; }
|
||||
public boolean isInvokeDynamic() { return tag == JVM_CONSTANT_InvokeDynamic; }
|
||||
|
||||
public boolean isInvalid() { return tag == JVM_CONSTANT_Invalid; }
|
||||
|
@ -238,6 +238,8 @@ final class CompilerToVM {
|
||||
*/
|
||||
native HotSpotResolvedJavaMethodImpl lookupMethodInPool(HotSpotConstantPool constantPool, int cpi, byte opcode);
|
||||
|
||||
// TODO resolving JVM_CONSTANT_Dynamic
|
||||
|
||||
/**
|
||||
* Ensures that the type referenced by the specified {@code JVM_CONSTANT_InvokeDynamic} entry at
|
||||
* index {@code cpi} in {@code constantPool} is loaded and initialized.
|
||||
|
@ -491,6 +491,14 @@ public final class HotSpotConstantPool implements ConstantPool, MetaspaceWrapper
|
||||
return UNSAFE.getInt(getMetaspaceConstantPool() + config().constantPoolLengthOffset);
|
||||
}
|
||||
|
||||
public boolean hasDynamicConstant() {
|
||||
return (flags() & config().constantPoolHasDynamicConstant) != 0;
|
||||
}
|
||||
|
||||
private int flags() {
|
||||
return UNSAFE.getInt(getMetaspaceConstantPool() + config().constantPoolFlagsOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object lookupConstant(int cpi) {
|
||||
assert cpi != 0;
|
||||
|
@ -214,8 +214,10 @@ class HotSpotVMConfig extends HotSpotVMConfigAccess {
|
||||
final int constantPoolTagsOffset = getFieldOffset("ConstantPool::_tags", Integer.class, "Array<u1>*");
|
||||
final int constantPoolHolderOffset = getFieldOffset("ConstantPool::_pool_holder", Integer.class, "InstanceKlass*");
|
||||
final int constantPoolLengthOffset = getFieldOffset("ConstantPool::_length", Integer.class, "int");
|
||||
final int constantPoolFlagsOffset = getFieldOffset("ConstantPool::_flags", Integer.class, "int");
|
||||
|
||||
final int constantPoolCpCacheIndexTag = getConstant("ConstantPool::CPCACHE_INDEX_TAG", Integer.class);
|
||||
final int constantPoolHasDynamicConstant = getConstant("ConstantPool::_has_dynamic_constant", Integer.class);
|
||||
|
||||
final int jvmConstantUtf8 = getConstant("JVM_CONSTANT_Utf8", Integer.class);
|
||||
final int jvmConstantInteger = getConstant("JVM_CONSTANT_Integer", Integer.class);
|
||||
|
@ -28,6 +28,7 @@ package com.sun.tools.classfile;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sun.tools.classfile.ConstantPool.CONSTANT_Class_info;
|
||||
import com.sun.tools.classfile.ConstantPool.CONSTANT_Dynamic_info;
|
||||
import com.sun.tools.classfile.ConstantPool.CONSTANT_Double_info;
|
||||
import com.sun.tools.classfile.ConstantPool.CONSTANT_Fieldref_info;
|
||||
import com.sun.tools.classfile.ConstantPool.CONSTANT_Float_info;
|
||||
@ -331,6 +332,20 @@ public class ClassTranslator
|
||||
return info;
|
||||
}
|
||||
|
||||
public CPInfo visitDynamicConstant(CONSTANT_Dynamic_info info, Map<Object, Object> translations) {
|
||||
CONSTANT_Dynamic_info info2 = (CONSTANT_Dynamic_info) translations.get(info);
|
||||
if (info2 == null) {
|
||||
ConstantPool cp2 = translate(info.cp, translations);
|
||||
if (cp2 == info.cp) {
|
||||
info2 = info;
|
||||
} else {
|
||||
info2 = new CONSTANT_Dynamic_info(cp2, info.bootstrap_method_attr_index, info.name_and_type_index);
|
||||
}
|
||||
translations.put(info, info2);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CPInfo visitLong(CONSTANT_Long_info info, Map<Object, Object> translations) {
|
||||
CONSTANT_Long_info info2 = (CONSTANT_Long_info) translations.get(info);
|
||||
|
@ -283,6 +283,12 @@ public class ClassWriter {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public Integer visitDynamicConstant(CONSTANT_Dynamic_info info, ClassOutputStream out) {
|
||||
out.writeShort(info.bootstrap_method_attr_index);
|
||||
out.writeShort(info.name_and_type_index);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer visitLong(CONSTANT_Long_info info, ClassOutputStream out) {
|
||||
out.writeLong(info.value);
|
||||
|
@ -116,6 +116,7 @@ public class ConstantPool {
|
||||
public static final int CONSTANT_NameAndType = 12;
|
||||
public static final int CONSTANT_MethodHandle = 15;
|
||||
public static final int CONSTANT_MethodType = 16;
|
||||
public static final int CONSTANT_Dynamic = 17;
|
||||
public static final int CONSTANT_InvokeDynamic = 18;
|
||||
public static final int CONSTANT_Module = 19;
|
||||
public static final int CONSTANT_Package = 20;
|
||||
@ -198,6 +199,10 @@ public class ConstantPool {
|
||||
pool[i] = new CONSTANT_InvokeDynamic_info(this, cr);
|
||||
break;
|
||||
|
||||
case CONSTANT_Dynamic:
|
||||
pool[i] = new CONSTANT_Dynamic_info(this, cr);
|
||||
break;
|
||||
|
||||
case CONSTANT_Long:
|
||||
pool[i] = new CONSTANT_Long_info(cr);
|
||||
i++;
|
||||
@ -352,6 +357,7 @@ public class ConstantPool {
|
||||
R visitInteger(CONSTANT_Integer_info info, P p);
|
||||
R visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info info, P p);
|
||||
R visitInvokeDynamic(CONSTANT_InvokeDynamic_info info, P p);
|
||||
R visitDynamicConstant(CONSTANT_Dynamic_info info, P p);
|
||||
R visitLong(CONSTANT_Long_info info, P p);
|
||||
R visitMethodref(CONSTANT_Methodref_info info, P p);
|
||||
R visitMethodHandle(CONSTANT_MethodHandle_info info, P p);
|
||||
@ -879,6 +885,44 @@ public class ConstantPool {
|
||||
public final int type_index;
|
||||
}
|
||||
|
||||
public static class CONSTANT_Dynamic_info extends CPInfo {
|
||||
CONSTANT_Dynamic_info(ConstantPool cp, ClassReader cr) throws IOException {
|
||||
super(cp);
|
||||
bootstrap_method_attr_index = cr.readUnsignedShort();
|
||||
name_and_type_index = cr.readUnsignedShort();
|
||||
}
|
||||
|
||||
public CONSTANT_Dynamic_info(ConstantPool cp, int bootstrap_method_index, int name_and_type_index) {
|
||||
super(cp);
|
||||
this.bootstrap_method_attr_index = bootstrap_method_index;
|
||||
this.name_and_type_index = name_and_type_index;
|
||||
}
|
||||
|
||||
public int getTag() {
|
||||
return CONSTANT_Dynamic;
|
||||
}
|
||||
|
||||
public int byteLength() {
|
||||
return 5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CONSTANT_Dynamic_info[bootstrap_method_index: " + bootstrap_method_attr_index + ", name_and_type_index: " + name_and_type_index + "]";
|
||||
}
|
||||
|
||||
public <R, D> R accept(Visitor<R, D> visitor, D data) {
|
||||
return visitor.visitDynamicConstant(this, data);
|
||||
}
|
||||
|
||||
public CONSTANT_NameAndType_info getNameAndTypeInfo() throws ConstantPoolException {
|
||||
return cp.getNameAndTypeInfo(name_and_type_index);
|
||||
}
|
||||
|
||||
public final int bootstrap_method_attr_index;
|
||||
public final int name_and_type_index;
|
||||
}
|
||||
|
||||
public static class CONSTANT_Package_info extends CPInfo {
|
||||
CONSTANT_Package_info(ConstantPool cp, ClassReader cr) throws IOException {
|
||||
super(cp);
|
||||
|
@ -688,6 +688,11 @@ public class Dependencies {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitDynamicConstant(CONSTANT_Dynamic_info info, Void aVoid) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Void visitLong(CONSTANT_Long_info info, Void p) {
|
||||
return null;
|
||||
}
|
||||
|
@ -160,6 +160,11 @@ public final class ReferenceFinder {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean visitDynamicConstant(CONSTANT_Dynamic_info info, ConstantPool constantPool) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Boolean visitLong(CONSTANT_Long_info info, ConstantPool cpool) {
|
||||
return false;
|
||||
}
|
||||
|
@ -104,6 +104,13 @@ public class ConstantWriter extends BasicWriter {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public Integer visitDynamicConstant(CONSTANT_Dynamic_info info, Void p) {
|
||||
print("#" + info.bootstrap_method_attr_index + ":#" + info.name_and_type_index);
|
||||
tab();
|
||||
println("// " + stringValue(info));
|
||||
return 1;
|
||||
}
|
||||
|
||||
public Integer visitLong(CONSTANT_Long_info info, Void p) {
|
||||
println(stringValue(info));
|
||||
return 2;
|
||||
@ -246,6 +253,8 @@ public class ConstantWriter extends BasicWriter {
|
||||
return "InterfaceMethod";
|
||||
case CONSTANT_InvokeDynamic:
|
||||
return "InvokeDynamic";
|
||||
case CONSTANT_Dynamic:
|
||||
return "Dynamic";
|
||||
case CONSTANT_NameAndType:
|
||||
return "NameAndType";
|
||||
default:
|
||||
@ -346,6 +355,15 @@ public class ConstantWriter extends BasicWriter {
|
||||
}
|
||||
}
|
||||
|
||||
public String visitDynamicConstant(CONSTANT_Dynamic_info info, Void p) {
|
||||
try {
|
||||
String callee = stringValue(info.getNameAndTypeInfo());
|
||||
return "#" + info.bootstrap_method_attr_index + ":" + callee;
|
||||
} catch (ConstantPoolException e) {
|
||||
return report(e);
|
||||
}
|
||||
}
|
||||
|
||||
public String visitLong(CONSTANT_Long_info info, Void p) {
|
||||
return info.value + "l";
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
package com.sun.tools.jdeprscan.scan;
|
||||
|
||||
import com.sun.tools.classfile.ConstantPool;
|
||||
import com.sun.tools.classfile.ConstantPool.CONSTANT_Dynamic_info;
|
||||
|
||||
/**
|
||||
* A visitor that selects constant pool entries by type and adds
|
||||
@ -70,6 +71,10 @@ class CPSelector implements ConstantPool.Visitor<Void,CPEntries> {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Void visitDynamicConstant(CONSTANT_Dynamic_info info, CPEntries p) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitLong(ConstantPool.CONSTANT_Long_info info, CPEntries p) {
|
||||
return null;
|
||||
|
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package test.java.lang.invoke.lib;
|
||||
|
||||
import jdk.experimental.bytecode.BasicClassBuilder;
|
||||
import jdk.experimental.bytecode.BasicTypeHelper;
|
||||
import jdk.experimental.bytecode.Flag;
|
||||
import jdk.experimental.bytecode.PoolHelper;
|
||||
import jdk.experimental.bytecode.TypedCodeBuilder;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static java.lang.invoke.MethodType.fromMethodDescriptorString;
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
|
||||
public class InstructionHelper {
|
||||
|
||||
static final BasicTypeHelper BTH = new BasicTypeHelper();
|
||||
|
||||
static final AtomicInteger COUNT = new AtomicInteger();
|
||||
|
||||
static BasicClassBuilder classBuilder(MethodHandles.Lookup l) {
|
||||
String className = l.lookupClass().getCanonicalName().replace('.', '/') + "$Code_" + COUNT.getAndIncrement();
|
||||
return new BasicClassBuilder(className, 55, 0)
|
||||
.withSuperclass("java/lang/Object")
|
||||
.withMethod("<init>", "()V", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.aload_0().invokespecial("java/lang/Object", "<init>", "()V", false).return_()
|
||||
));
|
||||
}
|
||||
|
||||
public static MethodHandle invokedynamic(MethodHandles.Lookup l,
|
||||
String name, MethodType type,
|
||||
String bsmMethodName, MethodType bsmType,
|
||||
Consumer<PoolHelper.StaticArgListBuilder<String, String, byte[]>> staticArgs) throws Exception {
|
||||
byte[] byteArray = classBuilder(l)
|
||||
.withMethod("m", type.toMethodDescriptorString(), M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new,
|
||||
C -> {
|
||||
for (int i = 0; i < type.parameterCount(); i++) {
|
||||
C.load(BTH.tag(cref(type.parameterType(i))), i);
|
||||
}
|
||||
C.invokedynamic(name, type.toMethodDescriptorString(),
|
||||
csym(l.lookupClass()), bsmMethodName, bsmType.toMethodDescriptorString(),
|
||||
staticArgs);
|
||||
C.return_(BTH.tag(cref(type.returnType())));
|
||||
}
|
||||
))
|
||||
.build();
|
||||
Class<?> gc = l.defineClass(byteArray);
|
||||
return l.findStatic(gc, "m", type);
|
||||
}
|
||||
|
||||
public static MethodHandle ldcMethodHandle(MethodHandles.Lookup l,
|
||||
int refKind, Class<?> owner, String name, MethodType type) throws Exception {
|
||||
return ldc(l, MethodHandle.class,
|
||||
P -> P.putHandle(refKind, csym(owner), name, type.toMethodDescriptorString()));
|
||||
}
|
||||
|
||||
public static MethodHandle ldcDynamicConstant(MethodHandles.Lookup l,
|
||||
String name, Class<?> type,
|
||||
String bsmMethodName, MethodType bsmType,
|
||||
Consumer<PoolHelper.StaticArgListBuilder<String, String, byte[]>> staticArgs) throws Exception {
|
||||
return ldcDynamicConstant(l, name, cref(type), bsmMethodName, bsmType.toMethodDescriptorString(), staticArgs);
|
||||
}
|
||||
|
||||
public static MethodHandle ldcDynamicConstant(MethodHandles.Lookup l,
|
||||
String name, String type,
|
||||
String bsmMethodName, String bsmType,
|
||||
Consumer<PoolHelper.StaticArgListBuilder<String, String, byte[]>> staticArgs) throws Exception {
|
||||
return ldc(l, type,
|
||||
P -> P.putDynamicConstant(name, type,
|
||||
csym(l.lookupClass()), bsmMethodName, bsmType,
|
||||
staticArgs));
|
||||
}
|
||||
|
||||
public static MethodHandle ldc(MethodHandles.Lookup l,
|
||||
Class<?> type,
|
||||
Function<PoolHelper<String, String, byte[]>, Integer> poolFunc) throws Exception {
|
||||
return ldc(l, cref(type), poolFunc);
|
||||
}
|
||||
|
||||
public static MethodHandle ldc(MethodHandles.Lookup l,
|
||||
String type,
|
||||
Function<PoolHelper<String, String, byte[]>, Integer> poolFunc) throws Exception {
|
||||
String methodType = "()" + type;
|
||||
byte[] byteArray = classBuilder(l)
|
||||
.withMethod("m", "()" + type, M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new,
|
||||
C -> {
|
||||
C.ldc(null, (P, v) -> poolFunc.apply(P));
|
||||
C.return_(BTH.tag(type));
|
||||
}
|
||||
))
|
||||
.build();
|
||||
Class<?> gc = l.defineClass(byteArray);
|
||||
return l.findStatic(gc, "m", fromMethodDescriptorString(methodType, l.lookupClass().getClassLoader()));
|
||||
}
|
||||
|
||||
public static String csym(Class<?> c) {
|
||||
return c.getCanonicalName().replace('.', '/');
|
||||
}
|
||||
|
||||
public static String cref(Class<?> c) {
|
||||
return methodType(c).toMethodDescriptorString().substring(2);
|
||||
}
|
||||
}
|
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8186046
|
||||
* @summary Test bootstrap methods throwing an exception
|
||||
* @library /lib/testlibrary/bytecode /java/lang/invoke/common
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
|
||||
* @run testng BootstrapMethodJumboArgsTest
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 BootstrapMethodJumboArgsTest
|
||||
*/
|
||||
|
||||
import jdk.experimental.bytecode.PoolHelper;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
import test.java.lang.invoke.lib.InstructionHelper;
|
||||
|
||||
import java.lang.invoke.ConstantCallSite;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
|
||||
public class BootstrapMethodJumboArgsTest {
|
||||
static final MethodHandles.Lookup L = MethodHandles.lookup();
|
||||
|
||||
|
||||
static Object bsmZero(MethodHandles.Lookup l, String name, Object type,
|
||||
Object... args) {
|
||||
Object[] a = args.clone();
|
||||
if (type instanceof MethodType) {
|
||||
return new ConstantCallSite(MethodHandles.constant(Object[].class, a));
|
||||
}
|
||||
else {
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
static Object bsmOne(MethodHandles.Lookup l, String name, Object type,
|
||||
Object first, Object... args) {
|
||||
Object[] a = new Object[args.length + 1];
|
||||
a[0] = first;
|
||||
System.arraycopy(args, 0, a, 1, args.length);
|
||||
if (type instanceof MethodType) {
|
||||
return new ConstantCallSite(MethodHandles.constant(Object[].class, a));
|
||||
}
|
||||
else {
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
static Object bsmTwo(MethodHandles.Lookup l, String name, Object type,
|
||||
Object first, Object second, Object... args) {
|
||||
Object[] a = new Object[args.length + 2];
|
||||
a[0] = first;
|
||||
a[1] = second;
|
||||
System.arraycopy(args, 0, a, 2, args.length);
|
||||
if (type instanceof MethodType) {
|
||||
return new ConstantCallSite(MethodHandles.constant(Object[].class, a));
|
||||
}
|
||||
else {
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
static void manyStaticStrings(String[] args, PoolHelper.StaticArgListBuilder<String, String, byte[]> staticArgs) {
|
||||
for (String s : args) {
|
||||
staticArgs.add(s);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCondyWithJumboArgs() throws Throwable {
|
||||
String[] expected = IntStream.range(0, 1000).mapToObj(Integer::toString).toArray(String[]::new);
|
||||
|
||||
{
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", Object[].class,
|
||||
"bsmZero", methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class, Object[].class),
|
||||
S -> manyStaticStrings(expected, S));
|
||||
|
||||
Object[] actual = (Object[]) mh.invoke();
|
||||
Assert.assertEquals(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", Object[].class,
|
||||
"bsmOne", methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class, Object.class, Object[].class),
|
||||
S -> manyStaticStrings(expected, S));
|
||||
|
||||
Object[] actual = (Object[]) mh.invoke();
|
||||
Assert.assertEquals(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", Object[].class,
|
||||
"bsmTwo", methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class, Object.class, Object.class, Object[].class),
|
||||
S -> manyStaticStrings(expected, S));
|
||||
|
||||
Object[] actual = (Object[]) mh.invoke();
|
||||
Assert.assertEquals(actual, expected);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndyWithJumboArgs() throws Throwable {
|
||||
String[] expected = IntStream.range(0, 1000).mapToObj(Integer::toString).toArray(String[]::new);
|
||||
|
||||
{
|
||||
MethodHandle mh = InstructionHelper.invokedynamic(
|
||||
L, "name", methodType(Object[].class),
|
||||
"bsmZero", methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class, Object[].class),
|
||||
S -> manyStaticStrings(expected, S));
|
||||
|
||||
Object[] actual = (Object[]) mh.invoke();
|
||||
Assert.assertEquals(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
MethodHandle mh = InstructionHelper.invokedynamic(
|
||||
L, "name", methodType(Object[].class),
|
||||
"bsmOne", methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class, Object.class, Object[].class),
|
||||
S -> manyStaticStrings(expected, S));
|
||||
|
||||
Object[] actual = (Object[]) mh.invoke();
|
||||
Assert.assertEquals(actual, expected);
|
||||
}
|
||||
|
||||
{
|
||||
MethodHandle mh = InstructionHelper.invokedynamic(
|
||||
L, "name", methodType(Object[].class),
|
||||
"bsmTwo", methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class, Object.class, Object.class, Object[].class),
|
||||
S -> manyStaticStrings(expected, S));
|
||||
|
||||
Object[] actual = (Object[]) mh.invoke();
|
||||
Assert.assertEquals(actual, expected);
|
||||
}
|
||||
}
|
||||
}
|
119
test/jdk/java/lang/invoke/condy/CondyBSMException.java
Normal file
119
test/jdk/java/lang/invoke/condy/CondyBSMException.java
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8186046
|
||||
* @summary Test bootstrap methods throwing an exception
|
||||
* @library /lib/testlibrary/bytecode /java/lang/invoke/common
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
|
||||
* @run testng CondyBSMException
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyBSMException
|
||||
*/
|
||||
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
import test.java.lang.invoke.lib.InstructionHelper;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
|
||||
public class CondyBSMException {
|
||||
|
||||
@Test
|
||||
public void testThrowable() {
|
||||
test("Throwable", BootstrapMethodError.class, Throwable.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testError() {
|
||||
test("Error", Error.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBootstrapMethodError() {
|
||||
test("BootstrapMethodError", BootstrapMethodError.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRuntimeException() {
|
||||
test("RuntimeException", BootstrapMethodError.class, RuntimeException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testException() throws Throwable {
|
||||
test("Exception", BootstrapMethodError.class, Exception.class);
|
||||
}
|
||||
|
||||
static void test(String message, Class<? extends Throwable>... ts) {
|
||||
MethodHandle mh = thrower(message, ts[ts.length - 1]);
|
||||
Throwable caught = null;
|
||||
try {
|
||||
mh.invoke();
|
||||
}
|
||||
catch (Throwable t) {
|
||||
caught = t;
|
||||
}
|
||||
|
||||
if (caught == null) {
|
||||
Assert.fail("Throwable expected");
|
||||
}
|
||||
|
||||
String actualMessage = null;
|
||||
for (int i = 0; i < ts.length; i++) {
|
||||
actualMessage = caught.getMessage();
|
||||
Assert.assertNotNull(caught);
|
||||
Assert.assertTrue(ts[i].isAssignableFrom(caught.getClass()));
|
||||
caught = caught.getCause();
|
||||
}
|
||||
|
||||
Assert.assertEquals(actualMessage, message);
|
||||
}
|
||||
|
||||
static Throwable throwingBsm(MethodHandles.Lookup l, String name, Class<Throwable> type) throws Throwable {
|
||||
Throwable t;
|
||||
try {
|
||||
Constructor<Throwable> c = type.getDeclaredConstructor(String.class);
|
||||
t = c.newInstance(name);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new InternalError();
|
||||
}
|
||||
throw t;
|
||||
}
|
||||
|
||||
static MethodHandle thrower(String message, Class<? extends Throwable> t) {
|
||||
try {
|
||||
return InstructionHelper.ldcDynamicConstant(
|
||||
MethodHandles.lookup(),
|
||||
message, t,
|
||||
"throwingBsm", methodType(Throwable.class, MethodHandles.Lookup.class, String.class, Class.class),
|
||||
S -> { });
|
||||
} catch (Exception e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
}
|
240
test/jdk/java/lang/invoke/condy/CondyBSMInvocation.java
Normal file
240
test/jdk/java/lang/invoke/condy/CondyBSMInvocation.java
Normal file
@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8186046
|
||||
* @summary Test basic invocation of bootstrap methods
|
||||
* @library /lib/testlibrary/bytecode /java/lang/invoke/common
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
|
||||
* @run testng CondyBSMInvocation
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyBSMInvocation
|
||||
*/
|
||||
|
||||
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
import test.java.lang.invoke.lib.InstructionHelper;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.WrongMethodTypeException;
|
||||
import java.util.Collections;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
|
||||
public class CondyBSMInvocation {
|
||||
static final MethodHandles.Lookup L = MethodHandles.lookup();
|
||||
|
||||
|
||||
@Test
|
||||
public void testNonexistent() throws Throwable {
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", Object.class,
|
||||
"bsm", methodType(Object.class),
|
||||
S -> {});
|
||||
|
||||
try {
|
||||
mh.invoke();
|
||||
Assert.fail("NoSuchMethodError expected to be thrown");
|
||||
} catch (NoSuchMethodError e) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static Object _bsm() {
|
||||
return "0";
|
||||
}
|
||||
|
||||
public static Object _bsm(Object a1) {
|
||||
return "0";
|
||||
}
|
||||
|
||||
// Note: when pull mode is supported for a BSM this test case
|
||||
// will fail and must be removed
|
||||
public static Object _bsm(Object a1, Object a2) {
|
||||
return "0";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrongArity() throws Throwable {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
final int n = i;
|
||||
MethodType mt = methodType(Object.class)
|
||||
.appendParameterTypes(Collections.nCopies(n, Object.class));
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", Object.class,
|
||||
"_bsm", mt,
|
||||
S -> IntStream.range(0, n).forEach(S::add)
|
||||
);
|
||||
|
||||
try {
|
||||
Object r = mh.invoke();
|
||||
Assert.fail("BootstrapMethodError expected to be thrown for arrity " + n);
|
||||
} catch (BootstrapMethodError e) {
|
||||
Throwable t = e.getCause();
|
||||
Assert.assertTrue(WrongMethodTypeException.class.isAssignableFrom(t.getClass()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static Object _bsm(String[] ss) {
|
||||
return "0";
|
||||
}
|
||||
|
||||
public static Object _bsm(String a1, String a2, String a3) {
|
||||
return "0";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrongSignature() throws Throwable {
|
||||
{
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", Object.class,
|
||||
"_bsm", methodType(Object.class, String[].class),
|
||||
S -> {}
|
||||
);
|
||||
|
||||
try {
|
||||
Object r = mh.invoke();
|
||||
Assert.fail("BootstrapMethodError expected to be thrown");
|
||||
}
|
||||
catch (BootstrapMethodError e) {
|
||||
Throwable t = e.getCause();
|
||||
Assert.assertTrue(WrongMethodTypeException.class.isAssignableFrom(t.getClass()));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", Object.class,
|
||||
"_bsm", methodType(Object.class, String.class, String.class, String.class),
|
||||
S -> {}
|
||||
);
|
||||
|
||||
try {
|
||||
Object r = mh.invoke();
|
||||
Assert.fail("BootstrapMethodError expected to be thrown");
|
||||
}
|
||||
catch (BootstrapMethodError e) {
|
||||
Throwable t = e.getCause();
|
||||
Assert.assertTrue(ClassCastException.class.isAssignableFrom(t.getClass()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type) {
|
||||
return "0";
|
||||
}
|
||||
|
||||
public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
|
||||
Object a1) {
|
||||
assertAll(a1);
|
||||
return "1";
|
||||
}
|
||||
|
||||
public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
|
||||
Object a1, Object a2) {
|
||||
assertAll(a1, a2);
|
||||
return "2";
|
||||
}
|
||||
|
||||
public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
|
||||
Object a1, Object a2, Object a3) {
|
||||
assertAll(a1, a2, a3);
|
||||
return "3";
|
||||
}
|
||||
|
||||
public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
|
||||
Object a1, Object a2, Object a3, Object a4) {
|
||||
assertAll(a1, a2, a3, a4);
|
||||
return "4";
|
||||
}
|
||||
|
||||
public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
|
||||
Object a1, Object a2, Object a3, Object a4, Object a5) {
|
||||
assertAll(a1, a2, a3, a4, a5);
|
||||
return "5";
|
||||
}
|
||||
|
||||
public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
|
||||
Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) {
|
||||
assertAll(a1, a2, a3, a4, a5, a6);
|
||||
return "6";
|
||||
}
|
||||
|
||||
public static Object bsm(MethodHandles.Lookup l, String name, Class<?> type,
|
||||
Object a1, Object a2, Object a3, Object a4, Object a5, Object a6, Object a7) {
|
||||
assertAll(a1, a2, a3, a4, a5, a6, a7);
|
||||
return "7";
|
||||
}
|
||||
|
||||
static void assertAll(Object... as) {
|
||||
for (int i = 0; i < as.length; i++) {
|
||||
Assert.assertEquals(as[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testArity() throws Throwable {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
final int n = i;
|
||||
MethodType mt = methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class)
|
||||
.appendParameterTypes(Collections.nCopies(n, Object.class));
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", Object.class,
|
||||
"bsm", mt,
|
||||
S -> IntStream.range(0, n).forEach(S::add)
|
||||
);
|
||||
|
||||
Object r = mh.invoke();
|
||||
Assert.assertEquals(r, Integer.toString(n));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrongNumberOfStaticArguments() throws Throwable {
|
||||
for (int i = 1; i < 8; i++) {
|
||||
final int n = i;
|
||||
MethodType mt = methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class)
|
||||
.appendParameterTypes(Collections.nCopies(n, Object.class));
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", Object.class,
|
||||
"bsm", mt,
|
||||
S -> IntStream.range(0, n - 1).forEach(S::add)
|
||||
);
|
||||
|
||||
try {
|
||||
Object r = mh.invoke();
|
||||
Assert.fail("BootstrapMethodError expected to be thrown for arrity " + n);
|
||||
} catch (BootstrapMethodError e) {
|
||||
Throwable t = e.getCause();
|
||||
Assert.assertTrue(WrongMethodTypeException.class.isAssignableFrom(t.getClass()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
67
test/jdk/java/lang/invoke/condy/CondyBSMValidationTest.java
Normal file
67
test/jdk/java/lang/invoke/condy/CondyBSMValidationTest.java
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8186046
|
||||
* @summary Test invalid name in name and type
|
||||
* @library /lib/testlibrary/bytecode /java/lang/invoke/common
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
|
||||
* @run testng CondyBSMValidationTest
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyBSMValidationTest
|
||||
*/
|
||||
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
import test.java.lang.invoke.lib.InstructionHelper;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
|
||||
public class CondyBSMValidationTest {
|
||||
static final MethodHandles.Lookup L = MethodHandles.lookup();
|
||||
static final String BSM_TYPE = methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class)
|
||||
.toMethodDescriptorString();
|
||||
|
||||
@DataProvider
|
||||
public Object[][] invalidSignaturesProvider() throws Exception {
|
||||
return Stream.of(BSM_TYPE.replace("(", ""),
|
||||
BSM_TYPE.replace(")", ""),
|
||||
BSM_TYPE.replace("(", "").replace(")", ""),
|
||||
BSM_TYPE.replace(";)", ")"),
|
||||
BSM_TYPE.replace(";", ""))
|
||||
.map(e -> new Object[]{e}).toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "invalidSignaturesProvider", expectedExceptions = ClassFormatError.class)
|
||||
public void testInvalidBSMSignature(String sig) throws Exception {
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", "Ljava/lang/Object;",
|
||||
"bsm", sig,
|
||||
S -> {
|
||||
});
|
||||
}
|
||||
}
|
84
test/jdk/java/lang/invoke/condy/CondyNameValidationTest.java
Normal file
84
test/jdk/java/lang/invoke/condy/CondyNameValidationTest.java
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8186046
|
||||
* @summary Test invalid name in name and type
|
||||
* @library /lib/testlibrary/bytecode /java/lang/invoke/common
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
|
||||
* @run testng CondyNameValidationTest
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyNameValidationTest
|
||||
*/
|
||||
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
import test.java.lang.invoke.lib.InstructionHelper;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
|
||||
public class CondyNameValidationTest {
|
||||
static final MethodHandles.Lookup L = MethodHandles.lookup();
|
||||
static final MethodType BSM_TYPE = methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class);
|
||||
|
||||
@DataProvider
|
||||
public Object[][] invalidNamesProvider() throws Exception {
|
||||
return Stream.of("",
|
||||
".",
|
||||
";",
|
||||
"[",
|
||||
"/")
|
||||
.map(e -> new Object[]{e}).toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "invalidNamesProvider", expectedExceptions = java.lang.ClassFormatError.class)
|
||||
public void testInvalidNames(String name) throws Exception {
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, name, Object.class,
|
||||
"bsm", BSM_TYPE,
|
||||
S -> {
|
||||
});
|
||||
}
|
||||
|
||||
@DataProvider
|
||||
public Object[][] validNamesProvider() throws Exception {
|
||||
return Stream.of("<clinit>",
|
||||
"<init>",
|
||||
"<foo>")
|
||||
.map(e -> new Object[]{e}).toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "validNamesProvider")
|
||||
public void testValidNames(String name) throws Exception {
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, name, Object.class,
|
||||
"bsm", BSM_TYPE,
|
||||
S -> {
|
||||
});
|
||||
}
|
||||
}
|
282
test/jdk/java/lang/invoke/condy/CondyNestedTest.java
Normal file
282
test/jdk/java/lang/invoke/condy/CondyNestedTest.java
Normal file
@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8186046
|
||||
* @summary Test nested dynamic constant declarations that are recursive
|
||||
* @library /lib/testlibrary/bytecode
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder
|
||||
* @run testng CondyNestedTest
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyNestedTest
|
||||
*/
|
||||
|
||||
import jdk.experimental.bytecode.BasicClassBuilder;
|
||||
import jdk.experimental.bytecode.Flag;
|
||||
import jdk.experimental.bytecode.MacroCodeBuilder;
|
||||
import jdk.experimental.bytecode.PoolHelper;
|
||||
import jdk.experimental.bytecode.TypeTag;
|
||||
import jdk.experimental.bytecode.TypedCodeBuilder;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.lang.invoke.CallSite;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Base64;
|
||||
|
||||
public class CondyNestedTest {
|
||||
|
||||
/**
|
||||
* NOTE: This is a temporary solution until asmtools is updated to support
|
||||
* dynamic constant and jtreg is updated to include a new version of
|
||||
* asmtools.
|
||||
*
|
||||
* These are the class file bytes for a class named CondyNestedTest_Code
|
||||
* whose bytes are 1) generated by the generator() function; 2) the bytes
|
||||
* converted to a jcod file:
|
||||
*
|
||||
* java -jar asmtools.jar jdec CondyNestedTest_Code.class >
|
||||
* CondyNestedTest_Code.jcod
|
||||
*
|
||||
* which was then edited so that dynamic constant declarations are
|
||||
* recursive both for an ldc or invokedynamic (specifically declaring a
|
||||
* BSM+attributes whose static argument is a dynamic constant
|
||||
* that refers to the same BSM+attributes); 3) the jcod file is converted
|
||||
* back to a class file:
|
||||
*
|
||||
* java -jar asmtools.jar jdis [options] CondyNestedTest_Code.jcod
|
||||
*
|
||||
* ;and finally 4) the newly generated class file bytes are converted to
|
||||
* a base64 representation and embedded in the following String.
|
||||
*/
|
||||
static final String CLASS_CondyNestedTest_Code =
|
||||
"yv66vgAAADcAQgEAEGphdmEvbGFuZy9PYmplY3QHAAEBAAY8aW5pdD4BAAMoKVYMAAMABAoAAgAFAQAE" +
|
||||
"Q29kZQEAEGphdmEvbGFuZy9TdHJpbmcHAAgBAAZpbnRlcm4BABQoKUxqYXZhL2xhbmcvU3RyaW5nOwwA" +
|
||||
"CgALCgAJAAwBABNjb25keV9ic21fY29uZHlfYnNtCAAOAQAUQ29uZHlOZXN0ZWRUZXN0X0NvZGUHABAB" +
|
||||
"ABQoKUxqYXZhL2xhbmcvT2JqZWN0OwwADgASCgARABMBABZpbmR5X2JzbUluZHlfY29uZHlfYnNtCAAV" +
|
||||
"DAAVABIKABEAFwEAEmluZHlfYnNtX2NvbmR5X2JzbQgAGQwAGQASCgARABsBAA1TdGFja01hcFRhYmxl" +
|
||||
"AQAEbWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYBABtqYXZhL2xhbmcvaW52b2tlL01ldGhvZFR5" +
|
||||
"cGUHACABACFqYXZhL2xhbmcvaW52b2tlL0NvbnN0YW50Q2FsbFNpdGUHACIBAB5qYXZhL2xhbmcvaW52" +
|
||||
"b2tlL01ldGhvZEhhbmRsZXMHACQBAAhjb25zdGFudAEARChMamF2YS9sYW5nL0NsYXNzO0xqYXZhL2xh" +
|
||||
"bmcvT2JqZWN0OylMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGU7DAAmACcKACUAKAEAIihMamF2" +
|
||||
"YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGU7KVYMAAMAKgoAIwArAQADYnNtAQBxKExqYXZhL2xhbmcv" +
|
||||
"aW52b2tlL01ldGhvZEhhbmRsZXMkTG9va3VwO0xqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xhbmcvT2Jq" +
|
||||
"ZWN0O0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL09iamVjdDsBAAdic21JbmR5AQB6KExqYXZh" +
|
||||
"L2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZXMkTG9va3VwO0xqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL2xh" +
|
||||
"bmcvT2JqZWN0O0xqYXZhL2xhbmcvT2JqZWN0OylMamF2YS9sYW5nL2ludm9rZS9DYWxsU2l0ZTsBAAlE" +
|
||||
"VU1NWV9BUkcIADEMAC0ALgoAEQAzDwYANAEABG5hbWUBABJMamF2YS9sYW5nL1N0cmluZzsMADYANxEA" +
|
||||
"AAA4EQABADgMAC8AMAoAEQA7DwYAPAwANgALEgACAD4SAAEAPgEAEEJvb3RzdHJhcE1ldGhvZHMAAAAR" +
|
||||
"AAIAAAAAAAcAAQADAAQAAQAHAAAAEQABAAEAAAAFKrcABrEAAAAAAAkAHgAfAAEABwAAAEIAAgACAAAA" +
|
||||
"JioDMrYADUwrEg+mAAe4ABSxKxIWpgAHuAAYsSsSGqYAB7gAHLGxAAAAAQAdAAAACgAD/AARBwAJCQkA" +
|
||||
"CQAtAC4AAQAHAAAALQAEAAQAAAAYLMEAIQOfABG7ACNZEgkruAAptwAssCuwAAAAAQAdAAAAAwABFgAJ" +
|
||||
"AC8AMAABAAcAAAAaAAQABAAAAA67ACNZEgkruAAptwAssAAAAAAACQAOABIAAQAHAAAADwABAAAAAAAD" +
|
||||
"EjqwAAAAAAAJABUAEgABAAcAAAASAAEAAAAAAAa6AD8AALAAAAAAAAkAGQASAAEABwAAABIAAQAAAAAA" +
|
||||
"BroAQAAAsAAAAAAAAQBBAAAAFAADADUAAQAyADUAAQA6AD0AAQA6";
|
||||
|
||||
static final MethodHandles.Lookup L = MethodHandles.lookup();
|
||||
|
||||
static final Class[] THROWABLES = {InvocationTargetException.class, StackOverflowError.class};
|
||||
|
||||
Class<?> c;
|
||||
|
||||
public static byte[] generator() throws Exception {
|
||||
String genClassName = L.lookupClass().getSimpleName() + "_Code";
|
||||
String bsmDescriptor = MethodType.methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class, Object.class).toMethodDescriptorString();
|
||||
String bsmIndyDescriptor = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, Object.class, Object.class).toMethodDescriptorString();
|
||||
|
||||
byte[] byteArray = new BasicClassBuilder(genClassName, 55, 0)
|
||||
.withSuperclass("java/lang/Object")
|
||||
.withMethod("<init>", "()V", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.aload_0().invokespecial("java/lang/Object", "<init>", "()V", false).return_()
|
||||
))
|
||||
.withMethod("main", "([Ljava/lang/String;)V", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C -> {
|
||||
C.aload_0().iconst_0().aaload();
|
||||
C.invokevirtual("java/lang/String", "intern", "()Ljava/lang/String;", false);
|
||||
C.astore_1();
|
||||
|
||||
C.aload_1();
|
||||
C.ldc("condy_bsm_condy_bsm");
|
||||
C.ifcmp(TypeTag.A, MacroCodeBuilder.CondKind.NE, "CASE1");
|
||||
C.invokestatic(genClassName, "condy_bsm_condy_bsm", "()Ljava/lang/Object;", false).return_();
|
||||
|
||||
C.label("CASE1");
|
||||
C.aload_1();
|
||||
C.ldc("indy_bsmIndy_condy_bsm");
|
||||
C.ifcmp(TypeTag.A, MacroCodeBuilder.CondKind.NE, "CASE2");
|
||||
C.invokestatic(genClassName, "indy_bsmIndy_condy_bsm", "()Ljava/lang/Object;", false).return_();
|
||||
|
||||
C.label("CASE2");
|
||||
C.aload_1();
|
||||
C.ldc("indy_bsm_condy_bsm");
|
||||
C.ifcmp(TypeTag.A, MacroCodeBuilder.CondKind.NE, "CASE3");
|
||||
C.invokestatic(genClassName, "indy_bsm_condy_bsm", "()Ljava/lang/Object;", false).return_();
|
||||
|
||||
C.label("CASE3");
|
||||
C.return_();
|
||||
}))
|
||||
.withMethod("bsm", bsmDescriptor, M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C -> {
|
||||
C.aload_2();
|
||||
C.instanceof_("java/lang/invoke/MethodType");
|
||||
C.iconst_0();
|
||||
C.ifcmp(TypeTag.I, MacroCodeBuilder.CondKind.EQ, "CONDY");
|
||||
C.new_("java/lang/invoke/ConstantCallSite").dup();
|
||||
C.ldc("java/lang/String", PoolHelper::putClass);
|
||||
C.aload_1();
|
||||
C.invokestatic("java/lang/invoke/MethodHandles", "constant", "(Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;", false);
|
||||
C.invokespecial("java/lang/invoke/ConstantCallSite", "<init>", "(Ljava/lang/invoke/MethodHandle;)V", false);
|
||||
C.areturn();
|
||||
C.label("CONDY");
|
||||
C.aload_1().areturn();
|
||||
}))
|
||||
.withMethod("bsmIndy", bsmIndyDescriptor, M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C -> {
|
||||
C.new_("java/lang/invoke/ConstantCallSite").dup();
|
||||
C.ldc("java/lang/String", PoolHelper::putClass);
|
||||
C.aload_1();
|
||||
C.invokestatic("java/lang/invoke/MethodHandles", "constant", "(Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;", false);
|
||||
C.invokespecial("java/lang/invoke/ConstantCallSite", "<init>", "(Ljava/lang/invoke/MethodHandle;)V", false);
|
||||
C.areturn();
|
||||
}))
|
||||
.withMethod("condy_bsm_condy_bsm", "()Ljava/lang/Object;", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("name", "Ljava/lang/String;", genClassName, "bsm", bsmDescriptor,
|
||||
S -> S.add(null, (P, v) -> {
|
||||
return P.putDynamicConstant("name", "Ljava/lang/String;", genClassName, "bsm", bsmDescriptor,
|
||||
S2 -> S2.add("DUMMY_ARG", PoolHelper::putString));
|
||||
}))
|
||||
.areturn()))
|
||||
.withMethod("indy_bsmIndy_condy_bsm", "()Ljava/lang/Object;", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.invokedynamic("name", "()Ljava/lang/String;", genClassName, "bsmIndy", bsmIndyDescriptor,
|
||||
S -> S.add(null, (P, v) -> {
|
||||
return P.putDynamicConstant("name", "Ljava/lang/String;", genClassName, "bsm", bsmDescriptor,
|
||||
S2 -> S2.add("DUMMY_ARG", PoolHelper::putString));
|
||||
}))
|
||||
.areturn()))
|
||||
.withMethod("indy_bsm_condy_bsm", "()Ljava/lang/Object;", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.invokedynamic("name", "()Ljava/lang/String;", genClassName, "bsm", bsmDescriptor,
|
||||
S -> S.add(null, (P, v) -> {
|
||||
return P.putDynamicConstant("name", "Ljava/lang/String;", genClassName, "bsm", bsmDescriptor,
|
||||
S2 -> S2.add("DUMMY_ARG", PoolHelper::putString));
|
||||
}))
|
||||
.areturn()))
|
||||
.build();
|
||||
|
||||
File f = new File(genClassName + ".class");
|
||||
if (f.getParentFile() != null) {
|
||||
f.getParentFile().mkdirs();
|
||||
}
|
||||
new FileOutputStream(f).write(byteArray);
|
||||
return byteArray;
|
||||
|
||||
}
|
||||
|
||||
static void test(Method m, Class<? extends Throwable>... ts) {
|
||||
Throwable caught = null;
|
||||
try {
|
||||
m.invoke(null);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
caught = t;
|
||||
}
|
||||
|
||||
if (caught == null) {
|
||||
Assert.fail("Throwable expected");
|
||||
}
|
||||
|
||||
String actualMessage = null;
|
||||
for (int i = 0; i < ts.length; i++) {
|
||||
actualMessage = caught.getMessage();
|
||||
Assert.assertNotNull(caught);
|
||||
Assert.assertTrue(ts[i].isAssignableFrom(caught.getClass()));
|
||||
caught = caught.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public void generateClass() throws Exception {
|
||||
byte[] ba = Base64.getDecoder().decode(CLASS_CondyNestedTest_Code);
|
||||
ClassLoader l = new ClassLoader(CondyReturnPrimitiveTest.class.getClassLoader()) {
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
if (name == "CondyNestedTest_Code") {
|
||||
return defineClass(name, ba, 0, ba.length);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
c = l.loadClass("CondyNestedTest_Code");
|
||||
}
|
||||
|
||||
/**
|
||||
* Testing an ldc of a dynamic constant, C say, with a BSM whose static
|
||||
* argument is C.
|
||||
*/
|
||||
@Test
|
||||
public void testCondyBsmCondyBsm() throws Exception {
|
||||
test("condy_bsm_condy_bsm", THROWABLES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Testing an invokedynamic with a BSM whose static argument is a constant
|
||||
* dynamic, C say, with a BSM whose static argument is C.
|
||||
*/
|
||||
@Test
|
||||
public void testIndyBsmIndyCondyBsm() throws Exception {
|
||||
test("indy_bsmIndy_condy_bsm", THROWABLES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Testing an invokedynamic with a BSM, B say, whose static argument is
|
||||
* a dynamic constant, C say, that uses BSM B.
|
||||
*/
|
||||
@Test
|
||||
public void testIndyBsmCondyBsm() throws Exception {
|
||||
test("indy_bsm_condy_bsm", THROWABLES);
|
||||
}
|
||||
|
||||
void test(String methodName, Class<? extends Throwable>... ts) throws Exception {
|
||||
Method m = c.getMethod(methodName);
|
||||
m.setAccessible(true);
|
||||
test(m, ts);
|
||||
}
|
||||
|
||||
}
|
264
test/jdk/java/lang/invoke/condy/CondyNestedTest_Code.jcod
Normal file
264
test/jdk/java/lang/invoke/condy/CondyNestedTest_Code.jcod
Normal file
@ -0,0 +1,264 @@
|
||||
class CondyNestedTest_Code {
|
||||
0xCAFEBABE;
|
||||
0; // minor version
|
||||
55; // version
|
||||
[] { // Constant Pool
|
||||
; // first element is empty
|
||||
Utf8 "java/lang/Object"; // #1
|
||||
class #1; // #2
|
||||
Utf8 "<init>"; // #3
|
||||
Utf8 "()V"; // #4
|
||||
NameAndType #3 #4; // #5
|
||||
Method #2 #5; // #6
|
||||
Utf8 "Code"; // #7
|
||||
Utf8 "java/lang/String"; // #8
|
||||
class #8; // #9
|
||||
Utf8 "intern"; // #10
|
||||
Utf8 "()Ljava/lang/String;"; // #11
|
||||
NameAndType #10 #11; // #12
|
||||
Method #9 #12; // #13
|
||||
Utf8 "condy_bsm_condy_bsm"; // #14
|
||||
String #14; // #15
|
||||
Utf8 "CondyNestedTest_Code"; // #16
|
||||
class #16; // #17
|
||||
Utf8 "()Ljava/lang/Object;"; // #18
|
||||
NameAndType #14 #18; // #19
|
||||
Method #17 #19; // #20
|
||||
Utf8 "indy_bsmIndy_condy_bsm"; // #21
|
||||
String #21; // #22
|
||||
NameAndType #21 #18; // #23
|
||||
Method #17 #23; // #24
|
||||
Utf8 "indy_bsm_condy_bsm"; // #25
|
||||
String #25; // #26
|
||||
NameAndType #25 #18; // #27
|
||||
Method #17 #27; // #28
|
||||
Utf8 "StackMapTable"; // #29
|
||||
Utf8 "main"; // #30
|
||||
Utf8 "([Ljava/lang/String;)V"; // #31
|
||||
Utf8 "java/lang/invoke/MethodType"; // #32
|
||||
class #32; // #33
|
||||
Utf8 "java/lang/invoke/ConstantCallSite"; // #34
|
||||
class #34; // #35
|
||||
Utf8 "java/lang/invoke/MethodHandles"; // #36
|
||||
class #36; // #37
|
||||
Utf8 "constant"; // #38
|
||||
Utf8 "(Ljava/lang/Class;Ljava/lang/Object;)Ljava/lang/invoke/MethodHandle;"; // #39
|
||||
NameAndType #38 #39; // #40
|
||||
Method #37 #40; // #41
|
||||
Utf8 "(Ljava/lang/invoke/MethodHandle;)V"; // #42
|
||||
NameAndType #3 #42; // #43
|
||||
Method #35 #43; // #44
|
||||
Utf8 "bsm"; // #45
|
||||
Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"; // #46
|
||||
Utf8 "bsmIndy"; // #47
|
||||
Utf8 "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/invoke/CallSite;"; // #48
|
||||
Utf8 "DUMMY_ARG"; // #49
|
||||
String #49; // #50
|
||||
NameAndType #45 #46; // #51
|
||||
Method #17 #51; // #52
|
||||
MethodHandle 6b #52; // #53
|
||||
Utf8 "name"; // #54
|
||||
Utf8 "Ljava/lang/String;"; // #55
|
||||
NameAndType #54 #55; // #56
|
||||
ConstantDynamic 0s #56; // #57
|
||||
ConstantDynamic 1s #56; // #58
|
||||
NameAndType #47 #48; // #59
|
||||
Method #17 #59; // #60
|
||||
MethodHandle 6b #60; // #61
|
||||
NameAndType #54 #11; // #62
|
||||
InvokeDynamic 2s #62; // #63
|
||||
InvokeDynamic 1s #62; // #64
|
||||
Utf8 "BootstrapMethods"; // #65
|
||||
} // Constant Pool
|
||||
|
||||
0x0000; // access
|
||||
#17;// this_cpx
|
||||
#2;// super_cpx
|
||||
|
||||
[] { // Interfaces
|
||||
} // Interfaces
|
||||
|
||||
[] { // fields
|
||||
} // fields
|
||||
|
||||
[] { // methods
|
||||
{ // Member
|
||||
0x0001; // access
|
||||
#3; // name_cpx
|
||||
#4; // sig_cpx
|
||||
[] { // Attributes
|
||||
Attr(#7) { // Code
|
||||
1; // max_stack
|
||||
1; // max_locals
|
||||
Bytes[]{
|
||||
0x2AB70006B1;
|
||||
};
|
||||
[] { // Traps
|
||||
} // end Traps
|
||||
[] { // Attributes
|
||||
} // Attributes
|
||||
} // end Code
|
||||
} // Attributes
|
||||
} // Member
|
||||
;
|
||||
{ // Member
|
||||
0x0009; // access
|
||||
#30; // name_cpx
|
||||
#31; // sig_cpx
|
||||
[] { // Attributes
|
||||
Attr(#7) { // Code
|
||||
2; // max_stack
|
||||
2; // max_locals
|
||||
Bytes[]{
|
||||
0x2A0332B6000D4C2B;
|
||||
0x120FA60007B80014;
|
||||
0xB12B1216A60007B8;
|
||||
0x0018B12B121AA600;
|
||||
0x07B8001CB1B1;
|
||||
};
|
||||
[] { // Traps
|
||||
} // end Traps
|
||||
[] { // Attributes
|
||||
Attr(#29) { // StackMapTable
|
||||
[] { //
|
||||
252b, 17, []z{O,9}; // append_frame 1
|
||||
9b; // same_frame
|
||||
9b; // same_frame
|
||||
}
|
||||
} // end StackMapTable
|
||||
} // Attributes
|
||||
} // end Code
|
||||
} // Attributes
|
||||
} // Member
|
||||
;
|
||||
{ // Member
|
||||
0x0009; // access
|
||||
#45; // name_cpx
|
||||
#46; // sig_cpx
|
||||
[] { // Attributes
|
||||
Attr(#7) { // Code
|
||||
4; // max_stack
|
||||
4; // max_locals
|
||||
Bytes[]{
|
||||
0x2CC10021039F0011;
|
||||
0xBB00235912092BB8;
|
||||
0x0029B7002CB02BB0;
|
||||
};
|
||||
[] { // Traps
|
||||
} // end Traps
|
||||
[] { // Attributes
|
||||
Attr(#29) { // StackMapTable
|
||||
[] { //
|
||||
22b; // same_frame
|
||||
}
|
||||
} // end StackMapTable
|
||||
} // Attributes
|
||||
} // end Code
|
||||
} // Attributes
|
||||
} // Member
|
||||
;
|
||||
{ // Member
|
||||
0x0009; // access
|
||||
#47; // name_cpx
|
||||
#48; // sig_cpx
|
||||
[] { // Attributes
|
||||
Attr(#7) { // Code
|
||||
4; // max_stack
|
||||
4; // max_locals
|
||||
Bytes[]{
|
||||
0xBB00235912092BB8;
|
||||
0x0029B7002CB0;
|
||||
};
|
||||
[] { // Traps
|
||||
} // end Traps
|
||||
[] { // Attributes
|
||||
} // Attributes
|
||||
} // end Code
|
||||
} // Attributes
|
||||
} // Member
|
||||
;
|
||||
{ // Member
|
||||
0x0009; // access
|
||||
#14; // name_cpx
|
||||
#18; // sig_cpx
|
||||
[] { // Attributes
|
||||
Attr(#7) { // Code
|
||||
1; // max_stack
|
||||
0; // max_locals
|
||||
Bytes[]{
|
||||
0x123AB0;
|
||||
};
|
||||
[] { // Traps
|
||||
} // end Traps
|
||||
[] { // Attributes
|
||||
} // Attributes
|
||||
} // end Code
|
||||
} // Attributes
|
||||
} // Member
|
||||
;
|
||||
{ // Member
|
||||
0x0009; // access
|
||||
#21; // name_cpx
|
||||
#18; // sig_cpx
|
||||
[] { // Attributes
|
||||
Attr(#7) { // Code
|
||||
1; // max_stack
|
||||
0; // max_locals
|
||||
Bytes[]{
|
||||
0xBA003F0000B0;
|
||||
};
|
||||
[] { // Traps
|
||||
} // end Traps
|
||||
[] { // Attributes
|
||||
} // Attributes
|
||||
} // end Code
|
||||
} // Attributes
|
||||
} // Member
|
||||
;
|
||||
{ // Member
|
||||
0x0009; // access
|
||||
#25; // name_cpx
|
||||
#18; // sig_cpx
|
||||
[] { // Attributes
|
||||
Attr(#7) { // Code
|
||||
1; // max_stack
|
||||
0; // max_locals
|
||||
Bytes[]{
|
||||
0xBA00400000B0;
|
||||
};
|
||||
[] { // Traps
|
||||
} // end Traps
|
||||
[] { // Attributes
|
||||
} // Attributes
|
||||
} // end Code
|
||||
} // Attributes
|
||||
} // Member
|
||||
} // methods
|
||||
|
||||
[] { // Attributes
|
||||
Attr(#65) { // BootstrapMethods
|
||||
[] { // bootstrap_methods
|
||||
{ // bootstrap_method
|
||||
#53; // bootstrap_method_ref
|
||||
[] { // bootstrap_arguments
|
||||
#50;
|
||||
} // bootstrap_arguments
|
||||
} // bootstrap_method
|
||||
;
|
||||
{ // bootstrap_method
|
||||
#53; // bootstrap_method_ref
|
||||
[] { // bootstrap_arguments
|
||||
#58;
|
||||
} // bootstrap_arguments
|
||||
} // bootstrap_method
|
||||
;
|
||||
{ // bootstrap_method
|
||||
#61; // bootstrap_method_ref
|
||||
[] { // bootstrap_arguments
|
||||
#58;
|
||||
} // bootstrap_arguments
|
||||
} // bootstrap_method
|
||||
}
|
||||
} // end BootstrapMethods
|
||||
} // Attributes
|
||||
} // end class CondyNestedTest_Code
|
285
test/jdk/java/lang/invoke/condy/CondyRepeatFailedResolution.java
Normal file
285
test/jdk/java/lang/invoke/condy/CondyRepeatFailedResolution.java
Normal file
@ -0,0 +1,285 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8186211
|
||||
* @summary Test basic invocation of multiple ldc's of the same dynamic constant that fail resolution
|
||||
* @requires os.arch == "x86_64"
|
||||
* @library /lib/testlibrary/bytecode /java/lang/invoke/common
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder
|
||||
* @run testng CondyRepeatFailedResolution
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyRepeatFailedResolution
|
||||
*/
|
||||
|
||||
import jdk.experimental.bytecode.BasicClassBuilder;
|
||||
import jdk.experimental.bytecode.Flag;
|
||||
import jdk.experimental.bytecode.TypedCodeBuilder;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
@Test
|
||||
public class CondyRepeatFailedResolution {
|
||||
// Counter used to determine if a given BSM is invoked more than once
|
||||
static int bsm_called = 0;
|
||||
|
||||
// Generated class with methods containing condy ldc
|
||||
Class<?> gc;
|
||||
|
||||
// Bootstrap method used to represent primitive values
|
||||
// that cannot be represented directly in the constant pool,
|
||||
// such as byte, and for completeness of testing primitive values
|
||||
// that can be represented directly, such as double or long that
|
||||
// take two slots
|
||||
public static Object intConversion(MethodHandles.Lookup l,
|
||||
String constantName,
|
||||
Class<?> constantType,
|
||||
int value) throws Throwable {
|
||||
++bsm_called;
|
||||
// replace constantName with a bogus value to trigger failed resolution
|
||||
constantName = "Foo";
|
||||
|
||||
switch (constantName) {
|
||||
case "B":
|
||||
return (byte) value;
|
||||
case "C":
|
||||
return (char) value;
|
||||
case "D":
|
||||
return (double) value;
|
||||
case "F":
|
||||
return (float) value;
|
||||
case "I":
|
||||
return value;
|
||||
case "J":
|
||||
return (long) value;
|
||||
case "S":
|
||||
return (short) value;
|
||||
case "Z":
|
||||
return value > 0;
|
||||
case "nullRef":
|
||||
return null;
|
||||
case "string":
|
||||
return "string";
|
||||
case "stringArray":
|
||||
return new String[]{"string", "string"};
|
||||
default:
|
||||
throw new BootstrapMethodError("Failure to generate a dynamic constant");
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public void generateClass() throws Exception {
|
||||
String genClassName = CondyRepeatFailedResolution.class.getSimpleName() + "$Code";
|
||||
String bsmClassName = CondyRepeatFailedResolution.class.getCanonicalName().replace('.', '/');
|
||||
String bsmMethodName = "intConversion";
|
||||
String bsmDescriptor = MethodType.methodType(Object.class, MethodHandles.Lookup.class,
|
||||
String.class, Class.class, int.class).toMethodDescriptorString();
|
||||
|
||||
byte[] byteArray = new BasicClassBuilder(genClassName, 55, 0)
|
||||
.withSuperclass("java/lang/Object")
|
||||
.withMethod("<init>", "()V", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.aload_0().invokespecial("java/lang/Object", "<init>", "()V", false).return_()
|
||||
))
|
||||
.withMethod("B", "()B", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("B", "B", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Byte.MAX_VALUE))
|
||||
.ireturn()
|
||||
))
|
||||
.withMethod("C", "()C", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("C", "C", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Character.MAX_VALUE))
|
||||
.ireturn()
|
||||
))
|
||||
.withMethod("D", "()D", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("D", "D", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.dreturn()
|
||||
))
|
||||
.withMethod("D_AsType", "()D", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("I", "D", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.dreturn()
|
||||
))
|
||||
.withMethod("F", "()F", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("F", "F", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.freturn()
|
||||
))
|
||||
.withMethod("F_AsType", "()F", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("I", "F", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.freturn()
|
||||
))
|
||||
.withMethod("I", "()I", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("I", "I", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.ireturn()
|
||||
))
|
||||
.withMethod("J", "()J", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("J", "J", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.lreturn()
|
||||
))
|
||||
.withMethod("J_AsType", "()J", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("I", "J", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.lreturn()
|
||||
))
|
||||
.withMethod("S", "()S", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("S", "S", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Short.MAX_VALUE))
|
||||
.ireturn()
|
||||
))
|
||||
.withMethod("Z_F", "()Z", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("Z", "Z", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(0))
|
||||
.ireturn()
|
||||
))
|
||||
.withMethod("Z_T", "()Z", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("Z", "Z", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(1))
|
||||
.ireturn()
|
||||
))
|
||||
.withMethod("null", "()Ljava/lang/Object;", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("nullRef", "Ljava/lang/Object;", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.areturn()
|
||||
))
|
||||
.withMethod("string", "()Ljava/lang/String;", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("string", "Ljava/lang/String;", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.areturn()
|
||||
))
|
||||
.withMethod("stringArray", "()[Ljava/lang/String;", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("stringArray", "[Ljava/lang/String;", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.areturn()
|
||||
))
|
||||
.build();
|
||||
|
||||
// For debugging purposes
|
||||
new FileOutputStream(new File(genClassName + ".class")).write(byteArray);
|
||||
|
||||
gc = MethodHandles.lookup().defineClass(byteArray);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrimitives() throws Exception {
|
||||
testConstants();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefs() throws Exception {
|
||||
testConstant("string", "string");
|
||||
testConstant("stringArray", new String[]{"string", "string"});
|
||||
}
|
||||
|
||||
void testConstants() throws Exception {
|
||||
// Note: for the _asType methods the BSM returns an int which is
|
||||
// then converted by an asType transformation
|
||||
|
||||
testConstant("B", Byte.MAX_VALUE);
|
||||
testConstant("C", Character.MAX_VALUE);
|
||||
testConstant("D", (double) Integer.MAX_VALUE);
|
||||
testConstant("D_AsType", (double) Integer.MAX_VALUE);
|
||||
testConstant("F", (float) Integer.MAX_VALUE);
|
||||
testConstant("F_AsType", (float) Integer.MAX_VALUE);
|
||||
testConstant("I", Integer.MAX_VALUE);
|
||||
testConstant("J", (long) Integer.MAX_VALUE);
|
||||
testConstant("J_AsType", (long) Integer.MAX_VALUE);
|
||||
testConstant("S", Short.MAX_VALUE);
|
||||
testConstant("Z_F", false);
|
||||
testConstant("Z_T", true);
|
||||
testConstant("null", null);
|
||||
}
|
||||
|
||||
void testConstant(String name, Object expected) throws Exception {
|
||||
Method m = gc.getDeclaredMethod(name);
|
||||
|
||||
bsm_called = 0;
|
||||
try {
|
||||
Object r1 = m.invoke(null);
|
||||
Assert.fail("InvocationTargetException expected to be thrown after first invocation");
|
||||
} catch (InvocationTargetException e1) {
|
||||
// bsm_called should have been incremented prior to the exception
|
||||
Assert.assertEquals(bsm_called, 1);
|
||||
Assert.assertTrue(e1.getCause() instanceof BootstrapMethodError);
|
||||
// Try invoking method again to ensure that the bootstrap
|
||||
// method is not invoked twice and a resolution failure
|
||||
// results.
|
||||
try {
|
||||
Object r2 = m.invoke(null);
|
||||
Assert.fail("InvocationTargetException expected to be thrown after second invocation");
|
||||
} catch (InvocationTargetException e2) {
|
||||
// bsm_called should remain at 1 since the bootstrap
|
||||
// method should not have been invoked.
|
||||
Assert.assertEquals(bsm_called, 1);
|
||||
Assert.assertTrue(e2.getCause() instanceof BootstrapMethodError);
|
||||
} catch (Throwable t2) {
|
||||
Assert.fail("InvocationTargetException expected to be thrown");
|
||||
}
|
||||
} catch (Throwable t1) {
|
||||
Assert.fail("InvocationTargetException expected to be thrown");
|
||||
}
|
||||
}
|
||||
}
|
267
test/jdk/java/lang/invoke/condy/CondyReturnPrimitiveTest.java
Normal file
267
test/jdk/java/lang/invoke/condy/CondyReturnPrimitiveTest.java
Normal file
@ -0,0 +1,267 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8186046
|
||||
* @summary Test for condy BSMs returning primitive values or null
|
||||
* @requires os.arch == "x86_64"
|
||||
* @library /lib/testlibrary/bytecode
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder
|
||||
* @run testng CondyReturnPrimitiveTest
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyReturnPrimitiveTest
|
||||
*/
|
||||
|
||||
import jdk.experimental.bytecode.BasicClassBuilder;
|
||||
import jdk.experimental.bytecode.Flag;
|
||||
import jdk.experimental.bytecode.TypedCodeBuilder;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@Test
|
||||
public class CondyReturnPrimitiveTest {
|
||||
// Counter for number of BSM calls
|
||||
// Use of an AtomicInteger is not strictly necessary in this test
|
||||
// since the BSM is not be called concurrently, but in general
|
||||
// a BSM can be called concurrently for linking different or the *same*
|
||||
// constant so care should be taken if a BSM operates on shared state
|
||||
static final AtomicInteger callCount = new AtomicInteger();
|
||||
// Generated class with methods containing condy ldc
|
||||
Class<?> gc;
|
||||
|
||||
// Bootstrap method used to represent primitive values
|
||||
// that cannot be represented directly in the constant pool,
|
||||
// such as byte, and for completeness of testing primitive values
|
||||
// that can be represented directly, such as double or long that
|
||||
// take two slots
|
||||
public static Object intConversion(MethodHandles.Lookup l,
|
||||
String constantName,
|
||||
Class<?> constantType,
|
||||
int value) {
|
||||
callCount.getAndIncrement();
|
||||
|
||||
switch (constantName) {
|
||||
case "B":
|
||||
return (byte) value;
|
||||
case "C":
|
||||
return (char) value;
|
||||
case "D":
|
||||
return (double) value;
|
||||
case "F":
|
||||
return (float) value;
|
||||
case "I":
|
||||
return value;
|
||||
case "J":
|
||||
return (long) value;
|
||||
case "S":
|
||||
return (short) value;
|
||||
case "Z":
|
||||
return value > 0;
|
||||
case "nullRef":
|
||||
return null;
|
||||
case "string":
|
||||
return "string";
|
||||
case "stringArray":
|
||||
return new String[]{"string", "string"};
|
||||
default:
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public void generateClass() throws Exception {
|
||||
String genClassName = CondyReturnPrimitiveTest.class.getSimpleName() + "$Code";
|
||||
String bsmClassName = CondyReturnPrimitiveTest.class.getCanonicalName().replace('.', '/');
|
||||
String bsmMethodName = "intConversion";
|
||||
String bsmDescriptor = MethodType.methodType(Object.class, MethodHandles.Lookup.class,
|
||||
String.class, Class.class, int.class).toMethodDescriptorString();
|
||||
|
||||
byte[] byteArray = new BasicClassBuilder(genClassName, 55, 0)
|
||||
.withSuperclass("java/lang/Object")
|
||||
.withMethod("<init>", "()V", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.aload_0().invokespecial("java/lang/Object", "<init>", "()V", false).return_()
|
||||
))
|
||||
.withMethod("B", "()B", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("B", "B", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Byte.MAX_VALUE))
|
||||
.ireturn()
|
||||
))
|
||||
.withMethod("C", "()C", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("C", "C", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Character.MAX_VALUE))
|
||||
.ireturn()
|
||||
))
|
||||
.withMethod("D", "()D", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("D", "D", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.dreturn()
|
||||
))
|
||||
.withMethod("D_AsType", "()D", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("I", "D", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.dreturn()
|
||||
))
|
||||
.withMethod("F", "()F", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("F", "F", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.freturn()
|
||||
))
|
||||
.withMethod("F_AsType", "()F", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("I", "F", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.freturn()
|
||||
))
|
||||
.withMethod("I", "()I", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("I", "I", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.ireturn()
|
||||
))
|
||||
.withMethod("J", "()J", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("J", "J", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.lreturn()
|
||||
))
|
||||
.withMethod("J_AsType", "()J", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("I", "J", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.lreturn()
|
||||
))
|
||||
.withMethod("S", "()S", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("S", "S", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Short.MAX_VALUE))
|
||||
.ireturn()
|
||||
))
|
||||
.withMethod("Z_F", "()Z", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("Z", "Z", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(0))
|
||||
.ireturn()
|
||||
))
|
||||
.withMethod("Z_T", "()Z", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("Z", "Z", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(1))
|
||||
.ireturn()
|
||||
))
|
||||
.withMethod("null", "()Ljava/lang/Object;", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("nullRef", "Ljava/lang/Object;", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.areturn()
|
||||
))
|
||||
.withMethod("string", "()Ljava/lang/String;", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("string", "Ljava/lang/String;", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.areturn()
|
||||
))
|
||||
.withMethod("stringArray", "()[Ljava/lang/String;", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.ldc("stringArray", "[Ljava/lang/String;", bsmClassName, bsmMethodName, bsmDescriptor,
|
||||
S -> S.add(Integer.MAX_VALUE))
|
||||
.areturn()
|
||||
))
|
||||
.build();
|
||||
|
||||
// For debugging purposes
|
||||
new FileOutputStream(new File(genClassName + ".class")).write(byteArray);
|
||||
|
||||
gc = MethodHandles.lookup().defineClass(byteArray);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrimitives() throws Exception {
|
||||
testConstants();
|
||||
int expectedCallCount = callCount.get();
|
||||
|
||||
// Ensure when run a second time that the bootstrap method is not
|
||||
// invoked and the constants are cached
|
||||
testConstants();
|
||||
Assert.assertEquals(callCount.get(), expectedCallCount);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefs() throws Exception {
|
||||
testConstant("string", "string");
|
||||
testConstant("stringArray", new String[]{"string", "string"});
|
||||
}
|
||||
|
||||
void testConstants() throws Exception {
|
||||
// Note: for the _asType methods the BSM returns an int which is
|
||||
// then converted by an asType transformation
|
||||
|
||||
testConstant("B", Byte.MAX_VALUE);
|
||||
testConstant("C", Character.MAX_VALUE);
|
||||
testConstant("D", (double) Integer.MAX_VALUE);
|
||||
testConstant("D_AsType", (double) Integer.MAX_VALUE);
|
||||
testConstant("F", (float) Integer.MAX_VALUE);
|
||||
testConstant("F_AsType", (float) Integer.MAX_VALUE);
|
||||
testConstant("I", Integer.MAX_VALUE);
|
||||
testConstant("J", (long) Integer.MAX_VALUE);
|
||||
testConstant("J_AsType", (long) Integer.MAX_VALUE);
|
||||
testConstant("S", Short.MAX_VALUE);
|
||||
testConstant("Z_F", false);
|
||||
testConstant("Z_T", true);
|
||||
testConstant("null", null);
|
||||
}
|
||||
|
||||
void testConstant(String name, Object expected) throws Exception {
|
||||
Method m = gc.getDeclaredMethod(name);
|
||||
Assert.assertEquals(m.invoke(null), expected);
|
||||
}
|
||||
}
|
201
test/jdk/java/lang/invoke/condy/CondyStaticArgumentsTest.java
Normal file
201
test/jdk/java/lang/invoke/condy/CondyStaticArgumentsTest.java
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8186046
|
||||
* @summary Test bootstrap arguments for condy
|
||||
* @library /lib/testlibrary/bytecode /java/lang/invoke/common
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
|
||||
* @run testng CondyStaticArgumentsTest
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyStaticArgumentsTest
|
||||
*/
|
||||
|
||||
import jdk.experimental.bytecode.PoolHelper;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
import test.java.lang.invoke.lib.InstructionHelper;
|
||||
|
||||
import java.lang.invoke.ConstantCallSite;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandleInfo;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
|
||||
public class CondyStaticArgumentsTest {
|
||||
static final MethodHandles.Lookup L = MethodHandles.lookup();
|
||||
|
||||
static class BSMInfo {
|
||||
final String methodName;
|
||||
final MethodHandle handle;
|
||||
final String descriptor;
|
||||
|
||||
BSMInfo(String name) {
|
||||
methodName = name;
|
||||
|
||||
Method m = Stream.of(CondyStaticArgumentsTest.class.getDeclaredMethods())
|
||||
.filter(x -> x.getName().equals(methodName)).findFirst()
|
||||
.get();
|
||||
try {
|
||||
handle = MethodHandles.lookup().unreflect(m);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
descriptor = handle.type().toMethodDescriptorString();
|
||||
}
|
||||
|
||||
static BSMInfo of(String name) {
|
||||
return new BSMInfo(name);
|
||||
}
|
||||
}
|
||||
|
||||
static String basicArgs(MethodHandles.Lookup l, String name, Class<?> type,
|
||||
int i, long j, float f, double d,
|
||||
Class<?> c, String s,
|
||||
MethodType mt, MethodHandle mh) {
|
||||
return new StringJoiner("-")
|
||||
.add(name)
|
||||
.add(type.getSimpleName())
|
||||
.add(Integer.toString(i))
|
||||
.add(Long.toString(j))
|
||||
.add(Float.toString(f))
|
||||
.add(Double.toString(d))
|
||||
.add(c.getSimpleName())
|
||||
.add(s)
|
||||
.add(mt.toString())
|
||||
.add(Integer.toString(mh.type().parameterCount()))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicArgs() throws Throwable {
|
||||
BSMInfo bi = BSMInfo.of("basicArgs");
|
||||
MethodHandleInfo mhi = MethodHandles.lookup().revealDirect(bi.handle);
|
||||
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "constant-name", String.class,
|
||||
bi.methodName, bi.handle.type(),
|
||||
S -> S.add(1).add(2L).add(3.0f).add(4.0d)
|
||||
.add("java/lang/Number", PoolHelper::putClass)
|
||||
.add("something", PoolHelper::putString)
|
||||
.add("(IJFD)V", PoolHelper::putMethodType)
|
||||
.add(mhi, (P, Z) -> {
|
||||
return P.putHandle(mhi.getReferenceKind(), "CondyStaticArgumentsTest", mhi.getName(), bi.descriptor);
|
||||
}));
|
||||
|
||||
Assert.assertEquals(mh.invoke(), "constant-name-String-1-2-3.0-4.0-Number-something-(int,long,float,double)void-11");
|
||||
}
|
||||
|
||||
|
||||
static MathContext mathContext(MethodHandles.Lookup l, String value, Class<?> type) {
|
||||
switch (value) {
|
||||
case "UNLIMITED":
|
||||
return MathContext.UNLIMITED;
|
||||
case "DECIMAL32":
|
||||
return MathContext.DECIMAL32;
|
||||
case "DECIMAL64":
|
||||
return MathContext.DECIMAL64;
|
||||
case "DECIMAL128":
|
||||
return MathContext.DECIMAL128;
|
||||
default:
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
static BigDecimal bigDecimal(MethodHandles.Lookup l, String name, Class<?> type,
|
||||
String value, MathContext mc) {
|
||||
return new BigDecimal(value, mc);
|
||||
}
|
||||
|
||||
static String condyWithCondy(MethodHandles.Lookup l, String name, Class<?> type,
|
||||
BigDecimal d) {
|
||||
return new StringJoiner("-")
|
||||
.add(name)
|
||||
.add(type.getSimpleName())
|
||||
.add(d.toString())
|
||||
.add(Integer.toString(d.precision()))
|
||||
.toString();
|
||||
}
|
||||
|
||||
static <E> int bigDecimalPoolHelper(String value, String mc, PoolHelper<String, String, E> P) {
|
||||
BSMInfo bi = BSMInfo.of("bigDecimal");
|
||||
return P.putDynamicConstant("big-decimal", "Ljava/math/BigDecimal;", InstructionHelper.csym(L.lookupClass()), bi.methodName, bi.descriptor,
|
||||
S -> S.add(value, PoolHelper::putString)
|
||||
.add(mc, (P2, s) -> {
|
||||
return mathContextPoolHelper(s, P2);
|
||||
}));
|
||||
}
|
||||
|
||||
static <E> int mathContextPoolHelper(String mc, PoolHelper<String, String, E> P) {
|
||||
BSMInfo bi = BSMInfo.of("mathContext");
|
||||
return P.putDynamicConstant(mc, "Ljava/math/MathContext;", InstructionHelper.csym(L.lookupClass()), bi.methodName, bi.descriptor,
|
||||
S -> {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCondyWithCondy() throws Throwable {
|
||||
BSMInfo bi = BSMInfo.of("condyWithCondy");
|
||||
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "big-decimal-math-context", String.class,
|
||||
bi.methodName, bi.handle.type(),
|
||||
S -> S.add(null, (P, v) -> {
|
||||
return bigDecimalPoolHelper("3.14159265358979323846", "DECIMAL32", P);
|
||||
}));
|
||||
Assert.assertEquals(mh.invoke(), "big-decimal-math-context-String-3.141593-7");
|
||||
}
|
||||
|
||||
|
||||
static ConstantCallSite indyWithCondy(MethodHandles.Lookup l, String name, MethodType type,
|
||||
BigDecimal d) {
|
||||
String s = new StringJoiner("-")
|
||||
.add(name)
|
||||
.add(type.toMethodDescriptorString())
|
||||
.add(d.toString())
|
||||
.add(Integer.toString(d.precision()))
|
||||
.toString();
|
||||
return new ConstantCallSite(MethodHandles.constant(String.class, s));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIndyWithCondy() throws Throwable {
|
||||
BSMInfo bi = BSMInfo.of("indyWithCondy");
|
||||
|
||||
MethodHandle mh = InstructionHelper.invokedynamic(
|
||||
L, "big-decimal-math-context", methodType(String.class),
|
||||
bi.methodName, bi.handle.type(),
|
||||
S -> S.add(null, (P, v) -> {
|
||||
return bigDecimalPoolHelper("3.14159265358979323846", "DECIMAL32", P);
|
||||
}));
|
||||
Assert.assertEquals(mh.invoke(), "big-decimal-math-context-()Ljava/lang/String;-3.141593-7");
|
||||
}
|
||||
}
|
93
test/jdk/java/lang/invoke/condy/CondyTypeValidationTest.java
Normal file
93
test/jdk/java/lang/invoke/condy/CondyTypeValidationTest.java
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8186046
|
||||
* @summary Test invalid name in name and type
|
||||
* @library /lib/testlibrary/bytecode /java/lang/invoke/common
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyTypeValidationTest
|
||||
*/
|
||||
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
import test.java.lang.invoke.lib.InstructionHelper;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
|
||||
public class CondyTypeValidationTest {
|
||||
static final MethodHandles.Lookup L = MethodHandles.lookup();
|
||||
static final String BSM_TYPE = methodType(Object.class, MethodHandles.Lookup.class, String.class, Object.class)
|
||||
.toMethodDescriptorString();
|
||||
|
||||
@DataProvider
|
||||
public Object[][] invalidTypesProvider() throws Exception {
|
||||
return Stream.of(
|
||||
// ByteCode API checks for the following invalid types
|
||||
// "",
|
||||
// "[",
|
||||
// "A",
|
||||
// "a",
|
||||
"L/java/lang/Object",
|
||||
Stream.generate(() -> "[").limit(256).collect(Collectors.joining("", "", "I")))
|
||||
.map(e -> new Object[]{e}).toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "invalidTypesProvider", expectedExceptions = ClassFormatError.class)
|
||||
public void testInvalidTypes(String type) throws Exception {
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", type,
|
||||
"bsm", BSM_TYPE,
|
||||
S -> {
|
||||
});
|
||||
}
|
||||
|
||||
@DataProvider
|
||||
public Object[][] validTypesProvider() throws Exception {
|
||||
List<String> t = new ArrayList<>(List.of("B", "C", "D", "F", "I", "J", "Ljava/lang/Object;", "S", "Z"));
|
||||
int l = t.size();
|
||||
for (int i = 0; i < l; i++) {
|
||||
t.add("[" + t.get(i));
|
||||
}
|
||||
|
||||
return t.stream()
|
||||
.map(e -> new Object[]{e}).toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "validTypesProvider")
|
||||
public void testValidTypes(String type) throws Exception {
|
||||
MethodHandle mh = InstructionHelper.ldcDynamicConstant(
|
||||
L, "name", type,
|
||||
"bsm", BSM_TYPE,
|
||||
S -> {
|
||||
});
|
||||
}
|
||||
}
|
170
test/jdk/java/lang/invoke/condy/CondyWithGarbageTest.java
Normal file
170
test/jdk/java/lang/invoke/condy/CondyWithGarbageTest.java
Normal file
@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8186046
|
||||
* @summary Stress test ldc to ensure HotSpot correctly manages oop maps
|
||||
* @library /lib/testlibrary/bytecode /java/lang/invoke/common
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
|
||||
* @run testng CondyWithGarbageTest
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyWithGarbageTest
|
||||
*/
|
||||
|
||||
|
||||
import jdk.experimental.bytecode.BasicClassBuilder;
|
||||
import jdk.experimental.bytecode.Flag;
|
||||
import jdk.experimental.bytecode.TypedCodeBuilder;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
import static test.java.lang.invoke.lib.InstructionHelper.cref;
|
||||
import static test.java.lang.invoke.lib.InstructionHelper.csym;
|
||||
|
||||
public class CondyWithGarbageTest {
|
||||
static final MethodHandles.Lookup L = MethodHandles.lookup();
|
||||
|
||||
@Test
|
||||
public void testString() throws Throwable {
|
||||
MethodHandle mh = lcdStringBasher();
|
||||
int l = 0;
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
l += +((String) mh.invoke()).length();
|
||||
}
|
||||
Assert.assertTrue(l > 0);
|
||||
}
|
||||
|
||||
public static Object bsmString(MethodHandles.Lookup l,
|
||||
String constantName,
|
||||
Class<?> constantType) {
|
||||
return new StringBuilder(constantName).toString();
|
||||
}
|
||||
|
||||
static MethodHandle lcdStringBasher() throws Exception {
|
||||
byte[] byteArray = new BasicClassBuilder(csym(L.lookupClass()) + "$Code$String", 55, 0)
|
||||
.withSuperclass("java/lang/Object")
|
||||
.withMethod("<init>", "()V", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.aload_0().invokespecial("java/lang/Object", "<init>", "()V", false).return_()
|
||||
))
|
||||
.withMethod("m", "()" + cref(String.class), M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C -> {
|
||||
C.new_(csym(StringBuilder.class))
|
||||
.dup()
|
||||
.invokespecial(csym(StringBuilder.class), "<init>", "()V", false)
|
||||
.astore_0();
|
||||
|
||||
for (int i = 10; i < 100; i++) {
|
||||
ldcString(C, Integer.toString(i));
|
||||
C.astore_1().aload_0().aload_1();
|
||||
C.invokevirtual(csym(StringBuilder.class), "append", methodType(StringBuilder.class, String.class).toMethodDescriptorString(), false);
|
||||
C.pop();
|
||||
}
|
||||
|
||||
C.aload_0();
|
||||
C.invokevirtual(csym(StringBuilder.class), "toString", methodType(String.class).toMethodDescriptorString(), false);
|
||||
C.areturn();
|
||||
}
|
||||
))
|
||||
.build();
|
||||
|
||||
Class<?> gc = L.defineClass(byteArray);
|
||||
return L.findStatic(gc, "m", methodType(String.class));
|
||||
}
|
||||
|
||||
static void ldcString(TypedCodeBuilder<String, String, byte[], ?> C, String name) {
|
||||
C.ldc(name, cref(String.class),
|
||||
csym(L.lookupClass()),
|
||||
"bsmString",
|
||||
methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class).toMethodDescriptorString(),
|
||||
S -> {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testStringArray() throws Throwable {
|
||||
MethodHandle mh = lcdStringArrayBasher();
|
||||
int l = 0;
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
l += +((String) mh.invoke()).length();
|
||||
}
|
||||
Assert.assertTrue(l > 0);
|
||||
}
|
||||
|
||||
public static Object bsmStringArray(MethodHandles.Lookup l,
|
||||
String constantName,
|
||||
Class<?> constantType) {
|
||||
return new String[]{new StringBuilder(constantName).toString()};
|
||||
}
|
||||
|
||||
static MethodHandle lcdStringArrayBasher() throws Exception {
|
||||
byte[] byteArray = new BasicClassBuilder(csym(L.lookupClass()) + "$Code$StringArray", 55, 0)
|
||||
.withSuperclass("java/lang/Object")
|
||||
.withMethod("<init>", "()V", M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC)
|
||||
.withCode(TypedCodeBuilder::new, C ->
|
||||
C.aload_0().invokespecial("java/lang/Object", "<init>", "()V", false).return_()
|
||||
))
|
||||
.withMethod("m", "()" + cref(String.class), M ->
|
||||
M.withFlags(Flag.ACC_PUBLIC, Flag.ACC_STATIC)
|
||||
.withCode(TypedCodeBuilder::new, C -> {
|
||||
C.new_(csym(StringBuilder.class))
|
||||
.dup()
|
||||
.invokespecial(csym(StringBuilder.class), "<init>", "()V", false)
|
||||
.astore_0();
|
||||
|
||||
for (int i = 10; i < 100; i++) {
|
||||
ldcStringArray(C, Integer.toString(i));
|
||||
C.bipush(0).aaload().astore_1();
|
||||
C.aload_0().aload_1();
|
||||
C.invokevirtual(csym(StringBuilder.class), "append", methodType(StringBuilder.class, String.class).toMethodDescriptorString(), false);
|
||||
C.pop();
|
||||
}
|
||||
|
||||
C.aload_0();
|
||||
C.invokevirtual(csym(StringBuilder.class), "toString", methodType(String.class).toMethodDescriptorString(), false);
|
||||
C.areturn();
|
||||
}
|
||||
))
|
||||
.build();
|
||||
|
||||
Class<?> gc = L.defineClass(byteArray);
|
||||
return L.findStatic(gc, "m", methodType(String.class));
|
||||
}
|
||||
|
||||
static void ldcStringArray(TypedCodeBuilder<String, String, byte[], ?> C, String name) {
|
||||
C.ldc(name, cref(String[].class),
|
||||
csym(L.lookupClass()),
|
||||
"bsmStringArray",
|
||||
methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class).toMethodDescriptorString(),
|
||||
S -> {
|
||||
});
|
||||
}
|
||||
}
|
175
test/jdk/java/lang/invoke/condy/CondyWrongType.java
Normal file
175
test/jdk/java/lang/invoke/condy/CondyWrongType.java
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8186046
|
||||
* @summary Test bootstrap methods returning the wrong type
|
||||
* @requires os.arch == "x86_64"
|
||||
* @library /lib/testlibrary/bytecode /java/lang/invoke/common
|
||||
* @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
|
||||
* @run testng CondyWrongType
|
||||
* @run testng/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyWrongType
|
||||
*/
|
||||
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
import test.java.lang.invoke.lib.InstructionHelper;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.WrongMethodTypeException;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.lang.invoke.MethodType.methodType;
|
||||
|
||||
public class CondyWrongType {
|
||||
|
||||
@DataProvider
|
||||
public Object[][] primitivesProvider() throws Exception {
|
||||
Map<String, Class<?>> typeMap = Map.of(
|
||||
"B", byte.class,
|
||||
"C", char.class,
|
||||
"D", double.class,
|
||||
"F", float.class,
|
||||
"I", int.class,
|
||||
"J", long.class,
|
||||
"S", short.class,
|
||||
"Z", boolean.class
|
||||
);
|
||||
|
||||
List<Object[]> cases = new ArrayList<>();
|
||||
for (String name : typeMap.keySet()) {
|
||||
MethodHandle zero = MethodHandles.zero(typeMap.get(name));
|
||||
for (String type : typeMap.keySet()) {
|
||||
// Use asType transformation to detect if primitive conversion
|
||||
// is supported from the BSM value type to the dynamic constant type
|
||||
boolean pass = true;
|
||||
try {
|
||||
zero.asType(MethodType.methodType(typeMap.get(type)));
|
||||
}
|
||||
catch (WrongMethodTypeException e) {
|
||||
pass = false;
|
||||
}
|
||||
cases.add(new Object[] { name, type, pass});
|
||||
}
|
||||
}
|
||||
|
||||
return cases.stream().toArray(Object[][]::new);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "primitivesProvider")
|
||||
public void testPrimitives(String name, String type, boolean pass) {
|
||||
test(name, type, pass);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReferences() {
|
||||
test("String", "Ljava/math/BigDecimal;", false);
|
||||
test("BigDecimal", "Ljava/lang/String;", false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReferenceAndPrimitives() {
|
||||
test("String", "B", false);
|
||||
test("String", "C", false);
|
||||
test("String", "D", false);
|
||||
test("String", "F", false);
|
||||
test("String", "I", false);
|
||||
test("String", "J", false);
|
||||
test("String", "S", false);
|
||||
test("String", "Z", false);
|
||||
}
|
||||
|
||||
static void test(String name, String type, boolean pass) {
|
||||
MethodHandle mh = caster(name, type);
|
||||
Throwable caught = null;
|
||||
try {
|
||||
mh.invoke();
|
||||
}
|
||||
catch (Throwable t) {
|
||||
caught = t;
|
||||
}
|
||||
|
||||
if (caught == null) {
|
||||
if (pass) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
Assert.fail("Throwable expected");
|
||||
}
|
||||
}
|
||||
else if (pass) {
|
||||
Assert.fail("Throwable not expected");
|
||||
}
|
||||
|
||||
Assert.assertTrue(BootstrapMethodError.class.isAssignableFrom(caught.getClass()));
|
||||
caught = caught.getCause();
|
||||
Assert.assertNotNull(caught);
|
||||
Assert.assertTrue(ClassCastException.class.isAssignableFrom(caught.getClass()));
|
||||
}
|
||||
|
||||
static Object bsm(MethodHandles.Lookup l, String name, Class<?> type) {
|
||||
switch (name) {
|
||||
case "B":
|
||||
return (byte) 1;
|
||||
case "C":
|
||||
return 'A';
|
||||
case "D":
|
||||
return 1.0;
|
||||
case "F":
|
||||
return 1.0f;
|
||||
case "I":
|
||||
return 1;
|
||||
case "J":
|
||||
return 1L;
|
||||
case "S":
|
||||
return (short) 1;
|
||||
case "Z":
|
||||
return true;
|
||||
case "String":
|
||||
return "string";
|
||||
case "BigDecimal":
|
||||
return BigDecimal.ONE;
|
||||
default:
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
static MethodHandle caster(String name, String type) {
|
||||
try {
|
||||
return InstructionHelper.ldcDynamicConstant(
|
||||
MethodHandles.lookup(),
|
||||
name, type,
|
||||
"bsm", methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class).toMethodDescriptorString(),
|
||||
S -> { });
|
||||
} catch (Exception e) {
|
||||
throw new Error(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.experimental.bytecode;
|
||||
|
||||
/**
|
||||
* Base builder.
|
||||
*
|
||||
* @param <S> the type of the symbol representation
|
||||
* @param <T> the type of type descriptors representation
|
||||
* @param <E> the type of pool entries
|
||||
* @param <D> the type of this builder
|
||||
*/
|
||||
public class AbstractBuilder<S, T, E, D extends AbstractBuilder<S, T, E, D>> {
|
||||
/**
|
||||
* The helper to build the constant pool.
|
||||
*/
|
||||
protected final PoolHelper<S, T, E> poolHelper;
|
||||
|
||||
/**
|
||||
* The helper to use to manipulate type descriptors.
|
||||
*/
|
||||
protected final TypeHelper<S, T> typeHelper;
|
||||
|
||||
/**
|
||||
* Create a builder.
|
||||
*
|
||||
* @param poolHelper the helper to build the constant pool
|
||||
* @param typeHelper the helper to use to manipulate type descriptors
|
||||
*/
|
||||
AbstractBuilder(PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
|
||||
this.poolHelper = poolHelper;
|
||||
this.typeHelper = typeHelper;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
D thisBuilder() {
|
||||
return (D) this;
|
||||
}
|
||||
}
|
@ -0,0 +1,338 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.experimental.bytecode;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.ToIntBiFunction;
|
||||
|
||||
public class AnnotationsBuilder<S, T, E> extends AbstractBuilder<S, T, E, AnnotationsBuilder<S, T, E>> {
|
||||
|
||||
GrowableByteBuffer annoAttribute;
|
||||
int nannos;
|
||||
|
||||
AnnotationsBuilder(PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
|
||||
super(poolHelper, typeHelper);
|
||||
this.annoAttribute = new GrowableByteBuffer();
|
||||
annoAttribute.writeChar(0);
|
||||
}
|
||||
|
||||
public enum Kind {
|
||||
RUNTIME_VISIBLE,
|
||||
RUNTIME_INVISIBLE;
|
||||
}
|
||||
|
||||
enum Tag {
|
||||
B('B'),
|
||||
C('C'),
|
||||
D('D'),
|
||||
F('F'),
|
||||
I('I'),
|
||||
J('J'),
|
||||
S('S'),
|
||||
Z('Z'),
|
||||
STRING('s'),
|
||||
ENUM('e'),
|
||||
CLASS('c'),
|
||||
ANNO('@'),
|
||||
ARRAY('[');
|
||||
|
||||
char tagChar;
|
||||
|
||||
Tag(char tagChar) {
|
||||
this.tagChar = tagChar;
|
||||
}
|
||||
}
|
||||
|
||||
AnnotationsBuilder<S, T, E> withAnnotation(T annoType, Consumer<? super AnnotationElementBuilder> annotationBuilder) {
|
||||
annoAttribute.writeChar(poolHelper.putType(annoType));
|
||||
int offset = annoAttribute.offset;
|
||||
annoAttribute.writeChar(0);
|
||||
if (annotationBuilder != null) {
|
||||
AnnotationElementBuilder _builder = new AnnotationElementBuilder();
|
||||
int nelems = _builder.withElements(annotationBuilder);
|
||||
patchCharAt(offset, nelems);
|
||||
}
|
||||
nannos++;
|
||||
return this;
|
||||
}
|
||||
|
||||
byte[] build() {
|
||||
patchCharAt(0, nannos);
|
||||
return annoAttribute.bytes();
|
||||
}
|
||||
|
||||
private void patchCharAt(int offset, int newChar) {
|
||||
int prevOffset = annoAttribute.offset;
|
||||
try {
|
||||
annoAttribute.offset = offset;
|
||||
annoAttribute.writeChar(newChar);
|
||||
} finally {
|
||||
annoAttribute.offset = prevOffset;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
static Consumer NO_BUILDER =
|
||||
new Consumer() {
|
||||
@Override
|
||||
public void accept(Object o) {
|
||||
//do nothing
|
||||
}
|
||||
};
|
||||
|
||||
public class AnnotationElementBuilder {
|
||||
|
||||
int nelems;
|
||||
|
||||
public AnnotationElementBuilder withString(String name, String s) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
writeStringValue(s);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void writeStringValue(String s) {
|
||||
annoAttribute.writeByte(Tag.STRING.tagChar);
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(s));
|
||||
nelems++;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withClass(String name, T s) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
writeClassValue(s);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void writeClassValue(T s) {
|
||||
annoAttribute.writeByte(Tag.CLASS.tagChar);
|
||||
annoAttribute.writeChar(poolHelper.putType(s));
|
||||
nelems++;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withEnum(String name, T enumType, int constant) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
writeEnumValue(enumType, constant);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void writeEnumValue(T enumType, int constant) {
|
||||
annoAttribute.writeByte(Tag.ENUM.tagChar);
|
||||
annoAttribute.writeChar(poolHelper.putType(enumType));
|
||||
annoAttribute.writeChar(constant);
|
||||
nelems++;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withAnnotation(String name, T annoType, Consumer<? super AnnotationElementBuilder> annotationBuilder) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
writeAnnotationValue(annoType, annotationBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void writeAnnotationValue(T annoType, Consumer<? super AnnotationElementBuilder> annotationBuilder) {
|
||||
annoAttribute.writeByte(Tag.ANNO.tagChar);
|
||||
annoAttribute.writeChar(poolHelper.putType(annoType));
|
||||
int offset = annoAttribute.offset;
|
||||
annoAttribute.writeChar(0);
|
||||
int nelems = withNestedElements(annotationBuilder);
|
||||
patchCharAt(offset, nelems);
|
||||
this.nelems++;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitive(String name, char c) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
writePrimitiveValue(Tag.C, (int)c, PoolHelper::putInt);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitive(String name, short s) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
writePrimitiveValue(Tag.S, (int)s, PoolHelper::putInt);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitive(String name, byte b) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
writePrimitiveValue(Tag.B, (int)b, PoolHelper::putInt);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitive(String name, int i) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
writePrimitiveValue(Tag.I, i, PoolHelper::putInt);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitive(String name, float f) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
writePrimitiveValue(Tag.F, f, PoolHelper::putFloat);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitive(String name, long l) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
writePrimitiveValue(Tag.J, l, PoolHelper::putLong);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitive(String name, double d) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
writePrimitiveValue(Tag.D, d, PoolHelper::putDouble);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitive(String name, boolean b) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
writePrimitiveValue(Tag.Z, b ? 1 : 0, PoolHelper::putInt);
|
||||
return this;
|
||||
}
|
||||
|
||||
private <Z> void writePrimitiveValue(Tag tag, Z value, ToIntBiFunction<PoolHelper<S, T, E>, Z> poolFunc) {
|
||||
annoAttribute.writeByte(tag.tagChar);
|
||||
annoAttribute.writeChar(poolFunc.applyAsInt(poolHelper, value));
|
||||
nelems++;
|
||||
}
|
||||
|
||||
AnnotationElementBuilder withStrings(String name, String... ss) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
annoAttribute.writeChar(ss.length);
|
||||
for (String s : ss) {
|
||||
writeStringValue(s);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
AnnotationElementBuilder withClasses(String name, T... cc) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
annoAttribute.writeChar(cc.length);
|
||||
for (T c : cc) {
|
||||
writeClassValue(c);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
AnnotationElementBuilder withEnums(String name, T enumType, int... constants) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
annoAttribute.writeChar(constants.length);
|
||||
for (int c : constants) {
|
||||
writeEnumValue(enumType, c);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public AnnotationElementBuilder withAnnotations(String name, T annoType, Consumer<? super AnnotationElementBuilder>... annotationBuilders) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
annoAttribute.writeChar(annotationBuilders.length);
|
||||
for (Consumer<? super AnnotationElementBuilder> annotationBuilder : annotationBuilders) {
|
||||
writeAnnotationValue(annoType, annotationBuilder);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitives(String name, char... cc) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
annoAttribute.writeChar(cc.length);
|
||||
for (char c : cc) {
|
||||
writePrimitiveValue(Tag.C, (int)c, PoolHelper::putInt);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitives(String name, short... ss) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
annoAttribute.writeChar(ss.length);
|
||||
for (short s : ss) {
|
||||
writePrimitiveValue(Tag.S, (int)s, PoolHelper::putInt);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitives(String name, byte... bb) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
annoAttribute.writeChar(bb.length);
|
||||
for (byte b : bb) {
|
||||
writePrimitiveValue(Tag.B, (int)b, PoolHelper::putInt);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitives(String name, int... ii) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
annoAttribute.writeChar(ii.length);
|
||||
for (int i : ii) {
|
||||
writePrimitiveValue(Tag.I, i, PoolHelper::putInt);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitives(String name, float... ff) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
annoAttribute.writeChar(ff.length);
|
||||
for (float f : ff) {
|
||||
writePrimitiveValue(Tag.F, f, PoolHelper::putFloat);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitives(String name, long... ll) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
annoAttribute.writeChar(ll.length);
|
||||
for (long l : ll) {
|
||||
writePrimitiveValue(Tag.J, l, PoolHelper::putLong);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitives(String name, double... dd) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
annoAttribute.writeChar(dd.length);
|
||||
for (double d : dd) {
|
||||
writePrimitiveValue(Tag.D, d, PoolHelper::putDouble);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationElementBuilder withPrimitives(String name, boolean... bb) {
|
||||
annoAttribute.writeChar(poolHelper.putUtf8(name));
|
||||
annoAttribute.writeChar(bb.length);
|
||||
for (boolean b : bb) {
|
||||
writePrimitiveValue(Tag.Z, b ? 1 : 0, PoolHelper::putInt);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
int withNestedElements(Consumer<? super AnnotationElementBuilder> annotationBuilder) {
|
||||
return withElements(new AnnotationElementBuilder(), annotationBuilder);
|
||||
}
|
||||
|
||||
int withElements(Consumer<? super AnnotationElementBuilder> annotationBuilder) {
|
||||
return withElements(this, annotationBuilder);
|
||||
}
|
||||
|
||||
private int withElements(AnnotationElementBuilder builder, Consumer<? super AnnotationElementBuilder> annotationBuilder) {
|
||||
annotationBuilder.accept(builder);
|
||||
return builder.nelems;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.experimental.bytecode;
|
||||
|
||||
/**
|
||||
* Base builder for attribute containing class file entities.
|
||||
*
|
||||
* @param <S> the type of the symbol representation
|
||||
* @param <T> the type of type descriptors representation
|
||||
* @param <E> the type of pool entries
|
||||
* @param <D> the type of this builder
|
||||
*/
|
||||
public class AttributeBuilder<S, T, E, D extends AttributeBuilder<S, T, E, D>>
|
||||
extends AbstractBuilder<S, T, E, D> {
|
||||
|
||||
/**
|
||||
* The number of attributes.
|
||||
*/
|
||||
protected int nattrs;
|
||||
|
||||
/**
|
||||
* The attributes represented as bytes.
|
||||
*/
|
||||
protected GrowableByteBuffer attributes = new GrowableByteBuffer();
|
||||
|
||||
/**
|
||||
* Create an attribute builder.
|
||||
*
|
||||
* @param poolHelper the helper to build the constant pool
|
||||
* @param typeHelper the helper to use to manipulate type descriptors
|
||||
*/
|
||||
public AttributeBuilder(PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
|
||||
super(poolHelper, typeHelper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a class file Attribute. Defined as:
|
||||
* <pre>
|
||||
* {@code attribute_info {
|
||||
* u2 attribute_name_index;
|
||||
* u4 attribute_length;
|
||||
* u1 info[attribute_length];
|
||||
* }}
|
||||
* </pre>
|
||||
*
|
||||
* @param name the attribute name
|
||||
* @param bytes the bytes of the attribute info
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public D withAttribute(CharSequence name, byte[] bytes) {
|
||||
attributes.writeChar(poolHelper.putUtf8(name));
|
||||
attributes.writeInt(bytes.length);
|
||||
attributes.writeBytes(bytes);
|
||||
nattrs++;
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a class file Attribute, using a writer. Defined as:
|
||||
* <pre>
|
||||
* {@code attribute_info {
|
||||
* u2 attribute_name_index;
|
||||
* u4 attribute_length;
|
||||
* u1 info[attribute_length];
|
||||
* }}
|
||||
* </pre>
|
||||
*
|
||||
* @param <Z> the type of the object representing the attribute
|
||||
* @param name the attribute name
|
||||
* @param attr the representation of the attribute
|
||||
* @param attrWriter the writer which transform the attribute representation into bytes
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public <Z> D withAttribute(CharSequence name, Z attr, AttributeWriter<S, T, E, Z> attrWriter) {
|
||||
attributes.writeChar(poolHelper.putUtf8(name));
|
||||
int offset = attributes.offset;
|
||||
attributes.writeInt(0);
|
||||
attrWriter.write(attr, poolHelper, attributes);
|
||||
int len = attributes.offset - offset - 4;
|
||||
attributes.withOffset(offset, buf -> buf.writeInt(len));
|
||||
nattrs++;
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writer for transforming attribute representations to bytes
|
||||
*
|
||||
* @param <S> the type of symbol representation
|
||||
* @param <T> the type of type descriptors representation
|
||||
* @param <E> the type of pool entries
|
||||
* @param <A> the type of the object representing the attribute
|
||||
*/
|
||||
public interface AttributeWriter<S, T, E, A> {
|
||||
|
||||
/**
|
||||
* Write an attribute representation into a byte buffer.
|
||||
*
|
||||
* @param attr the representation of the attribute
|
||||
* @param poolHelper the constant pool helper
|
||||
* @param buf the buffer to collect the bytes
|
||||
*/
|
||||
void write(A attr, PoolHelper<S, T, E> poolHelper, GrowableByteBuffer buf);
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.experimental.bytecode;
|
||||
|
||||
public class BasicClassBuilder extends ClassBuilder<String, String, BasicClassBuilder> {
|
||||
|
||||
public BasicClassBuilder(String thisClass, int majorVersion, int minorVersion) {
|
||||
this();
|
||||
withMinorVersion(minorVersion);
|
||||
withMajorVersion(majorVersion);
|
||||
withThisClass(thisClass);
|
||||
}
|
||||
|
||||
public BasicClassBuilder() {
|
||||
super(new BytePoolHelper<>(s->s, s->s), new BasicTypeHelper());
|
||||
}
|
||||
}
|
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.experimental.bytecode;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Helper to create and manipulate type descriptors, where type descriptors
|
||||
* are represented as JVM type descriptor strings and symbols are represented
|
||||
* as name strings
|
||||
*/
|
||||
public class BasicTypeHelper implements TypeHelper<String, String> {
|
||||
|
||||
@Override
|
||||
public String elemtype(String s) {
|
||||
if (!s.startsWith("[")) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return s.substring(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String arrayOf(String s) {
|
||||
return "[" + s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type(String s) {
|
||||
return "L" + s + ";";
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeTag tag(String s) {
|
||||
switch (s.charAt(0)) {
|
||||
case '[':
|
||||
case 'L':
|
||||
return TypeTag.A;
|
||||
case 'B':
|
||||
case 'C':
|
||||
case 'Z':
|
||||
case 'S':
|
||||
case 'I':
|
||||
return TypeTag.I;
|
||||
case 'F':
|
||||
return TypeTag.F;
|
||||
case 'J':
|
||||
return TypeTag.J;
|
||||
case 'D':
|
||||
return TypeTag.D;
|
||||
case 'V':
|
||||
return TypeTag.V;
|
||||
case 'Q':
|
||||
return TypeTag.Q;
|
||||
default:
|
||||
throw new IllegalStateException("Bad type: " + s);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String nullType() {
|
||||
// Needed in TypedCodeBuilder; ACONST_NULL pushes a 'null' onto the stack,
|
||||
// and stack maps handle null differently
|
||||
return "<null>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String commonSupertype(String t1, String t2) {
|
||||
if (t1.equals(t2)) {
|
||||
return t1;
|
||||
} else {
|
||||
try {
|
||||
Class<?> c1 = from(t1);
|
||||
Class<?> c2 = from(t2);
|
||||
if (c1.isAssignableFrom(c2)) {
|
||||
return t1;
|
||||
} else if (c2.isAssignableFrom(c1)) {
|
||||
return t2;
|
||||
} else {
|
||||
return "Ljava/lang/Object;";
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Class<?> from(String desc) throws ReflectiveOperationException {
|
||||
if (desc.startsWith("[")) {
|
||||
return Class.forName(desc.replaceAll("/", "."));
|
||||
} else {
|
||||
return Class.forName(symbol(desc).replaceAll("/", "."));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<String> parameterTypes(String s) {
|
||||
//TODO: gracefully non-method types
|
||||
return new Iterator<String>() {
|
||||
int ch = 1;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return s.charAt(ch) != ')';
|
||||
}
|
||||
|
||||
@Override
|
||||
public String next() {
|
||||
char curr = s.charAt(ch);
|
||||
switch (curr) {
|
||||
case 'C':
|
||||
case 'B':
|
||||
case 'S':
|
||||
case 'I':
|
||||
case 'J':
|
||||
case 'F':
|
||||
case 'D':
|
||||
case 'Z':
|
||||
ch++;
|
||||
return String.valueOf(curr);
|
||||
case '[':
|
||||
ch++;
|
||||
return "[" + next();
|
||||
case 'L':
|
||||
case 'Q':
|
||||
StringBuilder builder = new StringBuilder();
|
||||
while (curr != ';') {
|
||||
builder.append(curr);
|
||||
curr = s.charAt(++ch);
|
||||
}
|
||||
builder.append(';');
|
||||
ch++;
|
||||
return builder.toString();
|
||||
default:
|
||||
throw new AssertionError("cannot parse string: " + s);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String symbolFrom(String s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fromTag(TypeTag tag) {
|
||||
return tag.name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String symbol(String type) {
|
||||
return (type.startsWith("L") || type.startsWith("Q")) ? type.substring(1, type.length() - 1) : type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String returnType(String s) {
|
||||
return s.substring(s.indexOf(')') + 1, s.length());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,747 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.experimental.bytecode;
|
||||
|
||||
import java.lang.invoke.MethodHandleInfo;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.ToIntBiFunction;
|
||||
|
||||
/**
|
||||
* A helper for building and tracking constant pools whose entries are
|
||||
* represented as byte arrays.
|
||||
*
|
||||
* @param <S> the type of the symbol representation
|
||||
* @param <T> the type of type descriptors representation
|
||||
*/
|
||||
public class BytePoolHelper<S, T> implements PoolHelper<S, T, byte[]> {
|
||||
|
||||
GrowableByteBuffer pool = new GrowableByteBuffer();
|
||||
GrowableByteBuffer bsm_attr = new GrowableByteBuffer();
|
||||
//Map<PoolKey, PoolKey> indicesMap = new HashMap<>();
|
||||
int currentIndex = 1;
|
||||
int currentBsmIndex = 0;
|
||||
|
||||
KeyMap<PoolKey> entries = new KeyMap<>();
|
||||
KeyMap<BsmKey> bootstraps = new KeyMap<>();
|
||||
PoolKey key = new PoolKey();
|
||||
BsmKey bsmKey = new BsmKey();
|
||||
|
||||
Function<S, String> symbolToString;
|
||||
Function<T, String> typeToString;
|
||||
|
||||
public BytePoolHelper(Function<S, String> symbolToString, Function<T, String> typeToString) {
|
||||
this.symbolToString = symbolToString;
|
||||
this.typeToString = typeToString;
|
||||
}
|
||||
|
||||
static class KeyMap<K extends AbstractKey<K>> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
K[] table = (K[])new AbstractKey<?>[0x10];
|
||||
int nelems;
|
||||
|
||||
public void enter(K e) {
|
||||
if (nelems * 3 >= (table.length - 1) * 2)
|
||||
dble();
|
||||
int hash = getIndex(e);
|
||||
K old = table[hash];
|
||||
if (old == null) {
|
||||
nelems++;
|
||||
}
|
||||
e.next = old;
|
||||
table[hash] = e;
|
||||
}
|
||||
|
||||
protected K lookup(K other) {
|
||||
K e = table[getIndex(other)];
|
||||
while (e != null && !e.equals(other))
|
||||
e = e.next;
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for slot in the table.
|
||||
* We use open addressing with double hashing.
|
||||
*/
|
||||
int getIndex(K e) {
|
||||
int hashMask = table.length - 1;
|
||||
int h = e.hashCode();
|
||||
int i = h & hashMask;
|
||||
// The expression below is always odd, so it is guaranteed
|
||||
// to be mutually prime with table.length, a power of 2.
|
||||
int x = hashMask - ((h + (h >> 16)) << 1);
|
||||
for (; ; ) {
|
||||
K e2 = table[i];
|
||||
if (e2 == null)
|
||||
return i;
|
||||
else if (e.hash == e2.hash)
|
||||
return i;
|
||||
i = (i + x) & hashMask;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void dble() {
|
||||
K[] oldtable = table;
|
||||
table = (K[])new AbstractKey<?>[oldtable.length * 2];
|
||||
int n = 0;
|
||||
for (int i = oldtable.length; --i >= 0; ) {
|
||||
K e = oldtable[i];
|
||||
if (e != null) {
|
||||
table[getIndex(e)] = e;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
// We don't need to update nelems for shared inherited scopes,
|
||||
// since that gets handled by leave().
|
||||
nelems = n;
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class AbstractKey<K extends AbstractKey<K>> {
|
||||
int hash;
|
||||
int index = -1;
|
||||
K next;
|
||||
|
||||
abstract K dup();
|
||||
|
||||
public abstract boolean equals(Object o);
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
void at(int index) {
|
||||
this.index = index;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PoolKey extends AbstractKey<PoolKey> {
|
||||
PoolTag tag;
|
||||
Object o1;
|
||||
Object o2;
|
||||
Object o3;
|
||||
Object o4;
|
||||
int size = -1;
|
||||
|
||||
void setUtf8(CharSequence s) {
|
||||
tag = PoolTag.CONSTANT_UTF8;
|
||||
o1 = s;
|
||||
size = 1;
|
||||
hash = tag.tag | (s.hashCode() << 1);
|
||||
}
|
||||
|
||||
void setClass(String clazz) {
|
||||
tag = PoolTag.CONSTANT_CLASS;
|
||||
o1 = clazz;
|
||||
size = 1;
|
||||
hash = tag.tag | (clazz.hashCode() << 1);
|
||||
}
|
||||
|
||||
void setNameAndType(CharSequence name, String type) {
|
||||
tag = PoolTag.CONSTANT_NAMEANDTYPE;
|
||||
o1 = name;
|
||||
o2 = type;
|
||||
size = 2;
|
||||
hash = tag.tag | ((name.hashCode() | type.hashCode()) << 1);
|
||||
}
|
||||
|
||||
void setMemberRef(PoolTag poolTag, String owner, CharSequence name, String type) {
|
||||
tag = poolTag;
|
||||
o1 = owner;
|
||||
o2 = name;
|
||||
o3 = type;
|
||||
size = 3;
|
||||
hash = tag.tag | ((owner.hashCode() | name.hashCode() | type.hashCode()) << 1);
|
||||
}
|
||||
|
||||
void setInvokeDynamic(int bsmIndex, CharSequence name, String type) {
|
||||
tag = PoolTag.CONSTANT_INVOKEDYNAMIC;
|
||||
o1 = bsmIndex;
|
||||
o2 = name;
|
||||
o3 = type;
|
||||
size = 3;
|
||||
hash = tag.tag | ((bsmIndex | name.hashCode() | type.hashCode()) << 1);
|
||||
}
|
||||
|
||||
void setDynamicConstant(int bsmIndex, CharSequence name, String type) {
|
||||
tag = PoolTag.CONSTANT_DYNAMIC;
|
||||
o1 = bsmIndex;
|
||||
o2 = name;
|
||||
o3 = type;
|
||||
size = 3;
|
||||
hash = tag.tag | ((bsmIndex | name.hashCode() | type.hashCode()) << 1);
|
||||
}
|
||||
|
||||
void setString(String s) {
|
||||
tag = PoolTag.CONSTANT_STRING;
|
||||
o1 = s;
|
||||
size = 1;
|
||||
hash = tag.tag | (s.hashCode() << 1);
|
||||
}
|
||||
|
||||
void setInteger(Integer i) {
|
||||
tag = PoolTag.CONSTANT_INTEGER;
|
||||
o1 = i;
|
||||
size = 1;
|
||||
hash = tag.tag | (i.hashCode() << 1);
|
||||
}
|
||||
|
||||
void setFloat(Float f) {
|
||||
tag = PoolTag.CONSTANT_FLOAT;
|
||||
o1 = f;
|
||||
size = 1;
|
||||
hash = tag.tag | (f.hashCode() << 1);
|
||||
}
|
||||
|
||||
void setLong(Long l) {
|
||||
tag = PoolTag.CONSTANT_LONG;
|
||||
o1 = l;
|
||||
size = 1;
|
||||
hash = tag.tag | (l.hashCode() << 1);
|
||||
}
|
||||
|
||||
void setDouble(Double d) {
|
||||
tag = PoolTag.CONSTANT_DOUBLE;
|
||||
o1 = d;
|
||||
size = 1;
|
||||
hash = tag.tag | (d.hashCode() << 1);
|
||||
}
|
||||
|
||||
void setMethodType(String type) {
|
||||
tag = PoolTag.CONSTANT_METHODTYPE;
|
||||
o1 = type;
|
||||
size = 1;
|
||||
hash = tag.tag | (type.hashCode() << 1);
|
||||
}
|
||||
|
||||
void setMethodHandle(int bsmKind, String owner, CharSequence name, String type) {
|
||||
tag = PoolTag.CONSTANT_METHODHANDLE;
|
||||
o1 = bsmKind;
|
||||
o2 = owner;
|
||||
o3 = name;
|
||||
o4 = type;
|
||||
size = 4;
|
||||
hash = tag.tag | (bsmKind | owner.hashCode() | name.hashCode() | type.hashCode() << 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
PoolKey that = (PoolKey) obj;
|
||||
if (tag != that.tag) return false;
|
||||
switch (size) {
|
||||
case 1:
|
||||
if (!o1.equals(that.o1)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (!o2.equals(that.o2) || !o1.equals(that.o1)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (!o3.equals(that.o3) || !o2.equals(that.o2) || !o1.equals(that.o1)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (!o4.equals(that.o4) || !o3.equals(that.o3) || !o2.equals(that.o2) || !o1.equals(that.o1)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
PoolKey dup() {
|
||||
PoolKey poolKey = new PoolKey();
|
||||
poolKey.tag = tag;
|
||||
poolKey.size = size;
|
||||
poolKey.hash = hash;
|
||||
poolKey.o1 = o1;
|
||||
poolKey.o2 = o2;
|
||||
poolKey.o3 = o3;
|
||||
poolKey.o4 = o4;
|
||||
return poolKey;
|
||||
}
|
||||
}
|
||||
|
||||
static class BsmKey extends AbstractKey<BsmKey> {
|
||||
String bsmClass;
|
||||
CharSequence bsmName;
|
||||
String bsmType;
|
||||
List<Integer> bsmArgs;
|
||||
|
||||
void set(String bsmClass, CharSequence bsmName, String bsmType, List<Integer> bsmArgs) {
|
||||
this.bsmClass = bsmClass;
|
||||
this.bsmName = bsmName;
|
||||
this.bsmType = bsmType;
|
||||
this.bsmArgs = bsmArgs;
|
||||
hash = bsmClass.hashCode() | bsmName.hashCode() | bsmType.hashCode() | Objects.hash(bsmArgs);
|
||||
}
|
||||
|
||||
BsmKey dup() {
|
||||
BsmKey bsmKey = new BsmKey();
|
||||
bsmKey.bsmClass = bsmClass;
|
||||
bsmKey.bsmName = bsmName;
|
||||
bsmKey.bsmType = bsmType;
|
||||
bsmKey.bsmArgs = bsmArgs;
|
||||
bsmKey.hash = hash;
|
||||
return bsmKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof BsmKey) {
|
||||
BsmKey that = (BsmKey)obj;
|
||||
return Objects.equals(bsmClass, that.bsmClass) &&
|
||||
Objects.equals(bsmName, that.bsmName) &&
|
||||
Objects.equals(bsmType, that.bsmType) &&
|
||||
Objects.deepEquals(bsmArgs, that.bsmArgs);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putClass(S symbol) {
|
||||
return putClassInternal(symbolToString.apply(symbol));
|
||||
}
|
||||
|
||||
private int putClassInternal(String symbol) {
|
||||
key.setClass(symbol);
|
||||
PoolKey poolKey = entries.lookup(key);
|
||||
if (poolKey == null) {
|
||||
poolKey = key.dup();
|
||||
int utf8_idx = putUtf8(symbol);
|
||||
poolKey.at(currentIndex++);
|
||||
entries.enter(poolKey);
|
||||
pool.writeByte(PoolTag.CONSTANT_CLASS.tag);
|
||||
pool.writeChar(utf8_idx);
|
||||
}
|
||||
return poolKey.index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putFieldRef(S owner, CharSequence name, T type) {
|
||||
return putMemberRef(PoolTag.CONSTANT_FIELDREF, owner, name, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putMethodRef(S owner, CharSequence name, T type, boolean isInterface) {
|
||||
return putMemberRef(isInterface ? PoolTag.CONSTANT_INTERFACEMETHODREF : PoolTag.CONSTANT_METHODREF,
|
||||
owner, name, type);
|
||||
}
|
||||
|
||||
int putMemberRef(PoolTag poolTag, S owner, CharSequence name, T type) {
|
||||
return putMemberRefInternal(poolTag, symbolToString.apply(owner), name, typeToString.apply(type));
|
||||
}
|
||||
|
||||
int putMemberRefInternal(PoolTag poolTag, String owner, CharSequence name, String type) {
|
||||
key.setMemberRef(poolTag, owner, name, type);
|
||||
PoolKey poolKey = entries.lookup(key);
|
||||
if (poolKey == null) {
|
||||
poolKey = key.dup();
|
||||
int owner_idx = putClassInternal(owner);
|
||||
int nameAndType_idx = putNameAndType(name, type);
|
||||
poolKey.at(currentIndex++);
|
||||
entries.enter(poolKey);
|
||||
pool.writeByte(poolTag.tag);
|
||||
pool.writeChar(owner_idx);
|
||||
pool.writeChar(nameAndType_idx);
|
||||
}
|
||||
return poolKey.index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putInt(int i) {
|
||||
key.setInteger(i);
|
||||
PoolKey poolKey = entries.lookup(key);
|
||||
if (poolKey == null) {
|
||||
poolKey = key.dup();
|
||||
poolKey.at(currentIndex++);
|
||||
entries.enter(poolKey);
|
||||
pool.writeByte(PoolTag.CONSTANT_INTEGER.tag);
|
||||
pool.writeInt(i);
|
||||
}
|
||||
return poolKey.index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putFloat(float f) {
|
||||
key.setFloat(f);
|
||||
PoolKey poolKey = entries.lookup(key);
|
||||
if (poolKey == null) {
|
||||
poolKey = key.dup();
|
||||
poolKey.at(currentIndex++);
|
||||
entries.enter(poolKey);
|
||||
pool.writeByte(PoolTag.CONSTANT_FLOAT.tag);
|
||||
pool.writeFloat(f);
|
||||
}
|
||||
return poolKey.index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putLong(long l) {
|
||||
key.setLong(l);
|
||||
PoolKey poolKey = entries.lookup(key);
|
||||
if (poolKey == null) {
|
||||
poolKey = key.dup();
|
||||
poolKey.at(currentIndex++);
|
||||
entries.enter(poolKey);
|
||||
pool.writeByte(PoolTag.CONSTANT_LONG.tag);
|
||||
pool.writeLong(l);
|
||||
currentIndex++;
|
||||
}
|
||||
return poolKey.index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putDouble(double d) {
|
||||
key.setDouble(d);
|
||||
PoolKey poolKey = entries.lookup(key);
|
||||
if (poolKey == null) {
|
||||
poolKey = key.dup();
|
||||
poolKey.at(currentIndex++);
|
||||
entries.enter(poolKey);
|
||||
pool.writeByte(PoolTag.CONSTANT_DOUBLE.tag);
|
||||
pool.writeDouble(d);
|
||||
currentIndex++;
|
||||
}
|
||||
return poolKey.index;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int putInvokeDynamic(CharSequence invokedName, T invokedType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
|
||||
return putInvokeDynamicInternal(invokedName, typeToString.apply(invokedType), symbolToString.apply(bsmClass), bsmName, typeToString.apply(bsmType), staticArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putDynamicConstant(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
|
||||
return putDynamicConstantInternal(constName, typeToString.apply(constType), symbolToString.apply(bsmClass), bsmName, typeToString.apply(bsmType), staticArgs);
|
||||
}
|
||||
|
||||
private int putInvokeDynamicInternal(CharSequence invokedName, String invokedType, String bsmClass, CharSequence bsmName, String bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
|
||||
int bsmIndex = putBsmInternal(bsmClass, bsmName, bsmType, staticArgs);
|
||||
key.setInvokeDynamic(bsmIndex, invokedName, invokedType);
|
||||
PoolKey poolKey = entries.lookup(key);
|
||||
if (poolKey == null) {
|
||||
poolKey = key.dup();
|
||||
int nameAndType_idx = putNameAndType(invokedName, invokedType);
|
||||
poolKey.at(currentIndex++);
|
||||
entries.enter(poolKey);
|
||||
pool.writeByte(PoolTag.CONSTANT_INVOKEDYNAMIC.tag);
|
||||
pool.writeChar(bsmIndex);
|
||||
pool.writeChar(nameAndType_idx);
|
||||
}
|
||||
return poolKey.index;
|
||||
}
|
||||
|
||||
private int putDynamicConstantInternal(CharSequence constName, String constType, String bsmClass, CharSequence bsmName, String bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
|
||||
int bsmIndex = putBsmInternal(bsmClass, bsmName, bsmType, staticArgs);
|
||||
key.setDynamicConstant(bsmIndex, constName, constType);
|
||||
PoolKey poolKey = entries.lookup(key);
|
||||
if (poolKey == null) {
|
||||
poolKey = key.dup();
|
||||
int nameAndType_idx = putNameAndType(constName, constType);
|
||||
poolKey.at(currentIndex++);
|
||||
entries.enter(poolKey);
|
||||
pool.writeByte(PoolTag.CONSTANT_DYNAMIC.tag);
|
||||
pool.writeChar(bsmIndex);
|
||||
pool.writeChar(nameAndType_idx);
|
||||
}
|
||||
return poolKey.index;
|
||||
}
|
||||
|
||||
private int putBsmInternal(String bsmClass, CharSequence bsmName, String bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
|
||||
ByteStaticArgListBuilder staticArgsBuilder = new ByteStaticArgListBuilder();
|
||||
staticArgs.accept(staticArgsBuilder);
|
||||
List<Integer> static_idxs = staticArgsBuilder.indexes;
|
||||
bsmKey.set(bsmClass, bsmName, bsmType, static_idxs);
|
||||
BsmKey poolKey = bootstraps.lookup(bsmKey);
|
||||
if (poolKey == null) {
|
||||
poolKey = bsmKey.dup();
|
||||
int bsm_ref = putHandleInternal(MethodHandleInfo.REF_invokeStatic, bsmClass, bsmName, bsmType);
|
||||
poolKey.at(currentBsmIndex++);
|
||||
bootstraps.enter(poolKey);
|
||||
bsm_attr.writeChar(bsm_ref);
|
||||
bsm_attr.writeChar(static_idxs.size());
|
||||
for (int i : static_idxs) {
|
||||
bsm_attr.writeChar(i);
|
||||
}
|
||||
}
|
||||
return poolKey.index;
|
||||
}
|
||||
//where
|
||||
class ByteStaticArgListBuilder implements StaticArgListBuilder<S, T, byte[]> {
|
||||
|
||||
List<Integer> indexes = new ArrayList<>();
|
||||
|
||||
public ByteStaticArgListBuilder add(int i) {
|
||||
indexes.add(putInt(i));
|
||||
return this;
|
||||
}
|
||||
public ByteStaticArgListBuilder add(float f) {
|
||||
indexes.add(putFloat(f));
|
||||
return this;
|
||||
}
|
||||
public ByteStaticArgListBuilder add(long l) {
|
||||
indexes.add(putLong(l));
|
||||
return this;
|
||||
}
|
||||
public ByteStaticArgListBuilder add(double d) {
|
||||
indexes.add(putDouble(d));
|
||||
return this;
|
||||
}
|
||||
public ByteStaticArgListBuilder add(String s) {
|
||||
indexes.add(putString(s));
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public StaticArgListBuilder<S, T, byte[]> add(int refKind, S owner, CharSequence name, T type) {
|
||||
indexes.add(putHandle(refKind, owner, name, type));
|
||||
return this;
|
||||
}
|
||||
public <Z> ByteStaticArgListBuilder add(Z z, ToIntBiFunction<PoolHelper<S, T, byte[]>, Z> poolFunc) {
|
||||
indexes.add(poolFunc.applyAsInt(BytePoolHelper.this, z));
|
||||
return this;
|
||||
}
|
||||
public ByteStaticArgListBuilder add(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, byte[]>> staticArgs) {
|
||||
indexes.add(putDynamicConstant(constName, constType, bsmClass, bsmName, bsmType, staticArgs));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putMethodType(T s) {
|
||||
return putMethodTypeInternal(typeToString.apply(s));
|
||||
}
|
||||
|
||||
private int putMethodTypeInternal(String s) {
|
||||
key.setMethodType(s);
|
||||
PoolKey poolKey = entries.lookup(key);
|
||||
if (poolKey == null) {
|
||||
poolKey = key.dup();
|
||||
int desc_idx = putUtf8(s);
|
||||
poolKey.at(currentIndex++);
|
||||
entries.enter(poolKey);
|
||||
pool.writeByte(PoolTag.CONSTANT_METHODTYPE.tag);
|
||||
pool.writeChar(desc_idx);
|
||||
}
|
||||
return poolKey.index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putHandle(int refKind, S owner, CharSequence name, T type) {
|
||||
return putHandleInternal(refKind, symbolToString.apply(owner), name, typeToString.apply(type));
|
||||
}
|
||||
|
||||
private int putHandleInternal(int refKind, String owner, CharSequence name, String type) {
|
||||
key.setMethodHandle(refKind, owner, name, type);
|
||||
PoolKey poolKey = entries.lookup(key);
|
||||
if (poolKey == null) {
|
||||
poolKey = key.dup();
|
||||
int ref_idx = putMemberRefInternal(fromKind(refKind), owner, name, type);
|
||||
poolKey.at(currentIndex++);
|
||||
entries.enter(poolKey);
|
||||
pool.writeByte(PoolTag.CONSTANT_METHODHANDLE.tag);
|
||||
pool.writeByte(refKind);
|
||||
pool.writeChar(ref_idx);
|
||||
}
|
||||
return poolKey.index;
|
||||
}
|
||||
|
||||
PoolTag fromKind(int bsmKind) {
|
||||
switch (bsmKind) {
|
||||
case 1: // REF_getField
|
||||
case 2: // REF_getStatic
|
||||
case 3: // REF_putField
|
||||
case 4: // REF_putStatic
|
||||
return PoolTag.CONSTANT_FIELDREF;
|
||||
case 5: // REF_invokeVirtual
|
||||
case 6: // REF_invokeStatic
|
||||
case 7: // REF_invokeSpecial
|
||||
case 8: // REF_newInvokeSpecial
|
||||
return PoolTag.CONSTANT_METHODREF;
|
||||
case 9: // REF_invokeInterface
|
||||
return PoolTag.CONSTANT_INTERFACEMETHODREF;
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putType(T s) {
|
||||
return putUtf8(typeToString.apply(s));
|
||||
}
|
||||
|
||||
public int putUtf8(CharSequence s) {
|
||||
key.setUtf8(s);
|
||||
PoolKey poolKey = entries.lookup(key);
|
||||
if (poolKey == null) {
|
||||
poolKey = key.dup();
|
||||
poolKey.at(currentIndex++);
|
||||
entries.enter(poolKey);
|
||||
pool.writeByte(PoolTag.CONSTANT_UTF8.tag);
|
||||
putUTF8Internal(s);
|
||||
}
|
||||
return poolKey.index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts an UTF8 string into this byte vector. The byte vector is
|
||||
* automatically enlarged if necessary.
|
||||
*
|
||||
* @param s a String whose UTF8 encoded length must be less than 65536.
|
||||
* @return this byte vector.
|
||||
*/
|
||||
void putUTF8Internal(final CharSequence s) {
|
||||
int charLength = s.length();
|
||||
if (charLength > 65535) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
// optimistic algorithm: instead of computing the byte length and then
|
||||
// serializing the string (which requires two loops), we assume the byte
|
||||
// length is equal to char length (which is the most frequent case), and
|
||||
// we start serializing the string right away. During the serialization,
|
||||
// if we find that this assumption is wrong, we continue with the
|
||||
// general method.
|
||||
pool.writeChar(charLength);
|
||||
for (int i = 0; i < charLength; ++i) {
|
||||
char c = s.charAt(i);
|
||||
if (c >= '\001' && c <= '\177') {
|
||||
pool.writeByte((byte) c);
|
||||
} else {
|
||||
encodeUTF8(s, i, 65535);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts an UTF8 string into this byte vector. The byte vector is
|
||||
* automatically enlarged if necessary. The string length is encoded in two
|
||||
* bytes before the encoded characters, if there is space for that (i.e. if
|
||||
* this.length - i - 2 >= 0).
|
||||
*
|
||||
* @param s the String to encode.
|
||||
* @param i the index of the first character to encode. The previous
|
||||
* characters are supposed to have already been encoded, using
|
||||
* only one byte per character.
|
||||
* @param maxByteLength the maximum byte length of the encoded string, including the
|
||||
* already encoded characters.
|
||||
* @return this byte vector.
|
||||
*/
|
||||
void encodeUTF8(final CharSequence s, int i, int maxByteLength) {
|
||||
int charLength = s.length();
|
||||
int byteLength = i;
|
||||
char c;
|
||||
for (int j = i; j < charLength; ++j) {
|
||||
c = s.charAt(j);
|
||||
if (c >= '\001' && c <= '\177') {
|
||||
byteLength++;
|
||||
} else if (c > '\u07FF') {
|
||||
byteLength += 3;
|
||||
} else {
|
||||
byteLength += 2;
|
||||
}
|
||||
}
|
||||
if (byteLength > maxByteLength) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
int byteLengthFinal = byteLength;
|
||||
pool.withOffset(pool.offset - i - 2, buf -> buf.writeChar(byteLengthFinal));
|
||||
for (int j = i; j < charLength; ++j) {
|
||||
c = s.charAt(j);
|
||||
if (c >= '\001' && c <= '\177') {
|
||||
pool.writeChar((byte) c);
|
||||
} else if (c > '\u07FF') {
|
||||
pool.writeChar((byte) (0xE0 | c >> 12 & 0xF));
|
||||
pool.writeChar((byte) (0x80 | c >> 6 & 0x3F));
|
||||
pool.writeChar((byte) (0x80 | c & 0x3F));
|
||||
} else {
|
||||
pool.writeChar((byte) (0xC0 | c >> 6 & 0x1F));
|
||||
pool.writeChar((byte) (0x80 | c & 0x3F));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int putString(String s) {
|
||||
key.setString(s);
|
||||
PoolKey poolKey = entries.lookup(key);
|
||||
if (poolKey == null) {
|
||||
poolKey = key.dup();
|
||||
int utf8_index = putUtf8(s);
|
||||
poolKey.at(currentIndex++);
|
||||
entries.enter(poolKey);
|
||||
pool.writeByte(PoolTag.CONSTANT_STRING.tag);
|
||||
pool.writeChar(utf8_index);
|
||||
}
|
||||
return poolKey.index;
|
||||
}
|
||||
|
||||
int putNameAndType(CharSequence name, String type) {
|
||||
key.setNameAndType(name, type);
|
||||
PoolKey poolKey = entries.lookup(key);
|
||||
if (poolKey == null) {
|
||||
poolKey = key.dup();
|
||||
int name_idx = putUtf8(name);
|
||||
int type_idx = putUtf8(type);
|
||||
poolKey.at(currentIndex++);
|
||||
entries.enter(poolKey);
|
||||
pool.writeByte(PoolTag.CONSTANT_NAMEANDTYPE.tag);
|
||||
pool.writeChar(name_idx);
|
||||
pool.writeChar(type_idx);
|
||||
}
|
||||
return poolKey.index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return currentIndex - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] entries() {
|
||||
return pool.bytes();
|
||||
}
|
||||
|
||||
<Z extends ClassBuilder<S, T, Z>> void addAttributes(ClassBuilder<S , T, Z> cb) {
|
||||
if (currentBsmIndex > 0) {
|
||||
GrowableByteBuffer bsmAttrBuf = new GrowableByteBuffer();
|
||||
bsmAttrBuf.writeChar(currentBsmIndex);
|
||||
bsmAttrBuf.writeBytes(bsm_attr);
|
||||
cb.withAttribute("BootstrapMethods", bsmAttrBuf.bytes());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.experimental.bytecode;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Base class builder. The base of higher level class builders.
|
||||
*
|
||||
* @param <S> the type of symbol representation
|
||||
* @param <T> the type of type descriptors representation
|
||||
* @param <C> the type of this builder
|
||||
*/
|
||||
public class ClassBuilder<S, T, C extends ClassBuilder<S, T, C>>
|
||||
extends DeclBuilder<S, T, byte[], C> {
|
||||
|
||||
/**
|
||||
* The helper to use to manipulate type descriptors.
|
||||
*/
|
||||
protected TypeHelper<S, T> typeHelper;
|
||||
|
||||
/**
|
||||
* The symbol for the class being built.
|
||||
*/
|
||||
protected S thisClass;
|
||||
|
||||
/**
|
||||
* The super-interfaces of the class being built..
|
||||
*/
|
||||
protected GrowableByteBuffer interfaces = new GrowableByteBuffer();
|
||||
|
||||
/**
|
||||
* The fields of the class being built.
|
||||
*/
|
||||
protected GrowableByteBuffer fields = new GrowableByteBuffer();
|
||||
|
||||
/**
|
||||
* The methods of the class being built.
|
||||
*/
|
||||
protected GrowableByteBuffer methods = new GrowableByteBuffer();
|
||||
|
||||
int majorVersion;
|
||||
int minorVersion;
|
||||
int flags;
|
||||
int superclass;
|
||||
int nmethods, nfields, ninterfaces;
|
||||
|
||||
/**
|
||||
* Create a class builder.
|
||||
*
|
||||
* @param poolHelper the helper to build the constant pool
|
||||
* @param typeHelper the helper to use to manipulate type descriptors
|
||||
*/
|
||||
public ClassBuilder(BytePoolHelper<S, T> poolHelper, TypeHelper<S, T> typeHelper) {
|
||||
super(poolHelper, typeHelper);
|
||||
this.typeHelper = typeHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the minor class file version.
|
||||
*
|
||||
* @param minorVersion the minor version number
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public C withMinorVersion(int minorVersion) {
|
||||
this.minorVersion = minorVersion;
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the major class file version.
|
||||
*
|
||||
* @param majorVersion the major version number
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public C withMajorVersion(int majorVersion) {
|
||||
this.majorVersion = majorVersion;
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the class symbol
|
||||
*
|
||||
* @param thisClass the class symbol
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public C withThisClass(S thisClass) {
|
||||
this.thisClass = thisClass;
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the class access flags
|
||||
*
|
||||
* @param flags an array of {@code Flag}
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
@Override
|
||||
public C withFlags(Flag... flags) {
|
||||
for (Flag f : flags) {
|
||||
this.flags |= f.flag;
|
||||
}
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the superclass
|
||||
*
|
||||
* @param sup the superclass symbol
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public C withSuperclass(S sup) {
|
||||
this.superclass = poolHelper.putClass(sup);
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a super interface.
|
||||
*
|
||||
* @param sup an interface symbol
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public C withSuperinterface(S sup) {
|
||||
this.interfaces.writeChar(poolHelper.putClass(sup));
|
||||
ninterfaces++;
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a field.
|
||||
*
|
||||
* @param name the name of the field
|
||||
* @param type the type descriptor of the field
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public final C withField(CharSequence name, T type) {
|
||||
return withField(name, type, FB -> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a field.
|
||||
*
|
||||
* @param name the name of the field
|
||||
* @param type the type descriptor of the field
|
||||
* @param fieldConfig access to the {@code FieldBuilder} to allow clients to
|
||||
* adjust flags, annotations and bytecode attributes on the field declaration
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public C withField(CharSequence name, T type, Consumer<? super FieldBuilder<S, T, byte[]>> fieldConfig) {
|
||||
FieldBuilder<S, T, byte[]> F = new FieldBuilder<>(name, type, poolHelper, typeHelper);
|
||||
fieldConfig.accept(F);
|
||||
F.build(fields);
|
||||
nfields++;
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a method
|
||||
*
|
||||
* @param name the name of the method
|
||||
* @param type the type descriptor of the method
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public final C withMethod(CharSequence name, T type) {
|
||||
return withMethod(name, type, MB -> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a method
|
||||
*
|
||||
* @param name the name of the method
|
||||
* @param type the type descriptor of the method
|
||||
* @param methodConfig access to the {@code MethodBuilder} to allow clients to
|
||||
* adjust flags, annotations and bytecode attributes on the method declaration
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public C withMethod(CharSequence name, T type, Consumer<? super MethodBuilder<S, T, byte[]>> methodConfig) {
|
||||
MethodBuilder<S, T, byte[]> M = new MethodBuilder<>(thisClass, name, type, poolHelper, typeHelper);
|
||||
methodConfig.accept(M);
|
||||
M.build(methods);
|
||||
nmethods++;
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the constant pool into a byte array.
|
||||
*
|
||||
* @return a representation of this constant pool as a byte array
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public byte[] build() {
|
||||
((BytePoolHelper<S, T>)poolHelper).addAttributes(this);
|
||||
addAnnotations();
|
||||
int thisClassIdx = poolHelper.putClass(thisClass);
|
||||
byte[] poolBytes = poolHelper.entries();
|
||||
GrowableByteBuffer buf = new GrowableByteBuffer();
|
||||
buf.writeInt(0xCAFEBABE);
|
||||
buf.writeChar(minorVersion);
|
||||
buf.writeChar(majorVersion);
|
||||
buf.writeChar(poolHelper.size() + 1);
|
||||
buf.writeBytes(poolBytes);
|
||||
buf.writeChar(flags);
|
||||
buf.writeChar(thisClassIdx);
|
||||
buf.writeChar(superclass);
|
||||
buf.writeChar(ninterfaces);
|
||||
if (ninterfaces > 0) {
|
||||
buf.writeBytes(interfaces);
|
||||
}
|
||||
buf.writeChar(nfields);
|
||||
buf.writeBytes(fields);
|
||||
buf.writeChar(nmethods);
|
||||
buf.writeBytes(methods);
|
||||
buf.writeChar(nattrs);
|
||||
buf.writeBytes(attributes);
|
||||
return buf.bytes();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.experimental.bytecode;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Declaration (class, class member, ...) builder.
|
||||
*
|
||||
* @param <S> the type of symbol representation
|
||||
* @param <T> the type of type descriptors representation
|
||||
* @param <E> the type of pool entries
|
||||
* @param <D> the type of this builder
|
||||
*/
|
||||
public class DeclBuilder<S, T, E, D extends DeclBuilder<S, T, E, D>>
|
||||
extends AttributeBuilder<S, T, E, D> {
|
||||
|
||||
/**
|
||||
* The access flags of the declaration, as bit flags.
|
||||
*/
|
||||
protected int flags;
|
||||
|
||||
AnnotationsBuilder<S, T, E> runtimeInvisibleAnnotations;
|
||||
AnnotationsBuilder<S, T, E> runtimeVisibleAnnotations;
|
||||
|
||||
/**
|
||||
* Create a declaration builder,
|
||||
*
|
||||
* @param poolHelper the helper to build the constant pool
|
||||
* @param typeHelper the helper to use to manipulate type descriptors
|
||||
*/
|
||||
DeclBuilder(PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
|
||||
super(poolHelper, typeHelper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the class file flags for this declaration.
|
||||
*
|
||||
* @param flags the flags as {@code Flag} objects
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public D withFlags(Flag... flags) {
|
||||
for (Flag f : flags) {
|
||||
this.flags |= f.flag;
|
||||
}
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify, via bits, the class file flags for this declaration.
|
||||
*
|
||||
* @param flags the flags as bit settings
|
||||
* @return this builder, for chained calls
|
||||
*/
|
||||
public D withFlags(int flags) {
|
||||
withFlags(Flag.parse(flags));
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
public D withAnnotation(AnnotationsBuilder.Kind kind, T annoType) {
|
||||
getAnnotations(kind).withAnnotation(annoType, null);
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
public D withAnnotation(AnnotationsBuilder.Kind kind, T annoType, Consumer<? super AnnotationsBuilder<S, T, E>.AnnotationElementBuilder> annotations) {
|
||||
getAnnotations(kind).withAnnotation(annoType, annotations);
|
||||
return thisBuilder();
|
||||
}
|
||||
|
||||
private AnnotationsBuilder<S, T, E> getAnnotations(AnnotationsBuilder.Kind kind) {
|
||||
switch (kind) {
|
||||
case RUNTIME_INVISIBLE:
|
||||
if (runtimeInvisibleAnnotations == null) {
|
||||
runtimeInvisibleAnnotations = new AnnotationsBuilder<>(poolHelper, typeHelper);
|
||||
}
|
||||
return runtimeInvisibleAnnotations;
|
||||
case RUNTIME_VISIBLE:
|
||||
if (runtimeVisibleAnnotations == null) {
|
||||
runtimeVisibleAnnotations = new AnnotationsBuilder<>(poolHelper, typeHelper);
|
||||
}
|
||||
return runtimeVisibleAnnotations;
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
void addAnnotations() {
|
||||
if (runtimeVisibleAnnotations != null) {
|
||||
withAttribute("RuntimeVisibleAnnotations", runtimeVisibleAnnotations.build());
|
||||
}
|
||||
if (runtimeInvisibleAnnotations != null) {
|
||||
withAttribute("RuntimeInvisibleAnnotations", runtimeVisibleAnnotations.build());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.experimental.bytecode;
|
||||
|
||||
/**
|
||||
* Field builder.
|
||||
*
|
||||
* @param <S> the type of symbol representation
|
||||
* @param <T> the type of type descriptor representation
|
||||
* @param <E> the type of pool entries
|
||||
*/
|
||||
public class FieldBuilder<S, T, E> extends MemberBuilder<S, T, E, FieldBuilder<S, T, E>> {
|
||||
public FieldBuilder(CharSequence name, T type, PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
|
||||
super(name, type, poolHelper, typeHelper);
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.experimental.bytecode;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
public enum Flag {
|
||||
ACC_PUBLIC(0x0001),
|
||||
ACC_PROTECTED(0x0004),
|
||||
ACC_PRIVATE(0x0002),
|
||||
ACC_INTERFACE(0x0200),
|
||||
ACC_ENUM(0x4000),
|
||||
ACC_ANNOTATION(0x2000),
|
||||
ACC_SUPER(0x0020),
|
||||
ACC_ABSTRACT(0x0400),
|
||||
ACC_VOLATILE(0x0040),
|
||||
ACC_TRANSIENT(0x0080),
|
||||
ACC_SYNTHETIC(0x1000),
|
||||
ACC_STATIC(0x0008),
|
||||
ACC_FINAL(0x0010),
|
||||
ACC_SYNCHRONIZED(0x0020),
|
||||
ACC_BRIDGE(0x0040),
|
||||
ACC_VARARGS(0x0080),
|
||||
ACC_NATIVE(0x0100),
|
||||
ACC_STRICT(0x0800);
|
||||
|
||||
public int flag;
|
||||
|
||||
Flag(int flag) {
|
||||
this.flag = flag;
|
||||
}
|
||||
|
||||
static Flag[] parse(int flagsMask) {
|
||||
EnumSet<Flag> flags = EnumSet.noneOf(Flag.class);
|
||||
for (Flag f : Flag.values()) {
|
||||
if ((f.flag & flagsMask) != 0) {
|
||||
flags.add(f);
|
||||
}
|
||||
}
|
||||
return flags.stream().toArray(Flag[]::new);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user