# HG changeset patch # User dholmes # Date 1433981729 14400 # Node ID cd8fe1a9205aedb6b5a1fc16e664412e48aee565 # Parent 3820a7d6476004e9e1d54f44b729faf3d90550b9# Parent 78234388ae4f002426052ae1fb0e2dbe30721d82 Merge diff -r 3820a7d64760 -r cd8fe1a9205a src/share/vm/interpreter/linkResolver.cpp --- a/src/share/vm/interpreter/linkResolver.cpp Wed May 20 09:07:36 2015 -0400 +++ b/src/share/vm/interpreter/linkResolver.cpp Wed Jun 10 20:15:29 2015 -0400 @@ -1592,6 +1592,26 @@ result.set_handle(resolved_method, resolved_appendix, resolved_method_type, CHECK); } +static void wrap_invokedynamic_exception(TRAPS) { + if (HAS_PENDING_EXCEPTION) { + if (TraceMethodHandles) { + tty->print_cr("invokedynamic throws BSME for " INTPTR_FORMAT, p2i((void *)PENDING_EXCEPTION)); + PENDING_EXCEPTION->print(); + } + if (PENDING_EXCEPTION->is_a(SystemDictionary::BootstrapMethodError_klass())) { + // throw these guys, since they are already wrapped + return; + } + if (!PENDING_EXCEPTION->is_a(SystemDictionary::LinkageError_klass())) { + // intercept only LinkageErrors which might have failed to wrap + return; + } + // See the "Linking Exceptions" section for the invokedynamic instruction in the JVMS. + Handle nested_exception(THREAD, PENDING_EXCEPTION); + CLEAR_PENDING_EXCEPTION; + THROW_CAUSE(vmSymbols::java_lang_BootstrapMethodError(), nested_exception) + } +} void LinkResolver::resolve_invokedynamic(CallInfo& result, constantPoolHandle pool, int index, TRAPS) { assert(EnableInvokeDynamic, ""); @@ -1607,7 +1627,8 @@ ConstantPoolCacheEntry* cpce = pool->invokedynamic_cp_cache_entry_at(index); if (cpce->is_f1_null()) { int pool_index = cpce->constant_pool_index(); - oop bsm_info = pool->resolve_bootstrap_specifier_at(pool_index, CHECK); + oop bsm_info = pool->resolve_bootstrap_specifier_at(pool_index, THREAD); + wrap_invokedynamic_exception(CHECK); assert(bsm_info != NULL, ""); // FIXME: Cache this once per BootstrapMethods entry, not once per CONSTANT_InvokeDynamic. bootstrap_specifier = Handle(THREAD, bsm_info); @@ -1616,7 +1637,8 @@ methodHandle method( THREAD, cpce->f1_as_method()); 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, CHECK); + result.set_handle(method, appendix, method_type, THREAD); + wrap_invokedynamic_exception(CHECK); return; } @@ -1647,25 +1669,9 @@ &resolved_appendix, &resolved_method_type, THREAD); - if (HAS_PENDING_EXCEPTION) { - if (TraceMethodHandles) { - tty->print_cr("invokedynamic throws BSME for " INTPTR_FORMAT, p2i((void *)PENDING_EXCEPTION)); - PENDING_EXCEPTION->print(); - } - if (PENDING_EXCEPTION->is_a(SystemDictionary::BootstrapMethodError_klass())) { - // throw these guys, since they are already wrapped - return; - } - if (!PENDING_EXCEPTION->is_a(SystemDictionary::LinkageError_klass())) { - // intercept only LinkageErrors which might have failed to wrap - return; - } - // See the "Linking Exceptions" section for the invokedynamic instruction in the JVMS. - Handle nested_exception(THREAD, PENDING_EXCEPTION); - CLEAR_PENDING_EXCEPTION; - THROW_CAUSE(vmSymbols::java_lang_BootstrapMethodError(), nested_exception) - } - result.set_handle(resolved_method, resolved_appendix, resolved_method_type, CHECK); + wrap_invokedynamic_exception(CHECK); + result.set_handle(resolved_method, resolved_appendix, resolved_method_type, THREAD); + wrap_invokedynamic_exception(CHECK); } //------------------------------------------------------------------------------------------------------------------------ diff -r 3820a7d64760 -r cd8fe1a9205a src/share/vm/memory/metaspace.cpp --- a/src/share/vm/memory/metaspace.cpp Wed May 20 09:07:36 2015 -0400 +++ b/src/share/vm/memory/metaspace.cpp Wed Jun 10 20:15:29 2015 -0400 @@ -622,7 +622,8 @@ Metachunk* _chunks_in_use[NumberOfInUseLists]; Metachunk* _current_chunk; - // Maximum number of small chunks to allocate to a SpaceManager + // Number of small chunks to allocate to a manager + // If class space manager, small chunks are unlimited static uint const _small_chunk_limit; // Sum of all space in allocated chunks @@ -736,8 +737,6 @@ // Block allocation and deallocation. // Allocates a block from the current chunk MetaWord* allocate(size_t word_size); - // Allocates a block from a small chunk - MetaWord* get_small_chunk_and_allocate(size_t word_size); // Helper for allocations MetaWord* allocate_work(size_t word_size); @@ -2032,8 +2031,9 @@ size_t SpaceManager::calc_chunk_size(size_t word_size) { // Decide between a small chunk and a medium chunk. Up to - // _small_chunk_limit small chunks can be allocated. - // After that a medium chunk is preferred. + // _small_chunk_limit small chunks can be allocated but + // once a medium chunk has been allocated, no more small + // chunks will be allocated. size_t chunk_word_size; if (chunks_in_use(MediumIndex) == NULL && sum_count_in_chunks_in_use(SmallIndex) < _small_chunk_limit) { @@ -2101,7 +2101,7 @@ word_size, words_used, words_left); } - // Get another chunk + // Get another chunk out of the virtual space size_t grow_chunks_by_words = calc_chunk_size(word_size); Metachunk* next = get_new_chunk(word_size, grow_chunks_by_words); @@ -2432,43 +2432,6 @@ return next; } -/* - * The policy is to allocate up to _small_chunk_limit small chunks - * after which only medium chunks are allocated. This is done to - * reduce fragmentation. In some cases, this can result in a lot - * of small chunks being allocated to the point where it's not - * possible to expand. If this happens, there may be no medium chunks - * available and OOME would be thrown. Instead of doing that, - * if the allocation request size fits in a small chunk, an attempt - * will be made to allocate a small chunk. - */ -MetaWord* SpaceManager::get_small_chunk_and_allocate(size_t word_size) { - if (word_size + Metachunk::overhead() > small_chunk_size()) { - return NULL; - } - - MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag); - MutexLockerEx cl1(expand_lock(), Mutex::_no_safepoint_check_flag); - - Metachunk* chunk = chunk_manager()->chunk_freelist_allocate(small_chunk_size()); - - MetaWord* mem = NULL; - - if (chunk != NULL) { - // Add chunk to the in-use chunk list and do an allocation from it. - // Add to this manager's list of chunks in use. - add_chunk(chunk, false); - mem = chunk->allocate(word_size); - - inc_used_metrics(word_size); - - // Track metaspace memory usage statistic. - track_metaspace_memory_usage(); - } - - return mem; -} - MetaWord* SpaceManager::allocate(size_t word_size) { MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag); @@ -3548,18 +3511,7 @@ } if (result == NULL) { - SpaceManager* sm; - if (is_class_space_allocation(mdtype)) { - sm = loader_data->metaspace_non_null()->class_vsm(); - } else { - sm = loader_data->metaspace_non_null()->vsm(); - } - - result = sm->get_small_chunk_and_allocate(word_size); - - if (result == NULL) { - report_metadata_oome(loader_data, word_size, type, mdtype, CHECK_NULL); - } + report_metadata_oome(loader_data, word_size, type, mdtype, CHECK_NULL); } // Zero initialize. diff -r 3820a7d64760 -r cd8fe1a9205a src/share/vm/opto/loopTransform.cpp --- a/src/share/vm/opto/loopTransform.cpp Wed May 20 09:07:36 2015 -0400 +++ b/src/share/vm/opto/loopTransform.cpp Wed Jun 10 20:15:29 2015 -0400 @@ -1821,7 +1821,10 @@ // Find the pre-loop limit; we will expand it's iterations to // not ever trip low tests. Node *p_f = iffm->in(0); - assert(p_f->Opcode() == Op_IfFalse, ""); + // pre loop may have been optimized out + if (p_f->Opcode() != Op_IfFalse) { + return; + } CountedLoopEndNode *pre_end = p_f->in(0)->as_CountedLoopEnd(); assert(pre_end->loopnode()->is_pre_loop(), ""); Node *pre_opaq1 = pre_end->limit(); diff -r 3820a7d64760 -r cd8fe1a9205a src/share/vm/prims/jni.cpp --- a/src/share/vm/prims/jni.cpp Wed May 20 09:07:36 2015 -0400 +++ b/src/share/vm/prims/jni.cpp Wed Jun 10 20:15:29 2015 -0400 @@ -1325,39 +1325,32 @@ Method* m = Method::resolve_jmethod_id(method_id); number_of_parameters = m->size_of_parameters(); Klass* holder = m->method_holder(); - if (!(holder)->is_interface()) { + if (call_type != JNI_VIRTUAL) { + selected_method = m; + } else if (!m->has_itable_index()) { // non-interface call -- for that little speed boost, don't handlize debug_only(No_Safepoint_Verifier nosafepoint;) - if (call_type == JNI_VIRTUAL) { - // jni_GetMethodID makes sure class is linked and initialized - // so m should have a valid vtable index. - assert(!m->has_itable_index(), ""); - int vtbl_index = m->vtable_index(); - if (vtbl_index != Method::nonvirtual_vtable_index) { - Klass* k = h_recv->klass(); - // k might be an arrayKlassOop but all vtables start at - // the same place. The cast is to avoid virtual call and assertion. - InstanceKlass *ik = (InstanceKlass*)k; - selected_method = ik->method_at_vtable(vtbl_index); - } else { - // final method - selected_method = m; - } + // jni_GetMethodID makes sure class is linked and initialized + // so m should have a valid vtable index. + assert(m->valid_vtable_index(), "no valid vtable index"); + int vtbl_index = m->vtable_index(); + if (vtbl_index != Method::nonvirtual_vtable_index) { + Klass* k = h_recv->klass(); + // k might be an arrayKlassOop but all vtables start at + // the same place. The cast is to avoid virtual call and assertion. + InstanceKlass *ik = (InstanceKlass*)k; + selected_method = ik->method_at_vtable(vtbl_index); } else { - // JNI_NONVIRTUAL call + // final method selected_method = m; } } else { // interface call KlassHandle h_holder(THREAD, holder); - if (call_type == JNI_VIRTUAL) { - int itbl_index = m->itable_index(); - Klass* k = h_recv->klass(); - selected_method = InstanceKlass::cast(k)->method_at_itable(h_holder(), itbl_index, CHECK); - } else { - selected_method = m; - } + int itbl_index = m->itable_index(); + Klass* k = h_recv->klass(); + selected_method = InstanceKlass::cast(k)->method_at_itable(h_holder(), itbl_index, CHECK); } } diff -r 3820a7d64760 -r cd8fe1a9205a test/runtime/invokedynamic/BootstrapMethodErrorTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/runtime/invokedynamic/BootstrapMethodErrorTest.java Wed Jun 10 20:15:29 2015 -0400 @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2015, 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 8051045 + * @summary Test that exceptions from invokedynamic are wrapped in BootstrapMethodError + * @modules java.base/jdk.internal.org.objectweb.asm + * @run main BootstrapMethodErrorTest + */ + +import java.lang.reflect.Method; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import static java.lang.invoke.MethodHandles.*; +import static java.lang.invoke.MethodType.*; + +import jdk.internal.org.objectweb.asm.ClassWriter; +import jdk.internal.org.objectweb.asm.Handle; +import jdk.internal.org.objectweb.asm.MethodVisitor; +import jdk.internal.org.objectweb.asm.Opcodes; + +public class BootstrapMethodErrorTest extends ClassLoader implements Opcodes { + + @Override + public Class findClass(String name) throws ClassNotFoundException { + byte[] b; + try { + b = loadClassData(name); + } catch (Throwable th) { + throw new ClassNotFoundException("Loading error", th); + } + return defineClass(name, b, 0, b.length); + } + + private byte[] loadClassData(String name) throws Exception { + ClassWriter cw = new ClassWriter(0); + MethodVisitor mv; + + if (name.equals("C")) { + cw.visit(52, ACC_SUPER | ACC_PUBLIC, "C", null, "java/lang/Object", null); + { + mv = cw.visitMethod(ACC_PRIVATE | ACC_STATIC, "m", "()V", null, null); + mv.visitCode(); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 1); + mv.visitEnd(); + } + cw.visitEnd(); + return cw.toByteArray(); + } else if (name.equals("Exec")) { + cw.visit(52, ACC_SUPER | ACC_PUBLIC, "Exec", null, "java/lang/Object", null); + { + mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "invokeRef", "()V", null, null); + mv.visitCode(); + Handle h = new Handle(H_INVOKESTATIC, "C", "m", "()V"); + mv.visitInvokeDynamicInsn("C", "()V", h); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + cw.visitEnd(); + return cw.toByteArray(); + } + return null; + } + + public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, NoSuchMethodException { + new BootstrapMethodErrorTest().test(); + } + + public void test() throws ClassNotFoundException, IllegalAccessException, NoSuchMethodException { + Class.forName("C", true, this); + Class exec = Class.forName("Exec", true, this); + + try { + exec.getMethod("invokeRef").invoke(null); + } catch (Throwable e) { + Throwable c = e.getCause(); + if (c == null) { + throw new RuntimeException( + "Expected BootstrapMethodError wrapped in an InvocationTargetException but it wasn't wrapped", e); + } else if (c instanceof BootstrapMethodError) { + // Only way to pass test, all else should throw + return; + } else { + throw new RuntimeException( + "Expected BootstrapMethodError but got another Error: " + + c.getClass().getName(), + c); + } + } + throw new RuntimeException("Expected BootstrapMethodError but no Error at all was thrown"); + } +}