# HG changeset patch # User psandoz # Date 1504892806 25200 # Node ID c4d9d1b08e2e35257d605d8efb29b0fd726f852f # Parent ef8a98bc71f8f46b61d046e16445f0b5e50378d9 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 diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/cpu/x86/interp_masm_x86.cpp --- 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) diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/cpu/x86/macroAssembler_x86.cpp --- 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); diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/cpu/x86/templateTable_x86.cpp --- 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::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); diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/c1/c1_GraphBuilder.cpp --- 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; } diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/ci/ciEnv.cpp --- 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(); diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/ci/ciReplay.cpp --- 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?"); diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/ci/ciStreams.cpp --- 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); } // ------------------------------------------------------------------ diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/classfile/classFileParser.cpp --- 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: diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/classfile/systemDictionary.cpp --- 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); diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/classfile/systemDictionary.hpp --- 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, diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/classfile/verifier.cpp --- 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; diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/classfile/verifier.hpp --- 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; diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/classfile/vmSymbols.cpp --- 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; diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/classfile/vmSymbols.hpp --- 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") \ diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/interpreter/bytecode.cpp --- 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); } diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/interpreter/bytecodeInterpreter.cpp --- 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)) { diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/interpreter/bytecodeTracer.cpp --- 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); } diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/interpreter/interpreterRuntime.cpp --- 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 diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/interpreter/linkResolver.cpp --- 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); } diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/interpreter/linkResolver.hpp --- 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); diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/interpreter/rewriter.cpp --- 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; diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/interpreter/templateTable.cpp --- 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 , _ ); diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/interpreter/templateTable.hpp --- 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(); diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/jvmci/jvmciCompilerToVM.cpp --- 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)) diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/jvmci/vmStructs_jvmci.cpp --- 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*) \ 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) \ diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/memory/universe.cpp --- 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* 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("", CHECK); + _the_null_sentinel = tns(); + } + // Maybe this could be lifted up now that object array can be initialized // during the bootstrapping. diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/memory/universe.hpp --- 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); diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/oops/constantPool.cpp --- 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)); diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/oops/constantPool.hpp --- 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* _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); diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/oops/generateOopMap.cpp --- 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); diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/opto/parse2.cpp --- 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 diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/prims/jvm.cpp --- 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; } diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/prims/jvmtiRedefineClasses.cpp --- 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. diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/prims/methodHandles.cpp --- 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)}, diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/runtime/globals.hpp --- 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.)") \ diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/runtime/reflection.cpp --- 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; } diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/runtime/signature.cpp --- 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() { diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/runtime/signature.hpp --- 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(); diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/runtime/vmStructs.cpp --- 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) \ \ /*****************************/ \ diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/utilities/constantTag.cpp --- 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 : diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/utilities/constantTag.hpp --- 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; diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/utilities/exceptions.cpp --- 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; diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/utilities/exceptions.hpp --- 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; diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/hotspot/share/utilities/globalDefinitions.hpp --- 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 ) { diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/java.base/share/classes/java/lang/BootstrapMethodError.java --- 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 diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/java.base/share/classes/java/lang/invoke/AbstractConstantGroup.java --- /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 { + 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 asList() { + return new AsList(self, offset, offset + size); + } + + @Override + public List 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 { + 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 iterator() { + if (resolving) + return new AsIterator(self, offset, offset + size); + else + return new AsIterator(self, offset, offset + size, ifNotPresent); + } + + @Override public List 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[] 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 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 extends WithCache implements BootstrapCallInfo { + 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; } + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/java.base/share/classes/java/lang/invoke/BootstrapCallInfo.java --- /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 + * bootstrap method of an + * dynamic call site or dynamic constant. + * This information includes the method itself, the associated + * name and type, and any associated static arguments. + *

+ * 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. + *

+ * 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 pulls bootstrap parameters from + * the JVM under control of the bootstrap method, as opposed to + * the JVM pushing parameters to a bootstrap method + * by resolving them all before the bootstrap method is called. + * @apiNote + *

