# HG changeset patch # User herrick # Date 1574207108 18000 # Node ID d1fe86ccc832f119c0c1a4d1a3f474119bffae5e # Parent 4ad81e9e30fdf98428432f3ca58c345d65012d49# Parent f68fd04fe46388b63b86b722626464b92512bdec merge diff -r 4ad81e9e30fd -r d1fe86ccc832 make/lib/Awt2dLibraries.gmk --- a/make/lib/Awt2dLibraries.gmk Mon Nov 18 12:40:06 2019 -0500 +++ b/make/lib/Awt2dLibraries.gmk Tue Nov 19 18:45:08 2019 -0500 @@ -383,7 +383,7 @@ libawt/java2d, \ HEADERS_FROM_SRC := $(LIBLCMS_HEADERS_FROM_SRC), \ DISABLED_WARNINGS_gcc := format-nonliteral type-limits \ - misleading-indentation undef unused-function, \ + misleading-indentation undef unused-function stringop-truncation, \ DISABLED_WARNINGS_clang := tautological-compare format-nonliteral undef, \ DISABLED_WARNINGS_solstudio := E_STATEMENT_NOT_REACHED, \ DISABLED_WARNINGS_microsoft := 4819, \ diff -r 4ad81e9e30fd -r d1fe86ccc832 src/hotspot/share/classfile/defaultMethods.cpp --- a/src/hotspot/share/classfile/defaultMethods.cpp Mon Nov 18 12:40:06 2019 -0500 +++ b/src/hotspot/share/classfile/defaultMethods.cpp Tue Nov 19 18:45:08 2019 -0500 @@ -47,38 +47,6 @@ typedef enum { QUALIFIED, DISQUALIFIED } QualifiedState; -// Because we use an iterative algorithm when iterating over the type -// hierarchy, we can't use traditional scoped objects which automatically do -// cleanup in the destructor when the scope is exited. PseudoScope (and -// PseudoScopeMark) provides a similar functionality, but for when you want a -// scoped object in non-stack memory (such as in resource memory, as we do -// here). You've just got to remember to call 'destroy()' on the scope when -// leaving it (and marks have to be explicitly added). -class PseudoScopeMark : public ResourceObj { - public: - virtual void destroy() = 0; -}; - -class PseudoScope : public ResourceObj { - private: - GrowableArray _marks; - public: - - static PseudoScope* cast(void* data) { - return static_cast(data); - } - - void add_mark(PseudoScopeMark* psm) { - _marks.append(psm); - } - - void destroy() { - for (int i = 0; i < _marks.length(); ++i) { - _marks.at(i)->destroy(); - } - } -}; - static void print_slot(outputStream* str, Symbol* name, Symbol* signature) { str->print("%s%s", name->as_C_string(), signature->as_C_string()); } @@ -108,13 +76,13 @@ * * The ALGO class, must provide a visit() method, which each of which will be * called once for each node in the inheritance tree during the iteration. In - * addition, it can provide a memory block via new_node_data(InstanceKlass*), - * which it can use for node-specific storage (and access via the - * current_data() and data_at_depth(int) methods). + * addition, it can provide a memory block via new_node_data(), which it can + * use for node-specific storage (and access via the current_data() and + * data_at_depth(int) methods). * * Bare minimum needed to be an ALGO class: * class Algo : public HierarchyVisitor { - * void* new_node_data(InstanceKlass* cls) { return NULL; } + * void* new_node_data() { return NULL; } * void free_node_data(void* data) { return; } * bool visit() { return true; } * }; @@ -134,6 +102,12 @@ : _class(cls), _super_was_visited(!visit_super), _interface_index(0), _algorithm_data(data) {} + void update(InstanceKlass* cls, void* data, bool visit_super) { + _class = cls; + _super_was_visited = !visit_super; + _interface_index = 0; + _algorithm_data = data; + } int number_of_interfaces() { return _class->local_interfaces()->length(); } int interface_index() { return _interface_index; } void set_super_visited() { _super_was_visited = true; } @@ -155,19 +129,32 @@ }; bool _visited_Object; + GrowableArray _path; + GrowableArray _free_nodes; Node* current_top() const { return _path.top(); } - bool has_more_nodes() const { return !_path.is_empty(); } - void push(InstanceKlass* cls, void* data) { + bool has_more_nodes() const { return _path.length() > 0; } + void push(InstanceKlass* cls, ALGO* algo) { assert(cls != NULL, "Requires a valid instance class"); - Node* node = new Node(cls, data, has_super(cls)); if (cls == SystemDictionary::Object_klass()) { _visited_Object = true; } + void* data = algo->new_node_data(); + Node* node; + if (_free_nodes.is_empty()) { // Add a new node + node = new Node(cls, data, has_super(cls)); + } else { // Reuse existing node and data + node = _free_nodes.pop(); + node->update(cls, data, has_super(cls)); + } _path.push(node); } - void pop() { _path.pop(); } + void pop() { + Node* node = _path.pop(); + // Make the node available for reuse + _free_nodes.push(node); + } // Since the starting point can be an interface, we must ensure we catch // j.l.Object as the super once in those cases. The _visited_Object flag @@ -183,6 +170,11 @@ protected: + // Resets the visitor + void reset() { + _visited_Object = false; + } + // Accessors available to the algorithm int current_depth() const { return _path.length() - 1; } @@ -199,14 +191,13 @@ void* current_data() { return data_at_depth(0); } public: + HierarchyVisitor() : _visited_Object(false), _path() {} void run(InstanceKlass* root) { ALGO* algo = static_cast(this); - void* algo_data = algo->new_node_data(root); - push(root, algo_data); + push(root, algo); bool top_needs_visit = true; - do { Node* top = current_top(); if (top_needs_visit) { @@ -232,8 +223,7 @@ top->increment_visited_interface(); } assert(next != NULL, "Otherwise we shouldn't be here"); - algo_data = algo->new_node_data(next); - push(next, algo_data); + push(next, algo); top_needs_visit = true; } } while (has_more_nodes()); @@ -251,7 +241,7 @@ return true; } - void* new_node_data(InstanceKlass* cls) { return NULL; } + void* new_node_data() { return NULL; } void free_node_data(void* data) { return; } PrintHierarchy(outputStream* st = tty) : _st(st) {} @@ -270,7 +260,7 @@ GrowableArray _keep_alive; public: - KeepAliveRegistrar(Thread* thread) : _thread(thread), _keep_alive(20) { + KeepAliveRegistrar(Thread* thread) : _thread(thread), _keep_alive(6) { assert(thread == Thread::current(), "Must be current thread"); } @@ -299,7 +289,7 @@ public: KeepAliveVisitor(KeepAliveRegistrar* registrar) : _registrar(registrar) {} - void* new_node_data(InstanceKlass* cls) { return NULL; } + void* new_node_data() { return NULL; } void free_node_data(void* data) { return; } bool visit() { @@ -316,36 +306,41 @@ // from the root of hierarchy to the method that contains an interleaving // erased method defined in an interface. +class MethodState { + public: + Method* _method; + QualifiedState _state; + + MethodState() : _method(NULL), _state(DISQUALIFIED) {} + MethodState(Method* method, QualifiedState state) : _method(method), _state(state) {} +}; + class MethodFamily : public ResourceObj { private: - GrowableArray > _members; - ResourceHashtable _member_index; + GrowableArray _members; Method* _selected_target; // Filled in later, if a unique target exists Symbol* _exception_message; // If no unique target is found Symbol* _exception_name; // If no unique target is found - bool contains_method(Method* method) { - int* lookup = _member_index.get(method); - return lookup != NULL; + MethodState* find_method(Method* method) { + for (int i = 0; i < _members.length(); i++) { + if (_members.at(i)._method == method) { + return &_members.at(i); + } + } + return NULL; } void add_method(Method* method, QualifiedState state) { - Pair entry(method, state); - _member_index.put(method, _members.length()); - _members.append(entry); - } - - void disqualify_method(Method* method) { - int* index = _member_index.get(method); - guarantee(index != NULL && *index >= 0 && *index < _members.length(), "bad index"); - _members.at(*index).second = DISQUALIFIED; + MethodState method_state(method, state); + _members.append(method_state); } Symbol* generate_no_defaults_message(TRAPS) const; Symbol* generate_method_message(Symbol *klass_name, Method* method, TRAPS) const; - Symbol* generate_conflicts_message(GrowableArray* methods, TRAPS) const; + Symbol* generate_conflicts_message(GrowableArray* methods, TRAPS) const; public: @@ -358,23 +353,15 @@ } } - void record_qualified_method(Method* m) { - // If the method already exists in the set as qualified, this operation is - // redundant. If it already exists as disqualified, then we leave it as - // disqualfied. Thus we only add to the set if it's not already in the - // set. - if (!contains_method(m)) { - add_method(m, QUALIFIED); - } - } - - void record_disqualified_method(Method* m) { - // If not in the set, add it as disqualified. If it's already in the set, - // then set the state to disqualified no matter what the previous state was. - if (!contains_method(m)) { - add_method(m, DISQUALIFIED); - } else { - disqualify_method(m); + void record_method(Method* m, QualifiedState state) { + // If not in the set, add it. If it's already in the set, then leave it + // as is if state is qualified, or set it to disqualified if state is + // disqualified. + MethodState* method_state = find_method(m); + if (method_state == NULL) { + add_method(m, state); + } else if (state == DISQUALIFIED) { + method_state->_state = DISQUALIFIED; } } @@ -386,30 +373,43 @@ Symbol* get_exception_name() { return _exception_name; } // Either sets the target or the exception error message - void determine_target(InstanceKlass* root, TRAPS) { + void determine_target_or_set_exception_message(InstanceKlass* root, TRAPS) { if (has_target() || throws_exception()) { return; } // Qualified methods are maximally-specific methods // These include public, instance concrete (=default) and abstract methods - GrowableArray qualified_methods; int num_defaults = 0; int default_index = -1; - int qualified_index = -1; - for (int i = 0; i < _members.length(); ++i) { - Pair entry = _members.at(i); - if (entry.second == QUALIFIED) { - qualified_methods.append(entry.first); - qualified_index++; - if (entry.first->is_default_method()) { + for (int i = 0; i < _members.length(); i++) { + MethodState &member = _members.at(i); + if (member._state == QUALIFIED) { + if (member._method->is_default_method()) { num_defaults++; - default_index = qualified_index; - + default_index = i; } } } + if (num_defaults == 1) { + assert(_members.at(default_index)._state == QUALIFIED, ""); + _selected_target = _members.at(default_index)._method; + } else { + generate_and_set_exception_message(root, num_defaults, default_index, CHECK); + } + } + + void generate_and_set_exception_message(InstanceKlass* root, int num_defaults, int default_index, TRAPS) { + assert(num_defaults != 1, "invariant - should've been handled calling method"); + + GrowableArray qualified_methods; + for (int i = 0; i < _members.length(); i++) { + MethodState& member = _members.at(i); + if (member._state == QUALIFIED) { + qualified_methods.push(member._method); + } + } if (num_defaults == 0) { // If the root klass has a static method with matching name and signature // then do not generate an overpass method because it will hide the @@ -421,13 +421,8 @@ _exception_message = generate_method_message(root->name(), qualified_methods.at(0), CHECK); } _exception_name = vmSymbols::java_lang_AbstractMethodError(); - - // If only one qualified method is default, select that - } else if (num_defaults == 1) { - _selected_target = qualified_methods.at(default_index); - - } else if (num_defaults > 1) { - _exception_message = generate_conflicts_message(&qualified_methods,CHECK); + } else { + _exception_message = generate_conflicts_message(&_members,CHECK); _exception_name = vmSymbols::java_lang_IncompatibleClassChangeError(); LogTarget(Debug, defaultmethods) lt; if (lt.is_enabled()) { @@ -475,23 +470,23 @@ return SymbolTable::new_symbol(ss.base(), (int)ss.size()); } -Symbol* MethodFamily::generate_conflicts_message(GrowableArray* methods, TRAPS) const { +Symbol* MethodFamily::generate_conflicts_message(GrowableArray* methods, TRAPS) const { stringStream ss; ss.print("Conflicting default methods:"); for (int i = 0; i < methods->length(); ++i) { - Method* method = methods->at(i); - Symbol* klass = method->klass_name(); - Symbol* name = method->name(); + Method *method = methods->at(i)._method; + Symbol *klass = method->klass_name(); + Symbol *name = method->name(); ss.print(" "); - ss.write((const char*)klass->bytes(), klass->utf8_length()); + ss.write((const char*) klass->bytes(), klass->utf8_length()); ss.print("."); - ss.write((const char*)name->bytes(), name->utf8_length()); + ss.write((const char*) name->bytes(), name->utf8_length()); } return SymbolTable::new_symbol(ss.base(), (int)ss.size()); } -class StateRestorer; +class StateRestorerScope; // StatefulMethodFamily is a wrapper around a MethodFamily that maintains the // qualification state during hierarchy visitation, and applies that state @@ -517,32 +512,72 @@ MethodFamily* get_method_family() { return &_method_family; } - StateRestorer* record_method_and_dq_further(Method* mo); + void record_method_and_dq_further(StateRestorerScope* scope, Method* mo); }; -class StateRestorer : public PseudoScopeMark { - private: +// Because we use an iterative algorithm when iterating over the type +// hierarchy, we can't use traditional scoped objects which automatically do +// cleanup in the destructor when the scope is exited. StateRestorerScope (and +// StateRestorer) provides a similar functionality, but for when you want a +// scoped object in non-stack memory (such as in resource memory, as we do +// here). You've just got to remember to call 'restore_state()' on the scope when +// leaving it (and marks have to be explicitly added). The scope is reusable after +// 'restore_state()' has been called. +class StateRestorer : public ResourceObj { + public: StatefulMethodFamily* _method; QualifiedState _state_to_restore; - public: - StateRestorer(StatefulMethodFamily* dm, QualifiedState state) - : _method(dm), _state_to_restore(state) {} - ~StateRestorer() { destroy(); } + + StateRestorer() : _method(NULL), _state_to_restore(DISQUALIFIED) {} + void restore_state() { _method->set_qualification_state(_state_to_restore); } - virtual void destroy() { restore_state(); } }; -StateRestorer* StatefulMethodFamily::record_method_and_dq_further(Method* mo) { - StateRestorer* mark = new StateRestorer(this, _qualification_state); - if (_qualification_state == QUALIFIED) { - _method_family.record_qualified_method(mo); - } else { - _method_family.record_disqualified_method(mo); +class StateRestorerScope : public ResourceObj { + private: + GrowableArray _marks; + GrowableArray* _free_list; // Shared between scopes + public: + StateRestorerScope(GrowableArray* free_list) : _marks(), _free_list(free_list) {} + + static StateRestorerScope* cast(void* data) { + return static_cast(data); } + + void mark(StatefulMethodFamily* family, QualifiedState qualification_state) { + StateRestorer* restorer; + if (!_free_list->is_empty()) { + restorer = _free_list->pop(); + } else { + restorer = new StateRestorer(); + } + restorer->_method = family; + restorer->_state_to_restore = qualification_state; + _marks.append(restorer); + } + +#ifdef ASSERT + bool is_empty() { + return _marks.is_empty(); + } +#endif + + void restore_state() { + while(!_marks.is_empty()) { + StateRestorer* restorer = _marks.pop(); + restorer->restore_state(); + _free_list->push(restorer); + } + } +}; + +void StatefulMethodFamily::record_method_and_dq_further(StateRestorerScope* scope, Method* mo) { + scope->mark(this, _qualification_state); + _method_family.record_method(mo, _qualification_state); + // Everything found "above"??? this method in the hierarchy walk is set to // disqualified set_qualification_state(DISQUALIFIED); - return mark; } // Represents a location corresponding to a vtable slot for methods that @@ -660,11 +695,19 @@ Symbol* _method_signature; StatefulMethodFamily* _family; bool _cur_class_is_interface; - + // Free lists, used as an optimization + GrowableArray _free_scopes; + GrowableArray _free_restorers; public: - FindMethodsByErasedSig(Symbol* name, Symbol* signature, bool is_interf) : - _method_name(name), _method_signature(signature), _family(NULL), - _cur_class_is_interface(is_interf) {} + FindMethodsByErasedSig() : _free_scopes(6), _free_restorers(6) {}; + + void prepare(Symbol* name, Symbol* signature, bool is_interf) { + reset(); + _method_name = name; + _method_signature = signature; + _family = NULL; + _cur_class_is_interface = is_interf; + } void get_discovered_family(MethodFamily** family) { if (_family != NULL) { @@ -674,15 +717,25 @@ } } - void* new_node_data(InstanceKlass* cls) { return new PseudoScope(); } + void* new_node_data() { + if (!_free_scopes.is_empty()) { + StateRestorerScope* free_scope = _free_scopes.pop(); + assert(free_scope->is_empty(), "StateRestorerScope::_marks array not empty"); + return free_scope; + } + return new StateRestorerScope(&_free_restorers); + } void free_node_data(void* node_data) { - PseudoScope::cast(node_data)->destroy(); + StateRestorerScope* scope = StateRestorerScope::cast(node_data); + scope->restore_state(); + // Reuse scopes + _free_scopes.push(scope); } // Find all methods on this hierarchy that match this // method's erased (name, signature) bool visit() { - PseudoScope* scope = PseudoScope::cast(current_data()); + StateRestorerScope* scope = StateRestorerScope::cast(current_data()); InstanceKlass* iklass = current_class(); Method* m = iklass->find_method(_method_name, _method_signature); @@ -702,8 +755,7 @@ } if (iklass->is_interface()) { - StateRestorer* restorer = _family->record_method_and_dq_further(m); - scope->add_mark(restorer); + _family->record_method_and_dq_further(scope, m); } else { // This is the rule that methods in classes "win" (bad word) over // methods in interfaces. This works because of single inheritance. @@ -724,16 +776,20 @@ GrowableArray* slots, InstanceKlass* klass, TRAPS); static void generate_erased_defaults( - InstanceKlass* klass, EmptyVtableSlot* slot, bool is_intf, TRAPS) { + FindMethodsByErasedSig* visitor, + InstanceKlass* klass, EmptyVtableSlot* slot, bool is_intf, TRAPS) { + // the visitor needs to be initialized or re-initialized before use + // - this facilitates reusing the same visitor instance on multiple + // generation passes as an optimization + visitor->prepare(slot->name(), slot->signature(), is_intf); // sets up a set of methods with the same exact erased signature - FindMethodsByErasedSig visitor(slot->name(), slot->signature(), is_intf); - visitor.run(klass); + visitor->run(klass); MethodFamily* family; - visitor.get_discovered_family(&family); + visitor->get_discovered_family(&family); if (family != NULL) { - family->determine_target(klass, CHECK); + family->determine_target_or_set_exception_message(klass, CHECK); slot->bind_family(family); } } @@ -788,6 +844,7 @@ find_empty_vtable_slots(&empty_slots, klass, mirandas, CHECK); if (empty_slots.length() > 0) { + FindMethodsByErasedSig findMethodsByErasedSig; for (int i = 0; i < empty_slots.length(); ++i) { EmptyVtableSlot* slot = empty_slots.at(i); LogTarget(Debug, defaultmethods) lt; @@ -798,7 +855,7 @@ slot->print_on(&ls); ls.cr(); } - generate_erased_defaults(klass, slot, klass->is_interface(), CHECK); + generate_erased_defaults(&findMethodsByErasedSig, klass, slot, klass->is_interface(), CHECK); } log_debug(defaultmethods)("Creating defaults and overpasses..."); create_defaults_and_exceptions(&empty_slots, klass, CHECK); @@ -898,12 +955,12 @@ GrowableArray defaults; BytecodeConstantPool bpool(klass->constants()); + BytecodeBuffer* buffer = NULL; // Lazily create a reusable buffer for (int i = 0; i < slots->length(); ++i) { EmptyVtableSlot* slot = slots->at(i); if (slot->is_bound()) { MethodFamily* method = slot->get_binding(); - BytecodeBuffer buffer; LogTarget(Debug, defaultmethods) lt; if (lt.is_enabled()) { @@ -926,11 +983,16 @@ defaults.push(selected); } } else if (method->throws_exception()) { - int max_stack = assemble_method_error(&bpool, &buffer, + if (buffer == NULL) { + buffer = new BytecodeBuffer(); + } else { + buffer->clear(); + } + int max_stack = assemble_method_error(&bpool, buffer, method->get_exception_name(), method->get_exception_message(), CHECK); AccessFlags flags = accessFlags_from( JVM_ACC_PUBLIC | JVM_ACC_SYNTHETIC | JVM_ACC_BRIDGE); - Method* m = new_method(&bpool, &buffer, slot->name(), slot->signature(), + Method* m = new_method(&bpool, buffer, slot->name(), slot->signature(), flags, max_stack, slot->size_of_parameters(), ConstMethod::OVERPASS, CHECK); // We push to the methods list: diff -r 4ad81e9e30fd -r d1fe86ccc832 src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp --- a/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp Mon Nov 18 12:40:06 2019 -0500 +++ b/src/hotspot/share/gc/g1/g1PageBasedVirtualSpace.cpp Tue Nov 19 18:45:08 2019 -0500 @@ -279,7 +279,7 @@ if (pretouch_gang != NULL) { size_t num_chunks = MAX2((size_t)1, size_in_pages * _page_size / MAX2(G1PretouchTask::chunk_size(), _page_size)); - uint num_workers = MIN2((uint)num_chunks, pretouch_gang->active_workers()); + uint num_workers = MIN2((uint)num_chunks, pretouch_gang->total_workers()); log_debug(gc, heap)("Running %s with %u workers for " SIZE_FORMAT " work units pre-touching " SIZE_FORMAT "B.", cl.name(), num_workers, num_chunks, size_in_pages * _page_size); pretouch_gang->run_task(&cl, num_workers); diff -r 4ad81e9e30fd -r d1fe86ccc832 src/hotspot/share/memory/filemap.cpp --- a/src/hotspot/share/memory/filemap.cpp Mon Nov 18 12:40:06 2019 -0500 +++ b/src/hotspot/share/memory/filemap.cpp Tue Nov 19 18:45:08 2019 -0500 @@ -1133,7 +1133,7 @@ assert((base - (char*)CompressedKlassPointers::base()) % HeapWordSize == 0, "Sanity"); if (base != NULL) { _mapping_offset = (size_t)CompressedOops::encode_not_null((oop)base); - assert(_mapping_offset >> 32 == 0, "must be 32-bit only"); + assert(_mapping_offset == (size_t)(uint32_t)_mapping_offset, "must be 32-bit only"); } } else { if (base != NULL) { @@ -1566,7 +1566,7 @@ address FileMapInfo::decode_start_address(FileMapRegion* spc, bool with_current_oop_encoding_mode) { size_t offset = spc->mapping_offset(); - assert((offset >> 32) == 0, "must be 32-bit only"); + assert(offset == (size_t)(uint32_t)offset, "must be 32-bit only"); uint n = (uint)offset; if (with_current_oop_encoding_mode) { return (address)CompressedOops::decode_not_null(n); diff -r 4ad81e9e30fd -r d1fe86ccc832 src/hotspot/share/runtime/thread.cpp --- a/src/hotspot/share/runtime/thread.cpp Mon Nov 18 12:40:06 2019 -0500 +++ b/src/hotspot/share/runtime/thread.cpp Tue Nov 19 18:45:08 2019 -0500 @@ -1007,7 +1007,7 @@ address end = os::current_stack_pointer(); // Allow non Java threads to call this without stack_base if (_stack_base == NULL) return true; - if (stack_base() >= adr && adr >= end) return true; + if (stack_base() > adr && adr >= end) return true; return false; } diff -r 4ad81e9e30fd -r d1fe86ccc832 src/hotspot/share/runtime/thread.hpp --- a/src/hotspot/share/runtime/thread.hpp Mon Nov 18 12:40:06 2019 -0500 +++ b/src/hotspot/share/runtime/thread.hpp Tue Nov 19 18:45:08 2019 -0500 @@ -729,7 +729,7 @@ bool on_local_stack(address adr) const { // QQQ this has knowledge of direction, ought to be a stack method - return (_stack_base >= adr && adr >= stack_end()); + return (_stack_base > adr && adr >= stack_end()); } int lgrp_id() const { return _lgrp_id; } diff -r 4ad81e9e30fd -r d1fe86ccc832 src/java.base/share/classes/java/lang/Class.java --- a/src/java.base/share/classes/java/lang/Class.java Mon Nov 18 12:40:06 2019 -0500 +++ b/src/java.base/share/classes/java/lang/Class.java Tue Nov 19 18:45:08 2019 -0500 @@ -325,6 +325,10 @@ * @throws ExceptionInInitializerError if the initialization provoked * by this method fails * @throws ClassNotFoundException if the class cannot be located + * + * @jls 12.2 Loading of Classes and Interfaces + * @jls 12.3 Linking of Classes and Interfaces + * @jls 12.4 Initialization of Classes and Interfaces */ @CallerSensitive public static Class forName(String className) @@ -339,7 +343,7 @@ * interface with the given string name, using the given class loader. * Given the fully qualified name for a class or interface (in the same * format returned by {@code getName}) this method attempts to - * locate, load, and link the class or interface. The specified class + * locate and load the class or interface. The specified class * loader is used to load the class or interface. If the parameter * {@code loader} is null, the class is loaded through the bootstrap * class loader. The class is initialized only if the @@ -374,7 +378,7 @@ * is accessible to its caller. * * @param name fully qualified name of the desired class - * @param initialize if {@code true} the class will be initialized. + * @param initialize if {@code true} the class will be initialized (which implies linking). * See Section 12.4 of The Java Language Specification. * @param loader class loader from which the class must be loaded * @return class object representing the desired class @@ -392,6 +396,10 @@ * * @see java.lang.Class#forName(String) * @see java.lang.ClassLoader + * + * @jls 12.2 Loading of Classes and Interfaces + * @jls 12.3 Linking of Classes and Interfaces + * @jls 12.4 Initialization of Classes and Interfaces * @since 1.2 */ @CallerSensitive @@ -427,9 +435,9 @@ * Returns the {@code Class} with the given * binary name in the given module. * - *

