# HG changeset patch # User amurillo # Date 1468859888 25200 # Node ID f5d65fcf55e4f5cc24d3a749e4d6ef40dbd11aca # Parent 8bc25e077e83ca9cee383771ddcb4f109ea696fb# Parent 196f4e25d9f512cfa4c6a1fad9f68dedc2ef3326 Merge diff -r 8bc25e077e83 -r f5d65fcf55e4 .hgtags --- a/.hgtags Fri Jul 15 09:05:36 2016 -0700 +++ b/.hgtags Mon Jul 18 09:38:08 2016 -0700 @@ -369,3 +369,4 @@ 2a5697a98620c4f40e4a1a71478464399b8878de jdk-9+124 3aa52182b3ad7c5b3a61cf05a59dd07e4c5884e5 jdk-9+125 03e7b2c5ae345be3caf981d76ceb3efe5ff447f8 jdk-9+126 +8e45018bde9de4ad15b972ae62874bba52dba2d5 jdk-9+127 diff -r 8bc25e077e83 -r f5d65fcf55e4 .hgtags-top-repo --- a/.hgtags-top-repo Fri Jul 15 09:05:36 2016 -0700 +++ b/.hgtags-top-repo Mon Jul 18 09:38:08 2016 -0700 @@ -369,3 +369,4 @@ f80c841ae2545eaf9acd2724bccc305d98cefbe2 jdk-9+124 9aa7d40f3a453f51e47f4c1b19eff5740a74a9f8 jdk-9+125 3a58466296d36944454756ef01e7513ac5e14a16 jdk-9+126 +8fa686245bd2a072ece3392743460030f0854520 jdk-9+127 diff -r 8bc25e077e83 -r f5d65fcf55e4 corba/.hgtags --- a/corba/.hgtags Fri Jul 15 09:05:36 2016 -0700 +++ b/corba/.hgtags Mon Jul 18 09:38:08 2016 -0700 @@ -369,3 +369,4 @@ 45121d5afb9d5bfadab75378572ad96832e0809e jdk-9+124 1d48e67d1b91eb9f72e49e69a4021edb85e357fc jdk-9+125 c7f5ba08fcd4b8416e62c21229f9a07c95498919 jdk-9+126 +8fab452b6f4710762ba1d8e55fd62db00b1355fe jdk-9+127 diff -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/.hgtags --- a/hotspot/.hgtags Fri Jul 15 09:05:36 2016 -0700 +++ b/hotspot/.hgtags Mon Jul 18 09:38:08 2016 -0700 @@ -529,3 +529,4 @@ 479631362b4930be985245ea063d87d821a472eb jdk-9+124 bb640b49741af3f57f9994129934c46fc173219f jdk-9+125 adc8c84b7cf8c540d920182f78a2bc982366432a jdk-9+126 +352357128f602dcf0426b1cbe011a4685a4d9f97 jdk-9+127 diff -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/make/gensrc/GensrcDtrace.gmk --- a/hotspot/make/gensrc/GensrcDtrace.gmk Fri Jul 15 09:05:36 2016 -0700 +++ b/hotspot/make/gensrc/GensrcDtrace.gmk Mon Jul 18 09:38:08 2016 -0700 @@ -45,7 +45,8 @@ $(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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/make/lib/CompileDtracePostJvm.gmk --- a/hotspot/make/lib/CompileDtracePostJvm.gmk Fri Jul 15 09:05:36 2016 -0700 +++ b/hotspot/make/lib/CompileDtracePostJvm.gmk Mon Jul 18 09:38:08 2016 -0700 @@ -68,7 +68,7 @@ $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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/UncommonTrap.java --- a/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/UncommonTrap.java Fri Jul 15 09:05:36 2016 -0700 +++ b/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/UncommonTrap.java Mon Jul 18 09:38:08 2016 -0700 @@ -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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/src/share/vm/classfile/classLoaderData.cpp --- a/hotspot/src/share/vm/classfile/classLoaderData.cpp Fri Jul 15 09:05:36 2016 -0700 +++ b/hotspot/src/share/vm/classfile/classLoaderData.cpp Mon Jul 18 09:38:08 2016 -0700 @@ -501,13 +501,26 @@ } } -/** - * 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 @@ 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 @@ } 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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/src/share/vm/classfile/classLoaderData.hpp --- a/hotspot/src/share/vm/classfile/classLoaderData.hpp Fri Jul 15 09:05:36 2016 -0700 +++ b/hotspot/src/share/vm/classfile/classLoaderData.hpp Mon Jul 18 09:38:08 2016 -0700 @@ -270,7 +270,9 @@ 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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/src/share/vm/classfile/compactHashtable.cpp --- a/hotspot/src/share/vm/classfile/compactHashtable.cpp Fri Jul 15 09:05:36 2016 -0700 +++ b/hotspot/src/share/vm/classfile/compactHashtable.cpp Mon Jul 18 09:38:08 2016 -0700 @@ -248,7 +248,7 @@ } 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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/src/share/vm/classfile/moduleEntry.cpp --- a/hotspot/src/share/vm/classfile/moduleEntry.cpp Fri Jul 15 09:05:36 2016 -0700 +++ b/hotspot/src/share/vm/classfile/moduleEntry.cpp Mon Jul 18 09:38:08 2016 -0700 @@ -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 @@ // 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 @@ // 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 @@ 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 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 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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/src/share/vm/classfile/moduleEntry.hpp --- a/hotspot/src/share/vm/classfile/moduleEntry.hpp Fri Jul 15 09:05:36 2016 -0700 +++ b/hotspot/src/share/vm/classfile/moduleEntry.hpp Mon Jul 18 09:38:08 2016 -0700 @@ -43,6 +43,7 @@ // 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 @@ 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 @@ 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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/src/share/vm/classfile/modules.cpp --- a/hotspot/src/share/vm/classfile/modules.cpp Fri Jul 15 09:05:36 2016 -0700 +++ b/hotspot/src/share/vm/classfile/modules.cpp Mon Jul 18 09:38:08 2016 -0700 @@ -113,7 +113,7 @@ 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 @@ 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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/src/share/vm/classfile/packageEntry.cpp --- a/hotspot/src/share/vm/classfile/packageEntry.cpp Fri Jul 15 09:05:36 2016 -0700 +++ b/hotspot/src/share/vm/classfile/packageEntry.cpp Mon Jul 18 09:38:08 2016 -0700 @@ -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 @@ 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 @@ // 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 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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/src/share/vm/classfile/packageEntry.hpp --- a/hotspot/src/share/vm/classfile/packageEntry.hpp Fri Jul 15 09:05:36 2016 -0700 +++ b/hotspot/src/share/vm/classfile/packageEntry.hpp Mon Jul 18 09:38:08 2016 -0700 @@ -69,6 +69,7 @@ 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 @@ _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 @@ // 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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/src/share/vm/classfile/systemDictionary.cpp --- a/hotspot/src/share/vm/classfile/systemDictionary.cpp Fri Jul 15 09:05:36 2016 -0700 +++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp Mon Jul 18 09:38:08 2016 -0700 @@ -175,9 +175,18 @@ 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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/src/share/vm/classfile/systemDictionary.hpp --- a/hotspot/src/share/vm/classfile/systemDictionary.hpp Fri Jul 15 09:05:36 2016 -0700 +++ b/hotspot/src/share/vm/classfile/systemDictionary.hpp Mon Jul 18 09:38:08 2016 -0700 @@ -660,6 +660,7 @@ 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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/src/share/vm/gc/shared/preservedMarks.cpp --- a/hotspot/src/share/vm/gc/shared/preservedMarks.cpp Fri Jul 15 09:05:36 2016 -0700 +++ b/hotspot/src/share/vm/gc/shared/preservedMarks.cpp Mon Jul 18 09:38:08 2016 -0700 @@ -48,10 +48,10 @@ #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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/src/share/vm/interpreter/rewriter.cpp --- a/hotspot/src/share/vm/interpreter/rewriter.cpp Fri Jul 15 09:05:36 2016 -0700 +++ b/hotspot/src/share/vm/interpreter/rewriter.cpp Mon Jul 18 09:38:08 2016 -0700 @@ -419,21 +419,20 @@ 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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/src/share/vm/runtime/arguments.cpp --- a/hotspot/src/share/vm/runtime/arguments.cpp Fri Jul 15 09:05:36 2016 -0700 +++ b/hotspot/src/share/vm/runtime/arguments.cpp Mon Jul 18 09:38:08 2016 -0700 @@ -584,27 +584,26 @@ // 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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/src/share/vm/utilities/vmError.cpp --- a/hotspot/src/share/vm/utilities/vmError.cpp Fri Jul 15 09:05:36 2016 -0700 +++ b/hotspot/src/share/vm/utilities/vmError.cpp Mon Jul 18 09:38:08 2016 -0700 @@ -205,16 +205,39 @@ 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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/test/TEST.groups --- a/hotspot/test/TEST.groups Fri Jul 15 09:05:36 2016 -0700 +++ b/hotspot/test/TEST.groups Mon Jul 18 09:38:08 2016 -0700 @@ -395,6 +395,17 @@ :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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/test/gc/g1/humongousObjects/TestNoAllocationsInHRegions.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/gc/g1/humongousObjects/TestNoAllocationsInHRegions.java Mon Jul 18 09:38:08 2016 -0700 @@ -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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/test/gc/g1/humongousObjects/objectGraphTest/GC.java --- a/hotspot/test/gc/g1/humongousObjects/objectGraphTest/GC.java Fri Jul 15 09:05:36 2016 -0700 +++ b/hotspot/test/gc/g1/humongousObjects/objectGraphTest/GC.java Mon Jul 18 09:38:08 2016 -0700 @@ -138,6 +138,41 @@ } }, + 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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/test/gc/g1/humongousObjects/objectGraphTest/README --- a/hotspot/test/gc/g1/humongousObjects/objectGraphTest/README Fri Jul 15 09:05:36 2016 -0700 +++ b/hotspot/test/gc/g1/humongousObjects/objectGraphTest/README Mon Jul 18 09:38:08 2016 -0700 @@ -38,6 +38,9 @@ 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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/test/gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java --- a/hotspot/test/gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java Fri Jul 15 09:05:36 2016 -0700 +++ b/hotspot/test/gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java Mon Jul 18 09:38:08 2016 -0700 @@ -66,6 +66,12 @@ * 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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/test/native/runtime/test_arguments.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/native/runtime/test_arguments.cpp Mon Jul 18 09:38:08 2016 -0700 @@ -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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/test/runtime/Final/Bad.jasm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/Final/Bad.jasm Mon Jul 18 09:38:08 2016 -0700 @@ -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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/test/runtime/Final/PutfieldError.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/Final/PutfieldError.java Mon Jul 18 09:38:08 2016 -0700 @@ -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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/test/runtime/SharedArchiveFile/SharedStringsDedup.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/SharedArchiveFile/SharedStringsDedup.java Mon Jul 18 09:38:08 2016 -0700 @@ -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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/test/runtime/modules/ModuleStress/CustomSystemClassLoader.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/modules/ModuleStress/CustomSystemClassLoader.java Mon Jul 18 09:38:08 2016 -0700 @@ -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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/test/runtime/modules/ModuleStress/ModuleNonBuiltinCLMain.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/modules/ModuleStress/ModuleNonBuiltinCLMain.java Mon Jul 18 09:38:08 2016 -0700 @@ -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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/test/runtime/modules/ModuleStress/ModuleSameCLMain.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/modules/ModuleStress/ModuleSameCLMain.java Mon Jul 18 09:38:08 2016 -0700 @@ -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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/test/runtime/modules/ModuleStress/ModuleStress.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/modules/ModuleStress/ModuleStress.java Mon Jul 18 09:38:08 2016 -0700 @@ -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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/test/runtime/modules/ModuleStress/ModuleStressGC.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/modules/ModuleStress/ModuleStressGC.java Mon Jul 18 09:38:08 2016 -0700 @@ -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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/test/runtime/modules/ModuleStress/src/jdk.test/test/MainGC.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/modules/ModuleStress/src/jdk.test/test/MainGC.java Mon Jul 18 09:38:08 2016 -0700 @@ -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 -r 8bc25e077e83 -r f5d65fcf55e4 hotspot/test/runtime/modules/ModuleStress/src/jdk.translet/translet/MainGC.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/runtime/modules/ModuleStress/src/jdk.translet/translet/MainGC.java Mon Jul 18 09:38:08 2016 -0700 @@ -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 -r 8bc25e077e83 -r f5d65fcf55e4 jaxp/.hgtags --- a/jaxp/.hgtags Fri Jul 15 09:05:36 2016 -0700 +++ b/jaxp/.hgtags Mon Jul 18 09:38:08 2016 -0700 @@ -369,3 +369,4 @@ e04a15153cc293f05fcd60bc98236f50e16af46a jdk-9+124 493eb91ec32a6dea7604cfbd86c10045ad9af15b jdk-9+125 15722f71281f034bc696d8b96136da2ef34da44f jdk-9+126 +bdc3c0b737efbf899709eb3121ce760dcfb51151 jdk-9+127 diff -r 8bc25e077e83 -r f5d65fcf55e4 jaxws/.hgtags --- a/jaxws/.hgtags Fri Jul 15 09:05:36 2016 -0700 +++ b/jaxws/.hgtags Mon Jul 18 09:38:08 2016 -0700 @@ -372,3 +372,4 @@ 1600da1665cd2cc127014e8c002b328ec33a9147 jdk-9+124 5b0570e3db29f6b8c80a4beac70d51284507b203 jdk-9+125 264a44128cd6286e598d5a849ceeb613c06269d0 jdk-9+126 +06d706c70634775418dc79a2671780ba1c624fd2 jdk-9+127 diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/com/sun/security/ntlm/NTLM.java --- a/jdk/src/java.base/share/classes/com/sun/security/ntlm/NTLM.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/com/sun/security/ntlm/NTLM.java Mon Jul 18 09:38:08 2016 -0700 @@ -169,7 +169,7 @@ 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 -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/lang/Math.java --- a/jdk/src/java.base/share/classes/java/lang/Math.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/Math.java Mon Jul 18 09:38:08 2016 -0700 @@ -1613,6 +1613,8 @@ * @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 @@ * @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 -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/lang/StrictMath.java --- a/jdk/src/java.base/share/classes/java/lang/StrictMath.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/StrictMath.java Mon Jul 18 09:38:08 2016 -0700 @@ -1276,6 +1276,8 @@ * @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 @@ * @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 -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/lang/module/package-info.java --- a/jdk/src/java.base/share/classes/java/lang/module/package-info.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/lang/module/package-info.java Mon Jul 18 09:38:08 2016 -0700 @@ -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 -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/nio/file/Files.java --- a/jdk/src/java.base/share/classes/java/nio/file/Files.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/nio/file/Files.java Mon Jul 18 09:38:08 2016 -0700 @@ -3290,8 +3290,8 @@ * 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 @@ * 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 -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/Queue.java --- a/jdk/src/java.base/share/classes/java/util/Queue.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/Queue.java Mon Jul 18 09:38:08 2016 -0700 @@ -124,7 +124,6 @@ * always well-defined for queues with the same elements but different * ordering properties. * - * *

This interface is a member of the * * Java Collections Framework. diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java Mon Jul 18 09:38:08 2016 -0700 @@ -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 @@ * 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 @@ * 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 @@ * 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 @@ * 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 @@ /** 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 @@ /** 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 @@ /** 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 @@ * 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 @@ */ 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 @@ * 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 @@ 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 @@ throw (CompletionException)x; throw new CompletionException(x); } - @SuppressWarnings("unchecked") T t = (T) r; - return t; + return r; } /* ------------- Async task preliminaries -------------- */ @@ -449,12 +454,6 @@ 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 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 @@ 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 @@ 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 @@ */ 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 @@ } 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 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 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 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 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 @@ } 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 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 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 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 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 @@ } 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(); + unipush(new UniRun(e, d, this, f)); + return d; } - private CompletableFuture uniRunStage(Executor e, Runnable f) { - if (f == null) throw new NullPointerException(); + private CompletableFuture uniRunNow(Object r, Executor e, Runnable f) { + Throwable x; CompletableFuture d = newIncompleteFuture(); - if (e != null || !d.uniRun(this, f, null)) { - UniRun c = new UniRun(e, d, this, f); - push(c); - c.tryFire(SYNC); - } + 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 @@ } 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 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 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 @@ Executor e, BiConsumer 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 @@ } 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 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 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 @@ Executor e, BiFunction 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 @@ 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 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 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 @@ 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 @@ 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 @@ } final CompletableFuture tryFire(int mode) { CompletableFuture d; CompletableFuture a; - if ((d = dep) == null || - !d.uniCompose(a = src, fn, mode > 0 ? null : this)) + Function> 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> 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> f) { if (f == null) throw new NullPointerException(); + CompletableFuture d = newIncompleteFuture(); Object r, s; Throwable x; - CompletableFuture d = newIncompleteFuture(); - if (e == null && (r = result) != null) { + 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 @@ @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 @@ } 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 @@ 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 @@ 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 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 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 @@ private CompletableFuture biApplyStage( Executor e, CompletionStage o, BiFunction 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(null, d, this, b, f)); + } catch (Throwable ex) { + d.result = encodeThrowable(ex); + } return d; } @@ -1198,22 +1269,21 @@ CompletableFuture d; CompletableFuture a; CompletableFuture b; - if ((d = dep) == null || - !d.biAccept(a = src, b = snd, fn, mode > 0 ? null : this)) + Object r, s; BiConsumer f; + if ((d = dep) == null || (f = fn) == null + || (a = src) == null || (r = a.result) == null + || (b = snd) == null || (s = b.result) == null + || !d.biAccept(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 biAccept(CompletableFuture a, - CompletableFuture b, + final boolean biAccept(Object r, Object s, BiConsumer f, BiAccept 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) { @@ -1246,15 +1316,20 @@ private CompletableFuture biAcceptStage( Executor e, CompletionStage o, BiConsumer 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.biAccept(this, b, f, null)) { - BiAccept c = new BiAccept(e, d, this, b, f); - bipush(b, c); - c.tryFire(SYNC); - } + if ((r = result) == null || (s = b.result) == null) + bipush(b, new BiAccept(e, d, this, b, f)); + else if (e == null) + d.biAccept(r, s, f, null); + else + try { + e.execute(new BiAccept(null, d, this, b, f)); + } catch (Throwable ex) { + d.result = encodeThrowable(ex); + } return d; } @@ -1262,8 +1337,7 @@ static final class BiRun extends BiCompletion { Runnable fn; BiRun(Executor executor, CompletableFuture dep, - CompletableFuture src, - CompletableFuture snd, + CompletableFuture src, CompletableFuture snd, Runnable fn) { super(executor, dep, src, snd); this.fn = fn; } @@ -1271,25 +1345,25 @@ CompletableFuture d; CompletableFuture a; CompletableFuture b; - if ((d = dep) == null || - !d.biRun(a = src, b = snd, fn, mode > 0 ? null : this)) + Object r, s; Runnable f; + if ((d = dep) == null || (f = fn) == null + || (a = src) == null || (r = a.result) == null + || (b = snd) == null || (s = b.result) == null + || !d.biRun(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 biRun(CompletableFuture a, CompletableFuture b, - Runnable f, BiRun c) { - Object r, s; Throwable x; - if (a == null || (r = a.result) == null || - b == null || (s = b.result) == null || f == null) - return false; + final boolean biRun(Object r, Object s, Runnable f, BiRun c) { + Throwable x; Object z; if (result == null) { - if (r instanceof AltResult && (x = ((AltResult)r).ex) != null) - completeThrowable(x, r); - else if (s instanceof AltResult && (x = ((AltResult)s).ex) != null) - completeThrowable(x, s); + if ((r instanceof AltResult + && (x = ((AltResult)(z = r)).ex) != null) || + (s instanceof AltResult + && (x = ((AltResult)(z = s)).ex) != null)) + completeThrowable(x, z); else try { if (c != null && !c.claim()) @@ -1305,52 +1379,52 @@ private CompletableFuture biRunStage(Executor e, CompletionStage o, Runnable 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.biRun(this, b, f, null)) { - BiRun c = new BiRun<>(e, d, this, b, f); - bipush(b, c); - c.tryFire(SYNC); - } + if ((r = result) == null || (s = b.result) == null) + bipush(b, new BiRun<>(e, d, this, b, f)); + else if (e == null) + d.biRun(r, s, f, null); + else + try { + e.execute(new BiRun<>(null, d, this, b, f)); + } catch (Throwable ex) { + d.result = encodeThrowable(ex); + } return d; } @SuppressWarnings("serial") static final class BiRelay extends BiCompletion { // for And BiRelay(CompletableFuture dep, - CompletableFuture src, - CompletableFuture snd) { + CompletableFuture src, CompletableFuture snd) { super(null, dep, src, snd); } final CompletableFuture tryFire(int mode) { CompletableFuture d; CompletableFuture a; CompletableFuture b; - if ((d = dep) == null || !d.biRelay(a = src, b = snd)) + Object r, s, z; Throwable x; + if ((d = dep) == null + || (a = src) == null || (r = a.result) == null + || (b = snd) == null || (s = b.result) == null) return null; + if (d.result == null) { + if ((r instanceof AltResult + && (x = ((AltResult)(z = r)).ex) != null) || + (s instanceof AltResult + && (x = ((AltResult)(z = s)).ex) != null)) + d.completeThrowable(x, z); + else + d.completeNull(); + } src = null; snd = null; dep = null; return d.postFire(a, b, mode); } } - boolean biRelay(CompletableFuture a, CompletableFuture b) { - Object r, s; Throwable x; - if (a == null || (r = a.result) == null || - b == null || (s = b.result) == null) - return false; - if (result == null) { - if (r instanceof AltResult && (x = ((AltResult)r).ex) != null) - completeThrowable(x, r); - else if (s instanceof AltResult && (x = ((AltResult)s).ex) != null) - completeThrowable(x, s); - else - completeNull(); - } - return true; - } - /** Recursively constructs a tree of completions. */ static CompletableFuture andTree(CompletableFuture[] cfs, int lo, int hi) { @@ -1358,39 +1432,44 @@ if (lo > hi) // empty d.result = NIL; else { - CompletableFuture a, b; + CompletableFuture a, b; Object r, s, z; Throwable x; int mid = (lo + hi) >>> 1; if ((a = (lo == mid ? cfs[lo] : andTree(cfs, lo, mid))) == null || (b = (lo == hi ? a : (hi == mid+1) ? cfs[hi] : andTree(cfs, mid+1, hi))) == null) throw new NullPointerException(); - if (!d.biRelay(a, b)) { - BiRelay c = new BiRelay<>(d, a, b); - a.bipush(b, c); - c.tryFire(SYNC); - } + if ((r = a.result) == null || (s = b.result) == null) + a.bipush(b, new BiRelay<>(d, a, b)); + else if ((r instanceof AltResult + && (x = ((AltResult)(z = r)).ex) != null) || + (s instanceof AltResult + && (x = ((AltResult)(z = s)).ex) != null)) + d.result = encodeThrowable(x, z); + else + d.result = NIL; } return d; } /* ------------- Projected (Ored) BiCompletions -------------- */ - /** Pushes completion to this and b unless either done. */ + /** + * Pushes completion to this and b unless either done. + * Caller should first check that result and b.result are both null. + */ final void orpush(CompletableFuture b, BiCompletion c) { if (c != null) { - while ((b == null || b.result == null) && result == null) { - if (tryPushStack(c)) { - if (b != null && b != this && b.result == null) { - Completion q = new CoCompletion(c); - while (result == null && b.result == null && - !b.tryPushStack(q)) - lazySetNext(q, null); // clear on failure - } + while (!tryPushStack(c)) { + if (result != null) { + NEXT.set(c, null); break; } - lazySetNext(c, null); // clear on failure } + if (result != null) + c.tryFire(SYNC); + else + b.unipush(new CoCompletion(c)); } } @@ -1398,8 +1477,7 @@ static final class OrApply extends BiCompletion { Function fn; OrApply(Executor executor, CompletableFuture dep, - CompletableFuture src, - CompletableFuture snd, + CompletableFuture src, CompletableFuture snd, Function fn) { super(executor, dep, src, snd); this.fn = fn; } @@ -1407,54 +1485,46 @@ CompletableFuture d; CompletableFuture a; CompletableFuture b; - if ((d = dep) == null || - !d.orApply(a = src, b = snd, fn, mode > 0 ? null : this)) + Object r; Throwable x; Function f; + if ((d = dep) == null || (f = fn) == null + || (a = src) == null || (b = snd) == null + || ((r = a.result) == null && (r = b.result) == null)) return null; + tryComplete: if (d.result == null) { + try { + if (mode <= 0 && !claim()) + return null; + if (r instanceof AltResult) { + if ((x = ((AltResult)r).ex) != null) { + d.completeThrowable(x, r); + break tryComplete; + } + r = null; + } + @SuppressWarnings("unchecked") T t = (T) r; + d.completeValue(f.apply(t)); + } catch (Throwable ex) { + d.completeThrowable(ex); + } + } dep = null; src = null; snd = null; fn = null; return d.postFire(a, b, mode); } } - final boolean orApply(CompletableFuture a, - CompletableFuture b, - Function f, - OrApply c) { - Object r; Throwable x; - if (a == null || b == null || - ((r = a.result) == null && (r = b.result) == null) || f == null) - return false; - tryComplete: if (result == null) { - try { - if (c != null && !c.claim()) - return false; - if (r instanceof AltResult) { - if ((x = ((AltResult)r).ex) != null) { - completeThrowable(x, r); - break tryComplete; - } - r = null; - } - @SuppressWarnings("unchecked") R rr = (R) r; - completeValue(f.apply(rr)); - } catch (Throwable ex) { - completeThrowable(ex); - } - } - return true; - } - private CompletableFuture orApplyStage( - Executor e, CompletionStage o, - Function f) { + Executor e, CompletionStage o, Function f) { CompletableFuture b; if (f == null || (b = o.toCompletableFuture()) == null) throw new NullPointerException(); + + Object r; CompletableFuture z; + if ((r = (z = this).result) != null || + (r = (z = b).result) != null) + return z.uniApplyNow(r, e, f); + CompletableFuture d = newIncompleteFuture(); - if (e != null || !d.orApply(this, b, f, null)) { - OrApply c = new OrApply(e, d, this, b, f); - orpush(b, c); - c.tryFire(SYNC); - } + orpush(b, new OrApply(e, d, this, b, f)); return d; } @@ -1462,8 +1532,7 @@ static final class OrAccept extends BiCompletion { Consumer fn; OrAccept(Executor executor, CompletableFuture dep, - CompletableFuture src, - CompletableFuture snd, + CompletableFuture src, CompletableFuture snd, Consumer fn) { super(executor, dep, src, snd); this.fn = fn; } @@ -1471,54 +1540,47 @@ CompletableFuture d; CompletableFuture a; CompletableFuture b; - if ((d = dep) == null || - !d.orAccept(a = src, b = snd, fn, mode > 0 ? null : this)) + Object r; Throwable x; Consumer f; + if ((d = dep) == null || (f = fn) == null + || (a = src) == null || (b = snd) == null + || ((r = a.result) == null && (r = b.result) == null)) return null; + tryComplete: if (d.result == null) { + try { + if (mode <= 0 && !claim()) + return null; + if (r instanceof AltResult) { + if ((x = ((AltResult)r).ex) != null) { + d.completeThrowable(x, r); + break tryComplete; + } + r = null; + } + @SuppressWarnings("unchecked") T t = (T) r; + f.accept(t); + d.completeNull(); + } catch (Throwable ex) { + d.completeThrowable(ex); + } + } dep = null; src = null; snd = null; fn = null; return d.postFire(a, b, mode); } } - final boolean orAccept(CompletableFuture a, - CompletableFuture b, - Consumer f, - OrAccept c) { - Object r; Throwable x; - if (a == null || b == null || - ((r = a.result) == null && (r = b.result) == null) || f == null) - return false; - tryComplete: if (result == null) { - try { - if (c != null && !c.claim()) - return false; - if (r instanceof AltResult) { - if ((x = ((AltResult)r).ex) != null) { - completeThrowable(x, r); - break tryComplete; - } - r = null; - } - @SuppressWarnings("unchecked") R rr = (R) r; - f.accept(rr); - completeNull(); - } catch (Throwable ex) { - completeThrowable(ex); - } - } - return true; - } - private CompletableFuture orAcceptStage( Executor e, CompletionStage o, Consumer f) { CompletableFuture b; if (f == null || (b = o.toCompletableFuture()) == null) throw new NullPointerException(); + + Object r; CompletableFuture z; + if ((r = (z = this).result) != null || + (r = (z = b).result) != null) + return z.uniAcceptNow(r, e, f); + CompletableFuture d = newIncompleteFuture(); - if (e != null || !d.orAccept(this, b, f, null)) { - OrAccept c = new OrAccept(e, d, this, b, f); - orpush(b, c); - c.tryFire(SYNC); - } + orpush(b, new OrAccept(e, d, this, b, f)); return d; } @@ -1526,8 +1588,7 @@ static final class OrRun extends BiCompletion { Runnable fn; OrRun(Executor executor, CompletableFuture dep, - CompletableFuture src, - CompletableFuture snd, + CompletableFuture src, CompletableFuture snd, Runnable fn) { super(executor, dep, src, snd); this.fn = fn; } @@ -1535,95 +1596,84 @@ CompletableFuture d; CompletableFuture a; CompletableFuture b; - if ((d = dep) == null || - !d.orRun(a = src, b = snd, fn, mode > 0 ? null : this)) + Object r; Throwable x; Runnable f; + if ((d = dep) == null || (f = fn) == null + || (a = src) == null || (b = snd) == null + || ((r = a.result) == null && (r = b.result) == null)) return null; + if (d.result == null) { + try { + if (mode <= 0 && !claim()) + return null; + else if (r instanceof AltResult + && (x = ((AltResult)r).ex) != null) + d.completeThrowable(x, r); + else { + f.run(); + d.completeNull(); + } + } catch (Throwable ex) { + d.completeThrowable(ex); + } + } dep = null; src = null; snd = null; fn = null; return d.postFire(a, b, mode); } } - final boolean orRun(CompletableFuture a, CompletableFuture b, - Runnable f, OrRun c) { - Object r; Throwable x; - if (a == null || b == null || - ((r = a.result) == null && (r = b.result) == null) || f == null) - return false; - if (result == null) { - try { - if (c != null && !c.claim()) - return false; - if (r instanceof AltResult && (x = ((AltResult)r).ex) != null) - completeThrowable(x, r); - else { - f.run(); - completeNull(); - } - } catch (Throwable ex) { - completeThrowable(ex); - } - } - return true; - } - private CompletableFuture orRunStage(Executor e, CompletionStage o, Runnable f) { CompletableFuture b; if (f == null || (b = o.toCompletableFuture()) == null) throw new NullPointerException(); + + Object r; CompletableFuture z; + if ((r = (z = this).result) != null || + (r = (z = b).result) != null) + return z.uniRunNow(r, e, f); + CompletableFuture d = newIncompleteFuture(); - if (e != null || !d.orRun(this, b, f, null)) { - OrRun c = new OrRun<>(e, d, this, b, f); - orpush(b, c); - c.tryFire(SYNC); - } + orpush(b, new OrRun<>(e, d, this, b, f)); return d; } @SuppressWarnings("serial") static final class OrRelay extends BiCompletion { // for Or - OrRelay(CompletableFuture dep, CompletableFuture src, - CompletableFuture snd) { + OrRelay(CompletableFuture dep, + CompletableFuture src, CompletableFuture snd) { super(null, dep, src, snd); } final CompletableFuture tryFire(int mode) { CompletableFuture d; CompletableFuture a; CompletableFuture b; - if ((d = dep) == null || !d.orRelay(a = src, b = snd)) + Object r; + if ((d = dep) == null + || (a = src) == null || (b = snd) == null + || ((r = a.result) == null && (r = b.result) == null)) return null; + d.completeRelay(r); src = null; snd = null; dep = null; return d.postFire(a, b, mode); } } - final boolean orRelay(CompletableFuture a, CompletableFuture b) { - Object r; - if (a == null || b == null || - ((r = a.result) == null && (r = b.result) == null)) - return false; - if (result == null) - completeRelay(r); - return true; - } - /** Recursively constructs a tree of completions. */ static CompletableFuture orTree(CompletableFuture[] cfs, int lo, int hi) { CompletableFuture d = new CompletableFuture(); if (lo <= hi) { - CompletableFuture a, b; + CompletableFuture a, b; Object r; int mid = (lo + hi) >>> 1; if ((a = (lo == mid ? cfs[lo] : orTree(cfs, lo, mid))) == null || (b = (lo == hi ? a : (hi == mid+1) ? cfs[hi] : orTree(cfs, mid+1, hi))) == null) throw new NullPointerException(); - if (!d.orRelay(a, b)) { - OrRelay c = new OrRelay<>(d, a, b); - a.orpush(b, c); - c.tryFire(SYNC); - } + if ((r = a.result) != null && (r = b.result) != null) + d.result = encodeRelay(r); + else + a.orpush(b, new OrRelay<>(d, a, b)); } return d; } @@ -1640,7 +1690,7 @@ public final Void getRawResult() { return null; } public final void setRawResult(Void v) {} - public final boolean exec() { run(); return true; } + public final boolean exec() { run(); return false; } public void run() { CompletableFuture d; Supplier f; @@ -1676,7 +1726,7 @@ public final Void getRawResult() { return null; } public final void setRawResult(Void v) {} - public final boolean exec() { run(); return true; } + public final boolean exec() { run(); return false; } public void run() { CompletableFuture d; Runnable f; @@ -1760,15 +1810,13 @@ private Object waitingGet(boolean interruptible) { Signaller q = null; boolean queued = false; - int spins = SPINS; Object r; while ((r = result) == null) { - if (spins > 0) { - if (ThreadLocalRandom.nextSecondarySeed() >= 0) - --spins; + if (q == null) { + q = new Signaller(interruptible, 0L, 0L); + if (Thread.currentThread() instanceof ForkJoinWorkerThread) + ForkJoinPool.helpAsyncBlocker(defaultExecutor(), q); } - else if (q == null) - q = new Signaller(interruptible, 0L, 0L); else if (!queued) queued = tryPushStack(q); else { @@ -1781,16 +1829,14 @@ break; } } - if (q != null) { + if (q != null && queued) { q.thread = null; - if (q.interrupted) { - if (interruptible) - cleanStack(); - else - Thread.currentThread().interrupt(); - } + if (!interruptible && q.interrupted) + Thread.currentThread().interrupt(); + if (r == null) + cleanStack(); } - if (r != null) + if (r != null || (r = result) != null) postComplete(); return r; } @@ -1808,9 +1854,12 @@ Signaller q = null; boolean queued = false; Object r; - while ((r = result) == null) { // similar to untimed, without spins - if (q == null) + while ((r = result) == null) { // similar to untimed + if (q == null) { q = new Signaller(true, nanos, deadline); + if (Thread.currentThread() instanceof ForkJoinWorkerThread) + ForkJoinPool.helpAsyncBlocker(defaultExecutor(), q); + } else if (!queued) queued = tryPushStack(q); else if (q.nanos <= 0L) @@ -1825,12 +1874,13 @@ break; } } - if (q != null) + if (q != null && queued) { q.thread = null; - if (r != null) + if (r == null) + cleanStack(); + } + if (r != null || (r = result) != null) postComplete(); - else - cleanStack(); if (r != null || (q != null && q.interrupted)) return r; } @@ -1942,9 +1992,12 @@ * @throws InterruptedException if the current thread was interrupted * while waiting */ + @SuppressWarnings("unchecked") public T get() throws InterruptedException, ExecutionException { Object r; - return reportGet((r = result) == null ? waitingGet(true) : r); + if ((r = result) == null) + r = waitingGet(true); + return (T) reportGet(r); } /** @@ -1960,11 +2013,14 @@ * while waiting * @throws TimeoutException if the wait timed out */ + @SuppressWarnings("unchecked") public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + long nanos = unit.toNanos(timeout); Object r; - long nanos = unit.toNanos(timeout); - return reportGet((r = result) == null ? timedGet(nanos) : r); + if ((r = result) == null) + r = timedGet(nanos); + return (T) reportGet(r); } /** @@ -1981,9 +2037,12 @@ * @throws CompletionException if this future completed * exceptionally or a completion computation threw an exception */ + @SuppressWarnings("unchecked") public T join() { Object r; - return reportJoin((r = result) == null ? waitingGet(false) : r); + if ((r = result) == null) + r = waitingGet(false); + return (T) reportJoin(r); } /** @@ -1996,9 +2055,10 @@ * @throws CompletionException if this future completed * exceptionally or a completion computation threw an exception */ + @SuppressWarnings("unchecked") public T getNow(T valueIfAbsent) { Object r; - return ((r = result) == null) ? valueIfAbsent : reportJoin(r); + return ((r = result) == null) ? valueIfAbsent : (T) reportJoin(r); } /** @@ -2775,19 +2835,16 @@ throw new UnsupportedOperationException(); } } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long RESULT; - private static final long STACK; - private static final long NEXT; + // VarHandle mechanics + private static final VarHandle RESULT; + private static final VarHandle STACK; + private static final VarHandle NEXT; static { try { - RESULT = U.objectFieldOffset - (CompletableFuture.class.getDeclaredField("result")); - STACK = U.objectFieldOffset - (CompletableFuture.class.getDeclaredField("stack")); - NEXT = U.objectFieldOffset - (Completion.class.getDeclaredField("next")); + MethodHandles.Lookup l = MethodHandles.lookup(); + RESULT = l.findVarHandle(CompletableFuture.class, "result", Object.class); + STACK = l.findVarHandle(CompletableFuture.class, "stack", Completion.class); + NEXT = l.findVarHandle(Completion.class, "next", Completion.class); } catch (ReflectiveOperationException e) { throw new Error(e); } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java Mon Jul 18 09:38:08 2016 -0700 @@ -68,6 +68,7 @@ import java.util.function.ToLongBiFunction; import java.util.function.ToLongFunction; import java.util.stream.Stream; +import jdk.internal.misc.Unsafe; /** * A hash table supporting full concurrency of retrievals and @@ -747,7 +748,7 @@ /* ---------------- Table element access -------------- */ /* - * Volatile access methods are used for table elements as well as + * Atomic access methods are used for table elements as well as * elements of in-progress next table while resizing. All uses of * the tab arguments must be null checked by callers. All callers * also paranoically precheck that tab's length is not zero (or an @@ -757,14 +758,12 @@ * errors by users, these checks must operate on local variables, * which accounts for some odd-looking inline assignments below. * Note that calls to setTabAt always occur within locked regions, - * and so in principle require only release ordering, not - * full volatile semantics, but are currently coded as volatile - * writes to be conservative. + * and so require only release ordering. */ @SuppressWarnings("unchecked") static final Node tabAt(Node[] tab, int i) { - return (Node)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE); + return (Node)U.getObjectAcquire(tab, ((long)i << ASHIFT) + ABASE); } static final boolean casTabAt(Node[] tab, int i, @@ -773,7 +772,7 @@ } static final void setTabAt(Node[] tab, int i, Node v) { - U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v); + U.putObjectRelease(tab, ((long)i << ASHIFT) + ABASE, v); } /* ---------------- Fields -------------- */ @@ -3298,7 +3297,7 @@ return true; } - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); + private static final Unsafe U = Unsafe.getUnsafe(); private static final long LOCKSTATE; static { try { @@ -6341,7 +6340,7 @@ } // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); + private static final Unsafe U = Unsafe.getUnsafe(); private static final long SIZECTL; private static final long TRANSFERINDEX; private static final long BASECOUNT; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,6 +35,8 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.AbstractCollection; import java.util.Arrays; import java.util.Collection; @@ -292,64 +294,23 @@ volatile Node prev; volatile E item; volatile Node next; - - Node() { // default constructor for NEXT_TERMINATOR, PREV_TERMINATOR - } - - /** - * Constructs a new node. Uses relaxed write because item can - * only be seen after publication via casNext or casPrev. - */ - Node(E item) { - U.putObject(this, ITEM, item); - } - - boolean casItem(E cmp, E val) { - return U.compareAndSwapObject(this, ITEM, cmp, val); - } - - void lazySetNext(Node val) { - U.putObjectRelease(this, NEXT, val); - } - - boolean casNext(Node cmp, Node val) { - return U.compareAndSwapObject(this, NEXT, cmp, val); - } + } - void lazySetPrev(Node val) { - U.putObjectRelease(this, PREV, val); - } - - boolean casPrev(Node cmp, Node val) { - return U.compareAndSwapObject(this, PREV, cmp, val); - } - - // Unsafe mechanics - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long PREV; - private static final long ITEM; - private static final long NEXT; - - static { - try { - PREV = U.objectFieldOffset - (Node.class.getDeclaredField("prev")); - ITEM = U.objectFieldOffset - (Node.class.getDeclaredField("item")); - NEXT = U.objectFieldOffset - (Node.class.getDeclaredField("next")); - } catch (ReflectiveOperationException e) { - throw new Error(e); - } - } + /** + * Returns a new node holding item. Uses relaxed write because item + * can only be seen after piggy-backing publication via CAS. + */ + static Node newNode(E item) { + Node node = new Node(); + ITEM.set(node, item); + return node; } /** * Links e as first element. */ private void linkFirst(E e) { - final Node newNode = new Node(Objects.requireNonNull(e)); + final Node newNode = newNode(Objects.requireNonNull(e)); restartFromHead: for (;;) @@ -363,13 +324,13 @@ continue restartFromHead; else { // p is first node - newNode.lazySetNext(p); // CAS piggyback - if (p.casPrev(null, newNode)) { + NEXT.set(newNode, p); // CAS piggyback + if (PREV.compareAndSet(p, null, newNode)) { // Successful CAS is the linearization point // for e to become an element of this deque, // and for newNode to become "live". - if (p != h) // hop two nodes at a time - casHead(h, newNode); // Failure is OK. + if (p != h) // hop two nodes at a time; failure is OK + HEAD.weakCompareAndSetVolatile(this, h, newNode); return; } // Lost CAS race to another thread; re-read prev @@ -381,7 +342,7 @@ * Links e as last element. */ private void linkLast(E e) { - final Node newNode = new Node(Objects.requireNonNull(e)); + final Node newNode = newNode(Objects.requireNonNull(e)); restartFromTail: for (;;) @@ -395,13 +356,13 @@ continue restartFromTail; else { // p is last node - newNode.lazySetPrev(p); // CAS piggyback - if (p.casNext(null, newNode)) { + PREV.set(newNode, p); // CAS piggyback + if (NEXT.compareAndSet(p, null, newNode)) { // Successful CAS is the linearization point // for e to become an element of this deque, // and for newNode to become "live". - if (p != t) // hop two nodes at a time - casTail(t, newNode); // Failure is OK. + if (p != t) // hop two nodes at a time; failure is OK + TAIL.weakCompareAndSetVolatile(this, t, newNode); return; } // Lost CAS race to another thread; re-read next @@ -516,8 +477,8 @@ updateTail(); // Ensure x is not reachable from tail // Finally, actually gc-unlink - x.lazySetPrev(isFirst ? prevTerminator() : x); - x.lazySetNext(isLast ? nextTerminator() : x); + PREV.setRelease(x, isFirst ? prevTerminator() : x); + NEXT.setRelease(x, isLast ? nextTerminator() : x); } } } @@ -531,7 +492,8 @@ // assert first.item == null; for (Node o = null, p = next, q;;) { if (p.item != null || (q = p.next) == null) { - if (o != null && p.prev != p && first.casNext(next, p)) { + if (o != null && p.prev != p && + NEXT.compareAndSet(first, next, p)) { skipDeletedPredecessors(p); if (first.prev == null && (p.next == null || p.item != null) && @@ -541,8 +503,8 @@ updateTail(); // Ensure o is not reachable from tail // Finally, actually gc-unlink - o.lazySetNext(o); - o.lazySetPrev(prevTerminator()); + NEXT.setRelease(o, o); + PREV.setRelease(o, prevTerminator()); } } return; @@ -565,7 +527,8 @@ // assert last.item == null; for (Node o = null, p = prev, q;;) { if (p.item != null || (q = p.prev) == null) { - if (o != null && p.next != p && last.casPrev(prev, p)) { + if (o != null && p.next != p && + PREV.compareAndSet(last, prev, p)) { skipDeletedSuccessors(p); if (last.next == null && (p.prev == null || p.item != null) && @@ -575,8 +538,8 @@ updateTail(); // Ensure o is not reachable from tail // Finally, actually gc-unlink - o.lazySetPrev(o); - o.lazySetNext(nextTerminator()); + PREV.setRelease(o, o); + NEXT.setRelease(o, nextTerminator()); } } return; @@ -607,7 +570,7 @@ (q = (p = q).prev) == null) { // It is possible that p is PREV_TERMINATOR, // but if so, the CAS is guaranteed to fail. - if (casHead(h, p)) + if (HEAD.compareAndSet(this, h, p)) return; else continue restartFromHead; @@ -637,7 +600,7 @@ (q = (p = q).next) == null) { // It is possible that p is NEXT_TERMINATOR, // but if so, the CAS is guaranteed to fail. - if (casTail(t, p)) + if (TAIL.compareAndSet(this, t, p)) return; else continue restartFromTail; @@ -675,7 +638,7 @@ } // found active CAS target - if (prev == p || x.casPrev(prev, p)) + if (prev == p || PREV.compareAndSet(x, prev, p)) return; } while (x.item != null || x.next == null); @@ -706,7 +669,7 @@ } // found active CAS target - if (next == p || x.casNext(next, p)) + if (next == p || NEXT.compareAndSet(x, next, p)) return; } while (x.item != null || x.prev == null); @@ -751,7 +714,7 @@ else if (p == h // It is possible that p is PREV_TERMINATOR, // but if so, the CAS is guaranteed to fail. - || casHead(h, p)) + || HEAD.compareAndSet(this, h, p)) return p; else continue restartFromHead; @@ -776,7 +739,7 @@ else if (p == t // It is possible that p is NEXT_TERMINATOR, // but if so, the CAS is guaranteed to fail. - || casTail(t, p)) + || TAIL.compareAndSet(this, t, p)) return p; else continue restartFromTail; @@ -802,7 +765,7 @@ * Constructs an empty deque. */ public ConcurrentLinkedDeque() { - head = tail = new Node(null); + head = tail = new Node(); } /** @@ -818,12 +781,12 @@ // Copy c into a private chain of Nodes Node h = null, t = null; for (E e : c) { - Node newNode = new Node(Objects.requireNonNull(e)); + Node newNode = newNode(Objects.requireNonNull(e)); if (h == null) h = t = newNode; else { - t.lazySetNext(newNode); - newNode.lazySetPrev(t); + NEXT.set(t, newNode); + PREV.set(newNode, t); t = newNode; } } @@ -836,12 +799,12 @@ private void initHeadTail(Node h, Node t) { if (h == t) { if (h == null) - h = t = new Node(null); + h = t = new Node(); else { // Avoid edge case of a single Node with non-null item. - Node newNode = new Node(null); - t.lazySetNext(newNode); - newNode.lazySetPrev(t); + Node newNode = new Node(); + NEXT.set(t, newNode); + PREV.set(newNode, t); t = newNode; } } @@ -934,7 +897,7 @@ public E pollFirst() { for (Node p = first(); p != null; p = succ(p)) { E item = p.item; - if (item != null && p.casItem(item, null)) { + if (item != null && ITEM.compareAndSet(p, item, null)) { unlink(p); return item; } @@ -945,7 +908,7 @@ public E pollLast() { for (Node p = last(); p != null; p = pred(p)) { E item = p.item; - if (item != null && p.casItem(item, null)) { + if (item != null && ITEM.compareAndSet(p, item, null)) { unlink(p); return item; } @@ -1031,7 +994,8 @@ Objects.requireNonNull(o); for (Node p = first(); p != null; p = succ(p)) { E item = p.item; - if (item != null && o.equals(item) && p.casItem(item, null)) { + if (item != null && o.equals(item) && + ITEM.compareAndSet(p, item, null)) { unlink(p); return true; } @@ -1055,7 +1019,8 @@ Objects.requireNonNull(o); for (Node p = last(); p != null; p = pred(p)) { E item = p.item; - if (item != null && o.equals(item) && p.casItem(item, null)) { + if (item != null && o.equals(item) && + ITEM.compareAndSet(p, item, null)) { unlink(p); return true; } @@ -1159,12 +1124,12 @@ // Copy c into a private chain of Nodes Node beginningOfTheEnd = null, last = null; for (E e : c) { - Node newNode = new Node(Objects.requireNonNull(e)); + Node newNode = newNode(Objects.requireNonNull(e)); if (beginningOfTheEnd == null) beginningOfTheEnd = last = newNode; else { - last.lazySetNext(newNode); - newNode.lazySetPrev(last); + NEXT.set(last, newNode); + PREV.set(newNode, last); last = newNode; } } @@ -1184,16 +1149,16 @@ continue restartFromTail; else { // p is last node - beginningOfTheEnd.lazySetPrev(p); // CAS piggyback - if (p.casNext(null, beginningOfTheEnd)) { + PREV.set(beginningOfTheEnd, p); // CAS piggyback + if (NEXT.compareAndSet(p, null, beginningOfTheEnd)) { // Successful CAS is the linearization point // for all elements to be added to this deque. - if (!casTail(t, last)) { + if (!TAIL.weakCompareAndSetVolatile(this, t, last)) { // Try a little harder to update tail, // since we may be adding many elements. t = tail; if (last.next == null) - casTail(t, last); + TAIL.weakCompareAndSetVolatile(this, t, last); } return true; } @@ -1586,41 +1551,38 @@ Node h = null, t = null; for (Object item; (item = s.readObject()) != null; ) { @SuppressWarnings("unchecked") - Node newNode = new Node((E) item); + Node newNode = newNode((E) item); if (h == null) h = t = newNode; else { - t.lazySetNext(newNode); - newNode.lazySetPrev(t); + NEXT.set(t, newNode); + PREV.set(newNode, t); t = newNode; } } initHeadTail(h, t); } - private boolean casHead(Node cmp, Node val) { - return U.compareAndSwapObject(this, HEAD, cmp, val); - } - - private boolean casTail(Node cmp, Node val) { - return U.compareAndSwapObject(this, TAIL, cmp, val); - } - - // Unsafe mechanics - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long HEAD; - private static final long TAIL; + // VarHandle mechanics + private static final VarHandle HEAD; + private static final VarHandle TAIL; + private static final VarHandle PREV; + private static final VarHandle NEXT; + private static final VarHandle ITEM; static { PREV_TERMINATOR = new Node(); PREV_TERMINATOR.next = PREV_TERMINATOR; NEXT_TERMINATOR = new Node(); NEXT_TERMINATOR.prev = NEXT_TERMINATOR; try { - HEAD = U.objectFieldOffset - (ConcurrentLinkedDeque.class.getDeclaredField("head")); - TAIL = U.objectFieldOffset - (ConcurrentLinkedDeque.class.getDeclaredField("tail")); + MethodHandles.Lookup l = MethodHandles.lookup(); + HEAD = l.findVarHandle(ConcurrentLinkedDeque.class, "head", + Node.class); + TAIL = l.findVarHandle(ConcurrentLinkedDeque.class, "tail", + Node.class); + PREV = l.findVarHandle(Node.class, "prev", Node.class); + NEXT = l.findVarHandle(Node.class, "next", Node.class); + ITEM = l.findVarHandle(Node.class, "item", Object.class); } catch (ReflectiveOperationException e) { throw new Error(e); } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,6 +35,8 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.AbstractQueue; import java.util.Arrays; import java.util.Collection; @@ -166,9 +168,8 @@ * this is merely an optimization. * * When constructing a Node (before enqueuing it) we avoid paying - * for a volatile write to item by using Unsafe.putObject instead - * of a normal write. This allows the cost of enqueue to be - * "one-and-a-half" CASes. + * for a volatile write to item. This allows the cost of enqueue + * to be "one-and-a-half" CASes. * * Both head and tail may or may not point to a Node with a * non-null item. If the queue is empty, all items must of course @@ -178,33 +179,21 @@ * optimization. */ - private static class Node { + static final class Node { volatile E item; volatile Node next; } /** * Returns a new node holding item. Uses relaxed write because item - * can only be seen after piggy-backing publication via casNext. + * can only be seen after piggy-backing publication via CAS. */ static Node newNode(E item) { Node node = new Node(); - U.putObject(node, ITEM, item); + ITEM.set(node, item); return node; } - static boolean casItem(Node node, E cmp, E val) { - return U.compareAndSwapObject(node, ITEM, cmp, val); - } - - static void lazySetNext(Node node, Node val) { - U.putObjectRelease(node, NEXT, val); - } - - static boolean casNext(Node node, Node cmp, Node val) { - return U.compareAndSwapObject(node, NEXT, cmp, val); - } - /** * A node from which the first live (non-deleted) node (if any) * can be reached in O(1) time. @@ -256,7 +245,7 @@ if (h == null) h = t = newNode; else { - lazySetNext(t, newNode); + NEXT.set(t, newNode); t = newNode; } } @@ -286,8 +275,8 @@ */ final void updateHead(Node h, Node p) { // assert h != null && p != null && (h == p || h.item == null); - if (h != p && casHead(h, p)) - lazySetNext(h, h); + if (h != p && HEAD.compareAndSet(this, h, p)) + NEXT.setRelease(h, h); } /** @@ -314,12 +303,12 @@ Node q = p.next; if (q == null) { // p is last node - if (casNext(p, null, newNode)) { + if (NEXT.compareAndSet(p, null, newNode)) { // Successful CAS is the linearization point // for e to become an element of this queue, // and for newNode to become "live". - if (p != t) // hop two nodes at a time - casTail(t, newNode); // Failure is OK. + if (p != t) // hop two nodes at a time; failure is OK + TAIL.weakCompareAndSetVolatile(this, t, newNode); return true; } // Lost CAS race to another thread; re-read next @@ -342,7 +331,7 @@ for (Node h = head, p = h, q;;) { E item = p.item; - if (item != null && casItem(p, item, null)) { + if (item != null && ITEM.compareAndSet(p, item, null)) { // Successful CAS is the linearization point // for item to be removed from this queue. if (p != h) // hop two nodes at a time @@ -483,12 +472,12 @@ next = succ(p); continue; } - removed = casItem(p, item, null); + removed = ITEM.compareAndSet(p, item, null); } next = succ(p); if (pred != null && next != null) // unlink - casNext(pred, p, next); + NEXT.weakCompareAndSetVolatile(pred, p, next); if (removed) return true; } @@ -520,7 +509,7 @@ if (beginningOfTheEnd == null) beginningOfTheEnd = last = newNode; else { - lazySetNext(last, newNode); + NEXT.set(last, newNode); last = newNode; } } @@ -532,15 +521,15 @@ Node q = p.next; if (q == null) { // p is last node - if (casNext(p, null, beginningOfTheEnd)) { + if (NEXT.compareAndSet(p, null, beginningOfTheEnd)) { // Successful CAS is the linearization point // for all elements to be added to this queue. - if (!casTail(t, last)) { + if (!TAIL.weakCompareAndSetVolatile(this, t, last)) { // Try a little harder to update tail, // since we may be adding many elements. t = tail; if (last.next == null) - casTail(t, last); + TAIL.weakCompareAndSetVolatile(this, t, last); } return true; } @@ -744,7 +733,7 @@ } // unlink deleted nodes if ((q = succ(p)) != null) - casNext(pred, p, q); + NEXT.compareAndSet(pred, p, q); } } @@ -801,7 +790,7 @@ if (h == null) h = t = newNode; else { - lazySetNext(t, newNode); + NEXT.set(t, newNode); t = newNode; } } @@ -919,31 +908,20 @@ return new CLQSpliterator(this); } - private boolean casTail(Node cmp, Node val) { - return U.compareAndSwapObject(this, TAIL, cmp, val); - } - - private boolean casHead(Node cmp, Node val) { - return U.compareAndSwapObject(this, HEAD, cmp, val); - } - - // Unsafe mechanics - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long HEAD; - private static final long TAIL; - private static final long ITEM; - private static final long NEXT; + // VarHandle mechanics + private static final VarHandle HEAD; + private static final VarHandle TAIL; + private static final VarHandle ITEM; + private static final VarHandle NEXT; static { try { - HEAD = U.objectFieldOffset - (ConcurrentLinkedQueue.class.getDeclaredField("head")); - TAIL = U.objectFieldOffset - (ConcurrentLinkedQueue.class.getDeclaredField("tail")); - ITEM = U.objectFieldOffset - (Node.class.getDeclaredField("item")); - NEXT = U.objectFieldOffset - (Node.class.getDeclaredField("next")); + MethodHandles.Lookup l = MethodHandles.lookup(); + HEAD = l.findVarHandle(ConcurrentLinkedQueue.class, "head", + Node.class); + TAIL = l.findVarHandle(ConcurrentLinkedQueue.class, "tail", + Node.class); + ITEM = l.findVarHandle(Node.class, "item", Object.class); + NEXT = l.findVarHandle(Node.class, "next", Node.class); } catch (ReflectiveOperationException e) { throw new Error(e); } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,6 +35,8 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.io.Serializable; import java.util.AbstractCollection; import java.util.AbstractMap; @@ -401,7 +403,7 @@ * compareAndSet head node. */ private boolean casHead(HeadIndex cmp, HeadIndex val) { - return U.compareAndSwapObject(this, HEAD, cmp, val); + return HEAD.compareAndSet(this, cmp, val); } /* ---------------- Nodes -------------- */ @@ -444,14 +446,14 @@ * compareAndSet value field. */ boolean casValue(Object cmp, Object val) { - return U.compareAndSwapObject(this, VALUE, cmp, val); + return VALUE.compareAndSet(this, cmp, val); } /** * compareAndSet next field. */ boolean casNext(Node cmp, Node val) { - return U.compareAndSwapObject(this, NEXT, cmp, val); + return NEXT.compareAndSet(this, cmp, val); } /** @@ -532,20 +534,16 @@ return new AbstractMap.SimpleImmutableEntry(key, vv); } - // Unsafe mechanics - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long VALUE; - private static final long NEXT; - + // VarHandle mechanics + private static final VarHandle VALUE; + private static final VarHandle NEXT; static { try { - VALUE = U.objectFieldOffset - (Node.class.getDeclaredField("value")); - NEXT = U.objectFieldOffset - (Node.class.getDeclaredField("next")); + MethodHandles.Lookup l = MethodHandles.lookup(); + VALUE = l.findVarHandle(Node.class, "value", Object.class); + NEXT = l.findVarHandle(Node.class, "next", Node.class); } catch (ReflectiveOperationException e) { - throw new Error(e); + throw new Error(e); } } } @@ -577,7 +575,7 @@ * compareAndSet right field. */ final boolean casRight(Index cmp, Index val) { - return U.compareAndSwapObject(this, RIGHT, cmp, val); + return RIGHT.compareAndSet(this, cmp, val); } /** @@ -613,13 +611,12 @@ return node.value != null && casRight(succ, succ.right); } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long RIGHT; + // VarHandle mechanics + private static final VarHandle RIGHT; static { try { - RIGHT = U.objectFieldOffset - (Index.class.getDeclaredField("right")); + MethodHandles.Lookup l = MethodHandles.lookup(); + RIGHT = l.findVarHandle(Index.class, "right", Index.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -3607,13 +3604,13 @@ } } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long HEAD; + // VarHandle mechanics + private static final VarHandle HEAD; static { try { - HEAD = U.objectFieldOffset - (ConcurrentSkipListMap.class.getDeclaredField("head")); + MethodHandles.Lookup l = MethodHandles.lookup(); + HEAD = l.findVarHandle(ConcurrentSkipListMap.class, "head", + HeadIndex.class); } catch (ReflectiveOperationException e) { throw new Error(e); } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,6 +35,8 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.AbstractSet; import java.util.Collection; import java.util.Collections; @@ -507,15 +509,16 @@ // Support for resetting map in clone private void setMap(ConcurrentNavigableMap map) { - U.putObjectVolatile(this, MAP, map); + MAP.setVolatile(this, map); } - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long MAP; + // VarHandle mechanics + private static final VarHandle MAP; static { try { - MAP = U.objectFieldOffset - (ConcurrentSkipListSet.class.getDeclaredField("m")); + MethodHandles.Lookup l = MethodHandles.lookup(); + MAP = l.findVarHandle(ConcurrentSkipListSet.class, "m", + ConcurrentNavigableMap.class); } catch (ReflectiveOperationException e) { throw new Error(e); } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java Mon Jul 18 09:38:08 2016 -0700 @@ -34,6 +34,7 @@ package java.util.concurrent; +import java.lang.reflect.Field; import java.util.AbstractList; import java.util.Arrays; import java.util.Collection; @@ -1541,17 +1542,21 @@ } } - // Support for resetting lock while deserializing + /** Initializes the lock; for use when deserializing or cloning. */ private void resetLock() { - U.putObjectVolatile(this, LOCK, new Object()); - } - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long LOCK; - static { + Field lockField = java.security.AccessController.doPrivileged( + (java.security.PrivilegedAction) () -> { + try { + Field f = CopyOnWriteArrayList.class + .getDeclaredField("lock"); + f.setAccessible(true); + return f; + } catch (ReflectiveOperationException e) { + throw new Error(e); + }}); try { - LOCK = U.objectFieldOffset - (CopyOnWriteArrayList.class.getDeclaredField("lock")); - } catch (ReflectiveOperationException e) { + lockField.set(this, new Object()); + } catch (IllegalAccessException e) { throw new Error(e); } } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,6 +35,9 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; + /** * A {@link ForkJoinTask} with a completion action performed when * triggered and there are no remaining pending actions. @@ -524,7 +527,7 @@ * @param delta the value to add */ public final void addToPendingCount(int delta) { - U.getAndAddInt(this, PENDING, delta); + PENDING.getAndAdd(this, delta); } /** @@ -536,7 +539,7 @@ * @return {@code true} if successful */ public final boolean compareAndSetPendingCount(int expected, int count) { - return U.compareAndSwapInt(this, PENDING, expected, count); + return PENDING.compareAndSet(this, expected, count); } /** @@ -548,7 +551,7 @@ public final int decrementPendingCountUnlessZero() { int c; do {} while ((c = pending) != 0 && - !U.compareAndSwapInt(this, PENDING, c, c - 1)); + !PENDING.weakCompareAndSetVolatile(this, c, c - 1)); return c; } @@ -581,7 +584,7 @@ return; } } - else if (U.compareAndSwapInt(a, PENDING, c, c - 1)) + else if (PENDING.weakCompareAndSetVolatile(a, c, c - 1)) return; } } @@ -604,7 +607,7 @@ return; } } - else if (U.compareAndSwapInt(a, PENDING, c, c - 1)) + else if (PENDING.weakCompareAndSetVolatile(a, c, c - 1)) return; } } @@ -649,7 +652,7 @@ for (int c;;) { if ((c = pending) == 0) return this; - else if (U.compareAndSwapInt(this, PENDING, c, c - 1)) + else if (PENDING.weakCompareAndSetVolatile(this, c, c - 1)) return null; } } @@ -753,13 +756,13 @@ */ protected void setRawResult(T t) { } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long PENDING; + // VarHandle mechanics + private static final VarHandle PENDING; static { try { - PENDING = U.objectFieldOffset - (CountedCompleter.class.getDeclaredField("pending")); + MethodHandles.Lookup l = MethodHandles.lookup(); + PENDING = l.findVarHandle(CountedCompleter.class, "pending", int.class); + } catch (ReflectiveOperationException e) { throw new Error(e); } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/Exchanger.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/Exchanger.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/Exchanger.java Mon Jul 18 09:38:08 2016 -0700 @@ -36,6 +36,10 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.util.concurrent.locks.LockSupport; + /** * A synchronization point at which threads can pair and swap elements * within pairs. Each thread presents some object on entry to the @@ -155,9 +159,7 @@ * a value that is enough for common platforms. Additionally, * extra care elsewhere is taken to avoid other false/unintended * sharing and to enhance locality, including adding padding (via - * @Contended) to Nodes, embedding "bound" as an Exchanger field, - * and reworking some park/unpark mechanics compared to - * LockSupport versions. + * @Contended) to Nodes, embedding "bound" as an Exchanger field. * * The arena starts out with only one used slot. We expand the * effective arena size by tracking collisions; i.e., failed CASes @@ -234,12 +236,12 @@ * because most of the logic relies on reads of fields that are * maintained as local variables so can't be nicely factored -- * mainly, here, bulky spin->yield->block/cancel code), and - * heavily dependent on intrinsics (Unsafe) to use inlined + * heavily dependent on intrinsics (VarHandles) to use inlined * embedded CAS and related memory access operations (that tend * not to be as readily inlined by dynamic compilers when they are * hidden behind other methods that would more nicely name and * encapsulate the intended effects). This includes the use of - * putXRelease to clear fields of the per-thread Nodes between + * setRelease to clear fields of the per-thread Nodes between * uses. Note that field Node.item is not declared as volatile * even though it is read by releasing threads, because they only * do so after CAS operations that must precede access, and all @@ -252,10 +254,10 @@ */ /** - * The byte distance (as a shift value) between any two used slots - * in the arena. 1 << ASHIFT should be at least cacheline size. + * The index distance (as a shift value) between any two used slots + * in the arena, spacing them out to avoid false sharing. */ - private static final int ASHIFT = 7; + private static final int ASHIFT = 5; /** * The maximum supported arena index. The maximum allocatable @@ -356,27 +358,31 @@ */ private final Object arenaExchange(Object item, boolean timed, long ns) { Node[] a = arena; + int alen = a.length; Node p = participant.get(); for (int i = p.index;;) { // access slot at i - int b, m, c; long j; // j is raw array offset - Node q = (Node)U.getObjectVolatile(a, j = (i << ASHIFT) + ABASE); - if (q != null && U.compareAndSwapObject(a, j, q, null)) { + int b, m, c; + int j = (i << ASHIFT) + ((1 << ASHIFT) - 1); + if (j < 0 || j >= alen) + j = alen - 1; + Node q = (Node)AA.getAcquire(a, j); + if (q != null && AA.compareAndSet(a, j, q, null)) { Object v = q.item; // release q.match = item; Thread w = q.parked; if (w != null) - U.unpark(w); + LockSupport.unpark(w); return v; } else if (i <= (m = (b = bound) & MMASK) && q == null) { p.item = item; // offer - if (U.compareAndSwapObject(a, j, null, p)) { + if (AA.compareAndSet(a, j, null, p)) { long end = (timed && m == 0) ? System.nanoTime() + ns : 0L; Thread t = Thread.currentThread(); // wait for (int h = p.hash, spins = SPINS;;) { Object v = p.match; if (v != null) { - U.putObjectRelease(p, MATCH, null); + MATCH.setRelease(p, null); p.item = null; // clear for next use p.hash = h; return v; @@ -389,22 +395,24 @@ (--spins & ((SPINS >>> 1) - 1)) == 0) Thread.yield(); // two yields per wait } - else if (U.getObjectVolatile(a, j) != p) + else if (AA.getAcquire(a, j) != p) spins = SPINS; // releaser hasn't set match yet else if (!t.isInterrupted() && m == 0 && (!timed || (ns = end - System.nanoTime()) > 0L)) { - U.putObject(t, BLOCKER, this); // emulate LockSupport p.parked = t; // minimize window - if (U.getObjectVolatile(a, j) == p) - U.park(false, ns); + if (AA.getAcquire(a, j) == p) { + if (ns == 0L) + LockSupport.park(this); + else + LockSupport.parkNanos(this, ns); + } p.parked = null; - U.putObject(t, BLOCKER, null); } - else if (U.getObjectVolatile(a, j) == p && - U.compareAndSwapObject(a, j, p, null)) { + else if (AA.getAcquire(a, j) == p && + AA.compareAndSet(a, j, p, null)) { if (m != 0) // try to shrink - U.compareAndSwapInt(this, BOUND, b, b + SEQ - 1); + BOUND.compareAndSet(this, b, b + SEQ - 1); p.item = null; p.hash = h; i = p.index >>>= 1; // descend @@ -426,7 +434,7 @@ i = (i != m || m == 0) ? m : m - 1; } else if ((c = p.collides) < m || m == FULL || - !U.compareAndSwapInt(this, BOUND, b, b + SEQ + 1)) { + !BOUND.compareAndSet(this, b, b + SEQ + 1)) { p.collides = c + 1; i = (i == 0) ? m : i - 1; // cyclically traverse } @@ -455,24 +463,24 @@ for (Node q;;) { if ((q = slot) != null) { - if (U.compareAndSwapObject(this, SLOT, q, null)) { + if (SLOT.compareAndSet(this, q, null)) { Object v = q.item; q.match = item; Thread w = q.parked; if (w != null) - U.unpark(w); + LockSupport.unpark(w); return v; } // create arena on contention, but continue until slot null if (NCPU > 1 && bound == 0 && - U.compareAndSwapInt(this, BOUND, 0, SEQ)) + BOUND.compareAndSet(this, 0, SEQ)) arena = new Node[(FULL + 2) << ASHIFT]; } else if (arena != null) return null; // caller must reroute to arenaExchange else { p.item = item; - if (U.compareAndSwapObject(this, SLOT, null, p)) + if (SLOT.compareAndSet(this, null, p)) break; p.item = null; } @@ -495,19 +503,21 @@ spins = SPINS; else if (!t.isInterrupted() && arena == null && (!timed || (ns = end - System.nanoTime()) > 0L)) { - U.putObject(t, BLOCKER, this); p.parked = t; - if (slot == p) - U.park(false, ns); + if (slot == p) { + if (ns == 0L) + LockSupport.park(this); + else + LockSupport.parkNanos(this, ns); + } p.parked = null; - U.putObject(t, BLOCKER, null); } - else if (U.compareAndSwapObject(this, SLOT, p, null)) { + else if (SLOT.compareAndSet(this, p, null)) { v = timed && ns <= 0L && !t.isInterrupted() ? TIMED_OUT : null; break; } } - U.putObjectRelease(p, MATCH, null); + MATCH.setRelease(p, null); p.item = null; p.hash = h; return v; @@ -556,8 +566,9 @@ @SuppressWarnings("unchecked") public V exchange(V x) throws InterruptedException { Object v; + Node[] a; Object item = (x == null) ? NULL_ITEM : x; // translate null args - if ((arena != null || + if (((a = arena) != null || (v = slotExchange(item, false, 0L)) == null) && ((Thread.interrupted() || // disambiguates null return (v = arenaExchange(item, false, 0L)) == null))) @@ -623,31 +634,18 @@ return (v == NULL_ITEM) ? null : (V)v; } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long BOUND; - private static final long SLOT; - private static final long MATCH; - private static final long BLOCKER; - private static final int ABASE; + // VarHandle mechanics + private static final VarHandle BOUND; + private static final VarHandle SLOT; + private static final VarHandle MATCH; + private static final VarHandle AA; static { try { - BOUND = U.objectFieldOffset - (Exchanger.class.getDeclaredField("bound")); - SLOT = U.objectFieldOffset - (Exchanger.class.getDeclaredField("slot")); - - MATCH = U.objectFieldOffset - (Node.class.getDeclaredField("match")); - - BLOCKER = U.objectFieldOffset - (Thread.class.getDeclaredField("parkBlocker")); - - int scale = U.arrayIndexScale(Node[].class); - if ((scale & (scale - 1)) != 0 || scale > (1 << ASHIFT)) - throw new Error("Unsupported array scale"); - // ABASE absorbs padding in front of element 0 - ABASE = U.arrayBaseOffset(Node[].class) + (1 << ASHIFT); + MethodHandles.Lookup l = MethodHandles.lookup(); + BOUND = l.findVarHandle(Exchanger.class, "bound", int.class); + SLOT = l.findVarHandle(Exchanger.class, "slot", Node.class); + MATCH = l.findVarHandle(Node.class, "match", Object.class); + AA = MethodHandles.arrayElementVarHandle(Node[].class); } catch (ReflectiveOperationException e) { throw new Error(e); } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java Mon Jul 18 09:38:08 2016 -0700 @@ -36,6 +36,8 @@ package java.util.concurrent; import java.lang.Thread.UncaughtExceptionHandler; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.security.AccessControlContext; import java.security.Permissions; import java.security.ProtectionDomain; @@ -44,7 +46,11 @@ import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Predicate; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.CountedCompleter; +import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.ForkJoinWorkerThread; import java.util.concurrent.locks.LockSupport; /** @@ -81,7 +87,9 @@ * However, no such adjustments are guaranteed in the face of blocked * I/O or other unmanaged synchronization. The nested {@link * ManagedBlocker} interface enables extension of the kinds of - * synchronization accommodated. + * synchronization accommodated. The default policies may be + * overridden using a constructor with parameters corresponding to + * those documented in class {@link ThreadPoolExecutor}. * *

In addition to execution and lifecycle control methods, this * class provides status check methods (for example @@ -162,7 +170,6 @@ * @since 1.7 * @author Doug Lea */ -@jdk.internal.vm.annotation.Contended public class ForkJoinPool extends AbstractExecutorService { /* @@ -229,10 +236,9 @@ * (CAS slot to null)) * increment base and return task; * - * There are several variants of each of these; for example most - * versions of poll pre-screen the CAS by rechecking that the base - * has not changed since reading the slot, and most methods only - * attempt the CAS if base appears not to be equal to top. + * There are several variants of each of these. In particular, + * almost all uses of poll occur within scan operations that also + * interleave contention tracking (with associated code sprawl.) * * Memory ordering. See "Correct and Efficient Work-Stealing for * Weak Memory Models" by Le, Pop, Cohen, and Nardelli, PPoPP 2013 @@ -264,10 +270,7 @@ * thief chooses a different random victim target to try next. So, * in order for one thief to progress, it suffices for any * in-progress poll or new push on any empty queue to - * complete. (This is why we normally use method pollAt and its - * variants that try once at the apparent base index, else - * consider alternative actions, rather than method poll, which - * retries.) + * complete. * * This approach also enables support of a user mode in which * local task processing is in FIFO, not LIFO order, simply by @@ -282,16 +285,13 @@ * choosing existing queues, and may be randomly repositioned upon * contention with other submitters. In essence, submitters act * like workers except that they are restricted to executing local - * tasks that they submitted (or in the case of CountedCompleters, - * others with the same root task). Insertion of tasks in shared - * mode requires a lock but we use only a simple spinlock (using - * field qlock), because submitters encountering a busy queue move - * on to try or create other queues -- they block only when - * creating and registering new queues. Because it is used only as - * a spinlock, unlocking requires only a "releasing" store (using - * putIntRelease). The qlock is also used during termination - * detection, in which case it is forced to a negative - * non-lockable value. + * tasks that they submitted. Insertion of tasks in shared mode + * requires a lock but we use only a simple spinlock (using field + * phase), because submitters encountering a busy queue move to a + * different position to use or create other queues -- they block + * only when creating and registering new queues. Because it is + * used only as a spinlock, unlocking requires only a "releasing" + * store (using setRelease). * * Management * ========== @@ -305,42 +305,34 @@ * There are only a few properties that we can globally track or * maintain, so we pack them into a small number of variables, * often maintaining atomicity without blocking or locking. - * Nearly all essentially atomic control state is held in two + * Nearly all essentially atomic control state is held in a few * volatile variables that are by far most often read (not - * written) as status and consistency checks. (Also, field - * "config" holds unchanging configuration state.) + * written) as status and consistency checks. We pack as much + * information into them as we can. * * Field "ctl" contains 64 bits holding information needed to - * atomically decide to add, inactivate, enqueue (on an event - * queue), dequeue, and/or re-activate workers. To enable this + * atomically decide to add, enqueue (on an event queue), and + * dequeue (and release)-activate workers. To enable this * packing, we restrict maximum parallelism to (1<<15)-1 (which is * far in excess of normal operating range) to allow ids, counts, * and their negations (used for thresholding) to fit into 16bit * subfields. * - * Field "runState" holds lifetime status, atomically and - * monotonically setting STARTED, SHUTDOWN, STOP, and finally - * TERMINATED bits. - * - * Field "auxState" is a ReentrantLock subclass that also - * opportunistically holds some other bookkeeping fields accessed - * only when locked. It is mainly used to lock (infrequent) - * updates to workQueues. The auxState instance is itself lazily - * constructed (see tryInitialize), requiring a double-check-style - * bootstrapping use of field runState, and locking a private - * static. + * Field "mode" holds configuration parameters as well as lifetime + * status, atomically and monotonically setting SHUTDOWN, STOP, + * and finally TERMINATED bits. * * Field "workQueues" holds references to WorkQueues. It is - * updated (only during worker creation and termination) under the - * lock, but is otherwise concurrently readable, and accessed - * directly. We also ensure that reads of the array reference - * itself never become too stale (for example, re-reading before - * each scan). To simplify index-based operations, the array size - * is always a power of two, and all readers must tolerate null - * slots. Worker queues are at odd indices. Shared (submission) - * queues are at even indices, up to a maximum of 64 slots, to - * limit growth even if array needs to expand to add more - * workers. Grouping them together in this way simplifies and + * updated (only during worker creation and termination) under + * lock (using field workerNamePrefix as lock), but is otherwise + * concurrently readable, and accessed directly. We also ensure + * that uses of the array reference itself never become too stale + * in case of resizing. To simplify index-based operations, the + * array size is always a power of two, and all readers must + * tolerate null slots. Worker queues are at odd indices. Shared + * (submission) queues are at even indices, up to a maximum of 64 + * slots, to limit growth even if array needs to expand to add + * more workers. Grouping them together in this way simplifies and * speeds up task scanning. * * All worker thread creation is on-demand, triggered by task @@ -360,30 +352,37 @@ * workers unless there appear to be tasks available. On the * other hand, we must quickly prod them into action when new * tasks are submitted or generated. In many usages, ramp-up time - * to activate workers is the main limiting factor in overall - * performance, which is compounded at program start-up by JIT - * compilation and allocation. So we streamline this as much as - * possible. + * is the main limiting factor in overall performance, which is + * compounded at program start-up by JIT compilation and + * allocation. So we streamline this as much as possible. * - * The "ctl" field atomically maintains active and total worker - * counts as well as a queue to place waiting threads so they can - * be located for signalling. Active counts also play the role of - * quiescence indicators, so are decremented when workers believe - * that there are no more tasks to execute. The "queue" is - * actually a form of Treiber stack. A stack is ideal for - * activating threads in most-recently used order. This improves + * The "ctl" field atomically maintains total worker and + * "released" worker counts, plus the head of the available worker + * queue (actually stack, represented by the lower 32bit subfield + * of ctl). Released workers are those known to be scanning for + * and/or running tasks. Unreleased ("available") workers are + * recorded in the ctl stack. These workers are made available for + * signalling by enqueuing in ctl (see method runWorker). The + * "queue" is a form of Treiber stack. This is ideal for + * activating threads in most-recently used order, and improves * performance and locality, outweighing the disadvantages of * being prone to contention and inability to release a worker - * unless it is topmost on stack. We block/unblock workers after - * pushing on the idle worker stack (represented by the lower - * 32bit subfield of ctl) when they cannot find work. The top - * stack state holds the value of the "scanState" field of the - * worker: its index and status, plus a version counter that, in - * addition to the count subfields (also serving as version - * stamps) provide protection against Treiber stack ABA effects. + * unless it is topmost on stack. To avoid missed signal problems + * inherent in any wait/signal design, available workers rescan + * for (and if found run) tasks after enqueuing. Normally their + * release status will be updated while doing so, but the released + * worker ctl count may underestimate the number of active + * threads. (However, it is still possible to determine quiescence + * via a validation traversal -- see isQuiescent). After an + * unsuccessful rescan, available workers are blocked until + * signalled (see signalWork). The top stack state holds the + * value of the "phase" field of the worker: its index and status, + * plus a version counter that, in addition to the count subfields + * (also serving as version stamps) provide protection against + * Treiber stack ABA effects. * - * Creating workers. To create a worker, we pre-increment total - * count (serving as a reservation), and attempt to construct a + * Creating workers. To create a worker, we pre-increment counts + * (serving as a reservation), and attempt to construct a * ForkJoinWorkerThread via its factory. Upon construction, the * new thread invokes registerWorker, where it constructs a * WorkQueue and is assigned an index in the workQueues array @@ -405,16 +404,15 @@ * submission queues for existing external threads (see * externalPush). * - * WorkQueue field scanState is used by both workers and the pool - * to manage and track whether a worker is UNSIGNALLED (possibly - * blocked waiting for a signal). When a worker is inactivated, - * its scanState field is set, and is prevented from executing - * tasks, even though it must scan once for them to avoid queuing - * races. Note that scanState updates lag queue CAS releases so - * usage requires care. When queued, the lower 16 bits of - * scanState must hold its pool index. So we place the index there - * upon initialization (see registerWorker) and otherwise keep it - * there or restore it when necessary. + * WorkQueue field "phase" is used by both workers and the pool to + * manage and track whether a worker is UNSIGNALLED (possibly + * blocked waiting for a signal). When a worker is enqueued its + * phase field is set. Note that phase field updates lag queue CAS + * releases so usage requires care -- seeing a negative phase does + * not guarantee that the worker is available. When queued, the + * lower 16 bits of scanState must hold its pool index. So we + * place the index there upon initialization (see registerWorker) + * and otherwise keep it there or restore it when necessary. * * The ctl field also serves as the basis for memory * synchronization surrounding activation. This uses a more @@ -423,15 +421,14 @@ * if to its current value). This would be extremely costly. So * we relax it in several ways: (1) Producers only signal when * their queue is empty. Other workers propagate this signal (in - * method scan) when they find tasks. (2) Workers only enqueue - * after scanning (see below) and not finding any tasks. (3) - * Rather than CASing ctl to its current value in the common case - * where no action is required, we reduce write contention by - * equivalently prefacing signalWork when called by an external - * task producer using a memory access with full-volatile - * semantics or a "fullFence". (4) For internal task producers we - * rely on the fact that even if no other workers awaken, the - * producer itself will eventually see the task and execute it. + * method scan) when they find tasks; to further reduce flailing, + * each worker signals only one other per activation. (2) Workers + * only enqueue after scanning (see below) and not finding any + * tasks. (3) Rather than CASing ctl to its current value in the + * common case where no action is required, we reduce write + * contention by equivalently prefacing signalWork when called by + * an external task producer using a memory access with + * full-volatile semantics or a "fullFence". * * Almost always, too many signals are issued. A task producer * cannot in general tell if some existing worker is in the midst @@ -443,64 +440,40 @@ * and bookkeeping bottlenecks during ramp-up, ramp-down, and small * computations involving only a few workers. * - * Scanning. Method scan() performs top-level scanning for tasks. - * Each scan traverses (and tries to poll from) each queue in - * pseudorandom permutation order by randomly selecting an origin - * index and a step value. (The pseudorandom generator need not - * have high-quality statistical properties in the long term, but - * just within computations; We use 64bit and 32bit Marsaglia - * XorShifts, which are cheap and suffice here.) Scanning also - * employs contention reduction: When scanning workers fail a CAS - * polling for work, they soon restart with a different - * pseudorandom scan order (thus likely retrying at different - * intervals). This improves throughput when many threads are - * trying to take tasks from few queues. Scans do not otherwise - * explicitly take into account core affinities, loads, cache - * localities, etc, However, they do exploit temporal locality - * (which usually approximates these) by preferring to re-poll (up - * to POLL_LIMIT times) from the same queue after a successful - * poll before trying others. Restricted forms of scanning occur - * in methods helpComplete and findNonEmptyStealQueue, and take - * similar but simpler forms. - * - * Deactivation and waiting. Queuing encounters several intrinsic - * races; most notably that an inactivating scanning worker can - * miss seeing a task produced during a scan. So when a worker - * cannot find a task to steal, it inactivates and enqueues, and - * then rescans to ensure that it didn't miss one, reactivating - * upon seeing one with probability approximately proportional to - * probability of a miss. (In most cases, the worker will be - * signalled before self-signalling, avoiding cascades of multiple - * signals for the same task). - * - * Workers block (in method awaitWork) using park/unpark; - * advertising the need for signallers to unpark by setting their - * "parker" fields. + * Scanning. Method runWorker performs top-level scanning for + * tasks. Each scan traverses and tries to poll from each queue + * starting at a random index and circularly stepping. Scans are + * not performed in ideal random permutation order, to reduce + * cacheline contention. The pseudorandom generator need not have + * high-quality statistical properties in the long term, but just + * within computations; We use Marsaglia XorShifts (often via + * ThreadLocalRandom.nextSecondarySeed), which are cheap and + * suffice. Scanning also employs contention reduction: When + * scanning workers fail to extract an apparently existing task, + * they soon restart at a different pseudorandom index. This + * improves throughput when many threads are trying to take tasks + * from few queues, which can be common in some usages. Scans do + * not otherwise explicitly take into account core affinities, + * loads, cache localities, etc, However, they do exploit temporal + * locality (which usually approximates these) by preferring to + * re-poll (at most #workers times) from the same queue after a + * successful poll before trying others. * * Trimming workers. To release resources after periods of lack of * use, a worker starting to wait when the pool is quiescent will - * time out and terminate (see awaitWork) if the pool has remained - * quiescent for period given by IDLE_TIMEOUT_MS, increasing the - * period as the number of threads decreases, eventually removing - * all workers. + * time out and terminate (see method scan) if the pool has + * remained quiescent for period given by field keepAlive. * * Shutdown and Termination. A call to shutdownNow invokes * tryTerminate to atomically set a runState bit. The calling * thread, as well as every other worker thereafter terminating, - * helps terminate others by setting their (qlock) status, - * cancelling their unprocessed tasks, and waking them up, doing - * so repeatedly until stable. Calls to non-abrupt shutdown() - * preface this by checking whether termination should commence. - * This relies primarily on the active count bits of "ctl" - * maintaining consensus -- tryTerminate is called from awaitWork - * whenever quiescent. However, external submitters do not take - * part in this consensus. So, tryTerminate sweeps through queues - * (until stable) to ensure lack of in-flight submissions and - * workers about to process them before triggering the "STOP" - * phase of termination. (Note: there is an intrinsic conflict if - * helpQuiescePool is called when shutdown is enabled. Both wait - * for quiescence, but tryTerminate is biased to not trigger until - * helpQuiescePool completes.) + * helps terminate others by cancelling their unprocessed tasks, + * and waking them up, doing so repeatedly until stable. Calls to + * non-abrupt shutdown() preface this by checking whether + * termination should commence by sweeping through queues (until + * stable) to ensure lack of in-flight submissions and workers + * about to process them before triggering the "STOP" phase of + * termination. * * Joining Tasks * ============= @@ -508,12 +481,12 @@ * Any of several actions may be taken when one worker is waiting * to join a task stolen (or always held) by another. Because we * are multiplexing many tasks on to a pool of workers, we can't - * just let them block (as in Thread.join). We also cannot just - * reassign the joiner's run-time stack with another and replace - * it later, which would be a form of "continuation", that even if - * possible is not necessarily a good idea since we may need both - * an unblocked task and its continuation to progress. Instead we - * combine two tactics: + * always just let them block (as in Thread.join). We also cannot + * just reassign the joiner's run-time stack with another and + * replace it later, which would be a form of "continuation", that + * even if possible is not necessarily a good idea since we may + * need both an unblocked task and its continuation to progress. + * Instead we combine two tactics: * * Helping: Arranging for the joiner to execute some task that it * would be running if the steal had not occurred. @@ -526,79 +499,43 @@ * helping a hypothetical compensator: If we can readily tell that * a possible action of a compensator is to steal and execute the * task being joined, the joining thread can do so directly, - * without the need for a compensation thread (although at the - * expense of larger run-time stacks, but the tradeoff is - * typically worthwhile). + * without the need for a compensation thread. * * The ManagedBlocker extension API can't use helping so relies * only on compensation in method awaitBlocker. * - * The algorithm in helpStealer entails a form of "linear - * helping". Each worker records (in field currentSteal) the most - * recent task it stole from some other worker (or a submission). - * It also records (in field currentJoin) the task it is currently - * actively joining. Method helpStealer uses these markers to try - * to find a worker to help (i.e., steal back a task from and - * execute it) that could hasten completion of the actively joined - * task. Thus, the joiner executes a task that would be on its - * own local deque had the to-be-joined task not been stolen. This - * is a conservative variant of the approach described in Wagner & - * Calder "Leapfrogging: a portable technique for implementing - * efficient futures" SIGPLAN Notices, 1993 - * (http://portal.acm.org/citation.cfm?id=155354). It differs in - * that: (1) We only maintain dependency links across workers upon - * steals, rather than use per-task bookkeeping. This sometimes - * requires a linear scan of workQueues array to locate stealers, - * but often doesn't because stealers leave hints (that may become - * stale/wrong) of where to locate them. It is only a hint - * because a worker might have had multiple steals and the hint - * records only one of them (usually the most current). Hinting - * isolates cost to when it is needed, rather than adding to - * per-task overhead. (2) It is "shallow", ignoring nesting and - * potentially cyclic mutual steals. (3) It is intentionally - * racy: field currentJoin is updated only while actively joining, - * which means that we miss links in the chain during long-lived - * tasks, GC stalls etc (which is OK since blocking in such cases - * is usually a good idea). (4) We bound the number of attempts - * to find work using checksums and fall back to suspending the - * worker and if necessary replacing it with another. + * The algorithm in awaitJoin entails a form of "linear helping". + * Each worker records (in field source) the id of the queue from + * which it last stole a task. The scan in method awaitJoin uses + * these markers to try to find a worker to help (i.e., steal back + * a task from and execute it) that could hasten completion of the + * actively joined task. Thus, the joiner executes a task that + * would be on its own local deque if the to-be-joined task had + * not been stolen. This is a conservative variant of the approach + * described in Wagner & Calder "Leapfrogging: a portable + * technique for implementing efficient futures" SIGPLAN Notices, + * 1993 (http://portal.acm.org/citation.cfm?id=155354). It differs + * mainly in that we only record queue ids, not full dependency + * links. This requires a linear scan of the workQueues array to + * locate stealers, but isolates cost to when it is needed, rather + * than adding to per-task overhead. Searches can fail to locate + * stealers GC stalls and the like delay recording sources. + * Further, even when accurately identified, stealers might not + * ever produce a task that the joiner can in turn help with. So, + * compensation is tried upon failure to find tasks to run. * - * Helping actions for CountedCompleters do not require tracking - * currentJoins: Method helpComplete takes and executes any task - * with the same root as the task being waited on (preferring - * local pops to non-local polls). However, this still entails - * some traversal of completer chains, so is less efficient than - * using CountedCompleters without explicit joins. - * - * Compensation does not aim to keep exactly the target + * Compensation does not by default aim to keep exactly the target * parallelism number of unblocked threads running at any given * time. Some previous versions of this class employed immediate * compensations for any blocked join. However, in practice, the * vast majority of blockages are transient byproducts of GC and * other JVM or OS activities that are made worse by replacement. - * Currently, compensation is attempted only after validating that - * all purportedly active threads are processing tasks by checking - * field WorkQueue.scanState, which eliminates most false - * positives. Also, compensation is bypassed (tolerating fewer - * threads) in the most common case in which it is rarely - * beneficial: when a worker with an empty queue (thus no - * continuation tasks) blocks on a join and there still remain - * enough threads to ensure liveness. - * - * Spare threads are removed as soon as they notice that the - * target parallelism level has been exceeded, in method - * tryDropSpare. (Method scan arranges returns for rechecks upon - * each probe via the "bound" parameter.) - * - * The compensation mechanism may be bounded. Bounds for the - * commonPool (see COMMON_MAX_SPARES) better enable JVMs to cope - * with programming errors and abuse before running out of - * resources to do so. In other cases, users may supply factories - * that limit thread construction. The effects of bounding in this - * pool (like all others) is imprecise. Total worker counts are - * decremented when threads deregister, not when they exit and - * resources are reclaimed by the JVM and OS. So the number of - * simultaneously live threads may transiently exceed bounds. + * Rather than impose arbitrary policies, we allow users to + * override the default of only adding threads upon apparent + * starvation. The compensation mechanism may also be bounded. + * Bounds for the commonPool (see COMMON_MAX_SPARES) better enable + * JVMs to cope with programming errors and abuse before running + * out of resources to do so. * * Common Pool * =========== @@ -606,9 +543,7 @@ * The static common pool always exists after static * initialization. Since it (or any other created pool) need * never be used, we minimize initial construction overhead and - * footprint to the setup of about a dozen fields, with no nested - * allocation. Most bootstrapping occurs within method - * externalSubmit during the first submission to the pool. + * footprint to the setup of about a dozen fields. * * When external threads submit to the common pool, they can * perform subtask processing (see externalHelpComplete and @@ -628,28 +563,22 @@ * InnocuousForkJoinWorkerThread when there is a SecurityManager * present. These workers have no permissions set, do not belong * to any user-defined ThreadGroup, and erase all ThreadLocals - * after executing any top-level task (see WorkQueue.runTask). - * The associated mechanics (mainly in ForkJoinWorkerThread) may - * be JVM-dependent and must access particular Thread class fields - * to achieve this effect. + * after executing any top-level task (see + * WorkQueue.afterTopLevelExec). The associated mechanics (mainly + * in ForkJoinWorkerThread) may be JVM-dependent and must access + * particular Thread class fields to achieve this effect. * * Style notes * =========== * - * Memory ordering relies mainly on Unsafe intrinsics that carry - * the further responsibility of explicitly performing null- and - * bounds- checks otherwise carried out implicitly by JVMs. This - * can be awkward and ugly, but also reflects the need to control + * Memory ordering relies mainly on VarHandles. This can be + * awkward and ugly, but also reflects the need to control * outcomes across the unusual cases that arise in very racy code - * with very few invariants. So these explicit checks would exist - * in some form anyway. All fields are read into locals before - * use, and null-checked if they are references. This is usually - * done in a "C"-like style of listing declarations at the heads - * of methods or blocks, and using inline assignments on first - * encounter. Array bounds-checks are usually performed by - * masking with array.length-1, which relies on the invariant that - * these arrays are created with positive lengths, which is itself - * paranoically checked. Nearly all explicit checks lead to + * with very few invariants. All fields are read into locals + * before use, and null-checked if they are references. This is + * usually done in a "C"-like style of listing declarations at the + * heads of methods or blocks, and using inline assignments on + * first encounter. Nearly all explicit checks lead to * bypass/return, not exception throws, because they may * legitimately arise due to cancellation/revocation during * shutdown. @@ -701,10 +630,17 @@ public static interface ForkJoinWorkerThreadFactory { /** * Returns a new worker thread operating in the given pool. + * Returning null or throwing an exception may result in tasks + * never being executed. If this method throws an exception, + * it is relayed to the caller of the method (for example + * {@code execute}) causing attempted thread creation. If this + * method returns null or throws an exception, it is not + * retried until the next attempted creation (for example + * another call to {@code execute}). * * @param pool the pool this thread works in * @return the new worker thread, or {@code null} if the request - * to create a thread is rejected + * to create a thread is rejected. * @throws NullPointerException if the pool is null */ public ForkJoinWorkerThread newThread(ForkJoinPool pool); @@ -721,56 +657,35 @@ } } - /** - * Class for artificial tasks that are used to replace the target - * of local joins if they are removed from an interior queue slot - * in WorkQueue.tryRemoveAndExec. We don't need the proxy to - * actually do anything beyond having a unique identity. - */ - private static final class EmptyTask extends ForkJoinTask { - private static final long serialVersionUID = -7721805057305804111L; - EmptyTask() { status = ForkJoinTask.NORMAL; } // force done - public final Void getRawResult() { return null; } - public final void setRawResult(Void x) {} - public final boolean exec() { return true; } - } - - /** - * Additional fields and lock created upon initialization. - */ - private static final class AuxState extends ReentrantLock { - private static final long serialVersionUID = -6001602636862214147L; - volatile long stealCount; // cumulative steal count - long indexSeed; // index bits for registerWorker - AuxState() {} - } - // Constants shared across ForkJoinPool and WorkQueue // Bounds + static final int SWIDTH = 16; // width of short static final int SMASK = 0xffff; // short bits == max index static final int MAX_CAP = 0x7fff; // max #workers - 1 - static final int EVENMASK = 0xfffe; // even short bits static final int SQMASK = 0x007e; // max 64 (even) slots - // Masks and units for WorkQueue.scanState and ctl sp subfield + // Masks and units for WorkQueue.phase and ctl sp subfield static final int UNSIGNALLED = 1 << 31; // must be negative static final int SS_SEQ = 1 << 16; // version count + static final int QLOCK = 1; // must be 1 - // Mode bits for ForkJoinPool.config and WorkQueue.config - static final int MODE_MASK = 0xffff << 16; // top half of int - static final int SPARE_WORKER = 1 << 17; // set if tc > 0 on creation - static final int UNREGISTERED = 1 << 18; // to skip some of deregister - static final int FIFO_QUEUE = 1 << 31; // must be negative - static final int LIFO_QUEUE = 0; // for clarity - static final int IS_OWNED = 1; // low bit 0 if shared + // Mode bits and sentinels, some also used in WorkQueue id and.source fields + static final int OWNED = 1; // queue has owner thread + static final int FIFO = 1 << 16; // fifo queue or access mode + static final int SHUTDOWN = 1 << 18; + static final int TERMINATED = 1 << 19; + static final int STOP = 1 << 31; // must be negative + static final int QUIET = 1 << 30; // not scanning or working + static final int DORMANT = QUIET | UNSIGNALLED; /** - * The maximum number of task executions from the same queue - * before checking other queues, bounding unfairness and impact of - * infinite user task recursion. Must be a power of two minus 1. + * The maximum number of local polls from the same queue before + * checking others. This is a safeguard against infinitely unfair + * looping under unbounded user task recursion, and must be larger + * than plausible cases of intentional bounded task recursion. */ - static final int POLL_LIMIT = (1 << 10) - 1; + static final int POLL_LIMIT = 1 << 10; /** * Queues supporting work-stealing as well as external task @@ -805,23 +720,16 @@ static final int MAXIMUM_QUEUE_CAPACITY = 1 << 26; // 64M // Instance fields - - volatile int scanState; // versioned, negative if inactive - int stackPred; // pool stack (ctl) predecessor + volatile int phase; // versioned, negative: queued, 1: locked + int stackPred; // pool stack (ctl) predecessor link int nsteals; // number of steals - int hint; // randomization and stealer index hint - int config; // pool index and mode - volatile int qlock; // 1: locked, < 0: terminate; else 0 + int id; // index, mode, tag + volatile int source; // source queue id, or sentinel volatile int base; // index of next slot for poll int top; // index of next slot for push ForkJoinTask[] array; // the elements (initially unallocated) final ForkJoinPool pool; // the containing pool (may be null) final ForkJoinWorkerThread owner; // owning thread or null if shared - volatile Thread parker; // == owner during call to park; else null - volatile ForkJoinTask currentJoin; // task being joined in awaitJoin - - @jdk.internal.vm.annotation.Contended("group2") // segregate - volatile ForkJoinTask currentSteal; // nonnull when running some task WorkQueue(ForkJoinPool pool, ForkJoinWorkerThread owner) { this.pool = pool; @@ -834,7 +742,7 @@ * Returns an exportable index (used by ForkJoinWorkerThread). */ final int getPoolIndex() { - return (config & 0xffff) >>> 1; // ignore odd/even tag bit + return (id & 0xffff) >>> 1; // ignore odd/even tag bit } /** @@ -851,13 +759,14 @@ * near-empty queue has at least one unclaimed task. */ final boolean isEmpty() { - ForkJoinTask[] a; int n, al, s; - return ((n = base - (s = top)) >= 0 || // possibly one task + ForkJoinTask[] a; int n, al, b; + return ((n = (b = base) - top) >= 0 || // possibly one task (n == -1 && ((a = array) == null || (al = a.length) == 0 || - a[(al - 1) & (s - 1)] == null))); + a[(al - 1) & b] == null))); } + /** * Pushes a task. Call only by owner in unshared queues. * @@ -865,17 +774,17 @@ * @throws RejectedExecutionException if array cannot be resized */ final void push(ForkJoinTask task) { - U.storeFence(); // ensure safe publication - int s = top, al, d; ForkJoinTask[] a; + int s = top; ForkJoinTask[] a; int al, d; if ((a = array) != null && (al = a.length) > 0) { - a[(al - 1) & s] = task; // relaxed writes OK - top = s + 1; + int index = (al - 1) & s; ForkJoinPool p = pool; + top = s + 1; + QA.setRelease(a, index, task); if ((d = base - s) == 0 && p != null) { - U.fullFence(); + VarHandle.fullFence(); p.signalWork(); } - else if (al + d == 1) + else if (d + al == 1) growArray(); } } @@ -887,24 +796,24 @@ */ final ForkJoinTask[] growArray() { ForkJoinTask[] oldA = array; - int size = oldA != null ? oldA.length << 1 : INITIAL_QUEUE_CAPACITY; + int oldSize = oldA != null ? oldA.length : 0; + int size = oldSize > 0 ? oldSize << 1 : INITIAL_QUEUE_CAPACITY; if (size < INITIAL_QUEUE_CAPACITY || size > MAXIMUM_QUEUE_CAPACITY) throw new RejectedExecutionException("Queue capacity exceeded"); int oldMask, t, b; ForkJoinTask[] a = array = new ForkJoinTask[size]; - if (oldA != null && (oldMask = oldA.length - 1) > 0 && + if (oldA != null && (oldMask = oldSize - 1) > 0 && (t = top) - (b = base) > 0) { int mask = size - 1; do { // emulate poll from old array, push to new array int index = b & oldMask; - long offset = ((long)index << ASHIFT) + ABASE; ForkJoinTask x = (ForkJoinTask) - U.getObjectVolatile(oldA, offset); + QA.getAcquire(oldA, index); if (x != null && - U.compareAndSwapObject(oldA, offset, x, null)) + QA.compareAndSet(oldA, index, x, null)) a[b & mask] = x; } while (++b != t); - U.storeFence(); + VarHandle.releaseFence(); } return a; } @@ -917,33 +826,12 @@ int b = base, s = top, al, i; ForkJoinTask[] a; if ((a = array) != null && b != s && (al = a.length) > 0) { int index = (al - 1) & --s; - long offset = ((long)index << ASHIFT) + ABASE; ForkJoinTask t = (ForkJoinTask) - U.getObject(a, offset); + QA.get(a, index); if (t != null && - U.compareAndSwapObject(a, offset, t, null)) { + QA.compareAndSet(a, index, t, null)) { top = s; - return t; - } - } - return null; - } - - /** - * Takes a task in FIFO order if b is base of queue and a task - * can be claimed without contention. Specialized versions - * appear in ForkJoinPool methods scan and helpStealer. - */ - final ForkJoinTask pollAt(int b) { - ForkJoinTask[] a; int al; - if ((a = array) != null && (al = a.length) > 0) { - int index = (al - 1) & b; - long offset = ((long)index << ASHIFT) + ABASE; - ForkJoinTask t = (ForkJoinTask) - U.getObjectVolatile(a, offset); - if (t != null && b++ == base && - U.compareAndSwapObject(a, offset, t, null)) { - base = b; + VarHandle.releaseFence(); return t; } } @@ -959,12 +847,11 @@ if ((a = array) != null && (d = b - s) < 0 && (al = a.length) > 0) { int index = (al - 1) & b; - long offset = ((long)index << ASHIFT) + ABASE; ForkJoinTask t = (ForkJoinTask) - U.getObjectVolatile(a, offset); + QA.getAcquire(a, index); if (b++ == base) { if (t != null) { - if (U.compareAndSwapObject(a, offset, t, null)) { + if (QA.compareAndSet(a, index, t, null)) { base = b; return t; } @@ -983,7 +870,7 @@ * Takes next task, if one exists, in order specified by mode. */ final ForkJoinTask nextLocalTask() { - return (config < 0) ? poll() : pop(); + return ((id & FIFO) != 0) ? poll() : pop(); } /** @@ -992,7 +879,8 @@ final ForkJoinTask peek() { int al; ForkJoinTask[] a; return ((a = array) != null && (al = a.length) > 0) ? - a[(al - 1) & (config < 0 ? base : top - 1)] : null; + a[(al - 1) & + ((id & FIFO) != 0 ? base : top - 1)] : null; } /** @@ -1002,9 +890,9 @@ int b = base, s = top, al; ForkJoinTask[] a; if ((a = array) != null && b != s && (al = a.length) > 0) { int index = (al - 1) & --s; - long offset = ((long)index << ASHIFT) + ABASE; - if (U.compareAndSwapObject(a, offset, task, null)) { + if (QA.compareAndSet(a, index, task, null)) { top = s; + VarHandle.releaseFence(); return true; } } @@ -1012,105 +900,32 @@ } /** - * Shared version of push. Fails if already locked. - * - * @return status: > 0 locked, 0 possibly was empty, < 0 was nonempty - */ - final int sharedPush(ForkJoinTask task) { - int stat; - if (U.compareAndSwapInt(this, QLOCK, 0, 1)) { - int b = base, s = top, al, d; ForkJoinTask[] a; - if ((a = array) != null && (al = a.length) > 0 && - al - 1 + (d = b - s) > 0) { - a[(al - 1) & s] = task; - top = s + 1; // relaxed writes OK here - qlock = 0; - stat = (d < 0 && b == base) ? d : 0; - } - else { - growAndSharedPush(task); - stat = 0; - } - } - else - stat = 1; - return stat; - } - - /** - * Helper for sharedPush; called only when locked and resize - * needed. - */ - private void growAndSharedPush(ForkJoinTask task) { - try { - growArray(); - int s = top, al; ForkJoinTask[] a; - if ((a = array) != null && (al = a.length) > 0) { - a[(al - 1) & s] = task; - top = s + 1; - } - } finally { - qlock = 0; - } - } - - /** - * Shared version of tryUnpush. - */ - final boolean trySharedUnpush(ForkJoinTask task) { - boolean popped = false; - int s = top - 1, al; ForkJoinTask[] a; - if ((a = array) != null && (al = a.length) > 0) { - int index = (al - 1) & s; - long offset = ((long)index << ASHIFT) + ABASE; - ForkJoinTask t = (ForkJoinTask) U.getObject(a, offset); - if (t == task && - U.compareAndSwapInt(this, QLOCK, 0, 1)) { - if (top == s + 1 && array == a && - U.compareAndSwapObject(a, offset, task, null)) { - popped = true; - top = s; - } - U.putIntRelease(this, QLOCK, 0); - } - } - return popped; - } - - /** * Removes and cancels all known tasks, ignoring any exceptions. */ final void cancelAll() { - ForkJoinTask t; - if ((t = currentJoin) != null) { - currentJoin = null; - ForkJoinTask.cancelIgnoringExceptions(t); - } - if ((t = currentSteal) != null) { - currentSteal = null; - ForkJoinTask.cancelIgnoringExceptions(t); - } - while ((t = poll()) != null) + for (ForkJoinTask t; (t = poll()) != null; ) ForkJoinTask.cancelIgnoringExceptions(t); } // Specialized execution methods /** - * Pops and executes up to POLL_LIMIT tasks or until empty. + * Pops and executes up to limit consecutive tasks or until empty. + * + * @param limit max runs, or zero for no limit */ - final void localPopAndExec() { - for (int nexec = 0;;) { + final void localPopAndExec(int limit) { + for (;;) { int b = base, s = top, al; ForkJoinTask[] a; if ((a = array) != null && b != s && (al = a.length) > 0) { int index = (al - 1) & --s; - long offset = ((long)index << ASHIFT) + ABASE; ForkJoinTask t = (ForkJoinTask) - U.getAndSetObject(a, offset, null); + QA.getAndSet(a, index, null); if (t != null) { top = s; - (currentSteal = t).doExec(); - if (++nexec > POLL_LIMIT) + VarHandle.releaseFence(); + t.doExec(); + if (limit != 0 && --limit == 0) break; } else @@ -1122,22 +937,28 @@ } /** - * Polls and executes up to POLL_LIMIT tasks or until empty. + * Polls and executes up to limit consecutive tasks or until empty. + * + * @param limit, or zero for no limit */ - final void localPollAndExec() { - for (int nexec = 0;;) { - int b = base, s = top, al; ForkJoinTask[] a; - if ((a = array) != null && b != s && (al = a.length) > 0) { + final void localPollAndExec(int limit) { + for (int polls = 0;;) { + int b = base, s = top, d, al; ForkJoinTask[] a; + if ((a = array) != null && (d = b - s) < 0 && + (al = a.length) > 0) { int index = (al - 1) & b++; - long offset = ((long)index << ASHIFT) + ABASE; ForkJoinTask t = (ForkJoinTask) - U.getAndSetObject(a, offset, null); + QA.getAndSet(a, index, null); if (t != null) { base = b; t.doExec(); - if (++nexec > POLL_LIMIT) + if (limit != 0 && ++polls == limit) break; } + else if (d == -1) + break; // now empty + else + polls = 0; // stolen; reset } else break; @@ -1145,188 +966,156 @@ } /** - * Executes the given task and (some) remaining local tasks. + * If present, removes task from queue and executes it. */ - final void runTask(ForkJoinTask task) { - if (task != null) { - task.doExec(); - if (config < 0) - localPollAndExec(); - else - localPopAndExec(); - int ns = ++nsteals; - ForkJoinWorkerThread thread = owner; - currentSteal = null; - if (ns < 0) // collect on overflow - transferStealCount(pool); - if (thread != null) - thread.afterTopLevelExec(); - } - } - - /** - * Adds steal count to pool steal count if it exists, and resets. - */ - final void transferStealCount(ForkJoinPool p) { - AuxState aux; - if (p != null && (aux = p.auxState) != null) { - long s = nsteals; - nsteals = 0; // if negative, correct for overflow - if (s < 0) s = Integer.MAX_VALUE; - aux.lock(); - try { - aux.stealCount += s; - } finally { - aux.unlock(); + final void tryRemoveAndExec(ForkJoinTask task) { + ForkJoinTask[] wa; int s, wal; + if (base - (s = top) < 0 && // traverse from top + (wa = array) != null && (wal = wa.length) > 0) { + for (int m = wal - 1, ns = s - 1, i = ns; ; --i) { + int index = i & m; + ForkJoinTask t = (ForkJoinTask) + QA.get(wa, index); + if (t == null) + break; + else if (t == task) { + if (QA.compareAndSet(wa, index, t, null)) { + top = ns; // safely shift down + for (int j = i; j != ns; ++j) { + ForkJoinTask f; + int pindex = (j + 1) & m; + f = (ForkJoinTask)QA.get(wa, pindex); + QA.setVolatile(wa, pindex, null); + int jindex = j & m; + QA.setRelease(wa, jindex, f); + } + VarHandle.releaseFence(); + t.doExec(); + } + break; + } } } } /** - * If present, removes from queue and executes the given task, - * or any other cancelled task. Used only by awaitJoin. + * Tries to steal and run tasks within the target's + * computation until done, not found, or limit exceeded. * - * @return true if queue empty and task not known to be done + * @param task root of CountedCompleter computation + * @param limit max runs, or zero for no limit + * @return task status on exit */ - final boolean tryRemoveAndExec(ForkJoinTask task) { - if (task != null && task.status >= 0) { - int b, s, d, al; ForkJoinTask[] a; - while ((d = (b = base) - (s = top)) < 0 && - (a = array) != null && (al = a.length) > 0) { - for (;;) { // traverse from s to b - int index = --s & (al - 1); - long offset = (index << ASHIFT) + ABASE; - ForkJoinTask t = (ForkJoinTask) - U.getObjectVolatile(a, offset); - if (t == null) - break; // restart - else if (t == task) { - boolean removed = false; - if (s + 1 == top) { // pop - if (U.compareAndSwapObject(a, offset, t, null)) { - top = s; - removed = true; + final int localHelpCC(CountedCompleter task, int limit) { + int status = 0; + if (task != null && (status = task.status) >= 0) { + for (;;) { + boolean help = false; + int b = base, s = top, al; ForkJoinTask[] a; + if ((a = array) != null && b != s && (al = a.length) > 0) { + int index = (al - 1) & (s - 1); + ForkJoinTask o = (ForkJoinTask) + QA.get(a, index); + if (o instanceof CountedCompleter) { + CountedCompleter t = (CountedCompleter)o; + for (CountedCompleter f = t;;) { + if (f != task) { + if ((f = f.completer) == null) // try parent + break; + } + else { + if (QA.compareAndSet(a, index, t, null)) { + top = s - 1; + VarHandle.releaseFence(); + t.doExec(); + help = true; + } + break; } } - else if (base == b) // replace with proxy - removed = U.compareAndSwapObject(a, offset, t, - new EmptyTask()); - if (removed) { - ForkJoinTask ps = currentSteal; - (currentSteal = task).doExec(); - currentSteal = ps; - } - break; - } - else if (t.status < 0 && s + 1 == top) { - if (U.compareAndSwapObject(a, offset, t, null)) { - top = s; - } - break; // was cancelled - } - else if (++d == 0) { - if (base != b) // rescan - break; - return false; } } - if (task.status < 0) - return false; + if ((status = task.status) < 0 || !help || + (limit != 0 && --limit == 0)) + break; } } - return true; + return status; + } + + // Operations on shared queues + + /** + * Tries to lock shared queue by CASing phase field. + */ + final boolean tryLockSharedQueue() { + return PHASE.compareAndSet(this, 0, QLOCK); } /** - * Pops task if in the same CC computation as the given task, - * in either shared or owned mode. Used only by helpComplete. + * Shared version of tryUnpush. */ - final CountedCompleter popCC(CountedCompleter task, int mode) { - int b = base, s = top, al; ForkJoinTask[] a; - if ((a = array) != null && b != s && (al = a.length) > 0) { - int index = (al - 1) & (s - 1); - long offset = ((long)index << ASHIFT) + ABASE; - ForkJoinTask o = (ForkJoinTask) - U.getObjectVolatile(a, offset); - if (o instanceof CountedCompleter) { - CountedCompleter t = (CountedCompleter)o; - for (CountedCompleter r = t;;) { - if (r == task) { - if ((mode & IS_OWNED) == 0) { - boolean popped = false; - if (U.compareAndSwapInt(this, QLOCK, 0, 1)) { - if (top == s && array == a && - U.compareAndSwapObject(a, offset, - t, null)) { - popped = true; - top = s - 1; - } - U.putIntRelease(this, QLOCK, 0); - if (popped) - return t; - } - } - else if (U.compareAndSwapObject(a, offset, - t, null)) { - top = s - 1; - return t; - } - break; - } - else if ((r = r.completer) == null) // try parent - break; + final boolean trySharedUnpush(ForkJoinTask task) { + boolean popped = false; + int s = top - 1, al; ForkJoinTask[] a; + if ((a = array) != null && (al = a.length) > 0) { + int index = (al - 1) & s; + ForkJoinTask t = (ForkJoinTask) QA.get(a, index); + if (t == task && + PHASE.compareAndSet(this, 0, QLOCK)) { + if (top == s + 1 && array == a && + QA.compareAndSet(a, index, task, null)) { + popped = true; + top = s; } + PHASE.setRelease(this, 0); } } - return null; + return popped; } /** - * Steals and runs a task in the same CC computation as the - * given task if one exists and can be taken without - * contention. Otherwise returns a checksum/control value for - * use by method helpComplete. - * - * @return 1 if successful, 2 if retryable (lost to another - * stealer), -1 if non-empty but no matching task found, else - * the base index, forced negative. + * Shared version of localHelpCC. */ - final int pollAndExecCC(CountedCompleter task) { - ForkJoinTask[] a; - int b = base, s = top, al, h; - if ((a = array) != null && b != s && (al = a.length) > 0) { - int index = (al - 1) & b; - long offset = ((long)index << ASHIFT) + ABASE; - ForkJoinTask o = (ForkJoinTask) - U.getObjectVolatile(a, offset); - if (o == null) - h = 2; // retryable - else if (!(o instanceof CountedCompleter)) - h = -1; // unmatchable - else { - CountedCompleter t = (CountedCompleter)o; - for (CountedCompleter r = t;;) { - if (r == task) { - if (b++ == base && - U.compareAndSwapObject(a, offset, t, null)) { - base = b; - t.doExec(); - h = 1; // success + final int sharedHelpCC(CountedCompleter task, int limit) { + int status = 0; + if (task != null && (status = task.status) >= 0) { + for (;;) { + boolean help = false; + int b = base, s = top, al; ForkJoinTask[] a; + if ((a = array) != null && b != s && (al = a.length) > 0) { + int index = (al - 1) & (s - 1); + ForkJoinTask o = (ForkJoinTask) + QA.get(a, index); + if (o instanceof CountedCompleter) { + CountedCompleter t = (CountedCompleter)o; + for (CountedCompleter f = t;;) { + if (f != task) { + if ((f = f.completer) == null) + break; + } + else { + if (PHASE.compareAndSet(this, 0, QLOCK)) { + if (top == s && array == a && + QA.compareAndSet(a, index, t, null)) { + help = true; + top = s - 1; + } + PHASE.setRelease(this, 0); + if (help) + t.doExec(); + } + break; + } } - else - h = 2; // lost CAS - break; - } - else if ((r = r.completer) == null) { - h = -1; // unmatched - break; } } + if ((status = task.status) < 0 || !help || + (limit != 0 && --limit == 0)) + break; } } - else - h = b | Integer.MIN_VALUE; // to sense movement on re-poll - return h; + return status; } /** @@ -1334,27 +1123,18 @@ */ final boolean isApparentlyUnblocked() { Thread wt; Thread.State s; - return (scanState >= 0 && - (wt = owner) != null && + return ((wt = owner) != null && (s = wt.getState()) != Thread.State.BLOCKED && s != Thread.State.WAITING && s != Thread.State.TIMED_WAITING); } - // Unsafe mechanics. Note that some are (and must be) the same as in FJP - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long QLOCK; - private static final int ABASE; - private static final int ASHIFT; + // VarHandle mechanics. + private static final VarHandle PHASE; static { try { - QLOCK = U.objectFieldOffset - (WorkQueue.class.getDeclaredField("qlock")); - ABASE = U.arrayBaseOffset(ForkJoinTask[].class); - int scale = U.arrayIndexScale(ForkJoinTask[].class); - if ((scale & (scale - 1)) != 0) - throw new Error("array index scale not a power of two"); - ASHIFT = 31 - Integer.numberOfLeadingZeros(scale); + MethodHandles.Lookup l = MethodHandles.lookup(); + PHASE = l.findVarHandle(WorkQueue.class, "phase", int.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -1372,7 +1152,7 @@ /** * Permission required for callers of methods that may start or - * kill threads. Also used as a static lock in tryInitialize. + * kill threads. */ static final RuntimePermission modifyThreadPermission; @@ -1413,18 +1193,15 @@ // static configuration constants /** - * Initial timeout value (in milliseconds) for the thread - * triggering quiescence to park waiting for new work. On timeout, - * the thread will instead try to shrink the number of workers. - * The value should be large enough to avoid overly aggressive - * shrinkage during most transient stalls (long GCs etc). + * Default idle timeout value (in milliseconds) for the thread + * triggering quiescence to park waiting for new work */ - private static final long IDLE_TIMEOUT_MS = 2000L; // 2sec + private static final long DEFAULT_KEEPALIVE = 60000L; /** - * Tolerance for idle timeouts, to cope with timer undershoots. + * Undershoot tolerance for idle timeouts */ - private static final long TIMEOUT_SLOP_MS = 20L; // 20ms + private static final long TIMEOUT_SLOP = 20L; /** * The default value for COMMON_MAX_SPARES. Overridable using the @@ -1444,7 +1221,7 @@ /* * Bits and masks for field ctl, packed with 4 16 bit subfields: - * AC: Number of active running workers minus target parallelism + * RC: Number of released (unqueued) workers minus target parallelism * TC: Number of total workers minus target parallelism * SS: version count and status of top waiting thread * ID: poolIndex of top of Treiber stack of waiters @@ -1453,26 +1230,30 @@ * (including version bits) as sp=(int)ctl. The offsets of counts * by the target parallelism and the positionings of fields makes * it possible to perform the most common checks via sign tests of - * fields: When ac is negative, there are not enough active + * fields: When ac is negative, there are not enough unqueued * workers, when tc is negative, there are not enough total * workers. When sp is non-zero, there are waiting workers. To * deal with possibly negative fields, we use casts in and out of * "short" and/or signed shifts to maintain signedness. * - * Because it occupies uppermost bits, we can add one active count - * using getAndAddLong of AC_UNIT, rather than CAS, when returning + * Because it occupies uppermost bits, we can add one release count + * using getAndAddLong of RC_UNIT, rather than CAS, when returning * from a blocked join. Other updates entail multiple subfields * and masking, requiring CAS. + * + * The limits packed in field "bounds" are also offset by the + * parallelism level to make them comparable to the ctl rc and tc + * fields. */ // Lower and upper word masks private static final long SP_MASK = 0xffffffffL; private static final long UC_MASK = ~SP_MASK; - // Active counts - private static final int AC_SHIFT = 48; - private static final long AC_UNIT = 0x0001L << AC_SHIFT; - private static final long AC_MASK = 0xffffL << AC_SHIFT; + // Release counts + private static final int RC_SHIFT = 48; + private static final long RC_UNIT = 0x0001L << RC_SHIFT; + private static final long RC_MASK = 0xffffL << RC_SHIFT; // Total counts private static final int TC_SHIFT = 32; @@ -1480,52 +1261,21 @@ private static final long TC_MASK = 0xffffL << TC_SHIFT; private static final long ADD_WORKER = 0x0001L << (TC_SHIFT + 15); // sign - // runState bits: SHUTDOWN must be negative, others arbitrary powers of two - private static final int STARTED = 1; - private static final int STOP = 1 << 1; - private static final int TERMINATED = 1 << 2; - private static final int SHUTDOWN = 1 << 31; + // Instance fields - // Instance fields - volatile long ctl; // main pool control - volatile int runState; - final int config; // parallelism, mode - AuxState auxState; // lock, steal counts - volatile WorkQueue[] workQueues; // main registry - final String workerNamePrefix; // to create worker name string + volatile long stealCount; // collects worker nsteals + final long keepAlive; // milliseconds before dropping if idle + int indexSeed; // next worker index + final int bounds; // min, max threads packed as shorts + volatile int mode; // parallelism, runstate, queue mode + WorkQueue[] workQueues; // main registry + final String workerNamePrefix; // for worker thread string; sync lock final ForkJoinWorkerThreadFactory factory; final UncaughtExceptionHandler ueh; // per-worker UEH + final Predicate saturate; - /** - * Instantiates fields upon first submission, or upon shutdown if - * no submissions. If checkTermination true, also responds to - * termination by external calls submitting tasks. - */ - private void tryInitialize(boolean checkTermination) { - if (runState == 0) { // bootstrap by locking static field - int p = config & SMASK; - int n = (p > 1) ? p - 1 : 1; // ensure at least 2 slots - n |= n >>> 1; // create workQueues array with size a power of two - n |= n >>> 2; - n |= n >>> 4; - n |= n >>> 8; - n |= n >>> 16; - n = ((n + 1) << 1) & SMASK; - AuxState aux = new AuxState(); - WorkQueue[] ws = new WorkQueue[n]; - synchronized (modifyThreadPermission) { // double-check - if (runState == 0) { - workQueues = ws; - auxState = aux; - runState = STARTED; - } - } - } - if (checkTermination && runState < 0) { - tryTerminate(false, false); // help terminate - throw new RejectedExecutionException(); - } - } + @jdk.internal.vm.annotation.Contended("fjpctl") // segregate + volatile long ctl; // main pool control // Creating, registering and deregistering workers @@ -1534,18 +1284,14 @@ * count has already been incremented as a reservation. Invokes * deregisterWorker on any failure. * - * @param isSpare true if this is a spare thread * @return true if successful */ - private boolean createWorker(boolean isSpare) { + private boolean createWorker() { ForkJoinWorkerThreadFactory fac = factory; Throwable ex = null; ForkJoinWorkerThread wt = null; - WorkQueue q; try { if (fac != null && (wt = fac.newThread(this)) != null) { - if (isSpare && (q = wt.workQueue) != null) - q.config |= SPARE_WORKER; wt.start(); return true; } @@ -1566,10 +1312,10 @@ */ private void tryAddWorker(long c) { do { - long nc = ((AC_MASK & (c + AC_UNIT)) | + long nc = ((RC_MASK & (c + RC_UNIT)) | (TC_MASK & (c + TC_UNIT))); - if (ctl == c && U.compareAndSwapLong(this, CTL, c, nc)) { - createWorker(false); + if (ctl == c && CTL.compareAndSet(this, c, nc)) { + createWorker(); break; } } while (((c = ctl) & ADD_WORKER) != 0L && (int)c == 0); @@ -1584,41 +1330,57 @@ */ final WorkQueue registerWorker(ForkJoinWorkerThread wt) { UncaughtExceptionHandler handler; - AuxState aux; - wt.setDaemon(true); // configure thread + wt.setDaemon(true); // configure thread if ((handler = ueh) != null) wt.setUncaughtExceptionHandler(handler); WorkQueue w = new WorkQueue(this, wt); - int i = 0; // assign a pool index - int mode = config & MODE_MASK; - if ((aux = auxState) != null) { - aux.lock(); - try { - int s = (int)(aux.indexSeed += SEED_INCREMENT), n, m; - WorkQueue[] ws = workQueues; - if (ws != null && (n = ws.length) > 0) { - i = (m = n - 1) & ((s << 1) | 1); // odd-numbered indices - if (ws[i] != null) { // collision - int probes = 0; // step by approx half n - int step = (n <= 4) ? 2 : ((n >>> 1) & EVENMASK) + 2; - while (ws[i = (i + step) & m] != null) { - if (++probes >= n) { - workQueues = ws = Arrays.copyOf(ws, n <<= 1); - m = n - 1; - probes = 0; - } + int tid = 0; // for thread name + int fifo = mode & FIFO; + String prefix = workerNamePrefix; + if (prefix != null) { + synchronized (prefix) { + WorkQueue[] ws = workQueues; int n; + int s = indexSeed += SEED_INCREMENT; + if (ws != null && (n = ws.length) > 1) { + int m = n - 1; + tid = s & m; + int i = m & ((s << 1) | 1); // odd-numbered indices + for (int probes = n >>> 1;;) { // find empty slot + WorkQueue q; + if ((q = ws[i]) == null || q.phase == QUIET) + break; + else if (--probes == 0) { + i = n | 1; // resize below + break; } + else + i = (i + 2) & m; } - w.hint = s; // use as random seed - w.config = i | mode; - w.scanState = i | (s & 0x7fff0000); // random seq bits - ws[i] = w; + + int id = i | fifo | (s & ~(SMASK | FIFO | DORMANT)); + w.phase = w.id = id; // now publishable + + if (i < n) + ws[i] = w; + else { // expand array + int an = n << 1; + WorkQueue[] as = new WorkQueue[an]; + as[i] = w; + int am = an - 1; + for (int j = 0; j < n; ++j) { + WorkQueue v; // copy external queue + if ((v = ws[j]) != null) // position may change + as[v.id & am & SQMASK] = v; + if (++j >= n) + break; + as[j] = ws[j]; // copy worker + } + workQueues = as; + } } - } finally { - aux.unlock(); } + wt.setName(prefix.concat(Integer.toString(tid))); } - wt.setName(workerNamePrefix.concat(Integer.toString(i >>> 1))); return w; } @@ -1633,64 +1395,48 @@ */ final void deregisterWorker(ForkJoinWorkerThread wt, Throwable ex) { WorkQueue w = null; + int phase = 0; if (wt != null && (w = wt.workQueue) != null) { - AuxState aux; WorkQueue[] ws; // remove index from array - int idx = w.config & SMASK; - int ns = w.nsteals; - if ((aux = auxState) != null) { - aux.lock(); - try { + Object lock = workerNamePrefix; + long ns = (long)w.nsteals & 0xffffffffL; + int idx = w.id & SMASK; + if (lock != null) { + WorkQueue[] ws; // remove index from array + synchronized (lock) { if ((ws = workQueues) != null && ws.length > idx && ws[idx] == w) ws[idx] = null; - aux.stealCount += ns; - } finally { - aux.unlock(); + stealCount += ns; } } - } - if (w == null || (w.config & UNREGISTERED) == 0) { // else pre-adjusted - long c; // decrement counts - do {} while (!U.compareAndSwapLong - (this, CTL, c = ctl, ((AC_MASK & (c - AC_UNIT)) | - (TC_MASK & (c - TC_UNIT)) | - (SP_MASK & c)))); - } - if (w != null) { - w.currentSteal = null; - w.qlock = -1; // ensure set - w.cancelAll(); // cancel remaining tasks + phase = w.phase; } - while (tryTerminate(false, false) >= 0) { // possibly replace - WorkQueue[] ws; int wl, sp; long c; - if (w == null || w.array == null || - (ws = workQueues) == null || (wl = ws.length) <= 0) - break; - else if ((sp = (int)(c = ctl)) != 0) { // wake up replacement - if (tryRelease(c, ws[(wl - 1) & sp], AC_UNIT)) - break; - } - else if (ex != null && (c & ADD_WORKER) != 0L) { - tryAddWorker(c); // create replacement - break; - } - else // don't need replacement - break; + if (phase != QUIET) { // else pre-adjusted + long c; // decrement counts + do {} while (!CTL.weakCompareAndSetVolatile + (this, c = ctl, ((RC_MASK & (c - RC_UNIT)) | + (TC_MASK & (c - TC_UNIT)) | + (SP_MASK & c)))); } + if (w != null) + w.cancelAll(); // cancel remaining tasks + + if (!tryTerminate(false, false) && // possibly replace worker + w != null && w.array != null) // avoid repeated failures + signalWork(); + if (ex == null) // help clean on way out ForkJoinTask.helpExpungeStaleExceptions(); else // rethrow ForkJoinTask.rethrow(ex); } - // Signalling - /** - * Tries to create or activate a worker if too few are active. + * Tries to create or release a worker if too few are running. */ final void signalWork() { for (;;) { - long c; int sp, i; WorkQueue v; WorkQueue[] ws; + long c; int sp; WorkQueue[] ws; int i; WorkQueue v; if ((c = ctl) >= 0L) // enough workers break; else if ((sp = (int)c) == 0) { // no idle workers @@ -1705,12 +1451,14 @@ else if ((v = ws[i]) == null) break; // terminating else { - int ns = sp & ~UNSIGNALLED; - int vs = v.scanState; - long nc = (v.stackPred & SP_MASK) | (UC_MASK & (c + AC_UNIT)); - if (sp == vs && U.compareAndSwapLong(this, CTL, c, nc)) { - v.scanState = ns; - LockSupport.unpark(v.parker); + int np = sp & ~UNSIGNALLED; + int vp = v.phase; + long nc = (v.stackPred & SP_MASK) | (UC_MASK & (c + RC_UNIT)); + Thread vt = v.owner; + if (sp == vp && CTL.compareAndSet(this, c, nc)) { + v.phase = np; + if (v.source < 0) + LockSupport.unpark(vt); break; } } @@ -1718,442 +1466,183 @@ } /** - * Signals and releases worker v if it is top of idle worker - * stack. This performs a one-shot version of signalWork only if - * there is (apparently) at least one idle worker. + * Tries to decrement counts (sometimes implicitly) and possibly + * arrange for a compensating worker in preparation for blocking: + * If not all core workers yet exist, creates one, else if any are + * unreleased (possibly including caller) releases one, else if + * fewer than the minimum allowed number of workers running, + * checks to see that they are all active, and if so creates an + * extra worker unless over maximum limit and policy is to + * saturate. Most of these steps can fail due to interference, in + * which case 0 is returned so caller will retry. A negative + * return value indicates that the caller doesn't need to + * re-adjust counts when later unblocked. * - * @param c incoming ctl value - * @param v if non-null, a worker - * @param inc the increment to active count (zero when compensating) - * @return true if successful - */ - private boolean tryRelease(long c, WorkQueue v, long inc) { - int sp = (int)c, ns = sp & ~UNSIGNALLED; - if (v != null) { - int vs = v.scanState; - long nc = (v.stackPred & SP_MASK) | (UC_MASK & (c + inc)); - if (sp == vs && U.compareAndSwapLong(this, CTL, c, nc)) { - v.scanState = ns; - LockSupport.unpark(v.parker); - return true; - } - } - return false; - } - - /** - * With approx probability of a missed signal, tries (once) to - * reactivate worker w (or some other worker), failing if stale or - * known to be already active. - * - * @param w the worker - * @param ws the workQueue array to use - * @param r random seed - */ - private void tryReactivate(WorkQueue w, WorkQueue[] ws, int r) { - long c; int sp, wl; WorkQueue v; - if ((sp = (int)(c = ctl)) != 0 && w != null && - ws != null && (wl = ws.length) > 0 && - ((sp ^ r) & SS_SEQ) == 0 && - (v = ws[(wl - 1) & sp]) != null) { - long nc = (v.stackPred & SP_MASK) | (UC_MASK & (c + AC_UNIT)); - int ns = sp & ~UNSIGNALLED; - if (w.scanState < 0 && - v.scanState == sp && - U.compareAndSwapLong(this, CTL, c, nc)) { - v.scanState = ns; - LockSupport.unpark(v.parker); - } - } - } - - /** - * If worker w exists and is active, enqueues and sets status to inactive. - * - * @param w the worker - * @param ss current (non-negative) scanState + * @return 1: block then adjust, -1: block without adjust, 0 : retry */ - private void inactivate(WorkQueue w, int ss) { - int ns = (ss + SS_SEQ) | UNSIGNALLED; - long lc = ns & SP_MASK, nc, c; - if (w != null) { - w.scanState = ns; - do { - nc = lc | (UC_MASK & ((c = ctl) - AC_UNIT)); - w.stackPred = (int)c; - } while (!U.compareAndSwapLong(this, CTL, c, nc)); - } - } - - /** - * Possibly blocks worker w waiting for signal, or returns - * negative status if the worker should terminate. May return - * without status change if multiple stale unparks and/or - * interrupts occur. - * - * @param w the calling worker - * @return negative if w should terminate - */ - private int awaitWork(WorkQueue w) { - int stat = 0; - if (w != null && w.scanState < 0) { - long c = ctl; - if ((int)(c >> AC_SHIFT) + (config & SMASK) <= 0) - stat = timedAwaitWork(w, c); // possibly quiescent - else if ((runState & STOP) != 0) - stat = w.qlock = -1; // pool terminating - else if (w.scanState < 0) { - w.parker = Thread.currentThread(); - if (w.scanState < 0) // recheck after write - LockSupport.park(this); - w.parker = null; - if ((runState & STOP) != 0) - stat = w.qlock = -1; // recheck - else if (w.scanState < 0) - Thread.interrupted(); // clear status + private int tryCompensate(WorkQueue w) { + int t, n, sp; + long c = ctl; + WorkQueue[] ws = workQueues; + if ((t = (short)(c >>> TC_SHIFT)) >= 0) { + if (ws == null || (n = ws.length) <= 0 || w == null) + return 0; // disabled + else if ((sp = (int)c) != 0) { // replace or release + WorkQueue v = ws[sp & (n - 1)]; + int wp = w.phase; + long uc = UC_MASK & ((wp < 0) ? c + RC_UNIT : c); + int np = sp & ~UNSIGNALLED; + if (v != null) { + int vp = v.phase; + Thread vt = v.owner; + long nc = ((long)v.stackPred & SP_MASK) | uc; + if (vp == sp && CTL.compareAndSet(this, c, nc)) { + v.phase = np; + if (v.source < 0) + LockSupport.unpark(vt); + return (wp < 0) ? -1 : 1; + } + } + return 0; + } + else if ((int)(c >> RC_SHIFT) - // reduce parallelism + (short)(bounds & SMASK) > 0) { + long nc = ((RC_MASK & (c - RC_UNIT)) | (~RC_MASK & c)); + return CTL.compareAndSet(this, c, nc) ? 1 : 0; } - } - return stat; - } - - /** - * Possibly triggers shutdown and tries (once) to block worker - * when pool is (or may be) quiescent. Waits up to a duration - * determined by number of workers. On timeout, if ctl has not - * changed, terminates the worker, which will in turn wake up - * another worker to possibly repeat this process. - * - * @param w the calling worker - * @return negative if w should terminate - */ - private int timedAwaitWork(WorkQueue w, long c) { - int stat = 0; - int scale = 1 - (short)(c >>> TC_SHIFT); - long deadline = (((scale <= 0) ? 1 : scale) * IDLE_TIMEOUT_MS + - System.currentTimeMillis()); - if ((runState >= 0 || (stat = tryTerminate(false, false)) > 0) && - w != null && w.scanState < 0) { - int ss; AuxState aux; - w.parker = Thread.currentThread(); - if (w.scanState < 0) - LockSupport.parkUntil(this, deadline); - w.parker = null; - if ((runState & STOP) != 0) - stat = w.qlock = -1; // pool terminating - else if ((ss = w.scanState) < 0 && !Thread.interrupted() && - (int)c == ss && (aux = auxState) != null && ctl == c && - deadline - System.currentTimeMillis() <= TIMEOUT_SLOP_MS) { - aux.lock(); - try { // pre-deregister - WorkQueue[] ws; - int cfg = w.config, idx = cfg & SMASK; - long nc = ((UC_MASK & (c - TC_UNIT)) | - (SP_MASK & w.stackPred)); - if ((runState & STOP) == 0 && - (ws = workQueues) != null && - idx < ws.length && idx >= 0 && ws[idx] == w && - U.compareAndSwapLong(this, CTL, c, nc)) { - ws[idx] = null; - w.config = cfg | UNREGISTERED; - stat = w.qlock = -1; + else { // validate + int md = mode, pc = md & SMASK, tc = pc + t, bc = 0; + boolean unstable = false; + for (int i = 1; i < n; i += 2) { + WorkQueue q; Thread wt; Thread.State ts; + if ((q = ws[i]) != null) { + if (q.source == 0) { + unstable = true; + break; + } + else { + --tc; + if ((wt = q.owner) != null && + ((ts = wt.getState()) == Thread.State.BLOCKED || + ts == Thread.State.WAITING)) + ++bc; // worker is blocking + } } - } finally { - aux.unlock(); + } + if (unstable || tc != 0 || ctl != c) + return 0; // inconsistent + else if (t + pc >= MAX_CAP || t >= (bounds >>> SWIDTH)) { + Predicate sat; + if ((sat = saturate) != null && sat.test(this)) + return -1; + else if (bc < pc) { // lagging + Thread.yield(); // for retry spins + return 0; + } + else + throw new RejectedExecutionException( + "Thread limit exceeded replacing blocked worker"); } } } - return stat; - } - /** - * If the given worker is a spare with no queued tasks, and there - * are enough existing workers, drops it from ctl counts and sets - * its state to terminated. - * - * @param w the calling worker -- must be a spare - * @return true if dropped (in which case it must not process more tasks) - */ - private boolean tryDropSpare(WorkQueue w) { - if (w != null && w.isEmpty()) { // no local tasks - long c; int sp, wl; WorkQueue[] ws; WorkQueue v; - while ((short)((c = ctl) >> TC_SHIFT) > 0 && - ((sp = (int)c) != 0 || (int)(c >> AC_SHIFT) > 0) && - (ws = workQueues) != null && (wl = ws.length) > 0) { - boolean dropped, canDrop; - if (sp == 0) { // no queued workers - long nc = ((AC_MASK & (c - AC_UNIT)) | - (TC_MASK & (c - TC_UNIT)) | (SP_MASK & c)); - dropped = U.compareAndSwapLong(this, CTL, c, nc); - } - else if ( - (v = ws[(wl - 1) & sp]) == null || v.scanState != sp) - dropped = false; // stale; retry - else { - long nc = v.stackPred & SP_MASK; - if (w == v || w.scanState >= 0) { - canDrop = true; // w unqueued or topmost - nc |= ((AC_MASK & c) | // ensure replacement - (TC_MASK & (c - TC_UNIT))); - } - else { // w may be queued - canDrop = false; // help uncover - nc |= ((AC_MASK & (c + AC_UNIT)) | - (TC_MASK & c)); - } - if (U.compareAndSwapLong(this, CTL, c, nc)) { - v.scanState = sp & ~UNSIGNALLED; - LockSupport.unpark(v.parker); - dropped = canDrop; - } - else - dropped = false; - } - if (dropped) { // pre-deregister - int cfg = w.config, idx = cfg & SMASK; - if (idx >= 0 && idx < ws.length && ws[idx] == w) - ws[idx] = null; - w.config = cfg | UNREGISTERED; - w.qlock = -1; - return true; - } - } - } - return false; + long nc = ((c + TC_UNIT) & TC_MASK) | (c & ~TC_MASK); // expand pool + return CTL.compareAndSet(this, c, nc) && createWorker() ? 1 : 0; } /** * Top-level runloop for workers, called by ForkJoinWorkerThread.run. + * See above for explanation. */ final void runWorker(WorkQueue w) { + WorkQueue[] ws; w.growArray(); // allocate queue - int bound = (w.config & SPARE_WORKER) != 0 ? 0 : POLL_LIMIT; - long seed = w.hint * 0xdaba0b6eb09322e3L; // initial random seed - if ((runState & STOP) == 0) { - for (long r = (seed == 0L) ? 1L : seed;;) { // ensure nonzero - if (bound == 0 && tryDropSpare(w)) - break; - // high bits of prev seed for step; current low bits for idx - int step = (int)(r >>> 48) | 1; - r ^= r >>> 12; r ^= r << 25; r ^= r >>> 27; // xorshift - if (scan(w, bound, step, (int)r) < 0 && awaitWork(w) < 0) - break; - } - } - } - - // Scanning for tasks - - /** - * Repeatedly scans for and tries to steal and execute (via - * workQueue.runTask) a queued task. Each scan traverses queues in - * pseudorandom permutation. Upon finding a non-empty queue, makes - * at most the given bound attempts to re-poll (fewer if - * contended) on the same queue before returning (impossible - * scanState value) 0 to restart scan. Else returns after at least - * 1 and at most 32 full scans. - * - * @param w the worker (via its WorkQueue) - * @param bound repoll bound as bitmask (0 if spare) - * @param step (circular) index increment per iteration (must be odd) - * @param r a random seed for origin index - * @return negative if should await signal - */ - private int scan(WorkQueue w, int bound, int step, int r) { - int stat = 0, wl; WorkQueue[] ws; - if ((ws = workQueues) != null && w != null && (wl = ws.length) > 0) { - for (int m = wl - 1, - origin = m & r, idx = origin, - npolls = 0, - ss = w.scanState;;) { // negative if inactive - WorkQueue q; ForkJoinTask[] a; int b, al; - if ((q = ws[idx]) != null && (b = q.base) - q.top < 0 && + int r = w.id ^ ThreadLocalRandom.nextSecondarySeed(); + if (r == 0) // initial nonzero seed + r = 1; + int lastSignalId = 0; // avoid unneeded signals + while ((ws = workQueues) != null) { + boolean nonempty = false; // scan + for (int n = ws.length, j = n, m = n - 1; j > 0; --j) { + WorkQueue q; int i, b, al; ForkJoinTask[] a; + if ((i = r & m) >= 0 && i < n && // always true + (q = ws[i]) != null && (b = q.base) - q.top < 0 && (a = q.array) != null && (al = a.length) > 0) { + int qid = q.id; // (never zero) int index = (al - 1) & b; - long offset = ((long)index << ASHIFT) + ABASE; ForkJoinTask t = (ForkJoinTask) - U.getObjectVolatile(a, offset); - if (t == null) - break; // empty or busy - else if (b++ != q.base) - break; // busy - else if (ss < 0) { - tryReactivate(w, ws, r); - break; // retry upon rescan - } - else if (!U.compareAndSwapObject(a, offset, t, null)) - break; // contended - else { - q.base = b; - w.currentSteal = t; - if (b != q.top) // propagate signal - signalWork(); - w.runTask(t); - if (++npolls > bound) - break; + QA.getAcquire(a, index); + if (t != null && b++ == q.base && + QA.compareAndSet(a, index, t, null)) { + if ((q.base = b) - q.top < 0 && qid != lastSignalId) + signalWork(); // propagate signal + w.source = lastSignalId = qid; + t.doExec(); + if ((w.id & FIFO) != 0) // run remaining locals + w.localPollAndExec(POLL_LIMIT); + else + w.localPopAndExec(POLL_LIMIT); + ForkJoinWorkerThread thread = w.owner; + ++w.nsteals; + w.source = 0; // now idle + if (thread != null) + thread.afterTopLevelExec(); } + nonempty = true; } - else if (npolls != 0) // rescan + else if (nonempty) break; - else if ((idx = (idx + step) & m) == origin) { - if (ss < 0) { // await signal - stat = ss; - break; - } - else if (r >= 0) { - inactivate(w, ss); - break; - } - else - r <<= 1; // at most 31 rescans - } + else + ++r; } - } - return stat; - } - - // Joining tasks - /** - * Tries to steal and run tasks within the target's computation. - * Uses a variant of the top-level algorithm, restricted to tasks - * with the given task as ancestor: It prefers taking and running - * eligible tasks popped from the worker's own queue (via - * popCC). Otherwise it scans others, randomly moving on - * contention or execution, deciding to give up based on a - * checksum (via return codes from pollAndExecCC). The maxTasks - * argument supports external usages; internal calls use zero, - * allowing unbounded steps (external calls trap non-positive - * values). - * - * @param w caller - * @param maxTasks if non-zero, the maximum number of other tasks to run - * @return task status on exit - */ - final int helpComplete(WorkQueue w, CountedCompleter task, - int maxTasks) { - WorkQueue[] ws; int s = 0, wl; - if ((ws = workQueues) != null && (wl = ws.length) > 1 && - task != null && w != null) { - for (int m = wl - 1, - mode = w.config, - r = ~mode, // scanning seed - origin = r & m, k = origin, // first queue to scan - step = 3, // first scan step - h = 1, // 1:ran, >1:contended, <0:hash - oldSum = 0, checkSum = 0;;) { - CountedCompleter p; WorkQueue q; int i; - if ((s = task.status) < 0) - break; - if (h == 1 && (p = w.popCC(task, mode)) != null) { - p.doExec(); // run local task - if (maxTasks != 0 && --maxTasks == 0) - break; - origin = k; // reset - oldSum = checkSum = 0; - } - else { // poll other worker queues - if ((i = k | 1) < 0 || i > m || (q = ws[i]) == null) - h = 0; - else if ((h = q.pollAndExecCC(task)) < 0) - checkSum += h; - if (h > 0) { - if (h == 1 && maxTasks != 0 && --maxTasks == 0) - break; - step = (r >>> 16) | 3; - r ^= r << 13; r ^= r >>> 17; r ^= r << 5; // xorshift - k = origin = r & m; // move and restart - oldSum = checkSum = 0; - } - else if ((k = (k + step) & m) == origin) { - if (oldSum == (oldSum = checkSum)) - break; - checkSum = 0; - } - } + if (nonempty) { // move (xorshift) + r ^= r << 13; r ^= r >>> 17; r ^= r << 5; } - } - return s; - } - - /** - * Tries to locate and execute tasks for a stealer of the given - * task, or in turn one of its stealers. Traces currentSteal -> - * currentJoin links looking for a thread working on a descendant - * of the given task and with a non-empty queue to steal back and - * execute tasks from. The first call to this method upon a - * waiting join will often entail scanning/search, (which is OK - * because the joiner has nothing better to do), but this method - * leaves hints in workers to speed up subsequent calls. - * - * @param w caller - * @param task the task to join - */ - private void helpStealer(WorkQueue w, ForkJoinTask task) { - if (task != null && w != null) { - ForkJoinTask ps = w.currentSteal; - WorkQueue[] ws; int wl, oldSum = 0; - outer: while (w.tryRemoveAndExec(task) && task.status >= 0 && - (ws = workQueues) != null && (wl = ws.length) > 0) { - ForkJoinTask subtask; - int m = wl - 1, checkSum = 0; // for stability check - WorkQueue j = w, v; // v is subtask stealer - descent: for (subtask = task; subtask.status >= 0; ) { - for (int h = j.hint | 1, k = 0, i;;) { - if ((v = ws[i = (h + (k << 1)) & m]) != null) { - if (v.currentSteal == subtask) { - j.hint = i; - break; - } - checkSum += v.base; + else { + int phase; + lastSignalId = 0; // clear for next scan + if ((phase = w.phase) >= 0) { // enqueue + int np = w.phase = (phase + SS_SEQ) | UNSIGNALLED; + long c, nc; + do { + w.stackPred = (int)(c = ctl); + nc = ((c - RC_UNIT) & UC_MASK) | (SP_MASK & np); + } while (!CTL.weakCompareAndSetVolatile(this, c, nc)); + } + else { // already queued + int pred = w.stackPred; + w.source = DORMANT; // enable signal + for (int steps = 0;;) { + int md, rc; long c; + if (w.phase >= 0) { + w.source = 0; + break; } - if (++k > m) // can't find stealer - break outer; - } - - for (;;) { // help v or descend - ForkJoinTask[] a; int b, al; - if (subtask.status < 0) // too late to help - break descent; - checkSum += (b = v.base); - ForkJoinTask next = v.currentJoin; - ForkJoinTask t = null; - if ((a = v.array) != null && (al = a.length) > 0) { - int index = (al - 1) & b; - long offset = ((long)index << ASHIFT) + ABASE; - t = (ForkJoinTask) - U.getObjectVolatile(a, offset); - if (t != null && b++ == v.base) { - if (j.currentJoin != subtask || - v.currentSteal != subtask || - subtask.status < 0) - break descent; // stale - if (U.compareAndSwapObject(a, offset, t, null)) { - v.base = b; - w.currentSteal = t; - for (int top = w.top;;) { - t.doExec(); // help - w.currentSteal = ps; - if (task.status < 0) - break outer; - if (w.top == top) - break; // run local tasks - if ((t = w.pop()) == null) - break descent; - w.currentSteal = t; - } + else if ((md = mode) < 0) // shutting down + return; + else if ((rc = ((md & SMASK) + // possibly quiescent + (int)((c = ctl) >> RC_SHIFT))) <= 0 && + (md & SHUTDOWN) != 0 && + tryTerminate(false, false)) + return; // help terminate + else if ((++steps & 1) == 0) + Thread.interrupted(); // clear between parks + else if (rc <= 0 && pred != 0 && phase == (int)c) { + long d = keepAlive + System.currentTimeMillis(); + LockSupport.parkUntil(this, d); + if (ctl == c && + d - System.currentTimeMillis() <= TIMEOUT_SLOP) { + long nc = ((UC_MASK & (c - TC_UNIT)) | + (SP_MASK & pred)); + if (CTL.compareAndSet(this, c, nc)) { + w.phase = QUIET; + return; // drop on timeout } } } - if (t == null && b == v.base && b - v.top >= 0) { - if ((subtask = next) == null) { // try to descend - if (next == v.currentJoin && - oldSum == (oldSum = checkSum)) - break outer; - break descent; - } - j = v; - break; - } + else + LockSupport.park(this); } } } @@ -2161,59 +1650,10 @@ } /** - * Tries to decrement active count (sometimes implicitly) and - * possibly release or create a compensating worker in preparation - * for blocking. Returns false (retryable by caller), on - * contention, detected staleness, instability, or termination. - * - * @param w caller - */ - private boolean tryCompensate(WorkQueue w) { - boolean canBlock; int wl; - long c = ctl; - WorkQueue[] ws = workQueues; - int pc = config & SMASK; - int ac = pc + (int)(c >> AC_SHIFT); - int tc = pc + (short)(c >> TC_SHIFT); - if (w == null || w.qlock < 0 || pc == 0 || // terminating or disabled - ws == null || (wl = ws.length) <= 0) - canBlock = false; - else { - int m = wl - 1, sp; - boolean busy = true; // validate ac - for (int i = 0; i <= m; ++i) { - int k; WorkQueue v; - if ((k = (i << 1) | 1) <= m && k >= 0 && (v = ws[k]) != null && - v.scanState >= 0 && v.currentSteal == null) { - busy = false; - break; - } - } - if (!busy || ctl != c) - canBlock = false; // unstable or stale - else if ((sp = (int)c) != 0) // release idle worker - canBlock = tryRelease(c, ws[m & sp], 0L); - else if (tc >= pc && ac > 1 && w.isEmpty()) { - long nc = ((AC_MASK & (c - AC_UNIT)) | - (~AC_MASK & c)); // uncompensated - canBlock = U.compareAndSwapLong(this, CTL, c, nc); - } - else if (tc >= MAX_CAP || - (this == common && tc >= pc + COMMON_MAX_SPARES)) - throw new RejectedExecutionException( - "Thread limit exceeded replacing blocked worker"); - else { // similar to tryAddWorker - boolean isSpare = (tc >= pc); - long nc = (AC_MASK & c) | (TC_MASK & (c + TC_UNIT)); - canBlock = (U.compareAndSwapLong(this, CTL, c, nc) && - createWorker(isSpare)); // throws on exception - } - } - return canBlock; - } - - /** * Helps and/or blocks until the given task is done or timeout. + * First tries locally helping, then scans other queues for a task + * produced by one of w's stealers; compensating and blocking if + * none are found (rescanning if tryCompensate fails). * * @param w caller * @param task the task @@ -2222,61 +1662,166 @@ */ final int awaitJoin(WorkQueue w, ForkJoinTask task, long deadline) { int s = 0; - if (w != null) { - ForkJoinTask prevJoin = w.currentJoin; - if (task != null && (s = task.status) >= 0) { - w.currentJoin = task; - CountedCompleter cc = (task instanceof CountedCompleter) ? - (CountedCompleter)task : null; - for (;;) { - if (cc != null) - helpComplete(w, cc, 0); - else - helpStealer(w, task); - if ((s = task.status) < 0) - break; - long ms, ns; + if (w != null && task != null && + (!(task instanceof CountedCompleter) || + (s = w.localHelpCC((CountedCompleter)task, 0)) >= 0)) { + w.tryRemoveAndExec(task); + int src = w.source, id = w.id; + s = task.status; + while (s >= 0) { + WorkQueue[] ws; + boolean nonempty = false; + int r = ThreadLocalRandom.nextSecondarySeed() | 1; // odd indices + if ((ws = workQueues) != null) { // scan for matching id + for (int n = ws.length, m = n - 1, j = -n; j < n; j += 2) { + WorkQueue q; int i, b, al; ForkJoinTask[] a; + if ((i = (r + j) & m) >= 0 && i < n && + (q = ws[i]) != null && q.source == id && + (b = q.base) - q.top < 0 && + (a = q.array) != null && (al = a.length) > 0) { + int qid = q.id; + int index = (al - 1) & b; + ForkJoinTask t = (ForkJoinTask) + QA.getAcquire(a, index); + if (t != null && b++ == q.base && id == q.source && + QA.compareAndSet(a, index, t, null)) { + q.base = b; + w.source = qid; + t.doExec(); + w.source = src; + } + nonempty = true; + break; + } + } + } + if ((s = task.status) < 0) + break; + else if (!nonempty) { + long ms, ns; int block; if (deadline == 0L) - ms = 0L; + ms = 0L; // untimed else if ((ns = deadline - System.nanoTime()) <= 0L) - break; + break; // timeout else if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) <= 0L) - ms = 1L; - if (tryCompensate(w)) { + ms = 1L; // avoid 0 for timed wait + if ((block = tryCompensate(w)) != 0) { task.internalWait(ms); - U.getAndAddLong(this, CTL, AC_UNIT); + CTL.getAndAdd(this, (block > 0) ? RC_UNIT : 0L); } - if ((s = task.status) < 0) - break; + s = task.status; } - w.currentJoin = prevJoin; } } return s; } - // Specialized scanning + /** + * Runs tasks until {@code isQuiescent()}. Rather than blocking + * when tasks cannot be found, rescans until all others cannot + * find tasks either. + */ + final void helpQuiescePool(WorkQueue w) { + int prevSrc = w.source, fifo = w.id & FIFO; + for (int source = prevSrc, released = -1;;) { // -1 until known + WorkQueue[] ws; + if (fifo != 0) + w.localPollAndExec(0); + else + w.localPopAndExec(0); + if (released == -1 && w.phase >= 0) + released = 1; + boolean quiet = true, empty = true; + int r = ThreadLocalRandom.nextSecondarySeed(); + if ((ws = workQueues) != null) { + for (int n = ws.length, j = n, m = n - 1; j > 0; --j) { + WorkQueue q; int i, b, al; ForkJoinTask[] a; + if ((i = (r - j) & m) >= 0 && i < n && (q = ws[i]) != null) { + if ((b = q.base) - q.top < 0 && + (a = q.array) != null && (al = a.length) > 0) { + int qid = q.id; + if (released == 0) { // increment + released = 1; + CTL.getAndAdd(this, RC_UNIT); + } + int index = (al - 1) & b; + ForkJoinTask t = (ForkJoinTask) + QA.getAcquire(a, index); + if (t != null && b++ == q.base && + QA.compareAndSet(a, index, t, null)) { + q.base = b; + w.source = source = q.id; + t.doExec(); + w.source = source = prevSrc; + } + quiet = empty = false; + break; + } + else if ((q.source & QUIET) == 0) + quiet = false; + } + } + } + if (quiet) { + if (released == 0) + CTL.getAndAdd(this, RC_UNIT); + w.source = prevSrc; + break; + } + else if (empty) { + if (source != QUIET) + w.source = source = QUIET; + if (released == 1) { // decrement + released = 0; + CTL.getAndAdd(this, RC_MASK & -RC_UNIT); + } + } + } + } /** - * Returns a (probably) non-empty steal queue, if one is found - * during a scan, else null. This method must be retried by - * caller if, by the time it tries to use the queue, it is empty. + * Scans for and returns a polled task, if available. + * Used only for untracked polls. + * + * @param submissionsOnly if true, only scan submission queues */ - private WorkQueue findNonEmptyStealQueue() { - WorkQueue[] ws; int wl; // one-shot version of scan loop - int r = ThreadLocalRandom.nextSecondarySeed(); - if ((ws = workQueues) != null && (wl = ws.length) > 0) { - int m = wl - 1, origin = r & m; + private ForkJoinTask pollScan(boolean submissionsOnly) { + WorkQueue[] ws; int n; + rescan: while ((mode & STOP) == 0 && (ws = workQueues) != null && + (n = ws.length) > 0) { + int m = n - 1; + int r = ThreadLocalRandom.nextSecondarySeed(); + int h = r >>> 16; + int origin, step; + if (submissionsOnly) { + origin = (r & ~1) & m; // even indices and steps + step = (h & ~1) | 2; + } + else { + origin = r & m; + step = h | 1; + } for (int k = origin, oldSum = 0, checkSum = 0;;) { - WorkQueue q; int b; + WorkQueue q; int b, al; ForkJoinTask[] a; if ((q = ws[k]) != null) { - if ((b = q.base) - q.top < 0) - return q; - checkSum += b; + checkSum += b = q.base; + if (b - q.top < 0 && + (a = q.array) != null && (al = a.length) > 0) { + int index = (al - 1) & b; + ForkJoinTask t = (ForkJoinTask) + QA.getAcquire(a, index); + if (t != null && b++ == q.base && + QA.compareAndSet(a, index, t, null)) { + q.base = b; + return t; + } + else + break; // restart + } } - if ((k = (k + 1) & m) == origin) { + if ((k = (k + step) & m) == origin) { if (oldSum == (oldSum = checkSum)) - break; + break rescan; checkSum = 0; } } @@ -2285,58 +1830,160 @@ } /** - * Runs tasks until {@code isQuiescent()}. We piggyback on - * active count ctl maintenance, but rather than blocking - * when tasks cannot be found, we rescan until all others cannot - * find tasks either. - */ - final void helpQuiescePool(WorkQueue w) { - ForkJoinTask ps = w.currentSteal; // save context - int wc = w.config; - for (boolean active = true;;) { - long c; WorkQueue q; ForkJoinTask t; - if (wc >= 0 && (t = w.pop()) != null) { // run locals if LIFO - (w.currentSteal = t).doExec(); - w.currentSteal = ps; - } - else if ((q = findNonEmptyStealQueue()) != null) { - if (!active) { // re-establish active count - active = true; - U.getAndAddLong(this, CTL, AC_UNIT); - } - if ((t = q.pollAt(q.base)) != null) { - (w.currentSteal = t).doExec(); - w.currentSteal = ps; - if (++w.nsteals < 0) - w.transferStealCount(this); - } - } - else if (active) { // decrement active count without queuing - long nc = (AC_MASK & ((c = ctl) - AC_UNIT)) | (~AC_MASK & c); - if (U.compareAndSwapLong(this, CTL, c, nc)) - active = false; - } - else if ((int)((c = ctl) >> AC_SHIFT) + (config & SMASK) <= 0 && - U.compareAndSwapLong(this, CTL, c, c + AC_UNIT)) - break; - } - } - - /** * Gets and removes a local or stolen task for the given worker. * * @return a task, if available */ final ForkJoinTask nextTaskFor(WorkQueue w) { - for (ForkJoinTask t;;) { - WorkQueue q; - if ((t = w.nextLocalTask()) != null) - return t; - if ((q = findNonEmptyStealQueue()) == null) - return null; - if ((t = q.pollAt(q.base)) != null) - return t; + ForkJoinTask t; + if (w != null && + (t = (w.id & FIFO) != 0 ? w.poll() : w.pop()) != null) + return t; + else + return pollScan(false); + } + + // External operations + + /** + * Adds the given task to a submission queue at submitter's + * current queue, creating one if null or contended. + * + * @param task the task. Caller must ensure non-null. + */ + final void externalPush(ForkJoinTask task) { + int r; // initialize caller's probe + if ((r = ThreadLocalRandom.getProbe()) == 0) { + ThreadLocalRandom.localInit(); + r = ThreadLocalRandom.getProbe(); } + for (;;) { + int md = mode, n; + WorkQueue[] ws = workQueues; + if ((md & SHUTDOWN) != 0 || ws == null || (n = ws.length) <= 0) + throw new RejectedExecutionException(); + else { + WorkQueue q; + boolean push = false, grow = false; + if ((q = ws[(n - 1) & r & SQMASK]) == null) { + Object lock = workerNamePrefix; + int qid = (r | QUIET) & ~(FIFO | OWNED); + q = new WorkQueue(this, null); + q.id = qid; + q.source = QUIET; + q.phase = QLOCK; // lock queue + if (lock != null) { + synchronized (lock) { // lock pool to install + int i; + if ((ws = workQueues) != null && + (n = ws.length) > 0 && + ws[i = qid & (n - 1) & SQMASK] == null) { + ws[i] = q; + push = grow = true; + } + } + } + } + else if (q.tryLockSharedQueue()) { + int b = q.base, s = q.top, al, d; ForkJoinTask[] a; + if ((a = q.array) != null && (al = a.length) > 0 && + al - 1 + (d = b - s) > 0) { + a[(al - 1) & s] = task; + q.top = s + 1; // relaxed writes OK here + q.phase = 0; + if (d < 0 && q.base - s < -1) + break; // no signal needed + } + else + grow = true; + push = true; + } + if (push) { + if (grow) { + try { + q.growArray(); + int s = q.top, al; ForkJoinTask[] a; + if ((a = q.array) != null && (al = a.length) > 0) { + a[(al - 1) & s] = task; + q.top = s + 1; + } + } finally { + q.phase = 0; + } + } + signalWork(); + break; + } + else // move if busy + r = ThreadLocalRandom.advanceProbe(r); + } + } + } + + /** + * Pushes a possibly-external submission. + */ + private ForkJoinTask externalSubmit(ForkJoinTask task) { + Thread t; ForkJoinWorkerThread w; WorkQueue q; + if (task == null) + throw new NullPointerException(); + if (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) && + (w = (ForkJoinWorkerThread)t).pool == this && + (q = w.workQueue) != null) + q.push(task); + else + externalPush(task); + return task; + } + + /** + * Returns common pool queue for an external thread. + */ + static WorkQueue commonSubmitterQueue() { + ForkJoinPool p = common; + int r = ThreadLocalRandom.getProbe(); + WorkQueue[] ws; int n; + return (p != null && (ws = p.workQueues) != null && + (n = ws.length) > 0) ? + ws[(n - 1) & r & SQMASK] : null; + } + + /** + * Performs tryUnpush for an external submitter. + */ + final boolean tryExternalUnpush(ForkJoinTask task) { + int r = ThreadLocalRandom.getProbe(); + WorkQueue[] ws; WorkQueue w; int n; + return ((ws = workQueues) != null && + (n = ws.length) > 0 && + (w = ws[(n - 1) & r & SQMASK]) != null && + w.trySharedUnpush(task)); + } + + /** + * Performs helpComplete for an external submitter. + */ + final int externalHelpComplete(CountedCompleter task, int maxTasks) { + int r = ThreadLocalRandom.getProbe(); + WorkQueue[] ws; WorkQueue w; int n; + return ((ws = workQueues) != null && (n = ws.length) > 0 && + (w = ws[(n - 1) & r & SQMASK]) != null) ? + w.sharedHelpCC(task, maxTasks) : 0; + } + + /** + * Tries to steal and run tasks within the target's computation. + * The maxTasks argument supports external usages; internal calls + * use zero, allowing unbounded steps (external calls trap + * non-positive values). + * + * @param w caller + * @param maxTasks if non-zero, the maximum number of other tasks to run + * @return task status on exit + */ + final int helpComplete(WorkQueue w, CountedCompleter task, + int maxTasks) { + return (w == null) ? 0 : w.localHelpCC(task, maxTasks); } /** @@ -2383,10 +2030,12 @@ */ static int getSurplusQueuedTaskCount() { Thread t; ForkJoinWorkerThread wt; ForkJoinPool pool; WorkQueue q; - if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) { - int p = (pool = (wt = (ForkJoinWorkerThread)t).pool).config & SMASK; - int n = (q = wt.workQueue).top - q.base; - int a = (int)(pool.ctl >> AC_SHIFT) + p; + if (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) && + (pool = (wt = (ForkJoinWorkerThread)t).pool) != null && + (q = wt.workQueue) != null) { + int p = pool.mode & SMASK; + int a = p + (int)(pool.ctl >> RC_SHIFT); + int n = q.top - q.base; return n - (a > (p >>>= 1) ? 0 : a > (p >>>= 1) ? 1 : a > (p >>>= 1) ? 2 : @@ -2396,7 +2045,7 @@ return 0; } - // Termination + // Termination /** * Possibly initiates and/or completes termination. @@ -2404,198 +2053,86 @@ * @param now if true, unconditionally terminate, else only * if no work and no active workers * @param enable if true, terminate when next possible - * @return -1: terminating/terminated, 0: retry if internal caller, else 1 + * @return true if terminating or terminated */ - private int tryTerminate(boolean now, boolean enable) { - int rs; // 3 phases: try to set SHUTDOWN, then STOP, then TERMINATED + private boolean tryTerminate(boolean now, boolean enable) { + int md; // 3 phases: try to set SHUTDOWN, then STOP, then TERMINATED - while ((rs = runState) >= 0) { + while (((md = mode) & SHUTDOWN) == 0) { if (!enable || this == common) // cannot shutdown - return 1; - else if (rs == 0) - tryInitialize(false); // ensure initialized + return false; else - U.compareAndSwapInt(this, RUNSTATE, rs, rs | SHUTDOWN); + MODE.compareAndSet(this, md, md | SHUTDOWN); } - if ((rs & STOP) == 0) { // try to initiate termination - if (!now) { // check quiescence + while (((md = mode) & STOP) == 0) { // try to initiate termination + if (!now) { // check if quiescent & empty for (long oldSum = 0L;;) { // repeat until stable - WorkQueue[] ws; WorkQueue w; int b; + boolean running = false; long checkSum = ctl; - if ((int)(checkSum >> AC_SHIFT) + (config & SMASK) > 0) - return 0; // still active workers - if ((ws = workQueues) != null) { + WorkQueue[] ws = workQueues; + if ((md & SMASK) + (int)(checkSum >> RC_SHIFT) > 0) + running = true; + else if (ws != null) { + WorkQueue w; int b; for (int i = 0; i < ws.length; ++i) { if ((w = ws[i]) != null) { - checkSum += (b = w.base); - if (w.currentSteal != null || b != w.top) - return 0; // retry if internal caller + checkSum += (b = w.base) + w.id; + if (b != w.top || + ((i & 1) == 1 && w.source >= 0)) { + running = true; + break; + } } } } - if (oldSum == (oldSum = checkSum)) + if (((md = mode) & STOP) != 0) + break; // already triggered + else if (running) + return false; + else if (workQueues == ws && oldSum == (oldSum = checkSum)) break; } } - do {} while (!U.compareAndSwapInt(this, RUNSTATE, - rs = runState, rs | STOP)); + if ((md & STOP) == 0) + MODE.compareAndSet(this, md, md | STOP); } - for (long oldSum = 0L;;) { // repeat until stable - WorkQueue[] ws; WorkQueue w; ForkJoinWorkerThread wt; - long checkSum = ctl; - if ((ws = workQueues) != null) { // help terminate others - for (int i = 0; i < ws.length; ++i) { - if ((w = ws[i]) != null) { - w.cancelAll(); // clear queues - checkSum += w.base; - if (w.qlock >= 0) { - w.qlock = -1; // racy set OK - if ((wt = w.owner) != null) { + while (((md = mode) & TERMINATED) == 0) { // help terminate others + for (long oldSum = 0L;;) { // repeat until stable + WorkQueue[] ws; WorkQueue w; + long checkSum = ctl; + if ((ws = workQueues) != null) { + for (int i = 0; i < ws.length; ++i) { + if ((w = ws[i]) != null) { + ForkJoinWorkerThread wt = w.owner; + w.cancelAll(); // clear queues + if (wt != null) { try { // unblock join or park wt.interrupt(); } catch (Throwable ignore) { } } + checkSum += w.base + w.id; } } } - } - if (oldSum == (oldSum = checkSum)) - break; - } - - if ((short)(ctl >>> TC_SHIFT) + (config & SMASK) <= 0) { - runState = (STARTED | SHUTDOWN | STOP | TERMINATED); // final write - synchronized (this) { - notifyAll(); // for awaitTermination + if (((md = mode) & TERMINATED) != 0 || + (workQueues == ws && oldSum == (oldSum = checkSum))) + break; } - } - - return -1; - } - - // External operations - - /** - * Constructs and tries to install a new external queue, - * failing if the workQueues array already has a queue at - * the given index. - * - * @param index the index of the new queue - */ - private void tryCreateExternalQueue(int index) { - AuxState aux; - if ((aux = auxState) != null && index >= 0) { - WorkQueue q = new WorkQueue(this, null); - q.config = index; - q.scanState = ~UNSIGNALLED; - q.qlock = 1; // lock queue - boolean installed = false; - aux.lock(); - try { // lock pool to install - WorkQueue[] ws; - if ((ws = workQueues) != null && index < ws.length && - ws[index] == null) { - ws[index] = q; // else throw away - installed = true; + if ((md & TERMINATED) != 0) + break; + else if ((md & SMASK) + (short)(ctl >>> TC_SHIFT) > 0) + break; + else if (MODE.compareAndSet(this, md, md | TERMINATED)) { + synchronized (this) { + notifyAll(); // for awaitTermination } - } finally { - aux.unlock(); - } - if (installed) { - try { - q.growArray(); - } finally { - q.qlock = 0; - } + break; } } - } - - /** - * Adds the given task to a submission queue at submitter's - * current queue. Also performs secondary initialization upon the - * first submission of the first task to the pool, and detects - * first submission by an external thread and creates a new shared - * queue if the one at index if empty or contended. - * - * @param task the task. Caller must ensure non-null. - */ - final void externalPush(ForkJoinTask task) { - int r; // initialize caller's probe - if ((r = ThreadLocalRandom.getProbe()) == 0) { - ThreadLocalRandom.localInit(); - r = ThreadLocalRandom.getProbe(); - } - for (;;) { - WorkQueue q; int wl, k, stat; - int rs = runState; - WorkQueue[] ws = workQueues; - if (rs <= 0 || ws == null || (wl = ws.length) <= 0) - tryInitialize(true); - else if ((q = ws[k = (wl - 1) & r & SQMASK]) == null) - tryCreateExternalQueue(k); - else if ((stat = q.sharedPush(task)) < 0) - break; - else if (stat == 0) { - signalWork(); - break; - } - else // move if busy - r = ThreadLocalRandom.advanceProbe(r); - } - } - - /** - * Pushes a possibly-external submission. - */ - private ForkJoinTask externalSubmit(ForkJoinTask task) { - Thread t; ForkJoinWorkerThread w; WorkQueue q; - if (task == null) - throw new NullPointerException(); - if (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) && - (w = (ForkJoinWorkerThread)t).pool == this && - (q = w.workQueue) != null) - q.push(task); - else - externalPush(task); - return task; - } - - /** - * Returns common pool queue for an external thread. - */ - static WorkQueue commonSubmitterQueue() { - ForkJoinPool p = common; - int r = ThreadLocalRandom.getProbe(); - WorkQueue[] ws; int wl; - return (p != null && (ws = p.workQueues) != null && - (wl = ws.length) > 0) ? - ws[(wl - 1) & r & SQMASK] : null; - } - - /** - * Performs tryUnpush for an external submitter. - */ - final boolean tryExternalUnpush(ForkJoinTask task) { - int r = ThreadLocalRandom.getProbe(); - WorkQueue[] ws; WorkQueue w; int wl; - return ((ws = workQueues) != null && - (wl = ws.length) > 0 && - (w = ws[(wl - 1) & r & SQMASK]) != null && - w.trySharedUnpush(task)); - } - - /** - * Performs helpComplete for an external submitter. - */ - final int externalHelpComplete(CountedCompleter task, int maxTasks) { - WorkQueue[] ws; int wl; - int r = ThreadLocalRandom.getProbe(); - return ((ws = workQueues) != null && (wl = ws.length) > 0) ? - helpComplete(ws[(wl - 1) & r & SQMASK], task, maxTasks) : 0; + return true; } // Exported methods @@ -2604,9 +2141,10 @@ /** * Creates a {@code ForkJoinPool} with parallelism equal to {@link - * java.lang.Runtime#availableProcessors}, using the {@linkplain - * #defaultForkJoinWorkerThreadFactory default thread factory}, - * no UncaughtExceptionHandler, and non-async LIFO processing mode. + * java.lang.Runtime#availableProcessors}, using defaults for all + * other parameters (see {@link #ForkJoinPool(int, + * ForkJoinWorkerThreadFactory, UncaughtExceptionHandler, boolean, + * int, int, int, Predicate, long, TimeUnit)}). * * @throws SecurityException if a security manager exists and * the caller is not permitted to modify threads @@ -2615,14 +2153,16 @@ */ public ForkJoinPool() { this(Math.min(MAX_CAP, Runtime.getRuntime().availableProcessors()), - defaultForkJoinWorkerThreadFactory, null, false); + defaultForkJoinWorkerThreadFactory, null, false, + 0, MAX_CAP, 1, null, DEFAULT_KEEPALIVE, TimeUnit.MILLISECONDS); } /** * Creates a {@code ForkJoinPool} with the indicated parallelism - * level, the {@linkplain - * #defaultForkJoinWorkerThreadFactory default thread factory}, - * no UncaughtExceptionHandler, and non-async LIFO processing mode. + * level, using defaults for all other parameters (see {@link + * #ForkJoinPool(int, ForkJoinWorkerThreadFactory, + * UncaughtExceptionHandler, boolean, int, int, int, Predicate, + * long, TimeUnit)}). * * @param parallelism the parallelism level * @throws IllegalArgumentException if parallelism less than or @@ -2633,11 +2173,15 @@ * java.lang.RuntimePermission}{@code ("modifyThread")} */ public ForkJoinPool(int parallelism) { - this(parallelism, defaultForkJoinWorkerThreadFactory, null, false); + this(parallelism, defaultForkJoinWorkerThreadFactory, null, false, + 0, MAX_CAP, 1, null, DEFAULT_KEEPALIVE, TimeUnit.MILLISECONDS); } /** - * Creates a {@code ForkJoinPool} with the given parameters. + * Creates a {@code ForkJoinPool} with the given parameters (using + * defaults for others -- see {@link #ForkJoinPool(int, + * ForkJoinWorkerThreadFactory, UncaughtExceptionHandler, boolean, + * int, int, int, Predicate, long, TimeUnit)}). * * @param parallelism the parallelism level. For default value, * use {@link java.lang.Runtime#availableProcessors}. @@ -2664,43 +2208,185 @@ ForkJoinWorkerThreadFactory factory, UncaughtExceptionHandler handler, boolean asyncMode) { - this(checkParallelism(parallelism), - checkFactory(factory), - handler, - asyncMode ? FIFO_QUEUE : LIFO_QUEUE, - "ForkJoinPool-" + nextPoolId() + "-worker-"); + this(parallelism, factory, handler, asyncMode, + 0, MAX_CAP, 1, null, DEFAULT_KEEPALIVE, TimeUnit.MILLISECONDS); + } + + /** + * Creates a {@code ForkJoinPool} with the given parameters. + * + * @param parallelism the parallelism level. For default value, + * use {@link java.lang.Runtime#availableProcessors}. + * + * @param factory the factory for creating new threads. For + * default value, use {@link #defaultForkJoinWorkerThreadFactory}. + * + * @param handler the handler for internal worker threads that + * terminate due to unrecoverable errors encountered while + * executing tasks. For default value, use {@code null}. + * + * @param asyncMode if true, establishes local first-in-first-out + * scheduling mode for forked tasks that are never joined. This + * mode may be more appropriate than default locally stack-based + * mode in applications in which worker threads only process + * event-style asynchronous tasks. For default value, use {@code + * false}. + * + * @param corePoolSize the number of threads to keep in the pool + * (unless timed out after an elapsed keep-alive). Normally (and + * by default) this is the same value as the parallelism level, + * but may be set to a larger value to reduce dynamic overhead if + * tasks regularly block. Using a smaller value (for example + * {@code 0}) has the same effect as the default. + * + * @param maximumPoolSize the maximum number of threads allowed. + * When the maximum is reached, attempts to replace blocked + * threads fail. (However, because creation and termination of + * different threads may overlap, and may be managed by the given + * thread factory, this value may be transiently exceeded.) To + * arrange the same value as is used by default for the common + * pool, use {@code 256} plus the {@code parallelism} level. (By + * default, the common pool allows a maximum of 256 spare + * threads.) Using a value (for example {@code + * Integer.MAX_VALUE}) larger than the implementation's total + * thread limit has the same effect as using this limit (which is + * the default). + * + * @param minimumRunnable the minimum allowed number of core + * threads not blocked by a join or {@link ManagedBlocker}. To + * ensure progress, when too few unblocked threads exist and + * unexecuted tasks may exist, new threads are constructed, up to + * the given maximumPoolSize. For the default value, use {@code + * 1}, that ensures liveness. A larger value might improve + * throughput in the presence of blocked activities, but might + * not, due to increased overhead. A value of zero may be + * acceptable when submitted tasks cannot have dependencies + * requiring additional threads. + * + * @param saturate if non-null, a predicate invoked upon attempts + * to create more than the maximum total allowed threads. By + * default, when a thread is about to block on a join or {@link + * ManagedBlocker}, but cannot be replaced because the + * maximumPoolSize would be exceeded, a {@link + * RejectedExecutionException} is thrown. But if this predicate + * returns {@code true}, then no exception is thrown, so the pool + * continues to operate with fewer than the target number of + * runnable threads, which might not ensure progress. + * + * @param keepAliveTime the elapsed time since last use before + * a thread is terminated (and then later replaced if needed). + * For the default value, use {@code 60, TimeUnit.SECONDS}. + * + * @param unit the time unit for the {@code keepAliveTime} argument + * + * @throws IllegalArgumentException if parallelism is less than or + * equal to zero, or is greater than implementation limit, + * or if maximumPoolSize is less than parallelism, + * of if the keepAliveTime is less than or equal to zero. + * @throws NullPointerException if the factory is null + * @throws SecurityException if a security manager exists and + * the caller is not permitted to modify threads + * because it does not hold {@link + * java.lang.RuntimePermission}{@code ("modifyThread")} + * @since 9 + */ + public ForkJoinPool(int parallelism, + ForkJoinWorkerThreadFactory factory, + UncaughtExceptionHandler handler, + boolean asyncMode, + int corePoolSize, + int maximumPoolSize, + int minimumRunnable, + Predicate saturate, + long keepAliveTime, + TimeUnit unit) { + // check, encode, pack parameters + if (parallelism <= 0 || parallelism > MAX_CAP || + maximumPoolSize < parallelism || keepAliveTime <= 0L) + throw new IllegalArgumentException(); + if (factory == null) + throw new NullPointerException(); + long ms = Math.max(unit.toMillis(keepAliveTime), TIMEOUT_SLOP); + + String prefix = "ForkJoinPool-" + nextPoolId() + "-worker-"; + int corep = Math.min(Math.max(corePoolSize, parallelism), MAX_CAP); + long c = ((((long)(-corep) << TC_SHIFT) & TC_MASK) | + (((long)(-parallelism) << RC_SHIFT) & RC_MASK)); + int m = parallelism | (asyncMode ? FIFO : 0); + int maxSpares = Math.min(maximumPoolSize, MAX_CAP) - parallelism; + int minAvail = Math.min(Math.max(minimumRunnable, 0), MAX_CAP); + int b = ((minAvail - parallelism) & SMASK) | (maxSpares << SWIDTH); + int n = (parallelism > 1) ? parallelism - 1 : 1; // at least 2 slots + n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; + n = (n + 1) << 1; // power of two, including space for submission queues + + this.workQueues = new WorkQueue[n]; + this.workerNamePrefix = prefix; + this.factory = factory; + this.ueh = handler; + this.saturate = saturate; + this.keepAlive = ms; + this.bounds = b; + this.mode = m; + this.ctl = c; checkPermission(); } - private static int checkParallelism(int parallelism) { - if (parallelism <= 0 || parallelism > MAX_CAP) - throw new IllegalArgumentException(); - return parallelism; - } - - private static ForkJoinWorkerThreadFactory checkFactory - (ForkJoinWorkerThreadFactory factory) { - if (factory == null) - throw new NullPointerException(); - return factory; - } + /** + * Constructor for common pool using parameters possibly + * overridden by system properties + */ + @SuppressWarnings("deprecation") // Class.newInstance + private ForkJoinPool(byte forCommonPoolOnly) { + int parallelism = -1; + ForkJoinWorkerThreadFactory fac = null; + UncaughtExceptionHandler handler = null; + try { // ignore exceptions in accessing/parsing properties + String pp = System.getProperty + ("java.util.concurrent.ForkJoinPool.common.parallelism"); + String fp = System.getProperty + ("java.util.concurrent.ForkJoinPool.common.threadFactory"); + String hp = System.getProperty + ("java.util.concurrent.ForkJoinPool.common.exceptionHandler"); + if (pp != null) + parallelism = Integer.parseInt(pp); + if (fp != null) + fac = ((ForkJoinWorkerThreadFactory)ClassLoader. + getSystemClassLoader().loadClass(fp).newInstance()); + if (hp != null) + handler = ((UncaughtExceptionHandler)ClassLoader. + getSystemClassLoader().loadClass(hp).newInstance()); + } catch (Exception ignore) { + } - /** - * Creates a {@code ForkJoinPool} with the given parameters, without - * any security checks or parameter validation. Invoked directly by - * makeCommonPool. - */ - private ForkJoinPool(int parallelism, - ForkJoinWorkerThreadFactory factory, - UncaughtExceptionHandler handler, - int mode, - String workerNamePrefix) { - this.workerNamePrefix = workerNamePrefix; - this.factory = factory; + if (fac == null) { + if (System.getSecurityManager() == null) + fac = defaultForkJoinWorkerThreadFactory; + else // use security-managed default + fac = new InnocuousForkJoinWorkerThreadFactory(); + } + if (parallelism < 0 && // default 1 less than #cores + (parallelism = Runtime.getRuntime().availableProcessors() - 1) <= 0) + parallelism = 1; + if (parallelism > MAX_CAP) + parallelism = MAX_CAP; + + long c = ((((long)(-parallelism) << TC_SHIFT) & TC_MASK) | + (((long)(-parallelism) << RC_SHIFT) & RC_MASK)); + int b = ((1 - parallelism) & SMASK) | (COMMON_MAX_SPARES << SWIDTH); + int n = (parallelism > 1) ? parallelism - 1 : 1; + n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; + n = (n + 1) << 1; + + this.workQueues = new WorkQueue[n]; + this.workerNamePrefix = "ForkJoinPool.commonPool-worker-"; + this.factory = fac; this.ueh = handler; - this.config = (parallelism & SMASK) | mode; - long np = (long)(-parallelism); // offset ctl counts - this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK); + this.saturate = null; + this.keepAlive = DEFAULT_KEEPALIVE; + this.bounds = b; + this.mode = parallelism; + this.ctl = c; } /** @@ -2876,8 +2562,8 @@ * @return the targeted parallelism level of this pool */ public int getParallelism() { - int par; - return ((par = config & SMASK) > 0) ? par : 1; + int par = mode & SMASK; + return (par > 0) ? par : 1; } /** @@ -2899,7 +2585,7 @@ * @return the number of worker threads */ public int getPoolSize() { - return (config & SMASK) + (short)(ctl >>> TC_SHIFT); + return ((mode & SMASK) + (short)(ctl >>> TC_SHIFT)); } /** @@ -2909,7 +2595,7 @@ * @return {@code true} if this pool uses async mode */ public boolean getAsyncMode() { - return (config & FIFO_QUEUE) != 0; + return (mode & FIFO) != 0; } /** @@ -2940,7 +2626,7 @@ * @return the number of active threads */ public int getActiveThreadCount() { - int r = (config & SMASK) + (int)(ctl >> AC_SHIFT); + int r = (mode & SMASK) + (int)(ctl >> RC_SHIFT); return (r <= 0) ? 0 : r; // suppress momentarily negative values } @@ -2956,7 +2642,30 @@ * @return {@code true} if all threads are currently idle */ public boolean isQuiescent() { - return (config & SMASK) + (int)(ctl >> AC_SHIFT) <= 0; + for (;;) { + long c = ctl; + int md = mode, pc = md & SMASK; + int tc = pc + (short)(c >>> TC_SHIFT); + int rc = pc + (int)(c >> RC_SHIFT); + if ((md & (STOP | TERMINATED)) != 0) + return true; + else if (rc > 0) + return false; + else { + WorkQueue[] ws; WorkQueue v; + if ((ws = workQueues) != null) { + for (int i = 1; i < ws.length; i += 2) { + if ((v = ws[i]) != null) { + if ((v.source & QUIET) == 0) + return false; + --tc; + } + } + } + if (tc == 0 && ctl == c) + return true; + } + } } /** @@ -2971,13 +2680,12 @@ * @return the number of steals */ public long getStealCount() { - AuxState sc = auxState; - long count = (sc == null) ? 0L : sc.stealCount; + long count = stealCount; WorkQueue[] ws; WorkQueue w; if ((ws = workQueues) != null) { for (int i = 1; i < ws.length; i += 2) { if ((w = ws[i]) != null) - count += w.nsteals; + count += (long)w.nsteals & 0xffffffffL; } } return count; @@ -3049,15 +2757,7 @@ * @return the next submission, or {@code null} if none */ protected ForkJoinTask pollSubmission() { - WorkQueue[] ws; int wl; WorkQueue w; ForkJoinTask t; - int r = ThreadLocalRandom.nextSecondarySeed(); - if ((ws = workQueues) != null && (wl = ws.length) > 0) { - for (int m = wl - 1, i = 0; i < wl; ++i) { - if ((w = ws[(i << 1) & m]) != null && (t = w.poll()) != null) - return t; - } - } - return null; + return pollScan(true); } /** @@ -3103,9 +2803,7 @@ public String toString() { // Use a single pass through workQueues to collect counts long qt = 0L, qs = 0L; int rc = 0; - AuxState sc = auxState; - long st = (sc == null) ? 0L : sc.stealCount; - long c = ctl; + long st = stealCount; WorkQueue[] ws; WorkQueue w; if ((ws = workQueues) != null) { for (int i = 0; i < ws.length; ++i) { @@ -3115,22 +2813,24 @@ qs += size; else { qt += size; - st += w.nsteals; + st += (long)w.nsteals & 0xffffffffL; if (w.isApparentlyUnblocked()) ++rc; } } } } - int pc = (config & SMASK); + + int md = mode; + int pc = (md & SMASK); + long c = ctl; int tc = pc + (short)(c >>> TC_SHIFT); - int ac = pc + (int)(c >> AC_SHIFT); + int ac = pc + (int)(c >> RC_SHIFT); if (ac < 0) // ignore transient negative ac = 0; - int rs = runState; - String level = ((rs & TERMINATED) != 0 ? "Terminated" : - (rs & STOP) != 0 ? "Terminating" : - (rs & SHUTDOWN) != 0 ? "Shutting down" : + String level = ((md & TERMINATED) != 0 ? "Terminated" : + (md & STOP) != 0 ? "Terminating" : + (md & SHUTDOWN) != 0 ? "Shutting down" : "Running"); return super.toString() + "[" + level + @@ -3193,7 +2893,7 @@ * @return {@code true} if all tasks have completed following shut down */ public boolean isTerminated() { - return (runState & TERMINATED) != 0; + return (mode & TERMINATED) != 0; } /** @@ -3210,8 +2910,8 @@ * @return {@code true} if terminating but not yet terminated */ public boolean isTerminating() { - int rs = runState; - return (rs & STOP) != 0 && (rs & TERMINATED) == 0; + int md = mode; + return (md & STOP) != 0 && (md & TERMINATED) == 0; } /** @@ -3220,7 +2920,7 @@ * @return {@code true} if this pool has been shut down */ public boolean isShutdown() { - return (runState & SHUTDOWN) != 0; + return (mode & SHUTDOWN) != 0; } /** @@ -3284,30 +2984,19 @@ helpQuiescePool(wt.workQueue); return true; } - long startTime = System.nanoTime(); - WorkQueue[] ws; - int r = 0, wl; - boolean found = true; - while (!isQuiescent() && (ws = workQueues) != null && - (wl = ws.length) > 0) { - if (!found) { - if ((System.nanoTime() - startTime) > nanos) + else { + for (long startTime = System.nanoTime();;) { + ForkJoinTask t; + if ((t = pollScan(false)) != null) + t.doExec(); + else if (isQuiescent()) + return true; + else if ((System.nanoTime() - startTime) > nanos) return false; - Thread.yield(); // cannot block - } - found = false; - for (int m = wl - 1, j = (m + 1) << 2; j >= 0; --j) { - ForkJoinTask t; WorkQueue q; int b, k; - if ((k = r++ & m) <= m && k >= 0 && (q = ws[k]) != null && - (b = q.base) - q.top < 0) { - found = true; - if ((t = q.pollAt(b)) != null) - t.doExec(); - break; - } + else + Thread.yield(); // cannot block } } - return true; } /** @@ -3422,17 +3111,19 @@ throws InterruptedException { ForkJoinPool p; ForkJoinWorkerThread wt; + WorkQueue w; Thread t = Thread.currentThread(); if ((t instanceof ForkJoinWorkerThread) && - (p = (wt = (ForkJoinWorkerThread)t).pool) != null) { - WorkQueue w = wt.workQueue; + (p = (wt = (ForkJoinWorkerThread)t).pool) != null && + (w = wt.workQueue) != null) { + int block; while (!blocker.isReleasable()) { - if (p.tryCompensate(w)) { + if ((block = p.tryCompensate(w)) != 0) { try { do {} while (!blocker.isReleasable() && !blocker.block()); } finally { - U.getAndAddLong(p, CTL, AC_UNIT); + CTL.getAndAdd(p, (block > 0) ? RC_UNIT : 0L); } break; } @@ -3444,6 +3135,55 @@ } } + /** + * If the given executor is a ForkJoinPool, poll and execute + * AsynchronousCompletionTasks from worker's queue until none are + * available or blocker is released. + */ + static void helpAsyncBlocker(Executor e, ManagedBlocker blocker) { + if (blocker != null && (e instanceof ForkJoinPool)) { + WorkQueue w; ForkJoinWorkerThread wt; WorkQueue[] ws; int r, n; + ForkJoinPool p = (ForkJoinPool)e; + Thread thread = Thread.currentThread(); + if (thread instanceof ForkJoinWorkerThread && + (wt = (ForkJoinWorkerThread)thread).pool == p) + w = wt.workQueue; + else if ((r = ThreadLocalRandom.getProbe()) != 0 && + (ws = p.workQueues) != null && (n = ws.length) > 0) + w = ws[(n - 1) & r & SQMASK]; + else + w = null; + if (w != null) { + for (;;) { + int b = w.base, s = w.top, d, al; ForkJoinTask[] a; + if ((a = w.array) != null && (d = b - s) < 0 && + (al = a.length) > 0) { + int index = (al - 1) & b; + ForkJoinTask t = (ForkJoinTask) + QA.getAcquire(a, index); + if (blocker.isReleasable()) + break; + else if (b++ == w.base) { + if (t == null) { + if (d == -1) + break; + } + else if (!(t instanceof CompletableFuture. + AsynchronousCompletionTask)) + break; + else if (QA.compareAndSet(a, index, t, null)) { + w.base = b; + t.doExec(); + } + } + } + else + break; + } + } + } + } + // AbstractExecutorService overrides. These rely on undocumented // fact that ForkJoinTask.adapt returns ForkJoinTasks that also // implement RunnableFuture. @@ -3456,24 +3196,17 @@ return new ForkJoinTask.AdaptedCallable(callable); } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long CTL; - private static final long RUNSTATE; - private static final int ABASE; - private static final int ASHIFT; + // VarHandle mechanics + private static final VarHandle CTL; + private static final VarHandle MODE; + private static final VarHandle QA; static { try { - CTL = U.objectFieldOffset - (ForkJoinPool.class.getDeclaredField("ctl")); - RUNSTATE = U.objectFieldOffset - (ForkJoinPool.class.getDeclaredField("runState")); - ABASE = U.arrayBaseOffset(ForkJoinTask[].class); - int scale = U.arrayIndexScale(ForkJoinTask[].class); - if ((scale & (scale - 1)) != 0) - throw new Error("array index scale not a power of two"); - ASHIFT = 31 - Integer.numberOfLeadingZeros(scale); + MethodHandles.Lookup l = MethodHandles.lookup(); + CTL = l.findVarHandle(ForkJoinPool.class, "ctl", long.class); + MODE = l.findVarHandle(ForkJoinPool.class, "mode", int.class); + QA = MethodHandles.arrayElementVarHandle(ForkJoinTask[].class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -3497,51 +3230,10 @@ common = java.security.AccessController.doPrivileged (new java.security.PrivilegedAction() { - public ForkJoinPool run() { return makeCommonPool(); }}); - - // report 1 even if threads disabled - COMMON_PARALLELISM = Math.max(common.config & SMASK, 1); - } + public ForkJoinPool run() { + return new ForkJoinPool((byte)0); }}); - /** - * Creates and returns the common pool, respecting user settings - * specified via system properties. - */ - @SuppressWarnings("deprecation") // Class.newInstance - static ForkJoinPool makeCommonPool() { - int parallelism = -1; - ForkJoinWorkerThreadFactory factory = null; - UncaughtExceptionHandler handler = null; - try { // ignore exceptions in accessing/parsing properties - String pp = System.getProperty - ("java.util.concurrent.ForkJoinPool.common.parallelism"); - String fp = System.getProperty - ("java.util.concurrent.ForkJoinPool.common.threadFactory"); - String hp = System.getProperty - ("java.util.concurrent.ForkJoinPool.common.exceptionHandler"); - if (pp != null) - parallelism = Integer.parseInt(pp); - if (fp != null) - factory = ((ForkJoinWorkerThreadFactory)ClassLoader. - getSystemClassLoader().loadClass(fp).newInstance()); - if (hp != null) - handler = ((UncaughtExceptionHandler)ClassLoader. - getSystemClassLoader().loadClass(hp).newInstance()); - } catch (Exception ignore) { - } - if (factory == null) { - if (System.getSecurityManager() == null) - factory = defaultForkJoinWorkerThreadFactory; - else // use security-managed default - factory = new InnocuousForkJoinWorkerThreadFactory(); - } - if (parallelism < 0 && // default 1 less than #cores - (parallelism = Runtime.getRuntime().availableProcessors() - 1) <= 0) - parallelism = 1; - if (parallelism > MAX_CAP) - parallelism = MAX_CAP; - return new ForkJoinPool(parallelism, factory, handler, LIFO_QUEUE, - "ForkJoinPool.commonPool-worker-"); + COMMON_PARALLELISM = Math.max(common.mode & SMASK, 1); } /** diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java Mon Jul 18 09:38:08 2016 -0700 @@ -36,6 +36,8 @@ package java.util.concurrent; import java.io.Serializable; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.lang.reflect.Constructor; @@ -92,7 +94,7 @@ * encountering the exception; minimally only the latter. * *

It is possible to define and use ForkJoinTasks that may block, - * but doing do requires three further considerations: (1) Completion + * but doing so requires three further considerations: (1) Completion * of few if any other tasks should be dependent on a task * that blocks on external synchronization or I/O. Event-style async * tasks that are never joined (for example, those subclassing {@link @@ -259,7 +261,7 @@ for (int s;;) { if ((s = status) < 0) return s; - if (U.compareAndSwapInt(this, STATUS, s, s | completion)) { + if (STATUS.compareAndSet(this, s, s | completion)) { if ((s >>> 16) != 0) synchronized (this) { notifyAll(); } return completion; @@ -297,7 +299,7 @@ final void internalWait(long timeout) { int s; if ((s = status) >= 0 && // force completer to issue notify - U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) { + STATUS.compareAndSet(this, s, s | SIGNAL)) { synchronized (this) { if (status >= 0) try { wait(timeout); } catch (InterruptedException ie) { } @@ -319,7 +321,7 @@ if (s >= 0 && (s = status) >= 0) { boolean interrupted = false; do { - if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) { + if (STATUS.compareAndSet(this, s, s | SIGNAL)) { synchronized (this) { if (status >= 0) { try { @@ -353,7 +355,7 @@ ForkJoinPool.common.tryExternalUnpush(this) ? doExec() : 0)) >= 0) { while ((s = status) >= 0) { - if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) { + if (STATUS.compareAndSet(this, s, s | SIGNAL)) { synchronized (this) { if (status >= 0) wait(0L); @@ -400,22 +402,24 @@ // Exception table support /** - * Table of exceptions thrown by tasks, to enable reporting by - * callers. Because exceptions are rare, we don't directly keep + * Hash table of exceptions thrown by tasks, to enable reporting + * by callers. Because exceptions are rare, we don't directly keep * them with task objects, but instead use a weak ref table. Note * that cancellation exceptions don't appear in the table, but are * instead recorded as status values. * - * Note: These statics are initialized below in static block. + * The exception table has a fixed capacity. */ - private static final ExceptionNode[] exceptionTable; - private static final ReentrantLock exceptionTableLock; - private static final ReferenceQueue exceptionTableRefQueue; + private static final ExceptionNode[] exceptionTable + = new ExceptionNode[32]; - /** - * Fixed capacity for exceptionTable. - */ - private static final int EXCEPTION_MAP_CAPACITY = 32; + /** Lock protecting access to exceptionTable. */ + private static final ReentrantLock exceptionTableLock + = new ReentrantLock(); + + /** Reference queue of stale exceptionally completed tasks. */ + private static final ReferenceQueue> exceptionTableRefQueue + = new ReferenceQueue>(); /** * Key-value nodes for exception table. The chained hash table @@ -435,7 +439,7 @@ final long thrower; // use id not ref to avoid weak cycles final int hashCode; // store task hashCode before weak ref disappears ExceptionNode(ForkJoinTask task, Throwable ex, ExceptionNode next, - ReferenceQueue exceptionTableRefQueue) { + ReferenceQueue> exceptionTableRefQueue) { super(task, exceptionTableRefQueue); this.ex = ex; this.next = next; @@ -599,9 +603,8 @@ private static void expungeStaleExceptions() { for (Object x; (x = exceptionTableRefQueue.poll()) != null;) { if (x instanceof ExceptionNode) { - int hashCode = ((ExceptionNode)x).hashCode; ExceptionNode[] t = exceptionTable; - int i = hashCode & (t.length - 1); + int i = ((ExceptionNode)x).hashCode & (t.length - 1); ExceptionNode e = t[i]; ExceptionNode pred = null; while (e != null) { @@ -1031,7 +1034,7 @@ while ((s = status) >= 0 && (ns = deadline - System.nanoTime()) > 0L) { if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) > 0L && - U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) { + STATUS.compareAndSet(this, s, s | SIGNAL)) { synchronized (this) { if (status >= 0) wait(ms); // OK to throw InterruptedException @@ -1324,8 +1327,8 @@ */ public final short setForkJoinTaskTag(short newValue) { for (int s;;) { - if (U.compareAndSwapInt(this, STATUS, s = status, - (s & ~SMASK) | (newValue & SMASK))) + if (STATUS.compareAndSet(this, s = status, + (s & ~SMASK) | (newValue & SMASK))) return (short)s; } } @@ -1348,8 +1351,8 @@ for (int s;;) { if ((short)(s = status) != expect) return false; - if (U.compareAndSwapInt(this, STATUS, s, - (s & ~SMASK) | (update & SMASK))) + if (STATUS.compareAndSet(this, s, + (s & ~SMASK) | (update & SMASK))) return true; } } @@ -1510,17 +1513,12 @@ setExceptionalCompletion((Throwable)ex); } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long STATUS; - + // VarHandle mechanics + private static final VarHandle STATUS; static { - exceptionTableLock = new ReentrantLock(); - exceptionTableRefQueue = new ReferenceQueue(); - exceptionTable = new ExceptionNode[EXCEPTION_MAP_CAPACITY]; try { - STATUS = U.objectFieldOffset - (ForkJoinTask.class.getDeclaredField("status")); + MethodHandles.Lookup l = MethodHandles.lookup(); + STATUS = l.findVarHandle(ForkJoinTask.class, "status", int.class); } catch (ReflectiveOperationException e) { throw new Error(e); } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java Mon Jul 18 09:38:08 2016 -0700 @@ -66,8 +66,9 @@ * owning thread. * * Support for (non-public) subclass InnocuousForkJoinWorkerThread - * requires that we break quite a lot of encapsulation (via Unsafe) - * both here and in the subclass to access and set Thread fields. + * requires that we break quite a lot of encapsulation (via helper + * methods in ThreadLocalRandom) both here and in the subclass to + * access and set Thread fields. */ final ForkJoinPool pool; // the pool this thread works in @@ -92,8 +93,8 @@ ForkJoinWorkerThread(ForkJoinPool pool, ThreadGroup threadGroup, AccessControlContext acc) { super(threadGroup, null, "aForkJoinWorkerThread"); - U.putObjectRelease(this, INHERITEDACCESSCONTROLCONTEXT, acc); - eraseThreadLocals(); // clear before registering + ThreadLocalRandom.setInheritedAccessControlContext(this, acc); + ThreadLocalRandom.eraseThreadLocals(this); // clear before registering this.pool = pool; this.workQueue = pool.registerWorker(this); } @@ -171,37 +172,11 @@ } /** - * Erases ThreadLocals by nulling out Thread maps. - */ - final void eraseThreadLocals() { - U.putObject(this, THREADLOCALS, null); - U.putObject(this, INHERITABLETHREADLOCALS, null); - } - - /** * Non-public hook method for InnocuousForkJoinWorkerThread. */ void afterTopLevelExec() { } - // Set up to allow setting thread fields in constructor - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long THREADLOCALS; - private static final long INHERITABLETHREADLOCALS; - private static final long INHERITEDACCESSCONTROLCONTEXT; - static { - try { - THREADLOCALS = U.objectFieldOffset - (Thread.class.getDeclaredField("threadLocals")); - INHERITABLETHREADLOCALS = U.objectFieldOffset - (Thread.class.getDeclaredField("inheritableThreadLocals")); - INHERITEDACCESSCONTROLCONTEXT = U.objectFieldOffset - (Thread.class.getDeclaredField("inheritedAccessControlContext")); - } catch (ReflectiveOperationException e) { - throw new Error(e); - } - } - /** * A worker thread that has no permissions, is not a member of any * user-defined ThreadGroup, and erases all ThreadLocals after @@ -210,7 +185,7 @@ static final class InnocuousForkJoinWorkerThread extends ForkJoinWorkerThread { /** The ThreadGroup for all InnocuousForkJoinWorkerThreads */ private static final ThreadGroup innocuousThreadGroup = - createThreadGroup(); + ThreadLocalRandom.createThreadGroup("InnocuousForkJoinWorkerThreadGroup"); /** An AccessControlContext supporting no privileges */ private static final AccessControlContext INNOCUOUS_ACC = @@ -225,7 +200,7 @@ @Override // to erase ThreadLocals void afterTopLevelExec() { - eraseThreadLocals(); + ThreadLocalRandom.eraseThreadLocals(this); } @Override // to always report system loader @@ -241,33 +216,5 @@ throw new SecurityException("setContextClassLoader"); } - /** - * Returns a new group with the system ThreadGroup (the - * topmost, parent-less group) as parent. Uses Unsafe to - * traverse Thread.group and ThreadGroup.parent fields. - */ - private static ThreadGroup createThreadGroup() { - try { - jdk.internal.misc.Unsafe u = jdk.internal.misc.Unsafe.getUnsafe(); - long tg = u.objectFieldOffset - (Thread.class.getDeclaredField("group")); - long gp = u.objectFieldOffset - (ThreadGroup.class.getDeclaredField("parent")); - ThreadGroup group = (ThreadGroup) - u.getObject(Thread.currentThread(), tg); - while (group != null) { - ThreadGroup parent = (ThreadGroup)u.getObject(group, gp); - if (parent == null) - return new ThreadGroup(group, - "InnocuousForkJoinWorkerThreadGroup"); - group = parent; - } - } catch (ReflectiveOperationException e) { - throw new Error(e); - } - // fall through if null as cannot-happen safeguard - throw new Error("Cannot create ThreadGroup"); - } } - } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/FutureTask.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/FutureTask.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/FutureTask.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,6 +35,8 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.concurrent.locks.LockSupport; /** @@ -69,9 +71,6 @@ * cancellation races. Sync control in the current design relies * on a "state" field updated via CAS to track completion, along * with a simple Treiber stack to hold waiting threads. - * - * Style note: As usual, we bypass overhead of using - * AtomicXFieldUpdaters and instead directly use Unsafe intrinsics. */ /** @@ -163,9 +162,8 @@ } public boolean cancel(boolean mayInterruptIfRunning) { - if (!(state == NEW && - U.compareAndSwapInt(this, STATE, NEW, - mayInterruptIfRunning ? INTERRUPTING : CANCELLED))) + if (!(state == NEW && STATE.compareAndSet + (this, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED))) return false; try { // in case call to interrupt throws exception if (mayInterruptIfRunning) { @@ -174,7 +172,7 @@ if (t != null) t.interrupt(); } finally { // final state - U.putIntRelease(this, STATE, INTERRUPTED); + STATE.setRelease(this, INTERRUPTED); } } } finally { @@ -228,9 +226,9 @@ * @param v the value */ protected void set(V v) { - if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) { + if (STATE.compareAndSet(this, NEW, COMPLETING)) { outcome = v; - U.putIntRelease(this, STATE, NORMAL); // final state + STATE.setRelease(this, NORMAL); // final state finishCompletion(); } } @@ -246,16 +244,16 @@ * @param t the cause of failure */ protected void setException(Throwable t) { - if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) { + if (STATE.compareAndSet(this, NEW, COMPLETING)) { outcome = t; - U.putIntRelease(this, STATE, EXCEPTIONAL); // final state + STATE.setRelease(this, EXCEPTIONAL); // final state finishCompletion(); } } public void run() { if (state != NEW || - !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread())) + !RUNNER.compareAndSet(this, null, Thread.currentThread())) return; try { Callable c = callable; @@ -296,7 +294,7 @@ */ protected boolean runAndReset() { if (state != NEW || - !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread())) + !RUNNER.compareAndSet(this, null, Thread.currentThread())) return false; boolean ran = false; int s = state; @@ -363,7 +361,7 @@ private void finishCompletion() { // assert state > COMPLETING; for (WaitNode q; (q = waiters) != null;) { - if (U.compareAndSwapObject(this, WAITERS, q, null)) { + if (WAITERS.weakCompareAndSetVolatile(this, q, null)) { for (;;) { Thread t = q.thread; if (t != null) { @@ -425,8 +423,7 @@ q = new WaitNode(); } else if (!queued) - queued = U.compareAndSwapObject(this, WAITERS, - q.next = waiters, q); + queued = WAITERS.weakCompareAndSetVolatile(this, q.next = waiters, q); else if (timed) { final long parkNanos; if (startTime == 0L) { // first time @@ -475,7 +472,7 @@ if (pred.thread == null) // check for race continue retry; } - else if (!U.compareAndSwapObject(this, WAITERS, q, s)) + else if (!WAITERS.compareAndSet(this, q, s)) continue retry; } break; @@ -483,19 +480,16 @@ } } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long STATE; - private static final long RUNNER; - private static final long WAITERS; + // VarHandle mechanics + private static final VarHandle STATE; + private static final VarHandle RUNNER; + private static final VarHandle WAITERS; static { try { - STATE = U.objectFieldOffset - (FutureTask.class.getDeclaredField("state")); - RUNNER = U.objectFieldOffset - (FutureTask.class.getDeclaredField("runner")); - WAITERS = U.objectFieldOffset - (FutureTask.class.getDeclaredField("waiters")); + MethodHandles.Lookup l = MethodHandles.lookup(); + STATE = l.findVarHandle(FutureTask.class, "state", int.class); + RUNNER = l.findVarHandle(FutureTask.class, "runner", Thread.class); + WAITERS = l.findVarHandle(FutureTask.class, "waiters", WaitNode.class); } catch (ReflectiveOperationException e) { throw new Error(e); } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/LinkedTransferQueue.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/LinkedTransferQueue.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/LinkedTransferQueue.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,6 +35,8 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.AbstractQueue; import java.util.Arrays; import java.util.Collection; @@ -444,7 +446,7 @@ /** * Queue nodes. Uses Object, not E, for items to allow forgetting - * them after use. Relies heavily on Unsafe mechanics to minimize + * them after use. Relies heavily on VarHandles to minimize * unnecessary ordering constraints: Writes that are intrinsically * ordered wrt other accesses or CASes use simple relaxed forms. */ @@ -456,12 +458,12 @@ // CAS methods for fields final boolean casNext(Node cmp, Node val) { - return U.compareAndSwapObject(this, NEXT, cmp, val); + return NEXT.compareAndSet(this, cmp, val); } final boolean casItem(Object cmp, Object val) { // assert cmp == null || cmp.getClass() != Node.class; - return U.compareAndSwapObject(this, ITEM, cmp, val); + return ITEM.compareAndSet(this, cmp, val); } /** @@ -469,7 +471,7 @@ * only be seen after publication via casNext. */ Node(Object item, boolean isData) { - U.putObject(this, ITEM, item); // relaxed write + ITEM.set(this, item); // relaxed write this.isData = isData; } @@ -478,7 +480,7 @@ * only after CASing head field, so uses relaxed write. */ final void forgetNext() { - U.putObject(this, NEXT, this); + NEXT.set(this, this); } /** @@ -491,8 +493,8 @@ * else we don't care). */ final void forgetContents() { - U.putObject(this, ITEM, this); - U.putObject(this, WAITER, null); + ITEM.set(this, this); + WAITER.set(this, null); } /** @@ -537,19 +539,16 @@ private static final long serialVersionUID = -3375979862319811754L; - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long ITEM; - private static final long NEXT; - private static final long WAITER; + // VarHandle mechanics + private static final VarHandle ITEM; + private static final VarHandle NEXT; + private static final VarHandle WAITER; static { try { - ITEM = U.objectFieldOffset - (Node.class.getDeclaredField("item")); - NEXT = U.objectFieldOffset - (Node.class.getDeclaredField("next")); - WAITER = U.objectFieldOffset - (Node.class.getDeclaredField("waiter")); + MethodHandles.Lookup l = MethodHandles.lookup(); + ITEM = l.findVarHandle(Node.class, "item", Object.class); + NEXT = l.findVarHandle(Node.class, "next", Node.class); + WAITER = l.findVarHandle(Node.class, "waiter", Thread.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -567,15 +566,15 @@ // CAS methods for fields private boolean casTail(Node cmp, Node val) { - return U.compareAndSwapObject(this, TAIL, cmp, val); + return TAIL.compareAndSet(this, cmp, val); } private boolean casHead(Node cmp, Node val) { - return U.compareAndSwapObject(this, HEAD, cmp, val); + return HEAD.compareAndSet(this, cmp, val); } private boolean casSweepVotes(int cmp, int val) { - return U.compareAndSwapInt(this, SWEEPVOTES, cmp, val); + return SWEEPVOTES.compareAndSet(this, cmp, val); } /* @@ -1562,20 +1561,19 @@ } } - // Unsafe mechanics - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long HEAD; - private static final long TAIL; - private static final long SWEEPVOTES; + // VarHandle mechanics + private static final VarHandle HEAD; + private static final VarHandle TAIL; + private static final VarHandle SWEEPVOTES; static { try { - HEAD = U.objectFieldOffset - (LinkedTransferQueue.class.getDeclaredField("head")); - TAIL = U.objectFieldOffset - (LinkedTransferQueue.class.getDeclaredField("tail")); - SWEEPVOTES = U.objectFieldOffset - (LinkedTransferQueue.class.getDeclaredField("sweepVotes")); + MethodHandles.Lookup l = MethodHandles.lookup(); + HEAD = l.findVarHandle(LinkedTransferQueue.class, "head", + Node.class); + TAIL = l.findVarHandle(LinkedTransferQueue.class, "tail", + Node.class); + SWEEPVOTES = l.findVarHandle(LinkedTransferQueue.class, "sweepVotes", + int.class); } catch (ReflectiveOperationException e) { throw new Error(e); } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/Phaser.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/Phaser.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/Phaser.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,6 +35,8 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.LockSupport; @@ -221,7 +223,6 @@ * phaser.arriveAndDeregister(); * }} * - * *

To create a set of {@code n} tasks using a tree of phasers, you * could use code of the following form, assuming a Task class with a * constructor accepting a {@code Phaser} that it registers with upon @@ -384,7 +385,7 @@ int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK); if (unarrived <= 0) throw new IllegalStateException(badArrive(s)); - if (U.compareAndSwapLong(this, STATE, s, s-=adjust)) { + if (STATE.compareAndSet(this, s, s-=adjust)) { if (unarrived == 1) { long n = s & PARTIES_MASK; // base of next state int nextUnarrived = (int)n >>> PARTIES_SHIFT; @@ -397,12 +398,12 @@ n |= nextUnarrived; int nextPhase = (phase + 1) & MAX_PHASE; n |= (long)nextPhase << PHASE_SHIFT; - U.compareAndSwapLong(this, STATE, s, n); + STATE.compareAndSet(this, s, n); releaseWaiters(phase); } else if (nextUnarrived == 0) { // propagate deregistration phase = parent.doArrive(ONE_DEREGISTER); - U.compareAndSwapLong(this, STATE, s, s | EMPTY); + STATE.compareAndSet(this, s, s | EMPTY); } else phase = parent.doArrive(ONE_ARRIVAL); @@ -437,13 +438,13 @@ if (parent == null || reconcileState() == s) { if (unarrived == 0) // wait out advance root.internalAwaitAdvance(phase, null); - else if (U.compareAndSwapLong(this, STATE, s, s + adjust)) + else if (STATE.compareAndSet(this, s, s + adjust)) break; } } else if (parent == null) { // 1st root registration long next = ((long)phase << PHASE_SHIFT) | adjust; - if (U.compareAndSwapLong(this, STATE, s, next)) + if (STATE.compareAndSet(this, s, next)) break; } else { @@ -455,8 +456,8 @@ // finish registration whenever parent registration // succeeded, even when racing with termination, // since these are part of the same "transaction". - while (!U.compareAndSwapLong - (this, STATE, s, + while (!STATE.weakCompareAndSetVolatile + (this, s, ((long)phase << PHASE_SHIFT) | adjust)) { s = state; phase = (int)(root.state >>> PHASE_SHIFT); @@ -487,8 +488,8 @@ // CAS to root phase with current parties, tripping unarrived while ((phase = (int)(root.state >>> PHASE_SHIFT)) != (int)(s >>> PHASE_SHIFT) && - !U.compareAndSwapLong - (this, STATE, s, + !STATE.weakCompareAndSetVolatile + (this, s, s = (((long)phase << PHASE_SHIFT) | ((phase < 0) ? (s & COUNTS_MASK) : (((p = (int)s >>> PARTIES_SHIFT) == 0) ? EMPTY : @@ -677,7 +678,7 @@ int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK); if (unarrived <= 0) throw new IllegalStateException(badArrive(s)); - if (U.compareAndSwapLong(this, STATE, s, s -= ONE_ARRIVAL)) { + if (STATE.compareAndSet(this, s, s -= ONE_ARRIVAL)) { if (unarrived > 1) return root.internalAwaitAdvance(phase, null); if (root != this) @@ -692,7 +693,7 @@ n |= nextUnarrived; int nextPhase = (phase + 1) & MAX_PHASE; n |= (long)nextPhase << PHASE_SHIFT; - if (!U.compareAndSwapLong(this, STATE, s, n)) + if (!STATE.compareAndSet(this, s, n)) return (int)(state >>> PHASE_SHIFT); // terminated releaseWaiters(phase); return nextPhase; @@ -808,7 +809,7 @@ final Phaser root = this.root; long s; while ((s = root.state) >= 0) { - if (U.compareAndSwapLong(root, STATE, s, s | TERMINATION_BIT)) { + if (STATE.compareAndSet(root, s, s | TERMINATION_BIT)) { // signal all threads releaseWaiters(0); // Waiters on evenQ releaseWaiters(1); // Waiters on oddQ @@ -1043,6 +1044,8 @@ node = new QNode(this, phase, false, false, 0L); node.wasInterrupted = interrupted; } + else + Thread.onSpinWait(); } else if (node.isReleasable()) // done or aborted break; @@ -1131,14 +1134,12 @@ } } - // Unsafe mechanics - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long STATE; + // VarHandle mechanics + private static final VarHandle STATE; static { try { - STATE = U.objectFieldOffset - (Phaser.class.getDeclaredField("state")); + MethodHandles.Lookup l = MethodHandles.lookup(); + STATE = l.findVarHandle(Phaser.class, "state", long.class); } catch (ReflectiveOperationException e) { throw new Error(e); } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,6 +35,8 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.AbstractQueue; import java.util.Arrays; import java.util.Collection; @@ -289,7 +291,7 @@ lock.unlock(); // must release and then re-acquire main lock Object[] newArray = null; if (allocationSpinLock == 0 && - U.compareAndSwapInt(this, ALLOCATIONSPINLOCK, 0, 1)) { + ALLOCATIONSPINLOCK.compareAndSet(this, 0, 1)) { try { int newCap = oldCap + ((oldCap < 64) ? (oldCap + 2) : // grow faster if small @@ -1009,13 +1011,14 @@ return new PBQSpliterator(this, null, 0, -1); } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long ALLOCATIONSPINLOCK; + // VarHandle mechanics + private static final VarHandle ALLOCATIONSPINLOCK; static { try { - ALLOCATIONSPINLOCK = U.objectFieldOffset - (PriorityBlockingQueue.class.getDeclaredField("allocationSpinLock")); + MethodHandles.Lookup l = MethodHandles.lookup(); + ALLOCATIONSPINLOCK = l.findVarHandle(PriorityBlockingQueue.class, + "allocationSpinLock", + int.class); } catch (ReflectiveOperationException e) { throw new Error(e); } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,6 +35,8 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.LockSupport; @@ -866,7 +868,7 @@ /** Subscriber for method consume */ private static final class ConsumerSubscriber - implements Flow.Subscriber { + implements Flow.Subscriber { final CompletableFuture status; final Consumer consumer; Flow.Subscription subscription; @@ -906,7 +908,7 @@ */ @SuppressWarnings("serial") static final class ConsumerTask extends ForkJoinTask - implements Runnable { + implements Runnable, CompletableFuture.AsynchronousCompletionTask { final BufferedSubscription consumer; ConsumerTask(BufferedSubscription consumer) { this.consumer = consumer; @@ -959,11 +961,9 @@ * Blocking control relies on the "waiter" field. Producers set * the field before trying to block, but must then recheck (via * offer) before parking. Signalling then just unparks and clears - * waiter field. If the producer and consumer are both in the same - * ForkJoinPool, or consumers are running in commonPool, the - * producer attempts to help run consumer tasks that it forked - * before blocking. To avoid potential cycles, only one level of - * helping is currently supported. + * waiter field. If the producer and/or consumer are using a + * ForkJoinPool, the producer attempts to help run consumer tasks + * via ForkJoinPool.helpAsyncBlocker before blocking. * * This class uses @Contended and heuristic field declaration * ordering to reduce false-sharing-based memory contention among @@ -983,7 +983,6 @@ volatile long demand; // # unfilled requests int maxCapacity; // reduced on OOME int putStat; // offer result for ManagedBlocker - int helpDepth; // nested helping depth (at most 1) volatile int ctl; // atomic run state flags volatile int head; // next position to take int tail; // next position to put @@ -1077,7 +1076,7 @@ alloc = true; } else { - U.fullFence(); // recheck + VarHandle.fullFence(); // recheck int h = head, t = tail, size = t + 1 - h; if (cap >= size) { a[(cap - 1) & t] = item; @@ -1116,10 +1115,10 @@ if (a != null && cap > 0) { int mask = cap - 1; for (int j = head; j != t; ++j) { - long k = ((long)(j & mask) << ASHIFT) + ABASE; - Object x = U.getObjectVolatile(a, k); + int k = j & mask; + Object x = QA.getAcquire(a, k); if (x != null && // races with consumer - U.compareAndSwapObject(a, k, x, null)) + QA.compareAndSet(a, k, x, null)) newArray[j & newMask] = x; } } @@ -1136,28 +1135,20 @@ * initial offer return 0. */ final int submit(T item) { - int stat; Executor e; ForkJoinWorkerThread w; - if ((stat = offer(item)) == 0 && helpDepth == 0 && - ((e = executor) instanceof ForkJoinPool)) { - helpDepth = 1; - Thread thread = Thread.currentThread(); - if ((thread instanceof ForkJoinWorkerThread) && - ((w = (ForkJoinWorkerThread)thread)).getPool() == e) - stat = internalHelpConsume(w.workQueue, item); - else if (e == ForkJoinPool.commonPool()) - stat = externalHelpConsume - (ForkJoinPool.commonSubmitterQueue(), item); - helpDepth = 0; - } - if (stat == 0 && (stat = offer(item)) == 0) { + int stat; + if ((stat = offer(item)) == 0) { putItem = item; timeout = 0L; - try { - ForkJoinPool.managedBlock(this); - } catch (InterruptedException ie) { - timeout = INTERRUPTED; + putStat = 0; + ForkJoinPool.helpAsyncBlocker(executor, this); + if ((stat = putStat) == 0) { + try { + ForkJoinPool.managedBlock(this); + } catch (InterruptedException ie) { + timeout = INTERRUPTED; + } + stat = putStat; } - stat = putStat; if (timeout < 0L) Thread.currentThread().interrupt(); } @@ -1165,71 +1156,22 @@ } /** - * Tries helping for FJ submitter. - */ - private int internalHelpConsume(ForkJoinPool.WorkQueue w, T item) { - int stat = 0; - if (w != null) { - ForkJoinTask t; - while ((t = w.peek()) != null && (t instanceof ConsumerTask)) { - if ((stat = offer(item)) != 0 || !w.tryUnpush(t)) - break; - ((ConsumerTask)t).consumer.consume(); - } - } - return stat; - } - - /** - * Tries helping for non-FJ submitter. - */ - private int externalHelpConsume(ForkJoinPool.WorkQueue w, T item) { - int stat = 0; - if (w != null) { - ForkJoinTask t; - while ((t = w.peek()) != null && (t instanceof ConsumerTask)) { - if ((stat = offer(item)) != 0 || !w.trySharedUnpush(t)) - break; - ((ConsumerTask)t).consumer.consume(); - } - } - return stat; - } - - /** * Timeout version; similar to submit. */ final int timedOffer(T item, long nanos) { - int stat; Executor e; - if ((stat = offer(item)) == 0 && helpDepth == 0 && - ((e = executor) instanceof ForkJoinPool)) { - Thread thread = Thread.currentThread(); - if (((thread instanceof ForkJoinWorkerThread) && - ((ForkJoinWorkerThread)thread).getPool() == e) || - e == ForkJoinPool.commonPool()) { - helpDepth = 1; - ForkJoinTask t; - long deadline = System.nanoTime() + nanos; - while ((t = ForkJoinTask.peekNextLocalTask()) != null && - (t instanceof ConsumerTask)) { - if ((stat = offer(item)) != 0 || - (nanos = deadline - System.nanoTime()) <= 0L || - !t.tryUnfork()) - break; - ((ConsumerTask)t).consumer.consume(); + int stat; + if ((stat = offer(item)) == 0 && (timeout = nanos) > 0L) { + putItem = item; + putStat = 0; + ForkJoinPool.helpAsyncBlocker(executor, this); + if ((stat = putStat) == 0) { + try { + ForkJoinPool.managedBlock(this); + } catch (InterruptedException ie) { + timeout = INTERRUPTED; } - helpDepth = 0; + stat = putStat; } - } - if (stat == 0 && (stat = offer(item)) == 0 && - (timeout = nanos) > 0L) { - putItem = item; - try { - ForkJoinPool.managedBlock(this); - } catch (InterruptedException ie) { - timeout = INTERRUPTED; - } - stat = putStat; if (timeout < 0L) Thread.currentThread().interrupt(); } @@ -1249,22 +1191,20 @@ } else if ((c & ACTIVE) != 0) { // ensure keep-alive if ((c & CONSUME) != 0 || - U.compareAndSwapInt(this, CTL, c, - c | CONSUME)) + CTL.compareAndSet(this, c, c | CONSUME)) break; } else if (demand == 0L || tail == head) break; - else if (U.compareAndSwapInt(this, CTL, c, - c | (ACTIVE | CONSUME))) { + else if (CTL.compareAndSet(this, c, c | (ACTIVE | CONSUME))) { try { e.execute(new ConsumerTask(this)); break; } catch (RuntimeException | Error ex) { // back out do {} while (((c = ctl) & DISABLED) == 0 && (c & ACTIVE) != 0 && - !U.compareAndSwapInt(this, CTL, c, - c & ~ACTIVE)); + !CTL.weakCompareAndSetVolatile + (this, c, c & ~ACTIVE)); throw ex; } } @@ -1300,10 +1240,10 @@ break; else if ((c & ACTIVE) != 0) { pendingError = ex; - if (U.compareAndSwapInt(this, CTL, c, c | ERROR)) + if (CTL.compareAndSet(this, c, c | ERROR)) break; // cause consumer task to exit } - else if (U.compareAndSwapInt(this, CTL, c, DISABLED)) { + else if (CTL.compareAndSet(this, c, DISABLED)) { Flow.Subscriber s = subscriber; if (s != null && ex != null) { try { @@ -1330,7 +1270,7 @@ for (int c;;) { if ((c = ctl) == DISABLED || (c & ACTIVE) == 0) break; - if (U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE)) { + if (CTL.compareAndSet(this, c, c & ~ACTIVE)) { onError(ex); break; } @@ -1343,8 +1283,8 @@ for (int c;;) { if ((c = ctl) == DISABLED) break; - if (U.compareAndSwapInt(this, CTL, c, - c | (ACTIVE | CONSUME | COMPLETE))) { + if (CTL.compareAndSet(this, c, + c | (ACTIVE | CONSUME | COMPLETE))) { if ((c & ACTIVE) == 0) startOrDisable(); break; @@ -1356,8 +1296,8 @@ for (int c;;) { if ((c = ctl) == DISABLED) break; - if (U.compareAndSwapInt(this, CTL, c, - c | (ACTIVE | CONSUME | SUBSCRIBE))) { + if (CTL.compareAndSet(this, c, + c | (ACTIVE | CONSUME | SUBSCRIBE))) { if ((c & ACTIVE) == 0) startOrDisable(); break; @@ -1375,11 +1315,11 @@ if ((c = ctl) == DISABLED) break; else if ((c & ACTIVE) != 0) { - if (U.compareAndSwapInt(this, CTL, c, - c | (CONSUME | ERROR))) + if (CTL.compareAndSet(this, c, + c | (CONSUME | ERROR))) break; } - else if (U.compareAndSwapInt(this, CTL, c, DISABLED)) { + else if (CTL.compareAndSet(this, c, DISABLED)) { detach(); break; } @@ -1395,19 +1335,18 @@ long prev = demand, d; if ((d = prev + n) < prev) // saturate d = Long.MAX_VALUE; - if (U.compareAndSwapLong(this, DEMAND, prev, d)) { + if (DEMAND.compareAndSet(this, prev, d)) { for (int c, h;;) { if ((c = ctl) == DISABLED) break; else if ((c & ACTIVE) != 0) { if ((c & CONSUME) != 0 || - U.compareAndSwapInt(this, CTL, c, - c | CONSUME)) + CTL.compareAndSet(this, c, c | CONSUME)) break; } else if ((h = head) != tail) { - if (U.compareAndSwapInt(this, CTL, c, - c | (ACTIVE|CONSUME))) { + if (CTL.compareAndSet(this, c, + c | (ACTIVE|CONSUME))) { startOrDisable(); break; } @@ -1476,16 +1415,14 @@ if ((s = subscriber) != null) { // else disabled for (;;) { long d = demand; - int c; Object[] a; int n; long i; Object x; Thread w; + int c; Object[] a; int n, i; Object x; Thread w; if (((c = ctl) & (ERROR | SUBSCRIBE | DISABLED)) != 0) { if (!checkControl(s, c)) break; } else if ((a = array) == null || h == tail || (n = a.length) == 0 || - (x = U.getObjectVolatile - (a, (i = ((long)((n - 1) & h) << ASHIFT) + ABASE))) - == null) { + (x = QA.getAcquire(a, i = (n - 1) & h)) == null) { if (!checkEmpty(s, c)) break; } @@ -1494,10 +1431,10 @@ break; } else if (((c & CONSUME) != 0 || - U.compareAndSwapInt(this, CTL, c, c | CONSUME)) && - U.compareAndSwapObject(a, i, x, null)) { - U.putIntRelease(this, HEAD, ++h); - U.getAndAddLong(this, DEMAND, -1L); + CTL.compareAndSet(this, c, c | CONSUME)) && + QA.compareAndSet(a, i, x, null)) { + HEAD.setRelease(this, ++h); + DEMAND.getAndAdd(this, -1L); if ((w = waiter) != null) signalWaiter(w); try { @@ -1528,7 +1465,7 @@ } } else if ((c & SUBSCRIBE) != 0) { - if (U.compareAndSwapInt(this, CTL, c, c & ~SUBSCRIBE)) { + if (CTL.compareAndSet(this, c, c & ~SUBSCRIBE)) { try { if (s != null) s.onSubscribe(this); @@ -1551,9 +1488,9 @@ boolean stat = true; if (head == tail) { if ((c & CONSUME) != 0) - U.compareAndSwapInt(this, CTL, c, c & ~CONSUME); + CTL.compareAndSet(this, c, c & ~CONSUME); else if ((c & COMPLETE) != 0) { - if (U.compareAndSwapInt(this, CTL, c, DISABLED)) { + if (CTL.compareAndSet(this, c, DISABLED)) { try { if (s != null) s.onComplete(); @@ -1561,7 +1498,7 @@ } } } - else if (U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE)) + else if (CTL.compareAndSet(this, c, c & ~ACTIVE)) stat = false; } return stat; @@ -1574,8 +1511,8 @@ boolean stat = true; if (demand == 0L) { if ((c & CONSUME) != 0) - U.compareAndSwapInt(this, CTL, c, c & ~CONSUME); - else if (U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE)) + CTL.compareAndSet(this, c, c & ~CONSUME); + else if (CTL.compareAndSet(this, c, c & ~ACTIVE)) stat = false; } return stat; @@ -1595,31 +1532,25 @@ onError(ex); } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long CTL; - private static final long TAIL; - private static final long HEAD; - private static final long DEMAND; - private static final int ABASE; - private static final int ASHIFT; + // VarHandle mechanics + private static final VarHandle CTL; + private static final VarHandle TAIL; + private static final VarHandle HEAD; + private static final VarHandle DEMAND; + private static final VarHandle QA; static { try { - CTL = U.objectFieldOffset - (BufferedSubscription.class.getDeclaredField("ctl")); - TAIL = U.objectFieldOffset - (BufferedSubscription.class.getDeclaredField("tail")); - HEAD = U.objectFieldOffset - (BufferedSubscription.class.getDeclaredField("head")); - DEMAND = U.objectFieldOffset - (BufferedSubscription.class.getDeclaredField("demand")); - - ABASE = U.arrayBaseOffset(Object[].class); - int scale = U.arrayIndexScale(Object[].class); - if ((scale & (scale - 1)) != 0) - throw new Error("data type scale not a power of two"); - ASHIFT = 31 - Integer.numberOfLeadingZeros(scale); + MethodHandles.Lookup l = MethodHandles.lookup(); + CTL = l.findVarHandle(BufferedSubscription.class, "ctl", + int.class); + TAIL = l.findVarHandle(BufferedSubscription.class, "tail", + int.class); + HEAD = l.findVarHandle(BufferedSubscription.class, "head", + int.class); + DEMAND = l.findVarHandle(BufferedSubscription.class, "demand", + long.class); + QA = MethodHandles.arrayElementVarHandle(Object[].class); } catch (ReflectiveOperationException e) { throw new Error(e); } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/SynchronousQueue.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/SynchronousQueue.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/SynchronousQueue.java Mon Jul 18 09:38:08 2016 -0700 @@ -36,6 +36,8 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.AbstractQueue; import java.util.Collection; import java.util.Collections; @@ -247,7 +249,7 @@ boolean casNext(SNode cmp, SNode val) { return cmp == next && - U.compareAndSwapObject(this, NEXT, cmp, val); + SNEXT.compareAndSet(this, cmp, val); } /** @@ -260,7 +262,7 @@ */ boolean tryMatch(SNode s) { if (match == null && - U.compareAndSwapObject(this, MATCH, null, s)) { + SMATCH.compareAndSet(this, null, s)) { Thread w = waiter; if (w != null) { // waiters need at most one unpark waiter = null; @@ -275,24 +277,21 @@ * Tries to cancel a wait by matching node to itself. */ void tryCancel() { - U.compareAndSwapObject(this, MATCH, null, this); + SMATCH.compareAndSet(this, null, this); } boolean isCancelled() { return match == this; } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long MATCH; - private static final long NEXT; - + // VarHandle mechanics + private static final VarHandle SMATCH; + private static final VarHandle SNEXT; static { try { - MATCH = U.objectFieldOffset - (SNode.class.getDeclaredField("match")); - NEXT = U.objectFieldOffset - (SNode.class.getDeclaredField("next")); + MethodHandles.Lookup l = MethodHandles.lookup(); + SMATCH = l.findVarHandle(SNode.class, "match", SNode.class); + SNEXT = l.findVarHandle(SNode.class, "next", SNode.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -304,7 +303,7 @@ boolean casHead(SNode h, SNode nh) { return h == head && - U.compareAndSwapObject(this, HEAD, h, nh); + SHEAD.compareAndSet(this, h, nh); } /** @@ -451,8 +450,10 @@ continue; } } - if (spins > 0) + if (spins > 0) { + Thread.onSpinWait(); spins = shouldSpin(s) ? (spins - 1) : 0; + } else if (s.waiter == null) s.waiter = w; // establish waiter so can park next iter else if (!timed) @@ -508,13 +509,12 @@ } } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long HEAD; + // VarHandle mechanics + private static final VarHandle SHEAD; static { try { - HEAD = U.objectFieldOffset - (TransferStack.class.getDeclaredField("head")); + MethodHandles.Lookup l = MethodHandles.lookup(); + SHEAD = l.findVarHandle(TransferStack.class, "head", SNode.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -546,19 +546,19 @@ boolean casNext(QNode cmp, QNode val) { return next == cmp && - U.compareAndSwapObject(this, NEXT, cmp, val); + QNEXT.compareAndSet(this, cmp, val); } boolean casItem(Object cmp, Object val) { return item == cmp && - U.compareAndSwapObject(this, ITEM, cmp, val); + QITEM.compareAndSet(this, cmp, val); } /** * Tries to cancel by CAS'ing ref to this as item. */ void tryCancel(Object cmp) { - U.compareAndSwapObject(this, ITEM, cmp, this); + QITEM.compareAndSet(this, cmp, this); } boolean isCancelled() { @@ -574,17 +574,14 @@ return next == this; } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long ITEM; - private static final long NEXT; - + // VarHandle mechanics + private static final VarHandle QITEM; + private static final VarHandle QNEXT; static { try { - ITEM = U.objectFieldOffset - (QNode.class.getDeclaredField("item")); - NEXT = U.objectFieldOffset - (QNode.class.getDeclaredField("next")); + MethodHandles.Lookup l = MethodHandles.lookup(); + QITEM = l.findVarHandle(QNode.class, "item", Object.class); + QNEXT = l.findVarHandle(QNode.class, "next", QNode.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -614,7 +611,7 @@ */ void advanceHead(QNode h, QNode nh) { if (h == head && - U.compareAndSwapObject(this, HEAD, h, nh)) + QHEAD.compareAndSet(this, h, nh)) h.next = h; // forget old next } @@ -623,7 +620,7 @@ */ void advanceTail(QNode t, QNode nt) { if (tail == t) - U.compareAndSwapObject(this, TAIL, t, nt); + QTAIL.compareAndSet(this, t, nt); } /** @@ -631,7 +628,7 @@ */ boolean casCleanMe(QNode cmp, QNode val) { return cleanMe == cmp && - U.compareAndSwapObject(this, CLEANME, cmp, val); + QCLEANME.compareAndSet(this, cmp, val); } /** @@ -752,8 +749,10 @@ continue; } } - if (spins > 0) + if (spins > 0) { --spins; + Thread.onSpinWait(); + } else if (s.waiter == null) s.waiter = w; else if (!timed) @@ -817,18 +816,19 @@ } } - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long HEAD; - private static final long TAIL; - private static final long CLEANME; + // VarHandle mechanics + private static final VarHandle QHEAD; + private static final VarHandle QTAIL; + private static final VarHandle QCLEANME; static { try { - HEAD = U.objectFieldOffset - (TransferQueue.class.getDeclaredField("head")); - TAIL = U.objectFieldOffset - (TransferQueue.class.getDeclaredField("tail")); - CLEANME = U.objectFieldOffset - (TransferQueue.class.getDeclaredField("cleanMe")); + MethodHandles.Lookup l = MethodHandles.lookup(); + QHEAD = l.findVarHandle(TransferQueue.class, "head", + QNode.class); + QTAIL = l.findVarHandle(TransferQueue.class, "tail", + QNode.class); + QCLEANME = l.findVarHandle(TransferQueue.class, "cleanMe", + QNode.class); } catch (ReflectiveOperationException e) { throw new Error(e); } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java Mon Jul 18 09:38:08 2016 -0700 @@ -36,6 +36,7 @@ package java.util.concurrent; import java.io.ObjectStreamField; +import java.security.AccessControlContext; import java.util.Random; import java.util.Spliterator; import java.util.concurrent.atomic.AtomicInteger; @@ -47,6 +48,7 @@ import java.util.stream.IntStream; import java.util.stream.LongStream; import java.util.stream.StreamSupport; +import jdk.internal.misc.Unsafe; /** * A random number generator isolated to the current thread. Like the @@ -95,7 +97,9 @@ * ThreadLocalRandom sequence. The dual use is a marriage of * convenience, but is a simple and efficient way of reducing * application-level overhead and footprint of most concurrent - * programs. + * programs. Even more opportunistically, we also define here + * other package-private utilities that access Thread class + * fields. * * Even though this class subclasses java.util.Random, it uses the * same basic algorithm as java.util.SplittableRandom. (See its @@ -958,6 +962,49 @@ return r; } + // Support for other package-private ThreadLocal access + + /** + * Erases ThreadLocals by nulling out Thread maps. + */ + static final void eraseThreadLocals(Thread thread) { + U.putObject(thread, THREADLOCALS, null); + U.putObject(thread, INHERITABLETHREADLOCALS, null); + } + + static final void setInheritedAccessControlContext(Thread thread, + AccessControlContext acc) { + U.putObjectRelease(thread, INHERITEDACCESSCONTROLCONTEXT, acc); + } + + /** + * Returns a new group with the system ThreadGroup (the + * topmost, parent-less group) as parent. Uses Unsafe to + * traverse Thread.group and ThreadGroup.parent fields. + */ + static final ThreadGroup createThreadGroup(String name) { + if (name == null) + throw new NullPointerException(); + try { + long tg = U.objectFieldOffset + (Thread.class.getDeclaredField("group")); + long gp = U.objectFieldOffset + (ThreadGroup.class.getDeclaredField("parent")); + ThreadGroup group = (ThreadGroup) + U.getObject(Thread.currentThread(), tg); + while (group != null) { + ThreadGroup parent = (ThreadGroup)U.getObject(group, gp); + if (parent == null) + return new ThreadGroup(group, name); + group = parent; + } + } catch (ReflectiveOperationException e) { + throw new Error(e); + } + // fall through if null as cannot-happen safeguard + throw new Error("Cannot create ThreadGroup"); + } + // Serialization support private static final long serialVersionUID = -5851777807851030925L; @@ -1022,10 +1069,13 @@ static final String BAD_SIZE = "size must be non-negative"; // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); + private static final Unsafe U = Unsafe.getUnsafe(); private static final long SEED; private static final long PROBE; private static final long SECONDARY; + private static final long THREADLOCALS; + private static final long INHERITABLETHREADLOCALS; + private static final long INHERITEDACCESSCONTROLCONTEXT; static { try { SEED = U.objectFieldOffset @@ -1034,6 +1084,12 @@ (Thread.class.getDeclaredField("threadLocalRandomProbe")); SECONDARY = U.objectFieldOffset (Thread.class.getDeclaredField("threadLocalRandomSecondarySeed")); + THREADLOCALS = U.objectFieldOffset + (Thread.class.getDeclaredField("threadLocals")); + INHERITABLETHREADLOCALS = U.objectFieldOffset + (Thread.class.getDeclaredField("inheritableThreadLocals")); + INHERITEDACCESSCONTROLCONTEXT = U.objectFieldOffset + (Thread.class.getDeclaredField("inheritedAccessControlContext")); } catch (ReflectiveOperationException e) { throw new Error(e); } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicBoolean.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicBoolean.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicBoolean.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,27 +35,26 @@ package java.util.concurrent.atomic; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; + /** * A {@code boolean} value that may be updated atomically. See the - * {@link java.util.concurrent.atomic} package specification for - * description of the properties of atomic variables. An - * {@code AtomicBoolean} is used in applications such as atomically - * updated flags, and cannot be used as a replacement for a - * {@link java.lang.Boolean}. + * {@link VarHandle} specification for descriptions of the properties + * of atomic accesses. An {@code AtomicBoolean} is used in + * applications such as atomically updated flags, and cannot be used + * as a replacement for a {@link java.lang.Boolean}. * * @since 1.5 * @author Doug Lea */ public class AtomicBoolean implements java.io.Serializable { private static final long serialVersionUID = 4654671469794556979L; - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long VALUE; - + private static final VarHandle VALUE; static { try { - VALUE = U.objectFieldOffset - (AtomicBoolean.class.getDeclaredField("value")); + MethodHandles.Lookup l = MethodHandles.lookup(); + VALUE = l.findVarHandle(AtomicBoolean.class, "value", int.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -79,7 +78,8 @@ } /** - * Returns the current value. + * Returns the current value, + * with memory effects as specified by {@link VarHandle#getVolatile}. * * @return the current value */ @@ -88,40 +88,39 @@ } /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. + * Atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#compareAndSet}. * - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful. False return indicates that * the actual value was not equal to the expected value. */ - public final boolean compareAndSet(boolean expect, boolean update) { - return U.compareAndSwapInt(this, VALUE, - (expect ? 1 : 0), - (update ? 1 : 0)); + public final boolean compareAndSet(boolean expectedValue, boolean newValue) { + return VALUE.compareAndSet(this, + (expectedValue ? 1 : 0), + (newValue ? 1 : 0)); } /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. + * Possibly atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#weakCompareAndSet}. * - *

May fail - * spuriously and does not provide ordering guarantees, so is - * only rarely an appropriate alternative to {@code compareAndSet}. - * - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful */ - public boolean weakCompareAndSet(boolean expect, boolean update) { - return U.compareAndSwapInt(this, VALUE, - (expect ? 1 : 0), - (update ? 1 : 0)); + public boolean weakCompareAndSet(boolean expectedValue, boolean newValue) { + return VALUE.weakCompareAndSet(this, + (expectedValue ? 1 : 0), + (newValue ? 1 : 0)); } /** - * Unconditionally sets to the given value. + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setVolatile}. * * @param newValue the new value */ @@ -130,17 +129,19 @@ } /** - * Eventually sets to the given value. + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. * * @param newValue the new value * @since 1.6 */ public final void lazySet(boolean newValue) { - U.putIntRelease(this, VALUE, (newValue ? 1 : 0)); + VALUE.setRelease(this, (newValue ? 1 : 0)); } /** - * Atomically sets to the given value and returns the previous value. + * Atomically sets the value to {@code newValue} and returns the old value, + * with memory effects as specified by {@link VarHandle#getAndSet}. * * @param newValue the new value * @return the previous value @@ -161,4 +162,178 @@ return Boolean.toString(get()); } + // jdk9 + + /** + * Returns the current value, with memory semantics of reading as + * if the variable was declared non-{@code volatile}. + * + * @return the value + * @since 9 + */ + public final boolean getPlain() { + return (int)VALUE.get(this) != 0; + } + + /** + * Sets the value to {@code newValue}, with memory semantics + * of setting as if the variable was declared non-{@code volatile} + * and non-{@code final}. + * + * @param newValue the new value + * @since 9 + */ + public final void setPlain(boolean newValue) { + VALUE.set(this, newValue ? 1 : 0); + } + + /** + * Returns the current value, + * with memory effects as specified by {@link VarHandle#getOpaque}. + * + * @return the value + * @since 9 + */ + public final boolean getOpaque() { + return (int)VALUE.getOpaque(this) != 0; + } + + /** + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setOpaque}. + * + * @param newValue the new value + * @since 9 + */ + public final void setOpaque(boolean newValue) { + VALUE.setOpaque(this, newValue ? 1 : 0); + } + + /** + * Returns the current value, + * with memory effects as specified by {@link VarHandle#getAcquire}. + * + * @return the value + * @since 9 + */ + public final boolean getAcquire() { + return (int)VALUE.getAcquire(this) != 0; + } + + /** + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. + * + * @param newValue the new value + * @since 9 + */ + public final void setRelease(boolean newValue) { + VALUE.setRelease(this, newValue ? 1 : 0); + } + + /** + * Atomically sets the value to {@code newValue} if the current value, + * referred to as the witness value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchange}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final boolean compareAndExchange(boolean expectedValue, boolean newValue) { + return (int)VALUE.compareAndExchange(this, + (expectedValue ? 1 : 0), + (newValue ? 1 : 0)) != 0; + } + + /** + * Atomically sets the value to {@code newValue} if the current value, + * referred to as the witness value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeAcquire}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final boolean compareAndExchangeAcquire(boolean expectedValue, boolean newValue) { + return (int)VALUE.compareAndExchangeAcquire(this, + (expectedValue ? 1 : 0), + (newValue ? 1 : 0)) != 0; + } + + /** + * Atomically sets the value to {@code newValue} if the current value, + * referred to as the witness value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeRelease}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final boolean compareAndExchangeRelease(boolean expectedValue, boolean newValue) { + return (int)VALUE.compareAndExchangeRelease(this, + (expectedValue ? 1 : 0), + (newValue ? 1 : 0)) != 0; + } + + /** + * Possibly atomically sets the value to {@code newValue} if the current + * value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetVolatile}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetVolatile(boolean expectedValue, boolean newValue) { + return VALUE.weakCompareAndSetVolatile(this, + (expectedValue ? 1 : 0), + (newValue ? 1 : 0)); + } + + /** + * Possibly atomically sets the value to {@code newValue} if the current + * value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetAcquire}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetAcquire(boolean expectedValue, boolean newValue) { + return VALUE.weakCompareAndSetAcquire(this, + (expectedValue ? 1 : 0), + (newValue ? 1 : 0)); + } + + /** + * Possibly atomically sets the value to {@code newValue} if the current + * value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetRelease}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetRelease(boolean expectedValue, boolean newValue) { + return VALUE.weakCompareAndSetRelease(this, + (expectedValue ? 1 : 0), + (newValue ? 1 : 0)); + } + } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicInteger.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicInteger.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicInteger.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,32 +35,30 @@ package java.util.concurrent.atomic; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.function.IntBinaryOperator; import java.util.function.IntUnaryOperator; /** * An {@code int} value that may be updated atomically. See the - * {@link java.util.concurrent.atomic} package specification for - * description of the properties of atomic variables. An - * {@code AtomicInteger} is used in applications such as atomically - * incremented counters, and cannot be used as a replacement for an - * {@link java.lang.Integer}. However, this class does extend - * {@code Number} to allow uniform access by tools and utilities that - * deal with numerically-based classes. + * {@link VarHandle} specification for descriptions of the properties + * of atomic accesses. An {@code AtomicInteger} is used in + * applications such as atomically incremented counters, and cannot be + * used as a replacement for an {@link java.lang.Integer}. However, + * this class does extend {@code Number} to allow uniform access by + * tools and utilities that deal with numerically-based classes. * * @since 1.5 * @author Doug Lea */ public class AtomicInteger extends Number implements java.io.Serializable { private static final long serialVersionUID = 6214790243416807050L; - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long VALUE; - + private static final VarHandle VALUE; static { try { - VALUE = U.objectFieldOffset - (AtomicInteger.class.getDeclaredField("value")); + MethodHandles.Lookup l = MethodHandles.lookup(); + VALUE = l.findVarHandle(AtomicInteger.class, "value", int.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -84,7 +82,8 @@ } /** - * Gets the current value. + * Returns the current value, + * with memory effects as specified by {@link VarHandle#getVolatile}. * * @return the current value */ @@ -93,7 +92,8 @@ } /** - * Sets to the given value. + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setVolatile}. * * @param newValue the new value */ @@ -102,108 +102,122 @@ } /** - * Eventually sets to the given value. + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. * * @param newValue the new value * @since 1.6 */ public final void lazySet(int newValue) { - U.putIntRelease(this, VALUE, newValue); + VALUE.setRelease(this, newValue); } /** - * Atomically sets to the given value and returns the old value. + * Atomically sets the value to {@code newValue} and returns the old value, + * with memory effects as specified by {@link VarHandle#getAndSet}. * * @param newValue the new value * @return the previous value */ public final int getAndSet(int newValue) { - return U.getAndSetInt(this, VALUE, newValue); + return (int)VALUE.getAndSet(this, newValue); } /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. + * Atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#compareAndSet}. * - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful. False return indicates that * the actual value was not equal to the expected value. */ - public final boolean compareAndSet(int expect, int update) { - return U.compareAndSwapInt(this, VALUE, expect, update); + public final boolean compareAndSet(int expectedValue, int newValue) { + return VALUE.compareAndSet(this, expectedValue, newValue); } /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. + * Possibly atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#weakCompareAndSet}. * - *

May fail - * spuriously and does not provide ordering guarantees, so is - * only rarely an appropriate alternative to {@code compareAndSet}. - * - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful */ - public final boolean weakCompareAndSet(int expect, int update) { - return U.compareAndSwapInt(this, VALUE, expect, update); + public final boolean weakCompareAndSet(int expectedValue, int newValue) { + return VALUE.weakCompareAndSet(this, expectedValue, newValue); } /** - * Atomically increments by one the current value. + * Atomically increments the current value, + * with memory effects as specified by {@link VarHandle#getAndAdd}. + * + *

Equivalent to {@code getAndAdd(1)}. * * @return the previous value */ public final int getAndIncrement() { - return U.getAndAddInt(this, VALUE, 1); + return (int)VALUE.getAndAdd(this, 1); } /** - * Atomically decrements by one the current value. + * Atomically decrements the current value, + * with memory effects as specified by {@link VarHandle#getAndAdd}. + * + *

Equivalent to {@code getAndAdd(-1)}. * * @return the previous value */ public final int getAndDecrement() { - return U.getAndAddInt(this, VALUE, -1); + return (int)VALUE.getAndAdd(this, -1); } /** - * Atomically adds the given value to the current value. + * Atomically adds the given value to the current value, + * with memory effects as specified by {@link VarHandle#getAndAdd}. * * @param delta the value to add * @return the previous value */ public final int getAndAdd(int delta) { - return U.getAndAddInt(this, VALUE, delta); + return (int)VALUE.getAndAdd(this, delta); } /** - * Atomically increments by one the current value. + * Atomically increments the current value, + * with memory effects as specified by {@link VarHandle#addAndGet}. + * + *

Equivalent to {@code addAndGet(1)}. * * @return the updated value */ public final int incrementAndGet() { - return U.getAndAddInt(this, VALUE, 1) + 1; + return (int)VALUE.addAndGet(this, 1); } /** - * Atomically decrements by one the current value. + * Atomically decrements the current value, + * with memory effects as specified by {@link VarHandle#addAndGet}. + * + *

Equivalent to {@code addAndGet(-1)}. * * @return the updated value */ public final int decrementAndGet() { - return U.getAndAddInt(this, VALUE, -1) - 1; + return (int)VALUE.addAndGet(this, -1); } /** - * Atomically adds the given value to the current value. + * Atomically adds the given value to the current value, + * with memory effects as specified by {@link VarHandle#addAndGet}. * * @param delta the value to add * @return the updated value */ public final int addAndGet(int delta) { - return U.getAndAddInt(this, VALUE, delta) + delta; + return (int)VALUE.addAndGet(this, delta); } /** @@ -217,12 +231,14 @@ * @since 1.8 */ public final int getAndUpdate(IntUnaryOperator updateFunction) { - int prev, next; - do { - prev = get(); - next = updateFunction.applyAsInt(prev); - } while (!compareAndSet(prev, next)); - return prev; + int prev = get(), next = 0; + for (boolean haveNext = false;;) { + if (!haveNext) + next = updateFunction.applyAsInt(prev); + if (weakCompareAndSetVolatile(prev, next)) + return prev; + haveNext = (prev == (prev = get())); + } } /** @@ -236,12 +252,14 @@ * @since 1.8 */ public final int updateAndGet(IntUnaryOperator updateFunction) { - int prev, next; - do { - prev = get(); - next = updateFunction.applyAsInt(prev); - } while (!compareAndSet(prev, next)); - return next; + int prev = get(), next = 0; + for (boolean haveNext = false;;) { + if (!haveNext) + next = updateFunction.applyAsInt(prev); + if (weakCompareAndSetVolatile(prev, next)) + return next; + haveNext = (prev == (prev = get())); + } } /** @@ -260,12 +278,14 @@ */ public final int getAndAccumulate(int x, IntBinaryOperator accumulatorFunction) { - int prev, next; - do { - prev = get(); - next = accumulatorFunction.applyAsInt(prev, x); - } while (!compareAndSet(prev, next)); - return prev; + int prev = get(), next = 0; + for (boolean haveNext = false;;) { + if (!haveNext) + next = accumulatorFunction.applyAsInt(prev, x); + if (weakCompareAndSetVolatile(prev, next)) + return prev; + haveNext = (prev == (prev = get())); + } } /** @@ -284,12 +304,14 @@ */ public final int accumulateAndGet(int x, IntBinaryOperator accumulatorFunction) { - int prev, next; - do { - prev = get(); - next = accumulatorFunction.applyAsInt(prev, x); - } while (!compareAndSet(prev, next)); - return next; + int prev = get(), next = 0; + for (boolean haveNext = false;;) { + if (!haveNext) + next = accumulatorFunction.applyAsInt(prev, x); + if (weakCompareAndSetVolatile(prev, next)) + return next; + haveNext = (prev == (prev = get())); + } } /** @@ -301,7 +323,10 @@ } /** - * Returns the value of this {@code AtomicInteger} as an {@code int}. + * Returns the current value of this {@code AtomicInteger} as an + * {@code int}, + * with memory effects as specified by {@link VarHandle#getVolatile}. + * * Equivalent to {@link #get()}. */ public int intValue() { @@ -309,8 +334,9 @@ } /** - * Returns the value of this {@code AtomicInteger} as a {@code long} - * after a widening primitive conversion. + * Returns the current value of this {@code AtomicInteger} as a + * {@code long} after a widening primitive conversion, + * with memory effects as specified by {@link VarHandle#getVolatile}. * @jls 5.1.2 Widening Primitive Conversions */ public long longValue() { @@ -318,8 +344,9 @@ } /** - * Returns the value of this {@code AtomicInteger} as a {@code float} - * after a widening primitive conversion. + * Returns the current value of this {@code AtomicInteger} as a + * {@code float} after a widening primitive conversion, + * with memory effects as specified by {@link VarHandle#getVolatile}. * @jls 5.1.2 Widening Primitive Conversions */ public float floatValue() { @@ -327,12 +354,175 @@ } /** - * Returns the value of this {@code AtomicInteger} as a {@code double} - * after a widening primitive conversion. + * Returns the current value of this {@code AtomicInteger} as a + * {@code double} after a widening primitive conversion, + * with memory effects as specified by {@link VarHandle#getVolatile}. * @jls 5.1.2 Widening Primitive Conversions */ public double doubleValue() { return (double)get(); } + // jdk9 + + /** + * Returns the current value, with memory semantics of reading as + * if the variable was declared non-{@code volatile}. + * + * @return the value + * @since 9 + */ + public final int getPlain() { + return (int)VALUE.get(this); + } + + /** + * Sets the value to {@code newValue}, with memory semantics + * of setting as if the variable was declared non-{@code volatile} + * and non-{@code final}. + * + * @param newValue the new value + * @since 9 + */ + public final void setPlain(int newValue) { + VALUE.set(this, newValue); + } + + /** + * Returns the current value, + * with memory effects as specified by {@link VarHandle#getOpaque}. + * + * @return the value + * @since 9 + */ + public final int getOpaque() { + return (int)VALUE.getOpaque(this); + } + + /** + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setOpaque}. + * + * @param newValue the new value + * @since 9 + */ + public final void setOpaque(int newValue) { + VALUE.setOpaque(this, newValue); + } + + /** + * Returns the current value, + * with memory effects as specified by {@link VarHandle#getAcquire}. + * + * @return the value + * @since 9 + */ + public final int getAcquire() { + return (int)VALUE.getAcquire(this); + } + + /** + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. + * + * @param newValue the new value + * @since 9 + */ + public final void setRelease(int newValue) { + VALUE.setRelease(this, newValue); + } + + /** + * Atomically sets the value to {@code newValue} if the current value, + * referred to as the witness value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchange}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final int compareAndExchange(int expectedValue, int newValue) { + return (int)VALUE.compareAndExchange(this, expectedValue, newValue); + } + + /** + * Atomically sets the value to {@code newValue} if the current value, + * referred to as the witness value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeAcquire}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final int compareAndExchangeAcquire(int expectedValue, int newValue) { + return (int)VALUE.compareAndExchangeAcquire(this, expectedValue, newValue); + } + + /** + * Atomically sets the value to {@code newValue} if the current value, + * referred to as the witness value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeRelease}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final int compareAndExchangeRelease(int expectedValue, int newValue) { + return (int)VALUE.compareAndExchangeRelease(this, expectedValue, newValue); + } + + /** + * Possibly atomically sets the value to {@code newValue} if + * the current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetVolatile}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetVolatile(int expectedValue, int newValue) { + return VALUE.weakCompareAndSetVolatile(this, expectedValue, newValue); + } + + /** + * Possibly atomically sets the value to {@code newValue} if + * the current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetAcquire}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetAcquire(int expectedValue, int newValue) { + return VALUE.weakCompareAndSetAcquire(this, expectedValue, newValue); + } + + /** + * Possibly atomically sets the value to {@code newValue} if + * the current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetRelease}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetRelease(int expectedValue, int newValue) { + return VALUE.weakCompareAndSetRelease(this, expectedValue, newValue); + } + } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,44 +35,24 @@ package java.util.concurrent.atomic; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.function.IntBinaryOperator; import java.util.function.IntUnaryOperator; /** * An {@code int} array in which elements may be updated atomically. - * See the {@link java.util.concurrent.atomic} package - * specification for description of the properties of atomic - * variables. + * See the {@link VarHandle} specification for descriptions of the + * properties of atomic accesses. * @since 1.5 * @author Doug Lea */ public class AtomicIntegerArray implements java.io.Serializable { private static final long serialVersionUID = 2862133569453604235L; - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final int ABASE; - private static final int ASHIFT; + private static final VarHandle AA + = MethodHandles.arrayElementVarHandle(int[].class); private final int[] array; - static { - ABASE = U.arrayBaseOffset(int[].class); - int scale = U.arrayIndexScale(int[].class); - if ((scale & (scale - 1)) != 0) - throw new Error("array index scale not a power of two"); - ASHIFT = 31 - Integer.numberOfLeadingZeros(scale); - } - - private long checkedByteOffset(int i) { - if (i < 0 || i >= array.length) - throw new IndexOutOfBoundsException("index " + i); - - return byteOffset(i); - } - - private static long byteOffset(int i) { - return ((long) i << ASHIFT) + ABASE; - } - /** * Creates a new AtomicIntegerArray of the given length, with all * elements initially zero. @@ -105,147 +85,155 @@ } /** - * Gets the current value at position {@code i}. + * Returns the current value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getVolatile}. * * @param i the index * @return the current value */ public final int get(int i) { - return getRaw(checkedByteOffset(i)); - } - - private int getRaw(long offset) { - return U.getIntVolatile(array, offset); + return (int)AA.getVolatile(array, i); } /** - * Sets the element at position {@code i} to the given value. + * Sets the element at index {@code i} to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setVolatile}. * * @param i the index * @param newValue the new value */ public final void set(int i, int newValue) { - U.putIntVolatile(array, checkedByteOffset(i), newValue); + AA.setVolatile(array, i, newValue); } /** - * Eventually sets the element at position {@code i} to the given value. + * Sets the element at index {@code i} to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. * * @param i the index * @param newValue the new value * @since 1.6 */ public final void lazySet(int i, int newValue) { - U.putIntRelease(array, checkedByteOffset(i), newValue); + AA.setRelease(array, i, newValue); } /** - * Atomically sets the element at position {@code i} to the given - * value and returns the old value. + * Atomically sets the element at index {@code i} to {@code + * newValue} and returns the old value, + * with memory effects as specified by {@link VarHandle#getAndSet}. * * @param i the index * @param newValue the new value * @return the previous value */ public final int getAndSet(int i, int newValue) { - return U.getAndSetInt(array, checkedByteOffset(i), newValue); + return (int)AA.getAndSet(array, i, newValue); } /** - * Atomically sets the element at position {@code i} to the given - * updated value if the current value {@code ==} the expected value. + * Atomically sets the element at index {@code i} to {@code + * newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#compareAndSet}. * * @param i the index - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful. False return indicates that * the actual value was not equal to the expected value. */ - public final boolean compareAndSet(int i, int expect, int update) { - return compareAndSetRaw(checkedByteOffset(i), expect, update); - } - - private boolean compareAndSetRaw(long offset, int expect, int update) { - return U.compareAndSwapInt(array, offset, expect, update); + public final boolean compareAndSet(int i, int expectedValue, int newValue) { + return AA.compareAndSet(array, i, expectedValue, newValue); } /** - * Atomically sets the element at position {@code i} to the given - * updated value if the current value {@code ==} the expected value. - * - *

May fail - * spuriously and does not provide ordering guarantees, so is - * only rarely an appropriate alternative to {@code compareAndSet}. + * Possibly atomically sets the element at index {@code i} to + * {@code newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#weakCompareAndSet}. * * @param i the index - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful */ - public final boolean weakCompareAndSet(int i, int expect, int update) { - return compareAndSet(i, expect, update); + public final boolean weakCompareAndSet(int i, int expectedValue, int newValue) { + return AA.weakCompareAndSet(array, i, expectedValue, newValue); } /** - * Atomically increments by one the element at index {@code i}. + * Atomically increments the value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getAndAdd}. + * + *

Equivalent to {@code getAndAdd(i, 1)}. * * @param i the index * @return the previous value */ public final int getAndIncrement(int i) { - return getAndAdd(i, 1); + return (int)AA.getAndAdd(array, i, 1); } /** - * Atomically decrements by one the element at index {@code i}. + * Atomically decrements the value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getAndAdd}. + * + *

Equivalent to {@code getAndAdd(i, -1)}. * * @param i the index * @return the previous value */ public final int getAndDecrement(int i) { - return getAndAdd(i, -1); + return (int)AA.getAndAdd(array, i, -1); } /** - * Atomically adds the given value to the element at index {@code i}. + * Atomically adds the given value to the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getAndAdd}. * * @param i the index * @param delta the value to add * @return the previous value */ public final int getAndAdd(int i, int delta) { - return U.getAndAddInt(array, checkedByteOffset(i), delta); + return (int)AA.getAndAdd(array, i, delta); } /** - * Atomically increments by one the element at index {@code i}. + * Atomically increments the value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#addAndGet}. + * + *

Equivalent to {@code addAndGet(i, 1)}. * * @param i the index * @return the updated value */ public final int incrementAndGet(int i) { - return getAndAdd(i, 1) + 1; + return (int)AA.addAndGet(array, i, 1); } /** - * Atomically decrements by one the element at index {@code i}. + * Atomically decrements the value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#addAndGet}. + * + *

Equivalent to {@code addAndGet(i, -1)}. * * @param i the index * @return the updated value */ public final int decrementAndGet(int i) { - return getAndAdd(i, -1) - 1; + return (int)AA.addAndGet(array, i, -1); } /** - * Atomically adds the given value to the element at index {@code i}. + * Atomically adds the given value to the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#addAndGet}. * * @param i the index * @param delta the value to add * @return the updated value */ public final int addAndGet(int i, int delta) { - return getAndAdd(i, delta) + delta; + return (int)AA.addAndGet(array, i, delta); } /** @@ -260,13 +248,14 @@ * @since 1.8 */ public final int getAndUpdate(int i, IntUnaryOperator updateFunction) { - long offset = checkedByteOffset(i); - int prev, next; - do { - prev = getRaw(offset); - next = updateFunction.applyAsInt(prev); - } while (!compareAndSetRaw(offset, prev, next)); - return prev; + int prev = get(i), next = 0; + for (boolean haveNext = false;;) { + if (!haveNext) + next = updateFunction.applyAsInt(prev); + if (weakCompareAndSetVolatile(i, prev, next)) + return prev; + haveNext = (prev == (prev = get(i))); + } } /** @@ -281,23 +270,25 @@ * @since 1.8 */ public final int updateAndGet(int i, IntUnaryOperator updateFunction) { - long offset = checkedByteOffset(i); - int prev, next; - do { - prev = getRaw(offset); - next = updateFunction.applyAsInt(prev); - } while (!compareAndSetRaw(offset, prev, next)); - return next; + int prev = get(i), next = 0; + for (boolean haveNext = false;;) { + if (!haveNext) + next = updateFunction.applyAsInt(prev); + if (weakCompareAndSetVolatile(i, prev, next)) + return next; + haveNext = (prev == (prev = get(i))); + } } /** * Atomically updates the element at index {@code i} with the - * results of applying the given function to the current and - * given values, returning the previous value. The function should - * be side-effect-free, since it may be re-applied when attempted + * results of applying the given function to the current and given + * values, returning the previous value. The function should be + * side-effect-free, since it may be re-applied when attempted * updates fail due to contention among threads. The function is - * applied with the current value at index {@code i} as its first - * argument, and the given update as the second argument. + * applied with the current value of the element at index {@code i} + * as its first argument, and the given update as the second + * argument. * * @param i the index * @param x the update value @@ -307,23 +298,25 @@ */ public final int getAndAccumulate(int i, int x, IntBinaryOperator accumulatorFunction) { - long offset = checkedByteOffset(i); - int prev, next; - do { - prev = getRaw(offset); - next = accumulatorFunction.applyAsInt(prev, x); - } while (!compareAndSetRaw(offset, prev, next)); - return prev; + int prev = get(i), next = 0; + for (boolean haveNext = false;;) { + if (!haveNext) + next = accumulatorFunction.applyAsInt(prev, x); + if (weakCompareAndSetVolatile(i, prev, next)) + return prev; + haveNext = (prev == (prev = get(i))); + } } /** * Atomically updates the element at index {@code i} with the - * results of applying the given function to the current and - * given values, returning the updated value. The function should - * be side-effect-free, since it may be re-applied when attempted + * results of applying the given function to the current and given + * values, returning the updated value. The function should be + * side-effect-free, since it may be re-applied when attempted * updates fail due to contention among threads. The function is - * applied with the current value at index {@code i} as its first - * argument, and the given update as the second argument. + * applied with the current value of the element at index {@code i} + * as its first argument, and the given update as the second + * argument. * * @param i the index * @param x the update value @@ -333,13 +326,14 @@ */ public final int accumulateAndGet(int i, int x, IntBinaryOperator accumulatorFunction) { - long offset = checkedByteOffset(i); - int prev, next; - do { - prev = getRaw(offset); - next = accumulatorFunction.applyAsInt(prev, x); - } while (!compareAndSetRaw(offset, prev, next)); - return next; + int prev = get(i), next = 0; + for (boolean haveNext = false;;) { + if (!haveNext) + next = accumulatorFunction.applyAsInt(prev, x); + if (weakCompareAndSetVolatile(i, prev, next)) + return next; + haveNext = (prev == (prev = get(i))); + } } /** @@ -354,11 +348,190 @@ StringBuilder b = new StringBuilder(); b.append('['); for (int i = 0; ; i++) { - b.append(getRaw(byteOffset(i))); + b.append(get(i)); if (i == iMax) return b.append(']').toString(); b.append(',').append(' '); } } + // jdk9 + + /** + * Returns the current value of the element at index {@code i}, + * with memory semantics of reading as if the variable was declared + * non-{@code volatile}. + * + * @param i the index + * @return the value + * @since 9 + */ + public final int getPlain(int i) { + return (int)AA.get(array, i); + } + + /** + * Sets the element at index {@code i} to {@code newValue}, + * with memory semantics of setting as if the variable was + * declared non-{@code volatile} and non-{@code final}. + * + * @param i the index + * @param newValue the new value + * @since 9 + */ + public final void setPlain(int i, int newValue) { + AA.set(array, i, newValue); + } + + /** + * Returns the current value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getOpaque}. + * + * @param i the index + * @return the value + * @since 9 + */ + public final int getOpaque(int i) { + return (int)AA.getOpaque(array, i); + } + + /** + * Sets the element at index {@code i} to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setOpaque}. + * + * @param i the index + * @param newValue the new value + * @since 9 + */ + public final void setOpaque(int i, int newValue) { + AA.setOpaque(array, i, newValue); + } + + /** + * Returns the current value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getAcquire}. + * + * @param i the index + * @return the value + * @since 9 + */ + public final int getAcquire(int i) { + return (int)AA.getAcquire(array, i); + } + + /** + * Sets the element at index {@code i} to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. + * + * @param i the index + * @param newValue the new value + * @since 9 + */ + public final void setRelease(int i, int newValue) { + AA.setRelease(array, i, newValue); + } + + /** + * Atomically sets the element at index {@code i} to {@code newValue} + * if the element's current value, referred to as the witness + * value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchange}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final int compareAndExchange(int i, int expectedValue, int newValue) { + return (int)AA.compareAndExchange(array, i, expectedValue, newValue); + } + + /** + * Atomically sets the element at index {@code i} to {@code newValue} + * if the element's current value, referred to as the witness + * value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeAcquire}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final int compareAndExchangeAcquire(int i, int expectedValue, int newValue) { + return (int)AA.compareAndExchangeAcquire(array, i, expectedValue, newValue); + } + + /** + * Atomically sets the element at index {@code i} to {@code newValue} + * if the element's current value, referred to as the witness + * value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeRelease}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final int compareAndExchangeRelease(int i, int expectedValue, int newValue) { + return (int)AA.compareAndExchangeRelease(array, i, expectedValue, newValue); + } + + /** + * Possibly atomically sets the element at index {@code i} to + * {@code newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetVolatile}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetVolatile(int i, int expectedValue, int newValue) { + return AA.weakCompareAndSetVolatile(array, i, expectedValue, newValue); + } + + /** + * Possibly atomically sets the element at index {@code i} to + * {@code newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetAcquire}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetAcquire(int i, int expectedValue, int newValue) { + return AA.weakCompareAndSetAcquire(array, i, expectedValue, newValue); + } + + /** + * Possibly atomically sets the element at index {@code i} to + * {@code newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetRelease}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetRelease(int i, int expectedValue, int newValue) { + return AA.weakCompareAndSetRelease(array, i, expectedValue, newValue); + } + + } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java Mon Jul 18 09:38:08 2016 -0700 @@ -42,6 +42,7 @@ import java.security.PrivilegedExceptionAction; import java.util.function.IntBinaryOperator; import java.util.function.IntUnaryOperator; +import jdk.internal.misc.Unsafe; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; @@ -150,8 +151,8 @@ public abstract void lazySet(T obj, int newValue); /** - * Gets the current value held in the field of the given object managed - * by this updater. + * Returns the current value held in the field of the given object + * managed by this updater. * * @param obj An object whose field to get * @return the current value @@ -367,7 +368,7 @@ */ private static final class AtomicIntegerFieldUpdaterImpl extends AtomicIntegerFieldUpdater { - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); + private static final Unsafe U = Unsafe.getUnsafe(); private final long offset; /** * if field is protected, the subclass constructing updater, else diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,31 +35,30 @@ package java.util.concurrent.atomic; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.function.LongBinaryOperator; import java.util.function.LongUnaryOperator; /** * A {@code long} value that may be updated atomically. See the - * {@link java.util.concurrent.atomic} package specification for - * description of the properties of atomic variables. An - * {@code AtomicLong} is used in applications such as atomically - * incremented sequence numbers, and cannot be used as a replacement - * for a {@link java.lang.Long}. However, this class does extend - * {@code Number} to allow uniform access by tools and utilities that - * deal with numerically-based classes. + * {@link VarHandle} specification for descriptions of the properties + * of atomic accesses. An {@code AtomicLong} is used in applications + * such as atomically incremented sequence numbers, and cannot be used + * as a replacement for a {@link java.lang.Long}. However, this class + * does extend {@code Number} to allow uniform access by tools and + * utilities that deal with numerically-based classes. * * @since 1.5 * @author Doug Lea */ public class AtomicLong extends Number implements java.io.Serializable { private static final long serialVersionUID = 1927816293512124184L; - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long VALUE; + private static final VarHandle VALUE; /** * Records whether the underlying JVM supports lockless - * compareAndSwap for longs. While the Unsafe.compareAndSwapLong + * compareAndSwap for longs. While the intrinsic compareAndSwapLong * method works in either case, some constructions should be * handled at Java level to avoid locking user-visible locks. */ @@ -73,8 +72,8 @@ static { try { - VALUE = U.objectFieldOffset - (AtomicLong.class.getDeclaredField("value")); + MethodHandles.Lookup l = MethodHandles.lookup(); + VALUE = l.findVarHandle(AtomicLong.class, "value", long.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -98,7 +97,8 @@ } /** - * Gets the current value. + * Returns the current value, + * with memory effects as specified by {@link VarHandle#getVolatile}. * * @return the current value */ @@ -107,119 +107,132 @@ } /** - * Sets to the given value. + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setVolatile}. * * @param newValue the new value */ public final void set(long newValue) { - // Use putLongVolatile instead of ordinary volatile store when - // using compareAndSwapLong, for sake of some 32bit systems. - U.putLongVolatile(this, VALUE, newValue); + VALUE.setVolatile(this, newValue); } /** - * Eventually sets to the given value. + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. * * @param newValue the new value * @since 1.6 */ public final void lazySet(long newValue) { - U.putLongRelease(this, VALUE, newValue); + VALUE.setRelease(this, newValue); } /** - * Atomically sets to the given value and returns the old value. + * Atomically sets the value to {@code newValue} and returns the old value, + * with memory effects as specified by {@link VarHandle#getAndSet}. * * @param newValue the new value * @return the previous value */ public final long getAndSet(long newValue) { - return U.getAndSetLong(this, VALUE, newValue); + return (long)VALUE.getAndSet(this, newValue); } /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. + * Atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#compareAndSet}. * - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful. False return indicates that * the actual value was not equal to the expected value. */ - public final boolean compareAndSet(long expect, long update) { - return U.compareAndSwapLong(this, VALUE, expect, update); + public final boolean compareAndSet(long expectedValue, long newValue) { + return VALUE.compareAndSet(this, expectedValue, newValue); } /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. + * Possibly atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#weakCompareAndSet}. * - *

May fail - * spuriously and does not provide ordering guarantees, so is - * only rarely an appropriate alternative to {@code compareAndSet}. - * - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful */ - public final boolean weakCompareAndSet(long expect, long update) { - return U.compareAndSwapLong(this, VALUE, expect, update); + public final boolean weakCompareAndSet(long expectedValue, long newValue) { + return VALUE.weakCompareAndSet(this, expectedValue, newValue); } /** - * Atomically increments by one the current value. + * Atomically increments the current value, + * with memory effects as specified by {@link VarHandle#getAndAdd}. + * + *

Equivalent to {@code getAndAdd(1)}. * * @return the previous value */ public final long getAndIncrement() { - return U.getAndAddLong(this, VALUE, 1L); + return (long)VALUE.getAndAdd(this, 1L); } /** - * Atomically decrements by one the current value. + * Atomically decrements the current value, + * with memory effects as specified by {@link VarHandle#getAndAdd}. + * + *

Equivalent to {@code getAndAdd(-1)}. * * @return the previous value */ public final long getAndDecrement() { - return U.getAndAddLong(this, VALUE, -1L); + return (long)VALUE.getAndAdd(this, -1L); } /** - * Atomically adds the given value to the current value. + * Atomically adds the given value to the current value, + * with memory effects as specified by {@link VarHandle#getAndAdd}. * * @param delta the value to add * @return the previous value */ public final long getAndAdd(long delta) { - return U.getAndAddLong(this, VALUE, delta); + return (long)VALUE.getAndAdd(this, delta); } /** - * Atomically increments by one the current value. + * Atomically increments the current value, + * with memory effects as specified by {@link VarHandle#addAndGet}. + * + *

Equivalent to {@code addAndGet(1)}. * * @return the updated value */ public final long incrementAndGet() { - return U.getAndAddLong(this, VALUE, 1L) + 1L; + return (long)VALUE.addAndGet(this, 1L); } /** - * Atomically decrements by one the current value. + * Atomically decrements the current value, + * with memory effects as specified by {@link VarHandle#addAndGet}. + * + *

Equivalent to {@code addAndGet(-1)}. * * @return the updated value */ public final long decrementAndGet() { - return U.getAndAddLong(this, VALUE, -1L) - 1L; + return (long)VALUE.addAndGet(this, -1L); } /** - * Atomically adds the given value to the current value. + * Atomically adds the given value to the current value, + * with memory effects as specified by {@link VarHandle#addAndGet}. * * @param delta the value to add * @return the updated value */ public final long addAndGet(long delta) { - return U.getAndAddLong(this, VALUE, delta) + delta; + return (long)VALUE.addAndGet(this, delta); } /** @@ -233,12 +246,14 @@ * @since 1.8 */ public final long getAndUpdate(LongUnaryOperator updateFunction) { - long prev, next; - do { - prev = get(); - next = updateFunction.applyAsLong(prev); - } while (!compareAndSet(prev, next)); - return prev; + long prev = get(), next = 0L; + for (boolean haveNext = false;;) { + if (!haveNext) + next = updateFunction.applyAsLong(prev); + if (weakCompareAndSetVolatile(prev, next)) + return prev; + haveNext = (prev == (prev = get())); + } } /** @@ -252,12 +267,14 @@ * @since 1.8 */ public final long updateAndGet(LongUnaryOperator updateFunction) { - long prev, next; - do { - prev = get(); - next = updateFunction.applyAsLong(prev); - } while (!compareAndSet(prev, next)); - return next; + long prev = get(), next = 0L; + for (boolean haveNext = false;;) { + if (!haveNext) + next = updateFunction.applyAsLong(prev); + if (weakCompareAndSetVolatile(prev, next)) + return next; + haveNext = (prev == (prev = get())); + } } /** @@ -276,12 +293,14 @@ */ public final long getAndAccumulate(long x, LongBinaryOperator accumulatorFunction) { - long prev, next; - do { - prev = get(); - next = accumulatorFunction.applyAsLong(prev, x); - } while (!compareAndSet(prev, next)); - return prev; + long prev = get(), next = 0L; + for (boolean haveNext = false;;) { + if (!haveNext) + next = accumulatorFunction.applyAsLong(prev, x); + if (weakCompareAndSetVolatile(prev, next)) + return prev; + haveNext = (prev == (prev = get())); + } } /** @@ -300,12 +319,14 @@ */ public final long accumulateAndGet(long x, LongBinaryOperator accumulatorFunction) { - long prev, next; - do { - prev = get(); - next = accumulatorFunction.applyAsLong(prev, x); - } while (!compareAndSet(prev, next)); - return next; + long prev = get(), next = 0L; + for (boolean haveNext = false;;) { + if (!haveNext) + next = accumulatorFunction.applyAsLong(prev, x); + if (weakCompareAndSetVolatile(prev, next)) + return next; + haveNext = (prev == (prev = get())); + } } /** @@ -317,8 +338,9 @@ } /** - * Returns the value of this {@code AtomicLong} as an {@code int} - * after a narrowing primitive conversion. + * Returns the current value of this {@code AtomicLong} as an {@code int} + * after a narrowing primitive conversion, + * with memory effects as specified by {@link VarHandle#getVolatile}. * @jls 5.1.3 Narrowing Primitive Conversions */ public int intValue() { @@ -326,7 +348,8 @@ } /** - * Returns the value of this {@code AtomicLong} as a {@code long}. + * Returns the current value of this {@code AtomicLong} as a {@code long}, + * with memory effects as specified by {@link VarHandle#getVolatile}. * Equivalent to {@link #get()}. */ public long longValue() { @@ -334,8 +357,9 @@ } /** - * Returns the value of this {@code AtomicLong} as a {@code float} - * after a widening primitive conversion. + * Returns the current value of this {@code AtomicLong} as a {@code float} + * after a widening primitive conversion, + * with memory effects as specified by {@link VarHandle#getVolatile}. * @jls 5.1.2 Widening Primitive Conversions */ public float floatValue() { @@ -343,12 +367,175 @@ } /** - * Returns the value of this {@code AtomicLong} as a {@code double} - * after a widening primitive conversion. + * Returns the current value of this {@code AtomicLong} as a {@code double} + * after a widening primitive conversion, + * with memory effects as specified by {@link VarHandle#getVolatile}. * @jls 5.1.2 Widening Primitive Conversions */ public double doubleValue() { return (double)get(); } + // jdk9 + + /** + * Returns the current value, with memory semantics of reading as if the + * variable was declared non-{@code volatile}. + * + * @return the value + * @since 9 + */ + public final long getPlain() { + return (long)VALUE.get(this); + } + + /** + * Sets the value to {@code newValue}, with memory semantics + * of setting as if the variable was declared non-{@code volatile} + * and non-{@code final}. + * + * @param newValue the new value + * @since 9 + */ + public final void setPlain(long newValue) { + VALUE.set(this, newValue); + } + + /** + * Returns the current value, + * with memory effects as specified by {@link VarHandle#getOpaque}. + * + * @return the value + * @since 9 + */ + public final long getOpaque() { + return (long)VALUE.getOpaque(this); + } + + /** + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setOpaque}. + * + * @param newValue the new value + * @since 9 + */ + public final void setOpaque(long newValue) { + VALUE.setOpaque(this, newValue); + } + + /** + * Returns the current value, + * with memory effects as specified by {@link VarHandle#getAcquire}. + * + * @return the value + * @since 9 + */ + public final long getAcquire() { + return (long)VALUE.getAcquire(this); + } + + /** + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. + * + * @param newValue the new value + * @since 9 + */ + public final void setRelease(long newValue) { + VALUE.setRelease(this, newValue); + } + + /** + * Atomically sets the value to {@code newValue} if the current value, + * referred to as the witness value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchange}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final long compareAndExchange(long expectedValue, long newValue) { + return (long)VALUE.compareAndExchange(this, expectedValue, newValue); + } + + /** + * Atomically sets the value to {@code newValue} if the current value, + * referred to as the witness value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeAcquire}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final long compareAndExchangeAcquire(long expectedValue, long newValue) { + return (long)VALUE.compareAndExchangeAcquire(this, expectedValue, newValue); + } + + /** + * Atomically sets the value to {@code newValue} if the current value, + * referred to as the witness value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeRelease}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final long compareAndExchangeRelease(long expectedValue, long newValue) { + return (long)VALUE.compareAndExchangeRelease(this, expectedValue, newValue); + } + + /** + * Possibly atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetVolatile}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetVolatile(long expectedValue, long newValue) { + return VALUE.weakCompareAndSetVolatile(this, expectedValue, newValue); + } + + /** + * Possibly atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetAcquire}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetAcquire(long expectedValue, long newValue) { + return VALUE.weakCompareAndSetAcquire(this, expectedValue, newValue); + } + + /** + * Possibly atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetRelease}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetRelease(long expectedValue, long newValue) { + return VALUE.weakCompareAndSetRelease(this, expectedValue, newValue); + } + } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongArray.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongArray.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongArray.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,43 +35,24 @@ package java.util.concurrent.atomic; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.function.LongBinaryOperator; import java.util.function.LongUnaryOperator; /** * A {@code long} array in which elements may be updated atomically. - * See the {@link java.util.concurrent.atomic} package specification - * for description of the properties of atomic variables. + * See the {@link VarHandle} specification for descriptions of the + * properties of atomic accesses. * @since 1.5 * @author Doug Lea */ public class AtomicLongArray implements java.io.Serializable { private static final long serialVersionUID = -2308431214976778248L; - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final int ABASE; - private static final int ASHIFT; + private static final VarHandle AA + = MethodHandles.arrayElementVarHandle(long[].class); private final long[] array; - static { - ABASE = U.arrayBaseOffset(long[].class); - int scale = U.arrayIndexScale(long[].class); - if ((scale & (scale - 1)) != 0) - throw new Error("array index scale not a power of two"); - ASHIFT = 31 - Integer.numberOfLeadingZeros(scale); - } - - private long checkedByteOffset(int i) { - if (i < 0 || i >= array.length) - throw new IndexOutOfBoundsException("index " + i); - - return byteOffset(i); - } - - private static long byteOffset(int i) { - return ((long) i << ASHIFT) + ABASE; - } - /** * Creates a new AtomicLongArray of the given length, with all * elements initially zero. @@ -104,147 +85,155 @@ } /** - * Gets the current value at position {@code i}. + * Returns the current value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getVolatile}. * * @param i the index * @return the current value */ public final long get(int i) { - return getRaw(checkedByteOffset(i)); - } - - private long getRaw(long offset) { - return U.getLongVolatile(array, offset); + return (long)AA.getVolatile(array, i); } /** - * Sets the element at position {@code i} to the given value. + * Sets the element at index {@code i} to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setVolatile}. * * @param i the index * @param newValue the new value */ public final void set(int i, long newValue) { - U.putLongVolatile(array, checkedByteOffset(i), newValue); + AA.setVolatile(array, i, newValue); } /** - * Eventually sets the element at position {@code i} to the given value. + * Sets the element at index {@code i} to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. * * @param i the index * @param newValue the new value * @since 1.6 */ public final void lazySet(int i, long newValue) { - U.putLongRelease(array, checkedByteOffset(i), newValue); + AA.setRelease(array, i, newValue); } /** - * Atomically sets the element at position {@code i} to the given value - * and returns the old value. + * Atomically sets the element at index {@code i} to {@code + * newValue} and returns the old value, + * with memory effects as specified by {@link VarHandle#getAndSet}. * * @param i the index * @param newValue the new value * @return the previous value */ public final long getAndSet(int i, long newValue) { - return U.getAndSetLong(array, checkedByteOffset(i), newValue); + return (long)AA.getAndSet(array, i, newValue); } /** - * Atomically sets the element at position {@code i} to the given - * updated value if the current value {@code ==} the expected value. + * Atomically sets the element at index {@code i} to {@code newValue} + * if the element's current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#compareAndSet}. * * @param i the index - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful. False return indicates that * the actual value was not equal to the expected value. */ - public final boolean compareAndSet(int i, long expect, long update) { - return compareAndSetRaw(checkedByteOffset(i), expect, update); - } - - private boolean compareAndSetRaw(long offset, long expect, long update) { - return U.compareAndSwapLong(array, offset, expect, update); + public final boolean compareAndSet(int i, long expectedValue, long newValue) { + return AA.compareAndSet(array, i, expectedValue, newValue); } /** - * Atomically sets the element at position {@code i} to the given - * updated value if the current value {@code ==} the expected value. - * - *

May fail - * spuriously and does not provide ordering guarantees, so is - * only rarely an appropriate alternative to {@code compareAndSet}. + * Possibly atomically sets the element at index {@code i} to + * {@code newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#weakCompareAndSet}. * * @param i the index - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful */ - public final boolean weakCompareAndSet(int i, long expect, long update) { - return compareAndSet(i, expect, update); + public final boolean weakCompareAndSet(int i, long expectedValue, long newValue) { + return AA.weakCompareAndSet(array, i, expectedValue, newValue); } /** - * Atomically increments by one the element at index {@code i}. + * Atomically increments the value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getAndAdd}. + * + *

Equivalent to {@code getAndAdd(i, 1)}. * * @param i the index * @return the previous value */ public final long getAndIncrement(int i) { - return getAndAdd(i, 1); + return (long)AA.getAndAdd(array, i, 1L); } /** - * Atomically decrements by one the element at index {@code i}. + * Atomically decrements the value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getAndAdd}. + * + *

Equivalent to {@code getAndAdd(i, -1)}. * * @param i the index * @return the previous value */ public final long getAndDecrement(int i) { - return getAndAdd(i, -1); + return (long)AA.getAndAdd(array, i, -1L); } /** - * Atomically adds the given value to the element at index {@code i}. + * Atomically adds the given value to the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getAndAdd}. * * @param i the index * @param delta the value to add * @return the previous value */ public final long getAndAdd(int i, long delta) { - return U.getAndAddLong(array, checkedByteOffset(i), delta); + return (long)AA.getAndAdd(array, i, delta); } /** - * Atomically increments by one the element at index {@code i}. + * Atomically increments the value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#addAndGet}. + * + *

Equivalent to {@code addAndGet(i, 1)}. * * @param i the index * @return the updated value */ public final long incrementAndGet(int i) { - return getAndAdd(i, 1) + 1; + return (long)AA.addAndGet(array, i, 1L); } /** - * Atomically decrements by one the element at index {@code i}. + * Atomically decrements the value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#addAndGet}. + * + *

Equivalent to {@code addAndGet(i, -1)}. * * @param i the index * @return the updated value */ public final long decrementAndGet(int i) { - return getAndAdd(i, -1) - 1; + return (long)AA.addAndGet(array, i, -1L); } /** - * Atomically adds the given value to the element at index {@code i}. + * Atomically adds the given value to the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#addAndGet}. * * @param i the index * @param delta the value to add * @return the updated value */ public long addAndGet(int i, long delta) { - return getAndAdd(i, delta) + delta; + return (long)AA.addAndGet(array, i, delta); } /** @@ -259,13 +248,14 @@ * @since 1.8 */ public final long getAndUpdate(int i, LongUnaryOperator updateFunction) { - long offset = checkedByteOffset(i); - long prev, next; - do { - prev = getRaw(offset); - next = updateFunction.applyAsLong(prev); - } while (!compareAndSetRaw(offset, prev, next)); - return prev; + long prev = get(i), next = 0L; + for (boolean haveNext = false;;) { + if (!haveNext) + next = updateFunction.applyAsLong(prev); + if (weakCompareAndSetVolatile(i, prev, next)) + return prev; + haveNext = (prev == (prev = get(i))); + } } /** @@ -280,23 +270,25 @@ * @since 1.8 */ public final long updateAndGet(int i, LongUnaryOperator updateFunction) { - long offset = checkedByteOffset(i); - long prev, next; - do { - prev = getRaw(offset); - next = updateFunction.applyAsLong(prev); - } while (!compareAndSetRaw(offset, prev, next)); - return next; + long prev = get(i), next = 0L; + for (boolean haveNext = false;;) { + if (!haveNext) + next = updateFunction.applyAsLong(prev); + if (weakCompareAndSetVolatile(i, prev, next)) + return next; + haveNext = (prev == (prev = get(i))); + } } /** * Atomically updates the element at index {@code i} with the - * results of applying the given function to the current and - * given values, returning the previous value. The function should - * be side-effect-free, since it may be re-applied when attempted + * results of applying the given function to the current and given + * values, returning the previous value. The function should be + * side-effect-free, since it may be re-applied when attempted * updates fail due to contention among threads. The function is - * applied with the current value at index {@code i} as its first - * argument, and the given update as the second argument. + * applied with the current value of the element at index {@code i} + * as its first argument, and the given update as the second + * argument. * * @param i the index * @param x the update value @@ -306,23 +298,25 @@ */ public final long getAndAccumulate(int i, long x, LongBinaryOperator accumulatorFunction) { - long offset = checkedByteOffset(i); - long prev, next; - do { - prev = getRaw(offset); - next = accumulatorFunction.applyAsLong(prev, x); - } while (!compareAndSetRaw(offset, prev, next)); - return prev; + long prev = get(i), next = 0L; + for (boolean haveNext = false;;) { + if (!haveNext) + next = accumulatorFunction.applyAsLong(prev, x); + if (weakCompareAndSetVolatile(i, prev, next)) + return prev; + haveNext = (prev == (prev = get(i))); + } } /** * Atomically updates the element at index {@code i} with the - * results of applying the given function to the current and - * given values, returning the updated value. The function should - * be side-effect-free, since it may be re-applied when attempted + * results of applying the given function to the current and given + * values, returning the updated value. The function should be + * side-effect-free, since it may be re-applied when attempted * updates fail due to contention among threads. The function is - * applied with the current value at index {@code i} as its first - * argument, and the given update as the second argument. + * applied with the current value of the element at index {@code i} + * as its first argument, and the given update as the second + * argument. * * @param i the index * @param x the update value @@ -332,13 +326,14 @@ */ public final long accumulateAndGet(int i, long x, LongBinaryOperator accumulatorFunction) { - long offset = checkedByteOffset(i); - long prev, next; - do { - prev = getRaw(offset); - next = accumulatorFunction.applyAsLong(prev, x); - } while (!compareAndSetRaw(offset, prev, next)); - return next; + long prev = get(i), next = 0L; + for (boolean haveNext = false;;) { + if (!haveNext) + next = accumulatorFunction.applyAsLong(prev, x); + if (weakCompareAndSetVolatile(i, prev, next)) + return next; + haveNext = (prev == (prev = get(i))); + } } /** @@ -353,11 +348,189 @@ StringBuilder b = new StringBuilder(); b.append('['); for (int i = 0; ; i++) { - b.append(getRaw(byteOffset(i))); + b.append(get(i)); if (i == iMax) return b.append(']').toString(); b.append(',').append(' '); } } + // jdk9 + + /** + * Returns the current value of the element at index {@code i}, + * with memory semantics of reading as if the variable was declared + * non-{@code volatile}. + * + * @param i the index + * @return the value + * @since 9 + */ + public final long getPlain(int i) { + return (long)AA.get(array, i); + } + + /** + * Sets the element at index {@code i} to {@code newValue}, + * with memory semantics of setting as if the variable was + * declared non-{@code volatile} and non-{@code final}. + * + * @param i the index + * @param newValue the new value + * @since 9 + */ + public final void setPlain(int i, long newValue) { + AA.set(array, i, newValue); + } + + /** + * Returns the current value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getOpaque}. + * + * @param i the index + * @return the value + * @since 9 + */ + public final long getOpaque(int i) { + return (long)AA.getOpaque(array, i); + } + + /** + * Sets the element at index {@code i} to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setOpaque}. + * + * @param i the index + * @param newValue the new value + * @since 9 + */ + public final void setOpaque(int i, long newValue) { + AA.setOpaque(array, i, newValue); + } + + /** + * Returns the current value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getAcquire}. + * + * @param i the index + * @return the value + * @since 9 + */ + public final long getAcquire(int i) { + return (long)AA.getAcquire(array, i); + } + + /** + * Sets the element at index {@code i} to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. + * + * @param i the index + * @param newValue the new value + * @since 9 + */ + public final void setRelease(int i, long newValue) { + AA.setRelease(array, i, newValue); + } + + /** + * Atomically sets the element at index {@code i} to {@code newValue} + * if the element's current value, referred to as the witness + * value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchange}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final long compareAndExchange(int i, long expectedValue, long newValue) { + return (long)AA.compareAndExchange(array, i, expectedValue, newValue); + } + + /** + * Atomically sets the element at index {@code i} to {@code newValue} + * if the element's current value, referred to as the witness + * value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeAcquire}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final long compareAndExchangeAcquire(int i, long expectedValue, long newValue) { + return (long)AA.compareAndExchangeAcquire(array, i, expectedValue, newValue); + } + + /** + * Atomically sets the element at index {@code i} to {@code newValue} + * if the element's current value, referred to as the witness + * value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeRelease}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final long compareAndExchangeRelease(int i, long expectedValue, long newValue) { + return (long)AA.compareAndExchangeRelease(array, i, expectedValue, newValue); + } + + /** + * Possibly atomically sets the element at index {@code i} to + * {@code newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetVolatile}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetVolatile(int i, long expectedValue, long newValue) { + return AA.weakCompareAndSetVolatile(array, i, expectedValue, newValue); + } + + /** + * Possibly atomically sets the element at index {@code i} to + * {@code newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetAcquire}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetAcquire(int i, long expectedValue, long newValue) { + return AA.weakCompareAndSetAcquire(array, i, expectedValue, newValue); + } + + /** + * Possibly atomically sets the element at index {@code i} to + * {@code newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetRelease}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetRelease(int i, long expectedValue, long newValue) { + return AA.weakCompareAndSetRelease(array, i, expectedValue, newValue); + } + } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java Mon Jul 18 09:38:08 2016 -0700 @@ -42,6 +42,7 @@ import java.security.PrivilegedExceptionAction; import java.util.function.LongBinaryOperator; import java.util.function.LongUnaryOperator; +import jdk.internal.misc.Unsafe; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; @@ -153,8 +154,8 @@ public abstract void lazySet(T obj, long newValue); /** - * Gets the current value held in the field of the given object managed - * by this updater. + * Returns the current value held in the field of the given object + * managed by this updater. * * @param obj An object whose field to get * @return the current value @@ -366,7 +367,7 @@ } private static final class CASUpdater extends AtomicLongFieldUpdater { - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); + private static final Unsafe U = Unsafe.getUnsafe(); private final long offset; /** * if field is protected, the subclass constructing updater, else @@ -497,7 +498,7 @@ } private static final class LockedUpdater extends AtomicLongFieldUpdater { - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); + private static final Unsafe U = Unsafe.getUnsafe(); private final long offset; /** * if field is protected, the subclass constructing updater, else diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,6 +35,9 @@ package java.util.concurrent.atomic; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; + /** * An {@code AtomicMarkableReference} maintains an object reference * along with a mark bit, that can be updated atomically. @@ -188,20 +191,19 @@ casPair(current, Pair.of(expectedReference, newMark))); } - // Unsafe mechanics - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long PAIR; + // VarHandle mechanics + private static final VarHandle PAIR; static { try { - PAIR = U.objectFieldOffset - (AtomicMarkableReference.class.getDeclaredField("pair")); + MethodHandles.Lookup l = MethodHandles.lookup(); + PAIR = l.findVarHandle(AtomicMarkableReference.class, "pair", + Pair.class); } catch (ReflectiveOperationException e) { throw new Error(e); } } private boolean casPair(Pair cmp, Pair val) { - return U.compareAndSwapObject(this, PAIR, cmp, val); + return PAIR.compareAndSet(this, cmp, val); } } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReference.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReference.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReference.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,33 +35,32 @@ package java.util.concurrent.atomic; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.function.BinaryOperator; import java.util.function.UnaryOperator; /** - * An object reference that may be updated atomically. See the {@link - * java.util.concurrent.atomic} package specification for description - * of the properties of atomic variables. + * An object reference that may be updated atomically. See the {@link + * VarHandle} specification for descriptions of the properties of + * atomic accesses. * @since 1.5 * @author Doug Lea * @param The type of object referred to by this reference */ public class AtomicReference implements java.io.Serializable { private static final long serialVersionUID = -1848883965231344442L; - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long VALUE; - + private static final VarHandle VALUE; static { try { - VALUE = U.objectFieldOffset - (AtomicReference.class.getDeclaredField("value")); + MethodHandles.Lookup l = MethodHandles.lookup(); + VALUE = l.findVarHandle(AtomicReference.class, "value", Object.class); } catch (ReflectiveOperationException e) { throw new Error(e); } } - private volatile V value; + private volatile Object value; /** * Creates a new AtomicReference with the given initial value. @@ -79,16 +78,19 @@ } /** - * Gets the current value. + * Returns the current value, + * with memory effects as specified by {@link VarHandle#getVolatile}. * * @return the current value */ + @SuppressWarnings("unchecked") public final V get() { - return value; + return (V)value; } /** - * Sets to the given value. + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setVolatile}. * * @param newValue the new value */ @@ -97,52 +99,53 @@ } /** - * Eventually sets to the given value. + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. * * @param newValue the new value * @since 1.6 */ public final void lazySet(V newValue) { - U.putObjectRelease(this, VALUE, newValue); - } - - /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. - * @param expect the expected value - * @param update the new value - * @return {@code true} if successful. False return indicates that - * the actual value was not equal to the expected value. - */ - public final boolean compareAndSet(V expect, V update) { - return U.compareAndSwapObject(this, VALUE, expect, update); + VALUE.setRelease(this, newValue); } /** - * Atomically sets the value to the given updated value - * if the current value {@code ==} the expected value. - * - *

May fail - * spuriously and does not provide ordering guarantees, so is - * only rarely an appropriate alternative to {@code compareAndSet}. + * Atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#compareAndSet}. * - * @param expect the expected value - * @param update the new value - * @return {@code true} if successful + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful. False return indicates that + * the actual value was not equal to the expected value. */ - public final boolean weakCompareAndSet(V expect, V update) { - return U.compareAndSwapObject(this, VALUE, expect, update); + public final boolean compareAndSet(V expectedValue, V newValue) { + return VALUE.compareAndSet(this, expectedValue, newValue); } /** - * Atomically sets to the given value and returns the old value. + * Possibly atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#weakCompareAndSet}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + */ + public final boolean weakCompareAndSet(V expectedValue, V newValue) { + return VALUE.weakCompareAndSet(this, expectedValue, newValue); + } + + /** + * Atomically sets the value to {@code newValue} and returns the old value, + * with memory effects as specified by {@link VarHandle#getAndSet}. * * @param newValue the new value * @return the previous value */ @SuppressWarnings("unchecked") public final V getAndSet(V newValue) { - return (V)U.getAndSetObject(this, VALUE, newValue); + return (V)VALUE.getAndSet(this, newValue); } /** @@ -156,12 +159,14 @@ * @since 1.8 */ public final V getAndUpdate(UnaryOperator updateFunction) { - V prev, next; - do { - prev = get(); - next = updateFunction.apply(prev); - } while (!compareAndSet(prev, next)); - return prev; + V prev = get(), next = null; + for (boolean haveNext = false;;) { + if (!haveNext) + next = updateFunction.apply(prev); + if (weakCompareAndSetVolatile(prev, next)) + return prev; + haveNext = (prev == (prev = get())); + } } /** @@ -175,12 +180,14 @@ * @since 1.8 */ public final V updateAndGet(UnaryOperator updateFunction) { - V prev, next; - do { - prev = get(); - next = updateFunction.apply(prev); - } while (!compareAndSet(prev, next)); - return next; + V prev = get(), next = null; + for (boolean haveNext = false;;) { + if (!haveNext) + next = updateFunction.apply(prev); + if (weakCompareAndSetVolatile(prev, next)) + return next; + haveNext = (prev == (prev = get())); + } } /** @@ -199,12 +206,14 @@ */ public final V getAndAccumulate(V x, BinaryOperator accumulatorFunction) { - V prev, next; - do { - prev = get(); - next = accumulatorFunction.apply(prev, x); - } while (!compareAndSet(prev, next)); - return prev; + V prev = get(), next = null; + for (boolean haveNext = false;;) { + if (!haveNext) + next = accumulatorFunction.apply(prev, x); + if (weakCompareAndSetVolatile(prev, next)) + return prev; + haveNext = (prev == (prev = get())); + } } /** @@ -223,12 +232,14 @@ */ public final V accumulateAndGet(V x, BinaryOperator accumulatorFunction) { - V prev, next; - do { - prev = get(); - next = accumulatorFunction.apply(prev, x); - } while (!compareAndSet(prev, next)); - return next; + V prev = get(), next = null; + for (boolean haveNext = false;;) { + if (!haveNext) + next = accumulatorFunction.apply(prev, x); + if (weakCompareAndSetVolatile(prev, next)) + return next; + haveNext = (prev == (prev = get())); + } } /** @@ -239,4 +250,166 @@ return String.valueOf(get()); } + // jdk9 + + /** + * Returns the current value, with memory semantics of reading as + * if the variable was declared non-{@code volatile}. + * + * @return the value + * @since 9 + */ + public final V getPlain() { + return (V)VALUE.get(this); + } + + /** + * Sets the value to {@code newValue}, with memory semantics + * of setting as if the variable was declared non-{@code volatile} + * and non-{@code final}. + * + * @param newValue the new value + * @since 9 + */ + public final void setPlain(V newValue) { + VALUE.set(this, newValue); + } + + /** + * Returns the current value, + * with memory effects as specified by {@link VarHandle#getOpaque}. + * + * @return the value + * @since 9 + */ + public final V getOpaque() { + return (V)VALUE.getOpaque(this); + } + + /** + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setOpaque}. + * + * @param newValue the new value + * @since 9 + */ + public final void setOpaque(V newValue) { + VALUE.setOpaque(this, newValue); + } + + /** + * Returns the current value, + * with memory effects as specified by {@link VarHandle#getAcquire}. + * + * @return the value + * @since 9 + */ + public final V getAcquire() { + return (V)VALUE.getAcquire(this); + } + + /** + * Sets the value to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. + * + * @param newValue the new value + * @since 9 + */ + public final void setRelease(V newValue) { + VALUE.setRelease(this, newValue); + } + + /** + * Atomically sets the value to {@code newValue} if the current value, + * referred to as the witness value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchange}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final V compareAndExchange(V expectedValue, V newValue) { + return (V)VALUE.compareAndExchange(this, expectedValue, newValue); + } + + /** + * Atomically sets the value to {@code newValue} if the current value, + * referred to as the witness value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeAcquire}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final V compareAndExchangeAcquire(V expectedValue, V newValue) { + return (V)VALUE.compareAndExchangeAcquire(this, expectedValue, newValue); + } + + /** + * Atomically sets the value to {@code newValue} if the current value, + * referred to as the witness value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeRelease}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final V compareAndExchangeRelease(V expectedValue, V newValue) { + return (V)VALUE.compareAndExchangeRelease(this, expectedValue, newValue); + } + + /** + * Possibly atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetVolatile}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetVolatile(V expectedValue, V newValue) { + return VALUE.weakCompareAndSetVolatile(this, expectedValue, newValue); + } + + /** + * Possibly atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetAcquire}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetAcquire(V expectedValue, V newValue) { + return VALUE.weakCompareAndSetAcquire(this, expectedValue, newValue); + } + + /** + * Possibly atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetRelease}. + * + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetRelease(V expectedValue, V newValue) { + return VALUE.weakCompareAndSetRelease(this, expectedValue, newValue); + } + } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,54 +35,28 @@ package java.util.concurrent.atomic; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.lang.reflect.Array; +import java.lang.reflect.Field; import java.util.Arrays; import java.util.function.BinaryOperator; import java.util.function.UnaryOperator; /** * An array of object references in which elements may be updated - * atomically. See the {@link java.util.concurrent.atomic} package - * specification for description of the properties of atomic - * variables. + * atomically. See the {@link VarHandle} specification for + * descriptions of the properties of atomic accesses. * @since 1.5 * @author Doug Lea * @param The base class of elements held in this array */ public class AtomicReferenceArray implements java.io.Serializable { private static final long serialVersionUID = -6209656149925076980L; - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long ARRAY; - private static final int ABASE; - private static final int ASHIFT; + private static final VarHandle AA + = MethodHandles.arrayElementVarHandle(Object[].class); private final Object[] array; // must have exact type Object[] - static { - try { - ARRAY = U.objectFieldOffset - (AtomicReferenceArray.class.getDeclaredField("array")); - ABASE = U.arrayBaseOffset(Object[].class); - int scale = U.arrayIndexScale(Object[].class); - if ((scale & (scale - 1)) != 0) - throw new Error("array index scale not a power of two"); - ASHIFT = 31 - Integer.numberOfLeadingZeros(scale); - } catch (ReflectiveOperationException e) { - throw new Error(e); - } - } - - private long checkedByteOffset(int i) { - if (i < 0 || i >= array.length) - throw new IndexOutOfBoundsException("index " + i); - - return byteOffset(i); - } - - private static long byteOffset(int i) { - return ((long) i << ASHIFT) + ABASE; - } - /** * Creates a new AtomicReferenceArray of the given length, with all * elements initially null. @@ -115,44 +89,44 @@ } /** - * Gets the current value at position {@code i}. + * Returns the current value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getVolatile}. * * @param i the index * @return the current value */ + @SuppressWarnings("unchecked") public final E get(int i) { - return getRaw(checkedByteOffset(i)); - } - - @SuppressWarnings("unchecked") - private E getRaw(long offset) { - return (E) U.getObjectVolatile(array, offset); + return (E)AA.getVolatile(array, i); } /** - * Sets the element at position {@code i} to the given value. + * Sets the element at index {@code i} to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setVolatile}. * * @param i the index * @param newValue the new value */ public final void set(int i, E newValue) { - U.putObjectVolatile(array, checkedByteOffset(i), newValue); + AA.setVolatile(array, i, newValue); } /** - * Eventually sets the element at position {@code i} to the given value. + * Sets the element at index {@code i} to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. * * @param i the index * @param newValue the new value * @since 1.6 */ public final void lazySet(int i, E newValue) { - U.putObjectRelease(array, checkedByteOffset(i), newValue); + AA.setRelease(array, i, newValue); } /** - * Atomically sets the element at position {@code i} to the given - * value and returns the old value. + * Atomically sets the element at index {@code i} to {@code + * newValue} and returns the old value, + * with memory effects as specified by {@link VarHandle#getAndSet}. * * @param i the index * @param newValue the new value @@ -160,42 +134,36 @@ */ @SuppressWarnings("unchecked") public final E getAndSet(int i, E newValue) { - return (E)U.getAndSetObject(array, checkedByteOffset(i), newValue); + return (E)AA.getAndSet(array, i, newValue); } /** - * Atomically sets the element at position {@code i} to the given - * updated value if the current value {@code ==} the expected value. + * Atomically sets the element at index {@code i} to {@code newValue} + * if the element's current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#compareAndSet}. * * @param i the index - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful. False return indicates that * the actual value was not equal to the expected value. */ - public final boolean compareAndSet(int i, E expect, E update) { - return compareAndSetRaw(checkedByteOffset(i), expect, update); - } - - private boolean compareAndSetRaw(long offset, E expect, E update) { - return U.compareAndSwapObject(array, offset, expect, update); + public final boolean compareAndSet(int i, E expectedValue, E newValue) { + return AA.compareAndSet(array, i, expectedValue, newValue); } /** - * Atomically sets the element at position {@code i} to the given - * updated value if the current value {@code ==} the expected value. - * - *

May fail - * spuriously and does not provide ordering guarantees, so is - * only rarely an appropriate alternative to {@code compareAndSet}. + * Possibly atomically sets the element at index {@code i} to + * {@code newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#weakCompareAndSet}. * * @param i the index - * @param expect the expected value - * @param update the new value + * @param expectedValue the expected value + * @param newValue the new value * @return {@code true} if successful */ - public final boolean weakCompareAndSet(int i, E expect, E update) { - return compareAndSet(i, expect, update); + public final boolean weakCompareAndSet(int i, E expectedValue, E newValue) { + return AA.weakCompareAndSet(array, i, expectedValue, newValue); } /** @@ -210,13 +178,14 @@ * @since 1.8 */ public final E getAndUpdate(int i, UnaryOperator updateFunction) { - long offset = checkedByteOffset(i); - E prev, next; - do { - prev = getRaw(offset); - next = updateFunction.apply(prev); - } while (!compareAndSetRaw(offset, prev, next)); - return prev; + E prev = get(i), next = null; + for (boolean haveNext = false;;) { + if (!haveNext) + next = updateFunction.apply(prev); + if (weakCompareAndSetVolatile(i, prev, next)) + return prev; + haveNext = (prev == (prev = get(i))); + } } /** @@ -231,23 +200,25 @@ * @since 1.8 */ public final E updateAndGet(int i, UnaryOperator updateFunction) { - long offset = checkedByteOffset(i); - E prev, next; - do { - prev = getRaw(offset); - next = updateFunction.apply(prev); - } while (!compareAndSetRaw(offset, prev, next)); - return next; + E prev = get(i), next = null; + for (boolean haveNext = false;;) { + if (!haveNext) + next = updateFunction.apply(prev); + if (weakCompareAndSetVolatile(i, prev, next)) + return next; + haveNext = (prev == (prev = get(i))); + } } /** * Atomically updates the element at index {@code i} with the - * results of applying the given function to the current and - * given values, returning the previous value. The function should - * be side-effect-free, since it may be re-applied when attempted + * results of applying the given function to the current and given + * values, returning the previous value. The function should be + * side-effect-free, since it may be re-applied when attempted * updates fail due to contention among threads. The function is - * applied with the current value at index {@code i} as its first - * argument, and the given update as the second argument. + * applied with the current value of the element at index {@code i} + * as its first argument, and the given update as the second + * argument. * * @param i the index * @param x the update value @@ -257,23 +228,25 @@ */ public final E getAndAccumulate(int i, E x, BinaryOperator accumulatorFunction) { - long offset = checkedByteOffset(i); - E prev, next; - do { - prev = getRaw(offset); - next = accumulatorFunction.apply(prev, x); - } while (!compareAndSetRaw(offset, prev, next)); - return prev; + E prev = get(i), next = null; + for (boolean haveNext = false;;) { + if (!haveNext) + next = accumulatorFunction.apply(prev, x); + if (weakCompareAndSetVolatile(i, prev, next)) + return prev; + haveNext = (prev == (prev = get(i))); + } } /** * Atomically updates the element at index {@code i} with the - * results of applying the given function to the current and - * given values, returning the updated value. The function should - * be side-effect-free, since it may be re-applied when attempted + * results of applying the given function to the current and given + * values, returning the updated value. The function should be + * side-effect-free, since it may be re-applied when attempted * updates fail due to contention among threads. The function is - * applied with the current value at index {@code i} as its first - * argument, and the given update as the second argument. + * applied with the current value of the element at index {@code i} + * as its first argument, and the given update as the second + * argument. * * @param i the index * @param x the update value @@ -283,13 +256,14 @@ */ public final E accumulateAndGet(int i, E x, BinaryOperator accumulatorFunction) { - long offset = checkedByteOffset(i); - E prev, next; - do { - prev = getRaw(offset); - next = accumulatorFunction.apply(prev, x); - } while (!compareAndSetRaw(offset, prev, next)); - return next; + E prev = get(i), next = null; + for (boolean haveNext = false;;) { + if (!haveNext) + next = accumulatorFunction.apply(prev, x); + if (weakCompareAndSetVolatile(i, prev, next)) + return next; + haveNext = (prev == (prev = get(i))); + } } /** @@ -304,7 +278,7 @@ StringBuilder b = new StringBuilder(); b.append('['); for (int i = 0; ; i++) { - b.append(getRaw(byteOffset(i))); + b.append(get(i)); if (i == iMax) return b.append(']').toString(); b.append(',').append(' '); @@ -326,7 +300,199 @@ throw new java.io.InvalidObjectException("Not array type"); if (a.getClass() != Object[].class) a = Arrays.copyOf((Object[])a, Array.getLength(a), Object[].class); - U.putObjectVolatile(this, ARRAY, a); + Field arrayField = java.security.AccessController.doPrivileged( + (java.security.PrivilegedAction) () -> { + try { + Field f = AtomicReferenceArray.class + .getDeclaredField("array"); + f.setAccessible(true); + return f; + } catch (ReflectiveOperationException e) { + throw new Error(e); + }}); + try { + arrayField.set(this, a); + } catch (IllegalAccessException e) { + throw new Error(e); + } + } + + // jdk9 + + /** + * Returns the current value of the element at index {@code i}, + * with memory semantics of reading as if the variable was declared + * non-{@code volatile}. + * + * @param i the index + * @return the value + * @since 9 + */ + public final E getPlain(int i) { + return (E)AA.get(array, i); + } + + /** + * Sets the element at index {@code i} to {@code newValue}, + * with memory semantics of setting as if the variable was + * declared non-{@code volatile} and non-{@code final}. + * + * @param i the index + * @param newValue the new value + * @since 9 + */ + public final void setPlain(int i, E newValue) { + AA.set(array, i, newValue); + } + + /** + * Returns the current value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getOpaque}. + * + * @param i the index + * @return the value + * @since 9 + */ + public final E getOpaque(int i) { + return (E)AA.getOpaque(array, i); + } + + /** + * Sets the element at index {@code i} to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setOpaque}. + * + * @param i the index + * @param newValue the new value + * @since 9 + */ + public final void setOpaque(int i, E newValue) { + AA.setOpaque(array, i, newValue); + } + + /** + * Returns the current value of the element at index {@code i}, + * with memory effects as specified by {@link VarHandle#getAcquire}. + * + * @param i the index + * @return the value + * @since 9 + */ + public final E getAcquire(int i) { + return (E)AA.getAcquire(array, i); + } + + /** + * Sets the element at index {@code i} to {@code newValue}, + * with memory effects as specified by {@link VarHandle#setRelease}. + * + * @param i the index + * @param newValue the new value + * @since 9 + */ + public final void setRelease(int i, E newValue) { + AA.setRelease(array, i, newValue); + } + + /** + * Atomically sets the element at index {@code i} to {@code newValue} + * if the element's current value, referred to as the witness + * value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchange}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final E compareAndExchange(int i, E expectedValue, E newValue) { + return (E)AA.compareAndExchange(array, i, expectedValue, newValue); + } + + /** + * Atomically sets the element at index {@code i} to {@code newValue} + * if the element's current value, referred to as the witness + * value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeAcquire}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final E compareAndExchangeAcquire(int i, E expectedValue, E newValue) { + return (E)AA.compareAndExchangeAcquire(array, i, expectedValue, newValue); + } + + /** + * Atomically sets the element at index {@code i} to {@code newValue} + * if the element's current value, referred to as the witness + * value, {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#compareAndExchangeRelease}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return the witness value, which will be the same as the + * expected value if successful + * @since 9 + */ + public final E compareAndExchangeRelease(int i, E expectedValue, E newValue) { + return (E)AA.compareAndExchangeRelease(array, i, expectedValue, newValue); + } + + /** + * Possibly atomically sets the element at index {@code i} to + * {@code newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetVolatile}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetVolatile(int i, E expectedValue, E newValue) { + return AA.weakCompareAndSetVolatile(array, i, expectedValue, newValue); + } + + /** + * Possibly atomically sets the element at index {@code i} to + * {@code newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetAcquire}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetAcquire(int i, E expectedValue, E newValue) { + return AA.weakCompareAndSetAcquire(array, i, expectedValue, newValue); + } + + /** + * Possibly atomically sets the element at index {@code i} to + * {@code newValue} if the element's current value {@code == expectedValue}, + * with memory effects as specified by + * {@link VarHandle#weakCompareAndSetRelease}. + * + * @param i the index + * @param expectedValue the expected value + * @param newValue the new value + * @return {@code true} if successful + * @since 9 + */ + public final boolean weakCompareAndSetRelease(int i, E expectedValue, E newValue) { + return AA.weakCompareAndSetRelease(array, i, expectedValue, newValue); } } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java Mon Jul 18 09:38:08 2016 -0700 @@ -42,6 +42,7 @@ import java.security.PrivilegedExceptionAction; import java.util.function.BinaryOperator; import java.util.function.UnaryOperator; +import jdk.internal.misc.Unsafe; import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; @@ -168,8 +169,8 @@ public abstract void lazySet(T obj, V newValue); /** - * Gets the current value held in the field of the given object managed - * by this updater. + * Returns the current value held in the field of the given object + * managed by this updater. * * @param obj An object whose field to get * @return the current value @@ -284,7 +285,7 @@ private static final class AtomicReferenceFieldUpdaterImpl extends AtomicReferenceFieldUpdater { - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); + private static final Unsafe U = Unsafe.getUnsafe(); private final long offset; /** * if field is protected, the subclass constructing updater, else diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,6 +35,9 @@ package java.util.concurrent.atomic; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; + /** * An {@code AtomicStampedReference} maintains an object reference * along with an integer "stamp", that can be updated atomically. @@ -188,20 +191,19 @@ casPair(current, Pair.of(expectedReference, newStamp))); } - // Unsafe mechanics - - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long PAIR; + // VarHandle mechanics + private static final VarHandle PAIR; static { try { - PAIR = U.objectFieldOffset - (AtomicStampedReference.class.getDeclaredField("pair")); + MethodHandles.Lookup l = MethodHandles.lookup(); + PAIR = l.findVarHandle(AtomicStampedReference.class, "pair", + Pair.class); } catch (ReflectiveOperationException e) { throw new Error(e); } } private boolean casPair(Pair cmp, Pair val) { - return U.compareAndSwapObject(this, PAIR, cmp, val); + return PAIR.compareAndSet(this, cmp, val); } } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/atomic/LongAccumulator.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/LongAccumulator.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/LongAccumulator.java Mon Jul 18 09:38:08 2016 -0700 @@ -68,7 +68,7 @@ *

Class {@link LongAdder} provides analogs of the functionality of * this class for the common special case of maintaining counts and * sums. The call {@code new LongAdder()} is equivalent to {@code new - * LongAccumulator((x, y) -> x + y, 0L}. + * LongAccumulator((x, y) -> x + y, 0L)}. * *

This class extends {@link Number}, but does not define * methods such as {@code equals}, {@code hashCode} and {@code diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,10 +35,13 @@ package java.util.concurrent.atomic; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.Arrays; import java.util.concurrent.ThreadLocalRandom; import java.util.function.DoubleBinaryOperator; import java.util.function.LongBinaryOperator; +import jdk.internal.misc.Unsafe; /** * A package-local class holding common representation and mechanics @@ -123,22 +126,21 @@ volatile long value; Cell(long x) { value = x; } final boolean cas(long cmp, long val) { - return U.compareAndSwapLong(this, VALUE, cmp, val); + return VALUE.compareAndSet(this, cmp, val); } final void reset() { - U.putLongVolatile(this, VALUE, 0L); + VALUE.setVolatile(this, 0L); } final void reset(long identity) { - U.putLongVolatile(this, VALUE, identity); + VALUE.setVolatile(this, identity); } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long VALUE; + // VarHandle mechanics + private static final VarHandle VALUE; static { try { - VALUE = U.objectFieldOffset - (Cell.class.getDeclaredField("value")); + MethodHandles.Lookup l = MethodHandles.lookup(); + VALUE = l.findVarHandle(Cell.class, "value", long.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -174,14 +176,14 @@ * CASes the base field. */ final boolean casBase(long cmp, long val) { - return U.compareAndSwapLong(this, BASE, cmp, val); + return BASE.compareAndSet(this, cmp, val); } /** * CASes the cellsBusy field from 0 to 1 to acquire lock. */ final boolean casCellsBusy() { - return U.compareAndSwapInt(this, CELLSBUSY, 0, 1); + return CELLSBUSY.compareAndSet(this, 0, 1); } /** @@ -371,18 +373,16 @@ } } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long BASE; - private static final long CELLSBUSY; + // Unsafe and VarHandle mechanics + private static final Unsafe U = Unsafe.getUnsafe(); + private static final VarHandle BASE; + private static final VarHandle CELLSBUSY; private static final long PROBE; static { try { - BASE = U.objectFieldOffset - (Striped64.class.getDeclaredField("base")); - CELLSBUSY = U.objectFieldOffset - (Striped64.class.getDeclaredField("cellsBusy")); - + MethodHandles.Lookup l = MethodHandles.lookup(); + BASE = l.findVarHandle(Striped64.class, "base", long.class); + CELLSBUSY = l.findVarHandle(Striped64.class, "cellsBusy", int.class); PROBE = U.objectFieldOffset (Thread.class.getDeclaredField("threadLocalRandomProbe")); } catch (ReflectiveOperationException e) { diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/atomic/package-info.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/package-info.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/package-info.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,26 +35,10 @@ /** * A small toolkit of classes that support lock-free thread-safe - * programming on single variables. In essence, the classes in this - * package extend the notion of {@code volatile} values, fields, and - * array elements to those that also provide an atomic conditional update - * operation of the form: - * - *

 {@code boolean compareAndSet(expectedValue, updateValue);}
- * - *

This method (which varies in argument types across different - * classes) atomically sets a variable to the {@code updateValue} if it - * currently holds the {@code expectedValue}, reporting {@code true} on - * success. The classes in this package also contain methods to get and - * unconditionally set values, as well as a weaker conditional atomic - * update operation {@code weakCompareAndSet} described below. - * - *

The specifications of these methods enable implementations to - * employ efficient machine-level atomic instructions that are available - * on contemporary processors. However on some platforms, support may - * entail some form of internal locking. Thus the methods are not - * strictly guaranteed to be non-blocking -- - * a thread may block transiently before performing the operation. + * programming on single variables. Instances of Atomic classes + * maintain values that are accessed and updated using methods + * otherwise available for fields using associated atomic {@link + * java.lang.invoke.VarHandle} operations. * *

Instances of classes * {@link java.util.concurrent.atomic.AtomicBoolean}, @@ -92,45 +76,26 @@ * return prev; // return next; for transformAndGet * }} * - *

The memory effects for accesses and updates of atomics generally - * follow the rules for volatiles, as stated in - * - * Chapter 17 of - * The Java™ Language Specification: - * - *

    - * - *
  • {@code get} has the memory effects of reading a - * {@code volatile} variable. - * - *
  • {@code set} has the memory effects of writing (assigning) a - * {@code volatile} variable. + *

    These classes are not general purpose replacements for {@code + * java.lang.Integer} and related classes. They do not + * define methods such as {@code equals}, {@code hashCode} and {@code + * compareTo}. Because atomic variables are expected to be mutated, + * they are poor choices for hash table keys. * - *

  • {@code lazySet} has the memory effects of writing (assigning) - * a {@code volatile} variable except that it permits reorderings with - * subsequent (but not previous) memory actions that do not themselves - * impose reordering constraints with ordinary non-{@code volatile} - * writes. Among other usage contexts, {@code lazySet} may apply when - * nulling out, for the sake of garbage collection, a reference that is - * never accessed again. + *

    The + * {@link java.util.concurrent.atomic.AtomicIntegerArray}, + * {@link java.util.concurrent.atomic.AtomicLongArray}, and + * {@link java.util.concurrent.atomic.AtomicReferenceArray} classes + * further extend atomic operation support to arrays of these types. + * These classes are also notable in providing {@code volatile} access + * semantics for their array elements. * - *

  • {@code weakCompareAndSet} atomically reads and conditionally - * writes a variable but does not - * create any happens-before orderings, so provides no guarantees - * with respect to previous or subsequent reads and writes of any - * variables other than the target of the {@code weakCompareAndSet}. - * - *
  • {@code compareAndSet} - * and all other read-and-update operations such as {@code getAndIncrement} - * have the memory effects of both reading and - * writing {@code volatile} variables. - *
- * - *

In addition to classes representing single values, this package - * contains Updater classes that can be used to obtain - * {@code compareAndSet} operations on any selected {@code volatile} - * field of any selected class. - * + *

In addition to classes representing single values and arrays, + * this package contains Updater classes that can be used to + * obtain {@code compareAndSet} and related operations on any selected + * {@code volatile} field of any selected class. These classes + * predate the introduction of {@link + * java.lang.invoke.VarHandle}, and are of more limited use. * {@link java.util.concurrent.atomic.AtomicReferenceFieldUpdater}, * {@link java.util.concurrent.atomic.AtomicIntegerFieldUpdater}, and * {@link java.util.concurrent.atomic.AtomicLongFieldUpdater} are @@ -143,38 +108,6 @@ * reflection-based setup, less convenient usage, and weaker * guarantees. * - *

The - * {@link java.util.concurrent.atomic.AtomicIntegerArray}, - * {@link java.util.concurrent.atomic.AtomicLongArray}, and - * {@link java.util.concurrent.atomic.AtomicReferenceArray} classes - * further extend atomic operation support to arrays of these types. - * These classes are also notable in providing {@code volatile} access - * semantics for their array elements, which is not supported for - * ordinary arrays. - * - *

The atomic classes also support method - * {@code weakCompareAndSet}, which has limited applicability. On some - * platforms, the weak version may be more efficient than {@code - * compareAndSet} in the normal case, but differs in that any given - * invocation of the {@code weakCompareAndSet} method may return {@code - * false} spuriously (that is, for no apparent reason). A - * {@code false} return means only that the operation may be retried if - * desired, relying on the guarantee that repeated invocation when the - * variable holds {@code expectedValue} and no other thread is also - * attempting to set the variable will eventually succeed. (Such - * spurious failures may for example be due to memory contention effects - * that are unrelated to whether the expected and current values are - * equal.) Additionally {@code weakCompareAndSet} does not provide - * ordering guarantees that are usually needed for synchronization - * control. However, the method may be useful for updating counters and - * statistics when such updates are unrelated to the other - * happens-before orderings of a program. When a thread sees an update - * to an atomic variable caused by a {@code weakCompareAndSet}, it does - * not necessarily see updates to any other variables that - * occurred before the {@code weakCompareAndSet}. This may be - * acceptable when, for example, updating performance statistics, but - * rarely otherwise. - * *

The {@link java.util.concurrent.atomic.AtomicMarkableReference} * class associates a single boolean with a reference. For example, this * bit might be used inside a data structure to mean that the object @@ -185,29 +118,6 @@ * used for example, to represent version numbers corresponding to * series of updates. * - *

Atomic classes are designed primarily as building blocks for - * implementing non-blocking data structures and related infrastructure - * classes. The {@code compareAndSet} method is not a general - * replacement for locking. It applies only when critical updates for an - * object are confined to a single variable. - * - *

Atomic classes are not general purpose replacements for - * {@code java.lang.Integer} and related classes. They do not - * define methods such as {@code equals}, {@code hashCode} and - * {@code compareTo}. (Because atomic variables are expected to be - * mutated, they are poor choices for hash table keys.) Additionally, - * classes are provided only for those types that are commonly useful in - * intended applications. For example, there is no atomic class for - * representing {@code byte}. In those infrequent cases where you would - * like to do so, you can use an {@code AtomicInteger} to hold - * {@code byte} values, and cast appropriately. - * - * You can also hold floats using - * {@link java.lang.Float#floatToRawIntBits} and - * {@link java.lang.Float#intBitsToFloat} conversions, and doubles using - * {@link java.lang.Double#doubleToRawLongBits} and - * {@link java.lang.Double#longBitsToDouble} conversions. - * * @since 1.5 */ package java.util.concurrent.atomic; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,6 +35,8 @@ package java.util.concurrent.locks; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.ArrayList; import java.util.Collection; import java.util.Date; @@ -113,7 +115,7 @@ protected final void setState(long newState) { // Use putLongVolatile instead of ordinary volatile store when // using compareAndSwapLong, for sake of some 32bit systems. - U.putLongVolatile(this, STATE, newState); + STATE.setVolatile(this, newState); } /** @@ -128,7 +130,7 @@ * value was not equal to the expected value. */ protected final boolean compareAndSetState(long expect, long update) { - return U.compareAndSwapLong(this, STATE, expect, update); + return STATE.compareAndSet(this, expect, update); } // Queuing utilities @@ -149,7 +151,7 @@ for (;;) { Node oldTail = tail; if (oldTail != null) { - U.putObject(node, Node.PREV, oldTail); + node.setPrevRelaxed(oldTail); if (compareAndSetTail(oldTail, node)) { oldTail.next = node; return oldTail; @@ -172,7 +174,7 @@ for (;;) { Node oldTail = tail; if (oldTail != null) { - U.putObject(node, Node.PREV, oldTail); + node.setPrevRelaxed(oldTail); if (compareAndSetTail(oldTail, node)) { oldTail.next = node; return node; @@ -1810,28 +1812,17 @@ } } - /** - * Setup to support compareAndSet. We need to natively implement - * this here: For the sake of permitting future enhancements, we - * cannot explicitly subclass AtomicLong, which would be - * efficient and useful otherwise. So, as the lesser of evils, we - * natively implement using hotspot intrinsics API. And while we - * are at it, we do the same for other CASable fields (which could - * otherwise be done with atomic field updaters). - */ - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long STATE; - private static final long HEAD; - private static final long TAIL; + // VarHandle mechanics + private static final VarHandle STATE; + private static final VarHandle HEAD; + private static final VarHandle TAIL; static { try { - STATE = U.objectFieldOffset - (AbstractQueuedLongSynchronizer.class.getDeclaredField("state")); - HEAD = U.objectFieldOffset - (AbstractQueuedLongSynchronizer.class.getDeclaredField("head")); - TAIL = U.objectFieldOffset - (AbstractQueuedLongSynchronizer.class.getDeclaredField("tail")); + MethodHandles.Lookup l = MethodHandles.lookup(); + STATE = l.findVarHandle(AbstractQueuedLongSynchronizer.class, "state", long.class); + HEAD = l.findVarHandle(AbstractQueuedLongSynchronizer.class, "head", Node.class); + TAIL = l.findVarHandle(AbstractQueuedLongSynchronizer.class, "tail", Node.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -1846,7 +1837,7 @@ */ private final void initializeSyncQueue() { Node h; - if (U.compareAndSwapObject(this, HEAD, null, (h = new Node()))) + if (HEAD.compareAndSet(this, null, (h = new Node()))) tail = h; } @@ -1854,6 +1845,6 @@ * CASes tail field. */ private final boolean compareAndSetTail(Node expect, Node update) { - return U.compareAndSwapObject(this, TAIL, expect, update); + return TAIL.compareAndSet(this, expect, update); } } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,11 +35,12 @@ package java.util.concurrent.locks; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.concurrent.TimeUnit; -import jdk.internal.vm.annotation.ReservedStackAccess; /** * Provides a framework for implementing blocking locks and related @@ -506,40 +507,41 @@ /** Constructor used by addWaiter. */ Node(Node nextWaiter) { this.nextWaiter = nextWaiter; - U.putObject(this, THREAD, Thread.currentThread()); + THREAD.set(this, Thread.currentThread()); } /** Constructor used by addConditionWaiter. */ Node(int waitStatus) { - U.putInt(this, WAITSTATUS, waitStatus); - U.putObject(this, THREAD, Thread.currentThread()); + WAITSTATUS.set(this, waitStatus); + THREAD.set(this, Thread.currentThread()); } /** CASes waitStatus field. */ final boolean compareAndSetWaitStatus(int expect, int update) { - return U.compareAndSwapInt(this, WAITSTATUS, expect, update); + return WAITSTATUS.compareAndSet(this, expect, update); } /** CASes next field. */ final boolean compareAndSetNext(Node expect, Node update) { - return U.compareAndSwapObject(this, NEXT, expect, update); + return NEXT.compareAndSet(this, expect, update); + } + + final void setPrevRelaxed(Node p) { + PREV.set(this, p); } - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long NEXT; - static final long PREV; - private static final long THREAD; - private static final long WAITSTATUS; + // VarHandle mechanics + private static final VarHandle NEXT; + private static final VarHandle PREV; + private static final VarHandle THREAD; + private static final VarHandle WAITSTATUS; static { try { - NEXT = U.objectFieldOffset - (Node.class.getDeclaredField("next")); - PREV = U.objectFieldOffset - (Node.class.getDeclaredField("prev")); - THREAD = U.objectFieldOffset - (Node.class.getDeclaredField("thread")); - WAITSTATUS = U.objectFieldOffset - (Node.class.getDeclaredField("waitStatus")); + MethodHandles.Lookup l = MethodHandles.lookup(); + NEXT = l.findVarHandle(Node.class, "next", Node.class); + PREV = l.findVarHandle(Node.class, "prev", Node.class); + THREAD = l.findVarHandle(Node.class, "thread", Thread.class); + WAITSTATUS = l.findVarHandle(Node.class, "waitStatus", int.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -595,7 +597,7 @@ * value was not equal to the expected value. */ protected final boolean compareAndSetState(int expect, int update) { - return U.compareAndSwapInt(this, STATE, expect, update); + return STATE.compareAndSet(this, expect, update); } // Queuing utilities @@ -616,7 +618,7 @@ for (;;) { Node oldTail = tail; if (oldTail != null) { - U.putObject(node, Node.PREV, oldTail); + node.setPrevRelaxed(oldTail); if (compareAndSetTail(oldTail, node)) { oldTail.next = node; return oldTail; @@ -639,7 +641,7 @@ for (;;) { Node oldTail = tail; if (oldTail != null) { - U.putObject(node, Node.PREV, oldTail); + node.setPrevRelaxed(oldTail); if (compareAndSetTail(oldTail, node)) { oldTail.next = node; return node; @@ -887,7 +889,6 @@ * @param arg the acquire argument * @return {@code true} if interrupted while waiting */ - @ReservedStackAccess final boolean acquireQueued(final Node node, int arg) { try { boolean interrupted = false; @@ -1220,7 +1221,6 @@ * {@link #tryAcquire} but is otherwise uninterpreted and * can represent anything you like. */ - @ReservedStackAccess public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) @@ -1284,7 +1284,6 @@ * can represent anything you like. * @return the value returned from {@link #tryRelease} */ - @ReservedStackAccess public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; @@ -1365,7 +1364,6 @@ * and can represent anything you like. * @return the value returned from {@link #tryReleaseShared} */ - @ReservedStackAccess public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { doReleaseShared(); @@ -2279,28 +2277,17 @@ } } - /** - * Setup to support compareAndSet. We need to natively implement - * this here: For the sake of permitting future enhancements, we - * cannot explicitly subclass AtomicInteger, which would be - * efficient and useful otherwise. So, as the lesser of evils, we - * natively implement using hotspot intrinsics API. And while we - * are at it, we do the same for other CASable fields (which could - * otherwise be done with atomic field updaters). - */ - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long STATE; - private static final long HEAD; - private static final long TAIL; + // VarHandle mechanics + private static final VarHandle STATE; + private static final VarHandle HEAD; + private static final VarHandle TAIL; static { try { - STATE = U.objectFieldOffset - (AbstractQueuedSynchronizer.class.getDeclaredField("state")); - HEAD = U.objectFieldOffset - (AbstractQueuedSynchronizer.class.getDeclaredField("head")); - TAIL = U.objectFieldOffset - (AbstractQueuedSynchronizer.class.getDeclaredField("tail")); + MethodHandles.Lookup l = MethodHandles.lookup(); + STATE = l.findVarHandle(AbstractQueuedSynchronizer.class, "state", int.class); + HEAD = l.findVarHandle(AbstractQueuedSynchronizer.class, "head", Node.class); + TAIL = l.findVarHandle(AbstractQueuedSynchronizer.class, "tail", Node.class); } catch (ReflectiveOperationException e) { throw new Error(e); } @@ -2315,7 +2302,7 @@ */ private final void initializeSyncQueue() { Node h; - if (U.compareAndSwapObject(this, HEAD, null, (h = new Node()))) + if (HEAD.compareAndSet(this, null, (h = new Node()))) tail = h; } @@ -2323,6 +2310,6 @@ * CASes tail field. */ private final boolean compareAndSetTail(Node expect, Node update) { - return U.compareAndSwapObject(this, TAIL, expect, update); + return TAIL.compareAndSet(this, expect, update); } } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/locks/Condition.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/Condition.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/Condition.java Mon Jul 18 09:38:08 2016 -0700 @@ -396,7 +396,6 @@ * re-acquire the lock associated with this condition. When the * thread returns it is guaranteed to hold this lock. * - * *

If the current thread: *

    *
  • has its interrupted status set on entry to this method; or @@ -408,7 +407,6 @@ * case, whether or not the test for interruption occurs before the lock * is released. * - * *

    The return value indicates whether the deadline has elapsed, * which can be used as follows: *

     {@code
    diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java	Fri Jul 15 09:05:36 2016 -0700
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java	Mon Jul 18 09:38:08 2016 -0700
    @@ -35,6 +35,8 @@
     
     package java.util.concurrent.locks;
     
    +import jdk.internal.misc.Unsafe;
    +
     /**
      * Basic thread blocking primitives for creating locks and other
      * synchronization classes.
    @@ -405,16 +407,30 @@
             return r;
         }
     
    +    /**
    +     * Returns the thread id for the given thread.  We must access
    +     * this directly rather than via method Thread.getId() because
    +     * getId() is not final, and has been known to be overridden in
    +     * ways that do not preserve unique mappings.
    +     */
    +    static final long getThreadId(Thread thread) {
    +        return U.getLongVolatile(thread, TID);
    +    }
    +
         // Hotspot implementation via intrinsics API
    -    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
    +    private static final Unsafe U = Unsafe.getUnsafe();
         private static final long PARKBLOCKER;
         private static final long SECONDARY;
    +    private static final long TID;
         static {
             try {
                 PARKBLOCKER = U.objectFieldOffset
                     (Thread.class.getDeclaredField("parkBlocker"));
                 SECONDARY = U.objectFieldOffset
                     (Thread.class.getDeclaredField("threadLocalRandomSecondarySeed"));
    +            TID = U.objectFieldOffset
    +                (Thread.class.getDeclaredField("tid"));
    +
             } catch (ReflectiveOperationException e) {
                 throw new Error(e);
             }
    diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/locks/ReadWriteLock.java
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReadWriteLock.java	Fri Jul 15 09:05:36 2016 -0700
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReadWriteLock.java	Mon Jul 18 09:38:08 2016 -0700
    @@ -79,7 +79,6 @@
      * and measurement will establish whether the use of a read-write lock is
      * suitable for your application.
      *
    - *
      * 

    Although the basic operation of a read-write lock is straight-forward, * there are many policy decisions that an implementation must make, which * may affect the effectiveness of the read-write lock in a given application. diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantLock.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantLock.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantLock.java Mon Jul 18 09:38:08 2016 -0700 @@ -119,12 +119,6 @@ private static final long serialVersionUID = -5179523762034025860L; /** - * Performs {@link Lock#lock}. The main reason for subclassing - * is to allow fast path for nonfair version. - */ - abstract void lock(); - - /** * Performs non-fair tryLock. tryAcquire is implemented in * subclasses, but both need nonfair try for trylock method. */ @@ -201,19 +195,6 @@ */ static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; - - /** - * Performs lock. Try immediate barge, backing up to normal - * acquire on failure. - */ - @ReservedStackAccess - final void lock() { - if (compareAndSetState(0, 1)) - setExclusiveOwnerThread(Thread.currentThread()); - else - acquire(1); - } - protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } @@ -224,11 +205,6 @@ */ static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; - - final void lock() { - acquire(1); - } - /** * Fair version of tryAcquire. Don't grant access unless * recursive call or no waiters or is first. @@ -288,7 +264,7 @@ * at which time the lock hold count is set to one. */ public void lock() { - sync.lock(); + sync.acquire(1); } /** diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java Mon Jul 18 09:38:08 2016 -0700 @@ -37,6 +37,7 @@ import java.util.Collection; import java.util.concurrent.TimeUnit; +import jdk.internal.vm.annotation.ReservedStackAccess; /** * An implementation of {@link ReadWriteLock} supporting similar @@ -278,7 +279,7 @@ static final class HoldCounter { int count; // initially 0 // Use id, not reference, to avoid garbage retention - final long tid = getThreadId(Thread.currentThread()); + final long tid = LockSupport.getThreadId(Thread.currentThread()); } /** @@ -367,7 +368,7 @@ * both read and write holds that are all released during a * condition wait and re-established in tryAcquire. */ - + @ReservedStackAccess protected final boolean tryRelease(int releases) { if (!isHeldExclusively()) throw new IllegalMonitorStateException(); @@ -379,6 +380,7 @@ return free; } + @ReservedStackAccess protected final boolean tryAcquire(int acquires) { /* * Walkthrough: @@ -411,6 +413,7 @@ return true; } + @ReservedStackAccess protected final boolean tryReleaseShared(int unused) { Thread current = Thread.currentThread(); if (firstReader == current) { @@ -421,7 +424,8 @@ firstReaderHoldCount--; } else { HoldCounter rh = cachedHoldCounter; - if (rh == null || rh.tid != getThreadId(current)) + if (rh == null || + rh.tid != LockSupport.getThreadId(current)) rh = readHolds.get(); int count = rh.count; if (count <= 1) { @@ -447,6 +451,7 @@ "attempt to unlock read lock, not locked by current thread"); } + @ReservedStackAccess protected final int tryAcquireShared(int unused) { /* * Walkthrough: @@ -479,7 +484,8 @@ firstReaderHoldCount++; } else { HoldCounter rh = cachedHoldCounter; - if (rh == null || rh.tid != getThreadId(current)) + if (rh == null || + rh.tid != LockSupport.getThreadId(current)) cachedHoldCounter = rh = readHolds.get(); else if (rh.count == 0) readHolds.set(rh); @@ -516,7 +522,8 @@ } else { if (rh == null) { rh = cachedHoldCounter; - if (rh == null || rh.tid != getThreadId(current)) { + if (rh == null || + rh.tid != LockSupport.getThreadId(current)) { rh = readHolds.get(); if (rh.count == 0) readHolds.remove(); @@ -537,7 +544,8 @@ } else { if (rh == null) rh = cachedHoldCounter; - if (rh == null || rh.tid != getThreadId(current)) + if (rh == null || + rh.tid != LockSupport.getThreadId(current)) rh = readHolds.get(); else if (rh.count == 0) readHolds.set(rh); @@ -554,6 +562,7 @@ * This is identical in effect to tryAcquire except for lack * of calls to writerShouldBlock. */ + @ReservedStackAccess final boolean tryWriteLock() { Thread current = Thread.currentThread(); int c = getState(); @@ -575,6 +584,7 @@ * This is identical in effect to tryAcquireShared except for * lack of calls to readerShouldBlock. */ + @ReservedStackAccess final boolean tryReadLock() { Thread current = Thread.currentThread(); for (;;) { @@ -593,7 +603,8 @@ firstReaderHoldCount++; } else { HoldCounter rh = cachedHoldCounter; - if (rh == null || rh.tid != getThreadId(current)) + if (rh == null || + rh.tid != LockSupport.getThreadId(current)) cachedHoldCounter = rh = readHolds.get(); else if (rh.count == 0) readHolds.set(rh); @@ -644,7 +655,7 @@ return firstReaderHoldCount; HoldCounter rh = cachedHoldCounter; - if (rh != null && rh.tid == getThreadId(current)) + if (rh != null && rh.tid == LockSupport.getThreadId(current)) return rh.count; int count = readHolds.get().count; @@ -1490,26 +1501,4 @@ "[Write locks = " + w + ", Read locks = " + r + "]"; } - /** - * Returns the thread id for the given thread. We must access - * this directly rather than via method Thread.getId() because - * getId() is not final, and has been known to be overridden in - * ways that do not preserve unique mappings. - */ - static final long getThreadId(Thread thread) { - return U.getLongVolatile(thread, TID); - } - - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long TID; - static { - try { - TID = U.objectFieldOffset - (Thread.class.getDeclaredField("tid")); - } catch (ReflectiveOperationException e) { - throw new Error(e); - } - } - } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,7 +35,10 @@ package java.util.concurrent.locks; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.concurrent.TimeUnit; +import jdk.internal.vm.annotation.ReservedStackAccess; /** * A capability-based lock with three modes for controlling read/write @@ -108,6 +111,10 @@ * into initial unlocked state, so they are not useful for remote * locking. * + *

    Like {@link java.util.concurrent.Semaphore Semaphore}, but unlike most + * {@link Lock} implementations, StampedLocks have no notion of ownership. + * Locks acquired in one thread can be released or converted in another. + * *

    The scheduling policy of StampedLock does not consistently * prefer readers over writers or vice versa. All "try" methods are * best-effort and do not necessarily conform to any scheduling or @@ -126,7 +133,7 @@ * in a class that maintains simple two-dimensional points. The sample * code illustrates some try/catch conventions even though they are * not strictly needed here because no exceptions can occur in their - * bodies.
    + * bodies. * *

     {@code
      * class Point {
    @@ -234,9 +241,7 @@
          * used in the acquire methods to reduce (increasingly expensive)
          * context switching while also avoiding sustained memory
          * thrashing among many threads.  We limit spins to the head of
    -     * queue. A thread spin-waits up to SPINS times (where each
    -     * iteration decreases spin count with 50% probability) before
    -     * blocking. If, upon wakening it fails to obtain lock, and is
    +     * queue. If, upon wakening, a thread fails to obtain lock, and is
          * still (or becomes) the first waiting thread (which indicates
          * that some other thread barged and obtained lock), it escalates
          * spins (up to MAX_HEAD_SPINS) to reduce the likelihood of
    @@ -252,7 +257,7 @@
          * to normal volatile reads (of "state").  To force orderings of
          * reads before a validation and the validation itself in those
          * cases where this is not already forced, we use
    -     * Unsafe.loadFence.
    +     * VarHandle.acquireFence.
          *
          * The memory layout keeps lock state and queue pointers together
          * (normally on the same cache line). This usually works well for
    @@ -290,7 +295,20 @@
         private static final long ABITS = RBITS | WBIT;
         private static final long SBITS = ~RBITS; // note overlap with ABITS
     
    -    // Initial value for lock state; avoid failure value zero
    +    /*
    +     * 3 stamp modes can be distinguished by examining (m = stamp & ABITS):
    +     * write mode: m == WBIT
    +     * optimistic read mode: m == 0L (even when read lock is held)
    +     * read mode: m > 0L && m <= RFULL (the stamp is a copy of state, but the
    +     * read hold count in the stamp is unused other than to determine mode)
    +     *
    +     * This differs slightly from the encoding of state:
    +     * (state & ABITS) == 0L indicates the lock is currently unlocked.
    +     * (state & ABITS) == RBITS is a special transient value
    +     * indicating spin-locked to manipulate reader bits overflow.
    +     */
    +
    +    /** Initial value for lock state; avoids failure value zero. */
         private static final long ORIGIN = WBIT << 1;
     
         // Special value from cancelled acquire methods so caller can throw IE
    @@ -341,25 +359,27 @@
          * Exclusively acquires the lock, blocking if necessary
          * until available.
          *
    -     * @return a stamp that can be used to unlock or convert mode
    +     * @return a write stamp that can be used to unlock or convert mode
          */
    +    @ReservedStackAccess
         public long writeLock() {
             long s, next;  // bypass acquireWrite in fully unlocked case only
             return ((((s = state) & ABITS) == 0L &&
    -                 U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
    +                 STATE.compareAndSet(this, s, next = s + WBIT)) ?
                     next : acquireWrite(false, 0L));
         }
     
         /**
          * Exclusively acquires the lock if it is immediately available.
          *
    -     * @return a stamp that can be used to unlock or convert mode,
    +     * @return a write stamp that can be used to unlock or convert mode,
          * or zero if the lock is not available
          */
    +    @ReservedStackAccess
         public long tryWriteLock() {
             long s, next;
             return ((((s = state) & ABITS) == 0L &&
    -                 U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
    +                 STATE.compareAndSet(this, s, next = s + WBIT)) ?
                     next : 0L);
         }
     
    @@ -371,7 +391,7 @@
          *
          * @param time the maximum time to wait for the lock
          * @param unit the time unit of the {@code time} argument
    -     * @return a stamp that can be used to unlock or convert mode,
    +     * @return a write stamp that can be used to unlock or convert mode,
          * or zero if the lock is not available
          * @throws InterruptedException if the current thread is interrupted
          * before acquiring the lock
    @@ -399,10 +419,11 @@
          * Behavior under interruption matches that specified
          * for method {@link Lock#lockInterruptibly()}.
          *
    -     * @return a stamp that can be used to unlock or convert mode
    +     * @return a write stamp that can be used to unlock or convert mode
          * @throws InterruptedException if the current thread is interrupted
          * before acquiring the lock
          */
    +    @ReservedStackAccess
         public long writeLockInterruptibly() throws InterruptedException {
             long next;
             if (!Thread.interrupted() &&
    @@ -415,33 +436,34 @@
          * Non-exclusively acquires the lock, blocking if necessary
          * until available.
          *
    -     * @return a stamp that can be used to unlock or convert mode
    +     * @return a read stamp that can be used to unlock or convert mode
          */
    +    @ReservedStackAccess
         public long readLock() {
             long s = state, next;  // bypass acquireRead on common uncontended case
             return ((whead == wtail && (s & ABITS) < RFULL &&
    -                 U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) ?
    +                 STATE.compareAndSet(this, s, next = s + RUNIT)) ?
                     next : acquireRead(false, 0L));
         }
     
         /**
          * Non-exclusively acquires the lock if it is immediately available.
          *
    -     * @return a stamp that can be used to unlock or convert mode,
    +     * @return a read stamp that can be used to unlock or convert mode,
          * or zero if the lock is not available
          */
    +    @ReservedStackAccess
         public long tryReadLock() {
    -        for (;;) {
    -            long s, m, next;
    -            if ((m = (s = state) & ABITS) == WBIT)
    -                return 0L;
    -            else if (m < RFULL) {
    -                if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
    +        long s, m, next;
    +        while ((m = (s = state) & ABITS) != WBIT) {
    +            if (m < RFULL) {
    +                if (STATE.compareAndSet(this, s, next = s + RUNIT))
                         return next;
                 }
                 else if ((next = tryIncReaderOverflow(s)) != 0L)
                     return next;
             }
    +        return 0L;
         }
     
         /**
    @@ -452,11 +474,12 @@
          *
          * @param time the maximum time to wait for the lock
          * @param unit the time unit of the {@code time} argument
    -     * @return a stamp that can be used to unlock or convert mode,
    +     * @return a read stamp that can be used to unlock or convert mode,
          * or zero if the lock is not available
          * @throws InterruptedException if the current thread is interrupted
          * before acquiring the lock
          */
    +    @ReservedStackAccess
         public long tryReadLock(long time, TimeUnit unit)
             throws InterruptedException {
             long s, m, next, deadline;
    @@ -464,7 +487,7 @@
             if (!Thread.interrupted()) {
                 if ((m = (s = state) & ABITS) != WBIT) {
                     if (m < RFULL) {
    -                    if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
    +                    if (STATE.compareAndSet(this, s, next = s + RUNIT))
                             return next;
                     }
                     else if ((next = tryIncReaderOverflow(s)) != 0L)
    @@ -486,10 +509,11 @@
          * Behavior under interruption matches that specified
          * for method {@link Lock#lockInterruptibly()}.
          *
    -     * @return a stamp that can be used to unlock or convert mode
    +     * @return a read stamp that can be used to unlock or convert mode
          * @throws InterruptedException if the current thread is interrupted
          * before acquiring the lock
          */
    +    @ReservedStackAccess
         public long readLockInterruptibly() throws InterruptedException {
             long next;
             if (!Thread.interrupted() &&
    @@ -502,7 +526,7 @@
          * Returns a stamp that can later be validated, or zero
          * if exclusively locked.
          *
    -     * @return a stamp, or zero if exclusively locked
    +     * @return a valid optimistic read stamp, or zero if exclusively locked
          */
         public long tryOptimisticRead() {
             long s;
    @@ -522,11 +546,29 @@
          * since issuance of the given stamp; else false
          */
         public boolean validate(long stamp) {
    -        U.loadFence();
    +        VarHandle.acquireFence();
             return (stamp & SBITS) == (state & SBITS);
         }
     
         /**
    +     * Returns an unlocked state, incrementing the version and
    +     * avoiding special failure value 0L.
    +     *
    +     * @param s a write-locked state (or stamp)
    +     */
    +    private static long unlockWriteState(long s) {
    +        return ((s += WBIT) == 0L) ? ORIGIN : s;
    +    }
    +
    +    private long unlockWriteInternal(long s) {
    +        long next; WNode h;
    +        STATE.setVolatile(this, next = unlockWriteState(s));
    +        if ((h = whead) != null && h.status != 0)
    +            release(h);
    +        return next;
    +    }
    +
    +    /**
          * If the lock state matches the given stamp, releases the
          * exclusive lock.
          *
    @@ -534,13 +576,11 @@
          * @throws IllegalMonitorStateException if the stamp does
          * not match the current state of this lock
          */
    +    @ReservedStackAccess
         public void unlockWrite(long stamp) {
    -        WNode h;
             if (state != stamp || (stamp & WBIT) == 0L)
                 throw new IllegalMonitorStateException();
    -        U.putLongVolatile(this, STATE, (stamp += WBIT) == 0L ? ORIGIN : stamp);
    -        if ((h = whead) != null && h.status != 0)
    -            release(h);
    +        unlockWriteInternal(stamp);
         }
     
         /**
    @@ -551,22 +591,23 @@
          * @throws IllegalMonitorStateException if the stamp does
          * not match the current state of this lock
          */
    +    @ReservedStackAccess
         public void unlockRead(long stamp) {
             long s, m; WNode h;
    -        for (;;) {
    -            if (((s = state) & SBITS) != (stamp & SBITS) ||
    -                (stamp & ABITS) == 0L || (m = s & ABITS) == 0L || m == WBIT)
    -                throw new IllegalMonitorStateException();
    +        while (((s = state) & SBITS) == (stamp & SBITS)
    +               && (stamp & RBITS) > 0L
    +               && ((m = s & RBITS) > 0L)) {
                 if (m < RFULL) {
    -                if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
    +                if (STATE.compareAndSet(this, s, s - RUNIT)) {
                         if (m == RUNIT && (h = whead) != null && h.status != 0)
                             release(h);
    -                    break;
    +                    return;
                     }
                 }
                 else if (tryDecReaderOverflow(s) != 0L)
    -                break;
    +                return;
             }
    +        throw new IllegalMonitorStateException();
         }
     
         /**
    @@ -577,32 +618,12 @@
          * @throws IllegalMonitorStateException if the stamp does
          * not match the current state of this lock
          */
    +    @ReservedStackAccess
         public void unlock(long stamp) {
    -        long a = stamp & ABITS, m, s; WNode h;
    -        while (((s = state) & SBITS) == (stamp & SBITS)) {
    -            if ((m = s & ABITS) == 0L)
    -                break;
    -            else if (m == WBIT) {
    -                if (a != m)
    -                    break;
    -                U.putLongVolatile(this, STATE, (s += WBIT) == 0L ? ORIGIN : s);
    -                if ((h = whead) != null && h.status != 0)
    -                    release(h);
    -                return;
    -            }
    -            else if (a == 0L || a >= WBIT)
    -                break;
    -            else if (m < RFULL) {
    -                if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
    -                    if (m == RUNIT && (h = whead) != null && h.status != 0)
    -                        release(h);
    -                    return;
    -                }
    -            }
    -            else if (tryDecReaderOverflow(s) != 0L)
    -                return;
    -        }
    -        throw new IllegalMonitorStateException();
    +        if ((stamp & WBIT) != 0)
    +            unlockWrite(stamp);
    +        else
    +            unlockRead(stamp);
         }
     
         /**
    @@ -623,7 +644,7 @@
                 if ((m = s & ABITS) == 0L) {
                     if (a != 0L)
                         break;
    -                if (U.compareAndSwapLong(this, STATE, s, next = s + WBIT))
    +                if (STATE.compareAndSet(this, s, next = s + WBIT))
                         return next;
                 }
                 else if (m == WBIT) {
    @@ -632,8 +653,7 @@
                     return stamp;
                 }
                 else if (m == RUNIT && a != 0L) {
    -                if (U.compareAndSwapLong(this, STATE, s,
    -                                         next = s - RUNIT + WBIT))
    +                if (STATE.compareAndSet(this, s, next = s - RUNIT + WBIT))
                         return next;
                 }
                 else
    @@ -654,30 +674,32 @@
          * @return a valid read stamp, or zero on failure
          */
         public long tryConvertToReadLock(long stamp) {
    -        long a = stamp & ABITS, m, s, next; WNode h;
    +        long a, s, next; WNode h;
             while (((s = state) & SBITS) == (stamp & SBITS)) {
    -            if ((m = s & ABITS) == 0L) {
    -                if (a != 0L)
    +            if ((a = stamp & ABITS) >= WBIT) {
    +                // write stamp
    +                if (s != stamp)
                         break;
    -                else if (m < RFULL) {
    -                    if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
    +                STATE.setVolatile(this, next = unlockWriteState(s) + RUNIT);
    +                if ((h = whead) != null && h.status != 0)
    +                    release(h);
    +                return next;
    +            }
    +            else if (a == 0L) {
    +                // optimistic read stamp
    +                if ((s & ABITS) < RFULL) {
    +                    if (STATE.compareAndSet(this, s, next = s + RUNIT))
                             return next;
                     }
                     else if ((next = tryIncReaderOverflow(s)) != 0L)
                         return next;
                 }
    -            else if (m == WBIT) {
    -                if (a != m)
    +            else {
    +                // already a read stamp
    +                if ((s & ABITS) == 0L)
                         break;
    -                U.putLongVolatile(this, STATE, next = s + (WBIT + RUNIT));
    -                if ((h = whead) != null && h.status != 0)
    -                    release(h);
    -                return next;
    +                return stamp;
                 }
    -            else if (a != 0L && a < WBIT)
    -                return stamp;
    -            else
    -                break;
             }
             return 0L;
         }
    @@ -693,29 +715,22 @@
          * @return a valid optimistic read stamp, or zero on failure
          */
         public long tryConvertToOptimisticRead(long stamp) {
    -        long a = stamp & ABITS, m, s, next; WNode h;
    -        U.loadFence();
    -        for (;;) {
    -            if (((s = state) & SBITS) != (stamp & SBITS))
    -                break;
    -            if ((m = s & ABITS) == 0L) {
    -                if (a != 0L)
    +        long a, m, s, next; WNode h;
    +        VarHandle.acquireFence();
    +        while (((s = state) & SBITS) == (stamp & SBITS)) {
    +            if ((a = stamp & ABITS) >= WBIT) {
    +                // write stamp
    +                if (s != stamp)
                         break;
    -                return s;
    +                return unlockWriteInternal(s);
                 }
    -            else if (m == WBIT) {
    -                if (a != m)
    -                    break;
    -                U.putLongVolatile(this, STATE,
    -                                  next = (s += WBIT) == 0L ? ORIGIN : s);
    -                if ((h = whead) != null && h.status != 0)
    -                    release(h);
    -                return next;
    -            }
    -            else if (a == 0L || a >= WBIT)
    +            else if (a == 0L)
    +                // already an optimistic read stamp
    +                return stamp;
    +            else if ((m = s & ABITS) == 0L) // invalid read stamp
                     break;
                 else if (m < RFULL) {
    -                if (U.compareAndSwapLong(this, STATE, s, next = s - RUNIT)) {
    +                if (STATE.compareAndSet(this, s, next = s - RUNIT)) {
                         if (m == RUNIT && (h = whead) != null && h.status != 0)
                             release(h);
                         return next & SBITS;
    @@ -734,12 +749,11 @@
          *
          * @return {@code true} if the lock was held, else false
          */
    +    @ReservedStackAccess
         public boolean tryUnlockWrite() {
    -        long s; WNode h;
    +        long s;
             if (((s = state) & WBIT) != 0L) {
    -            U.putLongVolatile(this, STATE, (s += WBIT) == 0L ? ORIGIN : s);
    -            if ((h = whead) != null && h.status != 0)
    -                release(h);
    +            unlockWriteInternal(s);
                 return true;
             }
             return false;
    @@ -752,11 +766,12 @@
          *
          * @return {@code true} if the read lock was held, else false
          */
    +    @ReservedStackAccess
         public boolean tryUnlockRead() {
             long s, m; WNode h;
             while ((m = (s = state) & ABITS) != 0L && m < WBIT) {
                 if (m < RFULL) {
    -                if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
    +                if (STATE.compareAndSet(this, s, s - RUNIT)) {
                         if (m == RUNIT && (h = whead) != null && h.status != 0)
                             release(h);
                         return true;
    @@ -832,32 +847,30 @@
          * Returns a plain {@link Lock} view of this StampedLock in which
          * the {@link Lock#lock} method is mapped to {@link #readLock},
          * and similarly for other methods. The returned Lock does not
    -     * support a {@link Condition}; method {@link
    -     * Lock#newCondition()} throws {@code
    -     * UnsupportedOperationException}.
    +     * support a {@link Condition}; method {@link Lock#newCondition()}
    +     * throws {@code UnsupportedOperationException}.
          *
          * @return the lock
          */
         public Lock asReadLock() {
             ReadLockView v;
    -        return ((v = readLockView) != null ? v :
    -                (readLockView = new ReadLockView()));
    +        if ((v = readLockView) != null) return v;
    +        return readLockView = new ReadLockView();
         }
     
         /**
          * Returns a plain {@link Lock} view of this StampedLock in which
          * the {@link Lock#lock} method is mapped to {@link #writeLock},
          * and similarly for other methods. The returned Lock does not
    -     * support a {@link Condition}; method {@link
    -     * Lock#newCondition()} throws {@code
    -     * UnsupportedOperationException}.
    +     * support a {@link Condition}; method {@link Lock#newCondition()}
    +     * throws {@code UnsupportedOperationException}.
          *
          * @return the lock
          */
         public Lock asWriteLock() {
             WriteLockView v;
    -        return ((v = writeLockView) != null ? v :
    -                (writeLockView = new WriteLockView()));
    +        if ((v = writeLockView) != null) return v;
    +        return writeLockView = new WriteLockView();
         }
     
         /**
    @@ -870,8 +883,8 @@
          */
         public ReadWriteLock asReadWriteLock() {
             ReadWriteLockView v;
    -        return ((v = readWriteLockView) != null ? v :
    -                (readWriteLockView = new ReadWriteLockView()));
    +        if ((v = readWriteLockView) != null) return v;
    +        return readWriteLockView = new ReadWriteLockView();
         }
     
         // view classes
    @@ -917,35 +930,32 @@
         // Needed because view-class lock methods throw away stamps.
     
         final void unstampedUnlockWrite() {
    -        WNode h; long s;
    +        long s;
             if (((s = state) & WBIT) == 0L)
                 throw new IllegalMonitorStateException();
    -        U.putLongVolatile(this, STATE, (s += WBIT) == 0L ? ORIGIN : s);
    -        if ((h = whead) != null && h.status != 0)
    -            release(h);
    +        unlockWriteInternal(s);
         }
     
         final void unstampedUnlockRead() {
    -        for (;;) {
    -            long s, m; WNode h;
    -            if ((m = (s = state) & ABITS) == 0L || m >= WBIT)
    -                throw new IllegalMonitorStateException();
    -            else if (m < RFULL) {
    -                if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
    +        long s, m; WNode h;
    +        while ((m = (s = state) & RBITS) > 0L) {
    +            if (m < RFULL) {
    +                if (STATE.compareAndSet(this, s, s - RUNIT)) {
                         if (m == RUNIT && (h = whead) != null && h.status != 0)
                             release(h);
    -                    break;
    +                    return;
                     }
                 }
                 else if (tryDecReaderOverflow(s) != 0L)
    -                break;
    +                return;
             }
    +        throw new IllegalMonitorStateException();
         }
     
         private void readObject(java.io.ObjectInputStream s)
             throws java.io.IOException, ClassNotFoundException {
             s.defaultReadObject();
    -        U.putLongVolatile(this, STATE, ORIGIN); // reset to unlocked state
    +        STATE.setVolatile(this, ORIGIN); // reset to unlocked state
         }
     
         // internals
    @@ -961,15 +971,16 @@
         private long tryIncReaderOverflow(long s) {
             // assert (s & ABITS) >= RFULL;
             if ((s & ABITS) == RFULL) {
    -            if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
    +            if (STATE.compareAndSet(this, s, s | RBITS)) {
                     ++readerOverflow;
    -                U.putLongVolatile(this, STATE, s);
    +                STATE.setVolatile(this, s);
                     return s;
                 }
             }
    -        else if ((LockSupport.nextSecondarySeed() &
    -                  OVERFLOW_YIELD_RATE) == 0)
    +        else if ((LockSupport.nextSecondarySeed() & OVERFLOW_YIELD_RATE) == 0)
                 Thread.yield();
    +        else
    +            Thread.onSpinWait();
             return 0L;
         }
     
    @@ -982,7 +993,7 @@
         private long tryDecReaderOverflow(long s) {
             // assert (s & ABITS) >= RFULL;
             if ((s & ABITS) == RFULL) {
    -            if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
    +            if (STATE.compareAndSet(this, s, s | RBITS)) {
                     int r; long next;
                     if ((r = readerOverflow) > 0) {
                         readerOverflow = r - 1;
    @@ -990,13 +1001,14 @@
                     }
                     else
                         next = s - RUNIT;
    -                U.putLongVolatile(this, STATE, next);
    +                STATE.setVolatile(this, next);
                     return next;
                 }
             }
    -        else if ((LockSupport.nextSecondarySeed() &
    -                  OVERFLOW_YIELD_RATE) == 0)
    +        else if ((LockSupport.nextSecondarySeed() & OVERFLOW_YIELD_RATE) == 0)
                 Thread.yield();
    +        else
    +            Thread.onSpinWait();
             return 0L;
         }
     
    @@ -1010,14 +1022,14 @@
         private void release(WNode h) {
             if (h != null) {
                 WNode q; Thread w;
    -            U.compareAndSwapInt(h, WSTATUS, WAITING, 0);
    +            WSTATUS.compareAndSet(h, WAITING, 0);
                 if ((q = h.next) == null || q.status == CANCELLED) {
                     for (WNode t = wtail; t != null && t != h; t = t.prev)
                         if (t.status <= 0)
                             q = t;
                 }
                 if (q != null && (w = q.thread) != null)
    -                U.unpark(w);
    +                LockSupport.unpark(w);
             }
         }
     
    @@ -1035,25 +1047,25 @@
             for (int spins = -1;;) { // spin while enqueuing
                 long m, s, ns;
                 if ((m = (s = state) & ABITS) == 0L) {
    -                if (U.compareAndSwapLong(this, STATE, s, ns = s + WBIT))
    +                if (STATE.compareAndSet(this, s, ns = s + WBIT))
                         return ns;
                 }
                 else if (spins < 0)
                     spins = (m == WBIT && wtail == whead) ? SPINS : 0;
                 else if (spins > 0) {
    -                if (LockSupport.nextSecondarySeed() >= 0)
    -                    --spins;
    +                --spins;
    +                Thread.onSpinWait();
                 }
                 else if ((p = wtail) == null) { // initialize queue
                     WNode hd = new WNode(WMODE, null);
    -                if (U.compareAndSwapObject(this, WHEAD, null, hd))
    +                if (WHEAD.weakCompareAndSetVolatile(this, null, hd))
                         wtail = hd;
                 }
                 else if (node == null)
                     node = new WNode(WMODE, p);
                 else if (node.prev != p)
                     node.prev = p;
    -            else if (U.compareAndSwapObject(this, WTAIL, p, node)) {
    +            else if (WTAIL.weakCompareAndSetVolatile(this, p, node)) {
                     p.next = node;
                     break;
                 }
    @@ -1067,11 +1079,10 @@
                         spins = HEAD_SPINS;
                     else if (spins < MAX_HEAD_SPINS)
                         spins <<= 1;
    -                for (int k = spins;;) { // spin at head
    +                for (int k = spins; k > 0; --k) { // spin at head
                         long s, ns;
                         if (((s = state) & ABITS) == 0L) {
    -                        if (U.compareAndSwapLong(this, STATE, s,
    -                                                 ns = s + WBIT)) {
    +                        if (STATE.compareAndSet(this, s, ns = s + WBIT)) {
                                 whead = node;
                                 node.prev = null;
                                 if (wasInterrupted)
    @@ -1079,17 +1090,16 @@
                                 return ns;
                             }
                         }
    -                    else if (LockSupport.nextSecondarySeed() >= 0 &&
    -                             --k <= 0)
    -                        break;
    +                    else
    +                        Thread.onSpinWait();
                     }
                 }
                 else if (h != null) { // help release stale waiters
                     WNode c; Thread w;
                     while ((c = h.cowait) != null) {
    -                    if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
    +                    if (WCOWAIT.weakCompareAndSetVolatile(h, c, c.cowait) &&
                             (w = c.thread) != null)
    -                        U.unpark(w);
    +                        LockSupport.unpark(w);
                     }
                 }
                 if (whead == h) {
    @@ -1098,7 +1108,7 @@
                             (p = np).next = node;   // stale
                     }
                     else if ((ps = p.status) == 0)
    -                    U.compareAndSwapInt(p, WSTATUS, 0, WAITING);
    +                    WSTATUS.compareAndSet(p, 0, WAITING);
                     else if (ps == CANCELLED) {
                         if ((pp = p.prev) != null) {
                             node.prev = pp;
    @@ -1112,13 +1122,15 @@
                         else if ((time = deadline - System.nanoTime()) <= 0L)
                             return cancelWaiter(node, node, false);
                         Thread wt = Thread.currentThread();
    -                    U.putObject(wt, PARKBLOCKER, this);
                         node.thread = wt;
                         if (p.status < 0 && (p != h || (state & ABITS) != 0L) &&
    -                        whead == h && node.prev == p)
    -                        U.park(false, time);  // emulate LockSupport.park
    +                        whead == h && node.prev == p) {
    +                        if (time == 0L)
    +                            LockSupport.park(this);
    +                        else
    +                            LockSupport.parkNanos(this, time);
    +                    }
                         node.thread = null;
    -                    U.putObject(wt, PARKBLOCKER, null);
                         if (Thread.interrupted()) {
                             if (interruptible)
                                 return cancelWaiter(node, node, true);
    @@ -1146,7 +1158,7 @@
                 if ((h = whead) == (p = wtail)) {
                     for (long m, s, ns;;) {
                         if ((m = (s = state) & ABITS) < RFULL ?
    -                        U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
    +                        STATE.compareAndSet(this, s, ns = s + RUNIT) :
                             (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) {
                             if (wasInterrupted)
                                 Thread.currentThread().interrupt();
    @@ -1154,8 +1166,8 @@
                         }
                         else if (m >= WBIT) {
                             if (spins > 0) {
    -                            if (LockSupport.nextSecondarySeed() >= 0)
    -                                --spins;
    +                            --spins;
    +                            Thread.onSpinWait();
                             }
                             else {
                                 if (spins == 0) {
    @@ -1170,7 +1182,7 @@
                 }
                 if (p == null) { // initialize queue
                     WNode hd = new WNode(WMODE, null);
    -                if (U.compareAndSwapObject(this, WHEAD, null, hd))
    +                if (WHEAD.weakCompareAndSetVolatile(this, null, hd))
                         wtail = hd;
                 }
                 else if (node == null)
    @@ -1178,27 +1190,25 @@
                 else if (h == p || p.mode != RMODE) {
                     if (node.prev != p)
                         node.prev = p;
    -                else if (U.compareAndSwapObject(this, WTAIL, p, node)) {
    +                else if (WTAIL.weakCompareAndSetVolatile(this, p, node)) {
                         p.next = node;
                         break;
                     }
                 }
    -            else if (!U.compareAndSwapObject(p, WCOWAIT,
    -                                             node.cowait = p.cowait, node))
    +            else if (!WCOWAIT.compareAndSet(p, node.cowait = p.cowait, node))
                     node.cowait = null;
                 else {
                     for (;;) {
                         WNode pp, c; Thread w;
                         if ((h = whead) != null && (c = h.cowait) != null &&
    -                        U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
    +                        WCOWAIT.compareAndSet(h, c, c.cowait) &&
                             (w = c.thread) != null) // help release
    -                        U.unpark(w);
    +                        LockSupport.unpark(w);
                         if (h == (pp = p.prev) || h == p || pp == null) {
                             long m, s, ns;
                             do {
                                 if ((m = (s = state) & ABITS) < RFULL ?
    -                                U.compareAndSwapLong(this, STATE, s,
    -                                                     ns = s + RUNIT) :
    +                                STATE.compareAndSet(this, s, ns = s + RUNIT) :
                                     (m < WBIT &&
                                      (ns = tryIncReaderOverflow(s)) != 0L)) {
                                     if (wasInterrupted)
    @@ -1221,13 +1231,15 @@
                                 return cancelWaiter(node, p, false);
                             }
                             Thread wt = Thread.currentThread();
    -                        U.putObject(wt, PARKBLOCKER, this);
                             node.thread = wt;
                             if ((h != pp || (state & ABITS) == WBIT) &&
    -                            whead == h && p.prev == pp)
    -                            U.park(false, time);
    +                            whead == h && p.prev == pp) {
    +                            if (time == 0L)
    +                                LockSupport.park(this);
    +                            else
    +                                LockSupport.parkNanos(this, time);
    +                        }
                             node.thread = null;
    -                        U.putObject(wt, PARKBLOCKER, null);
                             if (Thread.interrupted()) {
                                 if (interruptible)
                                     return cancelWaiter(node, p, true);
    @@ -1248,32 +1260,32 @@
                     for (int k = spins;;) { // spin at head
                         long m, s, ns;
                         if ((m = (s = state) & ABITS) < RFULL ?
    -                        U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
    +                        STATE.compareAndSet(this, s, ns = s + RUNIT) :
                             (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) {
                             WNode c; Thread w;
                             whead = node;
                             node.prev = null;
                             while ((c = node.cowait) != null) {
    -                            if (U.compareAndSwapObject(node, WCOWAIT,
    -                                                       c, c.cowait) &&
    +                            if (WCOWAIT.compareAndSet(node, c, c.cowait) &&
                                     (w = c.thread) != null)
    -                                U.unpark(w);
    +                                LockSupport.unpark(w);
                             }
                             if (wasInterrupted)
                                 Thread.currentThread().interrupt();
                             return ns;
                         }
    -                    else if (m >= WBIT &&
    -                             LockSupport.nextSecondarySeed() >= 0 && --k <= 0)
    +                    else if (m >= WBIT && --k <= 0)
                             break;
    +                    else
    +                        Thread.onSpinWait();
                     }
                 }
                 else if (h != null) {
                     WNode c; Thread w;
                     while ((c = h.cowait) != null) {
    -                    if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
    +                    if (WCOWAIT.compareAndSet(h, c, c.cowait) &&
                             (w = c.thread) != null)
    -                        U.unpark(w);
    +                        LockSupport.unpark(w);
                     }
                 }
                 if (whead == h) {
    @@ -1282,7 +1294,7 @@
                             (p = np).next = node;   // stale
                     }
                     else if ((ps = p.status) == 0)
    -                    U.compareAndSwapInt(p, WSTATUS, 0, WAITING);
    +                    WSTATUS.compareAndSet(p, 0, WAITING);
                     else if (ps == CANCELLED) {
                         if ((pp = p.prev) != null) {
                             node.prev = pp;
    @@ -1296,14 +1308,16 @@
                         else if ((time = deadline - System.nanoTime()) <= 0L)
                             return cancelWaiter(node, node, false);
                         Thread wt = Thread.currentThread();
    -                    U.putObject(wt, PARKBLOCKER, this);
                         node.thread = wt;
                         if (p.status < 0 &&
                             (p != h || (state & ABITS) == WBIT) &&
    -                        whead == h && node.prev == p)
    -                        U.park(false, time);
    +                        whead == h && node.prev == p) {
    +                            if (time == 0L)
    +                                LockSupport.park(this);
    +                            else
    +                                LockSupport.parkNanos(this, time);
    +                    }
                         node.thread = null;
    -                    U.putObject(wt, PARKBLOCKER, null);
                         if (Thread.interrupted()) {
                             if (interruptible)
                                 return cancelWaiter(node, node, true);
    @@ -1325,7 +1339,7 @@
          * AbstractQueuedSynchronizer (see its detailed explanation in AQS
          * internal documentation).
          *
    -     * @param node if nonnull, the waiter
    +     * @param node if non-null, the waiter
          * @param group either node or the group node is cowaiting with
          * @param interrupted if already interrupted
          * @return INTERRUPTED if interrupted or Thread.interrupted, else zero
    @@ -1337,7 +1351,7 @@
                 // unsplice cancelled nodes from group
                 for (WNode p = group, q; (q = p.cowait) != null;) {
                     if (q.status == CANCELLED) {
    -                    U.compareAndSwapObject(p, WCOWAIT, q, q.cowait);
    +                    WCOWAIT.compareAndSet(p, q, q.cowait);
                         p = group; // restart
                     }
                     else
    @@ -1346,7 +1360,7 @@
                 if (group == node) {
                     for (WNode r = group.cowait; r != null; r = r.cowait) {
                         if ((w = r.thread) != null)
    -                        U.unpark(w);       // wake up uncancelled co-waiters
    +                        LockSupport.unpark(w); // wake up uncancelled co-waiters
                     }
                     for (WNode pred = node.prev; pred != null; ) { // unsplice
                         WNode succ, pp;        // find valid successor
    @@ -1357,23 +1371,23 @@
                                 if (t.status != CANCELLED)
                                     q = t;     // don't link if succ cancelled
                             if (succ == q ||   // ensure accurate successor
    -                            U.compareAndSwapObject(node, WNEXT,
    -                                                   succ, succ = q)) {
    +                            WNEXT.compareAndSet(node, succ, succ = q)) {
                                 if (succ == null && node == wtail)
    -                                U.compareAndSwapObject(this, WTAIL, node, pred);
    +                                WTAIL.compareAndSet(this, node, pred);
                                 break;
                             }
                         }
                         if (pred.next == node) // unsplice pred link
    -                        U.compareAndSwapObject(pred, WNEXT, node, succ);
    +                        WNEXT.compareAndSet(pred, node, succ);
                         if (succ != null && (w = succ.thread) != null) {
    +                        // wake up succ to observe new pred
                             succ.thread = null;
    -                        U.unpark(w);       // wake up succ to observe new pred
    +                        LockSupport.unpark(w);
                         }
                         if (pred.status != CANCELLED || (pp = pred.prev) == null)
                             break;
                         node.prev = pp;        // repeat if new pred wrong/cancelled
    -                    U.compareAndSwapObject(pp, WNEXT, pred, succ);
    +                    WNEXT.compareAndSet(pp, pred, succ);
                         pred = pp;
                     }
                 }
    @@ -1397,34 +1411,22 @@
             return (interrupted || Thread.interrupted()) ? INTERRUPTED : 0L;
         }
     
    -    // Unsafe mechanics
    -    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
    -    private static final long STATE;
    -    private static final long WHEAD;
    -    private static final long WTAIL;
    -    private static final long WNEXT;
    -    private static final long WSTATUS;
    -    private static final long WCOWAIT;
    -    private static final long PARKBLOCKER;
    -
    +    // VarHandle mechanics
    +    private static final VarHandle STATE;
    +    private static final VarHandle WHEAD;
    +    private static final VarHandle WTAIL;
    +    private static final VarHandle WNEXT;
    +    private static final VarHandle WSTATUS;
    +    private static final VarHandle WCOWAIT;
         static {
             try {
    -            STATE = U.objectFieldOffset
    -                (StampedLock.class.getDeclaredField("state"));
    -            WHEAD = U.objectFieldOffset
    -                (StampedLock.class.getDeclaredField("whead"));
    -            WTAIL = U.objectFieldOffset
    -                (StampedLock.class.getDeclaredField("wtail"));
    -
    -            WSTATUS = U.objectFieldOffset
    -                (WNode.class.getDeclaredField("status"));
    -            WNEXT = U.objectFieldOffset
    -                (WNode.class.getDeclaredField("next"));
    -            WCOWAIT = U.objectFieldOffset
    -                (WNode.class.getDeclaredField("cowait"));
    -
    -            PARKBLOCKER = U.objectFieldOffset
    -                (Thread.class.getDeclaredField("parkBlocker"));
    +            MethodHandles.Lookup l = MethodHandles.lookup();
    +            STATE = l.findVarHandle(StampedLock.class, "state", long.class);
    +            WHEAD = l.findVarHandle(StampedLock.class, "whead", WNode.class);
    +            WTAIL = l.findVarHandle(StampedLock.class, "wtail", WNode.class);
    +            WSTATUS = l.findVarHandle(WNode.class, "status", int.class);
    +            WNEXT = l.findVarHandle(WNode.class, "next", WNode.class);
    +            WCOWAIT = l.findVarHandle(WNode.class, "cowait", WNode.class);
             } catch (ReflectiveOperationException e) {
                 throw new Error(e);
             }
    diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.base/share/classes/java/util/concurrent/package-info.java
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/package-info.java	Fri Jul 15 09:05:36 2016 -0700
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/package-info.java	Mon Jul 18 09:38:08 2016 -0700
    @@ -262,7 +262,6 @@
      *
      * 
* - * * The methods of all classes in {@code java.util.concurrent} and its * subpackages extend these guarantees to higher-level * synchronization. In particular: diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/AsyncConnection.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/AsyncConnection.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/AsyncConnection.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.nio.ByteBuffer; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/AsyncEvent.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/AsyncEvent.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/AsyncEvent.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLConnection.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLConnection.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLConnection.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.io.IOException; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLDelegate.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLDelegate.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLDelegate.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.io.Closeable; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/AuthenticationFilter.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/AuthenticationFilter.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/AuthenticationFilter.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.io.IOException; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/BufferHandler.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/BufferHandler.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/BufferHandler.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/ByteBufferConsumer.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/ByteBufferConsumer.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ByteBufferConsumer.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/ByteBufferGenerator.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/ByteBufferGenerator.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ByteBufferGenerator.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/CharsetToolkit.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/CharsetToolkit.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/CharsetToolkit.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.nio.ByteBuffer; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/ConnectionPool.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/ConnectionPool.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ConnectionPool.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.net.InetSocketAddress; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/ContinuationFrame.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/ContinuationFrame.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ContinuationFrame.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/CookieFilter.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/CookieFilter.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/CookieFilter.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/DataFrame.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/DataFrame.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/DataFrame.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/ErrorFrame.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/ErrorFrame.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ErrorFrame.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/Exchange.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/Exchange.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Exchange.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.io.IOException; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/ExchangeImpl.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/ExchangeImpl.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ExchangeImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.io.IOException; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/ExecutorWrapper.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/ExecutorWrapper.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ExecutorWrapper.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.security.AccessControlContext; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/FilterFactory.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/FilterFactory.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/FilterFactory.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/FrameReader.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/FrameReader.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/FrameReader.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,8 +1,28 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Copyright (c) 2015, 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. */ + package java.net.http; import java.nio.ByteBuffer; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/GoAwayFrame.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/GoAwayFrame.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/GoAwayFrame.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/HeaderFilter.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/HeaderFilter.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HeaderFilter.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/HeaderFrame.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/HeaderFrame.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HeaderFrame.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/HeaderParser.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/HeaderParser.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HeaderParser.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.util.Iterator; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/HeadersFrame.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/HeadersFrame.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HeadersFrame.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/Http1Exchange.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/Http1Exchange.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http1Exchange.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.io.IOException; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/Http1Request.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/Http1Request.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http1Request.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.io.IOException; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/Http1Response.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/Http1Response.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http1Response.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.io.IOException; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/Http2ClientImpl.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/Http2ClientImpl.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http2ClientImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.io.IOException; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/Http2Connection.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/Http2Connection.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http2Connection.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.io.IOException; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/Http2Frame.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/Http2Frame.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http2Frame.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.io.IOException; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/HttpClientBuilderImpl.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpClientBuilderImpl.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpClientBuilderImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/HttpClientImpl.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpClientImpl.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpClientImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import javax.net.ssl.SSLContext; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/HttpConnection.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpConnection.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpConnection.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.io.Closeable; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/HttpHeadersImpl.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpHeadersImpl.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpHeadersImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.util.Collections; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestBuilderImpl.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestBuilderImpl.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestBuilderImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.net.URI; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestImpl.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestImpl.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.io.IOException; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/HttpResponse.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpResponse.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpResponse.java Mon Jul 18 09:38:08 2016 -0700 @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package java.net.http; import java.io.IOException; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/HttpResponseImpl.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpResponseImpl.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpResponseImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -178,7 +178,7 @@ */ RawChannel rawChannel() throws IOException { if (rawchan == null) { - rawchan = new RawChannel(request.client(), connection); + rawchan = new RawChannelImpl(request.client(), connection); } return rawchan; } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/ImmutableHeaders.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/ImmutableHeaders.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ImmutableHeaders.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/Log.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/Log.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Log.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.util.Locale; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/MultiExchange.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/MultiExchange.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/MultiExchange.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/OutgoingHeaders.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/OutgoingHeaders.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/OutgoingHeaders.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/Pair.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/Pair.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Pair.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,20 +1,20 @@ /* - * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 License version 2 only, as + * 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 License + * 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 License version + * 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. * @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package java.net.http; /** diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/PingFrame.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/PingFrame.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/PingFrame.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/PlainHttpConnection.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/PlainHttpConnection.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/PlainHttpConnection.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.io.IOException; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/PlainProxyConnection.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/PlainProxyConnection.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/PlainProxyConnection.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/PlainTunnelingConnection.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/PlainTunnelingConnection.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/PlainTunnelingConnection.java Mon Jul 18 09:38:08 2016 -0700 @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package java.net.http; import java.io.IOException; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/PriorityFrame.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/PriorityFrame.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/PriorityFrame.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/PushPromiseFrame.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/PushPromiseFrame.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/PushPromiseFrame.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/Queue.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/Queue.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Queue.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/RawChannel.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/RawChannel.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/RawChannel.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,151 +20,43 @@ * * 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 java.net.http; import java.io.IOException; import java.nio.ByteBuffer; -import java.nio.channels.ByteChannel; -import java.nio.channels.GatheringByteChannel; -import java.nio.channels.SelectableChannel; -import java.nio.channels.SelectionKey; -import java.nio.channels.SocketChannel; -// -// Used to implement WebSocket. Each RawChannel corresponds to a TCP connection -// (SocketChannel) but is connected to a Selector and an ExecutorService for -// invoking the send and receive callbacks. Also includes SSL processing. -// -final class RawChannel implements ByteChannel, GatheringByteChannel { +/* + * I/O abstraction used to implement WebSocket. + */ +public interface RawChannel { - private final HttpClientImpl client; - private final HttpConnection connection; + interface RawEvent { - private interface RawEvent { - - /** - * must return the selector interest op flags OR'd. + /* + * Must return the selector interest op flags. */ int interestOps(); - /** - * called when event occurs. + /* + * Called when event occurs. */ void handle(); } - interface NonBlockingEvent extends RawEvent { - } - - RawChannel(HttpClientImpl client, HttpConnection connection) - throws IOException { - this.client = client; - this.connection = connection; - SocketChannel chan = connection.channel(); - client.cancelRegistration(chan); - chan.configureBlocking(false); - } - - SocketChannel socketChannel() { - return connection.channel(); - } - - ByteBuffer getRemaining() { - return connection.getRemaining(); - } - - private class RawAsyncEvent extends AsyncEvent { - - private final RawEvent re; - - RawAsyncEvent(RawEvent re) { - super(AsyncEvent.BLOCKING); // BLOCKING & !REPEATING - this.re = re; - } - - RawAsyncEvent(RawEvent re, int flags) { - super(flags); - this.re = re; - } - - @Override - public SelectableChannel channel() { - return connection.channel(); - } - - // must return the selector interest op flags OR'd - @Override - public int interestOps() { - return re.interestOps(); - } - - // called when event occurs - @Override - public void handle() { - re.handle(); - } - - @Override - public void abort() { } - } - - private class NonBlockingRawAsyncEvent extends RawAsyncEvent { - - NonBlockingRawAsyncEvent(RawEvent re) { - super(re, 0); // !BLOCKING & !REPEATING - } - } - /* * Register given event whose callback will be called once only. * (i.e. register new event for each callback) */ - public void registerEvent(RawEvent event) throws IOException { - if (!(event instanceof NonBlockingEvent)) { - throw new InternalError(); - } - if ((event.interestOps() & SelectionKey.OP_READ) != 0 - && connection.buffer.hasRemaining()) { - // FIXME: a hack to deal with leftovers from previous reads into an - // internal buffer (works in conjunction with change in - // java.net.http.PlainHttpConnection.readImpl(java.nio.ByteBuffer) - connection.channel().configureBlocking(false); - event.handle(); - } else { - client.registerEvent(new NonBlockingRawAsyncEvent(event)); - } - } + void registerEvent(RawEvent event) throws IOException; - @Override - public int read(ByteBuffer dst) throws IOException { - assert !connection.channel().isBlocking(); - return connection.read(dst); - } + int read(ByteBuffer dst) throws IOException; - @Override - public boolean isOpen() { - return connection.isOpen(); - } - - @Override - public void close() throws IOException { - connection.close(); - } + long write(ByteBuffer[] src, int offset, int len) throws IOException; - @Override - public long write(ByteBuffer[] src) throws IOException { - return connection.write(src, 0, src.length); - } + boolean isOpen(); - @Override - public long write(ByteBuffer[] src, int offset, int len) - throws IOException { - return connection.write(src, offset, len); - } - - @Override - public int write(ByteBuffer src) throws IOException { - return (int) connection.write(src); - } + void close() throws IOException; } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/RawChannelImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/RawChannelImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2015, 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. + */ + +package java.net.http; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.SelectableChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.SocketChannel; + +/* + * Each RawChannel corresponds to a TCP connection (SocketChannel) but is + * connected to a Selector and an ExecutorService for invoking the send and + * receive callbacks. Also includes SSL processing. + */ +final class RawChannelImpl implements RawChannel { + + private final HttpClientImpl client; + private final HttpConnection connection; + + RawChannelImpl(HttpClientImpl client, HttpConnection connection) + throws IOException { + this.client = client; + this.connection = connection; + SocketChannel chan = connection.channel(); + client.cancelRegistration(chan); + chan.configureBlocking(false); + } + + private class NonBlockingRawAsyncEvent extends AsyncEvent { + + private final RawEvent re; + + NonBlockingRawAsyncEvent(RawEvent re) { + super(0); // !BLOCKING & !REPEATING + this.re = re; + } + + @Override + public SelectableChannel channel() { + return connection.channel(); + } + + @Override + public int interestOps() { + return re.interestOps(); + } + + @Override + public void handle() { + re.handle(); + } + + @Override + public void abort() { } + } + + @Override + public void registerEvent(RawEvent event) throws IOException { + if ((event.interestOps() & SelectionKey.OP_READ) != 0 + && connection.buffer.hasRemaining()) { + // FIXME: a hack to deal with leftovers from previous reads into an + // internal buffer (works in conjunction with change in + // java.net.http.PlainHttpConnection.readImpl(java.nio.ByteBuffer) + connection.channel().configureBlocking(false); + event.handle(); + } else { + client.registerEvent(new NonBlockingRawAsyncEvent(event)); + } + } + + @Override + public int read(ByteBuffer dst) throws IOException { + assert !connection.channel().isBlocking(); + return connection.read(dst); + } + + @Override + public long write(ByteBuffer[] src, int offset, int len) throws IOException { + return connection.write(src, offset, len); + } + + @Override + public boolean isOpen() { + return connection.isOpen(); + } + + @Override + public void close() throws IOException { + connection.close(); + } +} diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/RedirectFilter.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/RedirectFilter.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/RedirectFilter.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/ResetFrame.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/ResetFrame.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ResetFrame.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/ResponseContent.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/ResponseContent.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ResponseContent.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.io.IOException; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/ResponseHeaders.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/ResponseHeaders.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ResponseHeaders.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.io.IOException; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/SSLConnection.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/SSLConnection.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/SSLConnection.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.io.IOException; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/SSLDelegate.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/SSLDelegate.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/SSLDelegate.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.io.IOException; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/SSLTunnelConnection.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/SSLTunnelConnection.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/SSLTunnelConnection.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.io.IOException; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/SettingsFrame.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/SettingsFrame.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/SettingsFrame.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/Stream.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/Stream.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Stream.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/TimeoutEvent.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/TimeoutEvent.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/TimeoutEvent.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/Utils.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/Utils.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Utils.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,7 +20,9 @@ * * 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 java.net.http; import sun.net.NetProperties; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/WS.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/WS.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WS.java Mon Jul 18 09:38:08 2016 -0700 @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package java.net.http; import java.io.IOException; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/WSBuilder.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSBuilder.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSBuilder.java Mon Jul 18 09:38:08 2016 -0700 @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package java.net.http; import java.net.URI; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/WSCharsetToolkit.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSCharsetToolkit.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSCharsetToolkit.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -20,7 +20,9 @@ * * 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 java.net.http; import java.nio.ByteBuffer; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/WSDisposable.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSDisposable.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSDisposable.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,20 +1,20 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 License version 2 only, as + * 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 License + * 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 License version + * 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. * @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package java.net.http; interface WSDisposable { diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/WSFrame.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSFrame.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSFrame.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,20 +1,20 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 License version 2 only, as + * 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 License + * 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 License version + * 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. * @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package java.net.http; import java.nio.ByteBuffer; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/WSFrameConsumer.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSFrameConsumer.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSFrameConsumer.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package java.net.http; import java.net.http.WSFrame.Opcode; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/WSMessageConsumer.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSMessageConsumer.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSMessageConsumer.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package java.net.http; import java.net.http.WebSocket.CloseCode; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/WSMessageSender.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSMessageSender.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSMessageSender.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,20 +1,20 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 License version 2 only, as + * 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 License + * 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 License version + * 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. * @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package java.net.http; import java.net.http.WSFrame.HeaderBuilder; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/WSOpeningHandshake.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSOpeningHandshake.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSOpeningHandshake.java Mon Jul 18 09:38:08 2016 -0700 @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package java.net.http; import java.io.UncheckedIOException; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/WSOutgoingMessage.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSOutgoingMessage.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSOutgoingMessage.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,20 +1,20 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 License version 2 only, as + * 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 License + * 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 License version + * 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. * @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package java.net.http; import java.nio.ByteBuffer; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/WSProtocolException.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSProtocolException.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSProtocolException.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,3 +1,28 @@ +/* + * Copyright (c) 2015, 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. + */ + package java.net.http; import java.net.http.WebSocket.CloseCode; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/WSReceiver.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSReceiver.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSReceiver.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package java.net.http; import java.io.IOException; @@ -53,7 +54,7 @@ private final Supplier> buffersSupplier = new WSSharedPool<>(() -> ByteBuffer.allocateDirect(32768), 2); private final RawChannel channel; - private final RawChannel.NonBlockingEvent channelEvent; + private final RawChannel.RawEvent channelEvent; private final WSSignalHandler handler; private final AtomicLong demand = new AtomicLong(); private final AtomicBoolean readable = new AtomicBoolean(); @@ -251,8 +252,8 @@ assert newDemand >= 0 : newDemand; } - private RawChannel.NonBlockingEvent createChannelEvent() { - return new RawChannel.NonBlockingEvent() { + private RawChannel.RawEvent createChannelEvent() { + return new RawChannel.RawEvent() { @Override public int interestOps() { diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/WSShared.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSShared.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSShared.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,20 +1,20 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 License version 2 only, as + * 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 License + * 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 License version + * 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. * @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package java.net.http; import java.nio.Buffer; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/WSSharedPool.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSSharedPool.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSSharedPool.java Mon Jul 18 09:38:08 2016 -0700 @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package java.net.http; import java.nio.Buffer; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/WSSignalHandler.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSSignalHandler.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSSignalHandler.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,20 +1,20 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 License version 2 only, as + * 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 License + * 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 License version + * 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. * @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package java.net.http; import java.util.concurrent.Executor; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/WSTransmitter.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSTransmitter.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSTransmitter.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,20 +1,20 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 License version 2 only, as + * 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 License + * 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 License version + * 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. * @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package java.net.http; import java.net.http.WSOutgoingMessage.Binary; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/WSUtils.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSUtils.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSUtils.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,20 +1,20 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 License version 2 only, as + * 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 License + * 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 License version + * 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. * @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package java.net.http; import java.net.URI; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/WSWriter.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSWriter.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSWriter.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,20 +1,20 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 License version 2 only, as + * 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 License + * 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 License version + * 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. * @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package java.net.http; import java.io.IOException; @@ -60,7 +61,7 @@ final class WSWriter { private final RawChannel channel; - private final RawChannel.NonBlockingEvent writeReadinessHandler; + private final RawChannel.RawEvent writeReadinessHandler; private final Consumer completionCallback; private ByteBuffer[] buffers; private int offset; @@ -110,8 +111,8 @@ return -1; } - private RawChannel.NonBlockingEvent createHandler() { - return new RawChannel.NonBlockingEvent() { + private RawChannel.RawEvent createHandler() { + return new RawChannel.RawEvent() { @Override public int interestOps() { diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/WebSocket.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/WebSocket.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WebSocket.java Mon Jul 18 09:38:08 2016 -0700 @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package java.net.http; import java.io.IOException; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/WebSocketHandshakeException.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/WebSocketHandshakeException.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WebSocketHandshakeException.java Mon Jul 18 09:38:08 2016 -0700 @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package java.net.http; /** diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/java/net/http/WindowUpdateFrame.java --- a/jdk/src/java.httpclient/share/classes/java/net/http/WindowUpdateFrame.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WindowUpdateFrame.java Mon Jul 18 09:38:08 2016 -0700 @@ -20,6 +20,7 @@ * * 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 java.net.http; diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/java.httpclient/share/classes/module-info.java --- a/jdk/src/java.httpclient/share/classes/module-info.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/java.httpclient/share/classes/module-info.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 -r 8bc25e077e83 -r f5d65fcf55e4 jdk/src/jdk.httpserver/share/classes/com/sun/net/httpserver/package-info.java --- a/jdk/src/jdk.httpserver/share/classes/com/sun/net/httpserver/package-info.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/src/jdk.httpserver/share/classes/com/sun/net/httpserver/package-info.java Mon Jul 18 09:38:08 2016 -0700 @@ -54,7 +54,7 @@ } ... - HttpServer server = HttpServer.create(new InetSocketAddress(8000)); + HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0); server.createContext("/applications/myapp", new MyHandler()); server.setExecutor(null); // creates a default executor server.start(); diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/test/java/lang/ProcessBuilder/Zombies.java --- a/jdk/test/java/lang/ProcessBuilder/Zombies.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/test/java/lang/ProcessBuilder/Zombies.java Mon Jul 18 09:38:08 2016 -0700 @@ -24,6 +24,7 @@ /* * @test * @bug 6474073 + * @key intermittent * @summary Make sure zombies don't get created on Unix * @author Martin Buchholz */ diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/test/java/lang/invoke/LoopCombinatorLongSignatureTest.java --- a/jdk/test/java/lang/invoke/LoopCombinatorLongSignatureTest.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/test/java/lang/invoke/LoopCombinatorLongSignatureTest.java Mon Jul 18 09:38:08 2016 -0700 @@ -39,6 +39,11 @@ * If a loop with an excessive amount of clauses is created, so that the number of parameters to the resulting loop * handle exceeds the allowed maximum, an IAE must be signalled. The test is run first in LambdaForm interpretation mode * and then in default mode, wherein bytecode generation falls back to LFI mode due to excessively long methods. + *

+ * By default, the test run only checks whether loop handle construction succeeds and fails. If executing the generated + * loops is desired, this should be indicated by setting the {@code java.lang.invoke.LoopCombinatorLongSignatureTest.RUN} + * environment variable to {@code true}. This is disabled by default as it considerably increases the time needed to run + * the test. */ public class LoopCombinatorLongSignatureTest { @@ -51,13 +56,15 @@ static final int ARG_LIMIT = 254; // for internal reasons, this is the maximum allowed number of arguments public static void main(String[] args) { + boolean run = Boolean.parseBoolean( + System.getProperty("java.lang.invoke.LoopCombinatorLongSignatureTest.RUN", "false")); for (int loopArgs = 0; loopArgs < 2; ++loopArgs) { - testLongSignature(loopArgs, false); - testLongSignature(loopArgs, true); + testLongSignature(loopArgs, false, run); + testLongSignature(loopArgs, true, run); } } - static void testLongSignature(int loopArgs, boolean excessive) { + static void testLongSignature(int loopArgs, boolean excessive, boolean run) { int nClauses = ARG_LIMIT - loopArgs + (excessive ? 1 : 0); System.out.print((excessive ? "(EXCESSIVE)" : "(LONG )") + " arguments: " + loopArgs + ", clauses: " + nClauses + " -> "); @@ -78,7 +85,7 @@ MethodHandle loop = MethodHandles.loop(clauses); if (excessive) { throw new AssertionError("loop construction should have failed"); - } else { + } else if (run) { int r; if (loopArgs == 0) { r = (int) loop.invoke(); @@ -88,6 +95,8 @@ r = (int) loop.invokeWithArguments(args); } System.out.println("SUCCEEDED (OK) -> " + r); + } else { + System.out.println("SUCCEEDED (OK)"); } } catch (IllegalArgumentException iae) { if (excessive) { diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/test/java/net/httpclient/whitebox/java.httpclient/java/net/http/SelectorTest.java --- a/jdk/test/java/net/httpclient/whitebox/java.httpclient/java/net/http/SelectorTest.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/test/java/net/httpclient/whitebox/java.httpclient/java/net/http/SelectorTest.java Mon Jul 18 09:38:08 2016 -0700 @@ -78,7 +78,7 @@ final RawChannel chan = getARawChannel(port); - chan.registerEvent(new RawChannel.NonBlockingEvent() { + chan.registerEvent(new RawChannel.RawEvent() { @Override public int interestOps() { return SelectionKey.OP_READ; @@ -95,7 +95,7 @@ } }); - chan.registerEvent(new RawChannel.NonBlockingEvent() { + chan.registerEvent(new RawChannel.RawEvent() { @Override public int interestOps() { return SelectionKey.OP_WRITE; @@ -111,7 +111,7 @@ ByteBuffer bb = ByteBuffer.wrap(TestServer.INPUT); counter.incrementAndGet(); try { - chan.write(bb); + chan.write(new ByteBuffer[]{bb}, 0, 1); } catch (IOException e) { throw new UncheckedIOException(e); } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/test/java/nio/file/WatchService/DeleteInterference.java --- a/jdk/test/java/nio/file/WatchService/DeleteInterference.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/test/java/nio/file/WatchService/DeleteInterference.java Mon Jul 18 09:38:08 2016 -0700 @@ -32,6 +32,7 @@ import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.WatchService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -49,7 +50,8 @@ * directory. */ public static void main(String[] args) throws Exception { - Path dir = Files.createTempDirectory("DeleteInterference"); + Path testDir = Paths.get(System.getProperty("test.dir", ".")); + Path dir = Files.createTempDirectory(testDir, "DeleteInterference"); ExecutorService pool = Executors.newCachedThreadPool(); try { Future task1 = pool.submit(() -> openAndCloseWatcher(dir)); @@ -58,7 +60,6 @@ task2.get(); } finally { pool.shutdown(); - deleteFileTree(dir); } } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/test/java/nio/file/WatchService/LotsOfCancels.java --- a/jdk/test/java/nio/file/WatchService/LotsOfCancels.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/test/java/nio/file/WatchService/LotsOfCancels.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -27,11 +27,11 @@ * an outstanding I/O operation on directory completes after the * directory has been closed */ - import java.nio.file.ClosedWatchServiceException; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.WatchKey; import java.nio.file.WatchService; import static java.nio.file.StandardWatchEventKinds.*; @@ -50,8 +50,8 @@ // one to bash on cancel, the other to poll the events ExecutorService pool = Executors.newCachedThreadPool(); try { - Path top = Files.createTempDirectory("LotsOfCancels"); - top.toFile().deleteOnExit(); + Path testDir = Paths.get(System.getProperty("test.dir", ".")); + Path top = Files.createTempDirectory(testDir, "LotsOfCancels"); for (int i=1; i<=16; i++) { Path dir = Files.createDirectory(top.resolve("dir-" + i)); WatchService watcher = FileSystems.getDefault().newWatchService(); @@ -114,6 +114,4 @@ failed = true; } } - } - diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/test/java/util/concurrent/tck/Atomic8Test.java --- a/jdk/test/java/util/concurrent/tck/Atomic8Test.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/test/java/util/concurrent/tck/Atomic8Test.java Mon Jul 18 09:38:08 2016 -0700 @@ -179,7 +179,7 @@ * result of supplied function */ public void testReferenceGetAndUpdate() { - AtomicReference a = new AtomicReference(one); + AtomicReference a = new AtomicReference<>(one); assertEquals(new Integer(1), a.getAndUpdate(Atomic8Test::addInteger17)); assertEquals(new Integer(18), a.getAndUpdate(Atomic8Test::addInteger17)); assertEquals(new Integer(35), a.get()); @@ -190,7 +190,7 @@ * returns result. */ public void testReferenceUpdateAndGet() { - AtomicReference a = new AtomicReference(one); + AtomicReference a = new AtomicReference<>(one); assertEquals(new Integer(18), a.updateAndGet(Atomic8Test::addInteger17)); assertEquals(new Integer(35), a.updateAndGet(Atomic8Test::addInteger17)); assertEquals(new Integer(35), a.get()); @@ -201,7 +201,7 @@ * with supplied function. */ public void testReferenceGetAndAccumulate() { - AtomicReference a = new AtomicReference(one); + AtomicReference a = new AtomicReference<>(one); assertEquals(new Integer(1), a.getAndAccumulate(2, Atomic8Test::sumInteger)); assertEquals(new Integer(3), a.getAndAccumulate(3, Atomic8Test::sumInteger)); assertEquals(new Integer(6), a.get()); @@ -212,7 +212,7 @@ * returns result. */ public void testReferenceAccumulateAndGet() { - AtomicReference a = new AtomicReference(one); + AtomicReference a = new AtomicReference<>(one); assertEquals(new Integer(7), a.accumulateAndGet(6, Atomic8Test::sumInteger)); assertEquals(new Integer(10), a.accumulateAndGet(3, Atomic8Test::sumInteger)); assertEquals(new Integer(10), a.get()); diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/test/java/util/concurrent/tck/AtomicBoolean9Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/concurrent/tck/AtomicBoolean9Test.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,203 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.concurrent.atomic.AtomicBoolean; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicBoolean9Test extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicBoolean9Test.class); + } + + /** + * getPlain returns the last value set + */ + public void testGetPlainSet() { + AtomicBoolean ai = new AtomicBoolean(true); + assertEquals(true, ai.getPlain()); + ai.set(false); + assertEquals(false, ai.getPlain()); + ai.set(true); + assertEquals(true, ai.getPlain()); + } + + /** + * getOpaque returns the last value set + */ + public void testGetOpaqueSet() { + AtomicBoolean ai = new AtomicBoolean(true); + assertEquals(true, ai.getOpaque()); + ai.set(false); + assertEquals(false, ai.getOpaque()); + ai.set(true); + assertEquals(true, ai.getOpaque()); + } + + /** + * getAcquire returns the last value set + */ + public void testGetAcquireSet() { + AtomicBoolean ai = new AtomicBoolean(true); + assertEquals(true, ai.getAcquire()); + ai.set(false); + assertEquals(false, ai.getAcquire()); + ai.set(true); + assertEquals(true, ai.getAcquire()); + } + + /** + * get returns the last value setPlain + */ + public void testGetSetPlain() { + AtomicBoolean ai = new AtomicBoolean(true); + assertEquals(true, ai.get()); + ai.setPlain(false); + assertEquals(false, ai.get()); + ai.setPlain(true); + assertEquals(true, ai.get()); + } + + /** + * get returns the last value setOpaque + */ + public void testGetSetOpaque() { + AtomicBoolean ai = new AtomicBoolean(true); + assertEquals(true, ai.get()); + ai.setOpaque(false); + assertEquals(false, ai.get()); + ai.setOpaque(true); + assertEquals(true, ai.get()); + } + + /** + * get returns the last value setRelease + */ + public void testGetSetRelease() { + AtomicBoolean ai = new AtomicBoolean(true); + assertEquals(true, ai.get()); + ai.setRelease(false); + assertEquals(false, ai.get()); + ai.setRelease(true); + assertEquals(true, ai.get()); + } + + /** + * compareAndExchange succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchange() { + AtomicBoolean ai = new AtomicBoolean(true); + assertEquals(true, ai.compareAndExchange(true, false)); + assertEquals(false, ai.compareAndExchange(false, false)); + assertEquals(false, ai.get()); + assertEquals(false, ai.compareAndExchange(true, true)); + assertEquals(false, ai.get()); + assertEquals(false, ai.compareAndExchange(false, true)); + assertEquals(true, ai.get()); + } + + /** + * compareAndExchangeAcquire succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeAcquire() { + AtomicBoolean ai = new AtomicBoolean(true); + assertEquals(true, ai.compareAndExchangeAcquire(true, false)); + assertEquals(false, ai.compareAndExchangeAcquire(false, false)); + assertEquals(false, ai.get()); + assertEquals(false, ai.compareAndExchangeAcquire(true, true)); + assertEquals(false, ai.get()); + assertEquals(false, ai.compareAndExchangeAcquire(false, true)); + assertEquals(true, ai.get()); + } + + /** + * compareAndExchangeRelease succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeRelease() { + AtomicBoolean ai = new AtomicBoolean(true); + assertEquals(true, ai.compareAndExchangeRelease(true, false)); + assertEquals(false, ai.compareAndExchangeRelease(false, false)); + assertEquals(false, ai.get()); + assertEquals(false, ai.compareAndExchangeRelease(true, true)); + assertEquals(false, ai.get()); + assertEquals(false, ai.compareAndExchangeRelease(false, true)); + assertEquals(true, ai.get()); + } + + /** + * repeated weakCompareAndSetVolatile succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetVolatile() { + AtomicBoolean ai = new AtomicBoolean(true); + do {} while (!ai.weakCompareAndSetVolatile(true, false)); + do {} while (!ai.weakCompareAndSetVolatile(false, false)); + assertEquals(false, ai.get()); + do {} while (!ai.weakCompareAndSetVolatile(false, true)); + assertEquals(true, ai.get()); + } + + /** + * repeated weakCompareAndSetAcquire succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetAcquire() { + AtomicBoolean ai = new AtomicBoolean(true); + do {} while (!ai.weakCompareAndSetAcquire(true, false)); + do {} while (!ai.weakCompareAndSetAcquire(false, false)); + assertEquals(false, ai.get()); + do {} while (!ai.weakCompareAndSetAcquire(false, true)); + assertEquals(true, ai.get()); + } + + /** + * repeated weakCompareAndSetRelease succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetRelease() { + AtomicBoolean ai = new AtomicBoolean(true); + do {} while (!ai.weakCompareAndSetRelease(true, false)); + do {} while (!ai.weakCompareAndSetRelease(false, false)); + assertEquals(false, ai.get()); + do {} while (!ai.weakCompareAndSetRelease(false, true)); + assertEquals(true, ai.get()); + } + +} diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/test/java/util/concurrent/tck/AtomicInteger9Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/concurrent/tck/AtomicInteger9Test.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,203 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.concurrent.atomic.AtomicInteger; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicInteger9Test extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicInteger9Test.class); + } + + /** + * getPlain returns the last value set + */ + public void testGetPlainSet() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.getPlain()); + ai.set(2); + assertEquals(2, ai.getPlain()); + ai.set(-3); + assertEquals(-3, ai.getPlain()); + } + + /** + * getOpaque returns the last value set + */ + public void testGetOpaqueSet() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.getOpaque()); + ai.set(2); + assertEquals(2, ai.getOpaque()); + ai.set(-3); + assertEquals(-3, ai.getOpaque()); + } + + /** + * getAcquire returns the last value set + */ + public void testGetAcquireSet() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.getAcquire()); + ai.set(2); + assertEquals(2, ai.getAcquire()); + ai.set(-3); + assertEquals(-3, ai.getAcquire()); + } + + /** + * get returns the last value setPlain + */ + public void testGetSetPlain() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.get()); + ai.setPlain(2); + assertEquals(2, ai.get()); + ai.setPlain(-3); + assertEquals(-3, ai.get()); + } + + /** + * get returns the last value setOpaque + */ + public void testGetSetOpaque() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.get()); + ai.setOpaque(2); + assertEquals(2, ai.get()); + ai.setOpaque(-3); + assertEquals(-3, ai.get()); + } + + /** + * get returns the last value setRelease + */ + public void testGetSetRelease() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.get()); + ai.setRelease(2); + assertEquals(2, ai.get()); + ai.setRelease(-3); + assertEquals(-3, ai.get()); + } + + /** + * compareAndExchange succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchange() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.compareAndExchange(1, 2)); + assertEquals(2, ai.compareAndExchange(2, -4)); + assertEquals(-4, ai.get()); + assertEquals(-4, ai.compareAndExchange(-5, 7)); + assertEquals(-4, ai.get()); + assertEquals(-4, ai.compareAndExchange(-4, 7)); + assertEquals(7, ai.get()); + } + + /** + * compareAndExchangeAcquire succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeAcquire() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.compareAndExchangeAcquire(1, 2)); + assertEquals(2, ai.compareAndExchangeAcquire(2, -4)); + assertEquals(-4, ai.get()); + assertEquals(-4, ai.compareAndExchangeAcquire(-5, 7)); + assertEquals(-4, ai.get()); + assertEquals(-4, ai.compareAndExchangeAcquire(-4, 7)); + assertEquals(7, ai.get()); + } + + /** + * compareAndExchangeRelease succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeRelease() { + AtomicInteger ai = new AtomicInteger(1); + assertEquals(1, ai.compareAndExchangeRelease(1, 2)); + assertEquals(2, ai.compareAndExchangeRelease(2, -4)); + assertEquals(-4, ai.get()); + assertEquals(-4, ai.compareAndExchangeRelease(-5, 7)); + assertEquals(-4, ai.get()); + assertEquals(-4, ai.compareAndExchangeRelease(-4, 7)); + assertEquals(7, ai.get()); + } + + /** + * repeated weakCompareAndSetVolatile succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetVolatile() { + AtomicInteger ai = new AtomicInteger(1); + do {} while (!ai.weakCompareAndSetVolatile(1, 2)); + do {} while (!ai.weakCompareAndSetVolatile(2, -4)); + assertEquals(-4, ai.get()); + do {} while (!ai.weakCompareAndSetVolatile(-4, 7)); + assertEquals(7, ai.get()); + } + + /** + * repeated weakCompareAndSetAcquire succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetAcquire() { + AtomicInteger ai = new AtomicInteger(1); + do {} while (!ai.weakCompareAndSetAcquire(1, 2)); + do {} while (!ai.weakCompareAndSetAcquire(2, -4)); + assertEquals(-4, ai.get()); + do {} while (!ai.weakCompareAndSetAcquire(-4, 7)); + assertEquals(7, ai.get()); + } + + /** + * repeated weakCompareAndSetRelease succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetRelease() { + AtomicInteger ai = new AtomicInteger(1); + do {} while (!ai.weakCompareAndSetRelease(1, 2)); + do {} while (!ai.weakCompareAndSetRelease(2, -4)); + assertEquals(-4, ai.get()); + do {} while (!ai.weakCompareAndSetRelease(-4, 7)); + assertEquals(7, ai.get()); + } + +} diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/test/java/util/concurrent/tck/AtomicIntegerArray9Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/concurrent/tck/AtomicIntegerArray9Test.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,267 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicIntegerArray; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicIntegerArray9Test extends JSR166TestCase { + + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicIntegerArray9Test.class); + } + + /** + * get and set for out of bound indices throw IndexOutOfBoundsException + */ + public void testIndexing() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int index : new int[] { -1, SIZE }) { + final int j = index; + final Runnable[] tasks = { + () -> aa.getPlain(j), + () -> aa.getOpaque(j), + () -> aa.getAcquire(j), + () -> aa.setPlain(j, 1), + () -> aa.setOpaque(j, 1), + () -> aa.setRelease(j, 1), + () -> aa.compareAndExchange(j, 1, 2), + () -> aa.compareAndExchangeAcquire(j, 1, 2), + () -> aa.compareAndExchangeRelease(j, 1, 2), + () -> aa.weakCompareAndSetVolatile(j, 1, 2), + () -> aa.weakCompareAndSetAcquire(j, 1, 2), + () -> aa.weakCompareAndSetRelease(j, 1, 2), + }; + + assertThrows(IndexOutOfBoundsException.class, tasks); + } + } + + /** + * getPlain returns the last value set + */ + public void testGetPlainSet() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.getPlain(i)); + aa.set(i, 2); + assertEquals(2, aa.getPlain(i)); + aa.set(i, -3); + assertEquals(-3, aa.getPlain(i)); + } + } + + /** + * getOpaque returns the last value set + */ + public void testGetOpaqueSet() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.getOpaque(i)); + aa.set(i, 2); + assertEquals(2, aa.getOpaque(i)); + aa.set(i, -3); + assertEquals(-3, aa.getOpaque(i)); + } + } + + /** + * getAcquire returns the last value set + */ + public void testGetAcquireSet() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.getAcquire(i)); + aa.set(i, 2); + assertEquals(2, aa.getAcquire(i)); + aa.set(i, -3); + assertEquals(-3, aa.getAcquire(i)); + } + } + + /** + * get returns the last value setPlain + */ + public void testGetSetPlain() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.setPlain(i, 1); + assertEquals(1, aa.get(i)); + aa.setPlain(i, 2); + assertEquals(2, aa.get(i)); + aa.setPlain(i, -3); + assertEquals(-3, aa.get(i)); + } + } + + /** + * get returns the last value setOpaque + */ + public void testGetSetOpaque() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.setOpaque(i, 1); + assertEquals(1, aa.get(i)); + aa.setOpaque(i, 2); + assertEquals(2, aa.get(i)); + aa.setOpaque(i, -3); + assertEquals(-3, aa.get(i)); + } + } + + /** + * get returns the last value setRelease + */ + public void testGetSetRelease() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.setRelease(i, 1); + assertEquals(1, aa.get(i)); + aa.setRelease(i, 2); + assertEquals(2, aa.get(i)); + aa.setRelease(i, -3); + assertEquals(-3, aa.get(i)); + } + } + + /** + * compareAndExchange succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchange() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.compareAndExchange(i, 1, 2)); + assertEquals(2, aa.compareAndExchange(i, 2, -4)); + assertEquals(-4, aa.get(i)); + assertEquals(-4, aa.compareAndExchange(i,-5, 7)); + assertEquals(-4, aa.get(i)); + assertEquals(-4, aa.compareAndExchange(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + + /** + * compareAndExchangeAcquire succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeAcquire() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.compareAndExchangeAcquire(i, 1, 2)); + assertEquals(2, aa.compareAndExchangeAcquire(i, 2, -4)); + assertEquals(-4, aa.get(i)); + assertEquals(-4, aa.compareAndExchangeAcquire(i,-5, 7)); + assertEquals(-4, aa.get(i)); + assertEquals(-4, aa.compareAndExchangeAcquire(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + + /** + * compareAndExchangeRelease succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeRelease() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.compareAndExchangeRelease(i, 1, 2)); + assertEquals(2, aa.compareAndExchangeRelease(i, 2, -4)); + assertEquals(-4, aa.get(i)); + assertEquals(-4, aa.compareAndExchangeRelease(i,-5, 7)); + assertEquals(-4, aa.get(i)); + assertEquals(-4, aa.compareAndExchangeRelease(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + + /** + * repeated weakCompareAndSetVolatile succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetVolatile() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + do {} while (!aa.weakCompareAndSetVolatile(i, 1, 2)); + do {} while (!aa.weakCompareAndSetVolatile(i, 2, -4)); + assertEquals(-4, aa.get(i)); + do {} while (!aa.weakCompareAndSetVolatile(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + + /** + * repeated weakCompareAndSetAcquire succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetAcquire() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + do {} while (!aa.weakCompareAndSetAcquire(i, 1, 2)); + do {} while (!aa.weakCompareAndSetAcquire(i, 2, -4)); + assertEquals(-4, aa.get(i)); + do {} while (!aa.weakCompareAndSetAcquire(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + + /** + * repeated weakCompareAndSetRelease succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetRelease() { + AtomicIntegerArray aa = new AtomicIntegerArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + do {} while (!aa.weakCompareAndSetRelease(i, 1, 2)); + do {} while (!aa.weakCompareAndSetRelease(i, 2, -4)); + assertEquals(-4, aa.get(i)); + do {} while (!aa.weakCompareAndSetRelease(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + +} diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/test/java/util/concurrent/tck/AtomicLong9Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/concurrent/tck/AtomicLong9Test.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,203 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.concurrent.atomic.AtomicLong; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicLong9Test extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicLong9Test.class); + } + + /** + * getPlain returns the last value set + */ + public void testGetPlainSet() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.getPlain()); + ai.set(2); + assertEquals(2, ai.getPlain()); + ai.set(-3); + assertEquals(-3, ai.getPlain()); + } + + /** + * getOpaque returns the last value set + */ + public void testGetOpaqueSet() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.getOpaque()); + ai.set(2); + assertEquals(2, ai.getOpaque()); + ai.set(-3); + assertEquals(-3, ai.getOpaque()); + } + + /** + * getAcquire returns the last value set + */ + public void testGetAcquireSet() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.getAcquire()); + ai.set(2); + assertEquals(2, ai.getAcquire()); + ai.set(-3); + assertEquals(-3, ai.getAcquire()); + } + + /** + * get returns the last value setPlain + */ + public void testGetSetPlain() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.get()); + ai.setPlain(2); + assertEquals(2, ai.get()); + ai.setPlain(-3); + assertEquals(-3, ai.get()); + } + + /** + * get returns the last value setOpaque + */ + public void testGetSetOpaque() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.get()); + ai.setOpaque(2); + assertEquals(2, ai.get()); + ai.setOpaque(-3); + assertEquals(-3, ai.get()); + } + + /** + * get returns the last value setRelease + */ + public void testGetSetRelease() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.get()); + ai.setRelease(2); + assertEquals(2, ai.get()); + ai.setRelease(-3); + assertEquals(-3, ai.get()); + } + + /** + * compareAndExchange succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchange() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.compareAndExchange(1, 2)); + assertEquals(2, ai.compareAndExchange(2, -4)); + assertEquals(-4, ai.get()); + assertEquals(-4, ai.compareAndExchange(-5, 7)); + assertEquals(-4, ai.get()); + assertEquals(-4, ai.compareAndExchange(-4, 7)); + assertEquals(7, ai.get()); + } + + /** + * compareAndExchangeAcquire succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeAcquire() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.compareAndExchangeAcquire(1, 2)); + assertEquals(2, ai.compareAndExchangeAcquire(2, -4)); + assertEquals(-4, ai.get()); + assertEquals(-4, ai.compareAndExchangeAcquire(-5, 7)); + assertEquals(-4, ai.get()); + assertEquals(-4, ai.compareAndExchangeAcquire(-4, 7)); + assertEquals(7, ai.get()); + } + + /** + * compareAndExchangeRelease succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeRelease() { + AtomicLong ai = new AtomicLong(1); + assertEquals(1, ai.compareAndExchangeRelease(1, 2)); + assertEquals(2, ai.compareAndExchangeRelease(2, -4)); + assertEquals(-4, ai.get()); + assertEquals(-4, ai.compareAndExchangeRelease(-5, 7)); + assertEquals(-4, ai.get()); + assertEquals(-4, ai.compareAndExchangeRelease(-4, 7)); + assertEquals(7, ai.get()); + } + + /** + * repeated weakCompareAndSetVolatile succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetVolatile() { + AtomicLong ai = new AtomicLong(1); + do {} while (!ai.weakCompareAndSetVolatile(1, 2)); + do {} while (!ai.weakCompareAndSetVolatile(2, -4)); + assertEquals(-4, ai.get()); + do {} while (!ai.weakCompareAndSetVolatile(-4, 7)); + assertEquals(7, ai.get()); + } + + /** + * repeated weakCompareAndSetAcquire succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetAcquire() { + AtomicLong ai = new AtomicLong(1); + do {} while (!ai.weakCompareAndSetAcquire(1, 2)); + do {} while (!ai.weakCompareAndSetAcquire(2, -4)); + assertEquals(-4, ai.get()); + do {} while (!ai.weakCompareAndSetAcquire(-4, 7)); + assertEquals(7, ai.get()); + } + + /** + * repeated weakCompareAndSetRelease succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetRelease() { + AtomicLong ai = new AtomicLong(1); + do {} while (!ai.weakCompareAndSetRelease(1, 2)); + do {} while (!ai.weakCompareAndSetRelease(2, -4)); + assertEquals(-4, ai.get()); + do {} while (!ai.weakCompareAndSetRelease(-4, 7)); + assertEquals(7, ai.get()); + } + +} diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/test/java/util/concurrent/tck/AtomicLongArray9Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/concurrent/tck/AtomicLongArray9Test.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,266 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicLongArray; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicLongArray9Test extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicLongArray9Test.class); + } + + /** + * get and set for out of bound indices throw IndexOutOfBoundsException + */ + public void testIndexing() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int index : new int[] { -1, SIZE }) { + final int j = index; + final Runnable[] tasks = { + () -> aa.getPlain(j), + () -> aa.getOpaque(j), + () -> aa.getAcquire(j), + () -> aa.setPlain(j, 1), + () -> aa.setOpaque(j, 1), + () -> aa.setRelease(j, 1), + () -> aa.compareAndExchange(j, 1, 2), + () -> aa.compareAndExchangeAcquire(j, 1, 2), + () -> aa.compareAndExchangeRelease(j, 1, 2), + () -> aa.weakCompareAndSetVolatile(j, 1, 2), + () -> aa.weakCompareAndSetAcquire(j, 1, 2), + () -> aa.weakCompareAndSetRelease(j, 1, 2), + }; + + assertThrows(IndexOutOfBoundsException.class, tasks); + } + } + + /** + * getPlain returns the last value set + */ + public void testGetPlainSet() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.getPlain(i)); + aa.set(i, 2); + assertEquals(2, aa.getPlain(i)); + aa.set(i, -3); + assertEquals(-3, aa.getPlain(i)); + } + } + + /** + * getOpaque returns the last value set + */ + public void testGetOpaqueSet() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.getOpaque(i)); + aa.set(i, 2); + assertEquals(2, aa.getOpaque(i)); + aa.set(i, -3); + assertEquals(-3, aa.getOpaque(i)); + } + } + + /** + * getAcquire returns the last value set + */ + public void testGetAcquireSet() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.getAcquire(i)); + aa.set(i, 2); + assertEquals(2, aa.getAcquire(i)); + aa.set(i, -3); + assertEquals(-3, aa.getAcquire(i)); + } + } + + /** + * get returns the last value setPlain + */ + public void testGetSetPlain() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.setPlain(i, 1); + assertEquals(1, aa.get(i)); + aa.setPlain(i, 2); + assertEquals(2, aa.get(i)); + aa.setPlain(i, -3); + assertEquals(-3, aa.get(i)); + } + } + + /** + * get returns the last value setOpaque + */ + public void testGetSetOpaque() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.setOpaque(i, 1); + assertEquals(1, aa.get(i)); + aa.setOpaque(i, 2); + assertEquals(2, aa.get(i)); + aa.setOpaque(i, -3); + assertEquals(-3, aa.get(i)); + } + } + + /** + * get returns the last value setRelease + */ + public void testGetSetRelease() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.setRelease(i, 1); + assertEquals(1, aa.get(i)); + aa.setRelease(i, 2); + assertEquals(2, aa.get(i)); + aa.setRelease(i, -3); + assertEquals(-3, aa.get(i)); + } + } + + /** + * compareAndExchange succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchange() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.compareAndExchange(i, 1, 2)); + assertEquals(2, aa.compareAndExchange(i, 2, -4)); + assertEquals(-4, aa.get(i)); + assertEquals(-4, aa.compareAndExchange(i,-5, 7)); + assertEquals(-4, aa.get(i)); + assertEquals(-4, aa.compareAndExchange(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + + /** + * compareAndExchangeAcquire succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeAcquire() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.compareAndExchangeAcquire(i, 1, 2)); + assertEquals(2, aa.compareAndExchangeAcquire(i, 2, -4)); + assertEquals(-4, aa.get(i)); + assertEquals(-4, aa.compareAndExchangeAcquire(i,-5, 7)); + assertEquals(-4, aa.get(i)); + assertEquals(-4, aa.compareAndExchangeAcquire(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + + /** + * compareAndExchangeRelease succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeRelease() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + assertEquals(1, aa.compareAndExchangeRelease(i, 1, 2)); + assertEquals(2, aa.compareAndExchangeRelease(i, 2, -4)); + assertEquals(-4, aa.get(i)); + assertEquals(-4, aa.compareAndExchangeRelease(i,-5, 7)); + assertEquals(-4, aa.get(i)); + assertEquals(-4, aa.compareAndExchangeRelease(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + + /** + * repeated weakCompareAndSetVolatile succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetVolatile() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + do {} while (!aa.weakCompareAndSetVolatile(i, 1, 2)); + do {} while (!aa.weakCompareAndSetVolatile(i, 2, -4)); + assertEquals(-4, aa.get(i)); + do {} while (!aa.weakCompareAndSetVolatile(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + + /** + * repeated weakCompareAndSetAcquire succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetAcquire() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + do {} while (!aa.weakCompareAndSetAcquire(i, 1, 2)); + do {} while (!aa.weakCompareAndSetAcquire(i, 2, -4)); + assertEquals(-4, aa.get(i)); + do {} while (!aa.weakCompareAndSetAcquire(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + + /** + * repeated weakCompareAndSetRelease succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetRelease() { + AtomicLongArray aa = new AtomicLongArray(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, 1); + do {} while (!aa.weakCompareAndSetRelease(i, 1, 2)); + do {} while (!aa.weakCompareAndSetRelease(i, 2, -4)); + assertEquals(-4, aa.get(i)); + do {} while (!aa.weakCompareAndSetRelease(i, -4, 7)); + assertEquals(7, aa.get(i)); + } + } + +} diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/test/java/util/concurrent/tck/AtomicReference9Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/concurrent/tck/AtomicReference9Test.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,203 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.concurrent.atomic.AtomicReference; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicReference9Test extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicReference9Test.class); + } + + /** + * getPlain returns the last value set + */ + public void testGetPlainSet() { + AtomicReference ai = new AtomicReference<>(one); + assertEquals(one, ai.getPlain()); + ai.set(two); + assertEquals(two, ai.getPlain()); + ai.set(m3); + assertEquals(m3, ai.getPlain()); + } + + /** + * getOpaque returns the last value set + */ + public void testGetOpaqueSet() { + AtomicReference ai = new AtomicReference<>(one); + assertEquals(one, ai.getOpaque()); + ai.set(two); + assertEquals(two, ai.getOpaque()); + ai.set(m3); + assertEquals(m3, ai.getOpaque()); + } + + /** + * getAcquire returns the last value set + */ + public void testGetAcquireSet() { + AtomicReference ai = new AtomicReference<>(one); + assertEquals(one, ai.getAcquire()); + ai.set(two); + assertEquals(two, ai.getAcquire()); + ai.set(m3); + assertEquals(m3, ai.getAcquire()); + } + + /** + * get returns the last value setPlain + */ + public void testGetSetPlain() { + AtomicReference ai = new AtomicReference<>(one); + assertEquals(one, ai.get()); + ai.setPlain(two); + assertEquals(two, ai.get()); + ai.setPlain(m3); + assertEquals(m3, ai.get()); + } + + /** + * get returns the last value setOpaque + */ + public void testGetSetOpaque() { + AtomicReference ai = new AtomicReference<>(one); + assertEquals(one, ai.get()); + ai.setOpaque(two); + assertEquals(two, ai.get()); + ai.setOpaque(m3); + assertEquals(m3, ai.get()); + } + + /** + * get returns the last value setRelease + */ + public void testGetSetRelease() { + AtomicReference ai = new AtomicReference<>(one); + assertEquals(one, ai.get()); + ai.setRelease(two); + assertEquals(two, ai.get()); + ai.setRelease(m3); + assertEquals(m3, ai.get()); + } + + /** + * compareAndExchange succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchange() { + AtomicReference ai = new AtomicReference<>(one); + assertEquals(one, ai.compareAndExchange(one, two)); + assertEquals(two, ai.compareAndExchange(two, m4)); + assertEquals(m4, ai.get()); + assertEquals(m4, ai.compareAndExchange(m5, seven)); + assertEquals(m4, ai.get()); + assertEquals(m4, ai.compareAndExchange(m4, seven)); + assertEquals(seven, ai.get()); + } + + /** + * compareAndExchangeAcquire succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeAcquire() { + AtomicReference ai = new AtomicReference<>(one); + assertEquals(one, ai.compareAndExchangeAcquire(one, two)); + assertEquals(two, ai.compareAndExchangeAcquire(two, m4)); + assertEquals(m4, ai.get()); + assertEquals(m4, ai.compareAndExchangeAcquire(m5, seven)); + assertEquals(m4, ai.get()); + assertEquals(m4, ai.compareAndExchangeAcquire(m4, seven)); + assertEquals(seven, ai.get()); + } + + /** + * compareAndExchangeRelease succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeRelease() { + AtomicReference ai = new AtomicReference<>(one); + assertEquals(one, ai.compareAndExchangeRelease(one, two)); + assertEquals(two, ai.compareAndExchangeRelease(two, m4)); + assertEquals(m4, ai.get()); + assertEquals(m4, ai.compareAndExchangeRelease(m5, seven)); + assertEquals(m4, ai.get()); + assertEquals(m4, ai.compareAndExchangeRelease(m4, seven)); + assertEquals(seven, ai.get()); + } + + /** + * repeated weakCompareAndSetVolatile succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetVolatile() { + AtomicReference ai = new AtomicReference<>(one); + do {} while (!ai.weakCompareAndSetVolatile(one, two)); + do {} while (!ai.weakCompareAndSetVolatile(two, m4)); + assertEquals(m4, ai.get()); + do {} while (!ai.weakCompareAndSetVolatile(m4, seven)); + assertEquals(seven, ai.get()); + } + + /** + * repeated weakCompareAndSetAcquire succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetAcquire() { + AtomicReference ai = new AtomicReference<>(one); + do {} while (!ai.weakCompareAndSetAcquire(one, two)); + do {} while (!ai.weakCompareAndSetAcquire(two, m4)); + assertEquals(m4, ai.get()); + do {} while (!ai.weakCompareAndSetAcquire(m4, seven)); + assertEquals(seven, ai.get()); + } + + /** + * repeated weakCompareAndSetRelease succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetRelease() { + AtomicReference ai = new AtomicReference<>(one); + do {} while (!ai.weakCompareAndSetRelease(one, two)); + do {} while (!ai.weakCompareAndSetRelease(two, m4)); + assertEquals(m4, ai.get()); + do {} while (!ai.weakCompareAndSetRelease(m4, seven)); + assertEquals(seven, ai.get()); + } + +} diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/test/java/util/concurrent/tck/AtomicReferenceArray9Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/util/concurrent/tck/AtomicReferenceArray9Test.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,266 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Written by Doug Lea with assistance from members of JCP JSR-166 + * Expert Group and released to the public domain, as explained at + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicReferenceArray; + +import junit.framework.Test; +import junit.framework.TestSuite; + +public class AtomicReferenceArray9Test extends JSR166TestCase { + public static void main(String[] args) { + main(suite(), args); + } + public static Test suite() { + return new TestSuite(AtomicReferenceArray9Test.class); + } + + /** + * get and set for out of bound indices throw IndexOutOfBoundsException + */ + public void testIndexing() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int index : new int[] { -1, SIZE }) { + final int j = index; + final Runnable[] tasks = { + () -> aa.getPlain(j), + () -> aa.getOpaque(j), + () -> aa.getAcquire(j), + () -> aa.setPlain(j, null), + () -> aa.setOpaque(j, null), + () -> aa.setRelease(j, null), + () -> aa.compareAndExchange(j, null, null), + () -> aa.compareAndExchangeAcquire(j, null, null), + () -> aa.compareAndExchangeRelease(j, null, null), + () -> aa.weakCompareAndSetVolatile(j, null, null), + () -> aa.weakCompareAndSetAcquire(j, null, null), + () -> aa.weakCompareAndSetRelease(j, null, null), + }; + + assertThrows(IndexOutOfBoundsException.class, tasks); + } + } + + /** + * getPlain returns the last value set + */ + public void testGetPlainSet() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, one); + assertEquals(one, aa.getPlain(i)); + aa.set(i, two); + assertEquals(two, aa.getPlain(i)); + aa.set(i, m3); + assertEquals(m3, aa.getPlain(i)); + } + } + + /** + * getOpaque returns the last value set + */ + public void testGetOpaqueSet() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, one); + assertEquals(one, aa.getOpaque(i)); + aa.set(i, two); + assertEquals(two, aa.getOpaque(i)); + aa.set(i, m3); + assertEquals(m3, aa.getOpaque(i)); + } + } + + /** + * getAcquire returns the last value set + */ + public void testGetAcquireSet() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, one); + assertEquals(one, aa.getAcquire(i)); + aa.set(i, two); + assertEquals(two, aa.getAcquire(i)); + aa.set(i, m3); + assertEquals(m3, aa.getAcquire(i)); + } + } + + /** + * get returns the last value setPlain + */ + public void testGetSetPlain() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.setPlain(i, one); + assertEquals(one, aa.get(i)); + aa.setPlain(i, two); + assertEquals(two, aa.get(i)); + aa.setPlain(i, m3); + assertEquals(m3, aa.get(i)); + } + } + + /** + * get returns the last value setOpaque + */ + public void testGetSetOpaque() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.setOpaque(i, one); + assertEquals(one, aa.get(i)); + aa.setOpaque(i, two); + assertEquals(two, aa.get(i)); + aa.setOpaque(i, m3); + assertEquals(m3, aa.get(i)); + } + } + + /** + * get returns the last value setRelease + */ + public void testGetSetRelease() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.setRelease(i, one); + assertEquals(one, aa.get(i)); + aa.setRelease(i, two); + assertEquals(two, aa.get(i)); + aa.setRelease(i, m3); + assertEquals(m3, aa.get(i)); + } + } + + /** + * compareAndExchange succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchange() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, one); + assertEquals(one, aa.compareAndExchange(i, one, two)); + assertEquals(two, aa.compareAndExchange(i, two, m4)); + assertEquals(m4, aa.get(i)); + assertEquals(m4, aa.compareAndExchange(i,m5, seven)); + assertEquals(m4, aa.get(i)); + assertEquals(m4, aa.compareAndExchange(i, m4, seven)); + assertEquals(seven, aa.get(i)); + } + } + + /** + * compareAndExchangeAcquire succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeAcquire() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, one); + assertEquals(one, aa.compareAndExchangeAcquire(i, one, two)); + assertEquals(two, aa.compareAndExchangeAcquire(i, two, m4)); + assertEquals(m4, aa.get(i)); + assertEquals(m4, aa.compareAndExchangeAcquire(i,m5, seven)); + assertEquals(m4, aa.get(i)); + assertEquals(m4, aa.compareAndExchangeAcquire(i, m4, seven)); + assertEquals(seven, aa.get(i)); + } + } + + /** + * compareAndExchangeRelease succeeds in changing value if equal to + * expected else fails + */ + public void testCompareAndExchangeRelease() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, one); + assertEquals(one, aa.compareAndExchangeRelease(i, one, two)); + assertEquals(two, aa.compareAndExchangeRelease(i, two, m4)); + assertEquals(m4, aa.get(i)); + assertEquals(m4, aa.compareAndExchangeRelease(i,m5, seven)); + assertEquals(m4, aa.get(i)); + assertEquals(m4, aa.compareAndExchangeRelease(i, m4, seven)); + assertEquals(seven, aa.get(i)); + } + } + + /** + * repeated weakCompareAndSetVolatile succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetVolatile() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, one); + do {} while (!aa.weakCompareAndSetVolatile(i, one, two)); + do {} while (!aa.weakCompareAndSetVolatile(i, two, m4)); + assertEquals(m4, aa.get(i)); + do {} while (!aa.weakCompareAndSetVolatile(i, m4, seven)); + assertEquals(seven, aa.get(i)); + } + } + + /** + * repeated weakCompareAndSetAcquire succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetAcquire() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, one); + do {} while (!aa.weakCompareAndSetAcquire(i, one, two)); + do {} while (!aa.weakCompareAndSetAcquire(i, two, m4)); + assertEquals(m4, aa.get(i)); + do {} while (!aa.weakCompareAndSetAcquire(i, m4, seven)); + assertEquals(seven, aa.get(i)); + } + } + + /** + * repeated weakCompareAndSetRelease succeeds in changing value when equal + * to expected + */ + public void testWeakCompareAndSetRelease() { + AtomicReferenceArray aa = new AtomicReferenceArray<>(SIZE); + for (int i = 0; i < SIZE; i++) { + aa.set(i, one); + do {} while (!aa.weakCompareAndSetRelease(i, one, two)); + do {} while (!aa.weakCompareAndSetRelease(i, two, m4)); + assertEquals(m4, aa.get(i)); + do {} while (!aa.weakCompareAndSetRelease(i, m4, seven)); + assertEquals(seven, aa.get(i)); + } + } + +} diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/test/java/util/concurrent/tck/AtomicReferenceArrayTest.java --- a/jdk/test/java/util/concurrent/tck/AtomicReferenceArrayTest.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/test/java/util/concurrent/tck/AtomicReferenceArrayTest.java Mon Jul 18 09:38:08 2016 -0700 @@ -243,4 +243,5 @@ AtomicReferenceArray aa = new AtomicReferenceArray(a); assertEquals(Arrays.toString(a), aa.toString()); } + } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/test/java/util/concurrent/tck/AtomicReferenceTest.java --- a/jdk/test/java/util/concurrent/tck/AtomicReferenceTest.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/test/java/util/concurrent/tck/AtomicReferenceTest.java Mon Jul 18 09:38:08 2016 -0700 @@ -161,7 +161,7 @@ * toString returns current value. */ public void testToString() { - AtomicReference ai = new AtomicReference(one); + AtomicReference ai = new AtomicReference<>(one); assertEquals(one.toString(), ai.toString()); ai.set(two); assertEquals(two.toString(), ai.toString()); diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/test/java/util/concurrent/tck/CompletableFutureTest.java --- a/jdk/test/java/util/concurrent/tck/CompletableFutureTest.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/test/java/util/concurrent/tck/CompletableFutureTest.java Mon Jul 18 09:38:08 2016 -0700 @@ -57,6 +57,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -486,62 +487,68 @@ class FailingSupplier extends CheckedAction implements Supplier { - FailingSupplier(ExecutionMode m) { super(m); } + final CFException ex; + FailingSupplier(ExecutionMode m) { super(m); ex = new CFException(); } public Integer get() { invoked(); - throw new CFException(); + throw ex; } } class FailingConsumer extends CheckedIntegerAction implements Consumer { - FailingConsumer(ExecutionMode m) { super(m); } + final CFException ex; + FailingConsumer(ExecutionMode m) { super(m); ex = new CFException(); } public void accept(Integer x) { invoked(); value = x; - throw new CFException(); + throw ex; } } class FailingBiConsumer extends CheckedIntegerAction implements BiConsumer { - FailingBiConsumer(ExecutionMode m) { super(m); } + final CFException ex; + FailingBiConsumer(ExecutionMode m) { super(m); ex = new CFException(); } public void accept(Integer x, Integer y) { invoked(); value = subtract(x, y); - throw new CFException(); + throw ex; } } class FailingFunction extends CheckedIntegerAction implements Function { - FailingFunction(ExecutionMode m) { super(m); } + final CFException ex; + FailingFunction(ExecutionMode m) { super(m); ex = new CFException(); } public Integer apply(Integer x) { invoked(); value = x; - throw new CFException(); + throw ex; } } class FailingBiFunction extends CheckedIntegerAction implements BiFunction { - FailingBiFunction(ExecutionMode m) { super(m); } + final CFException ex; + FailingBiFunction(ExecutionMode m) { super(m); ex = new CFException(); } public Integer apply(Integer x, Integer y) { invoked(); value = subtract(x, y); - throw new CFException(); + throw ex; } } class FailingRunnable extends CheckedAction implements Runnable { - FailingRunnable(ExecutionMode m) { super(m); } + final CFException ex; + FailingRunnable(ExecutionMode m) { super(m); ex = new CFException(); } public void run() { invoked(); - throw new CFException(); + throw ex; } } @@ -561,11 +568,21 @@ class FailingCompletableFutureFunction extends CheckedIntegerAction implements Function> { - FailingCompletableFutureFunction(ExecutionMode m) { super(m); } + final CFException ex; + FailingCompletableFutureFunction(ExecutionMode m) { super(m); ex = new CFException(); } public CompletableFuture apply(Integer x) { invoked(); value = x; - throw new CFException(); + throw ex; + } + } + + static class CountingRejectingExecutor implements Executor { + final RejectedExecutionException ex = new RejectedExecutionException(); + final AtomicInteger count = new AtomicInteger(0); + public void execute(Runnable r) { + count.getAndIncrement(); + throw ex; } } @@ -1249,10 +1266,22 @@ { final FailingRunnable r = new FailingRunnable(m); final CompletableFuture f = m.runAsync(r); - checkCompletedWithWrappedCFException(f); + checkCompletedWithWrappedException(f, r.ex); r.assertInvoked(); }} + public void testRunAsync_rejectingExecutor() { + CountingRejectingExecutor e = new CountingRejectingExecutor(); + try { + CompletableFuture.runAsync(() -> {}, e); + shouldThrow(); + } catch (Throwable t) { + assertSame(e.ex, t); + } + + assertEquals(1, e.count.get()); + } + /** * supplyAsync completes with result of supplier */ @@ -1283,10 +1312,22 @@ { FailingSupplier r = new FailingSupplier(m); CompletableFuture f = m.supplyAsync(r); - checkCompletedWithWrappedCFException(f); + checkCompletedWithWrappedException(f, r.ex); r.assertInvoked(); }} + public void testSupplyAsync_rejectingExecutor() { + CountingRejectingExecutor e = new CountingRejectingExecutor(); + try { + CompletableFuture.supplyAsync(() -> null, e); + shouldThrow(); + } catch (Throwable t) { + assertSame(e.ex, t); + } + + assertEquals(1, e.count.get()); + } + // seq completion methods /** @@ -1405,12 +1446,12 @@ final CompletableFuture h4 = m.runAfterBoth(f, f, rs[4]); final CompletableFuture h5 = m.runAfterEither(f, f, rs[5]); - checkCompletedWithWrappedCFException(h0); - checkCompletedWithWrappedCFException(h1); - checkCompletedWithWrappedCFException(h2); - checkCompletedWithWrappedCFException(h3); - checkCompletedWithWrappedCFException(h4); - checkCompletedWithWrappedCFException(h5); + checkCompletedWithWrappedException(h0, rs[0].ex); + checkCompletedWithWrappedException(h1, rs[1].ex); + checkCompletedWithWrappedException(h2, rs[2].ex); + checkCompletedWithWrappedException(h3, rs[3].ex); + checkCompletedWithWrappedException(h4, rs[4].ex); + checkCompletedWithWrappedException(h5, rs[5].ex); checkCompletedNormally(f, v1); }} @@ -1509,10 +1550,10 @@ final CompletableFuture h2 = m.thenApply(f, rs[2]); final CompletableFuture h3 = m.applyToEither(f, f, rs[3]); - checkCompletedWithWrappedCFException(h0); - checkCompletedWithWrappedCFException(h1); - checkCompletedWithWrappedCFException(h2); - checkCompletedWithWrappedCFException(h3); + checkCompletedWithWrappedException(h0, rs[0].ex); + checkCompletedWithWrappedException(h1, rs[1].ex); + checkCompletedWithWrappedException(h2, rs[2].ex); + checkCompletedWithWrappedException(h3, rs[3].ex); checkCompletedNormally(f, v1); }} @@ -1611,10 +1652,10 @@ final CompletableFuture h2 = m.thenAccept(f, rs[2]); final CompletableFuture h3 = m.acceptEither(f, f, rs[3]); - checkCompletedWithWrappedCFException(h0); - checkCompletedWithWrappedCFException(h1); - checkCompletedWithWrappedCFException(h2); - checkCompletedWithWrappedCFException(h3); + checkCompletedWithWrappedException(h0, rs[0].ex); + checkCompletedWithWrappedException(h1, rs[1].ex); + checkCompletedWithWrappedException(h2, rs[2].ex); + checkCompletedWithWrappedException(h3, rs[3].ex); checkCompletedNormally(f, v1); }} @@ -1776,9 +1817,9 @@ assertTrue(snd.complete(w2)); final CompletableFuture h3 = m.thenCombine(f, g, r3); - checkCompletedWithWrappedCFException(h1); - checkCompletedWithWrappedCFException(h2); - checkCompletedWithWrappedCFException(h3); + checkCompletedWithWrappedException(h1, r1.ex); + checkCompletedWithWrappedException(h2, r2.ex); + checkCompletedWithWrappedException(h3, r3.ex); r1.assertInvoked(); r2.assertInvoked(); r3.assertInvoked(); @@ -1940,9 +1981,9 @@ assertTrue(snd.complete(w2)); final CompletableFuture h3 = m.thenAcceptBoth(f, g, r3); - checkCompletedWithWrappedCFException(h1); - checkCompletedWithWrappedCFException(h2); - checkCompletedWithWrappedCFException(h3); + checkCompletedWithWrappedException(h1, r1.ex); + checkCompletedWithWrappedException(h2, r2.ex); + checkCompletedWithWrappedException(h3, r3.ex); r1.assertInvoked(); r2.assertInvoked(); r3.assertInvoked(); @@ -2104,9 +2145,9 @@ assertTrue(snd.complete(w2)); final CompletableFuture h3 = m.runAfterBoth(f, g, r3); - checkCompletedWithWrappedCFException(h1); - checkCompletedWithWrappedCFException(h2); - checkCompletedWithWrappedCFException(h3); + checkCompletedWithWrappedException(h1, r1.ex); + checkCompletedWithWrappedException(h2, r2.ex); + checkCompletedWithWrappedException(h3, r3.ex); r1.assertInvoked(); r2.assertInvoked(); r3.assertInvoked(); @@ -2396,10 +2437,10 @@ f.complete(v1); final CompletableFuture h2 = m.applyToEither(f, g, rs[2]); final CompletableFuture h3 = m.applyToEither(g, f, rs[3]); - checkCompletedWithWrappedCFException(h0); - checkCompletedWithWrappedCFException(h1); - checkCompletedWithWrappedCFException(h2); - checkCompletedWithWrappedCFException(h3); + checkCompletedWithWrappedException(h0, rs[0].ex); + checkCompletedWithWrappedException(h1, rs[1].ex); + checkCompletedWithWrappedException(h2, rs[2].ex); + checkCompletedWithWrappedException(h3, rs[3].ex); for (int i = 0; i < 4; i++) rs[i].assertValue(v1); g.complete(v2); @@ -2408,10 +2449,10 @@ final CompletableFuture h4 = m.applyToEither(f, g, rs[4]); final CompletableFuture h5 = m.applyToEither(g, f, rs[5]); - checkCompletedWithWrappedCFException(h4); + checkCompletedWithWrappedException(h4, rs[4].ex); assertTrue(Objects.equals(v1, rs[4].value) || Objects.equals(v2, rs[4].value)); - checkCompletedWithWrappedCFException(h5); + checkCompletedWithWrappedException(h5, rs[5].ex); assertTrue(Objects.equals(v1, rs[5].value) || Objects.equals(v2, rs[5].value)); @@ -2655,10 +2696,10 @@ f.complete(v1); final CompletableFuture h2 = m.acceptEither(f, g, rs[2]); final CompletableFuture h3 = m.acceptEither(g, f, rs[3]); - checkCompletedWithWrappedCFException(h0); - checkCompletedWithWrappedCFException(h1); - checkCompletedWithWrappedCFException(h2); - checkCompletedWithWrappedCFException(h3); + checkCompletedWithWrappedException(h0, rs[0].ex); + checkCompletedWithWrappedException(h1, rs[1].ex); + checkCompletedWithWrappedException(h2, rs[2].ex); + checkCompletedWithWrappedException(h3, rs[3].ex); for (int i = 0; i < 4; i++) rs[i].assertValue(v1); g.complete(v2); @@ -2667,10 +2708,10 @@ final CompletableFuture h4 = m.acceptEither(f, g, rs[4]); final CompletableFuture h5 = m.acceptEither(g, f, rs[5]); - checkCompletedWithWrappedCFException(h4); + checkCompletedWithWrappedException(h4, rs[4].ex); assertTrue(Objects.equals(v1, rs[4].value) || Objects.equals(v2, rs[4].value)); - checkCompletedWithWrappedCFException(h5); + checkCompletedWithWrappedException(h5, rs[5].ex); assertTrue(Objects.equals(v1, rs[5].value) || Objects.equals(v2, rs[5].value)); @@ -2686,6 +2727,7 @@ for (ExecutionMode m : ExecutionMode.values()) for (Integer v1 : new Integer[] { 1, null }) for (Integer v2 : new Integer[] { 2, null }) + for (boolean pushNop : new boolean[] { true, false }) { final CompletableFuture f = new CompletableFuture<>(); final CompletableFuture g = new CompletableFuture<>(); @@ -2698,6 +2740,10 @@ checkIncomplete(h1); rs[0].assertNotInvoked(); rs[1].assertNotInvoked(); + if (pushNop) { // ad hoc test of intra-completion interference + m.thenRun(f, () -> {}); + m.thenRun(g, () -> {}); + } f.complete(v1); checkCompletedNormally(h0, null); checkCompletedNormally(h1, null); @@ -2910,16 +2956,16 @@ assertTrue(f.complete(v1)); final CompletableFuture h2 = m.runAfterEither(f, g, rs[2]); final CompletableFuture h3 = m.runAfterEither(g, f, rs[3]); - checkCompletedWithWrappedCFException(h0); - checkCompletedWithWrappedCFException(h1); - checkCompletedWithWrappedCFException(h2); - checkCompletedWithWrappedCFException(h3); + checkCompletedWithWrappedException(h0, rs[0].ex); + checkCompletedWithWrappedException(h1, rs[1].ex); + checkCompletedWithWrappedException(h2, rs[2].ex); + checkCompletedWithWrappedException(h3, rs[3].ex); for (int i = 0; i < 4; i++) rs[i].assertInvoked(); assertTrue(g.complete(v2)); final CompletableFuture h4 = m.runAfterEither(f, g, rs[4]); final CompletableFuture h5 = m.runAfterEither(g, f, rs[5]); - checkCompletedWithWrappedCFException(h4); - checkCompletedWithWrappedCFException(h5); + checkCompletedWithWrappedException(h4, rs[4].ex); + checkCompletedWithWrappedException(h5, rs[5].ex); checkCompletedNormally(f, v1); checkCompletedNormally(g, v2); @@ -2980,7 +3026,7 @@ final CompletableFuture g = m.thenCompose(f, r); if (createIncomplete) assertTrue(f.complete(v1)); - checkCompletedWithWrappedCFException(g); + checkCompletedWithWrappedException(g, r.ex); checkCompletedNormally(f, v1); }} @@ -3089,7 +3135,7 @@ } } - public void testAllOf_backwards() throws Exception { + public void testAllOf_normal_backwards() throws Exception { for (int k = 1; k < 10; k++) { CompletableFuture[] fs = (CompletableFuture[]) new CompletableFuture[k]; @@ -3337,6 +3383,151 @@ } /** + * Test submissions to an executor that rejects all tasks. + */ + public void testRejectingExecutor() { + for (Integer v : new Integer[] { 1, null }) + { + final CountingRejectingExecutor e = new CountingRejectingExecutor(); + + final CompletableFuture complete = CompletableFuture.completedFuture(v); + final CompletableFuture incomplete = new CompletableFuture<>(); + + List> futures = new ArrayList<>(); + + List> srcs = new ArrayList<>(); + srcs.add(complete); + srcs.add(incomplete); + + for (CompletableFuture src : srcs) { + List> fs = new ArrayList<>(); + fs.add(src.thenRunAsync(() -> {}, e)); + fs.add(src.thenAcceptAsync((z) -> {}, e)); + fs.add(src.thenApplyAsync((z) -> z, e)); + + fs.add(src.thenCombineAsync(src, (x, y) -> x, e)); + fs.add(src.thenAcceptBothAsync(src, (x, y) -> {}, e)); + fs.add(src.runAfterBothAsync(src, () -> {}, e)); + + fs.add(src.applyToEitherAsync(src, (z) -> z, e)); + fs.add(src.acceptEitherAsync(src, (z) -> {}, e)); + fs.add(src.runAfterEitherAsync(src, () -> {}, e)); + + fs.add(src.thenComposeAsync((z) -> null, e)); + fs.add(src.whenCompleteAsync((z, t) -> {}, e)); + fs.add(src.handleAsync((z, t) -> null, e)); + + for (CompletableFuture future : fs) { + if (src.isDone()) + checkCompletedWithWrappedException(future, e.ex); + else + checkIncomplete(future); + } + futures.addAll(fs); + } + + { + List> fs = new ArrayList<>(); + + fs.add(complete.thenCombineAsync(incomplete, (x, y) -> x, e)); + fs.add(incomplete.thenCombineAsync(complete, (x, y) -> x, e)); + + fs.add(complete.thenAcceptBothAsync(incomplete, (x, y) -> {}, e)); + fs.add(incomplete.thenAcceptBothAsync(complete, (x, y) -> {}, e)); + + fs.add(complete.runAfterBothAsync(incomplete, () -> {}, e)); + fs.add(incomplete.runAfterBothAsync(complete, () -> {}, e)); + + for (CompletableFuture future : fs) + checkIncomplete(future); + futures.addAll(fs); + } + + { + List> fs = new ArrayList<>(); + + fs.add(complete.applyToEitherAsync(incomplete, (z) -> z, e)); + fs.add(incomplete.applyToEitherAsync(complete, (z) -> z, e)); + + fs.add(complete.acceptEitherAsync(incomplete, (z) -> {}, e)); + fs.add(incomplete.acceptEitherAsync(complete, (z) -> {}, e)); + + fs.add(complete.runAfterEitherAsync(incomplete, () -> {}, e)); + fs.add(incomplete.runAfterEitherAsync(complete, () -> {}, e)); + + for (CompletableFuture future : fs) + checkCompletedWithWrappedException(future, e.ex); + futures.addAll(fs); + } + + incomplete.complete(v); + + for (CompletableFuture future : futures) + checkCompletedWithWrappedException(future, e.ex); + + assertEquals(futures.size(), e.count.get()); + }} + + /** + * Test submissions to an executor that rejects all tasks, but + * should never be invoked because the dependent future is + * explicitly completed. + */ + public void testRejectingExecutorNeverInvoked() { + for (Integer v : new Integer[] { 1, null }) + { + final CountingRejectingExecutor e = new CountingRejectingExecutor(); + + final CompletableFuture complete = CompletableFuture.completedFuture(v); + final CompletableFuture incomplete = new CompletableFuture<>(); + + List> futures = new ArrayList<>(); + + List> srcs = new ArrayList<>(); + srcs.add(complete); + srcs.add(incomplete); + + List> fs = new ArrayList<>(); + fs.add(incomplete.thenRunAsync(() -> {}, e)); + fs.add(incomplete.thenAcceptAsync((z) -> {}, e)); + fs.add(incomplete.thenApplyAsync((z) -> z, e)); + + fs.add(incomplete.thenCombineAsync(incomplete, (x, y) -> x, e)); + fs.add(incomplete.thenAcceptBothAsync(incomplete, (x, y) -> {}, e)); + fs.add(incomplete.runAfterBothAsync(incomplete, () -> {}, e)); + + fs.add(incomplete.applyToEitherAsync(incomplete, (z) -> z, e)); + fs.add(incomplete.acceptEitherAsync(incomplete, (z) -> {}, e)); + fs.add(incomplete.runAfterEitherAsync(incomplete, () -> {}, e)); + + fs.add(incomplete.thenComposeAsync((z) -> null, e)); + fs.add(incomplete.whenCompleteAsync((z, t) -> {}, e)); + fs.add(incomplete.handleAsync((z, t) -> null, e)); + + fs.add(complete.thenCombineAsync(incomplete, (x, y) -> x, e)); + fs.add(incomplete.thenCombineAsync(complete, (x, y) -> x, e)); + + fs.add(complete.thenAcceptBothAsync(incomplete, (x, y) -> {}, e)); + fs.add(incomplete.thenAcceptBothAsync(complete, (x, y) -> {}, e)); + + fs.add(complete.runAfterBothAsync(incomplete, () -> {}, e)); + fs.add(incomplete.runAfterBothAsync(complete, () -> {}, e)); + + for (CompletableFuture future : fs) + checkIncomplete(future); + + for (CompletableFuture future : fs) + future.complete(null); + + incomplete.complete(v); + + for (CompletableFuture future : fs) + checkCompletedNormally(future, null); + + assertEquals(0, e.count.get()); + }} + + /** * toCompletableFuture returns this CompletableFuture. */ public void testToCompletableFuture() { @@ -3659,12 +3850,25 @@ //--- tests of implementation details; not part of official tck --- Object resultOf(CompletableFuture f) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + try { + System.setSecurityManager(null); + } catch (SecurityException giveUp) { + return "Reflection not available"; + } + } + try { java.lang.reflect.Field resultField = CompletableFuture.class.getDeclaredField("result"); resultField.setAccessible(true); return resultField.get(f); - } catch (Throwable t) { throw new AssertionError(t); } + } catch (Throwable t) { + throw new AssertionError(t); + } finally { + if (sm != null) System.setSecurityManager(sm); + } } public void testExceptionPropagationReusesResultObject() { @@ -3675,33 +3879,44 @@ final CompletableFuture v42 = CompletableFuture.completedFuture(42); final CompletableFuture incomplete = new CompletableFuture<>(); + final Runnable noopRunnable = new Noop(m); + final Consumer noopConsumer = new NoopConsumer(m); + final Function incFunction = new IncFunction(m); + List, CompletableFuture>> funs = new ArrayList<>(); - funs.add((y) -> m.thenRun(y, new Noop(m))); - funs.add((y) -> m.thenAccept(y, new NoopConsumer(m))); - funs.add((y) -> m.thenApply(y, new IncFunction(m))); - - funs.add((y) -> m.runAfterEither(y, incomplete, new Noop(m))); - funs.add((y) -> m.acceptEither(y, incomplete, new NoopConsumer(m))); - funs.add((y) -> m.applyToEither(y, incomplete, new IncFunction(m))); - - funs.add((y) -> m.runAfterBoth(y, v42, new Noop(m))); + funs.add((y) -> m.thenRun(y, noopRunnable)); + funs.add((y) -> m.thenAccept(y, noopConsumer)); + funs.add((y) -> m.thenApply(y, incFunction)); + + funs.add((y) -> m.runAfterEither(y, incomplete, noopRunnable)); + funs.add((y) -> m.acceptEither(y, incomplete, noopConsumer)); + funs.add((y) -> m.applyToEither(y, incomplete, incFunction)); + + funs.add((y) -> m.runAfterBoth(y, v42, noopRunnable)); + funs.add((y) -> m.runAfterBoth(v42, y, noopRunnable)); funs.add((y) -> m.thenAcceptBoth(y, v42, new SubtractAction(m))); + funs.add((y) -> m.thenAcceptBoth(v42, y, new SubtractAction(m))); funs.add((y) -> m.thenCombine(y, v42, new SubtractFunction(m))); + funs.add((y) -> m.thenCombine(v42, y, new SubtractFunction(m))); funs.add((y) -> m.whenComplete(y, (Integer r, Throwable t) -> {})); funs.add((y) -> m.thenCompose(y, new CompletableFutureInc(m))); - funs.add((y) -> CompletableFuture.allOf(new CompletableFuture[] {y, v42})); - funs.add((y) -> CompletableFuture.anyOf(new CompletableFuture[] {y, incomplete})); + funs.add((y) -> CompletableFuture.allOf(y)); + funs.add((y) -> CompletableFuture.allOf(y, v42)); + funs.add((y) -> CompletableFuture.allOf(v42, y)); + funs.add((y) -> CompletableFuture.anyOf(y)); + funs.add((y) -> CompletableFuture.anyOf(y, incomplete)); + funs.add((y) -> CompletableFuture.anyOf(incomplete, y)); for (Function, CompletableFuture> fun : funs) { CompletableFuture f = new CompletableFuture<>(); f.completeExceptionally(ex); - CompletableFuture src = m.thenApply(f, new IncFunction(m)); + CompletableFuture src = m.thenApply(f, incFunction); checkCompletedWithWrappedException(src, ex); CompletableFuture dep = fun.apply(src); checkCompletedWithWrappedException(dep, ex); @@ -3711,7 +3926,7 @@ for (Function, CompletableFuture> fun : funs) { CompletableFuture f = new CompletableFuture<>(); - CompletableFuture src = m.thenApply(f, new IncFunction(m)); + CompletableFuture src = m.thenApply(f, incFunction); CompletableFuture dep = fun.apply(src); f.completeExceptionally(ex); checkCompletedWithWrappedException(src, ex); @@ -3725,7 +3940,7 @@ CompletableFuture f = new CompletableFuture<>(); f.cancel(mayInterruptIfRunning); checkCancelled(f); - CompletableFuture src = m.thenApply(f, new IncFunction(m)); + CompletableFuture src = m.thenApply(f, incFunction); checkCompletedWithWrappedCancellationException(src); CompletableFuture dep = fun.apply(src); checkCompletedWithWrappedCancellationException(dep); @@ -3736,7 +3951,7 @@ for (Function, CompletableFuture> fun : funs) { CompletableFuture f = new CompletableFuture<>(); - CompletableFuture src = m.thenApply(f, new IncFunction(m)); + CompletableFuture src = m.thenApply(f, incFunction); CompletableFuture dep = fun.apply(src); f.cancel(mayInterruptIfRunning); checkCancelled(f); @@ -3747,7 +3962,7 @@ }} /** - * Minimal completion stages throw UOE for all non-CompletionStage methods + * Minimal completion stages throw UOE for most non-CompletionStage methods */ public void testMinimalCompletionStage_minimality() { if (!testImplementationDetails) return; @@ -3776,8 +3991,10 @@ .filter((method) -> !permittedMethodSignatures.contains(toSignature.apply(method))) .collect(Collectors.toList()); - CompletionStage minimalStage = - new CompletableFuture().minimalCompletionStage(); + List> stages = new ArrayList<>(); + stages.add(new CompletableFuture().minimalCompletionStage()); + stages.add(CompletableFuture.completedStage(1)); + stages.add(CompletableFuture.failedStage(new CFException())); List bugs = new ArrayList<>(); for (Method method : allMethods) { @@ -3793,20 +4010,22 @@ else if (parameterTypes[i] == long.class) args[i] = 0L; } - try { - method.invoke(minimalStage, args); - bugs.add(method); + for (CompletionStage stage : stages) { + try { + method.invoke(stage, args); + bugs.add(method); + } + catch (java.lang.reflect.InvocationTargetException expected) { + if (! (expected.getCause() instanceof UnsupportedOperationException)) { + bugs.add(method); + // expected.getCause().printStackTrace(); + } + } + catch (ReflectiveOperationException bad) { throw new Error(bad); } } - catch (java.lang.reflect.InvocationTargetException expected) { - if (! (expected.getCause() instanceof UnsupportedOperationException)) { - bugs.add(method); - // expected.getCause().printStackTrace(); - } - } - catch (ReflectiveOperationException bad) { throw new Error(bad); } } if (!bugs.isEmpty()) - throw new Error("Methods did not throw UOE: " + bugs.toString()); + throw new Error("Methods did not throw UOE: " + bugs); } static class Monad { @@ -3955,12 +4174,33 @@ Monad.plus(godot, Monad.unit(5L))); } + /** Test long recursive chains of CompletableFutures with cascading completions */ + public void testRecursiveChains() throws Throwable { + for (ExecutionMode m : ExecutionMode.values()) + for (boolean addDeadEnds : new boolean[] { true, false }) + { + final int val = 42; + final int n = expensiveTests ? 1_000 : 2; + CompletableFuture head = new CompletableFuture<>(); + CompletableFuture tail = head; + for (int i = 0; i < n; i++) { + if (addDeadEnds) m.thenApply(tail, v -> v + 1); + tail = m.thenApply(tail, v -> v + 1); + if (addDeadEnds) m.applyToEither(tail, tail, v -> v + 1); + tail = m.applyToEither(tail, tail, v -> v + 1); + if (addDeadEnds) m.thenCombine(tail, tail, (v, w) -> v + 1); + tail = m.thenCombine(tail, tail, (v, w) -> v + 1); + } + head.complete(val); + assertEquals(val + 3 * n, (int) tail.join()); + }} + /** * A single CompletableFuture with many dependents. * A demo of scalability - runtime is O(n). */ public void testManyDependents() throws Throwable { - final int n = 1_000; + final int n = expensiveTests ? 1_000_000 : 10; final CompletableFuture head = new CompletableFuture<>(); final CompletableFuture complete = CompletableFuture.completedFuture((Void)null); final AtomicInteger count = new AtomicInteger(0); @@ -3987,6 +4227,78 @@ assertEquals(5 * 3 * n, count.get()); } + /** ant -Dvmoptions=-Xmx8m -Djsr166.expensiveTests=true -Djsr166.tckTestClass=CompletableFutureTest tck */ + public void testCoCompletionGarbageRetention() throws Throwable { + final int n = expensiveTests ? 1_000_000 : 10; + final CompletableFuture incomplete = new CompletableFuture<>(); + CompletableFuture f; + for (int i = 0; i < n; i++) { + f = new CompletableFuture<>(); + f.runAfterEither(incomplete, () -> {}); + f.complete(null); + + f = new CompletableFuture<>(); + f.acceptEither(incomplete, (x) -> {}); + f.complete(null); + + f = new CompletableFuture<>(); + f.applyToEither(incomplete, (x) -> x); + f.complete(null); + + f = new CompletableFuture<>(); + CompletableFuture.anyOf(new CompletableFuture[] { f, incomplete }); + f.complete(null); + } + + for (int i = 0; i < n; i++) { + f = new CompletableFuture<>(); + incomplete.runAfterEither(f, () -> {}); + f.complete(null); + + f = new CompletableFuture<>(); + incomplete.acceptEither(f, (x) -> {}); + f.complete(null); + + f = new CompletableFuture<>(); + incomplete.applyToEither(f, (x) -> x); + f.complete(null); + + f = new CompletableFuture<>(); + CompletableFuture.anyOf(new CompletableFuture[] { incomplete, f }); + f.complete(null); + } + } + + /* + * Tests below currently fail in stress mode due to memory retention. + * ant -Dvmoptions=-Xmx8m -Djsr166.expensiveTests=true -Djsr166.tckTestClass=CompletableFutureTest tck + */ + + /** Checks for garbage retention with anyOf. */ + public void testAnyOfGarbageRetention() throws Throwable { + for (Integer v : new Integer[] { 1, null }) + { + final int n = expensiveTests ? 100_000 : 10; + CompletableFuture[] fs + = (CompletableFuture[]) new CompletableFuture[100]; + for (int i = 0; i < fs.length; i++) + fs[i] = new CompletableFuture<>(); + fs[fs.length - 1].complete(v); + for (int i = 0; i < n; i++) + checkCompletedNormally(CompletableFuture.anyOf(fs), v); + }} + + /** Checks for garbage retention with allOf. */ + public void testCancelledAllOfGarbageRetention() throws Throwable { + final int n = expensiveTests ? 100_000 : 10; + CompletableFuture[] fs + = (CompletableFuture[]) new CompletableFuture[100]; + for (int i = 0; i < fs.length; i++) + fs[i] = new CompletableFuture<>(); + for (int i = 0; i < n; i++) + assertTrue(CompletableFuture.allOf(fs).cancel(false)); + } + // static U join(CompletionStage stage) { // CompletableFuture f = new CompletableFuture<>(); // stage.whenComplete((v, ex) -> { diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/test/java/util/concurrent/tck/JSR166TestCase.java --- a/jdk/test/java/util/concurrent/tck/JSR166TestCase.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/test/java/util/concurrent/tck/JSR166TestCase.java Mon Jul 18 09:38:08 2016 -0700 @@ -548,6 +548,13 @@ // Java9+ test classes if (atLeastJava9()) { String[] java9TestClassNames = { + "AtomicBoolean9Test", + "AtomicInteger9Test", + "AtomicIntegerArray9Test", + "AtomicLong9Test", + "AtomicLongArray9Test", + "AtomicReference9Test", + "AtomicReferenceArray9Test", "ExecutorCompletionService9Test", }; addNamedTestClasses(suite, java9TestClassNames); @@ -975,7 +982,11 @@ } } - /** Like Runnable, but with the freedom to throw anything */ + /** + * Like Runnable, but with the freedom to throw anything. + * junit folks had the same idea: + * http://junit.org/junit5/docs/snapshot/api/org/junit/gen5/api/Executable.html + */ interface Action { public void run() throws Throwable; } /** @@ -1006,6 +1017,15 @@ * Uninteresting threads are filtered out. */ static void dumpTestThreads() { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + try { + System.setSecurityManager(null); + } catch (SecurityException giveUp) { + return; + } + } + ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); System.err.println("------ stacktrace dump start ------"); for (ThreadInfo info : threadMXBean.dumpAllThreads(true, true)) { @@ -1023,6 +1043,8 @@ System.err.print(info); } System.err.println("------ stacktrace dump end ------"); + + if (sm != null) System.setSecurityManager(sm); } /** diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/test/java/util/concurrent/tck/StampedLockTest.java --- a/jdk/test/java/util/concurrent/tck/StampedLockTest.java Fri Jul 15 09:05:36 2016 -0700 +++ b/jdk/test/java/util/concurrent/tck/StampedLockTest.java Mon Jul 18 09:38:08 2016 -0700 @@ -745,28 +745,41 @@ public void testTryConvertToOptimisticRead() throws InterruptedException { StampedLock lock = new StampedLock(); long s, p; - s = 0L; - assertFalse((p = lock.tryConvertToOptimisticRead(s)) != 0L); + assertEquals(0L, lock.tryConvertToOptimisticRead(0L)); + assertTrue((s = lock.tryOptimisticRead()) != 0L); - assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L); + assertEquals(s, lock.tryConvertToOptimisticRead(s)); + assertTrue(lock.validate(s)); + + assertTrue((p = lock.readLock()) != 0L); + assertTrue((s = lock.tryOptimisticRead()) != 0L); + assertEquals(s, lock.tryConvertToOptimisticRead(s)); + assertTrue(lock.validate(s)); + lock.unlockRead(p); + assertTrue((s = lock.writeLock()) != 0L); assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L); assertTrue(lock.validate(p)); + assertTrue((s = lock.readLock()) != 0L); assertTrue(lock.validate(s)); assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L); assertTrue(lock.validate(p)); + assertTrue((s = lock.tryWriteLock()) != 0L); assertTrue(lock.validate(s)); assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L); assertTrue(lock.validate(p)); + assertTrue((s = lock.tryReadLock()) != 0L); assertTrue(lock.validate(s)); assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L); assertTrue(lock.validate(p)); + assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L); assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L); assertTrue(lock.validate(p)); + assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L); assertTrue(lock.validate(s)); assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L); @@ -780,39 +793,67 @@ public void testTryConvertToReadLock() throws InterruptedException { StampedLock lock = new StampedLock(); long s, p; - s = 0L; - assertFalse((p = lock.tryConvertToReadLock(s)) != 0L); + + assertFalse((p = lock.tryConvertToReadLock(0L)) != 0L); + assertTrue((s = lock.tryOptimisticRead()) != 0L); assertTrue((p = lock.tryConvertToReadLock(s)) != 0L); + assertTrue(lock.isReadLocked()); + assertEquals(1, lock.getReadLockCount()); lock.unlockRead(p); + + assertTrue((s = lock.tryOptimisticRead()) != 0L); + lock.readLock(); + assertTrue((p = lock.tryConvertToReadLock(s)) != 0L); + assertTrue(lock.isReadLocked()); + assertEquals(2, lock.getReadLockCount()); + lock.unlockRead(p); + lock.unlockRead(p); + assertTrue((s = lock.writeLock()) != 0L); assertTrue((p = lock.tryConvertToReadLock(s)) != 0L); assertTrue(lock.validate(p)); + assertTrue(lock.isReadLocked()); + assertEquals(1, lock.getReadLockCount()); lock.unlockRead(p); + assertTrue((s = lock.readLock()) != 0L); assertTrue(lock.validate(s)); - assertTrue((p = lock.tryConvertToReadLock(s)) != 0L); - assertTrue(lock.validate(p)); - lock.unlockRead(p); + assertEquals(s, lock.tryConvertToReadLock(s)); + assertTrue(lock.validate(s)); + assertTrue(lock.isReadLocked()); + assertEquals(1, lock.getReadLockCount()); + lock.unlockRead(s); + assertTrue((s = lock.tryWriteLock()) != 0L); assertTrue(lock.validate(s)); assertTrue((p = lock.tryConvertToReadLock(s)) != 0L); assertTrue(lock.validate(p)); + assertEquals(1, lock.getReadLockCount()); lock.unlockRead(p); + assertTrue((s = lock.tryReadLock()) != 0L); assertTrue(lock.validate(s)); - assertTrue((p = lock.tryConvertToReadLock(s)) != 0L); - assertTrue(lock.validate(p)); - lock.unlockRead(p); + assertEquals(s, lock.tryConvertToReadLock(s)); + assertTrue(lock.validate(s)); + assertTrue(lock.isReadLocked()); + assertEquals(1, lock.getReadLockCount()); + lock.unlockRead(s); + assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L); assertTrue((p = lock.tryConvertToReadLock(s)) != 0L); assertTrue(lock.validate(p)); + assertTrue(lock.isReadLocked()); + assertEquals(1, lock.getReadLockCount()); lock.unlockRead(p); + assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L); assertTrue(lock.validate(s)); - assertTrue((p = lock.tryConvertToReadLock(s)) != 0L); - assertTrue(lock.validate(p)); - lock.unlockRead(p); + assertEquals(s, lock.tryConvertToReadLock(s)); + assertTrue(lock.validate(s)); + assertTrue(lock.isReadLocked()); + assertEquals(1, lock.getReadLockCount()); + lock.unlockRead(s); } /** @@ -822,38 +863,52 @@ public void testTryConvertToWriteLock() throws InterruptedException { StampedLock lock = new StampedLock(); long s, p; - s = 0L; - assertFalse((p = lock.tryConvertToWriteLock(s)) != 0L); + + assertFalse((p = lock.tryConvertToWriteLock(0L)) != 0L); + assertTrue((s = lock.tryOptimisticRead()) != 0L); assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L); + assertTrue(lock.isWriteLocked()); lock.unlockWrite(p); + assertTrue((s = lock.writeLock()) != 0L); - assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L); - assertTrue(lock.validate(p)); - lock.unlockWrite(p); + assertEquals(s, lock.tryConvertToWriteLock(s)); + assertTrue(lock.validate(s)); + assertTrue(lock.isWriteLocked()); + lock.unlockWrite(s); + assertTrue((s = lock.readLock()) != 0L); assertTrue(lock.validate(s)); assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L); assertTrue(lock.validate(p)); + assertTrue(lock.isWriteLocked()); lock.unlockWrite(p); + assertTrue((s = lock.tryWriteLock()) != 0L); assertTrue(lock.validate(s)); - assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L); - assertTrue(lock.validate(p)); - lock.unlockWrite(p); + assertEquals(s, lock.tryConvertToWriteLock(s)); + assertTrue(lock.validate(s)); + assertTrue(lock.isWriteLocked()); + lock.unlockWrite(s); + assertTrue((s = lock.tryReadLock()) != 0L); assertTrue(lock.validate(s)); assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L); assertTrue(lock.validate(p)); + assertTrue(lock.isWriteLocked()); lock.unlockWrite(p); + assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L); assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L); assertTrue(lock.validate(p)); + assertTrue(lock.isWriteLocked()); lock.unlockWrite(p); + assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L); assertTrue(lock.validate(s)); assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L); assertTrue(lock.validate(p)); + assertTrue(lock.isWriteLocked()); lock.unlockWrite(p); } @@ -903,4 +958,124 @@ assertTrue(lock.tryLock()); } + /** + * Lock.newCondition throws UnsupportedOperationException + */ + public void testLockViewsDoNotSupportConditions() { + StampedLock sl = new StampedLock(); + assertThrows(UnsupportedOperationException.class, + () -> sl.asWriteLock().newCondition(), + () -> sl.asReadLock().newCondition(), + () -> sl.asReadWriteLock().writeLock().newCondition(), + () -> sl.asReadWriteLock().readLock().newCondition()); + } + + /** + * Passing optimistic read stamps to unlock operations result in + * IllegalMonitorStateException + */ + public void testCannotUnlockOptimisticReadStamps() { + Runnable[] actions = { + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryOptimisticRead(); + assertTrue(stamp != 0); + sl.unlockRead(stamp); + }, + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryOptimisticRead(); + sl.unlock(stamp); + }, + + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryOptimisticRead(); + sl.writeLock(); + sl.unlock(stamp); + }, + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryOptimisticRead(); + sl.readLock(); + sl.unlockRead(stamp); + }, + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryOptimisticRead(); + sl.readLock(); + sl.unlock(stamp); + }, + + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.writeLock()); + assertTrue(stamp != 0); + sl.writeLock(); + sl.unlockWrite(stamp); + }, + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.writeLock()); + sl.writeLock(); + sl.unlock(stamp); + }, + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.writeLock()); + sl.readLock(); + sl.unlockRead(stamp); + }, + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.writeLock()); + sl.readLock(); + sl.unlock(stamp); + }, + + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.readLock()); + assertTrue(stamp != 0); + sl.writeLock(); + sl.unlockWrite(stamp); + }, + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.readLock()); + sl.writeLock(); + sl.unlock(stamp); + }, + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.readLock()); + sl.readLock(); + sl.unlockRead(stamp); + }, + () -> { + StampedLock sl = new StampedLock(); + sl.readLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.readLock()); + assertTrue(stamp != 0); + sl.readLock(); + sl.unlockRead(stamp); + }, + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.readLock()); + sl.readLock(); + sl.unlock(stamp); + }, + () -> { + StampedLock sl = new StampedLock(); + sl.readLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.readLock()); + sl.readLock(); + sl.unlock(stamp); + }, + }; + + assertThrows(IllegalMonitorStateException.class, actions); + } + } diff -r 8bc25e077e83 -r f5d65fcf55e4 jdk/test/sun/net/www/protocol/http/NULLTargetInfoTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/sun/net/www/protocol/http/NULLTargetInfoTest.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,58 @@ +/* + * 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 8151788 + * @summary NullPointerException from ntlm.Client.type3 + * @modules java.base/com.sun.security.ntlm + * @run main NULLTargetInfoTest + */ +import com.sun.security.ntlm.Client; + +public class NULLTargetInfoTest { + + public static void main(String[] args) throws Exception { + Client c = new Client(null, "host", "user", "domain", "pass".toCharArray()); + c.type1(); + // this input does have the 0x800000 bit(NTLMSSP_NEGOTIATE_TARGET_INFO) set + // but after offset 40 all eight bytes are all zero which means there is no + // security buffer for target info. + byte[] type2 = hex( + "4E 54 4C 4D 53 53 50 00 02 00 00 00 00 00 00 00" + + "00 00 00 00 05 82 89 00 0B 87 81 B6 2D 6E 8B C1" + + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"); + byte[] nonce = new byte[10]; + c.type3(type2, nonce); + } + + private static byte[] hex(String str) { + str = str.replaceAll("\\s", ""); + byte[] response = new byte[str.length() / 2]; + int index = 0; + for (int i = 0; i < str.length(); i += 2) { + response[index++] = Integer.valueOf(str.substring(i, i + 2), 16).byteValue(); + } + return response; + } +} diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/.hgtags --- a/langtools/.hgtags Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/.hgtags Mon Jul 18 09:38:08 2016 -0700 @@ -369,3 +369,4 @@ 26aa3caa778eab1c931910149c414783ee83bce7 jdk-9+124 2d65e127e93d5ff0df61bf78e57d7f46a2f1edeb jdk-9+125 ea4eea2997b9e2f26cd7965839921710ff4065c8 jdk-9+126 +a42768b48cb0c5af9063e12093975baeeca3b5fa jdk-9+127 diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/make/src/classes/build/tools/listjdkinternals/ListJDKInternals.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/make/src/classes/build/tools/listjdkinternals/ListJDKInternals.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,195 @@ +/* + * 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. + */ + +package build.tools.listjdkinternals; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.UncheckedIOException; +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleFinder; +import java.lang.module.ModuleReference; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.BasicFileAttributes; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.stream.Collectors; + +/** + * Run this tool to generate the JDK internal APIs in the previous releases + * including platform-specific internal APIs. + */ +public class ListJDKInternals { + // Filter non-interesting JAR files + private final static List excludes = Arrays.asList( + "deploy.jar", + "javaws.jar", + "plugin.jar", + "cldrdata.jar", + "localedata.jar" + ); + private static void usage() { + System.out.println("ListJDKInternals [-o ] []*"); + } + + private static final Set EXPORTED_PACKAGES = new HashSet<>(); + + public static void main(String... args) throws IOException { + List paths = new ArrayList<>(); + Path outFile = null; + int i=0; + while (i < args.length) { + String arg = args[i++]; + if (arg.equals("-o")) { + outFile = Paths.get(args[i++]); + } else { + Path p = Paths.get(arg); + if (Files.notExists(p)) + throw new IllegalArgumentException(p + " not exist"); + paths.add(p); + } + } + if (paths.isEmpty()) { + usage(); + System.exit(1); + } + + // Get the exported APIs from the current JDK releases + Path javaHome = Paths.get(System.getProperty("java.home")); + ModuleFinder.ofSystem().findAll() + .stream() + .map(ModuleReference::descriptor) + .filter(md -> !md.name().equals("jdk.unsupported")) + .map(ModuleDescriptor::exports) + .flatMap(Set::stream) + .filter(exp -> !exp.isQualified()) + .map(ModuleDescriptor.Exports::source) + .forEach(EXPORTED_PACKAGES::add); + + ListJDKInternals listJDKInternals = new ListJDKInternals(paths); + if (outFile != null) { + try (OutputStream out = Files.newOutputStream(outFile); + PrintStream pw = new PrintStream(out)) { + listJDKInternals.write(pw); + } + } else { + listJDKInternals.write(System.out); + } + } + + private final Set packages = new HashSet<>(); + ListJDKInternals(List dirs) throws IOException { + for (Path p : dirs) { + packages.addAll(list(p)); + } + } + + private void write(PrintStream pw) { + pw.println("# This file is auto-generated by ListJDKInternals tool on " + + LocalDateTime.now().toString()); + packages.stream().sorted() + .forEach(pw::println); + } + + private Set list(Path javaHome) throws IOException { + Path jrt = javaHome.resolve("lib").resolve("modules"); + Path jre = javaHome.resolve("jre"); + + if (Files.exists(jrt)) { + return listModularRuntime(javaHome); + } else if (Files.exists(jre.resolve("lib").resolve("rt.jar"))) { + return listLegacyRuntime(javaHome); + } + throw new IllegalArgumentException("invalid " + javaHome); + } + + private Set listModularRuntime(Path javaHome) throws IOException { + Map env = new HashMap<>(); + env.put("java.home", javaHome.toString()); + FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), env); + Path root = fs.getPath("packages"); + return Files.walk(root, 1) + .map(Path::getFileName) + .map(Path::toString) + .filter(pn -> !EXPORTED_PACKAGES.contains(pn)) + .collect(Collectors.toSet()); + } + + private Set listLegacyRuntime(Path javaHome) throws IOException { + List dirs = new ArrayList<>(); + Path jre = javaHome.resolve("jre"); + Path lib = javaHome.resolve("lib"); + + dirs.add(jre.resolve("lib")); + dirs.add(jre.resolve("lib").resolve("ext")); + dirs.add(lib.resolve("tools.jar")); + dirs.add(lib.resolve("jconsole.jar")); + Set packages = new HashSet<>(); + for (Path d : dirs) { + Files.find(d, 1, (Path p, BasicFileAttributes attr) + -> p.getFileName().toString().endsWith(".jar") && + !excludes.contains(p.getFileName().toString())) + .map(ListJDKInternals::walkJarFile) + .flatMap(Set::stream) + .filter(pn -> !EXPORTED_PACKAGES.contains(pn)) + .forEach(packages::add); + } + return packages; + } + + static Set walkJarFile(Path jarfile) { + try (JarFile jf = new JarFile(jarfile.toFile())) { + return jf.stream() + .map(JarEntry::getName) + .filter(n -> n.endsWith(".class")) + .map(ListJDKInternals::toPackage) + .collect(Collectors.toSet()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + static String toPackage(String name) { + int i = name.lastIndexOf('/'); + if (i < 0) { + System.err.format("Warning: unnamed package %s%n", name); + } + return i >= 0 ? name.substring(0, i).replace("/", ".") : ""; + } +} diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java Mon Jul 18 09:38:08 2016 -0700 @@ -1152,10 +1152,6 @@ case TYP: if (sym.isLocal()) { mask = LocalClassFlags; - if (sym.name.isEmpty()) { // Anonymous class - // JLS: Anonymous classes are final. - implicit |= FINAL; - } if ((sym.owner.flags_field & STATIC) == 0 && (flags & ENUM) != 0) log.error(pos, "enums.must.be.static"); diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java Mon Jul 18 09:38:08 2016 -0700 @@ -1040,7 +1040,6 @@ inner.markAbstractIfNeeded(types); char flags = (char) adjustFlags(inner.flags_field); if ((flags & INTERFACE) != 0) flags |= ABSTRACT; // Interfaces are always ABSTRACT - if (inner.name.isEmpty()) flags &= ~FINAL; // Anonymous class: unset FINAL flag flags &= ~STRICTFP; //inner classes should not have the strictfp flag set. if (dumpInnerClassModifiers) { PrintWriter pw = log.getWriter(Log.WriterKind.ERROR); @@ -1679,7 +1678,6 @@ if ((flags & PROTECTED) != 0) flags |= PUBLIC; flags = flags & ClassFlags & ~STRICTFP; if ((flags & INTERFACE) == 0) flags |= ACC_SUPER; - if (c.isInner() && c.name.isEmpty()) flags &= ~FINAL; } if (dumpClassModifiers) { diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DCTree.java --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DCTree.java Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DCTree.java Mon Jul 18 09:38:08 2016 -0700 @@ -578,7 +578,7 @@ public final String signature; // The following are not directly exposed through ReferenceTree - // use DocTrees.getElement(TreePath,ReferenceTree) + // use DocTrees.getElement(DocTreePath) public final JCTree qualifierExpression; public final Name memberName; public final List paramTypes; diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeWriterImpl.java --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeWriterImpl.java Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeWriterImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -458,17 +458,6 @@ } /** - * Add gap between navigation bar elements. - * - * @param liNav the content tree to which the gap will be added - */ - protected void addNavGap(Content liNav) { - liNav.addContent(getSpace()); - liNav.addContent("|"); - liNav.addContent(getSpace()); - } - - /** * {@inheritDoc} */ @Override diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriterImpl.java --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriterImpl.java Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriterImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -764,17 +764,6 @@ } /** - * Add gap between navigation bar elements. - * - * @param liNav the content tree to which the gap will be added - */ - protected void addNavGap(Content liNav) { - liNav.addContent(getSpace()); - liNav.addContent("|"); - liNav.addContent(getSpace()); - } - - /** * Return the TypeElement being documented. * * @return the TypeElement being documented. diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java Mon Jul 18 09:38:08 2016 -0700 @@ -908,6 +908,17 @@ } /** + * Add gap between navigation bar elements. + * + * @param liNav the content tree to which the gap will be added + */ + protected void addNavGap(Content liNav) { + liNav.addContent(getSpace()); + liNav.addContent("|"); + liNav.addContent(getSpace()); + } + + /** * Get summary table header. * * @param header the header for the table diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleWriterImpl.java --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleWriterImpl.java Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleWriterImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -26,23 +26,28 @@ package jdk.javadoc.internal.doclets.formats.html; import java.io.*; +import java.util.ArrayList; +import java.util.EnumMap; import java.util.List; -import java.util.Set; +import java.util.Map; import javax.lang.model.element.ModuleElement; +import javax.lang.model.element.ModuleElement.DirectiveKind; import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; import com.sun.source.doctree.DocTree; - import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants; import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle; import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag; import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree; import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml; +import jdk.javadoc.internal.doclets.formats.html.markup.StringContent; import jdk.javadoc.internal.doclets.toolkit.Content; import jdk.javadoc.internal.doclets.toolkit.ModuleSummaryWriter; import jdk.javadoc.internal.doclets.toolkit.util.CommentHelper; import jdk.javadoc.internal.doclets.toolkit.util.DocPaths; +import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException; /** * Class to generate file for each module contents in the right-hand @@ -74,17 +79,25 @@ */ protected ModuleElement mdle; + private final Map> directiveMap + = new EnumMap<>(ModuleElement.DirectiveKind.class); + /** * The HTML tree for main tag. */ protected HtmlTree mainTree = HtmlTree.MAIN(); /** + * The HTML tree for section tag. + */ + protected HtmlTree sectionTree = HtmlTree.SECTION(); + + /** * Constructor to construct ModuleWriter object and to generate * "moduleName-summary.html" file. * * @param configuration the configuration of the doclet. - * @param module Module under consideration. + * @param mdle Module under consideration. * @param prevModule Previous module in the sorted array. * @param nextModule Next module in the sorted array. */ @@ -95,10 +108,13 @@ this.prevModule = prevModule; this.nextModule = nextModule; this.mdle = mdle; + generateDirectiveMap(); } /** - * {@inheritDoc} + * Get the module header. + * + * @param heading the heading for the section */ public Content getModuleHeader(String heading) { HtmlTree bodyTree = getBody(true, getWindowTitle(mdle.getQualifiedName().toString())); @@ -127,7 +143,7 @@ } /** - * {@inheritDoc} + * Get the content header. */ public Content getContentHeader() { HtmlTree div = new HtmlTree(HtmlTag.DIV); @@ -136,7 +152,7 @@ } /** - * {@inheritDoc} + * Get the summary section header. */ public Content getSummaryHeader() { HtmlTree li = new HtmlTree(HtmlTag.LI); @@ -145,7 +161,9 @@ } /** - * {@inheritDoc} + * Get the summary tree. + * + * @param summaryContentTree the content tree to be added to the summary tree. */ public Content getSummaryTree(Content summaryContentTree) { HtmlTree ul = HtmlTree.UL(HtmlStyle.blockList, summaryContentTree); @@ -153,18 +171,315 @@ } /** + * Generate the directive map for the directives on the module. + */ + public void generateDirectiveMap() { + for (ModuleElement.Directive d : mdle.getDirectives()) { + if (directiveMap.containsKey(d.getKind())) { + List dir = directiveMap.get(d.getKind()); + dir.add(d); + directiveMap.put(d.getKind(), dir); + } else { + List dir = new ArrayList<>(); + dir.add(d); + directiveMap.put(d.getKind(), dir); + } + } + } + + /** + * Add the summary header. + * + * @param startMarker the marker comment + * @param markerAnchor the marker anchor for the section + * @param heading the heading for the section + * @param htmltree the content tree to which the information is added + */ + public void addSummaryHeader(Content startMarker, SectionName markerAnchor, Content heading, Content htmltree) { + htmltree.addContent(startMarker); + htmltree.addContent(getMarkerAnchor(markerAnchor)); + htmltree.addContent(HtmlTree.HEADING(HtmlTag.H3, heading)); + } + + /** + * Add the summary for the module. + * + * @param text the table caption + * @param tableSummary the summary for the table + * @param htmltree the content tree to which the table will be added + * @param tableStyle the table style + * @param tableHeader the table header + * @param dirs the list of module directives + */ + public void addSummary(String text, String tableSummary, Content htmltree, HtmlStyle tableStyle, + List tableHeader, List dirs) { + Content table = (configuration.isOutputHtml5()) + ? HtmlTree.TABLE(tableStyle, getTableCaption(new RawHtml(text))) + : HtmlTree.TABLE(tableStyle, tableSummary, getTableCaption(new RawHtml(text))); + table.addContent(getSummaryTableHeader(tableHeader, "col")); + Content tbody = new HtmlTree(HtmlTag.TBODY); + addList(dirs, tbody); + table.addContent(tbody); + htmltree.addContent(table); + } + + /** + * Add the list of directives for the module. + * + * @param dirs the list of module directives + * @params tbody the content tree to which the list is added + */ + public void addList(List dirs, Content tbody) { + boolean altColor = true; + for (ModuleElement.Directive direct : dirs) { + DirectiveKind kind = direct.getKind(); + switch (kind) { + case REQUIRES: + addRequiresList((ModuleElement.RequiresDirective) direct, tbody, altColor); + break; + case EXPORTS: + addExportedPackagesList((ModuleElement.ExportsDirective) direct, tbody, altColor); + break; + case USES: + addUsesList((ModuleElement.UsesDirective) direct, tbody, altColor); + break; + case PROVIDES: + addProvidesList((ModuleElement.ProvidesDirective) direct, tbody, altColor); + break; + default: + throw new AssertionError("unknown directive kind: " + kind); + } + altColor = !altColor; + } + } + + /** + * {@inheritDoc} + */ + public void addModulesSummary(Content summaryContentTree) { + List dirs = directiveMap.get(DirectiveKind.REQUIRES); + if (dirs != null && !dirs.isEmpty()) { + HtmlTree li = new HtmlTree(HtmlTag.LI); + li.addStyle(HtmlStyle.blockList); + addSummaryHeader(HtmlConstants.START_OF_MODULES_SUMMARY, SectionName.MODULES, + getResource("doclet.navModules"), li); + String text = configuration.getText("doclet.Requires_Summary"); + String tableSummary = configuration.getText("doclet.Member_Table_Summary", + configuration.getText("doclet.Requires_Summary"), + configuration.getText("doclet.modules")); + addRequiresSummary(text, tableSummary, dirs, li); + HtmlTree ul = HtmlTree.UL(HtmlStyle.blockList, li); + summaryContentTree.addContent(ul); + } + } + + /** + * Add the requires summary for the module. + * + * @param text the table caption + * @param tableSummary the summary for the table + * @param dirs the list of module directives + * @param htmltree the content tree to which the table will be added + */ + public void addRequiresSummary(String text, String tableSummary, List dirs, + Content htmltree) { + addSummary(text, tableSummary, htmltree, HtmlStyle.requiresSummary, requiresTableHeader, dirs); + } + + /** + * Add the requires directive list for the module. + * + * @param direct the requires directive + * @param tbody the content tree to which the directive will be added + * @param altColor true if altColor style should be used or false if rowColor style should be used + */ + public void addRequiresList(ModuleElement.RequiresDirective direct, Content tbody, boolean altColor) { + ModuleElement m = direct.getDependency(); + Content moduleLinkContent = getModuleLink(m, new StringContent(m.getQualifiedName().toString())); + Content tdPackage = HtmlTree.TD(HtmlStyle.colFirst, moduleLinkContent); + HtmlTree tdSummary = new HtmlTree(HtmlTag.TD); + tdSummary.addStyle(HtmlStyle.colLast); + addSummaryComment(m, tdSummary); + HtmlTree tr = HtmlTree.TR(tdPackage); + tr.addContent(tdSummary); + tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor); + tbody.addContent(tr); + } + + /** * {@inheritDoc} */ - public void addPackagesSummary(Set packages, String text, - String tableSummary, Content summaryContentTree) { - Content table = (configuration.isOutputHtml5()) - ? HtmlTree.TABLE(HtmlStyle.overviewSummary, getTableCaption(new RawHtml(text))) - : HtmlTree.TABLE(HtmlStyle.overviewSummary, tableSummary, getTableCaption(new RawHtml(text))); - table.addContent(getSummaryTableHeader(packageTableHeader, "col")); - Content tbody = new HtmlTree(HtmlTag.TBODY); - addPackagesList(packages, tbody); - table.addContent(tbody); - summaryContentTree.addContent(table); + public void addPackagesSummary(Content summaryContentTree) { + List dirs = directiveMap.get(DirectiveKind.EXPORTS); + if (dirs != null && !dirs.isEmpty()) { + HtmlTree li = new HtmlTree(HtmlTag.LI); + li.addStyle(HtmlStyle.blockList); + addSummaryHeader(HtmlConstants.START_OF_PACKAGES_SUMMARY, SectionName.PACKAGES, + getResource("doclet.navPackages"), li); + String text = configuration.getText("doclet.Exported_Packages_Summary"); + String tableSummary = configuration.getText("doclet.Member_Table_Summary", + configuration.getText("doclet.Exported_Packages_Summary"), + configuration.getText("doclet.packages")); + addExportedPackagesSummary(text, tableSummary, dirs, li); + HtmlTree ul = HtmlTree.UL(HtmlStyle.blockList, li); + summaryContentTree.addContent(ul); + } + } + + /** + * Add the exported packages summary for the module. + * + * @param text the table caption + * @param tableSummary the summary for the table + * @param dirs the list of module directives + * @param htmltree the content tree to which the table will be added + */ + public void addExportedPackagesSummary(String text, String tableSummary, List dirs, + Content htmltree) { + addSummary(text, tableSummary, htmltree, HtmlStyle.packagesSummary, exportedPackagesTableHeader, dirs); + } + + /** + * Add the exported packages list for the module. + * + * @param direct the requires directive + * @param tbody the content tree to which the directive will be added + * @param altColor true if altColor style should be used or false if rowColor style should be used + */ + public void addExportedPackagesList(ModuleElement.ExportsDirective direct, Content tbody, boolean altColor) { + PackageElement pkg = direct.getPackage(); + Content pkgLinkContent = getPackageLink(pkg, new StringContent(utils.getPackageName(pkg))); + Content tdPackage = HtmlTree.TD(HtmlStyle.colFirst, pkgLinkContent); + HtmlTree tdModules = new HtmlTree(HtmlTag.TD); + tdModules.addStyle(HtmlStyle.colSecond); + List targetModules = direct.getTargetModules(); + if (targetModules != null) { + List mElements = direct.getTargetModules(); + for (int i = 0; i < mElements.size(); i++) { + if (i > 0) { + tdModules.addContent(new HtmlTree(HtmlTag.BR)); + } + ModuleElement m = mElements.get(i); + tdModules.addContent(new StringContent(m.getQualifiedName().toString())); + } + } else { + tdModules.addContent(configuration.getText("doclet.All_Modules")); + } + HtmlTree tdSummary = new HtmlTree(HtmlTag.TD); + tdSummary.addStyle(HtmlStyle.colLast); + addSummaryComment(pkg, tdSummary); + HtmlTree tr = HtmlTree.TR(tdPackage); + tr.addContent(tdModules); + tr.addContent(tdSummary); + tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor); + tbody.addContent(tr); + } + + /** + * {@inheritDoc} + */ + public void addServicesSummary(Content summaryContentTree) { + List usesDirs = directiveMap.get(DirectiveKind.USES); + List providesDirs = directiveMap.get(DirectiveKind.PROVIDES); + if ((usesDirs != null && !usesDirs.isEmpty()) || (providesDirs != null && !providesDirs.isEmpty())) { + HtmlTree li = new HtmlTree(HtmlTag.LI); + li.addStyle(HtmlStyle.blockList); + addSummaryHeader(HtmlConstants.START_OF_SERVICES_SUMMARY, SectionName.SERVICES, + getResource("doclet.navServices"), li); + String text; + String tableSummary; + if (usesDirs != null && !usesDirs.isEmpty()) { + text = configuration.getText("doclet.Uses_Summary"); + tableSummary = configuration.getText("doclet.Member_Table_Summary", + configuration.getText("doclet.Uses_Summary"), + configuration.getText("doclet.types")); + addUsesSummary(text, tableSummary, usesDirs, li); + } + if (providesDirs != null && !providesDirs.isEmpty()) { + text = configuration.getText("doclet.Provides_Summary"); + tableSummary = configuration.getText("doclet.Member_Table_Summary", + configuration.getText("doclet.Provides_Summary"), + configuration.getText("doclet.types")); + addProvidesSummary(text, tableSummary, providesDirs, li); + } + HtmlTree ul = HtmlTree.UL(HtmlStyle.blockList, li); + summaryContentTree.addContent(ul); + } + } + + /** + * Add the uses summary for the module. + * + * @param text the table caption + * @param tableSummary the summary for the table + * @param dirs the list of module directives + * @param htmltree the content tree to which the table will be added + */ + public void addUsesSummary(String text, String tableSummary, List dirs, + Content htmltree) { + addSummary(text, tableSummary, htmltree, HtmlStyle.usesSummary, usesTableHeader, dirs); + } + + /** + * Add the uses list for the module. + * + * @param direct the requires directive + * @param tbody the content tree to which the directive will be added + * @param altColor true if altColor style should be used or false if rowColor style should be used + */ + public void addUsesList(ModuleElement.UsesDirective direct, Content tbody, boolean altColor) { + TypeElement type = direct.getService(); + Content typeLinkContent = getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.PACKAGE, type)); + Content tdPackage = HtmlTree.TD(HtmlStyle.colFirst, typeLinkContent); + HtmlTree tdSummary = new HtmlTree(HtmlTag.TD); + tdSummary.addStyle(HtmlStyle.colLast); + addSummaryComment(type, tdSummary); + HtmlTree tr = HtmlTree.TR(tdPackage); + tr.addContent(tdSummary); + tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor); + tbody.addContent(tr); + } + + /** + * Add the provides summary for the module. + * + * @param text the table caption + * @param tableSummary the summary for the table + * @param dirs the list of module directives + * @param htmltree the content tree to which the table will be added + */ + public void addProvidesSummary(String text, String tableSummary, List dirs, + Content htmltree) { + addSummary(text, tableSummary, htmltree, HtmlStyle.providesSummary, providesTableHeader, dirs); + } + + /** + * Add the exported packages list for the module. + * + * @param direct the requires directive + * @param tbody the content tree to which the directive will be added + * @param altColor true if altColor style should be used or false if rowColor style should be used + */ + public void addProvidesList(ModuleElement.ProvidesDirective direct, Content tbody, boolean altColor) { + TypeElement impl = direct.getImplementation(); + TypeElement srv = direct.getService(); + Content implLinkContent = getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.PACKAGE, impl)); + Content srvLinkContent = getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.PACKAGE, srv)); + HtmlTree tdType = HtmlTree.TD(HtmlStyle.colFirst, srvLinkContent); + tdType.addContent(new HtmlTree(HtmlTag.BR)); + tdType.addContent("("); + HtmlTree implSpan = HtmlTree.SPAN(HtmlStyle.implementationLabel, getResource("doclet.Implementation")); + tdType.addContent(implSpan); + tdType.addContent(getSpace()); + tdType.addContent(implLinkContent); + tdType.addContent(")"); + HtmlTree tdDesc = new HtmlTree(HtmlTag.TD); + tdDesc.addStyle(HtmlStyle.colLast); + addSummaryComment(srv, tdDesc); + HtmlTree tr = HtmlTree.TR(tdType); + tr.addContent(tdDesc); + tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor); + tbody.addContent(tr); } /** @@ -196,29 +511,56 @@ } /** - * Adds list of packages in the package summary table. Generate link to each package. + * Add summary details to the navigation bar. * - * @param packages Packages to which link is to be generated - * @param tbody the documentation tree to which the list will be added + * @param subDiv the content tree to which the summary detail links will be added + */ + protected void addSummaryDetailLinks(Content subDiv) { + try { + Content div = HtmlTree.DIV(getNavSummaryLinks()); + subDiv.addContent(div); + } catch (Exception e) { + throw new DocletAbortException(e); + } + } + + /** + * Get summary links for navigation bar. + * + * @return the content tree for the navigation summary links */ - protected void addPackagesList(Set packages, Content tbody) { - boolean altColor = true; - for (PackageElement pkg : packages) { - if (pkg != null && !pkg.isUnnamed()) { - if (!(configuration.nodeprecated && utils.isDeprecated(pkg))) { - Content packageLinkContent = getPackageLink(pkg, getPackageName(pkg)); - Content tdPackage = HtmlTree.TD(HtmlStyle.colFirst, packageLinkContent); - HtmlTree tdSummary = new HtmlTree(HtmlTag.TD); - tdSummary.addStyle(HtmlStyle.colLast); - addSummaryComment(pkg, tdSummary); - HtmlTree tr = HtmlTree.TR(tdPackage); - tr.addContent(tdSummary); - tr.addStyle(altColor ? HtmlStyle.altColor : HtmlStyle.rowColor); - tbody.addContent(tr); - } - } - altColor = !altColor; - } + protected Content getNavSummaryLinks() throws Exception { + Content li = HtmlTree.LI(moduleSubNavLabel); + li.addContent(getSpace()); + Content ulNav = HtmlTree.UL(HtmlStyle.subNavList, li); + Content liNav = new HtmlTree(HtmlTag.LI); + liNav.addContent(!utils.getBody(mdle).isEmpty() && !configuration.nocomment + ? getHyperLink(SectionName.MODULE_DESCRIPTION, getResource("doclet.navModuleDescription")) + : getResource("doclet.navModuleDescription")); + addNavGap(liNav); + liNav.addContent(showDirectives(DirectiveKind.REQUIRES) + ? getHyperLink(SectionName.MODULES, getResource("doclet.navModules")) + : getResource("doclet.navModules")); + addNavGap(liNav); + liNav.addContent(showDirectives(DirectiveKind.EXPORTS) + ? getHyperLink(SectionName.PACKAGES, getResource("doclet.navPackages")) + : getResource("doclet.navPackages")); + addNavGap(liNav); + liNav.addContent((showDirectives(DirectiveKind.USES) || showDirectives(DirectiveKind.PROVIDES)) + ? getHyperLink(SectionName.SERVICES, getResource("doclet.navServices")) + : getResource("doclet.navServices")); + ulNav.addContent(liNav); + return ulNav; + } + + /** + * Return true if the directive should be displayed. + * + * @param dirKind the kind of directive for the module + * @return true if the directive should be displayed + */ + private boolean showDirectives(DirectiveKind dirKind) { + return directiveMap.get(dirKind) != null && !directiveMap.get(dirKind).isEmpty(); } /** diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SectionName.java --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SectionName.java Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SectionName.java Mon Jul 18 09:38:08 2016 -0700 @@ -54,6 +54,9 @@ METHODS_INHERITANCE("methods.inherited.from.class."), METHOD_SUMMARY("method.summary"), MODULE_DESCRIPTION("module.description"), + MODULES("modules.summary"), + PACKAGES("packages.summary"), + SERVICES("services.summary"), NAVBAR_BOTTOM("navbar.bottom"), NAVBAR_BOTTOM_FIRSTROW("navbar.bottom.firstrow"), NAVBAR_TOP("navbar.top"), diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlConstants.java --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlConstants.java Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlConstants.java Mon Jul 18 09:38:08 2016 -0700 @@ -70,6 +70,24 @@ new Comment("============ MODULE DESCRIPTION ==========="); /** + * Marker to identify start of modules summary. + */ + public static final Content START_OF_MODULES_SUMMARY = + new Comment("============ MODULES SUMMARY ==========="); + + /** + * Marker to identify start of packages summary. + */ + public static final Content START_OF_PACKAGES_SUMMARY = + new Comment("============ PACKAGES SUMMARY ==========="); + + /** + * Marker to identify start of services summary. + */ + public static final Content START_OF_SERVICES_SUMMARY = + new Comment("============ SERVICES SUMMARY ==========="); + + /** * Marker to identify start of class data. */ public static final Content START_OF_CLASS_DATA = diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyle.java --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyle.java Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyle.java Mon Jul 18 09:38:08 2016 -0700 @@ -49,6 +49,7 @@ colFirst, colLast, colOne, + colSecond, constantsSummary, constantValuesContainer, contentContainer, @@ -65,6 +66,7 @@ header, horizontal, footer, + implementationLabel, indexContainer, indexNav, inheritance, @@ -87,7 +89,10 @@ overviewSummary, packageHierarchyLabel, packageLabelInClass, + packagesSummary, paramLabel, + providesSummary, + requiresSummary, returnLabel, rightContainer, rightIframe, @@ -111,5 +116,6 @@ typeNameLabel, typeNameLink, typeSummary, - useSummary + useSummary, + usesSummary } diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlWriter.java --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlWriter.java Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlWriter.java Mon Jul 18 09:38:08 2016 -0700 @@ -77,6 +77,26 @@ protected final List packageTableHeader; /** + * Header for tables displaying modules and description.. + */ + protected final List requiresTableHeader; + + /** + * Header for tables displaying packages and description.. + */ + protected final List exportedPackagesTableHeader; + + /** + * Header for tables displaying types and description.. + */ + protected final List usesTableHeader; + + /** + * Header for tables displaying types and description.. + */ + protected final List providesTableHeader; + + /** * Summary for use tables displaying class and package use. */ protected final String useTableSummary; @@ -108,6 +128,8 @@ public final Content detailLabel; + public final Content moduleSubNavLabel; + public final Content framesLabel; public final Content noframesLabel; @@ -192,6 +214,19 @@ packageTableHeader = new ArrayList<>(); packageTableHeader.add(configuration.getText("doclet.Package")); packageTableHeader.add(configuration.getText("doclet.Description")); + requiresTableHeader = new ArrayList<>(); + requiresTableHeader.add(configuration.getText("doclet.Module")); + requiresTableHeader.add(configuration.getText("doclet.Description")); + exportedPackagesTableHeader = new ArrayList<>(); + exportedPackagesTableHeader.add(configuration.getText("doclet.Package")); + exportedPackagesTableHeader.add(configuration.getText("doclet.Module")); + exportedPackagesTableHeader.add(configuration.getText("doclet.Description")); + usesTableHeader = new ArrayList<>(); + usesTableHeader.add(configuration.getText("doclet.Type")); + usesTableHeader.add(configuration.getText("doclet.Description")); + providesTableHeader = new ArrayList<>(); + providesTableHeader.add(configuration.getText("doclet.Type")); + providesTableHeader.add(configuration.getText("doclet.Description")); useTableSummary = configuration.getText("doclet.Use_Table_Summary", configuration.getText("doclet.packages")); modifierTypeHeader = configuration.getText("doclet.0_and_1", @@ -208,6 +243,7 @@ nextclassLabel = getNonBreakResource("doclet.Next_Class"); summaryLabel = getResource("doclet.Summary"); detailLabel = getResource("doclet.Detail"); + moduleSubNavLabel = getResource("doclet.Module_Sub_Nav"); framesLabel = getResource("doclet.Frames"); noframesLabel = getNonBreakResource("doclet.No_Frames"); treeLabel = getResource("doclet.Tree"); diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties Mon Jul 18 09:38:08 2016 -0700 @@ -31,6 +31,11 @@ doclet.Href_Class_Or_Interface_Title=class or interface in {0} doclet.Summary=Summary: doclet.Detail=Detail: +doclet.Module_Sub_Nav=Module: +doclet.navModuleDescription=Description +doclet.navModules=Modules +doclet.navPackages=Packages +doclet.navServices=Services doclet.navNested=Nested doclet.navAnnotationTypeOptionalMember=Optional doclet.navAnnotationTypeRequiredMember=Required diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/ModuleSummaryWriter.java --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/ModuleSummaryWriter.java Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/ModuleSummaryWriter.java Mon Jul 18 09:38:08 2016 -0700 @@ -91,15 +91,25 @@ public abstract void addModuleTags(Content moduleContentTree); /** - * Adds the table of packages to the documentation tree. + * Adds the modules summary to the documentation tree. * - * @param packages the set of packages that should be added. - * @param label the label for this table. - * @param tableSummary the summary string for the table * @param summaryContentTree the content tree to which the summary will be added */ - public abstract void addPackagesSummary(Set packages, String label, - String tableSummary, Content summaryContentTree); + public abstract void addModulesSummary(Content summaryContentTree); + + /** + * Adds the packages summary to the documentation tree. + * + * @param summaryContentTree the content tree to which the summary will be added + */ + public abstract void addPackagesSummary(Content summaryContentTree); + + /** + * Adds the services summary to the documentation tree. + * + * @param summaryContentTree the content tree to which the summary will be added + */ + public abstract void addServicesSummary(Content summaryContentTree); /** * Adds the module content tree to the documentation tree. diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/ModuleSummaryBuilder.java --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/ModuleSummaryBuilder.java Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/ModuleSummaryBuilder.java Mon Jul 18 09:38:08 2016 -0700 @@ -167,23 +167,34 @@ } /** - * Build the module package summary. + * Build the modules summary. * * @param node the XML element that specifies which components to document * @param summaryContentTree the content tree to which the summaries will * be added */ - public void buildPackageSummary(XMLNode node, Content summaryContentTree) { - Set packages = configuration.modulePackages.get(mdle); - if (!packages.isEmpty()) { - String packageTableSummary - = configuration.getText("doclet.Member_Table_Summary", - configuration.getText("doclet.Package_Summary"), - configuration.getText("doclet.packages")); - moduleWriter.addPackagesSummary( - packages, configuration.getText("doclet.Package_Summary"), - packageTableSummary, summaryContentTree); + public void buildModulesSummary(XMLNode node, Content summaryContentTree) { + moduleWriter.addModulesSummary(summaryContentTree); + } + + /** + * Build the package summary. + * + * @param node the XML element that specifies which components to document + * @param summaryContentTree the content tree to which the summaries will be added + */ + public void buildPackagesSummary(XMLNode node, Content summaryContentTree) { + moduleWriter.addPackagesSummary(summaryContentTree); } + + /** + * Build the services summary. + * + * @param node the XML element that specifies which components to document + * @param summaryContentTree the content tree to which the summaries will be added + */ + public void buildServicesSummary(XMLNode node, Content summaryContentTree) { + moduleWriter.addServicesSummary(summaryContentTree); } /** diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclet.xml --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclet.xml Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclet.xml Mon Jul 18 09:38:08 2016 -0700 @@ -33,7 +33,9 @@

- + + + diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets.properties --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets.properties Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets.properties Mon Jul 18 09:38:08 2016 -0700 @@ -69,6 +69,10 @@ doclet.tag_misuse=Tag {0} cannot be used in {1} documentation. It can only be used in the following types of documentation: {2}. doclet.javafx_tag_misuse=Tags @propertyGetter, @propertySetter and @propertyDescription can only be used in JavaFX properties getters and setters. doclet.Package_Summary=Package Summary +doclet.Requires_Summary=Requires +doclet.Exported_Packages_Summary=Exported Packages +doclet.Uses_Summary=Uses +doclet.Provides_Summary=Provides doclet.Module_Summary=Module Summary doclet.Interface_Summary=Interface Summary doclet.Annotation_Types_Summary=Annotation Types Summary @@ -93,6 +97,7 @@ doclet.Packages=Packages doclet.packages=packages doclet.modules=modules +doclet.types=types doclet.All_Classes=All Classes doclet.All_Superinterfaces=All Superinterfaces: doclet.All_Implemented_Interfaces=All Implemented Interfaces: @@ -170,6 +175,7 @@ doclet.subinterfaces=subinterfaces doclet.Modifier=Modifier doclet.Type=Type +doclet.Implementation=Implementation: doclet.Types=Types doclet.Members=Members doclet.SearchTags=SearchTags diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/stylesheet.css --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/stylesheet.css Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/stylesheet.css Mon Jul 18 09:38:08 2016 -0700 @@ -412,18 +412,20 @@ /* Table styles */ -.overviewSummary, .memberSummary, .typeSummary, .useSummary, .constantsSummary, .deprecatedSummary { +.overviewSummary, .memberSummary, .typeSummary, .useSummary, .constantsSummary, .deprecatedSummary, +.requiresSummary, .packagesSummary, .providesSummary, .usesSummary { width:100%; border-spacing:0; border-left:1px solid #EEE; border-right:1px solid #EEE; border-bottom:1px solid #EEE; } -.overviewSummary, .memberSummary { +.overviewSummary, .memberSummary, .requiresSummary, .packagesSummary, .providesSummary, .usesSummary { padding:0px; } .overviewSummary caption, .memberSummary caption, .typeSummary caption, -.useSummary caption, .constantsSummary caption, .deprecatedSummary caption { +.useSummary caption, .constantsSummary caption, .deprecatedSummary caption, +.requiresSummary caption, .packagesSummary caption, .providesSummary caption, .usesSummary caption { position:relative; text-align:left; background-repeat:no-repeat; @@ -439,16 +441,26 @@ } .overviewSummary caption a:link, .memberSummary caption a:link, .typeSummary caption a:link, .useSummary caption a:link, .constantsSummary caption a:link, .deprecatedSummary caption a:link, +.requiresSummary caption a:link, .packagesSummary caption a:link, providesSummary caption a:link, +.usesSummary caption a:link, .overviewSummary caption a:hover, .memberSummary caption a:hover, .typeSummary caption a:hover, .useSummary caption a:hover, .constantsSummary caption a:hover, .deprecatedSummary caption a:hover, +.requiresSummary caption a:hover, .packagesSummary caption a:hover, providesSummary caption a:hover, +.usesSummary caption a:hover, .overviewSummary caption a:active, .memberSummary caption a:active, .typeSummary caption a:active, .useSummary caption a:active, .constantsSummary caption a:active, .deprecatedSummary caption a:active, +.requiresSummary caption a:active, .packagesSummary caption a:active, providesSummary caption a:active, +.usesSummary caption a:active, .overviewSummary caption a:visited, .memberSummary caption a:visited, .typeSummary caption a:visited, -.useSummary caption a:visited, .constantsSummary caption a:visited, .deprecatedSummary caption a:visited { +.useSummary caption a:visited, .constantsSummary caption a:visited, .deprecatedSummary caption a:visited +.requiresSummary caption a:visited, .packagesSummary caption a:visited, providesSummary caption a:visited, +.usesSummary caption a:visited { color:#FFFFFF; } .overviewSummary caption span, .memberSummary caption span, .typeSummary caption span, -.useSummary caption span, .constantsSummary caption span, .deprecatedSummary caption span { +.useSummary caption span, .constantsSummary caption span, .deprecatedSummary caption span, +.requiresSummary caption span, .packagesSummary caption span, .providesSummary caption span, +.usesSummary caption span { white-space:nowrap; padding-top:5px; padding-left:12px; @@ -491,7 +503,8 @@ display:inline; } .overviewSummary .tabEnd, .memberSummary .tabEnd, .typeSummary .tabEnd, -.useSummary .tabEnd, .constantsSummary .tabEnd, .deprecatedSummary .tabEnd { +.useSummary .tabEnd, .constantsSummary .tabEnd, .deprecatedSummary .tabEnd, +.requiresSummary .tabEnd, .packagesSummary .tabEnd, .providesSummary .tabEnd, .usesSummary .tabEnd { display:none; width:5px; position:relative; @@ -516,18 +529,19 @@ } .overviewSummary td, .memberSummary td, .typeSummary td, -.useSummary td, .constantsSummary td, .deprecatedSummary td { +.useSummary td, .constantsSummary td, .deprecatedSummary td, +.requiresSummary td, .packagesSummary td, .providesSummary td, .usesSummary td { text-align:left; padding:0px 0px 12px 10px; } -th.colOne, th.colFirst, th.colLast, .useSummary th, .constantsSummary th, -td.colOne, td.colFirst, td.colLast, .useSummary td, .constantsSummary td{ +th.colOne, th.colFirst, th.colSecond, th.colLast, .useSummary th, .constantsSummary th, .packagesSummary th, +td.colOne, td.colFirst, td.colSecond, td.colLast, .useSummary td, .constantsSummary td { vertical-align:top; padding-right:0px; padding-top:8px; padding-bottom:3px; } -th.colFirst, th.colLast, th.colOne, .constantsSummary th { +th.colFirst, th.colSecond, th.colLast, th.colOne, .constantsSummary th, .packagesSummary th { background:#dee3e9; text-align:left; padding:8px 3px 3px 7px; @@ -539,10 +553,19 @@ td.colLast, th.colLast { font-size:13px; } -td.colOne, th.colOne { +td.colOne, th.colOne, .constantsSummary th, .packagesSummary th { + font-size:13px; +} +.providesSummary th.colFirst, .providesSummary th.colLast, .providesSummary td.colFirst, +.providesSummary td.colLast { + white-space:normal; + width:50%; font-size:13px; } .overviewSummary td.colFirst, .overviewSummary th.colFirst, +.requiresSummary td.colFirst, .requiresSummary th.colFirst, +.packagesSummary td.colFirst, .packagesSummary td.colSecond, .packagesSummary th.colFirst, .packagesSummary th, +.usesSummary td.colFirst, .usesSummary th.colFirst, .useSummary td.colFirst, .useSummary th.colFirst, .overviewSummary td.colOne, .overviewSummary th.colOne, .memberSummary td.colFirst, .memberSummary th.colFirst, @@ -551,7 +574,7 @@ width:25%; vertical-align:top; } -td.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover { +td.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colSecond a:link, td.colSecond a:active, td.colSecond a:visited, td.colSecond a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover { font-weight:bold; } .tableSubHeadingColor { @@ -611,7 +634,7 @@ margin:3px 10px 2px 0px; color:#474747; } -.deprecatedLabel, .descfrmTypeLabel, .memberNameLabel, .memberNameLink, +.deprecatedLabel, .descfrmTypeLabel, .implementationLabel, .memberNameLabel, .memberNameLink, .moduleLabelInClass, .overrideSpecifyLabel, .packageLabelInClass, .packageHierarchyLabel, .paramLabel, .returnLabel, .seeLabel, .simpleTagLabel, .throwsLabel, .typeNameLabel, .typeNameLink, .searchTagLink { diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Analyzer.java --- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Analyzer.java Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Analyzer.java Mon Jul 18 09:38:08 2016 -0700 @@ -25,20 +25,19 @@ package com.sun.tools.jdeps; -import static com.sun.tools.jdeps.JdepsConfiguration.*; +import com.sun.tools.classfile.Dependency.Location; -import com.sun.tools.classfile.Dependency.Location; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.io.UncheckedIOException; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Map; -import java.util.MissingResourceException; import java.util.Objects; -import java.util.Optional; -import java.util.ResourceBundle; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -369,35 +368,29 @@ } } - static final JdkInternals REMOVED_JDK_INTERNALS = new JdkInternals(); + static final Jdk8Internals REMOVED_JDK_INTERNALS = new Jdk8Internals(); - static class JdkInternals extends Module { - private final String BUNDLE = "com.sun.tools.jdeps.resources.jdkinternals"; - - private final Set jdkinternals; - private final Set jdkUnsupportedClasses; - private JdkInternals() { + static class Jdk8Internals extends Module { + private final String JDK8_INTERNALS = "/com/sun/tools/jdeps/resources/jdk8_internals.txt"; + private final Set jdk8Internals; + private Jdk8Internals() { super("JDK removed internal API"); - - try { - ResourceBundle rb = ResourceBundle.getBundle(BUNDLE); - this.jdkinternals = rb.keySet(); - } catch (MissingResourceException e) { - throw new InternalError("Cannot find jdkinternals resource bundle"); + try (InputStream in = JdepsTask.class.getResourceAsStream(JDK8_INTERNALS); + BufferedReader reader = new BufferedReader(new InputStreamReader(in))) { + this.jdk8Internals = reader.lines() + .filter(ln -> !ln.startsWith("#")) + .collect(Collectors.toSet()); + } catch (IOException e) { + throw new UncheckedIOException(e); } - - this.jdkUnsupportedClasses = getUnsupportedClasses(); } public boolean contains(Location location) { - if (jdkUnsupportedClasses.contains(location.getName() + ".class")) { - return false; - } - String cn = location.getClassName(); int i = cn.lastIndexOf('.'); String pn = i > 0 ? cn.substring(0, i) : ""; - return jdkinternals.contains(cn) || jdkinternals.contains(pn); + + return jdk8Internals.contains(pn); } @Override @@ -414,25 +407,5 @@ public boolean isExported(String pn) { return false; } - - private Set getUnsupportedClasses() { - // jdk.unsupported may not be observable - Optional om = Profile.FULL_JRE.findModule(JDK_UNSUPPORTED); - if (om.isPresent()) { - return om.get().reader().entries(); - } - - // find from local run-time image - SystemModuleFinder system = new SystemModuleFinder(); - if (system.find(JDK_UNSUPPORTED).isPresent()) { - try { - return system.getClassReader(JDK_UNSUPPORTED).entries(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - return Collections.emptySet(); - } } } diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdk8_internals.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdk8_internals.txt Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,1029 @@ +# This file is auto-generated by ListJDKInternals tool on 2016-07-12T16:13:20.303865 +apple.applescript +apple.laf +apple.launcher +apple.security +com.apple.concurrent +com.apple.eawt +com.apple.eawt.event +com.apple.eio +com.apple.laf +com.apple.laf.resources +com.oracle.jrockit.jfr +com.oracle.jrockit.jfr.client +com.oracle.jrockit.jfr.management +com.oracle.security.ucrypto +com.oracle.util +com.oracle.webservices.internal.api +com.oracle.webservices.internal.api.databinding +com.oracle.webservices.internal.api.message +com.oracle.webservices.internal.impl.encoding +com.oracle.webservices.internal.impl.internalspi.encoding +com.oracle.xmlns.internal.webservices.jaxws_databinding +com.sun.accessibility.internal.resources +com.sun.activation.registries +com.sun.awt +com.sun.beans +com.sun.beans.decoder +com.sun.beans.editors +com.sun.beans.finder +com.sun.beans.infos +com.sun.beans.util +com.sun.codemodel.internal +com.sun.codemodel.internal.fmt +com.sun.codemodel.internal.util +com.sun.codemodel.internal.writer +com.sun.corba.se.impl.activation +com.sun.corba.se.impl.copyobject +com.sun.corba.se.impl.corba +com.sun.corba.se.impl.dynamicany +com.sun.corba.se.impl.encoding +com.sun.corba.se.impl.interceptors +com.sun.corba.se.impl.io +com.sun.corba.se.impl.ior +com.sun.corba.se.impl.ior.iiop +com.sun.corba.se.impl.javax.rmi +com.sun.corba.se.impl.javax.rmi.CORBA +com.sun.corba.se.impl.legacy.connection +com.sun.corba.se.impl.logging +com.sun.corba.se.impl.monitoring +com.sun.corba.se.impl.naming.cosnaming +com.sun.corba.se.impl.naming.namingutil +com.sun.corba.se.impl.naming.pcosnaming +com.sun.corba.se.impl.oa +com.sun.corba.se.impl.oa.poa +com.sun.corba.se.impl.oa.toa +com.sun.corba.se.impl.orb +com.sun.corba.se.impl.orbutil +com.sun.corba.se.impl.orbutil.closure +com.sun.corba.se.impl.orbutil.concurrent +com.sun.corba.se.impl.orbutil.fsm +com.sun.corba.se.impl.orbutil.graph +com.sun.corba.se.impl.orbutil.threadpool +com.sun.corba.se.impl.presentation.rmi +com.sun.corba.se.impl.protocol +com.sun.corba.se.impl.protocol.giopmsgheaders +com.sun.corba.se.impl.resolver +com.sun.corba.se.impl.transport +com.sun.corba.se.impl.util +com.sun.corba.se.internal.CosNaming +com.sun.corba.se.internal.Interceptors +com.sun.corba.se.internal.POA +com.sun.corba.se.internal.corba +com.sun.corba.se.internal.iiop +com.sun.corba.se.org.omg.CORBA +com.sun.corba.se.pept.broker +com.sun.corba.se.pept.encoding +com.sun.corba.se.pept.protocol +com.sun.corba.se.pept.transport +com.sun.corba.se.spi.activation +com.sun.corba.se.spi.activation.InitialNameServicePackage +com.sun.corba.se.spi.activation.LocatorPackage +com.sun.corba.se.spi.activation.RepositoryPackage +com.sun.corba.se.spi.copyobject +com.sun.corba.se.spi.encoding +com.sun.corba.se.spi.extension +com.sun.corba.se.spi.ior +com.sun.corba.se.spi.ior.iiop +com.sun.corba.se.spi.legacy.connection +com.sun.corba.se.spi.legacy.interceptor +com.sun.corba.se.spi.logging +com.sun.corba.se.spi.monitoring +com.sun.corba.se.spi.oa +com.sun.corba.se.spi.orb +com.sun.corba.se.spi.orbutil.closure +com.sun.corba.se.spi.orbutil.fsm +com.sun.corba.se.spi.orbutil.proxy +com.sun.corba.se.spi.orbutil.threadpool +com.sun.corba.se.spi.presentation.rmi +com.sun.corba.se.spi.protocol +com.sun.corba.se.spi.resolver +com.sun.corba.se.spi.servicecontext +com.sun.corba.se.spi.transport +com.sun.crypto.provider +com.sun.demo.jvmti.hprof +com.sun.deploy.uitoolkit.impl.fx +com.sun.deploy.uitoolkit.impl.fx.ui +com.sun.deploy.uitoolkit.impl.fx.ui.resources +com.sun.glass.events +com.sun.glass.events.mac +com.sun.glass.ui +com.sun.glass.ui.delegate +com.sun.glass.ui.gtk +com.sun.glass.ui.mac +com.sun.glass.ui.win +com.sun.glass.utils +com.sun.image.codec.jpeg +com.sun.imageio.plugins.bmp +com.sun.imageio.plugins.common +com.sun.imageio.plugins.gif +com.sun.imageio.plugins.jpeg +com.sun.imageio.plugins.png +com.sun.imageio.plugins.wbmp +com.sun.imageio.spi +com.sun.imageio.stream +com.sun.istack.internal +com.sun.istack.internal.localization +com.sun.istack.internal.logging +com.sun.istack.internal.tools +com.sun.java.accessibility +com.sun.java.accessibility.util.java.awt +com.sun.java.browser.dom +com.sun.java.browser.net +com.sun.java.swing +com.sun.java.swing.plaf.gtk +com.sun.java.swing.plaf.gtk.resources +com.sun.java.swing.plaf.motif +com.sun.java.swing.plaf.motif.resources +com.sun.java.swing.plaf.nimbus +com.sun.java.swing.plaf.windows +com.sun.java.swing.plaf.windows.resources +com.sun.java.util.jar.pack +com.sun.java_cup.internal.runtime +com.sun.javafx +com.sun.javafx.animation +com.sun.javafx.applet +com.sun.javafx.application +com.sun.javafx.beans +com.sun.javafx.beans.event +com.sun.javafx.binding +com.sun.javafx.charts +com.sun.javafx.collections +com.sun.javafx.css +com.sun.javafx.css.converters +com.sun.javafx.css.parser +com.sun.javafx.cursor +com.sun.javafx.effect +com.sun.javafx.embed +com.sun.javafx.event +com.sun.javafx.font +com.sun.javafx.font.coretext +com.sun.javafx.font.directwrite +com.sun.javafx.font.freetype +com.sun.javafx.font.t2k +com.sun.javafx.fxml +com.sun.javafx.fxml.builder +com.sun.javafx.fxml.expression +com.sun.javafx.geom +com.sun.javafx.geom.transform +com.sun.javafx.geometry +com.sun.javafx.iio +com.sun.javafx.iio.bmp +com.sun.javafx.iio.common +com.sun.javafx.iio.gif +com.sun.javafx.iio.ios +com.sun.javafx.iio.jpeg +com.sun.javafx.iio.png +com.sun.javafx.image +com.sun.javafx.image.impl +com.sun.javafx.jmx +com.sun.javafx.logging +com.sun.javafx.media +com.sun.javafx.menu +com.sun.javafx.perf +com.sun.javafx.print +com.sun.javafx.property +com.sun.javafx.property.adapter +com.sun.javafx.robot +com.sun.javafx.robot.impl +com.sun.javafx.runtime +com.sun.javafx.runtime.async +com.sun.javafx.runtime.eula +com.sun.javafx.scene +com.sun.javafx.scene.control +com.sun.javafx.scene.control.behavior +com.sun.javafx.scene.control.skin +com.sun.javafx.scene.control.skin.resources +com.sun.javafx.scene.input +com.sun.javafx.scene.layout.region +com.sun.javafx.scene.paint +com.sun.javafx.scene.shape +com.sun.javafx.scene.text +com.sun.javafx.scene.transform +com.sun.javafx.scene.traversal +com.sun.javafx.scene.web +com.sun.javafx.scene.web.behavior +com.sun.javafx.scene.web.skin +com.sun.javafx.sg.prism +com.sun.javafx.sg.prism.web +com.sun.javafx.stage +com.sun.javafx.text +com.sun.javafx.tk +com.sun.javafx.tk.quantum +com.sun.javafx.util +com.sun.javafx.webkit +com.sun.javafx.webkit.drt +com.sun.javafx.webkit.prism +com.sun.javafx.webkit.prism.theme +com.sun.javafx.webkit.theme +com.sun.jmx.defaults +com.sun.jmx.interceptor +com.sun.jmx.mbeanserver +com.sun.jmx.remote.internal +com.sun.jmx.remote.protocol.iiop +com.sun.jmx.remote.protocol.rmi +com.sun.jmx.remote.security +com.sun.jmx.remote.util +com.sun.jmx.snmp +com.sun.jmx.snmp.IPAcl +com.sun.jmx.snmp.agent +com.sun.jmx.snmp.daemon +com.sun.jmx.snmp.defaults +com.sun.jmx.snmp.internal +com.sun.jmx.snmp.mpm +com.sun.jmx.snmp.tasks +com.sun.jndi.cosnaming +com.sun.jndi.dns +com.sun.jndi.ldap +com.sun.jndi.ldap.ext +com.sun.jndi.ldap.pool +com.sun.jndi.ldap.sasl +com.sun.jndi.rmi.registry +com.sun.jndi.toolkit.corba +com.sun.jndi.toolkit.ctx +com.sun.jndi.toolkit.dir +com.sun.jndi.toolkit.url +com.sun.jndi.url.corbaname +com.sun.jndi.url.dns +com.sun.jndi.url.iiop +com.sun.jndi.url.iiopname +com.sun.jndi.url.ldap +com.sun.jndi.url.ldaps +com.sun.jndi.url.rmi +com.sun.management.jmx +com.sun.media.jfxmedia +com.sun.media.jfxmedia.control +com.sun.media.jfxmedia.effects +com.sun.media.jfxmedia.events +com.sun.media.jfxmedia.locator +com.sun.media.jfxmedia.logging +com.sun.media.jfxmedia.track +com.sun.media.jfxmediaimpl +com.sun.media.jfxmediaimpl.platform +com.sun.media.jfxmediaimpl.platform.gstreamer +com.sun.media.jfxmediaimpl.platform.ios +com.sun.media.jfxmediaimpl.platform.java +com.sun.media.jfxmediaimpl.platform.osx +com.sun.media.sound +com.sun.naming.internal +com.sun.net.ssl +com.sun.net.ssl.internal.ssl +com.sun.net.ssl.internal.www.protocol.https +com.sun.nio.file +com.sun.nio.zipfs +com.sun.openpisces +com.sun.org.apache.bcel.internal +com.sun.org.apache.bcel.internal.classfile +com.sun.org.apache.bcel.internal.generic +com.sun.org.apache.bcel.internal.util +com.sun.org.apache.regexp.internal +com.sun.org.apache.xalan.internal +com.sun.org.apache.xalan.internal.extensions +com.sun.org.apache.xalan.internal.lib +com.sun.org.apache.xalan.internal.res +com.sun.org.apache.xalan.internal.templates +com.sun.org.apache.xalan.internal.utils +com.sun.org.apache.xalan.internal.xslt +com.sun.org.apache.xalan.internal.xsltc +com.sun.org.apache.xalan.internal.xsltc.cmdline +com.sun.org.apache.xalan.internal.xsltc.cmdline.getopt +com.sun.org.apache.xalan.internal.xsltc.compiler +com.sun.org.apache.xalan.internal.xsltc.compiler.util +com.sun.org.apache.xalan.internal.xsltc.dom +com.sun.org.apache.xalan.internal.xsltc.runtime +com.sun.org.apache.xalan.internal.xsltc.runtime.output +com.sun.org.apache.xalan.internal.xsltc.trax +com.sun.org.apache.xalan.internal.xsltc.util +com.sun.org.apache.xerces.internal.dom +com.sun.org.apache.xerces.internal.dom.events +com.sun.org.apache.xerces.internal.impl +com.sun.org.apache.xerces.internal.impl.dtd +com.sun.org.apache.xerces.internal.impl.dtd.models +com.sun.org.apache.xerces.internal.impl.dv +com.sun.org.apache.xerces.internal.impl.dv.dtd +com.sun.org.apache.xerces.internal.impl.dv.util +com.sun.org.apache.xerces.internal.impl.dv.xs +com.sun.org.apache.xerces.internal.impl.io +com.sun.org.apache.xerces.internal.impl.msg +com.sun.org.apache.xerces.internal.impl.validation +com.sun.org.apache.xerces.internal.impl.xpath +com.sun.org.apache.xerces.internal.impl.xpath.regex +com.sun.org.apache.xerces.internal.impl.xs +com.sun.org.apache.xerces.internal.impl.xs.identity +com.sun.org.apache.xerces.internal.impl.xs.models +com.sun.org.apache.xerces.internal.impl.xs.opti +com.sun.org.apache.xerces.internal.impl.xs.traversers +com.sun.org.apache.xerces.internal.impl.xs.util +com.sun.org.apache.xerces.internal.jaxp +com.sun.org.apache.xerces.internal.jaxp.datatype +com.sun.org.apache.xerces.internal.jaxp.validation +com.sun.org.apache.xerces.internal.parsers +com.sun.org.apache.xerces.internal.util +com.sun.org.apache.xerces.internal.utils +com.sun.org.apache.xerces.internal.xinclude +com.sun.org.apache.xerces.internal.xni +com.sun.org.apache.xerces.internal.xni.grammars +com.sun.org.apache.xerces.internal.xni.parser +com.sun.org.apache.xerces.internal.xpointer +com.sun.org.apache.xerces.internal.xs +com.sun.org.apache.xerces.internal.xs.datatypes +com.sun.org.apache.xml.internal.dtm +com.sun.org.apache.xml.internal.dtm.ref +com.sun.org.apache.xml.internal.dtm.ref.dom2dtm +com.sun.org.apache.xml.internal.dtm.ref.sax2dtm +com.sun.org.apache.xml.internal.res +com.sun.org.apache.xml.internal.resolver +com.sun.org.apache.xml.internal.resolver.helpers +com.sun.org.apache.xml.internal.resolver.readers +com.sun.org.apache.xml.internal.resolver.tools +com.sun.org.apache.xml.internal.security +com.sun.org.apache.xml.internal.security.algorithms +com.sun.org.apache.xml.internal.security.algorithms.implementations +com.sun.org.apache.xml.internal.security.c14n +com.sun.org.apache.xml.internal.security.c14n.helper +com.sun.org.apache.xml.internal.security.c14n.implementations +com.sun.org.apache.xml.internal.security.encryption +com.sun.org.apache.xml.internal.security.exceptions +com.sun.org.apache.xml.internal.security.keys +com.sun.org.apache.xml.internal.security.keys.content +com.sun.org.apache.xml.internal.security.keys.content.keyvalues +com.sun.org.apache.xml.internal.security.keys.content.x509 +com.sun.org.apache.xml.internal.security.keys.keyresolver +com.sun.org.apache.xml.internal.security.keys.keyresolver.implementations +com.sun.org.apache.xml.internal.security.keys.storage +com.sun.org.apache.xml.internal.security.keys.storage.implementations +com.sun.org.apache.xml.internal.security.signature +com.sun.org.apache.xml.internal.security.signature.reference +com.sun.org.apache.xml.internal.security.transforms +com.sun.org.apache.xml.internal.security.transforms.implementations +com.sun.org.apache.xml.internal.security.transforms.params +com.sun.org.apache.xml.internal.security.utils +com.sun.org.apache.xml.internal.security.utils.resolver +com.sun.org.apache.xml.internal.security.utils.resolver.implementations +com.sun.org.apache.xml.internal.serialize +com.sun.org.apache.xml.internal.serializer +com.sun.org.apache.xml.internal.serializer.utils +com.sun.org.apache.xml.internal.utils +com.sun.org.apache.xml.internal.utils.res +com.sun.org.apache.xpath.internal +com.sun.org.apache.xpath.internal.axes +com.sun.org.apache.xpath.internal.compiler +com.sun.org.apache.xpath.internal.domapi +com.sun.org.apache.xpath.internal.functions +com.sun.org.apache.xpath.internal.jaxp +com.sun.org.apache.xpath.internal.objects +com.sun.org.apache.xpath.internal.operations +com.sun.org.apache.xpath.internal.patterns +com.sun.org.apache.xpath.internal.res +com.sun.org.glassfish.external.amx +com.sun.org.glassfish.external.arc +com.sun.org.glassfish.external.probe.provider +com.sun.org.glassfish.external.probe.provider.annotations +com.sun.org.glassfish.external.statistics +com.sun.org.glassfish.external.statistics.annotations +com.sun.org.glassfish.external.statistics.impl +com.sun.org.glassfish.gmbal +com.sun.org.glassfish.gmbal.util +com.sun.org.omg.CORBA +com.sun.org.omg.CORBA.ValueDefPackage +com.sun.org.omg.CORBA.portable +com.sun.org.omg.SendingContext +com.sun.org.omg.SendingContext.CodeBasePackage +com.sun.pisces +com.sun.prism +com.sun.prism.d3d +com.sun.prism.es2 +com.sun.prism.image +com.sun.prism.impl +com.sun.prism.impl.packrect +com.sun.prism.impl.paint +com.sun.prism.impl.ps +com.sun.prism.impl.shape +com.sun.prism.j2d +com.sun.prism.j2d.paint +com.sun.prism.j2d.print +com.sun.prism.paint +com.sun.prism.ps +com.sun.prism.shader +com.sun.prism.shape +com.sun.prism.sw +com.sun.rmi.rmid +com.sun.rowset +com.sun.rowset.internal +com.sun.rowset.providers +com.sun.scenario +com.sun.scenario.animation +com.sun.scenario.animation.shared +com.sun.scenario.effect +com.sun.scenario.effect.impl +com.sun.scenario.effect.impl.es2 +com.sun.scenario.effect.impl.hw +com.sun.scenario.effect.impl.hw.d3d +com.sun.scenario.effect.impl.prism +com.sun.scenario.effect.impl.prism.ps +com.sun.scenario.effect.impl.prism.sw +com.sun.scenario.effect.impl.state +com.sun.scenario.effect.impl.sw +com.sun.scenario.effect.impl.sw.java +com.sun.scenario.effect.impl.sw.sse +com.sun.scenario.effect.light +com.sun.security.cert.internal.x509 +com.sun.security.ntlm +com.sun.security.sasl +com.sun.security.sasl.digest +com.sun.security.sasl.gsskerb +com.sun.security.sasl.ntlm +com.sun.security.sasl.util +com.sun.swing.internal.plaf.basic.resources +com.sun.swing.internal.plaf.metal.resources +com.sun.swing.internal.plaf.synth.resources +com.sun.tools.classfile +com.sun.tools.corba.se.idl +com.sun.tools.corba.se.idl.constExpr +com.sun.tools.corba.se.idl.som.cff +com.sun.tools.corba.se.idl.som.idlemit +com.sun.tools.corba.se.idl.toJavaPortable +com.sun.tools.doclets.formats.html +com.sun.tools.doclets.formats.html.markup +com.sun.tools.doclets.formats.html.resources +com.sun.tools.doclets.internal.toolkit +com.sun.tools.doclets.internal.toolkit.builders +com.sun.tools.doclets.internal.toolkit.resources +com.sun.tools.doclets.internal.toolkit.taglets +com.sun.tools.doclets.internal.toolkit.util +com.sun.tools.doclets.internal.toolkit.util.links +com.sun.tools.doclint +com.sun.tools.doclint.resources +com.sun.tools.example.debug.expr +com.sun.tools.example.debug.tty +com.sun.tools.extcheck +com.sun.tools.hat +com.sun.tools.hat.internal.model +com.sun.tools.hat.internal.oql +com.sun.tools.hat.internal.parser +com.sun.tools.hat.internal.server +com.sun.tools.hat.internal.util +com.sun.tools.internal.jxc +com.sun.tools.internal.jxc.ap +com.sun.tools.internal.jxc.api +com.sun.tools.internal.jxc.api.impl.j2s +com.sun.tools.internal.jxc.gen.config +com.sun.tools.internal.jxc.model.nav +com.sun.tools.internal.ws +com.sun.tools.internal.ws.api +com.sun.tools.internal.ws.api.wsdl +com.sun.tools.internal.ws.processor +com.sun.tools.internal.ws.processor.generator +com.sun.tools.internal.ws.processor.model +com.sun.tools.internal.ws.processor.model.exporter +com.sun.tools.internal.ws.processor.model.java +com.sun.tools.internal.ws.processor.model.jaxb +com.sun.tools.internal.ws.processor.modeler +com.sun.tools.internal.ws.processor.modeler.annotation +com.sun.tools.internal.ws.processor.modeler.wsdl +com.sun.tools.internal.ws.processor.util +com.sun.tools.internal.ws.resources +com.sun.tools.internal.ws.spi +com.sun.tools.internal.ws.util +com.sun.tools.internal.ws.util.xml +com.sun.tools.internal.ws.wscompile +com.sun.tools.internal.ws.wscompile.plugin.at_generated +com.sun.tools.internal.ws.wsdl.document +com.sun.tools.internal.ws.wsdl.document.http +com.sun.tools.internal.ws.wsdl.document.jaxws +com.sun.tools.internal.ws.wsdl.document.mime +com.sun.tools.internal.ws.wsdl.document.schema +com.sun.tools.internal.ws.wsdl.document.soap +com.sun.tools.internal.ws.wsdl.framework +com.sun.tools.internal.ws.wsdl.parser +com.sun.tools.internal.xjc +com.sun.tools.internal.xjc.addon.accessors +com.sun.tools.internal.xjc.addon.at_generated +com.sun.tools.internal.xjc.addon.code_injector +com.sun.tools.internal.xjc.addon.episode +com.sun.tools.internal.xjc.addon.locator +com.sun.tools.internal.xjc.addon.sync +com.sun.tools.internal.xjc.api +com.sun.tools.internal.xjc.api.impl.s2j +com.sun.tools.internal.xjc.api.util +com.sun.tools.internal.xjc.generator.annotation.spec +com.sun.tools.internal.xjc.generator.bean +com.sun.tools.internal.xjc.generator.bean.field +com.sun.tools.internal.xjc.generator.util +com.sun.tools.internal.xjc.model +com.sun.tools.internal.xjc.model.nav +com.sun.tools.internal.xjc.outline +com.sun.tools.internal.xjc.reader +com.sun.tools.internal.xjc.reader.dtd +com.sun.tools.internal.xjc.reader.dtd.bindinfo +com.sun.tools.internal.xjc.reader.gbind +com.sun.tools.internal.xjc.reader.internalizer +com.sun.tools.internal.xjc.reader.relaxng +com.sun.tools.internal.xjc.reader.xmlschema +com.sun.tools.internal.xjc.reader.xmlschema.bindinfo +com.sun.tools.internal.xjc.reader.xmlschema.ct +com.sun.tools.internal.xjc.reader.xmlschema.parser +com.sun.tools.internal.xjc.runtime +com.sun.tools.internal.xjc.util +com.sun.tools.internal.xjc.writer +com.sun.tools.javac.api +com.sun.tools.javac.code +com.sun.tools.javac.comp +com.sun.tools.javac.file +com.sun.tools.javac.jvm +com.sun.tools.javac.main +com.sun.tools.javac.model +com.sun.tools.javac.nio +com.sun.tools.javac.parser +com.sun.tools.javac.processing +com.sun.tools.javac.resources +com.sun.tools.javac.sym +com.sun.tools.javac.tree +com.sun.tools.javac.util +com.sun.tools.javadoc.api +com.sun.tools.javadoc.resources +com.sun.tools.javah +com.sun.tools.javah.resources +com.sun.tools.javap +com.sun.tools.javap.resources +com.sun.tools.jdeps +com.sun.tools.jdeps.resources +com.sun.tools.jdi +com.sun.tools.jdi.resources +com.sun.tools.script.shell +com.sun.tracing +com.sun.tracing.dtrace +com.sun.webkit +com.sun.webkit.dom +com.sun.webkit.event +com.sun.webkit.graphics +com.sun.webkit.network +com.sun.webkit.network.about +com.sun.webkit.network.data +com.sun.webkit.perf +com.sun.webkit.plugin +com.sun.webkit.text +com.sun.xml.internal.bind +com.sun.xml.internal.bind.annotation +com.sun.xml.internal.bind.api +com.sun.xml.internal.bind.api.impl +com.sun.xml.internal.bind.marshaller +com.sun.xml.internal.bind.unmarshaller +com.sun.xml.internal.bind.util +com.sun.xml.internal.bind.v2 +com.sun.xml.internal.bind.v2.bytecode +com.sun.xml.internal.bind.v2.model.annotation +com.sun.xml.internal.bind.v2.model.core +com.sun.xml.internal.bind.v2.model.impl +com.sun.xml.internal.bind.v2.model.nav +com.sun.xml.internal.bind.v2.model.runtime +com.sun.xml.internal.bind.v2.model.util +com.sun.xml.internal.bind.v2.runtime +com.sun.xml.internal.bind.v2.runtime.output +com.sun.xml.internal.bind.v2.runtime.property +com.sun.xml.internal.bind.v2.runtime.reflect +com.sun.xml.internal.bind.v2.runtime.reflect.opt +com.sun.xml.internal.bind.v2.runtime.unmarshaller +com.sun.xml.internal.bind.v2.schemagen +com.sun.xml.internal.bind.v2.schemagen.episode +com.sun.xml.internal.bind.v2.schemagen.xmlschema +com.sun.xml.internal.bind.v2.util +com.sun.xml.internal.dtdparser +com.sun.xml.internal.fastinfoset +com.sun.xml.internal.fastinfoset.algorithm +com.sun.xml.internal.fastinfoset.alphabet +com.sun.xml.internal.fastinfoset.dom +com.sun.xml.internal.fastinfoset.org.apache.xerces.util +com.sun.xml.internal.fastinfoset.sax +com.sun.xml.internal.fastinfoset.stax +com.sun.xml.internal.fastinfoset.stax.events +com.sun.xml.internal.fastinfoset.stax.factory +com.sun.xml.internal.fastinfoset.stax.util +com.sun.xml.internal.fastinfoset.tools +com.sun.xml.internal.fastinfoset.util +com.sun.xml.internal.fastinfoset.vocab +com.sun.xml.internal.messaging.saaj +com.sun.xml.internal.messaging.saaj.client.p2p +com.sun.xml.internal.messaging.saaj.packaging.mime +com.sun.xml.internal.messaging.saaj.packaging.mime.internet +com.sun.xml.internal.messaging.saaj.packaging.mime.util +com.sun.xml.internal.messaging.saaj.soap +com.sun.xml.internal.messaging.saaj.soap.dynamic +com.sun.xml.internal.messaging.saaj.soap.impl +com.sun.xml.internal.messaging.saaj.soap.name +com.sun.xml.internal.messaging.saaj.soap.ver1_1 +com.sun.xml.internal.messaging.saaj.soap.ver1_2 +com.sun.xml.internal.messaging.saaj.util +com.sun.xml.internal.messaging.saaj.util.transform +com.sun.xml.internal.org.jvnet.fastinfoset +com.sun.xml.internal.org.jvnet.fastinfoset.sax +com.sun.xml.internal.org.jvnet.fastinfoset.sax.helpers +com.sun.xml.internal.org.jvnet.fastinfoset.stax +com.sun.xml.internal.org.jvnet.mimepull +com.sun.xml.internal.org.jvnet.staxex +com.sun.xml.internal.rngom.ast.builder +com.sun.xml.internal.rngom.ast.om +com.sun.xml.internal.rngom.ast.util +com.sun.xml.internal.rngom.binary +com.sun.xml.internal.rngom.binary.visitor +com.sun.xml.internal.rngom.digested +com.sun.xml.internal.rngom.dt +com.sun.xml.internal.rngom.dt.builtin +com.sun.xml.internal.rngom.nc +com.sun.xml.internal.rngom.parse +com.sun.xml.internal.rngom.parse.compact +com.sun.xml.internal.rngom.parse.host +com.sun.xml.internal.rngom.parse.xml +com.sun.xml.internal.rngom.util +com.sun.xml.internal.rngom.xml.sax +com.sun.xml.internal.rngom.xml.util +com.sun.xml.internal.stream +com.sun.xml.internal.stream.buffer +com.sun.xml.internal.stream.buffer.sax +com.sun.xml.internal.stream.buffer.stax +com.sun.xml.internal.stream.dtd +com.sun.xml.internal.stream.dtd.nonvalidating +com.sun.xml.internal.stream.events +com.sun.xml.internal.stream.util +com.sun.xml.internal.stream.writers +com.sun.xml.internal.txw2 +com.sun.xml.internal.txw2.annotation +com.sun.xml.internal.txw2.output +com.sun.xml.internal.ws +com.sun.xml.internal.ws.addressing +com.sun.xml.internal.ws.addressing.model +com.sun.xml.internal.ws.addressing.policy +com.sun.xml.internal.ws.addressing.v200408 +com.sun.xml.internal.ws.api +com.sun.xml.internal.ws.api.addressing +com.sun.xml.internal.ws.api.client +com.sun.xml.internal.ws.api.config.management +com.sun.xml.internal.ws.api.config.management.policy +com.sun.xml.internal.ws.api.databinding +com.sun.xml.internal.ws.api.fastinfoset +com.sun.xml.internal.ws.api.ha +com.sun.xml.internal.ws.api.handler +com.sun.xml.internal.ws.api.message +com.sun.xml.internal.ws.api.message.saaj +com.sun.xml.internal.ws.api.message.stream +com.sun.xml.internal.ws.api.model +com.sun.xml.internal.ws.api.model.soap +com.sun.xml.internal.ws.api.model.wsdl +com.sun.xml.internal.ws.api.model.wsdl.editable +com.sun.xml.internal.ws.api.pipe +com.sun.xml.internal.ws.api.pipe.helper +com.sun.xml.internal.ws.api.policy +com.sun.xml.internal.ws.api.policy.subject +com.sun.xml.internal.ws.api.server +com.sun.xml.internal.ws.api.streaming +com.sun.xml.internal.ws.api.wsdl.parser +com.sun.xml.internal.ws.api.wsdl.writer +com.sun.xml.internal.ws.assembler +com.sun.xml.internal.ws.assembler.dev +com.sun.xml.internal.ws.assembler.jaxws +com.sun.xml.internal.ws.binding +com.sun.xml.internal.ws.client +com.sun.xml.internal.ws.client.dispatch +com.sun.xml.internal.ws.client.sei +com.sun.xml.internal.ws.commons.xmlutil +com.sun.xml.internal.ws.config.management.policy +com.sun.xml.internal.ws.config.metro.dev +com.sun.xml.internal.ws.config.metro.util +com.sun.xml.internal.ws.db +com.sun.xml.internal.ws.db.glassfish +com.sun.xml.internal.ws.developer +com.sun.xml.internal.ws.dump +com.sun.xml.internal.ws.encoding +com.sun.xml.internal.ws.encoding.fastinfoset +com.sun.xml.internal.ws.encoding.policy +com.sun.xml.internal.ws.encoding.soap +com.sun.xml.internal.ws.encoding.soap.streaming +com.sun.xml.internal.ws.encoding.xml +com.sun.xml.internal.ws.fault +com.sun.xml.internal.ws.handler +com.sun.xml.internal.ws.message +com.sun.xml.internal.ws.message.jaxb +com.sun.xml.internal.ws.message.saaj +com.sun.xml.internal.ws.message.source +com.sun.xml.internal.ws.message.stream +com.sun.xml.internal.ws.model +com.sun.xml.internal.ws.model.soap +com.sun.xml.internal.ws.model.wsdl +com.sun.xml.internal.ws.org.objectweb.asm +com.sun.xml.internal.ws.policy +com.sun.xml.internal.ws.policy.jaxws +com.sun.xml.internal.ws.policy.jaxws.spi +com.sun.xml.internal.ws.policy.privateutil +com.sun.xml.internal.ws.policy.sourcemodel +com.sun.xml.internal.ws.policy.sourcemodel.attach +com.sun.xml.internal.ws.policy.sourcemodel.wspolicy +com.sun.xml.internal.ws.policy.spi +com.sun.xml.internal.ws.policy.subject +com.sun.xml.internal.ws.protocol.soap +com.sun.xml.internal.ws.protocol.xml +com.sun.xml.internal.ws.resources +com.sun.xml.internal.ws.runtime.config +com.sun.xml.internal.ws.server +com.sun.xml.internal.ws.server.provider +com.sun.xml.internal.ws.server.sei +com.sun.xml.internal.ws.spi +com.sun.xml.internal.ws.spi.db +com.sun.xml.internal.ws.streaming +com.sun.xml.internal.ws.transport +com.sun.xml.internal.ws.transport.http +com.sun.xml.internal.ws.transport.http.client +com.sun.xml.internal.ws.transport.http.server +com.sun.xml.internal.ws.util +com.sun.xml.internal.ws.util.exception +com.sun.xml.internal.ws.util.pipe +com.sun.xml.internal.ws.util.xml +com.sun.xml.internal.ws.wsdl +com.sun.xml.internal.ws.wsdl.parser +com.sun.xml.internal.ws.wsdl.writer +com.sun.xml.internal.ws.wsdl.writer.document +com.sun.xml.internal.ws.wsdl.writer.document.http +com.sun.xml.internal.ws.wsdl.writer.document.soap +com.sun.xml.internal.ws.wsdl.writer.document.soap12 +com.sun.xml.internal.ws.wsdl.writer.document.xsd +com.sun.xml.internal.xsom +com.sun.xml.internal.xsom.impl +com.sun.xml.internal.xsom.impl.parser +com.sun.xml.internal.xsom.impl.parser.state +com.sun.xml.internal.xsom.impl.scd +com.sun.xml.internal.xsom.impl.util +com.sun.xml.internal.xsom.parser +com.sun.xml.internal.xsom.util +com.sun.xml.internal.xsom.visitor +java.awt.dnd.peer +java.awt.peer +javafx.embed.swt +jdk +jdk.internal.cmm +jdk.internal.dynalink +jdk.internal.dynalink.beans +jdk.internal.dynalink.linker +jdk.internal.dynalink.support +jdk.internal.instrumentation +jdk.internal.org.objectweb.asm +jdk.internal.org.objectweb.asm.commons +jdk.internal.org.objectweb.asm.signature +jdk.internal.org.objectweb.asm.tree +jdk.internal.org.objectweb.asm.tree.analysis +jdk.internal.org.objectweb.asm.util +jdk.internal.org.xml.sax +jdk.internal.org.xml.sax.helpers +jdk.internal.util.xml +jdk.internal.util.xml.impl +jdk.jfr.events +jdk.management.resource.internal +jdk.management.resource.internal.inst +jdk.nashorn.internal +jdk.nashorn.internal.codegen +jdk.nashorn.internal.codegen.types +jdk.nashorn.internal.ir +jdk.nashorn.internal.ir.annotations +jdk.nashorn.internal.ir.debug +jdk.nashorn.internal.ir.visitor +jdk.nashorn.internal.lookup +jdk.nashorn.internal.objects +jdk.nashorn.internal.objects.annotations +jdk.nashorn.internal.parser +jdk.nashorn.internal.runtime +jdk.nashorn.internal.runtime.arrays +jdk.nashorn.internal.runtime.events +jdk.nashorn.internal.runtime.linker +jdk.nashorn.internal.runtime.logging +jdk.nashorn.internal.runtime.options +jdk.nashorn.internal.runtime.regexp +jdk.nashorn.internal.runtime.regexp.joni +jdk.nashorn.internal.runtime.regexp.joni.ast +jdk.nashorn.internal.runtime.regexp.joni.constants +jdk.nashorn.internal.runtime.regexp.joni.encoding +jdk.nashorn.internal.runtime.regexp.joni.exception +jdk.nashorn.internal.scripts +jdk.nashorn.tools +oracle.jrockit.jfr +oracle.jrockit.jfr.events +oracle.jrockit.jfr.jdkevents +oracle.jrockit.jfr.jdkevents.throwabletransform +oracle.jrockit.jfr.openmbean +oracle.jrockit.jfr.parser +oracle.jrockit.jfr.settings +oracle.jrockit.jfr.tools +org.jcp.xml.dsig.internal +org.jcp.xml.dsig.internal.dom +org.omg.stub.javax.management.remote.rmi +org.relaxng.datatype +org.relaxng.datatype.helpers +sun.applet +sun.applet.resources +sun.audio +sun.awt +sun.awt.X11 +sun.awt.datatransfer +sun.awt.dnd +sun.awt.event +sun.awt.geom +sun.awt.im +sun.awt.image +sun.awt.image.codec +sun.awt.motif +sun.awt.resources +sun.awt.shell +sun.awt.util +sun.awt.windows +sun.corba +sun.dc +sun.dc.path +sun.dc.pr +sun.font +sun.instrument +sun.invoke +sun.invoke.anon +sun.invoke.empty +sun.invoke.util +sun.io +sun.java2d +sun.java2d.cmm +sun.java2d.cmm.kcms +sun.java2d.cmm.lcms +sun.java2d.d3d +sun.java2d.jules +sun.java2d.loops +sun.java2d.opengl +sun.java2d.pipe +sun.java2d.pipe.hw +sun.java2d.pisces +sun.java2d.windows +sun.java2d.x11 +sun.java2d.xr +sun.jvmstat.monitor +sun.jvmstat.monitor.event +sun.jvmstat.monitor.remote +sun.jvmstat.perfdata.monitor +sun.jvmstat.perfdata.monitor.protocol.file +sun.jvmstat.perfdata.monitor.protocol.local +sun.jvmstat.perfdata.monitor.protocol.rmi +sun.jvmstat.perfdata.monitor.v1_0 +sun.jvmstat.perfdata.monitor.v2_0 +sun.launcher +sun.launcher.resources +sun.lwawt +sun.lwawt.macosx +sun.management +sun.management.counter +sun.management.counter.perf +sun.management.jdp +sun.management.jmxremote +sun.management.resources +sun.management.snmp +sun.management.snmp.jvminstr +sun.management.snmp.jvmmib +sun.management.snmp.util +sun.misc +sun.misc.resources +sun.net +sun.net.dns +sun.net.ftp +sun.net.ftp.impl +sun.net.httpserver +sun.net.idn +sun.net.sdp +sun.net.smtp +sun.net.spi +sun.net.spi.nameservice +sun.net.spi.nameservice.dns +sun.net.util +sun.net.www +sun.net.www.content.audio +sun.net.www.content.image +sun.net.www.content.text +sun.net.www.http +sun.net.www.protocol.file +sun.net.www.protocol.ftp +sun.net.www.protocol.http +sun.net.www.protocol.http.logging +sun.net.www.protocol.http.ntlm +sun.net.www.protocol.http.spnego +sun.net.www.protocol.https +sun.net.www.protocol.jar +sun.net.www.protocol.mailto +sun.net.www.protocol.netdoc +sun.nio +sun.nio.ch +sun.nio.ch.sctp +sun.nio.cs +sun.nio.cs.ext +sun.nio.fs +sun.print +sun.print.resources +sun.reflect +sun.reflect.annotation +sun.reflect.generics.factory +sun.reflect.generics.parser +sun.reflect.generics.reflectiveObjects +sun.reflect.generics.repository +sun.reflect.generics.scope +sun.reflect.generics.tree +sun.reflect.generics.visitor +sun.reflect.misc +sun.rmi.log +sun.rmi.registry +sun.rmi.rmic +sun.rmi.rmic.iiop +sun.rmi.rmic.newrmic +sun.rmi.rmic.newrmic.jrmp +sun.rmi.runtime +sun.rmi.server +sun.rmi.transport +sun.rmi.transport.proxy +sun.rmi.transport.tcp +sun.security.acl +sun.security.action +sun.security.ec +sun.security.internal.interfaces +sun.security.internal.spec +sun.security.jca +sun.security.jgss +sun.security.jgss.krb5 +sun.security.jgss.spi +sun.security.jgss.spnego +sun.security.jgss.wrapper +sun.security.krb5 +sun.security.krb5.internal +sun.security.krb5.internal.ccache +sun.security.krb5.internal.crypto +sun.security.krb5.internal.crypto.dk +sun.security.krb5.internal.ktab +sun.security.krb5.internal.rcache +sun.security.krb5.internal.tools +sun.security.krb5.internal.util +sun.security.mscapi +sun.security.pkcs +sun.security.pkcs10 +sun.security.pkcs11 +sun.security.pkcs11.wrapper +sun.security.pkcs12 +sun.security.provider +sun.security.provider.certpath +sun.security.provider.certpath.ldap +sun.security.provider.certpath.ssl +sun.security.rsa +sun.security.smartcardio +sun.security.ssl +sun.security.ssl.krb5 +sun.security.timestamp +sun.security.tools +sun.security.tools.jarsigner +sun.security.tools.keytool +sun.security.tools.policytool +sun.security.util +sun.security.validator +sun.security.x509 +sun.swing +sun.swing.icon +sun.swing.plaf +sun.swing.plaf.synth +sun.swing.plaf.windows +sun.swing.table +sun.swing.text +sun.swing.text.html +sun.text +sun.text.bidi +sun.text.normalizer +sun.text.resources +sun.text.resources.en +sun.tools.asm +sun.tools.attach +sun.tools.jar +sun.tools.jar.resources +sun.tools.java +sun.tools.javac +sun.tools.jcmd +sun.tools.jconsole +sun.tools.jconsole.inspector +sun.tools.jinfo +sun.tools.jmap +sun.tools.jps +sun.tools.jstack +sun.tools.jstat +sun.tools.jstatd +sun.tools.native2ascii +sun.tools.native2ascii.resources +sun.tools.serialver +sun.tools.tree +sun.tools.util +sun.tracing +sun.tracing.dtrace +sun.usagetracker +sun.util +sun.util.calendar +sun.util.cldr +sun.util.locale +sun.util.locale.provider +sun.util.logging +sun.util.logging.resources +sun.util.resources +sun.util.resources.en +sun.util.spi +sun.util.xml diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties --- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties Mon Jul 18 09:38:08 2016 -0700 @@ -28,7 +28,7 @@ sun.security.x509.X500Name=Use javax.security.auth.x500.X500Principal @since 1.4 sun.tools.jar=Use java.util.jar or jar tool @since 1.2 # Internal APIs removed in JDK 9 -com.apple.eawt=Use java.awt.desktop and JEP 272 @since 9 +com.apple.eawt=Use java.awt.Desktop and JEP 272 @since 9 com.apple.concurrent=Removed. See https://bugs.openjdk.java.net/browse/JDK-8148187 com.sun.image.codec.jpeg=Use javax.imageio @since 1.4 sun.awt.image.codec=Use javax.imageio @since 1.4 diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/test/ProblemList.txt --- a/langtools/test/ProblemList.txt Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/test/ProblemList.txt Mon Jul 18 09:38:08 2016 -0700 @@ -27,9 +27,6 @@ # # javadoc -com/sun/javadoc/testTypeAnnotations/TestTypeAnnotations.java 8006735 generic-all output type annotations in javadoc -com/sun/javadoc/typeAnnotations/smoke/TestSmoke.java 8013406 generic-all Test cases fail in javadoc test TestSmoke.java - jdk/javadoc/tool/6176978/T6176978.java 8152049 generic-all no longer applicable, should delete jdk/javadoc/tool/InlineTagsWithBraces.java 8152050 generic-all API, re-evaluate @bold, @maybe causes doclint to throw up. jdk/javadoc/tool/LangVers.java 8152051 generic-all API, re-evaluate, unsure of this test. diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/test/com/sun/javadoc/testTypeAnnotations/TestTypeAnnotations.java --- a/langtools/test/com/sun/javadoc/testTypeAnnotations/TestTypeAnnotations.java Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/test/com/sun/javadoc/testTypeAnnotations/TestTypeAnnotations.java Mon Jul 18 09:38:08 2016 -0700 @@ -28,7 +28,6 @@ * @author Bhavesh Patel * @library ../lib * @modules jdk.javadoc - * @ignore 8006735 output type annotations in javadoc * @build JavadocTester * @run main TestTypeAnnotations */ diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/test/com/sun/javadoc/typeAnnotations/smoke/TestSmoke.java --- a/langtools/test/com/sun/javadoc/typeAnnotations/smoke/TestSmoke.java Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/test/com/sun/javadoc/typeAnnotations/smoke/TestSmoke.java Mon Jul 18 09:38:08 2016 -0700 @@ -29,7 +29,6 @@ * @author Mahmood Ali * @library ../../lib * @modules jdk.javadoc - * @ignore * @build JavadocTester * @run main TestSmoke */ diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/test/jdk/javadoc/doclet/testModules/TestModules.java --- a/langtools/test/jdk/javadoc/doclet/testModules/TestModules.java Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/test/jdk/javadoc/doclet/testModules/TestModules.java Mon Jul 18 09:38:08 2016 -0700 @@ -23,7 +23,7 @@ /* * @test - * @bug 8154119 8154262 8156077 8157987 + * @bug 8154119 8154262 8156077 8157987 8154261 * @summary Test modules support in javadoc. * @author bpatel * @library ../lib @@ -110,6 +110,17 @@ testModuleTags(); } + @Test + void test7() { + javadoc("-d", "out-moduleSummary", "-use", + "-modulesourcepath", testSrc, + "-addmods", "module1,module2", + "testpkgmdl1", "testpkgmdl2", "testpkg2mdl2"); + checkExit(Exit.OK); + testModuleSummary(); + testNegatedModuleSummary(); + } + @Test void test8() { javadoc("-d", "out-html5-nomodule", "-html5", "-use", @@ -139,12 +150,16 @@ "
\n" + "
    \n" + "
  • \n" - + ""); + + "
      \n" + + "
    • \n" + + ""); checkOutput("module2-summary.html", found, "
      \n" + "
        \n" + "
      • \n" - + "
    "); + + "
      \n" + + "
    • \n" + + ""); } void testHtml5Description(boolean found) { @@ -171,12 +186,16 @@ "
      \n" + "
        \n" + "
      • \n" - + "
    "); + + "
      \n" + + "
    • \n" + + ""); checkOutput("module2-summary.html", found, "
      \n" + "
        \n" + "
      • \n" - + "
    "); + + "
      \n" + + "
    • \n" + + ""); } void testModuleLink() { @@ -313,4 +332,114 @@ + "
    \n" + ""); } + + void testModuleSummary() { + checkOutput("module1-summary.html", true, + ""); + checkOutput("module1-summary.html", true, + "\n" + + "\n" + + "\n" + + ""); + checkOutput("module1-summary.html", true, + "\n" + + "\n" + + "\n" + + "\n" + + ""); + checkOutput("module1-summary.html", true, + "\n" + + "\n" + + "\n" + + ""); + checkOutput("module1-summary.html", true, + "\n" + + "\n" + + "\n" + + ""); + checkOutput("module2-summary.html", true, + "
  • Description | Modules | " + + "Packages | Services
  • "); + checkOutput("module2-summary.html", true, + "\n" + + "\n" + + "\n" + + ""); + checkOutput("module2-summary.html", true, + "\n" + + "\n" + + "\n" + + "\n" + + ""); + checkOutput("module2-summary.html", true, + "\n" + + "\n" + + "\n" + + ""); + checkOutput("module2-summary.html", true, + "\n" + + "\n" + + "\n" + + ""); + checkOutput("module2-summary.html", true, + "\n" + + "\n" + + "\n" + + ""); + checkOutput("module2-summary.html", true, + "\n" + + "\n" + + "\n" + + ""); + checkOutput("module2-summary.html", true, + "\n" + + "\n" + + "\n" + + "Exported Packages \n" + + "\n" + + "\n" + + "\n" + + "\n" + + ""); + checkOutput("module2-summary.html", true, + "\n" + + "\n" + + "\n" + + "\n" + + ""); + checkOutput("module2-summary.html", true, + "\n" + + "\n" + + "\n" + + "\n" + + ""); + checkOutput("module2-summary.html", true, + "\n" + + "\n" + + "\n" + + "\n" + + ""); + } + + void testNegatedModuleSummary() { + checkOutput("module1-summary.html", false, + "\n" + + "\n" + + "\n" + + ""); + } } diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/test/jdk/javadoc/doclet/testModules/module2/module-info.java --- a/langtools/test/jdk/javadoc/doclet/testModules/module2/module-info.java Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/test/jdk/javadoc/doclet/testModules/module2/module-info.java Mon Jul 18 09:38:08 2016 -0700 @@ -28,4 +28,10 @@ */ module module2 { exports testpkgmdl2; + + exports testpkg2mdl2 to module1; + + uses testpkgmdl2.TestClassInModule2; + + provides testpkg2mdl2.TestInterfaceInModule2 with testpkgmdl2.TestClassInModule2; } diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/test/jdk/javadoc/doclet/testModules/module2/testpkg2mdl2/TestInterfaceInModule2.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/jdk/javadoc/doclet/testModules/module2/testpkg2mdl2/TestInterfaceInModule2.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,29 @@ +/* + * 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. + */ +package testpkg2mdl2; + +public interface TestInterfaceInModule2 { + void testMethod(); +} diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/test/jdk/javadoc/doclet/testModules/module2/testpkgmdl2/TestClassInModule2.java --- a/langtools/test/jdk/javadoc/doclet/testModules/module2/testpkgmdl2/TestClassInModule2.java Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/test/jdk/javadoc/doclet/testModules/module2/testpkgmdl2/TestClassInModule2.java Mon Jul 18 09:38:08 2016 -0700 @@ -24,5 +24,8 @@ */ package testpkgmdl2; -public class TestClassInModule2 { +import testpkg2mdl2.TestInterfaceInModule2; + +public class TestClassInModule2 implements TestInterfaceInModule2 { + void testMethod() {} } diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/test/jdk/javadoc/doclet/testStylesheet/TestStylesheet.java --- a/langtools/test/jdk/javadoc/doclet/testStylesheet/TestStylesheet.java Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/test/jdk/javadoc/doclet/testStylesheet/TestStylesheet.java Mon Jul 18 09:38:08 2016 -0700 @@ -23,7 +23,7 @@ /* * @test - * @bug 4494033 7028815 7052425 8007338 8023608 8008164 8016549 8072461 + * @bug 4494033 7028815 7052425 8007338 8023608 8008164 8016549 8072461 8154261 * @summary Run tests on doclet stylesheet. * @author jamieh * @library ../lib @@ -81,7 +81,8 @@ + " list-style-type:disc;\n" + "}", ".overviewSummary caption, .memberSummary caption, .typeSummary caption,\n" - + ".useSummary caption, .constantsSummary caption, .deprecatedSummary caption {\n" + + ".useSummary caption, .constantsSummary caption, .deprecatedSummary caption,\n" + + ".requiresSummary caption, .packagesSummary caption, .providesSummary caption, .usesSummary caption {\n" + " position:relative;\n" + " text-align:left;\n" + " background-repeat:no-repeat;\n" @@ -96,7 +97,9 @@ + " white-space:pre;\n" + "}", ".overviewSummary caption span, .memberSummary caption span, .typeSummary caption span,\n" - + ".useSummary caption span, .constantsSummary caption span, .deprecatedSummary caption span {\n" + + ".useSummary caption span, .constantsSummary caption span, .deprecatedSummary caption span,\n" + + ".requiresSummary caption span, .packagesSummary caption span, .providesSummary caption span,\n" + + ".usesSummary caption span {\n" + " white-space:nowrap;\n" + " padding-top:5px;\n" + " padding-left:12px;\n" @@ -132,6 +135,9 @@ + "}", // Test the formatting styles for proper content display in use and constant values pages. ".overviewSummary td.colFirst, .overviewSummary th.colFirst,\n" + + ".requiresSummary td.colFirst, .requiresSummary th.colFirst,\n" + + ".packagesSummary td.colFirst, .packagesSummary td.colSecond, .packagesSummary th.colFirst, .packagesSummary th,\n" + + ".usesSummary td.colFirst, .usesSummary th.colFirst,\n" + ".useSummary td.colFirst, .useSummary th.colFirst,\n" + ".overviewSummary td.colOne, .overviewSummary th.colOne,\n" + ".memberSummary td.colFirst, .memberSummary th.colFirst,\n" @@ -141,7 +147,8 @@ + " vertical-align:top;\n" + "}", ".overviewSummary td, .memberSummary td, .typeSummary td,\n" - + ".useSummary td, .constantsSummary td, .deprecatedSummary td {\n" + + ".useSummary td, .constantsSummary td, .deprecatedSummary td,\n" + + ".requiresSummary td, .packagesSummary td, .providesSummary td, .usesSummary td {\n" + " text-align:left;\n" + " padding:0px 0px 12px 10px;\n" + "}", diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/test/jdk/jshell/KullaCompletenessStressTest.java --- a/langtools/test/jdk/jshell/KullaCompletenessStressTest.java Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/test/jdk/jshell/KullaCompletenessStressTest.java Mon Jul 18 09:38:08 2016 -0700 @@ -37,10 +37,6 @@ public class KullaCompletenessStressTest extends CompletenessStressTest { @Override public File[] getDirectoriesToTest() { - return new File[]{ getKullaSourceDirectory() }; - } - - public File getKullaSourceDirectory() { String src = System.getProperty("test.src"); File file; if (src == null) { @@ -48,6 +44,11 @@ } else { file = new File(src, "../../../src/jdk.jshell/share/classes"); } - return file; + if (!file.exists()) { + System.out.println("jdk.jshell sources are not exist. Test has been skipped. Path: " + file.toString()); + return new File[]{}; + }else { + return new File[]{file}; + } } } diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/test/tools/javac/AnonymousClass/AnonymousClassFlags.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/javac/AnonymousClass/AnonymousClassFlags.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,139 @@ +/* + * 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 8161013 + * @summary Verify that anonymous class binaries have the correct flags set + * @modules jdk.jdeps/com.sun.tools.classfile + * @run main AnonymousClassFlags + */ + +import java.util.*; +import java.nio.file.Path; +import java.nio.file.Paths; + +import com.sun.tools.classfile.*; +import static com.sun.tools.classfile.AccessFlags.*; + +public class AnonymousClassFlags { + public static void main(String[] args) throws Exception { + new AnonymousClassFlags().test(System.getProperty("test.classes", ".")); + } + + /** Maps names of anonymous classes to their expected inner_class_access_flags */ + private static Map anonClasses = new LinkedHashMap<>(); + + // ******* TEST CASES ******** + + static Object o1 = new Object() { + { anonClasses.put(getClass().getName(), 0); } + }; + + static void staticMethod() { + Object o2 = new Object() { + { anonClasses.put(getClass().getName(), 0); } + }; + } + + static { + staticMethod(); + + Object o3 = new Object() { + { anonClasses.put(getClass().getName(), 0); } + }; + } + + Object o4 = new Object() { + { anonClasses.put(getClass().getName(), 0); } + }; + + void instanceMethod() { + Object o5 = new Object() { + { anonClasses.put(getClass().getName(), 0); } + }; + } + + { + instanceMethod(); + + Object o6 = new Object() { + { anonClasses.put(getClass().getName(), 0); } + }; + } + + // ******* TEST IMPLEMENTATION ******** + + void test(String classesDir) throws Exception { + staticMethod(); + instanceMethod(); + + Path outerFile = Paths.get(classesDir, getClass().getName() + ".class"); + ClassFile outerClass = ClassFile.read(outerFile); + for (Map.Entry entry : anonClasses.entrySet()) { + Path innerFile = Paths.get(classesDir, entry.getKey() + ".class"); + ClassFile innerClass = ClassFile.read(innerFile); + String name = entry.getKey(); + int expected = entry.getValue(); + assertInnerFlags(outerClass, name, expected); + assertClassFlags(innerClass, name, expected); + assertInnerFlags(innerClass, name, expected); + } + } + + static void assertClassFlags(ClassFile classFile, String name, int expected) { + int mask = ACC_PUBLIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT | + ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM; + int classExpected = (expected & mask) | ACC_SUPER; + int classActual = classFile.access_flags.flags; + if (classActual != classExpected) { + throw new AssertionError("Incorrect access_flags for class " + name + + ": expected=" + classExpected + ", actual=" + classActual); + } + + } + + static void assertInnerFlags(ClassFile classFile, String name, int expected) throws ConstantPoolException { + int innerActual = lookupInnerFlags(classFile, name).flags; + if (innerActual != expected) { + throw new AssertionError("Incorrect inner_class_access_flags for class " + name + + " in class " + classFile.getName() + + ": expected=" + expected + ", actual=" + innerActual); + } + } + + private static AccessFlags lookupInnerFlags(ClassFile classFile, String innerName) throws ConstantPoolException { + InnerClasses_attribute inners = (InnerClasses_attribute) classFile.getAttribute("InnerClasses"); + if (inners == null) { + throw new AssertionError("InnerClasses attribute missing in class " + classFile.getName()); + } + for (InnerClasses_attribute.Info info : inners.classes) { + String entryName = info.getInnerClassInfo(classFile.constant_pool).getName(); + if (innerName.equals(entryName)) { + return info.inner_class_access_flags; + } + } + throw new AssertionError("No InnerClasses entry in class " + classFile.getName() + " for class " + innerName); + } + +} diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/test/tools/javac/cast/6219964/T6219964.java --- a/langtools/test/tools/javac/cast/6219964/T6219964.java Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/test/tools/javac/cast/6219964/T6219964.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,8 +1,8 @@ /* * @test /nodynamiccopyright/ - * @bug 6219964 - * @summary Compiler allows illegal cast of anonymous inner class - * @compile/fail/ref=T6219964.out -XDrawDiagnostics T6219964.java + * @bug 6219964 8161013 + * @summary Anonymous class types are not final + * @compile T6219964.java */ public class T6219964 { diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/test/tools/javac/cast/6219964/T6219964.out --- a/langtools/test/tools/javac/cast/6219964/T6219964.out Fri Jul 15 09:05:36 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -T6219964.java:13:27: compiler.err.prob.found.req: (compiler.misc.inconvertible.types: compiler.misc.anonymous.class: java.lang.Object, T6219964.I) -1 error diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/test/tools/jdeps/jdkinternals/RemovedJDKInternals.java --- a/langtools/test/tools/jdeps/jdkinternals/RemovedJDKInternals.java Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/test/tools/jdeps/jdkinternals/RemovedJDKInternals.java Mon Jul 18 09:38:08 2016 -0700 @@ -71,7 +71,7 @@ assertTrue(CompilerUtils.compile(codecSrc, codecDest)); // patch jdk.unsupported and set -cp to codec types - assertTrue(CompilerUtils.compile(Paths.get(TEST_SRC, "src"), + assertTrue(CompilerUtils.compile(Paths.get(TEST_SRC, "src", "p"), CLASSES_DIR, "-Xpatch:jdk.unsupported=" + patchDir, "-cp", codecDest.toString())); diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/test/tools/jdeps/jdkinternals/ShowReplacement.java --- a/langtools/test/tools/jdeps/jdkinternals/ShowReplacement.java Fri Jul 15 09:05:36 2016 -0700 +++ b/langtools/test/tools/jdeps/jdkinternals/ShowReplacement.java Mon Jul 18 09:38:08 2016 -0700 @@ -59,14 +59,17 @@ public void compileAll() throws Exception { CompilerUtils.cleanDir(CLASSES_DIR); - assertTrue(CompilerUtils.compile(Paths.get(TEST_SRC, "p"), + Path tmp = Paths.get("tmp"); + assertTrue(CompilerUtils.compile(Paths.get(TEST_SRC, "src", "apple"), tmp)); + assertTrue(CompilerUtils.compile(Paths.get(TEST_SRC, "src", "q"), CLASSES_DIR, + "-cp", tmp.toString(), "-XaddExports:java.base/sun.security.util=ALL-UNNAMED")); } @Test public void withReplacement() { - Path file = Paths.get("p", "WithRepl.class"); + Path file = Paths.get("q", "WithRepl.class"); String[] output = JdepsUtil.jdeps("-jdkinternals", CLASSES_DIR.resolve(file).toString()); int i = 0; while (!output[i].contains("Suggested Replacement")) { @@ -90,9 +93,29 @@ } } + /* + * A JDK internal class has been removed while its package still exists. + */ @Test public void noReplacement() { - Path file = Paths.get("p", "NoRepl.class"); + Path file = Paths.get("q", "NoRepl.class"); + String[] output = JdepsUtil.jdeps("-jdkinternals", CLASSES_DIR.resolve(file).toString()); + int i = 0; + // expect no replacement + while (i < output.length && !output[i].contains("Suggested Replacement")) { + i++; + } + + // no replacement + assertEquals(output.length-i, 0); + } + + /* + * A JDK internal package has been removed. + */ + @Test + public void removedPackage() { + Path file = Paths.get("q", "RemovedPackage.class"); String[] output = JdepsUtil.jdeps("-jdkinternals", CLASSES_DIR.resolve(file).toString()); int i = 0; // expect no replacement diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/test/tools/jdeps/jdkinternals/p/NoRepl.java --- a/langtools/test/tools/jdeps/jdkinternals/p/NoRepl.java Fri Jul 15 09:05:36 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -/* - * 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 p; - -import java.io.IOException; -import java.io.OutputStream; -import sun.security.util.DerEncoder; - -public class NoRepl implements DerEncoder { - public void derEncode(OutputStream out) throws IOException { - throw new IOException(); - } -} diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/test/tools/jdeps/jdkinternals/p/WithRepl.java --- a/langtools/test/tools/jdeps/jdkinternals/p/WithRepl.java Fri Jul 15 09:05:36 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -/* - * 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 p; - -import sun.security.util.HostnameChecker; - -public class WithRepl { - public static void main(String[] argv) throws Exception { - HostnameChecker hc = HostnameChecker.getInstance(HostnameChecker.TYPE_LDAP); - } -} diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/test/tools/jdeps/jdkinternals/src/apple/applescript/AppleScriptEngine.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/jdeps/jdkinternals/src/apple/applescript/AppleScriptEngine.java Mon Jul 18 09:38:08 2016 -0700 @@ -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 apple.applescript; + +import javax.script.ScriptEngine; + +public interface AppleScriptEngine extends ScriptEngine { +} diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/test/tools/jdeps/jdkinternals/src/q/NoRepl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/jdeps/jdkinternals/src/q/NoRepl.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,34 @@ +/* + * 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 q; + +import java.io.IOException; +import java.io.OutputStream; +import sun.security.util.DerEncoder; + +public class NoRepl implements DerEncoder { + public void derEncode(OutputStream out) throws IOException { + throw new IOException(); + } +} diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/test/tools/jdeps/jdkinternals/src/q/RemovedPackage.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/jdeps/jdkinternals/src/q/RemovedPackage.java Mon Jul 18 09:38:08 2016 -0700 @@ -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 q; + +import apple.applescript.AppleScriptEngine; + +public class RemovedPackage { + AppleScriptEngine scriptEngine; +} diff -r 8bc25e077e83 -r f5d65fcf55e4 langtools/test/tools/jdeps/jdkinternals/src/q/WithRepl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/langtools/test/tools/jdeps/jdkinternals/src/q/WithRepl.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,32 @@ +/* + * 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 q; + +import sun.security.util.HostnameChecker; + +public class WithRepl { + public static void main(String[] argv) throws Exception { + HostnameChecker hc = HostnameChecker.getInstance(HostnameChecker.TYPE_LDAP); + } +} diff -r 8bc25e077e83 -r f5d65fcf55e4 make/CompileJavaModules.gmk --- a/make/CompileJavaModules.gmk Fri Jul 15 09:05:36 2016 -0700 +++ b/make/CompileJavaModules.gmk Mon Jul 18 09:38:08 2016 -0700 @@ -409,6 +409,8 @@ ################################################################################ +jdk.jdeps_COPY := .txt + jdk.jdeps_CLEAN_FILES := $(wildcard \ $(JDK_TOPDIR)/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/*.properties \ $(JDK_TOPDIR)/src/jdk.jdeps/share/classes/com/sun/tools/javap/resources/*.properties) diff -r 8bc25e077e83 -r f5d65fcf55e4 make/Javadoc.gmk --- a/make/Javadoc.gmk Fri Jul 15 09:05:36 2016 -0700 +++ b/make/Javadoc.gmk Mon Jul 18 09:38:08 2016 -0700 @@ -424,7 +424,7 @@ @($(call COMMON_JAVADOCFLAGS) ; \ $(call COMMON_JAVADOCTAGS) ; \ $(call OptionOnly,-Xdoclint:reference) ; \ - $(call OptionOnly,-Xdoclint/package:-org.omg.*) ; \ + $(call OptionOnly,-Xdoclint/package:-org.omg.*$(COMMA)jdk.internal.logging.*) ; \ $(call OptionPair,-system,none) ; \ $(call OptionPair,-modulesourcepath,$(RELEASEDOCS_MODULESOURCEPATH)) ; \ $(call OptionPair,-addmods,$(COREAPI_MODULES)) ; \ diff -r 8bc25e077e83 -r f5d65fcf55e4 make/common/MakeBase.gmk --- a/make/common/MakeBase.gmk Fri Jul 15 09:05:36 2016 -0700 +++ b/make/common/MakeBase.gmk Mon Jul 18 09:38:08 2016 -0700 @@ -801,15 +801,20 @@ # of the build in case of failure. The command line itself is stored in a file, # and also logged to stdout if the LOG=cmdlines option has been given. # +# NOTE: If the command redirects stdout, the caller needs to wrap it in a +# subshell (by adding parentheses around it), otherwise the redirect to the +# subshell tee process will create a race condition where the target file may +# not be fully written when the make recipe is done. +# # Param 1 - The path to base the name of the log file / command line file on # Param 2 - The command to run ExecuteWithLog = \ $(call LogCmdlines, Exececuting: [$(strip $2)]) \ $(call WriteFile, $2, $(strip $1).cmdline) \ - ( ( $(strip $2) > >($(TEE) $(strip $1).log) 2> >($(TEE) $(strip $1).log >&2) || \ + ( $(strip $2) > >($(TEE) $(strip $1).log) 2> >($(TEE) $(strip $1).log >&2) || \ ( exitcode=$(DOLLAR)? && \ $(CP) $(strip $1).log $(MAKESUPPORT_OUTPUTDIR)/failure-logs/$(subst /,_,$(patsubst $(BUILD_OUTPUT)/%,%,$(strip $1))).log && \ - exit $(DOLLAR)exitcode ) ) && wait ) + exit $(DOLLAR)exitcode ) ) ################################################################################ # Find lib dir for module diff -r 8bc25e077e83 -r f5d65fcf55e4 make/common/Modules.gmk --- a/make/common/Modules.gmk Fri Jul 15 09:05:36 2016 -0700 +++ b/make/common/Modules.gmk Mon Jul 18 09:38:08 2016 -0700 @@ -1,5 +1,5 @@ # -# Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2014, 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 @@ -71,7 +71,6 @@ # to be deprivileged BOOT_MODULES += \ - java.smartcardio \ jdk.naming.rmi \ # @@ -104,6 +103,7 @@ PLATFORM_MODULES += \ java.compiler \ java.scripting \ + java.smartcardio \ java.sql \ java.sql.rowset \ jdk.accessibility \ diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/.hgtags --- a/nashorn/.hgtags Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/.hgtags Mon Jul 18 09:38:08 2016 -0700 @@ -360,3 +360,4 @@ 5d68f5155dded7efec7d5aca5d631caa7ee1042b jdk-9+124 a32d419d73fe881a935b567c57dab9bfe3ed5f92 jdk-9+125 ee90c69a18409533df8f7b602044bf966a28381a jdk-9+126 +ff07be6106fa56b72c163244f45a3ecb4c995564 jdk-9+127 diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/make/project.properties --- a/nashorn/make/project.properties Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/make/project.properties Mon Jul 18 09:38:08 2016 -0700 @@ -35,7 +35,9 @@ javac.source=1.9 javac.target=1.9 -javadoc.option=-tag "implSpec:a:Implementation Requirements:" +javadoc.option=\ + -tag "implSpec:a:Implementation Requirements:"\ + -tag "implNote:a:Implementation Note:" # nashorn version information nashorn.version=0.1 diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/samples/prettyprinter.js --- a/nashorn/samples/prettyprinter.js Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/samples/prettyprinter.js Mon Jul 18 09:38:08 2016 -0700 @@ -209,7 +209,7 @@ } print("function "); if (func.name) { - print(func.name); + print(func.name.name); } printFunctionBody(func, extra, end); if (funcDecl) { @@ -608,7 +608,7 @@ visitVariable: function(node, extra) { indent(); - print("var " + node.name); + print("var " + node.binding.name); var init = node.initializer; if (init) { print(" = "); diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/NashornCompleter.java --- a/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/NashornCompleter.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/NashornCompleter.java Mon Jul 18 09:38:08 2016 -0700 @@ -439,6 +439,10 @@ args.add("-strict"); } + if (env._es6) { + args.add("--language=es6"); + } + return Parser.create(args.toArray(new String[0])); } } diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CatchTree.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CatchTree.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CatchTree.java Mon Jul 18 09:38:08 2016 -0700 @@ -38,11 +38,11 @@ */ public interface CatchTree extends Tree { /** - * Returns the catch parameter identifier of the exception caught. + * Returns the catch parameter identifier or parameter binding pattern of the exception caught. * - * @return the catch parameter identifier + * @return the catch parameter identifier or parameter binding pattern */ - IdentifierTree getParameter(); + ExpressionTree getParameter(); /** * Returns the code block of this catch block. diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CatchTreeImpl.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CatchTreeImpl.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CatchTreeImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -28,12 +28,12 @@ import jdk.nashorn.internal.ir.CatchNode; final class CatchTreeImpl extends TreeImpl implements CatchTree { - private final IdentifierTree param; + private final ExpressionTree param; private final BlockTree block; private final ExpressionTree condition; CatchTreeImpl(final CatchNode node, - final IdentifierTree param, + final ExpressionTree param, final BlockTree block, final ExpressionTree condition) { super(node); @@ -48,7 +48,7 @@ } @Override - public IdentifierTree getParameter() { + public ExpressionTree getParameter() { return param; } diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ClassDeclarationTree.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ClassDeclarationTree.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,63 @@ +/* + * 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. + */ +package jdk.nashorn.api.tree; + +import java.util.List; + +/** + * A tree node that represents a class declaration. + * + * @since 9 + */ +public interface ClassDeclarationTree extends StatementTree { + + /** + * Class identifier. + * + * @return the class identifier + */ + IdentifierTree getName(); + + /** + * The expression of the {@code extends} clause. Optional. + * + * @return the class heritage + */ + ExpressionTree getClassHeritage(); + + /** + * Get the constructor method definition. + * + * @return the constructor + */ + PropertyTree getConstructor(); + + /** + * Get other property definitions except for the constructor. + * + * @return the class elements + */ + List getClassElements(); +} diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ClassDeclarationTreeImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ClassDeclarationTreeImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,77 @@ +/* + * 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. + */ + +package jdk.nashorn.api.tree; + +import java.util.List; +import jdk.nashorn.internal.ir.VarNode; + +final class ClassDeclarationTreeImpl extends StatementTreeImpl implements ClassDeclarationTree { + + private final IdentifierTree name; + private final ExpressionTree classHeritage; + private final PropertyTree constructor; + private final List classElements; + + ClassDeclarationTreeImpl(final VarNode node, final IdentifierTree name, + final ExpressionTree classHeritage, final PropertyTree constructor, + final List classElements) { + super(node); + this.name = name; + this.classHeritage = classHeritage; + this.constructor = constructor; + this.classElements = classElements; + } + + @Override + public Tree.Kind getKind() { + return Tree.Kind.CLASS; + } + + @Override + public IdentifierTree getName() { + return name; + } + + @Override + public ExpressionTree getClassHeritage() { + return classHeritage; + } + + @Override + public PropertyTree getConstructor() { + return constructor; + } + + @Override + public List getClassElements() { + return classElements; + } + + @Override + public R accept(final TreeVisitor visitor, final D data) { + return visitor.visitClassDeclaration(this, data); + } +} \ No newline at end of file diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ClassExpressionTree.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ClassExpressionTree.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,63 @@ +/* + * 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. + */ + +package jdk.nashorn.api.tree; + +import java.util.List; + +/** + * A tree node that represents a class expression. + * + * @since 9 + */ +public interface ClassExpressionTree extends ExpressionTree { + /** + * Class identifier. Optional. + * + * @return the class identifier + */ + IdentifierTree getName(); + + /** + * The expression of the {@code extends} clause. Optional. + * + * @return the class heritage + */ + ExpressionTree getClassHeritage(); + + /** + * Get the constructor method definition. + * + * @return the constructor + */ + PropertyTree getConstructor(); + + /** + * Get other property definitions except for the constructor. + * + * @return the class elements + */ + List getClassElements(); +} diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ClassExpressionTreeImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ClassExpressionTreeImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,76 @@ +/* + * 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. + */ +package jdk.nashorn.api.tree; + +import java.util.List; +import jdk.nashorn.internal.ir.ClassNode; + +final class ClassExpressionTreeImpl extends ExpressionTreeImpl implements ClassExpressionTree { + + private final IdentifierTree name; + private final ExpressionTree classHeritage; + private final PropertyTree constructor; + private final List classElements; + + ClassExpressionTreeImpl(final ClassNode cn, final IdentifierTree name, + final ExpressionTree classHeritage, final PropertyTree constructor, + final List classElements) { + super(cn); + this.name = name; + this.classHeritage = classHeritage; + this.constructor = constructor; + this.classElements = classElements; + } + + @Override + public Kind getKind() { + return Kind.CLASS_EXPRESSION; + } + + @Override + public IdentifierTree getName() { + return name; + } + + @Override + public ExpressionTree getClassHeritage() { + return classHeritage; + } + + @Override + public PropertyTree getConstructor() { + return constructor; + } + + @Override + public List getClassElements() { + return classElements; + } + + @Override + public R accept(final TreeVisitor visitor, final D data) { + return visitor.visitClassExpression(this, data); + } +} diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CompilationUnitTree.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CompilationUnitTree.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CompilationUnitTree.java Mon Jul 18 09:38:08 2016 -0700 @@ -62,4 +62,12 @@ * @return the line map for this compilation unit */ LineMap getLineMap(); + + /** + * Return the {@link ModuleTree} associated with this compilation unit. This is null, + * if there is no module information from this compilation unit. + * + * @return the Module info or null + */ + ModuleTree getModule(); } diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CompilationUnitTreeImpl.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CompilationUnitTreeImpl.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CompilationUnitTreeImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -32,13 +32,18 @@ implements CompilationUnitTree { private final FunctionNode funcNode; private final List elements; + private final ModuleTree module; CompilationUnitTreeImpl(final FunctionNode node, - final List elements) { + final List elements, + final ModuleTree module) { super(node); this.funcNode = node; - assert funcNode.getKind() == FunctionNode.Kind.SCRIPT : "script function expected"; + assert funcNode.getKind() == FunctionNode.Kind.SCRIPT || + funcNode.getKind() == FunctionNode.Kind.MODULE : + "script or module function expected"; this.elements = elements; + this.module = module; } @Override @@ -67,6 +72,11 @@ } @Override + public ModuleTree getModule() { + return module; + } + + @Override public R accept(final TreeVisitor visitor, final D data) { return visitor.visitCompilationUnit(this, data); } diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/DestructuringDeclTreeImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/DestructuringDeclTreeImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,77 @@ +/* + * 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. + */ + +package jdk.nashorn.api.tree; + +import jdk.nashorn.internal.ir.ExpressionStatement; +import jdk.nashorn.internal.parser.TokenType; + +// This implementation of VariableTree represents a destructuring declaration +final class DestructuringDeclTreeImpl extends StatementTreeImpl + implements VariableTree { + + private final TokenType declType; + private final ExpressionTree lhs; + private final ExpressionTree init; + + DestructuringDeclTreeImpl(ExpressionStatement exprStat, final ExpressionTree lhs, final ExpressionTree init) { + super(exprStat); + assert exprStat.destructuringDeclarationType() != null : "expecting a destructuring decl. statement"; + + this.declType = exprStat.destructuringDeclarationType(); + this.lhs = lhs; + this.init = init; + } + + @Override + public Kind getKind() { + return Kind.VARIABLE; + } + + @Override + public ExpressionTree getBinding() { + return lhs; + } + + @Override + public ExpressionTree getInitializer() { + return init; + } + + @Override + public boolean isConst() { + return declType == TokenType.CONST; + } + + @Override + public boolean isLet() { + return declType == TokenType.LET; + } + + @Override + public R accept(final TreeVisitor visitor, final D data) { + return visitor.visitVariable(this, data); + } +} diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ExportEntryTree.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ExportEntryTree.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,60 @@ +/* + * 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. + */ +package jdk.nashorn.api.tree; + +import java.util.List; + +/** + * A Tree node for export entry in Module information. + */ +public interface ExportEntryTree extends Tree { + /** + * Returns the entry's export name. + * + * @return the export name + */ + public IdentifierTree getExportName(); + + /** + * Returns the entry's module request. + * + * @return the module request + */ + public IdentifierTree getModuleRequest(); + + /** + * Returns the entry's import name. + * + * @return the import name + */ + public IdentifierTree getImportName(); + + /** + * Returns the entry's local name. + * + * @return the local name + */ + public IdentifierTree getLocalName(); +} diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ExportEntryTreeImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ExportEntryTreeImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -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. + */ +package jdk.nashorn.api.tree; + +import java.util.List; +import java.util.stream.Collectors; +import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.IdentNode; +import jdk.nashorn.internal.ir.Module; +import static jdk.nashorn.api.tree.ModuleTreeImpl.identOrNull; + +final class ExportEntryTreeImpl extends TreeImpl implements ExportEntryTree { + private final long startPos, endPos; + private final IdentifierTree exportName; + private final IdentifierTree moduleRequest; + private final IdentifierTree importName; + private final IdentifierTree localName; + + private ExportEntryTreeImpl(final long startPos, final long endPos, + IdentifierTree exportName, + IdentifierTree moduleRequest, + IdentifierTree importName, + IdentifierTree localName) { + super(null); // no underlying Node! + this.startPos = startPos; + this.endPos = endPos; + this.exportName = exportName; + this.moduleRequest = moduleRequest; + this.importName = importName; + this.localName = localName; + } + + private static ExportEntryTreeImpl createExportEntry(Module.ExportEntry entry) { + return new ExportEntryTreeImpl(entry.getStartPosition(), + entry.getEndPosition(), + identOrNull(entry.getExportName()), + identOrNull(entry.getModuleRequest()), + identOrNull(entry.getImportName()), + identOrNull(entry.getLocalName())); + } + + static List createExportList(List exportList) { + return exportList.stream(). + map(ExportEntryTreeImpl::createExportEntry). + collect(Collectors.toList()); + } + + @Override + public Kind getKind() { + return Tree.Kind.EXPORT_ENTRY; + } + + @Override + public R accept(final TreeVisitor visitor, final D data) { + return visitor.visitExportEntry(this, data); + } + + @Override + public long getStartPosition() { + return startPos; + } + + @Override + public long getEndPosition() { + return endPos; + } + + @Override + public IdentifierTree getExportName() { + return exportName; + } + + @Override + public IdentifierTree getModuleRequest() { + return moduleRequest; + } + + @Override + public IdentifierTree getImportName() { + return importName; + } + + @Override + public IdentifierTree getLocalName() { + return localName; + } +} diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ForOfLoopTree.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ForOfLoopTree.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,62 @@ +/* + * 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. + */ + +package jdk.nashorn.api.tree; + +/** + * A tree node for for..of statement. + * + * For example: + *
    + *   for ( variable of expression )
    + *       statement
    + * 
    + * + * @since 9 + */ +public interface ForOfLoopTree extends LoopTree { + /** + * The for..in left hand side expression. + * + * @return the left hand side expression + */ + ExpressionTree getVariable(); + + /** + * The object or array being whose properties are iterated. + * + * @return the object or array expression being iterated + */ + ExpressionTree getExpression(); + + /** + * The statement contained in this for..in statement. + * + * @return the statement + */ + @Override + StatementTree getStatement(); +} + diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ForOfLoopTreeImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ForOfLoopTreeImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,69 @@ +/* + * 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. + */ +package jdk.nashorn.api.tree; + +import jdk.nashorn.internal.ir.ForNode; + +final class ForOfLoopTreeImpl extends StatementTreeImpl implements ForOfLoopTree { + private final ExpressionTree lhsExpr; + private final ExpressionTree expr; + private final StatementTree stat; + + ForOfLoopTreeImpl(final ForNode node, + final ExpressionTree lhsExpr, + final ExpressionTree expr, + final StatementTree stat) { + super(node); + assert node.isForIn() : "for ..in expected"; + this.lhsExpr = lhsExpr; + this.expr = expr; + this.stat = stat; + } + + @Override + public Kind getKind() { + return Kind.FOR_IN_LOOP; + } + + @Override + public ExpressionTree getVariable() { + return lhsExpr; + } + + @Override + public ExpressionTree getExpression() { + return expr; + } + + @Override + public StatementTree getStatement() { + return stat; + } + + @Override + public R accept(final TreeVisitor visitor, final D data) { + return visitor.visitForOfLoop(this, data); + } +} diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionDeclarationTree.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionDeclarationTree.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionDeclarationTree.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -28,7 +28,7 @@ import java.util.List; /** - * A tree node for a function declaration. + * A tree node for a function declaration. * * For example: *
    @@ -37,6 +37,12 @@
      *      body
      * 
    * + *
    + *   function* name
    + *      ( parameters )
    + *      body
    + * 
    + * * @since 9 */ public interface FunctionDeclarationTree extends StatementTree { @@ -45,7 +51,7 @@ * * @return name the function declared */ - String getName(); + IdentifierTree getName(); /** * Returns the parameters of this function. @@ -67,4 +73,11 @@ * @return true if this function is strict */ boolean isStrict(); + + /** + * Is this a generator function? + * + * @return true if this is a generator function + */ + boolean isGenerator(); } diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionDeclarationTreeImpl.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionDeclarationTreeImpl.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionDeclarationTreeImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -32,7 +32,7 @@ final class FunctionDeclarationTreeImpl extends StatementTreeImpl implements FunctionDeclarationTree { private final FunctionNode funcNode; - private final String funcName; + private final IdentifierTree funcName; private final List params; private final BlockTree body; @@ -43,7 +43,7 @@ assert node.getInit() instanceof FunctionNode : "function expected"; funcNode = (FunctionNode)node.getInit(); assert funcNode.isDeclared() : "function declaration expected"; - funcName = funcNode.isAnonymous()? null : node.getName().getName(); + funcName = funcNode.isAnonymous()? null : new IdentifierTreeImpl(node.getName()); this.params = params; this.body = body; } @@ -54,7 +54,7 @@ } @Override - public String getName() { + public IdentifierTree getName() { return funcName; } @@ -74,6 +74,11 @@ } @Override + public boolean isGenerator() { + return funcNode.getKind() == FunctionNode.Kind.GENERATOR; + } + + @Override public R accept(final TreeVisitor visitor, final D data) { return visitor.visitFunctionDeclaration(this, data); } diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionExpressionTree.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionExpressionTree.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionExpressionTree.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -28,7 +28,7 @@ import java.util.List; /** - * A tree node for a function expression. + * A tree node for function expressions including arrow functions. * * For example: *
    @@ -37,6 +37,10 @@
      *      body
      * 
    * + *
    + *   var func = (x) => x+1
    + * 
    + * * @since 9 */ public interface FunctionExpressionTree extends ExpressionTree { @@ -45,7 +49,7 @@ * * @return name the function declared */ - String getName(); + IdentifierTree getName(); /** * Returns the parameters of this function. @@ -55,11 +59,13 @@ List getParameters(); /** - * Returns the body of code of this function. + * Returns the body of this function. This may be a {@link BlockTree} when this + * function has a block body. This is an {@link ExpressionTree} when the function body + * is a concise expression as in an expression arrow, or in an expression closure. * - * @return the body of code + * @return the body of this function */ - BlockTree getBody(); + Tree getBody(); /** * Is this a strict function? @@ -67,4 +73,18 @@ * @return true if this function is strict */ boolean isStrict(); + + /** + * Is this a arrow function? + * + * @return true if this is a arrow function + */ + boolean isArrow(); + + /** + * Is this a generator function? + * + * @return true if this is a generator function + */ + boolean isGenerator(); } diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionExpressionTreeImpl.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionExpressionTreeImpl.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionExpressionTreeImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -31,9 +31,9 @@ final class FunctionExpressionTreeImpl extends ExpressionTreeImpl implements FunctionExpressionTree { private final FunctionNode funcNode; - private final String funcName; + private final IdentifierTree funcName; private final List params; - private final BlockTree body; + private final Tree body; FunctionExpressionTreeImpl(final FunctionNode node, final List params, @@ -46,11 +46,17 @@ if (node.isAnonymous() || kind == FunctionNode.Kind.GETTER || kind == FunctionNode.Kind.SETTER) { funcName = null; } else { - funcName = node.getIdent().getName(); + funcName = new IdentifierTreeImpl(node.getIdent()); } this.params = params; - this.body = body; + if (node.getFlag(FunctionNode.HAS_EXPRESSION_BODY)) { + StatementTree first = body.getStatements().get(0); + assert first instanceof ReturnTree : "consise func. expression should have a return statement"; + this.body = ((ReturnTree)first).getExpression(); + } else { + this.body = body; + } } @Override @@ -59,7 +65,7 @@ } @Override - public String getName() { + public IdentifierTree getName() { return funcName; } @@ -69,7 +75,7 @@ } @Override - public BlockTree getBody() { + public Tree getBody() { return body; } @@ -79,6 +85,16 @@ } @Override + public boolean isArrow() { + return funcNode.getKind() == FunctionNode.Kind.ARROW; + } + + @Override + public boolean isGenerator() { + return funcNode.getKind() == FunctionNode.Kind.GENERATOR; + } + + @Override public R accept(final TreeVisitor visitor, final D data) { return visitor.visitFunctionExpression(this, data); } diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IRTranslator.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IRTranslator.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IRTranslator.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.Map; import jdk.nashorn.internal.ir.AccessNode; import jdk.nashorn.internal.ir.BinaryNode; import jdk.nashorn.internal.ir.Block; @@ -35,6 +36,7 @@ import jdk.nashorn.internal.ir.CallNode; import jdk.nashorn.internal.ir.CaseNode; import jdk.nashorn.internal.ir.CatchNode; +import jdk.nashorn.internal.ir.ClassNode; import jdk.nashorn.internal.ir.ContinueNode; import jdk.nashorn.internal.ir.DebuggerNode; import jdk.nashorn.internal.ir.EmptyNode; @@ -56,6 +58,7 @@ import jdk.nashorn.internal.ir.SplitNode; import jdk.nashorn.internal.ir.Statement; import jdk.nashorn.internal.ir.SwitchNode; +import jdk.nashorn.internal.ir.TemplateLiteral; import jdk.nashorn.internal.ir.TernaryNode; import jdk.nashorn.internal.ir.ThrowNode; import jdk.nashorn.internal.ir.TryNode; @@ -87,11 +90,14 @@ return null; } - assert (node.getKind() == FunctionNode.Kind.SCRIPT) : "script function expected"; + assert node.getKind() == FunctionNode.Kind.SCRIPT || + node.getKind() == FunctionNode.Kind.MODULE : + "script or module function expected"; final Block body = node.getBody(); return new CompilationUnitTreeImpl(node, - translateStats(body != null? getOrderedStatements(body.getStatements()) : null)); + translateStats(body != null? getOrderedStatements(body.getStatements()) : null), + translateModule(node)); } @Override @@ -184,8 +190,15 @@ @Override public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) { - curStat = new ExpressionStatementTreeImpl(expressionStatement, + if (expressionStatement.destructuringDeclarationType() != null) { + ExpressionTree expr = translateExpr(expressionStatement.getExpression()); + assert expr instanceof AssignmentTree : "destructuring decl. statement does not have assignment"; + AssignmentTree assign = (AssignmentTree)expr; + curStat = new DestructuringDeclTreeImpl(expressionStatement, assign.getVariable(), assign.getExpression()); + } else { + curStat = new ExpressionStatementTreeImpl(expressionStatement, translateExpr(expressionStatement.getExpression())); + } return false; } @@ -209,6 +222,11 @@ translateExpr(forNode.getInit()), translateExpr(forNode.getModify()), translateBlock(forNode.getBody())); + } else if (forNode.isForOf()) { + curStat = new ForOfLoopTreeImpl(forNode, + translateExpr(forNode.getInit()), + translateExpr(forNode.getModify()), + translateBlock(forNode.getBody())); } else { curStat = new ForLoopTreeImpl(forNode, translateExpr(forNode.getInit()), @@ -224,8 +242,7 @@ public boolean enterFunctionNode(final FunctionNode functionNode) { assert !functionNode.isDeclared() || functionNode.isAnonymous() : "should not reach here for function declaration"; - final List paramTrees - = translateExprs(functionNode.getParameters()); + final List paramTrees = translateParameters(functionNode); final BlockTree blockTree = (BlockTree) translateBlock(functionNode.getBody(), true); curExpr = new FunctionExpressionTreeImpl(functionNode, paramTrees, blockTree); @@ -291,14 +308,7 @@ @Override public boolean enterObjectNode(final ObjectNode objectNode) { final List propNodes = objectNode.getElements(); - final List propTrees = new ArrayList<>(propNodes.size()); - for (final PropertyNode propNode : propNodes) { - propTrees.add(new PropertyTreeImpl(propNode, - translateExpr(propNode.getKey()), - translateExpr(propNode.getValue()), - (FunctionExpressionTree) translateExpr(propNode.getGetter()), - (FunctionExpressionTree) translateExpr(propNode.getSetter()))); - } + final List propTrees = translateProperties(propNodes); curExpr = new ObjectLiteralTreeImpl(objectNode, propTrees); return false; } @@ -347,6 +357,12 @@ } @Override + public boolean enterTemplateLiteral(final TemplateLiteral templateLiteral) { + curExpr = new TemplateLiteralTreeImpl(templateLiteral, translateExprs(templateLiteral.getExpressions())); + return false; + } + + @Override public boolean enterTernaryNode(final TernaryNode ternaryNode) { curExpr = new ConditionalExpressionTreeImpl(ternaryNode, translateExpr(ternaryNode.getTest()), @@ -386,6 +402,14 @@ if (unaryNode.isTokenType(TokenType.NEW)) { curExpr = new NewTreeImpl(unaryNode, translateExpr(unaryNode.getExpression())); + } else if (unaryNode.isTokenType(TokenType.YIELD) || + unaryNode.isTokenType(TokenType.YIELD_STAR)) { + curExpr = new YieldTreeImpl(unaryNode, + translateExpr(unaryNode.getExpression())); + } else if (unaryNode.isTokenType(TokenType.SPREAD_ARGUMENT) || + unaryNode.isTokenType(TokenType.SPREAD_ARRAY)) { + curExpr = new SpreadTreeImpl(unaryNode, + translateExpr(unaryNode.getExpression())); } else { curExpr = new UnaryTreeImpl(unaryNode, translateExpr(unaryNode.getExpression())); @@ -399,12 +423,19 @@ if (initNode instanceof FunctionNode && ((FunctionNode)initNode).isDeclared()) { final FunctionNode funcNode = (FunctionNode) initNode; - final List paramTrees - = translateExprs(funcNode.getParameters()); + final List paramTrees = translateParameters(funcNode); final BlockTree blockTree = (BlockTree) translateBlock(funcNode.getBody(), true); curStat = new FunctionDeclarationTreeImpl(varNode, paramTrees, blockTree); + } else if (initNode instanceof ClassNode && ((ClassNode)initNode).isStatement()) { + final ClassNode classNode = (ClassNode) initNode; + + curStat = new ClassDeclarationTreeImpl(varNode, + translateIdent(classNode.getIdent()), + translateExpr(classNode.getClassHeritage()), + translateProperty(classNode.getConstructor()), + translateProperties(classNode.getClassElements())); } else { - curStat = new VariableTreeImpl(varNode, translateExpr(initNode)); + curStat = new VariableTreeImpl(varNode, translateIdent(varNode.getName()), translateExpr(initNode)); } return false; @@ -433,6 +464,25 @@ return false; } + /** + * Callback for entering a ClassNode + * + * @param classNode the node + * @return true if traversal should continue and node children be traversed, false otherwise + */ + @Override + public boolean enterClassNode(final ClassNode classNode) { + assert !classNode.isStatement(): "should not reach here for class declaration"; + + curExpr = new ClassExpressionTreeImpl(classNode, + translateIdent(classNode.getIdent()), + translateExpr(classNode.getClassHeritage()), + translateProperty(classNode.getConstructor()), + translateProperties(classNode.getClassElements())); + + return false; + } + private StatementTree translateBlock(final Block blockNode) { return translateBlock(blockNode, false); } @@ -493,6 +543,24 @@ return statTrees; } + private List translateParameters(final FunctionNode func) { + Map paramExprs = func.getParameterExpressions(); + if (paramExprs != null) { + List params = func.getParameters(); + final List exprTrees = new ArrayList<>(params.size()); + for (final IdentNode ident : params) { + Expression expr = paramExprs.containsKey(ident)? paramExprs.get(ident) : ident; + curExpr = null; + expr.accept(this); + assert curExpr != null; + exprTrees.add(curExpr); + } + return exprTrees; + } else { + return translateExprs(func.getParameters()); + } + } + private List translateExprs(final List exprs) { if (exprs == null) { return null; @@ -532,4 +600,25 @@ private static IdentifierTree translateIdent(final IdentNode ident) { return new IdentifierTreeImpl(ident); } + + private List translateProperties(final List propNodes) { + final List propTrees = new ArrayList<>(propNodes.size()); + for (final PropertyNode propNode : propNodes) { + propTrees.add(translateProperty(propNode)); + } + return propTrees; + } + + private PropertyTree translateProperty(final PropertyNode propNode) { + return new PropertyTreeImpl(propNode, + translateExpr(propNode.getKey()), + translateExpr(propNode.getValue()), + (FunctionExpressionTree) translateExpr(propNode.getGetter()), + (FunctionExpressionTree) translateExpr(propNode.getSetter())); + } + + private ModuleTree translateModule(final FunctionNode func) { + return func.getKind() == FunctionNode.Kind.MODULE? + ModuleTreeImpl.create(func) : null; + } } diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IdentifierTree.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IdentifierTree.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IdentifierTree.java Mon Jul 18 09:38:08 2016 -0700 @@ -42,4 +42,46 @@ * @return the name of this identifier */ String getName(); + + /** + * Is this a rest parameter for a function or rest elements of an array? + * + * @return true if this is a rest parameter + */ + boolean isRestParameter(); + + /** + * Is this super identifier? + * + * @return true if this is super identifier + */ + boolean isSuper(); + + /** + * Is this 'this' identifier? + * + * @return true if this is 'this' identifier + */ + boolean isThis(); + + /** + * Is this "*" used in module export entry? + * + * @return true if this "*" used in module export entry? + */ + boolean isStar(); + + /** + * Is this "default" used in module export entry? + * + * @return true if this 'default' used in module export entry? + */ + boolean isDefault(); + + /** + * Is this "*default*" used in module export entry? + * + * @return true if this '*default*' used in module export entry? + */ + boolean isStarDefaultStar(); } diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IdentifierTreeImpl.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IdentifierTreeImpl.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IdentifierTreeImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -27,6 +27,7 @@ package jdk.nashorn.api.tree; import jdk.nashorn.internal.ir.IdentNode; +import jdk.nashorn.internal.ir.Module; final class IdentifierTreeImpl extends ExpressionTreeImpl implements IdentifierTree { private final String name; @@ -47,6 +48,37 @@ } @Override + public boolean isRestParameter() { + return ((IdentNode)node).isRestParameter(); + } + + @Override + public boolean isSuper() { + final IdentNode ident = (IdentNode)node; + return ident.isDirectSuper() || "super".equals(ident.getName()); + } + + @Override + public boolean isThis() { + return "this".equals(((IdentNode)node).getName()); + } + + @Override + public boolean isStar() { + return Module.STAR_NAME.equals(((IdentNode)node).getName()); + } + + @Override + public boolean isDefault() { + return Module.DEFAULT_NAME.equals(((IdentNode)node).getName()); + } + + @Override + public boolean isStarDefaultStar() { + return Module.DEFAULT_EXPORT_BINDING_NAME.equals(((IdentNode)node).getName()); + } + + @Override public R accept(final TreeVisitor visitor, final D data) { return visitor.visitIdentifier(this, data); } diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ImportEntryTree.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ImportEntryTree.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,53 @@ +/* + * 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. + */ +package jdk.nashorn.api.tree; + +import java.util.List; + +/** + * A Tree node for import entry of Module information. + */ +public interface ImportEntryTree extends Tree { + /** + * Returns the entry's module request. + * + * @return the module request + */ + public IdentifierTree getModuleRequest(); + + /** + * Returns the entry's import name. + * + * @return the import name + */ + public IdentifierTree getImportName(); + + /** + * Returns the entry's local name. + * + * @return the local name + */ + public IdentifierTree getLocalName(); +} diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ImportEntryTreeImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ImportEntryTreeImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,100 @@ +/* + * 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. + */ +package jdk.nashorn.api.tree; + +import java.util.List; +import java.util.stream.Collectors; +import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.IdentNode; +import jdk.nashorn.internal.ir.Module; +import static jdk.nashorn.api.tree.ModuleTreeImpl.identOrNull; + +final class ImportEntryTreeImpl extends TreeImpl implements ImportEntryTree { + private final long startPos, endPos; + private final IdentifierTree moduleRequest; + private final IdentifierTree importName; + private final IdentifierTree localName; + + private ImportEntryTreeImpl(final long startPos, final long endPos, + IdentifierTree moduleRequest, + IdentifierTree importName, + IdentifierTree localName) { + super(null); // No underlying Node! + this.startPos = startPos; + this.endPos = endPos; + this.moduleRequest = moduleRequest; + this.importName = importName; + this.localName = localName; + } + + private static ImportEntryTreeImpl createImportEntry(Module.ImportEntry entry) { + return new ImportEntryTreeImpl(entry.getStartPosition(), + entry.getEndPosition(), + identOrNull(entry.getModuleRequest()), + identOrNull(entry.getImportName()), + identOrNull(entry.getLocalName())); + } + + static List createImportList(List importList) { + return importList.stream(). + map(ImportEntryTreeImpl::createImportEntry). + collect(Collectors.toList()); + } + + @Override + public Kind getKind() { + return Tree.Kind.IMPORT_ENTRY; + } + + @Override + public R accept(final TreeVisitor visitor, final D data) { + return visitor.visitImportEntry(this, data); + } + + @Override + public long getStartPosition() { + return startPos; + } + + @Override + public long getEndPosition() { + return endPos; + } + + @Override + public IdentifierTree getModuleRequest() { + return moduleRequest; + } + + @Override + public IdentifierTree getImportName() { + return importName; + } + + @Override + public IdentifierTree getLocalName() { + return localName; + } +} diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ModuleTree.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ModuleTree.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,60 @@ +/* + * 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. + */ +package jdk.nashorn.api.tree; + +import java.util.List; + +/** + * A Tree node for Module information. + */ +public interface ModuleTree extends Tree { + /** + * Returns the list of import entries. + * + * @return the import entries + */ + public List getImportEntries(); + + /** + * Returns the list of local export entries. + * + * @return the local export entries + */ + public List getLocalExportEntries(); + + /** + * Returns the list of indirect export entries. + * + * @return the indirect export entries + */ + public List getIndirectExportEntries(); + + /** + * Returns the list of star export entries. + * + * @return the star export entries + */ + public List getStarExportEntries(); +} diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ModuleTreeImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ModuleTreeImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,99 @@ +/* + * 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. + */ +package jdk.nashorn.api.tree; + +import java.util.List; +import java.util.stream.Collectors; +import jdk.nashorn.internal.ir.FunctionNode; +import jdk.nashorn.internal.ir.IdentNode; +import jdk.nashorn.internal.ir.Module; +import static jdk.nashorn.api.tree.ExportEntryTreeImpl.createExportList; +import static jdk.nashorn.api.tree.ImportEntryTreeImpl.createImportList; + +final class ModuleTreeImpl extends TreeImpl implements ModuleTree { + + private final Module mod; + private final List imports; + private final List localExports; + private final List indirectExports; + private final List starExports; + + private ModuleTreeImpl(final FunctionNode func, + final List imports, + final List localExports, + final List indirectExports, + final List starExports) { + super(func); + assert func.getKind() == FunctionNode.Kind.MODULE : "module function node expected"; + this.mod = func.getModule(); + this.imports = imports; + this.localExports = localExports; + this.indirectExports = indirectExports; + this.starExports = starExports; + } + + static ModuleTreeImpl create(final FunctionNode func) { + final Module mod = func.getModule(); + return new ModuleTreeImpl(func, + createImportList(mod.getImportEntries()), + createExportList(mod.getLocalExportEntries()), + createExportList(mod.getIndirectExportEntries()), + createExportList(mod.getStarExportEntries())); + } + + @Override + public Kind getKind() { + return Tree.Kind.MODULE; + } + + @Override + public List getImportEntries() { + return imports; + } + + @Override + public List getLocalExportEntries() { + return localExports; + } + + @Override + public List getIndirectExportEntries() { + return indirectExports; + } + + @Override + public List getStarExportEntries() { + return starExports; + } + + @Override + public R accept(final TreeVisitor visitor, final D data) { + return visitor.visitModule(this, data); + } + + static IdentifierTree identOrNull(final IdentNode node) { + return node != null? new IdentifierTreeImpl(node) : null; + } +} diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/Parser.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/Parser.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/Parser.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -130,6 +130,8 @@ *
    "--no-syntax-extensions" or "-nse"
    disable ECMAScript syntax extensions
    *
    "-scripting"
    enable scripting mode extensions
    *
    "-strict"
    enable ECMAScript strict mode
    + *
    "--language=es6"
    enable ECMAScript 6 parsing mode
    + *
    "--es6-module"
    enable ECMAScript 6 module parsing mode. This option implies --language=es6
    * * * @throws NullPointerException if options array or any of its element is null @@ -148,6 +150,8 @@ case "-nse": case "-scripting": case "-strict": + case "--language=es6": + case "--es6-module": break; default: throw new IllegalArgumentException(opt); diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ParserImpl.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ParserImpl.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ParserImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -22,7 +22,6 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ - package jdk.nashorn.api.tree; import java.io.File; @@ -31,6 +30,7 @@ import java.io.Reader; import java.net.URL; import java.nio.file.Path; +import java.util.Arrays; import java.util.Map; import java.util.Objects; import jdk.nashorn.api.scripting.NashornException; @@ -47,51 +47,94 @@ final class ParserImpl implements Parser { private final ScriptEnvironment env; + private final boolean moduleMode; ParserImpl(final String... args) throws IllegalArgumentException { - Objects.requireNonNull(args); - Options options = new Options("nashorn"); - options.process(args); - this.env = new ScriptEnvironment(options, - new PrintWriter(System.out), new PrintWriter(System.err)); + Objects.requireNonNull(args); + + // handle the parser specific "--es6-module" option + boolean seenModuleOption = false; + for (int idx = 0; idx < args.length; idx++) { + final String opt = args[idx]; + if (opt.equals("--es6-module")) { + seenModuleOption = true; + /* + * Nashorn parser does not understand parser API specific + * option. This option implies --language=es6. So, we change + * the option to --language=es6. Note that if user specified + * --language=es6 explicitly, that is okay. Nashorn tolerates + * repeated options! + */ + args[idx] = "--language=es6"; + break; + } + } + this.moduleMode = seenModuleOption; + + // append "--parse-only to signal to the Nashorn that it + // is being used in "parse only" mode. + String[] newArgs = Arrays.copyOf(args, args.length + 1, String[].class); + newArgs[args.length] = "--parse-only"; + Options options = new Options("nashorn"); + options.process(newArgs); + this.env = new ScriptEnvironment(options, + new PrintWriter(System.out), new PrintWriter(System.err)); } @Override public CompilationUnitTree parse(final File file, final DiagnosticListener listener) throws IOException, NashornException { + if (moduleMode) { + return parseModule(file, listener); + } final Source src = Source.sourceFor(Objects.requireNonNull(file).getName(), file); return translate(makeParser(src, listener).parse()); } @Override public CompilationUnitTree parse(final Path path, final DiagnosticListener listener) throws IOException, NashornException { + if (moduleMode) { + return parseModule(path, listener); + } final Source src = Source.sourceFor(Objects.requireNonNull(path).toString(), path); return translate(makeParser(src, listener).parse()); } @Override public CompilationUnitTree parse(final URL url, final DiagnosticListener listener) throws IOException, NashornException { + if (moduleMode) { + return parseModule(url, listener); + } final Source src = Source.sourceFor(url.toString(), url); return translate(makeParser(src, listener).parse()); } @Override public CompilationUnitTree parse(final String name, final Reader reader, final DiagnosticListener listener) throws IOException, NashornException { + if (moduleMode) { + return parseModule(name, reader, listener); + } final Source src = Source.sourceFor(Objects.requireNonNull(name), Objects.requireNonNull(reader)); return translate(makeParser(src, listener).parse()); } @Override public CompilationUnitTree parse(final String name, final String code, final DiagnosticListener listener) throws NashornException { + if (moduleMode) { + return parseModule(name, code, listener); + } final Source src = Source.sourceFor(name, code); return translate(makeParser(src, listener).parse()); } @Override public CompilationUnitTree parse(final ScriptObjectMirror scriptObj, final DiagnosticListener listener) throws NashornException { - final Map map = Objects.requireNonNull(scriptObj); + if (moduleMode) { + return parseModule(scriptObj, listener); + } + final Map map = Objects.requireNonNull(scriptObj); if (map.containsKey("script") && map.containsKey("name")) { final String script = JSType.toString(map.get("script")); - final String name = JSType.toString(map.get("name")); + final String name = JSType.toString(map.get("name")); final Source src = Source.sourceFor(name, script); return translate(makeParser(src, listener).parse()); } else { @@ -99,12 +142,55 @@ } } + private CompilationUnitTree parseModule(File file, DiagnosticListener listener) throws IOException, NashornException { + final Source src = Source.sourceFor(Objects.requireNonNull(file).getName(), file); + return makeModule(src, listener); + } + + private CompilationUnitTree parseModule(Path path, DiagnosticListener listener) throws IOException, NashornException { + final Source src = Source.sourceFor(Objects.requireNonNull(path).toString(), path); + return makeModule(src, listener); + } + + private CompilationUnitTree parseModule(URL url, DiagnosticListener listener) throws IOException, NashornException { + final Source src = Source.sourceFor(url.toString(), url); + return makeModule(src, listener); + } + + private CompilationUnitTree parseModule(String name, Reader reader, DiagnosticListener listener) throws IOException, NashornException { + final Source src = Source.sourceFor(Objects.requireNonNull(name), Objects.requireNonNull(reader)); + return makeModule(src, listener); + } + + private CompilationUnitTree parseModule(String name, String code, DiagnosticListener listener) throws NashornException { + final Source src = Source.sourceFor(name, code); + return makeModule(src, listener); + } + + private CompilationUnitTree parseModule(ScriptObjectMirror scriptObj, DiagnosticListener listener) throws NashornException { + final Map map = Objects.requireNonNull(scriptObj); + if (map.containsKey("script") && map.containsKey("name")) { + final String script = JSType.toString(map.get("script")); + final String name = JSType.toString(map.get("name")); + final Source src = Source.sourceFor(name, script); + return makeModule(src, listener); + } else { + throw new IllegalArgumentException("can't find 'script' and 'name' properties"); + } + } + + private CompilationUnitTree makeModule(Source src, DiagnosticListener listener) { + final FunctionNode modFunc = makeParser(src, listener).parseModule(src.getName()); + return new IRTranslator().translate(modFunc); + } + private jdk.nashorn.internal.parser.Parser makeParser(final Source source, final DiagnosticListener listener) { - final ErrorManager errMgr = listener != null? new ListenerErrorManager(listener) : new Context.ThrowErrorManager(); + final ErrorManager errMgr = listener != null ? new ListenerErrorManager(listener) : new Context.ThrowErrorManager(); return new jdk.nashorn.internal.parser.Parser(env, source, errMgr); } private static class ListenerErrorManager extends ErrorManager { + private final DiagnosticListener listener; ListenerErrorManager(final DiagnosticListener listener) { diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/PropertyTree.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/PropertyTree.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/PropertyTree.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -60,4 +60,18 @@ * @return the getter function of the property */ public FunctionExpressionTree getSetter(); + + /** + * Is this a class static property? + * + * @return true if this is a static property + */ + public boolean isStatic(); + + /** + * Is this a computed property? + * + * @return true if this is a computed property + */ + public boolean isComputed(); } diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/PropertyTreeImpl.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/PropertyTreeImpl.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/PropertyTreeImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -32,6 +32,8 @@ private final ExpressionTree value; private final FunctionExpressionTree getter; private final FunctionExpressionTree setter; + private final boolean isStatic, isComputed; + PropertyTreeImpl(final PropertyNode node, final ExpressionTree key, final ExpressionTree value, @@ -42,6 +44,8 @@ this.value = value; this.getter = getter; this.setter = setter; + this.isStatic = node.isStatic(); + this.isComputed = node.isComputed(); } @Override @@ -70,6 +74,16 @@ } @Override + public boolean isStatic() { + return isStatic; + } + + @Override + public boolean isComputed() { + return isComputed; + } + + @Override public R accept(final TreeVisitor visitor, final D data) { return visitor.visitProperty(this, data); } diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/RegExpLiteralTree.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/RegExpLiteralTree.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/RegExpLiteralTree.java Mon Jul 18 09:38:08 2016 -0700 @@ -30,7 +30,7 @@ * * @since 9 */ -public interface RegExpLiteralTree extends Tree { +public interface RegExpLiteralTree extends ExpressionTree { /** * Regular expression pattern to match. * diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SimpleTreeVisitorES5_1.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SimpleTreeVisitorES5_1.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SimpleTreeVisitorES5_1.java Mon Jul 18 09:38:08 2016 -0700 @@ -61,6 +61,45 @@ return null; } + /** + * Visits a {@code ModuleTree} tree by calling {@code + * visitUnknown}. + * + * @param node {@inheritDoc} + * @param p {@inheritDoc} + * @return the result of {@code visitUnknown} + */ + @Override + public R visitModule(ModuleTree node, P p) { + return visitUnknown(node, p); + } + + /** + * Visits an {@code ExportEntryTree} tree by calling {@code + * visitUnknown}. + * + * @param node {@inheritDoc} + * @param p {@inheritDoc} + * @return the result of {@code visitUnknown} + */ + @Override + public R visitExportEntry(ExportEntryTree node, P p) { + return visitUnknown(node, p); + } + + /** + * Visits an {@code ImportEntryTree} tree by calling {@code + * visitUnknown}. + * + * @param node {@inheritDoc} + * @param p {@inheritDoc} + * @return the result of {@code visitUnknown} + */ + @Override + public R visitImportEntry(ImportEntryTree node, P p) { + return visitUnknown(node, p); + } + @Override public R visitBinary(final BinaryTree node, final P r) { node.getLeftOperand().accept(this, r); @@ -105,6 +144,32 @@ return null; } + /** + * Visits a {@code ClassDeclarationTree} tree by calling {@code + * visitUnknown}. + * + * @param node {@inheritDoc} + * @param p {@inheritDoc} + * @return the result of {@code visitUnknown} + */ + @Override + public R visitClassDeclaration(ClassDeclarationTree node, P p) { + return visitUnknown(node, p); + } + + /** + * Visits a {@code ClassExpressionTree} tree by calling {@code + * visitUnknown}. + * + * @param node {@inheritDoc} + * @param p {@inheritDoc} + * @return the result of {@code visitUnknown} + */ + @Override + public R visitClassExpression(ClassExpressionTree node, P p) { + return visitUnknown(node, p); + } + @Override public R visitConditionalExpression(final ConditionalExpressionTree node, final P r) { node.getCondition().accept(this, r); @@ -173,6 +238,19 @@ return null; } + /** + * Visits a {@code ForOfLoopTree} tree by calling {@code + * visitUnknown}. + * + * @param node {@inheritDoc} + * @param p {@inheritDoc} + * @return the result of {@code visitUnknown} + */ + @Override + public R visitForOfLoop(ForOfLoopTree node, P p) { + return visitUnknown(node, p); + } + @Override public R visitFunctionCall(final FunctionCallTree node, final P r) { node.getFunctionSelect().accept(this, r); @@ -305,11 +383,37 @@ return null; } + /** + * Visits a {@code TemplateLiteralTree} tree by calling {@code + * visitUnknown}. + * + * @param node {@inheritDoc} + * @param p {@inheritDoc} + * @return the result of {@code visitUnknown} + */ + @Override + public R visitTemplateLiteral(TemplateLiteralTree node, P p) { + return visitUnknown(node, p); + } + @Override public R visitEmptyStatement(final EmptyStatementTree node, final P r) { return null; } + /** + * Visits a {@code SpreadTree} tree by calling {@code + * visitUnknown}. + * + * @param node {@inheritDoc} + * @param p {@inheritDoc} + * @return the result of {@code visitUnknown} + */ + @Override + public R visitSpread(SpreadTree node, P p) { + return visitUnknown(node, p); + } + @Override public R visitSwitch(final SwitchTree node, final P r) { node.getExpression().accept(this, r); @@ -382,9 +486,36 @@ return null; } + /** + * Visits a {@code YieldTree} tree by calling {@code + * visitUnknown}. + * + * @param node {@inheritDoc} + * @param p {@inheritDoc} + * @return the result of {@code visitUnknown} + */ @Override - public R visitUnknown(final Tree node, final P r) { + public R visitYield(YieldTree node, P p) { + return visitUnknown(node, p); + } + + /** + * {@inheritDoc} + * + * @implSpec The default implementation of this method in {@code + * SimpleTreeVisitorES5_1} will always throw {@code + * UnknownTypeException}. This behavior is not required of a + * subclass. + * + * @param node {@inheritDoc} + * @param p {@inheritDoc} + * @return abnormal return by throwing exception always + * @throws UnknownTreeException + * a visitor implementation may optionally throw this exception + */ + @Override + public R visitUnknown(final Tree node, final P p) { // unknown in ECMAScript 5.1 edition - throw new UnknownTreeException(node, r); + throw new UnknownTreeException(node, p); } } diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SimpleTreeVisitorES6.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SimpleTreeVisitorES6.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,226 @@ +/* + * 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. + */ + +package jdk.nashorn.api.tree; + +import java.util.List; + +/** + * A simple implementation of the TreeVisitor for ECMAScript edition 6. + * + *

    The visit methods corresponding to ES 6 language constructs walk the + * "components" of the given tree by calling accept method passing the + * current visitor and the additional parameter. + * + *

    For constructs introduced in later versions, {@code visitUnknown} + * is called instead which throws {@link UnknownTreeException}. + * + *

    Methods in this class may be overridden subject to their + * general contract. Note that annotating methods in concrete + * subclasses with {@link java.lang.Override @Override} will help + * ensure that methods are overridden as intended. + * + * @param the return type of this visitor's methods. Use {@link + * Void} for visitors that do not need to return results. + * @param

    the type of the additional parameter to this visitor's + * methods. Use {@code Void} for visitors that do not need an + * additional parameter. + */ +public class SimpleTreeVisitorES6 extends SimpleTreeVisitorES5_1 { + @Override + public R visitCompilationUnit(final CompilationUnitTree node, final P r) { + final ModuleTree mod = node.getModule(); + if (mod != null) { + mod.accept(this, r); + } + return super.visitCompilationUnit(node, r); + } + + /** + * Visit Module tree. + * + * @param node node being visited + * @param p extra parameter passed to the visitor + * @return value from the visitor + */ + @Override + public R visitModule(ModuleTree node, P p) { + node.getImportEntries().forEach(e -> visitImportEntry(e, p)); + node.getLocalExportEntries().forEach(e -> visitExportEntry(e, p)); + node.getIndirectExportEntries().forEach(e -> visitExportEntry(e, p)); + node.getStarExportEntries().forEach(e -> visitExportEntry(e, p)); + return null; + } + + /** + * Visit Module ExportEntry tree. + * + * @param node node being visited + * @param p extra parameter passed to the visitor + * @return value from the visitor + */ + @Override + public R visitExportEntry(ExportEntryTree node, P p) { + return null; + } + + /** + * Visit Module ImportEntry tree. + * + * @param node node being visited + * @param p extra parameter passed to the visitor + * @return value from the visitor + */ + @Override + public R visitImportEntry(ImportEntryTree node, P p) { + return null; + } + + /** + * Visit class statement tree. + * + * @param node node being visited + * @param p extra parameter passed to the visitor + * @return value from the visitor + */ + @Override + public R visitClassDeclaration(ClassDeclarationTree node, P p) { + node.getName().accept(this, p); + final ExpressionTree heritage = node.getClassHeritage(); + if (heritage != null) { + heritage.accept(this, p); + } + final PropertyTree constructor = node.getConstructor(); + if (constructor != null) { + constructor.accept(this, p); + } + final List elements = node.getClassElements(); + if (elements != null) { + for (PropertyTree prop : elements) { + prop.accept(this, p); + } + } + + return null; + } + + /** + * Visit class expression tree. + * + * @param node node being visited + * @param p extra parameter passed to the visitor + * @return value from the visitor + */ + @Override + public R visitClassExpression(ClassExpressionTree node, P p) { + node.getName().accept(this, p); + final ExpressionTree heritage = node.getClassHeritage(); + if (heritage != null) { + heritage.accept(this, p); + } + final PropertyTree constructor = node.getConstructor(); + if (constructor != null) { + constructor.accept(this, p); + } + final List elements = node.getClassElements(); + if (elements != null) { + for (PropertyTree prop : elements) { + prop.accept(this, p); + } + } + + return null; + } + + /** + * Visit for..of statement tree. + * + * @param node node being visited + * @param p extra parameter passed to the visitor + * @return value from the visitor + */ + @Override + public R visitForOfLoop(final ForOfLoopTree node, final P p) { + node.getVariable().accept(this, p); + node.getExpression().accept(this, p); + final StatementTree stat = node.getStatement(); + if (stat != null) { + stat.accept(this, p); + } + return null; + } + + /** + * Visit 'yield' expression tree. + * + * @param node node being visited + * @param p extra parameter passed to the visitor + * @return value from the visitor + */ + @Override + public R visitYield(YieldTree node, P p) { + node.getExpression().accept(this, p); + return null; + } + + /** + * Visit 'spread' expression tree. + * + * @param node node being visited + * @param p extra parameter passed to the visitor + * @return value from the visitor + */ + @Override + public R visitSpread(SpreadTree node, P p) { + node.getExpression().accept(this, p); + return null; + } + + /** + * Visit template literal tree. + * + * @param node node being visited + * @param p extra parameter passed to the visitor + * @return value from the visitor + */ + @Override + public R visitTemplateLiteral(TemplateLiteralTree node, P p) { + final List expressions = node.getExpressions(); + for (ExpressionTree expr : expressions) { + expr.accept(this, p); + } + return null; + } + + @Override + public R visitVariable(final VariableTree node, final P r) { + final ExpressionTree expr = node.getBinding(); + if (expr != null) { + expr.accept(this, r); + } + super.visitVariable(node, r); + return null; + } +} diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SpreadTree.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SpreadTree.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,38 @@ +/* + * 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. + */ + +package jdk.nashorn.api.tree; + +/** + * A tree node for spread operator in array elements, function call arguments. + */ +public interface SpreadTree extends ExpressionTree { + /** + * Returns the expression that is being spread. + * + * @return The expression that is being spread. + */ + ExpressionTree getExpression(); +} diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SpreadTreeImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SpreadTreeImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,53 @@ +/* + * 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. + */ +package jdk.nashorn.api.tree; + +import jdk.nashorn.internal.ir.Expression; + +final class SpreadTreeImpl extends ExpressionTreeImpl + implements SpreadTree { + + private final ExpressionTree expr; + + SpreadTreeImpl(final Expression exprNode, final ExpressionTree expr) { + super(exprNode); + this.expr = expr; + } + + @Override + public Tree.Kind getKind() { + return Tree.Kind.SPREAD; + } + + @Override + public ExpressionTree getExpression() { + return expr; + } + + @Override + public R accept(final TreeVisitor visitor, final D data) { + return visitor.visitSpread(this, data); + } +} diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TemplateLiteralTree.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TemplateLiteralTree.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,48 @@ +/* + * 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. + */ + +package jdk.nashorn.api.tree; + +import java.util.List; + +/** + * A tree node for template literal strings. + * + * For example: + *

    + * `This is a String with ${computed} values in it`
    + * 
    + * + * @since 9 + * + */ +public interface TemplateLiteralTree extends ExpressionTree { + /** + * Returns the list of expressions in this template string + * + * @return the list of expressions in this template string + */ + List getExpressions(); +} diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TemplateLiteralTreeImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TemplateLiteralTreeImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,56 @@ +/* + * 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. + */ + +package jdk.nashorn.api.tree; + +import java.util.List; +import jdk.nashorn.internal.ir.Expression; + +final class TemplateLiteralTreeImpl extends ExpressionTreeImpl + implements TemplateLiteralTree { + + private final List expressions; + + TemplateLiteralTreeImpl(Expression node, List expressions) { + super(node); + this.expressions = expressions; + } + + @Override + public Kind getKind() { + return Kind.TEMPLATE_LITERAL; + } + + @Override + public List getExpressions() { + return expressions; + } + + + @Override + public R accept(final TreeVisitor visitor, final D data) { + return visitor.visitTemplateLiteral(this, data); + } +} diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/Tree.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/Tree.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/Tree.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -65,6 +65,16 @@ BREAK(BreakTree.class), /** + * Used for instances of {@link ClassDeclarationTree}. + */ + CLASS(ClassDeclarationTree.class), + + /** + * Used for instances of {@link ClassExpressionTree}. + */ + CLASS_EXPRESSION(ClassExpressionTree.class), + + /** * Used for instances of {@link CaseTree}. */ CASE(CaseTree.class), @@ -150,6 +160,21 @@ LABELED_STATEMENT(LabeledStatementTree.class), /** + * Used for instances of {@link ModuleTree}. + */ + MODULE(ModuleTree.class), + + /** + * Used for instances of {@link ExportEntryTree}. + */ + EXPORT_ENTRY(ExportEntryTree.class), + + /** + * Used for instances of {@link ImportEntryTree}. + */ + IMPORT_ENTRY(ImportEntryTree.class), + + /** * Used for instances of {@link FunctionDeclarationTree}. */ FUNCTION(FunctionDeclarationTree.class), @@ -185,6 +210,11 @@ REGEXP_LITERAL(RegExpLiteralTree.class), /** + * Used for instances of {@link TemplateLiteralTree}. + */ + TEMPLATE_LITERAL(TemplateLiteralTree.class), + + /** * Used for instances of {@link ReturnTree}. */ RETURN(ReturnTree.class), @@ -286,7 +316,7 @@ /** * Used for instances of {@link UnaryTree} representing logical - * void operator {@code typeof}. + * void operator {@code void}. */ VOID(UnaryTree.class), @@ -495,6 +525,18 @@ OR_ASSIGNMENT(CompoundAssignmentTree.class), /** + * Used for instances of {@link SpreadTree} representing + * spread "operator" for arrays and function call arguments. + */ + SPREAD(SpreadTree.class), + + /** + * Used for instances of {@link YieldTree} representing (generator) + * yield expression {@code yield expr}. + */ + YIELD(YieldTree.class), + + /** * Used for instances of {@link LiteralTree} representing * a number literal expression of type {@code double}. */ diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TreeImpl.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TreeImpl.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TreeImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -137,10 +137,15 @@ return Kind.BITWISE_COMPLEMENT; case DELETE: return Kind.DELETE; + case SPREAD_ARRAY: + case SPREAD_ARGUMENT: + return Kind.SPREAD; case TYPEOF: return Kind.TYPEOF; case VOID: return Kind.VOID; + case YIELD: + return Kind.YIELD; case IN: return Kind.IN; case INSTANCEOF: diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TreeVisitor.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TreeVisitor.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TreeVisitor.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -118,6 +118,24 @@ R visitCatch(CatchTree node, P p); /** + * Visit class statement tree. + * + * @param node node being visited + * @param p extra parameter passed to the visitor + * @return value from the visitor + */ + R visitClassDeclaration(ClassDeclarationTree node, P p); + + /** + * Visit class expression tree. + * + * @param node node being visited + * @param p extra parameter passed to the visitor + * @return value from the visitor + */ + R visitClassExpression(ClassExpressionTree node, P p); + + /** * Visit conditional expression tree. * * @param node node being visited @@ -190,6 +208,15 @@ R visitForInLoop(ForInLoopTree node, P p); /** + * Visit for..of statement tree. + * + * @param node node being visited + * @param p extra parameter passed to the visitor + * @return value from the visitor + */ + R visitForOfLoop(ForOfLoopTree node, P p); + + /** * Visit function call expression tree. * * @param node node being visited @@ -216,7 +243,7 @@ */ R visitFunctionExpression(FunctionExpressionTree node, P p); - /** + /** * Visit identifier tree. * * @param node node being visited @@ -334,6 +361,15 @@ R visitRegExpLiteral(RegExpLiteralTree node, P p); /** + * Visit template literal tree. + * + * @param node node being visited + * @param p extra parameter passed to the visitor + * @return value from the visitor + */ + R visitTemplateLiteral(TemplateLiteralTree node, P p); + + /** * Visit an empty statement tree. * * @param node node being visited @@ -343,6 +379,15 @@ R visitEmptyStatement(EmptyStatementTree node, P p); /** + * Visit 'spread' expression tree. + * + * @param node node being visited + * @param p extra parameter passed to the visitor + * @return value from the visitor + */ + R visitSpread(SpreadTree node, P p); + + /** * Visit 'switch' statement tree. * * @param node node being visited @@ -370,6 +415,33 @@ R visitCompilationUnit(CompilationUnitTree node, P p); /** + * Visit Module tree. + * + * @param node node being visited + * @param p extra parameter passed to the visitor + * @return value from the visitor + */ + R visitModule(ModuleTree node, P p); + + /** + * Visit Module ExportEntry tree. + * + * @param node node being visited + * @param p extra parameter passed to the visitor + * @return value from the visitor + */ + R visitExportEntry(ExportEntryTree node, P p); + + /** + * Visit Module ImportEntry tree. + * + * @param node node being visited + * @param p extra parameter passed to the visitor + * @return value from the visitor + */ + R visitImportEntry(ImportEntryTree node, P p); + + /** * Visit 'try' statement tree. * * @param node node being visited @@ -424,6 +496,15 @@ R visitWith(WithTree node, P p); /** + * Visit 'yield' expression tree. + * + * @param node node being visited + * @param p extra parameter passed to the visitor + * @return value from the visitor + */ + R visitYield(YieldTree node, P p); + + /** * Visit unknown expression/statement tree. This fallback will be * called if new Tree subtypes are introduced in future. A specific * implementation may throw {{@linkplain UnknownTreeException unknown tree exception} diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/VariableTree.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/VariableTree.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/VariableTree.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -26,22 +26,26 @@ package jdk.nashorn.api.tree; /** - * A tree node for a variable declaration. + * A tree node for a variable declaration statement. * * For example: *
    - *   var name initializer ;
    + *   var name [ initializer ] ;
    + *   var binding_pattern [ initializer ];
      * 
    * * @since 9 */ public interface VariableTree extends StatementTree { /** - * Returns the name of this variable. + * Returns the binding of this declaration. This is an {@link IdentifierTree} + * for a binding identifier case (simple variable declaration). + * This is an {@link ObjectLiteralTree} or a {@link ArrayLiteralTree} for a + * destructuring declaration. * - * @return the name of this variable + * @return the binding expression of this declaration */ - String getName(); + ExpressionTree getBinding(); /** * Returns the initial value expression for this variable. This is @@ -50,4 +54,18 @@ * @return the initial value expression */ ExpressionTree getInitializer(); + + /** + * Is this a const declaration? + * + * @return true if this is a const declaration + */ + boolean isConst(); + + /** + * Is this a let declaration? + * + * @return true if this is a let declaration + */ + boolean isLet(); } diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/VariableTreeImpl.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/VariableTreeImpl.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/VariableTreeImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -28,12 +28,12 @@ import jdk.nashorn.internal.ir.VarNode; final class VariableTreeImpl extends StatementTreeImpl implements VariableTree { - private final String name; + private final IdentifierTree ident; private final ExpressionTree init; - VariableTreeImpl(final VarNode node, final ExpressionTree init) { + VariableTreeImpl(final VarNode node, final IdentifierTree ident, final ExpressionTree init) { super(node); - this.name = node.getName().getName(); + this.ident = ident; this.init = init; } @@ -43,8 +43,8 @@ } @Override - public String getName() { - return name; + public ExpressionTree getBinding() { + return ident; } @Override @@ -53,6 +53,16 @@ } @Override + public boolean isConst() { + return ((VarNode)node).isConst(); + } + + @Override + public boolean isLet() { + return ((VarNode)node).isLet(); + } + + @Override public R accept(final TreeVisitor visitor, final D data) { return visitor.visitVariable(this, data); } diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/YieldTree.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/YieldTree.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,66 @@ +/* + * 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. + */ + +package jdk.nashorn.api.tree; + +/** + * A tree node for yield expressions used in generator functions. + * + * For example: + *
    + * function* id(){
    + *     var index = 0;
    + *     while(index < 10)
    + *         yield index++;
    + * }
    + * 
    + * + * @since 9 + */ +public interface YieldTree extends ExpressionTree { + /** + * Returns the expression that is yielded. + * + * @return The expression that is yielded. + */ + ExpressionTree getExpression(); + + /** + * Is this a yield * expression in a generator function? + * + * For example: + *
    +     * function* id(){
    +     *     yield 1;
    +     *     yield * anotherGeneratorFunc();
    +     *     yield 10;
    +     * }
    +     * 
    + * + * + * @return true if this is a yield * expression + */ + boolean isStar(); +} diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/YieldTreeImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/YieldTreeImpl.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,60 @@ +/* + * 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. + */ +package jdk.nashorn.api.tree; + +import jdk.nashorn.internal.ir.Expression; +import jdk.nashorn.internal.ir.UnaryNode; +import jdk.nashorn.internal.parser.TokenType; + +final class YieldTreeImpl extends ExpressionTreeImpl + implements YieldTree { + + private final ExpressionTree expr; + + YieldTreeImpl(final Expression exprNode, final ExpressionTree expr) { + super(exprNode); + this.expr = expr; + } + + @Override + public Kind getKind() { + return Kind.YIELD; + } + + @Override + public ExpressionTree getExpression() { + return expr; + } + + @Override + public boolean isStar() { + return ((UnaryNode) node).isTokenType(TokenType.YIELD_STAR); + } + + @Override + public R accept(final TreeVisitor visitor, final D data) { + return visitor.visitYield(this, data); + } +} diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SplitIntoFunctions.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SplitIntoFunctions.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SplitIntoFunctions.java Mon Jul 18 09:38:08 2016 -0700 @@ -167,6 +167,7 @@ createIdent(name), originalFn.getName() + "$" + name, isProgram ? Collections.singletonList(createReturnParamIdent()) : Collections.emptyList(), + null, FunctionNode.Kind.NORMAL, // We only need IS_SPLIT conservatively, in case it contains any array units so that we force // the :callee's existence, to force :scope to never be in a slot lower than 2. This is actually diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ClassNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ClassNode.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ClassNode.java Mon Jul 18 09:38:08 2016 -0700 @@ -42,6 +42,7 @@ private final PropertyNode constructor; private final List classElements; private final int line; + private final boolean isStatement; /** * Constructor. @@ -53,15 +54,17 @@ * @param classHeritage class heritage * @param constructor constructor * @param classElements class elements + * @param isStatement is this a statement or an expression? */ public ClassNode(final int line, final long token, final int finish, final IdentNode ident, final Expression classHeritage, final PropertyNode constructor, - final List classElements) { + final List classElements, final boolean isStatement) { super(token, finish); this.line = line; this.ident = ident; this.classHeritage = classHeritage; this.constructor = constructor; this.classElements = classElements; + this.isStatement = isStatement; } /** @@ -101,6 +104,15 @@ } /** + * Returns if this class was a statement or an expression + * + * @return true if this class was a statement + */ + public boolean isStatement() { + return isStatement; + } + + /** * Returns the line number. * * @return the line number diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ExpressionStatement.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ExpressionStatement.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ExpressionStatement.java Mon Jul 18 09:38:08 2016 -0700 @@ -27,6 +27,7 @@ import jdk.nashorn.internal.ir.annotations.Immutable; import jdk.nashorn.internal.ir.visitor.NodeVisitor; +import jdk.nashorn.internal.parser.TokenType; /** * IR representation for executing bare expressions. Basically, an expression @@ -39,6 +40,23 @@ /** Expression to execute. */ private final Expression expression; + private final TokenType destructuringDecl; + + /** + * Constructor + * + * @param lineNumber line number + * @param token token + * @param finish finish + * @param expression the expression to execute + * @param destructuringDecl does this statement represent a destructuring declaration? + */ + public ExpressionStatement(final int lineNumber, final long token, final int finish, + final Expression expression, final TokenType destructuringDecl) { + super(lineNumber, token, finish); + this.expression = expression; + this.destructuringDecl = destructuringDecl; + } /** * Constructor @@ -49,13 +67,13 @@ * @param expression the expression to execute */ public ExpressionStatement(final int lineNumber, final long token, final int finish, final Expression expression) { - super(lineNumber, token, finish); - this.expression = expression; + this(lineNumber, token, finish, expression, null); } private ExpressionStatement(final ExpressionStatement expressionStatement, final Expression expression) { super(expressionStatement); this.expression = expression; + this.destructuringDecl = null; } @Override @@ -81,6 +99,15 @@ } /** + * Return declaration type if this expression statement is a destructuring declaration + * + * @return declaration type (LET, VAR, CONST) if destructuring declaration, null otherwise. + */ + public TokenType destructuringDeclarationType() { + return destructuringDecl; + } + + /** * Reset the expression to be executed * @param expression the expression * @return new or same execute node diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java Mon Jul 18 09:38:08 2016 -0700 @@ -35,6 +35,7 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Map; import jdk.nashorn.internal.codegen.CompileUnit; import jdk.nashorn.internal.codegen.Compiler; import jdk.nashorn.internal.codegen.CompilerConstants; @@ -106,6 +107,9 @@ /** List of parameters. */ private final List parameters; + /** Map of ES6 function parameter expressions. */ + private final Map parameterExpressions; + /** First token of function. **/ private final long firstToken; @@ -242,6 +246,9 @@ /** Does this function use new.target? */ public static final int ES6_USES_NEW_TARGET = 1 << 25; + /** Does this function have expression as its body? */ + public static final int HAS_EXPRESSION_BODY = 1 << 26; + /** Does this function or any nested functions contain an eval? */ private static final int HAS_DEEP_EVAL = HAS_EVAL | HAS_NESTED_EVAL; @@ -306,6 +313,7 @@ * @param ident the identifier * @param name the name of the function * @param parameters parameter list + * @param paramExprs the ES6 function parameter expressions * @param kind kind of function as in {@link FunctionNode.Kind} * @param flags initial flags * @param body body of the function @@ -324,6 +332,7 @@ final IdentNode ident, final String name, final List parameters, + final Map paramExprs, final FunctionNode.Kind kind, final int flags, final Block body, @@ -338,6 +347,7 @@ this.name = name; this.kind = kind; this.parameters = parameters; + this.parameterExpressions = paramExprs; this.firstToken = firstToken; this.lastToken = lastToken; this.namespace = namespace; @@ -375,6 +385,7 @@ this.lastToken = lastToken; this.body = body; this.parameters = parameters; + this.parameterExpressions = functionNode.parameterExpressions; this.thisProperties = thisProperties; this.rootClass = rootClass; this.source = source; @@ -977,6 +988,15 @@ } /** + * Get the ES6 style parameter expressions of this function. This may be null. + * + * @return a Map of parameter IdentNode to Expression node (for ES6 parameter expressions) + */ + public Map getParameterExpressions() { + return parameterExpressions; + } + + /** * Return the number of parameters to this function * @return the number of parameters */ diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TemplateLiteral.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TemplateLiteral.java Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2010, 2013, 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. + */ + +package jdk.nashorn.internal.ir; + +import java.util.Collections; +import java.util.List; +import jdk.nashorn.internal.codegen.types.Type; +import jdk.nashorn.internal.ir.visitor.NodeVisitor; + +/** + * Represents ES6 template string expression. Note that this Node class is used + * only in "parse only" mode. In evaluation mode, Parser directly folds template + * literal as string concatenation. Parser API uses this node to represent ES6 + * template literals "as is" rather than as a String concatenation. + */ +public final class TemplateLiteral extends Expression { + private static final long serialVersionUID = 1L; + private final List exprs; + + public TemplateLiteral(final List exprs) { + super(exprs.get(0).getToken(), exprs.get(exprs.size() - 1).finish); + this.exprs = exprs; + } + + @Override + public Type getType() { + return Type.STRING; + } + + @Override + public Node accept(NodeVisitor visitor) { + if (visitor.enterTemplateLiteral(this)) { + return visitor.leaveTemplateLiteral(this); + } + + return this; + } + + @Override + public void toString(StringBuilder sb, boolean printType) { + for (Expression expr : exprs) { + sb.append(expr); + } + } + + /** + * The list of expressions that are part of this template literal. + * + * @return the list of expressions that are part of this template literal. + */ + public List getExpressions() { + return Collections.unmodifiableList(exprs); + } +} \ No newline at end of file diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/visitor/NodeVisitor.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/visitor/NodeVisitor.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/visitor/NodeVisitor.java Mon Jul 18 09:38:08 2016 -0700 @@ -59,6 +59,7 @@ import jdk.nashorn.internal.ir.SplitNode; import jdk.nashorn.internal.ir.SplitReturn; import jdk.nashorn.internal.ir.SwitchNode; +import jdk.nashorn.internal.ir.TemplateLiteral; import jdk.nashorn.internal.ir.TernaryNode; import jdk.nashorn.internal.ir.ThrowNode; import jdk.nashorn.internal.ir.TryNode; @@ -738,6 +739,26 @@ } /** + * Callback for entering a TemplateLiteral (used only in --parse-only mode) + * + * @param templateLiteral the node + * @return true if traversal should continue and node children be traversed, false otherwise + */ + public boolean enterTemplateLiteral(final TemplateLiteral templateLiteral) { + return enterDefault(templateLiteral); + } + + /** + * Callback for leaving a TemplateLiteral (used only in --parse-only mode) + * + * @param templateLiteral the node + * @return processed node, which will replace the original one, or the original node + */ + public Node leaveTemplateLiteral(final TemplateLiteral templateLiteral) { + return leaveDefault(templateLiteral); + } + + /** * Callback for entering a TernaryNode * * @param ternaryNode the node diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java Mon Jul 18 09:38:08 2016 -0700 @@ -127,6 +127,7 @@ import jdk.nashorn.internal.ir.RuntimeNode; import jdk.nashorn.internal.ir.Statement; import jdk.nashorn.internal.ir.SwitchNode; +import jdk.nashorn.internal.ir.TemplateLiteral; import jdk.nashorn.internal.ir.TernaryNode; import jdk.nashorn.internal.ir.ThrowNode; import jdk.nashorn.internal.ir.TryNode; @@ -545,7 +546,7 @@ sb.append(ident.getName()); final String name = namespace.uniqueName(sb.toString()); - assert parentFunction != null || name.equals(PROGRAM.symbolName()) : "name = " + name; + assert parentFunction != null || kind == FunctionNode.Kind.MODULE || name.equals(PROGRAM.symbolName()) : "name = " + name; int flags = 0; if (isStrictMode) { @@ -575,6 +576,7 @@ ident, function.getName(), parameters, + function.getParameterExpressions(), kind, function.getFlags(), body, @@ -623,19 +625,6 @@ } /** - * Get the statements in a case clause. - */ - private List caseStatementList() { - final ParserContextBlockNode newBlock = newBlock(); - try { - statementList(); - } finally { - restoreBlock(newBlock); - } - return newBlock.getStatements(); - } - - /** * Get all the statements generated by a single statement. * @return Statements. */ @@ -855,17 +844,6 @@ }); } - private static Expression newBinaryExpression(final long op, final Expression lhs, final Expression rhs) { - final TokenType opType = Token.descType(op); - - // Build up node. - if (BinaryNode.isLogical(opType)) { - return new BinaryNode(op, new JoinPredecessorExpression(lhs), new JoinPredecessorExpression(rhs)); - } - return new BinaryNode(op, lhs, rhs); - } - - /** * Reduce increment/decrement to simpler operations. * @param firstToken First token. @@ -1268,7 +1246,7 @@ className = getIdent(); } - return classTail(classLineNumber, classToken, className); + return classTail(classLineNumber, classToken, className, isStatement); } private static final class ClassElementKey { @@ -1317,7 +1295,8 @@ * static MethodDefinition[?Yield] * ; */ - private ClassNode classTail(final int classLineNumber, final long classToken, final IdentNode className) { + private ClassNode classTail(final int classLineNumber, final long classToken, + final IdentNode className, final boolean isStatement) { final boolean oldStrictMode = isStrictMode; isStrictMode = true; try { @@ -1399,7 +1378,7 @@ } classElements.trimToSize(); - return new ClassNode(classLineNumber, classToken, finish, className, classHeritage, constructor, classElements); + return new ClassNode(classLineNumber, classToken, finish, className, classHeritage, constructor, classElements, isStatement); } finally { isStrictMode = oldStrictMode; } @@ -1601,6 +1580,9 @@ private List variableDeclarationList(final TokenType varType, final boolean isStatement, final int sourceOrder) { // VAR tested in caller. assert varType == VAR || varType == LET || varType == CONST; + final int varLine = line; + final long varToken = token; + next(); final List bindings = new ArrayList<>(); @@ -1613,9 +1595,6 @@ Expression missingAssignment = null; while (true) { - // Get starting token. - final int varLine = line; - final long varToken = token; // Get name of var. if (type == YIELD && inGeneratorFunction()) { expect(IDENT); @@ -1627,10 +1606,14 @@ if (isDestructuring) { final int finalVarFlags = varFlags; verifyDestructuringBindingPattern(binding, new Consumer() { + @Override public void accept(final IdentNode identNode) { verifyIdent(identNode, contextString); - final VarNode var = new VarNode(varLine, varToken, sourceOrder, identNode.getFinish(), identNode.setIsDeclaredHere(), null, finalVarFlags); - appendStatement(var); + if (!env._parse_only) { + // don't bother adding a variable if we are just parsing! + final VarNode var = new VarNode(varLine, varToken, sourceOrder, identNode.getFinish(), identNode.setIsDeclaredHere(), null, finalVarFlags); + appendStatement(var); + } } }); } @@ -1682,7 +1665,7 @@ assert init != null || !isStatement; binding = init == null ? binding : verifyAssignment(Token.recast(varToken, ASSIGN), binding, init); if (isStatement) { - appendStatement(new ExpressionStatement(varLine, binding.getToken(), finish, binding)); + appendStatement(new ExpressionStatement(varLine, binding.getToken(), finish, binding, varType)); } else if (init == null) { if (missingAssignment == null) { missingAssignment = binding; @@ -1748,7 +1731,8 @@ * Verify destructuring variable declaration binding pattern and extract bound variable declarations. */ private void verifyDestructuringBindingPattern(final Expression pattern, final Consumer identifierCallback) { - assert pattern instanceof ObjectNode || pattern instanceof LiteralNode.ArrayLiteralNode; + assert (pattern instanceof BinaryNode && ((BinaryNode)pattern).isTokenType(ASSIGN)) || + pattern instanceof ObjectNode || pattern instanceof LiteralNode.ArrayLiteralNode; pattern.accept(new NodeVisitor(new LexicalContext()) { @Override public boolean enterLiteralNode(final LiteralNode literalNode) { @@ -1857,9 +1841,8 @@ // Get expression and add as statement. final Expression expression = expression(); - ExpressionStatement expressionStatement = null; if (expression != null) { - expressionStatement = new ExpressionStatement(expressionLine, expressionToken, finish, expression); + ExpressionStatement expressionStatement = new ExpressionStatement(expressionLine, expressionToken, finish, expression); appendStatement(expressionStatement); } else { expect(null); @@ -2625,6 +2608,10 @@ final long catchToken = token; next(); expect(LPAREN); + + // FIXME: ES6 catch parameter can be a BindingIdentifier or a BindingPattern + // We need to generalize this here! + // http://www.ecma-international.org/ecma-262/6.0/ final IdentNode exception = getIdent(); // ECMA 12.4.1 strict mode restrictions @@ -4021,12 +4008,18 @@ ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); if (currentFunction != null) { - // desugar to: param = (param === undefined) ? initializer : param; - // possible alternative: if (param === undefined) param = initializer; - BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), ident, newUndefinedLiteral(paramToken, finish)); - TernaryNode value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(ident)); - BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), ident, value); - lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); + if (env._parse_only) { + // keep what is seen in source "as is" and save it as parameter expression + BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), ident, initializer); + currentFunction.addParameterExpression(ident, assignment); + } else { + // desugar to: param = (param === undefined) ? initializer : param; + // possible alternative: if (param === undefined) param = initializer; + BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), ident, newUndefinedLiteral(paramToken, finish)); + TernaryNode value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(ident)); + BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), ident, value); + lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); + } } } @@ -4050,16 +4043,31 @@ // binding pattern with initializer. desugar to: (param === undefined) ? initializer : param Expression initializer = assignmentExpression(false); - // TODO initializer must not contain yield expression if yield=true (i.e. this is generator function's parameter list) - BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), ident, newUndefinedLiteral(paramToken, finish)); - value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(ident)); + + if (env._parse_only) { + // we don't want the synthetic identifier in parse only mode + value = initializer; + } else { + // TODO initializer must not contain yield expression if yield=true (i.e. this is generator function's parameter list) + BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), ident, newUndefinedLiteral(paramToken, finish)); + value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(ident)); + } } ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); if (currentFunction != null) { // destructuring assignment BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), pattern, value); - lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); + if (env._parse_only) { + // in parse-only mode, represent source tree "as is" + if (ident.isDefaultParameter()) { + currentFunction.addParameterExpression(ident, assignment); + } else { + currentFunction.addParameterExpression(ident, pattern); + } + } else { + lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); + } } } parameters.add(ident); @@ -4077,7 +4085,9 @@ ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); if (currentFunction != null) { // declare function-scope variables for destructuring bindings - lc.getFunctionBody(currentFunction).appendStatement(new VarNode(paramLine, Token.recast(paramToken, VAR), pattern.getFinish(), identNode, null)); + if (!env._parse_only) { + lc.getFunctionBody(currentFunction).appendStatement(new VarNode(paramLine, Token.recast(paramToken, VAR), pattern.getFinish(), identNode, null)); + } // detect duplicate bounds names in parameter list currentFunction.addParameterBinding(identNode); currentFunction.setSimpleParameterList(false); @@ -4136,6 +4146,7 @@ // the note below for reasoning on skipping happening before instead of after RBRACE for // details). if (parseBody) { + functionNode.setFlag(FunctionNode.HAS_EXPRESSION_BODY); final ReturnNode returnNode = new ReturnNode(functionNode.getLineNumber(), expr.getToken(), lastFinish, expr); appendStatement(returnNode); } @@ -4305,7 +4316,7 @@ } private RuntimeNode referenceError(final Expression lhs, final Expression rhs, final boolean earlyError) { - if (earlyError) { + if (env._parse_only || earlyError) { throw error(JSErrorType.REFERENCE_ERROR, AbstractParser.message("invalid.lvalue"), lhs.getToken()); } final ArrayList args = new ArrayList<>(); @@ -4838,10 +4849,14 @@ ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); if (currentFunction != null) { - BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), ident, newUndefinedLiteral(paramToken, finish)); - TernaryNode value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(ident)); - BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), ident, value); - lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); + if (env._parse_only) { + currentFunction.addParameterExpression(ident, param); + } else { + BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), ident, newUndefinedLiteral(paramToken, finish)); + TernaryNode value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(ident)); + BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), ident, value); + lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); + } currentFunction.addParameterBinding(ident); currentFunction.setSimpleParameterList(false); @@ -4855,10 +4870,14 @@ ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); if (currentFunction != null) { - BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), ident, newUndefinedLiteral(paramToken, finish)); - TernaryNode value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(ident)); - BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), param, value); - lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); + if (env._parse_only) { + currentFunction.addParameterExpression(ident, param); + } else { + BinaryNode test = new BinaryNode(Token.recast(paramToken, EQ_STRICT), ident, newUndefinedLiteral(paramToken, finish)); + TernaryNode value = new TernaryNode(Token.recast(paramToken, TERNARY), test, new JoinPredecessorExpression(initializer), new JoinPredecessorExpression(ident)); + BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), param, value); + lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); + } } return ident; } @@ -4872,8 +4891,12 @@ ParserContextFunctionNode currentFunction = lc.getCurrentFunction(); if (currentFunction != null) { - BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), param, ident); - lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); + if (env._parse_only) { + currentFunction.addParameterExpression(ident, param); + } else { + BinaryNode assignment = new BinaryNode(Token.recast(paramToken, ASSIGN), param, ident); + lc.getFunctionBody(currentFunction).appendStatement(new ExpressionStatement(paramLine, assignment.getToken(), assignment.getFinish(), assignment)); + } } return ident; } @@ -4982,20 +5005,37 @@ return literal; } - Expression concat = literal; - TokenType lastLiteralType; - do { - final Expression expression = expression(); - if (type != TEMPLATE_MIDDLE && type != TEMPLATE_TAIL) { - throw error(AbstractParser.message("unterminated.template.expression"), token); - } - concat = new BinaryNode(Token.recast(lastLiteralToken, TokenType.ADD), concat, expression); - lastLiteralType = type; - lastLiteralToken = token; - literal = getLiteral(); - concat = new BinaryNode(Token.recast(lastLiteralToken, TokenType.ADD), concat, literal); - } while (lastLiteralType == TEMPLATE_MIDDLE); - return concat; + if (env._parse_only) { + List exprs = new ArrayList<>(); + exprs.add(literal); + TokenType lastLiteralType; + do { + final Expression expression = expression(); + if (type != TEMPLATE_MIDDLE && type != TEMPLATE_TAIL) { + throw error(AbstractParser.message("unterminated.template.expression"), token); + } + exprs.add(expression); + lastLiteralType = type; + literal = getLiteral(); + exprs.add(literal); + } while (lastLiteralType == TEMPLATE_MIDDLE); + return new TemplateLiteral(exprs); + } else { + Expression concat = literal; + TokenType lastLiteralType; + do { + final Expression expression = expression(); + if (type != TEMPLATE_MIDDLE && type != TEMPLATE_TAIL) { + throw error(AbstractParser.message("unterminated.template.expression"), token); + } + concat = new BinaryNode(Token.recast(lastLiteralToken, TokenType.ADD), concat, expression); + lastLiteralType = type; + lastLiteralToken = token; + literal = getLiteral(); + concat = new BinaryNode(Token.recast(lastLiteralToken, TokenType.ADD), concat, literal); + } while (lastLiteralType == TEMPLATE_MIDDLE); + return concat; + } } /** diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextFunctionNode.java --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextFunctionNode.java Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextFunctionNode.java Mon Jul 18 09:38:08 2016 -0700 @@ -24,9 +24,12 @@ */ package jdk.nashorn.internal.parser; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import jdk.nashorn.internal.codegen.Namespace; +import jdk.nashorn.internal.ir.Expression; import jdk.nashorn.internal.ir.FunctionNode; import jdk.nashorn.internal.ir.IdentNode; import jdk.nashorn.internal.ir.Module; @@ -70,6 +73,7 @@ private Module module; private int debugFlags; + private Map parameterExpressions; /** * @param token The token for the function @@ -170,6 +174,22 @@ } /** + * Return ES6 function parameter expressions + * + * @return ES6 function parameter expressions + */ + public Map getParameterExpressions() { + return parameterExpressions; + } + + void addParameterExpression(IdentNode ident, Expression node) { + if (parameterExpressions == null) { + parameterExpressions = new HashMap<>(); + } + parameterExpressions.put(ident, node); + } + + /** * Set last token * @param token New last token */ diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/test/script/basic/JDK-8075207.js --- a/nashorn/test/script/basic/JDK-8075207.js Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/test/script/basic/JDK-8075207.js Mon Jul 18 09:38:08 2016 -0700 @@ -61,7 +61,7 @@ Assert.assertTrue(stats.get(2) instanceof VariableTree); var print_hello = stats.get(1); -Assert.assertEquals(print_hello.name, "print_hello"); +Assert.assertEquals(print_hello.name.name, "print_hello"); var print_hello_stats = print_hello.body.statements; Assert.assertTrue(print_hello_stats.get(0) instanceof VariableTree); Assert.assertTrue(print_hello_stats.get(1) instanceof ExpressionStatementTree); diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/test/script/basic/JDK-8075448.js --- a/nashorn/test/script/basic/JDK-8075448.js Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/test/script/basic/JDK-8075448.js Mon Jul 18 09:38:08 2016 -0700 @@ -44,6 +44,6 @@ var stats = ast.sourceElements; Assert.assertTrue(stats[0] instanceof VariableTree); -Assert.assertEquals(stats[0].name, "i"); +Assert.assertEquals(stats[0].binding.name, "i"); Assert.assertTrue(stats[1] instanceof ForLoopTree); diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/test/script/basic/es6/let-const-switch.js.EXPECTED --- a/nashorn/test/script/basic/es6/let-const-switch.js.EXPECTED Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/test/script/basic/es6/let-const-switch.js.EXPECTED Mon Jul 18 09:38:08 2016 -0700 @@ -1,12 +1,12 @@ -1 -2 -0 -1 -2 -0 -SyntaxError: test/script/basic/es6/let-const-switch.js#34:8:1:25 Unsupported let declaration in unprotected switch statement -switch (x) { case 0: let x = 1; } - ^ -SyntaxError: test/script/basic/es6/let-const-switch.js#34:8:1:27 Unsupported const declaration in unprotected switch statement -switch (x) { case 0: const x = 1; } - ^ +1 +2 +0 +1 +2 +0 +SyntaxError: test/script/basic/es6/let-const-switch.js#34:8:1:21 Unsupported let declaration in unprotected switch statement +switch (x) { case 0: let x = 1; } + ^ +SyntaxError: test/script/basic/es6/let-const-switch.js#34:8:1:21 Unsupported const declaration in unprotected switch statement +switch (x) { case 0: const x = 1; } + ^ diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/test/script/nosecurity/parserapi.js --- a/nashorn/test/script/nosecurity/parserapi.js Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/test/script/nosecurity/parserapi.js Mon Jul 18 09:38:08 2016 -0700 @@ -62,6 +62,26 @@ var result = {}; for (var i in obj) { var val = obj[i]; + // skip these ES6 specific properties to reduce noise + // in the output - unless there were set to true + if (typeof(val) == 'boolean' && val == false) { + switch (i) { + case "computed": + case "static": + case "restParameter": + case "this": + case "super": + case "star": + case "default": + case "starDefaultStar": + case "arrow": + case "generator": + case "let": + case "const": + continue; + } + } + if (val instanceof Parser.Tree) { result[i] = this.convert(val); } else if (val instanceof Parser.List) { diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/test/script/nosecurity/parserapi.js.EXPECTED --- a/nashorn/test/script/nosecurity/parserapi.js.EXPECTED Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/test/script/nosecurity/parserapi.js.EXPECTED Mon Jul 18 09:38:08 2016 -0700 @@ -1,4 +1,4 @@ -[ +[ { "endPosition": "1113", "kind": "COMPILATION_UNIT", @@ -6,8 +6,13 @@ { "endPosition": "1123", "kind": "VARIABLE", - "name": "x", - "startPosition": "1117", + "binding": { + "endPosition": "1118", + "kind": "IDENTIFIER", + "name": "x", + "startPosition": "1117" + }, + "startPosition": "1113", "initializer": { "endPosition": "1123", "kind": "ARRAY_LITERAL", @@ -18,8 +23,13 @@ { "endPosition": "1147", "kind": "VARIABLE", - "name": "y", - "startPosition": "1129", + "binding": { + "endPosition": "1130", + "kind": "IDENTIFIER", + "name": "y", + "startPosition": "1129" + }, + "startPosition": "1125", "initializer": { "endPosition": "1147", "kind": "ARRAY_LITERAL", @@ -55,8 +65,13 @@ { "endPosition": "1165", "kind": "VARIABLE", - "name": "z", - "startPosition": "1153", + "binding": { + "endPosition": "1154", + "kind": "IDENTIFIER", + "name": "z", + "startPosition": "1153" + }, + "startPosition": "1149", "initializer": { "endPosition": "1165", "kind": "ARRAY_LITERAL", @@ -82,8 +97,13 @@ { "endPosition": "1200", "kind": "VARIABLE", - "name": "k", - "startPosition": "1171", + "binding": { + "endPosition": "1172", + "kind": "IDENTIFIER", + "name": "k", + "startPosition": "1171" + }, + "startPosition": "1167", "initializer": { "endPosition": "1200", "kind": "ARRAY_LITERAL", @@ -132,8 +152,8 @@ "sourceName": "parsertests/array_literal.js", "strict": "false", "startPosition": "1113" -} -, +} +, { "endPosition": "1126", "kind": "COMPILATION_UNIT", @@ -406,8 +426,8 @@ "sourceName": "parsertests/assignmentExpr.js", "strict": "false", "startPosition": "1126" -} -, +} +, { "endPosition": "1116", "kind": "COMPILATION_UNIT", @@ -912,8 +932,8 @@ "sourceName": "parsertests/binaryExpr.js", "strict": "false", "startPosition": "1116" -} -, +} +, { "endPosition": "1117", "kind": "COMPILATION_UNIT", @@ -959,8 +979,8 @@ "sourceName": "parsertests/block.js", "strict": "false", "startPosition": "1117" -} -, +} +, { "endPosition": "1117", "kind": "COMPILATION_UNIT", @@ -1060,8 +1080,8 @@ "sourceName": "parsertests/breakStat.js", "strict": "false", "startPosition": "1117" -} -, +} +, { "endPosition": "1117", "kind": "COMPILATION_UNIT", @@ -1098,8 +1118,8 @@ "sourceName": "parsertests/condExpr.js", "strict": "false", "startPosition": "1117" -} -, +} +, { "endPosition": "1120", "kind": "COMPILATION_UNIT", @@ -1199,8 +1219,8 @@ "sourceName": "parsertests/continueStat.js", "strict": "false", "startPosition": "1120" -} -, +} +, { "endPosition": "1118", "kind": "COMPILATION_UNIT", @@ -1214,8 +1234,8 @@ "sourceName": "parsertests/debuggerStat.js", "strict": "false", "startPosition": "1118" -} -, +} +, { "endPosition": "1137", "kind": "COMPILATION_UNIT", @@ -1223,7 +1243,12 @@ { "endPosition": "1172", "kind": "FUNCTION", - "name": "hello", + "name": { + "endPosition": "1151", + "kind": "IDENTIFIER", + "name": "hello", + "startPosition": "1146" + }, "body": { "endPosition": "1170", "kind": "BLOCK", @@ -1262,7 +1287,12 @@ { "endPosition": "1203", "kind": "FUNCTION", - "name": "hello", + "name": { + "endPosition": "1187", + "kind": "IDENTIFIER", + "name": "hello", + "startPosition": "1182" + }, "body": { "endPosition": "1201", "kind": "BLOCK", @@ -1308,7 +1338,12 @@ { "endPosition": "1240", "kind": "FUNCTION", - "name": "hello", + "name": { + "endPosition": "1218", + "kind": "IDENTIFIER", + "name": "hello", + "startPosition": "1213" + }, "body": { "endPosition": "1238", "kind": "BLOCK", @@ -1366,8 +1401,13 @@ { "endPosition": "1282", "kind": "VARIABLE", - "name": "hello", - "startPosition": "1245", + "binding": { + "endPosition": "1250", + "kind": "IDENTIFIER", + "name": "hello", + "startPosition": "1245" + }, + "startPosition": "1241", "initializer": { "endPosition": "1264", "kind": "FUNCTION_EXPRESSION", @@ -1410,12 +1450,22 @@ { "endPosition": "1331", "kind": "VARIABLE", - "name": "hello", - "startPosition": "1288", + "binding": { + "endPosition": "1293", + "kind": "IDENTIFIER", + "name": "hello", + "startPosition": "1288" + }, + "startPosition": "1284", "initializer": { "endPosition": "1313", "kind": "FUNCTION_EXPRESSION", - "name": "hello", + "name": { + "endPosition": "1310", + "kind": "IDENTIFIER", + "name": "hello", + "startPosition": "1305" + }, "body": { "endPosition": "1329", "kind": "BLOCK", @@ -1473,7 +1523,12 @@ { "endPosition": "1380", "kind": "FUNCTION", - "name": "test", + "name": { + "endPosition": "1361", + "kind": "IDENTIFIER", + "name": "test", + "startPosition": "1357" + }, "body": { "endPosition": "1377", "kind": "BLOCK", @@ -1500,8 +1555,8 @@ "sourceName": "parsertests/functions.js", "strict": "false", "startPosition": "1137" -} -, +} +, { "endPosition": "1114", "kind": "COMPILATION_UNIT", @@ -1604,8 +1659,8 @@ "sourceName": "parsertests/ifStat.js", "strict": "false", "startPosition": "1114" -} -, +} +, { "endPosition": "1113", "kind": "COMPILATION_UNIT", @@ -1668,8 +1723,8 @@ "sourceName": "parsertests/labelledStat.js", "strict": "false", "startPosition": "1113" -} -, +} +, { "endPosition": "1125", "kind": "COMPILATION_UNIT", @@ -2066,8 +2121,8 @@ "sourceName": "parsertests/lhsExpr.js", "strict": "false", "startPosition": "1125" -} -, +} +, { "endPosition": "1110", "kind": "COMPILATION_UNIT", @@ -2350,8 +2405,8 @@ "sourceName": "parsertests/loopStat.js", "strict": "false", "startPosition": "1110" -} -, +} +, { "endPosition": "1125", "kind": "COMPILATION_UNIT", @@ -2608,6 +2663,7 @@ "expression": { "endPosition": "1272", "kind": "IDENTIFIER", + "this": "true", "name": "this", "startPosition": "1268" }, @@ -2650,6 +2706,7 @@ "expression": { "endPosition": "1300", "kind": "IDENTIFIER", + "this": "true", "name": "this", "startPosition": "1296" }, @@ -2705,8 +2762,8 @@ "sourceName": "parsertests/objectLitExpr.js", "strict": "false", "startPosition": "1125" -} -, +} +, { "endPosition": "1118", "kind": "COMPILATION_UNIT", @@ -2781,8 +2838,8 @@ "sourceName": "parsertests/parenExpr.js", "strict": "false", "startPosition": "1118" -} -, +} +, { "endPosition": "1119", "kind": "COMPILATION_UNIT", @@ -2791,6 +2848,7 @@ "expression": { "endPosition": "1123", "kind": "IDENTIFIER", + "this": "true", "name": "this", "startPosition": "1119" }, @@ -2995,8 +3053,8 @@ "sourceName": "parsertests/primaryExpr.js", "strict": "false", "startPosition": "1119" -} -, +} +, { "endPosition": "1114", "kind": "COMPILATION_UNIT", @@ -3004,8 +3062,13 @@ { "endPosition": "1127", "kind": "VARIABLE", - "name": "x", - "startPosition": "1118", + "binding": { + "endPosition": "1119", + "kind": "IDENTIFIER", + "name": "x", + "startPosition": "1118" + }, + "startPosition": "1114", "initializer": { "endPosition": "1127", "kind": "REGEXP_LITERAL", @@ -3017,8 +3080,13 @@ { "endPosition": "1143", "kind": "VARIABLE", - "name": "y", - "startPosition": "1133", + "binding": { + "endPosition": "1134", + "kind": "IDENTIFIER", + "name": "y", + "startPosition": "1133" + }, + "startPosition": "1129", "initializer": { "endPosition": "1143", "kind": "REGEXP_LITERAL", @@ -3030,8 +3098,13 @@ { "endPosition": "1168", "kind": "VARIABLE", - "name": "z", - "startPosition": "1149", + "binding": { + "endPosition": "1150", + "kind": "IDENTIFIER", + "name": "z", + "startPosition": "1149" + }, + "startPosition": "1145", "initializer": { "endPosition": "1168", "kind": "REGEXP_LITERAL", @@ -3044,8 +3117,8 @@ "sourceName": "parsertests/regexp_literal.js", "strict": "false", "startPosition": "1114" -} -, +} +, { "endPosition": "1118", "kind": "COMPILATION_UNIT", @@ -3144,8 +3217,8 @@ "sourceName": "parsertests/returnStat.js", "strict": "false", "startPosition": "1118" -} -, +} +, { "endPosition": "1111", "kind": "COMPILATION_UNIT", @@ -3309,8 +3382,8 @@ "sourceName": "parsertests/switchStat.js", "strict": "false", "startPosition": "1111" -} -, +} +, { "endPosition": "1110", "kind": "COMPILATION_UNIT", @@ -3421,8 +3494,8 @@ "sourceName": "parsertests/throwStat.js", "strict": "false", "startPosition": "1110" -} -, +} +, { "endPosition": "1121", "kind": "COMPILATION_UNIT", @@ -3783,8 +3856,8 @@ "sourceName": "parsertests/tryCatchStat.js", "strict": "false", "startPosition": "1121" -} -, +} +, { "endPosition": "1115", "kind": "COMPILATION_UNIT", @@ -3969,8 +4042,8 @@ "sourceName": "parsertests/unaryExpr.js", "strict": "false", "startPosition": "1115" -} -, +} +, { "endPosition": "1122", "kind": "COMPILATION_UNIT", @@ -3989,7 +4062,12 @@ { "endPosition": "1165", "kind": "FUNCTION", - "name": "f", + "name": { + "endPosition": "1146", + "kind": "IDENTIFIER", + "name": "f", + "startPosition": "1145" + }, "body": { "endPosition": "1162", "kind": "BLOCK", @@ -4016,8 +4094,8 @@ "sourceName": "parsertests/useStrict.js", "strict": "true", "startPosition": "1122" -} -, +} +, { "endPosition": "1143", "kind": "COMPILATION_UNIT", @@ -4025,26 +4103,46 @@ { "endPosition": "1148", "kind": "VARIABLE", - "name": "a", - "startPosition": "1147" + "binding": { + "endPosition": "1148", + "kind": "IDENTIFIER", + "name": "a", + "startPosition": "1147" + }, + "startPosition": "1143" }, { "endPosition": "1155", "kind": "VARIABLE", - "name": "a", - "startPosition": "1154" + "binding": { + "endPosition": "1155", + "kind": "IDENTIFIER", + "name": "a", + "startPosition": "1154" + }, + "startPosition": "1150" }, { "endPosition": "1158", "kind": "VARIABLE", - "name": "b", - "startPosition": "1157" + "binding": { + "endPosition": "1158", + "kind": "IDENTIFIER", + "name": "b", + "startPosition": "1157" + }, + "startPosition": "1150" }, { "endPosition": "1200", "kind": "VARIABLE", - "name": "a", - "startPosition": "1190", + "binding": { + "endPosition": "1191", + "kind": "IDENTIFIER", + "name": "a", + "startPosition": "1190" + }, + "startPosition": "1186", "initializer": { "endPosition": "1200", "kind": "STRING_LITERAL", @@ -4055,8 +4153,13 @@ { "endPosition": "1212", "kind": "VARIABLE", - "name": "a", - "startPosition": "1207", + "binding": { + "endPosition": "1208", + "kind": "IDENTIFIER", + "name": "a", + "startPosition": "1207" + }, + "startPosition": "1203", "initializer": { "endPosition": "1212", "kind": "NUMBER_LITERAL", @@ -4067,8 +4170,13 @@ { "endPosition": "1219", "kind": "VARIABLE", - "name": "b", - "startPosition": "1214", + "binding": { + "endPosition": "1215", + "kind": "IDENTIFIER", + "name": "b", + "startPosition": "1214" + }, + "startPosition": "1203", "initializer": { "endPosition": "1219", "kind": "NUMBER_LITERAL", @@ -4079,8 +4187,13 @@ { "endPosition": "1226", "kind": "VARIABLE", - "name": "c", - "startPosition": "1221", + "binding": { + "endPosition": "1222", + "kind": "IDENTIFIER", + "name": "c", + "startPosition": "1221" + }, + "startPosition": "1203", "initializer": { "endPosition": "1226", "kind": "NUMBER_LITERAL", @@ -4092,8 +4205,8 @@ "sourceName": "parsertests/varDecl.js", "strict": "false", "startPosition": "1143" -} -, +} +, { "endPosition": "1111", "kind": "COMPILATION_UNIT", @@ -4142,8 +4255,8 @@ "sourceName": "parsertests/withStat.js", "strict": "false", "startPosition": "1111" -} -, +} +, { "fileName": "parsernegativetests/caseoutofswitch.js", "code": "case (1090, 4)", @@ -4152,8 +4265,8 @@ "position": "1090", "message": "parsernegativetests/caseoutofswitch.js:29:0 Expected an operand but found case\ncase 23:\n^", "lineNumber": "29" -} -, +} +, { "fileName": "parsernegativetests/caseoutofswitch.js", "code": "default (1112, 7)", @@ -4162,8 +4275,8 @@ "position": "1112", "message": "parsernegativetests/caseoutofswitch.js:31:0 Expected an operand but found default\ndefault:\n^", "lineNumber": "31" -} -, +} +, { "endPosition": "1090", "kind": "COMPILATION_UNIT", @@ -4240,8 +4353,8 @@ "sourceName": "parsernegativetests/caseoutofswitch.js", "strict": "false", "startPosition": "1090" -} -, +} +, { "fileName": "parsernegativetests/illegalbreak.js", "code": "break (1090, 5)", @@ -4250,8 +4363,8 @@ "position": "1090", "message": "parsernegativetests/illegalbreak.js:29:0 Illegal break statement\nbreak;\n^", "lineNumber": "29" -} -, +} +, { "fileName": "parsernegativetests/illegalbreak.js", "code": "ident (1103, 3)", @@ -4260,8 +4373,8 @@ "position": "1103", "message": "parsernegativetests/illegalbreak.js:30:6 Undefined Label \"foo\"\nbreak foo;\n ^", "lineNumber": "30" -} -, +} +, { "endPosition": "1090", "kind": "COMPILATION_UNIT", @@ -4290,8 +4403,8 @@ "sourceName": "parsernegativetests/illegalbreak.js", "strict": "false", "startPosition": "1090" -} -, +} +, { "fileName": "parsernegativetests/illegalcontinue.js", "code": "continue (1090, 8)", @@ -4300,8 +4413,8 @@ "position": "1090", "message": "parsernegativetests/illegalcontinue.js:29:0 Illegal continue statement\ncontinue;\n^", "lineNumber": "29" -} -, +} +, { "fileName": "parsernegativetests/illegalcontinue.js", "code": "ident (1109, 3)", @@ -4310,8 +4423,8 @@ "position": "1109", "message": "parsernegativetests/illegalcontinue.js:30:9 Undefined Label \"foo\"\ncontinue foo;\n ^", "lineNumber": "30" -} -, +} +, { "endPosition": "1090", "kind": "COMPILATION_UNIT", @@ -4340,8 +4453,8 @@ "sourceName": "parsernegativetests/illegalcontinue.js", "strict": "false", "startPosition": "1090" -} -, +} +, { "fileName": "parsernegativetests/illegallvalue.js", "code": "decimal (1090, 2)", @@ -4350,8 +4463,8 @@ "position": "1090", "message": "parsernegativetests/illegallvalue.js:29:0 Invalid left hand side for assignment\n44 = 54;\n^", "lineNumber": "29" -} -, +} +, { "fileName": "parsernegativetests/illegallvalue.js", "code": "decimal (1099, 3)", @@ -4360,8 +4473,8 @@ "position": "1099", "message": "parsernegativetests/illegallvalue.js:30:0 Invalid left hand side for assignment\n233 += 33;\n^", "lineNumber": "30" -} -, +} +, { "fileName": "parsernegativetests/illegallvalue.js", "code": "decimal (1110, 4)", @@ -4370,8 +4483,8 @@ "position": "1110", "message": "parsernegativetests/illegallvalue.js:31:0 Invalid left hand side for assignment\n3423 -= 234;\n^", "lineNumber": "31" -} -, +} +, { "endPosition": "1090", "kind": "COMPILATION_UNIT", @@ -4410,8 +4523,8 @@ "sourceName": "parsernegativetests/illegallvalue.js", "strict": "false", "startPosition": "1090" -} -, +} +, { "fileName": "parsernegativetests/illegaloperator.js", "code": "* (1093, 1)", @@ -4420,8 +4533,8 @@ "position": "1093", "message": "parsernegativetests/illegaloperator.js:29:3 Expected an operand but found *\nx ** y\n ^", "lineNumber": "29" -} -, +} +, { "endPosition": "1090", "kind": "COMPILATION_UNIT", @@ -4440,8 +4553,8 @@ "sourceName": "parsernegativetests/illegaloperator.js", "strict": "false", "startPosition": "1090" -} -, +} +, { "fileName": "parsernegativetests/keywordident.js", "code": "var (1094, 3)", @@ -4450,8 +4563,8 @@ "position": "1094", "message": "parsernegativetests/keywordident.js:29:4 Expected ident but found var\nvar var = 23;\n ^", "lineNumber": "29" -} -, +} +, { "endPosition": "1090", "kind": "COMPILATION_UNIT", @@ -4469,8 +4582,13 @@ { "endPosition": "1115", "kind": "VARIABLE", - "name": "x", - "startPosition": "1108", + "binding": { + "endPosition": "1109", + "kind": "IDENTIFIER", + "name": "x", + "startPosition": "1108" + }, + "startPosition": "1104", "initializer": { "endPosition": "1115", "kind": "NUMBER_LITERAL", @@ -4482,8 +4600,8 @@ "sourceName": "parsernegativetests/keywordident.js", "strict": "false", "startPosition": "1090" -} -, +} +, { "fileName": "parsernegativetests/parenmissing.js", "code": "; (1096, 1)", @@ -4492,8 +4610,8 @@ "position": "1096", "message": "parsernegativetests/parenmissing.js:29:6 Expected ) but found ;\n(1 + 2;\n ^", "lineNumber": "29" -} -, +} +, { "fileName": "parsernegativetests/parenmissing.js", "code": ") (1103, 1)", @@ -4502,8 +4620,8 @@ "position": "1103", "message": "parsernegativetests/parenmissing.js:30:5 Expected ; but found )\nx * y);\n ^", "lineNumber": "30" -} -, +} +, { "endPosition": "1090", "kind": "COMPILATION_UNIT", @@ -4554,8 +4672,8 @@ "sourceName": "parsernegativetests/parenmissing.js", "strict": "false", "startPosition": "1090" -} -, +} +, { "fileName": "parsernegativetests/repeatedproperty.js", "code": "ident (1111, 3)", @@ -4564,8 +4682,8 @@ "position": "1111", "message": "parsernegativetests/repeatedproperty.js:29:21 Property \"foo\" already defined\nvar obj = { foo: 34, get foo() { return 'hello' } };\n ^", "lineNumber": "29" -} -, +} +, { "fileName": "parsernegativetests/repeatedproperty.js", "code": "ident (1165, 3)", @@ -4574,8 +4692,8 @@ "position": "1165", "message": "parsernegativetests/repeatedproperty.js:30:22 Property \"foo\" already defined\nvar obj1 = { foo: 34, set foo(x) { } };\n ^", "lineNumber": "30" -} -, +} +, { "fileName": "parsernegativetests/repeatedproperty.js", "code": "ident (1205, 3)", @@ -4584,8 +4702,8 @@ "position": "1205", "message": "parsernegativetests/repeatedproperty.js:31:22 Property \"foo\" already defined\nvar obj2 = { foo: 34, set foo(x) { } };\n ^", "lineNumber": "31" -} -, +} +, { "fileName": "parsernegativetests/repeatedproperty.js", "code": "ident (1251, 3)", @@ -4594,8 +4712,8 @@ "position": "1251", "message": "parsernegativetests/repeatedproperty.js:32:28 Property \"bar\" already defined\nvar obj3 = { get bar() { }, get bar() {} };\n ^", "lineNumber": "32" -} -, +} +, { "fileName": "parsernegativetests/repeatedproperty.js", "code": "ident (1296, 3)", @@ -4604,8 +4722,8 @@ "position": "1296", "message": "parsernegativetests/repeatedproperty.js:33:29 Property \"bar\" already defined\nvar obj4 = { set bar(x) { }, set bar(x) {} };\n ^", "lineNumber": "33" -} -, +} +, { "endPosition": "1090", "kind": "COMPILATION_UNIT", @@ -4664,8 +4782,8 @@ "sourceName": "parsernegativetests/repeatedproperty.js", "strict": "false", "startPosition": "1090" -} -, +} +, { "fileName": "parsernegativetests/strict_repeatedproperty.js", "code": "ident (1126, 3)", @@ -4674,8 +4792,8 @@ "position": "1126", "message": "parsernegativetests/strict_repeatedproperty.js:31:21 Property \"foo\" already defined\nvar obj = { foo: 34, foo: 'hello' };\n ^", "lineNumber": "31" -} -, +} +, { "endPosition": "1090", "kind": "COMPILATION_UNIT", @@ -4705,8 +4823,8 @@ "sourceName": "parsernegativetests/strict_repeatedproperty.js", "strict": "true", "startPosition": "1090" -} -, +} +, { "fileName": "parsernegativetests/strict_repeatparam.js", "code": "ident (1122, 1)", @@ -4715,8 +4833,8 @@ "position": "1122", "message": "parsernegativetests/strict_repeatparam.js:31:17 strict mode function cannot have duplicate parameter name \"x\"\nfunction func(x, x) {}\n ^", "lineNumber": "31" -} -, +} +, { "endPosition": "1090", "kind": "COMPILATION_UNIT", @@ -4746,8 +4864,8 @@ "sourceName": "parsernegativetests/strict_repeatparam.js", "strict": "true", "startPosition": "1090" -} -, +} +, { "fileName": "parsernegativetests/strict_with.js", "code": "with (1105, 4)", @@ -4756,8 +4874,8 @@ "position": "1105", "message": "parsernegativetests/strict_with.js:31:0 \"with\" statement cannot be used in strict mode\nwith({}) {}\n^", "lineNumber": "31" -} -, +} +, { "fileName": "parsernegativetests/strict_with.js", "code": ") (1112, 1)", @@ -4766,8 +4884,8 @@ "position": "1112", "message": "parsernegativetests/strict_with.js:31:7 Expected ; but found )\nwith({}) {}\n ^", "lineNumber": "31" -} -, +} +, { "endPosition": "1090", "kind": "COMPILATION_UNIT", @@ -4807,8 +4925,8 @@ "sourceName": "parsernegativetests/strict_with.js", "strict": "true", "startPosition": "1090" -} -, +} +, { "fileName": "parsernegativetests/toplevelreturn.js", "code": "return (1090, 6)", @@ -4817,8 +4935,8 @@ "position": "1090", "message": "parsernegativetests/toplevelreturn.js:29:0 Invalid return statement\nreturn;\n^", "lineNumber": "29" -} -, +} +, { "fileName": "parsernegativetests/toplevelreturn.js", "code": "return (1098, 6)", @@ -4827,8 +4945,8 @@ "position": "1098", "message": "parsernegativetests/toplevelreturn.js:30:0 Invalid return statement\nreturn 23;\n^", "lineNumber": "30" -} -, +} +, { "endPosition": "1090", "kind": "COMPILATION_UNIT", @@ -4857,8 +4975,8 @@ "sourceName": "parsernegativetests/toplevelreturn.js", "strict": "false", "startPosition": "1090" -} -, +} +, { "endPosition": "1136", "kind": "COMPILATION_UNIT", @@ -4866,7 +4984,12 @@ { "endPosition": "1222", "kind": "FUNCTION", - "name": "Parser", + "name": { + "endPosition": "1151", + "kind": "IDENTIFIER", + "name": "Parser", + "startPosition": "1145" + }, "body": { "endPosition": "1220", "kind": "BLOCK", @@ -4898,6 +5021,7 @@ "expression": { "endPosition": "1193", "kind": "IDENTIFIER", + "this": "true", "name": "this", "startPosition": "1189" }, @@ -5165,8 +5289,13 @@ { "endPosition": "1718", "kind": "VARIABLE", - "name": "tree", - "startPosition": "1669", + "binding": { + "endPosition": "1673", + "kind": "IDENTIFIER", + "name": "tree", + "startPosition": "1669" + }, + "startPosition": "1665", "initializer": { "endPosition": "1718", "kind": "FUNCTION_INVOCATION", @@ -5177,6 +5306,7 @@ "expression": { "endPosition": "1680", "kind": "IDENTIFIER", + "this": "true", "name": "this", "startPosition": "1676" }, @@ -5272,6 +5402,7 @@ "expression": { "endPosition": "1790", "kind": "IDENTIFIER", + "this": "true", "name": "this", "startPosition": "1786" }, @@ -5429,7 +5560,7 @@ "endPosition": "2016", "kind": "FUNCTION_EXPRESSION", "body": { - "endPosition": "2994", + "endPosition": "3634", "kind": "BLOCK", "statements": [ { @@ -5554,8 +5685,13 @@ { "endPosition": "2169", "kind": "VARIABLE", - "name": "obj", - "startPosition": "2132", + "binding": { + "endPosition": "2135", + "kind": "IDENTIFIER", + "name": "obj", + "startPosition": "2132" + }, + "startPosition": "2128", "initializer": { "endPosition": "2169", "kind": "FUNCTION_INVOCATION", @@ -5591,8 +5727,13 @@ { "endPosition": "2190", "kind": "VARIABLE", - "name": "result", - "startPosition": "2179", + "binding": { + "endPosition": "2185", + "kind": "IDENTIFIER", + "name": "result", + "startPosition": "2179" + }, + "startPosition": "2175", "initializer": { "endPosition": "2190", "kind": "OBJECT_LITERAL", @@ -5603,8 +5744,13 @@ { "endPosition": "2206", "kind": "VARIABLE", - "name": "i", - "startPosition": "2205" + "binding": { + "endPosition": "2206", + "kind": "IDENTIFIER", + "name": "i", + "startPosition": "2205" + }, + "startPosition": "2201" }, { "expression": { @@ -5613,7 +5759,7 @@ "name": "obj", "startPosition": "2210" }, - "endPosition": "2975", + "endPosition": "3615", "kind": "FOR_IN_LOOP", "forEach": "false", "variable": { @@ -5623,14 +5769,19 @@ "startPosition": "2205" }, "statement": { - "endPosition": "2975", + "endPosition": "3615", "kind": "BLOCK", "statements": [ { "endPosition": "2241", "kind": "VARIABLE", - "name": "val", - "startPosition": "2229", + "binding": { + "endPosition": "2232", + "kind": "IDENTIFIER", + "name": "val", + "startPosition": "2229" + }, + "startPosition": "2225", "initializer": { "expression": { "endPosition": "2238", @@ -5652,605 +5803,836 @@ { "condition": { "leftOperand": { - "endPosition": "2258", + "leftOperand": { + "expression": { + "endPosition": "2384", + "kind": "IDENTIFIER", + "name": "val", + "startPosition": "2381" + }, + "endPosition": "2384", + "kind": "TYPEOF", + "startPosition": "2374" + }, + "endPosition": "2397", + "kind": "EQUAL_TO", + "rightOperand": { + "endPosition": "2397", + "kind": "STRING_LITERAL", + "value": "boolean", + "startPosition": "2390" + }, + "startPosition": "2374" + }, + "endPosition": "2414", + "kind": "CONDITIONAL_AND", + "rightOperand": { + "leftOperand": { + "endPosition": "2405", + "kind": "IDENTIFIER", + "name": "val", + "startPosition": "2402" + }, + "endPosition": "2414", + "kind": "EQUAL_TO", + "rightOperand": { + "endPosition": "2414", + "kind": "BOOLEAN_LITERAL", + "value": "false", + "startPosition": "2409" + }, + "startPosition": "2402" + }, + "startPosition": "2374" + }, + "endPosition": "2881", + "kind": "IF", + "startPosition": "2370", + "thenStatement": { + "endPosition": "2881", + "kind": "BLOCK", + "statements": [ + { + "cases": [ + { + "expression": { + "endPosition": "2473", + "kind": "STRING_LITERAL", + "value": "computed", + "startPosition": "2465" + }, + "endPosition": "2475", + "kind": "CASE", + "statements": [], + "startPosition": "2459" + }, + { + "expression": { + "endPosition": "2504", + "kind": "STRING_LITERAL", + "value": "static", + "startPosition": "2498" + }, + "endPosition": "2506", + "kind": "CASE", + "statements": [], + "startPosition": "2492" + }, + { + "expression": { + "endPosition": "2542", + "kind": "STRING_LITERAL", + "value": "restParameter", + "startPosition": "2529" + }, + "endPosition": "2544", + "kind": "CASE", + "statements": [], + "startPosition": "2523" + }, + { + "expression": { + "endPosition": "2571", + "kind": "STRING_LITERAL", + "value": "this", + "startPosition": "2567" + }, + "endPosition": "2573", + "kind": "CASE", + "statements": [], + "startPosition": "2561" + }, + { + "expression": { + "endPosition": "2601", + "kind": "STRING_LITERAL", + "value": "super", + "startPosition": "2596" + }, + "endPosition": "2603", + "kind": "CASE", + "statements": [], + "startPosition": "2590" + }, + { + "expression": { + "endPosition": "2630", + "kind": "STRING_LITERAL", + "value": "star", + "startPosition": "2626" + }, + "endPosition": "2632", + "kind": "CASE", + "statements": [], + "startPosition": "2620" + }, + { + "expression": { + "endPosition": "2662", + "kind": "STRING_LITERAL", + "value": "default", + "startPosition": "2655" + }, + "endPosition": "2664", + "kind": "CASE", + "statements": [], + "startPosition": "2649" + }, + { + "expression": { + "endPosition": "2702", + "kind": "STRING_LITERAL", + "value": "starDefaultStar", + "startPosition": "2687" + }, + "endPosition": "2704", + "kind": "CASE", + "statements": [], + "startPosition": "2681" + }, + { + "expression": { + "endPosition": "2732", + "kind": "STRING_LITERAL", + "value": "arrow", + "startPosition": "2727" + }, + "endPosition": "2734", + "kind": "CASE", + "statements": [], + "startPosition": "2721" + }, + { + "expression": { + "endPosition": "2766", + "kind": "STRING_LITERAL", + "value": "generator", + "startPosition": "2757" + }, + "endPosition": "2768", + "kind": "CASE", + "statements": [], + "startPosition": "2751" + }, + { + "expression": { + "endPosition": "2794", + "kind": "STRING_LITERAL", + "value": "let", + "startPosition": "2791" + }, + "endPosition": "2796", + "kind": "CASE", + "statements": [], + "startPosition": "2785" + }, + { + "expression": { + "endPosition": "2824", + "kind": "STRING_LITERAL", + "value": "const", + "startPosition": "2819" + }, + "endPosition": "2856", + "kind": "CASE", + "statements": [ + { + "endPosition": "2856", + "kind": "CONTINUE", + "startPosition": "2847" + } + ], + "startPosition": "2813" + } + ], + "expression": { + "endPosition": "2439", + "kind": "IDENTIFIER", + "name": "i", + "startPosition": "2438" + }, + "endPosition": "2871", + "kind": "SWITCH", + "startPosition": "2430" + } + ], + "startPosition": "2416" + } + }, + { + "condition": { + "leftOperand": { + "endPosition": "2898", "kind": "IDENTIFIER", "name": "val", - "startPosition": "2255" + "startPosition": "2895" }, "expression": { - "endPosition": "2258", + "endPosition": "2898", "kind": "IDENTIFIER", "name": "val", - "startPosition": "2255" + "startPosition": "2895" }, - "endPosition": "2281", + "endPosition": "2921", "kind": "INSTANCE_OF", "rightOperand": { "identifier": "Tree", "expression": { - "endPosition": "2276", + "endPosition": "2916", "kind": "IDENTIFIER", "name": "Parser", - "startPosition": "2270" + "startPosition": "2910" }, - "endPosition": "2281", + "endPosition": "2921", "kind": "MEMBER_SELECT", - "startPosition": "2270" + "startPosition": "2910" }, "type": { "identifier": "Tree", "expression": { - "endPosition": "2276", + "endPosition": "2916", "kind": "IDENTIFIER", "name": "Parser", - "startPosition": "2270" + "startPosition": "2910" }, - "endPosition": "2281", + "endPosition": "2921", "kind": "MEMBER_SELECT", - "startPosition": "2270" + "startPosition": "2910" }, - "startPosition": "2255" + "startPosition": "2895" }, "elseStatement": { "condition": { "leftOperand": { - "endPosition": "2350", + "endPosition": "2990", "kind": "IDENTIFIER", "name": "val", - "startPosition": "2347" + "startPosition": "2987" }, "expression": { - "endPosition": "2350", + "endPosition": "2990", "kind": "IDENTIFIER", "name": "val", - "startPosition": "2347" + "startPosition": "2987" }, - "endPosition": "2373", + "endPosition": "3013", "kind": "INSTANCE_OF", "rightOperand": { "identifier": "List", "expression": { - "endPosition": "2368", + "endPosition": "3008", "kind": "IDENTIFIER", "name": "Parser", - "startPosition": "2362" + "startPosition": "3002" }, - "endPosition": "2373", + "endPosition": "3013", "kind": "MEMBER_SELECT", - "startPosition": "2362" + "startPosition": "3002" }, "type": { "identifier": "List", "expression": { - "endPosition": "2368", + "endPosition": "3008", "kind": "IDENTIFIER", "name": "Parser", - "startPosition": "2362" + "startPosition": "3002" }, - "endPosition": "2373", + "endPosition": "3013", "kind": "MEMBER_SELECT", - "startPosition": "2362" + "startPosition": "3002" }, - "startPosition": "2347" + "startPosition": "2987" }, "elseStatement": { - "endPosition": "2969", + "endPosition": "3609", "kind": "BLOCK", "statements": [ { "cases": [ { "expression": { - "endPosition": "2625", + "endPosition": "3265", "kind": "STRING_LITERAL", "value": "number", - "startPosition": "2619" + "startPosition": "3259" }, - "endPosition": "2627", + "endPosition": "3267", "kind": "CASE", "statements": [], - "startPosition": "2613" + "startPosition": "3253" }, { "expression": { - "endPosition": "2656", + "endPosition": "3296", "kind": "STRING_LITERAL", "value": "string", - "startPosition": "2650" + "startPosition": "3290" }, - "endPosition": "2658", + "endPosition": "3298", "kind": "CASE", "statements": [], - "startPosition": "2644" + "startPosition": "3284" }, { "expression": { - "endPosition": "2688", + "endPosition": "3328", "kind": "STRING_LITERAL", "value": "boolean", - "startPosition": "2681" + "startPosition": "3321" }, - "endPosition": "2762", + "endPosition": "3402", "kind": "CASE", "statements": [ { "expression": { "expression": { - "endPosition": "2734", + "endPosition": "3374", "kind": "FUNCTION_INVOCATION", "functionSelect": { - "endPosition": "2729", + "endPosition": "3369", "kind": "IDENTIFIER", "name": "String", - "startPosition": "2723" + "startPosition": "3363" }, "arguments": [ { - "endPosition": "2733", + "endPosition": "3373", "kind": "IDENTIFIER", "name": "val", - "startPosition": "2730" + "startPosition": "3370" } ], - "startPosition": "2723" + "startPosition": "3363" }, - "endPosition": "2734", + "endPosition": "3374", "kind": "ASSIGNMENT", "variable": { "expression": { - "endPosition": "2717", + "endPosition": "3357", "kind": "IDENTIFIER", "name": "result", - "startPosition": "2711" + "startPosition": "3351" }, - "endPosition": "2720", + "endPosition": "3360", "kind": "ARRAY_ACCESS", "index": { - "endPosition": "2719", + "endPosition": "3359", "kind": "IDENTIFIER", "name": "i", - "startPosition": "2718" + "startPosition": "3358" }, - "startPosition": "2711" + "startPosition": "3351" }, - "startPosition": "2711" + "startPosition": "3351" }, - "endPosition": "2734", + "endPosition": "3374", "kind": "EXPRESSION_STATEMENT", - "startPosition": "2711" + "startPosition": "3351" }, { - "endPosition": "2762", + "endPosition": "3402", "kind": "BREAK", - "startPosition": "2756" + "startPosition": "3396" } ], - "startPosition": "2675" + "startPosition": "3315" }, { - "endPosition": "2945", + "endPosition": "3585", "kind": "CASE", "statements": [ { "condition": { "leftOperand": { "leftOperand": { - "endPosition": "2815", + "endPosition": "3455", "kind": "IDENTIFIER", "name": "val", - "startPosition": "2812" + "startPosition": "3452" }, "expression": { - "endPosition": "2815", + "endPosition": "3455", "kind": "IDENTIFIER", "name": "val", - "startPosition": "2812" + "startPosition": "3452" }, - "endPosition": "2841", + "endPosition": "3481", "kind": "INSTANCE_OF", "rightOperand": { "identifier": "Long", "expression": { "identifier": "lang", "expression": { - "endPosition": "2831", + "endPosition": "3471", "kind": "IDENTIFIER", "name": "java", - "startPosition": "2827" + "startPosition": "3467" }, - "endPosition": "2836", + "endPosition": "3476", "kind": "MEMBER_SELECT", - "startPosition": "2827" + "startPosition": "3467" }, - "endPosition": "2841", + "endPosition": "3481", "kind": "MEMBER_SELECT", - "startPosition": "2827" + "startPosition": "3467" }, "type": { "identifier": "Long", "expression": { "identifier": "lang", "expression": { - "endPosition": "2831", + "endPosition": "3471", "kind": "IDENTIFIER", "name": "java", - "startPosition": "2827" + "startPosition": "3467" }, - "endPosition": "2836", + "endPosition": "3476", "kind": "MEMBER_SELECT", - "startPosition": "2827" + "startPosition": "3467" }, - "endPosition": "2841", + "endPosition": "3481", "kind": "MEMBER_SELECT", - "startPosition": "2827" + "startPosition": "3467" }, - "startPosition": "2812" + "startPosition": "3452" }, - "endPosition": "2871", + "endPosition": "3511", "kind": "CONDITIONAL_OR", "rightOperand": { "leftOperand": { - "endPosition": "2848", + "endPosition": "3488", "kind": "IDENTIFIER", "name": "val", - "startPosition": "2845" + "startPosition": "3485" }, "expression": { - "endPosition": "2848", + "endPosition": "3488", "kind": "IDENTIFIER", "name": "val", - "startPosition": "2845" + "startPosition": "3485" }, - "endPosition": "2871", + "endPosition": "3511", "kind": "INSTANCE_OF", "rightOperand": { "identifier": "Enum", "expression": { - "endPosition": "2866", + "endPosition": "3506", "kind": "IDENTIFIER", "name": "Parser", - "startPosition": "2860" + "startPosition": "3500" }, - "endPosition": "2871", + "endPosition": "3511", "kind": "MEMBER_SELECT", - "startPosition": "2860" + "startPosition": "3500" }, "type": { "identifier": "Enum", "expression": { - "endPosition": "2866", + "endPosition": "3506", "kind": "IDENTIFIER", "name": "Parser", - "startPosition": "2860" + "startPosition": "3500" }, - "endPosition": "2871", + "endPosition": "3511", "kind": "MEMBER_SELECT", - "startPosition": "2860" + "startPosition": "3500" }, - "startPosition": "2845" + "startPosition": "3485" }, - "startPosition": "2812" + "startPosition": "3452" }, - "endPosition": "2945", + "endPosition": "3585", "kind": "IF", - "startPosition": "2808", + "startPosition": "3448", "thenStatement": { - "endPosition": "2945", + "endPosition": "3585", "kind": "BLOCK", "statements": [ { "expression": { "expression": { - "endPosition": "2922", + "endPosition": "3562", "kind": "FUNCTION_INVOCATION", "functionSelect": { - "endPosition": "2917", + "endPosition": "3557", "kind": "IDENTIFIER", "name": "String", - "startPosition": "2911" + "startPosition": "3551" }, "arguments": [ { - "endPosition": "2921", + "endPosition": "3561", "kind": "IDENTIFIER", "name": "val", - "startPosition": "2918" + "startPosition": "3558" } ], - "startPosition": "2911" + "startPosition": "3551" }, - "endPosition": "2922", + "endPosition": "3562", "kind": "ASSIGNMENT", "variable": { "expression": { - "endPosition": "2905", + "endPosition": "3545", "kind": "IDENTIFIER", "name": "result", - "startPosition": "2899" + "startPosition": "3539" }, - "endPosition": "2908", + "endPosition": "3548", "kind": "ARRAY_ACCESS", "index": { - "endPosition": "2907", + "endPosition": "3547", "kind": "IDENTIFIER", "name": "i", - "startPosition": "2906" + "startPosition": "3546" }, - "startPosition": "2899" + "startPosition": "3539" }, - "startPosition": "2899" + "startPosition": "3539" }, - "endPosition": "2922", + "endPosition": "3562", "kind": "EXPRESSION_STATEMENT", - "startPosition": "2899" + "startPosition": "3539" } ], - "startPosition": "2873" + "startPosition": "3513" } } ], - "startPosition": "2779" + "startPosition": "3419" } ], "expression": { "expression": { - "endPosition": "2593", + "endPosition": "3233", "kind": "IDENTIFIER", "name": "val", - "startPosition": "2590" + "startPosition": "3230" }, - "endPosition": "2593", + "endPosition": "3233", "kind": "TYPEOF", - "startPosition": "2583" + "startPosition": "3223" }, - "endPosition": "2959", + "endPosition": "3599", "kind": "SWITCH", - "startPosition": "2575" + "startPosition": "3215" } ], - "startPosition": "2561" + "startPosition": "3201" }, - "endPosition": "2969", + "endPosition": "3609", "kind": "IF", - "startPosition": "2343", + "startPosition": "2983", "thenStatement": { - "endPosition": "2555", + "endPosition": "3195", "kind": "BLOCK", "statements": [ { - "endPosition": "2420", + "endPosition": "3060", "kind": "VARIABLE", - "name": "arr", - "startPosition": "2393", + "binding": { + "endPosition": "3036", + "kind": "IDENTIFIER", + "name": "arr", + "startPosition": "3033" + }, + "startPosition": "3029", "initializer": { "constructorExpression": { - "endPosition": "2420", + "endPosition": "3060", "kind": "FUNCTION_INVOCATION", "functionSelect": { - "endPosition": "2408", + "endPosition": "3048", "kind": "IDENTIFIER", "name": "Array", - "startPosition": "2403" + "startPosition": "3043" }, "arguments": [ { - "endPosition": "2419", + "endPosition": "3059", "kind": "FUNCTION_INVOCATION", "functionSelect": { "identifier": "size", "expression": { - "endPosition": "2412", + "endPosition": "3052", "kind": "IDENTIFIER", "name": "val", - "startPosition": "2409" + "startPosition": "3049" }, - "endPosition": "2417", + "endPosition": "3057", "kind": "MEMBER_SELECT", - "startPosition": "2409" + "startPosition": "3049" }, "arguments": [], - "startPosition": "2409" + "startPosition": "3049" } ], - "startPosition": "2403" + "startPosition": "3043" }, - "endPosition": "2420", + "endPosition": "3060", "kind": "NEW", - "startPosition": "2399" + "startPosition": "3039" } }, { - "endPosition": "2444", + "endPosition": "3084", "kind": "VARIABLE", - "name": "j", - "startPosition": "2443" + "binding": { + "endPosition": "3084", + "kind": "IDENTIFIER", + "name": "j", + "startPosition": "3083" + }, + "startPosition": "3079" }, { "expression": { - "endPosition": "2451", + "endPosition": "3091", "kind": "IDENTIFIER", "name": "val", - "startPosition": "2448" + "startPosition": "3088" }, - "endPosition": "2515", + "endPosition": "3155", "kind": "FOR_IN_LOOP", "forEach": "false", "variable": { - "endPosition": "2444", + "endPosition": "3084", "kind": "IDENTIFIER", "name": "j", - "startPosition": "2443" + "startPosition": "3083" }, "statement": { - "endPosition": "2515", + "endPosition": "3155", "kind": "BLOCK", "statements": [ { "expression": { "expression": { - "endPosition": "2500", + "endPosition": "3140", "kind": "FUNCTION_INVOCATION", "functionSelect": { "identifier": "convert", "expression": { - "endPosition": "2484", + "endPosition": "3124", "kind": "IDENTIFIER", + "this": "true", "name": "this", - "startPosition": "2480" + "startPosition": "3120" }, - "endPosition": "2492", + "endPosition": "3132", "kind": "MEMBER_SELECT", - "startPosition": "2480" + "startPosition": "3120" }, "arguments": [ { "expression": { - "endPosition": "2496", + "endPosition": "3136", "kind": "IDENTIFIER", "name": "val", - "startPosition": "2493" + "startPosition": "3133" }, - "endPosition": "2499", + "endPosition": "3139", "kind": "ARRAY_ACCESS", "index": { - "endPosition": "2498", + "endPosition": "3138", "kind": "IDENTIFIER", "name": "j", - "startPosition": "2497" + "startPosition": "3137" }, - "startPosition": "2493" + "startPosition": "3133" } ], - "startPosition": "2480" + "startPosition": "3120" }, - "endPosition": "2500", + "endPosition": "3140", "kind": "ASSIGNMENT", "variable": { "expression": { - "endPosition": "2474", + "endPosition": "3114", "kind": "IDENTIFIER", "name": "arr", - "startPosition": "2471" + "startPosition": "3111" }, - "endPosition": "2477", + "endPosition": "3117", "kind": "ARRAY_ACCESS", "index": { - "endPosition": "2476", + "endPosition": "3116", "kind": "IDENTIFIER", "name": "j", - "startPosition": "2475" + "startPosition": "3115" }, - "startPosition": "2471" + "startPosition": "3111" }, - "startPosition": "2471" + "startPosition": "3111" }, - "endPosition": "2500", + "endPosition": "3140", "kind": "EXPRESSION_STATEMENT", - "startPosition": "2471" + "startPosition": "3111" } ], - "startPosition": "2453" + "startPosition": "3093" }, - "startPosition": "2434" + "startPosition": "3074" }, { "expression": { "expression": { - "endPosition": "2544", + "endPosition": "3184", "kind": "IDENTIFIER", "name": "arr", - "startPosition": "2541" + "startPosition": "3181" }, - "endPosition": "2544", + "endPosition": "3184", "kind": "ASSIGNMENT", "variable": { "expression": { - "endPosition": "2535", + "endPosition": "3175", "kind": "IDENTIFIER", "name": "result", - "startPosition": "2529" + "startPosition": "3169" }, - "endPosition": "2538", + "endPosition": "3178", "kind": "ARRAY_ACCESS", "index": { - "endPosition": "2537", + "endPosition": "3177", "kind": "IDENTIFIER", "name": "i", - "startPosition": "2536" + "startPosition": "3176" }, - "startPosition": "2529" + "startPosition": "3169" }, - "startPosition": "2529" + "startPosition": "3169" }, - "endPosition": "2544", + "endPosition": "3184", "kind": "EXPRESSION_STATEMENT", - "startPosition": "2529" + "startPosition": "3169" } ], - "startPosition": "2375" + "startPosition": "3015" } }, - "endPosition": "2969", + "endPosition": "3609", "kind": "IF", - "startPosition": "2251", + "startPosition": "2891", "thenStatement": { - "endPosition": "2337", + "endPosition": "2977", "kind": "BLOCK", "statements": [ { "expression": { "expression": { - "endPosition": "2326", + "endPosition": "2966", "kind": "FUNCTION_INVOCATION", "functionSelect": { "identifier": "convert", "expression": { - "endPosition": "2313", + "endPosition": "2953", "kind": "IDENTIFIER", + "this": "true", "name": "this", - "startPosition": "2309" + "startPosition": "2949" }, - "endPosition": "2321", + "endPosition": "2961", "kind": "MEMBER_SELECT", - "startPosition": "2309" + "startPosition": "2949" }, "arguments": [ { - "endPosition": "2325", + "endPosition": "2965", "kind": "IDENTIFIER", "name": "val", - "startPosition": "2322" + "startPosition": "2962" } ], - "startPosition": "2309" + "startPosition": "2949" }, - "endPosition": "2326", + "endPosition": "2966", "kind": "ASSIGNMENT", "variable": { "expression": { - "endPosition": "2303", + "endPosition": "2943", "kind": "IDENTIFIER", "name": "result", - "startPosition": "2297" + "startPosition": "2937" }, - "endPosition": "2306", + "endPosition": "2946", "kind": "ARRAY_ACCESS", "index": { - "endPosition": "2305", + "endPosition": "2945", "kind": "IDENTIFIER", "name": "i", - "startPosition": "2304" + "startPosition": "2944" }, - "startPosition": "2297" + "startPosition": "2937" }, - "startPosition": "2297" + "startPosition": "2937" }, - "endPosition": "2326", + "endPosition": "2966", "kind": "EXPRESSION_STATEMENT", - "startPosition": "2297" + "startPosition": "2937" } ], - "startPosition": "2283" + "startPosition": "2923" } } ], @@ -6260,14 +6642,14 @@ }, { "expression": { - "endPosition": "2993", + "endPosition": "3633", "kind": "IDENTIFIER", "name": "result", - "startPosition": "2987" + "startPosition": "3627" }, - "endPosition": "2994", + "endPosition": "3634", "kind": "RETURN", - "startPosition": "2980" + "startPosition": "3620" } ], "startPosition": "2016" @@ -6305,105 +6687,120 @@ }, "startPosition": "1974" }, - "endPosition": "2996", + "endPosition": "3636", "kind": "EXPRESSION_STATEMENT", "startPosition": "1974" }, { - "endPosition": "3726", + "endPosition": "4366", "kind": "FUNCTION", - "name": "processFiles", + "name": { + "endPosition": "3659", + "kind": "IDENTIFIER", + "name": "processFiles", + "startPosition": "3647" + }, "body": { - "endPosition": "3724", + "endPosition": "4364", "kind": "BLOCK", "statements": [ { - "endPosition": "3070", + "endPosition": "3710", "kind": "VARIABLE", - "name": "File", - "startPosition": "3038", + "binding": { + "endPosition": "3682", + "kind": "IDENTIFIER", + "name": "File", + "startPosition": "3678" + }, + "startPosition": "3674", "initializer": { - "endPosition": "3070", + "endPosition": "3710", "kind": "FUNCTION_INVOCATION", "functionSelect": { "identifier": "type", "expression": { - "endPosition": "3049", + "endPosition": "3689", "kind": "IDENTIFIER", "name": "Java", - "startPosition": "3045" + "startPosition": "3685" }, - "endPosition": "3054", + "endPosition": "3694", "kind": "MEMBER_SELECT", - "startPosition": "3045" + "startPosition": "3685" }, "arguments": [ { - "endPosition": "3068", + "endPosition": "3708", "kind": "STRING_LITERAL", "value": "java.io.File", - "startPosition": "3056" + "startPosition": "3696" } ], - "startPosition": "3045" + "startPosition": "3685" } }, { - "endPosition": "3126", + "endPosition": "3766", "kind": "VARIABLE", - "name": "files", - "startPosition": "3080", + "binding": { + "endPosition": "3725", + "kind": "IDENTIFIER", + "name": "files", + "startPosition": "3720" + }, + "startPosition": "3716", "initializer": { - "endPosition": "3126", + "endPosition": "3766", "kind": "FUNCTION_INVOCATION", "functionSelect": { "identifier": "listFiles", "expression": { "constructorExpression": { - "endPosition": "3114", + "endPosition": "3754", "kind": "FUNCTION_INVOCATION", "functionSelect": { - "endPosition": "3096", + "endPosition": "3736", "kind": "IDENTIFIER", "name": "File", - "startPosition": "3092" + "startPosition": "3732" }, "arguments": [ { "leftOperand": { - "endPosition": "3104", + "endPosition": "3744", "kind": "IDENTIFIER", "name": "__DIR__", - "startPosition": "3097" + "startPosition": "3737" }, - "endPosition": "3113", + "endPosition": "3753", "kind": "PLUS", "rightOperand": { - "endPosition": "3113", + "endPosition": "3753", "kind": "IDENTIFIER", "name": "subdir", - "startPosition": "3107" + "startPosition": "3747" }, - "startPosition": "3097" + "startPosition": "3737" } ], - "startPosition": "3092" + "startPosition": "3732" }, - "endPosition": "3114", + "endPosition": "3754", "kind": "NEW", - "startPosition": "3088" + "startPosition": "3728" }, - "endPosition": "3124", + "endPosition": "3764", "kind": "MEMBER_SELECT", - "startPosition": "3088" + "startPosition": "3728" }, "arguments": [], - "startPosition": "3088" + "startPosition": "3728" } }, { "expression": { - "endPosition": "3160", + "endPosition": "3800", "kind": "FUNCTION_INVOCATION", "functionSelect": { "identifier": "sort", @@ -6412,751 +6809,786 @@ "expression": { "identifier": "util", "expression": { - "endPosition": "3136", + "endPosition": "3776", "kind": "IDENTIFIER", "name": "java", - "startPosition": "3132" + "startPosition": "3772" }, - "endPosition": "3141", + "endPosition": "3781", "kind": "MEMBER_SELECT", - "startPosition": "3132" + "startPosition": "3772" }, - "endPosition": "3148", + "endPosition": "3788", "kind": "MEMBER_SELECT", - "startPosition": "3132" + "startPosition": "3772" }, - "endPosition": "3153", + "endPosition": "3793", "kind": "MEMBER_SELECT", - "startPosition": "3132" + "startPosition": "3772" }, "arguments": [ { - "endPosition": "3159", + "endPosition": "3799", "kind": "IDENTIFIER", "name": "files", - "startPosition": "3154" + "startPosition": "3794" } ], - "startPosition": "3132" + "startPosition": "3772" }, - "endPosition": "3160", + "endPosition": "3800", "kind": "EXPRESSION_STATEMENT", - "startPosition": "3132" + "startPosition": "3772" }, { - "endPosition": "3184", + "endPosition": "3824", "kind": "VARIABLE", - "name": "file", - "startPosition": "3180" + "binding": { + "endPosition": "3824", + "kind": "IDENTIFIER", + "name": "file", + "startPosition": "3820" + }, + "startPosition": "3816" }, { "expression": { - "endPosition": "3193", + "endPosition": "3833", "kind": "IDENTIFIER", "name": "files", - "startPosition": "3188" + "startPosition": "3828" }, - "endPosition": "3724", + "endPosition": "4364", "kind": "FOR_IN_LOOP", "forEach": "true", "variable": { - "endPosition": "3184", + "endPosition": "3824", "kind": "IDENTIFIER", "name": "file", - "startPosition": "3180" + "startPosition": "3820" }, "statement": { - "endPosition": "3724", + "endPosition": "4364", "kind": "BLOCK", "statements": [ { "condition": { - "endPosition": "3234", + "endPosition": "3874", "kind": "FUNCTION_INVOCATION", "functionSelect": { "identifier": "endsWith", "expression": { "identifier": "name", "expression": { - "endPosition": "3213", + "endPosition": "3853", "kind": "IDENTIFIER", "name": "file", - "startPosition": "3209" + "startPosition": "3849" }, - "endPosition": "3218", + "endPosition": "3858", "kind": "MEMBER_SELECT", - "startPosition": "3209" + "startPosition": "3849" }, - "endPosition": "3227", + "endPosition": "3867", "kind": "MEMBER_SELECT", - "startPosition": "3209" + "startPosition": "3849" }, "arguments": [ { - "endPosition": "3232", + "endPosition": "3872", "kind": "STRING_LITERAL", "value": ".js", - "startPosition": "3229" + "startPosition": "3869" } ], - "startPosition": "3209" + "startPosition": "3849" }, - "endPosition": "3718", + "endPosition": "4358", "kind": "IF", - "startPosition": "3205", + "startPosition": "3845", "thenStatement": { - "endPosition": "3718", + "endPosition": "4358", "kind": "BLOCK", "statements": [ { - "endPosition": "3278", + "endPosition": "3918", "kind": "VARIABLE", - "name": "script", - "startPosition": "3254", + "binding": { + "endPosition": "3900", + "kind": "IDENTIFIER", + "name": "script", + "startPosition": "3894" + }, + "startPosition": "3890", "initializer": { - "endPosition": "3278", + "endPosition": "3918", "kind": "FUNCTION_INVOCATION", "functionSelect": { - "endPosition": "3272", + "endPosition": "3912", "kind": "IDENTIFIER", "name": "readFully", - "startPosition": "3263" + "startPosition": "3903" }, "arguments": [ { - "endPosition": "3277", + "endPosition": "3917", "kind": "IDENTIFIER", "name": "file", - "startPosition": "3273" + "startPosition": "3913" } ], - "startPosition": "3263" + "startPosition": "3903" } }, { - "endPosition": "3317", + "endPosition": "3957", "kind": "VARIABLE", - "name": "parser", - "startPosition": "3296", + "binding": { + "endPosition": "3942", + "kind": "IDENTIFIER", + "name": "parser", + "startPosition": "3936" + }, + "startPosition": "3932", "initializer": { "constructorExpression": { - "endPosition": "3317", + "endPosition": "3957", "kind": "FUNCTION_INVOCATION", "functionSelect": { - "endPosition": "3315", + "endPosition": "3955", "kind": "IDENTIFIER", "name": "Parser", - "startPosition": "3309" + "startPosition": "3949" }, "arguments": [], - "startPosition": "3309" + "startPosition": "3949" }, - "endPosition": "3317", + "endPosition": "3957", "kind": "NEW", - "startPosition": "3305" + "startPosition": "3945" } }, { - "endPosition": "3578", + "endPosition": "4218", "kind": "VARIABLE", - "name": "tree", - "startPosition": "3335", + "binding": { + "endPosition": "3979", + "kind": "IDENTIFIER", + "name": "tree", + "startPosition": "3975" + }, + "startPosition": "3971", "initializer": { - "endPosition": "3578", + "endPosition": "4218", "kind": "FUNCTION_INVOCATION", "functionSelect": { "identifier": "parse", "expression": { - "endPosition": "3348", + "endPosition": "3988", "kind": "IDENTIFIER", "name": "parser", - "startPosition": "3342" + "startPosition": "3982" }, - "endPosition": "3354", + "endPosition": "3994", "kind": "MEMBER_SELECT", - "startPosition": "3342" + "startPosition": "3982" }, "arguments": [ { "leftOperand": { "leftOperand": { - "endPosition": "3361", + "endPosition": "4001", "kind": "IDENTIFIER", "name": "subdir", - "startPosition": "3355" + "startPosition": "3995" }, - "endPosition": "3366", + "endPosition": "4006", "kind": "PLUS", "rightOperand": { - "endPosition": "3366", + "endPosition": "4006", "kind": "STRING_LITERAL", "value": "/", - "startPosition": "3365" + "startPosition": "4005" }, - "startPosition": "3355" + "startPosition": "3995" }, - "endPosition": "3379", + "endPosition": "4019", "kind": "PLUS", "rightOperand": { "identifier": "name", "expression": { - "endPosition": "3374", + "endPosition": "4014", "kind": "IDENTIFIER", "name": "file", - "startPosition": "3370" + "startPosition": "4010" }, - "endPosition": "3379", + "endPosition": "4019", "kind": "MEMBER_SELECT", - "startPosition": "3370" + "startPosition": "4010" }, - "startPosition": "3355" + "startPosition": "3995" }, { - "endPosition": "3387", + "endPosition": "4027", "kind": "IDENTIFIER", "name": "script", - "startPosition": "3381" + "startPosition": "4021" }, { - "endPosition": "3426", + "endPosition": "4066", "kind": "FUNCTION_EXPRESSION", "body": { - "endPosition": "3559", + "endPosition": "4199", "kind": "BLOCK", "statements": [ { "expression": { - "endPosition": "3526", + "endPosition": "4166", "kind": "FUNCTION_INVOCATION", "functionSelect": { - "endPosition": "3453", + "endPosition": "4093", "kind": "IDENTIFIER", "name": "print", - "startPosition": "3448" + "startPosition": "4088" }, "arguments": [ { - "endPosition": "3525", + "endPosition": "4165", "kind": "FUNCTION_INVOCATION", "functionSelect": { "identifier": "replace", "expression": { - "endPosition": "3505", + "endPosition": "4145", "kind": "FUNCTION_INVOCATION", "functionSelect": { "identifier": "stringify", "expression": { - "endPosition": "3458", + "endPosition": "4098", "kind": "IDENTIFIER", "name": "JSON", - "startPosition": "3454" + "startPosition": "4094" }, - "endPosition": "3468", + "endPosition": "4108", "kind": "MEMBER_SELECT", - "startPosition": "3454" + "startPosition": "4094" }, "arguments": [ { - "endPosition": "3495", + "endPosition": "4135", "kind": "FUNCTION_INVOCATION", "functionSelect": { "identifier": "convert", "expression": { - "endPosition": "3475", + "endPosition": "4115", "kind": "IDENTIFIER", "name": "parser", - "startPosition": "3469" + "startPosition": "4109" }, - "endPosition": "3483", + "endPosition": "4123", "kind": "MEMBER_SELECT", - "startPosition": "3469" + "startPosition": "4109" }, "arguments": [ { - "endPosition": "3494", + "endPosition": "4134", "kind": "IDENTIFIER", "name": "diagnostic", - "startPosition": "3484" + "startPosition": "4124" } ], - "startPosition": "3469" + "startPosition": "4109" }, { - "endPosition": "3501", + "endPosition": "4141", "kind": "NULL_LITERAL", - "startPosition": "3497" + "startPosition": "4137" }, { - "endPosition": "3504", + "endPosition": "4144", "kind": "NUMBER_LITERAL", "value": "2", - "startPosition": "3503" + "startPosition": "4143" } ], - "startPosition": "3454" + "startPosition": "4094" }, - "endPosition": "3513", + "endPosition": "4153", "kind": "MEMBER_SELECT", - "startPosition": "3454" + "startPosition": "4094" }, "arguments": [ { - "endPosition": "3520", + "endPosition": "4160", "kind": "REGEXP_LITERAL", "options": "g", "pattern": "\\\\r", - "startPosition": "3514" + "startPosition": "4154" }, { - "endPosition": "3523", + "endPosition": "4163", "kind": "STRING_LITERAL", "value": "", - "startPosition": "3523" + "startPosition": "4163" } ], - "startPosition": "3513" + "startPosition": "4153" } ], - "startPosition": "3448" + "startPosition": "4088" }, - "endPosition": "3526", + "endPosition": "4166", "kind": "EXPRESSION_STATEMENT", - "startPosition": "3448" + "startPosition": "4088" }, { "expression": { - "endPosition": "3558", + "endPosition": "4198", "kind": "FUNCTION_INVOCATION", "functionSelect": { - "endPosition": "3553", + "endPosition": "4193", "kind": "IDENTIFIER", "name": "print", - "startPosition": "3548" + "startPosition": "4188" }, "arguments": [ { - "endPosition": "3556", + "endPosition": "4196", "kind": "STRING_LITERAL", "value": ",", - "startPosition": "3555" + "startPosition": "4195" } ], - "startPosition": "3548" + "startPosition": "4188" }, - "endPosition": "3558", + "endPosition": "4198", "kind": "EXPRESSION_STATEMENT", - "startPosition": "3548" + "startPosition": "4188" } ], - "startPosition": "3426" + "startPosition": "4066" }, "strict": "false", - "startPosition": "3426", + "startPosition": "4066", "parameters": [ { - "endPosition": "3424", + "endPosition": "4064", "kind": "IDENTIFIER", "name": "diagnostic", - "startPosition": "3414" + "startPosition": "4054" } ] } ], - "startPosition": "3342" + "startPosition": "3982" } }, { "condition": { "leftOperand": { - "endPosition": "3601", + "endPosition": "4241", "kind": "IDENTIFIER", "name": "tree", - "startPosition": "3597" + "startPosition": "4237" }, - "endPosition": "3609", + "endPosition": "4249", "kind": "NOT_EQUAL_TO", "rightOperand": { - "endPosition": "3609", + "endPosition": "4249", "kind": "NULL_LITERAL", - "startPosition": "3605" + "startPosition": "4245" }, - "startPosition": "3597" + "startPosition": "4237" }, - "endPosition": "3708", + "endPosition": "4348", "kind": "IF", - "startPosition": "3593", + "startPosition": "4233", "thenStatement": { - "endPosition": "3708", + "endPosition": "4348", "kind": "BLOCK", "statements": [ { "expression": { - "endPosition": "3665", + "endPosition": "4305", "kind": "FUNCTION_INVOCATION", "functionSelect": { - "endPosition": "3634", + "endPosition": "4274", "kind": "IDENTIFIER", "name": "print", - "startPosition": "3629" + "startPosition": "4269" }, "arguments": [ { - "endPosition": "3664", + "endPosition": "4304", "kind": "FUNCTION_INVOCATION", "functionSelect": { "identifier": "stringify", "expression": { - "endPosition": "3639", + "endPosition": "4279", "kind": "IDENTIFIER", "name": "JSON", - "startPosition": "3635" + "startPosition": "4275" }, - "endPosition": "3649", + "endPosition": "4289", "kind": "MEMBER_SELECT", - "startPosition": "3635" + "startPosition": "4275" }, "arguments": [ { - "endPosition": "3654", + "endPosition": "4294", "kind": "IDENTIFIER", "name": "tree", - "startPosition": "3650" + "startPosition": "4290" }, { - "endPosition": "3660", + "endPosition": "4300", "kind": "NULL_LITERAL", - "startPosition": "3656" + "startPosition": "4296" }, { - "endPosition": "3663", + "endPosition": "4303", "kind": "NUMBER_LITERAL", "value": "2", - "startPosition": "3662" + "startPosition": "4302" } ], - "startPosition": "3635" + "startPosition": "4275" } ], - "startPosition": "3629" + "startPosition": "4269" }, - "endPosition": "3665", + "endPosition": "4305", "kind": "EXPRESSION_STATEMENT", - "startPosition": "3629" + "startPosition": "4269" }, { "expression": { - "endPosition": "3693", + "endPosition": "4333", "kind": "FUNCTION_INVOCATION", "functionSelect": { - "endPosition": "3688", + "endPosition": "4328", "kind": "IDENTIFIER", "name": "print", - "startPosition": "3683" + "startPosition": "4323" }, "arguments": [ { - "endPosition": "3691", + "endPosition": "4331", "kind": "STRING_LITERAL", "value": ",", - "startPosition": "3690" + "startPosition": "4330" } ], - "startPosition": "3683" + "startPosition": "4323" }, - "endPosition": "3693", + "endPosition": "4333", "kind": "EXPRESSION_STATEMENT", - "startPosition": "3683" + "startPosition": "4323" } ], - "startPosition": "3611" + "startPosition": "4251" } } ], - "startPosition": "3236" + "startPosition": "3876" } } ], - "startPosition": "3195" + "startPosition": "3835" }, - "startPosition": "3166" + "startPosition": "3806" } ], - "startPosition": "3028" + "startPosition": "3668" }, "strict": "false", - "startPosition": "2998", + "startPosition": "3638", "parameters": [ { - "endPosition": "3026", + "endPosition": "3666", "kind": "IDENTIFIER", "name": "subdir", - "startPosition": "3020" + "startPosition": "3660" } ] }, { - "endPosition": "4070", + "endPosition": "4710", "kind": "FUNCTION", - "name": "main", + "name": { + "endPosition": "4421", + "kind": "IDENTIFIER", + "name": "main", + "startPosition": "4417" + }, "body": { - "endPosition": "4068", + "endPosition": "4708", "kind": "BLOCK", "statements": [ { "expression": { - "endPosition": "3800", + "endPosition": "4440", "kind": "FUNCTION_INVOCATION", "functionSelect": { - "endPosition": "3795", + "endPosition": "4435", "kind": "IDENTIFIER", "name": "print", - "startPosition": "3790" + "startPosition": "4430" }, "arguments": [ { - "endPosition": "3798", + "endPosition": "4438", "kind": "STRING_LITERAL", "value": "[", - "startPosition": "3797" + "startPosition": "4437" } ], - "startPosition": "3790" + "startPosition": "4430" }, - "endPosition": "3800", + "endPosition": "4440", "kind": "EXPRESSION_STATEMENT", - "startPosition": "3790" + "startPosition": "4430" }, { "expression": { - "endPosition": "3834", + "endPosition": "4474", "kind": "FUNCTION_INVOCATION", "functionSelect": { - "endPosition": "3819", + "endPosition": "4459", "kind": "IDENTIFIER", "name": "processFiles", - "startPosition": "3807" + "startPosition": "4447" }, "arguments": [ { - "endPosition": "3832", + "endPosition": "4472", "kind": "STRING_LITERAL", "value": "parsertests", - "startPosition": "3821" + "startPosition": "4461" } ], - "startPosition": "3807" + "startPosition": "4447" }, - "endPosition": "3834", + "endPosition": "4474", "kind": "EXPRESSION_STATEMENT", - "startPosition": "3807" + "startPosition": "4447" }, { "expression": { - "endPosition": "3875", + "endPosition": "4515", "kind": "FUNCTION_INVOCATION", "functionSelect": { - "endPosition": "3852", + "endPosition": "4492", "kind": "IDENTIFIER", "name": "processFiles", - "startPosition": "3840" + "startPosition": "4480" }, "arguments": [ { - "endPosition": "3873", + "endPosition": "4513", "kind": "STRING_LITERAL", "value": "parsernegativetests", - "startPosition": "3854" + "startPosition": "4494" } ], - "startPosition": "3840" + "startPosition": "4480" }, - "endPosition": "3875", + "endPosition": "4515", "kind": "EXPRESSION_STATEMENT", - "startPosition": "3840" + "startPosition": "4480" }, { - "endPosition": "3944", + "endPosition": "4584", "kind": "VARIABLE", - "name": "script", - "startPosition": "3916", + "binding": { + "endPosition": "4562", + "kind": "IDENTIFIER", + "name": "script", + "startPosition": "4556" + }, + "startPosition": "4552", "initializer": { - "endPosition": "3944", + "endPosition": "4584", "kind": "FUNCTION_INVOCATION", "functionSelect": { - "endPosition": "3934", + "endPosition": "4574", "kind": "IDENTIFIER", "name": "readFully", - "startPosition": "3925" + "startPosition": "4565" }, "arguments": [ { - "endPosition": "3943", + "endPosition": "4583", "kind": "IDENTIFIER", "name": "__FILE__", - "startPosition": "3935" + "startPosition": "4575" } ], - "startPosition": "3925" + "startPosition": "4565" } }, { - "endPosition": "4009", + "endPosition": "4649", "kind": "VARIABLE", - "name": "tree", - "startPosition": "3954", + "binding": { + "endPosition": "4598", + "kind": "IDENTIFIER", + "name": "tree", + "startPosition": "4594" + }, + "startPosition": "4590", "initializer": { - "endPosition": "4009", + "endPosition": "4649", "kind": "FUNCTION_INVOCATION", "functionSelect": { "identifier": "parse", "expression": { "constructorExpression": { - "endPosition": "3973", + "endPosition": "4613", "kind": "FUNCTION_INVOCATION", "functionSelect": { - "endPosition": "3971", + "endPosition": "4611", "kind": "IDENTIFIER", "name": "Parser", - "startPosition": "3965" + "startPosition": "4605" }, "arguments": [], - "startPosition": "3965" + "startPosition": "4605" }, - "endPosition": "3973", + "endPosition": "4613", "kind": "NEW", - "startPosition": "3961" + "startPosition": "4601" }, - "endPosition": "3979", + "endPosition": "4619", "kind": "MEMBER_SELECT", - "startPosition": "3961" + "startPosition": "4601" }, "arguments": [ { - "endPosition": "3993", + "endPosition": "4633", "kind": "STRING_LITERAL", "value": "parserapi.js", - "startPosition": "3981" + "startPosition": "4621" }, { - "endPosition": "4002", + "endPosition": "4642", "kind": "IDENTIFIER", "name": "script", - "startPosition": "3996" + "startPosition": "4636" }, { - "endPosition": "4008", + "endPosition": "4648", "kind": "NULL_LITERAL", - "startPosition": "4004" + "startPosition": "4644" } ], - "startPosition": "3961" + "startPosition": "4601" } }, { "expression": { - "endPosition": "4051", + "endPosition": "4691", "kind": "FUNCTION_INVOCATION", "functionSelect": { - "endPosition": "4020", + "endPosition": "4660", "kind": "IDENTIFIER", "name": "print", - "startPosition": "4015" + "startPosition": "4655" }, "arguments": [ { - "endPosition": "4050", + "endPosition": "4690", "kind": "FUNCTION_INVOCATION", "functionSelect": { "identifier": "stringify", "expression": { - "endPosition": "4025", + "endPosition": "4665", "kind": "IDENTIFIER", "name": "JSON", - "startPosition": "4021" + "startPosition": "4661" }, - "endPosition": "4035", + "endPosition": "4675", "kind": "MEMBER_SELECT", - "startPosition": "4021" + "startPosition": "4661" }, "arguments": [ { - "endPosition": "4040", + "endPosition": "4680", "kind": "IDENTIFIER", "name": "tree", - "startPosition": "4036" + "startPosition": "4676" }, { - "endPosition": "4046", + "endPosition": "4686", "kind": "NULL_LITERAL", - "startPosition": "4042" + "startPosition": "4682" }, { - "endPosition": "4049", + "endPosition": "4689", "kind": "NUMBER_LITERAL", "value": "2", - "startPosition": "4048" + "startPosition": "4688" } ], - "startPosition": "4021" + "startPosition": "4661" } ], - "startPosition": "4015" + "startPosition": "4655" }, - "endPosition": "4051", + "endPosition": "4691", "kind": "EXPRESSION_STATEMENT", - "startPosition": "4015" + "startPosition": "4655" }, { "expression": { - "endPosition": "4067", + "endPosition": "4707", "kind": "FUNCTION_INVOCATION", "functionSelect": { - "endPosition": "4062", + "endPosition": "4702", "kind": "IDENTIFIER", "name": "print", - "startPosition": "4057" + "startPosition": "4697" }, "arguments": [ { - "endPosition": "4065", + "endPosition": "4705", "kind": "STRING_LITERAL", "value": "]", - "startPosition": "4064" + "startPosition": "4704" } ], - "startPosition": "4057" + "startPosition": "4697" }, - "endPosition": "4067", + "endPosition": "4707", "kind": "EXPRESSION_STATEMENT", - "startPosition": "4057" + "startPosition": "4697" } ], - "startPosition": "3784" + "startPosition": "4424" }, "strict": "false", - "startPosition": "3768", + "startPosition": "4408", "parameters": [] }, { "expression": { - "endPosition": "4078", + "endPosition": "4718", "kind": "FUNCTION_INVOCATION", "functionSelect": { - "endPosition": "4076", + "endPosition": "4716", "kind": "IDENTIFIER", "name": "main", - "startPosition": "4072" + "startPosition": "4712" }, "arguments": [], - "startPosition": "4072" - }, - "endPosition": "4078", + "startPosition": "4712" + }, + "endPosition": "4718", "kind": "EXPRESSION_STATEMENT", - "startPosition": "4072" + "startPosition": "4712" } ], "sourceName": "parserapi.js", "strict": "false", "startPosition": "1136" -} -] +} +] diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/test/script/nosecurity/parservisitor.js --- a/nashorn/test/script/nosecurity/parservisitor.js Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/test/script/nosecurity/parservisitor.js Mon Jul 18 09:38:08 2016 -0700 @@ -202,7 +202,7 @@ parse("funcdecl.js", "function func() {}", new (Java.extend(SimpleTreeVisitor))() { visitFunctionDeclaration: function(fd) { - print("in visitFunctionDeclaration " + fd.name); + print("in visitFunctionDeclaration " + fd.name.name); } }); @@ -361,7 +361,7 @@ parse("var.js", "var x = 34;", new (Java.extend(SimpleTreeVisitor))() { visitVariable: function(vn) { - print("in visitVariable " + vn.name + " = " + vn.initializer.value); + print("in visitVariable " + vn.binding.name + " = " + vn.initializer.value); } }); diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/test/script/nosecurity/treeapi/arrow.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/nosecurity/treeapi/arrow.js Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,49 @@ +/* + * 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. + */ + +/** + * Tests to check representation of ES6 arrows. + * + * @test + * @option -scripting + * @run + */ + +load(__DIR__ + "utils.js") + +var code = <x*2; +[].map(v => v + 1); + +EOF + +parse("arrow.js", code, "--language=es6", new (Java.extend(visitor_es6, { + visitVariable : function (node, obj) { + obj.push(convert(node)) + }, + visitExpressionStatement : function (node, obj) { + obj.push(convert(node)) + } +}))) + diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/test/script/nosecurity/treeapi/arrow.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/nosecurity/treeapi/arrow.js.EXPECTED Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,103 @@ +[ + { + "endPosition": "15", + "kind": "VARIABLE", + "binding": { + "endPosition": "6", + "kind": "IDENTIFIER", + "name": "f", + "startPosition": "5" + }, + "startPosition": "1", + "initializer": { + "endPosition": "12", + "arrow": "true", + "kind": "FUNCTION_EXPRESSION", + "name": "null", + "body": { + "leftOperand": { + "endPosition": "13", + "kind": "IDENTIFIER", + "name": "x", + "startPosition": "12" + }, + "endPosition": "15", + "kind": "MULTIPLY", + "rightOperand": { + "endPosition": "15", + "kind": "NUMBER_LITERAL", + "value": "2", + "startPosition": "14" + }, + "startPosition": "12" + }, + "strict": "false", + "startPosition": "12", + "parameters": [ + { + "endPosition": "10", + "kind": "IDENTIFIER", + "name": "x", + "startPosition": "9" + } + ] + } + }, + { + "expression": { + "endPosition": "35", + "kind": "FUNCTION_INVOCATION", + "functionSelect": { + "identifier": "map", + "expression": { + "endPosition": "19", + "kind": "ARRAY_LITERAL", + "elements": [], + "startPosition": "17" + }, + "endPosition": "23", + "kind": "MEMBER_SELECT", + "startPosition": "17" + }, + "arguments": [ + { + "endPosition": "29", + "arrow": "true", + "kind": "FUNCTION_EXPRESSION", + "name": "null", + "body": { + "leftOperand": { + "endPosition": "30", + "kind": "IDENTIFIER", + "name": "v", + "startPosition": "29" + }, + "endPosition": "34", + "kind": "PLUS", + "rightOperand": { + "endPosition": "34", + "kind": "NUMBER_LITERAL", + "value": "1", + "startPosition": "33" + }, + "startPosition": "29" + }, + "strict": "false", + "startPosition": "29", + "parameters": [ + { + "endPosition": "25", + "kind": "IDENTIFIER", + "name": "v", + "startPosition": "24" + } + ] + } + ], + "startPosition": "17" + }, + "endPosition": "35", + "kind": "EXPRESSION_STATEMENT", + "startPosition": "17" + } +] diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/test/script/nosecurity/treeapi/arrow_params.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/nosecurity/treeapi/arrow_params.js Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,49 @@ +/* + * 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. + */ + +/** + * Tests to check representation of ES6 arrow params. + * + * @test + * @option -scripting + * @run + */ + +load(__DIR__ + "utils.js") + +var code = <x*3; +var f2 = ({x, y})=>x*y; +var f3 = ([x, y])=>x+y; +var f4 = ({x, y}={y: 4, x: 5})=>x*y; +var f5 = ([x, y]=[3, 6])=>x+y; + +EOF + +parse("arrow_params.js", code, "--language=es6", new (Java.extend(visitor_es6, { + visitVariable : function (node, obj) { + obj.push(convert(node)) + } +}))) + diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/test/script/nosecurity/treeapi/arrow_params.js.EXPECTED --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/nosecurity/treeapi/arrow_params.js.EXPECTED Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,411 @@ +[ + { + "endPosition": "20", + "kind": "VARIABLE", + "binding": { + "endPosition": "7", + "kind": "IDENTIFIER", + "name": "f1", + "startPosition": "5" + }, + "startPosition": "1", + "initializer": { + "endPosition": "17", + "arrow": "true", + "kind": "FUNCTION_EXPRESSION", + "name": "null", + "body": { + "leftOperand": { + "endPosition": "18", + "kind": "IDENTIFIER", + "name": "x", + "startPosition": "17" + }, + "endPosition": "20", + "kind": "MULTIPLY", + "rightOperand": { + "endPosition": "20", + "kind": "NUMBER_LITERAL", + "value": "3", + "startPosition": "19" + }, + "startPosition": "17" + }, + "strict": "false", + "startPosition": "17", + "parameters": [ + { + "expression": { + "endPosition": "14", + "kind": "NUMBER_LITERAL", + "value": "2", + "startPosition": "13" + }, + "endPosition": "14", + "kind": "ASSIGNMENT", + "variable": { + "endPosition": "12", + "kind": "IDENTIFIER", + "name": "x", + "startPosition": "11" + }, + "startPosition": "11" + } + ] + } + }, + { + "endPosition": "44", + "kind": "VARIABLE", + "binding": { + "endPosition": "28", + "kind": "IDENTIFIER", + "name": "f2", + "startPosition": "26" + }, + "startPosition": "22", + "initializer": { + "endPosition": "41", + "arrow": "true", + "kind": "FUNCTION_EXPRESSION", + "name": "null", + "body": { + "leftOperand": { + "endPosition": "42", + "kind": "IDENTIFIER", + "name": "x", + "startPosition": "41" + }, + "endPosition": "44", + "kind": "MULTIPLY", + "rightOperand": { + "endPosition": "44", + "kind": "IDENTIFIER", + "name": "y", + "startPosition": "43" + }, + "startPosition": "41" + }, + "strict": "false", + "startPosition": "41", + "parameters": [ + { + "endPosition": "38", + "kind": "OBJECT_LITERAL", + "startPosition": "32", + "properties": [ + { + "getter": "null", + "endPosition": "34", + "kind": "PROPERTY", + "setter": "null", + "value": { + "endPosition": "34", + "kind": "IDENTIFIER", + "name": "x", + "startPosition": "33" + }, + "startPosition": "33", + "key": { + "endPosition": "34", + "kind": "IDENTIFIER", + "name": "x", + "startPosition": "33" + } + }, + { + "getter": "null", + "endPosition": "37", + "kind": "PROPERTY", + "setter": "null", + "value": { + "endPosition": "37", + "kind": "IDENTIFIER", + "name": "y", + "startPosition": "36" + }, + "startPosition": "36", + "key": { + "endPosition": "37", + "kind": "IDENTIFIER", + "name": "y", + "startPosition": "36" + } + } + ] + } + ] + } + }, + { + "endPosition": "68", + "kind": "VARIABLE", + "binding": { + "endPosition": "52", + "kind": "IDENTIFIER", + "name": "f3", + "startPosition": "50" + }, + "startPosition": "46", + "initializer": { + "endPosition": "65", + "arrow": "true", + "kind": "FUNCTION_EXPRESSION", + "name": "null", + "body": { + "leftOperand": { + "endPosition": "66", + "kind": "IDENTIFIER", + "name": "x", + "startPosition": "65" + }, + "endPosition": "68", + "kind": "PLUS", + "rightOperand": { + "endPosition": "68", + "kind": "IDENTIFIER", + "name": "y", + "startPosition": "67" + }, + "startPosition": "65" + }, + "strict": "false", + "startPosition": "65", + "parameters": [ + { + "endPosition": "62", + "kind": "ARRAY_LITERAL", + "elements": [ + { + "endPosition": "58", + "kind": "IDENTIFIER", + "name": "x", + "startPosition": "57" + }, + { + "endPosition": "61", + "kind": "IDENTIFIER", + "name": "y", + "startPosition": "60" + } + ], + "startPosition": "56" + } + ] + } + }, + { + "endPosition": "105", + "kind": "VARIABLE", + "binding": { + "endPosition": "76", + "kind": "IDENTIFIER", + "name": "f4", + "startPosition": "74" + }, + "startPosition": "70", + "initializer": { + "endPosition": "102", + "arrow": "true", + "kind": "FUNCTION_EXPRESSION", + "name": "null", + "body": { + "leftOperand": { + "endPosition": "103", + "kind": "IDENTIFIER", + "name": "x", + "startPosition": "102" + }, + "endPosition": "105", + "kind": "MULTIPLY", + "rightOperand": { + "endPosition": "105", + "kind": "IDENTIFIER", + "name": "y", + "startPosition": "104" + }, + "startPosition": "102" + }, + "strict": "false", + "startPosition": "102", + "parameters": [ + { + "expression": { + "endPosition": "99", + "kind": "OBJECT_LITERAL", + "startPosition": "87", + "properties": [ + { + "getter": "null", + "endPosition": "92", + "kind": "PROPERTY", + "setter": "null", + "value": { + "endPosition": "92", + "kind": "NUMBER_LITERAL", + "value": "4", + "startPosition": "91" + }, + "startPosition": "88", + "key": { + "endPosition": "89", + "kind": "IDENTIFIER", + "name": "y", + "startPosition": "88" + } + }, + { + "getter": "null", + "endPosition": "98", + "kind": "PROPERTY", + "setter": "null", + "value": { + "endPosition": "98", + "kind": "NUMBER_LITERAL", + "value": "5", + "startPosition": "97" + }, + "startPosition": "94", + "key": { + "endPosition": "95", + "kind": "IDENTIFIER", + "name": "x", + "startPosition": "94" + } + } + ] + }, + "endPosition": "99", + "kind": "ASSIGNMENT", + "variable": { + "endPosition": "86", + "kind": "OBJECT_LITERAL", + "startPosition": "80", + "properties": [ + { + "getter": "null", + "endPosition": "82", + "kind": "PROPERTY", + "setter": "null", + "value": { + "endPosition": "82", + "kind": "IDENTIFIER", + "name": "x", + "startPosition": "81" + }, + "startPosition": "81", + "key": { + "endPosition": "82", + "kind": "IDENTIFIER", + "name": "x", + "startPosition": "81" + } + }, + { + "getter": "null", + "endPosition": "85", + "kind": "PROPERTY", + "setter": "null", + "value": { + "endPosition": "85", + "kind": "IDENTIFIER", + "name": "y", + "startPosition": "84" + }, + "startPosition": "84", + "key": { + "endPosition": "85", + "kind": "IDENTIFIER", + "name": "y", + "startPosition": "84" + } + } + ] + }, + "startPosition": "80" + } + ] + } + }, + { + "endPosition": "136", + "kind": "VARIABLE", + "binding": { + "endPosition": "113", + "kind": "IDENTIFIER", + "name": "f5", + "startPosition": "111" + }, + "startPosition": "107", + "initializer": { + "endPosition": "133", + "arrow": "true", + "kind": "FUNCTION_EXPRESSION", + "name": "null", + "body": { + "leftOperand": { + "endPosition": "134", + "kind": "IDENTIFIER", + "name": "x", + "startPosition": "133" + }, + "endPosition": "136", + "kind": "PLUS", + "rightOperand": { + "endPosition": "136", + "kind": "IDENTIFIER", + "name": "y", + "startPosition": "135" + }, + "startPosition": "133" + }, + "strict": "false", + "startPosition": "133", + "parameters": [ + { + "expression": { + "endPosition": "130", + "kind": "ARRAY_LITERAL", + "elements": [ + { + "endPosition": "126", + "kind": "NUMBER_LITERAL", + "value": "3", + "startPosition": "125" + }, + { + "endPosition": "129", + "kind": "NUMBER_LITERAL", + "value": "6", + "startPosition": "128" + } + ], + "startPosition": "124" + }, + "endPosition": "130", + "kind": "ASSIGNMENT", + "variable": { + "endPosition": "123", + "kind": "ARRAY_LITERAL", + "elements": [ + { + "endPosition": "119", + "kind": "IDENTIFIER", + "name": "x", + "startPosition": "118" + }, + { + "endPosition": "122", + "kind": "IDENTIFIER", + "name": "y", + "startPosition": "121" + } + ], + "startPosition": "117" + }, + "startPosition": "117" + } + ] + } + } +] diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/test/script/nosecurity/treeapi/assignment.js.EXPECTED --- a/nashorn/test/script/nosecurity/treeapi/assignment.js.EXPECTED Fri Jul 15 09:05:36 2016 -0700 +++ b/nashorn/test/script/nosecurity/treeapi/assignment.js.EXPECTED Mon Jul 18 09:38:08 2016 -0700 @@ -146,4 +146,4 @@ }, "startPosition": "61" } -] +] diff -r 8bc25e077e83 -r f5d65fcf55e4 nashorn/test/script/nosecurity/treeapi/class.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/script/nosecurity/treeapi/class.js Mon Jul 18 09:38:08 2016 -0700 @@ -0,0 +1,91 @@ +/* + * 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. + */ + +/** + * Tests to check representation of ES6 class. + * + * @test + * @option -scripting + * @run + */ + +load(__DIR__ + "utils.js") + +var code = <
    Description
    testpkgmdl1All Modules 
    module2\n" + + "
    This is a test description for the module2 module.
    \n" + + "
    testpkg2mdl2module1 
    java.base 
    TestClassInModule2 
    testpkg2mdl2.TestInterfaceInModule2
    (Implementation: " + + "TestClassInModule2)
     
    PackageModuleDescription
    Requires 
    ModuleDescription
    Uses 
    TypeDescription
    Provides 
    TypeDescription