+ * The {@linkplain Lookup lookup object} is not 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. + *

+ * 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. + *

{@code
+static Object genericBSM(Lookup lookup, BootstrapCallInfo bsci)
+    throws Throwable {
+  ArrayList args = new ArrayList<>();
+  args.add(lookup);
+  args.add(bsci.invocationName());
+  args.add(bsci.invocationType());
+  MethodHandle bsm = (MethodHandle) bsci.get(0);
+  List restOfArgs = bsci.asList().subList(1, bsci.size();
+  // the next line eagerly resolves all remaining static arguments:
+  args.addAll(restOfArgs);
+  return bsm.invokeWithArguments(args);
+}
+ * }
+ *
+ * 

+ * 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. + *

{@code
+static Object genericBSM(Lookup lookup, String name, Object type,
+                         MethodHandle bsm, Object... args)
+    throws Throwable {
+  ConstantGroup cons = ConstantGroup.makeConstantGroup(Arrays.asList(args));
+  BootstrapCallInfo bsci = makeBootstrapCallInfo(bsm, name, type, cons);
+  return bsm.invoke(lookup, bsci);
+}
+ * }
+ *
+ * @since 1.10
+ */
+// public
+interface BootstrapCallInfo 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  the type of the invocation type, either {@link MethodHandle} or {@link Class}
+     * @return a new bootstrap call descriptor with the given components
+     */
+    static  BootstrapCallInfo makeBootstrapCallInfo(MethodHandle bsm,
+                                                          String name,
+                                                          T type,
+                                                          ConstantGroup constants) {
+        AbstractConstantGroup.BSCIWithCache bsci = new AbstractConstantGroup.BSCIWithCache<>(bsm, name, type, constants.size());
+        final Object NP = AbstractConstantGroup.BSCIWithCache.NOT_PRESENT;
+        bsci.initializeCache(constants.asList(NP), NP);
+        return bsci;
+    }
+}
diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java
--- /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  the expected return type
+     * @return the expected value, either a CallSite or a constant value
+     */
+    static  T invoke(Class 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 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 extends BSCIWithCache {
+        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);
+            }
+        }
+    }
+}
diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/java.base/share/classes/java/lang/invoke/CallSite.java
--- 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]);
-        }
-    }
 }
diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/java.base/share/classes/java/lang/invoke/ConstantGroup.java
--- /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.
+ * 

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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. + *

+ * 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 may 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. + *

+ * 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. + *

+ * 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 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 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 constants, + Object ifNotPresent, + IntFunction 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 constants) { + final Object NP = AbstractConstantGroup.WithCache.NOT_PRESENT; + assert(!constants.contains(NP)); // secret value + return makeConstantGroup(constants, NP, null); + } + +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/java.base/share/classes/java/lang/invoke/DynamicConstant.java --- /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); + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/java.base/share/classes/java/lang/invoke/MethodHandleNatives.java --- 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 initCauseFrom(E err, Exception ex) { Throwable th = ex.getCause(); - if (err.getClass().isInstance(th)) - return (Error) th; + @SuppressWarnings("unchecked") + final Class Eclass = (Class) err.getClass(); + if (Eclass.isInstance(th)) + return Eclass.cast(th); err.initCause(th == null ? ex : th); return err; } diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/java.base/share/classes/java/lang/invoke/MethodHandleStatics.java --- 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; + } } diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/java.base/share/classes/java/lang/invoke/MethodHandles.java --- 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 @@ *

* This method is caller sensitive, which means that it may return different * values to different callers. - *

- * 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 - * invokedynamic instruction - * executing in the same caller class {@code C}. * @return a lookup object for the caller of this method, with private access */ @CallerSensitive diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/java.base/share/classes/java/lang/invoke/package-info.java --- 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. * *

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

    *
  • The classes {@link java.lang.invoke.MethodHandle MethodHandle} * {@link java.lang.invoke.VarHandle VarHandle} contain @@ -40,77 +39,106 @@ *
  • * *
  • 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}. + *
  • + * + *
  • The {@code invokedynamic} instruction makes use of bootstrap {@code MethodHandle} + * constants to dynamically resolve {@code CallSite} objects for custom method invocation + * behavior. + *
  • + * + *
  • The {@code ldc} instruction makes use of bootstrap {@code MethodHandle} constants + * to dynamically resolve custom constant values. *
  • *