This method attempts to locate, load, and link the class or interface. - * It does not run the class initializer. If the class is not found, this - * method returns {@code null}.

+ *

This method attempts to locate and load the class or interface. + * It does not link the class, and does not run the class initializer. + * If the class is not found, this method returns {@code null}.

* *

If the class loader of the given module defines other modules and * the given name is a class defined in a different module, this method @@ -465,6 +473,8 @@ * in a module. * * + * @jls 12.2 Loading of Classes and Interfaces + * @jls 12.3 Linking of Classes and Interfaces * @since 9 * @spec JPMS */ diff -r 4ad81e9e30fd -r d1fe86ccc832 src/java.base/share/classes/java/lang/module/Configuration.java --- a/src/java.base/share/classes/java/lang/module/Configuration.java Mon Nov 18 12:40:06 2019 -0500 +++ b/src/java.base/share/classes/java/lang/module/Configuration.java Tue Nov 19 18:45:08 2019 -0500 @@ -312,7 +312,7 @@ { List parents = List.of(empty()); Resolver resolver = new Resolver(finder, parents, ModuleFinder.of(), traceOutput); - resolver.resolve(roots).bind(); + resolver.resolve(roots).bind(/*bindIncubatorModules*/false); return new Configuration(parents, resolver); } diff -r 4ad81e9e30fd -r d1fe86ccc832 src/java.base/share/classes/java/lang/module/Resolver.java --- a/src/java.base/share/classes/java/lang/module/Resolver.java Mon Nov 18 12:40:06 2019 -0500 +++ b/src/java.base/share/classes/java/lang/module/Resolver.java Tue Nov 19 18:45:08 2019 -0500 @@ -28,7 +28,6 @@ import java.io.PrintStream; import java.lang.module.ModuleDescriptor.Provides; import java.lang.module.ModuleDescriptor.Requires.Modifier; -import java.net.URI; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -45,6 +44,7 @@ import jdk.internal.module.ModuleHashes; import jdk.internal.module.ModuleReferenceImpl; +import jdk.internal.module.ModuleResolution; import jdk.internal.module.ModuleTarget; /** @@ -215,15 +215,32 @@ * service-use relation. */ Resolver bind() { + return bind(/*bindIncubatorModules*/true); + } + /** + * Augments the set of resolved modules with modules induced by the + * service-use relation. + * + * @param bindIncubatorModules true if incubator modules are candidates to + * add to the module graph + */ + Resolver bind(boolean bindIncubatorModules) { // Scan the finders for all available service provider modules. As // java.base uses services then the module finders will be scanned // anyway. Map> availableProviders = new HashMap<>(); for (ModuleReference mref : findAll()) { ModuleDescriptor descriptor = mref.descriptor(); - if (!descriptor.provides().isEmpty()) { + boolean candidate; + if (!bindIncubatorModules && (mref instanceof ModuleReferenceImpl)) { + ModuleResolution mres = ((ModuleReferenceImpl) mref).moduleResolution(); + candidate = (mres == null) || (mres.hasIncubatingWarning() == false); + } else { + candidate = true; + } + if (candidate && !descriptor.provides().isEmpty()) { for (Provides provides : descriptor.provides()) { String sn = provides.service(); diff -r 4ad81e9e30fd -r d1fe86ccc832 src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java --- a/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java Mon Nov 18 12:40:06 2019 -0500 +++ b/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java Tue Nov 19 18:45:08 2019 -0500 @@ -352,7 +352,7 @@ Configuration cf; if (needResolution) { - cf = JLMA.resolveAndBind(finder, roots, traceOutput); + cf = Modules.newBootLayerConfiguration(finder, roots, traceOutput); } else { if (archivedModuleGraph != null) { cf = archivedModuleGraph.configuration(); diff -r 4ad81e9e30fd -r d1fe86ccc832 src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java --- a/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java Mon Nov 18 12:40:06 2019 -0500 +++ b/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java Tue Nov 19 18:45:08 2019 -0500 @@ -34,6 +34,7 @@ import jdk.internal.org.objectweb.asm.ClassWriter; import jdk.internal.org.objectweb.asm.ModuleVisitor; import jdk.internal.org.objectweb.asm.Opcodes; +import jdk.internal.org.objectweb.asm.commons.ModuleResolutionAttribute; import jdk.internal.org.objectweb.asm.commons.ModuleTargetAttribute; import static jdk.internal.org.objectweb.asm.Opcodes.*; @@ -78,7 +79,9 @@ * Writes the given module descriptor to a module-info.class file, * returning it in a byte array. */ - private static byte[] toModuleInfo(ModuleDescriptor md, ModuleTarget target) { + private static byte[] toModuleInfo(ModuleDescriptor md, + ModuleResolution mres, + ModuleTarget target) { ClassWriter cw = new ClassWriter(0); cw.visit(Opcodes.V10, ACC_MODULE, "module-info", null, null, null); @@ -147,6 +150,11 @@ mv.visitEnd(); + // write ModuleResolution attribute if specified + if (mres != null) { + cw.visitAttribute(new ModuleResolutionAttribute(mres.value())); + } + // write ModuleTarget attribute if there is a target platform if (target != null && target.targetPlatform().length() > 0) { cw.visitAttribute(new ModuleTargetAttribute(target.targetPlatform())); @@ -161,11 +169,12 @@ * module-info.class. */ public static void write(ModuleDescriptor descriptor, + ModuleResolution mres, ModuleTarget target, OutputStream out) throws IOException { - byte[] bytes = toModuleInfo(descriptor, target); + byte[] bytes = toModuleInfo(descriptor, mres, target); out.write(bytes); } @@ -173,10 +182,34 @@ * Writes a module descriptor to the given output stream as a * module-info.class. */ + public static void write(ModuleDescriptor descriptor, + ModuleResolution mres, + OutputStream out) + throws IOException + { + write(descriptor, mres, null, out); + } + + /** + * Writes a module descriptor to the given output stream as a + * module-info.class. + */ + public static void write(ModuleDescriptor descriptor, + ModuleTarget target, + OutputStream out) + throws IOException + { + write(descriptor, null, target, out); + } + + /** + * Writes a module descriptor to the given output stream as a + * module-info.class. + */ public static void write(ModuleDescriptor descriptor, OutputStream out) throws IOException { - write(descriptor, null, out); + write(descriptor, null, null, out); } /** @@ -184,7 +217,7 @@ * in module-info.class format. */ public static ByteBuffer toByteBuffer(ModuleDescriptor descriptor) { - byte[] bytes = toModuleInfo(descriptor, null); + byte[] bytes = toModuleInfo(descriptor, null, null); return ByteBuffer.wrap(bytes); } } diff -r 4ad81e9e30fd -r d1fe86ccc832 src/java.base/share/classes/jdk/internal/module/Modules.java --- a/src/java.base/share/classes/jdk/internal/module/Modules.java Mon Nov 18 12:40:06 2019 -0500 +++ b/src/java.base/share/classes/jdk/internal/module/Modules.java Tue Nov 19 18:45:08 2019 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, 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 @@ -25,6 +25,7 @@ package jdk.internal.module; +import java.io.PrintStream; import java.lang.module.Configuration; import java.lang.module.ModuleDescriptor; import java.lang.module.ModuleFinder; @@ -33,6 +34,7 @@ import java.net.URI; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; @@ -40,6 +42,7 @@ import java.util.function.Function; import java.util.stream.Collectors; +import jdk.internal.access.JavaLangModuleAccess; import jdk.internal.loader.BootLoader; import jdk.internal.loader.BuiltinClassLoader; import jdk.internal.loader.ClassLoaders; @@ -61,6 +64,7 @@ private Modules() { } private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess(); + private static final JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess(); /** * Creates a new Module. The module has the given ModuleDescriptor and @@ -159,6 +163,20 @@ } /** + * Resolves a collection of root modules, with service binding and the empty + * Configuration as the parent to create a Configuration for the boot layer. + * + * This method is intended to be used to create the Configuration for the + * boot layer during startup or at a link-time. + */ + public static Configuration newBootLayerConfiguration(ModuleFinder finder, + Collection roots, + PrintStream traceOutput) + { + return JLMA.resolveAndBind(finder, roots, traceOutput); + } + + /** * Called by the VM when code in the given Module has been transformed by * an agent and so may have been instrumented to call into supporting * classes on the boot class path or application class path. diff -r 4ad81e9e30fd -r d1fe86ccc832 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java Mon Nov 18 12:40:06 2019 -0500 +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/runtime/JavaThread.java Tue Nov 19 18:45:08 2019 -0500 @@ -384,14 +384,14 @@ Address stackBase = getStackBase(); // Be robust if (sp == null) return false; - return stackBase.greaterThanOrEqual(a) && sp.lessThanOrEqual(a); + return stackBase.greaterThan(a) && sp.lessThanOrEqual(a); } public boolean isLockOwned(Address a) { Address stackBase = getStackBase(); Address stackLimit = stackBase.addOffsetTo(-getStackSize()); - return stackBase.greaterThanOrEqual(a) && stackLimit.lessThanOrEqual(a); + return stackBase.greaterThan(a) && stackLimit.lessThanOrEqual(a); // FIXME: should traverse MonitorArray/MonitorChunks as in VM } diff -r 4ad81e9e30fd -r d1fe86ccc832 src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java --- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java Mon Nov 18 12:40:06 2019 -0500 +++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java Tue Nov 19 18:45:08 2019 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, 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 @@ -61,6 +61,7 @@ import jdk.internal.module.Checks; import jdk.internal.module.DefaultRoots; import jdk.internal.module.IllegalAccessMaps; +import jdk.internal.module.Modules; import jdk.internal.module.ModuleHashes; import jdk.internal.module.ModuleInfo.Attributes; import jdk.internal.module.ModuleInfoExtender; @@ -291,10 +292,10 @@ /** * Resolves a collection of root modules, with service binding, to create - * configuration. + * a Configuration for the boot layer. */ private Configuration resolve(ModuleFinder finder, Set roots) { - return Configuration.empty().resolveAndBind(finder, ModuleFinder.of(), roots); + return Modules.newBootLayerConfiguration(finder, roots, null); } /** diff -r 4ad81e9e30fd -r d1fe86ccc832 test/hotspot/jtreg/compiler/codegen/TestCharVect2.java --- a/test/hotspot/jtreg/compiler/codegen/TestCharVect2.java Mon Nov 18 12:40:06 2019 -0500 +++ b/test/hotspot/jtreg/compiler/codegen/TestCharVect2.java Tue Nov 19 18:45:08 2019 -0500 @@ -24,9 +24,17 @@ /** * @test * @bug 8001183 - * @summary incorrect results of char vectors right shift operaiton + * @summary incorrect results of char vectors right shift operation * * @run main/othervm/timeout=400 -Xbatch -Xmx128m compiler.codegen.TestCharVect2 + */ + +/** + * @test + * @bug 8001183 + * @summary incorrect results of char vectors right shift operation + * @requires vm.compiler2.enabled | vm.graal.enabled + * * @run main/othervm/timeout=400 -Xbatch -Xmx128m -XX:MaxVectorSize=8 compiler.codegen.TestCharVect2 * @run main/othervm/timeout=400 -Xbatch -Xmx128m -XX:MaxVectorSize=16 compiler.codegen.TestCharVect2 * @run main/othervm/timeout=400 -Xbatch -Xmx128m -XX:MaxVectorSize=32 compiler.codegen.TestCharVect2 diff -r 4ad81e9e30fd -r d1fe86ccc832 test/hotspot/jtreg/runtime/cds/appcds/javaldr/AnonVmClassesDuringDump.java --- a/test/hotspot/jtreg/runtime/cds/appcds/javaldr/AnonVmClassesDuringDump.java Mon Nov 18 12:40:06 2019 -0500 +++ b/test/hotspot/jtreg/runtime/cds/appcds/javaldr/AnonVmClassesDuringDump.java Tue Nov 19 18:45:08 2019 -0500 @@ -32,16 +32,15 @@ * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds /test/hotspot/jtreg/runtime/cds/appcds/test-classes * @requires vm.cds * @requires vm.flavor != "minimal" - * @build AnonVmClassesDuringDumpTransformer Hello - * @run main/othervm AnonVmClassesDuringDump + * @run driver AnonVmClassesDuringDump */ public class AnonVmClassesDuringDump { public static String appClasses[] = { - "Hello", + Hello.class.getName(), }; public static String agentClasses[] = { - "AnonVmClassesDuringDumpTransformer", + AnonVmClassesDuringDumpTransformer.class.getName(), }; public static String cdsDiagnosticOption = "-XX:+AllowArchivingWithJavaAgent"; @@ -55,7 +54,7 @@ String appJar = ClassFileInstaller.writeJar("AnonVmClassesDuringDumpApp.jar", appClasses); - TestCommon.testDump(appJar, TestCommon.list("Hello"), + TestCommon.testDump(appJar, TestCommon.list(Hello.class.getName()), "-javaagent:" + agentJar, "-XX:+UnlockDiagnosticVMOptions", cdsDiagnosticOption, // Set the following property to see logs for dynamically generated classes @@ -71,13 +70,13 @@ String pattern = prefix + class_pattern + suffix; // during run time, anonymous classes shouldn't be loaded from the archive TestCommon.run("-cp", appJar, - "-XX:+UnlockDiagnosticVMOptions", cdsDiagnosticOption, "Hello") + "-XX:+UnlockDiagnosticVMOptions", cdsDiagnosticOption, Hello.class.getName()) .assertNormalExit(output -> output.shouldNotMatch(pattern)); // inspect the archive and make sure no anonymous class is in there TestCommon.run("-cp", appJar, "-XX:+UnlockDiagnosticVMOptions", cdsDiagnosticOption, - "-XX:+PrintSharedArchiveAndExit", "-XX:+PrintSharedDictionary", "Hello") + "-XX:+PrintSharedArchiveAndExit", "-XX:+PrintSharedDictionary", Hello.class.getName()) .assertNormalExit(output -> output.shouldNotMatch(class_pattern)); } } diff -r 4ad81e9e30fd -r d1fe86ccc832 test/hotspot/jtreg/runtime/cds/appcds/javaldr/ArrayTest.java --- a/test/hotspot/jtreg/runtime/cds/appcds/javaldr/ArrayTest.java Mon Nov 18 12:40:06 2019 -0500 +++ b/test/hotspot/jtreg/runtime/cds/appcds/javaldr/ArrayTest.java Tue Nov 19 18:45:08 2019 -0500 @@ -28,7 +28,6 @@ * @requires vm.cds * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds * @modules jdk.jartool/sun.tools.jar - * @compile ArrayTestHelper.java * @build sun.hotspot.WhiteBox * @run driver ClassFileInstaller sun.hotspot.WhiteBox * @run driver ArrayTest @@ -41,7 +40,7 @@ public class ArrayTest { static String arrayClasses[] = { - "ArrayTestHelper", + ArrayTestHelper.class.getName(), "[Ljava/lang/Comparable;", "[I", "[[[Ljava/lang/Object;", @@ -70,7 +69,7 @@ argsList.add("-cp"); argsList.add(appJar); argsList.add(bootClassPath); - argsList.add("ArrayTestHelper"); + argsList.add(ArrayTestHelper.class.getName()); // the following are input args to the ArrayTestHelper. // skip checking array classes during run time for (int i = 0; i < 1; i++) { diff -r 4ad81e9e30fd -r d1fe86ccc832 test/hotspot/jtreg/runtime/cds/appcds/javaldr/GCDuringDump.java --- a/test/hotspot/jtreg/runtime/cds/appcds/javaldr/GCDuringDump.java Mon Nov 18 12:40:06 2019 -0500 +++ b/test/hotspot/jtreg/runtime/cds/appcds/javaldr/GCDuringDump.java Tue Nov 19 18:45:08 2019 -0500 @@ -28,8 +28,7 @@ * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds /test/hotspot/jtreg/runtime/cds/appcds/test-classes * @requires vm.cds * @requires vm.flavor != "minimal" - * @build GCDuringDumpTransformer Hello - * @run main/othervm GCDuringDump + * @run driver GCDuringDump */ import jdk.test.lib.cds.CDSOptions; @@ -38,10 +37,10 @@ public class GCDuringDump { public static String appClasses[] = { - "Hello", + Hello.class.getName(), }; public static String agentClasses[] = { - "GCDuringDumpTransformer", + GCDuringDumpTransformer.class.getName(), }; public static void main(String[] args) throws Throwable { @@ -63,7 +62,7 @@ String extraArg = (i == 0) ? "-showversion" : "-javaagent:" + agentJar; String extraOption = (i == 0) ? "-showversion" : "-XX:+AllowArchivingWithJavaAgent"; - TestCommon.testDump(appJar, TestCommon.list("Hello"), + TestCommon.testDump(appJar, TestCommon.list(Hello.class.getName()), "-XX:+UnlockDiagnosticVMOptions", extraOption, extraArg, "-Xmx32m", gcLog); @@ -73,7 +72,7 @@ "-XX:+PrintSharedSpaces", "-XX:+UnlockDiagnosticVMOptions", extraOption, gcLog, - "Hello") + Hello.class.getName()) .assertNormalExit(); } } diff -r 4ad81e9e30fd -r d1fe86ccc832 test/hotspot/jtreg/runtime/cds/appcds/javaldr/GCSharedStringsDuringDump.java --- a/test/hotspot/jtreg/runtime/cds/appcds/javaldr/GCSharedStringsDuringDump.java Mon Nov 18 12:40:06 2019 -0500 +++ b/test/hotspot/jtreg/runtime/cds/appcds/javaldr/GCSharedStringsDuringDump.java Tue Nov 19 18:45:08 2019 -0500 @@ -29,9 +29,9 @@ * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds /test/hotspot/jtreg/runtime/cds/appcds/test-classes * @requires vm.cds.archived.java.heap * @modules jdk.jartool/sun.tools.jar - * @build sun.hotspot.WhiteBox GCDuringDumpTransformer GCSharedStringsDuringDumpWb + * @build sun.hotspot.WhiteBox * @run driver ClassFileInstaller sun.hotspot.WhiteBox - * @run main/othervm/timeout=480 GCSharedStringsDuringDump + * @run driver/timeout=480 GCSharedStringsDuringDump */ import java.io.File; @@ -41,14 +41,13 @@ import jdk.test.lib.cds.CDSOptions; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; -import sun.hotspot.WhiteBox; public class GCSharedStringsDuringDump { public static String appClasses[] = { - "GCSharedStringsDuringDumpWb", + GCSharedStringsDuringDumpWb.class.getName(), }; public static String agentClasses[] = { - "GCDuringDumpTransformer", + GCDuringDumpTransformer.class.getName(), }; public static void main(String[] args) throws Throwable { @@ -88,7 +87,7 @@ String extraArg = (i == 0) ? "-showversion" : "-javaagent:" + agentJar; String extraOption = (i == 0) ? "-showversion" : "-XX:+AllowArchivingWithJavaAgent"; OutputAnalyzer output = TestCommon.dump( - appJar, TestCommon.list("GCSharedStringsDuringDumpWb"), + appJar, TestCommon.list(GCSharedStringsDuringDumpWb.class.getName()), bootClassPath, extraArg, "-Xmx32m", gcLog, "-XX:SharedArchiveConfigFile=" + sharedArchiveCfgFile, "-XX:+UnlockDiagnosticVMOptions", extraOption); @@ -101,7 +100,7 @@ // Try again with larger heap and NewSize, this should increase the // G1 heap region size to 2M TestCommon.testDump( - appJar, TestCommon.list("GCSharedStringsDuringDumpWb"), + appJar, TestCommon.list(GCSharedStringsDuringDumpWb.class.getName()), bootClassPath, extraArg, "-Xmx8g", "-XX:NewSize=8m", gcLog, "-XX:SharedArchiveConfigFile=" + sharedArchiveCfgFile, "-XX:+UnlockDiagnosticVMOptions", extraOption); @@ -119,7 +118,7 @@ "-XX:+WhiteBoxAPI", "-XX:SharedReadOnlySize=30m", gcLog, - "GCSharedStringsDuringDumpWb") + GCSharedStringsDuringDumpWb.class.getName()) .assertNormalExit(); } } diff -r 4ad81e9e30fd -r d1fe86ccc832 test/hotspot/jtreg/runtime/cds/appcds/javaldr/HumongousDuringDump.java --- a/test/hotspot/jtreg/runtime/cds/appcds/javaldr/HumongousDuringDump.java Mon Nov 18 12:40:06 2019 -0500 +++ b/test/hotspot/jtreg/runtime/cds/appcds/javaldr/HumongousDuringDump.java Tue Nov 19 18:45:08 2019 -0500 @@ -28,8 +28,7 @@ * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds /test/hotspot/jtreg/runtime/cds/appcds/test-classes * @requires vm.cds.archived.java.heap * @requires vm.flavor != "minimal" - * @build HumongousDuringDumpTransformer Hello - * @run main/othervm/timeout=240 HumongousDuringDump + * @run driver/timeout=240 HumongousDuringDump */ import jdk.test.lib.cds.CDSOptions; @@ -38,10 +37,10 @@ public class HumongousDuringDump { public static String appClasses[] = { - "Hello", + Hello.class.getName(), }; public static String agentClasses[] = { - "HumongousDuringDumpTransformer", + HumongousDuringDumpTransformer.class.getName(), }; public static void main(String[] args) throws Throwable { @@ -60,7 +59,7 @@ String extraOption = "-XX:+AllowArchivingWithJavaAgent"; OutputAnalyzer out = - TestCommon.testDump(appJar, TestCommon.list("Hello"), + TestCommon.testDump(appJar, TestCommon.list(Hello.class.getName()), "-XX:+UnlockDiagnosticVMOptions", extraOption, "-Xlog:gc+region+cds", "-Xlog:gc+region=trace", @@ -79,7 +78,7 @@ "-XX:+PrintSharedSpaces", "-XX:+UnlockDiagnosticVMOptions", extraOption, gcLog, - "Hello") + Hello.class.getName()) .assertNormalExit(); } } diff -r 4ad81e9e30fd -r d1fe86ccc832 test/hotspot/jtreg/serviceability/tmtools/share/common/ToolRunner.java --- a/test/hotspot/jtreg/serviceability/tmtools/share/common/ToolRunner.java Mon Nov 18 12:40:06 2019 -0500 +++ b/test/hotspot/jtreg/serviceability/tmtools/share/common/ToolRunner.java Tue Nov 19 18:45:08 2019 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 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 @@ -22,15 +22,11 @@ */ package common; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.StringTokenizer; -import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.process.ProcessTools; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.time.Instant; /** * This class starts a process specified by the passed command line waits till @@ -38,14 +34,10 @@ * output as ToolResults */ class ToolRunner { - - private final List cmdArgs = new LinkedList<>(); + private final String[] cmdArgs; ToolRunner(String cmdLine) { - StringTokenizer st = new StringTokenizer(cmdLine); - while (st.hasMoreTokens()) { - cmdArgs.add(st.nextToken()); - } + cmdArgs = cmdLine.split(" +"); } /** @@ -56,23 +48,18 @@ * @throws Exception if anything goes wrong */ ToolResults runToCompletion() throws Exception { - ProcessBuilder pb = new ProcessBuilder(cmdArgs); - OutputAnalyzer oa = ProcessTools.executeProcess(pb); - - return new ToolResults(oa.getExitValue(), - stringToList(oa.getStdout()), - stringToList(oa.getStderr())); + Path out = Files.createTempFile(Paths.get("."), "out.", ".txt"); + Path err = out.resolveSibling(out.getFileName().toString().replaceFirst("out", "err")); - } - - private static List stringToList(String s) throws IOException { - BufferedReader reader = new BufferedReader(new StringReader(s)); - List strings = new ArrayList<>(); - for (String line = reader.readLine(); line != null; line = reader.readLine()) { - strings.add(line); - } - reader.close(); - return strings; + Process p = pb.redirectOutput(ProcessBuilder.Redirect.to(out.toFile())) + .redirectError(ProcessBuilder.Redirect.to(err.toFile())) + .start(); + System.out.printf("[%s] started process %d %s with out/err redirected to '%s' and '%s'%n", + Instant.now().toString(), p.pid(), pb.command(), out.toString(), err.toString()); + int exitCode = p.waitFor(); + System.out.printf("[%s] process %d finished with exit code = %d%n", + Instant.now().toString(), p.pid(), exitCode); + return new ToolResults(exitCode, Files.readAllLines(out), Files.readAllLines(err)); } } diff -r 4ad81e9e30fd -r d1fe86ccc832 test/jdk/java/lang/Class/forName/NonLinking/Container.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/Class/forName/NonLinking/Container.java Tue Nov 19 18:45:08 2019 -0500 @@ -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. + */ + +public class Container { + + public Container(MissingClass m) {} + + public Container() { + this(new MissingClass() {}); + } +} diff -r 4ad81e9e30fd -r d1fe86ccc832 test/jdk/java/lang/Class/forName/NonLinking/MissingClass.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/Class/forName/NonLinking/MissingClass.java Tue Nov 19 18:45:08 2019 -0500 @@ -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 MissingClass {} diff -r 4ad81e9e30fd -r d1fe86ccc832 test/jdk/java/lang/Class/forName/NonLinking/NonLinking.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/lang/Class/forName/NonLinking/NonLinking.java Tue Nov 19 18:45:08 2019 -0500 @@ -0,0 +1,77 @@ +/* + * 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. + */ + +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Path; +import java.nio.file.Paths; + +/* + * @test + * @bug 8231924 8233091 8233272 + * @summary Confirm load (but not link) behavior of Class.forName() + * @library /test/lib + * + * @compile MissingClass.java Container.java + * + * @run driver ClassFileInstaller -jar classes.jar Container Container$1 + * + * @run main/othervm NonLinking init + * @run main/othervm NonLinking load + */ +/* + * The @compile and '@main ClassFileInstaller' tasks above create a classes.jar + * file containing the .class file for Container, but not MissingClass. + */ + +public class NonLinking { + public static void main(String[] args) throws Throwable { + Path jarPath = Paths.get("classes.jar"); + URL url = jarPath.toUri().toURL(); + URLClassLoader ucl1 = new URLClassLoader("UCL1", + new URL[] { url }, + null); // Don't delegate + switch(args[0]) { + case "init": + try { + // Trying to initialize Container without MissingClass -> NCDFE + Class.forName("Container", true, ucl1); + throw new RuntimeException("Missed expected NoClassDefFoundError"); + } catch (NoClassDefFoundError expected) { + final String CLASSNAME = "MissingClass"; + Throwable cause = expected.getCause(); + if (!cause.getMessage().contains(CLASSNAME)) { + throw new RuntimeException("Cause of NoClassDefFoundError does not contain \"" + CLASSNAME + "\"", cause); + } + } + break; + case "load": + // Loading (but not linking) Container will succeed. + // Before 8233091, this fails with NCDFE due to linking. + Class.forName("Container", false, ucl1); + break; + default: + throw new RuntimeException("Unknown command: " + args[0]); + } + } +} diff -r 4ad81e9e30fd -r d1fe86ccc832 test/jdk/jdk/modules/etc/DefaultModules.java --- a/test/jdk/jdk/modules/etc/DefaultModules.java Mon Nov 18 12:40:06 2019 -0500 +++ b/test/jdk/jdk/modules/etc/DefaultModules.java Tue Nov 19 18:45:08 2019 -0500 @@ -59,8 +59,8 @@ String testSrc = System.getProperty("test.src"); // $JDK_HOME/bin/java TestModules.java - String source = Path.of(testSrc, "src", "TestRootModules.java").toString(); - ProcessTools.executeTestJava(source) + String source = Path.of(testSrc, "TestRootModules.java").toString(); + ProcessTools.executeTestJava("--add-exports", "java.base/jdk.internal.module=ALL-UNNAMED", source) .outputTo(System.out) .errorTo(System.err) .shouldHaveExitValue(0); @@ -89,15 +89,18 @@ javaLauncher += ".exe"; // $CUSTOM_JDK/bin/java TestRootModules.java - source = Path.of(testSrc, "src", "TestRootModules.java").toString(); - out.format("Command line: [%s %s]%n", javaLauncher, source); - ProcessTools.executeProcess(new ProcessBuilder(javaLauncher, source)) + source = Path.of(testSrc, "TestRootModules.java").toString(); + ProcessBuilder pb = new ProcessBuilder(javaLauncher, + "--add-exports", "java.base/jdk.internal.module=ALL-UNNAMED", + source); + out.format("Command line: [%s]%n", pb.command()); + ProcessTools.executeProcess(pb) .outputTo(System.out) .errorTo(System.err) .shouldHaveExitValue(0); // $CUSTOM_JDK/bin/java TestJson.java - source = Path.of(testSrc, "src", "TestJson.java").toString(); + source = Path.of(testSrc, "TestJson.java").toString(); out.format("Command line: [%s %s]%n", javaLauncher, source); ProcessTools.executeProcess(new ProcessBuilder(javaLauncher, source)) .outputTo(System.out) diff -r 4ad81e9e30fd -r d1fe86ccc832 test/jdk/jdk/modules/etc/TestJson.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/modules/etc/TestJson.java Tue Nov 19 18:45:08 2019 -0500 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018, 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. + */ + +import javax.json.*; +import java.io.InputStream; + +/** + * Exercise APIs exported by the java.json module + */ + +public class TestJson { + public static void main(String[] args) { + JsonParser parser = Json.createParser(InputStream.nullInputStream()); + } +} diff -r 4ad81e9e30fd -r d1fe86ccc832 test/jdk/jdk/modules/etc/TestRootModules.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/modules/etc/TestRootModules.java Tue Nov 19 18:45:08 2019 -0500 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2018, 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. + */ + +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleFinder; +import java.lang.module.ModuleReference; + +import jdk.internal.module.ModuleResolution; + +/** + * Test the set of modules in the boot layer includes all modules that export + * an API. Also test that java.se is not resolved. + */ + +public class TestRootModules { + public static void main(String[] args) { + // all modules that export an API should be resolved + // For now, this test ignores the ModuleResolution attribute + ModuleLayer bootLayer = ModuleLayer.boot(); + ModuleFinder.ofSystem().findAll().stream() + .filter(mref -> !ModuleResolution.doNotResolveByDefault(mref)) + .map(ModuleReference::descriptor) + .filter(descriptor -> descriptor.exports() + .stream() + .filter(e -> !e.isQualified()) + .findAny() + .isPresent()) + .map(ModuleDescriptor::name) + .forEach(name -> { + if (!bootLayer.findModule(name).isPresent()) + throw new RuntimeException(name + " not in boot layer"); + }); + + // java.se should not be resolved + ModuleLayer.boot() + .findModule("java.se") + .map(m -> { throw new RuntimeException("java.se should not be resolved"); }); + } +} diff -r 4ad81e9e30fd -r d1fe86ccc832 test/jdk/jdk/modules/etc/src/TestJson.java --- a/test/jdk/jdk/modules/etc/src/TestJson.java Mon Nov 18 12:40:06 2019 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2018, 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. - */ - -import javax.json.*; -import java.io.InputStream; - -/** - * Exercise APIs exported by the java.json module - */ - -public class TestJson { - public static void main(String[] args) { - JsonParser parser = Json.createParser(InputStream.nullInputStream()); - } -} diff -r 4ad81e9e30fd -r d1fe86ccc832 test/jdk/jdk/modules/etc/src/TestRootModules.java --- a/test/jdk/jdk/modules/etc/src/TestRootModules.java Mon Nov 18 12:40:06 2019 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2018, 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. - */ - - -import java.lang.module.ModuleDescriptor; -import java.lang.module.ModuleFinder; -import java.lang.module.ModuleReference; - -/** - * Test the set of modules in the boot layer includes all modules that export - * an API. Also test that java.se is not resolved. - */ - -public class TestRootModules { - public static void main(String[] args) { - // all modules that export an API should be resolved - // For now, this test ignores the ModuleResolution attribute - ModuleLayer bootLayer = ModuleLayer.boot(); - ModuleFinder.ofSystem().findAll().stream() - .map(ModuleReference::descriptor) - .filter(descriptor -> descriptor.exports() - .stream() - .filter(e -> !e.isQualified()) - .findAny() - .isPresent()) - .map(ModuleDescriptor::name) - .forEach(name -> { - if (!bootLayer.findModule(name).isPresent()) - throw new RuntimeException(name + " not in boot layer"); - }); - - // java.se should not be resolved - ModuleLayer.boot() - .findModule("java.se") - .map(m -> { throw new RuntimeException("java.se should not be resolved"); }); - } -} diff -r 4ad81e9e30fd -r d1fe86ccc832 test/jdk/jdk/modules/incubator/ServiceBinding.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/modules/incubator/ServiceBinding.java Tue Nov 19 18:45:08 2019 -0500 @@ -0,0 +1,226 @@ +/* + * 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 8233922 + * @modules java.base/jdk.internal.module + * @library /test/lib + * @build ServiceBinding TestBootLayer + * @run testng ServiceBinding + * @summary Test service binding with incubator modules + */ + +import java.io.File; +import java.io.OutputStream; +import java.lang.module.ModuleDescriptor; +import java.lang.module.Configuration; +import java.lang.module.ModuleFinder; +import java.lang.module.ResolvedModule; +import java.nio.file.Path; +import java.nio.file.Files; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.Stream; + +import static java.lang.module.ModuleDescriptor.newModule; + +import jdk.internal.module.ModuleInfoWriter; +import jdk.internal.module.ModuleResolution; + +import org.testng.annotations.Test; + +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.process.OutputAnalyzer; + +@Test +public class ServiceBinding { + private static final Path HERE = Path.of("."); + + /** + * module m1 uses p.S + * (incubating) module m2 requires m1 provides p.S + */ + public void test1() throws Exception { + Path mlib = Files.createTempDirectory(HERE, "mlib"); + + var m1 = newModule("m1").exports("p").uses("p.S").build(); + var m2 = newModule("m2").requires("m1").provides("p.S", List.of("impl.S1")).build(); + + writeModule(mlib, m1); + writeIncubatingModule(mlib, m2); + + // boot layer: root=m1, incubator module m2 should not be resolved + testBootLayer(mlib, Set.of("m1"), Set.of("m1"), Set.of("m2")) + .shouldNotMatch("WARNING:.*m2"); + + // custom configuration: root=m1, incubator module m2 should be resolved + testCustomConfiguration(mlib, Set.of("m1"), Set.of("m2")); + } + + /** + * module m1 uses p.S + * (incubating) module m2 requires m1 provides P.S uses q.S + * (incubating) module m3 requires m2 provides q.S + */ + public void test2() throws Exception { + Path mlib = Files.createTempDirectory("mlib"); + + var m1 = newModule("m1").exports("p").uses("p.S").build(); + var m2 = newModule("m2") + .requires("m1") + .provides("p.S", List.of("impl.S1")) + .exports("q") + .uses("q.S") + .build(); + var m3 = newModule("m3").requires("m2").provides("q.S", List.of("impl.S1")).build(); + + writeModule(mlib, m1); + writeIncubatingModule(mlib, m2); + writeIncubatingModule(mlib, m3); + + // boot layer: root=m1, incubator modules m2 and m3 should not be resolved + testBootLayer(mlib, Set.of("m1"), Set.of("m1"), Set.of("m2", "m3")) + .shouldNotMatch("WARNING:.*m2") + .shouldNotMatch("WARNING:.*m3"); + + // boot layer: root=m2, incubator module m3 should not be resolved + testBootLayer(mlib, Set.of("m2"), Set.of("m1", "m2"), Set.of("m3")) + .shouldMatch("WARNING:.*m2") + .shouldNotMatch("WARNING:.*m3"); + + // custom configuration: root=m1, incubator modules m2 and m3 should be resolved + testCustomConfiguration(mlib, Set.of("m1"), Set.of("m1", "m2", "m3")); + + // custom configuration: root=m2, incubator module m3 should be resolved + testCustomConfiguration(mlib, Set.of("m2"), Set.of("m1", "m2", "m3")); + } + + /** + * Creates an exploded module on the file system. + * + * @param mlib the top-level module directory + * @param descriptor the module descriptor of the module to write + */ + void writeModule(Path mlib, ModuleDescriptor descriptor) throws Exception { + writeModule(mlib, descriptor, false); + } + + /** + * Creates an exploded module on the file system. The module will be an + * incubating module. + * + * @param mlib the top-level module directory + * @param descriptor the module descriptor of the module to write + */ + void writeIncubatingModule(Path mlib, ModuleDescriptor descriptor) throws Exception { + writeModule(mlib, descriptor, true); + } + + /** + * Creates an exploded module on the file system. + * + * @param mlib the top-level module directory + * @param descriptor the module descriptor of the module to write + * @param incubating to create an incubating module + */ + void writeModule(Path mlib, ModuleDescriptor descriptor, boolean incubating) + throws Exception + { + // create ModuleResolution attribute if incubating module + ModuleResolution mres = (incubating) ? ModuleResolution.empty().withIncubating() : null; + String name = descriptor.name(); + + // create directory for module + Path dir = Files.createDirectory(mlib.resolve(name)); + + // module-info.class + try (OutputStream out = Files.newOutputStream(dir.resolve("module-info.class"))) { + ModuleInfoWriter.write(descriptor, mres, out); + } + + // create a dummy class file for each package + for (String pn : descriptor.packages()) { + Path subdir = dir.resolve(pn.replace('.', File.separatorChar)); + Files.createDirectories(subdir); + Files.createFile(subdir.resolve("C.class")); + } + } + + /** + * Run TestBootLayer in a child VM with the given module path and the + * --add-modules option with additional root modules. TestBootLayer checks + * the modules in the boot layer. + * + * @param mlib the module path + * @param roots the modules to specify to --add-modules + * @param expected the names of modules that should be in the boot layer + * @param notExpected the names of modules that should not be in boot layer + */ + OutputAnalyzer testBootLayer(Path mlib, + Set roots, + Set expected, + Set notExpected) + throws Exception + { + var opts = Stream.of("-p", mlib.toString(), + "--add-modules", commaSeparated(roots), + "TestBootLayer", commaSeparated(expected), commaSeparated(notExpected)); + return ProcessTools.executeTestJava(opts.toArray(String[]::new)) + .outputTo(System.out) + .errorTo(System.out) + .shouldHaveExitValue(0); + } + + /** + * Creates a Configuration by resolving a set of root modules, with service + * binding, then checks that the Configuration includes the expected modules. + * + * @param mlib the module path + * @param roots the names of the root modules + * @param expected the names of modules that should be in the configuration + */ + void testCustomConfiguration(Path mlib, Set roots, Set expected) { + ModuleFinder finder = ModuleFinder.of(mlib); + Configuration cf = ModuleLayer.boot() + .configuration() + .resolveAndBind(finder, ModuleFinder.of(), roots); + + Set modules = cf.modules().stream() + .map(ResolvedModule::name) + .collect(Collectors.toSet()); + + expected.stream() + .filter(mn -> !modules.contains(mn)) + .findAny() + .ifPresent(mn -> { + throw new RuntimeException(mn + " not in configuration!!!"); + }); + } + + String commaSeparated(Set s) { + return s.stream().collect(Collectors.joining(",")); + } +} diff -r 4ad81e9e30fd -r d1fe86ccc832 test/jdk/jdk/modules/incubator/TestBootLayer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/jdk/modules/incubator/TestBootLayer.java Tue Nov 19 18:45:08 2019 -0500 @@ -0,0 +1,60 @@ +/* + * 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. + */ + +import java.util.Set; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * Launched by the test ServiceBinding to test modules in the boot layer. + */ + +public class TestBootLayer { + public static void main(String[] args) throws Exception { + Pattern splitter = Pattern.compile(","); + + // the names of all modules in the boot layer + Set modules = ModuleLayer.boot().modules().stream() + .map(Module::getName) + .collect(Collectors.toSet()); + + // throw exception if an expected module is not in the boot layer + splitter.splitAsStream(args[0]) + .filter(Predicate.not(String::isEmpty)) + .filter(mn -> !modules.contains(mn)) + .findAny() + .ifPresent(mn -> { + throw new RuntimeException(mn + " not in boot layer!!!"); + }); + + // throw exception if an unexpected module is in the boot layer + splitter.splitAsStream(args[1]) + .filter(Predicate.not(String::isEmpty)) + .filter(mn -> modules.contains(mn)) + .findAny() + .ifPresent(mn -> { + throw new RuntimeException(mn + " in boot layer!!!!"); + }); + } +} \ No newline at end of file