# HG changeset patch # User jrose # Date 1288469303 25200 # Node ID 65d21c4c63377cbc3b130f6f5b4a0bc080eae458 # Parent f05a0ba2802f51f44d43aa5b2e70f0f5d4518ef6 6984311: JSR 292 needs optional bootstrap method parameters Summary: Allow CONSTANT_InvokeDynamic nodes to have any number of extra operands. Reviewed-by: twisti diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeLoadConstant.java --- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeLoadConstant.java Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/interpreter/BytecodeLoadConstant.java Sat Oct 30 13:08:23 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2003, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2010, 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 @@ -188,7 +188,7 @@ } else { throw new RuntimeException("should not reach here"); } - } else if (ctag.isMethodHandle() || ctag.isMethodType()) { + } else if (ctag.isMethodHandle()) { Oop x = getCachedConstant(); int refidx = cpool.getMethodHandleIndexAt(cpIndex); int refkind = cpool.getMethodHandleRefKindAt(cpIndex); diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPool.java --- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPool.java Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPool.java Sat Oct 30 13:08:23 2010 -0700 @@ -53,11 +53,19 @@ private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { Type type = db.lookupType("constantPoolOopDesc"); tags = new OopField(type.getOopField("_tags"), 0); + operands = new OopField(type.getOopField("_operands"), 0); cache = new OopField(type.getOopField("_cache"), 0); poolHolder = new OopField(type.getOopField("_pool_holder"), 0); length = new CIntField(type.getCIntegerField("_length"), 0); headerSize = type.getSize(); elementSize = 0; + // fetch constants: + MULTI_OPERAND_COUNT_OFFSET = db.lookupIntConstant("constantPoolOopDesc::_multi_operand_count_offset").intValue(); + MULTI_OPERAND_BASE_OFFSET = db.lookupIntConstant("constantPoolOopDesc::_multi_operand_base_offset").intValue(); + INDY_BSM_OFFSET = db.lookupIntConstant("constantPoolOopDesc::_indy_bsm_offset").intValue(); + INDY_NT_OFFSET = db.lookupIntConstant("constantPoolOopDesc::_indy_nt_offset").intValue(); + INDY_ARGC_OFFSET = db.lookupIntConstant("constantPoolOopDesc::_indy_argc_offset").intValue(); + INDY_ARGV_OFFSET = db.lookupIntConstant("constantPoolOopDesc::_indy_argv_offset").intValue(); } ConstantPool(OopHandle handle, ObjectHeap heap) { @@ -67,6 +75,7 @@ public boolean isConstantPool() { return true; } private static OopField tags; + private static OopField operands; private static OopField cache; private static OopField poolHolder; private static CIntField length; // number of elements in oop @@ -74,7 +83,15 @@ private static long headerSize; private static long elementSize; + private static int MULTI_OPERAND_COUNT_OFFSET; + private static int MULTI_OPERAND_BASE_OFFSET; + private static int INDY_BSM_OFFSET; + private static int INDY_NT_OFFSET; + private static int INDY_ARGC_OFFSET; + private static int INDY_ARGV_OFFSET; + public TypeArray getTags() { return (TypeArray) tags.getValue(this); } + public TypeArray getOperands() { return (TypeArray) operands.getValue(this); } public ConstantPoolCache getCache() { return (ConstantPoolCache) cache.getValue(this); } public Klass getPoolHolder() { return (Klass) poolHolder.getValue(this); } public int getLength() { return (int)length.getValue(this); } @@ -278,6 +295,25 @@ return res; } + /** Lookup for multi-operand (InvokeDynamic) entries. */ + public int[] getMultiOperandsAt(int i) { + if (Assert.ASSERTS_ENABLED) { + Assert.that(getTagAt(i).isInvokeDynamic(), "Corrupted constant pool"); + } + int pos = this.getIntAt(i); + int countPos = pos + MULTI_OPERAND_COUNT_OFFSET; // == pos-1 + int basePos = pos + MULTI_OPERAND_BASE_OFFSET; // == pos + if (countPos < 0) return null; // safety first + TypeArray operands = getOperands(); + if (operands == null) return null; // safety first + int length = operands.getIntAt(countPos); + int[] values = new int[length]; + for (int j = 0; j < length; j++) { + values[j] = operands.getIntAt(basePos+j); + } + return values; + } + final private static String[] nameForTag = new String[] { }; @@ -522,15 +558,20 @@ case JVM_CONSTANT_InvokeDynamic: { dos.writeByte(cpConstType); - int value = getIntAt(ci); - short bootstrapMethodIndex = (short) extractLowShortFromInt(value); - short nameAndTypeIndex = (short) extractHighShortFromInt(value); - dos.writeShort(bootstrapMethodIndex); - dos.writeShort(nameAndTypeIndex); + int[] values = getMultiOperandsAt(ci); + for (int vn = 0; vn < values.length; vn++) { + dos.writeShort(values[vn]); + } + int bootstrapMethodIndex = values[INDY_BSM_OFFSET]; + int nameAndTypeIndex = values[INDY_NT_OFFSET]; + int argumentCount = values[INDY_ARGC_OFFSET]; + assert(INDY_ARGV_OFFSET + argumentCount == values.length); if (DEBUG) debugMessage("CP[" + ci + "] = indy BSM = " + bootstrapMethodIndex - + ", N&T = " + nameAndTypeIndex); + + ", N&T = " + nameAndTypeIndex + + ", argc = " + argumentCount); break; } + default: throw new InternalError("unknown tag: " + cpConstType); } // switch diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ClassConstants.java --- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ClassConstants.java Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/runtime/ClassConstants.java Sat Oct 30 13:08:23 2010 -0700 @@ -42,7 +42,8 @@ public static final int JVM_CONSTANT_NameAndType = 12; public static final int JVM_CONSTANT_MethodHandle = 15; public static final int JVM_CONSTANT_MethodType = 16; - public static final int JVM_CONSTANT_InvokeDynamic = 17; + public static final int JVM_CONSTANT_InvokeDynamicTrans = 17; // only occurs in old class files + public static final int JVM_CONSTANT_InvokeDynamic = 18; // JVM_CONSTANT_MethodHandle subtypes public static final int JVM_REF_getField = 1; diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/ClassWriter.java --- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/ClassWriter.java Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/tools/jcore/ClassWriter.java Sat Oct 30 13:08:23 2010 -0700 @@ -323,10 +323,11 @@ case JVM_CONSTANT_InvokeDynamic: { dos.writeByte(cpConstType); - int value = cpool.getIntAt(ci); - short refIndex = (short) value; - dos.writeShort(refIndex); - if (DEBUG) debugMessage("CP[" + ci + "] = MT index = " + refIndex); + int[] values = cpool.getMultiOperandsAt(ci); + for (int vn = 0; vn < values.length; vn++) { + dos.writeShort(values[vn]); + } + if (DEBUG) debugMessage("CP[" + ci + "] = INDY indexes = " + Arrays.toString(values)); break; } diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java --- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/ui/classbrowser/HTMLGenerator.java Sat Oct 30 13:08:23 2010 -0700 @@ -460,6 +460,18 @@ return buf.toString(); } + private String genListOfShort(int[] values) { + Formatter buf = new Formatter(genHTML); + buf.append('['); + for (int i = 0; i < values.length; i++) { + if (i > 0) buf.append(' '); + buf.append('#'); + buf.append(Integer.toString(values[i])); + } + buf.append(']'); + return buf.toString(); + } + protected String genHTMLTableForConstantPool(ConstantPool cpool) { Formatter buf = new Formatter(genHTML); buf.beginTable(1); @@ -584,7 +596,7 @@ case JVM_CONSTANT_InvokeDynamic: buf.cell("JVM_CONSTANT_InvokeDynamic"); - buf.cell(genLowHighShort(cpool.getIntAt(index))); + buf.cell(genListOfShort(cpool.getMultiOperandsAt(index))); break; default: diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/ConstantTag.java --- a/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/ConstantTag.java Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/agent/src/share/classes/sun/jvm/hotspot/utilities/ConstantTag.java Sat Oct 30 13:08:23 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2010, 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 @@ -40,7 +40,8 @@ private static int JVM_CONSTANT_NameAndType = 12; private static int JVM_CONSTANT_MethodHandle = 15; // JSR 292 private static int JVM_CONSTANT_MethodType = 16; // JSR 292 - private static int JVM_CONSTANT_InvokeDynamic = 17; // JSR 292 + // static int JVM_CONSTANT_InvokeDynamicTrans = 17; // JSR 292, only occurs in old class files + private static int JVM_CONSTANT_InvokeDynamic = 18; // JSR 292 private static int JVM_CONSTANT_Invalid = 0; // For bad value initialization private static int JVM_CONSTANT_UnresolvedClass = 100; // Temporary tag until actual use private static int JVM_CONSTANT_ClassIndex = 101; // Temporary tag while constructing constant pool diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/src/cpu/sparc/vm/templateTable_sparc.cpp --- a/hotspot/src/cpu/sparc/vm/templateTable_sparc.cpp Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/src/cpu/sparc/vm/templateTable_sparc.cpp Sat Oct 30 13:08:23 2010 -0700 @@ -341,6 +341,26 @@ resolve_cache_and_index(f1_oop, Otos_i, Rcache, Rscratch, wide ? sizeof(u2) : sizeof(u1)); __ verify_oop(Otos_i); + + Label L_done; + const Register Rcon_klass = G3_scratch; // same as Rcache + const Register Rarray_klass = G4_scratch; // same as Rscratch + __ load_klass(Otos_i, Rcon_klass); + AddressLiteral array_klass_addr((address)Universe::systemObjArrayKlassObj_addr()); + __ load_contents(array_klass_addr, Rarray_klass); + __ cmp(Rarray_klass, Rcon_klass); + __ brx(Assembler::notEqual, false, Assembler::pt, L_done); + __ delayed()->nop(); + __ ld(Address(Otos_i, arrayOopDesc::length_offset_in_bytes()), Rcon_klass); + __ tst(Rcon_klass); + __ brx(Assembler::zero, true, Assembler::pt, L_done); + __ delayed()->clr(Otos_i); // executed only if branch is taken + + // Load the exception from the system-array which wraps it: + __ load_heap_oop(Otos_i, arrayOopDesc::base_offset_in_bytes(T_OBJECT), Otos_i); + __ throw_if_not_x(Assembler::never, Interpreter::throw_exception_entry(), G3_scratch); + + __ bind(L_done); } void TemplateTable::ldc2_w() { diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/src/cpu/x86/vm/templateTable_x86_32.cpp --- a/hotspot/src/cpu/x86/vm/templateTable_x86_32.cpp Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/src/cpu/x86/vm/templateTable_x86_32.cpp Sat Oct 30 13:08:23 2010 -0700 @@ -399,6 +399,23 @@ if (VerifyOops) { __ verify_oop(rax); } + + Label L_done, L_throw_exception; + const Register con_klass_temp = rcx; // same as Rcache + __ movptr(con_klass_temp, Address(rax, oopDesc::klass_offset_in_bytes())); + __ cmpptr(con_klass_temp, ExternalAddress((address)Universe::systemObjArrayKlassObj_addr())); + __ jcc(Assembler::notEqual, L_done); + __ cmpl(Address(rax, arrayOopDesc::length_offset_in_bytes()), 0); + __ jcc(Assembler::notEqual, L_throw_exception); + __ xorptr(rax, rax); + __ jmp(L_done); + + // Load the exception from the system-array which wraps it: + __ bind(L_throw_exception); + __ movptr(rax, Address(rax, arrayOopDesc::base_offset_in_bytes(T_OBJECT))); + __ jump(ExternalAddress(Interpreter::throw_exception_entry())); + + __ bind(L_done); } void TemplateTable::ldc2_w() { diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/src/cpu/x86/vm/templateTable_x86_64.cpp --- a/hotspot/src/cpu/x86/vm/templateTable_x86_64.cpp Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/src/cpu/x86/vm/templateTable_x86_64.cpp Sat Oct 30 13:08:23 2010 -0700 @@ -413,6 +413,25 @@ if (VerifyOops) { __ verify_oop(rax); } + + Label L_done, L_throw_exception; + const Register con_klass_temp = rcx; // same as cache + const Register array_klass_temp = rdx; // same as index + __ movptr(con_klass_temp, Address(rax, oopDesc::klass_offset_in_bytes())); + __ lea(array_klass_temp, ExternalAddress((address)Universe::systemObjArrayKlassObj_addr())); + __ cmpptr(con_klass_temp, Address(array_klass_temp, 0)); + __ jcc(Assembler::notEqual, L_done); + __ cmpl(Address(rax, arrayOopDesc::length_offset_in_bytes()), 0); + __ jcc(Assembler::notEqual, L_throw_exception); + __ xorptr(rax, rax); + __ jmp(L_done); + + // Load the exception from the system-array which wraps it: + __ bind(L_throw_exception); + __ movptr(rax, Address(rax, arrayOopDesc::base_offset_in_bytes(T_OBJECT))); + __ jump(ExternalAddress(Interpreter::throw_exception_entry())); + + __ bind(L_done); } void TemplateTable::ldc2_w() { diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/src/share/vm/classfile/classFileParser.cpp --- a/hotspot/src/share/vm/classfile/classFileParser.cpp Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/src/share/vm/classfile/classFileParser.cpp Sat Oct 30 13:08:23 2010 -0700 @@ -73,6 +73,12 @@ unsigned int hashValues[SymbolTable::symbol_alloc_batch_size]; int names_count = 0; + // Side buffer for operands of variable-sized (InvokeDynamic) entries. + GrowableArray* operands = NULL; +#ifdef ASSERT + GrowableArray* indy_instructions = new GrowableArray(THREAD, 10); +#endif + // parsing Index 0 is unused for (int index = 1; index < length; index++) { // Each of the following case guarantees one more byte in the stream @@ -141,6 +147,7 @@ ShouldNotReachHere(); } break; + case JVM_CONSTANT_InvokeDynamicTrans : // this tag appears only in old classfiles case JVM_CONSTANT_InvokeDynamic : { if (!EnableInvokeDynamic || @@ -151,10 +158,36 @@ "Class file version does not support constant tag %u in class file %s"), tag, CHECK); } - cfs->guarantee_more(5, CHECK); // bsm_index, name_and_type_index, tag/access_flags + if (!AllowTransitionalJSR292 && tag == JVM_CONSTANT_InvokeDynamicTrans) { + classfile_parse_error( + "This JVM does not support transitional InvokeDynamic tag %u in class file %s", + tag, CHECK); + } + bool trans_no_argc = AllowTransitionalJSR292 && (tag == JVM_CONSTANT_InvokeDynamicTrans); + cfs->guarantee_more(7, CHECK); // bsm_index, nt, argc, ..., tag/access_flags u2 bootstrap_method_index = cfs->get_u2_fast(); u2 name_and_type_index = cfs->get_u2_fast(); - cp->invoke_dynamic_at_put(index, bootstrap_method_index, name_and_type_index); + int argument_count = trans_no_argc ? 0 : cfs->get_u2_fast(); + cfs->guarantee_more(2*argument_count + 1, CHECK); // argv[argc]..., tag/access_flags + int argv_offset = constantPoolOopDesc::_indy_argv_offset; + int op_count = argv_offset + argument_count; // bsm, nt, argc, argv[]... + int op_base = start_operand_group(operands, op_count, CHECK); + assert(argv_offset == 3, "else adjust next 3 assignments"); + operands->at_put(op_base + constantPoolOopDesc::_indy_bsm_offset, bootstrap_method_index); + operands->at_put(op_base + constantPoolOopDesc::_indy_nt_offset, name_and_type_index); + operands->at_put(op_base + constantPoolOopDesc::_indy_argc_offset, argument_count); + for (int arg_i = 0; arg_i < argument_count; arg_i++) { + int arg = cfs->get_u2_fast(); + operands->at_put(op_base + constantPoolOopDesc::_indy_argv_offset + arg_i, arg); + } + cp->invoke_dynamic_at_put(index, op_base, op_count); +#ifdef ASSERT + // Record the steps just taken for later checking. + indy_instructions->append(index); + indy_instructions->append(bootstrap_method_index); + indy_instructions->append(name_and_type_index); + indy_instructions->append(argument_count); +#endif //ASSERT } break; case JVM_CONSTANT_Integer : @@ -257,6 +290,23 @@ oopFactory::new_symbols(cp, names_count, names, lengths, indices, hashValues, CHECK); } + if (operands != NULL && operands->length() > 0) { + store_operand_array(operands, cp, CHECK); + } +#ifdef ASSERT + // Re-assert the indy structures, now that assertion checking can work. + for (int indy_i = 0; indy_i < indy_instructions->length(); ) { + int index = indy_instructions->at(indy_i++); + int bootstrap_method_index = indy_instructions->at(indy_i++); + int name_and_type_index = indy_instructions->at(indy_i++); + int argument_count = indy_instructions->at(indy_i++); + assert(cp->check_invoke_dynamic_at(index, + bootstrap_method_index, name_and_type_index, + argument_count), + "indy structure is OK"); + } +#endif //ASSERT + // Copy _current pointer of local copy back to stream(). #ifdef ASSERT assert(cfs0->current() == old_current, "non-exclusive use of stream()"); @@ -264,6 +314,41 @@ cfs0->set_current(cfs1.current()); } +int ClassFileParser::start_operand_group(GrowableArray* &operands, int op_count, TRAPS) { + if (operands == NULL) { + operands = new GrowableArray(THREAD, 100); + int fillp_offset = constantPoolOopDesc::_multi_operand_buffer_fill_pointer_offset; + while (operands->length() <= fillp_offset) + operands->append(0); // force op_base > 0, for an error check + DEBUG_ONLY(operands->at_put(fillp_offset, (int)badHeapWordVal)); + } + int cnt_pos = operands->append(op_count); + int arg_pos = operands->length(); + operands->at_grow(arg_pos + op_count - 1); // grow to include the operands + assert(operands->length() == arg_pos + op_count, ""); + int op_base = cnt_pos - constantPoolOopDesc::_multi_operand_count_offset; + return op_base; +} + +void ClassFileParser::store_operand_array(GrowableArray* operands, constantPoolHandle cp, TRAPS) { + // Collect the buffer of operands from variable-sized entries into a permanent array. + int arraylen = operands->length(); + int fillp_offset = constantPoolOopDesc::_multi_operand_buffer_fill_pointer_offset; + assert(operands->at(fillp_offset) == (int)badHeapWordVal, "value unused so far"); + operands->at_put(fillp_offset, arraylen); + cp->multi_operand_buffer_grow(arraylen, CHECK); + typeArrayOop operands_oop = cp->operands(); + assert(operands_oop->length() == arraylen, ""); + for (int i = 0; i < arraylen; i++) { + operands_oop->int_at_put(i, operands->at(i)); + } + cp->set_operands(operands_oop); + // The fill_pointer is used only by constantPoolOop::copy_entry_to and friends, + // when constant pools need to be merged. Make sure it is sane now. + assert(cp->multi_operand_buffer_fill_pointer() == arraylen, ""); +} + + bool inline valid_cp_range(int index, int length) { return (index > 0 && index < length); } constantPoolHandle ClassFileParser::parse_constant_pool(TRAPS) { @@ -431,6 +516,8 @@ ref_index, CHECK_(nullHandle)); } break; + case JVM_CONSTANT_InvokeDynamicTrans : + ShouldNotReachHere(); // this tag does not appear in the heap case JVM_CONSTANT_InvokeDynamic : { int bootstrap_method_ref_index = cp->invoke_dynamic_bootstrap_method_ref_index_at(index); @@ -438,7 +525,7 @@ check_property((bootstrap_method_ref_index == 0 && AllowTransitionalJSR292) || (valid_cp_range(bootstrap_method_ref_index, length) && - cp->tag_at(bootstrap_method_ref_index).is_method_handle()), + (cp->tag_at(bootstrap_method_ref_index).is_method_handle())), "Invalid constant pool index %u in class file %s", bootstrap_method_ref_index, CHECK_(nullHandle)); @@ -447,6 +534,18 @@ "Invalid constant pool index %u in class file %s", name_and_type_ref_index, CHECK_(nullHandle)); + int argc = cp->invoke_dynamic_argument_count_at(index); + for (int arg_i = 0; arg_i < argc; arg_i++) { + int arg = cp->invoke_dynamic_argument_index_at(index, arg_i); + check_property(valid_cp_range(arg, length) && + cp->tag_at(arg).is_loadable_constant() || + // temporary early forms of string and class: + cp->tag_at(arg).is_klass_index() || + cp->tag_at(arg).is_string_index(), + "Invalid constant pool index %u in class file %s", + arg, + CHECK_(nullHandle)); + } break; } default: diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/src/share/vm/classfile/classFileParser.hpp --- a/hotspot/src/share/vm/classfile/classFileParser.hpp Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/src/share/vm/classfile/classFileParser.hpp Sat Oct 30 13:08:23 2010 -0700 @@ -56,6 +56,9 @@ constantPoolHandle parse_constant_pool(TRAPS); + static int start_operand_group(GrowableArray* &operands, int op_count, TRAPS); + static void store_operand_array(GrowableArray* operands, constantPoolHandle cp, TRAPS); + // Interface parsing objArrayHandle parse_interfaces(constantPoolHandle cp, int length, diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/src/share/vm/classfile/systemDictionary.cpp --- a/hotspot/src/share/vm/classfile/systemDictionary.cpp Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp Sat Oct 30 13:08:23 2010 -0700 @@ -2555,7 +2555,9 @@ } Handle SystemDictionary::find_bootstrap_method(methodHandle caller_method, int caller_bci, - int cache_index, TRAPS) { + int cache_index, + Handle& argument_info_result, + TRAPS) { Handle empty; constantPoolHandle pool; @@ -2569,7 +2571,7 @@ constantTag tag = pool->tag_at(constant_pool_index); if (tag.is_invoke_dynamic()) { - // JVM_CONSTANT_InvokeDynamic is an ordered pair of [bootm, name&type] + // 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. int bsm_index = pool->invoke_dynamic_bootstrap_method_ref_index_at(constant_pool_index); if (bsm_index != 0) { @@ -2585,9 +2587,38 @@ tty->print_cr("bootstrap method for "PTR_FORMAT" at %d retrieved as "PTR_FORMAT":", (intptr_t) caller_method(), caller_bci, (intptr_t) bsm_oop); } - assert(bsm_oop->is_oop() - && java_dyn_MethodHandle::is_instance(bsm_oop), "must be sane"); - return Handle(THREAD, bsm_oop); + assert(bsm_oop->is_oop(), "must be sane"); + // caller must verify that it is of type MethodHandle + Handle bsm(THREAD, bsm_oop); + bsm_oop = NULL; // safety + + // Extract the optional static arguments. + Handle argument_info; // either null, or one arg, or Object[]{arg...} + int argc = pool->invoke_dynamic_argument_count_at(constant_pool_index); + if (TraceInvokeDynamic) { + tty->print_cr("find_bootstrap_method: [%d/%d] CONSTANT_InvokeDynamic: %d[%d]", + constant_pool_index, cache_index, bsm_index, argc); + } + if (argc > 0) { + objArrayHandle arg_array; + if (argc > 1) { + objArrayOop arg_array_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(), argc, CHECK_(empty)); + arg_array = objArrayHandle(THREAD, arg_array_oop); + argument_info = arg_array; + } + for (int arg_i = 0; arg_i < argc; arg_i++) { + int arg_index = pool->invoke_dynamic_argument_index_at(constant_pool_index, arg_i); + oop arg_oop = pool->resolve_possibly_cached_constant_at(arg_index, CHECK_(empty)); + if (arg_array.is_null()) { + argument_info = Handle(THREAD, arg_oop); + } else { + arg_array->obj_at_put(arg_i, arg_oop); + } + } + } + + argument_info_result = argument_info; // return argument_info to caller + return bsm; } // else null BSM; fall through } else if (tag.is_name_and_type()) { @@ -2600,14 +2631,14 @@ // Fall through to pick up the per-class bootstrap method. // This mechanism may go away in the PFD. assert(AllowTransitionalJSR292, "else the verifier should have stopped us already"); + argument_info_result = empty; // return no argument_info to caller oop bsm_oop = instanceKlass::cast(caller_method->method_holder())->bootstrap_method(); if (bsm_oop != NULL) { if (TraceMethodHandles) { tty->print_cr("bootstrap method for "PTR_FORMAT" registered as "PTR_FORMAT":", (intptr_t) caller_method(), (intptr_t) bsm_oop); } - assert(bsm_oop->is_oop() - && java_dyn_MethodHandle::is_instance(bsm_oop), "must be sane"); + assert(bsm_oop->is_oop(), "must be sane"); return Handle(THREAD, bsm_oop); } diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/src/share/vm/classfile/systemDictionary.hpp --- a/hotspot/src/share/vm/classfile/systemDictionary.hpp Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/src/share/vm/classfile/systemDictionary.hpp Sat Oct 30 13:08:23 2010 -0700 @@ -496,6 +496,7 @@ static Handle find_bootstrap_method(methodHandle caller_method, int caller_bci, // N.B. must be an invokedynamic int cache_index, // must be corresponding main_entry + Handle &argument_info_result, // static BSM arguments, if any TRAPS); // Utility for printing loader "name" as part of tracing constraints diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/src/share/vm/classfile/verifier.cpp --- a/hotspot/src/share/vm/classfile/verifier.cpp Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/src/share/vm/classfile/verifier.cpp Sat Oct 30 13:08:23 2010 -0700 @@ -1909,7 +1909,7 @@ unsigned int types = (opcode == Bytecodes::_invokeinterface ? 1 << JVM_CONSTANT_InterfaceMethodref : opcode == Bytecodes::_invokedynamic - ? (1 << JVM_CONSTANT_NameAndType + ? ((AllowTransitionalJSR292 ? 1 << JVM_CONSTANT_NameAndType : 0) |1 << JVM_CONSTANT_InvokeDynamic) : 1 << JVM_CONSTANT_Methodref); verify_cp_type(index, cp, types, CHECK_VERIFY(this)); diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/src/share/vm/includeDB_core --- a/hotspot/src/share/vm/includeDB_core Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/src/share/vm/includeDB_core Sat Oct 30 13:08:23 2010 -0700 @@ -1265,6 +1265,7 @@ constantPoolOop.cpp linkResolver.hpp constantPoolOop.cpp objArrayKlass.hpp constantPoolOop.cpp oop.inline.hpp +constantPoolOop.cpp oopFactory.hpp constantPoolOop.cpp signature.hpp constantPoolOop.cpp symbolTable.hpp constantPoolOop.cpp systemDictionary.hpp diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/src/share/vm/interpreter/interpreterRuntime.cpp --- a/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp Sat Oct 30 13:08:23 2010 -0700 @@ -716,6 +716,7 @@ assert(constantPoolCacheOopDesc::is_secondary_index(site_index), "proper format"); // there is a second CPC entries that is of interest; it caches signature info: int main_index = pool->cache()->secondary_entry_at(site_index)->main_entry_index(); + int pool_index = pool->cache()->entry_at(main_index)->constant_pool_index(); // first resolve the signature to a MH.invoke methodOop if (!pool->cache()->entry_at(main_index)->is_resolved(bytecode)) { @@ -740,9 +741,10 @@ assert(signature_invoker.not_null() && signature_invoker->is_method() && signature_invoker->is_method_handle_invoke(), "correct result from LinkResolver::resolve_invokedynamic"); + Handle info; // optional argument(s) in JVM_CONSTANT_InvokeDynamic Handle bootm = SystemDictionary::find_bootstrap_method(caller_method, caller_bci, - main_index, CHECK); - if (bootm.is_null()) { + main_index, info, CHECK); + if (!java_dyn_MethodHandle::is_instance(bootm())) { THROW_MSG(vmSymbols::java_lang_IllegalStateException(), "no bootstrap method found for invokedynamic"); } @@ -753,8 +755,6 @@ symbolHandle call_site_name(THREAD, pool->name_ref_at(site_index)); - Handle info; // NYI: Other metadata from a new kind of CP entry. (Annotations?) - Handle call_site = SystemDictionary::make_dynamic_call_site(bootm, // Callee information: diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/src/share/vm/memory/universe.hpp --- a/hotspot/src/share/vm/memory/universe.hpp Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/src/share/vm/memory/universe.hpp Sat Oct 30 13:08:23 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, 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 @@ -340,6 +340,7 @@ static klassOop* longArrayKlassObj_addr() { return &_longArrayKlassObj; } static klassOop* singleArrayKlassObj_addr() { return &_singleArrayKlassObj; } static klassOop* doubleArrayKlassObj_addr() { return &_doubleArrayKlassObj; } + static klassOop* systemObjArrayKlassObj_addr() { return &_systemObjArrayKlassObj; } // The particular choice of collected heap. static CollectedHeap* heap() { return _collectedHeap; } diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/src/share/vm/oops/constantPoolKlass.cpp --- a/hotspot/src/share/vm/oops/constantPoolKlass.cpp Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/src/share/vm/oops/constantPoolKlass.cpp Sat Oct 30 13:08:23 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, 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 @@ -34,6 +34,7 @@ c->set_length(length); c->set_tags(NULL); c->set_cache(NULL); + c->set_operands(NULL); c->set_pool_holder(NULL); c->set_flags(0); // only set to non-zero if constant pool is merged by RedefineClasses @@ -92,6 +93,7 @@ // gc of constant pool instance variables MarkSweep::mark_and_push(cp->tags_addr()); MarkSweep::mark_and_push(cp->cache_addr()); + MarkSweep::mark_and_push(cp->operands_addr()); MarkSweep::mark_and_push(cp->pool_holder_addr()); } } @@ -118,6 +120,7 @@ // gc of constant pool instance variables PSParallelCompact::mark_and_push(cm, cp->tags_addr()); PSParallelCompact::mark_and_push(cm, cp->cache_addr()); + PSParallelCompact::mark_and_push(cm, cp->operands_addr()); PSParallelCompact::mark_and_push(cm, cp->pool_holder_addr()); } } @@ -146,6 +149,7 @@ } MarkSweep::adjust_pointer(cp->tags_addr()); MarkSweep::adjust_pointer(cp->cache_addr()); + MarkSweep::adjust_pointer(cp->operands_addr()); MarkSweep::adjust_pointer(cp->pool_holder_addr()); return size; } @@ -173,6 +177,7 @@ } blk->do_oop(cp->tags_addr()); blk->do_oop(cp->cache_addr()); + blk->do_oop(cp->operands_addr()); blk->do_oop(cp->pool_holder_addr()); return size; } @@ -205,6 +210,8 @@ blk->do_oop(addr); addr = cp->cache_addr(); blk->do_oop(addr); + addr = cp->operands_addr(); + blk->do_oop(addr); addr = cp->pool_holder_addr(); blk->do_oop(addr); return size; @@ -232,6 +239,7 @@ } PSParallelCompact::adjust_pointer(cp->tags_addr()); PSParallelCompact::adjust_pointer(cp->cache_addr()); + PSParallelCompact::adjust_pointer(cp->operands_addr()); PSParallelCompact::adjust_pointer(cp->pool_holder_addr()); return cp->object_size(); } @@ -262,6 +270,8 @@ PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); p = cp->cache_addr(); PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); + p = cp->operands_addr(); + PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); p = cp->pool_holder_addr(); PSParallelCompact::adjust_pointer(p, beg_addr, end_addr); @@ -363,8 +373,18 @@ st->print("signature_index=%d", cp->method_type_index_at(index)); break; case JVM_CONSTANT_InvokeDynamic : - st->print("bootstrap_method_index=%d", cp->invoke_dynamic_bootstrap_method_ref_index_at(index)); - st->print(" name_and_type_index=%d", cp->invoke_dynamic_name_and_type_ref_index_at(index)); + { + st->print("bootstrap_method_index=%d", cp->invoke_dynamic_bootstrap_method_ref_index_at(index)); + st->print(" name_and_type_index=%d", cp->invoke_dynamic_name_and_type_ref_index_at(index)); + int argc = cp->invoke_dynamic_argument_count_at(index); + if (argc > 0) { + for (int arg_i = 0; arg_i < argc; arg_i++) { + int arg = cp->invoke_dynamic_argument_index_at(index, arg_i); + st->print((arg_i == 0 ? " arguments={%d" : ", %d"), arg); + } + st->print("}"); + } + } break; default: ShouldNotReachHere(); @@ -381,6 +401,7 @@ st->print("constant pool [%d]", cp->length()); if (cp->has_pseudo_string()) st->print("/pseudo_string"); if (cp->has_invokedynamic()) st->print("/invokedynamic"); + if (cp->operands() != NULL) st->print("/operands[%d]", cp->operands()->length()); cp->print_address_on(st); st->print(" for "); cp->pool_holder()->print_value_on(st); @@ -440,6 +461,10 @@ guarantee(cp->cache()->is_perm(), "should be in permspace"); guarantee(cp->cache()->is_constantPoolCache(), "should be constant pool cache"); } + if (cp->operands() != NULL) { + guarantee(cp->operands()->is_perm(), "should be in permspace"); + guarantee(cp->operands()->is_typeArray(), "should be type array"); + } if (cp->pool_holder() != NULL) { // Note: pool_holder() can be NULL in temporary constant pools // used during constant pool merging diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/src/share/vm/oops/constantPoolOop.cpp --- a/hotspot/src/share/vm/oops/constantPoolOop.cpp Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/src/share/vm/oops/constantPoolOop.cpp Sat Oct 30 13:08:23 2010 -0700 @@ -267,7 +267,7 @@ if (constantPoolCacheOopDesc::is_secondary_index(which)) { // Invokedynamic index. int pool_index = cache()->main_entry_at(which)->constant_pool_index(); - if (tag_at(pool_index).is_invoke_dynamic()) + if (!AllowTransitionalJSR292 || tag_at(pool_index).is_invoke_dynamic()) pool_index = invoke_dynamic_name_and_type_ref_index_at(pool_index); assert(tag_at(pool_index).is_name_and_type(), ""); return pool_index; @@ -275,11 +275,17 @@ // change byte-ordering and go via cache i = remap_instruction_operand_from_cache(which); } else { - if (tag_at(which).is_name_and_type()) + if (AllowTransitionalJSR292 && tag_at(which).is_name_and_type()) // invokedynamic index is a simple name-and-type return which; + if (tag_at(which).is_invoke_dynamic()) { + 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"); jint ref_index = *int_at_addr(i); return extract_high_short_from_int(ref_index); } @@ -393,18 +399,61 @@ } } +// A resolved constant value in the CP cache is represented as a non-null +// value. As a special case, this value can be a 'systemObjArray' +// which masks an exception object to throw. +// This allows a MethodHandle constant reference to throw a consistent +// exception every time, if it fails to resolve. +static oop decode_exception_from_f1(oop result_oop, TRAPS) { + if (result_oop->klass() != Universe::systemObjArrayKlassObj()) + return result_oop; + + // Special cases here: Masked null, saved exception. + objArrayOop sys_array = (objArrayOop) result_oop; + assert(sys_array->length() == 1, "bad system array"); + if (sys_array->length() == 1) { + THROW_OOP_(sys_array->obj_at(0), NULL); + } + return NULL; +} + oop constantPoolOopDesc::resolve_constant_at_impl(constantPoolHandle this_oop, int index, int cache_index, 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 CP cache. + // We'll do a linear search. This should be OK because this usage is rare. + assert(index > 0, "valid index"); + constantPoolCacheOop cache = this_oop()->cache(); + for (int i = 0, len = cache->length(); i < len; i++) { + ConstantPoolCacheEntry* cpc_entry = cache->entry_at(i); + if (!cpc_entry->is_secondary_entry() && cpc_entry->constant_pool_index() == index) { + // Switch the query to use this CPC entry. + cache_index = i; + index = _no_index_sentinel; + break; + } + } + if (cache_index == _possible_index_sentinel) + cache_index = _no_index_sentinel; // not found + } + assert(cache_index == _no_index_sentinel || cache_index >= 0, ""); + assert(index == _no_index_sentinel || index >= 0, ""); + if (cache_index >= 0) { - assert(index < 0, "only one kind of index at a time"); + assert(index == _no_index_sentinel, "only one kind of index at a time"); ConstantPoolCacheEntry* cpc_entry = this_oop->cache()->entry_at(cache_index); result_oop = cpc_entry->f1(); if (result_oop != NULL) { - return result_oop; // that was easy... + return decode_exception_from_f1(result_oop, THREAD); + // That was easy... } index = cpc_entry->constant_pool_index(); } + jvalue prim_value; // temp used only in a few cases below + int tag_value = this_oop->tag_at(index).value(); switch (tag_value) { @@ -448,9 +497,14 @@ KlassHandle klass(THREAD, this_oop->pool_holder()); Handle value = SystemDictionary::link_method_handle_constant(klass, ref_kind, callee, name, signature, - CHECK_NULL); + THREAD); + if (HAS_PENDING_EXCEPTION) { + throw_exception = Handle(THREAD, PENDING_EXCEPTION); + CLEAR_PENDING_EXCEPTION; + break; + } result_oop = value(); - // FIXME: Uniquify errors, using SystemDictionary::find_resolution_error. + assert(result_oop != NULL, ""); break; } @@ -467,20 +521,36 @@ klass, false, ignore_is_on_bcp, - CHECK_NULL); + THREAD); + if (HAS_PENDING_EXCEPTION) { + throw_exception = Handle(THREAD, PENDING_EXCEPTION); + CLEAR_PENDING_EXCEPTION; + break; + } result_oop = value(); - // FIXME: Uniquify errors, using SystemDictionary::find_resolution_error. + assert(result_oop != NULL, ""); break; } - /* maybe some day case JVM_CONSTANT_Integer: + prim_value.i = this_oop->int_at(index); + result_oop = java_lang_boxing_object::create(T_INT, &prim_value, CHECK_NULL); + break; + case JVM_CONSTANT_Float: + prim_value.f = this_oop->float_at(index); + result_oop = java_lang_boxing_object::create(T_FLOAT, &prim_value, CHECK_NULL); + break; + case JVM_CONSTANT_Long: + prim_value.j = this_oop->long_at(index); + result_oop = java_lang_boxing_object::create(T_LONG, &prim_value, CHECK_NULL); + break; + case JVM_CONSTANT_Double: - result_oop = java_lang_boxing_object::create(...); + prim_value.d = this_oop->double_at(index); + result_oop = java_lang_boxing_object::create(T_DOUBLE, &prim_value, CHECK_NULL); break; - */ default: DEBUG_ONLY( tty->print_cr("*** %p: tag at CP[%d/%d] = %d", @@ -491,18 +561,31 @@ if (cache_index >= 0) { // Cache the oop here also. - Handle result(THREAD, result_oop); + if (throw_exception.not_null()) { + objArrayOop sys_array = oopFactory::new_system_objArray(1, CHECK_NULL); + sys_array->obj_at_put(0, throw_exception()); + result_oop = sys_array; + throw_exception = Handle(); // be tidy + } + Handle result_handle(THREAD, result_oop); result_oop = NULL; // safety ObjectLocker ol(this_oop, THREAD); ConstantPoolCacheEntry* cpc_entry = this_oop->cache()->entry_at(cache_index); - oop result_oop2 = cpc_entry->f1(); - if (result_oop2 != NULL) { - // Race condition: May already be filled in while we were trying to lock. - return result_oop2; + result_oop = cpc_entry->f1(); + // Benign race condition: f1 may already be filled in while we were trying to lock. + // 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. + // That result may be either a resolved constant or a failure exception. + if (result_oop == NULL) { + result_oop = result_handle(); + cpc_entry->set_f1(result_oop); } - cpc_entry->set_f1(result()); - return result(); + return decode_exception_from_f1(result_oop, THREAD); } else { + if (throw_exception.not_null()) { + THROW_HANDLE_(throw_exception, NULL); + } return result_oop; } } @@ -620,6 +703,7 @@ void constantPoolOopDesc::shared_tags_iterate(OopClosure* closure) { closure->do_oop(tags_addr()); + closure->do_oop(operands_addr()); } @@ -837,13 +921,19 @@ case JVM_CONSTANT_InvokeDynamic: { - int k1 = invoke_dynamic_bootstrap_method_ref_index_at(index1); - int k2 = cp2->invoke_dynamic_bootstrap_method_ref_index_at(index2); - if (k1 == k2) { - int i1 = invoke_dynamic_name_and_type_ref_index_at(index1); - int i2 = cp2->invoke_dynamic_name_and_type_ref_index_at(index2); - if (i1 == i2) { - return true; + int op_count = multi_operand_count_at(index1); + if (op_count == cp2->multi_operand_count_at(index2)) { + bool all_equal = true; + for (int op_i = 0; op_i < op_count; op_i++) { + int k1 = multi_operand_ref_at(index1, op_i); + int k2 = cp2->multi_operand_ref_at(index2, op_i); + if (k1 != k2) { + all_equal = false; + break; + } + } + if (all_equal) { + return true; // got through loop; all elements equal } } } break; @@ -880,6 +970,25 @@ } // end compare_entry_to() +// Grow this->operands() to the indicated length, unless it is already at least that long. +void constantPoolOopDesc::multi_operand_buffer_grow(int min_length, TRAPS) { + int old_length = multi_operand_buffer_fill_pointer(); + if (old_length >= min_length) return; + int new_length = min_length; + assert(new_length > _multi_operand_buffer_fill_pointer_offset, ""); + typeArrayHandle new_operands = oopFactory::new_permanent_intArray(new_length, CHECK); + if (operands() == NULL) { + new_operands->int_at_put(_multi_operand_buffer_fill_pointer_offset, old_length); + } else { + // copy fill pointer and everything else + for (int i = 0; i < old_length; i++) { + new_operands->int_at_put(i, operands()->int_at(i)); + } + } + set_operands(new_operands()); +} + + // Copy this constant pool's entries at start_i to end_i (inclusive) // to the constant pool to_cp's entries starting at to_i. A total of // (end_i - start_i) + 1 entries are copied. @@ -888,6 +997,13 @@ int dest_i = to_i; // leave original alone for debug purposes + if (operands() != NULL) { + // pre-grow the target CP's operand buffer + int nops = this->multi_operand_buffer_fill_pointer(); + nops += to_cp->multi_operand_buffer_fill_pointer(); + to_cp->multi_operand_buffer_grow(nops, CHECK); + } + for (int src_i = start_i; src_i <= end_i; /* see loop bottom */ ) { copy_entry_to(src_i, to_cp, dest_i, CHECK); @@ -1036,9 +1152,26 @@ case JVM_CONSTANT_InvokeDynamic: { + int op_count = multi_operand_count_at(from_i); + int fillp = to_cp->multi_operand_buffer_fill_pointer(); + int to_op_base = fillp - _multi_operand_count_offset; // fillp is count offset; get to base + to_cp->multi_operand_buffer_grow(to_op_base + op_count, CHECK); + to_cp->operands()->int_at_put(fillp++, op_count); + assert(fillp == to_op_base + _multi_operand_base_offset, "just wrote count, will now write args"); + for (int op_i = 0; op_i < op_count; op_i++) { + int op = multi_operand_ref_at(from_i, op_i); + to_cp->operands()->int_at_put(fillp++, op); + } + assert(fillp <= to_cp->operands()->length(), "oob"); + to_cp->set_multi_operand_buffer_fill_pointer(fillp); + to_cp->invoke_dynamic_at_put(to_i, to_op_base, op_count); +#ifdef ASSERT int k1 = invoke_dynamic_bootstrap_method_ref_index_at(from_i); int k2 = invoke_dynamic_name_and_type_ref_index_at(from_i); - to_cp->invoke_dynamic_at_put(to_i, k1, k2); + int k3 = invoke_dynamic_argument_count_at(from_i); + assert(to_cp->check_invoke_dynamic_at(to_i, k1, k2, k3), + "indy structure is OK"); +#endif //ASSERT } break; // Invalid is used as the tag for the second constant pool entry @@ -1256,8 +1389,11 @@ case JVM_CONSTANT_Methodref: case JVM_CONSTANT_InterfaceMethodref: case JVM_CONSTANT_NameAndType: + return 5; + case JVM_CONSTANT_InvokeDynamic: - return 5; + // u1 tag, u2 bsm, u2 nt, u2 argc, u2 argv[argc] + return 7 + 2 * invoke_dynamic_argument_count_at(idx); case JVM_CONSTANT_Long: case JVM_CONSTANT_Double: @@ -1474,9 +1610,15 @@ *bytes = JVM_CONSTANT_InvokeDynamic; idx1 = invoke_dynamic_bootstrap_method_ref_index_at(idx); idx2 = invoke_dynamic_name_and_type_ref_index_at(idx); + int argc = invoke_dynamic_argument_count_at(idx); Bytes::put_Java_u2((address) (bytes+1), idx1); Bytes::put_Java_u2((address) (bytes+3), idx2); - DBG(printf("JVM_CONSTANT_InvokeDynamic: %hd %hd", idx1, idx2)); + Bytes::put_Java_u2((address) (bytes+5), argc); + for (int arg_i = 0; arg_i < argc; arg_i++) { + int arg = invoke_dynamic_argument_index_at(idx, arg_i); + Bytes::put_Java_u2((address) (bytes+7+2*arg_i), arg); + } + DBG(printf("JVM_CONSTANT_InvokeDynamic: %hd %hd [%d]", idx1, idx2, argc)); break; } } diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/src/share/vm/oops/constantPoolOop.hpp --- a/hotspot/src/share/vm/oops/constantPoolOop.hpp Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/src/share/vm/oops/constantPoolOop.hpp Sat Oct 30 13:08:23 2010 -0700 @@ -41,6 +41,7 @@ typeArrayOop _tags; // the tag array describing the constant pool's contents constantPoolCacheOop _cache; // the cache holding interpreter runtime information klassOop _pool_holder; // the corresponding class + typeArrayOop _operands; // for variable-sized (InvokeDynamic) nodes, usually empty int _flags; // a few header bits to describe contents for GC int _length; // number of elements in the array volatile bool _is_conc_safe; // if true, safe for concurrent @@ -52,6 +53,8 @@ void tag_at_put(int which, jbyte t) { tags()->byte_at_put(which, t); } void release_tag_at_put(int which, jbyte t) { tags()->release_byte_at_put(which, t); } + void set_operands(typeArrayOop operands) { oop_store_without_check((oop*)&_operands, operands); } + enum FlagBit { FB_has_invokedynamic = 1, FB_has_pseudo_string = 2 @@ -67,6 +70,7 @@ intptr_t* base() const { return (intptr_t*) (((char*) this) + sizeof(constantPoolOopDesc)); } oop* tags_addr() { return (oop*)&_tags; } oop* cache_addr() { return (oop*)&_cache; } + oop* operands_addr() { return (oop*)&_operands; } oop* obj_at_addr(int which) const { assert(is_within_bounds(which), "index out of bounds"); @@ -95,6 +99,7 @@ public: typeArrayOop tags() const { return _tags; } + typeArrayOop operands() const { return _operands; } bool has_pseudo_string() const { return flag_at(FB_has_pseudo_string); } bool has_invokedynamic() const { return flag_at(FB_has_invokedynamic); } @@ -113,6 +118,7 @@ // Assembly code support static int tags_offset_in_bytes() { return offset_of(constantPoolOopDesc, _tags); } static int cache_offset_in_bytes() { return offset_of(constantPoolOopDesc, _cache); } + static int operands_offset_in_bytes() { return offset_of(constantPoolOopDesc, _operands); } static int pool_holder_offset_in_bytes() { return offset_of(constantPoolOopDesc, _pool_holder); } // Storing constants @@ -156,10 +162,28 @@ *int_at_addr(which) = ref_index; } - void invoke_dynamic_at_put(int which, int bootstrap_method_index, int name_and_type_index) { + void invoke_dynamic_at_put(int which, int operand_base, int operand_count) { tag_at_put(which, JVM_CONSTANT_InvokeDynamic); - *int_at_addr(which) = ((jint) name_and_type_index<<16) | bootstrap_method_index; + *int_at_addr(which) = operand_base; // this is the real information } +#ifdef ASSERT + bool check_invoke_dynamic_at(int which, + int bootstrap_method_index, + int name_and_type_index, + int argument_count) { + assert(invoke_dynamic_bootstrap_method_ref_index_at(which) == bootstrap_method_index, + "already stored by caller"); + assert(invoke_dynamic_name_and_type_ref_index_at(which) == name_and_type_index, + "already stored by caller"); + assert(invoke_dynamic_argument_count_at(which) == argument_count, + "consistent argument count"); + if (argument_count != 0) { + invoke_dynamic_argument_index_at(which, 0); + invoke_dynamic_argument_index_at(which, argument_count - 1); + } + return true; + } +#endif //ASSERT // Temporary until actual use void unresolved_string_at_put(int which, symbolOop s) { @@ -401,15 +425,76 @@ int sym = method_type_index_at(which); return symbol_at(sym); } + + private: + // some nodes (InvokeDynamic) have a variable number of operands, each a u2 value + enum { _multi_operand_count_offset = -1, + _multi_operand_base_offset = 0, + _multi_operand_buffer_fill_pointer_offset = 0 // shared at front of operands array + }; + int multi_operand_buffer_length() { + return operands() == NULL ? 0 : operands()->length(); + } + int multi_operand_buffer_fill_pointer() { + return operands() == NULL + ? _multi_operand_buffer_fill_pointer_offset + 1 + : operands()->int_at(_multi_operand_buffer_fill_pointer_offset); + } + void multi_operand_buffer_grow(int min_length, TRAPS); + void set_multi_operand_buffer_fill_pointer(int fillp) { + assert(operands() != NULL, ""); + operands()->int_at_put(_multi_operand_buffer_fill_pointer_offset, fillp); + } + int multi_operand_base_at(int which) { + assert(tag_at(which).is_invoke_dynamic(), "Corrupted constant pool"); + int op_base = *int_at_addr(which); + assert(op_base > _multi_operand_buffer_fill_pointer_offset, "Corrupted operand base"); + return op_base; + } + int multi_operand_count_at(int which) { + int op_base = multi_operand_base_at(which); + assert((uint)(op_base + _multi_operand_count_offset) < (uint)operands()->length(), "oob"); + int count = operands()->int_at(op_base + _multi_operand_count_offset); + return count; + } + int multi_operand_ref_at(int which, int i) { + int op_base = multi_operand_base_at(which); + assert((uint)i < (uint)multi_operand_count_at(which), "oob"); + assert((uint)(op_base + _multi_operand_base_offset + i) < (uint)operands()->length(), "oob"); + return operands()->int_at(op_base + _multi_operand_base_offset + i); + } + void set_multi_operand_ref_at(int which, int i, int ref) { + DEBUG_ONLY(multi_operand_ref_at(which, i)); // trigger asserts + int op_base = multi_operand_base_at(which); + operands()->int_at_put(op_base + _multi_operand_base_offset + i, ref); + } + + public: + // layout of InvokeDynamic: + enum { + _indy_bsm_offset = 0, // CONSTANT_MethodHandle bsm + _indy_nt_offset = 1, // CONSTANT_NameAndType descr + _indy_argc_offset = 2, // u2 argc + _indy_argv_offset = 3 // u2 argv[argc] + }; int invoke_dynamic_bootstrap_method_ref_index_at(int which) { assert(tag_at(which).is_invoke_dynamic(), "Corrupted constant pool"); - jint ref_index = *int_at_addr(which); - return extract_low_short_from_int(ref_index); + return multi_operand_ref_at(which, _indy_bsm_offset); } int invoke_dynamic_name_and_type_ref_index_at(int which) { assert(tag_at(which).is_invoke_dynamic(), "Corrupted constant pool"); - jint ref_index = *int_at_addr(which); - return extract_high_short_from_int(ref_index); + return multi_operand_ref_at(which, _indy_nt_offset); + } + int invoke_dynamic_argument_count_at(int which) { + assert(tag_at(which).is_invoke_dynamic(), "Corrupted constant pool"); + int argc = multi_operand_ref_at(which, _indy_argc_offset); + DEBUG_ONLY(int op_count = multi_operand_count_at(which)); + assert(_indy_argv_offset + argc == op_count, "consistent inner and outer counts"); + return argc; + } + int invoke_dynamic_argument_index_at(int which, int j) { + assert((uint)j < (uint)invoke_dynamic_argument_count_at(which), "oob"); + return multi_operand_ref_at(which, _indy_argv_offset + j); } // The following methods (name/signature/klass_ref_at, klass_ref_at_noresolve, @@ -448,15 +533,24 @@ resolve_string_constants_impl(h_this, CHECK); } + private: + enum { _no_index_sentinel = -1, _possible_index_sentinel = -2 }; + public: + // Resolve late bound constants. oop resolve_constant_at(int index, TRAPS) { constantPoolHandle h_this(THREAD, this); - return resolve_constant_at_impl(h_this, index, -1, THREAD); + return resolve_constant_at_impl(h_this, index, _no_index_sentinel, THREAD); } oop resolve_cached_constant_at(int cache_index, TRAPS) { constantPoolHandle h_this(THREAD, this); - return resolve_constant_at_impl(h_this, -1, cache_index, THREAD); + return resolve_constant_at_impl(h_this, _no_index_sentinel, cache_index, 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); } // Klass name matches name at offset diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/src/share/vm/oops/cpCacheOop.hpp --- a/hotspot/src/share/vm/oops/cpCacheOop.hpp Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/src/share/vm/oops/cpCacheOop.hpp Sat Oct 30 13:08:23 2010 -0700 @@ -319,7 +319,9 @@ // Sizing debug_only(friend class ClassVerifier;) + public: int length() const { return _length; } + private: void set_length(int length) { _length = length; } static int header_size() { return sizeof(constantPoolCacheOopDesc) / HeapWordSize; } diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/src/share/vm/prims/jvm.h --- a/hotspot/src/share/vm/prims/jvm.h Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/src/share/vm/prims/jvm.h Sat Oct 30 13:08:23 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, 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 @@ -1047,7 +1047,8 @@ JVM_CONSTANT_NameAndType, JVM_CONSTANT_MethodHandle = 15, // JSR 292 JVM_CONSTANT_MethodType = 16, // JSR 292 - JVM_CONSTANT_InvokeDynamic = 17 // JSR 292 + JVM_CONSTANT_InvokeDynamicTrans = 17, // JSR 292, only occurs in old class files + JVM_CONSTANT_InvokeDynamic = 18 // JSR 292 }; /* JVM_CONSTANT_MethodHandle subtypes */ diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/src/share/vm/prims/methodComparator.cpp --- a/hotspot/src/share/vm/prims/methodComparator.cpp Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/src/share/vm/prims/methodComparator.cpp Sat Oct 30 13:08:23 2010 -0700 @@ -147,10 +147,9 @@ case Bytecodes::_invokevirtual : // fall through case Bytecodes::_invokespecial : // fall through case Bytecodes::_invokestatic : // fall through - case Bytecodes::_invokedynamic : // fall through case Bytecodes::_invokeinterface : { - int cpci_old = _s_old->has_index_u4() ? _s_old->get_index_u4() : _s_old->get_index_u2_cpcache(); - int cpci_new = _s_new->has_index_u4() ? _s_new->get_index_u4() : _s_new->get_index_u2_cpcache(); + int cpci_old = _s_old->get_index_u2_cpcache(); + int cpci_new = _s_new->get_index_u2_cpcache(); // Check if the names of classes, field/method names and signatures at these indexes // are the same. Indices which are really into constantpool cache (rather than constant // pool itself) are accepted by the constantpool query routines below. @@ -160,6 +159,33 @@ return false; break; } + case Bytecodes::_invokedynamic: { + int cpci_old = _s_old->get_index_u4(); + int cpci_new = _s_new->get_index_u4(); + // Check if the names of classes, field/method names and signatures at these indexes + // are the same. Indices which are really into constantpool cache (rather than constant + // pool itself) are accepted by the constantpool query routines below. + if ((_old_cp->name_ref_at(cpci_old) != _new_cp->name_ref_at(cpci_new)) || + (_old_cp->signature_ref_at(cpci_old) != _new_cp->signature_ref_at(cpci_new))) + return false; + int cpi_old = _old_cp->cache()->main_entry_at(cpci_old)->constant_pool_index(); + int cpi_new = _new_cp->cache()->main_entry_at(cpci_new)->constant_pool_index(); + int bsm_old = _old_cp->invoke_dynamic_bootstrap_method_ref_index_at(cpi_old); + int bsm_new = _new_cp->invoke_dynamic_bootstrap_method_ref_index_at(cpi_new); + if (!pool_constants_same(bsm_old, bsm_new)) + return false; + int cnt_old = _old_cp->invoke_dynamic_argument_count_at(cpi_old); + int cnt_new = _new_cp->invoke_dynamic_argument_count_at(cpi_new); + if (cnt_old != cnt_new) + return false; + for (int arg_i = 0; arg_i < cnt_old; arg_i++) { + int idx_old = _old_cp->invoke_dynamic_argument_index_at(cpi_old, arg_i); + int idx_new = _new_cp->invoke_dynamic_argument_index_at(cpi_new, arg_i); + if (!pool_constants_same(idx_old, idx_new)) + return false; + } + break; + } case Bytecodes::_ldc : // fall through case Bytecodes::_ldc_w : { @@ -167,51 +193,8 @@ Bytecode_loadconstant* ldc_new = Bytecode_loadconstant_at(_s_new->method(), _s_new->bci()); int cpi_old = ldc_old->pool_index(); int cpi_new = ldc_new->pool_index(); - constantTag tag_old = _old_cp->tag_at(cpi_old); - constantTag tag_new = _new_cp->tag_at(cpi_new); - if (tag_old.is_int() || tag_old.is_float()) { - if (tag_old.value() != tag_new.value()) - return false; - if (tag_old.is_int()) { - if (_old_cp->int_at(cpi_old) != _new_cp->int_at(cpi_new)) - return false; - } else { - // Use jint_cast to compare the bits rather than numerical values. - // This makes a difference for NaN constants. - if (jint_cast(_old_cp->float_at(cpi_old)) != jint_cast(_new_cp->float_at(cpi_new))) - return false; - } - } else if (tag_old.is_string() || tag_old.is_unresolved_string()) { - if (! (tag_new.is_unresolved_string() || tag_new.is_string())) - return false; - if (strcmp(_old_cp->string_at_noresolve(cpi_old), - _new_cp->string_at_noresolve(cpi_new)) != 0) - return false; - } else if (tag_old.is_klass() || tag_old.is_unresolved_klass()) { - // tag_old should be klass - 4881222 - if (! (tag_new.is_unresolved_klass() || tag_new.is_klass())) - return false; - if (_old_cp->klass_at_noresolve(cpi_old) != - _new_cp->klass_at_noresolve(cpi_new)) - return false; - } else if (tag_old.is_method_type() && tag_new.is_method_type()) { - int mti_old = _old_cp->method_type_index_at(cpi_old); - int mti_new = _new_cp->method_type_index_at(cpi_new); - if ((_old_cp->symbol_at(mti_old) != _new_cp->symbol_at(mti_new))) - return false; - } else if (tag_old.is_method_handle() && tag_new.is_method_handle()) { - if (_old_cp->method_handle_ref_kind_at(cpi_old) != - _new_cp->method_handle_ref_kind_at(cpi_new)) - return false; - int mhi_old = _old_cp->method_handle_index_at(cpi_old); - int mhi_new = _new_cp->method_handle_index_at(cpi_new); - if ((_old_cp->uncached_klass_ref_at_noresolve(mhi_old) != _new_cp->uncached_klass_ref_at_noresolve(mhi_new)) || - (_old_cp->uncached_name_ref_at(mhi_old) != _new_cp->uncached_name_ref_at(mhi_new)) || - (_old_cp->uncached_signature_ref_at(mhi_old) != _new_cp->uncached_signature_ref_at(mhi_new))) - return false; - } else { - return false; // unknown tag - } + if (!pool_constants_same(cpi_old, cpi_new)) + return false; break; } @@ -392,6 +375,55 @@ return true; } +bool MethodComparator::pool_constants_same(int cpi_old, int cpi_new) { + constantTag tag_old = _old_cp->tag_at(cpi_old); + constantTag tag_new = _new_cp->tag_at(cpi_new); + if (tag_old.is_int() || tag_old.is_float()) { + if (tag_old.value() != tag_new.value()) + return false; + if (tag_old.is_int()) { + if (_old_cp->int_at(cpi_old) != _new_cp->int_at(cpi_new)) + return false; + } else { + // Use jint_cast to compare the bits rather than numerical values. + // This makes a difference for NaN constants. + if (jint_cast(_old_cp->float_at(cpi_old)) != jint_cast(_new_cp->float_at(cpi_new))) + return false; + } + } else if (tag_old.is_string() || tag_old.is_unresolved_string()) { + if (! (tag_new.is_unresolved_string() || tag_new.is_string())) + return false; + if (strcmp(_old_cp->string_at_noresolve(cpi_old), + _new_cp->string_at_noresolve(cpi_new)) != 0) + return false; + } else if (tag_old.is_klass() || tag_old.is_unresolved_klass()) { + // tag_old should be klass - 4881222 + if (! (tag_new.is_unresolved_klass() || tag_new.is_klass())) + return false; + if (_old_cp->klass_at_noresolve(cpi_old) != + _new_cp->klass_at_noresolve(cpi_new)) + return false; + } else if (tag_old.is_method_type() && tag_new.is_method_type()) { + int mti_old = _old_cp->method_type_index_at(cpi_old); + int mti_new = _new_cp->method_type_index_at(cpi_new); + if ((_old_cp->symbol_at(mti_old) != _new_cp->symbol_at(mti_new))) + return false; + } else if (tag_old.is_method_handle() && tag_new.is_method_handle()) { + if (_old_cp->method_handle_ref_kind_at(cpi_old) != + _new_cp->method_handle_ref_kind_at(cpi_new)) + return false; + int mhi_old = _old_cp->method_handle_index_at(cpi_old); + int mhi_new = _new_cp->method_handle_index_at(cpi_new); + if ((_old_cp->uncached_klass_ref_at_noresolve(mhi_old) != _new_cp->uncached_klass_ref_at_noresolve(mhi_new)) || + (_old_cp->uncached_name_ref_at(mhi_old) != _new_cp->uncached_name_ref_at(mhi_new)) || + (_old_cp->uncached_signature_ref_at(mhi_old) != _new_cp->uncached_signature_ref_at(mhi_new))) + return false; + } else { + return false; // unknown tag + } + return true; +} + int MethodComparator::check_stack_and_locals_size(methodOop old_method, methodOop new_method) { if (old_method->max_stack() != new_method->max_stack()) { diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/src/share/vm/prims/methodComparator.hpp --- a/hotspot/src/share/vm/prims/methodComparator.hpp Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/src/share/vm/prims/methodComparator.hpp Sat Oct 30 13:08:23 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2010, 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 @@ -36,6 +36,7 @@ static GrowableArray *_fwd_jmps; static bool args_same(Bytecodes::Code c_old, Bytecodes::Code c_new); + static bool pool_constants_same(int cpi_old, int cpi_new); static int check_stack_and_locals_size(methodOop old_method, methodOop new_method); public: diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/src/share/vm/runtime/vmStructs.cpp --- a/hotspot/src/share/vm/runtime/vmStructs.cpp Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/src/share/vm/runtime/vmStructs.cpp Sat Oct 30 13:08:23 2010 -0700 @@ -1527,6 +1527,17 @@ \ declare_constant(symbolOopDesc::max_symbol_length) \ \ + /*************************************************/ \ + /* constantPoolOop layout enum for InvokeDynamic */ \ + /*************************************************/ \ + \ + declare_constant(constantPoolOopDesc::_multi_operand_count_offset) \ + declare_constant(constantPoolOopDesc::_multi_operand_base_offset) \ + declare_constant(constantPoolOopDesc::_indy_bsm_offset) \ + declare_constant(constantPoolOopDesc::_indy_nt_offset) \ + declare_constant(constantPoolOopDesc::_indy_argc_offset) \ + declare_constant(constantPoolOopDesc::_indy_argv_offset) \ + \ /*********************************************/ \ /* ConstantPoolCacheEntry FlagBitValues enum */ \ /*********************************************/ \ diff -r f05a0ba2802f -r 65d21c4c6337 hotspot/src/share/vm/utilities/constantTag.hpp --- a/hotspot/src/share/vm/utilities/constantTag.hpp Sat Oct 30 12:19:07 2010 -0700 +++ b/hotspot/src/share/vm/utilities/constantTag.hpp Sat Oct 30 13:08:23 2010 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, 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 @@ -82,6 +82,13 @@ bool is_method_handle() const { return _tag == JVM_CONSTANT_MethodHandle; } 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_unresolved_klass() || is_unresolved_string() || + is_object()); + } + constantTag() { _tag = JVM_CONSTANT_Invalid; }