* - *

Summary of relevant Java Virtual Machine changes

+ *

Dynamic resolution of call sites and constants

* 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 dynamic call site. - *

{@code invokedynamic} instructions

- * A dynamic call site is originally in an unlinked state. In this state, there is - * no target method for the call site to invoke. + *

Dynamically-computed call sites

+ * An {@code invokedynamic} instruction is originally in an unlinked state. + * In this state, there is no target method for the instruction to invoke. *

- * Before the JVM can execute a dynamic call site (an {@code invokedynamic} instruction), - * the call site must first be linked. + * Before the JVM can execute an {@code invokedynamic} instruction, + * the instruction must first be linked. * Linking is accomplished by calling a bootstrap method - * 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. *

* 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. + * + *

Dynamically-computed constants

+ * The constant pool may contain constants tagged {@code CONSTANT_Dynamic}, + * equipped with bootstrap methods which perform their resolution. + * Such a dynamic constant is originally in an unresolved state. + * Before the JVM can use a dynamically-computed constant, it must first be resolved. + * Dynamically-computed constant resolution is accomplished by calling a bootstrap method + * which is given the static information content of the constant, + * and which must produce a value of the constant's statically declared type. *

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

- * 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}.) + * + *

Execution of bootstrap methods

+ * Resolving a dynamically-computed call site or constant + * starts with resolving constants from the constant pool for the + * following items: *
    - *
  • a {@code MethodHandles.Lookup}, a lookup object on the caller class - * in which dynamic call site occurs
  • - *
  • a {@code String}, the method name mentioned in the call site
  • - *
  • a {@code MethodType}, the resolved type descriptor of the call
  • - *
  • optionally, any number of additional static arguments taken from the constant pool
  • + *
  • the bootstrap method, a {@code CONSTANT_MethodHandle}
  • + *
  • the {@code Class} or {@code MethodType} derived from + * type component of the {@code CONSTANT_NameAndType} descriptor
  • + *
  • static arguments, if any (note that static arguments can themselves be + * dynamically-computed constants)
  • *
*

- * 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: + *

    + *
  • a {@code MethodHandles.Lookup}, which is a lookup object on the caller class + * in which dynamically-computed constant or call site occurs
  • + *
  • a {@code String}, the name mentioned in the {@code CONSTANT_NameAndType}
  • + *
  • a {@code MethodType} or {@code Class}, the resolved type descriptor of the {@code CONSTANT_NameAndType}
  • + *
  • a {@code Class}, the resolved type descriptor of the constant, if it is a dynamic constant
  • + *
  • the additional resolved static arguments, if any
  • + *
*

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

- * 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. + *

+ * 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. - *

timing of linkage

- * 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. + * + *

Timing of resolution

+ * 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. *

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

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

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

* Discussion: - * 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. * - *

types of bootstrap methods

- * As long as each bootstrap method can be correctly invoked - * by {@code MethodHandle.invoke}, its detailed type is arbitrary. + *

Types of bootstrap methods

+ * 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.) *

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

- * 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.) *

- * 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. * * @@ -208,28 +229,27 @@ * {@code String}. * The other examples work with all types of extra arguments. *

- * 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}.) - *

- * 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 directly supplied by {@code CONSTANT_Integer} + * constant pool entries, since the {@code asType} conversions do * not perform the necessary narrowing primitive conversions. *

