--- old/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java 2017-06-08 09:09:10.041485197 -0400 +++ new/src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/HotSpotVMConfig.java 2017-06-08 09:09:09.937527360 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 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 @@ -229,6 +229,7 @@ final int jvmConstantMethodHandleInError = getConstant("JVM_CONSTANT_MethodHandleInError", Integer.class); final int jvmConstantMethodType = getConstant("JVM_CONSTANT_MethodType", Integer.class); final int jvmConstantMethodTypeInError = getConstant("JVM_CONSTANT_MethodTypeInError", Integer.class); + final int jvmConstantInvokeDynamicInError = getConstant("JVM_CONSTANT_InvokeDynamicInError", Integer.class); final int jvmConstantInvokeDynamic = getConstant("JVM_CONSTANT_InvokeDynamic", Integer.class); final int jvmConstantExternalMax = getConstant("JVM_CONSTANT_ExternalMax", Integer.class); --- old/src/share/vm/interpreter/linkResolver.cpp 2017-06-08 09:09:10.433801822 -0400 +++ new/src/share/vm/interpreter/linkResolver.cpp 2017-06-08 09:09:10.328679983 -0400 @@ -1690,8 +1690,14 @@ Handle bootstrap_specifier; // Check if CallSite has been bound already: ConstantPoolCacheEntry* cpce = pool->invokedynamic_cp_cache_entry_at(index); + int pool_index = cpce->constant_pool_index(); + if (pool()->tag_at(pool_index).is_invoke_dynamic_in_error()) { + // Throw saved exception. + ConstantPool::throw_resolution_error(pool, pool_index, CHECK); + return; + } + if (cpce->is_f1_null()) { - int pool_index = cpce->constant_pool_index(); oop bsm_info = pool->resolve_bootstrap_specifier_at(pool_index, THREAD); wrap_invokedynamic_exception(CHECK); assert(bsm_info != NULL, ""); @@ -1716,7 +1722,17 @@ tty->print(" BSM info: "); bootstrap_specifier->print(); } - resolve_dynamic_call(result, bootstrap_specifier, method_name, method_signature, current_klass, CHECK); + resolve_dynamic_call(result, bootstrap_specifier, method_name, method_signature, current_klass, THREAD); + if (HAS_PENDING_EXCEPTION) { + if (pool()->tag_at(pool_index).is_invoke_dynamic()&& + PENDING_EXCEPTION->is_a(SystemDictionary::LinkageError_klass())) { + // Record the exception then throw it. + ConstantPool::save_and_throw_exception(pool, pool_index, pool()->tag_at(pool_index), CHECK); + } else if (pool()->tag_at(pool_index).is_invoke_dynamic_in_error()) { + // Throw saved exception. + ConstantPool::throw_resolution_error(pool, pool_index, CHECK); + } // else return the original exception. + } } void LinkResolver::resolve_dynamic_call(CallInfo& result, --- old/src/share/vm/jvmci/vmStructs_jvmci.cpp 2017-06-08 09:09:10.846647331 -0400 +++ new/src/share/vm/jvmci/vmStructs_jvmci.cpp 2017-06-08 09:09:10.741157823 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -411,6 +411,7 @@ declare_constant(JVM_CONSTANT_UnresolvedClassInError) \ declare_constant(JVM_CONSTANT_MethodHandleInError) \ declare_constant(JVM_CONSTANT_MethodTypeInError) \ + declare_constant(JVM_CONSTANT_InvokeDynamicInError) \ declare_constant(JVM_CONSTANT_InternalMax) \ \ declare_constant(ArrayData::array_len_off_set) \ --- old/src/share/vm/oops/constantPool.cpp 2017-06-08 09:09:11.241265961 -0400 +++ new/src/share/vm/oops/constantPool.cpp 2017-06-08 09:09:11.137201659 -0400 @@ -740,6 +740,7 @@ case JVM_CONSTANT_MethodHandleInError: case JVM_CONSTANT_MethodTypeInError: + case JVM_CONSTANT_InvokeDynamicInError: { throw_resolution_error(this_cp, index, CHECK_NULL); break; @@ -1407,6 +1408,7 @@ } break; case JVM_CONSTANT_InvokeDynamic: + case JVM_CONSTANT_InvokeDynamicInError: { 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); @@ -1668,6 +1670,7 @@ return 5; case JVM_CONSTANT_InvokeDynamic: + case JVM_CONSTANT_InvokeDynamicInError: // u1 tag, u2 bsm, u2 nt return 5; @@ -1852,7 +1855,8 @@ DBG(printf("JVM_CONSTANT_MethodType: %hd", idx1)); break; } - case JVM_CONSTANT_InvokeDynamic: { + case JVM_CONSTANT_InvokeDynamic: + case JVM_CONSTANT_InvokeDynamicInError: { *bytes = tag; idx1 = extract_low_short_from_int(*int_at_addr(idx)); idx2 = extract_high_short_from_int(*int_at_addr(idx)); @@ -2058,6 +2062,7 @@ st->print("signature_index=%d", method_type_index_at(index)); break; case JVM_CONSTANT_InvokeDynamic : + case JVM_CONSTANT_InvokeDynamicInError : { st->print("bootstrap_method_index=%d", invoke_dynamic_bootstrap_method_ref_index_at(index)); st->print(" name_and_type_index=%d", invoke_dynamic_name_and_type_ref_index_at(index)); --- old/src/share/vm/oops/constantPool.hpp 2017-06-08 09:09:11.633341264 -0400 +++ new/src/share/vm/oops/constantPool.hpp 2017-06-08 09:09:11.529557730 -0400 @@ -545,11 +545,13 @@ } 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_invoke_dynamic_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_invoke_dynamic_in_error(), "Corrupted constant pool"); return extract_low_short_from_int(*int_at_addr(which)); } int invoke_dynamic_operand_base(int which) { @@ -647,12 +649,14 @@ 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_invoke_dynamic_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_invoke_dynamic_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; @@ -853,11 +857,13 @@ static oop resolve_bootstrap_specifier_at_impl(const constantPoolHandle& this_cp, int index, TRAPS); // Exception handling - static void throw_resolution_error(const constantPoolHandle& this_cp, int which, TRAPS); static Symbol* exception_message(const constantPoolHandle& this_cp, int which, constantTag tag, oop pending_exception); - static void save_and_throw_exception(const constantPoolHandle& this_cp, int which, constantTag tag, TRAPS); public: + // Exception handling + static void throw_resolution_error(const constantPoolHandle& this_cp, int which, TRAPS); + static void save_and_throw_exception(const constantPoolHandle& this_cp, int which, constantTag tag, TRAPS); + // Merging ConstantPool* support: bool compare_entry_to(int index1, const constantPoolHandle& cp2, int index2, TRAPS); void copy_cp_to(int start_i, int end_i, const constantPoolHandle& to_cp, int to_i, TRAPS) { --- old/src/share/vm/prims/jvm.cpp 2017-06-08 09:09:12.061882872 -0400 +++ new/src/share/vm/prims/jvm.cpp 2017-06-08 09:09:11.952775284 -0400 @@ -2231,6 +2231,8 @@ result = JVM_CONSTANT_String; } else if (tag.is_method_type_in_error()) { result = JVM_CONSTANT_MethodType; + } else if (tag.is_invoke_dynamic_in_error()) { + result = JVM_CONSTANT_InvokeDynamic; } else if (tag.is_method_handle_in_error()) { result = JVM_CONSTANT_MethodHandle; } --- old/src/share/vm/runtime/vmStructs.cpp 2017-06-08 09:09:12.547699222 -0400 +++ new/src/share/vm/runtime/vmStructs.cpp 2017-06-08 09:09:12.437580372 -0400 @@ -2413,6 +2413,7 @@ declare_constant(JVM_CONSTANT_UnresolvedClassInError) \ declare_constant(JVM_CONSTANT_MethodHandleInError) \ declare_constant(JVM_CONSTANT_MethodTypeInError) \ + declare_constant(JVM_CONSTANT_InvokeDynamicInError) \ declare_constant(JVM_CONSTANT_InternalMax) \ \ /*****************************/ \ --- old/src/share/vm/utilities/constantTag.cpp 2017-06-08 09:09:12.994106422 -0400 +++ new/src/share/vm/utilities/constantTag.cpp 2017-06-08 09:09:12.890216827 -0400 @@ -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 @@ -55,6 +55,7 @@ case JVM_CONSTANT_MethodHandleInError : case JVM_CONSTANT_MethodType : case JVM_CONSTANT_MethodTypeInError : + case JVM_CONSTANT_InvokeDynamicInError : return T_OBJECT; default: ShouldNotReachHere(); @@ -71,6 +72,8 @@ return JVM_CONSTANT_MethodHandle; case JVM_CONSTANT_MethodTypeInError: return JVM_CONSTANT_MethodType; + case JVM_CONSTANT_InvokeDynamicInError: + return JVM_CONSTANT_InvokeDynamic; default: return _tag; } @@ -85,6 +88,8 @@ return JVM_CONSTANT_MethodHandleInError; case JVM_CONSTANT_MethodType: return JVM_CONSTANT_MethodTypeInError; + case JVM_CONSTANT_InvokeDynamic: + return JVM_CONSTANT_InvokeDynamicInError; default: ShouldNotReachHere(); return JVM_CONSTANT_Invalid; @@ -125,6 +130,8 @@ return "MethodType Error"; case JVM_CONSTANT_InvokeDynamic : return "InvokeDynamic"; + case JVM_CONSTANT_InvokeDynamicInError : + return "InvokeDynamic Error"; case JVM_CONSTANT_Utf8 : return "Utf8"; case JVM_CONSTANT_UnresolvedClass : --- old/src/share/vm/utilities/constantTag.hpp 2017-06-08 09:09:13.363096320 -0400 +++ new/src/share/vm/utilities/constantTag.hpp 2017-06-08 09:09:13.260592998 -0400 @@ -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_InvokeDynamicInError = 106, // Error tag due to resolution error + JVM_CONSTANT_InternalMax = 106 // Last implementation tag }; @@ -79,6 +80,9 @@ bool is_method_type_in_error() const { return _tag == JVM_CONSTANT_MethodTypeInError; } + bool is_invoke_dynamic_in_error() const { + return _tag == JVM_CONSTANT_InvokeDynamicInError; + } bool is_klass_index() const { return _tag == JVM_CONSTANT_ClassIndex; } bool is_string_index() const { return _tag == JVM_CONSTANT_StringIndex; } --- /dev/null 2017-05-25 11:08:16.604000221 -0400 +++ new/test/runtime/BootstrapMethod/BSMCalledTwice.java 2017-06-08 09:09:13.642356510 -0400 @@ -0,0 +1,198 @@ +/* + * 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 8174954 + * @library /test/lib + * @modules java.base/jdk.internal.org.objectweb.asm + * @compile -XDignore.symbol.file BSMCalledTwice.java + * @run main BSMCalledTwice + */ + +import java.io.File; +import java.io.FileOutputStream; +import java.util.*; +import static jdk.internal.org.objectweb.asm.Opcodes.*; +import jdk.internal.org.objectweb.asm.*; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +// BSMCalledTwice generates a class file named "TestC.class" that contains +// bytecodes that represent the following program +// +// public class TestC { +// public static void main(java.lang.String[] arg) { +// for (int i=0; i < 2; i++) { +// try { +// String f = "friend"; +// +// // The "hello " + f in the following statement produces an +// // invokedynamic with a BSM of +// // StringConcatFactory.java/makeConcatWithConstants. +// // The ASM below erroneously puts 2 static arguments, "hello " +// // and "goodbye" on the stack for the BSM. Causing a exception to +// // be thrown when creatingthe CallSite object. +// System.out.println("hello " + f); <--------------- invokedynamic +// +// } catch (Error e) { +// System.out.println("Caught Error:"); +// System.out.println(e.getMessage()); +// e.printStackTrace(); +// } +// } +// } +// } +// +public class BSMCalledTwice implements Opcodes { + static final String classTestCName = "TestC"; + + public static int count_makeSite(String text) { + int count = 0; + String text_ptr = text; + while (text_ptr.indexOf("makeSite") != -1) { + text_ptr = text_ptr.substring(text_ptr.indexOf("makeSite") + 1); + count++; + } + return count; + } + + public static void main(String[] args) throws Exception { + ClassLoader cl = new ClassLoader() { + public Class loadClass(String name) throws ClassNotFoundException { + if (findLoadedClass(name) != null) { + return findLoadedClass(name); + } + + if (classTestCName.equals(name)) { + byte[] classFile = null; + try { + classFile = dumpTestC(); + } catch (Exception e) { + } + return defineClass(classTestCName, classFile, 0, classFile.length); + } + return super.loadClass(name); + } + }; + + cl.loadClass(classTestCName); + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, "-cp", ".", classTestCName); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + String test_output = output.getOutput(); + if (test_output == null) { + throw new RuntimeException("Test failed, null test output"); + } + // "makeSite" is currently listed twice in the exception stacks for each + // failing call to the BootstrapMethod. So more that two calls means + // that the BootstrapMethod was called more than once. + int count = count_makeSite(test_output); + if (count < 1 || count > 2) { + throw new RuntimeException("Test failed, bad number of calls to BootstrapMethod"); + } + output.shouldHaveExitValue(0); + } + + public static byte[] dumpTestC () throws Exception { + ClassWriter cw = new ClassWriter(0); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + + cw.visit(53, ACC_PUBLIC + ACC_SUPER, classTestCName, null, "java/lang/Object", null); + + cw.visitInnerClass("java/lang/invoke/MethodHandles$Lookup", + "java/lang/invoke/MethodHandles", "Lookup", + ACC_PUBLIC + ACC_FINAL + ACC_STATIC); + + { + mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null); + mv.visitCode(); + Label l0 = new Label(); + Label l1 = new Label(); + Label l2 = new Label(); + mv.visitTryCatchBlock(l0, l1, l2, "java/lang/Error"); + mv.visitInsn(ICONST_0); + mv.visitVarInsn(ISTORE, 1); + Label l3 = new Label(); + mv.visitLabel(l3); + mv.visitFrame(Opcodes.F_APPEND,1, new Object[] {Opcodes.INTEGER}, 0, null); + mv.visitVarInsn(ILOAD, 1); + mv.visitInsn(ICONST_2); + Label l4 = new Label(); + mv.visitJumpInsn(IF_ICMPGE, l4); + mv.visitLabel(l0); + mv.visitLdcInsn("friend"); + mv.visitVarInsn(ASTORE, 2); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitVarInsn(ALOAD, 2); + mv.visitInvokeDynamicInsn("makeConcatWithConstants", + "(Ljava/lang/String;)Ljava/lang/String;", + new Handle(Opcodes.H_INVOKESTATIC, + "java/lang/invoke/StringConcatFactory", + "makeConcatWithConstants", + "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;"), + new Object[]{"hello \u0001", "goodbye"}); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); + mv.visitLabel(l1); + Label l5 = new Label(); + mv.visitJumpInsn(GOTO, l5); + mv.visitLabel(l2); + mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/Error"}); + mv.visitVarInsn(ASTORE, 2); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitLdcInsn("Caught Error:"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); + mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Error", "getMessage", "()Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Error", "printStackTrace", "()V", false); + mv.visitLabel(l5); + mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + mv.visitIincInsn(1, 1); + mv.visitJumpInsn(GOTO, l3); + mv.visitLabel(l4); + mv.visitFrame(Opcodes.F_CHOP,1, null, 0, null); + mv.visitInsn(RETURN); + mv.visitMaxs(2, 3); + mv.visitEnd(); + } + cw.visitEnd(); + + try(FileOutputStream fos = new FileOutputStream(new File("TestC.class"))) { + fos.write(cw.toByteArray()); + } + return cw.toByteArray(); + } +} --- /dev/null 2017-05-25 11:08:16.604000221 -0400 +++ new/test/runtime/modules/AccessCheck/MethodAccessReadTwice.java 2017-06-08 09:09:13.960043438 -0400 @@ -0,0 +1,140 @@ +/* + 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 8174954 + * @summary Class p5.c5 in module first_mod cannot access p2.c2 in module + * second_mod, because first_mod cannot read second_mod. Test that + * p5.c5 still cannot access p2.c2 even after a read edge is added + * that enables first_mod to read second_mod. + * @compile ModuleLibrary.java + * @compile p2/c2.java + * @compile p5/c5.java + * @run main/othervm MethodAccessReadTwice + */ + +import java.lang.module.Configuration; +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleFinder; +import java.lang.ModuleLayer; +import java.lang.Module; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +// defines first_mod --> packages p5 +// defines second_mod --> package p2, p2 is exported to first_mod + +public class MethodAccessReadTwice { + + // Create a Layer over the boot layer. + // Define modules within this layer to test access between + // publicly defined classes within packages of those modules. + public void createLayerOnBoot() throws Throwable { + + // Define module: first_mod + // Can read: java.base + // Packages: p5 + // Packages exported: none + // Packages exported: p5 is exported unqualifiedly + + ModuleDescriptor descriptor_first_mod = + ModuleDescriptor.newModule("first_mod") + .requires("java.base") + .exports("p5") + .build(); + + // Define module: second_mod + // Can read: java.base + // Packages: p2 + // Packages exported: p2 is exported to first_mod + ModuleDescriptor descriptor_second_mod = + ModuleDescriptor.newModule("second_mod") + .requires("java.base") + .exports("p2") + .build(); + + // Set up a ModuleFinder containing all modules for this layer + ModuleFinder finder = ModuleLibrary.of(descriptor_first_mod, descriptor_second_mod); + + // Resolves "first_mod" and "second_mod" + Configuration cf = ModuleLayer.boot() + .configuration() + .resolve(finder, ModuleFinder.of(), Set.of("first_mod", "second_mod")); + + // Map each module to this class loader + Map map = new HashMap<>(); + ClassLoader loader = MethodAccessReadTwice.class.getClassLoader(); + map.put("first_mod", loader); + map.put("second_mod", loader); + + // Create Layer that contains first_mod & second_mod + ModuleLayer layer = ModuleLayer.boot().defineModules(cf, map::get); + + Class p2_c2_class = loader.loadClass("p2.c2"); + Class p5_c5_class = loader.loadClass("p5.c5"); + + Module first_mod = p5_c5_class.getModule(); + Module second_mod = p2_c2_class.getModule(); + + p5.c5 c5_obj = new p5.c5(); + p2.c2 c2_obj = new p2.c2(); + + // First access check for p5.c5 --> call to method5 --> tries to access p2.c2 + try { + c5_obj.method5(c2_obj); // Should result in IAE + throw new RuntimeException("Test Failed, module first_mod should not have access to p2.c2"); + } catch (IllegalAccessError e) { + String message = e.getMessage(); + if (!(message.contains("cannot access") && + message.contains("because module first_mod does not read module second_mod"))) { + throw new RuntimeException("Wrong message: " + message); + } else { + System.out.println("Test Succeeded at attempt #1"); + } + } + + // Add a read edge from p5/c5's module (first_mod) to second_mod + c5_obj.methodAddReadEdge(p2_c2_class.getModule()); + + // Second access check for p5.c5, should have same result as first + try { + c5_obj.method5(c2_obj); // should result in IAE + throw new RuntimeException("Test Failed, access should have been cached above"); + } catch (IllegalAccessError e) { + String message = e.getMessage(); + if (!(message.contains("cannot access") && + message.contains("because module first_mod does not read module second_mod"))) { + throw new RuntimeException("Wrong message: " + message); + } else { + System.out.println("Test Succeeded at attempt #2"); + } + } + } + + public static void main(String args[]) throws Throwable { + MethodAccessReadTwice test = new MethodAccessReadTwice(); + test.createLayerOnBoot(); + } +} --- /dev/null 2017-05-25 11:08:16.604000221 -0400 +++ new/test/runtime/modules/AccessCheck/p5/c5.java 2017-06-08 09:09:14.269502967 -0400 @@ -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 p5; + +import java.lang.Module; +import p2.c2; + +public class c5 { + public void method5(p2.c2 param) { System.out.println("In c5's method5 with param = " + param); } + + public void methodAddReadEdge(Module m) { + // Add a read edge from p5/c5's module (first_mod) to second_mod + c5.class.getModule().addReads(m); + } +} +