mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-02 22:48:35 +00:00
Merge
This commit is contained in:
commit
bbd0fa6b1a
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 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
|
||||
@ -33,6 +33,12 @@ class ResolutionErrorEntry;
|
||||
// ResolutionError objects are used to record errors encountered during
|
||||
// constant pool resolution (JVMS 5.4.3).
|
||||
|
||||
// This value is added to the cpCache index of an invokedynamic instruction when
|
||||
// storing the resolution error resulting from that invokedynamic instruction.
|
||||
// This prevents issues where the cpCache index is the same as the constant pool
|
||||
// index of another entry in the table.
|
||||
const int CPCACHE_INDEX_MANGLE_VALUE = 1000000;
|
||||
|
||||
class ResolutionErrorTable : public Hashtable<ConstantPool*, mtClass> {
|
||||
|
||||
public:
|
||||
@ -73,6 +79,14 @@ public:
|
||||
|
||||
// RedefineClasses support - remove obsolete constant pool entry
|
||||
void delete_entry(ConstantPool* c);
|
||||
|
||||
// This function is used to encode an index to differentiate it from a
|
||||
// constant pool index. It assumes it is being called with a cpCache index
|
||||
// (that is less than 0).
|
||||
static int encode_cpcache_index(int index) {
|
||||
assert(index < 0, "Unexpected non-negative cpCache index");
|
||||
return index + CPCACHE_INDEX_MANGLE_VALUE;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
#include "precompiled.hpp"
|
||||
#include "classfile/defaultMethods.hpp"
|
||||
#include "classfile/javaClasses.hpp"
|
||||
#include "classfile/resolutionErrors.hpp"
|
||||
#include "classfile/symbolTable.hpp"
|
||||
#include "classfile/systemDictionary.hpp"
|
||||
#include "classfile/vmSymbols.hpp"
|
||||
@ -1696,8 +1697,22 @@ void LinkResolver::resolve_invokedynamic(CallInfo& result, const constantPoolHan
|
||||
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 (cpce->is_f1_null()) {
|
||||
int pool_index = cpce->constant_pool_index();
|
||||
if (cpce->indy_resolution_failed()) {
|
||||
ConstantPool::throw_resolution_error(pool,
|
||||
ResolutionErrorTable::encode_cpcache_index(index),
|
||||
CHECK);
|
||||
}
|
||||
|
||||
// The initial step in Call Site Specifier Resolution is to resolve the symbolic
|
||||
// reference to a method handle which will be the bootstrap method for a dynamic
|
||||
// call site. If resolution for the java.lang.invoke.MethodHandle for the bootstrap
|
||||
// method fails, then a MethodHandleInError is stored at the corresponding bootstrap
|
||||
// method's CP index for the CONSTANT_MethodHandle_info. So, there is no need to
|
||||
// set the indy_rf flag since any subsequent invokedynamic instruction which shares
|
||||
// this bootstrap method will encounter the resolution of MethodHandleInError.
|
||||
oop bsm_info = pool->resolve_bootstrap_specifier_at(pool_index, THREAD);
|
||||
wrap_invokedynamic_exception(CHECK);
|
||||
assert(bsm_info != NULL, "");
|
||||
@ -1722,7 +1737,31 @@ void LinkResolver::resolve_invokedynamic(CallInfo& result, const constantPoolHan
|
||||
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 && PENDING_EXCEPTION->is_a(SystemDictionary::LinkageError_klass())) {
|
||||
int encoded_index = ResolutionErrorTable::encode_cpcache_index(index);
|
||||
bool recorded_res_status = cpce->save_and_throw_indy_exc(pool, pool_index,
|
||||
encoded_index,
|
||||
pool()->tag_at(pool_index),
|
||||
CHECK);
|
||||
if (!recorded_res_status) {
|
||||
// Another thread got here just before we did. So, either use the method
|
||||
// that it resolved or throw the LinkageError exception that it threw.
|
||||
if (!cpce->is_f1_null()) {
|
||||
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, THREAD);
|
||||
wrap_invokedynamic_exception(CHECK);
|
||||
} else {
|
||||
assert(cpce->indy_resolution_failed(), "Resolution failure flag not set");
|
||||
ConstantPool::throw_resolution_error(pool, encoded_index, CHECK);
|
||||
}
|
||||
return;
|
||||
}
|
||||
assert(cpce->indy_resolution_failed(), "Resolution failure flag wasn't set");
|
||||
}
|
||||
}
|
||||
|
||||
void LinkResolver::resolve_dynamic_call(CallInfo& result,
|
||||
|
||||
@ -865,11 +865,13 @@ class ConstantPool : public Metadata {
|
||||
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);
|
||||
|
||||
// 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) {
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
#include "precompiled.hpp"
|
||||
#include "classfile/resolutionErrors.hpp"
|
||||
#include "interpreter/bytecodeStream.hpp"
|
||||
#include "interpreter/bytecodes.hpp"
|
||||
#include "interpreter/interpreter.hpp"
|
||||
@ -110,6 +111,10 @@ void ConstantPoolCacheEntry::release_set_f1(Metadata* f1) {
|
||||
OrderAccess::release_store(&_f1, f1);
|
||||
}
|
||||
|
||||
void ConstantPoolCacheEntry::set_indy_resolution_failed() {
|
||||
OrderAccess::release_store(&_flags, _flags | (1 << indy_resolution_failed_shift));
|
||||
}
|
||||
|
||||
// Note that concurrent update of both bytecodes can leave one of them
|
||||
// reset to zero. This is harmless; the interpreter will simply re-resolve
|
||||
// the damaged entry. More seriously, the memory synchronization is needed
|
||||
@ -318,6 +323,25 @@ void ConstantPoolCacheEntry::set_method_handle_common(const constantPoolHandle&
|
||||
return;
|
||||
}
|
||||
|
||||
if (indy_resolution_failed()) {
|
||||
// Before we got here, another thread got a LinkageError exception during
|
||||
// resolution. Ignore our success and throw their exception.
|
||||
ConstantPoolCache* cpCache = cpool->cache();
|
||||
int index = -1;
|
||||
for (int i = 0; i < cpCache->length(); i++) {
|
||||
if (cpCache->entry_at(i) == this) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
guarantee(index >= 0, "Didn't find cpCache entry!");
|
||||
int encoded_index = ResolutionErrorTable::encode_cpcache_index(
|
||||
ConstantPool::encode_invokedynamic_index(index));
|
||||
Thread* THREAD = Thread::current();
|
||||
ConstantPool::throw_resolution_error(cpool, encoded_index, THREAD);
|
||||
return;
|
||||
}
|
||||
|
||||
const methodHandle adapter = call_info.resolved_method();
|
||||
const Handle appendix = call_info.resolved_appendix();
|
||||
const Handle method_type = call_info.resolved_method_type();
|
||||
@ -389,6 +413,40 @@ void ConstantPoolCacheEntry::set_method_handle_common(const constantPoolHandle&
|
||||
}
|
||||
}
|
||||
|
||||
bool ConstantPoolCacheEntry::save_and_throw_indy_exc(
|
||||
const constantPoolHandle& cpool, int cpool_index, int index, constantTag tag, TRAPS) {
|
||||
|
||||
assert(HAS_PENDING_EXCEPTION, "No exception got thrown!");
|
||||
assert(PENDING_EXCEPTION->is_a(SystemDictionary::LinkageError_klass()),
|
||||
"No LinkageError exception");
|
||||
|
||||
// Use the resolved_references() lock for this cpCache entry.
|
||||
// resolved_references are created for all classes with Invokedynamic, MethodHandle
|
||||
// or MethodType constant pool cache entries.
|
||||
objArrayHandle resolved_references(Thread::current(), cpool->resolved_references());
|
||||
assert(resolved_references() != NULL,
|
||||
"a resolved_references array should have been created for this class");
|
||||
ObjectLocker ol(resolved_references, THREAD);
|
||||
|
||||
// if f1 is not null or the indy_resolution_failed flag is set then another
|
||||
// thread either succeeded in resolving the method or got a LinkageError
|
||||
// exception, before this thread was able to record its failure. So, clear
|
||||
// this thread's exception and return false so caller can use the earlier
|
||||
// thread's result.
|
||||
if (!is_f1_null() || indy_resolution_failed()) {
|
||||
CLEAR_PENDING_EXCEPTION;
|
||||
return false;
|
||||
}
|
||||
|
||||
Symbol* error = PENDING_EXCEPTION->klass()->name();
|
||||
Symbol* message = java_lang_Throwable::detail_message(PENDING_EXCEPTION);
|
||||
assert("message != NULL", "Missing detail message");
|
||||
|
||||
SystemDictionary::add_resolution_error(cpool, index, error, message);
|
||||
set_indy_resolution_failed();
|
||||
return true;
|
||||
}
|
||||
|
||||
Method* ConstantPoolCacheEntry::method_if_resolved(const constantPoolHandle& cpool) {
|
||||
// Decode the action of set_method and set_interface_call
|
||||
Bytecodes::Code invoke_code = bytecode_1();
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
#include "oops/oopHandle.hpp"
|
||||
#include "runtime/orderAccess.hpp"
|
||||
#include "utilities/align.hpp"
|
||||
#include "utilities/constantTag.hpp"
|
||||
|
||||
class PSPromotionManager;
|
||||
|
||||
@ -50,8 +51,8 @@ class PSPromotionManager;
|
||||
// _f1 [ entry specific ] metadata ptr (method or klass)
|
||||
// _f2 [ entry specific ] vtable or res_ref index, or vfinal method ptr
|
||||
// _flags [tos|0|F=1|0|0|0|f|v|0 |0000|field_index] (for field entries)
|
||||
// bit length [ 4 |1| 1 |1|1|1|1|1|1 |-4--|----16-----]
|
||||
// _flags [tos|0|F=0|M|A|I|f|0|vf|0000|00000|psize] (for method entries)
|
||||
// bit length [ 4 |1| 1 |1|1|1|1|1|1 |1 |-3-|----16-----]
|
||||
// _flags [tos|0|F=0|M|A|I|f|0|vf|indy_rf|000|00000|psize] (for method entries)
|
||||
// bit length [ 4 |1| 1 |1|1|1|1|1|1 |-4--|--8--|--8--]
|
||||
|
||||
// --------------------------------
|
||||
@ -71,6 +72,7 @@ class PSPromotionManager;
|
||||
// f = field or method is final
|
||||
// v = field is volatile
|
||||
// vf = virtual but final (method entries only: is_vfinal())
|
||||
// indy_rf = call site specifier method resolution failed
|
||||
//
|
||||
// The flags after TosState have the following interpretation:
|
||||
// bit 27: 0 for fields, 1 for methods
|
||||
@ -185,6 +187,7 @@ class ConstantPoolCacheEntry VALUE_OBJ_CLASS_SPEC {
|
||||
is_final_shift = 22, // (f) is the field or method final?
|
||||
is_volatile_shift = 21, // (v) is the field volatile?
|
||||
is_vfinal_shift = 20, // (vf) did the call resolve to a final method?
|
||||
indy_resolution_failed_shift= 19, // (indy_rf) did call site specifier resolution fail ?
|
||||
// low order bits give field index (for FieldInfo) or method parameter size:
|
||||
field_index_bits = 16,
|
||||
field_index_mask = right_n_bits(field_index_bits),
|
||||
@ -281,6 +284,13 @@ class ConstantPoolCacheEntry VALUE_OBJ_CLASS_SPEC {
|
||||
const CallInfo &call_info // Call link information
|
||||
);
|
||||
|
||||
// Return TRUE if resolution failed and this thread got to record the failure
|
||||
// status. Return FALSE if another thread succeeded or failed in resolving
|
||||
// the method and recorded the success or failure before this thread had a
|
||||
// chance to record its failure.
|
||||
bool save_and_throw_indy_exc(const constantPoolHandle& cpool, int cpool_index,
|
||||
int index, constantTag tag, TRAPS);
|
||||
|
||||
// invokedynamic and invokehandle call sites have two entries in the
|
||||
// resolved references array:
|
||||
// appendix (at index+0)
|
||||
@ -342,12 +352,14 @@ class ConstantPoolCacheEntry VALUE_OBJ_CLASS_SPEC {
|
||||
bool is_f1_null() const { Metadata* f1 = f1_ord(); return f1 == NULL; } // classifies a CPC entry as unbound
|
||||
int f2_as_index() const { assert(!is_vfinal(), ""); return (int) _f2; }
|
||||
Method* f2_as_vfinal_method() const { assert(is_vfinal(), ""); return (Method*)_f2; }
|
||||
intx flags_ord() const { return (intx)OrderAccess::load_acquire(&_flags); }
|
||||
int field_index() const { assert(is_field_entry(), ""); return (_flags & field_index_mask); }
|
||||
int parameter_size() const { assert(is_method_entry(), ""); return (_flags & parameter_size_mask); }
|
||||
bool is_volatile() const { return (_flags & (1 << is_volatile_shift)) != 0; }
|
||||
bool is_final() const { return (_flags & (1 << is_final_shift)) != 0; }
|
||||
bool is_forced_virtual() const { return (_flags & (1 << is_forced_virtual_shift)) != 0; }
|
||||
bool is_vfinal() const { return (_flags & (1 << is_vfinal_shift)) != 0; }
|
||||
bool indy_resolution_failed() const { intx flags = flags_ord(); return (flags & (1 << indy_resolution_failed_shift)) != 0; }
|
||||
bool has_appendix() const { return (!is_f1_null()) && (_flags & (1 << has_appendix_shift)) != 0; }
|
||||
bool has_method_type() const { return (!is_f1_null()) && (_flags & (1 << has_method_type_shift)) != 0; }
|
||||
bool is_method_entry() const { return (_flags & (1 << is_field_entry_shift)) == 0; }
|
||||
@ -356,6 +368,7 @@ class ConstantPoolCacheEntry VALUE_OBJ_CLASS_SPEC {
|
||||
bool is_double() const { return flag_state() == dtos; }
|
||||
TosState flag_state() const { assert((uint)number_of_states <= (uint)tos_state_mask+1, "");
|
||||
return (TosState)((_flags >> tos_state_shift) & tos_state_mask); }
|
||||
void set_indy_resolution_failed();
|
||||
|
||||
// Code generation support
|
||||
static WordSize size() {
|
||||
|
||||
198
test/hotspot/jtreg/runtime/BootstrapMethod/BSMCalledTwice.java
Normal file
198
test/hotspot/jtreg/runtime/BootstrapMethod/BSMCalledTwice.java
Normal file
@ -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, "<init>", "()V", null, null);
|
||||
mv.visitCode();
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()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();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,167 @@
|
||||
/*
|
||||
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 Test that invokedynamic instructions, that initially throw IAE exceptions
|
||||
* because of a missing module read edge, behave correctly when executed
|
||||
* after the module read edge is added.
|
||||
* @compile ModuleLibrary.java
|
||||
* p2/c2.java
|
||||
* p5/c5.java
|
||||
* p7/c7.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
|
||||
// defines third_mod --> packages p7
|
||||
|
||||
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: 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();
|
||||
|
||||
// Define module: third_mod
|
||||
// Can read: java.base
|
||||
// Packages: p7
|
||||
// Packages exported: p7 is exported unqualifiedly
|
||||
ModuleDescriptor descriptor_third_mod =
|
||||
ModuleDescriptor.newModule("third_mod")
|
||||
.requires("java.base")
|
||||
.exports("p7")
|
||||
.build();
|
||||
|
||||
// Set up a ModuleFinder containing all modules for this layer
|
||||
ModuleFinder finder = ModuleLibrary.of(descriptor_first_mod,
|
||||
descriptor_second_mod,
|
||||
descriptor_third_mod);
|
||||
|
||||
// Resolves "first_mod", "second_mod", and "third_mod"
|
||||
Configuration cf = ModuleLayer.boot()
|
||||
.configuration()
|
||||
.resolve(finder, ModuleFinder.of(),
|
||||
Set.of("first_mod", "second_mod", "third_mod"));
|
||||
|
||||
// Map each module to this class loader
|
||||
Map<String, ClassLoader> map = new HashMap<>();
|
||||
ClassLoader loader = MethodAccessReadTwice.class.getClassLoader();
|
||||
map.put("first_mod", loader);
|
||||
map.put("second_mod", loader);
|
||||
map.put("third_mod", loader);
|
||||
|
||||
// Create Layer that contains first_mod, second_mod, and third_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");
|
||||
Class p7_c7_class = loader.loadClass("p7.c7");
|
||||
|
||||
Module first_mod = p5_c5_class.getModule();
|
||||
Module second_mod = p2_c2_class.getModule();
|
||||
Module third_mod = p7_c7_class.getModule();
|
||||
|
||||
p5.c5 c5_obj = new p5.c5();
|
||||
p2.c2 c2_obj = new p2.c2();
|
||||
p7.c7 c7_obj = new p7.c7();
|
||||
|
||||
// Test that if an invokedynamic instruction gets an IAE exception because
|
||||
// of a module read issue, and then the read issue is fixed, that
|
||||
// re-executing the same invokedynamic instruction will get the same IAE.
|
||||
|
||||
// First access check for p5.c5 --> call to method5 --> tries to access p2.c2
|
||||
try {
|
||||
// Should throw IAE because p5.c5's module cannot read p2.c2's module.
|
||||
c5_obj.method5(c2_obj);
|
||||
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 p2.c2's module (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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Test that if one invokedynamic instruction gets an IAE exception
|
||||
// because of a module read issue, and then the read issue is fixed, that
|
||||
// a subsequent invokedynamic instruction, that tries the same access,
|
||||
// succeeds.
|
||||
c7_obj.method7(c2_obj, second_mod); // Should not result in IAE
|
||||
}
|
||||
|
||||
public static void main(String args[]) throws Throwable {
|
||||
MethodAccessReadTwice test = new MethodAccessReadTwice();
|
||||
test.createLayerOnBoot();
|
||||
}
|
||||
}
|
||||
41
test/hotspot/jtreg/runtime/modules/AccessCheck/p5/c5.java
Normal file
41
test/hotspot/jtreg/runtime/modules/AccessCheck/p5/c5.java
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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) {
|
||||
// The invokedynamic opcode that gets generated for the '+' string
|
||||
// concatenation operator throws an IllegalAccessError when trying to
|
||||
// access '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);
|
||||
}
|
||||
}
|
||||
57
test/hotspot/jtreg/runtime/modules/AccessCheck/p7/c7.java
Normal file
57
test/hotspot/jtreg/runtime/modules/AccessCheck/p7/c7.java
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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 p7;
|
||||
|
||||
import java.lang.Module;
|
||||
import p2.c2;
|
||||
|
||||
public class c7 {
|
||||
public void method7(p2.c2 param, Module c2Mod) {
|
||||
try {
|
||||
// The invokedynamic opcode that gets generated for the '+' string
|
||||
// concatenation operator throws an IllegalAccessError when trying
|
||||
// to access 'param'.
|
||||
System.out.println("In c7's method7 with param = " + param);
|
||||
throw new java.lang.RuntimeException("c7 failed to throw expected IllegalAccessError");
|
||||
} catch (IllegalAccessError e) {
|
||||
}
|
||||
methodAddReadEdge(c2Mod);
|
||||
|
||||
// This invokedynamic (for the string concat) should succeed because of
|
||||
// the added read edge. The fact that the invokedynamic executed before
|
||||
// the read edge was added threw an IllegalAccessError exception should
|
||||
// not affect this one.
|
||||
try {
|
||||
System.out.println("In c7's method7 with param = " + param);
|
||||
} catch (IllegalAccessError e) {
|
||||
throw new java.lang.RuntimeException("Unexpected IllegalAccessError: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void methodAddReadEdge(Module m) {
|
||||
// Add a read edge from p7/c7's module (third_mod) to module m.
|
||||
c7.class.getModule().addReads(m);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user