- * 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 diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/java.base/share/classes/jdk/internal/org/objectweb/asm/ClassReader.java --- 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: diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/java.base/share/native/include/classfile_constants.h.template --- 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 }; diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/jdk.aot/share/classes/jdk.tools.jaotc/src/jdk/tools/jaotc/GraalFilters.java --- 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; } diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassFile.java --- 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; diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java --- 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; diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ConstantPool.java --- 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; diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/ClassConstants.java --- 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 diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/tools/jcore/ClassWriter.java --- 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); diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java --- 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)) + diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/utilities/ConstantTag.java --- 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; } diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/CompilerToVM.java --- 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. diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotConstantPool.java --- 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; diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java --- 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*"); 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); diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassTranslator.java --- 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 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 translations) { CONSTANT_Long_info info2 = (CONSTANT_Long_info) translations.get(info); diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/jdk.jdeps/share/classes/com/sun/tools/classfile/ClassWriter.java --- 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); diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/jdk.jdeps/share/classes/com/sun/tools/classfile/ConstantPool.java --- 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 accept(Visitor 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); diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/jdk.jdeps/share/classes/com/sun/tools/classfile/Dependencies.java --- 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; } diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/jdk.jdeps/share/classes/com/sun/tools/classfile/ReferenceFinder.java --- 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; } diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/jdk.jdeps/share/classes/com/sun/tools/javap/ConstantWriter.java --- 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"; } diff -r ef8a98bc71f8 -r c4d9d1b08e2e src/jdk.jdeps/share/classes/com/sun/tools/jdeprscan/scan/CPSelector.java --- 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; diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/java/lang/invoke/common/test/java/lang/invoke/lib/InstructionHelper.java --- /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("", "()V", M -> + M.withFlags(Flag.ACC_PUBLIC) + .withCode(TypedCodeBuilder::new, C -> + C.aload_0().invokespecial("java/lang/Object", "", "()V", false).return_() + )); + } + + public static MethodHandle invokedynamic(MethodHandles.Lookup l, + String name, MethodType type, + String bsmMethodName, MethodType bsmType, + Consumer> 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> 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> 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, Integer> poolFunc) throws Exception { + return ldc(l, cref(type), poolFunc); + } + + public static MethodHandle ldc(MethodHandles.Lookup l, + String type, + Function, 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); + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/java/lang/invoke/condy/BootstrapMethodJumboArgsTest.java --- /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 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); + } + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/java/lang/invoke/condy/CondyBSMException.java --- /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... 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 type) throws Throwable { + Throwable t; + try { + Constructor c = type.getDeclaredConstructor(String.class); + t = c.newInstance(name); + } + catch (Exception e) { + throw new InternalError(); + } + throw t; + } + + static MethodHandle thrower(String message, Class 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); + } + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/java/lang/invoke/condy/CondyBSMInvocation.java --- /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())); + } + } + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/java/lang/invoke/condy/CondyBSMValidationTest.java --- /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 -> { + }); + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/java/lang/invoke/condy/CondyNameValidationTest.java --- /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("", + "", + "") + .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 -> { + }); + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/java/lang/invoke/condy/CondyNestedTest.java --- /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("", "()V", M -> + M.withFlags(Flag.ACC_PUBLIC) + .withCode(TypedCodeBuilder::new, C -> + C.aload_0().invokespecial("java/lang/Object", "", "()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", "", "(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", "", "(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... 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... ts) throws Exception { + Method m = c.getMethod(methodName); + m.setAccessible(true); + test(m, ts); + } + +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/java/lang/invoke/condy/CondyNestedTest_Code.jcod --- /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 ""; // #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 diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/java/lang/invoke/condy/CondyRepeatFailedResolution.java --- /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("", "()V", M -> + M.withFlags(Flag.ACC_PUBLIC) + .withCode(TypedCodeBuilder::new, C -> + C.aload_0().invokespecial("java/lang/Object", "", "()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"); + } + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/java/lang/invoke/condy/CondyReturnPrimitiveTest.java --- /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("", "()V", M -> + M.withFlags(Flag.ACC_PUBLIC) + .withCode(TypedCodeBuilder::new, C -> + C.aload_0().invokespecial("java/lang/Object", "", "()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); + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/java/lang/invoke/condy/CondyStaticArgumentsTest.java --- /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 int bigDecimalPoolHelper(String value, String mc, PoolHelper 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 int mathContextPoolHelper(String mc, PoolHelper 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"); + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/java/lang/invoke/condy/CondyTypeValidationTest.java --- /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 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 -> { + }); + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/java/lang/invoke/condy/CondyWithGarbageTest.java --- /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("", "()V", M -> + M.withFlags(Flag.ACC_PUBLIC) + .withCode(TypedCodeBuilder::new, C -> + C.aload_0().invokespecial("java/lang/Object", "", "()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), "", "()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 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("", "()V", M -> + M.withFlags(Flag.ACC_PUBLIC) + .withCode(TypedCodeBuilder::new, C -> + C.aload_0().invokespecial("java/lang/Object", "", "()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), "", "()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 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 -> { + }); + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/java/lang/invoke/condy/CondyWrongType.java --- /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> 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 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); + } + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/AbstractBuilder.java --- /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 the type of the symbol representation + * @param the type of type descriptors representation + * @param the type of pool entries + * @param the type of this builder + */ +public class AbstractBuilder> { + /** + * The helper to build the constant pool. + */ + protected final PoolHelper poolHelper; + + /** + * The helper to use to manipulate type descriptors. + */ + protected final TypeHelper 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 poolHelper, TypeHelper typeHelper) { + this.poolHelper = poolHelper; + this.typeHelper = typeHelper; + } + + @SuppressWarnings("unchecked") + D thisBuilder() { + return (D) this; + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/AnnotationsBuilder.java --- /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 extends AbstractBuilder> { + + GrowableByteBuffer annoAttribute; + int nannos; + + AnnotationsBuilder(PoolHelper poolHelper, TypeHelper 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 withAnnotation(T annoType, Consumer 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 annotationBuilder) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + writeAnnotationValue(annoType, annotationBuilder); + return this; + } + + private void writeAnnotationValue(T annoType, Consumer 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 void writePrimitiveValue(Tag tag, Z value, ToIntBiFunction, 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... annotationBuilders) { + annoAttribute.writeChar(poolHelper.putUtf8(name)); + annoAttribute.writeChar(annotationBuilders.length); + for (Consumer 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 annotationBuilder) { + return withElements(new AnnotationElementBuilder(), annotationBuilder); + } + + int withElements(Consumer annotationBuilder) { + return withElements(this, annotationBuilder); + } + + private int withElements(AnnotationElementBuilder builder, Consumer annotationBuilder) { + annotationBuilder.accept(builder); + return builder.nelems; + } + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/AttributeBuilder.java --- /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 the type of the symbol representation + * @param the type of type descriptors representation + * @param the type of pool entries + * @param the type of this builder + */ + public class AttributeBuilder> + extends AbstractBuilder { + + /** + * 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 poolHelper, TypeHelper typeHelper) { + super(poolHelper, typeHelper); + } + + /** + * Add a class file Attribute. Defined as: + *

+     * {@code   attribute_info {
+     *     u2 attribute_name_index;
+     *     u4 attribute_length;
+     *     u1 info[attribute_length];
+     *   }}
+     * 
+ * + * @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: + *
+     * {@code   attribute_info {
+     *     u2 attribute_name_index;
+     *     u4 attribute_length;
+     *     u1 info[attribute_length];
+     *   }}
+     * 
+ * + * @param 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 D withAttribute(CharSequence name, Z attr, AttributeWriter 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 the type of symbol representation + * @param the type of type descriptors representation + * @param the type of pool entries + * @param the type of the object representing the attribute + */ + public interface AttributeWriter { + + /** + * 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 poolHelper, GrowableByteBuffer buf); + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/BasicClassBuilder.java --- /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 { + + 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()); + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/BasicTypeHelper.java --- /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 { + + @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 ""; + } + + @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 parameterTypes(String s) { + //TODO: gracefully non-method types + return new Iterator() { + 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()); + } + +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/BytePoolHelper.java --- /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 the type of the symbol representation + * @param the type of type descriptors representation + */ +public class BytePoolHelper implements PoolHelper { + + GrowableByteBuffer pool = new GrowableByteBuffer(); + GrowableByteBuffer bsm_attr = new GrowableByteBuffer(); + //Map indicesMap = new HashMap<>(); + int currentIndex = 1; + int currentBsmIndex = 0; + + KeyMap entries = new KeyMap<>(); + KeyMap bootstraps = new KeyMap<>(); + PoolKey key = new PoolKey(); + BsmKey bsmKey = new BsmKey(); + + Function symbolToString; + Function typeToString; + + public BytePoolHelper(Function symbolToString, Function typeToString) { + this.symbolToString = symbolToString; + this.typeToString = typeToString; + } + + static class KeyMap> { + + @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> { + 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 { + 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 { + String bsmClass; + CharSequence bsmName; + String bsmType; + List bsmArgs; + + void set(String bsmClass, CharSequence bsmName, String bsmType, List 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> 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> 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> 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> 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> staticArgs) { + ByteStaticArgListBuilder staticArgsBuilder = new ByteStaticArgListBuilder(); + staticArgs.accept(staticArgsBuilder); + List 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 { + + List 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 add(int refKind, S owner, CharSequence name, T type) { + indexes.add(putHandle(refKind, owner, name, type)); + return this; + } + public ByteStaticArgListBuilder add(Z z, ToIntBiFunction, 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> 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(); + } + + > void addAttributes(ClassBuilder cb) { + if (currentBsmIndex > 0) { + GrowableByteBuffer bsmAttrBuf = new GrowableByteBuffer(); + bsmAttrBuf.writeChar(currentBsmIndex); + bsmAttrBuf.writeBytes(bsm_attr); + cb.withAttribute("BootstrapMethods", bsmAttrBuf.bytes()); + } + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/ClassBuilder.java --- /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 the type of symbol representation + * @param the type of type descriptors representation + * @param the type of this builder + */ +public class ClassBuilder> + extends DeclBuilder { + + /** + * The helper to use to manipulate type descriptors. + */ + protected TypeHelper 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 poolHelper, TypeHelper 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> fieldConfig) { + FieldBuilder 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> methodConfig) { + MethodBuilder 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)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(); + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/CodeBuilder.java --- /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: + *
+ * {@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];
+ * } }
+ * 
+ * + * @param the type of symbol representation + * @param the type of type descriptors representation + * @param the type of pool entries + * @param the type of this code builder + */ +public class CodeBuilder> extends AttributeBuilder { + + protected GrowableByteBuffer code = new GrowableByteBuffer(); + GrowableByteBuffer catchers = new GrowableByteBuffer(); + GrowableByteBuffer stackmaps = new GrowableByteBuffer(); + MethodBuilder methodBuilder; + int ncatchers; + int stacksize = -1; + int localsize = -1; + int nstackmaps = 0; + + public enum JumpMode { + NARROW, + WIDE; + } + + CodeBuilder(MethodBuilder 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 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> 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> staticArgs) { + boolean fat = typeHelper.tag(constType).width() == 2; + return ldc(pool -> pool.putDynamicConstant(constName, constType, bsmClass, bsmName, bsmType, staticArgs), fat); + } + + public C ldc(Z z, BiFunction, Z, Integer> poolFunc) { + return ldc(pool -> poolFunc.apply(pool, z), false); + } + + protected C ldc(ToIntFunction> 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 typed(TypeTag typeTag, Function 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 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 locals, List 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); + } + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/DeclBuilder.java --- /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 the type of symbol representation + * @param the type of type descriptors representation + * @param the type of pool entries + * @param the type of this builder + */ +public class DeclBuilder> + extends AttributeBuilder { + + /** + * The access flags of the declaration, as bit flags. + */ + protected int flags; + + AnnotationsBuilder runtimeInvisibleAnnotations; + AnnotationsBuilder 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 poolHelper, TypeHelper 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.AnnotationElementBuilder> annotations) { + getAnnotations(kind).withAnnotation(annoType, annotations); + return thisBuilder(); + } + + private AnnotationsBuilder 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()); + } + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/FieldBuilder.java --- /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 the type of symbol representation + * @param the type of type descriptor representation + * @param the type of pool entries + */ +public class FieldBuilder extends MemberBuilder> { + public FieldBuilder(CharSequence name, T type, PoolHelper poolHelper, TypeHelper typeHelper) { + super(name, type, poolHelper, typeHelper); + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/Flag.java --- /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 flags = EnumSet.noneOf(Flag.class); + for (Flag f : Flag.values()) { + if ((f.flag & flagsMask) != 0) { + flags.add(f); + } + } + return flags.stream().toArray(Flag[]::new); + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/GrowableByteBuffer.java --- /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 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; + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/IsolatedMethodBuilder.java --- /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, String, Object[]> { + + public IsolatedMethodBuilder(Lookup lookup, String name, String type) { + super(null, name, type, new IsolatedMethodPoolHelper(lookup), null); + } + + static class IsolatedMethodPoolHelper implements PoolHelper, String, Object[]> { + Map 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, String, Object[]>> staticArgs) { + return 0; //??? + } + + @Override + public int putDynamicConstant(CharSequence constName, String constType, Class bsmClass, CharSequence bsmName, String bsmType, Consumer, 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)); + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/MacroCodeBuilder.java --- /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> extends CodeBuilder { + + JumpMode jumpMode = JumpMode.NARROW; + + Map labels = new HashMap<>(); + List 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 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 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 tryBlock, Consumer 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> catchers = new LinkedHashMap<>(); + public Consumer finalizer; + List pendingGaps = new ArrayList<>(); + + public CatchBuilder(int start, int end) { + this.start = start; + this.end = end; + } + + public CatchBuilder withCatch(S exc, Consumer catcher) { + catchers.put(exc, catcher); + return this; + } + + public CatchBuilder withFinally(Consumer finalizer) { + this.finalizer = finalizer; + return this; + } + + @SuppressWarnings("unchecked") + void build() { + if (finalizer != null) { + finalizer.accept((C) MacroCodeBuilder.this); + } + goto_(endLabel); + for (Map.Entry> 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 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 catcher) { +// int offset = code.offset; +// MacroCodeBuilder.this.withCatch(exc, start, end, offset); +// catcher.accept((C)MacroCodeBuilder.this); +// return this; +// } +// +// @SuppressWarnings("unchecked") +// CatchBuilder withFinally(Consumer 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 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 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 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> cases = new TreeMap<>(); + int lo = Integer.MAX_VALUE; + int hi = Integer.MIN_VALUE; + String endLabel = labelName(); + + public Consumer defaultCase; + + @SuppressWarnings("unchecked") + public SwitchBuilder withCase(int value, Consumer case_, boolean fallthrough) { + if (value > hi) { + hi = value; + } + if (value < lo) { + lo = value; + } + if (!fallthrough) { + Consumer prevCase = case_; + case_ = C -> { + prevCase.accept(C); + C.goto_(endLabel); + }; + } + cases.put(value, case_); + return this; + } + + @SuppressWarnings("unchecked") + public SwitchBuilder withDefault(Consumer 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; + } + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/MemberBuilder.java --- /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 the type of symbol representation + * @param the type of type descriptors representation + * @param the type of pool entries + * @param the type of this builder + */ +public class MemberBuilder> extends DeclBuilder { + + 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 poolHelper, TypeHelper 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(); + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/MethodBuilder.java --- /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 extends MemberBuilder> { + + S thisClass; + ParameterAnnotationsBuilder runtimeVisibleParameterAnnotations; + ParameterAnnotationsBuilder runtimeInvisibleParameterAnnotations; + + public MethodBuilder(S thisClass, CharSequence name, T type, PoolHelper pool, TypeHelper typeHelper) { + super(name, type, pool, typeHelper); + this.thisClass = thisClass; + } + + public > MethodBuilder withCode(Function, ? extends C> func, + Consumer code) { + C codeBuilder = func.apply(this); + int start = attributes.offset; + try { + code.accept(codeBuilder); + } catch (MacroCodeBuilder.WideJumpException ex) { + //wide jumps! Redo the code + ((MacroCodeBuilder) codeBuilder).jumpMode = JumpMode.WIDE; + ((MacroCodeBuilder) 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 withCode(Consumer> code) { + return withCode(CodeBuilder::new, code); + } + + @SuppressWarnings({"varargs", "unchecked"}) + public MethodBuilder 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 withParameterAnnotation(AnnotationsBuilder.Kind kind, int nparam, T annoType) { + getParameterAnnotations(kind).builders[nparam].withAnnotation(annoType, null); + return this; + } + + public MethodBuilder withParameterAnnotation(AnnotationsBuilder.Kind kind, int nparam, T annoType, Consumer.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[] 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 builder : builders) { + parameterAnnos.writeBytes(builder.build()); + } + return parameterAnnos.bytes(); + } + + int nparams() { + Iterator 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()); + } + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/Opcode.java --- /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)]; + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/PoolHelper.java --- /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 the type of the symbol representation + * @param the type of type descriptors representation + * @param the type of pool entries + */ +public interface PoolHelper { + 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> staticArgs); + + int putDynamicConstant(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer> staticArgs); + + int size(); + + E entries(); + + interface StaticArgListBuilder { + StaticArgListBuilder add(int i); + StaticArgListBuilder add(float f); + StaticArgListBuilder add(long l); + StaticArgListBuilder add(double d); + StaticArgListBuilder add(String s); + StaticArgListBuilder add(int refKind, S owner, CharSequence name, T type); + StaticArgListBuilder add(Z z, ToIntBiFunction, Z> poolFunc); + StaticArgListBuilder add(CharSequence constName, T constType, S bsmClass, CharSequence bsmName, T bsmType, Consumer> staticArgList); + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/PoolTag.java --- /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]; + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/Type.java --- /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(); +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/TypeHelper.java --- /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 the type of symbols + * @param the type of type descriptors + */ +public interface TypeHelper { + /** + * 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 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(); +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/TypeTag.java --- /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; + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/lib/testlibrary/bytecode/jdk/experimental/bytecode/TypedCodeBuilder.java --- /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> extends MacroCodeBuilder { + + State lastStackMapState; + int lastStackMapPc = -1; + Map 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 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 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 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 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 op) { + state.pop(tag); + state.push(typeHelper.nullType()); + return op.get(); + } + + private C jumpAndUpdate(Supplier op) { + state.pop(tag); + state.pop(tag); + state.push(typeHelper.nullType()); + state.push(typeHelper.nullType()); + return op.get(); + } + } + + public class State { + public final ArrayList stack; + public final Vector locals; + boolean alive; + + State(ArrayList stack, Vector 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 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> 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 { + + 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> 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> 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 tryBlock, Consumer 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 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> 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 locals = state.locals; + List stack = state.stack; + List 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); + } +} diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/jdk/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java --- 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) { diff -r ef8a98bc71f8 -r c4d9d1b08e2e test/langtools/tools/javac/lambda/ByteCodeTest.java --- 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);
Static argument types