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:
Paul Sandoz 2017-09-08 10:46:46 -07:00
parent 52d3bf29b2
commit e55a05957d
114 changed files with 11762 additions and 404 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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();

View File

@ -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?");

View File

@ -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);
}
// ------------------------------------------------------------------

View File

@ -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:

View File

@ -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);

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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") \

View File

@ -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);
}

View File

@ -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)) {

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -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);

View File

@ -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;

View File

@ -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 , _ );

View File

@ -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();

View File

@ -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))

View File

@ -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) \

View File

@ -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.

View File

@ -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);

View File

@ -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));

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

@ -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.

View File

@ -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)},

View File

@ -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>)") \

View File

@ -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;
}

View File

@ -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() {

View File

@ -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();

View File

@ -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) \
\
/*****************************/ \

View File

@ -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 :

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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 ) {

View File

@ -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

View File

@ -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; }
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}
}

View File

@ -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]);
}
}
}

View 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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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 &ldquo;causeless&rdquo; 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

View File

@ -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:

View File

@ -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
};

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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)) +

View File

@ -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; }

View File

@ -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.

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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";
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View 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);
}
}
}

View 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()));
}
}
}
}

View 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 -> {
});
}
}

View 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 -> {
});
}
}

View 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);
}
}

View 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

View 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");
}
}
}

View 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);
}
}

View 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");
}
}

View 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 -> {
});
}
}

View 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 -> {
});
}
}

View 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);
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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());
}
}

View File

@ -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());
}
}
}

View 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.
*/
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

View File

@ -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());
}
}
}

View File

@ -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);
}
}

View File

@ -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