8244090: public lookup should find public members of public exported types
Reviewed-by: lfoltan, psandoz
This commit is contained in:
parent
49a9d49dbd
commit
4e6a4af186
@ -761,7 +761,7 @@ Method* ciEnv::lookup_method(ciInstanceKlass* accessor,
|
||||
InstanceKlass* accessor_klass = accessor->get_instanceKlass();
|
||||
Klass* holder_klass = holder->get_Klass();
|
||||
Method* dest_method;
|
||||
LinkInfo link_info(holder_klass, name, sig, accessor_klass, LinkInfo::AccessCheck::required, tag);
|
||||
LinkInfo link_info(holder_klass, name, sig, accessor_klass, LinkInfo::AccessCheck::required, LinkInfo::LoaderConstraintCheck::required, tag);
|
||||
switch (bc) {
|
||||
case Bytecodes::_invokestatic:
|
||||
dest_method =
|
||||
|
@ -809,7 +809,8 @@ ciMethod* ciMethod::resolve_invoke(ciKlass* caller, ciKlass* exact_receiver, boo
|
||||
Symbol* h_signature = signature()->get_symbol();
|
||||
|
||||
LinkInfo link_info(resolved, h_name, h_signature, caller_klass,
|
||||
check_access ? LinkInfo::AccessCheck::required : LinkInfo::AccessCheck::skip);
|
||||
check_access ? LinkInfo::AccessCheck::required : LinkInfo::AccessCheck::skip,
|
||||
check_access ? LinkInfo::LoaderConstraintCheck::required : LinkInfo::LoaderConstraintCheck::skip);
|
||||
Method* m = NULL;
|
||||
// Only do exact lookup if receiver klass has been linked. Otherwise,
|
||||
// the vtable has not been setup, and the LinkResolver will fail.
|
||||
|
@ -1134,7 +1134,11 @@ class java_lang_invoke_MemberName: AllStatic {
|
||||
MN_NESTMATE_CLASS = 0x00000001,
|
||||
MN_HIDDEN_CLASS = 0x00000002,
|
||||
MN_STRONG_LOADER_LINK = 0x00000004,
|
||||
MN_ACCESS_VM_ANNOTATIONS = 0x00000008
|
||||
MN_ACCESS_VM_ANNOTATIONS = 0x00000008,
|
||||
// Lookup modes
|
||||
MN_MODULE_MODE = 0x00000010,
|
||||
MN_UNCONDITIONAL_MODE = 0x00000020,
|
||||
MN_TRUSTED_MODE = -1
|
||||
};
|
||||
|
||||
// Accessors for code generation:
|
||||
|
@ -2830,7 +2830,7 @@ Handle SystemDictionary::link_method_handle_constant(Klass* caller,
|
||||
// There's special logic on JDK side to handle them
|
||||
// (see MethodHandles.linkMethodHandleConstant() and MethodHandles.findVirtualForMH()).
|
||||
} else {
|
||||
MethodHandles::resolve_MemberName(mname, caller, /*speculative_resolve*/false, CHECK_(empty));
|
||||
MethodHandles::resolve_MemberName(mname, caller, 0, false /*speculative_resolve*/, CHECK_(empty));
|
||||
}
|
||||
|
||||
// After method/field resolution succeeded, it's safe to resolve MH signature as well.
|
||||
|
@ -241,6 +241,7 @@ LinkInfo::LinkInfo(const constantPoolHandle& pool, int index, const methodHandle
|
||||
|
||||
// Coming from the constant pool always checks access
|
||||
_check_access = true;
|
||||
_check_loader_constraints = true;
|
||||
}
|
||||
|
||||
LinkInfo::LinkInfo(const constantPoolHandle& pool, int index, TRAPS) {
|
||||
@ -256,17 +257,20 @@ LinkInfo::LinkInfo(const constantPoolHandle& pool, int index, TRAPS) {
|
||||
|
||||
// Coming from the constant pool always checks access
|
||||
_check_access = true;
|
||||
_check_loader_constraints = true;
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
void LinkInfo::print() {
|
||||
ResourceMark rm;
|
||||
tty->print_cr("Link resolved_klass=%s name=%s signature=%s current_klass=%s check_access=%s",
|
||||
tty->print_cr("Link resolved_klass=%s name=%s signature=%s current_klass=%s check_access=%s check_loader_constraints=%s",
|
||||
_resolved_klass->name()->as_C_string(),
|
||||
_name->as_C_string(),
|
||||
_signature->as_C_string(),
|
||||
_current_klass == NULL ? "(none)" : _current_klass->name()->as_C_string(),
|
||||
_check_access ? "true" : "false");
|
||||
_check_access ? "true" : "false",
|
||||
_check_loader_constraints ? "true" : "false");
|
||||
|
||||
}
|
||||
#endif // PRODUCT
|
||||
//------------------------------------------------------------------------------------------------------------------------
|
||||
@ -795,7 +799,8 @@ Method* LinkResolver::resolve_method(const LinkInfo& link_info,
|
||||
resolved_method->method_holder(),
|
||||
resolved_method,
|
||||
CHECK_NULL);
|
||||
|
||||
}
|
||||
if (link_info.check_loader_constraints()) {
|
||||
// check loader constraints
|
||||
check_method_loader_constraints(link_info, resolved_method, "method", CHECK_NULL);
|
||||
}
|
||||
@ -891,7 +896,8 @@ Method* LinkResolver::resolve_interface_method(const LinkInfo& link_info, Byteco
|
||||
resolved_method->method_holder(),
|
||||
resolved_method,
|
||||
CHECK_NULL);
|
||||
|
||||
}
|
||||
if (link_info.check_loader_constraints()) {
|
||||
check_method_loader_constraints(link_info, resolved_method, "interface method", CHECK_NULL);
|
||||
}
|
||||
|
||||
@ -1055,7 +1061,7 @@ void LinkResolver::resolve_field(fieldDescriptor& fd,
|
||||
}
|
||||
}
|
||||
|
||||
if ((sel_klass != current_klass) && (current_klass != NULL)) {
|
||||
if (link_info.check_loader_constraints() && (sel_klass != current_klass) && (current_klass != NULL)) {
|
||||
check_field_loader_constraints(field, sig, current_klass, sel_klass, CHECK);
|
||||
}
|
||||
|
||||
@ -1089,7 +1095,8 @@ void LinkResolver::resolve_static_call(CallInfo& result,
|
||||
// Use updated LinkInfo to reresolve with resolved method holder
|
||||
LinkInfo new_info(resolved_klass, link_info.name(), link_info.signature(),
|
||||
link_info.current_klass(),
|
||||
link_info.check_access() ? LinkInfo::AccessCheck::required : LinkInfo::AccessCheck::skip);
|
||||
link_info.check_access() ? LinkInfo::AccessCheck::required : LinkInfo::AccessCheck::skip,
|
||||
link_info.check_loader_constraints() ? LinkInfo::LoaderConstraintCheck::required : LinkInfo::LoaderConstraintCheck::skip);
|
||||
resolved_method = linktime_resolve_static_method(new_info, CHECK);
|
||||
}
|
||||
|
||||
@ -1250,7 +1257,7 @@ void LinkResolver::runtime_resolve_special_method(CallInfo& result,
|
||||
ss.print("'");
|
||||
THROW_MSG(vmSymbols::java_lang_AbstractMethodError(), ss.as_string());
|
||||
// check loader constraints if found a different method
|
||||
} else if (sel_method() != resolved_method()) {
|
||||
} else if (link_info.check_loader_constraints() && sel_method() != resolved_method()) {
|
||||
check_method_loader_constraints(link_info, sel_method, "method", CHECK);
|
||||
}
|
||||
}
|
||||
|
@ -141,10 +141,12 @@ class LinkInfo : public StackObj {
|
||||
Klass* _current_klass; // class that owns the constant pool
|
||||
methodHandle _current_method; // sending method
|
||||
bool _check_access;
|
||||
bool _check_loader_constraints;
|
||||
constantTag _tag;
|
||||
|
||||
public:
|
||||
enum class AccessCheck { required, skip };
|
||||
enum class LoaderConstraintCheck { required, skip };
|
||||
|
||||
LinkInfo(const constantPoolHandle& pool, int index, const methodHandle& current_method, TRAPS);
|
||||
LinkInfo(const constantPoolHandle& pool, int index, TRAPS);
|
||||
@ -152,33 +154,38 @@ class LinkInfo : public StackObj {
|
||||
// Condensed information from other call sites within the vm.
|
||||
LinkInfo(Klass* resolved_klass, Symbol* name, Symbol* signature, Klass* current_klass,
|
||||
AccessCheck check_access = AccessCheck::required,
|
||||
LoaderConstraintCheck check_loader_constraints = LoaderConstraintCheck::required,
|
||||
constantTag tag = JVM_CONSTANT_Invalid) :
|
||||
_name(name),
|
||||
_signature(signature), _resolved_klass(resolved_klass), _current_klass(current_klass), _current_method(methodHandle()),
|
||||
_check_access(check_access == AccessCheck::required), _tag(tag) {}
|
||||
_check_access(check_access == AccessCheck::required),
|
||||
_check_loader_constraints(check_loader_constraints == LoaderConstraintCheck::required), _tag(tag) {}
|
||||
|
||||
LinkInfo(Klass* resolved_klass, Symbol* name, Symbol* signature, const methodHandle& current_method,
|
||||
AccessCheck check_access = AccessCheck::required,
|
||||
LoaderConstraintCheck check_loader_constraints = LoaderConstraintCheck::required,
|
||||
constantTag tag = JVM_CONSTANT_Invalid) :
|
||||
_name(name),
|
||||
_signature(signature), _resolved_klass(resolved_klass), _current_klass(current_method->method_holder()), _current_method(current_method),
|
||||
_check_access(check_access == AccessCheck::required), _tag(tag) {}
|
||||
_check_access(check_access == AccessCheck::required),
|
||||
_check_loader_constraints(check_loader_constraints == LoaderConstraintCheck::required), _tag(tag) {}
|
||||
|
||||
|
||||
// Case where we just find the method and don't check access against the current class
|
||||
LinkInfo(Klass* resolved_klass, Symbol*name, Symbol* signature) :
|
||||
_name(name),
|
||||
_signature(signature), _resolved_klass(resolved_klass), _current_klass(NULL), _current_method(methodHandle()),
|
||||
_check_access(false), _tag(JVM_CONSTANT_Invalid) {}
|
||||
_check_access(false), _check_loader_constraints(false), _tag(JVM_CONSTANT_Invalid) {}
|
||||
|
||||
// accessors
|
||||
Symbol* name() const { return _name; }
|
||||
Symbol* signature() const { return _signature; }
|
||||
Klass* resolved_klass() const { return _resolved_klass; }
|
||||
Klass* current_klass() const { return _current_klass; }
|
||||
Method* current_method() const { return _current_method(); }
|
||||
constantTag tag() const { return _tag; }
|
||||
bool check_access() const { return _check_access; }
|
||||
|
||||
Symbol* name() const { return _name; }
|
||||
Symbol* signature() const { return _signature; }
|
||||
Klass* resolved_klass() const { return _resolved_klass; }
|
||||
Klass* current_klass() const { return _current_klass; }
|
||||
Method* current_method() const { return _current_method(); }
|
||||
constantTag tag() const { return _tag; }
|
||||
bool check_access() const { return _check_access; }
|
||||
bool check_loader_constraints() const { return _check_loader_constraints; }
|
||||
void print() PRODUCT_RETURN;
|
||||
};
|
||||
|
||||
|
@ -90,7 +90,7 @@ void HotSpotJVMCI::compute_offset(int &dest_offset, Klass* klass, const char* na
|
||||
#ifndef PRODUCT
|
||||
static void check_resolve_method(const char* call_type, Klass* resolved_klass, Symbol* method_name, Symbol* method_signature, TRAPS) {
|
||||
Method* method;
|
||||
LinkInfo link_info(resolved_klass, method_name, method_signature, NULL, LinkInfo::AccessCheck::skip);
|
||||
LinkInfo link_info(resolved_klass, method_name, method_signature, NULL, LinkInfo::AccessCheck::skip, LinkInfo::LoaderConstraintCheck::skip);
|
||||
if (strcmp(call_type, "call_static") == 0) {
|
||||
method = LinkResolver::resolve_static_call_or_null(link_info);
|
||||
} else if (strcmp(call_type, "call_virtual") == 0) {
|
||||
|
@ -1327,7 +1327,7 @@ Method* JVMCIRuntime::lookup_method(InstanceKlass* accessor,
|
||||
assert(check_klass_accessibility(accessor, holder), "holder not accessible");
|
||||
|
||||
Method* dest_method;
|
||||
LinkInfo link_info(holder, name, sig, accessor, LinkInfo::AccessCheck::required, tag);
|
||||
LinkInfo link_info(holder, name, sig, accessor, LinkInfo::AccessCheck::required, LinkInfo::LoaderConstraintCheck::required, tag);
|
||||
switch (bc) {
|
||||
case Bytecodes::_invokestatic:
|
||||
dest_method =
|
||||
|
@ -132,6 +132,9 @@ enum {
|
||||
REFERENCE_KIND_MASK = java_lang_invoke_MemberName::MN_REFERENCE_KIND_MASK,
|
||||
SEARCH_SUPERCLASSES = java_lang_invoke_MemberName::MN_SEARCH_SUPERCLASSES,
|
||||
SEARCH_INTERFACES = java_lang_invoke_MemberName::MN_SEARCH_INTERFACES,
|
||||
LM_UNCONDITIONAL = java_lang_invoke_MemberName::MN_UNCONDITIONAL_MODE,
|
||||
LM_MODULE = java_lang_invoke_MemberName::MN_MODULE_MODE,
|
||||
LM_TRUSTED = java_lang_invoke_MemberName::MN_TRUSTED_MODE,
|
||||
ALL_KINDS = IS_METHOD | IS_CONSTRUCTOR | IS_FIELD | IS_TYPE
|
||||
};
|
||||
|
||||
@ -672,11 +675,10 @@ oop MethodHandles::field_signature_type_or_null(Symbol* s) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// An unresolved member name is a mere symbolic reference.
|
||||
// Resolving it plants a vmtarget/vmindex in it,
|
||||
// which refers directly to JVM internals.
|
||||
Handle MethodHandles::resolve_MemberName(Handle mname, Klass* caller,
|
||||
Handle MethodHandles::resolve_MemberName(Handle mname, Klass* caller, int lookup_mode,
|
||||
bool speculative_resolve, TRAPS) {
|
||||
Handle empty;
|
||||
assert(java_lang_invoke_MemberName::is_instance(mname()), "");
|
||||
@ -745,16 +747,21 @@ Handle MethodHandles::resolve_MemberName(Handle mname, Klass* caller,
|
||||
TempNewSymbol type = lookup_signature(type_str(), (mh_invoke_id != vmIntrinsics::_none), CHECK_(empty));
|
||||
if (type == NULL) return empty; // no such signature exists in the VM
|
||||
|
||||
// skip access check if it's trusted lookup
|
||||
LinkInfo::AccessCheck access_check = caller != NULL ?
|
||||
LinkInfo::AccessCheck::required :
|
||||
LinkInfo::AccessCheck::skip;
|
||||
// skip loader constraints if it's trusted lookup or a public lookup
|
||||
LinkInfo::LoaderConstraintCheck loader_constraint_check = (caller != NULL && (lookup_mode & LM_UNCONDITIONAL) == 0) ?
|
||||
LinkInfo::LoaderConstraintCheck::required :
|
||||
LinkInfo::LoaderConstraintCheck::skip;
|
||||
|
||||
// Time to do the lookup.
|
||||
switch (flags & ALL_KINDS) {
|
||||
case IS_METHOD:
|
||||
{
|
||||
CallInfo result;
|
||||
LinkInfo link_info(defc, name, type, caller, access_check);
|
||||
LinkInfo link_info(defc, name, type, caller, access_check, loader_constraint_check);
|
||||
{
|
||||
assert(!HAS_PENDING_EXCEPTION, "");
|
||||
if (ref_kind == JVM_REF_invokeStatic) {
|
||||
@ -795,7 +802,7 @@ Handle MethodHandles::resolve_MemberName(Handle mname, Klass* caller,
|
||||
case IS_CONSTRUCTOR:
|
||||
{
|
||||
CallInfo result;
|
||||
LinkInfo link_info(defc, name, type, caller, access_check);
|
||||
LinkInfo link_info(defc, name, type, caller, access_check, loader_constraint_check);
|
||||
{
|
||||
assert(!HAS_PENDING_EXCEPTION, "");
|
||||
if (name == vmSymbols::object_initializer_name()) {
|
||||
@ -820,7 +827,7 @@ Handle MethodHandles::resolve_MemberName(Handle mname, Klass* caller,
|
||||
fieldDescriptor result; // find_field initializes fd if found
|
||||
{
|
||||
assert(!HAS_PENDING_EXCEPTION, "");
|
||||
LinkInfo link_info(defc, name, type, caller, LinkInfo::AccessCheck::skip);
|
||||
LinkInfo link_info(defc, name, type, caller, LinkInfo::AccessCheck::skip, loader_constraint_check);
|
||||
LinkResolver::resolve_field(result, link_info, Bytecodes::_nop, false, THREAD);
|
||||
if (HAS_PENDING_EXCEPTION) {
|
||||
if (speculative_resolve) {
|
||||
@ -1117,6 +1124,9 @@ void MethodHandles::trace_method_handle_interpreter_entry(MacroAssembler* _masm,
|
||||
template(java_lang_invoke_MemberName,MN_HIDDEN_CLASS) \
|
||||
template(java_lang_invoke_MemberName,MN_STRONG_LOADER_LINK) \
|
||||
template(java_lang_invoke_MemberName,MN_ACCESS_VM_ANNOTATIONS) \
|
||||
template(java_lang_invoke_MemberName,MN_MODULE_MODE) \
|
||||
template(java_lang_invoke_MemberName,MN_UNCONDITIONAL_MODE) \
|
||||
template(java_lang_invoke_MemberName,MN_TRUSTED_MODE) \
|
||||
/*end*/
|
||||
|
||||
#define IGNORE_REQ(req_expr) /* req_expr */
|
||||
@ -1190,13 +1200,17 @@ JVM_END
|
||||
|
||||
// void resolve(MemberName self, Class<?> caller)
|
||||
JVM_ENTRY(jobject, MHN_resolve_Mem(JNIEnv *env, jobject igcls, jobject mname_jh, jclass caller_jh,
|
||||
jboolean speculative_resolve)) {
|
||||
jint lookup_mode, jboolean speculative_resolve)) {
|
||||
if (mname_jh == NULL) { THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), "mname is null"); }
|
||||
Handle mname(THREAD, JNIHandles::resolve_non_null(mname_jh));
|
||||
|
||||
// The trusted Java code that calls this method should already have performed
|
||||
// access checks on behalf of the given caller. But, we can verify this.
|
||||
if (VerifyMethodHandles && caller_jh != NULL &&
|
||||
// This only verifies from the context of the lookup class. It does not
|
||||
// verify the lookup context for a Lookup object teleported from one module
|
||||
// to another. Such Lookup object can only access the intersection of the set
|
||||
// of accessible classes from both lookup class and previous lookup class.
|
||||
if (VerifyMethodHandles && (lookup_mode & LM_TRUSTED) == LM_TRUSTED && caller_jh != NULL &&
|
||||
java_lang_invoke_MemberName::clazz(mname()) != NULL) {
|
||||
Klass* reference_klass = java_lang_Class::as_Klass(java_lang_invoke_MemberName::clazz(mname()));
|
||||
if (reference_klass != NULL && reference_klass->is_objArray_klass()) {
|
||||
@ -1207,18 +1221,25 @@ JVM_ENTRY(jobject, MHN_resolve_Mem(JNIEnv *env, jobject igcls, jobject mname_jh,
|
||||
if (reference_klass != NULL && reference_klass->is_instance_klass()) {
|
||||
// Emulate LinkResolver::check_klass_accessability.
|
||||
Klass* caller = java_lang_Class::as_Klass(JNIHandles::resolve_non_null(caller_jh));
|
||||
if (caller != SystemDictionary::Object_klass()
|
||||
// access check on behalf of the caller if this is not a public lookup
|
||||
// i.e. lookup mode is not UNCONDITIONAL
|
||||
if ((lookup_mode & LM_UNCONDITIONAL) == 0
|
||||
&& Reflection::verify_class_access(caller,
|
||||
InstanceKlass::cast(reference_klass),
|
||||
true) != Reflection::ACCESS_OK) {
|
||||
THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), reference_klass->external_name());
|
||||
ResourceMark rm(THREAD);
|
||||
stringStream ss;
|
||||
ss.print("caller %s tried to access %s", caller->class_in_module_of_loader(),
|
||||
reference_klass->class_in_module_of_loader());
|
||||
THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), ss.as_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Klass* caller = caller_jh == NULL ? NULL :
|
||||
java_lang_Class::as_Klass(JNIHandles::resolve_non_null(caller_jh));
|
||||
Handle resolved = MethodHandles::resolve_MemberName(mname, caller, speculative_resolve == JNI_TRUE,
|
||||
Handle resolved = MethodHandles::resolve_MemberName(mname, caller, lookup_mode,
|
||||
speculative_resolve == JNI_TRUE,
|
||||
CHECK_NULL);
|
||||
|
||||
if (resolved.is_null()) {
|
||||
@ -1518,7 +1539,7 @@ JVM_END
|
||||
static JNINativeMethod MHN_methods[] = {
|
||||
{CC "init", CC "(" MEM "" OBJ ")V", FN_PTR(MHN_init_Mem)},
|
||||
{CC "expand", CC "(" MEM ")V", FN_PTR(MHN_expand_Mem)},
|
||||
{CC "resolve", CC "(" MEM "" CLS "Z)" MEM, FN_PTR(MHN_resolve_Mem)},
|
||||
{CC "resolve", CC "(" MEM "" CLS "IZ)" MEM, FN_PTR(MHN_resolve_Mem)},
|
||||
// static native int getNamedCon(int which, Object[] name)
|
||||
{CC "getNamedCon", CC "(I[" OBJ ")I", FN_PTR(MHN_getNamedCon)},
|
||||
// static native int getMembers(Class<?> defc, String matchName, String matchSig,
|
||||
|
@ -60,7 +60,7 @@ class MethodHandles: AllStatic {
|
||||
|
||||
public:
|
||||
// working with member names
|
||||
static Handle resolve_MemberName(Handle mname, Klass* caller,
|
||||
static Handle resolve_MemberName(Handle mname, Klass* caller, int lookup_mode,
|
||||
bool speculative_resolve, TRAPS); // compute vmtarget/vmindex from name/type
|
||||
static void expand_MemberName(Handle mname, int suppress, TRAPS); // expand defc/name/type if missing
|
||||
static oop init_MemberName(Handle mname_h, Handle target_h, TRAPS); // compute vmtarget/vmindex from target
|
||||
|
@ -28,7 +28,7 @@ package java.lang.invoke;
|
||||
import java.util.Arrays;
|
||||
import static java.lang.invoke.LambdaForm.*;
|
||||
import static java.lang.invoke.LambdaForm.Kind.*;
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeVirtual;
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
|
||||
/**
|
||||
@ -177,7 +177,7 @@ abstract class DelegatingMethodHandle extends MethodHandle {
|
||||
MethodType.methodType(MethodHandle.class), REF_invokeVirtual);
|
||||
NF_getTarget = new NamedFunction(
|
||||
MemberName.getFactory()
|
||||
.resolveOrFail(REF_invokeVirtual, member, DelegatingMethodHandle.class, NoSuchMethodException.class));
|
||||
.resolveOrFail(REF_invokeVirtual, member, DelegatingMethodHandle.class, LM_TRUSTED, NoSuchMethodException.class));
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw newInternalError(ex);
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ class DirectMethodHandle extends MethodHandle {
|
||||
member.isMethod() && !member.isAbstract()) {
|
||||
// Check for corner case: invokeinterface of Object method
|
||||
MemberName m = new MemberName(Object.class, member.getName(), member.getMethodType(), member.getReferenceKind());
|
||||
m = MemberName.getFactory().resolveOrNull(m.getReferenceKind(), m, null);
|
||||
m = MemberName.getFactory().resolveOrNull(m.getReferenceKind(), m, null, LM_TRUSTED);
|
||||
if (m != null && m.isPublic()) {
|
||||
assert(member.getReferenceKind() == m.getReferenceKind()); // else this.form is wrong
|
||||
member = m;
|
||||
@ -260,7 +260,8 @@ class DirectMethodHandle extends MethodHandle {
|
||||
.changeReturnType(void.class); // <init> returns void
|
||||
MemberName linker = new MemberName(MethodHandle.class, linkerName, mtypeWithArg, REF_invokeStatic);
|
||||
try {
|
||||
linker = IMPL_NAMES.resolveOrFail(REF_invokeStatic, linker, null, NoSuchMethodException.class);
|
||||
linker = IMPL_NAMES.resolveOrFail(REF_invokeStatic, linker, null, LM_TRUSTED,
|
||||
NoSuchMethodException.class);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw newInternalError(ex);
|
||||
}
|
||||
@ -771,7 +772,8 @@ class DirectMethodHandle extends MethodHandle {
|
||||
linkerType = MethodType.methodType(void.class, Object.class, long.class, ft);
|
||||
MemberName linker = new MemberName(Unsafe.class, kind.methodName, linkerType, REF_invokeVirtual);
|
||||
try {
|
||||
linker = IMPL_NAMES.resolveOrFail(REF_invokeVirtual, linker, null, NoSuchMethodException.class);
|
||||
linker = IMPL_NAMES.resolveOrFail(REF_invokeVirtual, linker, null, LM_TRUSTED,
|
||||
NoSuchMethodException.class);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw newInternalError(ex);
|
||||
}
|
||||
@ -914,13 +916,15 @@ class DirectMethodHandle extends MethodHandle {
|
||||
case NF_UNSAFE:
|
||||
MemberName member = new MemberName(MethodHandleStatics.class, "UNSAFE", Unsafe.class, REF_getField);
|
||||
return new NamedFunction(
|
||||
MemberName.getFactory()
|
||||
.resolveOrFail(REF_getField, member, DirectMethodHandle.class, NoSuchMethodException.class));
|
||||
MemberName.getFactory().resolveOrFail(REF_getField, member,
|
||||
DirectMethodHandle.class, LM_TRUSTED,
|
||||
NoSuchMethodException.class));
|
||||
case NF_checkReceiver:
|
||||
member = new MemberName(DirectMethodHandle.class, "checkReceiver", OBJ_OBJ_TYPE, REF_invokeVirtual);
|
||||
return new NamedFunction(
|
||||
MemberName.getFactory()
|
||||
.resolveOrFail(REF_invokeVirtual, member, DirectMethodHandle.class, NoSuchMethodException.class));
|
||||
MemberName.getFactory().resolveOrFail(REF_invokeVirtual, member,
|
||||
DirectMethodHandle.class, LM_TRUSTED,
|
||||
NoSuchMethodException.class));
|
||||
default:
|
||||
throw newInternalError("Unknown function: " + func);
|
||||
}
|
||||
@ -934,8 +938,9 @@ class DirectMethodHandle extends MethodHandle {
|
||||
{
|
||||
MemberName member = new MemberName(DirectMethodHandle.class, name, type, REF_invokeStatic);
|
||||
return new NamedFunction(
|
||||
MemberName.getFactory()
|
||||
.resolveOrFail(REF_invokeStatic, member, DirectMethodHandle.class, NoSuchMethodException.class));
|
||||
MemberName.getFactory().resolveOrFail(REF_invokeStatic, member,
|
||||
DirectMethodHandle.class, LM_TRUSTED,
|
||||
NoSuchMethodException.class));
|
||||
}
|
||||
|
||||
static {
|
||||
|
@ -322,7 +322,9 @@ class InvokerBytecodeGenerator {
|
||||
private static MemberName resolveInvokerMember(Class<?> invokerClass, String name, MethodType type) {
|
||||
MemberName member = new MemberName(invokerClass, name, type, REF_invokeStatic);
|
||||
try {
|
||||
member = MEMBERNAME_FACTORY.resolveOrFail(REF_invokeStatic, member, HOST_CLASS, ReflectiveOperationException.class);
|
||||
member = MEMBERNAME_FACTORY.resolveOrFail(REF_invokeStatic, member,
|
||||
HOST_CLASS, LM_TRUSTED,
|
||||
ReflectiveOperationException.class);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw newInternalError(e);
|
||||
}
|
||||
@ -693,7 +695,7 @@ class InvokerBytecodeGenerator {
|
||||
|
||||
private static MemberName resolveFrom(String name, MethodType type, Class<?> holder) {
|
||||
MemberName member = new MemberName(holder, name, type, REF_invokeStatic);
|
||||
MemberName resolvedMember = MemberName.getFactory().resolveOrNull(REF_invokeStatic, member, holder);
|
||||
MemberName resolvedMember = MemberName.getFactory().resolveOrNull(REF_invokeStatic, member, holder, LM_TRUSTED);
|
||||
if (TRACE_RESOLVE) {
|
||||
System.out.println("[LF_RESOLVE] " + holder.getName() + " " + name + " " +
|
||||
shortenSignature(basicTypeSignature(type)) + (resolvedMember != null ? " (success)" : " (fail)") );
|
||||
|
@ -661,7 +661,7 @@ class Invokers {
|
||||
MemberName member = new MemberName(Invokers.class, name, type, REF_invokeStatic);
|
||||
return new NamedFunction(
|
||||
MemberName.getFactory()
|
||||
.resolveOrFail(REF_invokeStatic, member, Invokers.class, NoSuchMethodException.class));
|
||||
.resolveOrFail(REF_invokeStatic, member, Invokers.class, LM_TRUSTED, NoSuchMethodException.class));
|
||||
}
|
||||
|
||||
private static class Lazy {
|
||||
|
@ -40,7 +40,7 @@ import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
|
||||
import static java.lang.invoke.LambdaForm.BasicType.*;
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic;
|
||||
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
||||
import static java.lang.invoke.MethodHandleStatics.*;
|
||||
|
||||
/**
|
||||
@ -1758,10 +1758,10 @@ class LambdaForm {
|
||||
MemberName idMem = new MemberName(LambdaForm.class, "identity_"+btChar, idType, REF_invokeStatic);
|
||||
MemberName zeMem = null;
|
||||
try {
|
||||
idMem = IMPL_NAMES.resolveOrFail(REF_invokeStatic, idMem, null, NoSuchMethodException.class);
|
||||
idMem = IMPL_NAMES.resolveOrFail(REF_invokeStatic, idMem, null, LM_TRUSTED, NoSuchMethodException.class);
|
||||
if (!isVoid) {
|
||||
zeMem = new MemberName(LambdaForm.class, "zero_"+btChar, zeType, REF_invokeStatic);
|
||||
zeMem = IMPL_NAMES.resolveOrFail(REF_invokeStatic, zeMem, null, NoSuchMethodException.class);
|
||||
zeMem = IMPL_NAMES.resolveOrFail(REF_invokeStatic, zeMem, null, LM_TRUSTED, NoSuchMethodException.class);
|
||||
}
|
||||
} catch (IllegalAccessException|NoSuchMethodException ex) {
|
||||
throw newInternalError(ex);
|
||||
|
@ -1064,7 +1064,7 @@ final class MemberName implements Member, Cloneable {
|
||||
* If lookup fails or access is not permitted, null is returned.
|
||||
* Otherwise a fresh copy of the given member is returned, with modifier bits filled in.
|
||||
*/
|
||||
private MemberName resolve(byte refKind, MemberName ref, Class<?> lookupClass,
|
||||
private MemberName resolve(byte refKind, MemberName ref, Class<?> lookupClass, int allowedModes,
|
||||
boolean speculativeResolve) {
|
||||
MemberName m = ref.clone(); // JVM will side-effect the ref
|
||||
assert(refKind == m.getReferenceKind());
|
||||
@ -1084,7 +1084,7 @@ final class MemberName implements Member, Cloneable {
|
||||
//
|
||||
// REFC view on PTYPES doesn't matter, since it is used only as a starting point for resolution and doesn't
|
||||
// participate in method selection.
|
||||
m = MethodHandleNatives.resolve(m, lookupClass, speculativeResolve);
|
||||
m = MethodHandleNatives.resolve(m, lookupClass, allowedModes, speculativeResolve);
|
||||
if (m == null && speculativeResolve) {
|
||||
return null;
|
||||
}
|
||||
@ -1108,10 +1108,12 @@ final class MemberName implements Member, Cloneable {
|
||||
* Otherwise a fresh copy of the given member is returned, with modifier bits filled in.
|
||||
*/
|
||||
public <NoSuchMemberException extends ReflectiveOperationException>
|
||||
MemberName resolveOrFail(byte refKind, MemberName m, Class<?> lookupClass,
|
||||
Class<NoSuchMemberException> nsmClass)
|
||||
MemberName resolveOrFail(byte refKind, MemberName m,
|
||||
Class<?> lookupClass, int allowedModes,
|
||||
Class<NoSuchMemberException> nsmClass)
|
||||
throws IllegalAccessException, NoSuchMemberException {
|
||||
MemberName result = resolve(refKind, m, lookupClass, false);
|
||||
assert lookupClass != null || allowedModes == LM_TRUSTED;
|
||||
MemberName result = resolve(refKind, m, lookupClass, allowedModes, false);
|
||||
if (result.isResolved())
|
||||
return result;
|
||||
ReflectiveOperationException ex = result.makeAccessException();
|
||||
@ -1124,8 +1126,9 @@ final class MemberName implements Member, Cloneable {
|
||||
* If lookup fails or access is not permitted, return null.
|
||||
* Otherwise a fresh copy of the given member is returned, with modifier bits filled in.
|
||||
*/
|
||||
public MemberName resolveOrNull(byte refKind, MemberName m, Class<?> lookupClass) {
|
||||
MemberName result = resolve(refKind, m, lookupClass, true);
|
||||
public MemberName resolveOrNull(byte refKind, MemberName m, Class<?> lookupClass, int allowedModes) {
|
||||
assert lookupClass != null || allowedModes == LM_TRUSTED;
|
||||
MemberName result = resolve(refKind, m, lookupClass, allowedModes, true);
|
||||
if (result != null && result.isResolved())
|
||||
return result;
|
||||
return null;
|
||||
|
@ -51,7 +51,7 @@ class MethodHandleNatives {
|
||||
|
||||
static native void init(MemberName self, Object ref);
|
||||
static native void expand(MemberName self);
|
||||
static native MemberName resolve(MemberName self, Class<?> caller,
|
||||
static native MemberName resolve(MemberName self, Class<?> caller, int lookupMode,
|
||||
boolean speculativeResolve) throws LinkageError, ClassNotFoundException;
|
||||
static native int getMembers(Class<?> defc, String matchName, String matchSig,
|
||||
int matchFlags, Class<?> caller, int skip, MemberName[] results);
|
||||
@ -149,6 +149,15 @@ class MethodHandleNatives {
|
||||
HIDDEN_CLASS = 0x00000002,
|
||||
STRONG_LOADER_LINK = 0x00000004,
|
||||
ACCESS_VM_ANNOTATIONS = 0x00000008;
|
||||
|
||||
/**
|
||||
* Lookup modes
|
||||
*/
|
||||
static final int
|
||||
LM_MODULE = Lookup.MODULE,
|
||||
LM_UNCONDITIONAL = Lookup.UNCONDITIONAL,
|
||||
LM_TRUSTED = -1;
|
||||
|
||||
}
|
||||
|
||||
static boolean refKindIsValid(int refKind) {
|
||||
@ -561,7 +570,7 @@ class MethodHandleNatives {
|
||||
guardType, REF_invokeStatic);
|
||||
|
||||
linker = MemberName.getFactory().resolveOrNull(REF_invokeStatic, linker,
|
||||
VarHandleGuards.class);
|
||||
VarHandleGuards.class, LM_TRUSTED);
|
||||
if (linker != null) {
|
||||
return linker;
|
||||
}
|
||||
|
@ -1409,14 +1409,7 @@ public class MethodHandles {
|
||||
|
||||
// This is just for calling out to MethodHandleImpl.
|
||||
private Class<?> lookupClassOrNull() {
|
||||
if (allowedModes == TRUSTED) {
|
||||
return null;
|
||||
}
|
||||
if (allowedModes == UNCONDITIONAL) {
|
||||
// use Object as the caller to pass to VM doing resolution
|
||||
return Object.class;
|
||||
}
|
||||
return lookupClass;
|
||||
return (allowedModes == TRUSTED) ? null : lookupClass;
|
||||
}
|
||||
|
||||
/** Tells which access-protection classes of members this lookup object can produce.
|
||||
@ -3442,7 +3435,7 @@ return mh1;
|
||||
checkSymbolicClass(refc); // do this before attempting to resolve
|
||||
Objects.requireNonNull(name);
|
||||
Objects.requireNonNull(type);
|
||||
return IMPL_NAMES.resolveOrFail(refKind, new MemberName(refc, name, type, refKind), lookupClassOrNull(),
|
||||
return IMPL_NAMES.resolveOrFail(refKind, new MemberName(refc, name, type, refKind), lookupClassOrNull(), allowedModes,
|
||||
NoSuchFieldException.class);
|
||||
}
|
||||
|
||||
@ -3451,7 +3444,7 @@ return mh1;
|
||||
Objects.requireNonNull(name);
|
||||
Objects.requireNonNull(type);
|
||||
checkMethodName(refKind, name); // NPE check on name
|
||||
return IMPL_NAMES.resolveOrFail(refKind, new MemberName(refc, name, type, refKind), lookupClassOrNull(),
|
||||
return IMPL_NAMES.resolveOrFail(refKind, new MemberName(refc, name, type, refKind), lookupClassOrNull(), allowedModes,
|
||||
NoSuchMethodException.class);
|
||||
}
|
||||
|
||||
@ -3459,7 +3452,7 @@ return mh1;
|
||||
checkSymbolicClass(member.getDeclaringClass()); // do this before attempting to resolve
|
||||
Objects.requireNonNull(member.getName());
|
||||
Objects.requireNonNull(member.getType());
|
||||
return IMPL_NAMES.resolveOrFail(refKind, member, lookupClassOrNull(),
|
||||
return IMPL_NAMES.resolveOrFail(refKind, member, lookupClassOrNull(), allowedModes,
|
||||
ReflectiveOperationException.class);
|
||||
}
|
||||
|
||||
@ -3470,7 +3463,7 @@ return mh1;
|
||||
}
|
||||
Objects.requireNonNull(member.getName());
|
||||
Objects.requireNonNull(member.getType());
|
||||
return IMPL_NAMES.resolveOrNull(refKind, member, lookupClassOrNull());
|
||||
return IMPL_NAMES.resolveOrNull(refKind, member, lookupClassOrNull(), allowedModes);
|
||||
}
|
||||
|
||||
void checkSymbolicClass(Class<?> refc) throws IllegalAccessException {
|
||||
@ -3774,7 +3767,7 @@ return mh1;
|
||||
method.getName(),
|
||||
method.getMethodType(),
|
||||
REF_invokeSpecial);
|
||||
m2 = IMPL_NAMES.resolveOrNull(refKind, m2, lookupClassOrNull());
|
||||
m2 = IMPL_NAMES.resolveOrNull(refKind, m2, lookupClassOrNull(), allowedModes);
|
||||
} while (m2 == null && // no method is found yet
|
||||
refc != refcAsSuper); // search up to refc
|
||||
if (m2 == null) throw new InternalError(method.toString());
|
||||
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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 8244090
|
||||
* @build m1/* m2/*
|
||||
* @run main/othervm m1/p.Main
|
||||
* @summary Tests public lookups produced from MethodHandles.publicLookup()::in
|
||||
*/
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*/
|
||||
module m1 {
|
||||
requires m2;
|
||||
exports p;
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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 p;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static java.lang.invoke.MethodHandles.Lookup.*;
|
||||
|
||||
public class Main {
|
||||
/*
|
||||
* Each Test object loads q.EndPoint and q.T with a custom loader.
|
||||
* These types are used to look up a method handle "EndPoint::test(T)".
|
||||
*/
|
||||
static class Test {
|
||||
final ClassLoader loader;
|
||||
final Class<?> target;
|
||||
final Class<?> param;
|
||||
Test(String name) throws Exception {
|
||||
URL url = Paths.get(System.getProperty("test.classes"), "modules", "m2")
|
||||
.toUri().toURL();
|
||||
this.loader = new URLClassLoader(name, new URL[]{url}, null);
|
||||
this.target = Class.forName("q.EndPoint", true, loader);
|
||||
this.param = Class.forName("q.T", true, loader);
|
||||
assertTrue(target != q.EndPoint.class);
|
||||
assertTrue(param != q.T.class);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
public void verifyAccess(Lookup... publicLookups) throws Throwable {
|
||||
System.err.println(loader.getName() + ": verify access for " + Arrays.toString(publicLookups));
|
||||
for (Lookup lookup : publicLookups) {
|
||||
assertTrue((lookup.lookupModes() & UNCONDITIONAL) == UNCONDITIONAL);
|
||||
assertTrue((lookup.lookupModes() & PUBLIC) == 0);
|
||||
MethodHandle mh = lookup.findVirtual(target, "test", MethodType.methodType(void.class, param));
|
||||
mh.invoke(target.newInstance(), param.newInstance());
|
||||
checkTypeConsistency(mh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify that publicLookup can teleport to:
|
||||
* 1) q.EndPoint defined in m2
|
||||
* 2) q.EndPoint defined by a custom loader CL1 in one unnamed module
|
||||
* 3) q.EndPoint defined by a custom loader CL2 in another unnamed module
|
||||
*
|
||||
* All the resulting Lookup objects are public lookups and can find
|
||||
* any public accessible member.
|
||||
*/
|
||||
public static void main(String... args) throws Throwable {
|
||||
Test test1 = new Test("CL1");
|
||||
Test test2 = new Test("CL2");
|
||||
|
||||
Lookup lookup1 = MethodHandles.publicLookup();
|
||||
Lookup lookup2 = MethodHandles.publicLookup().in(test1.target);
|
||||
Lookup lookup3 = MethodHandles.publicLookup().in(test2.target);
|
||||
assertTrue(lookup2.lookupClass().getClassLoader() != lookup3.lookupClass().getClassLoader());
|
||||
|
||||
test1.verifyAccess(lookup1, lookup2, lookup3);
|
||||
test2.verifyAccess(lookup1, lookup2, lookup3);
|
||||
}
|
||||
|
||||
static void checkTypeConsistency(MethodHandle mh) throws Throwable {
|
||||
try {
|
||||
mh.invoke(new q.EndPoint(), new q.T());
|
||||
throw new Error("expect fail to invoke due to type inconsistency");
|
||||
} catch (ClassCastException e) {
|
||||
assertTrue(e.getMessage().startsWith("Cannot cast q."));
|
||||
}
|
||||
}
|
||||
|
||||
static void assertTrue(boolean v) {
|
||||
if (!v) {
|
||||
throw new AssertionError("unexpected result");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*/
|
||||
module m2 {
|
||||
exports q;
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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 q;
|
||||
|
||||
public class EndPoint {
|
||||
public void test(T t) {
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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 q;
|
||||
|
||||
public class T {
|
||||
}
|
Loading…
Reference in New Issue
Block a user