diff --git a/.hgtags b/.hgtags
index a2481e251c9..dd3fabee47c 100644
--- a/.hgtags
+++ b/.hgtags
@@ -369,3 +369,4 @@ d53037a90c441cb528dc41c30827985de0e67c62 jdk-9+123
2a5697a98620c4f40e4a1a71478464399b8878de jdk-9+124
3aa52182b3ad7c5b3a61cf05a59dd07e4c5884e5 jdk-9+125
03e7b2c5ae345be3caf981d76ceb3efe5ff447f8 jdk-9+126
+8e45018bde9de4ad15b972ae62874bba52dba2d5 jdk-9+127
diff --git a/.hgtags-top-repo b/.hgtags-top-repo
index 783312526e8..b4f5b7c73a6 100644
--- a/.hgtags-top-repo
+++ b/.hgtags-top-repo
@@ -369,3 +369,4 @@ cae471d3b87783e0a3deea658e1e1c84b2485b6c jdk-9+121
f80c841ae2545eaf9acd2724bccc305d98cefbe2 jdk-9+124
9aa7d40f3a453f51e47f4c1b19eff5740a74a9f8 jdk-9+125
3a58466296d36944454756ef01e7513ac5e14a16 jdk-9+126
+8fa686245bd2a072ece3392743460030f0854520 jdk-9+127
diff --git a/corba/.hgtags b/corba/.hgtags
index d514b137ea5..2667a3cc368 100644
--- a/corba/.hgtags
+++ b/corba/.hgtags
@@ -369,3 +369,4 @@ e33a34cc551907617d8129c4faaf1a5a7e61d21c jdk-9+123
45121d5afb9d5bfadab75378572ad96832e0809e jdk-9+124
1d48e67d1b91eb9f72e49e69a4021edb85e357fc jdk-9+125
c7f5ba08fcd4b8416e62c21229f9a07c95498919 jdk-9+126
+8fab452b6f4710762ba1d8e55fd62db00b1355fe jdk-9+127
diff --git a/hotspot/.hgtags b/hotspot/.hgtags
index 393252486a7..7415efbd4d7 100644
--- a/hotspot/.hgtags
+++ b/hotspot/.hgtags
@@ -529,3 +529,4 @@ af6b4ad908e732d23021f12e8322b204433d5cf6 jdk-9+122
479631362b4930be985245ea063d87d821a472eb jdk-9+124
bb640b49741af3f57f9994129934c46fc173219f jdk-9+125
adc8c84b7cf8c540d920182f78a2bc982366432a jdk-9+126
+352357128f602dcf0426b1cbe011a4685a4d9f97 jdk-9+127
diff --git a/hotspot/make/gensrc/GensrcDtrace.gmk b/hotspot/make/gensrc/GensrcDtrace.gmk
index 563f22112fb..126be0b3599 100644
--- a/hotspot/make/gensrc/GensrcDtrace.gmk
+++ b/hotspot/make/gensrc/GensrcDtrace.gmk
@@ -45,7 +45,8 @@ ifeq ($(call check-jvm-feature, dtrace), true)
$(DTRACE_GENSRC_DIR)/%.h: $(DTRACE_SOURCE_DIR)/%.d
$(call LogInfo, Generating dtrace header file $(@F))
$(call MakeDir, $(@D) $(DTRACE_SUPPORT_DIR))
- $(call ExecuteWithLog, $(DTRACE_SUPPORT_DIR)/$(@F).d, $(CC) -E $(DTRACE_CPP_FLAGS) $< > $(DTRACE_SUPPORT_DIR)/$(@F).d)
+ $(call ExecuteWithLog, $(DTRACE_SUPPORT_DIR)/$(@F).d, \
+ ( $(CC) -E $(DTRACE_CPP_FLAGS) $< > $(DTRACE_SUPPORT_DIR)/$(@F).d ) )
$(call ExecuteWithLog, $@, $(DTRACE) $(DTRACE_FLAGS) -h -o $@ -s $(DTRACE_SUPPORT_DIR)/$(@F).d)
# Process all .d files in DTRACE_SOURCE_DIR. They are:
diff --git a/hotspot/make/lib/CompileDtracePostJvm.gmk b/hotspot/make/lib/CompileDtracePostJvm.gmk
index 50a7f10d67d..45c81ad9c4e 100644
--- a/hotspot/make/lib/CompileDtracePostJvm.gmk
+++ b/hotspot/make/lib/CompileDtracePostJvm.gmk
@@ -68,7 +68,7 @@ ifeq ($(call check-jvm-feature, dtrace), true)
$1: $$(BUILD_DTRACE_GEN_OFFSETS)
$$(call LogInfo, Generating dtrace $2 file $$(@F))
$$(call MakeDir, $$(@D))
- $$(call ExecuteWithLog, $$@, $$(DTRACE_GEN_OFFSETS_TOOL) -$$(strip $2) > $$@)
+ $$(call ExecuteWithLog, $$@, ( $$(DTRACE_GEN_OFFSETS_TOOL) -$$(strip $2) > $$@ ) )
TARGETS += $1
endef
diff --git a/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/UncommonTrap.java b/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/UncommonTrap.java
index c4401c59a87..63001d35f7f 100644
--- a/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/UncommonTrap.java
+++ b/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/UncommonTrap.java
@@ -16,9 +16,9 @@
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
- * CA 95054 USA or visit www.sun.com if you need additional information or
- * have any questions.
+ * 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 com.sun.hotspot.tools.compiler;
diff --git a/hotspot/src/share/vm/classfile/classLoaderData.cpp b/hotspot/src/share/vm/classfile/classLoaderData.cpp
index 7635879a49a..f0fdc648095 100644
--- a/hotspot/src/share/vm/classfile/classLoaderData.cpp
+++ b/hotspot/src/share/vm/classfile/classLoaderData.cpp
@@ -501,13 +501,26 @@ ClassLoaderData::~ClassLoaderData() {
}
}
-/**
- * Returns true if this class loader data is for the platform class loader.
- */
+// Returns true if this class loader data is for the system class loader.
+bool ClassLoaderData::is_system_class_loader_data() const {
+ return SystemDictionary::is_system_class_loader(class_loader());
+}
+
+// Returns true if this class loader data is for the platform class loader.
bool ClassLoaderData::is_platform_class_loader_data() const {
return SystemDictionary::is_platform_class_loader(class_loader());
}
+// Returns true if this class loader data is one of the 3 builtin
+// (boot, application/system or platform) class loaders. Note, the
+// builtin loaders are not freed by a GC.
+bool ClassLoaderData::is_builtin_class_loader_data() const {
+ Handle classLoaderHandle = class_loader();
+ return (is_the_null_class_loader_data() ||
+ SystemDictionary::is_system_class_loader(classLoaderHandle) ||
+ SystemDictionary::is_platform_class_loader(classLoaderHandle));
+}
+
Metaspace* ClassLoaderData::metaspace_non_null() {
assert(!DumpSharedSpaces, "wrong metaspace!");
// If the metaspace has not been allocated, create a new one. Might want
@@ -957,12 +970,6 @@ bool ClassLoaderDataGraph::do_unloading(BoolObjectClosure* is_alive_closure,
data = _head;
while (data != NULL) {
if (data->is_alive(is_alive_closure)) {
- if (data->packages_defined()) {
- data->packages()->purge_all_package_exports();
- }
- if (data->modules_defined()) {
- data->modules()->purge_all_module_reads();
- }
// clean metaspace
if (walk_all_metadata) {
data->classes_do(InstanceKlass::purge_previous_versions);
@@ -990,6 +997,23 @@ bool ClassLoaderDataGraph::do_unloading(BoolObjectClosure* is_alive_closure,
}
if (seen_dead_loader) {
+ // Walk a ModuleEntry's reads and a PackageEntry's exports lists
+ // to determine if there are modules on those lists that are now
+ // dead and should be removed. A module's life cycle is equivalent
+ // to its defining class loader's life cycle. Since a module is
+ // considered dead if its class loader is dead, these walks must
+ // occur after each class loader's aliveness is determined.
+ data = _head;
+ while (data != NULL) {
+ if (data->packages_defined()) {
+ data->packages()->purge_all_package_exports();
+ }
+ if (data->modules_defined()) {
+ data->modules()->purge_all_module_reads();
+ }
+ data = data->next();
+ }
+
post_class_unload_events();
}
diff --git a/hotspot/src/share/vm/classfile/classLoaderData.hpp b/hotspot/src/share/vm/classfile/classLoaderData.hpp
index 9fc9839b7cf..602097935cc 100644
--- a/hotspot/src/share/vm/classfile/classLoaderData.hpp
+++ b/hotspot/src/share/vm/classfile/classLoaderData.hpp
@@ -270,7 +270,9 @@ class ClassLoaderData : public CHeapObj {
bool is_the_null_class_loader_data() const {
return this == _the_null_class_loader_data;
}
+ bool is_system_class_loader_data() const;
bool is_platform_class_loader_data() const;
+ bool is_builtin_class_loader_data() const;
// The Metaspace is created lazily so may be NULL. This
// method will allocate a Metaspace if needed.
diff --git a/hotspot/src/share/vm/classfile/compactHashtable.cpp b/hotspot/src/share/vm/classfile/compactHashtable.cpp
index f2feec3c810..a9b6b28f365 100644
--- a/hotspot/src/share/vm/classfile/compactHashtable.cpp
+++ b/hotspot/src/share/vm/classfile/compactHashtable.cpp
@@ -248,7 +248,7 @@ inline void SimpleCompactHashtable::iterate(const I& iterator) {
} else {
u4*entry_max = _entries + BUCKET_OFFSET(_buckets[i + 1]);
while (entry < entry_max) {
- iterator.do_value(_base_address, entry[0]);
+ iterator.do_value(_base_address, entry[1]);
entry += 2;
}
}
diff --git a/hotspot/src/share/vm/classfile/moduleEntry.cpp b/hotspot/src/share/vm/classfile/moduleEntry.cpp
index c507584c712..a8b67c54e64 100644
--- a/hotspot/src/share/vm/classfile/moduleEntry.cpp
+++ b/hotspot/src/share/vm/classfile/moduleEntry.cpp
@@ -40,7 +40,6 @@
ModuleEntry* ModuleEntryTable::_javabase_module = NULL;
-
void ModuleEntry::set_location(Symbol* location) {
if (_location != NULL) {
// _location symbol's refcounts are managed by ModuleEntry,
@@ -115,10 +114,35 @@ void ModuleEntry::add_read(ModuleEntry* m) {
// Lazily create a module's reads list
_reads = new (ResourceObj::C_HEAP, mtModule)GrowableArray(MODULE_READS_SIZE, true);
}
+
+ // Determine, based on this newly established read edge to module m,
+ // if this module's read list should be walked at a GC safepoint.
+ set_read_walk_required(m->loader_data());
+
+ // Establish readability to module m
_reads->append_if_missing(m);
}
}
+// If the module's loader, that a read edge is being established to, is
+// not the same loader as this module's and is not one of the 3 builtin
+// class loaders, then this module's reads list must be walked at GC
+// safepoint. Modules have the same life cycle as their defining class
+// loaders and should be removed if dead.
+void ModuleEntry::set_read_walk_required(ClassLoaderData* m_loader_data) {
+ assert_locked_or_safepoint(Module_lock);
+ if (!_must_walk_reads &&
+ loader_data() != m_loader_data &&
+ !m_loader_data->is_builtin_class_loader_data()) {
+ _must_walk_reads = true;
+ if (log_is_enabled(Trace, modules)) {
+ ResourceMark rm;
+ log_trace(modules)("ModuleEntry::set_read_walk_required(): module %s reads list must be walked",
+ (name() != NULL) ? name()->as_C_string() : UNNAMED_MODULE);
+ }
+ }
+}
+
bool ModuleEntry::has_reads() const {
assert_locked_or_safepoint(Module_lock);
return ((_reads != NULL) && !_reads->is_empty());
@@ -127,14 +151,28 @@ bool ModuleEntry::has_reads() const {
// Purge dead module entries out of reads list.
void ModuleEntry::purge_reads() {
assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
- if (has_reads()) {
+
+ if (_must_walk_reads && has_reads()) {
+ // This module's _must_walk_reads flag will be reset based
+ // on the remaining live modules on the reads list.
+ _must_walk_reads = false;
+
+ if (log_is_enabled(Trace, modules)) {
+ ResourceMark rm;
+ log_trace(modules)("ModuleEntry::purge_reads(): module %s reads list being walked",
+ (name() != NULL) ? name()->as_C_string() : UNNAMED_MODULE);
+ }
+
// Go backwards because this removes entries that are dead.
int len = _reads->length();
for (int idx = len - 1; idx >= 0; idx--) {
ModuleEntry* module_idx = _reads->at(idx);
- ClassLoaderData* cld = module_idx->loader();
- if (cld->is_unloading()) {
+ ClassLoaderData* cld_idx = module_idx->loader_data();
+ if (cld_idx->is_unloading()) {
_reads->delete_at(idx);
+ } else {
+ // Update the need to walk this module's reads based on live modules
+ set_read_walk_required(cld_idx);
}
}
}
@@ -248,7 +286,7 @@ ModuleEntry* ModuleEntryTable::new_entry(unsigned int hash, Handle module_handle
entry->set_module(loader_data->add_handle(module_handle));
}
- entry->set_loader(loader_data);
+ entry->set_loader_data(loader_data);
entry->set_version(version);
entry->set_location(location);
@@ -375,11 +413,11 @@ void ModuleEntryTable::print(outputStream* st) {
void ModuleEntry::print(outputStream* st) {
ResourceMark rm;
- st->print_cr("entry "PTR_FORMAT" name %s module "PTR_FORMAT" loader %s version %s location %s strict %s next "PTR_FORMAT,
+ st->print_cr("entry " PTR_FORMAT " name %s module " PTR_FORMAT " loader %s version %s location %s strict %s next " PTR_FORMAT,
p2i(this),
name() == NULL ? UNNAMED_MODULE : name()->as_C_string(),
p2i(module()),
- loader()->loader_name(),
+ loader_data()->loader_name(),
version() != NULL ? version()->as_C_string() : "NULL",
location() != NULL ? location()->as_C_string() : "NULL",
BOOL_TO_STR(!can_read_all_unnamed()), p2i(next()));
@@ -401,5 +439,5 @@ void ModuleEntryTable::verify() {
}
void ModuleEntry::verify() {
- guarantee(loader() != NULL, "A module entry must be associated with a loader.");
+ guarantee(loader_data() != NULL, "A module entry must be associated with a loader.");
}
diff --git a/hotspot/src/share/vm/classfile/moduleEntry.hpp b/hotspot/src/share/vm/classfile/moduleEntry.hpp
index 9b68c01d424..8ec8237ec3f 100644
--- a/hotspot/src/share/vm/classfile/moduleEntry.hpp
+++ b/hotspot/src/share/vm/classfile/moduleEntry.hpp
@@ -43,6 +43,7 @@ class ModuleClosure;
// It contains:
// - Symbol* containing the module's name.
// - pointer to the java.lang.reflect.Module for this module.
+// - pointer to the java.security.ProtectionDomain shared by classes defined to this module.
// - ClassLoaderData*, class loader of this module.
// - a growable array containg other module entries that this module can read.
// - a flag indicating if this module can read all unnamed modules.
@@ -54,56 +55,58 @@ private:
jobject _module; // java.lang.reflect.Module
jobject _pd; // java.security.ProtectionDomain, cached
// for shared classes from this module
- ClassLoaderData* _loader;
+ ClassLoaderData* _loader_data;
GrowableArray* _reads; // list of modules that are readable by this module
Symbol* _version; // module version number
Symbol* _location; // module location
bool _can_read_all_unnamed;
bool _has_default_read_edges; // JVMTI redefine/retransform support
+ bool _must_walk_reads; // walk module's reads list at GC safepoints to purge out dead modules
TRACE_DEFINE_TRACE_ID_FIELD;
enum {MODULE_READS_SIZE = 101}; // Initial size of list of modules that the module can read.
public:
void init() {
_module = NULL;
- _loader = NULL;
+ _loader_data = NULL;
_pd = NULL;
_reads = NULL;
_version = NULL;
_location = NULL;
_can_read_all_unnamed = false;
_has_default_read_edges = false;
+ _must_walk_reads = false;
}
- Symbol* name() const { return literal(); }
- void set_name(Symbol* n) { set_literal(n); }
+ Symbol* name() const { return literal(); }
+ void set_name(Symbol* n) { set_literal(n); }
- jobject module() const { return _module; }
- void set_module(jobject j) { _module = j; }
+ jobject module() const { return _module; }
+ void set_module(jobject j) { _module = j; }
// The shared ProtectionDomain reference is set once the VM loads a shared class
// originated from the current Module. The referenced ProtectionDomain object is
// created by the ClassLoader when loading a class (shared or non-shared) from the
// Module for the first time. This ProtectionDomain object is used for all
// classes from the Module loaded by the same ClassLoader.
- Handle shared_protection_domain();
- void set_shared_protection_domain(ClassLoaderData *loader_data,
- Handle pd);
+ Handle shared_protection_domain();
+ void set_shared_protection_domain(ClassLoaderData *loader_data, Handle pd);
- ClassLoaderData* loader() const { return _loader; }
- void set_loader(ClassLoaderData* l) { _loader = l; }
+ ClassLoaderData* loader_data() const { return _loader_data; }
+ void set_loader_data(ClassLoaderData* l) { _loader_data = l; }
- Symbol* version() const { return _version; }
- void set_version(Symbol* version);
+ Symbol* version() const { return _version; }
+ void set_version(Symbol* version);
- Symbol* location() const { return _location; }
- void set_location(Symbol* location);
+ Symbol* location() const { return _location; }
+ void set_location(Symbol* location);
- bool can_read(ModuleEntry* m) const;
- bool has_reads() const;
- void add_read(ModuleEntry* m);
+ bool can_read(ModuleEntry* m) const;
+ bool has_reads() const;
+ void add_read(ModuleEntry* m);
+ void set_read_walk_required(ClassLoaderData* m_loader_data);
- bool is_named() const { return (literal() != NULL); }
+ bool is_named() const { return (name() != NULL); }
bool can_read_all_unnamed() const {
assert(is_named() || _can_read_all_unnamed == true,
@@ -178,7 +181,7 @@ private:
ModuleEntry* _unnamed_module;
ModuleEntry* new_entry(unsigned int hash, Handle module_handle, Symbol* name, Symbol* version,
- Symbol* location, ClassLoaderData* class_loader);
+ Symbol* location, ClassLoaderData* loader_data);
void add_entry(int index, ModuleEntry* new_entry);
int entry_size() const { return BasicHashtable::entry_size(); }
diff --git a/hotspot/src/share/vm/classfile/modules.cpp b/hotspot/src/share/vm/classfile/modules.cpp
index de8aac056b7..cd0937267f8 100644
--- a/hotspot/src/share/vm/classfile/modules.cpp
+++ b/hotspot/src/share/vm/classfile/modules.cpp
@@ -113,7 +113,7 @@ static PackageEntry* get_package_entry(ModuleEntry* module_entry, jstring packag
const char *package_name = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(package));
if (package_name == NULL) return NULL;
TempNewSymbol pkg_symbol = SymbolTable::new_symbol(package_name, CHECK_NULL);
- PackageEntryTable* package_entry_table = module_entry->loader()->packages();
+ PackageEntryTable* package_entry_table = module_entry->loader_data()->packages();
assert(package_entry_table != NULL, "Unexpected null package entry table");
return package_entry_table->lookup_only(pkg_symbol);
}
@@ -868,7 +868,7 @@ void Modules::add_module_package(jobject module, jstring package, TRAPS) {
package_name, module_entry->name()->as_C_string());
TempNewSymbol pkg_symbol = SymbolTable::new_symbol(package_name, CHECK);
- PackageEntryTable* package_table = module_entry->loader()->packages();
+ PackageEntryTable* package_table = module_entry->loader_data()->packages();
assert(package_table != NULL, "Missing package_table");
bool pkg_exists = false;
diff --git a/hotspot/src/share/vm/classfile/packageEntry.cpp b/hotspot/src/share/vm/classfile/packageEntry.cpp
index 11cc5ea71aa..2389cb25559 100644
--- a/hotspot/src/share/vm/classfile/packageEntry.cpp
+++ b/hotspot/src/share/vm/classfile/packageEntry.cpp
@@ -25,6 +25,7 @@
#include "precompiled.hpp"
#include "classfile/moduleEntry.hpp"
#include "classfile/packageEntry.hpp"
+#include "logging/log.hpp"
#include "memory/resourceArea.hpp"
#include "oops/symbol.hpp"
#include "runtime/handles.inline.hpp"
@@ -53,12 +54,40 @@ void PackageEntry::add_qexport(ModuleEntry* m) {
if (!has_qual_exports_list()) {
// Lazily create a package's qualified exports list.
// Initial size is small, do not anticipate export lists to be large.
- _qualified_exports =
- new (ResourceObj::C_HEAP, mtModule) GrowableArray(QUAL_EXP_SIZE, true);
+ _qualified_exports = new (ResourceObj::C_HEAP, mtModule) GrowableArray(QUAL_EXP_SIZE, true);
}
+
+ // Determine, based on this newly established export to module m,
+ // if this package's export list should be walked at a GC safepoint.
+ set_export_walk_required(m->loader_data());
+
+ // Establish exportability to module m
_qualified_exports->append_if_missing(m);
}
+// If the module's loader, that an export is being established to, is
+// not the same loader as this module's and is not one of the 3 builtin
+// class loaders, then this package's export list must be walked at GC
+// safepoint. Modules have the same life cycle as their defining class
+// loaders and should be removed if dead.
+void PackageEntry::set_export_walk_required(ClassLoaderData* m_loader_data) {
+ assert_locked_or_safepoint(Module_lock);
+ ModuleEntry* this_pkg_mod = module();
+ if (!_must_walk_exports &&
+ (this_pkg_mod == NULL || this_pkg_mod->loader_data() != m_loader_data) &&
+ !m_loader_data->is_builtin_class_loader_data()) {
+ _must_walk_exports = true;
+ if (log_is_enabled(Trace, modules)) {
+ ResourceMark rm;
+ assert(name() != NULL, "PackageEntry without a valid name");
+ log_trace(modules)("PackageEntry::set_export_walk_required(): package %s defined in module %s, exports list must be walked",
+ name()->as_C_string(),
+ (this_pkg_mod == NULL || this_pkg_mod->name() == NULL) ?
+ UNNAMED_MODULE : this_pkg_mod->name()->as_C_string());
+ }
+ }
+}
+
// Set the package's exported states based on the value of the ModuleEntry.
void PackageEntry::set_exported(ModuleEntry* m) {
MutexLocker m1(Module_lock);
@@ -96,14 +125,34 @@ void PackageEntry::set_is_exported_allUnnamed() {
// Remove dead module entries within the package's exported list.
void PackageEntry::purge_qualified_exports() {
assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
- if (_qualified_exports != NULL) {
+ if (_must_walk_exports &&
+ _qualified_exports != NULL &&
+ !_qualified_exports->is_empty()) {
+ ModuleEntry* pkg_module = module();
+
+ // This package's _must_walk_exports flag will be reset based
+ // on the remaining live modules on the exports list.
+ _must_walk_exports = false;
+
+ if (log_is_enabled(Trace, modules)) {
+ ResourceMark rm;
+ assert(name() != NULL, "PackageEntry without a valid name");
+ ModuleEntry* pkg_mod = module();
+ log_trace(modules)("PackageEntry::purge_qualified_exports(): package %s defined in module %s, exports list being walked",
+ name()->as_C_string(),
+ (pkg_mod == NULL || pkg_mod->name() == NULL) ? UNNAMED_MODULE : pkg_mod->name()->as_C_string());
+ }
+
// Go backwards because this removes entries that are dead.
int len = _qualified_exports->length();
for (int idx = len - 1; idx >= 0; idx--) {
ModuleEntry* module_idx = _qualified_exports->at(idx);
- ClassLoaderData* cld = module_idx->loader();
- if (cld->is_unloading()) {
+ ClassLoaderData* cld_idx = module_idx->loader_data();
+ if (cld_idx->is_unloading()) {
_qualified_exports->delete_at(idx);
+ } else {
+ // Update the need to walk this package's exports based on live modules
+ set_export_walk_required(cld_idx);
}
}
}
@@ -297,8 +346,8 @@ void PackageEntryTable::print(outputStream* st) {
void PackageEntry::print(outputStream* st) {
ResourceMark rm;
- st->print_cr("package entry "PTR_FORMAT" name %s module %s classpath_index "
- INT32_FORMAT " is_exported_unqualified %d is_exported_allUnnamed %d " "next "PTR_FORMAT,
+ st->print_cr("package entry " PTR_FORMAT " name %s module %s classpath_index "
+ INT32_FORMAT " is_exported_unqualified %d is_exported_allUnnamed %d " "next " PTR_FORMAT,
p2i(this), name()->as_C_string(),
(module()->is_named() ? module()->name()->as_C_string() : UNNAMED_MODULE),
_classpath_index, _is_exported_unqualified, _is_exported_allUnnamed, p2i(next()));
diff --git a/hotspot/src/share/vm/classfile/packageEntry.hpp b/hotspot/src/share/vm/classfile/packageEntry.hpp
index e1bf62b3aa4..a379bf9de3b 100644
--- a/hotspot/src/share/vm/classfile/packageEntry.hpp
+++ b/hotspot/src/share/vm/classfile/packageEntry.hpp
@@ -69,6 +69,7 @@ private:
s2 _classpath_index;
bool _is_exported_unqualified;
bool _is_exported_allUnnamed;
+ bool _must_walk_exports;
GrowableArray* _exported_pending_delete; // transitioned from qualified to unqualified, delete at safepoint
GrowableArray* _qualified_exports;
TRACE_DEFINE_TRACE_ID_FIELD;
@@ -82,6 +83,7 @@ public:
_classpath_index = -1;
_is_exported_unqualified = false;
_is_exported_allUnnamed = false;
+ _must_walk_exports = false;
_exported_pending_delete = NULL;
_qualified_exports = NULL;
}
@@ -147,6 +149,7 @@ public:
// add the module to the package's qualified exports
void add_qexport(ModuleEntry* m);
+ void set_export_walk_required(ClassLoaderData* m_loader_data);
PackageEntry* next() const {
return (PackageEntry*)HashtableEntry::next();
diff --git a/hotspot/src/share/vm/classfile/systemDictionary.cpp b/hotspot/src/share/vm/classfile/systemDictionary.cpp
index aaa2ad5dda1..b747fae54f2 100644
--- a/hotspot/src/share/vm/classfile/systemDictionary.cpp
+++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp
@@ -175,9 +175,18 @@ bool SystemDictionary::is_parallelDefine(Handle class_loader) {
return false;
}
-/**
- * Returns true if the passed class loader is the platform class loader.
- */
+// Returns true if the passed class loader is the builtin application class loader
+// or a custom system class loader. A customer system class loader can be
+// specified via -Djava.system.class.loader.
+bool SystemDictionary::is_system_class_loader(Handle class_loader) {
+ if (class_loader.is_null()) {
+ return false;
+ }
+ return (class_loader->klass() == SystemDictionary::jdk_internal_loader_ClassLoaders_AppClassLoader_klass() ||
+ class_loader() == _java_system_loader);
+}
+
+// Returns true if the passed class loader is the platform class loader.
bool SystemDictionary::is_platform_class_loader(Handle class_loader) {
if (class_loader.is_null()) {
return false;
diff --git a/hotspot/src/share/vm/classfile/systemDictionary.hpp b/hotspot/src/share/vm/classfile/systemDictionary.hpp
index 3626d881736..76a0b0772e9 100644
--- a/hotspot/src/share/vm/classfile/systemDictionary.hpp
+++ b/hotspot/src/share/vm/classfile/systemDictionary.hpp
@@ -660,6 +660,7 @@ public:
static instanceKlassHandle load_shared_class(Symbol* class_name,
Handle class_loader,
TRAPS);
+ static bool is_system_class_loader(Handle class_loader);
static bool is_platform_class_loader(Handle class_loader);
protected:
diff --git a/hotspot/src/share/vm/gc/shared/preservedMarks.cpp b/hotspot/src/share/vm/gc/shared/preservedMarks.cpp
index 7a1078ebb34..2fcd86344be 100644
--- a/hotspot/src/share/vm/gc/shared/preservedMarks.cpp
+++ b/hotspot/src/share/vm/gc/shared/preservedMarks.cpp
@@ -48,10 +48,10 @@ void PreservedMarks::restore_and_increment(volatile size_t* const total_size_add
#ifndef PRODUCT
void PreservedMarks::assert_empty() {
- assert(_stack.is_empty(), "stack expected to be empty, size = "SIZE_FORMAT,
+ assert(_stack.is_empty(), "stack expected to be empty, size = " SIZE_FORMAT,
_stack.size());
assert(_stack.cache_size() == 0,
- "stack expected to have no cached segments, cache size = "SIZE_FORMAT,
+ "stack expected to have no cached segments, cache size = " SIZE_FORMAT,
_stack.cache_size());
}
#endif // ndef PRODUCT
diff --git a/hotspot/src/share/vm/interpreter/rewriter.cpp b/hotspot/src/share/vm/interpreter/rewriter.cpp
index 52832e781ab..dfc110b8222 100644
--- a/hotspot/src/share/vm/interpreter/rewriter.cpp
+++ b/hotspot/src/share/vm/interpreter/rewriter.cpp
@@ -419,21 +419,20 @@ void Rewriter::scan_method(Method* method, bool reverse, bool* invokespecial_err
InstanceKlass* klass = method->method_holder();
u2 bc_index = Bytes::get_Java_u2(bcp + prefix_length + 1);
constantPoolHandle cp(method->constants());
- Symbol* field_name = cp->name_ref_at(bc_index);
- Symbol* field_sig = cp->signature_ref_at(bc_index);
Symbol* ref_class_name = cp->klass_name_at(cp->klass_ref_index_at(bc_index));
if (klass->name() == ref_class_name) {
+ Symbol* field_name = cp->name_ref_at(bc_index);
+ Symbol* field_sig = cp->signature_ref_at(bc_index);
+
fieldDescriptor fd;
klass->find_field(field_name, field_sig, &fd);
if (fd.access_flags().is_final()) {
if (fd.access_flags().is_static()) {
- assert(c == Bytecodes::_putstatic, "must be putstatic");
if (!method->is_static_initializer()) {
fd.set_has_initialized_final_update(true);
}
} else {
- assert(c == Bytecodes::_putfield, "must be putfield");
if (!method->is_object_initializer()) {
fd.set_has_initialized_final_update(true);
}
diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp
index 4b29b2830ca..a9e944e4f7f 100644
--- a/hotspot/src/share/vm/runtime/arguments.cpp
+++ b/hotspot/src/share/vm/runtime/arguments.cpp
@@ -584,27 +584,26 @@ static bool verify_special_jvm_flags() {
// Parses a size specification string.
bool Arguments::atojulong(const char *s, julong* result) {
julong n = 0;
- int args_read = 0;
- bool is_hex = false;
- // Skip leading 0[xX] for hexadecimal
- if (*s =='0' && (*(s+1) == 'x' || *(s+1) == 'X')) {
- s += 2;
- is_hex = true;
- args_read = sscanf(s, JULONG_FORMAT_X, &n);
- } else {
- args_read = sscanf(s, JULONG_FORMAT, &n);
- }
- if (args_read != 1) {
+
+ // First char must be a digit. Don't allow negative numbers or leading spaces.
+ if (!isdigit(*s)) {
return false;
}
- while (*s != '\0' && (isdigit(*s) || (is_hex && isxdigit(*s)))) {
- s++;
- }
- // 4705540: illegal if more characters are found after the first non-digit
- if (strlen(s) > 1) {
+
+ bool is_hex = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'));
+ char* remainder;
+ errno = 0;
+ n = strtoull(s, &remainder, (is_hex ? 16 : 10));
+ if (errno != 0) {
return false;
}
- switch (*s) {
+
+ // Fail if no number was read at all or if the remainder contains more than a single non-digit character.
+ if (remainder == s || strlen(remainder) > 1) {
+ return false;
+ }
+
+ switch (*remainder) {
case 'T': case 't':
*result = n * G * K;
// Check for overflow.
diff --git a/hotspot/src/share/vm/utilities/vmError.cpp b/hotspot/src/share/vm/utilities/vmError.cpp
index d2c9cadb504..4a2ac95995b 100644
--- a/hotspot/src/share/vm/utilities/vmError.cpp
+++ b/hotspot/src/share/vm/utilities/vmError.cpp
@@ -205,16 +205,39 @@ void VMError::print_stack_trace(outputStream* st, JavaThread* jt,
static void print_oom_reasons(outputStream* st) {
st->print_cr("# Possible reasons:");
st->print_cr("# The system is out of physical RAM or swap space");
- st->print_cr("# In 32 bit mode, the process size limit was hit");
+ if (UseCompressedOops) {
+ st->print_cr("# The process is running with CompressedOops enabled, and the Java Heap may be blocking the growth of the native heap");
+ }
+ if (LogBytesPerWord == 2) {
+ st->print_cr("# In 32 bit mode, the process size limit was hit");
+ }
st->print_cr("# Possible solutions:");
st->print_cr("# Reduce memory load on the system");
st->print_cr("# Increase physical memory or swap space");
st->print_cr("# Check if swap backing store is full");
- st->print_cr("# Use 64 bit Java on a 64 bit OS");
+ if (LogBytesPerWord == 2) {
+ st->print_cr("# Use 64 bit Java on a 64 bit OS");
+ }
st->print_cr("# Decrease Java heap size (-Xmx/-Xms)");
st->print_cr("# Decrease number of Java threads");
st->print_cr("# Decrease Java thread stack sizes (-Xss)");
st->print_cr("# Set larger code cache with -XX:ReservedCodeCacheSize=");
+ if (UseCompressedOops) {
+ switch (Universe::narrow_oop_mode()) {
+ case Universe::UnscaledNarrowOop:
+ st->print_cr("# JVM is running with Unscaled Compressed Oops mode in which the Java heap is");
+ st->print_cr("# placed in the first 4GB address space. The Java Heap base address is the");
+ st->print_cr("# maximum limit for the native heap growth. Please use -XX:HeapBaseMinAddress");
+ st->print_cr("# to set the Java Heap base and to place the Java Heap above 4GB virtual address.");
+ break;
+ case Universe::ZeroBasedNarrowOop:
+ st->print_cr("# JVM is running with Zero Based Compressed Oops mode in which the Java heap is");
+ st->print_cr("# placed in the first 32GB address space. The Java Heap base address is the");
+ st->print_cr("# maximum limit for the native heap growth. Please use -XX:HeapBaseMinAddress");
+ st->print_cr("# to set the Java Heap base and to place the Java Heap above 32GB virtual address.");
+ break;
+ }
+ }
st->print_cr("# This output file may be truncated or incomplete.");
}
diff --git a/hotspot/test/TEST.groups b/hotspot/test/TEST.groups
index 262232e706c..b46499f7902 100644
--- a/hotspot/test/TEST.groups
+++ b/hotspot/test/TEST.groups
@@ -395,6 +395,17 @@ hotspot_jprt = \
:hotspot_fast_gc_gcold \
:hotspot_fast_runtime \
:hotspot_fast_serviceability
+
+hotspot_runtime_tier2 = \
+ runtime/ \
+ serviceability/ \
+ -:hotspot_fast_runtime \
+ -:hotspot_fast_serviceability \
+ -:hotspot_runtime_tier2_platform_agnostic
+
+hotspot_runtime_tier2_platform_agnostic = \
+ runtime/SelectionResolution \
+ -:hotspot_fast_runtime
#All tests that depends on nashorn extension.
#
diff --git a/hotspot/test/gc/g1/humongousObjects/TestNoAllocationsInHRegions.java b/hotspot/test/gc/g1/humongousObjects/TestNoAllocationsInHRegions.java
new file mode 100644
index 00000000000..bbd15683324
--- /dev/null
+++ b/hotspot/test/gc/g1/humongousObjects/TestNoAllocationsInHRegions.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2016, 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.humongousObjects;
+
+import jdk.test.lib.Utils;
+import sun.hotspot.WhiteBox;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+import java.util.stream.Collectors;
+
+/**
+ * @test TestNoAllocationsInHRegions
+ * @summary Checks that no additional allocations are made in humongous regions
+ * @requires vm.gc.G1
+ * @library /testlibrary /test/lib /
+ * @modules java.management java.base/jdk.internal.misc
+ * @build sun.hotspot.WhiteBox
+ * gc.testlibrary.Helpers
+ * gc.g1.humongousObjects.TestNoAllocationsInHRegions
+ *
+ * @run driver ClassFileInstaller sun.hotspot.WhiteBox
+ * sun.hotspot.WhiteBox$WhiteBoxPermission
+ *
+ * @run main/othervm -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
+ * -XX:G1HeapRegionSize=1M -Xms200m -Xmx200m -XX:MaxTenuringThreshold=0
+ * -Xlog:gc=trace:file=TestNoAllocationsInHRegions10.log
+ * gc.g1.humongousObjects.TestNoAllocationsInHRegions 30 10
+ *
+ * @run main/othervm -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
+ * -XX:G1HeapRegionSize=1M -Xms200m -Xmx200m -XX:MaxTenuringThreshold=0
+ * -Xlog:gc=trace:file=TestNoAllocationsInHRegions50.log
+ * gc.g1.humongousObjects.TestNoAllocationsInHRegions 30 50
+ *
+ * @run main/othervm -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
+ * -XX:G1HeapRegionSize=1M -Xms200m -Xmx200m -XX:MaxTenuringThreshold=0
+ * -Xlog:gc=trace:file=TestNoAllocationsInHRegions70.log
+ * gc.g1.humongousObjects.TestNoAllocationsInHRegions 30 70
+ */
+public class TestNoAllocationsInHRegions {
+ private static final WhiteBox WB = WhiteBox.getWhiteBox();
+ private static final Random RND = Utils.getRandomInstance();
+ private static final int G1_REGION_SIZE = WB.g1RegionSize();
+ private static final int[] HUMONGOUS_SIZES = {G1_REGION_SIZE / 2, G1_REGION_SIZE + 1, G1_REGION_SIZE * 2 + 1};
+ private static final int ALLOC_THREAD_COUNT = 5;
+
+ // We fill specified part of heap with humongous objects - we need public static to prevent escape analysis to
+ // collect this field
+ public static LinkedList humongousAllocations = new LinkedList<>();
+
+ private static volatile boolean shouldStop = false;
+ private static volatile Error error = null;
+
+ static class Allocator implements Runnable {
+
+ private final List liveObjects = new LinkedList<>();
+ private int usedMemory = 0;
+ public final Runnable[] actions;
+
+ /**
+ * Maximum size of simple allocation
+ */
+ private static final int MAX_ALLOCATION_SIZE = (int) (G1_REGION_SIZE / 2 * 0.9);
+
+ /**
+ * Maximum size of dead (i.e. one which is made unreachable right after allocation) object
+ */
+ private static final int DEAD_OBJECT_MAX_SIZE = G1_REGION_SIZE / 10;
+
+ public Allocator(int maxAllocationMemory) {
+
+ actions = new Runnable[]{
+ // Allocation
+ () -> {
+ if (maxAllocationMemory - usedMemory != 0) {
+ int arraySize = RND.nextInt(Math.min(maxAllocationMemory - usedMemory,
+ MAX_ALLOCATION_SIZE));
+
+ if (arraySize != 0) {
+ byte[] allocation = new byte[arraySize];
+ liveObjects.add(allocation);
+ usedMemory += arraySize;
+
+ // Sanity check
+ if (WB.g1IsHumongous(allocation)) {
+ String errorMessage = String.format("Test Bug: Byte array of size"
+ + " %d is expected to be non-humongous but it is humongous",
+ allocation.length);
+
+ System.out.println(errorMessage);
+ error = new Error(errorMessage);
+ shouldStop = true;
+ }
+
+ // Test check
+ if (WB.g1BelongsToHumongousRegion(WB.getObjectAddress(allocation))) {
+ String errorMessage = String.format("Non-humongous allocation of byte array of "
+ + "length %d and size %d with address %d was made in Humongous Region",
+ allocation.length, WB.getObjectSize(allocation),
+ WB.getObjectAddress(allocation));
+
+ System.out.println(errorMessage);
+ error = new Error(errorMessage);
+ shouldStop = true;
+ }
+ }
+ }
+ },
+
+ // Deallocation
+ () -> {
+ if (liveObjects.size() != 0) {
+ int elementNum = RND.nextInt(liveObjects.size());
+ int shouldFree = liveObjects.get(elementNum).length;
+ liveObjects.remove(elementNum);
+ usedMemory -= shouldFree;
+ }
+ },
+
+ // Dead object allocation
+ () -> {
+ int size = RND.nextInt(DEAD_OBJECT_MAX_SIZE);
+ byte[] deadObject = new byte[size];
+ },
+
+ // Check
+ () -> {
+ List wrongHumongousAllocations = liveObjects.stream()
+ .filter(WB::g1IsHumongous)
+ .collect(Collectors.toList());
+
+ if (wrongHumongousAllocations.size() > 0) {
+ wrongHumongousAllocations.stream().forEach(a ->
+ System.out.format("Non-humongous allocation of byte array of length %d and"
+ + " size %d with address %d was made in Humongous Region",
+ a.length, WB.getObjectSize(a), WB.getObjectAddress(a)));
+ error = new Error("Some non-humongous allocations were made to humongous region");
+ shouldStop = true;
+ }
+ }
+ };
+ }
+
+ @Override
+ public void run() {
+ while (!shouldStop) {
+ actions[RND.nextInt(actions.length)].run();
+ Thread.yield();
+ }
+ }
+ }
+
+ public static void main(String[] args) {
+ if (args.length != 2) {
+ throw new Error("Test Bug: Expected duration (in seconds) and percent of allocated regions were not "
+ + "provided as command line argument");
+ }
+
+ // test duration
+ long duration = Integer.parseInt(args[0]) * 1000L;
+ // part of heap preallocated with humongous objects (in percents)
+ int percentOfAllocatedHeap = Integer.parseInt(args[1]);
+
+ long startTime = System.currentTimeMillis();
+
+ long initialFreeRegionsCount = WB.g1NumFreeRegions();
+ int regionsToAllocate = (int) ((double) initialFreeRegionsCount / 100.0 * percentOfAllocatedHeap);
+ long freeRegionLeft = initialFreeRegionsCount - regionsToAllocate;
+
+ System.out.println("Regions to allocate: " + regionsToAllocate + "; regions to left free: " + freeRegionLeft);
+
+ int maxMemoryPerAllocThread = (int) ((Runtime.getRuntime().freeMemory() / 100.0
+ * (100 - percentOfAllocatedHeap)) / ALLOC_THREAD_COUNT * 0.5);
+
+ System.out.println("Using " + maxMemoryPerAllocThread / 1024 + "KB for each of " + ALLOC_THREAD_COUNT
+ + " allocation threads");
+
+ while (WB.g1NumFreeRegions() > freeRegionLeft) {
+ try {
+ humongousAllocations.add(new byte[HUMONGOUS_SIZES[RND.nextInt(HUMONGOUS_SIZES.length)]]);
+ } catch (OutOfMemoryError oom) {
+ //We got OOM trying to fill heap with humongous objects
+ //It probably means that heap is fragmented which is strange since the test logic should avoid it
+ System.out.println("Warning: OOM while allocating humongous objects - it likely means "
+ + "that heap is fragmented");
+ break;
+ }
+ }
+
+ System.out.println("Initial free regions " + initialFreeRegionsCount + "; Free regions left "
+ + WB.g1NumFreeRegions());
+
+ LinkedList threads = new LinkedList<>();
+
+ for (int i = 0; i < ALLOC_THREAD_COUNT; i++) {
+ threads.add(new Thread(new Allocator(maxMemoryPerAllocThread)));
+ }
+
+ threads.stream().forEach(Thread::start);
+
+ while ((System.currentTimeMillis() - startTime < duration) && error == null) {
+ Thread.yield();
+ }
+
+ shouldStop = true;
+ System.out.println("Finished test");
+ if (error != null) {
+ throw error;
+ }
+ }
+}
diff --git a/hotspot/test/gc/g1/humongousObjects/objectGraphTest/GC.java b/hotspot/test/gc/g1/humongousObjects/objectGraphTest/GC.java
index 8da879dec5e..be721f54fd5 100644
--- a/hotspot/test/gc/g1/humongousObjects/objectGraphTest/GC.java
+++ b/hotspot/test/gc/g1/humongousObjects/objectGraphTest/GC.java
@@ -138,6 +138,41 @@ public enum GC {
}
},
+ MIXED_GC {
+ @Override
+ public Runnable get() {
+ return () -> {
+ WHITE_BOX.youngGC();
+ Helpers.waitTillCMCFinished(WHITE_BOX, 0);
+ WHITE_BOX.youngGC();
+ Helpers.waitTillCMCFinished(WHITE_BOX, 0);
+
+ WHITE_BOX.g1StartConcMarkCycle();
+ Helpers.waitTillCMCFinished(WHITE_BOX, 0);
+
+ WHITE_BOX.youngGC();
+ Helpers.waitTillCMCFinished(WHITE_BOX, 0);
+ // Provoking Mixed GC
+ WHITE_BOX.youngGC();// second evacuation pause will be mixed
+ Helpers.waitTillCMCFinished(WHITE_BOX, 0);
+ };
+ }
+
+ public Consumer> getChecker() {
+ return getCheckerImpl(true, false, true, false);
+ }
+
+ @Override
+ public List shouldContain() {
+ return Arrays.asList(GCTokens.WB_INITIATED_CMC);
+ }
+
+ @Override
+ public List shouldNotContain() {
+ return Arrays.asList(GCTokens.YOUNG_GC);
+ }
+ },
+
FULL_GC_MEMORY_PRESSURE {
@Override
public Runnable get() {
diff --git a/hotspot/test/gc/g1/humongousObjects/objectGraphTest/README b/hotspot/test/gc/g1/humongousObjects/objectGraphTest/README
index 4ec9f0d3792..5366fc876be 100644
--- a/hotspot/test/gc/g1/humongousObjects/objectGraphTest/README
+++ b/hotspot/test/gc/g1/humongousObjects/objectGraphTest/README
@@ -38,6 +38,9 @@ The test checks that after different type of GC unreachable objects behave as ex
non-humongous and humongous objects are not collected since we make 2 Young GC to promote all
weak references to Old Gen.
+6. Mixed GC - weakly referenced non-humongous and humongous objects are collected, softly referenced non-humongous and
+ humongous objects are not collected.
+
The test gets gc type as a command line argument.
Then the test allocates object graph in heap (currently testing scenarios are pre-generated and stored in
TestcaseData.getPregeneratedTestcases()) with TestObjectGraphAfterGC::allocateObjectGraph.
diff --git a/hotspot/test/gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java b/hotspot/test/gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java
index c7a4f2ff081..07f7a3bc89d 100644
--- a/hotspot/test/gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java
+++ b/hotspot/test/gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java
@@ -66,6 +66,12 @@ import java.util.stream.Collectors;
* sun.hotspot.WhiteBox$WhiteBoxPermission
*
* @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
+ * -XX:+UnlockExperimentalVMOptions -XX:MaxGCPauseMillis=30000 -XX:G1MixedGCLiveThresholdPercent=100 -XX:G1HeapWastePercent=0
+ * -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_MIXED_GC.gc.log -XX:MaxTenuringThreshold=1
+ * -XX:G1MixedGCCountTarget=1 -XX:G1OldCSetRegionThresholdPercent=100 -XX:SurvivorRatio=1 -XX:InitiatingHeapOccupancyPercent=0
+ * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC MIXED_GC
+ *
+ * @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
* -XX:G1HeapRegionSize=1M -Xlog:gc*=debug:file=TestObjectGraphAfterGC_YOUNG_GC.gc.log
* gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC YOUNG_GC
*
diff --git a/hotspot/test/native/runtime/test_arguments.cpp b/hotspot/test/native/runtime/test_arguments.cpp
new file mode 100644
index 00000000000..f4327f3498d
--- /dev/null
+++ b/hotspot/test/native/runtime/test_arguments.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2016, 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 "runtime/arguments.hpp"
+#include "unittest.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+TEST(arguments, atojulong) {
+ char ullong_max[32];
+ int ret = jio_snprintf(ullong_max, sizeof(ullong_max), JULONG_FORMAT, ULLONG_MAX);
+ ASSERT_NE(-1, ret);
+
+ julong value;
+ const char* invalid_strings[] = {
+ "", "-1", "-100", " 1", "2 ", "3 2", "1.0",
+ "0x4.5", "0x", "0x0x1" "0.001", "4e10", "e"
+ "K", "M", "G", "1MB", "1KM", "AA", "0B",
+ "18446744073709551615K", "17179869184G",
+ "999999999999999999999999999999"
+ };
+ for (uint i = 0; i < ARRAY_SIZE(invalid_strings); i++) {
+ ASSERT_FALSE(Arguments::atojulong(invalid_strings[i], &value))
+ << "Invalid string '" << invalid_strings[i] << "' parsed without error.";
+ }
+
+ struct {
+ const char* str;
+ julong expected_value;
+ } valid_strings[] = {
+ { "0", 0 },
+ { "4711", 4711 },
+ { "1K", 1ULL * K },
+ { "1k", 1ULL * K },
+ { "2M", 2ULL * M },
+ { "2m", 2ULL * M },
+ { "4G", 4ULL * G },
+ { "4g", 4ULL * G },
+ { "0K", 0 },
+ { ullong_max, ULLONG_MAX },
+ { "0xcafebabe", 0xcafebabe },
+ { "0XCAFEBABE", 0xcafebabe },
+ { "0XCAFEbabe", 0xcafebabe },
+ { "0x10K", 0x10 * K }
+ };
+ for (uint i = 0; i < ARRAY_SIZE(valid_strings); i++) {
+ ASSERT_TRUE(Arguments::atojulong(valid_strings[i].str, &value))
+ << "Valid string '" << valid_strings[i].str << "' did not parse.";
+ ASSERT_EQ(valid_strings[i].expected_value, value);
+ }
+}
diff --git a/hotspot/test/runtime/Final/Bad.jasm b/hotspot/test/runtime/Final/Bad.jasm
new file mode 100644
index 00000000000..607e2bcc078
--- /dev/null
+++ b/hotspot/test/runtime/Final/Bad.jasm
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+/* Recoded in jasm to provoke an ICCE assigning a non-static final field with putstatic.
+class Bad {
+ public static final int i; //rewritten
+ //rewritten to: public final int i;
+ static { i = 5; } // putstatic instruction
+}
+*/
+
+super class Bad
+ version 53:0
+{
+
+// Remove 'static' keyword
+public final Field i:I;
+
+Method "":"()V"
+ stack 1 locals 1
+{
+ aload_0;
+ invokespecial Method java/lang/Object."":"()V";
+ return;
+}
+
+static Method "":"()V"
+ stack 1 locals 0
+{
+ iconst_5;
+ putstatic Field i:"I";
+ return;
+}
+
+} // end Class Bad
diff --git a/hotspot/test/runtime/Final/PutfieldError.java b/hotspot/test/runtime/Final/PutfieldError.java
new file mode 100644
index 00000000000..9669a1b1ade
--- /dev/null
+++ b/hotspot/test/runtime/Final/PutfieldError.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2016, 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 PutfieldError
+ * @bug 8160551
+ * @summary Throw ICCE rather than crashing for nonstatic final field in static initializer
+ * @compile Bad.jasm
+ * @run main PutfieldError
+ */
+
+public class PutfieldError {
+ public static void main(java.lang.String[] unused) {
+ try {
+ Bad b = new Bad();
+ System.out.println("Bad.i = " + 5);
+ throw new RuntimeException("ICCE NOT thrown as expected");
+ } catch (IncompatibleClassChangeError icce) {
+ System.out.println("ICCE thrown as expected");
+ }
+ }
+}
diff --git a/hotspot/test/runtime/SharedArchiveFile/SharedStringsDedup.java b/hotspot/test/runtime/SharedArchiveFile/SharedStringsDedup.java
new file mode 100644
index 00000000000..432b36bffd8
--- /dev/null
+++ b/hotspot/test/runtime/SharedArchiveFile/SharedStringsDedup.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2016, 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 SharedStringsDedup
+ * @summary Test -Xshare:auto with shared strings and -XX:+UseStringDeduplication
+ * Feature support: G1GC only, compressed oops/kptrs, 64-bit os, not on windows
+ * @requires (sun.arch.data.model != "32") & (os.family != "windows")
+ * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true)
+ * @requires vm.gc.G1
+ * @library /testlibrary
+ * @modules java.base/jdk.internal.misc
+ * java.management
+ * @run main SharedStringsDedup
+ */
+
+import jdk.test.lib.*;
+import java.io.File;
+
+// The main purpose is to test the interaction between shared strings
+// and -XX:+UseStringDeduplication. We run in -Xshare:auto mode so
+// we don't need to worry about CDS archive mapping failure (which
+// doesn't happen often so it won't impact coverage).
+public class SharedStringsDedup {
+ public static void main(String[] args) throws Exception {
+ // Dump
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:SharedArchiveFile=./SharedStringsDedup.jsa",
+ "-XX:+UseCompressedOops", "-XX:+UseG1GC",
+ "-XX:+PrintSharedSpaces",
+ "-Xshare:dump");
+
+ new OutputAnalyzer(pb.start())
+ .shouldContain("Loading classes to share")
+ .shouldContain("Shared string table stats")
+ .shouldHaveExitValue(0);
+
+ // Run with -Xshare:auto
+ pb = ProcessTools.createJavaProcessBuilder(
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:SharedArchiveFile=./SharedStringsDedup.jsa",
+ "-XX:+UseCompressedOops", "-XX:+UseG1GC",
+ "-XX:+UseStringDeduplication",
+ "-Xshare:auto",
+ "-version");
+
+ new OutputAnalyzer(pb.start())
+ .shouldMatch("(java|openjdk) version")
+ .shouldHaveExitValue(0);
+ }
+}
diff --git a/hotspot/test/runtime/modules/ModuleStress/CustomSystemClassLoader.java b/hotspot/test/runtime/modules/ModuleStress/CustomSystemClassLoader.java
new file mode 100644
index 00000000000..dca359f6458
--- /dev/null
+++ b/hotspot/test/runtime/modules/ModuleStress/CustomSystemClassLoader.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2016, 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.
+ */
+
+/**
+ * A custom system ClassLoader to define the module "m2" to during iterations of
+ * differing test runs within the test ModuleStress.java
+ */
+public class CustomSystemClassLoader extends ClassLoader {
+ public CustomSystemClassLoader() {
+ super();
+ }
+ public CustomSystemClassLoader(ClassLoader parent) {
+ super(parent);
+ }
+}
diff --git a/hotspot/test/runtime/modules/ModuleStress/ModuleNonBuiltinCLMain.java b/hotspot/test/runtime/modules/ModuleStress/ModuleNonBuiltinCLMain.java
new file mode 100644
index 00000000000..a8390f0fd5d
--- /dev/null
+++ b/hotspot/test/runtime/modules/ModuleStress/ModuleNonBuiltinCLMain.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2016, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 static jdk.test.lib.Asserts.*;
+
+import java.lang.reflect.Layer;
+import java.lang.module.Configuration;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleFinder;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+//
+// ClassLoader1 --> defines m1 --> packages p1
+// ClassLoader2 --> defines m2 --> packages p2
+// Java System Class Loader --> defines m3 --> packages p3
+//
+// m1 can read m2
+// package p2 in m2 is exported to m1 and m3
+//
+// class p1.c1 defined in m1 tries to access p2.c2 defined in m2
+// Access allowed since m1 can read m2 and package p2 is exported to m1.
+//
+public class ModuleNonBuiltinCLMain {
+
+ // Create a Layer over the boot layer.
+ // Define modules within this layer to test access between
+ // publically defined classes within packages of those modules.
+ public void createLayerOnBoot() throws Throwable {
+
+ // Define module: m1
+ // Can read: java.base, m2
+ // Packages: p1
+ // Packages exported: p1 is exported to unqualifiedly
+ ModuleDescriptor descriptor_m1 =
+ new ModuleDescriptor.Builder("m1")
+ .requires("java.base")
+ .requires("m2")
+ .exports("p1")
+ .build();
+
+ // Define module: m2
+ // Can read: java.base, m3
+ // Packages: p2
+ // Packages exported: package p2 is exported to m1 and m3
+ Set targets = new HashSet<>();
+ targets.add("m1");
+ targets.add("m3");
+ ModuleDescriptor descriptor_m2 =
+ new ModuleDescriptor.Builder("m2")
+ .requires("java.base")
+ .requires("m3")
+ .exports("p2", targets)
+ .build();
+
+ // Define module: m3
+ // Can read: java.base
+ // Packages: p3
+ // Packages exported: none
+ ModuleDescriptor descriptor_m3 =
+ new ModuleDescriptor.Builder("m3")
+ .requires("java.base")
+ .build();
+
+ // Set up a ModuleFinder containing all modules for this layer.
+ ModuleFinder finder = ModuleLibrary.of(descriptor_m1, descriptor_m2, descriptor_m3);
+
+ // Resolves "m1"
+ Configuration cf = Layer.boot()
+ .configuration()
+ .resolveRequires(finder, ModuleFinder.of(), Set.of("m1"));
+
+ // map each module to differing user defined class loaders for this test
+ Map map = new HashMap<>();
+ Loader1 cl1 = new Loader1();
+ Loader2 cl2 = new Loader2();
+ ClassLoader cl3 = ClassLoader.getSystemClassLoader();
+ map.put("m1", cl1);
+ map.put("m2", cl2);
+ map.put("m3", cl3);
+
+ // Create Layer that contains m1 & m2
+ Layer layer = Layer.boot().defineModules(cf, map::get);
+ assertTrue(layer.findLoader("m1") == cl1);
+ assertTrue(layer.findLoader("m2") == cl2);
+ assertTrue(layer.findLoader("m3") == cl3);
+ assertTrue(layer.findLoader("java.base") == null);
+
+ // now use the same loader to load class p1.c1
+ Class p1_c1_class = cl1.loadClass("p1.c1");
+ try {
+ p1_c1_class.newInstance();
+ } catch (IllegalAccessError e) {
+ throw new RuntimeException("Test Failed, an IAE should not be thrown since p2 is exported qualifiedly to m1");
+ }
+ }
+
+ public static void main(String args[]) throws Throwable {
+ ModuleNonBuiltinCLMain test = new ModuleNonBuiltinCLMain();
+ test.createLayerOnBoot();
+ }
+
+ static class Loader1 extends ClassLoader { }
+ static class Loader2 extends ClassLoader { }
+}
diff --git a/hotspot/test/runtime/modules/ModuleStress/ModuleSameCLMain.java b/hotspot/test/runtime/modules/ModuleStress/ModuleSameCLMain.java
new file mode 100644
index 00000000000..b14a9a9b060
--- /dev/null
+++ b/hotspot/test/runtime/modules/ModuleStress/ModuleSameCLMain.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2016, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 static jdk.test.lib.Asserts.*;
+
+import java.lang.reflect.Layer;
+import java.lang.module.Configuration;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleFinder;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+//
+// ClassLoader1 --> defines m1 --> packages p1
+// ClassLoader1 --> defines m2 --> packages p2
+//
+// m1 can read m2
+// package p2 in m2 is exported to m1
+//
+// class p1.c1 defined in m1 tries to access p2.c2 defined in m2
+// Access allowed since m1 can read m2 and package p2 is exported to m1.
+//
+public class ModuleSameCLMain {
+
+ // Create a Layer over the boot layer.
+ // Define modules within this layer to test access between
+ // publically defined classes within packages of those modules.
+ public void createLayerOnBoot() throws Throwable {
+
+ // Define module: m1
+ // Can read: java.base, m2
+ // Packages: p1
+ // Packages exported: p1 is exported to unqualifiedly
+ ModuleDescriptor descriptor_m1 =
+ new ModuleDescriptor.Builder("m1")
+ .requires("java.base")
+ .requires("m2")
+ .exports("p1")
+ .build();
+
+ // Define module: m2
+ // Can read: java.base
+ // Packages: p2
+ // Packages exported: package p2 is exported to m1
+ ModuleDescriptor descriptor_m2 =
+ new ModuleDescriptor.Builder("m2")
+ .requires("java.base")
+ .exports("p2", "m1")
+ .build();
+
+ // Set up a ModuleFinder containing all modules for this layer.
+ ModuleFinder finder = ModuleLibrary.of(descriptor_m1, descriptor_m2);
+
+ // Resolves "m1"
+ Configuration cf = Layer.boot()
+ .configuration()
+ .resolveRequires(finder, ModuleFinder.of(), Set.of("m1"));
+
+ // map each module to the same class loader for this test
+ Map map = new HashMap<>();
+ Loader1 cl1 = new Loader1();
+ map.put("m1", cl1);
+ map.put("m2", cl1);
+
+ // Create Layer that contains m1 & m2
+ Layer layer = Layer.boot().defineModules(cf, map::get);
+ assertTrue(layer.findLoader("m1") == cl1);
+ assertTrue(layer.findLoader("m2") == cl1);
+ assertTrue(layer.findLoader("java.base") == null);
+
+ // now use the same loader to load class p1.c1
+ Class p1_c1_class = cl1.loadClass("p1.c1");
+ try {
+ p1_c1_class.newInstance();
+ } catch (IllegalAccessError e) {
+ throw new RuntimeException("Test Failed, an IAE should not be thrown since p2 is exported qualifiedly to m1");
+ }
+ }
+
+ public static void main(String args[]) throws Throwable {
+ ModuleSameCLMain test = new ModuleSameCLMain();
+ test.createLayerOnBoot();
+ }
+
+ static class Loader1 extends ClassLoader { }
+}
diff --git a/hotspot/test/runtime/modules/ModuleStress/ModuleStress.java b/hotspot/test/runtime/modules/ModuleStress/ModuleStress.java
new file mode 100644
index 00000000000..d7791e9b43a
--- /dev/null
+++ b/hotspot/test/runtime/modules/ModuleStress/ModuleStress.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2016, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 8159262
+ * @summary Test differing scenarios where a module's readability list and a package's exportability list should be walked
+ * @modules java.base/jdk.internal.misc
+ * @library /testlibrary /test/lib
+ * @compile ../AccessCheck/ModuleLibrary.java
+ * @compile ModuleSameCLMain.java
+ * @compile ModuleNonBuiltinCLMain.java
+ * @compile CustomSystemClassLoader.java
+ * @build ModuleStress
+ * @run main/othervm ModuleStress
+ */
+
+import jdk.test.lib.*;
+import java.io.File;
+
+public class ModuleStress {
+
+ public static void main(String[] args) throws Exception {
+
+ // Test #1: java -version
+ // All modules' readability lists and packages' exportability
+ // lists should contain only modules defined to the 3 builtin
+ // loaders (boot, application, platform). Thus there is
+ // not a need to walk those lists at a GC safepoint since
+ // those loaders never die.
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+ "-Xbootclasspath/a:.",
+ "-Xlog:modules=trace",
+ "-version");
+
+ OutputAnalyzer oa = new OutputAnalyzer(pb.start());
+ oa.shouldNotContain("must be walked")
+ .shouldNotContain("being walked")
+ .shouldHaveExitValue(0);
+
+ // Next 2 tests involve the use of class p1.c1 and p2.c2
+ String source1 = "package p1;" +
+ "import p2.c2;" +
+ "public class c1 {" +
+ " public c1() {" +
+ " p2.c2 c2_obj = new p2.c2();" +
+ " c2_obj.method2();" +
+ " }" +
+ "}";
+
+ String source2 = "package p2;" +
+ "public class c2 {" +
+ " public void method2() { }" +
+ "}";
+
+ ClassFileInstaller.writeClassToDisk("p2/c2",
+ InMemoryJavaCompiler.compile("p2.c2", source2), System.getProperty("test.classes"));
+
+ ClassFileInstaller.writeClassToDisk("p1/c1",
+ InMemoryJavaCompiler.compile("p1.c1", source1), System.getProperty("test.classes"));
+
+ // Test #2: Load two modules defined to the same customer class loader.
+ // m1's module readability list and package p2's exportability should
+ // not be walked at a GC safepoint since both modules are defined to
+ // the same loader and thus have the exact same life cycle.
+ pb = ProcessTools.createJavaProcessBuilder(
+ "-Xbootclasspath/a:.",
+ "-Xlog:modules=trace",
+ "ModuleSameCLMain");
+
+ oa = new OutputAnalyzer(pb.start());
+ oa.shouldNotContain("must be walked")
+ .shouldNotContain("being walked")
+ .shouldHaveExitValue(0);
+
+ // Test #3: Load two modules in differing custom class loaders.
+ // m1's module readability list and package p2's exportability list must
+ // be walked at a GC safepoint since both modules are defined to non-builtin
+ // class loaders which could die and thus be unloaded.
+ pb = ProcessTools.createJavaProcessBuilder(
+ "-Xbootclasspath/a:.",
+ "-Xlog:modules=trace",
+ "ModuleNonBuiltinCLMain");
+
+ oa = new OutputAnalyzer(pb.start());
+ oa.shouldContain("module m1 reads list must be walked")
+ .shouldContain("package p2 defined in module m2, exports list must be walked")
+ .shouldNotContain("module m2 reads list must be walked")
+ .shouldHaveExitValue(0);
+
+ // Test #4: Load two modules in differing custom class loaders,
+ // of which one has been designated as the custom system class loader
+ // via -Djava.system.class.loader=CustomSystemClassLoader. Since
+ // m3 is defined to the system class loader, m2's module readability
+ // list does not have to be walked at a GC safepoint, but package p2's
+ // exportability list does.
+ pb = ProcessTools.createJavaProcessBuilder(
+ "-Djava.system.class.loader=CustomSystemClassLoader",
+ "-Xbootclasspath/a:.",
+ "-Xlog:modules=trace",
+ "ModuleNonBuiltinCLMain");
+
+ oa = new OutputAnalyzer(pb.start());
+ oa.shouldContain("package p2 defined in module m2, exports list must be walked")
+ .shouldNotContain("module m2 reads list must be walked")
+ .shouldHaveExitValue(0);
+
+ }
+}
diff --git a/hotspot/test/runtime/modules/ModuleStress/ModuleStressGC.java b/hotspot/test/runtime/modules/ModuleStress/ModuleStressGC.java
new file mode 100644
index 00000000000..1ebb198ba5c
--- /dev/null
+++ b/hotspot/test/runtime/modules/ModuleStress/ModuleStressGC.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2016, 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 8159262
+ * @summary layers over the boot layer are repeatedly created, during this iteration, GCs are forced to verify correct walk of module and package lists.
+ * @modules java.base/jdk.internal.misc
+ * @library /testlibrary /test/lib
+ * @compile ../CompilerUtils.java
+ * @build ModuleStressGC
+ * @run main/othervm ModuleStressGC
+ */
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import jdk.test.lib.*;
+
+public class ModuleStressGC {
+
+ private static final String TEST_SRC = System.getProperty("test.src");
+ private static final String TEST_CLASSES = System.getProperty("test.classes");
+
+ private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
+ private static final Path MODS_DIR = Paths.get(TEST_CLASSES, "mods");
+
+ /**
+ * Compile two module definitions used by the test, jdk.test and jdk.translet.
+ */
+ public static void main(String[] args) throws Exception {
+
+ boolean compiled;
+ // Compile module jdk.test declaration
+ compiled = CompilerUtils.compile(
+ SRC_DIR.resolve("jdk.test"),
+ MODS_DIR.resolve("jdk.test"));
+ if (!compiled) {
+ throw new RuntimeException("Test failed to compile module jdk.test");
+ }
+
+ // Compile module jdk.translet declaration
+ compiled = CompilerUtils.compile(
+ SRC_DIR.resolve("jdk.translet"),
+ MODS_DIR.resolve("jdk.translet"),
+ "-XaddExports:jdk.test/test=jdk.translet",
+ "-mp", MODS_DIR.toString());
+ if (!compiled) {
+ throw new RuntimeException("Test failed to compile module jdk.translet");
+ }
+
+ // Sanity check that the test, jdk.test/test/MainGC.java,
+ // correctly walks module jdk.test's reads list and package
+ // test's, defined to module jdk.translet, export list at
+ // GC safepoints.
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+ "-Xlog:modules=trace",
+ "-mp", MODS_DIR.toString(),
+ "-m", "jdk.test/test.MainGC");
+ OutputAnalyzer oa = new OutputAnalyzer(pb.start());
+ oa.shouldContain("package test defined in module jdk.test, exports list being walked")
+ .shouldContain("module jdk.test reads list being walked")
+ .shouldHaveExitValue(0);
+ }
+}
diff --git a/hotspot/test/runtime/modules/ModuleStress/src/jdk.test/test/MainGC.java b/hotspot/test/runtime/modules/ModuleStress/src/jdk.test/test/MainGC.java
new file mode 100644
index 00000000000..25c121d3625
--- /dev/null
+++ b/hotspot/test/runtime/modules/ModuleStress/src/jdk.test/test/MainGC.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2016, 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 test;
+
+import java.lang.module.Configuration;
+import java.lang.module.ModuleFinder;
+import java.lang.reflect.Layer;
+import java.lang.reflect.Method;
+import java.lang.reflect.Module;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.*;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+public class MainGC {
+
+ private static final Path MODS_DIR = Paths.get(System.getProperty("jdk.module.path"));
+ static final String MODULE_NAME = "jdk.translet";
+
+ public static void main(String[] args) throws Exception {
+
+ ModuleFinder finder = ModuleFinder.of(MODS_DIR);
+ Layer layerBoot = Layer.boot();
+
+ Configuration cf = layerBoot
+ .configuration()
+ .resolveRequires(ModuleFinder.of(), finder, Set.of(MODULE_NAME));
+
+ Module testModule = MainGC.class.getModule();
+ ClassLoader scl = ClassLoader.getSystemClassLoader();
+
+ // Create an unique module/class loader in a layer above the boot layer.
+ // Export this module to the jdk.test/test package.
+ // Add a read edge from module jdk.test to this module.
+ Callable task = new Callable() {
+ @Override
+ public Void call() throws Exception {
+ Layer layer = Layer.boot().defineModulesWithOneLoader(cf, scl);
+ Module transletModule = layer.findModule(MODULE_NAME).get();
+ testModule.addExports("test", transletModule);
+ testModule.addReads(transletModule);
+ Class> c = layer.findLoader(MODULE_NAME).loadClass("translet.MainGC");
+ Method method = c.getDeclaredMethod("go");
+ method.invoke(null);
+ return null;
+ }
+ };
+
+ List> results = new ArrayList<>();
+
+ // Repeatedly create the layer above stressing the exportation of
+ // package jdk.test/test to several different modules.
+ ExecutorService pool = Executors.newFixedThreadPool(Math.min(100, Runtime.getRuntime().availableProcessors()*10));
+ try {
+ for (int i = 0; i < 10000; i++) {
+ results.add(pool.submit(task));
+ // At specified intervals, force a GC. This provides an
+ // opportunity to verify that both the module jdk.test's reads
+ // and the package test's, which is defined to jdk.test, exports
+ // lists are being walked.
+ if (i == 3000 || i == 6000 || i == 9000) {
+ System.gc();
+ }
+ }
+ } finally {
+ pool.shutdown();
+ }
+
+ int passed = 0;
+ int failed = 0;
+
+ // The failed state should be 0, the created modules in layers above the
+ // boot layer should be allowed access to the contents of the jdk.test/test
+ // package since that package was exported to the transletModule above.
+ for (Future result : results) {
+ try {
+ result.get();
+ passed++;
+ } catch (Throwable x) {
+ x.printStackTrace();
+ failed++;
+ }
+ }
+
+ System.out.println("passed: " + passed);
+ System.out.println("failed: " + failed);
+ }
+
+ public static void callback() { }
+}
diff --git a/hotspot/test/runtime/modules/ModuleStress/src/jdk.translet/translet/MainGC.java b/hotspot/test/runtime/modules/ModuleStress/src/jdk.translet/translet/MainGC.java
new file mode 100644
index 00000000000..b5e7b7790d6
--- /dev/null
+++ b/hotspot/test/runtime/modules/ModuleStress/src/jdk.translet/translet/MainGC.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2016, 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 translet;
+
+public class MainGC {
+ public static void go() {
+ test.MainGC.callback();
+ }
+}
diff --git a/jaxp/.hgtags b/jaxp/.hgtags
index a9a25f979d4..2355e43df26 100644
--- a/jaxp/.hgtags
+++ b/jaxp/.hgtags
@@ -369,3 +369,4 @@ f8899b1884e2c4a000dbcc5b1a80954245fe462e jdk-9+122
e04a15153cc293f05fcd60bc98236f50e16af46a jdk-9+124
493eb91ec32a6dea7604cfbd86c10045ad9af15b jdk-9+125
15722f71281f034bc696d8b96136da2ef34da44f jdk-9+126
+bdc3c0b737efbf899709eb3121ce760dcfb51151 jdk-9+127
diff --git a/jaxws/.hgtags b/jaxws/.hgtags
index cd59b05a750..98fe07cb10f 100644
--- a/jaxws/.hgtags
+++ b/jaxws/.hgtags
@@ -372,3 +372,4 @@ c42decd28bbfa817347112ed6053b5fbd30517a2 jdk-9+123
1600da1665cd2cc127014e8c002b328ec33a9147 jdk-9+124
5b0570e3db29f6b8c80a4beac70d51284507b203 jdk-9+125
264a44128cd6286e598d5a849ceeb613c06269d0 jdk-9+126
+06d706c70634775418dc79a2671780ba1c624fd2 jdk-9+127
diff --git a/jdk/.hgtags b/jdk/.hgtags
index f2f2ce61f7e..b835fba9fa3 100644
--- a/jdk/.hgtags
+++ b/jdk/.hgtags
@@ -370,3 +370,4 @@ c40c8739bcdc88892ff58ebee3fd8a3f287be94d jdk-9+123
073ab1d4edf5590cf1af7b6d819350c14e425c1a jdk-9+125
6fda66a5bdf2da8994032b9da2078a4137f4d954 jdk-9+126
7a97b89ba83077ca62e4aa5a05437adc8f315343 jdk-9+127
+9446c534f0222b4eecfd9d9e25ab37c4fd4400a5 jdk-9+128
diff --git a/jdk/src/java.base/macosx/classes/java/lang/ClassLoaderHelper.java b/jdk/src/java.base/macosx/classes/java/lang/ClassLoaderHelper.java
index 505ca0814dc..bcadaac2359 100644
--- a/jdk/src/java.base/macosx/classes/java/lang/ClassLoaderHelper.java
+++ b/jdk/src/java.base/macosx/classes/java/lang/ClassLoaderHelper.java
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.lang;
import java.io.File;
diff --git a/jdk/src/java.base/macosx/native/libjava/java_props_macosx.c b/jdk/src/java.base/macosx/native/libjava/java_props_macosx.c
index ea3dc25c624..b43f290b0a2 100644
--- a/jdk/src/java.base/macosx/native/libjava/java_props_macosx.c
+++ b/jdk/src/java.base/macosx/native/libjava/java_props_macosx.c
@@ -177,8 +177,14 @@ void setOSNameAndVersion(java_props_t *sprops) {
OSVerStruct (*procInfoFn)(id rec, SEL sel) = (OSVerStruct(*)(id, SEL))objc_msgSend_stret;
OSVerStruct osVer = procInfoFn([NSProcessInfo processInfo],
@selector(operatingSystemVersion));
- NSString *nsVerStr = [NSString stringWithFormat:@"%ld.%ld.%ld",
- (long)osVer.majorVersion, (long)osVer.minorVersion, (long)osVer.patchVersion];
+ NSString *nsVerStr;
+ if (osVer.patchVersion == 0) { // Omit trailing ".0"
+ nsVerStr = [NSString stringWithFormat:@"%ld.%ld",
+ (long)osVer.majorVersion, (long)osVer.minorVersion];
+ } else {
+ nsVerStr = [NSString stringWithFormat:@"%ld.%ld.%ld",
+ (long)osVer.majorVersion, (long)osVer.minorVersion, (long)osVer.patchVersion];
+ }
// Copy out the char*
osVersionCStr = strdup([nsVerStr UTF8String]);
}
diff --git a/jdk/src/java.base/share/classes/com/sun/crypto/provider/AESCipher.java b/jdk/src/java.base/share/classes/com/sun/crypto/provider/AESCipher.java
index 32cb3e39d3d..9d11f0091df 100644
--- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/AESCipher.java
+++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/AESCipher.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2016, 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
@@ -172,6 +172,11 @@ abstract class AESCipher extends CipherSpi {
*/
private final int fixedKeySize; // in bytes, -1 if no restriction
+ /*
+ * needed to enforce ISE thrown when updateAAD is called after update for GCM mode.
+ */
+ private boolean updateCalled;
+
/**
* Creates an instance of AES cipher with default ECB mode and
* PKCS5Padding.
@@ -304,6 +309,7 @@ abstract class AESCipher extends CipherSpi {
protected void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException {
checkKeySize(key, fixedKeySize);
+ updateCalled = false;
core.init(opmode, key, random);
}
@@ -336,6 +342,7 @@ abstract class AESCipher extends CipherSpi {
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
checkKeySize(key, fixedKeySize);
+ updateCalled = false;
core.init(opmode, key, params, random);
}
@@ -344,6 +351,7 @@ abstract class AESCipher extends CipherSpi {
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
checkKeySize(key, fixedKeySize);
+ updateCalled = false;
core.init(opmode, key, params, random);
}
@@ -368,6 +376,7 @@ abstract class AESCipher extends CipherSpi {
*/
protected byte[] engineUpdate(byte[] input, int inputOffset,
int inputLen) {
+ updateCalled = true;
return core.update(input, inputOffset, inputLen);
}
@@ -397,6 +406,7 @@ abstract class AESCipher extends CipherSpi {
protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws ShortBufferException {
+ updateCalled = true;
return core.update(input, inputOffset, inputLen, output,
outputOffset);
}
@@ -433,7 +443,9 @@ abstract class AESCipher extends CipherSpi {
*/
protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
throws IllegalBlockSizeException, BadPaddingException {
- return core.doFinal(input, inputOffset, inputLen);
+ byte[] out = core.doFinal(input, inputOffset, inputLen);
+ updateCalled = false;
+ return out;
}
/**
@@ -476,8 +488,10 @@ abstract class AESCipher extends CipherSpi {
byte[] output, int outputOffset)
throws IllegalBlockSizeException, ShortBufferException,
BadPaddingException {
- return core.doFinal(input, inputOffset, inputLen, output,
- outputOffset);
+ int outLen = core.doFinal(input, inputOffset, inputLen, output,
+ outputOffset);
+ updateCalled = false;
+ return outLen;
}
/**
@@ -574,6 +588,9 @@ abstract class AESCipher extends CipherSpi {
*/
@Override
protected void engineUpdateAAD(byte[] src, int offset, int len) {
+ if (core.getMode() == CipherCore.GCM_MODE && updateCalled) {
+ throw new IllegalStateException("AAD must be supplied before encryption/decryption starts");
+ }
core.updateAAD(src, offset, len);
}
@@ -606,6 +623,9 @@ abstract class AESCipher extends CipherSpi {
*/
@Override
protected void engineUpdateAAD(ByteBuffer src) {
+ if (core.getMode() == CipherCore.GCM_MODE && updateCalled) {
+ throw new IllegalStateException("AAD must be supplied before encryption/decryption starts");
+ }
if (src != null) {
int aadLen = src.limit() - src.position();
if (aadLen != 0) {
diff --git a/jdk/src/java.base/share/classes/com/sun/crypto/provider/CipherCore.java b/jdk/src/java.base/share/classes/com/sun/crypto/provider/CipherCore.java
index 4cdfdf8850e..592ec411407 100644
--- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/CipherCore.java
+++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/CipherCore.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2016, 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
@@ -124,7 +124,7 @@ final class CipherCore {
private static final int PCBC_MODE = 4;
private static final int CTR_MODE = 5;
private static final int CTS_MODE = 6;
- private static final int GCM_MODE = 7;
+ static final int GCM_MODE = 7;
/*
* variables used for performing the GCM (key+iv) uniqueness check.
@@ -196,7 +196,7 @@ final class CipherCore {
cipher = new CounterMode(rawImpl);
unitBytes = 1;
padding = null;
- } else if (modeUpperCase.startsWith("GCM")) {
+ } else if (modeUpperCase.equals("GCM")) {
// can only be used for block ciphers w/ 128-bit block size
if (blockSize != 16) {
throw new NoSuchAlgorithmException
@@ -223,6 +223,15 @@ final class CipherCore {
}
}
+ /**
+ * Returns the mode of this cipher.
+ *
+ * @return the parsed cipher mode
+ */
+ int getMode() {
+ return cipherMode;
+ }
+
private static int getNumOfUnit(String mode, int offset, int blockSize)
throws NoSuchAlgorithmException {
int result = blockSize; // use blockSize as default value
diff --git a/jdk/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java b/jdk/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java
index 87fa4a383f3..7dd9791f1b1 100644
--- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java
+++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2016, 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
@@ -49,6 +49,16 @@ final class GaloisCounterMode extends FeedbackCipher {
static int DEFAULT_TAG_LEN = AES_BLOCK_SIZE;
static int DEFAULT_IV_LEN = 12; // in bytes
+ // In NIST SP 800-38D, GCM input size is limited to be no longer
+ // than (2^36 - 32) bytes. Otherwise, the counter will wrap
+ // around and lead to a leak of plaintext.
+ // However, given the current GCM spec requirement that recovered
+ // text can only be returned after successful tag verification,
+ // we are bound by limiting the data size to the size limit of
+ // java byte array, e.g. Integer.MAX_VALUE, since all data
+ // can only be returned by the doFinal(...) call.
+ private static final int MAX_BUF_SIZE = Integer.MAX_VALUE;
+
// buffer for AAD data; if null, meaning update has been called
private ByteArrayOutputStream aadBuffer = new ByteArrayOutputStream();
private int sizeOfAAD = 0;
@@ -89,9 +99,13 @@ final class GaloisCounterMode extends FeedbackCipher {
}
}
- // ivLen in bits
- private static byte[] getLengthBlock(int ivLen) {
+ private static byte[] getLengthBlock(int ivLenInBytes) {
+ long ivLen = ((long)ivLenInBytes) << 3;
byte[] out = new byte[AES_BLOCK_SIZE];
+ out[8] = (byte)(ivLen >>> 56);
+ out[9] = (byte)(ivLen >>> 48);
+ out[10] = (byte)(ivLen >>> 40);
+ out[11] = (byte)(ivLen >>> 32);
out[12] = (byte)(ivLen >>> 24);
out[13] = (byte)(ivLen >>> 16);
out[14] = (byte)(ivLen >>> 8);
@@ -99,13 +113,22 @@ final class GaloisCounterMode extends FeedbackCipher {
return out;
}
- // aLen and cLen both in bits
- private static byte[] getLengthBlock(int aLen, int cLen) {
+ private static byte[] getLengthBlock(int aLenInBytes, int cLenInBytes) {
+ long aLen = ((long)aLenInBytes) << 3;
+ long cLen = ((long)cLenInBytes) << 3;
byte[] out = new byte[AES_BLOCK_SIZE];
+ out[0] = (byte)(aLen >>> 56);
+ out[1] = (byte)(aLen >>> 48);
+ out[2] = (byte)(aLen >>> 40);
+ out[3] = (byte)(aLen >>> 32);
out[4] = (byte)(aLen >>> 24);
out[5] = (byte)(aLen >>> 16);
out[6] = (byte)(aLen >>> 8);
out[7] = (byte)aLen;
+ out[8] = (byte)(cLen >>> 56);
+ out[9] = (byte)(cLen >>> 48);
+ out[10] = (byte)(cLen >>> 40);
+ out[11] = (byte)(cLen >>> 32);
out[12] = (byte)(cLen >>> 24);
out[13] = (byte)(cLen >>> 16);
out[14] = (byte)(cLen >>> 8);
@@ -142,13 +165,20 @@ final class GaloisCounterMode extends FeedbackCipher {
} else {
g.update(iv);
}
- byte[] lengthBlock = getLengthBlock(iv.length*8);
+ byte[] lengthBlock = getLengthBlock(iv.length);
g.update(lengthBlock);
j0 = g.digest();
}
return j0;
}
+ private static void checkDataLength(int processed, int len) {
+ if (processed > MAX_BUF_SIZE - len) {
+ throw new ProviderException("SunJCE provider only supports " +
+ "input size up to " + MAX_BUF_SIZE + " bytes");
+ }
+ }
+
GaloisCounterMode(SymmetricCipher embeddedCipher) {
super(embeddedCipher);
aadBuffer = new ByteArrayOutputStream();
@@ -319,20 +349,22 @@ final class GaloisCounterMode extends FeedbackCipher {
// Feed the AAD data to GHASH, pad if necessary
void processAAD() {
- if (aadBuffer != null && aadBuffer.size() > 0) {
- byte[] aad = aadBuffer.toByteArray();
- sizeOfAAD = aad.length;
- aadBuffer = null;
+ if (aadBuffer != null) {
+ if (aadBuffer.size() > 0) {
+ byte[] aad = aadBuffer.toByteArray();
+ sizeOfAAD = aad.length;
- int lastLen = aad.length % AES_BLOCK_SIZE;
- if (lastLen != 0) {
- ghashAllToS.update(aad, 0, aad.length - lastLen);
- byte[] padded = expandToOneBlock(aad, aad.length - lastLen,
- lastLen);
- ghashAllToS.update(padded);
- } else {
- ghashAllToS.update(aad);
+ int lastLen = aad.length % AES_BLOCK_SIZE;
+ if (lastLen != 0) {
+ ghashAllToS.update(aad, 0, aad.length - lastLen);
+ byte[] padded = expandToOneBlock(aad, aad.length - lastLen,
+ lastLen);
+ ghashAllToS.update(padded);
+ } else {
+ ghashAllToS.update(aad);
+ }
}
+ aadBuffer = null;
}
}
@@ -384,6 +416,9 @@ final class GaloisCounterMode extends FeedbackCipher {
if ((len % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering");
}
+
+ checkDataLength(processed, len);
+
processAAD();
if (len > 0) {
gctrPAndC.update(in, inOfs, len, out, outOfs);
@@ -405,17 +440,23 @@ final class GaloisCounterMode extends FeedbackCipher {
*/
int encryptFinal(byte[] in, int inOfs, int len, byte[] out, int outOfs)
throws IllegalBlockSizeException, ShortBufferException {
+ if (len > MAX_BUF_SIZE - tagLenBytes) {
+ throw new ShortBufferException
+ ("Can't fit both data and tag into one buffer");
+ }
if (out.length - outOfs < (len + tagLenBytes)) {
throw new ShortBufferException("Output buffer too small");
}
+ checkDataLength(processed, len);
+
processAAD();
if (len > 0) {
doLastBlock(in, inOfs, len, out, outOfs, true);
}
byte[] lengthBlock =
- getLengthBlock(sizeOfAAD*8, processed*8);
+ getLengthBlock(sizeOfAAD, processed);
ghashAllToS.update(lengthBlock);
byte[] s = ghashAllToS.digest();
byte[] sOut = new byte[s.length];
@@ -447,6 +488,9 @@ final class GaloisCounterMode extends FeedbackCipher {
if ((len % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering");
}
+
+ checkDataLength(ibuffer.size(), len);
+
processAAD();
if (len > 0) {
@@ -481,10 +525,21 @@ final class GaloisCounterMode extends FeedbackCipher {
if (len < tagLenBytes) {
throw new AEADBadTagException("Input too short - need tag");
}
+ // do this check here can also catch the potential integer overflow
+ // scenario for the subsequent output buffer capacity check.
+ checkDataLength(ibuffer.size(), (len - tagLenBytes));
+
if (out.length - outOfs < ((ibuffer.size() + len) - tagLenBytes)) {
throw new ShortBufferException("Output buffer too small");
}
+
processAAD();
+
+ // get the trailing tag bytes from 'in'
+ byte[] tag = new byte[tagLenBytes];
+ System.arraycopy(in, inOfs + len - tagLenBytes, tag, 0, tagLenBytes);
+ len -= tagLenBytes;
+
if (len != 0) {
ibuffer.write(in, inOfs, len);
}
@@ -495,17 +550,12 @@ final class GaloisCounterMode extends FeedbackCipher {
len = in.length;
ibuffer.reset();
- byte[] tag = new byte[tagLenBytes];
- // get the trailing tag bytes from 'in'
- System.arraycopy(in, len - tagLenBytes, tag, 0, tagLenBytes);
- len -= tagLenBytes;
-
if (len > 0) {
doLastBlock(in, inOfs, len, out, outOfs, false);
}
byte[] lengthBlock =
- getLengthBlock(sizeOfAAD*8, processed*8);
+ getLengthBlock(sizeOfAAD, processed);
ghashAllToS.update(lengthBlock);
byte[] s = ghashAllToS.digest();
diff --git a/jdk/src/java.base/share/classes/com/sun/security/ntlm/NTLM.java b/jdk/src/java.base/share/classes/com/sun/security/ntlm/NTLM.java
index 0d13d83411c..cb558c6d072 100644
--- a/jdk/src/java.base/share/classes/com/sun/security/ntlm/NTLM.java
+++ b/jdk/src/java.base/share/classes/com/sun/security/ntlm/NTLM.java
@@ -169,7 +169,7 @@ class NTLM {
byte[] readSecurityBuffer(int offset) throws NTLMException {
int pos = readInt(offset+4);
- if (pos == 0) return null;
+ if (pos == 0) return new byte[0];
try {
return Arrays.copyOfRange(
internal, pos, pos + readShort(offset));
diff --git a/jdk/src/java.base/share/classes/com/sun/security/ntlm/Server.java b/jdk/src/java.base/share/classes/com/sun/security/ntlm/Server.java
index 2e97579e669..c826d82f990 100644
--- a/jdk/src/java.base/share/classes/com/sun/security/ntlm/Server.java
+++ b/jdk/src/java.base/share/classes/com/sun/security/ntlm/Server.java
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
diff --git a/jdk/src/java.base/share/classes/java/lang/Class.java b/jdk/src/java.base/share/classes/java/lang/Class.java
index 4649b04b135..7ac3d50202c 100644
--- a/jdk/src/java.base/share/classes/java/lang/Class.java
+++ b/jdk/src/java.base/share/classes/java/lang/Class.java
@@ -238,15 +238,11 @@ public final class Class implements java.io.Serializable,
TypeVariable>[] typeparms = component.getTypeParameters();
if (typeparms.length > 0) {
- boolean first = true;
- sb.append('<');
+ StringJoiner sj = new StringJoiner(",", "<", ">");
for(TypeVariable> typeparm: typeparms) {
- if (!first)
- sb.append(',');
- sb.append(typeparm.getTypeName());
- first = false;
+ sj.add(typeparm.getTypeName());
}
- sb.append('>');
+ sb.append(sj.toString());
}
for (int i = 0; i < arrayDepth; i++)
diff --git a/jdk/src/java.base/share/classes/java/lang/Math.java b/jdk/src/java.base/share/classes/java/lang/Math.java
index aa678b875ec..4261bd2c3f1 100644
--- a/jdk/src/java.base/share/classes/java/lang/Math.java
+++ b/jdk/src/java.base/share/classes/java/lang/Math.java
@@ -1613,6 +1613,8 @@ public final class Math {
* @return (a × b + c)
* computed, as if with unlimited range and precision, and rounded
* once to the nearest {@code double} value
+ *
+ * @since 9
*/
// @HotSpotIntrinsicCandidate
public static double fma(double a, double b, double c) {
@@ -1728,6 +1730,8 @@ public final class Math {
* @return (a × b + c)
* computed, as if with unlimited range and precision, and rounded
* once to the nearest {@code float} value
+ *
+ * @since 9
*/
// @HotSpotIntrinsicCandidate
public static float fma(float a, float b, float c) {
diff --git a/jdk/src/java.base/share/classes/java/lang/Runtime.java b/jdk/src/java.base/share/classes/java/lang/Runtime.java
index fd5cb88a7d6..8cac75b4826 100644
--- a/jdk/src/java.base/share/classes/java/lang/Runtime.java
+++ b/jdk/src/java.base/share/classes/java/lang/Runtime.java
@@ -945,7 +945,7 @@ public class Runtime {
}
/**
- * A representation of a version string for an implemenation of the
+ * A representation of a version string for an implementation of the
* Java SE Platform. A version string contains a version number
* optionally followed by pre-release and build information.
*
@@ -1058,10 +1058,10 @@ public class Runtime {
* When comparing two version strings, the value of {@code $OPT}, if
* present, may or may not be significant depending on the chosen
* comparison method. The comparison methods {@link #compareTo(Version)
- * compareTo()} and {@link #compareToIgnoreOpt(Version)
- * compareToIgnoreOpt()} should be used consistently with the
+ * compareTo()} and {@link #compareToIgnoreOptional(Version)
+ * compareToIgnoreOptional()} should be used consistently with the
* corresponding methods {@link #equals(Object) equals()} and {@link
- * #equalsIgnoreOpt(Object) equalsIgnoreOpt()}.
+ * #equalsIgnoreOptional(Object) equalsIgnoreOptional()}.
*
* A short version string, {@code $SVSTR}, often useful in
* less formal contexts, is a version number optionally followed by a
@@ -1249,7 +1249,7 @@ public class Runtime {
* @throws NullPointerException
* If the given object is {@code null}
*/
- public int compareToIgnoreOpt(Version ob) {
+ public int compareToIgnoreOptional(Version ob) {
return compare(ob, true);
}
@@ -1270,7 +1270,7 @@ public class Runtime {
return ret;
if (!ignoreOpt)
- return compareOpt(ob);
+ return compareOptional(ob);
return 0;
}
@@ -1325,7 +1325,7 @@ public class Runtime {
return 0;
}
- private int compareOpt(Version ob) {
+ private int compareOptional(Version ob) {
Optional oOpt = ob.optional();
if (!optional.isPresent()) {
if (oOpt.isPresent())
@@ -1384,7 +1384,7 @@ public class Runtime {
*/
@Override
public boolean equals(Object ob) {
- boolean ret = equalsIgnoreOpt(ob);
+ boolean ret = equalsIgnoreOptional(ob);
if (!ret)
return false;
@@ -1407,7 +1407,7 @@ public class Runtime {
* ignoring the optinal build information
*
*/
- public boolean equalsIgnoreOpt(Object ob) {
+ public boolean equalsIgnoreOptional(Object ob) {
if (this == ob)
return true;
if (!(ob instanceof Version))
diff --git a/jdk/src/java.base/share/classes/java/lang/StrictMath.java b/jdk/src/java.base/share/classes/java/lang/StrictMath.java
index 998fc1eb426..3ef67145d0c 100644
--- a/jdk/src/java.base/share/classes/java/lang/StrictMath.java
+++ b/jdk/src/java.base/share/classes/java/lang/StrictMath.java
@@ -1276,6 +1276,8 @@ public final class StrictMath {
* @return (a × b + c)
* computed, as if with unlimited range and precision, and rounded
* once to the nearest {@code double} value
+ *
+ * @since 9
*/
public static double fma(double a, double b, double c) {
return Math.fma(a, b, c);
@@ -1328,6 +1330,8 @@ public final class StrictMath {
* @return (a × b + c)
* computed, as if with unlimited range and precision, and rounded
* once to the nearest {@code float} value
+ *
+ * @since 9
*/
public static float fma(float a, float b, float c) {
return Math.fma(a, b, c);
diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java b/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java
index f9c864a1ac1..fe77e01fcc7 100644
--- a/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java
@@ -155,7 +155,7 @@ class DirectMethodHandle extends MethodHandle {
private static LambdaForm preparedLambdaForm(MemberName m) {
assert(m.isInvocable()) : m; // call preparedFieldLambdaForm instead
MethodType mtype = m.getInvocationType().basicType();
- assert(!m.isMethodHandleInvoke() || "invokeBasic".equals(m.getName())) : m;
+ assert(!m.isMethodHandleInvoke()) : m;
int which;
switch (m.getReferenceKind()) {
case REF_invokeVirtual: which = LF_INVVIRTUAL; break;
diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java
index a0a04559ff9..2a722db28e0 100644
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java
@@ -1049,7 +1049,7 @@ class LambdaForm {
this.member = member;
this.resolvedHandle = resolvedHandle;
// The following assert is almost always correct, but will fail for corner cases, such as PrivateInvokeTest.
- //assert(!isInvokeBasic());
+ //assert(!isInvokeBasic(member));
}
NamedFunction(MethodType basicInvokerType) {
assert(basicInvokerType == basicInvokerType.basicType()) : basicInvokerType;
@@ -1060,13 +1060,13 @@ class LambdaForm {
// necessary to pass BigArityTest
this.member = Invokers.invokeBasicMethod(basicInvokerType);
}
- assert(isInvokeBasic());
+ assert(isInvokeBasic(member));
}
- private boolean isInvokeBasic() {
+ private static boolean isInvokeBasic(MemberName member) {
return member != null &&
- member.isMethodHandleInvoke() &&
- "invokeBasic".equals(member.getName());
+ member.getDeclaringClass() == MethodHandle.class &&
+ "invokeBasic".equals(member.getName());
}
// The next 2 constructors are used to break circular dependencies on MH.invokeStatic, etc.
@@ -1204,7 +1204,7 @@ class LambdaForm {
assert(mh.type().basicType() == MethodType.genericMethodType(arity).changeReturnType(rtype))
: Arrays.asList(mh, rtype, arity);
MemberName member = mh.internalMemberName();
- if (member != null && member.getName().equals("invokeBasic") && member.isMethodHandleInvoke()) {
+ if (isInvokeBasic(member)) {
assert(arity > 0);
assert(a[0] instanceof MethodHandle);
MethodHandle mh2 = (MethodHandle) a[0];
diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java b/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java
index 776d5266db3..3b56095d438 100644
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java
@@ -346,7 +346,6 @@ import static java.lang.invoke.MethodHandleStatics.newInternalError;
}
/** Utility method to query if this member is a method handle invocation (invoke or invokeExact).
- * Also returns true for the non-public MH.invokeBasic.
*/
public boolean isMethodHandleInvoke() {
final int bits = MH_INVOKE_MODS &~ Modifier.PUBLIC;
@@ -361,7 +360,6 @@ import static java.lang.invoke.MethodHandleStatics.newInternalError;
switch (name) {
case "invoke":
case "invokeExact":
- case "invokeBasic": // internal sig-poly method
return true;
default:
return false;
diff --git a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
index 1150c188b8c..14f7d32e2e2 100644
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
@@ -951,8 +951,6 @@ assertEquals("", (String) MH_newString.invokeExact());
return invoker(type);
if ("invokeExact".equals(name))
return exactInvoker(type);
- if ("invokeBasic".equals(name))
- return basicInvoker(type);
assert(!MemberName.isMethodHandleInvokeName(name));
return null;
}
@@ -3268,6 +3266,16 @@ assertEquals("yz", (String) d0.invokeExact(123, "x", "y", "z"));
*/
public static
MethodHandle dropArguments(MethodHandle target, int pos, List> valueTypes) {
+ return dropArguments0(target, pos, copyTypes(valueTypes));
+ }
+
+ private static List> copyTypes(List> types) {
+ Object[] a = types.toArray();
+ return Arrays.asList(Arrays.copyOf(a, a.length, Class[].class));
+ }
+
+ private static
+ MethodHandle dropArguments0(MethodHandle target, int pos, List> valueTypes) {
MethodType oldType = target.type(); // get NPE
int dropped = dropArgumentChecks(oldType, pos, valueTypes);
MethodType newType = oldType.insertParameterTypes(pos, valueTypes);
@@ -3348,6 +3356,7 @@ assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
// private version which allows caller some freedom with error handling
private static MethodHandle dropArgumentsToMatch(MethodHandle target, int skip, List> newTypes, int pos,
boolean nullOnFailure) {
+ newTypes = copyTypes(newTypes);
List> oldTypes = target.type().parameterList();
int match = oldTypes.size();
if (skip != 0) {
@@ -3379,11 +3388,11 @@ assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
// target: ( S*[skip], M*[match] )
MethodHandle adapter = target;
if (add > 0) {
- adapter = dropArguments(adapter, skip+ match, addTypes);
+ adapter = dropArguments0(adapter, skip+ match, addTypes);
}
// adapter: (S*[skip], M*[match], A*[add] )
if (pos > 0) {
- adapter = dropArguments(adapter, skip, newTypes.subList(0, pos));
+ adapter = dropArguments0(adapter, skip, newTypes.subList(0, pos));
}
// adapter: (S*[skip], P*[pos], M*[match], A*[add] )
return adapter;
@@ -3787,7 +3796,7 @@ System.out.println((int) f0.invokeExact("x", "y")); // 2
int filterValues = filterType.parameterCount();
if (filterValues == 0
? (rtype != void.class)
- : (rtype != filterType.parameterType(0)))
+ : (rtype != filterType.parameterType(0) || filterValues != 1))
throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
}
@@ -4290,7 +4299,7 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
step.set(i, dropArgumentsToMatch(identityOrVoid(t), 0, commonParameterSequence, i));
}
if (pred.get(i) == null) {
- pred.set(i, dropArguments(constant(boolean.class, true), 0, commonParameterSequence));
+ pred.set(i, dropArguments0(constant(boolean.class, true), 0, commonParameterSequence));
}
if (fini.get(i) == null) {
fini.set(i, empty(methodType(t, commonParameterSequence)));
@@ -4315,7 +4324,7 @@ assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
return hs.stream().map(h -> {
int pc = h.type().parameterCount();
int tpsize = targetParams.size();
- return pc < tpsize ? dropArguments(h, pc, targetParams.subList(pc, tpsize)) : h;
+ return pc < tpsize ? dropArguments0(h, pc, targetParams.subList(pc, tpsize)) : h;
}).collect(Collectors.toList());
}
diff --git a/jdk/src/java.base/share/classes/java/lang/module/ModulePath.java b/jdk/src/java.base/share/classes/java/lang/module/ModulePath.java
index 92c16bdfbc2..d300bc007e3 100644
--- a/jdk/src/java.base/share/classes/java/lang/module/ModulePath.java
+++ b/jdk/src/java.base/share/classes/java/lang/module/ModulePath.java
@@ -52,6 +52,7 @@ import java.util.jar.Manifest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -420,7 +421,7 @@ class ModulePath implements ConfigurableModuleFinder {
// scan the entries in the JAR file to locate the .class and service
// configuration file
Map> map =
- jf.stream()
+ versionedStream(jf)
.map(JarEntry::getName)
.filter(s -> (s.endsWith(".class") ^ s.startsWith(SERVICES_PREFIX)))
.collect(Collectors.partitioningBy(s -> s.endsWith(".class"),
@@ -503,8 +504,21 @@ class ModulePath implements ConfigurableModuleFinder {
return mn;
}
+ private Stream versionedStream(JarFile jf) {
+ if (jf.isMultiRelease()) {
+ // a stream of JarEntries whose names are base names and whose
+ // contents are from the corresponding versioned entries in
+ // a multi-release jar file
+ return jf.stream().map(JarEntry::getName)
+ .filter(name -> !name.startsWith("META-INF/versions/"))
+ .map(jf::getJarEntry);
+ } else {
+ return jf.stream();
+ }
+ }
+
private Set jarPackages(JarFile jf) {
- return jf.stream()
+ return versionedStream(jf)
.filter(e -> e.getName().endsWith(".class"))
.map(e -> toPackageName(e.getName()))
.filter(pkg -> pkg.length() > 0) // module-info
diff --git a/jdk/src/java.base/share/classes/java/lang/module/package-info.java b/jdk/src/java.base/share/classes/java/lang/module/package-info.java
index a1c44d68145..f641638cb2c 100644
--- a/jdk/src/java.base/share/classes/java/lang/module/package-info.java
+++ b/jdk/src/java.base/share/classes/java/lang/module/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2016 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2016, 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
diff --git a/jdk/src/java.base/share/classes/java/lang/reflect/Executable.java b/jdk/src/java.base/share/classes/java/lang/reflect/Executable.java
index 88a9f5de9ac..812e22816f1 100644
--- a/jdk/src/java.base/share/classes/java/lang/reflect/Executable.java
+++ b/jdk/src/java.base/share/classes/java/lang/reflect/Executable.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2016, 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
@@ -28,6 +28,7 @@ package java.lang.reflect;
import java.lang.annotation.*;
import java.util.Map;
import java.util.Objects;
+import java.util.StringJoiner;
import jdk.internal.misc.SharedSecrets;
import sun.reflect.annotation.AnnotationParser;
@@ -86,15 +87,6 @@ public abstract class Executable extends AccessibleObject
getDeclaringClass());
}
- void separateWithCommas(Class>[] types, StringBuilder sb) {
- for (int j = 0; j < types.length; j++) {
- sb.append(types[j].getTypeName());
- if (j < (types.length - 1))
- sb.append(",");
- }
-
- }
-
void printModifiersIfNonzero(StringBuilder sb, int mask, boolean isDefault) {
int mod = getModifiers() & mask;
@@ -121,13 +113,20 @@ public abstract class Executable extends AccessibleObject
printModifiersIfNonzero(sb, modifierMask, isDefault);
specificToStringHeader(sb);
-
sb.append('(');
- separateWithCommas(parameterTypes, sb);
+ StringJoiner sj = new StringJoiner(",");
+ for (Class> parameterType : parameterTypes) {
+ sj.add(parameterType.getTypeName());
+ }
+ sb.append(sj.toString());
sb.append(')');
+
if (exceptionTypes.length > 0) {
- sb.append(" throws ");
- separateWithCommas(exceptionTypes, sb);
+ StringJoiner joiner = new StringJoiner(",", "throws ", "");
+ for (Class> exceptionType : exceptionTypes) {
+ joiner.add(exceptionType.getTypeName());
+ }
+ sb.append(joiner.toString());
}
return sb.toString();
} catch (Exception e) {
@@ -149,42 +148,34 @@ public abstract class Executable extends AccessibleObject
TypeVariable>[] typeparms = getTypeParameters();
if (typeparms.length > 0) {
- boolean first = true;
- sb.append('<');
+ StringJoiner sj = new StringJoiner(",", "<", "> ");
for(TypeVariable> typeparm: typeparms) {
- if (!first)
- sb.append(',');
- // Class objects can't occur here; no need to test
- // and call Class.getName().
- sb.append(typeparm.toString());
- first = false;
+ sj.add(typeparm.getTypeName());
}
- sb.append("> ");
+ sb.append(sj.toString());
}
specificToGenericStringHeader(sb);
sb.append('(');
+ StringJoiner sj = new StringJoiner(",");
Type[] params = getGenericParameterTypes();
for (int j = 0; j < params.length; j++) {
String param = params[j].getTypeName();
if (isVarArgs() && (j == params.length - 1)) // replace T[] with T...
param = param.replaceFirst("\\[\\]$", "...");
- sb.append(param);
- if (j < (params.length - 1))
- sb.append(',');
+ sj.add(param);
}
+ sb.append(sj.toString());
sb.append(')');
- Type[] exceptions = getGenericExceptionTypes();
- if (exceptions.length > 0) {
- sb.append(" throws ");
- for (int k = 0; k < exceptions.length; k++) {
- sb.append((exceptions[k] instanceof Class)?
- ((Class)exceptions[k]).getName():
- exceptions[k].toString());
- if (k < (exceptions.length - 1))
- sb.append(',');
+
+ Type[] exceptionTypes = getGenericExceptionTypes();
+ if (exceptionTypes.length > 0) {
+ StringJoiner joiner = new StringJoiner(",", " throws ", "");
+ for (Type exceptionType : exceptionTypes) {
+ joiner.add(exceptionType.getTypeName());
}
+ sb.append(joiner.toString());
}
return sb.toString();
} catch (Exception e) {
diff --git a/jdk/src/java.base/share/classes/java/nio/file/Files.java b/jdk/src/java.base/share/classes/java/nio/file/Files.java
index e1ab41b7759..3a6491440a7 100644
--- a/jdk/src/java.base/share/classes/java/nio/file/Files.java
+++ b/jdk/src/java.base/share/classes/java/nio/file/Files.java
@@ -3290,8 +3290,8 @@ public final class Files {
* a size of {@code 0}. All bytes in the byte array are written to the file.
* The method ensures that the file is closed when all bytes have been
* written (or an I/O error or other runtime exception is thrown). If an I/O
- * error occurs then it may do so after the file has created or truncated,
- * or after some bytes have been written to the file.
+ * error occurs then it may do so after the file has been created or
+ * truncated, or after some bytes have been written to the file.
*
* Usage example: By default the method creates a new file or
* overwrites an existing file. Suppose you instead want to append bytes
@@ -3360,7 +3360,8 @@ public final class Files {
* a size of {@code 0}. The method ensures that the file is closed when all
* lines have been written (or an I/O error or other runtime exception is
* thrown). If an I/O error occurs then it may do so after the file has
- * created or truncated, or after some bytes have been written to the file.
+ * been created or truncated, or after some bytes have been written to the
+ * file.
*
* @param path
* the path to the file
diff --git a/jdk/src/java.base/share/classes/java/security/ProtectionDomain.java b/jdk/src/java.base/share/classes/java/security/ProtectionDomain.java
index 32cb2186a2e..fcd6f0be465 100644
--- a/jdk/src/java.base/share/classes/java/security/ProtectionDomain.java
+++ b/jdk/src/java.base/share/classes/java/security/ProtectionDomain.java
@@ -132,7 +132,7 @@ public class ProtectionDomain {
/* the PermissionCollection is static (pre 1.4 constructor)
or dynamic (via a policy refresh) */
- private boolean staticPermissions;
+ private final boolean staticPermissions;
/*
* An object used as a key when the ProtectionDomain is stored in a Map.
@@ -143,8 +143,12 @@ public class ProtectionDomain {
* Creates a new ProtectionDomain with the given CodeSource and
* Permissions. If the permissions object is not null, then
* {@code setReadOnly()} will be called on the passed in
- * Permissions object. The only permissions granted to this domain
- * are the ones specified; the current Policy will not be consulted.
+ * Permissions object.
+ *
+ * The permissions granted to this domain are static, i.e.
+ * invoking the {@link #staticPermissionsOnly()} method returns true.
+ * They contain only the ones passed to this constructor and
+ * the current Policy will not be consulted.
*
* @param codesource the codesource associated with this domain
* @param permissions the permissions granted to this domain
@@ -170,9 +174,11 @@ public class ProtectionDomain {
* Permissions, ClassLoader and array of Principals. If the
* permissions object is not null, then {@code setReadOnly()}
* will be called on the passed in Permissions object.
- * The permissions granted to this domain are dynamic; they include
- * both the static permissions passed to this constructor, and any
- * permissions granted to this domain by the current Policy at the
+ *
+ * The permissions granted to this domain are dynamic, i.e.
+ * invoking the {@link #staticPermissionsOnly()} method returns false.
+ * They include both the static permissions passed to this constructor,
+ * and any permissions granted to this domain by the current Policy at the
* time a permission is checked.
*
* This constructor is typically used by
@@ -255,6 +261,19 @@ public class ProtectionDomain {
return permissions;
}
+ /**
+ * Returns true if this domain contains only static permissions
+ * and does not check the current {@code Policy} at the time of
+ * permission checking.
+ *
+ * @return true if this domain contains only static permissions.
+ *
+ * @since 9
+ */
+ public final boolean staticPermissionsOnly() {
+ return this.staticPermissions;
+ }
+
/**
* Check and see if this ProtectionDomain implies the permissions
* expressed in the Permission object.
@@ -263,25 +282,19 @@ public class ProtectionDomain {
* ProtectionDomain was constructed with a static set of permissions
* or it was bound to a dynamically mapped set of permissions.
*
- * If the ProtectionDomain was constructed to a
- * {@link #ProtectionDomain(CodeSource, PermissionCollection)
- * statically bound} PermissionCollection then the permission will
- * only be checked against the PermissionCollection supplied at
- * construction.
+ * If the {@link #staticPermissionsOnly()} method returns
+ * true, then the permission will only be checked against the
+ * PermissionCollection supplied at construction.
*
- * However, if the ProtectionDomain was constructed with
- * the constructor variant which supports
- * {@link #ProtectionDomain(CodeSource, PermissionCollection,
- * ClassLoader, java.security.Principal[]) dynamically binding}
- * permissions, then the permission will be checked against the
- * combination of the PermissionCollection supplied at construction and
+ * Otherwise, the permission will be checked against the combination
+ * of the PermissionCollection supplied at construction and
* the current Policy binding.
*
- * @param permission the Permission object to check.
+ * @param perm the Permission object to check.
*
- * @return true if "permission" is implicit to this ProtectionDomain.
+ * @return true if {@code perm} is implied by this ProtectionDomain.
*/
- public boolean implies(Permission permission) {
+ public boolean implies(Permission perm) {
if (hasAllPerm) {
// internal permission collection already has AllPermission -
@@ -290,10 +303,10 @@ public class ProtectionDomain {
}
if (!staticPermissions &&
- Policy.getPolicyNoCheck().implies(this, permission))
+ Policy.getPolicyNoCheck().implies(this, perm))
return true;
if (permissions != null)
- return permissions.implies(permission);
+ return permissions.implies(perm);
return false;
}
diff --git a/jdk/src/java.base/share/classes/java/security/Provider.java b/jdk/src/java.base/share/classes/java/security/Provider.java
index de09dc49240..f9b0583df84 100644
--- a/jdk/src/java.base/share/classes/java/security/Provider.java
+++ b/jdk/src/java.base/share/classes/java/security/Provider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved
+ * Copyright (c) 1996, 2016, 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
diff --git a/jdk/src/java.base/share/classes/java/util/Queue.java b/jdk/src/java.base/share/classes/java/util/Queue.java
index 7d5e39c7030..e94b22c7fb2 100644
--- a/jdk/src/java.base/share/classes/java/util/Queue.java
+++ b/jdk/src/java.base/share/classes/java/util/Queue.java
@@ -124,7 +124,6 @@ package java.util;
* always well-defined for queues with the same elements but different
* ordering properties.
*
- *
*
This interface is a member of the
*
* Java Collections Framework.
diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java b/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java
index a558cdad2f3..7f3a6200953 100644
--- a/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java
@@ -35,6 +35,8 @@
package java.util.concurrent;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.util.concurrent.locks.LockSupport;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
@@ -149,26 +151,29 @@ public class CompletableFuture implements Future, CompletionStage {
* applies across normal vs exceptional outcomes, sync vs async
* actions, binary triggers, and various forms of completions.
*
- * Non-nullness of field result (set via CAS) indicates done. An
- * AltResult is used to box null as a result, as well as to hold
- * exceptions. Using a single field makes completion simple to
- * detect and trigger. Encoding and decoding is straightforward
- * but adds to the sprawl of trapping and associating exceptions
- * with targets. Minor simplifications rely on (static) NIL (to
- * box null results) being the only AltResult with a null
- * exception field, so we don't usually need explicit comparisons.
- * Even though some of the generics casts are unchecked (see
- * SuppressWarnings annotations), they are placed to be
- * appropriate even if checked.
+ * Non-nullness of volatile field "result" indicates done. It may
+ * be set directly if known to be thread-confined, else via CAS.
+ * An AltResult is used to box null as a result, as well as to
+ * hold exceptions. Using a single field makes completion simple
+ * to detect and trigger. Result encoding and decoding is
+ * straightforward but tedious and adds to the sprawl of trapping
+ * and associating exceptions with targets. Minor simplifications
+ * rely on (static) NIL (to box null results) being the only
+ * AltResult with a null exception field, so we don't usually need
+ * explicit comparisons. Even though some of the generics casts
+ * are unchecked (see SuppressWarnings annotations), they are
+ * placed to be appropriate even if checked.
*
* Dependent actions are represented by Completion objects linked
* as Treiber stacks headed by field "stack". There are Completion
- * classes for each kind of action, grouped into single-input
- * (UniCompletion), two-input (BiCompletion), projected
- * (BiCompletions using either (not both) of two inputs), shared
- * (CoCompletion, used by the second of two sources), zero-input
- * source actions, and Signallers that unblock waiters. Class
- * Completion extends ForkJoinTask to enable async execution
+ * classes for each kind of action, grouped into:
+ * - single-input (UniCompletion),
+ * - two-input (BiCompletion),
+ * - projected (BiCompletions using exactly one of two inputs),
+ * - shared (CoCompletion, used by the second of two sources),
+ * - zero-input source actions,
+ * - Signallers that unblock waiters.
+ * Class Completion extends ForkJoinTask to enable async execution
* (adding no space overhead because we exploit its "tag" methods
* to maintain claims). It is also declared as Runnable to allow
* usage with arbitrary executors.
@@ -184,7 +189,7 @@ public class CompletableFuture implements Future, CompletionStage {
* encounter layers of adapters in common usages.
*
* * Boolean CompletableFuture method x(...) (for example
- * uniApply) takes all of the arguments needed to check that an
+ * biApply) takes all of the arguments needed to check that an
* action is triggerable, and then either runs the action or
* arranges its async execution by executing its Completion
* argument, if present. The method returns true if known to be
@@ -194,24 +199,29 @@ public class CompletableFuture implements Future, CompletionStage {
* method with its held arguments, and on success cleans up.
* The mode argument allows tryFire to be called twice (SYNC,
* then ASYNC); the first to screen and trap exceptions while
- * arranging to execute, and the second when called from a
- * task. (A few classes are not used async so take slightly
- * different forms.) The claim() callback suppresses function
- * invocation if already claimed by another thread.
+ * arranging to execute, and the second when called from a task.
+ * (A few classes are not used async so take slightly different
+ * forms.) The claim() callback suppresses function invocation
+ * if already claimed by another thread.
+ *
+ * * Some classes (for example UniApply) have separate handling
+ * code for when known to be thread-confined ("now" methods) and
+ * for when shared (in tryFire), for efficiency.
*
* * CompletableFuture method xStage(...) is called from a public
- * stage method of CompletableFuture x. It screens user
+ * stage method of CompletableFuture f. It screens user
* arguments and invokes and/or creates the stage object. If
- * not async and x is already complete, the action is run
- * immediately. Otherwise a Completion c is created, pushed to
- * x's stack (unless done), and started or triggered via
- * c.tryFire. This also covers races possible if x completes
- * while pushing. Classes with two inputs (for example BiApply)
- * deal with races across both while pushing actions. The
- * second completion is a CoCompletion pointing to the first,
- * shared so that at most one performs the action. The
- * multiple-arity methods allOf and anyOf do this pairwise to
- * form trees of completions.
+ * not async and already triggerable, the action is run
+ * immediately. Otherwise a Completion c is created, and
+ * submitted to the executor if triggerable, or pushed onto f's
+ * stack if not. Completion actions are started via c.tryFire.
+ * We recheck after pushing to a source future's stack to cover
+ * possible races if the source completes while pushing.
+ * Classes with two inputs (for example BiApply) deal with races
+ * across both while pushing actions. The second completion is
+ * a CoCompletion pointing to the first, shared so that at most
+ * one performs the action. The multiple-arity methods allOf
+ * and anyOf do this pairwise to form trees of completions.
*
* Note that the generic type parameters of methods vary according
* to whether "this" is a source, dependent, or completion.
@@ -236,29 +246,30 @@ public class CompletableFuture implements Future, CompletionStage {
* pointing back to its sources. So we null out fields as soon as
* possible. The screening checks needed anyway harmlessly ignore
* null arguments that may have been obtained during races with
- * threads nulling out fields. We also try to unlink fired
- * Completions from stacks that might never be popped (see method
- * postFire). Completion fields need not be declared as final or
- * volatile because they are only visible to other threads upon
- * safe publication.
+ * threads nulling out fields. We also try to unlink non-isLive
+ * (fired or cancelled) Completions from stacks that might
+ * otherwise never be popped: Method cleanStack always unlinks non
+ * isLive completions from the head of stack; others may
+ * occasionally remain if racing with other cancellations or
+ * removals.
+ *
+ * Completion fields need not be declared as final or volatile
+ * because they are only visible to other threads upon safe
+ * publication.
*/
volatile Object result; // Either the result or boxed AltResult
volatile Completion stack; // Top of Treiber stack of dependent actions
final boolean internalComplete(Object r) { // CAS from null to r
- return U.compareAndSwapObject(this, RESULT, null, r);
- }
-
- final boolean casStack(Completion cmp, Completion val) {
- return U.compareAndSwapObject(this, STACK, cmp, val);
+ return RESULT.compareAndSet(this, null, r);
}
/** Returns true if successfully pushed c onto stack. */
final boolean tryPushStack(Completion c) {
Completion h = stack;
- lazySetNext(c, h);
- return U.compareAndSwapObject(this, STACK, h, c);
+ NEXT.set(c, h); // CAS piggyback
+ return STACK.compareAndSet(this, h, c);
}
/** Unconditionally pushes c onto stack, retrying if necessary. */
@@ -278,8 +289,7 @@ public class CompletableFuture implements Future, CompletionStage {
/** Completes with the null value, unless already completed. */
final boolean completeNull() {
- return U.compareAndSwapObject(this, RESULT, null,
- NIL);
+ return RESULT.compareAndSet(this, null, NIL);
}
/** Returns the encoding of the given non-exceptional value. */
@@ -289,8 +299,7 @@ public class CompletableFuture implements Future, CompletionStage {
/** Completes with a non-exceptional result, unless already completed. */
final boolean completeValue(T t) {
- return U.compareAndSwapObject(this, RESULT, null,
- (t == null) ? NIL : t);
+ return RESULT.compareAndSet(this, null, (t == null) ? NIL : t);
}
/**
@@ -304,8 +313,7 @@ public class CompletableFuture implements Future, CompletionStage {
/** Completes with an exceptional result, unless already completed. */
final boolean completeThrowable(Throwable x) {
- return U.compareAndSwapObject(this, RESULT, null,
- encodeThrowable(x));
+ return RESULT.compareAndSet(this, null, encodeThrowable(x));
}
/**
@@ -332,8 +340,7 @@ public class CompletableFuture implements Future, CompletionStage {
* existing CompletionException.
*/
final boolean completeThrowable(Throwable x, Object r) {
- return U.compareAndSwapObject(this, RESULT, null,
- encodeThrowable(x, r));
+ return RESULT.compareAndSet(this, null, encodeThrowable(x, r));
}
/**
@@ -351,10 +358,11 @@ public class CompletableFuture implements Future, CompletionStage {
*/
static Object encodeRelay(Object r) {
Throwable x;
- return (((r instanceof AltResult) &&
- (x = ((AltResult)r).ex) != null &&
- !(x instanceof CompletionException)) ?
- new AltResult(new CompletionException(x)) : r);
+ if (r instanceof AltResult
+ && (x = ((AltResult)r).ex) != null
+ && !(x instanceof CompletionException))
+ r = new AltResult(new CompletionException(x));
+ return r;
}
/**
@@ -362,14 +370,13 @@ public class CompletableFuture implements Future, CompletionStage {
* If exceptional, r is first coerced to a CompletionException.
*/
final boolean completeRelay(Object r) {
- return U.compareAndSwapObject(this, RESULT, null,
- encodeRelay(r));
+ return RESULT.compareAndSet(this, null, encodeRelay(r));
}
/**
* Reports result using Future.get conventions.
*/
- private static T reportGet(Object r)
+ private static Object reportGet(Object r)
throws InterruptedException, ExecutionException {
if (r == null) // by convention below, null means interrupted
throw new InterruptedException();
@@ -384,14 +391,13 @@ public class CompletableFuture implements Future, CompletionStage {
x = cause;
throw new ExecutionException(x);
}
- @SuppressWarnings("unchecked") T t = (T) r;
- return t;
+ return r;
}
/**
* Decodes outcome to return result or throw unchecked exception.
*/
- private static T reportJoin(Object r) {
+ private static Object reportJoin(Object r) {
if (r instanceof AltResult) {
Throwable x;
if ((x = ((AltResult)r).ex) == null)
@@ -402,8 +408,7 @@ public class CompletableFuture implements Future, CompletionStage {
throw (CompletionException)x;
throw new CompletionException(x);
}
- @SuppressWarnings("unchecked") T t = (T) r;
- return t;
+ return r;
}
/* ------------- Async task preliminaries -------------- */
@@ -449,12 +454,6 @@ public class CompletableFuture implements Future, CompletionStage {
static final int ASYNC = 1;
static final int NESTED = -1;
- /**
- * Spins before blocking in waitingGet
- */
- static final int SPINS = (Runtime.getRuntime().availableProcessors() > 1 ?
- 1 << 8 : 0);
-
/* ------------- Base Completion classes and operations -------------- */
@SuppressWarnings("serial")
@@ -479,10 +478,6 @@ public class CompletableFuture implements Future, CompletionStage {
public final void setRawResult(Void v) {}
}
- static void lazySetNext(Completion c, Completion next) {
- U.putObjectRelease(c, NEXT, next);
- }
-
/**
* Pops and tries to trigger all reachable dependents. Call only
* when known to be done.
@@ -497,40 +492,47 @@ public class CompletableFuture implements Future, CompletionStage {
while ((h = f.stack) != null ||
(f != this && (h = (f = this).stack) != null)) {
CompletableFuture> d; Completion t;
- if (f.casStack(h, t = h.next)) {
+ if (STACK.compareAndSet(f, h, t = h.next)) {
if (t != null) {
if (f != this) {
pushStack(h);
continue;
}
- h.next = null; // detach
+ NEXT.compareAndSet(h, t, null); // try to detach
}
f = (d = h.tryFire(NESTED)) == null ? this : d;
}
}
}
- /** Traverses stack and unlinks dead Completions. */
+ /** Traverses stack and unlinks one or more dead Completions, if found. */
final void cleanStack() {
- for (Completion p = null, q = stack; q != null;) {
+ Completion p = stack;
+ // ensure head of stack live
+ for (boolean unlinked = false;;) {
+ if (p == null)
+ return;
+ else if (p.isLive()) {
+ if (unlinked)
+ return;
+ else
+ break;
+ }
+ else if (STACK.weakCompareAndSetVolatile(this, p, (p = p.next)))
+ unlinked = true;
+ else
+ p = stack;
+ }
+ // try to unlink first non-live
+ for (Completion q = p.next; q != null;) {
Completion s = q.next;
if (q.isLive()) {
p = q;
q = s;
- }
- else if (p == null) {
- casStack(q, s);
- q = stack;
- }
- else {
- p.next = s;
- if (p.isLive())
- q = s;
- else {
- p = null; // restart
- q = stack;
- }
- }
+ } else if (NEXT.weakCompareAndSetVolatile(p, q, s))
+ break;
+ else
+ q = p.next;
}
}
@@ -568,11 +570,20 @@ public class CompletableFuture implements Future, CompletionStage {
final boolean isLive() { return dep != null; }
}
- /** Pushes the given completion (if it exists) unless done. */
- final void push(UniCompletion,?> c) {
+ /**
+ * Pushes the given completion unless it completes while trying.
+ * Caller should first check that result is null.
+ */
+ final void unipush(Completion c) {
if (c != null) {
- while (result == null && !tryPushStack(c))
- lazySetNext(c, null); // clear on failure
+ while (!tryPushStack(c)) {
+ if (result != null) {
+ NEXT.set(c, null);
+ break;
+ }
+ }
+ if (result != null)
+ c.tryFire(SYNC);
}
}
@@ -583,9 +594,10 @@ public class CompletableFuture implements Future, CompletionStage {
*/
final CompletableFuture postFire(CompletableFuture> a, int mode) {
if (a != null && a.stack != null) {
- if (a.result == null)
+ Object r;
+ if ((r = a.result) == null)
a.cleanStack();
- else if (mode >= 0)
+ if (mode >= 0 && (r != null || a.result != null))
a.postComplete();
}
if (result != null && stack != null) {
@@ -607,48 +619,65 @@ public class CompletableFuture implements Future, CompletionStage {
}
final CompletableFuture tryFire(int mode) {
CompletableFuture d; CompletableFuture a;
- if ((d = dep) == null ||
- !d.uniApply(a = src, fn, mode > 0 ? null : this))
+ Object r; Throwable x; Function super T,? extends V> f;
+ if ((d = dep) == null || (f = fn) == null
+ || (a = src) == null || (r = a.result) == null)
return null;
+ tryComplete: if (d.result == null) {
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ d.completeThrowable(x, r);
+ break tryComplete;
+ }
+ r = null;
+ }
+ try {
+ if (mode <= 0 && !claim())
+ return null;
+ else {
+ @SuppressWarnings("unchecked") T t = (T) r;
+ d.completeValue(f.apply(t));
+ }
+ } catch (Throwable ex) {
+ d.completeThrowable(ex);
+ }
+ }
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
- final boolean uniApply(CompletableFuture a,
- Function super S,? extends T> f,
- UniApply c) {
- Object r; Throwable x;
- if (a == null || (r = a.result) == null || f == null)
- return false;
- tryComplete: if (result == null) {
- if (r instanceof AltResult) {
- if ((x = ((AltResult)r).ex) != null) {
- completeThrowable(x, r);
- break tryComplete;
- }
- r = null;
- }
- try {
- if (c != null && !c.claim())
- return false;
- @SuppressWarnings("unchecked") S s = (S) r;
- completeValue(f.apply(s));
- } catch (Throwable ex) {
- completeThrowable(ex);
- }
- }
- return true;
- }
-
private CompletableFuture uniApplyStage(
Executor e, Function super T,? extends V> f) {
if (f == null) throw new NullPointerException();
+ Object r;
+ if ((r = result) != null)
+ return uniApplyNow(r, e, f);
CompletableFuture d = newIncompleteFuture();
- if (e != null || !d.uniApply(this, f, null)) {
- UniApply c = new UniApply(e, d, this, f);
- push(c);
- c.tryFire(SYNC);
+ unipush(new UniApply(e, d, this, f));
+ return d;
+ }
+
+ private CompletableFuture uniApplyNow(
+ Object r, Executor e, Function super T,? extends V> f) {
+ Throwable x;
+ CompletableFuture d = newIncompleteFuture();
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ d.result = encodeThrowable(x, r);
+ return d;
+ }
+ r = null;
+ }
+ try {
+ if (e != null) {
+ e.execute(new UniApply(null, d, this, f));
+ } else {
+ @SuppressWarnings("unchecked") T t = (T) r;
+ d.result = d.encodeValue(f.apply(t));
+ }
+ } catch (Throwable ex) {
+ d.result = encodeThrowable(ex);
}
return d;
}
@@ -662,48 +691,67 @@ public class CompletableFuture implements Future, CompletionStage {
}
final CompletableFuture tryFire(int mode) {
CompletableFuture d; CompletableFuture a;
- if ((d = dep) == null ||
- !d.uniAccept(a = src, fn, mode > 0 ? null : this))
+ Object r; Throwable x; Consumer super T> f;
+ if ((d = dep) == null || (f = fn) == null
+ || (a = src) == null || (r = a.result) == null)
return null;
+ tryComplete: if (d.result == null) {
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ d.completeThrowable(x, r);
+ break tryComplete;
+ }
+ r = null;
+ }
+ try {
+ if (mode <= 0 && !claim())
+ return null;
+ else {
+ @SuppressWarnings("unchecked") T t = (T) r;
+ f.accept(t);
+ d.completeNull();
+ }
+ } catch (Throwable ex) {
+ d.completeThrowable(ex);
+ }
+ }
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
- final boolean uniAccept(CompletableFuture a,
- Consumer super S> f, UniAccept c) {
- Object r; Throwable x;
- if (a == null || (r = a.result) == null || f == null)
- return false;
- tryComplete: if (result == null) {
- if (r instanceof AltResult) {
- if ((x = ((AltResult)r).ex) != null) {
- completeThrowable(x, r);
- break tryComplete;
- }
- r = null;
- }
- try {
- if (c != null && !c.claim())
- return false;
- @SuppressWarnings("unchecked") S s = (S) r;
- f.accept(s);
- completeNull();
- } catch (Throwable ex) {
- completeThrowable(ex);
- }
- }
- return true;
- }
-
private CompletableFuture uniAcceptStage(Executor e,
Consumer super T> f) {
if (f == null) throw new NullPointerException();
+ Object r;
+ if ((r = result) != null)
+ return uniAcceptNow(r, e, f);
CompletableFuture d = newIncompleteFuture();
- if (e != null || !d.uniAccept(this, f, null)) {
- UniAccept c = new UniAccept(e, d, this, f);
- push(c);
- c.tryFire(SYNC);
+ unipush(new UniAccept(e, d, this, f));
+ return d;
+ }
+
+ private CompletableFuture uniAcceptNow(
+ Object r, Executor e, Consumer super T> f) {
+ Throwable x;
+ CompletableFuture d = newIncompleteFuture();
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ d.result = encodeThrowable(x, r);
+ return d;
+ }
+ r = null;
+ }
+ try {
+ if (e != null) {
+ e.execute(new UniAccept(null, d, this, f));
+ } else {
+ @SuppressWarnings("unchecked") T t = (T) r;
+ f.accept(t);
+ d.result = NIL;
+ }
+ } catch (Throwable ex) {
+ d.result = encodeThrowable(ex);
}
return d;
}
@@ -717,42 +765,56 @@ public class CompletableFuture implements Future, CompletionStage {
}
final CompletableFuture tryFire(int mode) {
CompletableFuture d; CompletableFuture a;
- if ((d = dep) == null ||
- !d.uniRun(a = src, fn, mode > 0 ? null : this))
+ Object r; Throwable x; Runnable f;
+ if ((d = dep) == null || (f = fn) == null
+ || (a = src) == null || (r = a.result) == null)
return null;
+ if (d.result == null) {
+ if (r instanceof AltResult && (x = ((AltResult)r).ex) != null)
+ d.completeThrowable(x, r);
+ else
+ try {
+ if (mode <= 0 && !claim())
+ return null;
+ else {
+ f.run();
+ d.completeNull();
+ }
+ } catch (Throwable ex) {
+ d.completeThrowable(ex);
+ }
+ }
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
- final boolean uniRun(CompletableFuture> a, Runnable f, UniRun> c) {
- Object r; Throwable x;
- if (a == null || (r = a.result) == null || f == null)
- return false;
- if (result == null) {
- if (r instanceof AltResult && (x = ((AltResult)r).ex) != null)
- completeThrowable(x, r);
- else
- try {
- if (c != null && !c.claim())
- return false;
- f.run();
- completeNull();
- } catch (Throwable ex) {
- completeThrowable(ex);
- }
- }
- return true;
- }
-
private CompletableFuture uniRunStage(Executor e, Runnable f) {
if (f == null) throw new NullPointerException();
+ Object r;
+ if ((r = result) != null)
+ return uniRunNow(r, e, f);
CompletableFuture d = newIncompleteFuture();
- if (e != null || !d.uniRun(this, f, null)) {
- UniRun c = new UniRun(e, d, this, f);
- push(c);
- c.tryFire(SYNC);
- }
+ unipush(new UniRun(e, d, this, f));
+ return d;
+ }
+
+ private CompletableFuture uniRunNow(Object r, Executor e, Runnable f) {
+ Throwable x;
+ CompletableFuture d = newIncompleteFuture();
+ if (r instanceof AltResult && (x = ((AltResult)r).ex) != null)
+ d.result = encodeThrowable(x, r);
+ else
+ try {
+ if (e != null) {
+ e.execute(new UniRun(null, d, this, f));
+ } else {
+ f.run();
+ d.result = NIL;
+ }
+ } catch (Throwable ex) {
+ d.result = encodeThrowable(ex);
+ }
return d;
}
@@ -766,20 +828,20 @@ public class CompletableFuture implements Future, CompletionStage {
}
final CompletableFuture tryFire(int mode) {
CompletableFuture d; CompletableFuture a;
- if ((d = dep) == null ||
- !d.uniWhenComplete(a = src, fn, mode > 0 ? null : this))
+ Object r; BiConsumer super T, ? super Throwable> f;
+ if ((d = dep) == null || (f = fn) == null
+ || (a = src) == null || (r = a.result) == null
+ || !d.uniWhenComplete(r, f, mode > 0 ? null : this))
return null;
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
- final boolean uniWhenComplete(CompletableFuture a,
+ final boolean uniWhenComplete(Object r,
BiConsumer super T,? super Throwable> f,
UniWhenComplete c) {
- Object r; T t; Throwable x = null;
- if (a == null || (r = a.result) == null || f == null)
- return false;
+ T t; Throwable x = null;
if (result == null) {
try {
if (c != null && !c.claim())
@@ -811,10 +873,17 @@ public class CompletableFuture implements Future, CompletionStage {
Executor e, BiConsumer super T, ? super Throwable> f) {
if (f == null) throw new NullPointerException();
CompletableFuture d = newIncompleteFuture();
- if (e != null || !d.uniWhenComplete(this, f, null)) {
- UniWhenComplete c = new UniWhenComplete(e, d, this, f);
- push(c);
- c.tryFire(SYNC);
+ Object r;
+ if ((r = result) == null)
+ unipush(new UniWhenComplete(e, d, this, f));
+ else if (e == null)
+ d.uniWhenComplete(r, f, null);
+ else {
+ try {
+ e.execute(new UniWhenComplete(null, d, this, f));
+ } catch (Throwable ex) {
+ d.result = encodeThrowable(ex);
+ }
}
return d;
}
@@ -829,20 +898,20 @@ public class CompletableFuture implements Future, CompletionStage {
}
final CompletableFuture tryFire(int mode) {
CompletableFuture d; CompletableFuture a;
- if ((d = dep) == null ||
- !d.uniHandle(a = src, fn, mode > 0 ? null : this))
+ Object r; BiFunction super T, Throwable, ? extends V> f;
+ if ((d = dep) == null || (f = fn) == null
+ || (a = src) == null || (r = a.result) == null
+ || !d.uniHandle(r, f, mode > 0 ? null : this))
return null;
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
- final boolean uniHandle(CompletableFuture a,
+ final boolean uniHandle(Object r,
BiFunction super S, Throwable, ? extends T> f,
UniHandle c) {
- Object r; S s; Throwable x;
- if (a == null || (r = a.result) == null || f == null)
- return false;
+ S s; Throwable x;
if (result == null) {
try {
if (c != null && !c.claim())
@@ -867,10 +936,17 @@ public class CompletableFuture implements Future, CompletionStage {
Executor e, BiFunction super T, Throwable, ? extends V> f) {
if (f == null) throw new NullPointerException();
CompletableFuture d = newIncompleteFuture();
- if (e != null || !d.uniHandle(this, f, null)) {
- UniHandle c = new UniHandle(e, d, this, f);
- push(c);
- c.tryFire(SYNC);
+ Object r;
+ if ((r = result) == null)
+ unipush(new UniHandle(e, d, this, f));
+ else if (e == null)
+ d.uniHandle(r, f, null);
+ else {
+ try {
+ e.execute(new UniHandle(null, d, this, f));
+ } catch (Throwable ex) {
+ d.result = encodeThrowable(ex);
+ }
}
return d;
}
@@ -885,19 +961,20 @@ public class CompletableFuture implements Future, CompletionStage {
final CompletableFuture tryFire(int mode) { // never ASYNC
// assert mode != ASYNC;
CompletableFuture d; CompletableFuture a;
- if ((d = dep) == null || !d.uniExceptionally(a = src, fn, this))
+ Object r; Function super Throwable, ? extends T> f;
+ if ((d = dep) == null || (f = fn) == null
+ || (a = src) == null || (r = a.result) == null
+ || !d.uniExceptionally(r, f, this))
return null;
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
- final boolean uniExceptionally(CompletableFuture a,
+ final boolean uniExceptionally(Object r,
Function super Throwable, ? extends T> f,
UniExceptionally c) {
- Object r; Throwable x;
- if (a == null || (r = a.result) == null || f == null)
- return false;
+ Throwable x;
if (result == null) {
try {
if (r instanceof AltResult && (x = ((AltResult)r).ex) != null) {
@@ -917,47 +994,38 @@ public class CompletableFuture implements Future, CompletionStage {
Function f) {
if (f == null) throw new NullPointerException();
CompletableFuture d = newIncompleteFuture();
- if (!d.uniExceptionally(this, f, null)) {
- UniExceptionally c = new UniExceptionally(d, this, f);
- push(c);
- c.tryFire(SYNC);
- }
+ Object r;
+ if ((r = result) == null)
+ unipush(new UniExceptionally(d, this, f));
+ else
+ d.uniExceptionally(r, f, null);
return d;
}
@SuppressWarnings("serial")
- static final class UniRelay extends UniCompletion { // for Compose
+ static final class UniRelay extends UniCompletion {
UniRelay(CompletableFuture dep, CompletableFuture src) {
super(null, dep, src);
}
final CompletableFuture tryFire(int mode) {
- CompletableFuture d; CompletableFuture a;
- if ((d = dep) == null || !d.uniRelay(a = src))
+ CompletableFuture d; CompletableFuture a; Object r;
+ if ((d = dep) == null
+ || (a = src) == null || (r = a.result) == null)
return null;
+ if (d.result == null)
+ d.completeRelay(r);
src = null; dep = null;
return d.postFire(a, mode);
}
}
- final boolean uniRelay(CompletableFuture a) {
- Object r;
- if (a == null || (r = a.result) == null)
- return false;
- if (result == null) // no need to claim
- completeRelay(r);
- return true;
- }
-
private CompletableFuture uniCopyStage() {
Object r;
CompletableFuture d = newIncompleteFuture();
if ((r = result) != null)
- d.completeRelay(r);
- else {
- UniRelay c = new UniRelay(d, this);
- push(c);
- c.tryFire(SYNC);
- }
+ d.result = encodeRelay(r);
+ else
+ unipush(new UniRelay(d, this));
return d;
}
@@ -966,9 +1034,7 @@ public class CompletableFuture implements Future, CompletionStage {
if ((r = result) != null)
return new MinimalStage(encodeRelay(r));
MinimalStage d = new MinimalStage();
- UniRelay c = new UniRelay(d, this);
- push(c);
- c.tryFire(SYNC);
+ unipush(new UniRelay(d, this));
return d;
}
@@ -982,54 +1048,48 @@ public class CompletableFuture implements Future, CompletionStage {
}
final CompletableFuture tryFire(int mode) {
CompletableFuture d; CompletableFuture a;
- if ((d = dep) == null ||
- !d.uniCompose(a = src, fn, mode > 0 ? null : this))
+ Function super T, ? extends CompletionStage> f;
+ Object r; Throwable x;
+ if ((d = dep) == null || (f = fn) == null
+ || (a = src) == null || (r = a.result) == null)
return null;
+ tryComplete: if (d.result == null) {
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ d.completeThrowable(x, r);
+ break tryComplete;
+ }
+ r = null;
+ }
+ try {
+ if (mode <= 0 && !claim())
+ return null;
+ @SuppressWarnings("unchecked") T t = (T) r;
+ CompletableFuture g = f.apply(t).toCompletableFuture();
+ if ((r = g.result) != null)
+ d.completeRelay(r);
+ else {
+ g.unipush(new UniRelay(d, g));
+ if (d.result == null)
+ return null;
+ }
+ } catch (Throwable ex) {
+ d.completeThrowable(ex);
+ }
+ }
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
- final boolean uniCompose(
- CompletableFuture a,
- Function super S, ? extends CompletionStage> f,
- UniCompose c) {
- Object r; Throwable x;
- if (a == null || (r = a.result) == null || f == null)
- return false;
- tryComplete: if (result == null) {
- if (r instanceof AltResult) {
- if ((x = ((AltResult)r).ex) != null) {
- completeThrowable(x, r);
- break tryComplete;
- }
- r = null;
- }
- try {
- if (c != null && !c.claim())
- return false;
- @SuppressWarnings("unchecked") S s = (S) r;
- CompletableFuture g = f.apply(s).toCompletableFuture();
- if (g.result == null || !uniRelay(g)) {
- UniRelay copy = new UniRelay(this, g);
- g.push(copy);
- copy.tryFire(SYNC);
- if (result == null)
- return false;
- }
- } catch (Throwable ex) {
- completeThrowable(ex);
- }
- }
- return true;
- }
-
private CompletableFuture uniComposeStage(
Executor e, Function super T, ? extends CompletionStage> f) {
if (f == null) throw new NullPointerException();
- Object r, s; Throwable x;
CompletableFuture d = newIncompleteFuture();
- if (e == null && (r = result) != null) {
+ Object r, s; Throwable x;
+ if ((r = result) == null)
+ unipush(new UniCompose(e, d, this, f));
+ else if (e == null) {
if (r instanceof AltResult) {
if ((x = ((AltResult)r).ex) != null) {
d.result = encodeThrowable(x, r);
@@ -1041,21 +1101,20 @@ public class CompletableFuture implements Future, CompletionStage {
@SuppressWarnings("unchecked") T t = (T) r;
CompletableFuture g = f.apply(t).toCompletableFuture();
if ((s = g.result) != null)
- d.completeRelay(s);
+ d.result = encodeRelay(s);
else {
- UniRelay c = new UniRelay(d, g);
- g.push(c);
- c.tryFire(SYNC);
+ g.unipush(new UniRelay(d, g));
}
- return d;
} catch (Throwable ex) {
d.result = encodeThrowable(ex);
- return d;
}
}
- UniCompose c = new UniCompose(e, d, this, f);
- push(c);
- c.tryFire(SYNC);
+ else
+ try {
+ e.execute(new UniCompose(null, d, this, f));
+ } catch (Throwable ex) {
+ d.result = encodeThrowable(ex);
+ }
return d;
}
@@ -1085,21 +1144,28 @@ public class CompletableFuture implements Future, CompletionStage {
}
final boolean isLive() {
BiCompletion,?,?> c;
- return (c = base) != null && c.dep != null;
+ return (c = base) != null
+ // && c.isLive()
+ && c.dep != null;
}
}
- /** Pushes completion to this and b unless both done. */
+ /**
+ * Pushes completion to this and b unless both done.
+ * Caller should first check that either result or b.result is null.
+ */
final void bipush(CompletableFuture> b, BiCompletion,?,?> c) {
if (c != null) {
- Object r;
- while ((r = result) == null && !tryPushStack(c))
- lazySetNext(c, null); // clear on failure
- if (b != null && b != this && b.result == null) {
- Completion q = (r != null) ? c : new CoCompletion(c);
- while (b.result == null && !b.tryPushStack(q))
- lazySetNext(q, null); // clear on failure
+ while (result == null) {
+ if (tryPushStack(c)) {
+ if (b.result == null)
+ b.unipush(new CoCompletion(c));
+ else if (result != null)
+ c.tryFire(SYNC);
+ return;
+ }
}
+ b.unipush(c);
}
}
@@ -1107,9 +1173,10 @@ public class CompletableFuture implements Future, CompletionStage {
final CompletableFuture postFire(CompletableFuture> a,
CompletableFuture> b, int mode) {
if (b != null && b.stack != null) { // clean second source
- if (b.result == null)
+ Object r;
+ if ((r = b.result) == null)
b.cleanStack();
- else if (mode >= 0)
+ if (mode >= 0 && (r != null || b.result != null))
b.postComplete();
}
return postFire(a, mode);
@@ -1127,22 +1194,21 @@ public class CompletableFuture implements Future, CompletionStage {
CompletableFuture d;
CompletableFuture a;
CompletableFuture b;
- if ((d = dep) == null ||
- !d.biApply(a = src, b = snd, fn, mode > 0 ? null : this))
+ Object r, s; BiFunction super T,? super U,? extends V> f;
+ if ((d = dep) == null || (f = fn) == null
+ || (a = src) == null || (r = a.result) == null
+ || (b = snd) == null || (s = b.result) == null
+ || !d.biApply(r, s, f, mode > 0 ? null : this))
return null;
dep = null; src = null; snd = null; fn = null;
return d.postFire(a, b, mode);
}
}
- final boolean biApply(CompletableFuture a,
- CompletableFuture b,
+ final boolean biApply(Object r, Object s,
BiFunction super R,? super S,? extends T> f,
BiApply c) {
- Object r, s; Throwable x;
- if (a == null || (r = a.result) == null ||
- b == null || (s = b.result) == null || f == null)
- return false;
+ Throwable x;
tryComplete: if (result == null) {
if (r instanceof AltResult) {
if ((x = ((AltResult)r).ex) != null) {
@@ -1174,15 +1240,20 @@ public class CompletableFuture implements Future, CompletionStage {
private CompletableFuture biApplyStage(
Executor e, CompletionStage o,
BiFunction super T,? super U,? extends V> f) {
- CompletableFuture b;
+ CompletableFuture b; Object r, s;
if (f == null || (b = o.toCompletableFuture()) == null)
throw new NullPointerException();
CompletableFuture d = newIncompleteFuture();
- if (e != null || !d.biApply(this, b, f, null)) {
- BiApply c = new BiApply(e, d, this, b, f);
- bipush(b, c);
- c.tryFire(SYNC);
- }
+ if ((r = result) == null || (s = b.result) == null)
+ bipush(b, new BiApply(e, d, this, b, f));
+ else if (e == null)
+ d.biApply(r, s, f, null);
+ else
+ try {
+ e.execute(new BiApply