--- a/.hgtags Wed Jul 24 12:49:44 2019 +0530
+++ b/.hgtags Thu Jul 25 12:23:54 2019 +0530
@@ -575,3 +575,4 @@
0f1e29c77e50c7da11d83df410026392c4d1a28c jdk-14+5
2e63fb0a885fa908a97bbb0da8d7c3de11536aca jdk-13+30
443f7359b34d60e7821216ffc60f88b6ffe0ccdd jdk-14+6
+28ab01c067551ef158abaef08e154e1051ca0893 jdk-14+7
--- a/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp Wed Jul 24 12:49:44 2019 +0530
+++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.cpp Thu Jul 25 12:23:54 2019 +0530
@@ -2040,17 +2040,6 @@
bind(L_fallthrough);
}
-void MacroAssembler::check_method_handle_type(Register mtype_reg, Register mh_reg,
- Register temp_reg,
- Label& wrong_method_type) {
- assert_different_registers(mtype_reg, mh_reg, temp_reg);
- // Compare method type against that of the receiver.
- load_heap_oop(temp_reg, delayed_value(java_lang_invoke_MethodHandle::type_offset_in_bytes, temp_reg), mh_reg,
- noreg, noreg, false, IS_NOT_NULL);
- cmpd(CCR0, temp_reg, mtype_reg);
- bne(CCR0, wrong_method_type);
-}
-
RegisterOrConstant MacroAssembler::argument_offset(RegisterOrConstant arg_slot,
Register temp_reg,
int extra_slot_offset) {
--- a/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp Wed Jul 24 12:49:44 2019 +0530
+++ b/src/hotspot/cpu/ppc/macroAssembler_ppc.hpp Thu Jul 25 12:23:54 2019 +0530
@@ -565,8 +565,6 @@
Label* L_slow_path = NULL);
// Method handle support (JSR 292).
- void check_method_handle_type(Register mtype_reg, Register mh_reg, Register temp_reg, Label& wrong_method_type);
-
RegisterOrConstant argument_offset(RegisterOrConstant arg_slot, Register temp_reg, int extra_slot_offset = 0);
// Biased locking support
--- a/src/hotspot/share/asm/assembler.cpp Wed Jul 24 12:49:44 2019 +0530
+++ b/src/hotspot/share/asm/assembler.cpp Thu Jul 25 12:23:54 2019 +0530
@@ -218,85 +218,6 @@
}
}
-struct DelayedConstant {
- typedef void (*value_fn_t)();
- BasicType type;
- intptr_t value;
- value_fn_t value_fn;
- // This limit of 20 is generous for initial uses.
- // The limit needs to be large enough to store the field offsets
- // into classes which do not have statically fixed layouts.
- // (Initial use is for method handle object offsets.)
- // Look for uses of "delayed_value" in the source code
- // and make sure this number is generous enough to handle all of them.
- enum { DC_LIMIT = 20 };
- static DelayedConstant delayed_constants[DC_LIMIT];
- static DelayedConstant* add(BasicType type, value_fn_t value_fn);
- bool match(BasicType t, value_fn_t cfn) {
- return type == t && value_fn == cfn;
- }
- static void update_all();
-};
-
-DelayedConstant DelayedConstant::delayed_constants[DC_LIMIT];
-// Default C structure initialization rules have the following effect here:
-// = { { (BasicType)0, (intptr_t)NULL }, ... };
-
-DelayedConstant* DelayedConstant::add(BasicType type,
- DelayedConstant::value_fn_t cfn) {
- for (int i = 0; i < DC_LIMIT; i++) {
- DelayedConstant* dcon = &delayed_constants[i];
- if (dcon->match(type, cfn))
- return dcon;
- if (dcon->value_fn == NULL) {
- dcon->value_fn = cfn;
- dcon->type = type;
- return dcon;
- }
- }
- // If this assert is hit (in pre-integration testing!) then re-evaluate
- // the comment on the definition of DC_LIMIT.
- guarantee(false, "too many delayed constants");
- return NULL;
-}
-
-void DelayedConstant::update_all() {
- for (int i = 0; i < DC_LIMIT; i++) {
- DelayedConstant* dcon = &delayed_constants[i];
- if (dcon->value_fn != NULL && dcon->value == 0) {
- typedef int (*int_fn_t)();
- typedef address (*address_fn_t)();
- switch (dcon->type) {
- case T_INT: dcon->value = (intptr_t) ((int_fn_t) dcon->value_fn)(); break;
- case T_ADDRESS: dcon->value = (intptr_t) ((address_fn_t)dcon->value_fn)(); break;
- default: break;
- }
- }
- }
-}
-
-RegisterOrConstant AbstractAssembler::delayed_value(int(*value_fn)(), Register tmp, int offset) {
- intptr_t val = (intptr_t) (*value_fn)();
- if (val != 0) return val + offset;
- return delayed_value_impl(delayed_value_addr(value_fn), tmp, offset);
-}
-RegisterOrConstant AbstractAssembler::delayed_value(address(*value_fn)(), Register tmp, int offset) {
- intptr_t val = (intptr_t) (*value_fn)();
- if (val != 0) return val + offset;
- return delayed_value_impl(delayed_value_addr(value_fn), tmp, offset);
-}
-intptr_t* AbstractAssembler::delayed_value_addr(int(*value_fn)()) {
- DelayedConstant* dcon = DelayedConstant::add(T_INT, (DelayedConstant::value_fn_t) value_fn);
- return &dcon->value;
-}
-intptr_t* AbstractAssembler::delayed_value_addr(address(*value_fn)()) {
- DelayedConstant* dcon = DelayedConstant::add(T_ADDRESS, (DelayedConstant::value_fn_t) value_fn);
- return &dcon->value;
-}
-void AbstractAssembler::update_delayed_values() {
- DelayedConstant::update_all();
-}
-
void AbstractAssembler::block_comment(const char* comment) {
if (sect() == CodeBuffer::SECT_INSTS) {
code_section()->outer()->block_comment(offset(), comment);
--- a/src/hotspot/share/classfile/javaClasses.cpp Wed Jul 24 12:49:44 2019 +0530
+++ b/src/hotspot/share/classfile/javaClasses.cpp Thu Jul 25 12:23:54 2019 +0530
@@ -4547,9 +4547,6 @@
// BASIC_JAVA_CLASSES_DO_PART1 classes (java_lang_String and java_lang_Class)
// earlier inside SystemDictionary::resolve_well_known_classes()
BASIC_JAVA_CLASSES_DO_PART2(DO_COMPUTE_OFFSETS);
-
- // generated interpreter code wants to know about the offsets we just computed:
- AbstractAssembler::update_delayed_values();
}
#if INCLUDE_CDS
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp Wed Jul 24 12:49:44 2019 +0530
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp Thu Jul 25 12:23:54 2019 +0530
@@ -48,6 +48,7 @@
#include "gc/g1/g1HotCardCache.hpp"
#include "gc/g1/g1MemoryPool.hpp"
#include "gc/g1/g1OopClosures.inline.hpp"
+#include "gc/g1/g1ParallelCleaning.hpp"
#include "gc/g1/g1ParScanThreadState.inline.hpp"
#include "gc/g1/g1Policy.hpp"
#include "gc/g1/g1RedirtyCardsQueue.hpp"
@@ -74,7 +75,6 @@
#include "gc/shared/generationSpec.hpp"
#include "gc/shared/isGCActiveMark.hpp"
#include "gc/shared/oopStorageParState.hpp"
-#include "gc/shared/parallelCleaning.hpp"
#include "gc/shared/preservedMarks.inline.hpp"
#include "gc/shared/suspendibleThreadSet.hpp"
#include "gc/shared/referenceProcessor.inline.hpp"
@@ -1080,7 +1080,7 @@
// Discard all remembered set updates.
G1BarrierSet::dirty_card_queue_set().abandon_logs();
- assert(G1BarrierSet::dirty_card_queue_set().completed_buffers_num() == 0,
+ assert(G1BarrierSet::dirty_card_queue_set().num_completed_buffers() == 0,
"DCQS should be empty");
redirty_cards_queue_set().verify_empty();
}
@@ -1957,7 +1957,7 @@
while (dcqs.apply_closure_during_gc(cl, worker_i)) {
n_completed_buffers++;
}
- assert(dcqs.completed_buffers_num() == 0, "Completed buffers exist!");
+ assert(dcqs.num_completed_buffers() == 0, "Completed buffers exist!");
phase_times()->record_thread_work_item(G1GCPhaseTimes::MergeLB, worker_i, n_completed_buffers, G1GCPhaseTimes::MergeLBProcessedBuffers);
}
@@ -2613,10 +2613,9 @@
Threads::threads_do(&count_from_threads);
G1DirtyCardQueueSet& dcqs = G1BarrierSet::dirty_card_queue_set();
- size_t buffer_size = dcqs.buffer_size();
- size_t buffer_num = dcqs.completed_buffers_num();
-
- return buffer_size * buffer_num + count_from_threads._cards;
+ dcqs.verify_num_entries_in_completed_buffers();
+
+ return dcqs.num_entries_in_completed_buffers() + count_from_threads._cards;
}
bool G1CollectedHeap::is_potential_eager_reclaim_candidate(HeapRegion* r) const {
@@ -3166,7 +3165,7 @@
void G1CollectedHeap::complete_cleaning(BoolObjectClosure* is_alive,
bool class_unloading_occurred) {
uint num_workers = workers()->active_workers();
- ParallelCleaningTask unlink_task(is_alive, num_workers, class_unloading_occurred, false);
+ G1ParallelCleaningTask unlink_task(is_alive, num_workers, class_unloading_occurred, false);
workers()->run_task(&unlink_task);
}
--- a/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp Wed Jul 24 12:49:44 2019 +0530
+++ b/src/hotspot/share/gc/g1/g1ConcurrentRefine.cpp Thu Jul 25 12:23:54 2019 +0530
@@ -397,7 +397,7 @@
dcqs.set_max_completed_buffers(red_zone());
}
- size_t curr_queue_size = dcqs.completed_buffers_num();
+ size_t curr_queue_size = dcqs.num_completed_buffers();
if ((dcqs.max_completed_buffers() > 0) &&
(curr_queue_size >= yellow_zone())) {
dcqs.set_completed_buffers_padding(curr_queue_size);
@@ -430,7 +430,7 @@
bool G1ConcurrentRefine::do_refinement_step(uint worker_id) {
G1DirtyCardQueueSet& dcqs = G1BarrierSet::dirty_card_queue_set();
- size_t curr_buffer_num = dcqs.completed_buffers_num();
+ size_t curr_buffer_num = dcqs.num_completed_buffers();
// If the number of the buffers falls down into the yellow zone,
// that means that the transition period after the evacuation pause has ended.
// Since the value written to the DCQS is the same for all threads, there is no
--- a/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp Wed Jul 24 12:49:44 2019 +0530
+++ b/src/hotspot/share/gc/g1/g1ConcurrentRefineThread.cpp Thu Jul 25 12:23:54 2019 +0530
@@ -104,7 +104,7 @@
size_t buffers_processed = 0;
log_debug(gc, refine)("Activated worker %d, on threshold: " SIZE_FORMAT ", current: " SIZE_FORMAT,
_worker_id, _cr->activation_threshold(_worker_id),
- G1BarrierSet::dirty_card_queue_set().completed_buffers_num());
+ G1BarrierSet::dirty_card_queue_set().num_completed_buffers());
{
SuspendibleThreadSetJoiner sts_join;
@@ -126,7 +126,7 @@
log_debug(gc, refine)("Deactivated worker %d, off threshold: " SIZE_FORMAT
", current: " SIZE_FORMAT ", processed: " SIZE_FORMAT,
_worker_id, _cr->deactivation_threshold(_worker_id),
- G1BarrierSet::dirty_card_queue_set().completed_buffers_num(),
+ G1BarrierSet::dirty_card_queue_set().num_completed_buffers(),
buffers_processed);
if (os::supports_vtime()) {
--- a/src/hotspot/share/gc/g1/g1DirtyCardQueue.cpp Wed Jul 24 12:49:44 2019 +0530
+++ b/src/hotspot/share/gc/g1/g1DirtyCardQueue.cpp Thu Jul 25 12:23:54 2019 +0530
@@ -84,7 +84,7 @@
_cbl_mon(NULL),
_completed_buffers_head(NULL),
_completed_buffers_tail(NULL),
- _n_completed_buffers(0),
+ _num_entries_in_completed_buffers(0),
_process_completed_buffers_threshold(ProcessCompletedBuffersThresholdNever),
_process_completed_buffers(false),
_notify_when_complete(notify_when_complete),
@@ -133,42 +133,56 @@
_completed_buffers_tail->set_next(cbn);
_completed_buffers_tail = cbn;
}
- _n_completed_buffers++;
+ _num_entries_in_completed_buffers += buffer_size() - cbn->index();
if (!process_completed_buffers() &&
- (_n_completed_buffers > process_completed_buffers_threshold())) {
+ (num_completed_buffers() > process_completed_buffers_threshold())) {
set_process_completed_buffers(true);
if (_notify_when_complete) {
_cbl_mon->notify_all();
}
}
- assert_completed_buffers_list_len_correct_locked();
+ verify_num_entries_in_completed_buffers();
}
BufferNode* G1DirtyCardQueueSet::get_completed_buffer(size_t stop_at) {
MutexLocker x(_cbl_mon, Mutex::_no_safepoint_check_flag);
- if (_n_completed_buffers <= stop_at) {
+ if (num_completed_buffers() <= stop_at) {
return NULL;
}
- assert(_n_completed_buffers > 0, "invariant");
+ assert(num_completed_buffers() > 0, "invariant");
assert(_completed_buffers_head != NULL, "invariant");
assert(_completed_buffers_tail != NULL, "invariant");
BufferNode* bn = _completed_buffers_head;
- _n_completed_buffers--;
+ _num_entries_in_completed_buffers -= buffer_size() - bn->index();
_completed_buffers_head = bn->next();
if (_completed_buffers_head == NULL) {
- assert(_n_completed_buffers == 0, "invariant");
+ assert(num_completed_buffers() == 0, "invariant");
_completed_buffers_tail = NULL;
set_process_completed_buffers(false);
}
- assert_completed_buffers_list_len_correct_locked();
+ verify_num_entries_in_completed_buffers();
bn->set_next(NULL);
return bn;
}
+#ifdef ASSERT
+void G1DirtyCardQueueSet::verify_num_entries_in_completed_buffers() const {
+ size_t actual = 0;
+ BufferNode* cur = _completed_buffers_head;
+ while (cur != NULL) {
+ actual += buffer_size() - cur->index();
+ cur = cur->next();
+ }
+ assert(actual == _num_entries_in_completed_buffers,
+ "Num entries in completed buffers should be " SIZE_FORMAT " but are " SIZE_FORMAT,
+ _num_entries_in_completed_buffers, actual);
+}
+#endif
+
void G1DirtyCardQueueSet::abandon_completed_buffers() {
BufferNode* buffers_to_delete = NULL;
{
@@ -176,7 +190,7 @@
buffers_to_delete = _completed_buffers_head;
_completed_buffers_head = NULL;
_completed_buffers_tail = NULL;
- _n_completed_buffers = 0;
+ _num_entries_in_completed_buffers = 0;
set_process_completed_buffers(false);
}
while (buffers_to_delete != NULL) {
@@ -189,26 +203,13 @@
void G1DirtyCardQueueSet::notify_if_necessary() {
MutexLocker x(_cbl_mon, Mutex::_no_safepoint_check_flag);
- if (_n_completed_buffers > process_completed_buffers_threshold()) {
+ if (num_completed_buffers() > process_completed_buffers_threshold()) {
set_process_completed_buffers(true);
if (_notify_when_complete)
_cbl_mon->notify();
}
}
-#ifdef ASSERT
-void G1DirtyCardQueueSet::assert_completed_buffers_list_len_correct_locked() {
- assert_lock_strong(_cbl_mon);
- size_t n = 0;
- for (BufferNode* bn = _completed_buffers_head; bn != NULL; bn = bn->next()) {
- ++n;
- }
- assert(n == _n_completed_buffers,
- "Completed buffer length is wrong: counted: " SIZE_FORMAT
- ", expected: " SIZE_FORMAT, n, _n_completed_buffers);
-}
-#endif // ASSERT
-
// Merge lists of buffers. Notify the processing threads.
// The source queue is emptied as a result. The queues
// must share the monitor.
@@ -227,12 +228,12 @@
_completed_buffers_tail->set_next(from._head);
_completed_buffers_tail = from._tail;
}
- _n_completed_buffers += from._count;
+ _num_entries_in_completed_buffers += from._entry_count;
assert(_completed_buffers_head == NULL && _completed_buffers_tail == NULL ||
_completed_buffers_head != NULL && _completed_buffers_tail != NULL,
"Sanity");
- assert_completed_buffers_list_len_correct_locked();
+ verify_num_entries_in_completed_buffers();
}
bool G1DirtyCardQueueSet::apply_closure_to_buffer(G1CardTableEntryClosure* cl,
@@ -278,7 +279,7 @@
// add of padding could overflow, which is treated as unlimited.
size_t max_buffers = max_completed_buffers();
size_t limit = max_buffers + completed_buffers_padding();
- if ((completed_buffers_num() > limit) && (limit >= max_buffers)) {
+ if ((num_completed_buffers() > limit) && (limit >= max_buffers)) {
if (mut_process_buffer(node)) {
return true;
}
--- a/src/hotspot/share/gc/g1/g1DirtyCardQueue.hpp Wed Jul 24 12:49:44 2019 +0530
+++ b/src/hotspot/share/gc/g1/g1DirtyCardQueue.hpp Thu Jul 25 12:23:54 2019 +0530
@@ -69,7 +69,9 @@
Monitor* _cbl_mon; // Protects the fields below.
BufferNode* _completed_buffers_head;
BufferNode* _completed_buffers_tail;
- volatile size_t _n_completed_buffers;
+
+ // Number of actual entries in the list of completed buffers.
+ volatile size_t _num_entries_in_completed_buffers;
size_t _process_completed_buffers_threshold;
volatile bool _process_completed_buffers;
@@ -77,8 +79,6 @@
// If true, notify_all on _cbl_mon when the threshold is reached.
bool _notify_when_complete;
- void assert_completed_buffers_list_len_correct_locked() NOT_DEBUG_RETURN;
-
void abandon_completed_buffers();
// Apply the closure to the elements of "node" from it's index to
@@ -150,8 +150,17 @@
// return a completed buffer from the list. Otherwise, return NULL.
BufferNode* get_completed_buffer(size_t stop_at = 0);
- // The number of buffers in the list. Racy...
- size_t completed_buffers_num() const { return _n_completed_buffers; }
+ // The number of buffers in the list. Derived as an approximation from the number
+ // of entries in the buffers. Racy.
+ size_t num_completed_buffers() const {
+ return (num_entries_in_completed_buffers() + buffer_size() - 1) / buffer_size();
+ }
+ // The number of entries in completed buffers. Read without synchronization.
+ size_t num_entries_in_completed_buffers() const { return _num_entries_in_completed_buffers; }
+
+ // Verify that _num_entries_in_completed_buffers is equal to the sum of actual entries
+ // in the completed buffers.
+ void verify_num_entries_in_completed_buffers() const NOT_DEBUG_RETURN;
bool process_completed_buffers() { return _process_completed_buffers; }
void set_process_completed_buffers(bool x) { _process_completed_buffers = x; }
--- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp Wed Jul 24 12:49:44 2019 +0530
+++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp Thu Jul 25 12:23:54 2019 +0530
@@ -86,8 +86,14 @@
_gc_par_phases[MergeLB] = new WorkerDataArray<double>(max_gc_threads, "Log Buffers (ms):");
if (G1HotCardCache::default_use_cache()) {
_gc_par_phases[MergeHCC] = new WorkerDataArray<double>(max_gc_threads, "Hot Card Cache (ms):");
+ _merge_hcc_dirty_cards = new WorkerDataArray<size_t>(max_gc_threads, "Dirty Cards:");
+ _gc_par_phases[MergeHCC]->link_thread_work_items(_merge_hcc_dirty_cards, MergeHCCDirtyCards);
+ _merge_hcc_skipped_cards = new WorkerDataArray<size_t>(max_gc_threads, "Skipped Cards:");
+ _gc_par_phases[MergeHCC]->link_thread_work_items(_merge_hcc_skipped_cards, MergeHCCSkippedCards);
} else {
_gc_par_phases[MergeHCC] = NULL;
+ _merge_hcc_dirty_cards = NULL;
+ _merge_hcc_skipped_cards = NULL;
}
_gc_par_phases[ScanHR] = new WorkerDataArray<double>(max_gc_threads, "Scan Heap Roots (ms):");
_gc_par_phases[OptScanHR] = new WorkerDataArray<double>(max_gc_threads, "Optional Scan Heap Roots (ms):");
--- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp Wed Jul 24 12:49:44 2019 +0530
+++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp Thu Jul 25 12:23:54 2019 +0530
@@ -100,6 +100,11 @@
ScanHRUsedMemory
};
+ enum GCMergeHCCWorkItems {
+ MergeHCCDirtyCards,
+ MergeHCCSkippedCards
+ };
+
enum GCMergeLBWorkItems {
MergeLBProcessedBuffers,
MergeLBDirtyCards,
@@ -121,6 +126,9 @@
WorkerDataArray<size_t>* _merge_rs_merged_fine;
WorkerDataArray<size_t>* _merge_rs_merged_coarse;
+ WorkerDataArray<size_t>* _merge_hcc_dirty_cards;
+ WorkerDataArray<size_t>* _merge_hcc_skipped_cards;
+
WorkerDataArray<size_t>* _merge_lb_processed_buffers;
WorkerDataArray<size_t>* _merge_lb_dirty_cards;
WorkerDataArray<size_t>* _merge_lb_skipped_cards;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/g1/g1ParallelCleaning.cpp Thu Jul 25 12:23:54 2019 +0530
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ */
+
+#include "precompiled.hpp"
+
+#include "gc/g1/g1ParallelCleaning.hpp"
+#if INCLUDE_JVMCI
+#include "jvmci/jvmci.hpp"
+#endif
+
+#if INCLUDE_JVMCI
+JVMCICleaningTask::JVMCICleaningTask() :
+ _cleaning_claimed(0) {
+}
+
+bool JVMCICleaningTask::claim_cleaning_task() {
+ if (_cleaning_claimed) {
+ return false;
+ }
+
+ return Atomic::cmpxchg(1, &_cleaning_claimed, 0) == 0;
+}
+
+void JVMCICleaningTask::work(bool unloading_occurred) {
+ // One worker will clean JVMCI metadata handles.
+ if (unloading_occurred && EnableJVMCI && claim_cleaning_task()) {
+ JVMCI::do_unloading(unloading_occurred);
+ }
+}
+#endif // INCLUDE_JVMCI
+
+G1ParallelCleaningTask::G1ParallelCleaningTask(BoolObjectClosure* is_alive,
+ uint num_workers,
+ bool unloading_occurred,
+ bool resize_dedup_table) :
+ AbstractGangTask("G1 Parallel Cleaning"),
+ _unloading_occurred(unloading_occurred),
+ _string_dedup_task(is_alive, NULL, resize_dedup_table),
+ _code_cache_task(num_workers, is_alive, unloading_occurred),
+ JVMCI_ONLY(_jvmci_cleaning_task() COMMA)
+ _klass_cleaning_task() {
+}
+
+// The parallel work done by all worker threads.
+void G1ParallelCleaningTask::work(uint worker_id) {
+ // Clean JVMCI metadata handles.
+ // Execute this task first because it is serial task.
+ JVMCI_ONLY(_jvmci_cleaning_task.work(_unloading_occurred);)
+
+ // Do first pass of code cache cleaning.
+ _code_cache_task.work(worker_id);
+
+ // Clean the string dedup data structures.
+ _string_dedup_task.work(worker_id);
+
+ // Clean all klasses that were not unloaded.
+ // The weak metadata in klass doesn't need to be
+ // processed if there was no unloading.
+ if (_unloading_occurred) {
+ _klass_cleaning_task.work();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/g1/g1ParallelCleaning.hpp Thu Jul 25 12:23:54 2019 +0530
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2019, 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.
+ *
+ */
+
+#ifndef SHARE_GC_G1_G1PARALLELCLEANING_HPP
+#define SHARE_GC_G1_G1PARALLELCLEANING_HPP
+
+#include "gc/shared/parallelCleaning.hpp"
+
+#if INCLUDE_JVMCI
+class JVMCICleaningTask : public StackObj {
+ volatile int _cleaning_claimed;
+
+public:
+ JVMCICleaningTask();
+ // Clean JVMCI metadata handles.
+ void work(bool unloading_occurred);
+
+private:
+ bool claim_cleaning_task();
+};
+#endif
+
+// Do cleanup of some weakly held data in the same parallel task.
+// Assumes a non-moving context.
+class G1ParallelCleaningTask : public AbstractGangTask {
+private:
+ bool _unloading_occurred;
+ StringDedupCleaningTask _string_dedup_task;
+ CodeCacheUnloadingTask _code_cache_task;
+#if INCLUDE_JVMCI
+ JVMCICleaningTask _jvmci_cleaning_task;
+#endif
+ KlassCleaningTask _klass_cleaning_task;
+
+public:
+ // The constructor is run in the VMThread.
+ G1ParallelCleaningTask(BoolObjectClosure* is_alive,
+ uint num_workers,
+ bool unloading_occurred,
+ bool resize_dedup_table);
+
+ void work(uint worker_id);
+};
+
+#endif // SHARE_GC_G1_G1PARALLELCLEANING_HPP
--- a/src/hotspot/share/gc/g1/g1RedirtyCardsQueue.cpp Wed Jul 24 12:49:44 2019 +0530
+++ b/src/hotspot/share/gc/g1/g1RedirtyCardsQueue.cpp Thu Jul 25 12:23:54 2019 +0530
@@ -31,15 +31,15 @@
// G1RedirtyCardsBufferList
G1RedirtyCardsBufferList::G1RedirtyCardsBufferList() :
- _head(NULL), _tail(NULL), _count(0) {}
+ _head(NULL), _tail(NULL), _entry_count(0) {}
G1RedirtyCardsBufferList::G1RedirtyCardsBufferList(BufferNode* head,
BufferNode* tail,
- size_t count) :
- _head(head), _tail(tail), _count(count)
+ size_t entry_count) :
+ _head(head), _tail(tail), _entry_count(entry_count)
{
assert((_head == NULL) == (_tail == NULL), "invariant");
- assert((_head == NULL) == (_count == 0), "invariant");
+ assert((_head == NULL) == (_entry_count == 0), "invariant");
}
// G1RedirtyCardsQueueBase::LocalQSet
@@ -55,11 +55,11 @@
G1RedirtyCardsQueueBase::LocalQSet::~LocalQSet() {
assert(_buffers._head == NULL, "unflushed qset");
assert(_buffers._tail == NULL, "invariant");
- assert(_buffers._count == 0, "invariant");
+ assert(_buffers._entry_count == 0, "invariant");
}
void G1RedirtyCardsQueueBase::LocalQSet::enqueue_completed_buffer(BufferNode* node) {
- ++_buffers._count;
+ _buffers._entry_count += buffer_size() - node->index();
node->set_next(_buffers._head);
_buffers._head = node;
if (_buffers._tail == NULL) {
@@ -67,8 +67,7 @@
}
}
-G1RedirtyCardsBufferList
-G1RedirtyCardsQueueBase::LocalQSet::take_all_completed_buffers() {
+G1RedirtyCardsBufferList G1RedirtyCardsQueueBase::LocalQSet::take_all_completed_buffers() {
G1RedirtyCardsBufferList result = _buffers;
_buffers = G1RedirtyCardsBufferList();
return result;
@@ -103,7 +102,7 @@
G1RedirtyCardsQueueSet::G1RedirtyCardsQueueSet() :
PtrQueueSet(),
_list(),
- _count(0),
+ _entry_count(0),
_tail(NULL)
DEBUG_ONLY(COMMA _collecting(true))
{}
@@ -116,7 +115,7 @@
void G1RedirtyCardsQueueSet::verify_empty() const {
assert(_list.empty(), "precondition");
assert(_tail == NULL, "invariant");
- assert(_count == 0, "invariant");
+ assert(_entry_count == 0, "invariant");
}
#endif // ASSERT
@@ -127,9 +126,9 @@
G1RedirtyCardsBufferList G1RedirtyCardsQueueSet::take_all_completed_buffers() {
DEBUG_ONLY(_collecting = false;)
- G1RedirtyCardsBufferList result(_list.pop_all(), _tail, _count);
+ G1RedirtyCardsBufferList result(_list.pop_all(), _tail, _entry_count);
_tail = NULL;
- _count = 0;
+ _entry_count = 0;
DEBUG_ONLY(_collecting = true;)
return result;
}
@@ -146,7 +145,7 @@
void G1RedirtyCardsQueueSet::enqueue_completed_buffer(BufferNode* node) {
assert(_collecting, "precondition");
- Atomic::inc(&_count);
+ Atomic::add(buffer_size() - node->index(), &_entry_count);
_list.push(*node);
update_tail(node);
}
@@ -156,7 +155,7 @@
const G1RedirtyCardsBufferList from = src->take_all_completed_buffers();
if (from._head != NULL) {
assert(from._tail != NULL, "invariant");
- Atomic::add(from._count, &_count);
+ Atomic::add(from._entry_count, &_entry_count);
_list.prepend(*from._head, *from._tail);
update_tail(from._tail);
}
--- a/src/hotspot/share/gc/g1/g1RedirtyCardsQueue.hpp Wed Jul 24 12:49:44 2019 +0530
+++ b/src/hotspot/share/gc/g1/g1RedirtyCardsQueue.hpp Thu Jul 25 12:23:54 2019 +0530
@@ -36,10 +36,10 @@
struct G1RedirtyCardsBufferList {
BufferNode* _head;
BufferNode* _tail;
- size_t _count;
+ size_t _entry_count;
G1RedirtyCardsBufferList();
- G1RedirtyCardsBufferList(BufferNode* head, BufferNode* tail, size_t count);
+ G1RedirtyCardsBufferList(BufferNode* head, BufferNode* tail, size_t entry_count);
};
// Provide G1RedirtyCardsQueue with a thread-local qset. It provides an
@@ -100,7 +100,7 @@
DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE, 0);
BufferNode::Stack _list;
DEFINE_PAD_MINUS_SIZE(2, DEFAULT_CACHE_LINE_SIZE, sizeof(size_t));
- volatile size_t _count;
+ volatile size_t _entry_count;
DEFINE_PAD_MINUS_SIZE(3, DEFAULT_CACHE_LINE_SIZE, sizeof(BufferNode*));
BufferNode* _tail;
DEBUG_ONLY(mutable bool _collecting;)
--- a/src/hotspot/share/gc/g1/g1RemSet.cpp Wed Jul 24 12:49:44 2019 +0530
+++ b/src/hotspot/share/gc/g1/g1RemSet.cpp Thu Jul 25 12:23:54 2019 +0530
@@ -94,24 +94,35 @@
// to (>=) HeapRegion::CardsPerRegion (completely scanned).
uint volatile* _card_table_scan_state;
- // Random power of two number of cards we want to claim per thread. This corresponds
- // to a 64k of memory work chunk area for every thread.
- // We use the same claim size as Parallel GC. No particular measurements have been
- // performed to determine an optimal number.
- static const uint CardsPerChunk = 128;
+ // Return "optimal" number of chunks per region we want to use for claiming areas
+ // within a region to claim. Dependent on the region size as proxy for the heap
+ // size, we limit the total number of chunks to limit memory usage and maintenance
+ // effort of that table vs. granularity of distributing scanning work.
+ // Testing showed that 8 for 1M/2M region, 16 for 4M/8M regions, 32 for 16/32M regions
+ // seems to be such a good trade-off.
+ static uint get_chunks_per_region(uint log_region_size) {
+ // Limit the expected input values to current known possible values of the
+ // (log) region size. Adjust as necessary after testing if changing the permissible
+ // values for region size.
+ assert(log_region_size >= 20 && log_region_size <= 25,
+ "expected value in [20,25], but got %u", log_region_size);
+ return 1u << (log_region_size / 2 - 7);
+ }
- uint _scan_chunks_per_region;
+ uint _scan_chunks_per_region; // Number of chunks per region.
+ uint8_t _log_scan_chunks_per_region; // Log of number of chunks per region.
bool* _region_scan_chunks;
- uint8_t _scan_chunks_shift;
+ size_t _num_total_scan_chunks; // Total number of elements in _region_scan_chunks.
+ uint8_t _scan_chunks_shift; // For conversion between card index and chunk index.
public:
uint scan_chunk_size() const { return (uint)1 << _scan_chunks_shift; }
// Returns whether the chunk corresponding to the given region/card in region contain a
// dirty card, i.e. actually needs scanning.
bool chunk_needs_scan(uint const region_idx, uint const card_in_region) const {
- size_t const idx = (size_t)region_idx * _scan_chunks_per_region + (card_in_region >> _scan_chunks_shift);
- assert(idx < (_max_regions * _scan_chunks_per_region), "Index " SIZE_FORMAT " out of bounds " SIZE_FORMAT,
- idx, _max_regions * _scan_chunks_per_region);
+ size_t const idx = ((size_t)region_idx << _log_scan_chunks_per_region) + (card_in_region >> _scan_chunks_shift);
+ assert(idx < _num_total_scan_chunks, "Index " SIZE_FORMAT " out of bounds " SIZE_FORMAT,
+ idx, _num_total_scan_chunks);
return _region_scan_chunks[idx];
}
@@ -286,8 +297,10 @@
_max_regions(0),
_collection_set_iter_state(NULL),
_card_table_scan_state(NULL),
- _scan_chunks_per_region((uint)(HeapRegion::CardsPerRegion / CardsPerChunk)),
+ _scan_chunks_per_region(get_chunks_per_region(HeapRegion::LogOfHRGrainBytes)),
+ _log_scan_chunks_per_region(log2_uint(_scan_chunks_per_region)),
_region_scan_chunks(NULL),
+ _num_total_scan_chunks(0),
_scan_chunks_shift(0),
_all_dirty_regions(NULL),
_next_dirty_regions(NULL),
@@ -306,7 +319,8 @@
_max_regions = max_regions;
_collection_set_iter_state = NEW_C_HEAP_ARRAY(G1RemsetIterState, max_regions, mtGC);
_card_table_scan_state = NEW_C_HEAP_ARRAY(uint, max_regions, mtGC);
- _region_scan_chunks = NEW_C_HEAP_ARRAY(bool, max_regions * _scan_chunks_per_region, mtGC);
+ _num_total_scan_chunks = max_regions * _scan_chunks_per_region;
+ _region_scan_chunks = NEW_C_HEAP_ARRAY(bool, _num_total_scan_chunks, mtGC);
_scan_chunks_shift = (uint8_t)log2_intptr(HeapRegion::CardsPerRegion / _scan_chunks_per_region);
_scan_top = NEW_C_HEAP_ARRAY(HeapWord*, max_regions, mtGC);
@@ -333,7 +347,7 @@
_card_table_scan_state[i] = 0;
}
- ::memset(_region_scan_chunks, false, _max_regions * _scan_chunks_per_region * sizeof(*_region_scan_chunks));
+ ::memset(_region_scan_chunks, false, _num_total_scan_chunks * sizeof(*_region_scan_chunks));
}
// Returns whether the given region contains cards we need to scan. The remembered
@@ -349,12 +363,12 @@
size_t num_visited_cards() const {
size_t result = 0;
- for (uint i = 0; i < _max_regions * _scan_chunks_per_region; i++) {
+ for (uint i = 0; i < _num_total_scan_chunks; i++) {
if (_region_scan_chunks[i]) {
result++;
}
}
- return result * CardsPerChunk;
+ return result * (HeapRegion::CardsPerRegion / _scan_chunks_per_region);
}
size_t num_cards_in_dirty_regions() const {
@@ -369,9 +383,9 @@
}
void set_chunk_dirty(size_t const card_idx) {
- assert((card_idx >> _scan_chunks_shift) < (_max_regions * _scan_chunks_per_region),
+ assert((card_idx >> _scan_chunks_shift) < _num_total_scan_chunks,
"Trying to access index " SIZE_FORMAT " out of bounds " SIZE_FORMAT,
- card_idx >> _scan_chunks_shift, _max_regions * _scan_chunks_per_region);
+ card_idx >> _scan_chunks_shift, _num_total_scan_chunks);
size_t const chunk_idx = card_idx >> _scan_chunks_shift;
if (!_region_scan_chunks[chunk_idx]) {
_region_scan_chunks[chunk_idx] = true;
@@ -1133,6 +1147,9 @@
G1GCParPhaseTimesTracker x(p, G1GCPhaseTimes::MergeHCC, worker_id);
G1MergeLogBufferCardsClosure cl(g1h, _scan_state);
g1h->iterate_hcc_closure(&cl, worker_id);
+
+ p->record_thread_work_item(G1GCPhaseTimes::MergeHCC, worker_id, cl.cards_dirty(), G1GCPhaseTimes::MergeHCCDirtyCards);
+ p->record_thread_work_item(G1GCPhaseTimes::MergeHCC, worker_id, cl.cards_skipped(), G1GCPhaseTimes::MergeHCCSkippedCards);
}
// Now apply the closure to all remaining log entries.
--- a/src/hotspot/share/gc/shared/parallelCleaning.cpp Wed Jul 24 12:49:44 2019 +0530
+++ b/src/hotspot/share/gc/shared/parallelCleaning.cpp Thu Jul 25 12:23:54 2019 +0530
@@ -30,9 +30,6 @@
#include "logging/log.hpp"
#include "memory/resourceArea.hpp"
#include "logging/log.hpp"
-#if INCLUDE_JVMCI
-#include "jvmci/jvmci.hpp"
-#endif
StringDedupCleaningTask::StringDedupCleaningTask(BoolObjectClosure* is_alive,
OopClosure* keep_alive,
@@ -160,56 +157,3 @@
clean_klass(klass);
}
}
-
-#if INCLUDE_JVMCI
-JVMCICleaningTask::JVMCICleaningTask() :
- _cleaning_claimed(0) {
-}
-
-bool JVMCICleaningTask::claim_cleaning_task() {
- if (_cleaning_claimed) {
- return false;
- }
-
- return Atomic::cmpxchg(1, &_cleaning_claimed, 0) == 0;
-}
-
-void JVMCICleaningTask::work(bool unloading_occurred) {
- // One worker will clean JVMCI metadata handles.
- if (unloading_occurred && EnableJVMCI && claim_cleaning_task()) {
- JVMCI::do_unloading(unloading_occurred);
- }
-}
-#endif // INCLUDE_JVMCI
-
-ParallelCleaningTask::ParallelCleaningTask(BoolObjectClosure* is_alive,
- uint num_workers,
- bool unloading_occurred,
- bool resize_dedup_table) :
- AbstractGangTask("Parallel Cleaning"),
- _unloading_occurred(unloading_occurred),
- _string_dedup_task(is_alive, NULL, resize_dedup_table),
- _code_cache_task(num_workers, is_alive, unloading_occurred),
- JVMCI_ONLY(_jvmci_cleaning_task() COMMA)
- _klass_cleaning_task() {
-}
-
-// The parallel work done by all worker threads.
-void ParallelCleaningTask::work(uint worker_id) {
- // Clean JVMCI metadata handles.
- // Execute this task first because it is serial task.
- JVMCI_ONLY(_jvmci_cleaning_task.work(_unloading_occurred);)
-
- // Do first pass of code cache cleaning.
- _code_cache_task.work(worker_id);
-
- // Clean the string dedup data structures.
- _string_dedup_task.work(worker_id);
-
- // Clean all klasses that were not unloaded.
- // The weak metadata in klass doesn't need to be
- // processed if there was no unloading.
- if (_unloading_occurred) {
- _klass_cleaning_task.work();
- }
-}
--- a/src/hotspot/share/gc/shared/parallelCleaning.hpp Wed Jul 24 12:49:44 2019 +0530
+++ b/src/hotspot/share/gc/shared/parallelCleaning.hpp Thu Jul 25 12:23:54 2019 +0530
@@ -31,8 +31,6 @@
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "gc/shared/workgroup.hpp"
-class ParallelCleaningTask;
-
class StringDedupCleaningTask : public AbstractGangTask {
StringDedupUnlinkOrOopsDoClosure _dedup_closure;
@@ -87,40 +85,4 @@
void work();
};
-#if INCLUDE_JVMCI
-class JVMCICleaningTask : public StackObj {
- volatile int _cleaning_claimed;
-
-public:
- JVMCICleaningTask();
- // Clean JVMCI metadata handles.
- void work(bool unloading_occurred);
-
-private:
- bool claim_cleaning_task();
-};
-#endif
-
-// Do cleanup of some weakly held data in the same parallel task.
-// Assumes a non-moving context.
-class ParallelCleaningTask : public AbstractGangTask {
-private:
- bool _unloading_occurred;
- StringDedupCleaningTask _string_dedup_task;
- CodeCacheUnloadingTask _code_cache_task;
-#if INCLUDE_JVMCI
- JVMCICleaningTask _jvmci_cleaning_task;
-#endif
- KlassCleaningTask _klass_cleaning_task;
-
-public:
- // The constructor is run in the VMThread.
- ParallelCleaningTask(BoolObjectClosure* is_alive,
- uint num_workers,
- bool unloading_occurred,
- bool resize_dedup_table);
-
- void work(uint worker_id);
-};
-
#endif // SHARE_GC_SHARED_PARALLELCLEANING_HPP
--- a/src/hotspot/share/oops/constantPool.cpp Wed Jul 24 12:49:44 2019 +0530
+++ b/src/hotspot/share/oops/constantPool.cpp Thu Jul 25 12:23:54 2019 +0530
@@ -760,6 +760,10 @@
// return the method type signature in the error message
message = this_cp->method_type_signature_at(which);
break;
+ case JVM_CONSTANT_Dynamic:
+ // return the name of the condy in the error message
+ message = this_cp->uncached_name_ref_at(which);
+ break;
default:
ShouldNotReachHere();
}
--- a/src/java.base/share/classes/java/lang/invoke/MemberName.java Wed Jul 24 12:49:44 2019 +0530
+++ b/src/java.base/share/classes/java/lang/invoke/MemberName.java Thu Jul 25 12:23:54 2019 +0530
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2019, 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
@@ -521,7 +521,7 @@
public boolean isAccessibleFrom(Class<?> lookupClass) {
int mode = (ALL_ACCESS|MethodHandles.Lookup.PACKAGE|MethodHandles.Lookup.MODULE);
return VerifyAccess.isMemberAccessible(this.getDeclaringClass(), this.getDeclaringClass(), flags,
- lookupClass, mode);
+ lookupClass, null, mode);
}
/**
@@ -930,13 +930,21 @@
message += ", from public Lookup";
} else {
Module m;
+ Class<?> plc;
if (from instanceof MethodHandles.Lookup) {
MethodHandles.Lookup lookup = (MethodHandles.Lookup)from;
+ from = lookup.lookupClass();
m = lookup.lookupClass().getModule();
+ plc = lookup.previousLookupClass();
} else {
- m = from.getClass().getModule();
+ m = ((Class<?>)from).getModule();
+ plc = null;
}
message += ", from " + from + " (" + m + ")";
+ if (plc != null) {
+ message += ", previous lookup " +
+ plc.getName() + " (" + plc.getModule() + ")";
+ }
}
}
return new IllegalAccessException(message);
--- a/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Wed Jul 24 12:49:44 2019 +0530
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Thu Jul 25 12:23:54 2019 +0530
@@ -125,7 +125,7 @@
/**
* Returns a {@link Lookup lookup object} which is trusted minimally.
- * The lookup has the {@code PUBLIC} and {@code UNCONDITIONAL} modes.
+ * The lookup has the {@code UNCONDITIONAL} mode.
* It can only be used to create method handles to public members of
* public classes in packages that are exported unconditionally.
* <p>
@@ -134,14 +134,14 @@
*
* @apiNote The use of Object is conventional, and because the lookup modes are
* limited, there is no special access provided to the internals of Object, its package
- * or its module. Consequently, the lookup context of this lookup object will be the
- * bootstrap class loader, which means it cannot find user classes.
+ * or its module. This public lookup object or other lookup object with
+ * {@code UNCONDITIONAL} mode assumes readability. Consequently, the lookup class
+ * is not used to determine the lookup context.
*
* <p style="font-size:smaller;">
* <em>Discussion:</em>
* The lookup class can be changed to any other class {@code C} using an expression of the form
* {@link Lookup#in publicLookup().in(C.class)}.
- * but may change the lookup context by virtue of changing the class loader.
* A public lookup object is always subject to
* <a href="MethodHandles.Lookup.html#secmgr">security manager checks</a>.
* Also, it cannot access
@@ -156,64 +156,106 @@
}
/**
- * Returns a {@link Lookup lookup object} with full capabilities to emulate all
- * supported bytecode behaviors, including <a href="MethodHandles.Lookup.html#privacc">
- * private access</a>, on a target class.
- * This method checks that a caller, specified as a {@code Lookup} object, is allowed to
- * do <em>deep reflection</em> on the target class. If {@code m1} is the module containing
- * the {@link Lookup#lookupClass() lookup class}, and {@code m2} is the module containing
- * the target class, then this check ensures that
+ * Returns a {@link Lookup lookup} object on a target class to emulate all supported
+ * bytecode behaviors, including <a href="MethodHandles.Lookup.html#privacc"> private access</a>.
+ * The returned lookup object can provide access to classes in modules and packages,
+ * and members of those classes, outside the normal rules of Java access control,
+ * instead conforming to the more permissive rules for modular <em>deep reflection</em>.
+ * <p>
+ * A caller, specified as a {@code Lookup} object, in module {@code M1} is
+ * allowed to do deep reflection on module {@code M2} and package of the target class
+ * if and only if all of the following conditions are {@code true}:
* <ul>
- * <li>{@code m1} {@link Module#canRead reads} {@code m2}.</li>
- * <li>{@code m2} {@link Module#isOpen(String,Module) opens} the package containing
- * the target class to at least {@code m1}.</li>
- * <li>The lookup has the {@link Lookup#MODULE MODULE} lookup mode.</li>
+ * <li>If there is a security manager, its {@code checkPermission} method is
+ * called to check {@code ReflectPermission("suppressAccessChecks")} and
+ * that must return normally.
+ * <li>The caller lookup object must have the {@link Lookup#MODULE MODULE} lookup mode.
+ * (This is because otherwise there would be no way to ensure the original lookup
+ * creator was a member of any particular module, and so any subsequent checks
+ * for readability and qualified exports would become ineffective.)
+ * <li>The caller lookup object must have {@link Lookup#PRIVATE PRIVATE} access.
+ * (This is because an application intending to share intra-module access
+ * using {@link Lookup#MODULE MODULE} alone will inadvertently also share
+ * deep reflection to its own module.)
+ * <li>The target class must be a proper class, not a primitive or array class.
+ * (Thus, {@code M2} is well-defined.)
+ * <li>If the caller module {@code M1} differs from
+ * the target module {@code M2} then both of the following must be true:
+ * <ul>
+ * <li>{@code M1} {@link Module#canRead reads} {@code M2}.</li>
+ * <li>{@code M2} {@link Module#isOpen(String,Module) opens} the package
+ * containing the target class to at least {@code M1}.</li>
+ * </ul>
* </ul>
* <p>
- * If there is a security manager, its {@code checkPermission} method is called to
- * check {@code ReflectPermission("suppressAccessChecks")}.
- * @apiNote The {@code MODULE} lookup mode serves to authenticate that the lookup object
- * was created by code in the caller module (or derived from a lookup object originally
- * created by the caller). A lookup object with the {@code MODULE} lookup mode can be
- * shared with trusted parties without giving away {@code PRIVATE} and {@code PACKAGE}
- * access to the caller.
+ * If any of the above checks is violated, this method fails with an
+ * exception.
+ * <p>
+ * Otherwise, if {@code M1} and {@code M2} are the same module, this method
+ * returns a {@code Lookup} on {@code targetClass} with full capabilities and
+ * {@code null} previous lookup class.
+ * <p>
+ * Otherwise, {@code M1} and {@code M2} are two different modules. This method
+ * returns a {@code Lookup} on {@code targetClass} that records
+ * the lookup class of the caller as the new previous lookup class and
+ * drops {@code MODULE} access from the full capabilities mode.
+ *
* @param targetClass the target class
- * @param lookup the caller lookup object
+ * @param caller the caller lookup object
* @return a lookup object for the target class, with private access
- * @throws IllegalArgumentException if {@code targetClass} is a primitve type or array class
+ * @throws IllegalArgumentException if {@code targetClass} is a primitive type or array class
* @throws NullPointerException if {@code targetClass} or {@code caller} is {@code null}
- * @throws IllegalAccessException if the access check specified above fails
* @throws SecurityException if denied by the security manager
+ * @throws IllegalAccessException if any of the other access checks specified above fails
* @since 9
* @spec JPMS
* @see Lookup#dropLookupMode
+ * @see <a href="MethodHandles.Lookup.html#cross-module-lookup">Cross-module lookups</a>
*/
- public static Lookup privateLookupIn(Class<?> targetClass, Lookup lookup) throws IllegalAccessException {
+ public static Lookup privateLookupIn(Class<?> targetClass, Lookup caller) throws IllegalAccessException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
if (targetClass.isPrimitive())
throw new IllegalArgumentException(targetClass + " is a primitive class");
if (targetClass.isArray())
throw new IllegalArgumentException(targetClass + " is an array class");
- Module targetModule = targetClass.getModule();
- Module callerModule = lookup.lookupClass().getModule();
- if (!callerModule.canRead(targetModule))
- throw new IllegalAccessException(callerModule + " does not read " + targetModule);
- if (targetModule.isNamed()) {
- String pn = targetClass.getPackageName();
- assert !pn.isEmpty() : "unnamed package cannot be in named module";
- if (!targetModule.isOpen(pn, callerModule))
- throw new IllegalAccessException(targetModule + " does not open " + pn + " to " + callerModule);
+ // Ensure that we can reason accurately about private and module access.
+ if ((caller.lookupModes() & Lookup.PRIVATE) == 0)
+ throw new IllegalAccessException("caller does not have PRIVATE lookup mode");
+ if ((caller.lookupModes() & Lookup.MODULE) == 0)
+ throw new IllegalAccessException("caller does not have MODULE lookup mode");
+
+ // previous lookup class is never set if it has MODULE access
+ assert caller.previousLookupClass() == null;
+
+ Class<?> callerClass = caller.lookupClass();
+ Module callerModule = callerClass.getModule(); // M1
+ Module targetModule = targetClass.getModule(); // M2
+ Class<?> newPreviousClass = null;
+ int newModes = Lookup.FULL_POWER_MODES;
+
+ if (targetModule != callerModule) {
+ if (!callerModule.canRead(targetModule))
+ throw new IllegalAccessException(callerModule + " does not read " + targetModule);
+ if (targetModule.isNamed()) {
+ String pn = targetClass.getPackageName();
+ assert !pn.isEmpty() : "unnamed package cannot be in named module";
+ if (!targetModule.isOpen(pn, callerModule))
+ throw new IllegalAccessException(targetModule + " does not open " + pn + " to " + callerModule);
+ }
+
+ // M2 != M1, set previous lookup class to M1 and drop MODULE access
+ newPreviousClass = callerClass;
+ newModes &= ~Lookup.MODULE;
}
- if ((lookup.lookupModes() & Lookup.MODULE) == 0)
- throw new IllegalAccessException("lookup does not have MODULE lookup mode");
+
if (!callerModule.isNamed() && targetModule.isNamed()) {
IllegalAccessLogger logger = IllegalAccessLogger.illegalAccessLogger();
if (logger != null) {
- logger.logIfOpenedForIllegalAccess(lookup, targetClass);
+ logger.logIfOpenedForIllegalAccess(caller, targetClass);
}
}
- return new Lookup(targetClass);
+ return Lookup.newLookup(targetClass, newPreviousClass, newModes);
}
/**
@@ -533,6 +575,514 @@
* whose <a href="MethodHandles.Lookup.html#equiv">bytecode behaviors</a> and Java language access permissions
* can be reliably determined and emulated by method handles.
*
+ * <h2><a id="cross-module-lookup"></a>Cross-module lookups</h2>
+ * When a lookup class in one module {@code M1} accesses a class in another module
+ * {@code M2}, extra access checking is performed beyond the access mode bits.
+ * A {@code Lookup} with {@link #PUBLIC} mode and a lookup class in {@code M1}
+ * can access public types in {@code M2} when {@code M2} is readable to {@code M1}
+ * and when the type is in a package of {@code M2} that is exported to
+ * at least {@code M1}.
+ * <p>
+ * A {@code Lookup} on {@code C} can also <em>teleport</em> to a target class
+ * via {@link #in(Class) Lookup.in} and {@link MethodHandles#privateLookupIn(Class, Lookup)
+ * MethodHandles.privateLookupIn} methods.
+ * Teleporting across modules will always record the original lookup class as
+ * the <em>{@linkplain #previousLookupClass() previous lookup class}</em>
+ * and drops {@link Lookup#MODULE MODULE} access.
+ * If the target class is in the same module as the lookup class {@code C},
+ * then the target class becomes the new lookup class
+ * and there is no change to the previous lookup class.
+ * If the target class is in a different module from {@code M1} ({@code C}'s module),
+ * {@code C} becomes the new previous lookup class
+ * and the target class becomes the new lookup class.
+ * In that case, if there was already a previous lookup class in {@code M0},
+ * and it differs from {@code M1} and {@code M2}, then the resulting lookup
+ * drops all privileges.
+ * For example,
+ * <blockquote><pre>
+ * {@code
+ * Lookup lookup = MethodHandles.lookup(); // in class C
+ * Lookup lookup2 = lookup.in(D.class);
+ * MethodHandle mh = lookup2.findStatic(E.class, "m", MT);
+ * }</pre></blockquote>
+ * <p>
+ * The {@link #lookup()} factory method produces a {@code Lookup} object
+ * with {@code null} previous lookup class.
+ * {@link Lookup#in lookup.in(D.class)} transforms the {@code lookup} on class {@code C}
+ * to class {@code D} without elevation of privileges.
+ * If {@code C} and {@code D} are in the same module,
+ * {@code lookup2} records {@code D} as the new lookup class and keeps the
+ * same previous lookup class as the original {@code lookup}, or
+ * {@code null} if not present.
+ * <p>
+ * When a {@code Lookup} teleports from a class
+ * in one nest to another nest, {@code PRIVATE} access is dropped.
+ * When a {@code Lookup} teleports from a class in one package to
+ * another package, {@code PACKAGE} access is dropped.
+ * When a {@code Lookup} teleports from a class in one module to another module,
+ * {@code MODULE} access is dropped.
+ * Teleporting across modules drops the ability to access non-exported classes
+ * in both the module of the new lookup class and the module of the old lookup class
+ * and the resulting {@code Lookup} remains only {@code PUBLIC} access.
+ * A {@code Lookup} can teleport back and forth to a class in the module of
+ * the lookup class and the module of the previous class lookup.
+ * Teleporting across modules can only decrease access but cannot increase it.
+ * Teleporting to some third module drops all accesses.
+ * <p>
+ * In the above example, if {@code C} and {@code D} are in different modules,
+ * {@code lookup2} records {@code D} as its lookup class and
+ * {@code C} as its previous lookup class and {@code lookup2} has only
+ * {@code PUBLIC} access. {@code lookup2} can teleport to other class in
+ * {@code C}'s module and {@code D}'s module.
+ * If class {@code E} is in a third module, {@code lookup2.in(E.class)} creates
+ * a {@code Lookup} on {@code E} with no access and {@code lookup2}'s lookup
+ * class {@code D} is recorded as its previous lookup class.
+ * <p>
+ * Teleporting across modules restricts access to the public types that
+ * both the lookup class and the previous lookup class can equally access
+ * (see below).
+ * <p>
+ * {@link MethodHandles#privateLookupIn(Class, Lookup) MethodHandles.privateLookupIn(T.class, lookup)}
+ * can be used to teleport a {@code lookup} from class {@code C} to class {@code T}
+ * and create a new {@code Lookup} with <a href="#privcc">private access</a>
+ * if the lookup class is allowed to do <em>deep reflection</em> on {@code T}.
+ * The {@code lookup} must have {@link #MODULE} and {@link #PRIVATE} access
+ * to call {@code privateLookupIn}.
+ * A {@code lookup} on {@code C} in module {@code M1} is allowed to do deep reflection
+ * on all classes in {@code M1}. If {@code T} is in {@code M1}, {@code privateLookupIn}
+ * produces a new {@code Lookup} on {@code T} with full capabilities.
+ * A {@code lookup} on {@code C} is also allowed
+ * to do deep reflection on {@code T} in another module {@code M2} if
+ * {@code M1} reads {@code M2} and {@code M2} {@link Module#isOpen(String,Module) opens}
+ * the package containing {@code T} to at least {@code M1}.
+ * {@code T} becomes the new lookup class and {@code C} becomes the new previous
+ * lookup class and {@code MODULE} access is dropped from the resulting {@code Lookup}.
+ * The resulting {@code Lookup} can be used to do member lookup or teleport
+ * to another lookup class by calling {@link #in Lookup::in}. But
+ * it cannot be used to obtain another private {@code Lookup} by calling
+ * {@link MethodHandles#privateLookupIn(Class, Lookup) privateLookupIn}
+ * because it has no {@code MODULE} access.
+ *
+ * <h2><a id="module-access-check"></a>Cross-module access checks</h2>
+ *
+ * A {@code Lookup} with {@link #PUBLIC} or with {@link #UNCONDITIONAL} mode
+ * allows cross-module access. The access checking is performed with respect
+ * to both the lookup class and the previous lookup class if present.
+ * <p>
+ * A {@code Lookup} with {@link #UNCONDITIONAL} mode can access public type
+ * in all modules when the type is in a package that is {@linkplain Module#isExported(String)
+ * exported unconditionally}.
+ * <p>
+ * If a {@code Lookup} on {@code LC} in {@code M1} has no previous lookup class,
+ * the lookup with {@link #PUBLIC} mode can access all public types in modules
+ * that are readable to {@code M1} and the type is in a package that is exported
+ * at least to {@code M1}.
+ * <p>
+ * If a {@code Lookup} on {@code LC} in {@code M1} has a previous lookup class
+ * {@code PLC} on {@code M0}, the lookup with {@link #PUBLIC} mode can access
+ * the intersection of all public types that are accessible to {@code M1}
+ * with all public types that are accessible to {@code M0}. {@code M0}
+ * reads {@code M1} and hence the set of accessible types includes:
+ *
+ * <table class="striped">
+ * <caption style="display:none">
+ * Public types in the following packages are accessible to the
+ * lookup class and the previous lookup class.
+ * </caption>
+ * <thead>
+ * <tr>
+ * <th scope="col">Equally accessible types to {@code M0} and {@code M1}</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <th scope="row" style="text-align:left">unconditional-exported packages from {@code M1}</th>
+ * </tr>
+ * <tr>
+ * <th scope="row" style="text-align:left">unconditional-exported packages from {@code M0} if {@code M1} reads {@code M0}</th>
+ * </tr>
+ * <tr>
+ * <th scope="row" style="text-align:left">unconditional-exported packages from a third module {@code M2}
+ * if both {@code M0} and {@code M1} read {@code M2}</th>
+ * </tr>
+ * <tr>
+ * <th scope="row" style="text-align:left">qualified-exported packages from {@code M1} to {@code M0}</th>
+ * </tr>
+ * <tr>
+ * <th scope="row" style="text-align:left">qualified-exported packages from {@code M0} to {@code M1}
+ * if {@code M1} reads {@code M0}</th>
+ * </tr>
+ * <tr>
+ * <th scope="row" style="text-align:left">qualified-exported packages from a third module {@code M2} to
+ * both {@code M0} and {@code M1} if both {@code M0} and {@code M1} read {@code M2}</th>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * <h2><a id="access-modes"></a>Access modes</h2>
+ *
+ * The table below shows the access modes of a {@code Lookup} produced by
+ * any of the following factory or transformation methods:
+ * <ul>
+ * <li>{@link #lookup() MethodHandles.lookup()}</li>
+ * <li>{@link #publicLookup() MethodHandles.publicLookup()}</li>
+ * <li>{@link #privateLookupIn(Class, Lookup) MethodHandles.privateLookupIn}</li>
+ * <li>{@link Lookup#in}</li>
+ * <li>{@link Lookup#dropLookupMode(int)}</li>
+ * </ul>
+ *
+ * <table class="striped">
+ * <caption style="display:none">
+ * Access mode summary
+ * </caption>
+ * <thead>
+ * <tr>
+ * <th scope="col">Lookup object</th>
+ * <th style="text-align:center">protected</th>
+ * <th style="text-align:center">private</th>
+ * <th style="text-align:center">package</th>
+ * <th style="text-align:center">module</th>
+ * <th style="text-align:center">public</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <th scope="row" style="text-align:left">{@code CL = MethodHandles.lookup()} in {@code C}</th>
+ * <td style="text-align:center">PRO</td>
+ * <td style="text-align:center">PRI</td>
+ * <td style="text-align:center">PAC</td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <th scope="row" style="text-align:left">{@code CL.in(C1)} same package</th>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">PAC</td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <th scope="row" style="text-align:left">{@code CL.in(C1)} same module</th>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <th scope="row" style="text-align:left">{@code CL.in(D)} different module</th>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">2R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code CL.in(D).in(C)} hop back to module</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">2R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI1 = privateLookupIn(C1,CL)}</td>
+ * <td style="text-align:center">PRO</td>
+ * <td style="text-align:center">PRI</td>
+ * <td style="text-align:center">PAC</td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI1a = privateLookupIn(C,PRI1)}</td>
+ * <td style="text-align:center">PRO</td>
+ * <td style="text-align:center">PRI</td>
+ * <td style="text-align:center">PAC</td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI1.in(C1)} same package</td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">PAC</td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI1.in(C1)} different package</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI1.in(D)} different module</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">2R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI1.dropLookupMode(PROTECTED)}</td>
+ * <td></td>
+ * <td style="text-align:center">PRI</td>
+ * <td style="text-align:center">PAC</td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI1.dropLookupMode(PRIVATE)}</td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">PAC</td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI1.dropLookupMode(PACKAGE)}</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI1.dropLookupMode(MODULE)}</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI1.dropLookupMode(PUBLIC)}</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">none</td>
+ * <tr>
+ * <td>{@code PRI2 = privateLookupIn(D,CL)}</td>
+ * <td style="text-align:center">PRO</td>
+ * <td style="text-align:center">PRI</td>
+ * <td style="text-align:center">PAC</td>
+ * <td></td>
+ * <td style="text-align:center">2R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code privateLookupIn(D,PRI1)}</td>
+ * <td style="text-align:center">PRO</td>
+ * <td style="text-align:center">PRI</td>
+ * <td style="text-align:center">PAC</td>
+ * <td></td>
+ * <td style="text-align:center">2R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code privateLookupIn(C,PRI2)} fails</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">IAE</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI2.in(D2)} same package</td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">PAC</td>
+ * <td></td>
+ * <td style="text-align:center">2R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI2.in(D2)} different package</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">2R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI2.in(C1)} hop back to module</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">2R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI2.in(E)} hop to third module</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">none</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI2.dropLookupMode(PROTECTED)}</td>
+ * <td></td>
+ * <td style="text-align:center">PRI</td>
+ * <td style="text-align:center">PAC</td>
+ * <td></td>
+ * <td style="text-align:center">2R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI2.dropLookupMode(PRIVATE)}</td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">PAC</td>
+ * <td></td>
+ * <td style="text-align:center">2R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI2.dropLookupMode(PACKAGE)}</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">2R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI2.dropLookupMode(MODULE)}</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">2R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PRI2.dropLookupMode(PUBLIC)}</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">none</td>
+ * </tr>
+ * <tr>
+ * <td>{@code CL.dropLookupMode(PROTECTED)}</td>
+ * <td></td>
+ * <td style="text-align:center">PRI</td>
+ * <td style="text-align:center">PAC</td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code CL.dropLookupMode(PRIVATE)}</td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">PAC</td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code CL.dropLookupMode(PACKAGE)}</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">MOD</td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code CL.dropLookupMode(MODULE)}</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">1R</td>
+ * </tr>
+ * <tr>
+ * <td>{@code CL.dropLookupMode(PUBLIC)}</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">none</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PUB = publicLookup()}</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">U</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PUB.in(D)} different module</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">U</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PUB.in(D).in(E)} third module</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">U</td>
+ * </tr>
+ * <tr>
+ * <td>{@code PUB.dropLookupMode(UNCONDITIONAL)}</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">none</td>
+ * </tr>
+ * <tr>
+ * <td>{@code privateLookupIn(C1,PUB)} fails</td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">IAE</td>
+ * </tr>
+ * <tr>
+ * <td>{@code ANY.in(X)}, for inaccessible <code>X</code></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td></td>
+ * <td style="text-align:center">none</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * <p>
+ * Notes:
+ * <ul>
+ * <li>Class {@code C} and class {@code C1} are in module {@code M1},
+ * but {@code D} and {@code D2} are in module {@code M2}, and {@code E}
+ * is in module {@code M3}. {@code X} stands for class which is inaccessible
+ * to the lookup. {@code ANY} stands for any of the example lookups.</li>
+ * <li>{@code PRO} indicates {@link #PROTECTED} bit set,
+ * {@code PRI} indicates {@link #PRIVATE} bit set,
+ * {@code PAC} indicates {@link #PACKAGE} bit set,
+ * {@code MOD} indicates {@link #MODULE} bit set,
+ * {@code 1R} and {@code 2R} indicate {@link #PUBLIC} bit set,
+ * {@code U} indicates {@link #UNCONDITIONAL} bit set,
+ * {@code IAE} indicates {@code IllegalAccessException} thrown.</li>
+ * <li>Public access comes in three kinds:
+ * <ul>
+ * <li>unconditional ({@code U}): the lookup assumes readability.
+ * The lookup has {@code null} previous lookup class.
+ * <li>one-module-reads ({@code 1R}): the module access checking is
+ * performed with respect to the lookup class. The lookup has {@code null}
+ * previous lookup class.
+ * <li>two-module-reads ({@code 2R}): the module access checking is
+ * performed with respect to the lookup class and the previous lookup class.
+ * The lookup has a non-null previous lookup class which is in a
+ * different module from the current lookup class.
+ * </ul>
+ * <li>Any attempt to reach a third module loses all access.</li>
+ * <li>If a target class {@code X} is not accessible to {@code Lookup::in}
+ * all access modes are dropped.</li>
+ * </ul>
+ *
* <h2><a id="secmgr"></a>Security manager interactions</h2>
* Although bytecode instructions can only refer to classes in
* a related class loader, this API can search for methods in any
@@ -645,6 +1195,9 @@
/** The class on behalf of whom the lookup is being performed. */
private final Class<?> lookupClass;
+ /** previous lookup class */
+ private final Class<?> prevLookupClass;
+
/** The allowed sorts of members which may be looked up (PUBLIC, etc.). */
private final int allowedModes;
@@ -656,6 +1209,10 @@
* which may contribute to the result of {@link #lookupModes lookupModes}.
* The value, {@code 0x01}, happens to be the same as the value of the
* {@code public} {@linkplain java.lang.reflect.Modifier#PUBLIC modifier bit}.
+ * <p>
+ * A {@code Lookup} with this lookup mode performs cross-module access check
+ * with respect to the {@linkplain #lookupClass() lookup class} and
+ * {@linkplain #previousLookupClass() previous lookup class} if present.
*/
public static final int PUBLIC = Modifier.PUBLIC;
@@ -680,7 +1237,7 @@
*/
public static final int PACKAGE = Modifier.STATIC;
- /** A single-bit mask representing {@code module} access (default access),
+ /** A single-bit mask representing {@code module} access,
* which may contribute to the result of {@link #lookupModes lookupModes}.
* The value is {@code 0x10}, which does not correspond meaningfully to
* any particular {@linkplain java.lang.reflect.Modifier modifier bit}.
@@ -688,6 +1245,10 @@
* with this lookup mode can access all public types in the module of the
* lookup class and public types in packages exported by other modules
* to the module of the lookup class.
+ * <p>
+ * If this lookup mode is set, the {@linkplain #previousLookupClass()
+ * previous lookup class} is always {@code null}.
+ *
* @since 9
* @spec JPMS
*/
@@ -699,10 +1260,14 @@
* any particular {@linkplain java.lang.reflect.Modifier modifier bit}.
* A {@code Lookup} with this lookup mode assumes {@linkplain
* java.lang.Module#canRead(java.lang.Module) readability}.
- * In conjunction with the {@code PUBLIC} modifier bit, a {@code Lookup}
- * with this lookup mode can access all public members of public types
- * of all modules where the type is in a package that is {@link
+ * This lookup mode can access all public members of public types
+ * of all modules when the type is in a package that is {@link
* java.lang.Module#isExported(String) exported unconditionally}.
+ *
+ * <p>
+ * If this lookup mode is set, the {@linkplain #previousLookupClass()
+ * previous lookup class} is always {@code null}.
+ *
* @since 9
* @spec JPMS
* @see #publicLookup()
@@ -713,24 +1278,55 @@
private static final int FULL_POWER_MODES = (ALL_MODES & ~UNCONDITIONAL);
private static final int TRUSTED = -1;
+ /*
+ * Adjust PUBLIC => PUBLIC|MODULE|UNCONDITIONAL
+ * Adjust 0 => PACKAGE
+ */
private static int fixmods(int mods) {
mods &= (ALL_MODES - PACKAGE - MODULE - UNCONDITIONAL);
- return (mods != 0) ? mods : (PACKAGE | MODULE | UNCONDITIONAL);
+ if (Modifier.isPublic(mods))
+ mods |= UNCONDITIONAL;
+ return (mods != 0) ? mods : PACKAGE;
}
/** Tells which class is performing the lookup. It is this class against
* which checks are performed for visibility and access permissions.
* <p>
+ * If this lookup object has a {@linkplain #previousLookupClass() previous lookup class},
+ * access checks are performed against both the lookup class and the previous lookup class.
+ * <p>
* The class implies a maximum level of access permission,
* but the permissions may be additionally limited by the bitmask
* {@link #lookupModes lookupModes}, which controls whether non-public members
* can be accessed.
* @return the lookup class, on behalf of which this lookup object finds members
+ * @see <a href="#cross-module-lookup">Cross-module lookups</a>
*/
public Class<?> lookupClass() {
return lookupClass;
}
+ /** Reports a lookup class in another module that this lookup object
+ * was previously teleported from, or {@code null}.
+ * <p>
+ * A {@code Lookup} object produced by the factory methods, such as the
+ * {@link #lookup() lookup()} and {@link #publicLookup() publicLookup()} method,
+ * has {@code null} previous lookup class.
+ * A {@code Lookup} object has a non-null previous lookup class
+ * when this lookup was teleported from an old lookup class
+ * in one module to a new lookup class in another module.
+ *
+ * @return the lookup class in another module that this lookup object was
+ * previously teleported from, or {@code null}
+ * @since 14
+ * @see #in(Class)
+ * @see MethodHandles#privateLookupIn(Class, Lookup)
+ * @see <a href="#cross-module-lookup">Cross-module lookups</a>
+ */
+ public Class<?> previousLookupClass() {
+ return prevLookupClass;
+ }
+
// This is just for calling out to MethodHandleImpl.
private Class<?> lookupClassOrNull() {
return (allowedModes == TRUSTED) ? null : lookupClass;
@@ -774,48 +1370,69 @@
* which in turn is called by a method not in this package.
*/
Lookup(Class<?> lookupClass) {
- this(lookupClass, FULL_POWER_MODES);
+ this(lookupClass, null, FULL_POWER_MODES);
// make sure we haven't accidentally picked up a privileged class:
checkUnprivilegedlookupClass(lookupClass);
}
- private Lookup(Class<?> lookupClass, int allowedModes) {
+ private Lookup(Class<?> lookupClass, Class<?> prevLookupClass, int allowedModes) {
+ assert prevLookupClass == null || ((allowedModes & MODULE) == 0
+ && prevLookupClass.getModule() != lookupClass.getModule());
+
this.lookupClass = lookupClass;
+ this.prevLookupClass = prevLookupClass;
this.allowedModes = allowedModes;
}
+ private static Lookup newLookup(Class<?> lookupClass, Class<?> prevLookupClass, int allowedModes) {
+ // make sure we haven't accidentally picked up a privileged class:
+ checkUnprivilegedlookupClass(lookupClass);
+ return new Lookup(lookupClass, prevLookupClass, allowedModes);
+ }
+
/**
* Creates a lookup on the specified new lookup class.
* The resulting object will report the specified
* class as its own {@link #lookupClass() lookupClass}.
+ *
* <p>
* However, the resulting {@code Lookup} object is guaranteed
* to have no more access capabilities than the original.
* In particular, access capabilities can be lost as follows:<ul>
- * <li>If the old lookup class is in a {@link Module#isNamed() named} module, and
- * the new lookup class is in a different module {@code M}, then no members, not
- * even public members in {@code M}'s exported packages, will be accessible.
- * The exception to this is when this lookup is {@link #publicLookup()
- * publicLookup}, in which case {@code PUBLIC} access is not lost.
- * <li>If the old lookup class is in an unnamed module, and the new lookup class
- * is a different module then {@link #MODULE MODULE} access is lost.
- * <li>If the new lookup class differs from the old one then {@code UNCONDITIONAL} is lost.
+ * <li>If the new lookup class is in a different module from the old one,
+ * i.e. {@link #MODULE MODULE} access is lost.
* <li>If the new lookup class is in a different package
- * than the old one, protected and default (package) members will not be accessible.
+ * than the old one, protected and default (package) members will not be accessible,
+ * i.e. {@link #PROTECTED PROTECTED} and {@link #PACKAGE PACKAGE} access are lost.
* <li>If the new lookup class is not within the same package member
* as the old one, private members will not be accessible, and protected members
- * will not be accessible by virtue of inheritance.
+ * will not be accessible by virtue of inheritance,
+ * i.e. {@link #PRIVATE PRIVATE} access is lost.
* (Protected members may continue to be accessible because of package sharing.)
- * <li>If the new lookup class is not accessible to the old lookup class,
- * then no members, not even public members, will be accessible.
- * (In all other cases, public members will continue to be accessible.)
+ * <li>If the new lookup class is not
+ * {@linkplain #accessClass(Class) accessible} to this lookup,
+ * then no members, not even public members, will be accessible
+ * i.e. all access modes are lost.
+ * <li>If the new lookup class, the old lookup class and the previous lookup class
+ * are all in different modules i.e. teleporting to a third module,
+ * all access modes are lost.
* </ul>
* <p>
+ * The new previous lookup class is chosen as follows:
+ * <ul>
+ * <li>If the new lookup object has {@link #UNCONDITIONAL UNCONDITIONAL} bit,
+ * the new previous lookup class is {@code null}.
+ * <li>If the new lookup class is in the same module as the old lookup class,
+ * the new previous lookup class is the old previous lookup class.
+ * <li>If the new lookup class is in a different module from the old lookup class,
+ * the new previous lookup class is the the old lookup class.
+ *</ul>
+ * <p>
* The resulting lookup's capabilities for loading classes
* (used during {@link #findClass} invocations)
* are determined by the lookup class' loader,
* which may change due to this operation.
- *
+ * <p>
* @param requestedLookupClass the desired lookup class for the new lookup object
* @return a lookup object which reports the desired lookup class, or the same object
* if there is no change
@@ -823,22 +1440,32 @@
*
* @revised 9
* @spec JPMS
+ * @see #accessClass(Class)
+ * @see <a href="#cross-module-lookup">Cross-module lookups</a>
*/
public Lookup in(Class<?> requestedLookupClass) {
Objects.requireNonNull(requestedLookupClass);
if (allowedModes == TRUSTED) // IMPL_LOOKUP can make any lookup at all
- return new Lookup(requestedLookupClass, FULL_POWER_MODES);
+ return new Lookup(requestedLookupClass, null, FULL_POWER_MODES);
if (requestedLookupClass == this.lookupClass)
return this; // keep same capabilities
int newModes = (allowedModes & FULL_POWER_MODES);
- if (!VerifyAccess.isSameModule(this.lookupClass, requestedLookupClass)) {
- // Need to drop all access when teleporting from a named module to another
- // module. The exception is publicLookup where PUBLIC is not lost.
- if (this.lookupClass.getModule().isNamed()
- && (this.allowedModes & UNCONDITIONAL) == 0)
+ Module fromModule = this.lookupClass.getModule();
+ Module targetModule = requestedLookupClass.getModule();
+ Class<?> plc = this.previousLookupClass();
+ if ((this.allowedModes & UNCONDITIONAL) != 0) {
+ assert plc == null;
+ newModes = UNCONDITIONAL;
+ } else if (fromModule != targetModule) {
+ if (plc != null && !VerifyAccess.isSameModule(plc, requestedLookupClass)) {
+ // allow hopping back and forth between fromModule and plc's module
+ // but not the third module
newModes = 0;
- else
- newModes &= ~(MODULE|PACKAGE|PRIVATE|PROTECTED);
+ }
+ // drop MODULE access
+ newModes &= ~(MODULE|PACKAGE|PRIVATE|PROTECTED);
+ // teleport from this lookup class
+ plc = this.lookupClass;
}
if ((newModes & PACKAGE) != 0
&& !VerifyAccess.isSamePackage(this.lookupClass, requestedLookupClass)) {
@@ -849,29 +1476,39 @@
&& !VerifyAccess.isSamePackageMember(this.lookupClass, requestedLookupClass)) {
newModes &= ~(PRIVATE|PROTECTED);
}
- if ((newModes & PUBLIC) != 0
- && !VerifyAccess.isClassAccessible(requestedLookupClass, this.lookupClass, allowedModes)) {
+ if ((newModes & (PUBLIC|UNCONDITIONAL)) != 0
+ && !VerifyAccess.isClassAccessible(requestedLookupClass, this.lookupClass, this.prevLookupClass, allowedModes)) {
// The requested class it not accessible from the lookup class.
// No permissions.
newModes = 0;
}
-
- checkUnprivilegedlookupClass(requestedLookupClass);
- return new Lookup(requestedLookupClass, newModes);
+ return newLookup(requestedLookupClass, plc, newModes);
}
-
/**
* Creates a lookup on the same lookup class which this lookup object
* finds members, but with a lookup mode that has lost the given lookup mode.
* The lookup mode to drop is one of {@link #PUBLIC PUBLIC}, {@link #MODULE
* MODULE}, {@link #PACKAGE PACKAGE}, {@link #PROTECTED PROTECTED} or {@link #PRIVATE PRIVATE}.
- * {@link #PROTECTED PROTECTED} and {@link #UNCONDITIONAL UNCONDITIONAL} are always
- * dropped and so the resulting lookup mode will never have these access capabilities.
+ * {@link #PROTECTED PROTECTED} is always
+ * dropped and so the resulting lookup mode will never have this access capability.
* When dropping {@code PACKAGE} then the resulting lookup will not have {@code PACKAGE}
* or {@code PRIVATE} access. When dropping {@code MODULE} then the resulting lookup will
* not have {@code MODULE}, {@code PACKAGE}, or {@code PRIVATE} access. If {@code PUBLIC}
+ * is dropped then the resulting lookup has no access. If {@code UNCONDITIONAL}
* is dropped then the resulting lookup has no access.
+ *
+ * @apiNote
+ * A lookup with {@code PACKAGE} but not {@code PRIVATE} mode can safely
+ * delegate non-public access within the package of the lookup class without
+ * conferring private access. A lookup with {@code MODULE} but not
+ * {@code PACKAGE} mode can safely delegate {@code PUBLIC} access within
+ * the module of the lookup class without conferring package access.
+ * A lookup with a {@linkplain #previousLookupClass() previous lookup class}
+ * (and {@code PUBLIC} but not {@code MODULE} mode) can safely delegate access
+ * to public classes accessible to both the module of the lookup class
+ * and the module of the previous lookup class.
+ *
* @param modeToDrop the lookup mode to drop
* @return a lookup object which lacks the indicated mode, or the same object if there is no change
* @throws IllegalArgumentException if {@code modeToDrop} is not one of {@code PUBLIC},
@@ -881,9 +1518,9 @@
*/
public Lookup dropLookupMode(int modeToDrop) {
int oldModes = lookupModes();
- int newModes = oldModes & ~(modeToDrop | PROTECTED | UNCONDITIONAL);
+ int newModes = oldModes & ~(modeToDrop | PROTECTED);
switch (modeToDrop) {
- case PUBLIC: newModes &= ~(ALL_MODES); break;
+ case PUBLIC: newModes &= ~(FULL_POWER_MODES); break;
case MODULE: newModes &= ~(PACKAGE | PRIVATE); break;
case PACKAGE: newModes &= ~(PRIVATE); break;
case PROTECTED:
@@ -892,7 +1529,7 @@
default: throw new IllegalArgumentException(modeToDrop + " is not a valid mode to drop");
}
if (newModes == oldModes) return this; // return self if no change
- return new Lookup(lookupClass(), newModes);
+ return newLookup(lookupClass(), previousLookupClass(), newModes);
}
/**
@@ -997,13 +1634,13 @@
static { IMPL_NAMES.getClass(); }
/** Package-private version of lookup which is trusted. */
- static final Lookup IMPL_LOOKUP = new Lookup(Object.class, TRUSTED);
+ static final Lookup IMPL_LOOKUP = new Lookup(Object.class, null, TRUSTED);
/** Version of lookup which is trusted minimally.
* It can only be used to create method handles to publicly accessible
* members in packages that are exported unconditionally.
*/
- static final Lookup PUBLIC_LOOKUP = new Lookup(Object.class, (PUBLIC|UNCONDITIONAL));
+ static final Lookup PUBLIC_LOOKUP = new Lookup(Object.class, null, UNCONDITIONAL);
private static void checkUnprivilegedlookupClass(Class<?> lookupClass) {
String name = lookupClass.getName();
@@ -1013,6 +1650,8 @@
/**
* Displays the name of the class from which lookups are to be made.
+ * followed with "/" and the name of the {@linkplain #previousLookupClass()
+ * previous lookup class} if present.
* (The name is the one reported by {@link java.lang.Class#getName() Class.getName}.)
* If there are restrictions on the access permitted to this lookup,
* this is indicated by adding a suffix to the class name, consisting
@@ -1020,14 +1659,14 @@
* allowed access, and is chosen as follows:
* <ul>
* <li>If no access is allowed, the suffix is "/noaccess".
+ * <li>If only unconditional access is allowed, the suffix is "/publicLookup".
* <li>If only public access to types in exported packages is allowed, the suffix is "/public".
- * <li>If only public access and unconditional access are allowed, the suffix is "/publicLookup".
* <li>If only public and module access are allowed, the suffix is "/module".
- * <li>If only public, module and package access are allowed, the suffix is "/package".
- * <li>If only public, module, package, and private access are allowed, the suffix is "/private".
+ * <li>If public and package access are allowed, the suffix is "/package".
+ * <li>If public, package, and private access are allowed, the suffix is "/private".
* </ul>
- * If none of the above cases apply, it is the case that full
- * access (public, module, package, private, and protected) is allowed.
+ * If none of the above cases apply, it is the case that full access
+ * (public, module, package, private, and protected) is allowed.
* In this case, no suffix is added.
* This is true only of an object obtained originally from
* {@link java.lang.invoke.MethodHandles#lookup MethodHandles.lookup}.
@@ -1047,20 +1686,25 @@
@Override
public String toString() {
String cname = lookupClass.getName();
+ if (prevLookupClass != null)
+ cname += "/" + prevLookupClass.getName();
switch (allowedModes) {
case 0: // no privileges
return cname + "/noaccess";
+ case UNCONDITIONAL:
+ return cname + "/publicLookup";
case PUBLIC:
return cname + "/public";
- case PUBLIC|UNCONDITIONAL:
- return cname + "/publicLookup";
case PUBLIC|MODULE:
return cname + "/module";
+ case PUBLIC|PACKAGE:
case PUBLIC|MODULE|PACKAGE:
return cname + "/package";
- case FULL_POWER_MODES & ~PROTECTED:
- return cname + "/private";
+ case FULL_POWER_MODES & (~PROTECTED):
+ case FULL_POWER_MODES & ~(PROTECTED|MODULE):
+ return cname + "/private";
case FULL_POWER_MODES:
+ case FULL_POWER_MODES & (~MODULE):
return cname;
case TRUSTED:
return "/trusted"; // internal only; not exported
@@ -1301,24 +1945,75 @@
}
/**
- * Determines if a class can be accessed from the lookup context defined by this {@code Lookup} object. The
- * static initializer of the class is not run.
+ * Determines if a class can be accessed from the lookup context defined by
+ * this {@code Lookup} object. The static initializer of the class is not run.
+ * <p>
+ * If the {@code targetClass} is in the same module as the lookup class,
+ * the lookup class is {@code LC} in module {@code M1} and
+ * the previous lookup class is in module {@code M0} or
+ * {@code null} if not present,
+ * {@code targetClass} is accessible if and only if one of the following is true:
+ * <ul>
+ * <li>If this lookup has {@link #PRIVATE} access, {@code targetClass} is
+ * {@code LC} or other class in the same nest of {@code LC}.</li>
+ * <li>If this lookup has {@link #PACKAGE} access, {@code targetClass} is
+ * in the same runtime package of {@code LC}.</li>
+ * <li>If this lookup has {@link #MODULE} access, {@code targetClass} is
+ * a public type in {@code M1}.</li>
+ * <li>If this lookup has {@link #PUBLIC} access, {@code targetClass} is
+ * a public type in a package exported by {@code M1} to at least {@code M0}
+ * if the previous lookup class is present; otherwise, {@code targetClass}
+ * is a public type in a package exported by {@code M1} unconditionally.</li>
+ * </ul>
+ *
+ * <p>
+ * Otherwise, if this lookup has {@link #UNCONDITIONAL} access, this lookup
+ * can access public types in all modules when the type is in a package
+ * that is exported unconditionally.
* <p>
- * The lookup context here is determined by the {@linkplain #lookupClass() lookup class} and the
- * {@linkplain #lookupModes() lookup modes}.
+ * Otherwise, the target class is in a different module from {@code lookupClass},
+ * and if this lookup does not have {@code PUBLIC} access, {@code lookupClass}
+ * is inaccessible.
+ * <p>
+ * Otherwise, if this lookup has no {@linkplain #previousLookupClass() previous lookup class},
+ * {@code M1} is the module containing {@code lookupClass} and
+ * {@code M2} is the module containing {@code targetClass},
+ * then {@code targetClass} is accessible if and only if
+ * <ul>
+ * <li>{@code M1} reads {@code M2}, and
+ * <li>{@code targetClass} is public and in a package exported by
+ * {@code M2} at least to {@code M1}.
+ * </ul>
+ * <p>
+ * Otherwise, if this lookup has a {@linkplain #previousLookupClass() previous lookup class},
+ * {@code M1} and {@code M2} are as before, and {@code M0} is the module
+ * containing the previous lookup class, then {@code targetClass} is accessible
+ * if and only if one of the following is true:
+ * <ul>
+ * <li>{@code targetClass} is in {@code M0} and {@code M1}
+ * {@linkplain Module#reads reads} {@code M0} and the type is
+ * in a package that is exported to at least {@code M1}.
+ * <li>{@code targetClass} is in {@code M1} and {@code M0}
+ * {@linkplain Module#reads reads} {@code M1} and the type is
+ * in a package that is exported to at least {@code M0}.
+ * <li>{@code targetClass} is in a third module {@code M2} and both {@code M0}
+ * and {@code M1} reads {@code M2} and the type is in a package
+ * that is exported to at least both {@code M0} and {@code M2}.
+ * </ul>
+ * <p>
+ * Otherwise, {@code targetClass} is not accessible.
*
* @param targetClass the class to be access-checked
- *
* @return the class that has been access-checked
- *
- * @throws IllegalAccessException if the class is not accessible from the lookup class, using the allowed access
- * modes.
+ * @throws IllegalAccessException if the class is not accessible from the lookup class
+ * and previous lookup class, if present, using the allowed access modes.
* @exception SecurityException if a security manager is present and it
* <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
* @since 9
+ * @see <a href="#cross-module-lookup">Cross-module lookups</a>
*/
public Class<?> accessClass(Class<?> targetClass) throws IllegalAccessException {
- if (!VerifyAccess.isClassAccessible(targetClass, lookupClass, allowedModes)) {
+ if (!VerifyAccess.isClassAccessible(targetClass, lookupClass, prevLookupClass, allowedModes)) {
throw new MemberName(targetClass).makeAccessException("access violation", this);
}
checkSecurityManager(targetClass, null);
@@ -2083,7 +2778,7 @@
boolean isClassAccessible(Class<?> refc) {
Objects.requireNonNull(refc);
Class<?> caller = lookupClassOrNull();
- return caller == null || VerifyAccess.isClassAccessible(refc, caller, allowedModes);
+ return caller == null || VerifyAccess.isClassAccessible(refc, caller, prevLookupClass, allowedModes);
}
/** Check name for an illegal leading "<" character. */
@@ -2220,7 +2915,7 @@
int requestedModes = fixmods(mods); // adjust 0 => PACKAGE
if ((requestedModes & allowedModes) != 0) {
if (VerifyAccess.isMemberAccessible(refc, m.getDeclaringClass(),
- mods, lookupClass(), allowedModes))
+ mods, lookupClass(), previousLookupClass(), allowedModes))
return;
} else {
// Protected members can also be checked as if they were package-private.
@@ -2239,9 +2934,10 @@
(defc == refc ||
Modifier.isPublic(refc.getModifiers())));
if (!classOK && (allowedModes & PACKAGE) != 0) {
- classOK = (VerifyAccess.isClassAccessible(defc, lookupClass(), FULL_POWER_MODES) &&
+ // ignore previous lookup class to check if default package access
+ classOK = (VerifyAccess.isClassAccessible(defc, lookupClass(), null, FULL_POWER_MODES) &&
(defc == refc ||
- VerifyAccess.isClassAccessible(refc, lookupClass(), FULL_POWER_MODES)));
+ VerifyAccess.isClassAccessible(refc, lookupClass(), null, FULL_POWER_MODES)));
}
if (!classOK)
return "class is not public";
--- a/src/java.base/share/classes/sun/invoke/util/VerifyAccess.java Wed Jul 24 12:49:44 2019 +0530
+++ b/src/java.base/share/classes/sun/invoke/util/VerifyAccess.java Thu Jul 25 12:23:54 2019 +0530
@@ -88,18 +88,20 @@
* @param defc the class in which the proposed member is actually defined
* @param mods modifier flags for the proposed member
* @param lookupClass the class for which the access check is being made
+ * @param prevLookupClass the class for which the access check is being made
+ * @param allowedModes allowed modes
* @return true iff the accessing class can access such a member
*/
public static boolean isMemberAccessible(Class<?> refc, // symbolic ref class
Class<?> defc, // actual def class
int mods, // actual member mods
Class<?> lookupClass,
+ Class<?> prevLookupClass,
int allowedModes) {
if (allowedModes == 0) return false;
- assert((allowedModes & PUBLIC) != 0 &&
- (allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED)) == 0);
+ assert((allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED)) == 0);
// The symbolic reference class (refc) must always be fully verified.
- if (!isClassAccessible(refc, lookupClass, allowedModes)) {
+ if (!isClassAccessible(refc, lookupClass, prevLookupClass, allowedModes)) {
return false;
}
// Usually refc and defc are the same, but verify defc also in case they differ.
@@ -109,6 +111,7 @@
switch (mods & ALL_ACCESS_MODES) {
case PUBLIC:
+ assert (allowedModes & PUBLIC) != 0 || (allowedModes & UNCONDITIONAL_ALLOWED) != 0;
return true; // already checked above
case PROTECTED:
assert !defc.isInterface(); // protected members aren't allowed in interfaces
@@ -175,14 +178,23 @@
* package that is exported to the module that contains D.
* <li>C and D are members of the same runtime package.
* </ul>
+ *
* @param refc the symbolic reference class to which access is being checked (C)
* @param lookupClass the class performing the lookup (D)
+ * @param prevLookupClass the class from which the lookup was teleported or null
+ * @param allowedModes allowed modes
*/
- public static boolean isClassAccessible(Class<?> refc, Class<?> lookupClass,
+ public static boolean isClassAccessible(Class<?> refc,
+ Class<?> lookupClass,
+ Class<?> prevLookupClass,
int allowedModes) {
if (allowedModes == 0) return false;
- assert((allowedModes & PUBLIC) != 0 &&
- (allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED)) == 0);
+ assert((allowedModes & ~(ALL_ACCESS_MODES|PACKAGE_ALLOWED|MODULE_ALLOWED|UNCONDITIONAL_ALLOWED)) == 0);
+
+ if ((allowedModes & PACKAGE_ALLOWED) != 0 &&
+ isSamePackage(lookupClass, refc))
+ return true;
+
int mods = getClassModifiers(refc);
if (isPublic(mods)) {
@@ -195,37 +207,62 @@
return true;
}
- // trivially allow
- if ((allowedModes & MODULE_ALLOWED) != 0 &&
- (lookupModule == refModule))
+ // allow access to public types in all unconditionally exported packages
+ if ((allowedModes & UNCONDITIONAL_ALLOWED) != 0) {
+ return refModule.isExported(refc.getPackageName());
+ }
+
+ if (lookupModule == refModule && prevLookupClass == null) {
+ // allow access to all public types in lookupModule
+ if ((allowedModes & MODULE_ALLOWED) != 0)
+ return true;
+
+ assert (allowedModes & PUBLIC) != 0;
+ return refModule.isExported(refc.getPackageName());
+ }
+
+ // cross-module access
+ // 1. refc is in different module from lookupModule, or
+ // 2. refc is in lookupModule and a different module from prevLookupModule
+ Module prevLookupModule = prevLookupClass != null ? prevLookupClass.getModule()
+ : null;
+ assert refModule != lookupModule || refModule != prevLookupModule;
+ if (isModuleAccessible(refc, lookupModule, prevLookupModule))
return true;
- // check readability when UNCONDITIONAL not allowed
- if (((allowedModes & UNCONDITIONAL_ALLOWED) != 0)
- || lookupModule.canRead(refModule)) {
-
- // check that refc is in an exported package
- if ((allowedModes & MODULE_ALLOWED) != 0) {
- if (refModule.isExported(refc.getPackageName(), lookupModule))
- return true;
- } else {
- // exported unconditionally
- if (refModule.isExported(refc.getPackageName()))
- return true;
- }
-
- // not exported but allow access during VM initialization
- // because java.base does not have its exports setup
- if (!jdk.internal.misc.VM.isModuleSystemInited())
- return true;
- }
+ // not exported but allow access during VM initialization
+ // because java.base does not have its exports setup
+ if (!jdk.internal.misc.VM.isModuleSystemInited())
+ return true;
// public class not accessible to lookupClass
return false;
}
- if ((allowedModes & PACKAGE_ALLOWED) != 0 &&
- isSamePackage(lookupClass, refc))
- return true;
+
+ return false;
+ }
+
+ /*
+ * Tests if a class or interface REFC is accessible to m1 and m2 where m2
+ * may be null.
+ *
+ * A class or interface REFC in m is accessible to m1 and m2 if and only if
+ * both m1 and m2 read m and m exports the package of REFC at least to
+ * both m1 and m2.
+ */
+ public static boolean isModuleAccessible(Class<?> refc, Module m1, Module m2) {
+ Module refModule = refc.getModule();
+ assert refModule != m1 || refModule != m2;
+ int mods = getClassModifiers(refc);
+ if (isPublic(mods)) {
+ if (m1.canRead(refModule) && (m2 == null || m2.canRead(refModule))) {
+ String pn = refc.getPackageName();
+
+ // refc is exported package to at least both m1 and m2
+ if (refModule.isExported(pn, m1) && (m2 == null || refModule.isExported(pn, m2)))
+ return true;
+ }
+ }
return false;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/gc/g1/TestNoUseHCC.java Thu Jul 25 12:23:54 2019 +0530
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2019, 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 gc.g1;
+
+/*
+ * @test TestNoUseHCC
+ * @summary Check that G1 survives a GC without HCC enabled
+ * @requires vm.gc.G1
+ * @key gc
+ * @modules java.base/jdk.internal.misc
+ * @library /test/lib
+ * @build sun.hotspot.WhiteBox
+ * @run driver ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main/othervm -Xbootclasspath/a:. -Xlog:gc+phases=debug -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC -Xmx64M -XX:G1ConcRSLogCacheSize=0 gc.g1.TestNoUseHCC
+ */
+
+import sun.hotspot.WhiteBox;
+
+public class TestNoUseHCC {
+
+ private static final WhiteBox WB = WhiteBox.getWhiteBox();
+
+ public static void main(String [] args) {
+ WB.youngGC();
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/condy/staticInit/Example.jasm Thu Jul 25 12:23:54 2019 +0530
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+// This class gets an initialization error in a condy invokestatic. Need jasm so that StaticInit isn't
+// initialized before the condy call.
+// Test that second invocation gets same error as first.
+
+public class Example
+ version 55:0
+{
+
+
+static Method $jacocoInit:"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;"
+ stack 1 locals 3
+{
+ invokestatic Method StaticInit.get:"()Ljava/lang/Object;";
+ areturn;
+}
+
+public static Method foo:"()V"
+ stack 1 locals 2
+{
+ ldc Dynamic REF_invokeStatic:Example.$jacocoInit:"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;":$jacocoData:"Ljava/lang/Object;";
+ astore_1;
+ return;
+}
+
+public static Method main:"([Ljava/lang/String;)V"
+ stack 1 locals 2
+{
+ try t0;
+ invokestatic Method Example.foo:"()V";
+ endtry t0;
+ goto L7;
+ catch t0 java/lang/Error;
+ stack_frame_type stack1;
+ stack_map class java/lang/Error;
+ astore_1;
+ aload_1;
+ invokevirtual Method java/lang/Error.printStackTrace:"()V";
+ L7: stack_frame_type same;
+ invokestatic Method Example.foo:"()V";
+ return;
+}
+} // end Class Example
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/condy/staticInit/StaticInit.java Thu Jul 25 12:23:54 2019 +0530
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+class StaticInit {
+ static {
+ if (true)
+ throw new RuntimeException();
+ }
+
+ static Object get() {
+ return new Object();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/condy/staticInit/TestInitException.java Thu Jul 25 12:23:54 2019 +0530
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2019, 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 8228485
+ * @summary Correctly handle initialization error for Condy BSM.
+ * @modules java.base/jdk.internal.misc
+ * @library /test/lib
+ * @compile Example.jasm
+ * @compile StaticInit.java
+ * @run main/othervm TestInitException
+ */
+
+import jdk.test.lib.process.ProcessTools;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class TestInitException {
+ public static void main(java.lang.String[] unused) throws Exception {
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("Example");
+ OutputAnalyzer oa = new OutputAnalyzer(pb.start());
+ // First call stack trace
+ oa.shouldContain("at Example.$jacocoInit(Example.jasm)");
+ oa.shouldContain("Caused by: java.lang.RuntimeException");
+ oa.shouldContain("at StaticInit.<clinit>(StaticInit.java:27)");
+ // Second call stack trace, with the message
+ oa.shouldContain("java.lang.ExceptionInInitializerError: $jacocoData");
+ oa.shouldContain("at Example.foo(Example.jasm)");
+ oa.shouldContain("at Example.main(Example.jasm)");
+ oa.shouldHaveExitValue(1);
+ }
+}
+
--- a/test/jdk/java/lang/invoke/AccessControlTest.java Wed Jul 24 12:49:44 2019 +0530
+++ b/test/jdk/java/lang/invoke/AccessControlTest.java Thu Jul 25 12:23:54 2019 +0530
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2019, 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,7 +33,6 @@
import java.lang.reflect.*;
import java.lang.reflect.Modifier;
import java.util.*;
-import org.testng.*;
import org.testng.annotations.*;
import static java.lang.invoke.MethodHandles.*;
@@ -62,23 +61,28 @@
private class LookupCase implements Comparable<LookupCase> {
final Lookup lookup;
final Class<?> lookupClass;
+ final Class<?> prevLookupClass;
final int lookupModes;
public LookupCase(Lookup lookup) {
this.lookup = lookup;
this.lookupClass = lookup.lookupClass();
+ this.prevLookupClass = lookup.previousLookupClass();
this.lookupModes = lookup.lookupModes();
+
assert(lookupString().equals(lookup.toString()));
numberOf(lookupClass().getClassLoader()); // assign CL#
}
- public LookupCase(Class<?> lookupClass, int lookupModes) {
+ public LookupCase(Class<?> lookupClass, Class<?> prevLookupClass, int lookupModes) {
this.lookup = null;
this.lookupClass = lookupClass;
+ this.prevLookupClass = prevLookupClass;
this.lookupModes = lookupModes;
numberOf(lookupClass().getClassLoader()); // assign CL#
}
- public final Class<?> lookupClass() { return lookupClass; }
- public final int lookupModes() { return lookupModes; }
+ public final Class<?> lookupClass() { return lookupClass; }
+ public final Class<?> prevLookupClass() { return prevLookupClass; }
+ public final int lookupModes() { return lookupModes; }
public Lookup lookup() { lookup.getClass(); return lookup; }
@@ -86,12 +90,24 @@
public int compareTo(LookupCase that) {
Class<?> c1 = this.lookupClass();
Class<?> c2 = that.lookupClass();
+ Class<?> p1 = this.prevLookupClass();
+ Class<?> p2 = that.prevLookupClass();
if (c1 != c2) {
int cmp = c1.getName().compareTo(c2.getName());
if (cmp != 0) return cmp;
cmp = numberOf(c1.getClassLoader()) - numberOf(c2.getClassLoader());
assert(cmp != 0);
return cmp;
+ } else if (p1 != p2){
+ if (p1 == null)
+ return 1;
+ else if (p2 == null)
+ return -1;
+ int cmp = p1.getName().compareTo(p2.getName());
+ if (cmp != 0) return cmp;
+ cmp = numberOf(p1.getClassLoader()) - numberOf(p2.getClassLoader());
+ assert(cmp != 0);
+ return cmp;
}
return -(this.lookupModes() - that.lookupModes());
}
@@ -102,6 +118,7 @@
}
public boolean equals(LookupCase that) {
return (this.lookupClass() == that.lookupClass() &&
+ this.prevLookupClass() == that.prevLookupClass() &&
this.lookupModes() == that.lookupModes());
}
@@ -113,20 +130,25 @@
/** Simulate all assertions in the spec. for Lookup.toString. */
private String lookupString() {
String name = lookupClass.getName();
+ if (prevLookupClass != null)
+ name += "/" + prevLookupClass.getName();
String suffix = "";
if (lookupModes == 0)
suffix = "/noaccess";
else if (lookupModes == PUBLIC)
suffix = "/public";
- else if (lookupModes == (PUBLIC|UNCONDITIONAL))
+ else if (lookupModes == UNCONDITIONAL)
suffix = "/publicLookup";
else if (lookupModes == (PUBLIC|MODULE))
suffix = "/module";
- else if (lookupModes == (PUBLIC|MODULE|PACKAGE))
+ else if (lookupModes == (PUBLIC|PACKAGE)
+ || lookupModes == (PUBLIC|MODULE|PACKAGE))
suffix = "/package";
- else if (lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE))
+ else if (lookupModes == (PUBLIC|PACKAGE|PRIVATE)
+ || lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE))
suffix = "/private";
- else if (lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED))
+ else if (lookupModes == (PUBLIC|PACKAGE|PRIVATE|PROTECTED)
+ || lookupModes == (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED))
suffix = "";
else
suffix = "/#"+Integer.toHexString(lookupModes);
@@ -138,41 +160,50 @@
* Creates a lookup on the specified new lookup class.
* [A1] The resulting object will report the specified
* class as its own {@link #lookupClass lookupClass}.
- * <p>
* [A2] However, the resulting {@code Lookup} object is guaranteed
* to have no more access capabilities than the original.
* In particular, access capabilities can be lost as follows:<ul>
- * <li> [A3] If the old lookup class is in a named module, and the new
- * lookup class is in a different module {@code M}, then no members, not
- * even public members in {@code M}'s exported packages, will be accessible.
- * The exception to this is when this lookup is publicLookup, in which case
- * public access is not lost.
- * <li> [A4] If the old lookup class is in an unnamed module, and the new
- * lookup class is a different module then module access is lost.
- * <li> [A5] If the new lookup class differs from the old one then UNCONDITIONAL
- * is lost. If the new lookup class is not within the same package member as the
- * old one, protected members will not be accessible by virtue of inheritance.
+ * [A3] If the new lookup class is in a different module from the old one,
+ * i.e. {@link #MODULE MODULE} access is lost.
+ * [A4] If the new lookup class is in a different package
+ * than the old one, protected and default (package) members will not be accessible,
+ * i.e. {@link #PROTECTED PROTECTED} and {@link #PACKAGE PACKAGE} access are lost.
+ * [A5] If the new lookup class is not within the same package member
+ * as the old one, private members will not be accessible, and protected members
+ * will not be accessible by virtue of inheritance,
+ * i.e. {@link #PRIVATE PRIVATE} access is lost.
* (Protected members may continue to be accessible because of package sharing.)
- * <li> [A6] If the new lookup class is in a different package than the old one,
- * protected and default (package) members will not be accessible.
- * <li> [A7] If the new lookup class is not within the same package member
- * as the old one, private members will not be accessible.
- * <li> [A8] If the new lookup class is not accessible to the old lookup class,
- * then no members, not even public members, will be accessible.
- * <li> [A9] (In all other cases, public members will continue to be accessible.)
- * </ul>
+ * [A6] If the new lookup class is not
+ * {@linkplain #accessClass(Class) accessible} to this lookup,
+ * then no members, not even public members, will be accessible
+ * i.e. all access modes are lost.
+ * [A7] If the new lookup class, the old lookup class and the previous lookup class
+ * are all in different modules i.e. teleporting to a third module,
+ * all access modes are lost.
+ * <p>
+ * The new previous lookup class is chosen as follows:
+ * [A8] If the new lookup object has {@link #UNCONDITIONAL UNCONDITIONAL} bit,
+ * the new previous lookup class is {@code null}.
+ * [A9] If the new lookup class is in the same module as the old lookup class,
+ * the new previous lookup class is the old previous lookup class.
+ * [A10] If the new lookup class is in a different module from the old lookup class,
+ * the new previous lookup class is the the old lookup class.
+ *
* Other than the above cases, the new lookup will have the same
- * access capabilities as the original. [A10]
+ * access capabilities as the original. [A11]
* <hr>
*/
public LookupCase in(Class<?> c2) {
Class<?> c1 = lookupClass();
- int m1 = lookupModes();
+ Module m1 = c1.getModule();
+ Module m2 = c2.getModule();
+ Module m0 = prevLookupClass() != null ? prevLookupClass.getModule() : c1.getModule();
+ int modes1 = lookupModes();
int changed = 0;
// for the purposes of access control then treat classes in different unnamed
// modules as being in the same module.
- boolean sameModule = (c1.getModule() == c2.getModule()) ||
- (!c1.getModule().isNamed() && !c2.getModule().isNamed());
+ boolean sameModule = (m1 == m2) ||
+ (!m1.isNamed() && !m2.isNamed());
boolean samePackage = (c1.getClassLoader() == c2.getClassLoader() &&
c1.getPackageName().equals(c2.getPackageName()));
boolean sameTopLevel = (topLevelClass(c1) == topLevelClass(c2));
@@ -180,40 +211,85 @@
assert(samePackage || !sameTopLevel);
assert(sameTopLevel || !sameClass);
boolean accessible = sameClass;
- if ((m1 & PACKAGE) != 0) accessible |= samePackage;
- if ((m1 & PUBLIC ) != 0) accessible |= (c2.getModifiers() & PUBLIC) != 0;
- if (!sameModule) {
- if (c1.getModule().isNamed() && (m1 & UNCONDITIONAL) == 0) {
- accessible = false; // [A3]
- } else {
- changed |= (MODULE|PACKAGE|PRIVATE|PROTECTED); // [A3] [A4]
- }
+
+ if ((modes1 & PACKAGE) != 0) accessible |= samePackage;
+ if ((modes1 & PUBLIC ) != 0) {
+ if (isModuleAccessible(c2))
+ accessible |= (c2.getModifiers() & PUBLIC) != 0;
+ else
+ accessible = false;
+ }
+ if ((modes1 & UNCONDITIONAL) != 0) {
+ if (m2.isExported(c2.getPackageName()))
+ accessible |= (c2.getModifiers() & PUBLIC) != 0;
+ else
+ accessible = false;
}
if (!accessible) {
- // Different package and no access to c2; lose all access.
- changed |= (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED); // [A8]
+ // no access to c2; lose all access.
+ changed |= (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED|UNCONDITIONAL); // [A6]
+ }
+ if (m2 != m1 && m0 != m1) {
+ // hop to a third module; lose all access
+ changed |= (PUBLIC|MODULE|PACKAGE|PRIVATE|PROTECTED); // [A7]
+ }
+ if (!sameModule) {
+ changed |= MODULE; // [A3]
}
if (!samePackage) {
// Different package; loose PACKAGE and lower access.
- changed |= (PACKAGE|PRIVATE|PROTECTED); // [A6]
+ changed |= (PACKAGE|PRIVATE|PROTECTED); // [A4]
}
if (!sameTopLevel) {
// Different top-level class. Lose PRIVATE and PROTECTED access.
- changed |= (PRIVATE|PROTECTED); // [A5] [A7]
+ changed |= (PRIVATE|PROTECTED); // [A5]
}
- if (!sameClass) {
- changed |= (UNCONDITIONAL); // [A5]
- } else {
- assert(changed == 0); // [A10] (no deprivation if same class)
+ if (sameClass) {
+ assert(changed == 0); // [A11] (no deprivation if same class)
}
- if (accessible) assert((changed & PUBLIC) == 0); // [A9]
- int m2 = m1 & ~changed;
- LookupCase l2 = new LookupCase(c2, m2);
- assert(l2.lookupClass() == c2); // [A1]
- assert((m1 | m2) == m1); // [A2] (no elevation of access)
+
+ if (accessible) assert((changed & PUBLIC) == 0);
+ int modes2 = modes1 & ~changed;
+ Class<?> plc = (m1 == m2) ? prevLookupClass() : c1; // [A9] [A10]
+ if ((modes1 & UNCONDITIONAL) != 0) plc = null; // [A8]
+ LookupCase l2 = new LookupCase(c2, plc, modes2);
+ assert(l2.lookupClass() == c2); // [A1]
+ assert((modes1 | modes2) == modes1); // [A2] (no elevation of access)
+ assert(l2.prevLookupClass() == null || (modes2 & MODULE) == 0);
return l2;
}
+ LookupCase dropLookupMode(int modeToDrop) {
+ int oldModes = lookupModes();
+ int newModes = oldModes & ~(modeToDrop | PROTECTED);
+ switch (modeToDrop) {
+ case PUBLIC: newModes &= ~(MODULE|PACKAGE|PROTECTED|PRIVATE); break;
+ case MODULE: newModes &= ~(PACKAGE|PRIVATE); break;
+ case PACKAGE: newModes &= ~(PRIVATE); break;
+ case PROTECTED:
+ case PRIVATE:
+ case UNCONDITIONAL: break;
+ default: throw new IllegalArgumentException(modeToDrop + " is not a valid mode to drop");
+ }
+ if (newModes == oldModes) return this; // return self if no change
+ LookupCase l2 = new LookupCase(lookupClass(), prevLookupClass(), newModes);
+ assert((oldModes | newModes) == oldModes); // [A2] (no elevation of access)
+ assert(l2.prevLookupClass() == null || (newModes & MODULE) == 0);
+ return l2;
+ }
+
+ boolean isModuleAccessible(Class<?> c) {
+ Module m1 = lookupClass().getModule();
+ Module m2 = c.getModule();
+ Module m0 = prevLookupClass() != null ? prevLookupClass.getModule() : m1;
+ String pn = c.getPackageName();
+ boolean accessible = m1.canRead(m2) && m2.isExported(pn, m1);
+ if (m1 != m0) {
+ accessible = accessible && m0.canRead(m2) && m2.isExported(pn, m0);
+ }
+ return accessible;
+ }
+
@Override
public String toString() {
String s = lookupClass().getSimpleName();
@@ -229,33 +305,48 @@
public boolean willAccess(Method m) {
Class<?> c1 = lookupClass();
Class<?> c2 = m.getDeclaringClass();
+ Module m1 = c1.getModule();
+ Module m2 = c2.getModule();
+ Module m0 = prevLookupClass != null ? prevLookupClass.getModule() : m1;
+ // unconditional has access to all public types/members of types that is in a package
+ // are unconditionally exported
+ if ((lookupModes & UNCONDITIONAL) != 0) {
+ return m2.isExported(c2.getPackageName())
+ && Modifier.isPublic(c2.getModifiers())
+ && Modifier.isPublic(m.getModifiers());
+ }
- // publicLookup has access to all public types/members of types in unnamed modules
- if ((lookupModes & UNCONDITIONAL) != 0
- && (lookupModes & PUBLIC) != 0
- && !c2.getModule().isNamed()
- && Modifier.isPublic(c2.getModifiers())
- && Modifier.isPublic(m.getModifiers()))
- return true;
+ // c1 and c2 are in different module
+ if (m1 != m2 || m0 != m2) {
+ return (lookupModes & PUBLIC) != 0
+ && isModuleAccessible(c2)
+ && Modifier.isPublic(c2.getModifiers())
+ && Modifier.isPublic(m.getModifiers());
+ }
+
+ assert(m1 == m2 && prevLookupClass == null);
+
+ if (!willAccessClass(c2, false))
+ return false;
LookupCase lc = this.in(c2);
- int m1 = lc.lookupModes();
- int m2 = fixMods(m.getModifiers());
+ int modes1 = lc.lookupModes();
+ int modes2 = fixMods(m.getModifiers());
// allow private lookup on nestmates. Otherwise, privacy is strictly enforced
- if (c1 != c2 && ((m2 & PRIVATE) == 0 || !c1.isNestmateOf(c2))) {
- m1 &= ~PRIVATE;
+ if (c1 != c2 && ((modes2 & PRIVATE) == 0 || !c1.isNestmateOf(c2))) {
+ modes1 &= ~PRIVATE;
}
// protected access is sometimes allowed
- if ((m2 & PROTECTED) != 0) {
- int prev = m2;
- m2 |= PACKAGE; // it acts like a package method also
+ if ((modes2 & PROTECTED) != 0) {
+ int prev = modes2;
+ modes2 |= PACKAGE; // it acts like a package method also
if ((lookupModes() & PROTECTED) != 0 &&
c2.isAssignableFrom(c1))
- m2 |= PUBLIC; // from a subclass, it acts like a public method also
+ modes2 |= PUBLIC; // from a subclass, it acts like a public method also
}
if (verbosity >= 2)
- System.out.format("%s willAccess %s m1=0x%h m2=0x%h => %s%n", this, lc, m1, m2, ((m2 & m1) != 0));
- return (m2 & m1) != 0;
+ System.out.format("%s willAccess %s modes1=0x%h modes2=0x%h => %s%n", lookupString(), lc.lookupString(), modes1, modes2, (modes2 & modes1) != 0);
+ return (modes2 & modes1) != 0;
}
/** Predict the success or failure of accessing this class. */
@@ -268,24 +359,36 @@
}
}
- // publicLookup has access to all public types/members of types in unnamed modules
- if ((lookupModes & UNCONDITIONAL) != 0
- && (lookupModes & PUBLIC) != 0
- && (!c2.getModule().isNamed())
- && Modifier.isPublic(c2.getModifiers()))
- return true;
+ Module m1 = c1.getModule();
+ Module m2 = c2.getModule();
+ Module m0 = prevLookupClass != null ? prevLookupClass.getModule() : m1;
+ // unconditional has access to all public types that is in an unconditionally exported package
+ if ((lookupModes & UNCONDITIONAL) != 0) {
+ return m2.isExported(c2.getPackageName()) && Modifier.isPublic(c2.getModifiers());
+ }
+ // c1 and c2 are in different module
+ if (m1 != m2 || m0 != m2) {
+ return (lookupModes & PUBLIC) != 0
+ && isModuleAccessible(c2)
+ && Modifier.isPublic(c2.getModifiers());
+ }
+
+ assert(m1 == m2 && prevLookupClass == null);
LookupCase lc = this.in(c2);
- int m1 = lc.lookupModes();
+ int modes1 = lc.lookupModes();
boolean r = false;
- if (m1 == 0) {
+ if (modes1 == 0) {
r = false;
} else {
- int m2 = fixMods(c2.getModifiers());
- if ((m2 & PUBLIC) != 0) {
- r = true;
- } else if ((m1 & PACKAGE) != 0 && c1.getPackage() == c2.getPackage()) {
- r = true;
+ if (Modifier.isPublic(c2.getModifiers())) {
+ if ((modes1 & MODULE) != 0)
+ r = true;
+ else if ((modes1 & PUBLIC) != 0)
+ r = m1.isExported(c2.getPackageName());
+ } else {
+ if ((modes1 & PACKAGE) != 0 && c1.getPackage() == c2.getPackage())
+ r = true;
}
}
if (verbosity >= 2) {
@@ -328,7 +431,7 @@
return i+1;
}
- private void addLookupEdge(LookupCase l1, Class<?> c2, LookupCase l2) {
+ private void addLookupEdge(LookupCase l1, Class<?> c2, LookupCase l2, int dropAccess) {
TreeSet<LookupCase> edges = CASE_EDGES.get(l2);
if (edges == null) CASE_EDGES.put(l2, edges = new TreeSet<>());
if (edges.add(l1)) {
@@ -337,7 +440,7 @@
int m1 = l1.lookupModes();
int m2 = l2.lookupModes();
assert((m1 | m2) == m1); // [A2] (no elevation of access)
- LookupCase expect = l1.in(c2);
+ LookupCase expect = dropAccess == 0 ? l1.in(c2) : l1.in(c2).dropLookupMode(dropAccess);
if (!expect.equals(l2))
System.out.println("*** expect "+l1+" => "+expect+" but got "+l2);
assertEquals(l2, expect);
@@ -358,9 +461,14 @@
for (int lastCount = -1; lastCount != CASES.size(); ) {
lastCount = CASES.size(); // if CASES grow in the loop we go round again
for (LookupCase lc1 : CASES.toArray(new LookupCase[0])) {
+ for (int mode : ACCESS_CASES) {
+ LookupCase lc2 = new LookupCase(lc1.lookup().dropLookupMode(mode));
+ addLookupEdge(lc1, lc1.lookupClass(), lc2, mode);
+ CASES.add(lc2);
+ }
for (Class<?> c2 : classes) {
LookupCase lc2 = new LookupCase(lc1.lookup().in(c2));
- addLookupEdge(lc1, c2, lc2);
+ addLookupEdge(lc1, c2, lc2, 0);
CASES.add(lc2);
}
}
@@ -386,8 +494,8 @@
if (verbosity > 0) {
verbosity += 9;
Method pro_in_self = targetMethod(THIS_CLASS, PROTECTED, methodType(void.class));
- testOneAccess(lookupCase("AccessControlTest/public"), pro_in_self, "find");
- testOneAccess(lookupCase("Remote_subclass/public"), pro_in_self, "find");
+ testOneAccess(lookupCase("AccessControlTest/module"), pro_in_self, "find");
+ testOneAccess(lookupCase("Remote_subclass/module"), pro_in_self, "find");
testOneAccess(lookupCase("Remote_subclass"), pro_in_self, "find");
verbosity -= 9;
}
@@ -398,6 +506,8 @@
String targetPlace = placeName(targetClass);
if (targetPlace == null) continue; // Object, String, not a target
for (int targetAccess : ACCESS_CASES) {
+ if (targetAccess == MODULE || targetAccess == UNCONDITIONAL)
+ continue;
MethodType methodType = methodType(void.class);
Method method = targetMethod(targetClass, targetAccess, methodType);
// Try to access target method from various contexts.
@@ -457,7 +567,6 @@
}
static Method targetMethod(Class<?> targetClass, int targetAccess, MethodType methodType) {
- assert targetAccess != MODULE;
String methodName = accessName(targetAccess)+placeName(targetClass);
if (verbosity >= 2)
System.out.println(targetClass.getSimpleName()+"."+methodName+methodType);
@@ -491,10 +600,13 @@
assert(false);
return "?";
}
- // MODULE not a test case at this time
private static final int[] ACCESS_CASES = {
- PUBLIC, PACKAGE, PRIVATE, PROTECTED
+ PUBLIC, PACKAGE, PRIVATE, PROTECTED, MODULE, UNCONDITIONAL
};
+ /*
+ * Adjust PUBLIC => PUBLIC|MODULE|UNCONDITIONAL
+ * Adjust 0 => PACKAGE
+ */
/** Return one of the ACCESS_CASES. */
static int fixMods(int mods) {
mods &= (PUBLIC|PRIVATE|PROTECTED);
--- a/test/jdk/java/lang/invoke/DropLookupModeTest.java Wed Jul 24 12:49:44 2019 +0530
+++ b/test/jdk/java/lang/invoke/DropLookupModeTest.java Thu Jul 25 12:23:54 2019 +0530
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, 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
@@ -106,37 +106,39 @@
assertTrue(lookup.lookupModes() == 0);
}
- /**
- * Test dropLookupMode on the public Lookup.
- */
- public void testPublicLookup() {
- final Lookup publicLookup = MethodHandles.publicLookup();
- final Class<?> lc = publicLookup.lookupClass();
- assertTrue(publicLookup.lookupModes() == (PUBLIC|UNCONDITIONAL));
-
- Lookup lookup = publicLookup.dropLookupMode(PRIVATE);
- assertTrue(lookup.lookupClass() == lc);
- assertTrue(lookup.lookupModes() == PUBLIC);
-
- lookup = publicLookup.dropLookupMode(PROTECTED);
- assertTrue(lookup.lookupClass() == lc);
- assertTrue(lookup.lookupModes() == PUBLIC);
+ @DataProvider(name = "unconditionals")
+ public Object[][] unconditionals() {
+ Lookup publicLookup = MethodHandles.publicLookup();
+ return new Object[][] {
+ { publicLookup, Object.class },
+ { publicLookup.in(String.class), String.class },
+ { publicLookup.in(DropLookupModeTest.class), DropLookupModeTest.class },
+ };
+ }
- lookup = publicLookup.dropLookupMode(PACKAGE);
- assertTrue(lookup.lookupClass() == lc);
- assertTrue(lookup.lookupModes() == PUBLIC);
-
- lookup = publicLookup.dropLookupMode(MODULE);
- assertTrue(lookup.lookupClass() == lc);
- assertTrue(lookup.lookupModes() == PUBLIC);
+ /**
+ * Test dropLookupMode on the lookup with public lookup
+ * and UNCONDITIONAL
+ */
+ @Test(dataProvider = "unconditionals")
+ public void testUnconditionalLookup(Lookup unconditionalLookup, Class<?> expected) {
+ assertTrue(unconditionalLookup.lookupModes() == UNCONDITIONAL);
- lookup = publicLookup.dropLookupMode(PUBLIC);
- assertTrue(lookup.lookupClass() == lc);
- assertTrue(lookup.lookupModes() == 0);
+ assertPublicLookup(unconditionalLookup.dropLookupMode(PRIVATE), expected);
+ assertPublicLookup(unconditionalLookup.dropLookupMode(PROTECTED), expected);
+ assertPublicLookup(unconditionalLookup.dropLookupMode(PACKAGE), expected);
+ assertPublicLookup(unconditionalLookup.dropLookupMode(MODULE), expected);
+ assertPublicLookup(unconditionalLookup.dropLookupMode(PUBLIC), expected);
- lookup = publicLookup.dropLookupMode(UNCONDITIONAL);
- assertTrue(lookup.lookupClass() == lc);
- assertTrue(lookup.lookupModes() == PUBLIC);
+ // drop all access
+ Lookup lookup = unconditionalLookup.dropLookupMode(UNCONDITIONAL);
+ assertTrue(lookup.lookupClass() == expected);
+ assertTrue(lookup.lookupModes() == 0);
+ }
+
+ private void assertPublicLookup(Lookup lookup, Class<?> expected) {
+ assertTrue(lookup.lookupClass() == expected);
+ assertTrue(lookup.lookupModes() == UNCONDITIONAL);
}
@DataProvider(name = "badInput")
@@ -157,4 +159,4 @@
MethodHandles.lookup().dropLookupMode(modeToDrop);
}
-}
\ No newline at end of file
+}
--- a/test/jdk/java/lang/invoke/MethodHandles/privateLookupIn/test/p/PrivateLookupInTests.java Wed Jul 24 12:49:44 2019 +0530
+++ b/test/jdk/java/lang/invoke/MethodHandles/privateLookupIn/test/p/PrivateLookupInTests.java Thu Jul 25 12:23:54 2019 +0530
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -82,6 +82,7 @@
}
// Invoke MethodHandles.privateLookupIn with a reduced-power caller
+ @Test(expectedExceptions = {IllegalAccessException.class})
public void testReducedAccessCallerSameModule() throws Throwable {
Lookup caller = MethodHandles.lookup().dropLookupMode(PACKAGE);
assertTrue((caller.lookupModes() & PRIVATE) == 0);
@@ -89,12 +90,6 @@
assertTrue((caller.lookupModes() & MODULE) != 0);
Lookup lookup = MethodHandles.privateLookupIn(nonPublicType, caller);
- assertTrue(lookup.lookupClass() == nonPublicType);
- assertTrue(lookup.hasPrivateAccess());
-
- // use it
- MethodHandle mh = lookup.findStaticGetter(nonPublicType, "obj", Object.class);
- Object obj = mh.invokeExact();
}
// Invoke MethodHandles.privateLookupIn with the public lookup as caller
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/Driver1.java Thu Jul 25 12:23:54 2019 +0530
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019, 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 8173978
+ * @build m3/* m4/* m5/* Unnamed Unnamed1
+ * @run testng/othervm m3/jdk.test.ModuleAccessTest
+ * @summary Basic test case for module access checks and Lookup.in and
+ * MethodHandles.privateLookupIn
+ */
--- a/test/jdk/java/lang/invoke/modules/Unnamed.java Wed Jul 24 12:49:44 2019 +0530
+++ b/test/jdk/java/lang/invoke/modules/Unnamed.java Thu Jul 25 12:23:54 2019 +0530
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2019, 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
@@ -21,4 +21,11 @@
* questions.
*/
-public class Unnamed { }
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+
+public class Unnamed {
+ public static Lookup lookup() {
+ return MethodHandles.lookup();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/Unnamed1.java Thu Jul 25 12:23:54 2019 +0530
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+public class Unnamed1 { }
--- a/test/jdk/java/lang/invoke/modules/m1/p1/Main.java Wed Jul 24 12:49:44 2019 +0530
+++ b/test/jdk/java/lang/invoke/modules/m1/p1/Main.java Thu Jul 25 12:23:54 2019 +0530
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -45,7 +45,7 @@
private Class<?> p2_Type2; // m1, not exported
private Class<?> q1_Type1; // m2, exported
private Class<?> q2_Type2; // m2, not exported
- private Class<?> x500NameClass; // java.base, not exported
+ private Class<?> signalClass; // java.base, not exported
private Class<?> unnamedClass; // class in unnamed module
@BeforeTest
@@ -55,7 +55,7 @@
p2_Type2 = Class.forName("p2.Type2");
q1_Type1 = Class.forName("q1.Type1");
q2_Type2 = Class.forName("q2.Type2");
- x500NameClass = Class.forName("sun.security.x509.X500Name");
+ signalClass = Class.forName("jdk.internal.misc.Signal");
unnamedClass = Class.forName("Unnamed");
} catch (ClassNotFoundException e) {
throw new AssertionError(e);
@@ -105,7 +105,7 @@
// java.base
findConstructor(lookup, Object.class, void.class); // [A2]
- findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class); // [A3]
+ findConstructorExpectingIAE(lookup, signalClass, void.class, String.class); // [A3]
// unnamed
findConstructor(lookup, unnamedClass, void.class); // [A3]
@@ -130,7 +130,7 @@
// java.base
findConstructor(lookup, Object.class, void.class);
- findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
+ findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
// unnamed
findConstructor(lookup, unnamedClass, void.class);
@@ -139,51 +139,70 @@
/**
* Hop to lookup class in another named module
*
- * [A0] has no access
+ * [A0] has PUBLIC access if accessible; otherwise no access
+ * [A1] old lookup class becomes previous lookup class
*/
public void testFromNamedToNamedModule() throws Exception {
+ // m2/q1_Type1 is accessible to m1 whereas m2/q_Type2 is not accessible
Lookup lookup = MethodHandles.lookup().in(q1_Type1);
- assertTrue(lookup.lookupModes() == 0); // [A0]
+ assertTrue(lookup.lookupModes() == PUBLIC); // [A0]
+ assertTrue(lookup.previousLookupClass() == Main.class); // [A1]
+
+ Lookup lookup2 = MethodHandles.lookup().in(q2_Type2);
+ assertTrue(lookup2.lookupModes() == 0); // [A0]
+ assertTrue(lookup2.previousLookupClass() == Main.class); // [A1]
// m1
findConstructorExpectingIAE(lookup, p1_Type1, void.class);
findConstructorExpectingIAE(lookup, p2_Type2, void.class);
+ findConstructorExpectingIAE(lookup2, p1_Type1, void.class);
+ findConstructorExpectingIAE(lookup2, p2_Type2, void.class);
+
// m2
- findConstructorExpectingIAE(lookup, q1_Type1, void.class);
+ findConstructor(lookup, q1_Type1, void.class); // m2/q1 is exported
findConstructorExpectingIAE(lookup, q2_Type2, void.class);
+ findConstructorExpectingIAE(lookup2, q1_Type1, void.class);
+ findConstructorExpectingIAE(lookup2, q2_Type2, void.class);
+
// java.base
- findConstructorExpectingIAE(lookup, Object.class, void.class);
- findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
+ findConstructor(lookup, Object.class, void.class);
+ findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
+
+ findConstructorExpectingIAE(lookup2, Object.class, void.class);
+ findConstructorExpectingIAE(lookup2, signalClass, void.class, String.class);
// unnamed
findConstructorExpectingIAE(lookup, unnamedClass, void.class);
+
+ findConstructorExpectingIAE(lookup2, unnamedClass, void.class);
+
}
/**
* Hop to lookup class in an unnamed module
*
- * [A0] has no access
+ * [A0] has PUBLIC access
*/
public void testFromNamedToUnnamedModule() throws Exception {
Lookup lookup = MethodHandles.lookup().in(unnamedClass);
- assertTrue(lookup.lookupModes() == 0); // [A0]
+ assertTrue(lookup.lookupModes() == PUBLIC); // [A0]
// m1
- findConstructorExpectingIAE(lookup, p1_Type1, void.class);
+ findConstructor(lookup, p1_Type1, void.class); // p1 is exported
findConstructorExpectingIAE(lookup, p2_Type2, void.class);
// m2
- findConstructorExpectingIAE(lookup, q1_Type1, void.class);
+ findConstructor(lookup, q1_Type1, void.class);
findConstructorExpectingIAE(lookup, q2_Type2, void.class);
// java.base
- findConstructorExpectingIAE(lookup, Object.class, void.class);
- findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
+ findConstructor(lookup, Object.class, void.class);
+ findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
// unnamed
- findConstructorExpectingIAE(lookup, unnamedClass, void.class);
+ findConstructor(lookup, unnamedClass, void.class);
}
/**
@@ -206,7 +225,7 @@
// java.base
findConstructor(lookup, Object.class, void.class);
- findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
+ findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
// unnamed
findConstructor(lookup, unnamedClass, void.class);
@@ -215,11 +234,11 @@
/**
* MethodHandles.publicLookup()
*
- * [A0] has PUBLIC|UNCONDITIONAL access
+ * [A0] has UNCONDITIONAL access
*/
public void testPublicLookup() throws Exception {
Lookup lookup = MethodHandles.publicLookup();
- assertTrue(lookup.lookupModes() == (PUBLIC|UNCONDITIONAL)); // A0
+ assertTrue(lookup.lookupModes() == UNCONDITIONAL); // A0
// m1
findConstructor(lookup, p1_Type1, void.class);
@@ -231,7 +250,7 @@
// java.base
findConstructor(lookup, Object.class, void.class);
- findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
+ findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
// unnamed
findConstructor(lookup, unnamedClass, void.class);
@@ -239,36 +258,12 @@
/**
* Hop from publicLookup to accessible type in java.base
+ *
+ * [A0] has UNCONDITIONAL access
*/
public void testPublicLookupToBaseModule() throws Exception {
Lookup lookup = MethodHandles.publicLookup().in(String.class);
- assertTrue(lookup.lookupModes() == PUBLIC); // A0
-
- // m1
- findConstructorExpectingIAE(lookup, p1_Type1, void.class);
- findConstructorExpectingIAE(lookup, p2_Type2, void.class);
-
- // m2
- findConstructorExpectingIAE(lookup, q1_Type1, void.class);
- findConstructorExpectingIAE(lookup, q2_Type2, void.class);
-
- // java.base
- findConstructor(lookup, Object.class, void.class);
- findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
-
- // unnamed
- findConstructorExpectingIAE(lookup, unnamedClass, void.class);
- }
-
-
- /**
- * Hop from publicLookup to accessible type in named module.
- *
- * [A0] has PUBLIC access
- */
- public void testPublicLookupToAccessibleTypeInNamedModule() throws Exception {
- Lookup lookup = MethodHandles.publicLookup().in(p1_Type1);
- assertTrue(lookup.lookupModes() == PUBLIC); // A0
+ assertTrue(lookup.lookupModes() == UNCONDITIONAL); // A0
// m1
findConstructor(lookup, p1_Type1, void.class);
@@ -280,7 +275,33 @@
// java.base
findConstructor(lookup, Object.class, void.class);
- findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
+ findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
+
+ // unnamed
+ findConstructor(lookup, unnamedClass, void.class);
+ }
+
+
+ /**
+ * Hop from publicLookup to accessible type in named module.
+ *
+ * [A0] has UNCONDITIONAL access
+ */
+ public void testPublicLookupToAccessibleTypeInNamedModule() throws Exception {
+ Lookup lookup = MethodHandles.publicLookup().in(p1_Type1);
+ assertTrue(lookup.lookupModes() == UNCONDITIONAL); // A0
+
+ // m1
+ findConstructor(lookup, p1_Type1, void.class);
+ findConstructorExpectingIAE(lookup, p2_Type2, void.class);
+
+ // m2
+ findConstructor(lookup, q1_Type1, void.class);
+ findConstructorExpectingIAE(lookup, q2_Type2, void.class);
+
+ // java.base
+ findConstructor(lookup, Object.class, void.class);
+ findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
// unnamed
findConstructor(lookup, unnamedClass, void.class);
@@ -305,7 +326,7 @@
// java.base
findConstructorExpectingIAE(lookup, Object.class, void.class);
- findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
+ findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
// unnamed
findConstructorExpectingIAE(lookup, unnamedClass, void.class);
@@ -314,11 +335,11 @@
/**
* Teleport from publicLookup to public type in unnamed module
*
- * [A0] has PUBLIC access
+ * [A0] has UNCONDITIONAL access
*/
public void testPublicLookupToUnnamedModule() throws Exception {
Lookup lookup = MethodHandles.publicLookup().in(unnamedClass);
- assertTrue(lookup.lookupModes() == PUBLIC); // A0
+ assertTrue(lookup.lookupModes() == UNCONDITIONAL); // A0
// m1
findConstructor(lookup, p1_Type1, void.class);
@@ -330,7 +351,7 @@
// java.base
findConstructor(lookup, Object.class, void.class);
- findConstructorExpectingIAE(lookup, x500NameClass, void.class, String.class);
+ findConstructorExpectingIAE(lookup, signalClass, void.class, String.class);
// unnamed
findConstructor(lookup, unnamedClass, void.class);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m3/c1/C1.java Thu Jul 25 12:23:54 2019 +0530
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019, 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 c1;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+
+public class C1 {
+ public C1() { }
+
+ public static Lookup lookup() {
+ return MethodHandles.lookup();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m3/c1/C2.java Thu Jul 25 12:23:54 2019 +0530
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, 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 c1;
+
+public class C2 {
+ public C2() { }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m3/c2/C3.java Thu Jul 25 12:23:54 2019 +0530
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, 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 c2;
+
+public class C3{
+ public C3() { }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m3/jdk/test/ModuleAccessTest.java Thu Jul 25 12:23:54 2019 +0530
@@ -0,0 +1,568 @@
+/*
+ * Copyright (c) 2019, 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 jdk.test;
+
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import e1.CrackM5Access;
+
+import static java.lang.invoke.MethodHandles.Lookup.*;
+import static org.testng.Assert.*;
+
+public class ModuleAccessTest {
+ static ModuleLookup m3;
+ static ModuleLookup m4;
+ static ModuleLookup m5;
+ static Map<String, ModuleLookup> moduleLookupMap = new HashMap<>();
+ static Lookup privLookupIn;
+ static Lookup privLookupIn2;
+ static Lookup unnamedLookup;
+ static Class<?> unnamed;
+ static Class<?> unnamed1;
+
+ @BeforeTest
+ public void setup() throws Exception {
+ m3 = new ModuleLookup("m3", 'C');
+ m4 = new ModuleLookup("m4", 'D');
+ m5 = new ModuleLookup("m5", 'E');
+ moduleLookupMap.put(m3.name(), m3);
+ moduleLookupMap.put(m4.name(), m4);
+ moduleLookupMap.put(m5.name(), m5);
+
+ privLookupIn = MethodHandles.privateLookupIn(m3.type2, m3.lookup);
+ privLookupIn2 = MethodHandles.privateLookupIn(m4.type1, m3.lookup);
+
+ unnamed = Class.forName("Unnamed");
+ unnamed1 = Class.forName("Unnamed1");
+ unnamedLookup = (Lookup)unnamed.getMethod("lookup").invoke(null);
+
+ // m5 reads m3
+ CrackM5Access.addReads(m3.module);
+ CrackM5Access.addReads(unnamed.getModule());
+ }
+
+ @DataProvider(name = "samePackage")
+ public Object[][] samePackage() throws Exception {
+ return new Object[][] {
+ { m3.lookup, m3.type2 },
+ { privLookupIn, m3.type1 },
+ { privLookupIn2, m4.type2 },
+ { unnamedLookup, unnamed1 }
+ };
+ }
+
+ /**
+ * Test lookup.in(T) where T is in the same package of the lookup class.
+ *
+ * [A0] targetClass becomes the lookup class
+ * [A1] no change in previous lookup class
+ * [A2] PROTECTED and PRIVATE are dropped
+ */
+ @Test(dataProvider = "samePackage")
+ public void testLookupInSamePackage(Lookup lookup, Class<?> targetClass) throws Exception {
+ Class<?> lookupClass = lookup.lookupClass();
+ Lookup lookup2 = lookup.in(targetClass);
+
+ assertTrue(lookupClass.getPackage() == targetClass.getPackage());
+ assertTrue(lookupClass.getModule() == targetClass.getModule());
+ assertTrue(lookup2.lookupClass() == targetClass); // [A0]
+ assertTrue(lookup2.previousLookupClass() == lookup.previousLookupClass()); // [A1]
+ assertTrue(lookup2.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE))); // [A2]
+ }
+
+ @DataProvider(name = "sameModule")
+ public Object[][] sameModule() throws Exception {
+ return new Object[][] {
+ { m3.lookup, m3.type3},
+ { privLookupIn, m3.type3},
+ { privLookupIn2, m4.type3}
+ };
+ }
+
+ /**
+ * Test lookup.in(T) where T is in the same module but different package from the lookup class.
+ *
+ * [A0] targetClass becomes the lookup class
+ * [A1] no change in previous lookup class
+ * [A2] PROTECTED, PRIVATE and PACKAGE are dropped
+ */
+ @Test(dataProvider = "sameModule")
+ public void testLookupInSameModule(Lookup lookup, Class<?> targetClass) throws Exception {
+ Class<?> lookupClass = lookup.lookupClass();
+ Lookup lookup2 = lookup.in(targetClass);
+
+ assertTrue(lookupClass.getPackage() != targetClass.getPackage());
+ assertTrue(lookupClass.getModule() == targetClass.getModule());
+ assertTrue(lookup2.lookupClass() == targetClass); // [A0]
+ assertTrue(lookup2.previousLookupClass() == lookup.previousLookupClass()); // [A1]
+ assertTrue(lookup2.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE))); // [A2]
+ }
+
+ @DataProvider(name = "anotherModule")
+ public Object[][] anotherModule() throws Exception {
+ return new Object[][] {
+ { m3.lookup, m4.type1, m5, m5.accessibleTypesTo(m3.module, m4.module) },
+ { m4.lookup, m5.type2, m3, m3.accessibleTypesTo(m4.module, m5.module) },
+ { m3.lookup, m5.type1, m4, m4.accessibleTypesTo(m3.module, m5.module) },
+ { m5.lookup, unnamed, m3, m3.accessibleTypesTo(m5.module, unnamed.getModule()) },
+ };
+ }
+
+ /**
+ * Test lookup.in(T) where T is in a different module from the lookup class.
+ *
+ * [A0] targetClass becomes the lookup class
+ * [A1] lookup class becomes previous lookup class
+ * [A2] PROTECTED, PRIVATE, PACKAGE, and MODULE are dropped
+ * [A3] no access to module internal types in m0 and m1
+ * [A4] if m1 reads m0, can access public types in m0; otherwise no access.
+ * [A5] can access public types in m1 exported to m0
+ * [A6] can access public types in m2 exported to m0 and m1
+ */
+ @Test(dataProvider = "anotherModule")
+ public void testLookupInAnotherModule(Lookup lookup, Class<?> targetClass,
+ ModuleLookup m2, Set<Class<?>> otherTypes) throws Exception {
+ Class<?> lookupClass = lookup.lookupClass();
+ Module m0 = lookupClass.getModule();
+ Module m1 = targetClass.getModule();
+
+ assertTrue(m0 != m1);
+ assertTrue(m0.canRead(m1));
+ assertTrue(m1.isExported(targetClass.getPackageName(), m0));
+
+ Lookup lookup2 = lookup.in(targetClass);
+ assertTrue(lookup2.lookupClass() == targetClass); // [A0]
+ assertTrue(lookup2.previousLookupClass() == lookup.lookupClass()); // [A1]
+ assertTrue(lookup2.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE|MODULE))); // [A2]
+
+ // [A3] no access to module internal type in m0
+ // [A4] if m1 reads m0,
+ // [A4] no access to public types exported from m0 unconditionally
+ // [A4] no access to public types exported from m0
+ ModuleLookup ml0 = moduleLookupMap.get(m0.getName());
+ if (m1.canRead(m0)) {
+ for (Class<?> type : ml0.unconditionalExports()) {
+ testAccess(lookup2, type);
+ }
+ for (Class<?> type : ml0.qualifiedExportsTo(m1)) {
+ testAccess(lookup2, type);
+ }
+ } else {
+ findConstructorExpectingIAE(lookup2, ml0.type1, void.class);
+ findConstructorExpectingIAE(lookup2, ml0.type2, void.class);
+ findConstructorExpectingIAE(lookup2, ml0.type3, void.class);
+ }
+
+ // [A5] can access public types exported from m1 unconditionally
+ // [A5] can access public types exported from m1 to m0
+ if (m1.isNamed()) {
+ ModuleLookup ml1 = moduleLookupMap.get(m1.getName());
+ assertTrue(ml1.unconditionalExports().size() + ml1.qualifiedExportsTo(m0).size() > 0);
+ for (Class<?> type : ml1.unconditionalExports()) {
+ testAccess(lookup2, type);
+ }
+ for (Class<?> type : ml1.qualifiedExportsTo(m0)) {
+ testAccess(lookup2, type);
+ }
+ } else {
+ // unnamed module
+ testAccess(lookup2, unnamed1);
+ }
+
+ // [A5] can access public types exported from m2 unconditionally
+ // [A5] can access public types exported from m2 to m0 and m1
+ for (Class<?> type : otherTypes) {
+ assertTrue(type.getModule() == m2.module);
+ testAccess(lookup2, type);
+ }
+
+ // test inaccessible types
+ for (Class<?> type : Set.of(m2.type1, m2.type2, m2.type3)) {
+ if (!otherTypes.contains(type)) {
+ // type is accessible to this lookup
+ try {
+ lookup2.accessClass(type);
+ assertTrue(false);
+ } catch (IllegalAccessException e) {}
+
+ findConstructorExpectingIAE(lookup2, type, void.class);
+ }
+ }
+ }
+
+ public void testAccess(Lookup lookup, Class<?> type) throws Exception {
+ // type is accessible to this lookup
+ assertTrue(lookup.accessClass(type) == type);
+
+ // can find constructor
+ findConstructor(lookup, type, void.class);
+
+ Module m0 = lookup.previousLookupClass().getModule();
+ Module m1 = lookup.lookupClass().getModule();
+ Module m2 = type.getModule();
+
+ assertTrue(m0 != m1 && m0 != null);
+ assertTrue((lookup.lookupModes() & MODULE) == 0);
+ assertTrue(m0 != m2 || m1 != m2);
+
+ MethodHandles.Lookup lookup2 = lookup.in(type);
+ if (m2 == m1) {
+ // the same module of the lookup class
+ assertTrue(lookup2.lookupClass() == type);
+ assertTrue(lookup2.previousLookupClass() == lookup.previousLookupClass());
+ } else if (m2 == m0) {
+ // hop back to the module of the previous lookup class
+ assertTrue(lookup2.lookupClass() == type);
+ assertTrue(lookup2.previousLookupClass() == lookup.lookupClass());
+ } else {
+ // hop to a third module
+ assertTrue(lookup2.lookupClass() == type);
+ assertTrue(lookup2.previousLookupClass() == lookup.lookupClass());
+ assertTrue(lookup2.lookupModes() == 0);
+ }
+ }
+
+ @DataProvider(name = "thirdModule")
+ public Object[][] thirdModule() throws Exception {
+ return new Object[][] {
+ { m3.lookup, m4.type1, m5.type1},
+ { m3.lookup, m4.type2, m5.type1},
+ { unnamedLookup, m3.type1, m4.type1 },
+ };
+ }
+
+ /**
+ * Test lookup.in(c1).in(c2) where c1 is in second module and c2 is in a third module.
+ *
+ * [A0] c2 becomes the lookup class
+ * [A1] c1 becomes previous lookup class
+ * [A2] all access bits are dropped
+ */
+ @Test(dataProvider = "thirdModule")
+ public void testLookupInThirdModule(Lookup lookup, Class<?> c1, Class<?> c2) throws Exception {
+ Class<?> c0 = lookup.lookupClass();
+ Module m0 = c0.getModule();
+ Module m1 = c1.getModule();
+ Module m2 = c2.getModule();
+
+ assertTrue(m0 != m1 && m0 != m2 && m1 != m2);
+ assertTrue(m0.canRead(m1) && m0.canRead(m2));
+ assertTrue(m1.canRead(m2));
+ assertTrue(m1.isExported(c1.getPackageName(), m0));
+ assertTrue(m2.isExported(c2.getPackageName(), m0) && m2.isExported(c2.getPackageName(), m1));
+
+ Lookup lookup1 = lookup.in(c1);
+ assertTrue(lookup1.lookupClass() == c1);
+ assertTrue(lookup1.previousLookupClass() == c0);
+ assertTrue(lookup1.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE|MODULE)));
+
+ Lookup lookup2 = lookup1.in(c2);
+ assertTrue(lookup2.lookupClass() == c2); // [A0]
+ assertTrue(lookup2.previousLookupClass() == c1); // [A1]
+ assertTrue(lookup2.lookupModes() == 0, lookup2.toString()); // [A2]
+ }
+
+ @DataProvider(name = "privLookupIn")
+ public Object[][] privLookupIn() throws Exception {
+ return new Object[][] {
+ { m3.lookup, m4.type1 },
+ { m3.lookup, m5.type1 },
+ { m4.lookup, m5.type2 },
+ { m5.lookup, m3.type3 },
+ { m5.lookup, unnamed }
+ };
+ }
+
+ /**
+ * Test privateLookupIn(T, lookup) where T is in another module
+ *
+ * [A0] full capabilities except MODULE bit
+ * [A1] target class becomes the lookup class
+ * [A2] the lookup class becomes previous lookup class
+ * [A3] IAE thrown if lookup has no MODULE access
+ */
+ @Test(dataProvider = "privLookupIn")
+ public void testPrivateLookupIn(Lookup lookup, Class<?> targetClass) throws Exception {
+ Module m0 = lookup.lookupClass().getModule();
+ Module m1 = targetClass.getModule();
+
+ // privateLookupIn from m0 to m1
+ assertTrue(m0 != m1);
+ assertTrue(m1.isOpen(targetClass.getPackageName(), m0));
+ Lookup privLookup1 = MethodHandles.privateLookupIn(targetClass, lookup);
+ assertTrue(privLookup1.lookupModes() == (PROTECTED|PRIVATE|PACKAGE|PUBLIC)); // [A0]
+ assertTrue(privLookup1.lookupClass() == targetClass); // [A1]
+ assertTrue(privLookup1.previousLookupClass() == lookup.lookupClass()); // [A2]
+
+ // privLookup1 has no MODULE access; can't do privateLookupIn
+ try {
+ Lookup privLookup2 = MethodHandles.privateLookupIn(targetClass, privLookup1); // [A3]
+ assertFalse(privLookup2 != null);
+ } catch (IllegalAccessException e) {}
+ }
+
+ /**
+ * Test member access from the Lookup returned from privateLookupIn
+ */
+ @Test
+ public void testPrivateLookupAccess() throws Exception {
+ Class<?> staticsClass = e1.Statics.class;
+ Lookup privLookup1 = MethodHandles.privateLookupIn(staticsClass, m4.lookup);
+ assertTrue((privLookup1.lookupModes() & MODULE) == 0);
+ assertTrue(privLookup1.lookupClass() == staticsClass);
+ assertTrue(privLookup1.previousLookupClass() == m4.lookup.lookupClass());
+
+ // access private member and default package member in m5
+ MethodType mtype = MethodType.methodType(void.class);
+ MethodHandle mh1 = privLookup1.findStatic(staticsClass, "privateMethod", mtype);
+ MethodHandle mh2 = privLookup1.findStatic(staticsClass, "packageMethod", mtype);
+
+ // access public member in exported types from m5 to m4
+ findConstructor(privLookup1, m5.type1, void.class);
+ // no access to public member in non-exported types to m5
+ findConstructorExpectingIAE(privLookup1, m5.type3, void.class);
+
+ // no access to public types in m4 since m5 does not read m4
+ assertFalse(m5.module.canRead(m4.module));
+ findConstructorExpectingIAE(privLookup1, m4.type1, void.class);
+
+ // teleport from a privateLookup to another class in the same package
+ // lose private access
+ Lookup privLookup2 = MethodHandles.privateLookupIn(m5.type1, m4.lookup);
+ Lookup lookup = privLookup2.in(staticsClass);
+ assertTrue((lookup.lookupModes() & PRIVATE) == 0);
+ MethodHandle mh3 = lookup.findStatic(staticsClass, "packageMethod", mtype);
+ try {
+ lookup.findStatic(staticsClass, "privateMethod", mtype);
+ assertTrue(false);
+ } catch (IllegalAccessException e) {}
+ }
+
+ /**
+ * Test member access from the Lookup returned from privateLookupIn and
+ * the lookup mode after dropLookupMode
+ */
+ @Test
+ public void testDropLookupMode() throws Exception {
+ Lookup lookup = MethodHandles.privateLookupIn(m5.type1, m4.lookup);
+ assertTrue((lookup.lookupModes() & MODULE) == 0);
+
+ Lookup lookup1 = lookup.dropLookupMode(PRIVATE);
+ assertTrue(lookup1.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE)));
+ Lookup lookup2 = lookup.dropLookupMode(PACKAGE);
+ assertTrue(lookup2.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE)));
+ Lookup lookup3 = lookup.dropLookupMode(MODULE);
+ assertTrue(lookup3.lookupModes() == (lookup.lookupModes() & ~(PROTECTED|PRIVATE|PACKAGE)));
+ Lookup lookup4 = lookup.dropLookupMode(PUBLIC);
+ assertTrue(lookup4.lookupModes() == 0);
+
+ }
+
+ /**
+ * Test no access to a public member on a non-public class
+ */
+ @Test
+ public void testPrivateLookupOnNonPublicType() throws Exception {
+ // privateLookup in a non-public type
+ Class<?> nonPUblicType = Class.forName("e1.NonPublic");
+ Lookup privLookup = MethodHandles.privateLookupIn(nonPUblicType, m4.lookup);
+ MethodType mtype = MethodType.methodType(void.class);
+ MethodHandle mh1 = privLookup.findStatic(nonPUblicType, "publicStatic", mtype);
+
+ // drop MODULE access i.e. only PUBLIC access
+ Lookup lookup = privLookup.dropLookupMode(MODULE);
+ assertTrue(lookup.lookupModes() == PUBLIC);
+ try {
+ MethodHandle mh2 = lookup.findStatic(nonPUblicType, "publicStatic", mtype);
+ assertFalse(mh2 != null);
+ } catch (IllegalAccessException e) {}
+ }
+
+ @Test
+ public void testPublicLookup() {
+ Lookup publicLookup = MethodHandles.publicLookup();
+ Lookup pub1 = publicLookup.in(m3.type1);
+ Lookup pub2 = pub1.in(java.lang.String.class);
+ Lookup pub3 = pub2.in(java.lang.management.ThreadMXBean.class);
+ Lookup pub4 = pub3.dropLookupMode(UNCONDITIONAL);
+
+ assertTrue(publicLookup.lookupClass() == Object.class);
+ assertTrue(publicLookup.lookupModes() == UNCONDITIONAL);
+ assertTrue(pub1.lookupClass() == m3.type1);
+ assertTrue(pub1.lookupModes() == UNCONDITIONAL);
+ assertTrue(pub2.lookupClass() == String.class);
+ assertTrue(pub2.lookupModes() == UNCONDITIONAL);
+ assertTrue(pub3.lookupClass() == java.lang.management.ThreadMXBean.class);
+ assertTrue(pub3.lookupModes() == UNCONDITIONAL);
+ assertTrue(pub4.lookupModes() == 0);
+
+ // publicLookup has no MODULE access; can't do privateLookupIn
+ try {
+ Lookup pub5 = MethodHandles.privateLookupIn(m4.type1, pub1);
+ assertFalse(pub5 != null);
+ } catch (IllegalAccessException e) {}
+ }
+
+ static class ModuleLookup {
+ private final Module module;
+ private final Set<String> packages;
+ private final Lookup lookup;
+ private final Class<?> type1;
+ private final Class<?> type2;
+ private final Class<?> type3;
+
+ ModuleLookup(String mn, char c) throws Exception {
+ this.module = ModuleLayer.boot().findModule(mn).orElse(null);
+ assertNotNull(this.module);
+ this.packages = module.getDescriptor().packages();
+ assertTrue(packages.size() <= 3);
+ Lookup lookup = null;
+ Class<?> type1 = null;
+ Class<?> type2 = null;
+ Class<?> type3 = null;
+ for (String pn : packages) {
+ char n = pn.charAt(pn.length() - 1);
+ switch (n) {
+ case '1':
+ type1 = Class.forName(pn + "." + c + "1");
+ type2 = Class.forName(pn + "." + c + "2");
+ Method m = type1.getMethod("lookup");
+ lookup = (Lookup) m.invoke(null);
+ break;
+ case '2':
+ type3 = Class.forName(pn + "." + c + "3");
+ break;
+
+ default:
+ }
+ }
+ this.lookup = lookup;
+ this.type1 = type1;
+ this.type2 = type2;
+ this.type3 = type3;
+ }
+
+ String name() {
+ return module.getName();
+ }
+
+ /*
+ * Returns the set of types that are unconditionally exported.
+ */
+ Set<Class<?>> unconditionalExports() {
+ return Stream.of(type1, type2, type3)
+ .filter(c -> module.isExported(c.getPackageName()))
+ .collect(Collectors.toSet());
+ }
+
+ /*
+ * Returns the set of types that are qualifiedly exported to the specified
+ * caller module
+ */
+ Set<Class<?>> qualifiedExportsTo(Module caller) {
+ if (caller.canRead(this.module)) {
+ return Stream.of(type1, type2, type3)
+ .filter(c -> !module.isExported(c.getPackageName())
+ && module.isExported(c.getPackageName(), caller))
+ .collect(Collectors.toSet());
+ } else {
+ return Set.of();
+ }
+ }
+
+ /*
+ * Returns the set of types that are qualifiedly exported to the specified
+ * caller module
+ */
+ Set<Class<?>> accessibleTypesTo(Module m0, Module m1) {
+ if (m0.canRead(this.module) && m1.canRead(this.module)) {
+ return Stream.of(type1, type2, type3)
+ .filter(c -> module.isExported(c.getPackageName(), m0)
+ && module.isExported(c.getPackageName(), m1))
+ .collect(Collectors.toSet());
+ } else {
+ return Set.of();
+ }
+ }
+
+ /*
+ * Returns the set of types that are open to the specified caller
+ * unconditionally or qualifiedly.
+ */
+ Set<Class<?>> opensTo(Module caller) {
+ if (caller.canRead(this.module)) {
+ return Stream.of(type1, type2, type3)
+ .filter(c -> module.isOpen(c.getPackageName(), caller))
+ .collect(Collectors.toSet());
+ } else {
+ return Set.of();
+ }
+ }
+
+ public String toString() {
+ return module.toString();
+ }
+ }
+
+ /**
+ * Invokes Lookup findConstructor with a method type constructed from the
+ * given return and parameter types, expecting IllegalAccessException to be
+ * thrown.
+ */
+ static void findConstructorExpectingIAE(Lookup lookup,
+ Class<?> clazz,
+ Class<?> rtype,
+ Class<?>... ptypes) throws Exception {
+ try {
+ MethodHandle mh = findConstructor(lookup, clazz, rtype, ptypes);
+ assertTrue(false);
+ } catch (IllegalAccessException expected) { }
+ }
+
+ /**
+ * Invokes Lookup findConstructor with a method type constructored from the
+ * given return and parameter types.
+ */
+ static MethodHandle findConstructor(Lookup lookup,
+ Class<?> clazz,
+ Class<?> rtype,
+ Class<?>... ptypes) throws Exception {
+ MethodType mt = MethodType.methodType(rtype, ptypes);
+ return lookup.findConstructor(clazz, mt);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m3/module-info.java Thu Jul 25 12:23:54 2019 +0530
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2019, 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 m3 {
+ requires m4;
+ requires m5;
+ requires testng;
+ requires java.management;
+ exports c1;
+ opens c2 to m5;
+ exports jdk.test;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m4/d1/D1.java Thu Jul 25 12:23:54 2019 +0530
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019, 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 d1;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+
+public class D1 {
+ public D1() { }
+
+ public static Lookup lookup() {
+ return MethodHandles.lookup();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m4/d1/D2.java Thu Jul 25 12:23:54 2019 +0530
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, 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 d1;
+
+public class D2 {
+ public D2() { }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m4/d2/D3.java Thu Jul 25 12:23:54 2019 +0530
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, 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 d2;
+
+public class D3 {
+ public D3() { }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m4/module-info.java Thu Jul 25 12:23:54 2019 +0530
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019, 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 m4 {
+ requires m5;
+ opens d1;
+ exports d2 to m3;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m5/e1/CrackM5Access.java Thu Jul 25 12:23:54 2019 +0530
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2019, 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 e1;
+
+public class CrackM5Access {
+ private static void privateMethod() { }
+
+ static void packageMethod() { }
+
+ public static void addReads(Module m) {
+ CrackM5Access.class.getModule().addReads(m);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m5/e1/E1.java Thu Jul 25 12:23:54 2019 +0530
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019, 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 e1;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+
+public class E1 {
+ public E1() { }
+
+ public static Lookup lookup() {
+ return MethodHandles.lookup();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m5/e1/E2.java Thu Jul 25 12:23:54 2019 +0530
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, 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 e1;
+
+public class E2 {
+ public E2() { }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m5/e1/NonPublic.java Thu Jul 25 12:23:54 2019 +0530
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, 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 e1;
+
+class NonPublic {
+ public static void publicStatic() { }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m5/e1/Statics.java Thu Jul 25 12:23:54 2019 +0530
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019, 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 e1;
+
+public class Statics {
+ private static void privateMethod() { }
+
+ static void packageMethod() { }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m5/e2/E3.java Thu Jul 25 12:23:54 2019 +0530
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019, 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 e2;
+
+public class E3 {
+ public E3() { }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/lang/invoke/modules/m5/module-info.java Thu Jul 25 12:23:54 2019 +0530
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2019, 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 m5 {
+ exports e1 to m3;
+ opens e1 to m3, m4;
+ exports e2 to m3;
+}
--- a/test/jdk/java/net/NetworkInterface/NetworkInterfaceRetrievalTests.java Wed Jul 24 12:49:44 2019 +0530
+++ b/test/jdk/java/net/NetworkInterface/NetworkInterfaceRetrievalTests.java Thu Jul 25 12:23:54 2019 +0530
@@ -24,6 +24,7 @@
/**
* @test
* @bug 8179559 8225239
+ * @modules java.base/java.net:open
*/
import java.net.InetAddress;