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
Reviewed-by: acorn, coleenp, kvn
Contributed-by: lois.foltan@oracle.com, john.r.rose@oracle.com, paul.sandoz@oracle.com
--- a/src/hotspot/cpu/x86/interp_masm_x86.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/cpu/x86/interp_masm_x86.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -516,6 +516,8 @@
// 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)
--- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -836,7 +836,8 @@
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);
--- a/src/hotspot/cpu/x86/templateTable_x86.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/cpu/x86/templateTable_x86.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -419,7 +419,7 @@
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 @@
__ 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 @@
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 @@
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::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 @@
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);
--- a/src/hotspot/share/c1/c1_GraphBuilder.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/c1/c1_GraphBuilder.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -874,6 +874,8 @@
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 @@
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;
}
--- a/src/hotspot/share/ci/ciEnv.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/ci/ciEnv.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -584,8 +584,34 @@
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 @@
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 @@
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();
--- a/src/hotspot/share/ci/ciReplay.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/ci/ciReplay.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -721,6 +721,7 @@
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?");
--- a/src/hotspot/share/ci/ciStreams.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/ci/ciStreams.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -254,7 +254,8 @@
// 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);
}
// ------------------------------------------------------------------
--- a/src/hotspot/share/classfile/classFileParser.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/classfile/classFileParser.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -204,6 +204,21 @@
}
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 @@
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 @@
}
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:
--- a/src/hotspot/share/classfile/systemDictionary.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/classfile/systemDictionary.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -2641,6 +2641,81 @@
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 @@
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 @@
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 @@
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 @@
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);
--- a/src/hotspot/share/classfile/systemDictionary.hpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/classfile/systemDictionary.hpp Fri Sep 08 10:46:46 2017 -0700
@@ -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 @@
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 @@
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,
--- a/src/hotspot/share/classfile/verifier.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/classfile/verifier.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -2054,19 +2054,21 @@
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 @@
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 @@
// 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;
--- a/src/hotspot/share/classfile/verifier.hpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/classfile/verifier.hpp Fri Sep 08 10:46:46 2017 -0700
@@ -40,7 +40,8 @@
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;
--- a/src/hotspot/share/classfile/vmSymbols.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/classfile/vmSymbols.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -98,6 +98,14 @@
_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 @@
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;
--- a/src/hotspot/share/classfile/vmSymbols.hpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/classfile/vmSymbols.hpp Fri Sep 08 10:46:46 2017 -0700
@@ -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") \
--- a/src/hotspot/share/interpreter/bytecode.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/interpreter/bytecode.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -207,8 +207,7 @@
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 @@
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);
}
--- a/src/hotspot/share/interpreter/bytecodeInterpreter.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/interpreter/bytecodeInterpreter.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -2368,6 +2368,30 @@
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 @@
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 @@
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 @@
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 @@
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)) {
--- a/src/hotspot/share/interpreter/bytecodeTracer.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/interpreter/bytecodeTracer.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -367,6 +367,7 @@
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 @@
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);
}
--- a/src/hotspot/share/interpreter/interpreterRuntime.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -118,22 +118,54 @@
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
--- a/src/hotspot/share/interpreter/linkResolver.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/interpreter/linkResolver.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -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 @@
//------------------------------------------------------------------------------------------------------------------------
// 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 @@
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 @@
// 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 @@
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 @@
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 @@
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_dynamic_call(CallInfo& result,
+ int pool_index,
Handle bootstrap_specifier,
Symbol* method_name, Symbol* method_signature,
Klass* current_klass,
@@ -1775,12 +1763,13 @@
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);
}
--- a/src/hotspot/share/interpreter/linkResolver.hpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/interpreter/linkResolver.hpp Fri Sep 08 10:46:46 2017 -0700
@@ -274,7 +274,16 @@
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 @@
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);
--- a/src/hotspot/share/interpreter/rewriter.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/interpreter/rewriter.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -48,7 +48,11 @@
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 @@
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;
--- a/src/hotspot/share/interpreter/templateTable.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/interpreter/templateTable.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -278,7 +278,7 @@
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 , _ );
--- a/src/hotspot/share/interpreter/templateTable.hpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/interpreter/templateTable.hpp Fri Sep 08 10:46:46 2017 -0700
@@ -295,6 +295,7 @@
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();
--- a/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/jvmci/jvmciCompilerToVM.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -766,11 +766,10 @@
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))
--- a/src/hotspot/share/jvmci/vmStructs_jvmci.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/jvmci/vmStructs_jvmci.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -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) \
--- a/src/hotspot/share/memory/universe.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/memory/universe.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -111,6 +111,7 @@
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 @@
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 @@
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.
--- a/src/hotspot/share/memory/universe.hpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/memory/universe.hpp Fri Sep 08 10:46:46 2017 -0700
@@ -141,6 +141,7 @@
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 @@
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);
--- a/src/hotspot/share/oops/constantPool.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/oops/constantPool.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -607,7 +607,6 @@
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 @@
// 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 @@
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 @@
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 @@
}
}
+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 @@
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 @@
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 @@
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 @@
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 @@
// 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::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 @@
// 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 @@
}
} 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 @@
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 @@
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 @@
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 @@
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));
--- a/src/hotspot/share/oops/constantPool.hpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/oops/constantPool.hpp Fri Sep 08 10:46:46 2017 -0700
@@ -113,9 +113,10 @@
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 @@
// 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 @@
*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 @@
}
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 @@
}
#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 @@
// 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 @@
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 @@
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 @@
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 @@
// 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);
--- a/src/hotspot/share/oops/generateOopMap.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/oops/generateOopMap.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -1878,13 +1878,15 @@
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);
--- a/src/hotspot/share/opto/parse2.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/opto/parse2.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -1483,8 +1483,9 @@
// 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
--- a/src/hotspot/share/prims/jvm.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/prims/jvm.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -2212,6 +2212,8 @@
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;
}
--- a/src/hotspot/share/prims/jvmtiRedefineClasses.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/prims/jvmtiRedefineClasses.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -497,6 +497,7 @@
} 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 @@
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.
--- a/src/hotspot/share/prims/methodHandles.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/prims/methodHandles.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -1359,6 +1359,87 @@
}
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 @@
{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)},
--- a/src/hotspot/share/runtime/globals.hpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/runtime/globals.hpp Fri Sep 08 10:46:46 2017 -0700
@@ -3961,6 +3961,14 @@
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>)") \
--- a/src/hotspot/share/runtime/reflection.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/runtime/reflection.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -765,27 +765,26 @@
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");
- 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);
+ oop mirror_oop = ss->as_java_mirror(Handle(THREAD, accessing_klass->class_loader()),
+ Handle(THREAD, accessing_klass->protection_domain()),
+ SignatureStream::NCDFError,
+ CHECK_NULL);
+
+ // 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 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));
+ 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)) {
+ Klass* result = java_lang_Class::as_Klass(mirror());
+ if (result != NULL) {
+ trace_class_resolution(result);
+ }
}
- Klass* result =
- SystemDictionary::resolve_or_fail(signature,
- Handle(THREAD, k->class_loader()),
- Handle(THREAD, k->protection_domain()),
- true, CHECK_(Handle()));
-
- if (log_is_enabled(Debug, class, resolve)) {
- trace_class_resolution(result);
- }
-
- oop nt = result->java_mirror();
- return Handle(THREAD, nt);
+ return mirror;
}
--- a/src/hotspot/share/runtime/signature.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/runtime/signature.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -392,11 +392,19 @@
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() {
--- a/src/hotspot/share/runtime/signature.hpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/runtime/signature.hpp Fri Sep 08 10:46:46 2017 -0700
@@ -409,6 +409,11 @@
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();
--- a/src/hotspot/share/runtime/vmStructs.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/runtime/vmStructs.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -2339,6 +2339,7 @@
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 @@
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) \
\
/*****************************/ \
--- a/src/hotspot/share/utilities/constantTag.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/utilities/constantTag.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -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 @@
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 @@
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 @@
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 @@
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 :
--- a/src/hotspot/share/utilities/constantTag.hpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/utilities/constantTag.hpp Fri Sep 08 10:46:46 2017 -0700
@@ -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 @@
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 @@
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 @@
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 @@
_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;
--- a/src/hotspot/share/utilities/exceptions.cpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/utilities/exceptions.cpp Fri Sep 08 10:46:46 2017 -0700
@@ -403,6 +403,37 @@
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;
--- a/src/hotspot/share/utilities/exceptions.hpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/utilities/exceptions.hpp Fri Sep 08 10:46:46 2017 -0700
@@ -166,6 +166,8 @@
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;
--- a/src/hotspot/share/utilities/globalDefinitions.hpp Wed Jan 31 10:55:49 2018 -0800
+++ b/src/hotspot/share/utilities/globalDefinitions.hpp Fri Sep 08 10:46:46 2017 -0700
@@ -596,6 +596,10 @@
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 ) {
--- a/src/java.base/share/classes/java/lang/BootstrapMethodError.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/java.base/share/classes/java/lang/BootstrapMethodError.java Fri Sep 08 10:46:46 2017 -0700
@@ -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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/invoke/AbstractConstantGroup.java Fri Sep 08 10:46:46 2017 -0700
@@ -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; }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/invoke/BootstrapCallInfo.java Fri Sep 08 10:46:46 2017 -0700
@@ -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;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java Fri Sep 08 10:46:46 2017 -0700
@@ -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);
+ }
+ }
+ }
+}
--- a/src/java.base/share/classes/java/lang/invoke/CallSite.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/java.base/share/classes/java/lang/invoke/CallSite.java Fri Sep 08 10:46:46 2017 -0700
@@ -302,65 +302,10 @@
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 @@
// 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 @@
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]);
- }
- }
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/invoke/ConstantGroup.java Fri Sep 08 10:46:46 2017 -0700
@@ -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);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/invoke/DynamicConstant.java Fri Sep 08 10:46:46 2017 -0700
@@ -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);
+ }
+}
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java Fri Sep 08 10:46:46 2017 -0700
@@ -65,6 +65,12 @@
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 @@
* 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 @@
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 @@
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 @@
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;
}
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java Fri Sep 08 10:46:46 2017 -0700
@@ -142,4 +142,13 @@
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;
+ }
}
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Fri Sep 08 10:46:46 2017 -0700
@@ -98,12 +98,6 @@
* <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
--- a/src/java.base/share/classes/java/lang/invoke/package-info.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/java.base/share/classes/java/lang/invoke/package-info.java Fri Sep 08 10:46:46 2017 -0700
@@ -24,13 +24,12 @@
*/
/**
- * The {@code java.lang.invoke} package contains dynamic language support provided directly by
- * the Java core class libraries and virtual machine.
+ * The {@code java.lang.invoke} package provides low-level primitives for interacting
+ * with the Java Virtual Machine.
*
* <p>
- * As described in the Java Virtual Machine Specification,
- * certain types in this package have special relations to dynamic
- * language support in the virtual machine:
+ * As described in the Java Virtual Machine Specification, certain types in this package
+ * are given special treatment by the virtual machine:
* <ul>
* <li>The classes {@link java.lang.invoke.MethodHandle MethodHandle}
* {@link java.lang.invoke.VarHandle VarHandle} contain
@@ -40,77 +39,106 @@
* </li>
*
* <li>The JVM bytecode format supports immediate constants of
- * the classes {@link java.lang.invoke.MethodHandle MethodHandle} and {@link java.lang.invoke.MethodType MethodType}.
+ * the classes {@link java.lang.invoke.MethodHandle MethodHandle} and
+ * {@link java.lang.invoke.MethodType MethodType}.
+ * </li>
+ *
+ * <li>The {@code invokedynamic} instruction makes use of bootstrap {@code MethodHandle}
+ * constants to dynamically resolve {@code CallSite} objects for custom method invocation
+ * behavior.
+ * </li>
+ *
+ * <li>The {@code ldc} instruction makes use of bootstrap {@code MethodHandle} constants
+ * to dynamically resolve custom constant values.
* </li>
* </ul>
*
- * <h1><a id="jvm_mods"></a>Summary of relevant Java Virtual Machine changes</h1>
+ * <h1><a id="jvm_mods"></a>Dynamic resolution of call sites and constants</h1>
* The following low-level information summarizes relevant parts of the
* Java Virtual Machine specification. For full details, please see the
* current version of that specification.
*
- * Each occurrence of an {@code invokedynamic} instruction is called a <em>dynamic call site</em>.
- * <h2><a id="indyinsn"></a>{@code invokedynamic} instructions</h2>
- * A dynamic call site is originally in an unlinked state. In this state, there is
- * no target method for the call site to invoke.
+ * <h2><a id="indyinsn"></a>Dynamically-computed call sites</h2>
+ * An {@code invokedynamic} instruction is originally in an unlinked state.
+ * In this state, there is no target method for the instruction to invoke.
* <p>
- * Before the JVM can execute a dynamic call site (an {@code invokedynamic} instruction),
- * the call site must first be <em>linked</em>.
+ * Before the JVM can execute an {@code invokedynamic} instruction,
+ * the instruction must first be <em>linked</em>.
* Linking is accomplished by calling a <em>bootstrap method</em>
- * which is given the static information content of the call site,
- * and which must produce a {@link java.lang.invoke.MethodHandle method handle}
- * that gives the behavior of the call site.
+ * which is given the static information content of the call,
+ * and which must produce a {@link java.lang.invoke.CallSite}
+ * that gives the behavior of the invocation.
* <p>
* Each {@code invokedynamic} instruction statically specifies its own
* bootstrap method as a constant pool reference.
- * The constant pool reference also specifies the call site's name and type descriptor,
- * just like {@code invokevirtual} and the other invoke instructions.
+ * The constant pool reference also specifies the invocation's name and method type descriptor,
+ * just like {@code invokestatic} and the other invoke instructions.
+ *
+ * <h2><a id="condycon"></a>Dynamically-computed constants</h2>
+ * The constant pool may contain constants tagged {@code CONSTANT_Dynamic},
+ * equipped with bootstrap methods which perform their resolution.
+ * Such a <em>dynamic constant</em> is originally in an unresolved state.
+ * Before the JVM can use a dynamically-computed constant, it must first be <em>resolved</em>.
+ * Dynamically-computed constant resolution is accomplished by calling a <em>bootstrap method</em>
+ * which is given the static information content of the constant,
+ * and which must produce a value of the constant's statically declared type.
* <p>
- * Linking starts with resolving the constant pool entry for the
- * bootstrap method, and resolving a {@link java.lang.invoke.MethodType MethodType} object for
- * the type descriptor of the dynamic call site.
- * This resolution process may trigger class loading.
- * It may therefore throw an error if a class fails to load.
- * This error becomes the abnormal termination of the dynamic
- * call site execution.
- * Linkage does not trigger class initialization.
- * <p>
- * The bootstrap method is invoked on at least three values:
+ * Each dynamically-computed constant statically specifies its own
+ * bootstrap method as a constant pool reference.
+ * The constant pool reference also specifies the constant's name and field type descriptor,
+ * just like {@code getstatic} and the other field reference instructions.
+ * (Roughly speaking, a dynamically-computed constant is to a dynamically-computed call site
+ * as a {@code CONSTANT_Fieldref} is to a {@code CONSTANT_Methodref}.)
+ *
+ * <h2><a id="bsm"></a>Execution of bootstrap methods</h2>
+ * Resolving a dynamically-computed call site or constant
+ * starts with resolving constants from the constant pool for the
+ * following items:
* <ul>
- * <li>a {@code MethodHandles.Lookup}, a lookup object on the <em>caller class</em>
- * in which dynamic call site occurs </li>
- * <li>a {@code String}, the method name mentioned in the call site </li>
- * <li>a {@code MethodType}, the resolved type descriptor of the call </li>
- * <li>optionally, any number of additional static arguments taken from the constant pool </li>
+ * <li>the bootstrap method, a {@code CONSTANT_MethodHandle}</li>
+ * <li>the {@code Class} or {@code MethodType} derived from
+ * type component of the {@code CONSTANT_NameAndType} descriptor</li>
+ * <li>static arguments, if any (note that static arguments can themselves be
+ * dynamically-computed constants)</li>
* </ul>
* <p>
- * In all cases, bootstrap method invocation is as if by
- * {@link java.lang.invoke.MethodHandle#invokeWithArguments MethodHandle.invokeWithArguments},
- * (This is also equivalent to
- * {@linkplain java.lang.invoke.MethodHandle#invoke generic invocation}
- * if the number of arguments is small enough.)
+ * The bootstrap method is then invoked, as if by
+ * {@link java.lang.invoke.MethodHandle#invoke MethodHandle.invoke},
+ * with the following arguments:
+ * <ul>
+ * <li>a {@code MethodHandles.Lookup}, which is a lookup object on the <em>caller class</em>
+ * in which dynamically-computed constant or call site occurs</li>
+ * <li>a {@code String}, the name mentioned in the {@code CONSTANT_NameAndType}</li>
+ * <li>a {@code MethodType} or {@code Class}, the resolved type descriptor of the {@code CONSTANT_NameAndType}</li>
+ * <li>a {@code Class}, the resolved type descriptor of the constant, if it is a dynamic constant </li>
+ * <li>the additional resolved static arguments, if any</li>
+ * </ul>
* <p>
- * For an {@code invokedynamic} instruction, the
- * returned result must be convertible to a non-null reference to a
+ * For a dynamically-computed call site, the returned result must be a non-null reference to a
* {@link java.lang.invoke.CallSite CallSite}.
- * If the returned result cannot be converted to the expected type,
- * {@link java.lang.BootstrapMethodError BootstrapMethodError} is thrown.
* The type of the call site's target must be exactly equal to the type
- * derived from the dynamic call site's type descriptor and passed to
- * the bootstrap method, otherwise a {@code BootstrapMethodError} is thrown.
- * On success the call site then becomes permanently linked to the dynamic call
- * site.
+ * derived from the invocation's type descriptor and passed to
+ * the bootstrap method. If these conditions are not met, a {@code BootstrapMethodError} is thrown.
+ * On success the call site then becomes permanently linked to the {@code invokedynamic}
+ * instruction.
* <p>
- * If an exception, {@code E} say, occurs when linking the call site then the
- * linkage fails and terminates abnormally. {@code E} is rethrown if the type of
+ * For a dynamically-computed constant, the result of the bootstrap method is cached
+ * as the resolved constant value.
+ * <p>
+ * If an exception, {@code E} say, occurs during execution of the bootstrap method, then
+ * resolution fails and terminates abnormally. {@code E} is rethrown if the type of
* {@code E} is {@code Error} or a subclass, otherwise a
* {@code BootstrapMethodError} that wraps {@code E} is thrown.
- * If this happens, the same {@code Error} or subclass will the thrown for all
- * subsequent attempts to execute the dynamic call site.
- * <h2>timing of linkage</h2>
- * A dynamic call site is linked just before its first execution.
+ * If this happens, the same error will be thrown for all
+ * subsequent attempts to execute the {@code invokedynamic} instruction or load the
+ * dynamically-computed constant.
+ *
+ * <h2>Timing of resolution</h2>
+ * An {@code invokedynamic} instruction is linked just before its first execution.
+ * A dynamically-computed constant is resolved just before the first time it is used
+ * (by pushing it on the stack or linking it as a bootstrap method parameter).
* The bootstrap method call implementing the linkage occurs within
- * a thread that is attempting a first execution.
+ * a thread that is attempting a first execution or first use.
* <p>
* If there are several such threads, the bootstrap method may be
* invoked in several threads concurrently.
@@ -119,7 +147,7 @@
* In any case, every {@code invokedynamic} instruction is either
* unlinked or linked to a unique {@code CallSite} object.
* <p>
- * In an application which requires dynamic call sites with individually
+ * In an application which requires {@code invokedynamic} instructions with individually
* mutable behaviors, their bootstrap methods should produce distinct
* {@link java.lang.invoke.CallSite CallSite} objects, one for each linkage request.
* Alternatively, an application can link a single {@code CallSite} object
@@ -127,53 +155,46 @@
* a change to the target method will become visible at each of
* the instructions.
* <p>
- * If several threads simultaneously execute a bootstrap method for a single dynamic
- * call site, the JVM must choose one {@code CallSite} object and install it visibly to
+ * If several threads simultaneously execute a bootstrap method for a single dynamically-computed
+ * call site or constant, the JVM must choose one bootstrap method result and install it visibly to
* all threads. Any other bootstrap method calls are allowed to complete, but their
- * results are ignored, and their dynamic call site invocations proceed with the originally
- * chosen target object.
+ * results are ignored.
* <p style="font-size:smaller;">
* <em>Discussion:</em>
- * These rules do not enable the JVM to duplicate dynamic call sites,
+ * These rules do not enable the JVM to share call sites,
* or to issue “causeless” bootstrap method calls.
- * Every dynamic call site transitions at most once from unlinked to linked,
+ * Every {@code invokedynamic} instruction transitions at most once from unlinked to linked,
* just before its first invocation.
* There is no way to undo the effect of a completed bootstrap method call.
*
- * <h2>types of bootstrap methods</h2>
- * As long as each bootstrap method can be correctly invoked
- * by {@code MethodHandle.invoke}, its detailed type is arbitrary.
+ * <h2>Types of bootstrap methods</h2>
+ * For a dynamically-computed call site, the bootstrap method is invoked with parameter
+ * types {@code MethodHandles.Lookup}, {@code String}, {@code MethodType}, and the types
+ * of any static arguments; the return type is {@code CallSite}. For a
+ * dynamically-computed constant, the bootstrap method is invoked with parameter types
+ * {@code MethodHandles.Lookup}, {@code String}, {@code Class}, and the types of any
+ * static arguments; the return type is the type represented by the {@code Class}.
+ *
+ * Because {@link java.lang.invoke.MethodHandle#invoke MethodHandle.invoke} allows for
+ * adaptations between the invoked method type and the method handle's method type,
+ * there is flexibility in the declaration of the bootstrap method.
* For example, the first argument could be {@code Object}
* instead of {@code MethodHandles.Lookup}, and the return type
* could also be {@code Object} instead of {@code CallSite}.
* (Note that the types and number of the stacked arguments limit
* the legal kinds of bootstrap methods to appropriately typed
- * static methods and constructors of {@code CallSite} subclasses.)
+ * static methods and constructors.)
* <p>
- * If a given {@code invokedynamic} instruction specifies no static arguments,
- * the instruction's bootstrap method will be invoked on three arguments,
- * conveying the instruction's caller class, name, and method type.
- * If the {@code invokedynamic} instruction specifies one or more static arguments,
- * those values will be passed as additional arguments to the method handle.
- * (Note that because there is a limit of 255 arguments to any method,
- * at most 251 extra arguments can be supplied to a non-varargs bootstrap method,
- * since the bootstrap method
- * handle itself and its first three arguments must also be stacked.)
- * The bootstrap method will be invoked as if by {@code MethodHandle.invokeWithArguments}.
- * A variable-arity bootstrap method can accept thousands of static arguments,
- * subject only by limits imposed by the class-file format.
- * <p>
- * The normal argument conversion rules for {@code MethodHandle.invoke} apply to all stacked arguments.
- * For example, if a pushed value is a primitive type, it may be converted to a reference by boxing conversion.
+ * If a pushed value is a primitive type, it may be converted to a reference by boxing conversion.
* If the bootstrap method is a variable arity method (its modifier bit {@code 0x0080} is set),
* then some or all of the arguments specified here may be collected into a trailing array parameter.
* (This is not a special rule, but rather a useful consequence of the interaction
* between {@code CONSTANT_MethodHandle} constants, the modifier bit for variable arity methods,
* and the {@link java.lang.invoke.MethodHandle#asVarargsCollector asVarargsCollector} transformation.)
* <p>
- * Given these rules, here are examples of legal bootstrap method declarations,
- * given various numbers {@code N} of extra arguments.
+ * Given these rules, here are examples of legal bootstrap method declarations for
+ * dynamically-computed call sites, given various numbers {@code N} of extra arguments.
* The first row (marked {@code *}) will work for any number of extra arguments.
* <table class="plain" style="vertical-align:top">
* <caption style="display:none">Static argument types</caption>
@@ -208,28 +229,27 @@
* {@code String}.
* The other examples work with all types of extra arguments.
* <p>
- * As noted above, the actual method type of the bootstrap method can vary.
- * For example, the fourth argument could be {@code MethodHandle},
- * if that is the type of the corresponding constant in
- * the {@code CONSTANT_InvokeDynamic} entry.
- * In that case, the {@code MethodHandle.invoke} call will pass the extra method handle
- * constant as an {@code Object}, but the type matching machinery of {@code MethodHandle.invoke}
- * will cast the reference back to {@code MethodHandle} before invoking the bootstrap method.
- * (If a string constant were passed instead, by badly generated code, that cast would then fail,
- * resulting in a {@code BootstrapMethodError}.)
- * <p>
- * Note that, as a consequence of the above rules, the bootstrap method may accept a primitive
- * argument, if it can be represented by a constant pool entry.
+ * Since dynamically-computed constants can be provided as static arguments to bootstrap
+ * methods, there are no limitations on the types of bootstrap arguments.
* However, arguments of type {@code boolean}, {@code byte}, {@code short}, or {@code char}
- * cannot be created for bootstrap methods, since such constants cannot be directly
- * represented in the constant pool, and the invocation of the bootstrap method will
+ * cannot be <em>directly</em> supplied by {@code CONSTANT_Integer}
+ * constant pool entries, since the {@code asType} conversions do
* not perform the necessary narrowing primitive conversions.
* <p>
- * Extra bootstrap method arguments are intended to allow language implementors
- * to safely and compactly encode metadata.
- * In principle, the name and extra arguments are redundant,
- * since each call site could be given its own unique bootstrap method.
- * Such a practice would be likely to produce large class files and constant pools.
+ * In the above examples, the return type is always {@code CallSite},
+ * but that is not a necessary feature of bootstrap methods.
+ * In the case of a dynamically-computed call site, the only requirement is that
+ * the return type of the bootstrap method must be convertible
+ * (using the {@code asType} conversions) to {@code CallSite}, which
+ * means the bootstrap method return type might be {@code Object} or
+ * {@code ConstantCallSite}.
+ * In the case of a dynamically-resolved constant, the return type of the bootstrap
+ * method must be convertible to the type of the constant, as
+ * represented by its field type descriptor. For example, if the
+ * dynamic constant has a field type descriptor of {@code "C"}
+ * ({@code char}) then the bootstrap method return type could be
+ * {@code Object}, {@code Character}, or {@code char}, but not
+ * {@code int} or {@code Integer}.
*
* @author John Rose, JSR 292 EG
* @since 1.7
--- a/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassReader.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassReader.java Fri Sep 08 10:46:46 2017 -0700
@@ -205,6 +205,10 @@
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:
--- a/src/java.base/share/native/include/classfile_constants.h.template Wed Jan 31 10:55:49 2018 -0800
+++ b/src/java.base/share/native/include/classfile_constants.h.template Fri Sep 08 10:46:46 2017 -0700
@@ -107,6 +107,7 @@
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
};
--- a/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/GraalFilters.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/GraalFilters.java Fri Sep 08 10:46:46 2017 -0700
@@ -30,6 +30,8 @@
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 @@
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;
}
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassFile.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassFile.java Fri Sep 08 10:46:46 2017 -0700
@@ -84,6 +84,7 @@
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;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java Fri Sep 08 10:46:46 2017 -0700
@@ -426,6 +426,7 @@
case CONSTANT_NameandType:
case CONSTANT_Integer:
case CONSTANT_Float:
+ case CONSTANT_Dynamic:
case CONSTANT_InvokeDynamic:
bp = bp + 4;
break;
@@ -501,6 +502,7 @@
case CONSTANT_MethodType:
skipBytes(3);
break;
+ case CONSTANT_Dynamic:
case CONSTANT_InvokeDynamic:
skipBytes(5);
break;
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ConstantPool.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ConstantPool.java Fri Sep 08 10:46:46 2017 -0700
@@ -296,7 +296,7 @@
// 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 @@
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 @@
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 @@
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;
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ClassConstants.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ClassConstants.java Fri Sep 08 10:46:46 2017 -0700
@@ -42,7 +42,7 @@
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
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/jcore/ClassWriter.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/jcore/ClassWriter.java Fri Sep 08 10:46:46 2017 -0700
@@ -296,6 +296,18 @@
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);
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java Fri Sep 08 10:46:46 2017 -0700
@@ -558,6 +558,12 @@
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)) +
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/ConstantTag.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/ConstantTag.java Fri Sep 08 10:46:46 2017 -0700
@@ -42,7 +42,7 @@
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 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; }
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java Fri Sep 08 10:46:46 2017 -0700
@@ -238,6 +238,8 @@
*/
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.
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotConstantPool.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotConstantPool.java Fri Sep 08 10:46:46 2017 -0700
@@ -491,6 +491,14 @@
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;
--- a/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java Fri Sep 08 10:46:46 2017 -0700
@@ -214,8 +214,10 @@
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);
--- a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassTranslator.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassTranslator.java Fri Sep 08 10:46:46 2017 -0700
@@ -28,6 +28,7 @@
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 @@
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);
--- a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassWriter.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassWriter.java Fri Sep 08 10:46:46 2017 -0700
@@ -283,6 +283,12 @@
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);
--- a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ConstantPool.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ConstantPool.java Fri Sep 08 10:46:46 2017 -0700
@@ -116,6 +116,7 @@
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 @@
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 @@
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 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);
--- a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/Dependencies.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/Dependencies.java Fri Sep 08 10:46:46 2017 -0700
@@ -688,6 +688,11 @@
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;
}
--- a/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ReferenceFinder.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/classfile/ReferenceFinder.java Fri Sep 08 10:46:46 2017 -0700
@@ -160,6 +160,11 @@
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;
}
--- a/src/jdk.jdeps/share/classes/com/sun/tools/javap/ConstantWriter.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/javap/ConstantWriter.java Fri Sep 08 10:46:46 2017 -0700
@@ -104,6 +104,13 @@
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 @@
return "InterfaceMethod";
case CONSTANT_InvokeDynamic:
return "InvokeDynamic";
+ case CONSTANT_Dynamic:
+ return "Dynamic";
case CONSTANT_NameAndType:
return "NameAndType";
default:
@@ -346,6 +355,15 @@
}
}
+ 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";
}
--- a/src/jdk.jdeps/share/classes/com/sun/tools/jdeprscan/scan/CPSelector.java Wed Jan 31 10:55:49 2018 -0800
+++ b/src/jdk.jdeps/share/classes/com/sun/tools/jdeprscan/scan/CPSelector.java Fri Sep 08 10:46:46 2017 -0700
@@ -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 @@
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;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/common/test/java/lang/invoke/lib/InstructionHelper.java Fri Sep 08 10:46:46 2017 -0700
@@ -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);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/BootstrapMethodJumboArgsTest.java Fri Sep 08 10:46:46 2017 -0700
@@ -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);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyBSMException.java Fri Sep 08 10:46:46 2017 -0700
@@ -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);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyBSMInvocation.java Fri Sep 08 10:46:46 2017 -0700
@@ -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()));
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyBSMValidationTest.java Fri Sep 08 10:46:46 2017 -0700
@@ -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 -> {
+ });
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyNameValidationTest.java Fri Sep 08 10:46:46 2017 -0700
@@ -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 -> {
+ });
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyNestedTest.java Fri Sep 08 10:46:46 2017 -0700
@@ -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);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyNestedTest_Code.jcod Fri Sep 08 10:46:46 2017 -0700
@@ -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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyRepeatFailedResolution.java Fri Sep 08 10:46:46 2017 -0700
@@ -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");
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyReturnPrimitiveTest.java Fri Sep 08 10:46:46 2017 -0700
@@ -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);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyStaticArgumentsTest.java Fri Sep 08 10:46:46 2017 -0700
@@ -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");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyTypeValidationTest.java Fri Sep 08 10:46:46 2017 -0700
@@ -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 -> {
+ });
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyWithGarbageTest.java Fri Sep 08 10:46:46 2017 -0700
@@ -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 -> {
+ });
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/condy/CondyWrongType.java Fri Sep 08 10:46:46 2017 -0700
@@ -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);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/AbstractBuilder.java Fri Sep 08 10:46:46 2017 -0700
@@ -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;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/AnnotationsBuilder.java Fri Sep 08 10:46:46 2017 -0700
@@ -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;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/AttributeBuilder.java Fri Sep 08 10:46:46 2017 -0700
@@ -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);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/BasicClassBuilder.java Fri Sep 08 10:46:46 2017 -0700
@@ -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());
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/BasicTypeHelper.java Fri Sep 08 10:46:46 2017 -0700
@@ -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());
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/BytePoolHelper.java Fri Sep 08 10:46:46 2017 -0700
@@ -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());
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/ClassBuilder.java Fri Sep 08 10:46:46 2017 -0700
@@ -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();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/CodeBuilder.java Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,1339 @@
+/*
+ * 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 jdk.experimental.bytecode.PoolHelper.StaticArgListBuilder;
+
+import java.lang.invoke.MethodHandle;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.ToIntFunction;
+
+/**
+ * Builder for class file code attributes. A code attribute is defined:
+ * <pre>
+ * {@code
+ * Code_attribute {
+ * u2 attribute_name_index;
+ * u4 attribute_length;
+ * u2 max_stack;
+ * u2 max_locals;
+ * u4 code_length;
+ * u1 code[code_length];
+ * u2 exception_table_length;
+ * { u2 start_pc;
+ * u2 end_pc;
+ * u2 handler_pc;
+ * u2 catch_type;
+ * } exception_table[exception_table_length];
+ * u2 attributes_count;
+ * attribute_info attributes[attributes_count];
+ * } }
+ * </pre>
+ *
+ * @param <S> the type of symbol representation
+ * @param <T> the type of type descriptors representation
+ * @param <E> the type of pool entries
+ * @param <C> the type of this code builder
+ */
+public class CodeBuilder<S, T, E, C extends CodeBuilder<S, T, E, C>> extends AttributeBuilder<S, T, E, C> {
+
+ protected GrowableByteBuffer code = new GrowableByteBuffer();
+ GrowableByteBuffer catchers = new GrowableByteBuffer();
+ GrowableByteBuffer stackmaps = new GrowableByteBuffer();
+ MethodBuilder<S, T, E> methodBuilder;
+ int ncatchers;
+ int stacksize = -1;
+ int localsize = -1;
+ int nstackmaps = 0;
+
+ public enum JumpMode {
+ NARROW,
+ WIDE;
+ }
+
+ CodeBuilder(MethodBuilder<S, T, E> methodBuilder) {
+ super(methodBuilder.poolHelper, methodBuilder.typeHelper);
+ this.methodBuilder = methodBuilder;
+ }
+
+ public C getstatic(S owner, CharSequence name, T type) {
+ emitOp(Opcode.GETSTATIC, type);
+ code.writeChar(poolHelper.putFieldRef(owner, name, type));
+ return thisBuilder();
+ }
+
+ public C putstatic(S owner, CharSequence name, T type) {
+ emitOp(Opcode.PUTSTATIC, type);
+ code.writeChar(poolHelper.putFieldRef(owner, name, type));
+ return thisBuilder();
+ }
+
+ public C getfield(S owner, CharSequence name, T type) {
+ emitOp(Opcode.GETFIELD, type);
+ code.writeChar(poolHelper.putFieldRef(owner, name, type));
+ return thisBuilder();
+ }
+
+ public C vgetfield(S owner, CharSequence name, T type) {
+ emitOp(Opcode.VGETFIELD, type);
+ code.writeChar(poolHelper.putFieldRef(owner, name, type));
+ return thisBuilder();
+ }
+
+ public C putfield(S owner, CharSequence name, T type) {
+ emitOp(Opcode.PUTFIELD, type);
+ code.writeChar(poolHelper.putFieldRef(owner, name, type));
+ return thisBuilder();
+ }
+
+ public C invokevirtual(S owner, CharSequence name, T type, boolean isInterface) {
+ emitOp(Opcode.INVOKEVIRTUAL, type);
+ code.writeChar(poolHelper.putMethodRef(owner, name, type, isInterface));
+ return thisBuilder();
+ }
+
+ public C invokespecial(S owner, CharSequence name, T type, boolean isInterface) {
+ emitOp(Opcode.INVOKESPECIAL, type);
+ code.writeChar(poolHelper.putMethodRef(owner, name, type, isInterface));
+ return thisBuilder();
+ }
+
+ public C invokestatic(S owner, CharSequence name, T type, boolean isInterface) {
+ emitOp(Opcode.INVOKESTATIC, type);
+ code.writeChar(poolHelper.putMethodRef(owner, name, type, isInterface));
+ return thisBuilder();
+ }
+
+ public C invokeinterface(S owner, CharSequence name, T type) {
+ emitOp(Opcode.INVOKEINTERFACE, type);
+ code.writeChar(poolHelper.putMethodRef(owner, name, type, true));
+ int nargs = 1;
+ Iterator<T> it = typeHelper.parameterTypes(type);
+ while (it.hasNext()) {
+ nargs += typeHelper.tag(it.next()).width;
+ }
+ code.writeByte(nargs);
+ code.writeByte(0);
+ return thisBuilder();
+ }
+
+ public C invokedynamic(CharSequence invokedName, T invokedType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs) {
+ emitOp(Opcode.INVOKEDYNAMIC, invokedType);
+ code.writeChar(poolHelper.putInvokeDynamic(invokedName, invokedType, bsmClass, bsmName, bsmType, staticArgs));
+ code.writeChar(0); //padding
+ return thisBuilder();
+ }
+
+ public C new_(S clazz) {
+ emitOp(Opcode.NEW, clazz);
+ code.writeChar(poolHelper.putClass(clazz));
+ return thisBuilder();
+ }
+
+ public C vnew_(S clazz, CharSequence name, T desc) {
+ emitOp(Opcode.VNEW, clazz);
+ code.writeChar(poolHelper.putMethodRef(clazz, name, desc, false));
+ return thisBuilder();
+ }
+
+ public C vnewarray(S array) {
+ emitOp(Opcode.VNEWARRAY, array);
+ code.writeChar(poolHelper.putClass(array));
+ return thisBuilder();
+ }
+
+ public C newarray(TypeTag tag) {
+ emitOp(Opcode.NEWARRAY, tag);
+ int newarraycode = tag.newarraycode;
+ if (newarraycode == -1) {
+ throw new IllegalStateException("Bad tag " + tag);
+ }
+ code.writeByte(newarraycode);
+ return thisBuilder();
+ }
+
+ public C anewarray(S array) {
+ emitOp(Opcode.ANEWARRAY, array);
+ code.writeChar(poolHelper.putClass(array));
+ return thisBuilder();
+ }
+
+ public C checkcast(S target) {
+ emitOp(Opcode.CHECKCAST);
+ code.writeChar(poolHelper.putClass(target));
+ return thisBuilder();
+ }
+
+ public C instanceof_(S target) {
+ emitOp(Opcode.INSTANCEOF);
+ code.writeChar(poolHelper.putClass(target));
+ return thisBuilder();
+ }
+
+ public C multianewarray(S array, byte dims) {
+ emitOp(Opcode.MULTIANEWARRAY, new Object[]{array, dims});
+ code.writeChar(poolHelper.putClass(array)).writeByte(dims);
+ return thisBuilder();
+ }
+
+ public C multivnewarray(S array, byte dims) {
+ emitOp(Opcode.MULTIVNEWARRAY, new Object[]{array, dims});
+ code.writeChar(poolHelper.putClass(array)).writeByte(dims);
+ return thisBuilder();
+ }
+
+ public C vbox(S target) {
+ emitOp(Opcode.VBOX, target);
+ code.writeChar(poolHelper.putClass(target));
+ return thisBuilder();
+ }
+
+ public C vunbox(S target) {
+ emitOp(Opcode.VUNBOX, target);
+ code.writeChar(poolHelper.putClass(target));
+ return thisBuilder();
+ }
+
+ public C ldc(int i) {
+ return ldc(pool -> pool.putInt(i), false);
+ }
+
+ public C ldc(long l) {
+ return ldc(pool -> pool.putLong(l), true);
+ }
+
+ public C ldc(float f) {
+ return ldc(pool -> pool.putFloat(f), false);
+ }
+
+ public C ldc(double d) {
+ return ldc(pool -> pool.putDouble(d), true);
+ }
+
+ public C ldc(String s) {
+ return ldc(pool -> pool.putString(s), false);
+ }
+
+ public C ldc(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs) {
+ boolean fat = typeHelper.tag(constType).width() == 2;
+ return ldc(pool -> pool.putDynamicConstant(constName, constType, bsmClass, bsmName, bsmType, staticArgs), fat);
+ }
+
+ public <Z> C ldc(Z z, BiFunction<PoolHelper<S, T, E>, Z, Integer> poolFunc) {
+ return ldc(pool -> poolFunc.apply(pool, z), false);
+ }
+
+ protected C ldc(ToIntFunction<PoolHelper<S, T, E>> indexFunc, boolean fat) {
+ // @@@ This should probably be abstract
+ int index = indexFunc.applyAsInt(poolHelper);
+ return ldc(index, null, fat);
+ }
+
+ protected final C ldc(int index, T type, boolean fat) {
+ if (fat) {
+ emitOp(Opcode.LDC2_W, type);
+ code.writeChar(index);
+ } else if (index > 63) {
+ emitOp(Opcode.LDC_W, type);
+ code.writeChar(index);
+ } else {
+ emitOp(Opcode.LDC, type);
+ code.writeByte(index);
+ }
+ return thisBuilder();
+ }
+
+ //other non-CP dependent opcodes
+ public C areturn() {
+ return emitOp(Opcode.ARETURN);
+ }
+
+ public C ireturn() {
+ return emitOp(Opcode.IRETURN);
+ }
+
+ public C freturn() {
+ return emitOp(Opcode.FRETURN);
+ }
+
+ public C lreturn() {
+ return emitOp(Opcode.LRETURN);
+ }
+
+ public C dreturn() {
+ return emitOp(Opcode.DRETURN);
+ }
+
+ public C return_() {
+ return emitOp(Opcode.RETURN);
+ }
+
+ public C vreturn() {
+ return emitOp(Opcode.VRETURN);
+ }
+
+ protected C emitWideIfNeeded(Opcode opcode, int n) {
+ boolean wide = n > Byte.MAX_VALUE;
+ if (wide) {
+ wide();
+ }
+ emitOp(opcode, n);
+ if (wide) {
+ code.writeChar(n);
+ } else {
+ code.writeByte(n);
+ }
+ return thisBuilder();
+ }
+
+ protected C emitWideIfNeeded(Opcode opcode, int n, int v) {
+ boolean wide = n > Byte.MAX_VALUE || v > Byte.MAX_VALUE;
+ if (wide) {
+ wide();
+ }
+ emitOp(opcode, n);
+ if (wide) {
+ code.writeChar(n).writeChar(v);
+ } else {
+ code.writeByte(n).writeByte(v);
+ }
+ return thisBuilder();
+ }
+
+ public TypedBuilder typed(TypeTag typeTag) {
+ return typed(typeTag, _unused -> new TypedBuilder());
+ }
+
+ protected <TB extends TypedBuilder> TB typed(TypeTag typeTag, Function<TypeTag, TB> typedBuilderFunc) {
+ emitOp(Opcode.TYPED);
+ code.writeChar(poolHelper.putType(typeHelper.fromTag(typeTag)));
+ return typedBuilderFunc.apply(typeTag);
+ }
+
+ public class TypedBuilder {
+ public C aload_0() {
+ return CodeBuilder.this.aload_0();
+ }
+
+ public C aload_1() {
+ return CodeBuilder.this.aload_1();
+ }
+
+ public C aload_2() {
+ return CodeBuilder.this.aload_2();
+ }
+
+ public C aload_3() {
+ return CodeBuilder.this.aload_3();
+ }
+
+ public C aload(int n) {
+ return CodeBuilder.this.aload(n);
+ }
+
+ public C astore_0() {
+ return CodeBuilder.this.astore_0();
+ }
+
+ public C astore_1() {
+ return CodeBuilder.this.astore_1();
+ }
+
+ public C astore_2() {
+ return CodeBuilder.this.astore_2();
+ }
+
+ public C astore_3() {
+ return CodeBuilder.this.astore_3();
+ }
+
+ public C astore(int n) {
+ return CodeBuilder.this.astore(n);
+ }
+
+ public C aaload() {
+ return CodeBuilder.this.aaload();
+ }
+
+ public C aastore() {
+ return CodeBuilder.this.aastore();
+ }
+
+ public C areturn() {
+ return CodeBuilder.this.areturn();
+ }
+
+ public C anewarray(S s) {
+ return CodeBuilder.this.anewarray(s);
+ }
+
+ public C aconst_null() {
+ return CodeBuilder.this.aconst_null();
+ }
+
+ public C if_acmpeq(short target) {
+ return CodeBuilder.this.if_acmpeq(target);
+ }
+
+ public C if_acmpne(short target) {
+ return CodeBuilder.this.if_acmpeq(target);
+ }
+ }
+
+ public C vload(int i) {
+ return emitWideIfNeeded(Opcode.VLOAD, i);
+ }
+
+ public C aload(int i) {
+ return emitWideIfNeeded(Opcode.ALOAD, i);
+ }
+
+ public C iload(int i) {
+ return emitWideIfNeeded(Opcode.ILOAD, i);
+ }
+
+ public C fload(int i) {
+ return emitWideIfNeeded(Opcode.FLOAD, i);
+ }
+
+ public C lload(int i) {
+ return emitWideIfNeeded(Opcode.LLOAD, i);
+ }
+
+ public C dload(int i) {
+ return emitWideIfNeeded(Opcode.DLOAD, i);
+ }
+
+ public C aload_0() {
+ return emitOp(Opcode.ALOAD_0);
+ }
+
+ public C iload_0() {
+ return emitOp(Opcode.ILOAD_0);
+ }
+
+ public C fload_0() {
+ return emitOp(Opcode.FLOAD_0);
+ }
+
+ public C lload_0() {
+ return emitOp(Opcode.LLOAD_0);
+ }
+
+ public C dload_0() {
+ return emitOp(Opcode.DLOAD_0);
+ }
+
+ public C aload_1() {
+ return emitOp(Opcode.ALOAD_1);
+ }
+
+ public C iload_1() {
+ return emitOp(Opcode.ILOAD_1);
+ }
+
+ public C fload_1() {
+ return emitOp(Opcode.FLOAD_1);
+ }
+
+ public C lload_1() {
+ return emitOp(Opcode.LLOAD_1);
+ }
+
+ public C dload_1() {
+ return emitOp(Opcode.DLOAD_1);
+ }
+
+ public C aload_2() {
+ return emitOp(Opcode.ALOAD_2);
+ }
+
+ public C iload_2() {
+ return emitOp(Opcode.ILOAD_2);
+ }
+
+ public C fload_2() {
+ return emitOp(Opcode.FLOAD_2);
+ }
+
+ public C lload_2() {
+ return emitOp(Opcode.LLOAD_2);
+ }
+
+ public C dload_2() {
+ return emitOp(Opcode.DLOAD_2);
+ }
+
+ public C aload_3() {
+ return emitOp(Opcode.ALOAD_3);
+ }
+
+ public C iload_3() {
+ return emitOp(Opcode.ILOAD_3);
+ }
+
+ public C fload_3() {
+ return emitOp(Opcode.FLOAD_3);
+ }
+
+ public C lload_3() {
+ return emitOp(Opcode.LLOAD_3);
+ }
+
+ public C dload_3() {
+ return emitOp(Opcode.DLOAD_3);
+ }
+
+ public C vstore(int i) {
+ return emitWideIfNeeded(Opcode.VSTORE, i);
+ }
+
+ public C astore(int i) {
+ return emitWideIfNeeded(Opcode.ASTORE, i);
+ }
+
+ public C istore(int i) {
+ return emitWideIfNeeded(Opcode.ISTORE, i);
+ }
+
+ public C fstore(int i) {
+ return emitWideIfNeeded(Opcode.FSTORE, i);
+ }
+
+ public C lstore(int i) {
+ return emitWideIfNeeded(Opcode.LSTORE, i);
+ }
+
+ public C dstore(int i) {
+ return emitWideIfNeeded(Opcode.DSTORE, i);
+ }
+
+ public C astore_0() {
+ return emitOp(Opcode.ASTORE_0);
+ }
+
+ public C istore_0() {
+ return emitOp(Opcode.ISTORE_0);
+ }
+
+ public C fstore_0() {
+ return emitOp(Opcode.FSTORE_0);
+ }
+
+ public C lstore_0() {
+ return emitOp(Opcode.LSTORE_0);
+ }
+
+ public C dstore_0() {
+ return emitOp(Opcode.DSTORE_0);
+ }
+
+ public C astore_1() {
+ return emitOp(Opcode.ASTORE_1);
+ }
+
+ public C istore_1() {
+ return emitOp(Opcode.ISTORE_1);
+ }
+
+ public C fstore_1() {
+ return emitOp(Opcode.FSTORE_1);
+ }
+
+ public C lstore_1() {
+ return emitOp(Opcode.LSTORE_1);
+ }
+
+ public C dstore_1() {
+ return emitOp(Opcode.DSTORE_1);
+ }
+
+ public C astore_2() {
+ return emitOp(Opcode.ASTORE_2);
+ }
+
+ public C istore_2() {
+ return emitOp(Opcode.ISTORE_2);
+ }
+
+ public C fstore_2() {
+ return emitOp(Opcode.FSTORE_2);
+ }
+
+ public C lstore_2() {
+ return emitOp(Opcode.LSTORE_2);
+ }
+
+ public C dstore_2() {
+ return emitOp(Opcode.DSTORE_2);
+ }
+
+ public C astore_3() {
+ return emitOp(Opcode.ASTORE_3);
+ }
+
+ public C istore_3() {
+ return emitOp(Opcode.ISTORE_3);
+ }
+
+ public C fstore_3() {
+ return emitOp(Opcode.FSTORE_3);
+ }
+
+ public C lstore_3() {
+ return emitOp(Opcode.LSTORE_3);
+ }
+
+ public C dstore_3() {
+ return emitOp(Opcode.DSTORE_3);
+ }
+
+ //...
+
+ public C iaload() {
+ return emitOp(Opcode.IALOAD);
+ }
+
+ public C laload() {
+ return emitOp(Opcode.LALOAD);
+ }
+
+ public C faload() {
+ return emitOp(Opcode.FALOAD);
+ }
+
+ public C daload() {
+ return emitOp(Opcode.DALOAD);
+ }
+
+ public C vaload() {
+ return emitOp(Opcode.VALOAD);
+ }
+
+ public C aaload() {
+ return emitOp(Opcode.AALOAD);
+ }
+
+ public C baload() {
+ return emitOp(Opcode.BALOAD);
+ }
+
+ public C caload() {
+ return emitOp(Opcode.CALOAD);
+ }
+
+ public C saload() {
+ return emitOp(Opcode.SALOAD);
+ }
+
+ public C iastore() {
+ return emitOp(Opcode.IASTORE);
+ }
+
+ public C lastore() {
+ return emitOp(Opcode.LASTORE);
+ }
+
+ public C fastore() {
+ return emitOp(Opcode.FASTORE);
+ }
+
+ public C dastore() {
+ return emitOp(Opcode.DASTORE);
+ }
+
+ public C vastore() {
+ return emitOp(Opcode.VASTORE);
+ }
+
+ public C aastore() {
+ return emitOp(Opcode.AASTORE);
+ }
+
+ public C bastore() {
+ return emitOp(Opcode.BASTORE);
+ }
+
+ public C castore() {
+ return emitOp(Opcode.CASTORE);
+ }
+
+ public C sastore() {
+ return emitOp(Opcode.SASTORE);
+ }
+
+ public C nop() {
+ return emitOp(Opcode.NOP);
+ }
+
+ public C aconst_null() {
+ return emitOp(Opcode.ACONST_NULL);
+ }
+
+ public C iconst_0() {
+ return emitOp(Opcode.ICONST_0);
+ }
+
+ public C iconst_1() {
+ return emitOp(Opcode.ICONST_1);
+ }
+
+ public C iconst_2() {
+ return emitOp(Opcode.ICONST_2);
+ }
+
+ public C iconst_3() {
+ return emitOp(Opcode.ICONST_3);
+ }
+
+ public C iconst_4() {
+ return emitOp(Opcode.ICONST_4);
+ }
+
+ public C iconst_5() {
+ return emitOp(Opcode.ICONST_5);
+ }
+
+ public C iconst_m1() {
+ return emitOp(Opcode.ICONST_M1);
+ }
+
+ public C lconst_0() {
+ return emitOp(Opcode.LCONST_0);
+ }
+
+ public C lconst_1() {
+ return emitOp(Opcode.LCONST_1);
+ }
+
+ public C fconst_0() {
+ return emitOp(Opcode.FCONST_0);
+ }
+
+ public C fconst_1() {
+ return emitOp(Opcode.FCONST_1);
+ }
+
+ public C fconst_2() {
+ return emitOp(Opcode.FCONST_2);
+ }
+
+ public C dconst_0() {
+ return emitOp(Opcode.DCONST_0);
+ }
+
+ public C dconst_1() {
+ return emitOp(Opcode.DCONST_1);
+ }
+
+ public C sipush(int s) {
+ emitOp(Opcode.SIPUSH);
+ code.writeChar(s);
+ return thisBuilder();
+ }
+
+ public C bipush(int b) {
+ emitOp(Opcode.BIPUSH);
+ code.writeByte(b);
+ return thisBuilder();
+ }
+
+ public C pop() {
+ return emitOp(Opcode.POP);
+ }
+
+ public C pop2() {
+ return emitOp(Opcode.POP2);
+ }
+
+ public C dup() {
+ return emitOp(Opcode.DUP);
+ }
+
+ public C dup_x1() {
+ return emitOp(Opcode.DUP_X1);
+ }
+
+ public C dup_x2() {
+ return emitOp(Opcode.DUP_X2);
+ }
+
+ public C dup2() {
+ return emitOp(Opcode.DUP2);
+ }
+
+ public C dup2_x1() {
+ return emitOp(Opcode.DUP2_X1);
+ }
+
+ public C dup2_x2() {
+ return emitOp(Opcode.DUP2_X2);
+ }
+
+ public C swap() {
+ return emitOp(Opcode.SWAP);
+ }
+
+ public C iadd() {
+ return emitOp(Opcode.IADD);
+ }
+
+ public C ladd() {
+ return emitOp(Opcode.LADD);
+ }
+
+ public C fadd() {
+ return emitOp(Opcode.FADD);
+ }
+
+ public C dadd() {
+ return emitOp(Opcode.DADD);
+ }
+
+ public C isub() {
+ return emitOp(Opcode.ISUB);
+ }
+
+ public C lsub() {
+ return emitOp(Opcode.LSUB);
+ }
+
+ public C fsub() {
+ return emitOp(Opcode.FSUB);
+ }
+
+ public C dsub() {
+ return emitOp(Opcode.DSUB);
+ }
+
+ public C imul() {
+ return emitOp(Opcode.IMUL);
+ }
+
+ public C lmul() {
+ return emitOp(Opcode.LMUL);
+ }
+
+ public C fmul() {
+ return emitOp(Opcode.FMUL);
+ }
+
+ public C dmul() {
+ return emitOp(Opcode.DMUL);
+ }
+
+ public C idiv() {
+ return emitOp(Opcode.IDIV);
+ }
+
+ public C ldiv() {
+ return emitOp(Opcode.LDIV);
+ }
+
+ public C fdiv() {
+ return emitOp(Opcode.FDIV);
+ }
+
+ public C ddiv() {
+ return emitOp(Opcode.DDIV);
+ }
+
+ public C irem() {
+ return emitOp(Opcode.IREM);
+ }
+
+ public C lrem() {
+ return emitOp(Opcode.LREM);
+ }
+
+ public C frem() {
+ return emitOp(Opcode.FREM);
+ }
+
+ public C drem() {
+ return emitOp(Opcode.DREM);
+ }
+
+ public C ineg() {
+ return emitOp(Opcode.INEG);
+ }
+
+ public C lneg() {
+ return emitOp(Opcode.LNEG);
+ }
+
+ public C fneg() {
+ return emitOp(Opcode.FNEG);
+ }
+
+ public C dneg() {
+ return emitOp(Opcode.DNEG);
+ }
+
+ public C ishl() {
+ return emitOp(Opcode.ISHL);
+ }
+
+ public C lshl() {
+ return emitOp(Opcode.LSHL);
+ }
+
+ public C ishr() {
+ return emitOp(Opcode.ISHR);
+ }
+
+ public C lshr() {
+ return emitOp(Opcode.LSHR);
+ }
+
+ public C iushr() {
+ return emitOp(Opcode.IUSHR);
+ }
+
+ public C lushr() {
+ return emitOp(Opcode.LUSHR);
+ }
+
+ public C iand() {
+ return emitOp(Opcode.IAND);
+ }
+
+ public C land() {
+ return emitOp(Opcode.LAND);
+ }
+
+ public C ior() {
+ return emitOp(Opcode.IOR);
+ }
+
+ public C lor() {
+ return emitOp(Opcode.LOR);
+ }
+
+ public C ixor() {
+ return emitOp(Opcode.IXOR);
+ }
+
+ public C lxor() {
+ return emitOp(Opcode.LXOR);
+ }
+
+ public C iinc(int index, int val) {
+ return emitWideIfNeeded(Opcode.IINC, index, val);
+ }
+
+ public C i2l() {
+ return emitOp(Opcode.I2L);
+ }
+
+ public C i2f() {
+ return emitOp(Opcode.I2F);
+ }
+
+ public C i2d() {
+ return emitOp(Opcode.I2D);
+ }
+
+ public C l2i() {
+ return emitOp(Opcode.L2I);
+ }
+
+ public C l2f() {
+ return emitOp(Opcode.L2F);
+ }
+
+ public C l2d() {
+ return emitOp(Opcode.L2D);
+ }
+
+ public C f2i() {
+ return emitOp(Opcode.F2I);
+ }
+
+ public C f2l() {
+ return emitOp(Opcode.F2L);
+ }
+
+ public C f2d() {
+ return emitOp(Opcode.F2D);
+ }
+
+ public C d2i() {
+ return emitOp(Opcode.D2I);
+ }
+
+ public C d2l() {
+ return emitOp(Opcode.D2L);
+ }
+
+ public C d2f() {
+ return emitOp(Opcode.D2F);
+ }
+
+ public C i2b() {
+ return emitOp(Opcode.I2B);
+ }
+
+ public C i2c() {
+ return emitOp(Opcode.I2C);
+ }
+
+ public C i2s() {
+ return emitOp(Opcode.I2S);
+ }
+
+ public C lcmp() {
+ return emitOp(Opcode.LCMP);
+ }
+
+ public C fcmpl() {
+ return emitOp(Opcode.FCMPL);
+ }
+
+ public C fcmpg() {
+ return emitOp(Opcode.FCMPG);
+ }
+
+ public C dcmpl() {
+ return emitOp(Opcode.DCMPL);
+ }
+
+ public C dcmpg() {
+ return emitOp(Opcode.DCMPG);
+ }
+
+ public C ifeq(short target) {
+ return emitNarrowJumpOp(Opcode.IFEQ, target);
+ }
+
+ public C ifne(short target) {
+ return emitNarrowJumpOp(Opcode.IFNE, target);
+ }
+
+ public C iflt(short target) {
+ return emitNarrowJumpOp(Opcode.IFLT, target);
+ }
+
+ public C ifge(short target) {
+ return emitNarrowJumpOp(Opcode.IFGE, target);
+ }
+
+ public C ifgt(short target) {
+ return emitNarrowJumpOp(Opcode.IFGT, target);
+ }
+
+ public C ifle(short target) {
+ return emitNarrowJumpOp(Opcode.IFLE, target);
+ }
+
+ public C if_icmpeq(short target) {
+ return emitNarrowJumpOp(Opcode.IF_ICMPEQ, target);
+ }
+
+ public C if_icmpne(short target) {
+ return emitNarrowJumpOp(Opcode.IF_ICMPNE, target);
+ }
+
+ public C if_icmplt(short target) {
+ return emitNarrowJumpOp(Opcode.IF_ICMPLT, target);
+ }
+
+ public C if_icmpge(short target) {
+ return emitNarrowJumpOp(Opcode.IF_ICMPGE, target);
+ }
+
+ public C if_icmpgt(short target) {
+ return emitNarrowJumpOp(Opcode.IF_ICMPGT, target);
+ }
+
+ public C if_icmple(short target) {
+ return emitNarrowJumpOp(Opcode.IF_ICMPLE, target);
+ }
+
+ public C if_acmpeq(short target) {
+ return emitNarrowJumpOp(Opcode.IF_ACMPEQ, target);
+ }
+
+ public C if_acmpne(short target) {
+ return emitNarrowJumpOp(Opcode.IF_ACMPNE, target);
+ }
+
+ public C goto_(short target) {
+ return emitNarrowJumpOp(Opcode.GOTO_, target);
+ }
+
+ public C jsr(short target) {
+ return emitNarrowJumpOp(Opcode.JSR, target);
+ }
+
+ public C ret(int index) {
+ return emitWideIfNeeded(Opcode.RET, index);
+ }
+
+ public C tableswitch(int low, int high, int defaultTarget, int... targets) {
+ if (high - low + 1 != targets.length) throw new IllegalStateException("Bad targets length");
+ emitOp(Opcode.TABLESWITCH);
+ //padding
+ int start = code.offset;
+ if ((start % 4) != 0) {
+ //add padding
+ for (int i = 0; i < 4 - (start % 4); i++) {
+ code.writeByte(0);
+ }
+ }
+ code.writeInt(defaultTarget)
+ .writeInt(low)
+ .writeInt(high);
+ for (int target : targets) {
+ code.writeInt(target);
+ }
+ return thisBuilder();
+ }
+
+ public C lookupswitch(int defaultTarget, int... npairs) {
+ if (npairs.length % 2 != 0) throw new IllegalStateException("Bad npairs length");
+ emitOp(Opcode.LOOKUPSWITCH);
+ //padding
+ int start = code.offset;
+ for (int i = 0; i < (4 - (start % 4)); i++) {
+ code.writeByte(0);
+ }
+ code.writeInt(defaultTarget)
+ .writeInt(npairs.length / 2);
+ for (int i = 0; i < npairs.length; i += 2) {
+ code.writeInt(npairs[i]);
+ code.writeInt(npairs[i + 1]);
+ }
+ return thisBuilder();
+ }
+
+ public C arraylength() {
+ return emitOp(Opcode.ARRAYLENGTH);
+ }
+
+ public C athrow() {
+ return emitOp(Opcode.ATHROW);
+ }
+
+ public C monitorenter() {
+ return emitOp(Opcode.MONITORENTER);
+ }
+
+ public C monitorexit() {
+ return emitOp(Opcode.MONITOREXIT);
+ }
+
+ public C wide() {
+ return emitOp(Opcode.WIDE);
+ }
+
+ public C if_null(short offset) {
+ return emitNarrowJumpOp(Opcode.IF_NULL, offset);
+ }
+
+ public C if_nonnull(short offset) {
+ return emitNarrowJumpOp(Opcode.IF_NONNULL, offset);
+ }
+
+ public C goto_w(int target) {
+ return emitWideJumpOp(Opcode.GOTO_W, target);
+ }
+
+ public C jsr_w(int target) {
+ return emitWideJumpOp(Opcode.JSR_W, target);
+ }
+
+ public C withCatch(S type, int start, int end, int offset) {
+ catchers.writeChar(start);
+ catchers.writeChar(end);
+ catchers.writeChar(offset);
+ catchers.writeChar(type != null ? poolHelper.putClass(type) : 0);
+ ncatchers++;
+ return thisBuilder();
+ }
+
+ public C withLocalSize(int localsize) {
+ this.localsize = localsize;
+ return thisBuilder();
+ }
+
+ public C withStackSize(int stacksize) {
+ this.stacksize = stacksize;
+ return thisBuilder();
+ }
+
+ protected int localsize() {
+ return localsize;
+ }
+
+ void build(GrowableByteBuffer buf) {
+ buf.writeChar(stacksize); //max stack size
+ buf.writeChar(localsize()); //max locals
+ buf.writeInt(code.offset);
+ buf.writeBytes(code);
+ buf.writeChar(ncatchers);
+ buf.writeBytes(catchers);
+ buf.writeChar(nattrs); //attributes
+ buf.writeBytes(attributes);
+ }
+
+ byte[] build() {
+ GrowableByteBuffer buf = new GrowableByteBuffer();
+ build(buf);
+ return buf.bytes();
+ }
+
+ protected C emitNarrowJumpOp(Opcode opcode, short target) {
+ emitOp(opcode);
+ emitOffset(code, JumpMode.NARROW, target);
+ return thisBuilder();
+ }
+
+ protected C emitWideJumpOp(Opcode opcode, int target) {
+ emitOp(opcode);
+ emitOffset(code, JumpMode.WIDE, target);
+ return thisBuilder();
+ }
+
+ protected C emitOp(Opcode opcode) {
+ return emitOp(opcode, null);
+ }
+
+ protected C emitOp(Opcode opcode, Object optPoolValue) {
+ code.writeByte(opcode.code);
+ return thisBuilder();
+ }
+
+ protected void emitOffset(GrowableByteBuffer buf, JumpMode jumpMode, int offset) {
+ if (jumpMode == JumpMode.NARROW) {
+ buf.writeChar((short) offset);
+ } else {
+ buf.writeInt(offset);
+ }
+ }
+
+ int offset() {
+ return code.offset;
+ }
+
+ /*** stackmap support ***/
+
+ /**
+ * The tags and constants used in compressed stackmap.
+ */
+ static final int SAME_FRAME_SIZE = 64;
+ static final int SAME_LOCALS_1_STACK_ITEM_EXTENDED = 247;
+ static final int SAME_FRAME_EXTENDED = 251;
+ static final int FULL_FRAME = 255;
+ static final int MAX_LOCAL_LENGTH_DIFF = 4;
+
+ @SuppressWarnings("unchecked")
+ private void writeStackMapType(T t) {
+ if (t == null) {
+ stackmaps.writeByte(0);
+ } else {
+ switch (typeHelper.tag(t)) {
+ case B:
+ case C:
+ case S:
+ case I:
+ case Z:
+ stackmaps.writeByte(1);
+ break;
+ case F:
+ stackmaps.writeByte(2);
+ break;
+ case D:
+ stackmaps.writeByte(3);
+ break;
+ case J:
+ stackmaps.writeByte(4);
+ break;
+ case A:
+ if (t == typeHelper.nullType()) {
+ stackmaps.writeByte(5); //null
+ } else {
+ //TODO: uninit this, top?
+ stackmaps.writeByte(7);
+ stackmaps.writeChar(poolHelper.putClass(typeHelper.symbol(t)));
+ }
+ break;
+ default:
+ throw new IllegalStateException("Bad type");
+ }
+ }
+ }
+
+ public void sameFrame(int offsetDelta) {
+ int frameType = (offsetDelta < SAME_FRAME_SIZE) ?
+ offsetDelta : SAME_FRAME_EXTENDED;
+ stackmaps.writeByte(frameType);
+ if (frameType == SAME_FRAME_EXTENDED) {
+ stackmaps.writeChar(offsetDelta);
+ }
+ }
+
+ public void sameLocals1StackItemFrame(int offsetDelta, T stackItem) {
+ int frameType = (offsetDelta < SAME_FRAME_SIZE) ?
+ (SAME_FRAME_SIZE + offsetDelta) : SAME_LOCALS_1_STACK_ITEM_EXTENDED;
+ stackmaps.writeByte(frameType);
+ if (frameType == SAME_LOCALS_1_STACK_ITEM_EXTENDED) {
+ stackmaps.writeChar(offsetDelta);
+ }
+ writeStackMapType(stackItem);
+ }
+
+ public void appendFrame(int offsetDelta, int prevLocalsSize, List<T> locals) {
+ int frameType = SAME_FRAME_EXTENDED + (locals.size() - prevLocalsSize);
+ stackmaps.writeByte(frameType);
+ stackmaps.writeChar(offsetDelta);
+ for (int i = prevLocalsSize; i < locals.size(); i++) {
+ writeStackMapType(locals.get(i));
+ }
+ }
+
+ public void chopFrame(int offsetDelta, int droppedVars) {
+ int frameType = SAME_FRAME_EXTENDED - droppedVars;
+ stackmaps.writeByte(frameType);
+ stackmaps.writeChar(offsetDelta);
+ }
+
+ public void fullFrame(int offsetDelta, List<T> locals, List<T> stackItems) {
+ stackmaps.writeByte(FULL_FRAME);
+ stackmaps.writeChar(offsetDelta);
+ stackmaps.writeChar(locals.size());
+ for (T local : locals) {
+ writeStackMapType(local);
+ }
+
+ stackmaps.writeChar(stackItems.size());
+ for (T stackType : stackItems) {
+ writeStackMapType(stackType);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/DeclBuilder.java Fri Sep 08 10:46:46 2017 -0700
@@ -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());
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/FieldBuilder.java Fri Sep 08 10:46:46 2017 -0700
@@ -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);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/Flag.java Fri Sep 08 10:46:46 2017 -0700
@@ -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);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/GrowableByteBuffer.java Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,108 @@
+/*
+ * 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;
+
+public class GrowableByteBuffer {
+
+ public GrowableByteBuffer() {
+ }
+
+ byte[] elems = new byte[64];
+ int offset = 0;
+
+ public GrowableByteBuffer writeByte(int b) {
+ return writeBytes(1, b);
+ }
+
+ public GrowableByteBuffer writeChar(int x) {
+ return writeBytes(2, x);
+ }
+
+ public GrowableByteBuffer writeInt(int x) {
+ return writeBytes(4, x);
+ }
+
+ public GrowableByteBuffer writeFloat(float x) {
+ return writeInt(Float.floatToIntBits(x));
+ }
+
+ public GrowableByteBuffer writeLong(long x) {
+ return writeBytes(8, x);
+ }
+
+ public GrowableByteBuffer writeDouble(double x) {
+ writeLong(Double.doubleToLongBits(x));
+ return this;
+ }
+
+ public GrowableByteBuffer writeBytes(byte[] barr) {
+ expandIfNeeded(barr.length);
+ System.arraycopy(barr, 0, elems, offset, barr.length);
+ offset += barr.length;
+ return this;
+ }
+
+ public GrowableByteBuffer writeBytes(GrowableByteBuffer bb) {
+ expandIfNeeded(bb.offset);
+ System.arraycopy(bb.elems, 0, elems, offset, bb.offset);
+ offset += bb.offset;
+ return this;
+ }
+
+ public GrowableByteBuffer withOffset(int offset, Consumer<GrowableByteBuffer> actions) {
+ int prevOffset = this.offset;
+ this.offset = offset;
+ actions.accept(this);
+ this.offset = prevOffset;
+ return this;
+ }
+
+ private GrowableByteBuffer writeBytes(int size, long x) {
+ expandIfNeeded(size);
+ for (int i = 0; i < size; i++) {
+ elems[offset++] = (byte) ((x >> 8 * (size - i - 1)) & 0xFF);
+ }
+ return this;
+ }
+
+ void expandIfNeeded(int increment) {
+ if (offset + increment > elems.length) {
+ int newsize = elems.length * 2;
+ while (offset + increment > newsize) {
+ newsize *= 2;
+ }
+ byte[] newelems = new byte[newsize];
+ System.arraycopy(elems, 0, newelems, 0, offset);
+ elems = newelems;
+ }
+ }
+
+ public byte[] bytes() {
+ byte[] bytes = new byte[offset];
+ System.arraycopy(elems, 0, bytes, 0, offset);
+ return bytes;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/IsolatedMethodBuilder.java Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,177 @@
+/*
+ * 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.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.ToIntFunction;
+
+public class IsolatedMethodBuilder extends MethodBuilder<Class<?>, String, Object[]> {
+
+ public IsolatedMethodBuilder(Lookup lookup, String name, String type) {
+ super(null, name, type, new IsolatedMethodPoolHelper(lookup), null);
+ }
+
+ static class IsolatedMethodPoolHelper implements PoolHelper<Class<?>, String, Object[]> {
+ Map<Object, Integer> constants = new HashMap<>();
+ Lookup lookup;
+
+ private IsolatedMethodPoolHelper(Lookup lookup) {
+ this.lookup = lookup;
+ }
+
+ @Override
+ public int putClass(Class<?> symbol) {
+ return putIfAbsent(symbol);
+ }
+
+ @Override
+ public int putFieldRef(Class<?> owner, CharSequence name, String type) {
+ try {
+ Field f = owner.getDeclaredField(name.toString()); //TODO: we should unreflect for a var handle
+ return putIfAbsent(lookup.unreflectGetter(f));
+ } catch (Throwable ex) {
+ ex.printStackTrace();
+ return -1;
+ }
+ }
+
+ @Override
+ public int putMethodRef(Class<?> owner, CharSequence name, String type, boolean isInterface) {
+ try {
+ Method m = owner.getDeclaredMethod(name.toString()); //we should unreflect according to method vs. constructor
+ //and static vs. private etc.
+ return putIfAbsent(lookup.unreflect(m));
+ } catch (Throwable ex) {
+ ex.printStackTrace();
+ return -1;
+ }
+ }
+
+ @Override
+ public int putInt(int i) {
+ return putIfAbsent(i);
+ }
+
+ @Override
+ public int putFloat(float f) {
+ return putIfAbsent(f);
+ }
+
+ @Override
+ public int putLong(long l) {
+ return putIfAbsent(l);
+ }
+
+ @Override
+ public int putDouble(double d) {
+ return putIfAbsent(d);
+ }
+
+ @Override
+ public int putString(String s) {
+ return putIfAbsent(s);
+ }
+
+ @Override
+ public int putInvokeDynamic(CharSequence invokedName, String invokedType, Class<?> bsmClass, CharSequence bsmName, String bsmType, Consumer<StaticArgListBuilder<Class<?>, String, Object[]>> staticArgs) {
+ return 0; //???
+ }
+
+ @Override
+ public int putDynamicConstant(CharSequence constName, String constType, Class<?> bsmClass, CharSequence bsmName, String bsmType, Consumer<StaticArgListBuilder<Class<?>, String, Object[]>> staticArgs) {
+ return 0; //???
+ }
+
+ @Override
+ public int putHandle(int refKind, Class<?> owner, CharSequence name, String type) {
+ return 0; //???
+ }
+
+ @Override
+ public int putMethodType(String s) {
+ return 0; //???
+ }
+
+ @Override
+ public int putUtf8(CharSequence s) {
+ return putIfAbsent(s);
+ }
+
+ @Override
+ public int putType(String s) {
+ return putIfAbsent(s);
+ }
+
+ @Override
+ public int size() {
+ return constants.size();
+ }
+
+ @Override
+ public Object[] entries() {
+ return constants.keySet().toArray();
+ }
+
+ int putIfAbsent(Object o) {
+ int nextIndex = constants.size() + 1;
+ Object res = constants.putIfAbsent(o, nextIndex);
+ return res == null ?
+ nextIndex : (Integer)res;
+ }
+ }
+
+ public Object[] entries() {
+ return poolHelper.entries();
+ }
+
+ @Override
+ public byte[] build() {
+ byte[] arr = super.build();
+ int codelength_offset = 2 + 2 + 2 + 2 +
+ 2 + 4 + 2 + 2;
+ int code_offset = codelength_offset + 4;
+ int length = ByteBuffer.wrap(arr).getInt(codelength_offset);
+ byte[] opcodes = new byte[length];
+ System.arraycopy(arr, code_offset, opcodes, 0, length);
+ return opcodes;
+ }
+
+ public static void main(String[] args) {
+ IsolatedMethodBuilder imb = new IsolatedMethodBuilder(MethodHandles.lookup(), "foo", "(java/lang/String;)I");
+ imb.withCode(C ->
+ C.aload_0()
+ .invokevirtual(String.class, "length", "()I", false)
+ .ireturn());
+ byte[] opcodes = imb.build();
+ System.out.println(Arrays.toString(opcodes));
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/MacroCodeBuilder.java Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,712 @@
+/*
+ * 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.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.function.Consumer;
+
+public class MacroCodeBuilder<S, T, E, C extends MacroCodeBuilder<S, T, E, C>> extends CodeBuilder<S, T, E, C> {
+
+ JumpMode jumpMode = JumpMode.NARROW;
+
+ Map<CharSequence, Integer> labels = new HashMap<>();
+ List<PendingJump> pendingJumps = new LinkedList<>();
+
+ class PendingJump {
+ CharSequence label;
+ int pc;
+
+ PendingJump(CharSequence label, int pc) {
+ this.label = label;
+ this.pc = pc;
+ }
+
+ boolean resolve(CharSequence label, int offset) {
+ if (this.label.equals(label)) {
+ //patch offset
+ code.withOffset(pc + 1, buf -> emitOffset(buf, jumpMode, offset - pc));
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ public enum InvocationKind {
+ INVOKESTATIC,
+ INVOKEVIRTUAL,
+ INVOKESPECIAL,
+ INVOKEINTERFACE;
+ }
+
+ public enum FieldAccessKind {
+ STATIC,
+ INSTANCE;
+ }
+
+ public enum CondKind {
+ EQ(0),
+ NE(1),
+ LT(2),
+ GE(3),
+ GT(4),
+ LE(5);
+
+ int offset;
+
+ CondKind(int offset) {
+ this.offset = offset;
+ }
+
+ public CondKind negate() {
+ switch (this) {
+ case EQ:
+ return NE;
+ case NE:
+ return EQ;
+ case LT:
+ return GE;
+ case GE:
+ return LT;
+ case GT:
+ return LE;
+ case LE:
+ return GT;
+ default:
+ throw new IllegalStateException("Unknown cond");
+ }
+ }
+ }
+
+ static class WideJumpException extends RuntimeException {
+ static final long serialVersionUID = 42L;
+ }
+
+ public MacroCodeBuilder(MethodBuilder<S, T, E> methodBuilder) {
+ super(methodBuilder);
+ }
+
+ public C load(TypeTag type, int n) {
+ if (type == TypeTag.Q) {
+ return vload(n);
+ } else {
+ switch (n) {
+ case 0:
+ return emitOp(Opcode.ILOAD_0.at(type, 4));
+ case 1:
+ return emitOp(Opcode.ILOAD_1.at(type, 4));
+ case 2:
+ return emitOp(Opcode.ILOAD_2.at(type, 4));
+ case 3:
+ return emitOp(Opcode.ILOAD_3.at(type, 4));
+ default:
+ return emitWideIfNeeded(Opcode.ILOAD.at(type), n);
+ }
+ }
+ }
+
+ public C store(TypeTag type, int n) {
+ if (type == TypeTag.Q) {
+ return vstore(n);
+ } else {
+ switch (n) {
+ case 0:
+ return emitOp(Opcode.ISTORE_0.at(type, 4));
+ case 1:
+ return emitOp(Opcode.ISTORE_1.at(type, 4));
+ case 2:
+ return emitOp(Opcode.ISTORE_2.at(type, 4));
+ case 3:
+ return emitOp(Opcode.ISTORE_3.at(type, 4));
+ default:
+ return emitWideIfNeeded(Opcode.ISTORE.at(type), n);
+ }
+ }
+ }
+
+ public C arrayload(TypeTag type) {
+ return emitOp(Opcode.IALOAD.at(type));
+ }
+
+ public C arraystore(TypeTag type, int n) {
+ return emitOp(Opcode.IASTORE.at(type));
+ }
+
+ public C const_(int i) {
+ switch (i) {
+ case -1:
+ return iconst_m1();
+ case 0:
+ return iconst_0();
+ case 1:
+ return iconst_1();
+ case 2:
+ return iconst_2();
+ case 3:
+ return iconst_3();
+ case 4:
+ return iconst_4();
+ case 5:
+ return iconst_5();
+ default:
+ if (i > 0 && i <= Byte.MAX_VALUE) {
+ return bipush(i);
+ } else if (i >= Short.MIN_VALUE && i <= Short.MAX_VALUE) {
+ return sipush(i);
+ } else {
+ return ldc(i);
+ }
+ }
+ }
+
+ public C const_(long l) {
+ if (l == 0) {
+ return lconst_0();
+ } else if (l == 1) {
+ return lconst_1();
+ } else {
+ return ldc(l);
+ }
+ }
+
+ public C const_(float f) {
+ if (f == 0) {
+ return fconst_0();
+ } else if (f == 1) {
+ return fconst_1();
+ } else if (f == 2) {
+ return fconst_2();
+ } else {
+ return ldc(f);
+ }
+ }
+
+ public C const_(double d) {
+ if (d == 0) {
+ return dconst_0();
+ } else if (d == 1) {
+ return dconst_1();
+ } else {
+ return ldc(d);
+ }
+ }
+
+ public C getfield(FieldAccessKind fak, S owner, CharSequence name, T type) {
+ switch (fak) {
+ case INSTANCE:
+ return getfield(owner, name, type);
+ case STATIC:
+ return getstatic(owner, name, type);
+ default:
+ throw new IllegalStateException();
+ }
+ }
+
+ public C putfield(FieldAccessKind fak, S owner, CharSequence name, T type) {
+ switch (fak) {
+ case INSTANCE:
+ return putfield(owner, name, type);
+ case STATIC:
+ return putstatic(owner, name, type);
+ default:
+ throw new IllegalStateException();
+ }
+ }
+
+ public C invoke(InvocationKind ik, S owner, CharSequence name, T type, boolean isInterface) {
+ switch (ik) {
+ case INVOKESTATIC:
+ return invokestatic(owner, name, type, isInterface);
+ case INVOKEVIRTUAL:
+ return invokevirtual(owner, name, type, isInterface);
+ case INVOKESPECIAL:
+ return invokespecial(owner, name, type, isInterface);
+ case INVOKEINTERFACE:
+ if (!isInterface) throw new AssertionError();
+ return invokeinterface(owner, name, type);
+ default:
+ throw new IllegalStateException();
+ }
+ }
+
+ public C add(TypeTag type) {
+ return emitOp(Opcode.IADD.at(type));
+ }
+
+ public C sub(TypeTag type) {
+ return emitOp(Opcode.ISUB.at(type));
+ }
+
+ public C mul(TypeTag type) {
+ return emitOp(Opcode.IMUL.at(type));
+ }
+
+ public C div(TypeTag type) {
+ return emitOp(Opcode.IDIV.at(type));
+ }
+
+ public C rem(TypeTag type) {
+ return emitOp(Opcode.IREM.at(type));
+ }
+
+ public C neg(TypeTag type) {
+ return emitOp(Opcode.INEG.at(type));
+ }
+
+ public C shl(TypeTag type) {
+ return emitOp(Opcode.ISHL.at(type));
+ }
+
+ public C shr(TypeTag type) {
+ return emitOp(Opcode.ISHR.at(type));
+ }
+
+ public C ushr(TypeTag type) {
+ return emitOp(Opcode.ISHR.at(type));
+ }
+
+ public C and(TypeTag type) {
+ return emitOp(Opcode.IAND.at(type));
+ }
+
+ public C or(TypeTag type) {
+ return emitOp(Opcode.IOR.at(type));
+ }
+
+ public C xor(TypeTag type) {
+ return emitOp(Opcode.IXOR.at(type));
+ }
+
+ public C return_(TypeTag type) {
+ switch (type) {
+ case V:
+ return return_();
+ case Q:
+ return vreturn();
+ default:
+ return emitOp(Opcode.IRETURN.at(type));
+ }
+ }
+
+ @Override
+ public LabelledTypedBuilder typed(TypeTag typeTag) {
+ return super.typed(typeTag, _unused -> new LabelledTypedBuilder());
+ }
+
+ public class LabelledTypedBuilder extends TypedBuilder {
+ public C if_acmpeq(CharSequence target) {
+ return ifcmp(TypeTag.A, CondKind.EQ, target);
+ }
+
+ public C if_acmpne(CharSequence target) {
+ return ifcmp(TypeTag.A, CondKind.NE, target);
+ }
+ }
+
+ public C conv(TypeTag from, TypeTag to) {
+ switch (from) {
+ case B:
+ case C:
+ case S:
+ switch (to) {
+ case J:
+ return i2l();
+ case F:
+ return i2f();
+ case D:
+ return i2d();
+ }
+ break;
+ case I:
+ switch (to) {
+ case J:
+ return i2l();
+ case F:
+ return i2f();
+ case D:
+ return i2d();
+ case B:
+ return i2b();
+ case C:
+ return i2c();
+ case S:
+ return i2s();
+ }
+ break;
+ case J:
+ switch (to) {
+ case I:
+ return l2i();
+ case F:
+ return l2f();
+ case D:
+ return l2d();
+ }
+ break;
+ case F:
+ switch (to) {
+ case I:
+ return f2i();
+ case J:
+ return f2l();
+ case D:
+ return f2d();
+ }
+ break;
+ case D:
+ switch (to) {
+ case I:
+ return d2i();
+ case J:
+ return d2l();
+ case F:
+ return d2f();
+ }
+ break;
+ }
+ //no conversion is necessary - do nothing!
+ return thisBuilder();
+ }
+
+ public C if_null(CharSequence label) {
+ return emitCondJump(Opcode.IF_NULL, Opcode.IF_NONNULL, label);
+ }
+
+ public C if_nonnull(CharSequence label) {
+ return emitCondJump(Opcode.IF_NONNULL, Opcode.IF_NULL, label);
+ }
+
+ public C ifcmp(TypeTag type, CondKind cond, CharSequence label) {
+ switch (type) {
+ case I:
+ return emitCondJump(Opcode.IF_ICMPEQ, cond, label);
+ case A:
+ return emitCondJump(Opcode.IF_ACMPEQ, cond, label);
+ case J:
+ return lcmp().emitCondJump(Opcode.IFEQ, cond, label);
+ case D:
+ return dcmpg().emitCondJump(Opcode.IFEQ, cond, label);
+ case F:
+ return fcmpg().emitCondJump(Opcode.IFEQ, cond, label);
+ default:
+ throw new IllegalArgumentException("Bad cmp type");
+ }
+ }
+
+ public C goto_(CharSequence label) {
+ emitOp(jumpMode == JumpMode.NARROW ? Opcode.GOTO_ : Opcode.GOTO_W);
+ emitOffset(code, jumpMode, labelOffset(label));
+ return thisBuilder();
+ }
+
+ protected int labelOffset(CharSequence label) {
+ int pc = code.offset - 1;
+ Integer labelPc = labels.get(label);
+ if (labelPc == null) {
+ addPendingJump(label, pc);
+ }
+ return labelPc == null ? 0 : (labelPc - pc);
+ }
+
+ public C label(CharSequence s) {
+ int pc = code.offset;
+ Object old = labels.put(s, pc);
+ if (old != null) {
+ throw new IllegalStateException("label already exists");
+ }
+ resolveJumps(s, pc);
+ return thisBuilder();
+ }
+
+ //FIXME: address this jumpy mess - i.e. offset and state update work against each other!
+ public C emitCondJump(Opcode opcode, CondKind ck, CharSequence label) {
+ return emitCondJump(opcode.at(ck), opcode.at(ck.negate()), label);
+ }
+
+ public C emitCondJump(Opcode pos, Opcode neg, CharSequence label) {
+ if (jumpMode == JumpMode.NARROW) {
+ emitOp(pos);
+ emitOffset(code, jumpMode, labelOffset(label));
+ } else {
+ emitOp(neg);
+ emitOffset(code, JumpMode.NARROW, 8);
+ goto_w(labelOffset(label));
+ }
+ return thisBuilder();
+ }
+
+ void addPendingJump(CharSequence label, int pc) {
+ pendingJumps.add(new PendingJump(label, pc));
+ }
+
+ void resolveJumps(CharSequence label, int pc) {
+ Iterator<PendingJump> jumpsIt = pendingJumps.iterator();
+ while (jumpsIt.hasNext()) {
+ PendingJump jump = jumpsIt.next();
+ if (jump.resolve(label, pc)) {
+ jumpsIt.remove();
+ }
+ }
+ }
+
+ @Override
+ protected void emitOffset(GrowableByteBuffer buf, JumpMode jumpMode, int offset) {
+ if (jumpMode == JumpMode.NARROW && (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE)) {
+ throw new WideJumpException();
+ }
+ super.emitOffset(buf, jumpMode, offset);
+ }
+
+ public C jsr(CharSequence label) {
+ emitOp(jumpMode == JumpMode.NARROW ? Opcode.JSR : Opcode.JSR_W);
+ emitOffset(code, jumpMode, labelOffset(label));
+ return thisBuilder();
+ }
+
+ @SuppressWarnings("unchecked")
+ public C withTry(Consumer<? super C> tryBlock, Consumer<? super CatchBuilder> catchBlocks) {
+ int start = code.offset;
+ tryBlock.accept((C) this);
+ int end = code.offset;
+ CatchBuilder catchBuilder = makeCatchBuilder(start, end);
+ catchBlocks.accept(catchBuilder);
+ catchBuilder.build();
+ return thisBuilder();
+ }
+
+ void clear() {
+ code.offset = 0;
+ catchers.offset = 0;
+ ncatchers = 0;
+ labels.clear();
+ pendingJumps = null;
+ }
+
+ protected CatchBuilder makeCatchBuilder(int start, int end) {
+ return new CatchBuilder(start, end);
+ }
+
+ public class CatchBuilder {
+ int start, end;
+
+ String endLabel = labelName();
+
+ Map<S, Consumer<? super C>> catchers = new LinkedHashMap<>();
+ public Consumer<? super C> finalizer;
+ List<Integer> pendingGaps = new ArrayList<>();
+
+ public CatchBuilder(int start, int end) {
+ this.start = start;
+ this.end = end;
+ }
+
+ public CatchBuilder withCatch(S exc, Consumer<? super C> catcher) {
+ catchers.put(exc, catcher);
+ return this;
+ }
+
+ public CatchBuilder withFinally(Consumer<? super C> finalizer) {
+ this.finalizer = finalizer;
+ return this;
+ }
+
+ @SuppressWarnings("unchecked")
+ void build() {
+ if (finalizer != null) {
+ finalizer.accept((C) MacroCodeBuilder.this);
+ }
+ goto_(endLabel);
+ for (Map.Entry<S, Consumer<? super C>> catcher_entry : catchers.entrySet()) {
+ emitCatch(catcher_entry.getKey(), catcher_entry.getValue());
+ }
+ if (finalizer != null) {
+ emitFinalizer();
+ }
+ resolveJumps(endLabel, code.offset);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void emitCatch(S exc, Consumer<? super C> catcher) {
+ int offset = code.offset;
+ MacroCodeBuilder.this.withCatch(exc, start, end, offset);
+ catcher.accept((C) MacroCodeBuilder.this);
+ if (finalizer != null) {
+ int startFinalizer = code.offset;
+ finalizer.accept((C) MacroCodeBuilder.this);
+ pendingGaps.add(startFinalizer);
+ pendingGaps.add(code.offset);
+ }
+ goto_(endLabel);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected void emitFinalizer() {
+ int offset = code.offset;
+ pop();
+ for (int i = 0; i < pendingGaps.size(); i += 2) {
+ MacroCodeBuilder.this.withCatch(null, pendingGaps.get(i), pendingGaps.get(i + 1), offset);
+ }
+ MacroCodeBuilder.this.withCatch(null, start, end, offset);
+ finalizer.accept((C) MacroCodeBuilder.this);
+ }
+
+// @SuppressWarnings("unchecked")
+// CatchBuilder withCatch(S exc, Consumer<? super C> catcher) {
+// int offset = code.offset;
+// MacroCodeBuilder.this.withCatch(exc, start, end, offset);
+// catcher.accept((C)MacroCodeBuilder.this);
+// return this;
+// }
+//
+// @SuppressWarnings("unchecked")
+// CatchBuilder withFinally(Consumer<? super C> catcher) {
+// int offset = code.offset;
+// MacroCodeBuilder.this.withCatch(null, start, end, offset);
+// catcher.accept((C)MacroCodeBuilder.this);
+// return this;
+// }
+ }
+
+ @SuppressWarnings("unchecked")
+ public C switch_(Consumer<? super SwitchBuilder> consumer) {
+ int start = code.offset;
+ SwitchBuilder sb = makeSwitchBuilder();
+ consumer.accept(sb);
+ int nlabels = sb.cases.size();
+ switch (sb.switchCode()) {
+ case LOOKUPSWITCH: {
+ int[] lookupOffsets = new int[nlabels * 2];
+ int i = 0;
+ for (Integer v : sb.cases.keySet()) {
+ lookupOffsets[i] = v;
+ i += 2;
+ }
+ lookupswitch(0, lookupOffsets);
+ //backpatch lookup
+ int curr = code.offset - (8 * nlabels) - 8;
+ int defaultOffset = code.offset - start;
+ code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, defaultOffset));
+ sb.defaultCase.accept((C) this);
+ curr += 12;
+ for (Consumer<? super C> case_ : sb.cases.values()) {
+ int offset = code.offset;
+ code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, offset - start));
+ case_.accept((C) this);
+ curr += 8;
+ }
+ break;
+ }
+ case TABLESWITCH: {
+ int[] tableOffsets = new int[sb.hi - sb.lo + 1];
+ tableswitch(sb.lo, sb.hi, 0, tableOffsets);
+ //backpatch table
+ int curr = code.offset - (4 * tableOffsets.length) - 12;
+ int defaultOffset = code.offset - start;
+ code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, defaultOffset));
+ sb.defaultCase.accept((C) this);
+ curr += 12;
+ int lastCasePc = -1;
+ for (int i = sb.lo; i <= sb.hi; i++) {
+ Consumer<? super C> case_ = sb.cases.get(i);
+ if (case_ != null) {
+ lastCasePc = code.offset;
+ case_.accept((C) this);
+ }
+ int offset = lastCasePc - start;
+ code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, offset));
+ curr += 4;
+ }
+ }
+ }
+ resolveJumps(sb.endLabel, code.offset);
+ return thisBuilder();
+ }
+
+ private static int labelCount = 0;
+
+ String labelName() {
+ return "label" + labelCount++;
+ }
+
+ protected SwitchBuilder makeSwitchBuilder() {
+ return new SwitchBuilder();
+ }
+
+ public class SwitchBuilder {
+ Map<Integer, Consumer<? super C>> cases = new TreeMap<>();
+ int lo = Integer.MAX_VALUE;
+ int hi = Integer.MIN_VALUE;
+ String endLabel = labelName();
+
+ public Consumer<? super C> defaultCase;
+
+ @SuppressWarnings("unchecked")
+ public SwitchBuilder withCase(int value, Consumer<? super C> case_, boolean fallthrough) {
+ if (value > hi) {
+ hi = value;
+ }
+ if (value < lo) {
+ lo = value;
+ }
+ if (!fallthrough) {
+ Consumer<? super C> prevCase = case_;
+ case_ = C -> {
+ prevCase.accept(C);
+ C.goto_(endLabel);
+ };
+ }
+ cases.put(value, case_);
+ return this;
+ }
+
+ @SuppressWarnings("unchecked")
+ public SwitchBuilder withDefault(Consumer<? super C> defaultCase) {
+ if (this.defaultCase != null) {
+ throw new IllegalStateException("default already set");
+ }
+ this.defaultCase = defaultCase;
+ return this;
+ }
+
+ Opcode switchCode() {
+ int nlabels = cases.size();
+ // Determine whether to issue a tableswitch or a lookupswitch
+ // instruction.
+ long table_space_cost = 4 + ((long) hi - lo + 1); // words
+ long lookup_space_cost = 3 + 2 * (long) nlabels;
+ return
+ nlabels > 0 &&
+ table_space_cost <= lookup_space_cost
+ ?
+ Opcode.TABLESWITCH : Opcode.LOOKUPSWITCH;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/MemberBuilder.java Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,78 @@
+/*
+ * 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;
+
+/**
+ * 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 <M> the type of this builder
+ */
+public class MemberBuilder<S, T, E, M extends MemberBuilder<S, T, E, M>> extends DeclBuilder<S, T, E, M> {
+
+ CharSequence name;
+ T desc;
+
+ /**
+ * Create a member builder.
+ *
+ * @param name the name of the class member
+ * @param type the type descriptor of the class member
+ * @param poolHelper the helper to build the constant pool
+ * @param typeHelper the helper to use to manipulate type descriptors
+ */
+ MemberBuilder(CharSequence name, T type, PoolHelper<S, T, E> poolHelper, TypeHelper<S, T> typeHelper) {
+ super(poolHelper, typeHelper);
+ this.name = name;
+ this.desc = type;
+ }
+
+ /**
+ * Build the member.
+ *
+ * @param buf the {@code GrowableByteBuffer} to build the member into
+ */
+ protected void build(GrowableByteBuffer buf) {
+ addAnnotations();
+ buf.writeChar(flags);
+ buf.writeChar(poolHelper.putUtf8(name));
+ buf.writeChar(poolHelper.putType(desc));
+ buf.writeChar(nattrs);
+ buf.writeBytes(attributes);
+ }
+
+ /**
+ * Build the member.
+ *
+ * @return a byte array representation of the member
+ */
+ protected byte[] build() {
+ GrowableByteBuffer buf = new GrowableByteBuffer();
+ addAnnotations();
+ build(buf);
+ return buf.bytes();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/MethodBuilder.java Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,156 @@
+/*
+ * 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 jdk.experimental.bytecode.CodeBuilder.JumpMode;
+
+import java.util.Iterator;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+public class MethodBuilder<S, T, E> extends MemberBuilder<S, T, E, MethodBuilder<S, T, E>> {
+
+ S thisClass;
+ ParameterAnnotationsBuilder runtimeVisibleParameterAnnotations;
+ ParameterAnnotationsBuilder runtimeInvisibleParameterAnnotations;
+
+ public MethodBuilder(S thisClass, CharSequence name, T type, PoolHelper<S, T, E> pool, TypeHelper<S, T> typeHelper) {
+ super(name, type, pool, typeHelper);
+ this.thisClass = thisClass;
+ }
+
+ public <C extends CodeBuilder<S, T, E, ?>> MethodBuilder<S, T, E> withCode(Function<? super MethodBuilder<S, T, E>, ? extends C> func,
+ Consumer<? super C> code) {
+ C codeBuilder = func.apply(this);
+ int start = attributes.offset;
+ try {
+ code.accept(codeBuilder);
+ } catch (MacroCodeBuilder.WideJumpException ex) {
+ //wide jumps! Redo the code
+ ((MacroCodeBuilder<S, T, E, ?>) codeBuilder).jumpMode = JumpMode.WIDE;
+ ((MacroCodeBuilder<S, T, E, ?>) codeBuilder).clear();
+ code.accept(codeBuilder);
+ }
+
+ attributes.writeChar(poolHelper.putUtf8("Code"));
+ attributes.writeInt(0);
+ codeBuilder.build(attributes);
+ int length = attributes.offset - start;
+ //avoid using lambda here
+ int prevOffset = attributes.offset;
+ try {
+ attributes.offset = start + 2;
+ attributes.writeInt(length - 6);
+ } finally {
+ attributes.offset = prevOffset;
+ }
+ nattrs++;
+ return this;
+ }
+
+ public MethodBuilder<S, T, E> withCode(Consumer<? super CodeBuilder<S, T, E, ?>> code) {
+ return withCode(CodeBuilder::new, code);
+ }
+
+ @SuppressWarnings({"varargs", "unchecked"})
+ public MethodBuilder<S, T, E> withExceptions(S... exceptions) {
+ attributes.writeChar(poolHelper.putUtf8("Exceptions"));
+ attributes.writeInt(2 + (2 * exceptions.length));
+ attributes.writeChar(exceptions.length);
+ for (S exception : exceptions) {
+ attributes.writeChar(poolHelper.putClass(exception));
+ }
+ nattrs++;
+ return this;
+ }
+
+ public MethodBuilder<S, T, E> withParameterAnnotation(AnnotationsBuilder.Kind kind, int nparam, T annoType) {
+ getParameterAnnotations(kind).builders[nparam].withAnnotation(annoType, null);
+ return this;
+ }
+
+ public MethodBuilder<S, T, E> withParameterAnnotation(AnnotationsBuilder.Kind kind, int nparam, T annoType, Consumer<? super AnnotationsBuilder<S, T, E>.AnnotationElementBuilder> annotations) {
+ getParameterAnnotations(kind).builders[nparam].withAnnotation(annoType, annotations);
+ return this;
+ }
+
+ private ParameterAnnotationsBuilder getParameterAnnotations(AnnotationsBuilder.Kind kind) {
+ switch (kind) {
+ case RUNTIME_INVISIBLE:
+ if (runtimeInvisibleParameterAnnotations == null) {
+ runtimeInvisibleParameterAnnotations = new ParameterAnnotationsBuilder();
+ }
+ return runtimeInvisibleParameterAnnotations;
+ case RUNTIME_VISIBLE:
+ if (runtimeVisibleParameterAnnotations == null) {
+ runtimeVisibleParameterAnnotations = new ParameterAnnotationsBuilder();
+ }
+ return runtimeVisibleParameterAnnotations;
+ }
+ throw new IllegalStateException();
+ }
+
+ class ParameterAnnotationsBuilder {
+
+ GrowableByteBuffer parameterAnnos = new GrowableByteBuffer();
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ AnnotationsBuilder<S, T, E>[] builders = new AnnotationsBuilder[nparams()];
+
+ ParameterAnnotationsBuilder() {
+ for (int i = 0; i < builders.length; i++) {
+ builders[i] = new AnnotationsBuilder<>(poolHelper, typeHelper);
+ }
+ }
+
+ byte[] build() {
+ parameterAnnos.writeByte(builders.length);
+ for (AnnotationsBuilder<S, T, E> builder : builders) {
+ parameterAnnos.writeBytes(builder.build());
+ }
+ return parameterAnnos.bytes();
+ }
+
+ int nparams() {
+ Iterator<T> paramsIt = typeHelper.parameterTypes(desc);
+ int nparams = 0;
+ while (paramsIt.hasNext()) {
+ paramsIt.next();
+ nparams++;
+ }
+ return nparams;
+ }
+ }
+
+ @Override
+ void addAnnotations() {
+ super.addAnnotations();
+ if (runtimeInvisibleParameterAnnotations != null) {
+ withAttribute("RuntimeInvisibleParameterAnnotations", runtimeInvisibleParameterAnnotations.build());
+ }
+ if (runtimeVisibleParameterAnnotations != null) {
+ withAttribute("RuntimeVisibleParameterAnnotations", runtimeVisibleParameterAnnotations.build());
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/Opcode.java Fri Sep 08 10:46:46 2017 -0700
@@ -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.
+ */
+
+package jdk.experimental.bytecode;
+
+import jdk.experimental.bytecode.MacroCodeBuilder.CondKind;
+
+public enum Opcode {
+
+ NOP(0),
+ ACONST_NULL(1),
+ ICONST_M1(2),
+ ICONST_0(3),
+ ICONST_1(4),
+ ICONST_2(5),
+ ICONST_3(6),
+ ICONST_4(7),
+ ICONST_5(8),
+ LCONST_0(9),
+ LCONST_1(10),
+ FCONST_0(11),
+ FCONST_1(12),
+ FCONST_2(13),
+ DCONST_0(14),
+ DCONST_1(15),
+ BIPUSH(16),
+ SIPUSH(17),
+ LDC(18),
+ LDC_W(19),
+ LDC2_W(20),
+ ILOAD(21),
+ LLOAD(22),
+ FLOAD(23),
+ DLOAD(24),
+ ALOAD(25),
+ ILOAD_0(26),
+ ILOAD_1(27),
+ ILOAD_2(28),
+ ILOAD_3(29),
+ LLOAD_0(30),
+ LLOAD_1(31),
+ LLOAD_2(32),
+ LLOAD_3(33),
+ FLOAD_0(34),
+ FLOAD_1(35),
+ FLOAD_2(36),
+ FLOAD_3(37),
+ DLOAD_0(38),
+ DLOAD_1(39),
+ DLOAD_2(40),
+ DLOAD_3(41),
+ ALOAD_0(42),
+ ALOAD_1(43),
+ ALOAD_2(44),
+ ALOAD_3(45),
+ IALOAD(46),
+ LALOAD(47),
+ FALOAD(48),
+ DALOAD(49),
+ AALOAD(50),
+ BALOAD(51),
+ CALOAD(52),
+ SALOAD(53),
+ ISTORE(54),
+ LSTORE(55),
+ FSTORE(56),
+ DSTORE(57),
+ ASTORE(58),
+ ISTORE_0(59),
+ ISTORE_1(60),
+ ISTORE_2(61),
+ ISTORE_3(62),
+ LSTORE_0(63),
+ LSTORE_1(64),
+ LSTORE_2(65),
+ LSTORE_3(66),
+ FSTORE_0(67),
+ FSTORE_1(68),
+ FSTORE_2(69),
+ FSTORE_3(70),
+ DSTORE_0(71),
+ DSTORE_1(72),
+ DSTORE_2(73),
+ DSTORE_3(74),
+ ASTORE_0(75),
+ ASTORE_1(76),
+ ASTORE_2(77),
+ ASTORE_3(78),
+ IASTORE(79),
+ LASTORE(80),
+ FASTORE(81),
+ DASTORE(82),
+ AASTORE(83),
+ BASTORE(84),
+ CASTORE(85),
+ SASTORE(86),
+ POP(87),
+ POP2(88),
+ DUP(89),
+ DUP_X1(90),
+ DUP_X2(91),
+ DUP2(92),
+ DUP2_X1(93),
+ DUP2_X2(94),
+ SWAP(95),
+ IADD(96),
+ LADD(97),
+ FADD(98),
+ DADD(99),
+ ISUB(100),
+ LSUB(101),
+ FSUB(102),
+ DSUB(103),
+ IMUL(104),
+ LMUL(105),
+ FMUL(106),
+ DMUL(107),
+ IDIV(108),
+ LDIV(109),
+ FDIV(110),
+ DDIV(111),
+ IREM(112),
+ LREM(113),
+ FREM(114),
+ DREM(115),
+ INEG(116),
+ LNEG(117),
+ FNEG(118),
+ DNEG(119),
+ ISHL(120),
+ LSHL(121),
+ ISHR(122),
+ LSHR(123),
+ IUSHR(124),
+ LUSHR(125),
+ IAND(126),
+ LAND(127),
+ IOR(128),
+ LOR(129),
+ IXOR(130),
+ LXOR(131),
+ IINC(132),
+ I2L(133),
+ I2F(134),
+ I2D(135),
+ L2I(136),
+ L2F(137),
+ L2D(138),
+ F2I(139),
+ F2L(140),
+ F2D(141),
+ D2I(142),
+ D2L(143),
+ D2F(144),
+ I2B(145),
+ I2C(146),
+ I2S(147),
+ LCMP(148),
+ FCMPL(149),
+ FCMPG(150),
+ DCMPL(151),
+ DCMPG(152),
+ IFEQ(153),
+ IFNE(154),
+ IFLT(155),
+ IFGE(156),
+ IFGT(157),
+ IFLE(158),
+ IF_ICMPEQ(159),
+ IF_ICMPNE(160),
+ IF_ICMPLT(161),
+ IF_ICMPGE(162),
+ IF_ICMPGT(163),
+ IF_ICMPLE(164),
+ IF_ACMPEQ(165),
+ IF_ACMPNE(166),
+ GOTO_(167),
+ JSR(168),
+ RET(169),
+ TABLESWITCH(170),
+ LOOKUPSWITCH(171),
+ IRETURN(172),
+ LRETURN(173),
+ FRETURN(174),
+ DRETURN(175),
+ ARETURN(176),
+ RETURN(177),
+ GETSTATIC(178),
+ PUTSTATIC(179),
+ GETFIELD(180),
+ PUTFIELD(181),
+ INVOKEVIRTUAL(182),
+ INVOKESPECIAL(183),
+ INVOKESTATIC(184),
+ INVOKEINTERFACE(185),
+ INVOKEDYNAMIC(186),
+ NEW(187),
+ NEWARRAY(188),
+ ANEWARRAY(189),
+ ARRAYLENGTH(190),
+ ATHROW(191),
+ CHECKCAST(192),
+ INSTANCEOF(193),
+ MONITORENTER(194),
+ MONITOREXIT(195),
+ WIDE(196),
+ MULTIANEWARRAY(197),
+ IF_NULL(198),
+ IF_NONNULL(199),
+ GOTO_W(200),
+ JSR_W(201),
+ VLOAD(203),
+ VSTORE(204),
+ VALOAD(205),
+ VASTORE(206),
+ VNEW(207),
+ VNEWARRAY(208),
+ MULTIVNEWARRAY(209),
+ VRETURN(210),
+ VGETFIELD(211),
+ TYPED(212),
+ VBOX(216),
+ VUNBOX(217);
+
+ int code;
+
+ Opcode(int code) {
+ this.code = code;
+ }
+
+ protected Opcode at(TypeTag type) {
+ return at(type, 1);
+ }
+
+ protected Opcode at(CondKind cond) {
+ return at(cond.offset, 1);
+ }
+
+ protected Opcode at(TypeTag type, int multiplier) {
+ return at(type.offset, multiplier);
+ }
+
+ private Opcode at(int offset, int multiplier) {
+ if (offset < 0) throw new AssertionError();
+ return Opcode.values()[code + (multiplier * offset)];
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/PoolHelper.java Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,79 @@
+/*
+ * 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;
+
+/**
+ * An interface for building and tracking constant pools.
+ *
+ * @param <S> the type of the symbol representation
+ * @param <T> the type of type descriptors representation
+ * @param <E> the type of pool entries
+ */
+public interface PoolHelper<S, T, E> {
+ int putClass(S symbol);
+
+ int putFieldRef(S owner, CharSequence name, T type);
+
+ int putMethodRef(S owner, CharSequence name, T type, boolean isInterface);
+
+ int putUtf8(CharSequence s);
+
+ int putInt(int i);
+
+ int putFloat(float f);
+
+ int putLong(long l);
+
+ int putDouble(double d);
+
+ int putString(String s);
+
+ int putType(T t);
+
+ int putMethodType(T t);
+
+ int putHandle(int refKind, S owner, CharSequence name, T type);
+
+ int putInvokeDynamic(CharSequence invokedName, T invokedType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs);
+
+ int putDynamicConstant(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs);
+
+ int size();
+
+ E entries();
+
+ interface StaticArgListBuilder<S, T, E> {
+ StaticArgListBuilder<S, T, E> add(int i);
+ StaticArgListBuilder<S, T, E> add(float f);
+ StaticArgListBuilder<S, T, E> add(long l);
+ StaticArgListBuilder<S, T, E> add(double d);
+ StaticArgListBuilder<S, T, E> add(String s);
+ StaticArgListBuilder<S, T, E> add(int refKind, S owner, CharSequence name, T type);
+ <Z> StaticArgListBuilder<S, T, E> add(Z z, ToIntBiFunction<PoolHelper<S, T, E>, Z> poolFunc);
+ StaticArgListBuilder<S, T, E> add(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgList);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/PoolTag.java Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,53 @@
+/*
+ * 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 enum PoolTag {
+ CONSTANT_UTF8(1),
+ CONSTANT_UNICODE(2),
+ CONSTANT_INTEGER(3),
+ CONSTANT_FLOAT(4),
+ CONSTANT_LONG(5),
+ CONSTANT_DOUBLE(6),
+ CONSTANT_CLASS(7),
+ CONSTANT_STRING(8),
+ CONSTANT_FIELDREF(9),
+ CONSTANT_METHODREF(10),
+ CONSTANT_INTERFACEMETHODREF(11),
+ CONSTANT_NAMEANDTYPE(12),
+ CONSTANT_METHODHANDLE(15),
+ CONSTANT_METHODTYPE(16),
+ CONSTANT_DYNAMIC(17),
+ CONSTANT_INVOKEDYNAMIC(18);
+
+ public final int tag;
+
+ PoolTag(int tag) {
+ this.tag = tag;
+ }
+
+ static PoolTag from(int tag) {
+ return values()[tag - 1];
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/Type.java Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,28 @@
+/*
+ * 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 interface Type {
+ TypeTag getTag();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/TypeHelper.java Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,126 @@
+/*
+ * 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 of T.
+ *
+ * @param <S> the type of symbols
+ * @param <T> the type of type descriptors
+ */
+public interface TypeHelper<S, T> {
+ /**
+ * Return the type descriptor of an element given the type
+ * descriptor of an array.
+ *
+ * @param t the type descriptor of the array
+ * @return the element type
+ */
+ T elemtype(T t);
+
+ /**
+ * Return the type descriptor of an array given the type descriptor
+ * of an element.
+ *
+ * @param t the type descriptor of the element
+ * @return the type descriptor of the array
+ */
+ T arrayOf(T t);
+
+ /**
+ * Return an iterator over the type descriptors of the parameters of a
+ * method.
+ *
+ * @param t the method type descriptor
+ * @return an iterator over the type descriptors of the parameters
+ */
+ Iterator<T> parameterTypes(T t);
+
+ /**
+ * Return the type descriptor of a {@code TypeTag}.
+ *
+ * @param tag the {@code TypeTag} of a primitive type
+ * @return the type descriptor of the primitive type
+ */
+ T fromTag(TypeTag tag);
+
+ /**
+ * Return the return type descriptor of a method.
+ *
+ * @param t the method type descriptor
+ * @return the return type descriptor
+ */
+ T returnType(T t);
+
+ /**
+ * Return the type descriptor for a symbol.
+ *
+ * @param s the symbol
+ * @return the type descriptor
+ */
+ T type(S s);
+
+ /**
+ * Return the symbol corresponding to a type descriptor.
+ *
+ * @param type the type descriptor
+ * @return the symbol
+ */
+ S symbol(T type);
+
+ /**
+ * Return the {@code TypeTag} corresponding to a type descriptor. Reference
+ * types return {@code TypeTag.A}.
+ *
+ * @param t a type descriptor
+ * @return the corresponding {@code TypeTag}
+ */
+ TypeTag tag(T t);
+
+ /**
+ * Return the symbol corresponding to a JVM type descriptor string.
+ *
+ * @param s a JVM type descriptor string
+ * @return the corresponding symbol
+ */
+ S symbolFrom(String s);
+
+ /**
+ * Return the common supertype descriptor of two type descriptors.
+ *
+ * @param t1 a type descriptor
+ * @param t2 a type descriptor
+ * @return the common supertype descriptor
+ */
+ T commonSupertype(T t1, T t2);
+
+ /**
+ * Return the type descriptor for the null type.
+ *
+ * @return the type descriptor for the null type
+ */
+ T nullType();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/TypeTag.java Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,113 @@
+/*
+ * 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 enum TypeTag implements Type {
+ /**
+ * byte
+ */
+ B("B", 0, 1, 8),
+ /**
+ * short
+ */
+ S("S", 0, 1, 9),
+ /**
+ * int
+ */
+ I("I", 0, 1, 10),
+ /**
+ * float
+ */
+ F("F", 2, 1, 6),
+ /**
+ * long
+ */
+ J("J", 1, 2, 11),
+ /**
+ * double
+ */
+ D("D", 3, 2, 7),
+ /**
+ * Reference type
+ */
+ A("A", 4, 1, -1),
+ /**
+ * char
+ */
+ C("C", 0, 1, 5),
+ /**
+ * boolean
+ */
+ Z("Z", 0, 1, 4),
+ /**
+ * void
+ */
+ V("V", -1, -1, -1),
+ /**
+ * Value type
+ */
+ Q("Q", -1, 1, -1);
+
+ String typeStr;
+ int offset;
+ int width;
+ int newarraycode;
+
+ TypeTag(String typeStr, int offset, int width, int newarraycode) {
+ this.typeStr = typeStr;
+ this.offset = offset;
+ this.width = width;
+ this.newarraycode = newarraycode;
+ }
+
+ static TypeTag commonSupertype(TypeTag t1, TypeTag t2) {
+ if (t1.isIntegral() && t2.isIntegral()) {
+ int p1 = t1.ordinal();
+ int p2 = t2.ordinal();
+ return (p1 <= p2) ? t2 : t1;
+ } else {
+ return null;
+ }
+ }
+
+ public int width() {
+ return width;
+ }
+
+ boolean isIntegral() {
+ switch (this) {
+ case B:
+ case S:
+ case I:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public TypeTag getTag() {
+ return this;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/TypedCodeBuilder.java Fri Sep 08 10:46:46 2017 -0700
@@ -0,0 +1,1215 @@
+/*
+ * 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.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Vector;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+import java.util.function.ToIntFunction;
+
+public class TypedCodeBuilder<S, T, E, C extends TypedCodeBuilder<S, T, E, C>> extends MacroCodeBuilder<S, T, E, C> {
+
+ State lastStackMapState;
+ int lastStackMapPc = -1;
+ Map<CharSequence, LocalVarInfo> lvarOffsets = new HashMap<>();
+ protected State state;
+ int depth = 0;
+ int currLocalOffset = 0;
+
+ class StatefulPendingJump extends PendingJump {
+
+ State state;
+
+ StatefulPendingJump(CharSequence label, int pc, State state) {
+ super(label, pc);
+ this.state = state;
+ }
+
+ @Override
+ boolean resolve(CharSequence label, int pc) {
+ boolean b = super.resolve(label, pc);
+ if (b) {
+ TypedCodeBuilder.this.state = TypedCodeBuilder.this.state.merge(state);
+ }
+ return b;
+ }
+ }
+
+ class LocalVarInfo {
+ CharSequence name;
+ int offset;
+ int depth;
+ TypeTag type;
+
+ LocalVarInfo(CharSequence name, int offset, int depth, TypeTag type) {
+ this.name = name;
+ this.offset = offset;
+ this.depth = depth;
+ this.type = type;
+ }
+ }
+
+ public TypedCodeBuilder(MethodBuilder<S, T, E> methodBuilder) {
+ super(methodBuilder);
+ T t = methodBuilder.desc;
+ state = new State();
+ if ((methodBuilder.flags & Flag.ACC_STATIC.flag) == 0) {
+ T clazz = typeHelper.type(methodBuilder.thisClass);
+ state.load(clazz, currLocalOffset++); //TODO: uninit??
+ }
+ Iterator<T> paramsIt = typeHelper.parameterTypes(t);
+ while (paramsIt.hasNext()) {
+ T p = paramsIt.next();
+ state.load(p, currLocalOffset);
+ currLocalOffset += typeHelper.tag(p).width;
+ }
+ lastStackMapState = state.dup();
+ stacksize = state.stack.size();
+ localsize = state.locals.size();
+ }
+
+ @Override
+ protected C emitOp(Opcode opcode, Object optPoolValue) {
+ updateState(opcode, optPoolValue);
+ return super.emitOp(opcode, optPoolValue);
+ }
+
+ @Override
+ protected SwitchBuilder makeSwitchBuilder() {
+ return new TypedSwitchBuilder();
+ }
+
+ class TypedSwitchBuilder extends SwitchBuilder {
+
+ @Override
+ public SwitchBuilder withCase(int value, Consumer<? super C> case_, boolean fallthrough) {
+ super.withCase(value, c -> {
+ withLocalScope(() -> {
+ State prevState = state;
+ state = prevState.dup();
+ emitStackMap(c.offset());
+ case_.accept(c);
+ state = prevState;
+ });
+ }, fallthrough);
+ return this;
+ }
+
+ @Override
+ public SwitchBuilder withDefault(Consumer<? super C> defaultCase) {
+ super.withDefault(c -> {
+ withLocalScope(() -> {
+ State prevState = state;
+ state = prevState.dup();
+ emitStackMap(c.offset());
+ defaultCase.accept(c);
+ state = prevState;
+ });
+ });
+ return this;
+ }
+ }
+
+ @Override
+ public StatefulTypedBuilder typed(TypeTag tag) {
+ return super.typed(tag, StatefulTypedBuilder::new);
+ }
+
+ public class StatefulTypedBuilder extends LabelledTypedBuilder {
+
+ TypeTag tag;
+
+ StatefulTypedBuilder(TypeTag tag) {
+ this.tag = tag;
+ }
+
+ @Override
+ public C astore_0() {
+ return storeAndUpdate(super::astore_0);
+ }
+
+ @Override
+ public C astore_1() {
+ return storeAndUpdate(super::astore_1);
+ }
+
+ @Override
+ public C astore_2() {
+ return storeAndUpdate(super::astore_2);
+ }
+
+ @Override
+ public C astore_3() {
+ return storeAndUpdate(super::astore_3);
+ }
+
+ @Override
+ public C astore(int n) {
+ return storeAndUpdate(() -> super.astore(n));
+ }
+
+ @Override
+ public C aastore() {
+ return storeAndUpdate(super::aastore);
+ }
+
+ @Override
+ public C areturn() {
+ state.pop(tag);
+ state.push(typeHelper.nullType());
+ return super.areturn();
+ }
+
+ @Override
+ public C anewarray(S s) {
+ super.anewarray(s);
+ state.pop();
+ state.push(typeHelper.arrayOf(typeHelper.type(s)));
+ return thisBuilder();
+ }
+
+ @Override
+ public C aconst_null() {
+ super.aconst_null();
+ state.pop();
+ state.push(tag);
+ return thisBuilder();
+ }
+
+ public C if_acmpeq(CharSequence label) {
+ return jumpAndUpdate(() -> super.if_acmpeq(label));
+ }
+
+ public C if_acmpne(CharSequence label) {
+ return jumpAndUpdate(() -> super.if_acmpne(label));
+ }
+
+ private C storeAndUpdate(Supplier<C> op) {
+ state.pop(tag);
+ state.push(typeHelper.nullType());
+ return op.get();
+ }
+
+ private C jumpAndUpdate(Supplier<C> op) {
+ state.pop(tag);
+ state.pop(tag);
+ state.push(typeHelper.nullType());
+ state.push(typeHelper.nullType());
+ return op.get();
+ }
+ }
+
+ public class State {
+ public final ArrayList<T> stack;
+ public final Vector<T> locals;
+ boolean alive;
+
+ State(ArrayList<T> stack, Vector<T> locals) {
+ this.stack = stack;
+ this.locals = locals;
+ }
+
+ State() {
+ this(new ArrayList<>(), new Vector<>());
+ }
+
+ void push(TypeTag tag) {
+ switch (tag) {
+ case A:
+ case V:
+ throw new IllegalStateException("Bad type tag");
+ default:
+ push(typeHelper.fromTag(tag));
+ }
+ }
+
+ void push(T t) {
+ stack.add(t);
+ if (width(t) == 2) {
+ stack.add(null);
+ }
+ if (stack.size() > stacksize) {
+ stacksize = stack.size();
+ }
+ }
+
+ T peek() {
+ return stack.get(stack.size() - 1);
+ }
+
+ T tosType() {
+ T tos = peek();
+ if (tos == null) {
+ //double slot
+ tos = stack.get(stack.size() - 2);
+ }
+ return tos;
+ }
+
+ T popInternal() {
+ return stack.remove(stack.size() - 1);
+ }
+
+ @SuppressWarnings("unchecked")
+ T pop() {
+ if (stack.size() == 0 || peek() == null) throw new IllegalStateException();
+ return popInternal();
+ }
+
+ T pop2() {
+ T o = stack.get(stack.size() - 2);
+ TypeTag t = typeHelper.tag(o);
+ if (t.width != 2) throw new IllegalStateException();
+ popInternal();
+ popInternal();
+ return o;
+ }
+
+ T pop(TypeTag t) {
+ return (t.width() == 2) ?
+ pop2() : pop();
+ }
+
+ void load(TypeTag tag, int index) {
+ if (tag == TypeTag.A) throw new IllegalStateException("Bad type tag");
+ load(typeHelper.fromTag(tag), index);
+ }
+
+ void load(T t, int index) {
+ ensureDefined(index);
+ locals.set(index, t);
+ if (width(t) == 2) {
+ locals.add(null);
+ }
+ if (locals.size() > localsize) {
+ localsize = locals.size();
+ }
+ }
+
+ void ensureDefined(int index) {
+ if (index >= locals.size()) {
+ locals.setSize(index + 1);
+ }
+ }
+
+ State dup() {
+ State newState = new State(new ArrayList<>(stack), new Vector<>(locals));
+ return newState;
+ }
+
+ State merge(State that) {
+ if (!alive) { return that; }
+ if (that.stack.size() != stack.size()) {
+ throw new IllegalStateException("Bad stack size at merge point");
+ }
+ for (int i = 0; i < stack.size(); i++) {
+ T t1 = stack.get(i);
+ T t2 = that.stack.get(i);
+ stack.set(i, merge(t1, t2, "Bad stack type at merge point"));
+ }
+ int nlocals = locals.size() > that.locals.size() ? that.locals.size() : locals.size();
+ for (int i = 0; i < nlocals; i++) {
+ T t1 = locals.get(i);
+ T t2 = that.locals.get(i);
+ locals.set(i, merge(t1, t2, "Bad local type at merge point"));
+ }
+ if (locals.size() > nlocals) {
+ for (int i = nlocals; i < locals.size(); i++) {
+ locals.remove(i);
+ }
+ }
+ return this;
+ }
+
+ T merge(T t1, T t2, String msg) {
+ if (t1 == null && t2 == null) {
+ return t1;
+ }
+ T res;
+ TypeTag tag1 = typeHelper.tag(t1);
+ TypeTag tag2 = typeHelper.tag(t2);
+ if (tag1 != TypeTag.A && tag2 != TypeTag.A &&
+ tag1 != TypeTag.Q && tag2 != TypeTag.Q) {
+ res = typeHelper.fromTag(TypeTag.commonSupertype(tag1, tag2));
+ } else if (t1 == typeHelper.nullType()) {
+ res = t2;
+ } else if (t2 == typeHelper.nullType()) {
+ res = t1;
+ } else {
+ res = typeHelper.commonSupertype(t1, t2);
+ }
+ if (res == null) {
+ throw new IllegalStateException(msg);
+ }
+ return res;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("[locals = %s, stack = %s]", locals, stack);
+ }
+ }
+
+ int width(T o) {
+ return o == typeHelper.nullType() ?
+ TypeTag.A.width() :
+ typeHelper.tag(o).width;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void updateState(Opcode op, Object optValue) {
+ switch (op) {
+ case VALOAD:
+ case AALOAD:
+ state.pop();
+ state.push(typeHelper.elemtype(state.pop()));
+ break;
+ case GOTO_:
+ state.alive = false;
+ break;
+ case NOP:
+ case INEG:
+ case LNEG:
+ case FNEG:
+ case DNEG:
+ break;
+ case ACONST_NULL:
+ state.push(typeHelper.nullType());
+ break;
+ case ICONST_M1:
+ case ICONST_0:
+ case ICONST_1:
+ case ICONST_2:
+ case ICONST_3:
+ case ICONST_4:
+ case ICONST_5:
+ state.push(TypeTag.I);
+ break;
+ case LCONST_0:
+ case LCONST_1:
+ state.push(TypeTag.J);
+ break;
+ case FCONST_0:
+ case FCONST_1:
+ case FCONST_2:
+ state.push(TypeTag.F);
+ break;
+ case DCONST_0:
+ case DCONST_1:
+ state.push(TypeTag.D);
+ break;
+ case ILOAD_0:
+ case FLOAD_0:
+ case ALOAD_0:
+ case LLOAD_0:
+ case DLOAD_0:
+ state.push(state.locals.get(0));
+ break;
+ case ILOAD_1:
+ case FLOAD_1:
+ case ALOAD_1:
+ case LLOAD_1:
+ case DLOAD_1:
+ state.push(state.locals.get(1));
+ break;
+ case ILOAD_2:
+ case FLOAD_2:
+ case ALOAD_2:
+ case LLOAD_2:
+ case DLOAD_2:
+ state.push(state.locals.get(2));
+ break;
+ case ILOAD_3:
+ case FLOAD_3:
+ case ALOAD_3:
+ case LLOAD_3:
+ case DLOAD_3:
+ state.push(state.locals.get(3));
+ break;
+ case ILOAD:
+ case FLOAD:
+ case ALOAD:
+ case LLOAD:
+ case DLOAD:
+ case VLOAD:
+ state.push(state.locals.get((Integer) optValue));
+ break;
+ case IALOAD:
+ case BALOAD:
+ case CALOAD:
+ case SALOAD:
+ state.pop();
+ state.pop();
+ state.push(TypeTag.I);
+ break;
+ case LALOAD:
+ state.pop();
+ state.pop();
+ state.push(TypeTag.J);
+ break;
+ case FALOAD:
+ state.pop();
+ state.pop();
+ state.push(TypeTag.F);
+ break;
+ case DALOAD:
+ state.pop();
+ state.pop();
+ state.push(TypeTag.D);
+ break;
+ case ISTORE_0:
+ case FSTORE_0:
+ case ASTORE_0:
+ state.load(state.pop(), 0);
+ break;
+ case ISTORE_1:
+ case FSTORE_1:
+ case ASTORE_1:
+ state.load(state.pop(), 1);
+ break;
+ case ISTORE_2:
+ case FSTORE_2:
+ case ASTORE_2:
+ state.load(state.pop(), 2);
+ break;
+ case ISTORE_3:
+ case FSTORE_3:
+ case ASTORE_3:
+ state.load(state.pop(), 3);
+ break;
+ case ISTORE:
+ case FSTORE:
+ case ASTORE:
+ case VSTORE:
+ state.load(state.pop(), (int) optValue);
+ break;
+ case LSTORE_0:
+ case DSTORE_0:
+ state.load(state.pop2(), 0);
+ break;
+ case LSTORE_1:
+ case DSTORE_1:
+ state.load(state.pop2(), 1);
+ break;
+ case LSTORE_2:
+ case DSTORE_2:
+ state.load(state.pop2(), 2);
+ break;
+ case LSTORE_3:
+ case DSTORE_3:
+ state.load(state.pop2(), 3);
+ break;
+ case LSTORE:
+ case DSTORE:
+ state.load(state.pop2(), (int) optValue);
+ break;
+ case POP:
+ case LSHR:
+ case LSHL:
+ case LUSHR:
+ state.pop();
+ break;
+ case VRETURN:
+ case ARETURN:
+ case IRETURN:
+ case FRETURN:
+ state.pop();
+ break;
+ case ATHROW:
+ state.pop();
+ break;
+ case POP2:
+ state.pop2();
+ break;
+ case LRETURN:
+ case DRETURN:
+ state.pop2();
+ break;
+ case DUP:
+ state.push(state.peek());
+ break;
+ case RETURN:
+ break;
+ case ARRAYLENGTH:
+ state.pop();
+ state.push(TypeTag.I);
+ break;
+ case ISUB:
+ case IADD:
+ case IMUL:
+ case IDIV:
+ case IREM:
+ case ISHL:
+ case ISHR:
+ case IUSHR:
+ case IAND:
+ case IOR:
+ case IXOR:
+ state.pop();
+ state.pop();
+ state.push(TypeTag.I);
+ break;
+ case VASTORE:
+ case AASTORE:
+ state.pop();
+ state.pop();
+ state.pop();
+ break;
+ case LAND:
+ case LOR:
+ case LXOR:
+ case LREM:
+ case LDIV:
+ case LMUL:
+ case LSUB:
+ case LADD:
+ state.pop2();
+ state.pop2();
+ state.push(TypeTag.J);
+ break;
+ case LCMP:
+ state.pop2();
+ state.pop2();
+ state.push(TypeTag.I);
+ break;
+ case L2I:
+ state.pop2();
+ state.push(TypeTag.I);
+ break;
+ case I2L:
+ state.pop();
+ state.push(TypeTag.J);
+ break;
+ case I2F:
+ state.pop();
+ state.push(TypeTag.F);
+ break;
+ case I2D:
+ state.pop();
+ state.push(TypeTag.D);
+ break;
+ case L2F:
+ state.pop2();
+ state.push(TypeTag.F);
+ break;
+ case L2D:
+ state.pop2();
+ state.push(TypeTag.D);
+ break;
+ case F2I:
+ state.pop();
+ state.push(TypeTag.I);
+ break;
+ case F2L:
+ state.pop();
+ state.push(TypeTag.J);
+ break;
+ case F2D:
+ state.pop();
+ state.push(TypeTag.D);
+ break;
+ case D2I:
+ state.pop2();
+ state.push(TypeTag.I);
+ break;
+ case D2L:
+ state.pop2();
+ state.push(TypeTag.J);
+ break;
+ case D2F:
+ state.pop2();
+ state.push(TypeTag.F);
+ break;
+ case TABLESWITCH:
+ case LOOKUPSWITCH:
+ state.pop();
+ break;
+ case DUP_X1: {
+ T val1 = state.pop();
+ T val2 = state.pop();
+ state.push(val1);
+ state.push(val2);
+ state.push(val1);
+ break;
+ }
+ case BASTORE:
+ state.pop();
+ state.pop();
+ state.pop();
+ break;
+ case I2B:
+ case I2C:
+ case I2S:
+ break;
+ case FMUL:
+ case FADD:
+ case FSUB:
+ case FDIV:
+ case FREM:
+ state.pop();
+ state.pop();
+ state.push(TypeTag.F);
+ break;
+ case CASTORE:
+ case IASTORE:
+ case FASTORE:
+ case SASTORE:
+ state.pop();
+ state.pop();
+ state.pop();
+ break;
+ case LASTORE:
+ case DASTORE:
+ state.pop2();
+ state.pop();
+ state.pop();
+ break;
+ case DUP2:
+ if (state.peek() != null) {
+ //form 1
+ T value1 = state.pop();
+ T value2 = state.pop();
+ state.push(value2);
+ state.push(value1);
+ state.push(value2);
+ state.push(value1);
+ } else {
+ //form 2
+ T value = state.pop2();
+ state.push(value);
+ state.push(value);
+ }
+ break;
+ case DUP2_X1:
+ if (state.peek() != null) {
+ T value1 = state.pop();
+ T value2 = state.pop();
+ T value3 = state.pop();
+ state.push(value2);
+ state.push(value1);
+ state.push(value3);
+ state.push(value2);
+ state.push(value1);
+ } else {
+ T value1 = state.pop2();
+ T value2 = state.pop();
+ state.push(value1);
+ state.push(value2);
+ state.push(value1);
+ }
+ break;
+ case DUP2_X2:
+ if (state.peek() != null) {
+ T value1 = state.pop();
+ T value2 = state.pop();
+ if (state.peek() != null) {
+ // form 1
+ T value3 = state.pop();
+ T value4 = state.pop();
+ state.push(value2);
+ state.push(value1);
+ state.push(value4);
+ state.push(value3);
+ state.push(value2);
+ state.push(value1);
+ } else {
+ // form 3
+ T value3 = state.pop2();
+ state.push(value2);
+ state.push(value1);
+ state.push(value3);
+ state.push(value2);
+ state.push(value1);
+ }
+ } else {
+ T value1 = state.pop2();
+ if (state.peek() != null) {
+ // form 2
+ T value2 = state.pop();
+ T value3 = state.pop();
+ state.push(value1);
+ state.push(value3);
+ state.push(value2);
+ state.push(value1);
+ } else {
+ // form 4
+ T value2 = state.pop2();
+ state.push(value1);
+ state.push(value2);
+ state.push(value1);
+ }
+ }
+ break;
+ case DUP_X2: {
+ T value1 = state.pop();
+ if (state.peek() != null) {
+ // form 1
+ T value2 = state.pop();
+ T value3 = state.pop();
+ state.push(value1);
+ state.push(value3);
+ state.push(value2);
+ state.push(value1);
+ } else {
+ // form 2
+ T value2 = state.pop2();
+ state.push(value1);
+ state.push(value2);
+ state.push(value1);
+ }
+ }
+ break;
+ case FCMPL:
+ case FCMPG:
+ state.pop();
+ state.pop();
+ state.push(TypeTag.I);
+ break;
+ case DCMPL:
+ case DCMPG:
+ state.pop2();
+ state.pop2();
+ state.push(TypeTag.I);
+ break;
+ case SWAP: {
+ T value1 = state.pop();
+ T value2 = state.pop();
+ state.push(value1);
+ state.push(value2);
+ break;
+ }
+ case DADD:
+ case DSUB:
+ case DMUL:
+ case DDIV:
+ case DREM:
+ state.pop2();
+ state.pop2();
+ state.push(TypeTag.D);
+ break;
+ case RET:
+ break;
+ case WIDE:
+ // must be handled by the caller.
+ return;
+ case MONITORENTER:
+ case MONITOREXIT:
+ state.pop();
+ break;
+ case VNEW:
+ case NEW:
+ state.push(typeHelper.type((S) optValue));
+ break;
+ case NEWARRAY:
+ state.pop();
+ state.push(typeHelper.arrayOf(typeHelper.fromTag((TypeTag) optValue)));
+ break;
+ case ANEWARRAY:
+ state.pop();
+ state.push(typeHelper.arrayOf(typeHelper.arrayOf(typeHelper.type((S)optValue))));
+ break;
+ case VNEWARRAY:
+ case VBOX:
+ case VUNBOX:
+ state.pop();
+ state.push(typeHelper.type((S) optValue));
+ break;
+ case MULTIVNEWARRAY:
+ case MULTIANEWARRAY:
+ for (int i = 0; i < (byte) ((Object[]) optValue)[1]; i++) {
+ state.pop();
+ }
+ state.push(typeHelper.type((S) ((Object[]) optValue)[0]));
+ break;
+ case INVOKEINTERFACE:
+ case INVOKEVIRTUAL:
+ case INVOKESPECIAL:
+ case INVOKESTATIC:
+ case INVOKEDYNAMIC:
+ processInvoke(op, (T) optValue);
+ break;
+ case GETSTATIC:
+ state.push((T) optValue);
+ break;
+ case VGETFIELD:
+ case GETFIELD:
+ state.pop();
+ state.push((T) optValue);
+ break;
+ case PUTSTATIC: {
+ TypeTag tag = typeHelper.tag((T) optValue);
+ if (tag.width == 1) {
+ state.pop();
+ } else {
+ state.pop2();
+ }
+ break;
+ }
+ case PUTFIELD: {
+ TypeTag tag = typeHelper.tag((T) optValue);
+ if (tag.width == 1) {
+ state.pop();
+ } else {
+ state.pop2();
+ }
+ state.pop();
+ break;
+ }
+ case BIPUSH:
+ case SIPUSH:
+ state.push(TypeTag.I);
+ break;
+ case LDC:
+ case LDC_W:
+ case LDC2_W:
+ state.push((T)optValue);
+ break;
+ case IF_ACMPEQ:
+ case IF_ICMPEQ:
+ case IF_ACMPNE:
+ case IF_ICMPGE:
+ case IF_ICMPGT:
+ case IF_ICMPLE:
+ case IF_ICMPLT:
+ case IF_ICMPNE:
+ state.pop();
+ state.pop();
+ break;
+ case IF_NONNULL:
+ case IF_NULL:
+ case IFEQ:
+ case IFGE:
+ case IFGT:
+ case IFLE:
+ case IFLT:
+ case IFNE:
+ state.pop();
+ break;
+ case INSTANCEOF:
+ state.pop();
+ state.push(TypeTag.Z);
+ break;
+ case TYPED:
+ case CHECKCAST:
+ break;
+
+ default:
+ throw new UnsupportedOperationException("Unsupported opcode: " + op);
+ }
+ }
+
+ void processInvoke(Opcode opcode, T invokedType) {
+ Iterator<T> paramsIt = typeHelper.parameterTypes(invokedType);
+ while (paramsIt.hasNext()) {
+ T t = paramsIt.next();
+ TypeTag tag = typeHelper.tag(t);
+ if (tag.width == 2) {
+ state.popInternal();
+ state.popInternal();
+ } else {
+ state.popInternal();
+ }
+ }
+ if (opcode != Opcode.INVOKESTATIC && opcode != Opcode.INVOKEDYNAMIC) {
+ state.pop(); //receiver
+ }
+ T retType = typeHelper.returnType(invokedType);
+ TypeTag retTag = typeHelper.tag(retType);
+ if (retTag != TypeTag.V)
+ state.push(retType);
+ }
+
+ @Override
+ protected C ldc(ToIntFunction<PoolHelper<S, T, E>> indexFunc, boolean fat) {
+ LdcPoolHelper ldcPoolHelper = new LdcPoolHelper();
+ int index = indexFunc.applyAsInt(ldcPoolHelper);
+ fat = typeHelper.tag(ldcPoolHelper.type).width() == 2;
+ return super.ldc(index, ldcPoolHelper.type, fat);
+ }
+ //where
+ class LdcPoolHelper implements PoolHelper<S, T, E> {
+
+ T type;
+
+ @Override
+ public int putClass(S symbol) {
+ type = typeHelper.type(symbol);
+ return poolHelper.putClass(symbol);
+ }
+
+ @Override
+ public int putInt(int i) {
+ type = typeHelper.fromTag(TypeTag.I);
+ return poolHelper.putInt(i);
+ }
+
+ @Override
+ public int putFloat(float f) {
+ type = typeHelper.fromTag(TypeTag.F);
+ return poolHelper.putFloat(f);
+ }
+
+ @Override
+ public int putLong(long l) {
+ type = typeHelper.fromTag(TypeTag.J);
+ return poolHelper.putLong(l);
+ }
+
+ @Override
+ public int putDouble(double d) {
+ type = typeHelper.fromTag(TypeTag.D);
+ return poolHelper.putDouble(d);
+ }
+
+ @Override
+ public int putString(String s) {
+ type = typeHelper.type(typeHelper.symbolFrom("java/lang/String"));
+ return poolHelper.putString(s);
+ }
+
+ @Override
+ public int putDynamicConstant(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs) {
+ type = constType;
+ return poolHelper.putDynamicConstant(constName, constType, bsmClass, bsmName, bsmType, staticArgs);
+ }
+
+ @Override
+ public int putFieldRef(S owner, CharSequence name, T type) {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public int putMethodRef(S owner, CharSequence name, T type, boolean isInterface) {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public int putUtf8(CharSequence s) {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public int putType(T t) {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public int putMethodType(T t) {
+ type = typeHelper.type(typeHelper.symbolFrom("java/lang/invoke/MethodType"));
+ return poolHelper.putMethodType(t);
+ }
+
+ @Override
+ public int putHandle(int refKind, S owner, CharSequence name, T t) {
+ type = typeHelper.type(typeHelper.symbolFrom("java/lang/invoke/MethodHandle"));
+ return poolHelper.putHandle(refKind, owner, name, t);
+ }
+
+ @Override
+ public int putInvokeDynamic(CharSequence invokedName, T invokedType, S bsmClass, CharSequence bsmName, T bsmType, Consumer<StaticArgListBuilder<S, T, E>> staticArgs) {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public int size() {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public E entries() {
+ throw new IllegalStateException();
+ }
+ }
+
+ public C load(int index) {
+ return load(typeHelper.tag(state.locals.get(index)), index);
+ }
+
+ public C store(int index) {
+ return store(typeHelper.tag(state.tosType()), index);
+ }
+
+ @Override
+ public C withLocalSize(int localsize) {
+ throw new IllegalStateException("Local size automatically computed");
+ }
+
+ @Override
+ public C withStackSize(int stacksize) {
+ throw new IllegalStateException("Stack size automatically computed");
+ }
+
+ public C withLocal(CharSequence name, T type) {
+ int offset = currLocalOffset;
+ TypeTag tag = typeHelper.tag(type);
+ lvarOffsets.put(name, new LocalVarInfo(name, offset, depth, tag));
+ state.load(type, offset);
+ currLocalOffset += tag.width;
+ return thisBuilder();
+ }
+
+ public C load(CharSequence local) {
+ return load(lvarOffsets.get(local).offset);
+ }
+
+ public C store(CharSequence local) {
+ return store(lvarOffsets.get(local).offset);
+ }
+
+ @Override
+ public C withTry(Consumer<? super C> tryBlock, Consumer<? super CatchBuilder> catchBlocks) {
+ return super.withTry(c -> {
+ withLocalScope(() -> {
+ tryBlock.accept(c);
+ });
+ }, catchBlocks);
+ }
+
+ @Override
+ protected CatchBuilder makeCatchBuilder(int start, int end) {
+ return new TypedCatchBuilder(start, end);
+ }
+
+ class TypedCatchBuilder extends CatchBuilder {
+
+ State initialState = state.dup();
+
+ TypedCatchBuilder(int start, int end) {
+ super(start, end);
+ }
+
+ @Override
+ protected void emitCatch(S exc, Consumer<? super C> catcher) {
+ withLocalScope(() -> {
+ state.push(typeHelper.type(exc));
+ emitStackMap(code.offset);
+ super.emitCatch(exc, catcher);
+ state = initialState;
+ });
+ }
+
+ @Override
+ protected void emitFinalizer() {
+ withLocalScope(() -> {
+ state.push(typeHelper.type(typeHelper.symbolFrom("java/lang/Throwable")));
+ emitStackMap(code.offset);
+ super.emitFinalizer();
+ });
+ }
+ }
+
+ protected void withLocalScope(Runnable runnable) {
+ int prevDepth = depth;
+ try {
+ depth++;
+ runnable.run();
+ } finally {
+ Iterator<Entry<CharSequence, LocalVarInfo>> lvarIt = lvarOffsets.entrySet().iterator();
+ while (lvarIt.hasNext()) {
+ LocalVarInfo lvi = lvarIt.next().getValue();
+ if (lvi.depth == depth) {
+ int width = lvi.type.width;
+ currLocalOffset -= width;
+ lvarIt.remove();
+ }
+ }
+ depth = prevDepth;
+ }
+ }
+
+ @Override
+ void addPendingJump(CharSequence label, int pc) {
+ pendingJumps.add(new StatefulPendingJump(label, pc, state.dup()));
+ }
+
+ @Override
+ void resolveJumps(CharSequence label, int pc) {
+ super.resolveJumps(label, pc);
+ emitStackMap(pc);
+ }
+
+ //TODO: optimize stackmap generation by avoiding intermediate classes
+ protected void emitStackMap(int pc) {
+ //stack map generation
+ if (pc > lastStackMapPc) {
+ writeStackMapFrame(pc);
+ lastStackMapState = state.dup();
+ lastStackMapPc = pc;
+ nstackmaps++;
+ }
+ }
+
+ @Override
+ void build(GrowableByteBuffer buf) {
+ if (stacksize == -1) {
+ throw new IllegalStateException("Bad stack size");
+ }
+ if (localsize == -1) {
+ throw new IllegalStateException("Bad locals size");
+ }
+ if (nstackmaps > 0) {
+ GrowableByteBuffer stackmapsAttr = new GrowableByteBuffer();
+ stackmapsAttr.writeChar(nstackmaps);
+ stackmapsAttr.writeBytes(stackmaps);
+ withAttribute("StackMapTable", stackmapsAttr.bytes());
+ }
+ super.build(buf);
+ }
+
+ /**
+ * Compare this frame with the previous frame and produce
+ * an entry of compressed stack map frame.
+ */
+ void writeStackMapFrame(int pc) {
+ List<T> locals = state.locals;
+ List<T> stack = state.stack;
+ List<T> prev_locals = lastStackMapState.locals;
+ int offset_delta = lastStackMapPc == -1 ? pc : pc - lastStackMapPc - 1;
+ if (stack.size() == 1) {
+ if (locals.size() == prev_locals.size() && prev_locals.equals(locals)) {
+ sameLocals1StackItemFrame(offset_delta, stack.get(stack.size() - 1));
+ return;
+ }
+ } else if (stack.size() == 0) {
+ int diff_length = prev_locals.size() - locals.size();
+ if (diff_length == 0) {
+ sameFrame(offset_delta);
+ return;
+ } else if (-MAX_LOCAL_LENGTH_DIFF < diff_length && diff_length < 0) {
+ appendFrame(offset_delta, prev_locals.size(), locals);
+ return;
+ } else if (0 < diff_length && diff_length < MAX_LOCAL_LENGTH_DIFF) {
+ chopFrame(offset_delta, diff_length);
+ return;
+ }
+ }
+ fullFrame(offset_delta, locals, stack);
+ }
+}
--- a/test/jdk/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java Wed Jan 31 10:55:49 2018 -0800
+++ b/test/jdk/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java Fri Sep 08 10:46:46 2017 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -805,6 +805,25 @@
}
@Override
+ public String visitDynamicConstant(ConstantPool.CONSTANT_Dynamic_info c, Integer p) {
+ String value = slist.get(p);
+ if (value == null) {
+ try {
+ value = bsmlist.get(c.bootstrap_method_attr_index) + " "
+ + visit(cfpool.get(c.name_and_type_index), c.name_and_type_index);
+ slist.set(p, value);
+ xpool.add(new Element("CONSTANT_Dynamic",
+ new String[]{"id", p.toString()},
+ value));
+
+ } catch (ConstantPoolException ex) {
+ ex.printStackTrace();
+ }
+ }
+ return value;
+ }
+
+ @Override
public String visitLong(CONSTANT_Long_info c, Integer p) {
String value = slist.get(p);
if (value == null) {
--- a/test/langtools/tools/javac/lambda/ByteCodeTest.java Wed Jan 31 10:55:49 2018 -0800
+++ b/test/langtools/tools/javac/lambda/ByteCodeTest.java Fri Sep 08 10:46:46 2017 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 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
@@ -541,6 +541,22 @@
}
@Override
+ public String visitDynamicConstant(CONSTANT_Dynamic_info c, Integer p) {
+
+ String value = slist.get(p);
+ if (value == null) {
+ try {
+ value = bsmMap.get(c.bootstrap_method_attr_index) + " "
+ + visit(cfpool.get(c.name_and_type_index), c.name_and_type_index);
+ slist.set(p, value);
+ } catch (ConstantPoolException ex) {
+ ex.printStackTrace();
+ }
+ }
+ return value;
+ }
+
+ @Override
public String visitLong(CONSTANT_Long_info c, Integer p) {
String value = slist.get(p);