From 651e15fda63ec190b99092e9dd38ba8003f39eed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Per=20Lid=C3=A9n?= Date: Tue, 28 Jun 2016 10:37:52 +0200 Subject: [PATCH 01/44] 8159890: SIGSEGV with UseStringDeduplication and UseSharedSpaces/RequireSharedSpaces Co-authored-by: Ioi Lam Reviewed-by: stefank, drwhite, tschatzl, jiangli, iklam --- .../share/vm/classfile/compactHashtable.cpp | 2 +- .../SharedArchiveFile/SharedStringsDedup.java | 72 +++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 hotspot/test/runtime/SharedArchiveFile/SharedStringsDedup.java diff --git a/hotspot/src/share/vm/classfile/compactHashtable.cpp b/hotspot/src/share/vm/classfile/compactHashtable.cpp index f2feec3c810..a9b6b28f365 100644 --- a/hotspot/src/share/vm/classfile/compactHashtable.cpp +++ b/hotspot/src/share/vm/classfile/compactHashtable.cpp @@ -248,7 +248,7 @@ inline void SimpleCompactHashtable::iterate(const I& iterator) { } else { u4*entry_max = _entries + BUCKET_OFFSET(_buckets[i + 1]); while (entry < entry_max) { - iterator.do_value(_base_address, entry[0]); + iterator.do_value(_base_address, entry[1]); entry += 2; } } diff --git a/hotspot/test/runtime/SharedArchiveFile/SharedStringsDedup.java b/hotspot/test/runtime/SharedArchiveFile/SharedStringsDedup.java new file mode 100644 index 00000000000..432b36bffd8 --- /dev/null +++ b/hotspot/test/runtime/SharedArchiveFile/SharedStringsDedup.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test SharedStringsDedup + * @summary Test -Xshare:auto with shared strings and -XX:+UseStringDeduplication + * Feature support: G1GC only, compressed oops/kptrs, 64-bit os, not on windows + * @requires (sun.arch.data.model != "32") & (os.family != "windows") + * @requires (vm.opt.UseCompressedOops == null) | (vm.opt.UseCompressedOops == true) + * @requires vm.gc.G1 + * @library /testlibrary + * @modules java.base/jdk.internal.misc + * java.management + * @run main SharedStringsDedup + */ + +import jdk.test.lib.*; +import java.io.File; + +// The main purpose is to test the interaction between shared strings +// and -XX:+UseStringDeduplication. We run in -Xshare:auto mode so +// we don't need to worry about CDS archive mapping failure (which +// doesn't happen often so it won't impact coverage). +public class SharedStringsDedup { + public static void main(String[] args) throws Exception { + // Dump + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:SharedArchiveFile=./SharedStringsDedup.jsa", + "-XX:+UseCompressedOops", "-XX:+UseG1GC", + "-XX:+PrintSharedSpaces", + "-Xshare:dump"); + + new OutputAnalyzer(pb.start()) + .shouldContain("Loading classes to share") + .shouldContain("Shared string table stats") + .shouldHaveExitValue(0); + + // Run with -Xshare:auto + pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", + "-XX:SharedArchiveFile=./SharedStringsDedup.jsa", + "-XX:+UseCompressedOops", "-XX:+UseG1GC", + "-XX:+UseStringDeduplication", + "-Xshare:auto", + "-version"); + + new OutputAnalyzer(pb.start()) + .shouldMatch("(java|openjdk) version") + .shouldHaveExitValue(0); + } +} From 92eb334c91899599823d6d33fd4a60c6bc088c97 Mon Sep 17 00:00:00 2001 From: Lois Foltan Date: Tue, 28 Jun 2016 10:11:01 -0400 Subject: [PATCH 02/44] 8159262: Walking PackageEntry Export and ModuleEntry Reads Must Occur Only When Neccessary And Wait Until ClassLoader's Aliveness Determined Fixed an issue in class unloading to delay walk until class loader's aliveness is determined of modularity lists to remove dead modules Reviewed-by: coleenp, dholmes, sspitsyn, zgu --- .../share/vm/classfile/classLoaderData.cpp | 42 ++++-- .../share/vm/classfile/classLoaderData.hpp | 2 + .../src/share/vm/classfile/moduleEntry.cpp | 52 ++++++- .../src/share/vm/classfile/moduleEntry.hpp | 43 +++--- hotspot/src/share/vm/classfile/modules.cpp | 4 +- .../src/share/vm/classfile/packageEntry.cpp | 59 +++++++- .../src/share/vm/classfile/packageEntry.hpp | 3 + .../share/vm/classfile/systemDictionary.cpp | 15 +- .../share/vm/classfile/systemDictionary.hpp | 1 + .../ModuleStress/CustomSystemClassLoader.java | 35 +++++ .../ModuleStress/ModuleNonBuiltinCLMain.java | 129 +++++++++++++++++ .../ModuleStress/ModuleSameCLMain.java | 109 +++++++++++++++ .../modules/ModuleStress/ModuleStress.java | 131 ++++++++++++++++++ .../modules/ModuleStress/ModuleStressGC.java | 84 +++++++++++ .../src/jdk.test/test/MainGC.java | 114 +++++++++++++++ .../src/jdk.translet/translet/MainGC.java | 30 ++++ 16 files changed, 807 insertions(+), 46 deletions(-) create mode 100644 hotspot/test/runtime/modules/ModuleStress/CustomSystemClassLoader.java create mode 100644 hotspot/test/runtime/modules/ModuleStress/ModuleNonBuiltinCLMain.java create mode 100644 hotspot/test/runtime/modules/ModuleStress/ModuleSameCLMain.java create mode 100644 hotspot/test/runtime/modules/ModuleStress/ModuleStress.java create mode 100644 hotspot/test/runtime/modules/ModuleStress/ModuleStressGC.java create mode 100644 hotspot/test/runtime/modules/ModuleStress/src/jdk.test/test/MainGC.java create mode 100644 hotspot/test/runtime/modules/ModuleStress/src/jdk.translet/translet/MainGC.java diff --git a/hotspot/src/share/vm/classfile/classLoaderData.cpp b/hotspot/src/share/vm/classfile/classLoaderData.cpp index 7635879a49a..f0fdc648095 100644 --- a/hotspot/src/share/vm/classfile/classLoaderData.cpp +++ b/hotspot/src/share/vm/classfile/classLoaderData.cpp @@ -501,13 +501,26 @@ ClassLoaderData::~ClassLoaderData() { } } -/** - * Returns true if this class loader data is for the platform class loader. - */ +// Returns true if this class loader data is for the system class loader. +bool ClassLoaderData::is_system_class_loader_data() const { + return SystemDictionary::is_system_class_loader(class_loader()); +} + +// Returns true if this class loader data is for the platform class loader. bool ClassLoaderData::is_platform_class_loader_data() const { return SystemDictionary::is_platform_class_loader(class_loader()); } +// Returns true if this class loader data is one of the 3 builtin +// (boot, application/system or platform) class loaders. Note, the +// builtin loaders are not freed by a GC. +bool ClassLoaderData::is_builtin_class_loader_data() const { + Handle classLoaderHandle = class_loader(); + return (is_the_null_class_loader_data() || + SystemDictionary::is_system_class_loader(classLoaderHandle) || + SystemDictionary::is_platform_class_loader(classLoaderHandle)); +} + Metaspace* ClassLoaderData::metaspace_non_null() { assert(!DumpSharedSpaces, "wrong metaspace!"); // If the metaspace has not been allocated, create a new one. Might want @@ -957,12 +970,6 @@ bool ClassLoaderDataGraph::do_unloading(BoolObjectClosure* is_alive_closure, data = _head; while (data != NULL) { if (data->is_alive(is_alive_closure)) { - if (data->packages_defined()) { - data->packages()->purge_all_package_exports(); - } - if (data->modules_defined()) { - data->modules()->purge_all_module_reads(); - } // clean metaspace if (walk_all_metadata) { data->classes_do(InstanceKlass::purge_previous_versions); @@ -990,6 +997,23 @@ bool ClassLoaderDataGraph::do_unloading(BoolObjectClosure* is_alive_closure, } if (seen_dead_loader) { + // Walk a ModuleEntry's reads and a PackageEntry's exports lists + // to determine if there are modules on those lists that are now + // dead and should be removed. A module's life cycle is equivalent + // to its defining class loader's life cycle. Since a module is + // considered dead if its class loader is dead, these walks must + // occur after each class loader's aliveness is determined. + data = _head; + while (data != NULL) { + if (data->packages_defined()) { + data->packages()->purge_all_package_exports(); + } + if (data->modules_defined()) { + data->modules()->purge_all_module_reads(); + } + data = data->next(); + } + post_class_unload_events(); } diff --git a/hotspot/src/share/vm/classfile/classLoaderData.hpp b/hotspot/src/share/vm/classfile/classLoaderData.hpp index 9fc9839b7cf..602097935cc 100644 --- a/hotspot/src/share/vm/classfile/classLoaderData.hpp +++ b/hotspot/src/share/vm/classfile/classLoaderData.hpp @@ -270,7 +270,9 @@ class ClassLoaderData : public CHeapObj { bool is_the_null_class_loader_data() const { return this == _the_null_class_loader_data; } + bool is_system_class_loader_data() const; bool is_platform_class_loader_data() const; + bool is_builtin_class_loader_data() const; // The Metaspace is created lazily so may be NULL. This // method will allocate a Metaspace if needed. diff --git a/hotspot/src/share/vm/classfile/moduleEntry.cpp b/hotspot/src/share/vm/classfile/moduleEntry.cpp index c507584c712..a05c1644b92 100644 --- a/hotspot/src/share/vm/classfile/moduleEntry.cpp +++ b/hotspot/src/share/vm/classfile/moduleEntry.cpp @@ -40,7 +40,6 @@ ModuleEntry* ModuleEntryTable::_javabase_module = NULL; - void ModuleEntry::set_location(Symbol* location) { if (_location != NULL) { // _location symbol's refcounts are managed by ModuleEntry, @@ -115,10 +114,35 @@ void ModuleEntry::add_read(ModuleEntry* m) { // Lazily create a module's reads list _reads = new (ResourceObj::C_HEAP, mtModule)GrowableArray(MODULE_READS_SIZE, true); } + + // Determine, based on this newly established read edge to module m, + // if this module's read list should be walked at a GC safepoint. + set_read_walk_required(m->loader_data()); + + // Establish readability to module m _reads->append_if_missing(m); } } +// If the module's loader, that a read edge is being established to, is +// not the same loader as this module's and is not one of the 3 builtin +// class loaders, then this module's reads list must be walked at GC +// safepoint. Modules have the same life cycle as their defining class +// loaders and should be removed if dead. +void ModuleEntry::set_read_walk_required(ClassLoaderData* m_loader_data) { + assert_locked_or_safepoint(Module_lock); + if (!_must_walk_reads && + loader_data() != m_loader_data && + !m_loader_data->is_builtin_class_loader_data()) { + _must_walk_reads = true; + if (log_is_enabled(Trace, modules)) { + ResourceMark rm; + log_trace(modules)("ModuleEntry::set_read_walk_required(): module %s reads list must be walked", + (name() != NULL) ? name()->as_C_string() : UNNAMED_MODULE); + } + } +} + bool ModuleEntry::has_reads() const { assert_locked_or_safepoint(Module_lock); return ((_reads != NULL) && !_reads->is_empty()); @@ -127,14 +151,28 @@ bool ModuleEntry::has_reads() const { // Purge dead module entries out of reads list. void ModuleEntry::purge_reads() { assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); - if (has_reads()) { + + if (_must_walk_reads && has_reads()) { + // This module's _must_walk_reads flag will be reset based + // on the remaining live modules on the reads list. + _must_walk_reads = false; + + if (log_is_enabled(Trace, modules)) { + ResourceMark rm; + log_trace(modules)("ModuleEntry::purge_reads(): module %s reads list being walked", + (name() != NULL) ? name()->as_C_string() : UNNAMED_MODULE); + } + // Go backwards because this removes entries that are dead. int len = _reads->length(); for (int idx = len - 1; idx >= 0; idx--) { ModuleEntry* module_idx = _reads->at(idx); - ClassLoaderData* cld = module_idx->loader(); - if (cld->is_unloading()) { + ClassLoaderData* cld_idx = module_idx->loader_data(); + if (cld_idx->is_unloading()) { _reads->delete_at(idx); + } else { + // Update the need to walk this module's reads based on live modules + set_read_walk_required(cld_idx); } } } @@ -248,7 +286,7 @@ ModuleEntry* ModuleEntryTable::new_entry(unsigned int hash, Handle module_handle entry->set_module(loader_data->add_handle(module_handle)); } - entry->set_loader(loader_data); + entry->set_loader_data(loader_data); entry->set_version(version); entry->set_location(location); @@ -379,7 +417,7 @@ void ModuleEntry::print(outputStream* st) { p2i(this), name() == NULL ? UNNAMED_MODULE : name()->as_C_string(), p2i(module()), - loader()->loader_name(), + loader_data()->loader_name(), version() != NULL ? version()->as_C_string() : "NULL", location() != NULL ? location()->as_C_string() : "NULL", BOOL_TO_STR(!can_read_all_unnamed()), p2i(next())); @@ -401,5 +439,5 @@ void ModuleEntryTable::verify() { } void ModuleEntry::verify() { - guarantee(loader() != NULL, "A module entry must be associated with a loader."); + guarantee(loader_data() != NULL, "A module entry must be associated with a loader."); } diff --git a/hotspot/src/share/vm/classfile/moduleEntry.hpp b/hotspot/src/share/vm/classfile/moduleEntry.hpp index 9b68c01d424..8ec8237ec3f 100644 --- a/hotspot/src/share/vm/classfile/moduleEntry.hpp +++ b/hotspot/src/share/vm/classfile/moduleEntry.hpp @@ -43,6 +43,7 @@ class ModuleClosure; // It contains: // - Symbol* containing the module's name. // - pointer to the java.lang.reflect.Module for this module. +// - pointer to the java.security.ProtectionDomain shared by classes defined to this module. // - ClassLoaderData*, class loader of this module. // - a growable array containg other module entries that this module can read. // - a flag indicating if this module can read all unnamed modules. @@ -54,56 +55,58 @@ private: jobject _module; // java.lang.reflect.Module jobject _pd; // java.security.ProtectionDomain, cached // for shared classes from this module - ClassLoaderData* _loader; + ClassLoaderData* _loader_data; GrowableArray* _reads; // list of modules that are readable by this module Symbol* _version; // module version number Symbol* _location; // module location bool _can_read_all_unnamed; bool _has_default_read_edges; // JVMTI redefine/retransform support + bool _must_walk_reads; // walk module's reads list at GC safepoints to purge out dead modules TRACE_DEFINE_TRACE_ID_FIELD; enum {MODULE_READS_SIZE = 101}; // Initial size of list of modules that the module can read. public: void init() { _module = NULL; - _loader = NULL; + _loader_data = NULL; _pd = NULL; _reads = NULL; _version = NULL; _location = NULL; _can_read_all_unnamed = false; _has_default_read_edges = false; + _must_walk_reads = false; } - Symbol* name() const { return literal(); } - void set_name(Symbol* n) { set_literal(n); } + Symbol* name() const { return literal(); } + void set_name(Symbol* n) { set_literal(n); } - jobject module() const { return _module; } - void set_module(jobject j) { _module = j; } + jobject module() const { return _module; } + void set_module(jobject j) { _module = j; } // The shared ProtectionDomain reference is set once the VM loads a shared class // originated from the current Module. The referenced ProtectionDomain object is // created by the ClassLoader when loading a class (shared or non-shared) from the // Module for the first time. This ProtectionDomain object is used for all // classes from the Module loaded by the same ClassLoader. - Handle shared_protection_domain(); - void set_shared_protection_domain(ClassLoaderData *loader_data, - Handle pd); + Handle shared_protection_domain(); + void set_shared_protection_domain(ClassLoaderData *loader_data, Handle pd); - ClassLoaderData* loader() const { return _loader; } - void set_loader(ClassLoaderData* l) { _loader = l; } + ClassLoaderData* loader_data() const { return _loader_data; } + void set_loader_data(ClassLoaderData* l) { _loader_data = l; } - Symbol* version() const { return _version; } - void set_version(Symbol* version); + Symbol* version() const { return _version; } + void set_version(Symbol* version); - Symbol* location() const { return _location; } - void set_location(Symbol* location); + Symbol* location() const { return _location; } + void set_location(Symbol* location); - bool can_read(ModuleEntry* m) const; - bool has_reads() const; - void add_read(ModuleEntry* m); + bool can_read(ModuleEntry* m) const; + bool has_reads() const; + void add_read(ModuleEntry* m); + void set_read_walk_required(ClassLoaderData* m_loader_data); - bool is_named() const { return (literal() != NULL); } + bool is_named() const { return (name() != NULL); } bool can_read_all_unnamed() const { assert(is_named() || _can_read_all_unnamed == true, @@ -178,7 +181,7 @@ private: ModuleEntry* _unnamed_module; ModuleEntry* new_entry(unsigned int hash, Handle module_handle, Symbol* name, Symbol* version, - Symbol* location, ClassLoaderData* class_loader); + Symbol* location, ClassLoaderData* loader_data); void add_entry(int index, ModuleEntry* new_entry); int entry_size() const { return BasicHashtable::entry_size(); } diff --git a/hotspot/src/share/vm/classfile/modules.cpp b/hotspot/src/share/vm/classfile/modules.cpp index de8aac056b7..cd0937267f8 100644 --- a/hotspot/src/share/vm/classfile/modules.cpp +++ b/hotspot/src/share/vm/classfile/modules.cpp @@ -113,7 +113,7 @@ static PackageEntry* get_package_entry(ModuleEntry* module_entry, jstring packag const char *package_name = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(package)); if (package_name == NULL) return NULL; TempNewSymbol pkg_symbol = SymbolTable::new_symbol(package_name, CHECK_NULL); - PackageEntryTable* package_entry_table = module_entry->loader()->packages(); + PackageEntryTable* package_entry_table = module_entry->loader_data()->packages(); assert(package_entry_table != NULL, "Unexpected null package entry table"); return package_entry_table->lookup_only(pkg_symbol); } @@ -868,7 +868,7 @@ void Modules::add_module_package(jobject module, jstring package, TRAPS) { package_name, module_entry->name()->as_C_string()); TempNewSymbol pkg_symbol = SymbolTable::new_symbol(package_name, CHECK); - PackageEntryTable* package_table = module_entry->loader()->packages(); + PackageEntryTable* package_table = module_entry->loader_data()->packages(); assert(package_table != NULL, "Missing package_table"); bool pkg_exists = false; diff --git a/hotspot/src/share/vm/classfile/packageEntry.cpp b/hotspot/src/share/vm/classfile/packageEntry.cpp index 11cc5ea71aa..d224941c32d 100644 --- a/hotspot/src/share/vm/classfile/packageEntry.cpp +++ b/hotspot/src/share/vm/classfile/packageEntry.cpp @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "classfile/moduleEntry.hpp" #include "classfile/packageEntry.hpp" +#include "logging/log.hpp" #include "memory/resourceArea.hpp" #include "oops/symbol.hpp" #include "runtime/handles.inline.hpp" @@ -53,12 +54,40 @@ void PackageEntry::add_qexport(ModuleEntry* m) { if (!has_qual_exports_list()) { // Lazily create a package's qualified exports list. // Initial size is small, do not anticipate export lists to be large. - _qualified_exports = - new (ResourceObj::C_HEAP, mtModule) GrowableArray(QUAL_EXP_SIZE, true); + _qualified_exports = new (ResourceObj::C_HEAP, mtModule) GrowableArray(QUAL_EXP_SIZE, true); } + + // Determine, based on this newly established export to module m, + // if this package's export list should be walked at a GC safepoint. + set_export_walk_required(m->loader_data()); + + // Establish exportability to module m _qualified_exports->append_if_missing(m); } +// If the module's loader, that an export is being established to, is +// not the same loader as this module's and is not one of the 3 builtin +// class loaders, then this package's export list must be walked at GC +// safepoint. Modules have the same life cycle as their defining class +// loaders and should be removed if dead. +void PackageEntry::set_export_walk_required(ClassLoaderData* m_loader_data) { + assert_locked_or_safepoint(Module_lock); + ModuleEntry* this_pkg_mod = module(); + if (!_must_walk_exports && + (this_pkg_mod == NULL || this_pkg_mod->loader_data() != m_loader_data) && + !m_loader_data->is_builtin_class_loader_data()) { + _must_walk_exports = true; + if (log_is_enabled(Trace, modules)) { + ResourceMark rm; + assert(name() != NULL, "PackageEntry without a valid name"); + log_trace(modules)("PackageEntry::set_export_walk_required(): package %s defined in module %s, exports list must be walked", + name()->as_C_string(), + (this_pkg_mod == NULL || this_pkg_mod->name() == NULL) ? + UNNAMED_MODULE : this_pkg_mod->name()->as_C_string()); + } + } +} + // Set the package's exported states based on the value of the ModuleEntry. void PackageEntry::set_exported(ModuleEntry* m) { MutexLocker m1(Module_lock); @@ -96,14 +125,34 @@ void PackageEntry::set_is_exported_allUnnamed() { // Remove dead module entries within the package's exported list. void PackageEntry::purge_qualified_exports() { assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); - if (_qualified_exports != NULL) { + if (_must_walk_exports && + _qualified_exports != NULL && + !_qualified_exports->is_empty()) { + ModuleEntry* pkg_module = module(); + + // This package's _must_walk_exports flag will be reset based + // on the remaining live modules on the exports list. + _must_walk_exports = false; + + if (log_is_enabled(Trace, modules)) { + ResourceMark rm; + assert(name() != NULL, "PackageEntry without a valid name"); + ModuleEntry* pkg_mod = module(); + log_trace(modules)("PackageEntry::purge_qualified_exports(): package %s defined in module %s, exports list being walked", + name()->as_C_string(), + (pkg_mod == NULL || pkg_mod->name() == NULL) ? UNNAMED_MODULE : pkg_mod->name()->as_C_string()); + } + // Go backwards because this removes entries that are dead. int len = _qualified_exports->length(); for (int idx = len - 1; idx >= 0; idx--) { ModuleEntry* module_idx = _qualified_exports->at(idx); - ClassLoaderData* cld = module_idx->loader(); - if (cld->is_unloading()) { + ClassLoaderData* cld_idx = module_idx->loader_data(); + if (cld_idx->is_unloading()) { _qualified_exports->delete_at(idx); + } else { + // Update the need to walk this package's exports based on live modules + set_export_walk_required(cld_idx); } } } diff --git a/hotspot/src/share/vm/classfile/packageEntry.hpp b/hotspot/src/share/vm/classfile/packageEntry.hpp index e1bf62b3aa4..a379bf9de3b 100644 --- a/hotspot/src/share/vm/classfile/packageEntry.hpp +++ b/hotspot/src/share/vm/classfile/packageEntry.hpp @@ -69,6 +69,7 @@ private: s2 _classpath_index; bool _is_exported_unqualified; bool _is_exported_allUnnamed; + bool _must_walk_exports; GrowableArray* _exported_pending_delete; // transitioned from qualified to unqualified, delete at safepoint GrowableArray* _qualified_exports; TRACE_DEFINE_TRACE_ID_FIELD; @@ -82,6 +83,7 @@ public: _classpath_index = -1; _is_exported_unqualified = false; _is_exported_allUnnamed = false; + _must_walk_exports = false; _exported_pending_delete = NULL; _qualified_exports = NULL; } @@ -147,6 +149,7 @@ public: // add the module to the package's qualified exports void add_qexport(ModuleEntry* m); + void set_export_walk_required(ClassLoaderData* m_loader_data); PackageEntry* next() const { return (PackageEntry*)HashtableEntry::next(); diff --git a/hotspot/src/share/vm/classfile/systemDictionary.cpp b/hotspot/src/share/vm/classfile/systemDictionary.cpp index aaa2ad5dda1..b747fae54f2 100644 --- a/hotspot/src/share/vm/classfile/systemDictionary.cpp +++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp @@ -175,9 +175,18 @@ bool SystemDictionary::is_parallelDefine(Handle class_loader) { return false; } -/** - * Returns true if the passed class loader is the platform class loader. - */ +// Returns true if the passed class loader is the builtin application class loader +// or a custom system class loader. A customer system class loader can be +// specified via -Djava.system.class.loader. +bool SystemDictionary::is_system_class_loader(Handle class_loader) { + if (class_loader.is_null()) { + return false; + } + return (class_loader->klass() == SystemDictionary::jdk_internal_loader_ClassLoaders_AppClassLoader_klass() || + class_loader() == _java_system_loader); +} + +// Returns true if the passed class loader is the platform class loader. bool SystemDictionary::is_platform_class_loader(Handle class_loader) { if (class_loader.is_null()) { return false; diff --git a/hotspot/src/share/vm/classfile/systemDictionary.hpp b/hotspot/src/share/vm/classfile/systemDictionary.hpp index 3626d881736..76a0b0772e9 100644 --- a/hotspot/src/share/vm/classfile/systemDictionary.hpp +++ b/hotspot/src/share/vm/classfile/systemDictionary.hpp @@ -660,6 +660,7 @@ public: static instanceKlassHandle load_shared_class(Symbol* class_name, Handle class_loader, TRAPS); + static bool is_system_class_loader(Handle class_loader); static bool is_platform_class_loader(Handle class_loader); protected: diff --git a/hotspot/test/runtime/modules/ModuleStress/CustomSystemClassLoader.java b/hotspot/test/runtime/modules/ModuleStress/CustomSystemClassLoader.java new file mode 100644 index 00000000000..dca359f6458 --- /dev/null +++ b/hotspot/test/runtime/modules/ModuleStress/CustomSystemClassLoader.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * A custom system ClassLoader to define the module "m2" to during iterations of + * differing test runs within the test ModuleStress.java + */ +public class CustomSystemClassLoader extends ClassLoader { + public CustomSystemClassLoader() { + super(); + } + public CustomSystemClassLoader(ClassLoader parent) { + super(parent); + } +} diff --git a/hotspot/test/runtime/modules/ModuleStress/ModuleNonBuiltinCLMain.java b/hotspot/test/runtime/modules/ModuleStress/ModuleNonBuiltinCLMain.java new file mode 100644 index 00000000000..a8390f0fd5d --- /dev/null +++ b/hotspot/test/runtime/modules/ModuleStress/ModuleNonBuiltinCLMain.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import static jdk.test.lib.Asserts.*; + +import java.lang.reflect.Layer; +import java.lang.module.Configuration; +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleFinder; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +// +// ClassLoader1 --> defines m1 --> packages p1 +// ClassLoader2 --> defines m2 --> packages p2 +// Java System Class Loader --> defines m3 --> packages p3 +// +// m1 can read m2 +// package p2 in m2 is exported to m1 and m3 +// +// class p1.c1 defined in m1 tries to access p2.c2 defined in m2 +// Access allowed since m1 can read m2 and package p2 is exported to m1. +// +public class ModuleNonBuiltinCLMain { + + // Create a Layer over the boot layer. + // Define modules within this layer to test access between + // publically defined classes within packages of those modules. + public void createLayerOnBoot() throws Throwable { + + // Define module: m1 + // Can read: java.base, m2 + // Packages: p1 + // Packages exported: p1 is exported to unqualifiedly + ModuleDescriptor descriptor_m1 = + new ModuleDescriptor.Builder("m1") + .requires("java.base") + .requires("m2") + .exports("p1") + .build(); + + // Define module: m2 + // Can read: java.base, m3 + // Packages: p2 + // Packages exported: package p2 is exported to m1 and m3 + Set targets = new HashSet<>(); + targets.add("m1"); + targets.add("m3"); + ModuleDescriptor descriptor_m2 = + new ModuleDescriptor.Builder("m2") + .requires("java.base") + .requires("m3") + .exports("p2", targets) + .build(); + + // Define module: m3 + // Can read: java.base + // Packages: p3 + // Packages exported: none + ModuleDescriptor descriptor_m3 = + new ModuleDescriptor.Builder("m3") + .requires("java.base") + .build(); + + // Set up a ModuleFinder containing all modules for this layer. + ModuleFinder finder = ModuleLibrary.of(descriptor_m1, descriptor_m2, descriptor_m3); + + // Resolves "m1" + Configuration cf = Layer.boot() + .configuration() + .resolveRequires(finder, ModuleFinder.of(), Set.of("m1")); + + // map each module to differing user defined class loaders for this test + Map map = new HashMap<>(); + Loader1 cl1 = new Loader1(); + Loader2 cl2 = new Loader2(); + ClassLoader cl3 = ClassLoader.getSystemClassLoader(); + map.put("m1", cl1); + map.put("m2", cl2); + map.put("m3", cl3); + + // Create Layer that contains m1 & m2 + Layer layer = Layer.boot().defineModules(cf, map::get); + assertTrue(layer.findLoader("m1") == cl1); + assertTrue(layer.findLoader("m2") == cl2); + assertTrue(layer.findLoader("m3") == cl3); + assertTrue(layer.findLoader("java.base") == null); + + // now use the same loader to load class p1.c1 + Class p1_c1_class = cl1.loadClass("p1.c1"); + try { + p1_c1_class.newInstance(); + } catch (IllegalAccessError e) { + throw new RuntimeException("Test Failed, an IAE should not be thrown since p2 is exported qualifiedly to m1"); + } + } + + public static void main(String args[]) throws Throwable { + ModuleNonBuiltinCLMain test = new ModuleNonBuiltinCLMain(); + test.createLayerOnBoot(); + } + + static class Loader1 extends ClassLoader { } + static class Loader2 extends ClassLoader { } +} diff --git a/hotspot/test/runtime/modules/ModuleStress/ModuleSameCLMain.java b/hotspot/test/runtime/modules/ModuleStress/ModuleSameCLMain.java new file mode 100644 index 00000000000..b14a9a9b060 --- /dev/null +++ b/hotspot/test/runtime/modules/ModuleStress/ModuleSameCLMain.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import static jdk.test.lib.Asserts.*; + +import java.lang.reflect.Layer; +import java.lang.module.Configuration; +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleFinder; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +// +// ClassLoader1 --> defines m1 --> packages p1 +// ClassLoader1 --> defines m2 --> packages p2 +// +// m1 can read m2 +// package p2 in m2 is exported to m1 +// +// class p1.c1 defined in m1 tries to access p2.c2 defined in m2 +// Access allowed since m1 can read m2 and package p2 is exported to m1. +// +public class ModuleSameCLMain { + + // Create a Layer over the boot layer. + // Define modules within this layer to test access between + // publically defined classes within packages of those modules. + public void createLayerOnBoot() throws Throwable { + + // Define module: m1 + // Can read: java.base, m2 + // Packages: p1 + // Packages exported: p1 is exported to unqualifiedly + ModuleDescriptor descriptor_m1 = + new ModuleDescriptor.Builder("m1") + .requires("java.base") + .requires("m2") + .exports("p1") + .build(); + + // Define module: m2 + // Can read: java.base + // Packages: p2 + // Packages exported: package p2 is exported to m1 + ModuleDescriptor descriptor_m2 = + new ModuleDescriptor.Builder("m2") + .requires("java.base") + .exports("p2", "m1") + .build(); + + // Set up a ModuleFinder containing all modules for this layer. + ModuleFinder finder = ModuleLibrary.of(descriptor_m1, descriptor_m2); + + // Resolves "m1" + Configuration cf = Layer.boot() + .configuration() + .resolveRequires(finder, ModuleFinder.of(), Set.of("m1")); + + // map each module to the same class loader for this test + Map map = new HashMap<>(); + Loader1 cl1 = new Loader1(); + map.put("m1", cl1); + map.put("m2", cl1); + + // Create Layer that contains m1 & m2 + Layer layer = Layer.boot().defineModules(cf, map::get); + assertTrue(layer.findLoader("m1") == cl1); + assertTrue(layer.findLoader("m2") == cl1); + assertTrue(layer.findLoader("java.base") == null); + + // now use the same loader to load class p1.c1 + Class p1_c1_class = cl1.loadClass("p1.c1"); + try { + p1_c1_class.newInstance(); + } catch (IllegalAccessError e) { + throw new RuntimeException("Test Failed, an IAE should not be thrown since p2 is exported qualifiedly to m1"); + } + } + + public static void main(String args[]) throws Throwable { + ModuleSameCLMain test = new ModuleSameCLMain(); + test.createLayerOnBoot(); + } + + static class Loader1 extends ClassLoader { } +} diff --git a/hotspot/test/runtime/modules/ModuleStress/ModuleStress.java b/hotspot/test/runtime/modules/ModuleStress/ModuleStress.java new file mode 100644 index 00000000000..d7791e9b43a --- /dev/null +++ b/hotspot/test/runtime/modules/ModuleStress/ModuleStress.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8159262 + * @summary Test differing scenarios where a module's readability list and a package's exportability list should be walked + * @modules java.base/jdk.internal.misc + * @library /testlibrary /test/lib + * @compile ../AccessCheck/ModuleLibrary.java + * @compile ModuleSameCLMain.java + * @compile ModuleNonBuiltinCLMain.java + * @compile CustomSystemClassLoader.java + * @build ModuleStress + * @run main/othervm ModuleStress + */ + +import jdk.test.lib.*; +import java.io.File; + +public class ModuleStress { + + public static void main(String[] args) throws Exception { + + // Test #1: java -version + // All modules' readability lists and packages' exportability + // lists should contain only modules defined to the 3 builtin + // loaders (boot, application, platform). Thus there is + // not a need to walk those lists at a GC safepoint since + // those loaders never die. + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-Xbootclasspath/a:.", + "-Xlog:modules=trace", + "-version"); + + OutputAnalyzer oa = new OutputAnalyzer(pb.start()); + oa.shouldNotContain("must be walked") + .shouldNotContain("being walked") + .shouldHaveExitValue(0); + + // Next 2 tests involve the use of class p1.c1 and p2.c2 + String source1 = "package p1;" + + "import p2.c2;" + + "public class c1 {" + + " public c1() {" + + " p2.c2 c2_obj = new p2.c2();" + + " c2_obj.method2();" + + " }" + + "}"; + + String source2 = "package p2;" + + "public class c2 {" + + " public void method2() { }" + + "}"; + + ClassFileInstaller.writeClassToDisk("p2/c2", + InMemoryJavaCompiler.compile("p2.c2", source2), System.getProperty("test.classes")); + + ClassFileInstaller.writeClassToDisk("p1/c1", + InMemoryJavaCompiler.compile("p1.c1", source1), System.getProperty("test.classes")); + + // Test #2: Load two modules defined to the same customer class loader. + // m1's module readability list and package p2's exportability should + // not be walked at a GC safepoint since both modules are defined to + // the same loader and thus have the exact same life cycle. + pb = ProcessTools.createJavaProcessBuilder( + "-Xbootclasspath/a:.", + "-Xlog:modules=trace", + "ModuleSameCLMain"); + + oa = new OutputAnalyzer(pb.start()); + oa.shouldNotContain("must be walked") + .shouldNotContain("being walked") + .shouldHaveExitValue(0); + + // Test #3: Load two modules in differing custom class loaders. + // m1's module readability list and package p2's exportability list must + // be walked at a GC safepoint since both modules are defined to non-builtin + // class loaders which could die and thus be unloaded. + pb = ProcessTools.createJavaProcessBuilder( + "-Xbootclasspath/a:.", + "-Xlog:modules=trace", + "ModuleNonBuiltinCLMain"); + + oa = new OutputAnalyzer(pb.start()); + oa.shouldContain("module m1 reads list must be walked") + .shouldContain("package p2 defined in module m2, exports list must be walked") + .shouldNotContain("module m2 reads list must be walked") + .shouldHaveExitValue(0); + + // Test #4: Load two modules in differing custom class loaders, + // of which one has been designated as the custom system class loader + // via -Djava.system.class.loader=CustomSystemClassLoader. Since + // m3 is defined to the system class loader, m2's module readability + // list does not have to be walked at a GC safepoint, but package p2's + // exportability list does. + pb = ProcessTools.createJavaProcessBuilder( + "-Djava.system.class.loader=CustomSystemClassLoader", + "-Xbootclasspath/a:.", + "-Xlog:modules=trace", + "ModuleNonBuiltinCLMain"); + + oa = new OutputAnalyzer(pb.start()); + oa.shouldContain("package p2 defined in module m2, exports list must be walked") + .shouldNotContain("module m2 reads list must be walked") + .shouldHaveExitValue(0); + + } +} diff --git a/hotspot/test/runtime/modules/ModuleStress/ModuleStressGC.java b/hotspot/test/runtime/modules/ModuleStress/ModuleStressGC.java new file mode 100644 index 00000000000..1ebb198ba5c --- /dev/null +++ b/hotspot/test/runtime/modules/ModuleStress/ModuleStressGC.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8159262 + * @summary layers over the boot layer are repeatedly created, during this iteration, GCs are forced to verify correct walk of module and package lists. + * @modules java.base/jdk.internal.misc + * @library /testlibrary /test/lib + * @compile ../CompilerUtils.java + * @build ModuleStressGC + * @run main/othervm ModuleStressGC + */ + +import java.nio.file.Path; +import java.nio.file.Paths; +import jdk.test.lib.*; + +public class ModuleStressGC { + + private static final String TEST_SRC = System.getProperty("test.src"); + private static final String TEST_CLASSES = System.getProperty("test.classes"); + + private static final Path SRC_DIR = Paths.get(TEST_SRC, "src"); + private static final Path MODS_DIR = Paths.get(TEST_CLASSES, "mods"); + + /** + * Compile two module definitions used by the test, jdk.test and jdk.translet. + */ + public static void main(String[] args) throws Exception { + + boolean compiled; + // Compile module jdk.test declaration + compiled = CompilerUtils.compile( + SRC_DIR.resolve("jdk.test"), + MODS_DIR.resolve("jdk.test")); + if (!compiled) { + throw new RuntimeException("Test failed to compile module jdk.test"); + } + + // Compile module jdk.translet declaration + compiled = CompilerUtils.compile( + SRC_DIR.resolve("jdk.translet"), + MODS_DIR.resolve("jdk.translet"), + "-XaddExports:jdk.test/test=jdk.translet", + "-mp", MODS_DIR.toString()); + if (!compiled) { + throw new RuntimeException("Test failed to compile module jdk.translet"); + } + + // Sanity check that the test, jdk.test/test/MainGC.java, + // correctly walks module jdk.test's reads list and package + // test's, defined to module jdk.translet, export list at + // GC safepoints. + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-Xlog:modules=trace", + "-mp", MODS_DIR.toString(), + "-m", "jdk.test/test.MainGC"); + OutputAnalyzer oa = new OutputAnalyzer(pb.start()); + oa.shouldContain("package test defined in module jdk.test, exports list being walked") + .shouldContain("module jdk.test reads list being walked") + .shouldHaveExitValue(0); + } +} diff --git a/hotspot/test/runtime/modules/ModuleStress/src/jdk.test/test/MainGC.java b/hotspot/test/runtime/modules/ModuleStress/src/jdk.test/test/MainGC.java new file mode 100644 index 00000000000..25c121d3625 --- /dev/null +++ b/hotspot/test/runtime/modules/ModuleStress/src/jdk.test/test/MainGC.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package test; + +import java.lang.module.Configuration; +import java.lang.module.ModuleFinder; +import java.lang.reflect.Layer; +import java.lang.reflect.Method; +import java.lang.reflect.Module; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.*; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +public class MainGC { + + private static final Path MODS_DIR = Paths.get(System.getProperty("jdk.module.path")); + static final String MODULE_NAME = "jdk.translet"; + + public static void main(String[] args) throws Exception { + + ModuleFinder finder = ModuleFinder.of(MODS_DIR); + Layer layerBoot = Layer.boot(); + + Configuration cf = layerBoot + .configuration() + .resolveRequires(ModuleFinder.of(), finder, Set.of(MODULE_NAME)); + + Module testModule = MainGC.class.getModule(); + ClassLoader scl = ClassLoader.getSystemClassLoader(); + + // Create an unique module/class loader in a layer above the boot layer. + // Export this module to the jdk.test/test package. + // Add a read edge from module jdk.test to this module. + Callable task = new Callable() { + @Override + public Void call() throws Exception { + Layer layer = Layer.boot().defineModulesWithOneLoader(cf, scl); + Module transletModule = layer.findModule(MODULE_NAME).get(); + testModule.addExports("test", transletModule); + testModule.addReads(transletModule); + Class c = layer.findLoader(MODULE_NAME).loadClass("translet.MainGC"); + Method method = c.getDeclaredMethod("go"); + method.invoke(null); + return null; + } + }; + + List> results = new ArrayList<>(); + + // Repeatedly create the layer above stressing the exportation of + // package jdk.test/test to several different modules. + ExecutorService pool = Executors.newFixedThreadPool(Math.min(100, Runtime.getRuntime().availableProcessors()*10)); + try { + for (int i = 0; i < 10000; i++) { + results.add(pool.submit(task)); + // At specified intervals, force a GC. This provides an + // opportunity to verify that both the module jdk.test's reads + // and the package test's, which is defined to jdk.test, exports + // lists are being walked. + if (i == 3000 || i == 6000 || i == 9000) { + System.gc(); + } + } + } finally { + pool.shutdown(); + } + + int passed = 0; + int failed = 0; + + // The failed state should be 0, the created modules in layers above the + // boot layer should be allowed access to the contents of the jdk.test/test + // package since that package was exported to the transletModule above. + for (Future result : results) { + try { + result.get(); + passed++; + } catch (Throwable x) { + x.printStackTrace(); + failed++; + } + } + + System.out.println("passed: " + passed); + System.out.println("failed: " + failed); + } + + public static void callback() { } +} diff --git a/hotspot/test/runtime/modules/ModuleStress/src/jdk.translet/translet/MainGC.java b/hotspot/test/runtime/modules/ModuleStress/src/jdk.translet/translet/MainGC.java new file mode 100644 index 00000000000..b5e7b7790d6 --- /dev/null +++ b/hotspot/test/runtime/modules/ModuleStress/src/jdk.translet/translet/MainGC.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package translet; + +public class MainGC { + public static void go() { + test.MainGC.callback(); + } +} From 69a42dc15a5331b00e98a9e8757ab0dd047c90f0 Mon Sep 17 00:00:00 2001 From: Kirill Zhaldybin Date: Wed, 29 Jun 2016 17:01:55 +0300 Subject: [PATCH 03/44] 8132715: Add tests which check that no allocations allowed in any of humongous regions Reviewed-by: dfazunen, tschatzl --- .../TestNoAllocationsInHRegions.java | 234 ++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100644 hotspot/test/gc/g1/humongousObjects/TestNoAllocationsInHRegions.java diff --git a/hotspot/test/gc/g1/humongousObjects/TestNoAllocationsInHRegions.java b/hotspot/test/gc/g1/humongousObjects/TestNoAllocationsInHRegions.java new file mode 100644 index 00000000000..bbd15683324 --- /dev/null +++ b/hotspot/test/gc/g1/humongousObjects/TestNoAllocationsInHRegions.java @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +package gc.g1.humongousObjects; + +import jdk.test.lib.Utils; +import sun.hotspot.WhiteBox; + +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; + +/** + * @test TestNoAllocationsInHRegions + * @summary Checks that no additional allocations are made in humongous regions + * @requires vm.gc.G1 + * @library /testlibrary /test/lib / + * @modules java.management java.base/jdk.internal.misc + * @build sun.hotspot.WhiteBox + * gc.testlibrary.Helpers + * gc.g1.humongousObjects.TestNoAllocationsInHRegions + * + * @run driver ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * + * @run main/othervm -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:G1HeapRegionSize=1M -Xms200m -Xmx200m -XX:MaxTenuringThreshold=0 + * -Xlog:gc=trace:file=TestNoAllocationsInHRegions10.log + * gc.g1.humongousObjects.TestNoAllocationsInHRegions 30 10 + * + * @run main/othervm -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:G1HeapRegionSize=1M -Xms200m -Xmx200m -XX:MaxTenuringThreshold=0 + * -Xlog:gc=trace:file=TestNoAllocationsInHRegions50.log + * gc.g1.humongousObjects.TestNoAllocationsInHRegions 30 50 + * + * @run main/othervm -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:G1HeapRegionSize=1M -Xms200m -Xmx200m -XX:MaxTenuringThreshold=0 + * -Xlog:gc=trace:file=TestNoAllocationsInHRegions70.log + * gc.g1.humongousObjects.TestNoAllocationsInHRegions 30 70 + */ +public class TestNoAllocationsInHRegions { + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + private static final Random RND = Utils.getRandomInstance(); + private static final int G1_REGION_SIZE = WB.g1RegionSize(); + private static final int[] HUMONGOUS_SIZES = {G1_REGION_SIZE / 2, G1_REGION_SIZE + 1, G1_REGION_SIZE * 2 + 1}; + private static final int ALLOC_THREAD_COUNT = 5; + + // We fill specified part of heap with humongous objects - we need public static to prevent escape analysis to + // collect this field + public static LinkedList humongousAllocations = new LinkedList<>(); + + private static volatile boolean shouldStop = false; + private static volatile Error error = null; + + static class Allocator implements Runnable { + + private final List liveObjects = new LinkedList<>(); + private int usedMemory = 0; + public final Runnable[] actions; + + /** + * Maximum size of simple allocation + */ + private static final int MAX_ALLOCATION_SIZE = (int) (G1_REGION_SIZE / 2 * 0.9); + + /** + * Maximum size of dead (i.e. one which is made unreachable right after allocation) object + */ + private static final int DEAD_OBJECT_MAX_SIZE = G1_REGION_SIZE / 10; + + public Allocator(int maxAllocationMemory) { + + actions = new Runnable[]{ + // Allocation + () -> { + if (maxAllocationMemory - usedMemory != 0) { + int arraySize = RND.nextInt(Math.min(maxAllocationMemory - usedMemory, + MAX_ALLOCATION_SIZE)); + + if (arraySize != 0) { + byte[] allocation = new byte[arraySize]; + liveObjects.add(allocation); + usedMemory += arraySize; + + // Sanity check + if (WB.g1IsHumongous(allocation)) { + String errorMessage = String.format("Test Bug: Byte array of size" + + " %d is expected to be non-humongous but it is humongous", + allocation.length); + + System.out.println(errorMessage); + error = new Error(errorMessage); + shouldStop = true; + } + + // Test check + if (WB.g1BelongsToHumongousRegion(WB.getObjectAddress(allocation))) { + String errorMessage = String.format("Non-humongous allocation of byte array of " + + "length %d and size %d with address %d was made in Humongous Region", + allocation.length, WB.getObjectSize(allocation), + WB.getObjectAddress(allocation)); + + System.out.println(errorMessage); + error = new Error(errorMessage); + shouldStop = true; + } + } + } + }, + + // Deallocation + () -> { + if (liveObjects.size() != 0) { + int elementNum = RND.nextInt(liveObjects.size()); + int shouldFree = liveObjects.get(elementNum).length; + liveObjects.remove(elementNum); + usedMemory -= shouldFree; + } + }, + + // Dead object allocation + () -> { + int size = RND.nextInt(DEAD_OBJECT_MAX_SIZE); + byte[] deadObject = new byte[size]; + }, + + // Check + () -> { + List wrongHumongousAllocations = liveObjects.stream() + .filter(WB::g1IsHumongous) + .collect(Collectors.toList()); + + if (wrongHumongousAllocations.size() > 0) { + wrongHumongousAllocations.stream().forEach(a -> + System.out.format("Non-humongous allocation of byte array of length %d and" + + " size %d with address %d was made in Humongous Region", + a.length, WB.getObjectSize(a), WB.getObjectAddress(a))); + error = new Error("Some non-humongous allocations were made to humongous region"); + shouldStop = true; + } + } + }; + } + + @Override + public void run() { + while (!shouldStop) { + actions[RND.nextInt(actions.length)].run(); + Thread.yield(); + } + } + } + + public static void main(String[] args) { + if (args.length != 2) { + throw new Error("Test Bug: Expected duration (in seconds) and percent of allocated regions were not " + + "provided as command line argument"); + } + + // test duration + long duration = Integer.parseInt(args[0]) * 1000L; + // part of heap preallocated with humongous objects (in percents) + int percentOfAllocatedHeap = Integer.parseInt(args[1]); + + long startTime = System.currentTimeMillis(); + + long initialFreeRegionsCount = WB.g1NumFreeRegions(); + int regionsToAllocate = (int) ((double) initialFreeRegionsCount / 100.0 * percentOfAllocatedHeap); + long freeRegionLeft = initialFreeRegionsCount - regionsToAllocate; + + System.out.println("Regions to allocate: " + regionsToAllocate + "; regions to left free: " + freeRegionLeft); + + int maxMemoryPerAllocThread = (int) ((Runtime.getRuntime().freeMemory() / 100.0 + * (100 - percentOfAllocatedHeap)) / ALLOC_THREAD_COUNT * 0.5); + + System.out.println("Using " + maxMemoryPerAllocThread / 1024 + "KB for each of " + ALLOC_THREAD_COUNT + + " allocation threads"); + + while (WB.g1NumFreeRegions() > freeRegionLeft) { + try { + humongousAllocations.add(new byte[HUMONGOUS_SIZES[RND.nextInt(HUMONGOUS_SIZES.length)]]); + } catch (OutOfMemoryError oom) { + //We got OOM trying to fill heap with humongous objects + //It probably means that heap is fragmented which is strange since the test logic should avoid it + System.out.println("Warning: OOM while allocating humongous objects - it likely means " + + "that heap is fragmented"); + break; + } + } + + System.out.println("Initial free regions " + initialFreeRegionsCount + "; Free regions left " + + WB.g1NumFreeRegions()); + + LinkedList threads = new LinkedList<>(); + + for (int i = 0; i < ALLOC_THREAD_COUNT; i++) { + threads.add(new Thread(new Allocator(maxMemoryPerAllocThread))); + } + + threads.stream().forEach(Thread::start); + + while ((System.currentTimeMillis() - startTime < duration) && error == null) { + Thread.yield(); + } + + shouldStop = true; + System.out.println("Finished test"); + if (error != null) { + throw error; + } + } +} From ed9f8153362674b0af9c44c25039a07564b5b469 Mon Sep 17 00:00:00 2001 From: Kirill Zhaldybin Date: Wed, 29 Jun 2016 18:40:28 +0300 Subject: [PATCH 04/44] 8132711: Add tests which check that Humongous objects behave as expected after Mixed GC Reviewed-by: tschatzl, dfazunen --- .../humongousObjects/objectGraphTest/GC.java | 35 +++++++++++++++++++ .../humongousObjects/objectGraphTest/README | 3 ++ .../TestObjectGraphAfterGC.java | 6 ++++ 3 files changed, 44 insertions(+) diff --git a/hotspot/test/gc/g1/humongousObjects/objectGraphTest/GC.java b/hotspot/test/gc/g1/humongousObjects/objectGraphTest/GC.java index 8da879dec5e..be721f54fd5 100644 --- a/hotspot/test/gc/g1/humongousObjects/objectGraphTest/GC.java +++ b/hotspot/test/gc/g1/humongousObjects/objectGraphTest/GC.java @@ -138,6 +138,41 @@ public enum GC { } }, + MIXED_GC { + @Override + public Runnable get() { + return () -> { + WHITE_BOX.youngGC(); + Helpers.waitTillCMCFinished(WHITE_BOX, 0); + WHITE_BOX.youngGC(); + Helpers.waitTillCMCFinished(WHITE_BOX, 0); + + WHITE_BOX.g1StartConcMarkCycle(); + Helpers.waitTillCMCFinished(WHITE_BOX, 0); + + WHITE_BOX.youngGC(); + Helpers.waitTillCMCFinished(WHITE_BOX, 0); + // Provoking Mixed GC + WHITE_BOX.youngGC();// second evacuation pause will be mixed + Helpers.waitTillCMCFinished(WHITE_BOX, 0); + }; + } + + public Consumer> getChecker() { + return getCheckerImpl(true, false, true, false); + } + + @Override + public List shouldContain() { + return Arrays.asList(GCTokens.WB_INITIATED_CMC); + } + + @Override + public List shouldNotContain() { + return Arrays.asList(GCTokens.YOUNG_GC); + } + }, + FULL_GC_MEMORY_PRESSURE { @Override public Runnable get() { diff --git a/hotspot/test/gc/g1/humongousObjects/objectGraphTest/README b/hotspot/test/gc/g1/humongousObjects/objectGraphTest/README index 4ec9f0d3792..5366fc876be 100644 --- a/hotspot/test/gc/g1/humongousObjects/objectGraphTest/README +++ b/hotspot/test/gc/g1/humongousObjects/objectGraphTest/README @@ -38,6 +38,9 @@ The test checks that after different type of GC unreachable objects behave as ex non-humongous and humongous objects are not collected since we make 2 Young GC to promote all weak references to Old Gen. +6. Mixed GC - weakly referenced non-humongous and humongous objects are collected, softly referenced non-humongous and + humongous objects are not collected. + The test gets gc type as a command line argument. Then the test allocates object graph in heap (currently testing scenarios are pre-generated and stored in TestcaseData.getPregeneratedTestcases()) with TestObjectGraphAfterGC::allocateObjectGraph. diff --git a/hotspot/test/gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java b/hotspot/test/gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java index c7a4f2ff081..07f7a3bc89d 100644 --- a/hotspot/test/gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java +++ b/hotspot/test/gc/g1/humongousObjects/objectGraphTest/TestObjectGraphAfterGC.java @@ -66,6 +66,12 @@ import java.util.stream.Collectors; * sun.hotspot.WhiteBox$WhiteBoxPermission * * @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. + * -XX:+UnlockExperimentalVMOptions -XX:MaxGCPauseMillis=30000 -XX:G1MixedGCLiveThresholdPercent=100 -XX:G1HeapWastePercent=0 + * -XX:G1HeapRegionSize=1M -Xlog:gc=info:file=TestObjectGraphAfterGC_MIXED_GC.gc.log -XX:MaxTenuringThreshold=1 + * -XX:G1MixedGCCountTarget=1 -XX:G1OldCSetRegionThresholdPercent=100 -XX:SurvivorRatio=1 -XX:InitiatingHeapOccupancyPercent=0 + * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC MIXED_GC + * + * @run main/othervm -Xms200M -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. * -XX:G1HeapRegionSize=1M -Xlog:gc*=debug:file=TestObjectGraphAfterGC_YOUNG_GC.gc.log * gc.g1.humongousObjects.objectGraphTest.TestObjectGraphAfterGC YOUNG_GC * From fd1c7ff505159c24a3cc75035dbd59d0ddaa7a15 Mon Sep 17 00:00:00 2001 From: Coleen Phillimore Date: Wed, 29 Jun 2016 20:16:05 -0400 Subject: [PATCH 05/44] 8160551: assert(c == Bytecodes::_putfield) failed: must be putfield Illegal bytecodes which are detected later hit this assert first. Reviewed-by: jrose --- hotspot/src/share/vm/interpreter/rewriter.cpp | 7 +-- hotspot/test/runtime/Final/Bad.jasm | 55 +++++++++++++++++++ hotspot/test/runtime/Final/PutfieldError.java | 42 ++++++++++++++ 3 files changed, 100 insertions(+), 4 deletions(-) create mode 100644 hotspot/test/runtime/Final/Bad.jasm create mode 100644 hotspot/test/runtime/Final/PutfieldError.java diff --git a/hotspot/src/share/vm/interpreter/rewriter.cpp b/hotspot/src/share/vm/interpreter/rewriter.cpp index 52832e781ab..dfc110b8222 100644 --- a/hotspot/src/share/vm/interpreter/rewriter.cpp +++ b/hotspot/src/share/vm/interpreter/rewriter.cpp @@ -419,21 +419,20 @@ void Rewriter::scan_method(Method* method, bool reverse, bool* invokespecial_err InstanceKlass* klass = method->method_holder(); u2 bc_index = Bytes::get_Java_u2(bcp + prefix_length + 1); constantPoolHandle cp(method->constants()); - Symbol* field_name = cp->name_ref_at(bc_index); - Symbol* field_sig = cp->signature_ref_at(bc_index); Symbol* ref_class_name = cp->klass_name_at(cp->klass_ref_index_at(bc_index)); if (klass->name() == ref_class_name) { + Symbol* field_name = cp->name_ref_at(bc_index); + Symbol* field_sig = cp->signature_ref_at(bc_index); + fieldDescriptor fd; klass->find_field(field_name, field_sig, &fd); if (fd.access_flags().is_final()) { if (fd.access_flags().is_static()) { - assert(c == Bytecodes::_putstatic, "must be putstatic"); if (!method->is_static_initializer()) { fd.set_has_initialized_final_update(true); } } else { - assert(c == Bytecodes::_putfield, "must be putfield"); if (!method->is_object_initializer()) { fd.set_has_initialized_final_update(true); } diff --git a/hotspot/test/runtime/Final/Bad.jasm b/hotspot/test/runtime/Final/Bad.jasm new file mode 100644 index 00000000000..607e2bcc078 --- /dev/null +++ b/hotspot/test/runtime/Final/Bad.jasm @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* Recoded in jasm to provoke an ICCE assigning a non-static final field with putstatic. +class Bad { + public static final int i; //rewritten + //rewritten to: public final int i; + static { i = 5; } // putstatic instruction +} +*/ + +super class Bad + version 53:0 +{ + +// Remove 'static' keyword +public final Field i:I; + +Method "":"()V" + stack 1 locals 1 +{ + aload_0; + invokespecial Method java/lang/Object."":"()V"; + return; +} + +static Method "":"()V" + stack 1 locals 0 +{ + iconst_5; + putstatic Field i:"I"; + return; +} + +} // end Class Bad diff --git a/hotspot/test/runtime/Final/PutfieldError.java b/hotspot/test/runtime/Final/PutfieldError.java new file mode 100644 index 00000000000..9669a1b1ade --- /dev/null +++ b/hotspot/test/runtime/Final/PutfieldError.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test PutfieldError + * @bug 8160551 + * @summary Throw ICCE rather than crashing for nonstatic final field in static initializer + * @compile Bad.jasm + * @run main PutfieldError + */ + +public class PutfieldError { + public static void main(java.lang.String[] unused) { + try { + Bad b = new Bad(); + System.out.println("Bad.i = " + 5); + throw new RuntimeException("ICCE NOT thrown as expected"); + } catch (IncompatibleClassChangeError icce) { + System.out.println("ICCE thrown as expected"); + } + } +} From 992b6c464a9655438a808cf2e1979e04356123be Mon Sep 17 00:00:00 2001 From: Marcus Larsson Date: Wed, 29 Jun 2016 16:11:50 +0200 Subject: [PATCH 06/44] 8159695: Arguments::atojulong() fails to detect overflows Reviewed-by: dholmes, dsamersoff --- hotspot/src/share/vm/runtime/arguments.cpp | 33 +++++---- .../test/native/runtime/test_arguments.cpp | 71 +++++++++++++++++++ 2 files changed, 87 insertions(+), 17 deletions(-) create mode 100644 hotspot/test/native/runtime/test_arguments.cpp diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp index 4b29b2830ca..a9e944e4f7f 100644 --- a/hotspot/src/share/vm/runtime/arguments.cpp +++ b/hotspot/src/share/vm/runtime/arguments.cpp @@ -584,27 +584,26 @@ static bool verify_special_jvm_flags() { // Parses a size specification string. bool Arguments::atojulong(const char *s, julong* result) { julong n = 0; - int args_read = 0; - bool is_hex = false; - // Skip leading 0[xX] for hexadecimal - if (*s =='0' && (*(s+1) == 'x' || *(s+1) == 'X')) { - s += 2; - is_hex = true; - args_read = sscanf(s, JULONG_FORMAT_X, &n); - } else { - args_read = sscanf(s, JULONG_FORMAT, &n); - } - if (args_read != 1) { + + // First char must be a digit. Don't allow negative numbers or leading spaces. + if (!isdigit(*s)) { return false; } - while (*s != '\0' && (isdigit(*s) || (is_hex && isxdigit(*s)))) { - s++; - } - // 4705540: illegal if more characters are found after the first non-digit - if (strlen(s) > 1) { + + bool is_hex = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')); + char* remainder; + errno = 0; + n = strtoull(s, &remainder, (is_hex ? 16 : 10)); + if (errno != 0) { return false; } - switch (*s) { + + // Fail if no number was read at all or if the remainder contains more than a single non-digit character. + if (remainder == s || strlen(remainder) > 1) { + return false; + } + + switch (*remainder) { case 'T': case 't': *result = n * G * K; // Check for overflow. diff --git a/hotspot/test/native/runtime/test_arguments.cpp b/hotspot/test/native/runtime/test_arguments.cpp new file mode 100644 index 00000000000..f4327f3498d --- /dev/null +++ b/hotspot/test/native/runtime/test_arguments.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +#include "precompiled.hpp" +#include "runtime/arguments.hpp" +#include "unittest.hpp" +#include "utilities/globalDefinitions.hpp" + +TEST(arguments, atojulong) { + char ullong_max[32]; + int ret = jio_snprintf(ullong_max, sizeof(ullong_max), JULONG_FORMAT, ULLONG_MAX); + ASSERT_NE(-1, ret); + + julong value; + const char* invalid_strings[] = { + "", "-1", "-100", " 1", "2 ", "3 2", "1.0", + "0x4.5", "0x", "0x0x1" "0.001", "4e10", "e" + "K", "M", "G", "1MB", "1KM", "AA", "0B", + "18446744073709551615K", "17179869184G", + "999999999999999999999999999999" + }; + for (uint i = 0; i < ARRAY_SIZE(invalid_strings); i++) { + ASSERT_FALSE(Arguments::atojulong(invalid_strings[i], &value)) + << "Invalid string '" << invalid_strings[i] << "' parsed without error."; + } + + struct { + const char* str; + julong expected_value; + } valid_strings[] = { + { "0", 0 }, + { "4711", 4711 }, + { "1K", 1ULL * K }, + { "1k", 1ULL * K }, + { "2M", 2ULL * M }, + { "2m", 2ULL * M }, + { "4G", 4ULL * G }, + { "4g", 4ULL * G }, + { "0K", 0 }, + { ullong_max, ULLONG_MAX }, + { "0xcafebabe", 0xcafebabe }, + { "0XCAFEBABE", 0xcafebabe }, + { "0XCAFEbabe", 0xcafebabe }, + { "0x10K", 0x10 * K } + }; + for (uint i = 0; i < ARRAY_SIZE(valid_strings); i++) { + ASSERT_TRUE(Arguments::atojulong(valid_strings[i].str, &value)) + << "Valid string '" << valid_strings[i].str << "' did not parse."; + ASSERT_EQ(valid_strings[i].expected_value, value); + } +} From 8ea7496925d011e3333417e960c8b9285fe57e15 Mon Sep 17 00:00:00 2001 From: Yasumasa Suenaga Date: Thu, 30 Jun 2016 19:16:14 -0400 Subject: [PATCH 07/44] 8160356: invalid suffix on literal warning is occurred with GCC 6 Add whitespace separation Reviewed-by: kbarrett, kvn --- hotspot/src/share/vm/classfile/moduleEntry.cpp | 2 +- hotspot/src/share/vm/classfile/packageEntry.cpp | 4 ++-- hotspot/src/share/vm/gc/shared/preservedMarks.cpp | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hotspot/src/share/vm/classfile/moduleEntry.cpp b/hotspot/src/share/vm/classfile/moduleEntry.cpp index a05c1644b92..a8b67c54e64 100644 --- a/hotspot/src/share/vm/classfile/moduleEntry.cpp +++ b/hotspot/src/share/vm/classfile/moduleEntry.cpp @@ -413,7 +413,7 @@ void ModuleEntryTable::print(outputStream* st) { void ModuleEntry::print(outputStream* st) { ResourceMark rm; - st->print_cr("entry "PTR_FORMAT" name %s module "PTR_FORMAT" loader %s version %s location %s strict %s next "PTR_FORMAT, + st->print_cr("entry " PTR_FORMAT " name %s module " PTR_FORMAT " loader %s version %s location %s strict %s next " PTR_FORMAT, p2i(this), name() == NULL ? UNNAMED_MODULE : name()->as_C_string(), p2i(module()), diff --git a/hotspot/src/share/vm/classfile/packageEntry.cpp b/hotspot/src/share/vm/classfile/packageEntry.cpp index d224941c32d..2389cb25559 100644 --- a/hotspot/src/share/vm/classfile/packageEntry.cpp +++ b/hotspot/src/share/vm/classfile/packageEntry.cpp @@ -346,8 +346,8 @@ void PackageEntryTable::print(outputStream* st) { void PackageEntry::print(outputStream* st) { ResourceMark rm; - st->print_cr("package entry "PTR_FORMAT" name %s module %s classpath_index " - INT32_FORMAT " is_exported_unqualified %d is_exported_allUnnamed %d " "next "PTR_FORMAT, + st->print_cr("package entry " PTR_FORMAT " name %s module %s classpath_index " + INT32_FORMAT " is_exported_unqualified %d is_exported_allUnnamed %d " "next " PTR_FORMAT, p2i(this), name()->as_C_string(), (module()->is_named() ? module()->name()->as_C_string() : UNNAMED_MODULE), _classpath_index, _is_exported_unqualified, _is_exported_allUnnamed, p2i(next())); diff --git a/hotspot/src/share/vm/gc/shared/preservedMarks.cpp b/hotspot/src/share/vm/gc/shared/preservedMarks.cpp index 7a1078ebb34..2fcd86344be 100644 --- a/hotspot/src/share/vm/gc/shared/preservedMarks.cpp +++ b/hotspot/src/share/vm/gc/shared/preservedMarks.cpp @@ -48,10 +48,10 @@ void PreservedMarks::restore_and_increment(volatile size_t* const total_size_add #ifndef PRODUCT void PreservedMarks::assert_empty() { - assert(_stack.is_empty(), "stack expected to be empty, size = "SIZE_FORMAT, + assert(_stack.is_empty(), "stack expected to be empty, size = " SIZE_FORMAT, _stack.size()); assert(_stack.cache_size() == 0, - "stack expected to have no cached segments, cache size = "SIZE_FORMAT, + "stack expected to have no cached segments, cache size = " SIZE_FORMAT, _stack.cache_size()); } #endif // ndef PRODUCT From 5b2f045b846a4b542186dff4bc34b74d7679b69a Mon Sep 17 00:00:00 2001 From: Erik Joelsson Date: Fri, 1 Jul 2016 11:55:25 +0200 Subject: [PATCH 08/44] 8158629: bash >(...) construct still causes race conditions Reviewed-by: tbell --- hotspot/make/gensrc/GensrcDtrace.gmk | 3 ++- hotspot/make/lib/CompileDtracePostJvm.gmk | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/hotspot/make/gensrc/GensrcDtrace.gmk b/hotspot/make/gensrc/GensrcDtrace.gmk index 563f22112fb..126be0b3599 100644 --- a/hotspot/make/gensrc/GensrcDtrace.gmk +++ b/hotspot/make/gensrc/GensrcDtrace.gmk @@ -45,7 +45,8 @@ ifeq ($(call check-jvm-feature, dtrace), true) $(DTRACE_GENSRC_DIR)/%.h: $(DTRACE_SOURCE_DIR)/%.d $(call LogInfo, Generating dtrace header file $(@F)) $(call MakeDir, $(@D) $(DTRACE_SUPPORT_DIR)) - $(call ExecuteWithLog, $(DTRACE_SUPPORT_DIR)/$(@F).d, $(CC) -E $(DTRACE_CPP_FLAGS) $< > $(DTRACE_SUPPORT_DIR)/$(@F).d) + $(call ExecuteWithLog, $(DTRACE_SUPPORT_DIR)/$(@F).d, \ + ( $(CC) -E $(DTRACE_CPP_FLAGS) $< > $(DTRACE_SUPPORT_DIR)/$(@F).d ) ) $(call ExecuteWithLog, $@, $(DTRACE) $(DTRACE_FLAGS) -h -o $@ -s $(DTRACE_SUPPORT_DIR)/$(@F).d) # Process all .d files in DTRACE_SOURCE_DIR. They are: diff --git a/hotspot/make/lib/CompileDtracePostJvm.gmk b/hotspot/make/lib/CompileDtracePostJvm.gmk index 50a7f10d67d..45c81ad9c4e 100644 --- a/hotspot/make/lib/CompileDtracePostJvm.gmk +++ b/hotspot/make/lib/CompileDtracePostJvm.gmk @@ -68,7 +68,7 @@ ifeq ($(call check-jvm-feature, dtrace), true) $1: $$(BUILD_DTRACE_GEN_OFFSETS) $$(call LogInfo, Generating dtrace $2 file $$(@F)) $$(call MakeDir, $$(@D)) - $$(call ExecuteWithLog, $$@, $$(DTRACE_GEN_OFFSETS_TOOL) -$$(strip $2) > $$@) + $$(call ExecuteWithLog, $$@, ( $$(DTRACE_GEN_OFFSETS_TOOL) -$$(strip $2) > $$@ ) ) TARGETS += $1 endef From 7fbf406a0eca73265d4a2750e5322c67eb8fe6c2 Mon Sep 17 00:00:00 2001 From: Erik Joelsson Date: Fri, 1 Jul 2016 12:32:21 +0200 Subject: [PATCH 09/44] 8158629: bash >(...) construct still causes race conditions Reviewed-by: tbell --- make/common/MakeBase.gmk | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/make/common/MakeBase.gmk b/make/common/MakeBase.gmk index 703994958d6..15c6bc6254d 100644 --- a/make/common/MakeBase.gmk +++ b/make/common/MakeBase.gmk @@ -801,15 +801,20 @@ endif # 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 From f06ef85609fa75a73cb212de9d56b4c5c89e0104 Mon Sep 17 00:00:00 2001 From: Christian Tornqvist Date: Fri, 1 Jul 2016 07:21:33 -0400 Subject: [PATCH 10/44] 8160484: Implement Hotspot Runtime tier 2 Reviewed-by: dholmes, gtriantafill --- hotspot/test/TEST.groups | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hotspot/test/TEST.groups b/hotspot/test/TEST.groups index 262232e706c..b46499f7902 100644 --- a/hotspot/test/TEST.groups +++ b/hotspot/test/TEST.groups @@ -395,6 +395,17 @@ hotspot_jprt = \ :hotspot_fast_gc_gcold \ :hotspot_fast_runtime \ :hotspot_fast_serviceability + +hotspot_runtime_tier2 = \ + runtime/ \ + serviceability/ \ + -:hotspot_fast_runtime \ + -:hotspot_fast_serviceability \ + -:hotspot_runtime_tier2_platform_agnostic + +hotspot_runtime_tier2_platform_agnostic = \ + runtime/SelectionResolution \ + -:hotspot_fast_runtime #All tests that depends on nashorn extension. # From f97651b3560d43158f1f0196845f6844b7352144 Mon Sep 17 00:00:00 2001 From: Poonam Bajaj Date: Fri, 1 Jul 2016 12:51:29 -0700 Subject: [PATCH 11/44] 8064814: Print more helpful error message when getting OOM due to low Java Heap base when running with CompressedOops Improve OOM error message Reviewed-by: coleenp, dholmes --- hotspot/src/share/vm/utilities/vmError.cpp | 27 ++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/hotspot/src/share/vm/utilities/vmError.cpp b/hotspot/src/share/vm/utilities/vmError.cpp index d2c9cadb504..4a2ac95995b 100644 --- a/hotspot/src/share/vm/utilities/vmError.cpp +++ b/hotspot/src/share/vm/utilities/vmError.cpp @@ -205,16 +205,39 @@ void VMError::print_stack_trace(outputStream* st, JavaThread* jt, static void print_oom_reasons(outputStream* st) { st->print_cr("# Possible reasons:"); st->print_cr("# The system is out of physical RAM or swap space"); - st->print_cr("# In 32 bit mode, the process size limit was hit"); + if (UseCompressedOops) { + st->print_cr("# The process is running with CompressedOops enabled, and the Java Heap may be blocking the growth of the native heap"); + } + if (LogBytesPerWord == 2) { + st->print_cr("# In 32 bit mode, the process size limit was hit"); + } st->print_cr("# Possible solutions:"); st->print_cr("# Reduce memory load on the system"); st->print_cr("# Increase physical memory or swap space"); st->print_cr("# Check if swap backing store is full"); - st->print_cr("# Use 64 bit Java on a 64 bit OS"); + if (LogBytesPerWord == 2) { + st->print_cr("# Use 64 bit Java on a 64 bit OS"); + } st->print_cr("# Decrease Java heap size (-Xmx/-Xms)"); st->print_cr("# Decrease number of Java threads"); st->print_cr("# Decrease Java thread stack sizes (-Xss)"); st->print_cr("# Set larger code cache with -XX:ReservedCodeCacheSize="); + if (UseCompressedOops) { + switch (Universe::narrow_oop_mode()) { + case Universe::UnscaledNarrowOop: + st->print_cr("# JVM is running with Unscaled Compressed Oops mode in which the Java heap is"); + st->print_cr("# placed in the first 4GB address space. The Java Heap base address is the"); + st->print_cr("# maximum limit for the native heap growth. Please use -XX:HeapBaseMinAddress"); + st->print_cr("# to set the Java Heap base and to place the Java Heap above 4GB virtual address."); + break; + case Universe::ZeroBasedNarrowOop: + st->print_cr("# JVM is running with Zero Based Compressed Oops mode in which the Java heap is"); + st->print_cr("# placed in the first 32GB address space. The Java Heap base address is the"); + st->print_cr("# maximum limit for the native heap growth. Please use -XX:HeapBaseMinAddress"); + st->print_cr("# to set the Java Heap base and to place the Java Heap above 32GB virtual address."); + break; + } + } st->print_cr("# This output file may be truncated or incomplete."); } From 9d97255ffdcdbfa320b9bdb3d40d12e61bef48fe Mon Sep 17 00:00:00 2001 From: Michael Haupt Date: Tue, 12 Jul 2016 10:39:46 +0200 Subject: [PATCH 12/44] 8161032: GPL header incorrect - address wrong - not swapped in licensee bundles Reviewed-by: dholmes, kvn --- .../src/com/sun/hotspot/tools/compiler/UncommonTrap.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/UncommonTrap.java b/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/UncommonTrap.java index c4401c59a87..63001d35f7f 100644 --- a/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/UncommonTrap.java +++ b/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/UncommonTrap.java @@ -16,9 +16,9 @@ * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. * */ package com.sun.hotspot.tools.compiler; From 45976492091bf49432c4623b15250187df441aa1 Mon Sep 17 00:00:00 2001 From: Athijegannathan Sundararajan Date: Tue, 12 Jul 2016 21:18:13 +0530 Subject: [PATCH 13/44] 8149929: Nashorn Parser API needs to be updated for ES6 Reviewed-by: mhaupt, hannesw --- nashorn/make/project.properties | 4 +- nashorn/samples/prettyprinter.js | 4 +- .../nashorn/tools/jjs/NashornCompleter.java | 4 + .../jdk/nashorn/api/tree/CatchTree.java | 6 +- .../jdk/nashorn/api/tree/CatchTreeImpl.java | 6 +- .../api/tree/ClassDeclarationTree.java | 63 + .../api/tree/ClassDeclarationTreeImpl.java | 77 + .../nashorn/api/tree/ClassExpressionTree.java | 63 + .../api/tree/ClassExpressionTreeImpl.java | 76 + .../nashorn/api/tree/CompilationUnitTree.java | 8 + .../api/tree/CompilationUnitTreeImpl.java | 14 +- .../api/tree/DestructuringDeclTreeImpl.java | 77 + .../jdk/nashorn/api/tree/ExportEntryTree.java | 60 + .../nashorn/api/tree/ExportEntryTreeImpl.java | 109 + .../jdk/nashorn/api/tree/ForOfLoopTree.java | 62 + .../nashorn/api/tree/ForOfLoopTreeImpl.java | 69 + .../api/tree/FunctionDeclarationTree.java | 19 +- .../api/tree/FunctionDeclarationTreeImpl.java | 13 +- .../api/tree/FunctionExpressionTree.java | 32 +- .../api/tree/FunctionExpressionTreeImpl.java | 30 +- .../jdk/nashorn/api/tree/IRTranslator.java | 123 +- .../jdk/nashorn/api/tree/IdentifierTree.java | 42 + .../nashorn/api/tree/IdentifierTreeImpl.java | 32 + .../jdk/nashorn/api/tree/ImportEntryTree.java | 53 + .../nashorn/api/tree/ImportEntryTreeImpl.java | 100 + .../jdk/nashorn/api/tree/ModuleTree.java | 60 + .../jdk/nashorn/api/tree/ModuleTreeImpl.java | 99 + .../classes/jdk/nashorn/api/tree/Parser.java | 6 +- .../jdk/nashorn/api/tree/ParserImpl.java | 106 +- .../jdk/nashorn/api/tree/PropertyTree.java | 16 +- .../nashorn/api/tree/PropertyTreeImpl.java | 16 +- .../nashorn/api/tree/RegExpLiteralTree.java | 2 +- .../api/tree/SimpleTreeVisitorES5_1.java | 135 +- .../api/tree/SimpleTreeVisitorES6.java | 226 +++ .../jdk/nashorn/api/tree/SpreadTree.java | 38 + .../jdk/nashorn/api/tree/SpreadTreeImpl.java | 53 + .../nashorn/api/tree/TemplateLiteralTree.java | 48 + .../api/tree/TemplateLiteralTreeImpl.java | 56 + .../classes/jdk/nashorn/api/tree/Tree.java | 46 +- .../jdk/nashorn/api/tree/TreeImpl.java | 7 +- .../jdk/nashorn/api/tree/TreeVisitor.java | 85 +- .../jdk/nashorn/api/tree/VariableTree.java | 30 +- .../nashorn/api/tree/VariableTreeImpl.java | 22 +- .../jdk/nashorn/api/tree/YieldTree.java | 66 + .../jdk/nashorn/api/tree/YieldTreeImpl.java | 60 + .../internal/codegen/SplitIntoFunctions.java | 1 + .../jdk/nashorn/internal/ir/ClassNode.java | 14 +- .../internal/ir/ExpressionStatement.java | 31 +- .../jdk/nashorn/internal/ir/FunctionNode.java | 20 + .../nashorn/internal/ir/TemplateLiteral.java | 77 + .../internal/ir/visitor/NodeVisitor.java | 21 + .../jdk/nashorn/internal/parser/Parser.java | 186 +- .../parser/ParserContextFunctionNode.java | 20 + nashorn/test/script/basic/JDK-8075207.js | 2 +- nashorn/test/script/basic/JDK-8075448.js | 2 +- .../basic/es6/let-const-switch.js.EXPECTED | 24 +- nashorn/test/script/nosecurity/parserapi.js | 20 + .../script/nosecurity/parserapi.js.EXPECTED | 1792 ++++++++++------- .../test/script/nosecurity/parservisitor.js | 4 +- .../test/script/nosecurity/treeapi/arrow.js | 49 + .../nosecurity/treeapi/arrow.js.EXPECTED | 103 + .../script/nosecurity/treeapi/arrow_params.js | 49 + .../treeapi/arrow_params.js.EXPECTED | 411 ++++ .../nosecurity/treeapi/assignment.js.EXPECTED | 2 +- .../test/script/nosecurity/treeapi/class.js | 91 + .../nosecurity/treeapi/class.js.EXPECTED | 751 +++++++ .../test/script/nosecurity/treeapi/const.js | 45 + .../nosecurity/treeapi/const.js.EXPECTED | 20 + .../nosecurity/treeapi/defaultparams.js | 52 + .../treeapi/defaultparams.js.EXPECTED | 151 ++ .../treeapi/destructuring_assign.js | 50 + .../treeapi/destructuring_assign.js.EXPECTED | 164 ++ .../nosecurity/treeapi/destructuring_decl.js | 49 + .../treeapi/destructuring_decl.js.EXPECTED | 101 + .../treeapi/destructuring_params.js | 53 + .../treeapi/destructuring_params.js.EXPECTED | 253 +++ .../treeapi/functionCall.js.EXPECTED | 2 +- .../treeapi/functionDeclaration.js.EXPECTED | 37 +- .../treeapi/functionExpr.js.EXPECTED | 2 +- .../script/nosecurity/treeapi/generator.js | 67 + .../nosecurity/treeapi/generator.js.EXPECTED | 266 +++ nashorn/test/script/nosecurity/treeapi/let.js | 45 + .../script/nosecurity/treeapi/let.js.EXPECTED | 20 + .../treeapi/memberSelect.js.EXPECTED | 3 +- .../test/script/nosecurity/treeapi/modules.js | 43 + .../nosecurity/treeapi/modules.js.EXPECTED | 82 + .../script/nosecurity/treeapi/new.js.EXPECTED | 2 +- .../treeapi/objectLiteral.js.EXPECTED | 4 +- .../nosecurity/treeapi/property.js.EXPECTED | 2 +- .../test/script/nosecurity/treeapi/rest.js | 53 + .../nosecurity/treeapi/rest.js.EXPECTED | 134 ++ .../nosecurity/treeapi/return.js.EXPECTED | 2 +- .../test/script/nosecurity/treeapi/spread.js | 53 + .../nosecurity/treeapi/spread.js.EXPECTED | 176 ++ .../nosecurity/treeapi/template_literal.js | 40 + .../treeapi/template_literal.js.EXPECTED | 39 + .../test/script/nosecurity/treeapi/utils.js | 32 +- .../nosecurity/treeapi/variable.js.EXPECTED | 47 +- .../nosecurity/treeapi/with.js.EXPECTED | 3 +- 99 files changed, 7204 insertions(+), 881 deletions(-) create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ClassDeclarationTree.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ClassDeclarationTreeImpl.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ClassExpressionTree.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ClassExpressionTreeImpl.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/DestructuringDeclTreeImpl.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ExportEntryTree.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ExportEntryTreeImpl.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ForOfLoopTree.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ForOfLoopTreeImpl.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ImportEntryTree.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ImportEntryTreeImpl.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ModuleTree.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ModuleTreeImpl.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SimpleTreeVisitorES6.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SpreadTree.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SpreadTreeImpl.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TemplateLiteralTree.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TemplateLiteralTreeImpl.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/YieldTree.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/YieldTreeImpl.java create mode 100644 nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TemplateLiteral.java create mode 100644 nashorn/test/script/nosecurity/treeapi/arrow.js create mode 100644 nashorn/test/script/nosecurity/treeapi/arrow.js.EXPECTED create mode 100644 nashorn/test/script/nosecurity/treeapi/arrow_params.js create mode 100644 nashorn/test/script/nosecurity/treeapi/arrow_params.js.EXPECTED create mode 100644 nashorn/test/script/nosecurity/treeapi/class.js create mode 100644 nashorn/test/script/nosecurity/treeapi/class.js.EXPECTED create mode 100644 nashorn/test/script/nosecurity/treeapi/const.js create mode 100644 nashorn/test/script/nosecurity/treeapi/const.js.EXPECTED create mode 100644 nashorn/test/script/nosecurity/treeapi/defaultparams.js create mode 100644 nashorn/test/script/nosecurity/treeapi/defaultparams.js.EXPECTED create mode 100644 nashorn/test/script/nosecurity/treeapi/destructuring_assign.js create mode 100644 nashorn/test/script/nosecurity/treeapi/destructuring_assign.js.EXPECTED create mode 100644 nashorn/test/script/nosecurity/treeapi/destructuring_decl.js create mode 100644 nashorn/test/script/nosecurity/treeapi/destructuring_decl.js.EXPECTED create mode 100644 nashorn/test/script/nosecurity/treeapi/destructuring_params.js create mode 100644 nashorn/test/script/nosecurity/treeapi/destructuring_params.js.EXPECTED create mode 100644 nashorn/test/script/nosecurity/treeapi/generator.js create mode 100644 nashorn/test/script/nosecurity/treeapi/generator.js.EXPECTED create mode 100644 nashorn/test/script/nosecurity/treeapi/let.js create mode 100644 nashorn/test/script/nosecurity/treeapi/let.js.EXPECTED create mode 100644 nashorn/test/script/nosecurity/treeapi/modules.js create mode 100644 nashorn/test/script/nosecurity/treeapi/modules.js.EXPECTED create mode 100644 nashorn/test/script/nosecurity/treeapi/rest.js create mode 100644 nashorn/test/script/nosecurity/treeapi/rest.js.EXPECTED create mode 100644 nashorn/test/script/nosecurity/treeapi/spread.js create mode 100644 nashorn/test/script/nosecurity/treeapi/spread.js.EXPECTED create mode 100644 nashorn/test/script/nosecurity/treeapi/template_literal.js create mode 100644 nashorn/test/script/nosecurity/treeapi/template_literal.js.EXPECTED diff --git a/nashorn/make/project.properties b/nashorn/make/project.properties index 43f4394495e..b6c61b32480 100644 --- a/nashorn/make/project.properties +++ b/nashorn/make/project.properties @@ -35,7 +35,9 @@ build.compiler=modern 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 --git a/nashorn/samples/prettyprinter.js b/nashorn/samples/prettyprinter.js index c9cec3cdab4..a4c708c8eff 100644 --- a/nashorn/samples/prettyprinter.js +++ b/nashorn/samples/prettyprinter.js @@ -209,7 +209,7 @@ function prettyPrint(file) { } print("function "); if (func.name) { - print(func.name); + print(func.name.name); } printFunctionBody(func, extra, end); if (funcDecl) { @@ -608,7 +608,7 @@ function prettyPrint(file) { visitVariable: function(node, extra) { indent(); - print("var " + node.name); + print("var " + node.binding.name); var init = node.initializer; if (init) { print(" = "); diff --git a/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/NashornCompleter.java b/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/NashornCompleter.java index 65c5e28b2a5..6f7ff336eb2 100644 --- a/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/NashornCompleter.java +++ b/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/NashornCompleter.java @@ -439,6 +439,10 @@ final class NashornCompleter implements Completer { args.add("-strict"); } + if (env._es6) { + args.add("--language=es6"); + } + return Parser.create(args.toArray(new String[0])); } } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CatchTree.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CatchTree.java index 87e30ae9428..25b4b710afd 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CatchTree.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CatchTree.java @@ -38,11 +38,11 @@ package jdk.nashorn.api.tree; */ 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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CatchTreeImpl.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CatchTreeImpl.java index 1b692222f53..f392d59ad25 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CatchTreeImpl.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CatchTreeImpl.java @@ -28,12 +28,12 @@ package jdk.nashorn.api.tree; 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 @@ final class CatchTreeImpl extends TreeImpl implements CatchTree { } @Override - public IdentifierTree getParameter() { + public ExpressionTree getParameter() { return param; } diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ClassDeclarationTree.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ClassDeclarationTree.java new file mode 100644 index 00000000000..0eb98cfe9f7 --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ClassDeclarationTree.java @@ -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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ClassDeclarationTreeImpl.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ClassDeclarationTreeImpl.java new file mode 100644 index 00000000000..824f677483b --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ClassDeclarationTreeImpl.java @@ -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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ClassExpressionTree.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ClassExpressionTree.java new file mode 100644 index 00000000000..938c6849b58 --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ClassExpressionTree.java @@ -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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ClassExpressionTreeImpl.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ClassExpressionTreeImpl.java new file mode 100644 index 00000000000..c4108c969c5 --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ClassExpressionTreeImpl.java @@ -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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CompilationUnitTree.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CompilationUnitTree.java index 472e451f05f..a7afb51f859 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CompilationUnitTree.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CompilationUnitTree.java @@ -62,4 +62,12 @@ public interface CompilationUnitTree extends Tree { * @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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CompilationUnitTreeImpl.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CompilationUnitTreeImpl.java index 6e2230a05d5..552a4c5816e 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CompilationUnitTreeImpl.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/CompilationUnitTreeImpl.java @@ -32,13 +32,18 @@ final class CompilationUnitTreeImpl extends TreeImpl 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 @@ -66,6 +71,11 @@ final class CompilationUnitTreeImpl extends TreeImpl return new LineMapImpl(funcNode.getSource()); } + @Override + public ModuleTree getModule() { + return module; + } + @Override public R accept(final TreeVisitor visitor, final D data) { return visitor.visitCompilationUnit(this, data); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/DestructuringDeclTreeImpl.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/DestructuringDeclTreeImpl.java new file mode 100644 index 00000000000..584874bbb3c --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/DestructuringDeclTreeImpl.java @@ -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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ExportEntryTree.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ExportEntryTree.java new file mode 100644 index 00000000000..2e830abd219 --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ExportEntryTree.java @@ -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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ExportEntryTreeImpl.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ExportEntryTreeImpl.java new file mode 100644 index 00000000000..01bc4844859 --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ExportEntryTreeImpl.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ForOfLoopTree.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ForOfLoopTree.java new file mode 100644 index 00000000000..d95e2ed638e --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ForOfLoopTree.java @@ -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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ForOfLoopTreeImpl.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ForOfLoopTreeImpl.java new file mode 100644 index 00000000000..e2306e51c1e --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ForOfLoopTreeImpl.java @@ -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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionDeclarationTree.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionDeclarationTree.java index 1fbd0945c8f..2ff8eb7d802 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionDeclarationTree.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionDeclarationTree.java @@ -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 @@ package jdk.nashorn.api.tree; import java.util.List; /** - * A tree node for a function declaration. + * A tree node for a function declaration. * * For example: *
@@ -37,6 +37,12 @@ import java.util.List;
  *      body
  * 
* + *
+ *   function* name
+ *      ( parameters )
+ *      body
+ * 
+ * * @since 9 */ public interface FunctionDeclarationTree extends StatementTree { @@ -45,7 +51,7 @@ public interface FunctionDeclarationTree extends StatementTree { * * @return name the function declared */ - String getName(); + IdentifierTree getName(); /** * Returns the parameters of this function. @@ -67,4 +73,11 @@ public interface FunctionDeclarationTree extends StatementTree { * @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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionDeclarationTreeImpl.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionDeclarationTreeImpl.java index dfd5af938f6..592e4dce9ea 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionDeclarationTreeImpl.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionDeclarationTreeImpl.java @@ -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 @@ import jdk.nashorn.internal.ir.VarNode; 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 @@ final class FunctionDeclarationTreeImpl extends StatementTreeImpl 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 @@ final class FunctionDeclarationTreeImpl extends StatementTreeImpl } @Override - public String getName() { + public IdentifierTree getName() { return funcName; } @@ -73,6 +73,11 @@ final class FunctionDeclarationTreeImpl extends StatementTreeImpl return funcNode.isStrict(); } + @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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionExpressionTree.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionExpressionTree.java index 13a89bdadbd..d3cd3d233c1 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionExpressionTree.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionExpressionTree.java @@ -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 @@ package jdk.nashorn.api.tree; 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 @@ import java.util.List;
  *      body
  * 
* + *
+ *   var func = (x) => x+1
+ * 
+ * * @since 9 */ public interface FunctionExpressionTree extends ExpressionTree { @@ -45,7 +49,7 @@ public interface FunctionExpressionTree extends ExpressionTree { * * @return name the function declared */ - String getName(); + IdentifierTree getName(); /** * Returns the parameters of this function. @@ -55,11 +59,13 @@ public interface FunctionExpressionTree extends ExpressionTree { 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 @@ public interface FunctionExpressionTree extends ExpressionTree { * @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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionExpressionTreeImpl.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionExpressionTreeImpl.java index b39cfbdfeea..6681f258070 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionExpressionTreeImpl.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/FunctionExpressionTreeImpl.java @@ -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 @@ import jdk.nashorn.internal.ir.FunctionNode; 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 @@ final class FunctionExpressionTreeImpl extends ExpressionTreeImpl 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 @@ final class FunctionExpressionTreeImpl extends ExpressionTreeImpl } @Override - public String getName() { + public IdentifierTree getName() { return funcName; } @@ -69,7 +75,7 @@ final class FunctionExpressionTreeImpl extends ExpressionTreeImpl } @Override - public BlockTree getBody() { + public Tree getBody() { return body; } @@ -78,6 +84,16 @@ final class FunctionExpressionTreeImpl extends ExpressionTreeImpl return funcNode.isStrict(); } + @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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IRTranslator.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IRTranslator.java index 44d6276ca07..0ba7b984788 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IRTranslator.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IRTranslator.java @@ -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 @@ package jdk.nashorn.api.tree; 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.BreakNode; 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.RuntimeNode; 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 @@ final class IRTranslator extends SimpleNodeVisitor { 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 @@ final class IRTranslator extends SimpleNodeVisitor { @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 @@ final class IRTranslator extends SimpleNodeVisitor { 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 @@ final class IRTranslator extends SimpleNodeVisitor { 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 @@ final class IRTranslator extends SimpleNodeVisitor { @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; } @@ -346,6 +356,12 @@ final class IRTranslator extends SimpleNodeVisitor { return false; } + @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, @@ -386,6 +402,14 @@ final class IRTranslator extends SimpleNodeVisitor { 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 @@ final class IRTranslator extends SimpleNodeVisitor { 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 @@ final class IRTranslator extends SimpleNodeVisitor { 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 @@ final class IRTranslator extends SimpleNodeVisitor { 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 @@ final class IRTranslator extends SimpleNodeVisitor { 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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IdentifierTree.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IdentifierTree.java index 95498572e86..e82e257cdce 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IdentifierTree.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IdentifierTree.java @@ -42,4 +42,46 @@ public interface IdentifierTree extends ExpressionTree { * @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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IdentifierTreeImpl.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IdentifierTreeImpl.java index d455dac22f4..a2af0dda856 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IdentifierTreeImpl.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/IdentifierTreeImpl.java @@ -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; @@ -46,6 +47,37 @@ final class IdentifierTreeImpl extends ExpressionTreeImpl implements IdentifierT return name; } + @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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ImportEntryTree.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ImportEntryTree.java new file mode 100644 index 00000000000..f28db074080 --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ImportEntryTree.java @@ -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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ImportEntryTreeImpl.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ImportEntryTreeImpl.java new file mode 100644 index 00000000000..68af2d45921 --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ImportEntryTreeImpl.java @@ -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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ModuleTree.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ModuleTree.java new file mode 100644 index 00000000000..cb7d3538c80 --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ModuleTree.java @@ -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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ModuleTreeImpl.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ModuleTreeImpl.java new file mode 100644 index 00000000000..df32b0914b8 --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ModuleTreeImpl.java @@ -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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/Parser.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/Parser.java index 63a1bad3a91..a6643d30226 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/Parser.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/Parser.java @@ -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 @@ public interface Parser { *
"--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 @@ public interface Parser { case "-nse": case "-scripting": case "-strict": + case "--language=es6": + case "--es6-module": break; default: throw new IllegalArgumentException(opt); diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ParserImpl.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ParserImpl.java index 97a2c8c219c..1fccd7e94db 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ParserImpl.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/ParserImpl.java @@ -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.PrintWriter; 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 @@ import jdk.nashorn.internal.runtime.options.Options; 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 @@ final class ParserImpl implements Parser { } } + 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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/PropertyTree.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/PropertyTree.java index 0b4af44ce4b..9292bdb15ed 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/PropertyTree.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/PropertyTree.java @@ -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 @@ public interface PropertyTree extends Tree { * @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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/PropertyTreeImpl.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/PropertyTreeImpl.java index 3e9a73adc6a..1ba734ec731 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/PropertyTreeImpl.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/PropertyTreeImpl.java @@ -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 @@ final class PropertyTreeImpl extends TreeImpl implements PropertyTree { 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 @@ final class PropertyTreeImpl extends TreeImpl implements PropertyTree { this.value = value; this.getter = getter; this.setter = setter; + this.isStatic = node.isStatic(); + this.isComputed = node.isComputed(); } @Override @@ -69,6 +73,16 @@ final class PropertyTreeImpl extends TreeImpl implements PropertyTree { return setter; } + @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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/RegExpLiteralTree.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/RegExpLiteralTree.java index d3729e47dd8..ff77c267d12 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/RegExpLiteralTree.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/RegExpLiteralTree.java @@ -30,7 +30,7 @@ package jdk.nashorn.api.tree; * * @since 9 */ -public interface RegExpLiteralTree extends Tree { +public interface RegExpLiteralTree extends ExpressionTree { /** * Regular expression pattern to match. * diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SimpleTreeVisitorES5_1.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SimpleTreeVisitorES5_1.java index 1b32c4ee96b..b377690a33f 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SimpleTreeVisitorES5_1.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SimpleTreeVisitorES5_1.java @@ -61,6 +61,45 @@ public class SimpleTreeVisitorES5_1 implements TreeVisitor { 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 @@ public class SimpleTreeVisitorES5_1 implements TreeVisitor { 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 @@ public class SimpleTreeVisitorES5_1 implements TreeVisitor { 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 @@ public class SimpleTreeVisitorES5_1 implements TreeVisitor { 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 @@ public class SimpleTreeVisitorES5_1 implements TreeVisitor { 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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SimpleTreeVisitorES6.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SimpleTreeVisitorES6.java new file mode 100644 index 00000000000..18cca4e6843 --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SimpleTreeVisitorES6.java @@ -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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SpreadTree.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SpreadTree.java new file mode 100644 index 00000000000..ca66dce8580 --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SpreadTree.java @@ -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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SpreadTreeImpl.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SpreadTreeImpl.java new file mode 100644 index 00000000000..a01601f7097 --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/SpreadTreeImpl.java @@ -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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TemplateLiteralTree.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TemplateLiteralTree.java new file mode 100644 index 00000000000..86bc4195ed5 --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TemplateLiteralTree.java @@ -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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TemplateLiteralTreeImpl.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TemplateLiteralTreeImpl.java new file mode 100644 index 00000000000..e3be74fe929 --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TemplateLiteralTreeImpl.java @@ -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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/Tree.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/Tree.java index 6b3de2c3591..3d8c2bf13d6 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/Tree.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/Tree.java @@ -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 @@ -64,6 +64,16 @@ public interface Tree { */ 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}. */ @@ -149,6 +159,21 @@ public interface Tree { */ 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}. */ @@ -184,6 +209,11 @@ public interface Tree { */ REGEXP_LITERAL(RegExpLiteralTree.class), + /** + * Used for instances of {@link TemplateLiteralTree}. + */ + TEMPLATE_LITERAL(TemplateLiteralTree.class), + /** * Used for instances of {@link ReturnTree}. */ @@ -286,7 +316,7 @@ public interface Tree { /** * Used for instances of {@link UnaryTree} representing logical - * void operator {@code typeof}. + * void operator {@code void}. */ VOID(UnaryTree.class), @@ -494,6 +524,18 @@ public interface Tree { */ 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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TreeImpl.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TreeImpl.java index 1e3b6646f23..0df9037df31 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TreeImpl.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TreeImpl.java @@ -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 @@ abstract class TreeImpl implements Tree { 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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TreeVisitor.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TreeVisitor.java index c9c8a9666d8..ea8fc0d4222 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TreeVisitor.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/TreeVisitor.java @@ -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 @@ -117,6 +117,24 @@ public interface TreeVisitor { */ 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. * @@ -189,6 +207,15 @@ public interface TreeVisitor { */ 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. * @@ -216,7 +243,7 @@ public interface TreeVisitor { */ R visitFunctionExpression(FunctionExpressionTree node, P p); - /** + /** * Visit identifier tree. * * @param node node being visited @@ -333,6 +360,15 @@ public interface TreeVisitor { */ 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. * @@ -342,6 +378,15 @@ public interface TreeVisitor { */ 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. * @@ -369,6 +414,33 @@ public interface TreeVisitor { */ 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. * @@ -423,6 +495,15 @@ public interface TreeVisitor { */ 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 diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/VariableTree.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/VariableTree.java index 8697360b9bc..b0cbada632f 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/VariableTree.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/VariableTree.java @@ -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 @@ public interface VariableTree extends StatementTree { * @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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/VariableTreeImpl.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/VariableTreeImpl.java index ca56511f1f9..a1e6f6f7e89 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/VariableTreeImpl.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/VariableTreeImpl.java @@ -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 @@ package jdk.nashorn.api.tree; 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 @@ final class VariableTreeImpl extends StatementTreeImpl implements VariableTree { } @Override - public String getName() { - return name; + public ExpressionTree getBinding() { + return ident; } @Override @@ -52,6 +52,16 @@ final class VariableTreeImpl extends StatementTreeImpl implements VariableTree { return init; } + @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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/YieldTree.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/YieldTree.java new file mode 100644 index 00000000000..0b21bb0be2c --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/YieldTree.java @@ -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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/YieldTreeImpl.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/YieldTreeImpl.java new file mode 100644 index 00000000000..aea75d81a4c --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/api/tree/YieldTreeImpl.java @@ -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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SplitIntoFunctions.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SplitIntoFunctions.java index 1489e01a859..20074338de3 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SplitIntoFunctions.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/SplitIntoFunctions.java @@ -167,6 +167,7 @@ final class SplitIntoFunctions extends NodeVisitor { 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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ClassNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ClassNode.java index 4c0c6ffb175..4b06d223a43 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ClassNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ClassNode.java @@ -42,6 +42,7 @@ public class ClassNode extends Expression { private final PropertyNode constructor; private final List classElements; private final int line; + private final boolean isStatement; /** * Constructor. @@ -53,15 +54,17 @@ public class ClassNode extends Expression { * @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; } /** @@ -100,6 +103,15 @@ public class ClassNode extends Expression { return Collections.unmodifiableList(classElements); } + /** + * 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. * diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ExpressionStatement.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ExpressionStatement.java index b8e5428011e..21618ab8657 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ExpressionStatement.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/ExpressionStatement.java @@ -27,6 +27,7 @@ package jdk.nashorn.internal.ir; 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 @@ public final class ExpressionStatement extends Statement { /** 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 @@ public final class ExpressionStatement extends Statement { * @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 @@ -80,6 +98,15 @@ public final class ExpressionStatement extends Statement { return expression; } + /** + * 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 diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java index 8eda934b542..89bfcab5a76 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/FunctionNode.java @@ -35,6 +35,7 @@ import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALL 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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag /** 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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag /** 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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag * @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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag 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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag 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 @@ public final class FunctionNode extends LexicalContextExpression implements Flag this.lastToken = lastToken; this.body = body; this.parameters = parameters; + this.parameterExpressions = functionNode.parameterExpressions; this.thisProperties = thisProperties; this.rootClass = rootClass; this.source = source; @@ -976,6 +987,15 @@ public final class FunctionNode extends LexicalContextExpression implements Flag return Collections.unmodifiableList(parameters); } + /** + * 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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TemplateLiteral.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TemplateLiteral.java new file mode 100644 index 00000000000..0a7dc89f2f0 --- /dev/null +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/TemplateLiteral.java @@ -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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/visitor/NodeVisitor.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/visitor/NodeVisitor.java index c83587c34d4..2aba91fadfd 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/visitor/NodeVisitor.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/visitor/NodeVisitor.java @@ -59,6 +59,7 @@ import jdk.nashorn.internal.ir.SetSplitState; 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; @@ -737,6 +738,26 @@ public abstract class NodeVisitor { return leaveDefault(switchNode); } + /** + * 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 * diff --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java index b9a926d1029..46675820e4b 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java @@ -127,6 +127,7 @@ import jdk.nashorn.internal.ir.ReturnNode; 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 @@ public class Parser extends AbstractParser implements Loggable { 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 @@ public class Parser extends AbstractParser implements Loggable { ident, function.getName(), parameters, + function.getParameterExpressions(), kind, function.getFlags(), body, @@ -622,19 +624,6 @@ public class Parser extends AbstractParser implements Loggable { return new Block(blockToken, finish, flags, newBlock.getStatements()); } - /** - * 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 @@ public class Parser extends AbstractParser implements Loggable { }); } - 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 @@ public class Parser extends AbstractParser implements Loggable { className = getIdent(); } - return classTail(classLineNumber, classToken, className); + return classTail(classLineNumber, classToken, className, isStatement); } private static final class ClassElementKey { @@ -1317,7 +1295,8 @@ public class Parser extends AbstractParser implements Loggable { * 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 @@ public class Parser extends AbstractParser implements Loggable { } 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 @@ public class Parser extends AbstractParser implements Loggable { 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 @@ public class Parser extends AbstractParser implements Loggable { 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 @@ public class Parser extends AbstractParser implements Loggable { 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 @@ public class Parser extends AbstractParser implements Loggable { 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 @@ public class Parser extends AbstractParser implements Loggable { * 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 @@ public class Parser extends AbstractParser implements Loggable { // 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 @@ public class Parser extends AbstractParser implements Loggable { 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 @@ public class Parser extends AbstractParser implements Loggable { 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 @@ public class Parser extends AbstractParser implements Loggable { // 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 @@ public class Parser extends AbstractParser implements Loggable { 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 @@ public class Parser extends AbstractParser implements Loggable { // 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 @@ public class Parser extends AbstractParser implements Loggable { } 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 @@ public class Parser extends AbstractParser implements Loggable { 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 @@ public class Parser extends AbstractParser implements Loggable { 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 @@ public class Parser extends AbstractParser implements Loggable { 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 @@ public class Parser extends AbstractParser implements Loggable { 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 --git a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextFunctionNode.java b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextFunctionNode.java index cfd8b293071..0e53c2b376a 100644 --- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextFunctionNode.java +++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/ParserContextFunctionNode.java @@ -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 @@ class ParserContextFunctionNode extends ParserContextBaseNode { private Module module; private int debugFlags; + private Map parameterExpressions; /** * @param token The token for the function @@ -169,6 +173,22 @@ class ParserContextFunctionNode extends ParserContextBaseNode { this.parameters = parameters; } + /** + * 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 --git a/nashorn/test/script/basic/JDK-8075207.js b/nashorn/test/script/basic/JDK-8075207.js index d44fc5512b9..168345d9ded 100644 --- a/nashorn/test/script/basic/JDK-8075207.js +++ b/nashorn/test/script/basic/JDK-8075207.js @@ -61,7 +61,7 @@ Assert.assertTrue(stats.get(1) instanceof FunctionDeclarationTree); 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 --git a/nashorn/test/script/basic/JDK-8075448.js b/nashorn/test/script/basic/JDK-8075448.js index 3f5e1fd0fa9..ec6cb6f11ba 100644 --- a/nashorn/test/script/basic/JDK-8075448.js +++ b/nashorn/test/script/basic/JDK-8075448.js @@ -44,6 +44,6 @@ var ast = parser.parse("test.js", code, print); 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 --git a/nashorn/test/script/basic/es6/let-const-switch.js.EXPECTED b/nashorn/test/script/basic/es6/let-const-switch.js.EXPECTED index 90309bfe1af..c534d448fbd 100644 --- a/nashorn/test/script/basic/es6/let-const-switch.js.EXPECTED +++ b/nashorn/test/script/basic/es6/let-const-switch.js.EXPECTED @@ -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 --git a/nashorn/test/script/nosecurity/parserapi.js b/nashorn/test/script/nosecurity/parserapi.js index 3c1086f4ef3..d2868fa9a6e 100644 --- a/nashorn/test/script/nosecurity/parserapi.js +++ b/nashorn/test/script/nosecurity/parserapi.js @@ -62,6 +62,26 @@ Parser.prototype.convert = function(tree) { 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 --git a/nashorn/test/script/nosecurity/parserapi.js.EXPECTED b/nashorn/test/script/nosecurity/parserapi.js.EXPECTED index 82f4506ed69..6378c3004ee 100644 --- a/nashorn/test/script/nosecurity/parserapi.js.EXPECTED +++ b/nashorn/test/script/nosecurity/parserapi.js.EXPECTED @@ -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" + "startPosition": "4712" }, - "endPosition": "4078", + "endPosition": "4718", "kind": "EXPRESSION_STATEMENT", - "startPosition": "4072" + "startPosition": "4712" } ], "sourceName": "parserapi.js", "strict": "false", "startPosition": "1136" -} -] +} +] diff --git a/nashorn/test/script/nosecurity/parservisitor.js b/nashorn/test/script/nosecurity/parservisitor.js index 069f50f68d4..9f71addc81a 100644 --- a/nashorn/test/script/nosecurity/parservisitor.js +++ b/nashorn/test/script/nosecurity/parservisitor.js @@ -202,7 +202,7 @@ parse("funccall.js", "func()", 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 @@ unaryExpr("void.js", "void x"); 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 --git a/nashorn/test/script/nosecurity/treeapi/arrow.js b/nashorn/test/script/nosecurity/treeapi/arrow.js new file mode 100644 index 00000000000..b4b92f50c3d --- /dev/null +++ b/nashorn/test/script/nosecurity/treeapi/arrow.js @@ -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 --git a/nashorn/test/script/nosecurity/treeapi/arrow.js.EXPECTED b/nashorn/test/script/nosecurity/treeapi/arrow.js.EXPECTED new file mode 100644 index 00000000000..e53a895f641 --- /dev/null +++ b/nashorn/test/script/nosecurity/treeapi/arrow.js.EXPECTED @@ -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 --git a/nashorn/test/script/nosecurity/treeapi/arrow_params.js b/nashorn/test/script/nosecurity/treeapi/arrow_params.js new file mode 100644 index 00000000000..9a477c6db10 --- /dev/null +++ b/nashorn/test/script/nosecurity/treeapi/arrow_params.js @@ -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 --git a/nashorn/test/script/nosecurity/treeapi/arrow_params.js.EXPECTED b/nashorn/test/script/nosecurity/treeapi/arrow_params.js.EXPECTED new file mode 100644 index 00000000000..e25cbd8cda2 --- /dev/null +++ b/nashorn/test/script/nosecurity/treeapi/arrow_params.js.EXPECTED @@ -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 --git a/nashorn/test/script/nosecurity/treeapi/assignment.js.EXPECTED b/nashorn/test/script/nosecurity/treeapi/assignment.js.EXPECTED index bac257f888a..bb86c2aa69e 100644 --- a/nashorn/test/script/nosecurity/treeapi/assignment.js.EXPECTED +++ b/nashorn/test/script/nosecurity/treeapi/assignment.js.EXPECTED @@ -146,4 +146,4 @@ }, "startPosition": "61" } -] +] diff --git a/nashorn/test/script/nosecurity/treeapi/class.js b/nashorn/test/script/nosecurity/treeapi/class.js new file mode 100644 index 00000000000..ce48d423a3e --- /dev/null +++ b/nashorn/test/script/nosecurity/treeapi/class.js @@ -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 = < Date: Tue, 12 Jul 2016 12:55:18 -0700 Subject: [PATCH 14/44] 8154261: Module summary page should display directives for the module Reviewed-by: jjg, ksrini --- .../html/AnnotationTypeWriterImpl.java | 11 - .../doclets/formats/html/ClassWriterImpl.java | 11 - .../formats/html/HtmlDocletWriter.java | 11 + .../formats/html/ModuleWriterImpl.java | 416 ++++++++++++++++-- .../doclets/formats/html/SectionName.java | 3 + .../formats/html/markup/HtmlConstants.java | 18 + .../formats/html/markup/HtmlStyle.java | 8 +- .../formats/html/markup/HtmlWriter.java | 36 ++ .../html/resources/standard.properties | 5 + .../doclets/toolkit/ModuleSummaryWriter.java | 22 +- .../builders/ModuleSummaryBuilder.java | 33 +- .../doclets/toolkit/resources/doclet.xml | 4 +- .../toolkit/resources/doclets.properties | 6 + .../doclets/toolkit/resources/stylesheet.css | 49 ++- .../doclet/testModules/TestModules.java | 139 +++++- .../testModules/module2/module-info.java | 6 + .../testpkg2mdl2/TestInterfaceInModule2.java | 29 ++ .../testpkgmdl2/TestClassInModule2.java | 5 +- .../doclet/testStylesheet/TestStylesheet.java | 15 +- 19 files changed, 726 insertions(+), 101 deletions(-) create mode 100644 langtools/test/jdk/javadoc/doclet/testModules/module2/testpkg2mdl2/TestInterfaceInModule2.java diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeWriterImpl.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeWriterImpl.java index 61b101ce5e6..efb0612f22b 100644 --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeWriterImpl.java +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AnnotationTypeWriterImpl.java @@ -457,17 +457,6 @@ public class AnnotationTypeWriterImpl extends SubWriterHolderWriter return ulNav; } - /** - * 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} */ diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriterImpl.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriterImpl.java index 5d6f174ffa8..0356b0a8c78 100644 --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriterImpl.java +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ClassWriterImpl.java @@ -763,17 +763,6 @@ public class ClassWriterImpl extends SubWriterHolderWriter implements ClassWrite return ulNav; } - /** - * 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. * diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java index c5af88209b9..6073a6d5aff 100644 --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java @@ -907,6 +907,17 @@ public class HtmlDocletWriter extends HtmlDocWriter { return li; } + /** + * 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. * diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleWriterImpl.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleWriterImpl.java index cc7e78d0506..4dd90f6efd1 100644 --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleWriterImpl.java +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleWriterImpl.java @@ -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 @@ public class ModuleWriterImpl extends HtmlDocletWriter implements ModuleSummaryW */ 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 @@ public class ModuleWriterImpl extends HtmlDocletWriter implements ModuleSummaryW 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 @@ public class ModuleWriterImpl extends HtmlDocletWriter implements ModuleSummaryW } /** - * {@inheritDoc} + * Get the content header. */ public Content getContentHeader() { HtmlTree div = new HtmlTree(HtmlTag.DIV); @@ -136,7 +152,7 @@ public class ModuleWriterImpl extends HtmlDocletWriter implements ModuleSummaryW } /** - * {@inheritDoc} + * Get the summary section header. */ public Content getSummaryHeader() { HtmlTree li = new HtmlTree(HtmlTag.LI); @@ -145,26 +161,325 @@ public class ModuleWriterImpl extends HtmlDocletWriter implements ModuleSummaryW } /** - * {@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); return ul; } + /** + * 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 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 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(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,31 +511,58 @@ public class ModuleWriterImpl extends HtmlDocletWriter implements ModuleSummaryW } /** - * 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 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 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 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(); + } + /** * {@inheritDoc} */ diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SectionName.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SectionName.java index 19e9a556c4c..6a2fa547bab 100644 --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SectionName.java +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SectionName.java @@ -54,6 +54,9 @@ public enum SectionName { 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 --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlConstants.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlConstants.java index 8807cd75bba..32383d1b288 100644 --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlConstants.java +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlConstants.java @@ -69,6 +69,24 @@ public class HtmlConstants { public static final Content START_OF_MODULE_DESCRIPTION = 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. */ diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyle.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyle.java index 301f86dec6d..3b5de9f4cd4 100644 --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyle.java +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlStyle.java @@ -49,6 +49,7 @@ public enum HtmlStyle { colFirst, colLast, colOne, + colSecond, constantsSummary, constantValuesContainer, contentContainer, @@ -65,6 +66,7 @@ public enum HtmlStyle { header, horizontal, footer, + implementationLabel, indexContainer, indexNav, inheritance, @@ -87,7 +89,10 @@ public enum HtmlStyle { overviewSummary, packageHierarchyLabel, packageLabelInClass, + packagesSummary, paramLabel, + providesSummary, + requiresSummary, returnLabel, rightContainer, rightIframe, @@ -111,5 +116,6 @@ public enum HtmlStyle { typeNameLabel, typeNameLink, typeSummary, - useSummary + useSummary, + usesSummary } diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlWriter.java index f3774d2cc9d..79184184107 100644 --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlWriter.java +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlWriter.java @@ -76,6 +76,26 @@ public class HtmlWriter { */ 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. */ @@ -108,6 +128,8 @@ public class HtmlWriter { public final Content detailLabel; + public final Content moduleSubNavLabel; + public final Content framesLabel; public final Content noframesLabel; @@ -192,6 +214,19 @@ public class HtmlWriter { 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 @@ public class HtmlWriter { 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 --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties index e38bb411787..f6453170835 100644 --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties @@ -31,6 +31,11 @@ doclet.Href_Type_Param_Title=type parameter in {0} 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 --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/ModuleSummaryWriter.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/ModuleSummaryWriter.java index 3be3acca47c..27906a50daa 100644 --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/ModuleSummaryWriter.java +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/ModuleSummaryWriter.java @@ -91,15 +91,25 @@ public interface ModuleSummaryWriter { 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 --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/ModuleSummaryBuilder.java b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/ModuleSummaryBuilder.java index 41ae7605d24..7bfb05b51b3 100644 --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/ModuleSummaryBuilder.java +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/builders/ModuleSummaryBuilder.java @@ -167,23 +167,34 @@ public class ModuleSummaryBuilder extends AbstractBuilder { } /** - * 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 --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclet.xml b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclet.xml index abfc24ca9be..45a7e7fcd44 100644 --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclet.xml +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclet.xml @@ -33,7 +33,9 @@ - + + + diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets.properties b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets.properties index 6252aa2dbcc..8471fd9e6a3 100644 --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets.properties +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/doclets.properties @@ -69,6 +69,10 @@ doclet.noInheritedDoc=@inheritDoc used but {0} does not override or implement an 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.Classes=Classes 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.subclasses=subclasses doclet.subinterfaces=subinterfaces doclet.Modifier=Modifier doclet.Type=Type +doclet.Implementation=Implementation: doclet.Types=Types doclet.Members=Members doclet.SearchTags=SearchTags diff --git a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/stylesheet.css b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/stylesheet.css index eef017b8556..2861c046a4b 100644 --- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/stylesheet.css +++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/resources/stylesheet.css @@ -412,18 +412,20 @@ table tr td dl, table tr td dl dt, table tr td dl dd { /* 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 @@ Table styles } .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 @@ Table styles 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 @@ Table styles } .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.colFirst, th.colFirst { 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 @@ td.colOne, th.colOne { 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 @@ h1.hidden { 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 --git a/langtools/test/jdk/javadoc/doclet/testModules/TestModules.java b/langtools/test/jdk/javadoc/doclet/testModules/TestModules.java index dfcbb880c40..b9648811d6b 100644 --- a/langtools/test/jdk/javadoc/doclet/testModules/TestModules.java +++ b/langtools/test/jdk/javadoc/doclet/testModules/TestModules.java @@ -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 @@ public class TestModules extends JavadocTester { 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 @@ public class TestModules extends JavadocTester { "
\n" + "
    \n" + "
  • \n" - + ""); + + "
      \n" + + "
    • \n" + + ""); checkOutput("module2-summary.html", found, "
      \n" + "
        \n" + "
      • \n" - + "
    "); + + "
      \n" + + "
    • \n" + + ""); } void testHtml5Description(boolean found) { @@ -171,12 +186,16 @@ public class TestModules extends JavadocTester { "
      \n" + "
        \n" + "
      • \n" - + "
    "); + + "
      \n" + + "
    • \n" + + ""); checkOutput("module2-summary.html", found, "
      \n" + "
        \n" + "
      • \n" - + "
    "); + + "
      \n" + + "
    • \n" + + ""); } void testModuleLink() { @@ -313,4 +332,114 @@ public class TestModules extends JavadocTester { + "
    \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 --git a/langtools/test/jdk/javadoc/doclet/testModules/module2/module-info.java b/langtools/test/jdk/javadoc/doclet/testModules/module2/module-info.java index 13c77f05998..c0f316d7982 100644 --- a/langtools/test/jdk/javadoc/doclet/testModules/module2/module-info.java +++ b/langtools/test/jdk/javadoc/doclet/testModules/module2/module-info.java @@ -28,4 +28,10 @@ */ module module2 { exports testpkgmdl2; + + exports testpkg2mdl2 to module1; + + uses testpkgmdl2.TestClassInModule2; + + provides testpkg2mdl2.TestInterfaceInModule2 with testpkgmdl2.TestClassInModule2; } diff --git a/langtools/test/jdk/javadoc/doclet/testModules/module2/testpkg2mdl2/TestInterfaceInModule2.java b/langtools/test/jdk/javadoc/doclet/testModules/module2/testpkg2mdl2/TestInterfaceInModule2.java new file mode 100644 index 00000000000..fb46042270b --- /dev/null +++ b/langtools/test/jdk/javadoc/doclet/testModules/module2/testpkg2mdl2/TestInterfaceInModule2.java @@ -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 --git a/langtools/test/jdk/javadoc/doclet/testModules/module2/testpkgmdl2/TestClassInModule2.java b/langtools/test/jdk/javadoc/doclet/testModules/module2/testpkgmdl2/TestClassInModule2.java index d4995649b5c..04a6c5022ed 100644 --- a/langtools/test/jdk/javadoc/doclet/testModules/module2/testpkgmdl2/TestClassInModule2.java +++ b/langtools/test/jdk/javadoc/doclet/testModules/module2/testpkgmdl2/TestClassInModule2.java @@ -24,5 +24,8 @@ */ package testpkgmdl2; -public class TestClassInModule2 { +import testpkg2mdl2.TestInterfaceInModule2; + +public class TestClassInModule2 implements TestInterfaceInModule2 { + void testMethod() {} } diff --git a/langtools/test/jdk/javadoc/doclet/testStylesheet/TestStylesheet.java b/langtools/test/jdk/javadoc/doclet/testStylesheet/TestStylesheet.java index c5ab9d99879..fc37f6ed400 100644 --- a/langtools/test/jdk/javadoc/doclet/testStylesheet/TestStylesheet.java +++ b/langtools/test/jdk/javadoc/doclet/testStylesheet/TestStylesheet.java @@ -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 @@ public class TestStylesheet extends JavadocTester { + " 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 @@ public class TestStylesheet extends JavadocTester { + " 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 @@ public class TestStylesheet extends JavadocTester { + "}", // 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 @@ public class TestStylesheet extends JavadocTester { + " 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" + "}", From 29852f47c03927ddcb98342f2910a059867d6170 Mon Sep 17 00:00:00 2001 From: Bhavesh Patel Date: Wed, 13 Jul 2016 14:23:53 -0700 Subject: [PATCH 15/44] 8161255: jdk build "all" (docs) fails on all platforms, error from DefaultLoggerFinder.java Reviewed-by: tbell, ksrini, darcy --- make/Javadoc.gmk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/Javadoc.gmk b/make/Javadoc.gmk index f194dd95f89..d1607b24bab 100644 --- a/make/Javadoc.gmk +++ b/make/Javadoc.gmk @@ -424,7 +424,7 @@ $(COREAPI_OPTIONS_FILE): $(COREAPI_OVERVIEW) @($(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)) ; \ From de4f424b8ca88a5477f9fa770be359548d9d7f8e Mon Sep 17 00:00:00 2001 From: Mandy Chung Date: Wed, 13 Jul 2016 14:41:27 -0700 Subject: [PATCH 16/44] 8160398: (jdeps) Replace a list of JDK 8 internal API for detecting if it's removed in JDK 9 or later Reviewed-by: dfuchs --- .../listjdkinternals/ListJDKInternals.java | 195 ++++ .../classes/com/sun/tools/jdeps/Analyzer.java | 63 +- .../tools/jdeps/resources/jdk8_internals.txt | 1029 +++++++++++++++++ .../jdeps/resources/jdkinternals.properties | 2 +- .../jdkinternals/RemovedJDKInternals.java | 2 +- .../jdeps/jdkinternals/ShowReplacement.java | 29 +- .../apple/applescript/AppleScriptEngine.java | 30 + .../jdkinternals/{p => src/q}/NoRepl.java | 2 +- .../jdkinternals/src/q/RemovedPackage.java | 30 + .../jdkinternals/{p => src/q}/WithRepl.java | 2 +- 10 files changed, 1332 insertions(+), 52 deletions(-) create mode 100644 langtools/make/src/classes/build/tools/listjdkinternals/ListJDKInternals.java create mode 100644 langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdk8_internals.txt create mode 100644 langtools/test/tools/jdeps/jdkinternals/src/apple/applescript/AppleScriptEngine.java rename langtools/test/tools/jdeps/jdkinternals/{p => src/q}/NoRepl.java (99%) create mode 100644 langtools/test/tools/jdeps/jdkinternals/src/q/RemovedPackage.java rename langtools/test/tools/jdeps/jdkinternals/{p => src/q}/WithRepl.java (99%) diff --git a/langtools/make/src/classes/build/tools/listjdkinternals/ListJDKInternals.java b/langtools/make/src/classes/build/tools/listjdkinternals/ListJDKInternals.java new file mode 100644 index 00000000000..3732943b932 --- /dev/null +++ b/langtools/make/src/classes/build/tools/listjdkinternals/ListJDKInternals.java @@ -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 --git a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Analyzer.java b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Analyzer.java index c39f5f274e6..aeb712bbc90 100644 --- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Analyzer.java +++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/Analyzer.java @@ -25,20 +25,19 @@ package com.sun.tools.jdeps; -import static com.sun.tools.jdeps.JdepsConfiguration.*; - 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 @@ public class Analyzer { } } - 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 class Analyzer { 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 --git a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdk8_internals.txt b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdk8_internals.txt new file mode 100644 index 00000000000..be46f2e0398 --- /dev/null +++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdk8_internals.txt @@ -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 --git a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties index 8044923aec6..4eb67eaa029 100644 --- a/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties +++ b/langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/resources/jdkinternals.properties @@ -28,7 +28,7 @@ sun.security.util.SecurityConstants=Use appropriate java.security.Permission sub 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 --git a/langtools/test/tools/jdeps/jdkinternals/RemovedJDKInternals.java b/langtools/test/tools/jdeps/jdkinternals/RemovedJDKInternals.java index 9bfda2a5fa6..68822f8d71c 100644 --- a/langtools/test/tools/jdeps/jdkinternals/RemovedJDKInternals.java +++ b/langtools/test/tools/jdeps/jdkinternals/RemovedJDKInternals.java @@ -71,7 +71,7 @@ public class RemovedJDKInternals { 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 --git a/langtools/test/tools/jdeps/jdkinternals/ShowReplacement.java b/langtools/test/tools/jdeps/jdkinternals/ShowReplacement.java index 6f6075dbb8b..c5fc541842f 100644 --- a/langtools/test/tools/jdeps/jdkinternals/ShowReplacement.java +++ b/langtools/test/tools/jdeps/jdkinternals/ShowReplacement.java @@ -59,14 +59,17 @@ public class ShowReplacement { 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 @@ public class ShowReplacement { } } + /* + * 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 --git a/langtools/test/tools/jdeps/jdkinternals/src/apple/applescript/AppleScriptEngine.java b/langtools/test/tools/jdeps/jdkinternals/src/apple/applescript/AppleScriptEngine.java new file mode 100644 index 00000000000..94e1b0bd967 --- /dev/null +++ b/langtools/test/tools/jdeps/jdkinternals/src/apple/applescript/AppleScriptEngine.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +package apple.applescript; + +import javax.script.ScriptEngine; + +public interface AppleScriptEngine extends ScriptEngine { +} diff --git a/langtools/test/tools/jdeps/jdkinternals/p/NoRepl.java b/langtools/test/tools/jdeps/jdkinternals/src/q/NoRepl.java similarity index 99% rename from langtools/test/tools/jdeps/jdkinternals/p/NoRepl.java rename to langtools/test/tools/jdeps/jdkinternals/src/q/NoRepl.java index 8cc52405662..16731f55b26 100644 --- a/langtools/test/tools/jdeps/jdkinternals/p/NoRepl.java +++ b/langtools/test/tools/jdeps/jdkinternals/src/q/NoRepl.java @@ -21,7 +21,7 @@ * questions. */ -package p; +package q; import java.io.IOException; import java.io.OutputStream; diff --git a/langtools/test/tools/jdeps/jdkinternals/src/q/RemovedPackage.java b/langtools/test/tools/jdeps/jdkinternals/src/q/RemovedPackage.java new file mode 100644 index 00000000000..b27c3f2120c --- /dev/null +++ b/langtools/test/tools/jdeps/jdkinternals/src/q/RemovedPackage.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package q; + +import apple.applescript.AppleScriptEngine; + +public class RemovedPackage { + AppleScriptEngine scriptEngine; +} diff --git a/langtools/test/tools/jdeps/jdkinternals/p/WithRepl.java b/langtools/test/tools/jdeps/jdkinternals/src/q/WithRepl.java similarity index 99% rename from langtools/test/tools/jdeps/jdkinternals/p/WithRepl.java rename to langtools/test/tools/jdeps/jdkinternals/src/q/WithRepl.java index d31b9935b13..513a411fd95 100644 --- a/langtools/test/tools/jdeps/jdkinternals/p/WithRepl.java +++ b/langtools/test/tools/jdeps/jdkinternals/src/q/WithRepl.java @@ -21,7 +21,7 @@ * questions. */ -package p; +package q; import sun.security.util.HostnameChecker; From 1c7cd18b25c89ff10045bc54b249998c3bc80bd2 Mon Sep 17 00:00:00 2001 From: Mandy Chung Date: Wed, 13 Jul 2016 14:42:09 -0700 Subject: [PATCH 17/44] 8160398: (jdeps) Replace a list of JDK 8 internal API for detecting if it's removed in JDK 9 or later Reviewed-by: dfuchs --- make/CompileJavaModules.gmk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/make/CompileJavaModules.gmk b/make/CompileJavaModules.gmk index a53f2b13887..f377d6687c5 100644 --- a/make/CompileJavaModules.gmk +++ b/make/CompileJavaModules.gmk @@ -409,6 +409,8 @@ jdk.jconsole_CLEAN_FILES := $(wildcard \ ################################################################################ +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) From dfc1da33a254a0ffc236a1e2668c69e2a2b6fa9b Mon Sep 17 00:00:00 2001 From: Joe Darcy Date: Wed, 13 Jul 2016 17:28:29 -0700 Subject: [PATCH 18/44] 8161336: Remove two javadoc tests from the problem list Reviewed-by: jjg --- langtools/test/ProblemList.txt | 3 --- .../sun/javadoc/testTypeAnnotations/TestTypeAnnotations.java | 1 - .../test/com/sun/javadoc/typeAnnotations/smoke/TestSmoke.java | 1 - 3 files changed, 5 deletions(-) diff --git a/langtools/test/ProblemList.txt b/langtools/test/ProblemList.txt index 66d46dcf74d..f54ea86a7f1 100644 --- a/langtools/test/ProblemList.txt +++ b/langtools/test/ProblemList.txt @@ -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 --git a/langtools/test/com/sun/javadoc/testTypeAnnotations/TestTypeAnnotations.java b/langtools/test/com/sun/javadoc/testTypeAnnotations/TestTypeAnnotations.java index 7d28c10a2f8..abf9c6992f5 100644 --- a/langtools/test/com/sun/javadoc/testTypeAnnotations/TestTypeAnnotations.java +++ b/langtools/test/com/sun/javadoc/testTypeAnnotations/TestTypeAnnotations.java @@ -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 --git a/langtools/test/com/sun/javadoc/typeAnnotations/smoke/TestSmoke.java b/langtools/test/com/sun/javadoc/typeAnnotations/smoke/TestSmoke.java index 9902ce6e02e..3ae32570e38 100644 --- a/langtools/test/com/sun/javadoc/typeAnnotations/smoke/TestSmoke.java +++ b/langtools/test/com/sun/javadoc/typeAnnotations/smoke/TestSmoke.java @@ -29,7 +29,6 @@ * @author Mahmood Ali * @library ../../lib * @modules jdk.javadoc - * @ignore * @build JavadocTester * @run main TestSmoke */ From 9ff51bb7bb3f16ab9b52ad2105231597814a4ce2 Mon Sep 17 00:00:00 2001 From: Alejandro Murillo Date: Thu, 14 Jul 2016 15:47:44 +0000 Subject: [PATCH 19/44] Added tag jdk-9+127 for changeset 6a80821ee59f --- .hgtags-top-repo | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags-top-repo b/.hgtags-top-repo index 783312526e8..b4f5b7c73a6 100644 --- a/.hgtags-top-repo +++ b/.hgtags-top-repo @@ -369,3 +369,4 @@ cae471d3b87783e0a3deea658e1e1c84b2485b6c jdk-9+121 f80c841ae2545eaf9acd2724bccc305d98cefbe2 jdk-9+124 9aa7d40f3a453f51e47f4c1b19eff5740a74a9f8 jdk-9+125 3a58466296d36944454756ef01e7513ac5e14a16 jdk-9+126 +8fa686245bd2a072ece3392743460030f0854520 jdk-9+127 From 5593c544a5269aedf8516592356ce038f2415a07 Mon Sep 17 00:00:00 2001 From: Alejandro Murillo Date: Thu, 14 Jul 2016 15:47:44 +0000 Subject: [PATCH 20/44] Added tag jdk-9+127 for changeset 8e5b413c199b --- hotspot/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/hotspot/.hgtags b/hotspot/.hgtags index 393252486a7..7415efbd4d7 100644 --- a/hotspot/.hgtags +++ b/hotspot/.hgtags @@ -529,3 +529,4 @@ af6b4ad908e732d23021f12e8322b204433d5cf6 jdk-9+122 479631362b4930be985245ea063d87d821a472eb jdk-9+124 bb640b49741af3f57f9994129934c46fc173219f jdk-9+125 adc8c84b7cf8c540d920182f78a2bc982366432a jdk-9+126 +352357128f602dcf0426b1cbe011a4685a4d9f97 jdk-9+127 From 6cbadc73483239fad9f4ae0670dc73e900a97db1 Mon Sep 17 00:00:00 2001 From: Alejandro Murillo Date: Thu, 14 Jul 2016 15:47:44 +0000 Subject: [PATCH 21/44] Added tag jdk-9+127 for changeset a7aae19b3746 --- corba/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/corba/.hgtags b/corba/.hgtags index d514b137ea5..2667a3cc368 100644 --- a/corba/.hgtags +++ b/corba/.hgtags @@ -369,3 +369,4 @@ e33a34cc551907617d8129c4faaf1a5a7e61d21c jdk-9+123 45121d5afb9d5bfadab75378572ad96832e0809e jdk-9+124 1d48e67d1b91eb9f72e49e69a4021edb85e357fc jdk-9+125 c7f5ba08fcd4b8416e62c21229f9a07c95498919 jdk-9+126 +8fab452b6f4710762ba1d8e55fd62db00b1355fe jdk-9+127 From 3430ccd59767365c8c53a6be901e5313b4064468 Mon Sep 17 00:00:00 2001 From: Alejandro Murillo Date: Thu, 14 Jul 2016 15:47:45 +0000 Subject: [PATCH 22/44] Added tag jdk-9+127 for changeset 78fb48df5d13 --- jaxp/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/jaxp/.hgtags b/jaxp/.hgtags index a9a25f979d4..2355e43df26 100644 --- a/jaxp/.hgtags +++ b/jaxp/.hgtags @@ -369,3 +369,4 @@ f8899b1884e2c4a000dbcc5b1a80954245fe462e jdk-9+122 e04a15153cc293f05fcd60bc98236f50e16af46a jdk-9+124 493eb91ec32a6dea7604cfbd86c10045ad9af15b jdk-9+125 15722f71281f034bc696d8b96136da2ef34da44f jdk-9+126 +bdc3c0b737efbf899709eb3121ce760dcfb51151 jdk-9+127 From b9fb346749ff8533d685a4c42ff9c8d5189588ee Mon Sep 17 00:00:00 2001 From: Alejandro Murillo Date: Thu, 14 Jul 2016 15:47:45 +0000 Subject: [PATCH 23/44] Added tag jdk-9+127 for changeset 246d692a7700 --- jaxws/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/jaxws/.hgtags b/jaxws/.hgtags index cd59b05a750..98fe07cb10f 100644 --- a/jaxws/.hgtags +++ b/jaxws/.hgtags @@ -372,3 +372,4 @@ c42decd28bbfa817347112ed6053b5fbd30517a2 jdk-9+123 1600da1665cd2cc127014e8c002b328ec33a9147 jdk-9+124 5b0570e3db29f6b8c80a4beac70d51284507b203 jdk-9+125 264a44128cd6286e598d5a849ceeb613c06269d0 jdk-9+126 +06d706c70634775418dc79a2671780ba1c624fd2 jdk-9+127 From 6d2a40170baf12b4cd26a3f24d49a366ab8420ec Mon Sep 17 00:00:00 2001 From: Alejandro Murillo Date: Thu, 14 Jul 2016 15:47:47 +0000 Subject: [PATCH 24/44] Added tag jdk-9+127 for changeset d165f39ffd5e --- nashorn/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/nashorn/.hgtags b/nashorn/.hgtags index e724c480a5e..57218d612f0 100644 --- a/nashorn/.hgtags +++ b/nashorn/.hgtags @@ -360,3 +360,4 @@ b1de131a3fed6845c78bdda358ee127532f16a3f jdk-9+122 5d68f5155dded7efec7d5aca5d631caa7ee1042b jdk-9+124 a32d419d73fe881a935b567c57dab9bfe3ed5f92 jdk-9+125 ee90c69a18409533df8f7b602044bf966a28381a jdk-9+126 +ff07be6106fa56b72c163244f45a3ecb4c995564 jdk-9+127 From 1aba64dc854b1be3f642918cbe71a1cb46ffc2a2 Mon Sep 17 00:00:00 2001 From: Alejandro Murillo Date: Thu, 14 Jul 2016 15:47:47 +0000 Subject: [PATCH 25/44] Added tag jdk-9+127 for changeset bf58ed488873 --- langtools/.hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/langtools/.hgtags b/langtools/.hgtags index 7677d43f11d..04743efa5fc 100644 --- a/langtools/.hgtags +++ b/langtools/.hgtags @@ -369,3 +369,4 @@ d0c742ddfb01ebe427720798c4c8335023ae20f8 jdk-9+123 26aa3caa778eab1c931910149c414783ee83bce7 jdk-9+124 2d65e127e93d5ff0df61bf78e57d7f46a2f1edeb jdk-9+125 ea4eea2997b9e2f26cd7965839921710ff4065c8 jdk-9+126 +a42768b48cb0c5af9063e12093975baeeca3b5fa jdk-9+127 From a78d1e0b449986f671c220b58bb54a394a056e54 Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Thu, 14 Jul 2016 11:57:15 -0600 Subject: [PATCH 26/44] 8161013: Never treat anonymous classes as 'final' Reviewed-by: vromero --- .../com/sun/tools/javac/comp/Check.java | 4 - .../com/sun/tools/javac/jvm/ClassWriter.java | 2 - .../AnonymousClass/AnonymousClassFlags.java | 139 ++++++++++++++++++ .../tools/javac/cast/6219964/T6219964.java | 6 +- .../tools/javac/cast/6219964/T6219964.out | 2 - 5 files changed, 142 insertions(+), 11 deletions(-) create mode 100644 langtools/test/tools/javac/AnonymousClass/AnonymousClassFlags.java delete mode 100644 langtools/test/tools/javac/cast/6219964/T6219964.out diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index 894f73af967..64e01b0d735 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -1152,10 +1152,6 @@ public class Check { 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 --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java index 4abefda20b0..73b3803e892 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java @@ -1040,7 +1040,6 @@ public class ClassWriter extends ClassFile { 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 @@ public class ClassWriter extends ClassFile { 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 --git a/langtools/test/tools/javac/AnonymousClass/AnonymousClassFlags.java b/langtools/test/tools/javac/AnonymousClass/AnonymousClassFlags.java new file mode 100644 index 00000000000..bfe31eb14d9 --- /dev/null +++ b/langtools/test/tools/javac/AnonymousClass/AnonymousClassFlags.java @@ -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 --git a/langtools/test/tools/javac/cast/6219964/T6219964.java b/langtools/test/tools/javac/cast/6219964/T6219964.java index 6372f77435c..2e7a208b305 100644 --- a/langtools/test/tools/javac/cast/6219964/T6219964.java +++ b/langtools/test/tools/javac/cast/6219964/T6219964.java @@ -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 --git a/langtools/test/tools/javac/cast/6219964/T6219964.out b/langtools/test/tools/javac/cast/6219964/T6219964.out deleted file mode 100644 index ef807150d9e..00000000000 --- a/langtools/test/tools/javac/cast/6219964/T6219964.out +++ /dev/null @@ -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 From 1a9a1d8961d83bc65bbdad2848780ef274518ab9 Mon Sep 17 00:00:00 2001 From: Andrey Nazarov Date: Thu, 14 Jul 2016 11:14:22 -0700 Subject: [PATCH 27/44] 8161132: JShell tests: jdk/jshell/KullaCompletenessStressTest.java should pass if jdk.shell sources are not provided Reviewed-by: rfield --- .../test/jdk/jshell/KullaCompletenessStressTest.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/langtools/test/jdk/jshell/KullaCompletenessStressTest.java b/langtools/test/jdk/jshell/KullaCompletenessStressTest.java index fe90de0d5c9..e79e043dd10 100644 --- a/langtools/test/jdk/jshell/KullaCompletenessStressTest.java +++ b/langtools/test/jdk/jshell/KullaCompletenessStressTest.java @@ -37,10 +37,6 @@ import org.testng.annotations.Test; 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 @@ public class KullaCompletenessStressTest extends CompletenessStressTest { } 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}; + } } } From 517db2dadc28393ede154841cd88e7a9754b8e8f Mon Sep 17 00:00:00 2001 From: Jonathan Gibbons Date: Thu, 14 Jul 2016 13:30:56 -0700 Subject: [PATCH 28/44] 8071484: fix comment in DCReference Reviewed-by: darcy --- .../share/classes/com/sun/tools/javac/tree/DCTree.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DCTree.java b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DCTree.java index 2293d49137d..20b82a675ca 100644 --- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DCTree.java +++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/DCTree.java @@ -578,7 +578,7 @@ public abstract class DCTree implements DocTree { 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; From 799d14348e7f335fbf49fb22077219f5dab94549 Mon Sep 17 00:00:00 2001 From: Valerie Peng Date: Fri, 15 Jul 2016 00:24:35 +0000 Subject: [PATCH 29/44] 8161171: Missed the make/common/Modules.gmk file when integrating JDK-8154191 Re-apply the same changes as reviewed under JDK-8154191. Reviewed-by: mchung --- make/common/Modules.gmk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/make/common/Modules.gmk b/make/common/Modules.gmk index 92cc11b95ac..bd4747891f8 100644 --- a/make/common/Modules.gmk +++ b/make/common/Modules.gmk @@ -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 @@ BOOT_MODULES += \ # to be deprivileged BOOT_MODULES += \ - java.smartcardio \ jdk.naming.rmi \ # @@ -104,6 +103,7 @@ PLATFORM_MODULES += \ PLATFORM_MODULES += \ java.compiler \ java.scripting \ + java.smartcardio \ java.sql \ java.sql.rowset \ jdk.accessibility \ From 05b3d5c63f6407aa85e8bc4c87179b8f97102c62 Mon Sep 17 00:00:00 2001 From: Vyom Tewari Date: Fri, 15 Jul 2016 14:06:50 +0530 Subject: [PATCH 30/44] 8151788: NullPointerException from ntlm.Client.type3 Reviewed-by: chegar, prappo, weijun --- .../classes/com/sun/security/ntlm/NTLM.java | 2 +- .../www/protocol/http/NULLTargetInfoTest.java | 58 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 jdk/test/sun/net/www/protocol/http/NULLTargetInfoTest.java diff --git a/jdk/src/java.base/share/classes/com/sun/security/ntlm/NTLM.java b/jdk/src/java.base/share/classes/com/sun/security/ntlm/NTLM.java index 0d13d83411c..cb558c6d072 100644 --- a/jdk/src/java.base/share/classes/com/sun/security/ntlm/NTLM.java +++ b/jdk/src/java.base/share/classes/com/sun/security/ntlm/NTLM.java @@ -169,7 +169,7 @@ class NTLM { byte[] readSecurityBuffer(int offset) throws NTLMException { int pos = readInt(offset+4); - if (pos == 0) return null; + if (pos == 0) return new byte[0]; try { return Arrays.copyOfRange( internal, pos, pos + readShort(offset)); diff --git a/jdk/test/sun/net/www/protocol/http/NULLTargetInfoTest.java b/jdk/test/sun/net/www/protocol/http/NULLTargetInfoTest.java new file mode 100644 index 00000000000..d8e88554f86 --- /dev/null +++ b/jdk/test/sun/net/www/protocol/http/NULLTargetInfoTest.java @@ -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; + } +} From 4b1893fad98a76850572abce293f2080799dfe77 Mon Sep 17 00:00:00 2001 From: Vyom Tewari Date: Fri, 15 Jul 2016 13:59:03 +0530 Subject: [PATCH 31/44] 8144692: HttpServer API: use of non-existant method in example in package Javadoc Reviewed-by: chegar, prappo --- .../share/classes/com/sun/net/httpserver/package-info.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jdk/src/jdk.httpserver/share/classes/com/sun/net/httpserver/package-info.java b/jdk/src/jdk.httpserver/share/classes/com/sun/net/httpserver/package-info.java index 9737f97cca7..f0af11d3959 100644 --- a/jdk/src/jdk.httpserver/share/classes/com/sun/net/httpserver/package-info.java +++ b/jdk/src/jdk.httpserver/share/classes/com/sun/net/httpserver/package-info.java @@ -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(); From 90e6b1dedda11d90d094e623ae6de9af41ca4144 Mon Sep 17 00:00:00 2001 From: Alan Bateman Date: Fri, 15 Jul 2016 17:31:30 +0100 Subject: [PATCH 32/44] 8161036: Fix copyright header Reviewed-by: mchung --- .../java.base/share/classes/java/lang/module/package-info.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jdk/src/java.base/share/classes/java/lang/module/package-info.java b/jdk/src/java.base/share/classes/java/lang/module/package-info.java index a1c44d68145..f641638cb2c 100644 --- a/jdk/src/java.base/share/classes/java/lang/module/package-info.java +++ b/jdk/src/java.base/share/classes/java/lang/module/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it From d46ee264dff6171a9024380a36515531026fc242 Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Fri, 15 Jul 2016 12:30:10 -0700 Subject: [PATCH 33/44] 8161413: Math.fma javadoc doesn't have @since 9 Add @since tag for float and double versions of fma(). Reviewed-by: rriggs --- jdk/src/java.base/share/classes/java/lang/Math.java | 4 ++++ jdk/src/java.base/share/classes/java/lang/StrictMath.java | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/jdk/src/java.base/share/classes/java/lang/Math.java b/jdk/src/java.base/share/classes/java/lang/Math.java index aa678b875ec..4261bd2c3f1 100644 --- a/jdk/src/java.base/share/classes/java/lang/Math.java +++ b/jdk/src/java.base/share/classes/java/lang/Math.java @@ -1613,6 +1613,8 @@ public final class Math { * @return (a × b + c) * computed, as if with unlimited range and precision, and rounded * once to the nearest {@code double} value + * + * @since 9 */ // @HotSpotIntrinsicCandidate public static double fma(double a, double b, double c) { @@ -1728,6 +1730,8 @@ public final class Math { * @return (a × b + c) * computed, as if with unlimited range and precision, and rounded * once to the nearest {@code float} value + * + * @since 9 */ // @HotSpotIntrinsicCandidate public static float fma(float a, float b, float c) { diff --git a/jdk/src/java.base/share/classes/java/lang/StrictMath.java b/jdk/src/java.base/share/classes/java/lang/StrictMath.java index 998fc1eb426..3ef67145d0c 100644 --- a/jdk/src/java.base/share/classes/java/lang/StrictMath.java +++ b/jdk/src/java.base/share/classes/java/lang/StrictMath.java @@ -1276,6 +1276,8 @@ public final class StrictMath { * @return (a × b + c) * computed, as if with unlimited range and precision, and rounded * once to the nearest {@code double} value + * + * @since 9 */ public static double fma(double a, double b, double c) { return Math.fma(a, b, c); @@ -1328,6 +1330,8 @@ public final class StrictMath { * @return (a × b + c) * computed, as if with unlimited range and precision, and rounded * once to the nearest {@code float} value + * + * @since 9 */ public static float fma(float a, float b, float c) { return Math.fma(a, b, c); From 9d9145a699e2fff02b7a375d0d1fe5932aa6b23b Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Fri, 15 Jul 2016 13:23:02 -0700 Subject: [PATCH 34/44] 8161455: Missing word in API documentation Change "file has created" to "file has been created" in two places. Reviewed-by: rriggs, alanb --- jdk/src/java.base/share/classes/java/nio/file/Files.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/jdk/src/java.base/share/classes/java/nio/file/Files.java b/jdk/src/java.base/share/classes/java/nio/file/Files.java index e1ab41b7759..3a6491440a7 100644 --- a/jdk/src/java.base/share/classes/java/nio/file/Files.java +++ b/jdk/src/java.base/share/classes/java/nio/file/Files.java @@ -3290,8 +3290,8 @@ public final class Files { * a size of {@code 0}. All bytes in the byte array are written to the file. * The method ensures that the file is closed when all bytes have been * written (or an I/O error or other runtime exception is thrown). If an I/O - * error occurs then it may do so after the file has created or truncated, - * or after some bytes have been written to the file. + * error occurs then it may do so after the file has been created or + * truncated, or after some bytes have been written to the file. * *

    Usage example: By default the method creates a new file or * overwrites an existing file. Suppose you instead want to append bytes @@ -3360,7 +3360,8 @@ public final class Files { * a size of {@code 0}. The method ensures that the file is closed when all * lines have been written (or an I/O error or other runtime exception is * thrown). If an I/O error occurs then it may do so after the file has - * created or truncated, or after some bytes have been written to the file. + * been created or truncated, or after some bytes have been written to the + * file. * * @param path * the path to the file From d4f4ebc1480056651c3ac33bdd59f987f092ba21 Mon Sep 17 00:00:00 2001 From: "J. Duke" Date: Wed, 5 Jul 2017 21:57:12 +0200 Subject: [PATCH 35/44] Added tag jdk-9+127 for changeset 8e45018bde9d --- .hgtags | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgtags b/.hgtags index a2481e251c9..dd3fabee47c 100644 --- a/.hgtags +++ b/.hgtags @@ -369,3 +369,4 @@ d53037a90c441cb528dc41c30827985de0e67c62 jdk-9+123 2a5697a98620c4f40e4a1a71478464399b8878de jdk-9+124 3aa52182b3ad7c5b3a61cf05a59dd07e4c5884e5 jdk-9+125 03e7b2c5ae345be3caf981d76ceb3efe5ff447f8 jdk-9+126 +8e45018bde9de4ad15b972ae62874bba52dba2d5 jdk-9+127 From aa81d50820b4ef9b093f1082d9b562677c30a747 Mon Sep 17 00:00:00 2001 From: Doug Lea Date: Fri, 15 Jul 2016 13:51:43 -0700 Subject: [PATCH 36/44] 8159924: Various improvements to StampedLock code Reviewed-by: martin, psandoz, rriggs, plevart, dfuchs --- .../util/concurrent/locks/StampedLock.java | 442 +++++++++--------- .../util/concurrent/tck/StampedLockTest.java | 219 ++++++++- 2 files changed, 419 insertions(+), 242 deletions(-) diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java index 0e8252587b5..b90aef6206e 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java @@ -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 @@ import java.util.concurrent.TimeUnit; * 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 @@ import java.util.concurrent.TimeUnit; * 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 @@ public class StampedLock implements java.io.Serializable {
          * 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 @@ public class StampedLock implements java.io.Serializable {
          * 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 @@ public class StampedLock implements java.io.Serializable {
         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 @@ public class StampedLock implements java.io.Serializable {
          * 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 @@ public class StampedLock implements java.io.Serializable {
          *
          * @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 @@ public class StampedLock implements java.io.Serializable {
          * 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 @@ public class StampedLock implements java.io.Serializable {
          * 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 @@ public class StampedLock implements java.io.Serializable {
          *
          * @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 @@ public class StampedLock implements java.io.Serializable {
             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 @@ public class StampedLock implements java.io.Serializable {
          * 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 @@ public class StampedLock implements java.io.Serializable {
          * 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,10 +546,28 @@ public class StampedLock implements java.io.Serializable {
          * 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 @@ public class StampedLock implements java.io.Serializable {
          * @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 @@ public class StampedLock implements java.io.Serializable {
          * @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 @@ public class StampedLock implements java.io.Serializable {
          * @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 @@ public class StampedLock implements java.io.Serializable {
                 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 @@ public class StampedLock implements java.io.Serializable {
                     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 @@ public class StampedLock implements java.io.Serializable {
          * @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;
    -            }
    -            else if (a != 0L && a < WBIT)
                     return stamp;
    -            else
    -                break;
    +            }
             }
             return 0L;
         }
    @@ -693,29 +715,22 @@ public class StampedLock implements java.io.Serializable {
          * @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 @@ public class StampedLock implements java.io.Serializable {
          *
          * @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 @@ public class StampedLock implements java.io.Serializable {
          *
          * @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 @@ public class StampedLock implements java.io.Serializable {
          * 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 class StampedLock implements java.io.Serializable {
          */
         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 @@ public class StampedLock implements java.io.Serializable {
         // 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 @@ public class StampedLock implements java.io.Serializable {
         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 @@ public class StampedLock implements java.io.Serializable {
         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 @@ public class StampedLock implements java.io.Serializable {
                     }
                     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 @@ public class StampedLock implements java.io.Serializable {
         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 @@ public class StampedLock implements java.io.Serializable {
             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 @@ public class StampedLock implements java.io.Serializable {
                         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 @@ public class StampedLock implements java.io.Serializable {
                                 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 @@ public class StampedLock implements java.io.Serializable {
                             (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 @@ public class StampedLock implements java.io.Serializable {
                         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 @@ public class StampedLock implements java.io.Serializable {
                 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 @@ public class StampedLock implements java.io.Serializable {
                         }
                         else if (m >= WBIT) {
                             if (spins > 0) {
    -                            if (LockSupport.nextSecondarySeed() >= 0)
    -                                --spins;
    +                            --spins;
    +                            Thread.onSpinWait();
                             }
                             else {
                                 if (spins == 0) {
    @@ -1170,7 +1182,7 @@ public class StampedLock implements java.io.Serializable {
                 }
                 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 @@ public class StampedLock implements java.io.Serializable {
                 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 @@ public class StampedLock implements java.io.Serializable {
                                 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 @@ public class StampedLock implements java.io.Serializable {
                     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 @@ public class StampedLock implements java.io.Serializable {
                             (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 @@ public class StampedLock implements java.io.Serializable {
                         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 @@ public class StampedLock implements java.io.Serializable {
          * 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 @@ public class StampedLock implements java.io.Serializable {
                 // 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 @@ public class StampedLock implements java.io.Serializable {
                 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 @@ public class StampedLock implements java.io.Serializable {
                                 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 @@ public class StampedLock implements java.io.Serializable {
             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 --git a/jdk/test/java/util/concurrent/tck/StampedLockTest.java b/jdk/test/java/util/concurrent/tck/StampedLockTest.java
    index 84026409d7b..cf4f271768b 100644
    --- a/jdk/test/java/util/concurrent/tck/StampedLockTest.java
    +++ b/jdk/test/java/util/concurrent/tck/StampedLockTest.java
    @@ -745,28 +745,41 @@ public class StampedLockTest extends JSR166TestCase {
         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 class StampedLockTest extends JSR166TestCase {
         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 class StampedLockTest extends JSR166TestCase {
         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 @@ public class StampedLockTest extends JSR166TestCase {
             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);
    +    }
    +
     }
    
    From 7fa43fa58f106ea8d1b78be630df1ae53c52043b Mon Sep 17 00:00:00 2001
    From: Doug Lea 
    Date: Fri, 15 Jul 2016 13:55:51 -0700
    Subject: [PATCH 37/44] 8157523: Various improvements to
     ForkJoin/SubmissionPublisher code
    
    Reviewed-by: martin, psandoz, rriggs, plevart, dfuchs
    ---
     .../java/util/concurrent/ForkJoinPool.java    | 2866 ++++++++---------
     .../java/util/concurrent/ForkJoinTask.java    |   62 +-
     .../util/concurrent/ForkJoinWorkerThread.java |   67 +-
     .../util/concurrent/SubmissionPublisher.java  |  227 +-
     .../util/concurrent/ThreadLocalRandom.java    |   60 +-
     5 files changed, 1453 insertions(+), 1829 deletions(-)
    
    diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java b/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java
    index 6ec5e6cba7e..9cc3aef305b 100644
    --- a/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java
    +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java
    @@ -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.Arrays;
     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 @@ import java.util.concurrent.locks.LockSupport;
      * 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 @@ import java.util.concurrent.locks.LockSupport; * @since 1.7 * @author Doug Lea */ -@jdk.internal.vm.annotation.Contended public class ForkJoinPool extends AbstractExecutorService { /* @@ -229,10 +236,9 @@ public class ForkJoinPool extends AbstractExecutorService { * (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 @@ public class ForkJoinPool extends AbstractExecutorService { * 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 @@ public class ForkJoinPool extends AbstractExecutorService { * 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 @@ public class ForkJoinPool extends AbstractExecutorService { * 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 @@ public class ForkJoinPool extends AbstractExecutorService { * 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 @@ public class ForkJoinPool extends AbstractExecutorService { * 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 @@ public class ForkJoinPool extends AbstractExecutorService { * 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 @@ public class ForkJoinPool extends AbstractExecutorService { * 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 @@ public class ForkJoinPool extends AbstractExecutorService { * 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 @@ public class ForkJoinPool extends AbstractExecutorService { * 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 @@ public class ForkJoinPool extends AbstractExecutorService { * 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 @@ public class ForkJoinPool extends AbstractExecutorService { * 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 class ForkJoinPool extends AbstractExecutorService { 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 @@ public class ForkJoinPool extends AbstractExecutorService { } } - /** - * 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 @@ public class ForkJoinPool extends AbstractExecutorService { 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 @@ public class ForkJoinPool extends AbstractExecutorService { * 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 @@ public class ForkJoinPool extends AbstractExecutorService { * 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 @@ public class ForkJoinPool extends AbstractExecutorService { * @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 @@ public class ForkJoinPool extends AbstractExecutorService { */ 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 @@ public class ForkJoinPool extends AbstractExecutorService { 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 @@ public class ForkJoinPool extends AbstractExecutorService { 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 @@ public class ForkJoinPool extends AbstractExecutorService { * 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 @@ public class ForkJoinPool extends AbstractExecutorService { 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,115 +890,42 @@ public class ForkJoinPool extends AbstractExecutorService { 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; } } return false; } - /** - * 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 @@ public class ForkJoinPool extends AbstractExecutorService { } /** - * 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 @@ public class ForkJoinPool extends AbstractExecutorService { } /** - * 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(); - } - } - } - - /** - * If present, removes from queue and executes the given task, - * or any other cancelled task. Used only by awaitJoin. - * - * @return true if queue empty and task not known to be done - */ - 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 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); } - 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; + VarHandle.releaseFence(); + t.doExec(); } + break; } - if (task.status < 0) - return false; } } - return true; } /** - * Pops task if in the same CC computation as the given task, - * in either shared or owned mode. Used only by helpComplete. + * Tries to steal and run tasks within the target's + * computation until done, not found, or limit exceeded. + * + * @param task root of CountedCompleter computation + * @param limit max runs, or zero for no limit + * @return task status on exit */ - 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; + 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; } - U.putIntRelease(this, QLOCK, 0); - if (popped) - return t; + break; } } - else if (U.compareAndSwapObject(a, offset, - t, null)) { - top = s - 1; - return t; - } - break; } - else if ((r = r.completer) == null) // try parent - break; } + if ((status = task.status) < 0 || !help || + (limit != 0 && --limit == 0)) + break; } } - return null; + return status; + } + + // Operations on shared queues + + /** + * Tries to lock shared queue by CASing phase field. + */ + final boolean tryLockSharedQueue() { + return PHASE.compareAndSet(this, 0, QLOCK); } /** - * 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 tryUnpush. */ - 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 - } - else - h = 2; // lost CAS - break; - } - else if ((r = r.completer) == null) { - h = -1; // unmatched - 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); } } - else - h = b | Integer.MIN_VALUE; // to sense movement on re-poll - return h; + return popped; + } + + /** + * Shared version of localHelpCC. + */ + 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; + } + } + } + } + if ((status = task.status) < 0 || !help || + (limit != 0 && --limit == 0)) + break; + } + } + return status; } /** @@ -1334,27 +1123,18 @@ public class ForkJoinPool extends AbstractExecutorService { */ 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 @@ public class ForkJoinPool extends AbstractExecutorService { /** * 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 @@ public class ForkJoinPool extends AbstractExecutorService { // 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 @@ public class ForkJoinPool extends AbstractExecutorService { /* * 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 @@ public class ForkJoinPool extends AbstractExecutorService { * (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 @@ public class ForkJoinPool extends AbstractExecutorService { 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 - 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 @@ public class ForkJoinPool extends AbstractExecutorService { * 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 @@ public class ForkJoinPool extends AbstractExecutorService { */ 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 @@ public class ForkJoinPool extends AbstractExecutorService { */ 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; + } + + 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; } - w.hint = s; // use as random seed - w.config = i | mode; - w.scanState = i | (s & 0x7fff0000); // random seq bits - ws[i] = w; } - } finally { - aux.unlock(); } + wt.setName(prefix.concat(Integer.toString(tid))); } - wt.setName(workerNamePrefix.concat(Integer.toString(i >>> 1))); return w; } @@ -1633,64 +1395,48 @@ public class ForkJoinPool extends AbstractExecutorService { */ 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; } } + phase = w.phase; } - if (w == null || (w.config & UNREGISTERED) == 0) { // else pre-adjusted + if (phase != QUIET) { // 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)))); + do {} while (!CTL.weakCompareAndSetVolatile + (this, c = ctl, ((RC_MASK & (c - RC_UNIT)) | + (TC_MASK & (c - TC_UNIT)) | + (SP_MASK & c)))); } - if (w != null) { - w.currentSteal = null; - w.qlock = -1; // ensure set + if (w != null) w.cancelAll(); // cancel remaining tasks - } - 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 (!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 @@ public class ForkJoinPool extends AbstractExecutorService { 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,502 +1466,194 @@ public class ForkJoinPool extends AbstractExecutorService { } /** - * 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 + * @return 1: block then adjust, -1: block without adjust, 0 : retry */ - 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 - */ - 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 - } - } - 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; + 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; } - } finally { - aux.unlock(); } + return 0; } - } - 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 ((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; + } + 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 + } + } } - 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; + 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 - 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; + throw new RejectedExecutionException( + "Thread limit exceeded replacing blocked worker"); } } } - 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); } } } } } - /** - * 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 @@ public class ForkJoinPool extends AbstractExecutorService { */ 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 (deadline == 0L) - ms = 0L; - else if ((ns = deadline - System.nanoTime()) <= 0L) - break; - else if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) <= 0L) - ms = 1L; - if (tryCompensate(w)) { - task.internalWait(ms); - U.getAndAddLong(this, CTL, AC_UNIT); + 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; } - w.currentJoin = prevJoin; + if ((s = task.status) < 0) + break; + else if (!nonempty) { + long ms, ns; int block; + if (deadline == 0L) + ms = 0L; // untimed + else if ((ns = deadline - System.nanoTime()) <= 0L) + break; // timeout + else if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) <= 0L) + ms = 1L; // avoid 0 for timed wait + if ((block = tryCompensate(w)) != 0) { + task.internalWait(ms); + CTL.getAndAdd(this, (block > 0) ? RC_UNIT : 0L); + } + s = task.status; + } } } 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; } } @@ -2284,59 +1829,161 @@ public class ForkJoinPool extends AbstractExecutorService { return null; } - /** - * 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 @@ public class ForkJoinPool extends AbstractExecutorService { */ 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 @@ public class ForkJoinPool extends AbstractExecutorService { return 0; } - // Termination + // Termination /** * Possibly initiates and/or completes termination. @@ -2404,198 +2053,86 @@ public class ForkJoinPool extends AbstractExecutorService { * @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 (((md = mode) & TERMINATED) != 0 || + (workQueues == ws && oldSum == (oldSum = checkSum))) + break; } - if (oldSum == (oldSum = checkSum)) + if ((md & TERMINATED) != 0) break; - } - - if ((short)(ctl >>> TC_SHIFT) + (config & SMASK) <= 0) { - runState = (STARTED | SHUTDOWN | STOP | TERMINATED); // final write - synchronized (this) { - notifyAll(); // for awaitTermination - } - } - - 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; + 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; - } - } - } - } - - /** - * 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 @@ public class ForkJoinPool extends AbstractExecutorService { /** * 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 class ForkJoinPool extends AbstractExecutorService { */ 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 @@ public class ForkJoinPool extends AbstractExecutorService { * 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 @@ public class ForkJoinPool extends AbstractExecutorService { ForkJoinWorkerThreadFactory factory, UncaughtExceptionHandler handler, boolean asyncMode) { - this(checkParallelism(parallelism), - checkFactory(factory), - handler, - asyncMode ? FIFO_QUEUE : LIFO_QUEUE, - "ForkJoinPool-" + nextPoolId() + "-worker-"); - 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; + this(parallelism, factory, handler, asyncMode, + 0, MAX_CAP, 1, null, DEFAULT_KEEPALIVE, TimeUnit.MILLISECONDS); } /** - * Creates a {@code ForkJoinPool} with the given parameters, without - * any security checks or parameter validation. Invoked directly by - * makeCommonPool. + * 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 */ - private ForkJoinPool(int parallelism, - ForkJoinWorkerThreadFactory factory, - UncaughtExceptionHandler handler, - int mode, - String workerNamePrefix) { - this.workerNamePrefix = workerNamePrefix; + 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.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 = saturate; + this.keepAlive = ms; + this.bounds = b; + this.mode = m; + this.ctl = c; + checkPermission(); + } + + /** + * 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) { + } + + 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.saturate = null; + this.keepAlive = DEFAULT_KEEPALIVE; + this.bounds = b; + this.mode = parallelism; + this.ctl = c; } /** @@ -2876,8 +2562,8 @@ public class ForkJoinPool extends AbstractExecutorService { * @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 @@ public class ForkJoinPool extends AbstractExecutorService { * @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 @@ public class ForkJoinPool extends AbstractExecutorService { * @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 @@ public class ForkJoinPool extends AbstractExecutorService { * @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 @@ public class ForkJoinPool extends AbstractExecutorService { * @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 @@ public class ForkJoinPool extends AbstractExecutorService { * @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 @@ public class ForkJoinPool extends AbstractExecutorService { * @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 class ForkJoinPool extends AbstractExecutorService { 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 @@ public class ForkJoinPool extends AbstractExecutorService { 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 @@ public class ForkJoinPool extends AbstractExecutorService { * @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 @@ public class ForkJoinPool extends AbstractExecutorService { * @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 @@ public class ForkJoinPool extends AbstractExecutorService { * @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 @@ public class ForkJoinPool extends AbstractExecutorService { 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 @@ public class ForkJoinPool extends AbstractExecutorService { 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 @@ public class ForkJoinPool extends AbstractExecutorService { } } + /** + * 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 @@ public class ForkJoinPool extends AbstractExecutorService { 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 @@ public class ForkJoinPool extends AbstractExecutorService { common = java.security.AccessController.doPrivileged (new java.security.PrivilegedAction() { - public ForkJoinPool run() { return makeCommonPool(); }}); + public ForkJoinPool run() { + return new ForkJoinPool((byte)0); }}); - // report 1 even if threads disabled - COMMON_PARALLELISM = Math.max(common.config & SMASK, 1); - } - - /** - * 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java b/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java index fa14cbf984a..bcd51f7d6a2 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java @@ -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 @@ import java.util.concurrent.locks.ReentrantLock; * 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 @@ public abstract class ForkJoinTask implements Future, Serializable { 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 @@ public abstract class ForkJoinTask implements Future, Serializable { 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 @@ public abstract class ForkJoinTask implements Future, Serializable { 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 @@ public abstract class ForkJoinTask implements Future, Serializable { 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 @@ public abstract class ForkJoinTask implements Future, Serializable { // 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 @@ public abstract class ForkJoinTask implements Future, Serializable { 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 @@ public abstract class ForkJoinTask implements Future, Serializable { 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 @@ public abstract class ForkJoinTask implements Future, Serializable { 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 abstract class ForkJoinTask implements Future, Serializable { */ 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 @@ public abstract class ForkJoinTask implements Future, Serializable { 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 @@ public abstract class ForkJoinTask implements Future, Serializable { 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java b/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java index 9629e83bd3b..5e56fe1b05e 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java @@ -66,8 +66,9 @@ public class ForkJoinWorkerThread extends Thread { * 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 @@ public class ForkJoinWorkerThread extends Thread { 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); } @@ -170,38 +171,12 @@ public class ForkJoinWorkerThread extends Thread { } } - /** - * 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 @@ public class ForkJoinWorkerThread extends Thread { 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 @@ public class ForkJoinWorkerThread extends Thread { @Override // to erase ThreadLocals void afterTopLevelExec() { - eraseThreadLocals(); + ThreadLocalRandom.eraseThreadLocals(this); } @Override // to always report system loader @@ -241,33 +216,5 @@ public class ForkJoinWorkerThread extends Thread { 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java b/jdk/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java index 0e84bf4e050..8ba78b3aeb8 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java @@ -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 @@ public class SubmissionPublisher implements Flow.Publisher, /** 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 @@ public class SubmissionPublisher implements Flow.Publisher, */ @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 @@ public class SubmissionPublisher implements Flow.Publisher, * 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 @@ public class SubmissionPublisher implements Flow.Publisher, 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 @@ public class SubmissionPublisher implements Flow.Publisher, 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 @@ public class SubmissionPublisher implements Flow.Publisher, 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,100 +1135,43 @@ public class SubmissionPublisher implements Flow.Publisher, * 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(); } return stat; } - /** - * 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(); - } - helpDepth = 0; - } - } - if (stat == 0 && (stat = offer(item)) == 0 && - (timeout = nanos) > 0L) { + int stat; + if ((stat = offer(item)) == 0 && (timeout = nanos) > 0L) { putItem = item; - 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(); } @@ -1249,22 +1191,20 @@ public class SubmissionPublisher implements Flow.Publisher, } 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 @@ public class SubmissionPublisher implements Flow.Publisher, 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 @@ public class SubmissionPublisher implements Flow.Publisher, 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 @@ public class SubmissionPublisher implements Flow.Publisher, 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 @@ public class SubmissionPublisher implements Flow.Publisher, 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 @@ public class SubmissionPublisher implements Flow.Publisher, 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 @@ public class SubmissionPublisher implements Flow.Publisher, 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 @@ public class SubmissionPublisher implements Flow.Publisher, 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 @@ public class SubmissionPublisher implements Flow.Publisher, 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 @@ public class SubmissionPublisher implements Flow.Publisher, } } 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 @@ public class SubmissionPublisher implements Flow.Publisher, 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 @@ public class SubmissionPublisher implements Flow.Publisher, } } } - 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 @@ public class SubmissionPublisher implements Flow.Publisher, 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 @@ public class SubmissionPublisher implements Flow.Publisher, 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java b/jdk/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java index 2af74b20961..e64930755bf 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java @@ -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.DoubleStream; 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 @@ public class ThreadLocalRandom extends Random { * 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 @@ public class ThreadLocalRandom extends Random { 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 @@ public class ThreadLocalRandom extends Random { 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 @@ public class ThreadLocalRandom extends Random { (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); } From a09ddefd05310d1631745c8036f08507bfb28f62 Mon Sep 17 00:00:00 2001 From: Doug Lea Date: Fri, 15 Jul 2016 13:59:58 -0700 Subject: [PATCH 38/44] 8157522: Performance improvements to CompletableFuture Reviewed-by: martin, psandoz, rriggs, plevart, dfuchs --- .../util/concurrent/CompletableFuture.java | 1153 +++++++++-------- .../concurrent/tck/CompletableFutureTest.java | 484 +++++-- 2 files changed, 1003 insertions(+), 634 deletions(-) diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java b/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java index a558cdad2f3..7f3a6200953 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java @@ -35,6 +35,8 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.concurrent.locks.LockSupport; import java.util.function.BiConsumer; import java.util.function.BiFunction; @@ -149,26 +151,29 @@ public class CompletableFuture implements Future, CompletionStage { * applies across normal vs exceptional outcomes, sync vs async * actions, binary triggers, and various forms of completions. * - * Non-nullness of field result (set via CAS) indicates done. An - * AltResult is used to box null as a result, as well as to hold - * exceptions. Using a single field makes completion simple to - * detect and trigger. Encoding and decoding is straightforward - * but adds to the sprawl of trapping and associating exceptions - * with targets. Minor simplifications rely on (static) NIL (to - * box null results) being the only AltResult with a null - * exception field, so we don't usually need explicit comparisons. - * Even though some of the generics casts are unchecked (see - * SuppressWarnings annotations), they are placed to be - * appropriate even if checked. + * Non-nullness of volatile field "result" indicates done. It may + * be set directly if known to be thread-confined, else via CAS. + * An AltResult is used to box null as a result, as well as to + * hold exceptions. Using a single field makes completion simple + * to detect and trigger. Result encoding and decoding is + * straightforward but tedious and adds to the sprawl of trapping + * and associating exceptions with targets. Minor simplifications + * rely on (static) NIL (to box null results) being the only + * AltResult with a null exception field, so we don't usually need + * explicit comparisons. Even though some of the generics casts + * are unchecked (see SuppressWarnings annotations), they are + * placed to be appropriate even if checked. * * Dependent actions are represented by Completion objects linked * as Treiber stacks headed by field "stack". There are Completion - * classes for each kind of action, grouped into single-input - * (UniCompletion), two-input (BiCompletion), projected - * (BiCompletions using either (not both) of two inputs), shared - * (CoCompletion, used by the second of two sources), zero-input - * source actions, and Signallers that unblock waiters. Class - * Completion extends ForkJoinTask to enable async execution + * classes for each kind of action, grouped into: + * - single-input (UniCompletion), + * - two-input (BiCompletion), + * - projected (BiCompletions using exactly one of two inputs), + * - shared (CoCompletion, used by the second of two sources), + * - zero-input source actions, + * - Signallers that unblock waiters. + * Class Completion extends ForkJoinTask to enable async execution * (adding no space overhead because we exploit its "tag" methods * to maintain claims). It is also declared as Runnable to allow * usage with arbitrary executors. @@ -184,7 +189,7 @@ public class CompletableFuture implements Future, CompletionStage { * encounter layers of adapters in common usages. * * * Boolean CompletableFuture method x(...) (for example - * uniApply) takes all of the arguments needed to check that an + * biApply) takes all of the arguments needed to check that an * action is triggerable, and then either runs the action or * arranges its async execution by executing its Completion * argument, if present. The method returns true if known to be @@ -194,24 +199,29 @@ public class CompletableFuture implements Future, CompletionStage { * method with its held arguments, and on success cleans up. * The mode argument allows tryFire to be called twice (SYNC, * then ASYNC); the first to screen and trap exceptions while - * arranging to execute, and the second when called from a - * task. (A few classes are not used async so take slightly - * different forms.) The claim() callback suppresses function - * invocation if already claimed by another thread. + * arranging to execute, and the second when called from a task. + * (A few classes are not used async so take slightly different + * forms.) The claim() callback suppresses function invocation + * if already claimed by another thread. + * + * * Some classes (for example UniApply) have separate handling + * code for when known to be thread-confined ("now" methods) and + * for when shared (in tryFire), for efficiency. * * * CompletableFuture method xStage(...) is called from a public - * stage method of CompletableFuture x. It screens user + * stage method of CompletableFuture f. It screens user * arguments and invokes and/or creates the stage object. If - * not async and x is already complete, the action is run - * immediately. Otherwise a Completion c is created, pushed to - * x's stack (unless done), and started or triggered via - * c.tryFire. This also covers races possible if x completes - * while pushing. Classes with two inputs (for example BiApply) - * deal with races across both while pushing actions. The - * second completion is a CoCompletion pointing to the first, - * shared so that at most one performs the action. The - * multiple-arity methods allOf and anyOf do this pairwise to - * form trees of completions. + * not async and already triggerable, the action is run + * immediately. Otherwise a Completion c is created, and + * submitted to the executor if triggerable, or pushed onto f's + * stack if not. Completion actions are started via c.tryFire. + * We recheck after pushing to a source future's stack to cover + * possible races if the source completes while pushing. + * Classes with two inputs (for example BiApply) deal with races + * across both while pushing actions. The second completion is + * a CoCompletion pointing to the first, shared so that at most + * one performs the action. The multiple-arity methods allOf + * and anyOf do this pairwise to form trees of completions. * * Note that the generic type parameters of methods vary according * to whether "this" is a source, dependent, or completion. @@ -236,29 +246,30 @@ public class CompletableFuture implements Future, CompletionStage { * pointing back to its sources. So we null out fields as soon as * possible. The screening checks needed anyway harmlessly ignore * null arguments that may have been obtained during races with - * threads nulling out fields. We also try to unlink fired - * Completions from stacks that might never be popped (see method - * postFire). Completion fields need not be declared as final or - * volatile because they are only visible to other threads upon - * safe publication. + * threads nulling out fields. We also try to unlink non-isLive + * (fired or cancelled) Completions from stacks that might + * otherwise never be popped: Method cleanStack always unlinks non + * isLive completions from the head of stack; others may + * occasionally remain if racing with other cancellations or + * removals. + * + * Completion fields need not be declared as final or volatile + * because they are only visible to other threads upon safe + * publication. */ volatile Object result; // Either the result or boxed AltResult volatile Completion stack; // Top of Treiber stack of dependent actions final boolean internalComplete(Object r) { // CAS from null to r - return U.compareAndSwapObject(this, RESULT, null, r); - } - - final boolean casStack(Completion cmp, Completion val) { - return U.compareAndSwapObject(this, STACK, cmp, val); + return RESULT.compareAndSet(this, null, r); } /** Returns true if successfully pushed c onto stack. */ final boolean tryPushStack(Completion c) { Completion h = stack; - lazySetNext(c, h); - return U.compareAndSwapObject(this, STACK, h, c); + NEXT.set(c, h); // CAS piggyback + return STACK.compareAndSet(this, h, c); } /** Unconditionally pushes c onto stack, retrying if necessary. */ @@ -278,8 +289,7 @@ public class CompletableFuture implements Future, CompletionStage { /** Completes with the null value, unless already completed. */ final boolean completeNull() { - return U.compareAndSwapObject(this, RESULT, null, - NIL); + return RESULT.compareAndSet(this, null, NIL); } /** Returns the encoding of the given non-exceptional value. */ @@ -289,8 +299,7 @@ public class CompletableFuture implements Future, CompletionStage { /** Completes with a non-exceptional result, unless already completed. */ final boolean completeValue(T t) { - return U.compareAndSwapObject(this, RESULT, null, - (t == null) ? NIL : t); + return RESULT.compareAndSet(this, null, (t == null) ? NIL : t); } /** @@ -304,8 +313,7 @@ public class CompletableFuture implements Future, CompletionStage { /** Completes with an exceptional result, unless already completed. */ final boolean completeThrowable(Throwable x) { - return U.compareAndSwapObject(this, RESULT, null, - encodeThrowable(x)); + return RESULT.compareAndSet(this, null, encodeThrowable(x)); } /** @@ -332,8 +340,7 @@ public class CompletableFuture implements Future, CompletionStage { * existing CompletionException. */ final boolean completeThrowable(Throwable x, Object r) { - return U.compareAndSwapObject(this, RESULT, null, - encodeThrowable(x, r)); + return RESULT.compareAndSet(this, null, encodeThrowable(x, r)); } /** @@ -351,10 +358,11 @@ public class CompletableFuture implements Future, CompletionStage { */ static Object encodeRelay(Object r) { Throwable x; - return (((r instanceof AltResult) && - (x = ((AltResult)r).ex) != null && - !(x instanceof CompletionException)) ? - new AltResult(new CompletionException(x)) : r); + if (r instanceof AltResult + && (x = ((AltResult)r).ex) != null + && !(x instanceof CompletionException)) + r = new AltResult(new CompletionException(x)); + return r; } /** @@ -362,14 +370,13 @@ public class CompletableFuture implements Future, CompletionStage { * If exceptional, r is first coerced to a CompletionException. */ final boolean completeRelay(Object r) { - return U.compareAndSwapObject(this, RESULT, null, - encodeRelay(r)); + return RESULT.compareAndSet(this, null, encodeRelay(r)); } /** * Reports result using Future.get conventions. */ - private static T reportGet(Object r) + private static Object reportGet(Object r) throws InterruptedException, ExecutionException { if (r == null) // by convention below, null means interrupted throw new InterruptedException(); @@ -384,14 +391,13 @@ public class CompletableFuture implements Future, CompletionStage { x = cause; throw new ExecutionException(x); } - @SuppressWarnings("unchecked") T t = (T) r; - return t; + return r; } /** * Decodes outcome to return result or throw unchecked exception. */ - private static T reportJoin(Object r) { + private static Object reportJoin(Object r) { if (r instanceof AltResult) { Throwable x; if ((x = ((AltResult)r).ex) == null) @@ -402,8 +408,7 @@ public class CompletableFuture implements Future, CompletionStage { throw (CompletionException)x; throw new CompletionException(x); } - @SuppressWarnings("unchecked") T t = (T) r; - return t; + return r; } /* ------------- Async task preliminaries -------------- */ @@ -449,12 +454,6 @@ public class CompletableFuture implements Future, CompletionStage { static final int ASYNC = 1; static final int NESTED = -1; - /** - * Spins before blocking in waitingGet - */ - static final int SPINS = (Runtime.getRuntime().availableProcessors() > 1 ? - 1 << 8 : 0); - /* ------------- Base Completion classes and operations -------------- */ @SuppressWarnings("serial") @@ -479,10 +478,6 @@ public class CompletableFuture implements Future, CompletionStage { public final void setRawResult(Void v) {} } - static void lazySetNext(Completion c, Completion next) { - U.putObjectRelease(c, NEXT, next); - } - /** * Pops and tries to trigger all reachable dependents. Call only * when known to be done. @@ -497,40 +492,47 @@ public class CompletableFuture implements Future, CompletionStage { while ((h = f.stack) != null || (f != this && (h = (f = this).stack) != null)) { CompletableFuture d; Completion t; - if (f.casStack(h, t = h.next)) { + if (STACK.compareAndSet(f, h, t = h.next)) { if (t != null) { if (f != this) { pushStack(h); continue; } - h.next = null; // detach + NEXT.compareAndSet(h, t, null); // try to detach } f = (d = h.tryFire(NESTED)) == null ? this : d; } } } - /** Traverses stack and unlinks dead Completions. */ + /** Traverses stack and unlinks one or more dead Completions, if found. */ final void cleanStack() { - for (Completion p = null, q = stack; q != null;) { + Completion p = stack; + // ensure head of stack live + for (boolean unlinked = false;;) { + if (p == null) + return; + else if (p.isLive()) { + if (unlinked) + return; + else + break; + } + else if (STACK.weakCompareAndSetVolatile(this, p, (p = p.next))) + unlinked = true; + else + p = stack; + } + // try to unlink first non-live + for (Completion q = p.next; q != null;) { Completion s = q.next; if (q.isLive()) { p = q; q = s; - } - else if (p == null) { - casStack(q, s); - q = stack; - } - else { - p.next = s; - if (p.isLive()) - q = s; - else { - p = null; // restart - q = stack; - } - } + } else if (NEXT.weakCompareAndSetVolatile(p, q, s)) + break; + else + q = p.next; } } @@ -568,11 +570,20 @@ public class CompletableFuture implements Future, CompletionStage { final boolean isLive() { return dep != null; } } - /** Pushes the given completion (if it exists) unless done. */ - final void push(UniCompletion c) { + /** + * Pushes the given completion unless it completes while trying. + * Caller should first check that result is null. + */ + final void unipush(Completion c) { if (c != null) { - while (result == null && !tryPushStack(c)) - lazySetNext(c, null); // clear on failure + while (!tryPushStack(c)) { + if (result != null) { + NEXT.set(c, null); + break; + } + } + if (result != null) + c.tryFire(SYNC); } } @@ -583,9 +594,10 @@ public class CompletableFuture implements Future, CompletionStage { */ final CompletableFuture postFire(CompletableFuture a, int mode) { if (a != null && a.stack != null) { - if (a.result == null) + Object r; + if ((r = a.result) == null) a.cleanStack(); - else if (mode >= 0) + if (mode >= 0 && (r != null || a.result != null)) a.postComplete(); } if (result != null && stack != null) { @@ -607,48 +619,65 @@ public class CompletableFuture implements Future, CompletionStage { } final CompletableFuture tryFire(int mode) { CompletableFuture d; CompletableFuture a; - if ((d = dep) == null || - !d.uniApply(a = src, fn, mode > 0 ? null : this)) + Object r; Throwable x; Function 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 @@ public class CompletableFuture implements Future, CompletionStage { } final CompletableFuture tryFire(int mode) { CompletableFuture d; CompletableFuture a; - if ((d = dep) == null || - !d.uniAccept(a = src, fn, mode > 0 ? null : this)) + Object r; Throwable x; Consumer 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 @@ public class CompletableFuture implements Future, CompletionStage { } final CompletableFuture tryFire(int mode) { CompletableFuture d; CompletableFuture a; - if ((d = dep) == null || - !d.uniRun(a = src, fn, mode > 0 ? null : this)) + Object r; Throwable x; Runnable f; + if ((d = dep) == null || (f = fn) == null + || (a = src) == null || (r = a.result) == null) return null; + if (d.result == null) { + if (r instanceof AltResult && (x = ((AltResult)r).ex) != null) + d.completeThrowable(x, r); + else + try { + if (mode <= 0 && !claim()) + return null; + else { + f.run(); + d.completeNull(); + } + } catch (Throwable ex) { + d.completeThrowable(ex); + } + } dep = null; src = null; fn = null; return d.postFire(a, mode); } } - final boolean uniRun(CompletableFuture a, Runnable f, UniRun c) { - Object r; Throwable x; - if (a == null || (r = a.result) == null || f == null) - return false; - if (result == null) { - if (r instanceof AltResult && (x = ((AltResult)r).ex) != null) - completeThrowable(x, r); - else - try { - if (c != null && !c.claim()) - return false; - f.run(); - completeNull(); - } catch (Throwable ex) { - completeThrowable(ex); - } - } - return true; - } - private CompletableFuture uniRunStage(Executor e, Runnable f) { if (f == null) throw new NullPointerException(); + Object r; + if ((r = result) != null) + return uniRunNow(r, e, f); CompletableFuture d = newIncompleteFuture(); - if (e != null || !d.uniRun(this, f, null)) { - UniRun c = new UniRun(e, d, this, f); - push(c); - c.tryFire(SYNC); - } + unipush(new UniRun(e, d, this, f)); + return d; + } + + private CompletableFuture uniRunNow(Object r, Executor e, Runnable f) { + Throwable x; + CompletableFuture d = newIncompleteFuture(); + if (r instanceof AltResult && (x = ((AltResult)r).ex) != null) + d.result = encodeThrowable(x, r); + else + try { + if (e != null) { + e.execute(new UniRun(null, d, this, f)); + } else { + f.run(); + d.result = NIL; + } + } catch (Throwable ex) { + d.result = encodeThrowable(ex); + } return d; } @@ -766,20 +828,20 @@ public class CompletableFuture implements Future, CompletionStage { } final CompletableFuture tryFire(int mode) { CompletableFuture d; CompletableFuture a; - if ((d = dep) == null || - !d.uniWhenComplete(a = src, fn, mode > 0 ? null : this)) + Object r; BiConsumer 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 @@ public class CompletableFuture implements Future, CompletionStage { 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 @@ public class CompletableFuture implements Future, CompletionStage { } final CompletableFuture tryFire(int mode) { CompletableFuture d; CompletableFuture a; - if ((d = dep) == null || - !d.uniHandle(a = src, fn, mode > 0 ? null : this)) + Object r; BiFunction 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 @@ public class CompletableFuture implements Future, CompletionStage { 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 @@ public class CompletableFuture implements Future, CompletionStage { final CompletableFuture tryFire(int mode) { // never ASYNC // assert mode != ASYNC; CompletableFuture d; CompletableFuture a; - if ((d = dep) == null || !d.uniExceptionally(a = src, fn, this)) + Object r; Function 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 @@ public class CompletableFuture implements Future, CompletionStage { Function f) { if (f == null) throw new NullPointerException(); CompletableFuture d = newIncompleteFuture(); - if (!d.uniExceptionally(this, f, null)) { - UniExceptionally c = new UniExceptionally(d, this, f); - push(c); - c.tryFire(SYNC); - } + Object r; + if ((r = result) == null) + unipush(new UniExceptionally(d, this, f)); + else + d.uniExceptionally(r, f, null); return d; } @SuppressWarnings("serial") - static final class UniRelay extends UniCompletion { // for Compose + static final class UniRelay extends UniCompletion { UniRelay(CompletableFuture dep, CompletableFuture src) { super(null, dep, src); } final CompletableFuture tryFire(int mode) { - CompletableFuture d; CompletableFuture a; - if ((d = dep) == null || !d.uniRelay(a = src)) + CompletableFuture d; CompletableFuture a; Object r; + if ((d = dep) == null + || (a = src) == null || (r = a.result) == null) return null; + if (d.result == null) + d.completeRelay(r); src = null; dep = null; return d.postFire(a, mode); } } - final boolean uniRelay(CompletableFuture a) { - Object r; - if (a == null || (r = a.result) == null) - return false; - if (result == null) // no need to claim - completeRelay(r); - return true; - } - private CompletableFuture uniCopyStage() { Object r; CompletableFuture d = newIncompleteFuture(); if ((r = result) != null) - d.completeRelay(r); - else { - UniRelay c = new UniRelay(d, this); - push(c); - c.tryFire(SYNC); - } + d.result = encodeRelay(r); + else + unipush(new UniRelay(d, this)); return d; } @@ -966,9 +1034,7 @@ public class CompletableFuture implements Future, CompletionStage { if ((r = result) != null) return new MinimalStage(encodeRelay(r)); MinimalStage d = new MinimalStage(); - UniRelay c = new UniRelay(d, this); - push(c); - c.tryFire(SYNC); + unipush(new UniRelay(d, this)); return d; } @@ -982,54 +1048,48 @@ public class CompletableFuture implements Future, CompletionStage { } final CompletableFuture tryFire(int mode) { CompletableFuture d; CompletableFuture a; - if ((d = dep) == null || - !d.uniCompose(a = src, fn, mode > 0 ? null : this)) + Function> 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(); - Object r, s; Throwable x; CompletableFuture d = newIncompleteFuture(); - if (e == null && (r = result) != null) { + Object r, s; Throwable x; + if ((r = result) == null) + unipush(new UniCompose(e, d, this, f)); + else if (e == null) { if (r instanceof AltResult) { if ((x = ((AltResult)r).ex) != null) { d.result = encodeThrowable(x, r); @@ -1041,21 +1101,20 @@ public class CompletableFuture implements Future, CompletionStage { @SuppressWarnings("unchecked") T t = (T) r; CompletableFuture g = f.apply(t).toCompletableFuture(); if ((s = g.result) != null) - d.completeRelay(s); + d.result = encodeRelay(s); else { - UniRelay c = new UniRelay(d, g); - g.push(c); - c.tryFire(SYNC); + g.unipush(new UniRelay(d, g)); } - return d; } catch (Throwable ex) { d.result = encodeThrowable(ex); - return d; } } - UniCompose c = new UniCompose(e, d, this, f); - push(c); - c.tryFire(SYNC); + else + try { + e.execute(new UniCompose(null, d, this, f)); + } catch (Throwable ex) { + d.result = encodeThrowable(ex); + } return d; } @@ -1085,21 +1144,28 @@ public class CompletableFuture implements Future, CompletionStage { } final boolean isLive() { BiCompletion c; - return (c = base) != null && c.dep != null; + return (c = base) != null + // && c.isLive() + && c.dep != null; } } - /** Pushes completion to this and b unless both done. */ + /** + * Pushes completion to this and b unless both done. + * Caller should first check that either result or b.result is null. + */ final void bipush(CompletableFuture b, BiCompletion c) { if (c != null) { - Object r; - while ((r = result) == null && !tryPushStack(c)) - lazySetNext(c, null); // clear on failure - if (b != null && b != this && b.result == null) { - Completion q = (r != null) ? c : new CoCompletion(c); - while (b.result == null && !b.tryPushStack(q)) - lazySetNext(q, null); // clear on failure + while (result == null) { + if (tryPushStack(c)) { + if (b.result == null) + b.unipush(new CoCompletion(c)); + else if (result != null) + c.tryFire(SYNC); + return; + } } + b.unipush(c); } } @@ -1107,9 +1173,10 @@ public class CompletableFuture implements Future, CompletionStage { final CompletableFuture postFire(CompletableFuture a, CompletableFuture b, int mode) { if (b != null && b.stack != null) { // clean second source - if (b.result == null) + Object r; + if ((r = b.result) == null) b.cleanStack(); - else if (mode >= 0) + if (mode >= 0 && (r != null || b.result != null)) b.postComplete(); } return postFire(a, mode); @@ -1127,22 +1194,21 @@ public class CompletableFuture implements Future, CompletionStage { CompletableFuture d; CompletableFuture a; CompletableFuture b; - if ((d = dep) == null || - !d.biApply(a = src, b = snd, fn, mode > 0 ? null : this)) + Object r, s; BiFunction 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 @@ public class CompletableFuture implements Future, CompletionStage { 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 @@ public class CompletableFuture implements Future, CompletionStage { 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 @@ public class CompletableFuture implements Future, CompletionStage { 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 @@ public class CompletableFuture implements Future, CompletionStage { 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 @@ public class CompletableFuture implements Future, CompletionStage { 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 @@ public class CompletableFuture implements Future, CompletionStage { 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 @@ public class CompletableFuture implements Future, CompletionStage { 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 @@ public class CompletableFuture implements Future, CompletionStage { 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 @@ public class CompletableFuture implements Future, CompletionStage { 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 @@ public class CompletableFuture implements Future, CompletionStage { 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 @@ public class CompletableFuture implements Future, CompletionStage { 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 @@ public class CompletableFuture implements Future, CompletionStage { 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 @@ public class CompletableFuture implements Future, CompletionStage { 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 class CompletableFuture implements Future, CompletionStage { 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 class CompletableFuture implements Future, CompletionStage { 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 @@ public class CompletableFuture implements Future, CompletionStage { 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; - } - else if (q == null) + if (q == null) { q = new Signaller(interruptible, 0L, 0L); + if (Thread.currentThread() instanceof ForkJoinWorkerThread) + ForkJoinPool.helpAsyncBlocker(defaultExecutor(), q); + } else if (!queued) queued = tryPushStack(q); else { @@ -1781,16 +1829,14 @@ public class CompletableFuture implements Future, CompletionStage { 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 @@ public class CompletableFuture implements Future, CompletionStage { 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 @@ public class CompletableFuture implements Future, CompletionStage { 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 @@ public class CompletableFuture implements Future, CompletionStage { * @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 @@ public class CompletableFuture implements Future, CompletionStage { * while waiting * @throws TimeoutException if the wait timed out */ + @SuppressWarnings("unchecked") public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { - Object r; long nanos = unit.toNanos(timeout); - return reportGet((r = result) == null ? timedGet(nanos) : r); + Object r; + if ((r = result) == null) + r = timedGet(nanos); + return (T) reportGet(r); } /** @@ -1981,9 +2037,12 @@ public class CompletableFuture implements Future, CompletionStage { * @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 @@ public class CompletableFuture implements Future, CompletionStage { * @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 @@ public class CompletableFuture implements Future, CompletionStage { 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 --git a/jdk/test/java/util/concurrent/tck/CompletableFutureTest.java b/jdk/test/java/util/concurrent/tck/CompletableFutureTest.java index 4149559adad..8c290bb82e4 100644 --- a/jdk/test/java/util/concurrent/tck/CompletableFutureTest.java +++ b/jdk/test/java/util/concurrent/tck/CompletableFutureTest.java @@ -57,6 +57,7 @@ import java.util.concurrent.ExecutionException; 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 @@ public class CompletableFutureTest extends JSR166TestCase { 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 @@ public class CompletableFutureTest extends JSR166TestCase { 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 @@ public class CompletableFutureTest extends JSR166TestCase { { 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 @@ public class CompletableFutureTest extends JSR166TestCase { { 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 @@ public class CompletableFutureTest extends JSR166TestCase { 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 @@ public class CompletableFutureTest extends JSR166TestCase { 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 @@ public class CompletableFutureTest extends JSR166TestCase { 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 @@ public class CompletableFutureTest extends JSR166TestCase { 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 @@ public class CompletableFutureTest extends JSR166TestCase { 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 @@ public class CompletableFutureTest extends JSR166TestCase { 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 @@ public class CompletableFutureTest extends JSR166TestCase { 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 @@ public class CompletableFutureTest extends JSR166TestCase { 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 @@ public class CompletableFutureTest extends JSR166TestCase { 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 @@ public class CompletableFutureTest extends JSR166TestCase { 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 @@ public class CompletableFutureTest extends JSR166TestCase { 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 @@ public class CompletableFutureTest extends JSR166TestCase { 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 @@ public class CompletableFutureTest extends JSR166TestCase { 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 @@ public class CompletableFutureTest extends JSR166TestCase { 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 class CompletableFutureTest extends JSR166TestCase { } } - 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]; @@ -3336,6 +3382,151 @@ public class CompletableFutureTest extends JSR166TestCase { assertEquals(0, exec.count.get()); } + /** + * 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. */ @@ -3659,12 +3850,25 @@ public class CompletableFutureTest extends JSR166TestCase { //--- 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 @@ public class CompletableFutureTest extends JSR166TestCase { 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.thenRun(y, noopRunnable)); + funs.add((y) -> m.thenAccept(y, noopConsumer)); + funs.add((y) -> m.thenApply(y, incFunction)); - 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.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, new Noop(m))); + 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 @@ public class CompletableFutureTest extends JSR166TestCase { 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 @@ public class CompletableFutureTest extends JSR166TestCase { 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 @@ public class CompletableFutureTest extends JSR166TestCase { 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 @@ public class CompletableFutureTest extends JSR166TestCase { }} /** - * 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 @@ public class CompletableFutureTest extends JSR166TestCase { .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 @@ public class CompletableFutureTest extends JSR166TestCase { else if (parameterTypes[i] == long.class) args[i] = 0L; } - try { - method.invoke(minimalStage, args); - bugs.add(method); - } - catch (java.lang.reflect.InvocationTargetException expected) { - if (! (expected.getCause() instanceof UnsupportedOperationException)) { + for (CompletionStage stage : stages) { + try { + method.invoke(stage, args); bugs.add(method); - // expected.getCause().printStackTrace(); } + 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 (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 @@ public class CompletableFutureTest extends JSR166TestCase { 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 @@ public class CompletableFutureTest extends JSR166TestCase { 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) -> { From 14d4754bddc86d28a88137c6778a9378590604e9 Mon Sep 17 00:00:00 2001 From: Doug Lea Date: Fri, 15 Jul 2016 14:04:09 -0700 Subject: [PATCH 39/44] 8080603: Replace Unsafe with VarHandle in java.util.concurrent classes 8153715: Use Unsafe.weakCompareAndSet in java.util.concurrent Reviewed-by: martin, psandoz, rriggs, plevart, dfuchs, shade --- .../share/classes/java/util/Queue.java | 1 - .../util/concurrent/ConcurrentHashMap.java | 15 +- .../concurrent/ConcurrentLinkedDeque.java | 188 ++++----- .../concurrent/ConcurrentLinkedQueue.java | 90 ++-- .../concurrent/ConcurrentSkipListMap.java | 47 +-- .../concurrent/ConcurrentSkipListSet.java | 13 +- .../util/concurrent/CopyOnWriteArrayList.java | 23 +- .../util/concurrent/CountedCompleter.java | 25 +- .../java/util/concurrent/Exchanger.java | 112 +++-- .../java/util/concurrent/FutureTask.java | 50 +-- .../util/concurrent/LinkedTransferQueue.java | 64 ++- .../classes/java/util/concurrent/Phaser.java | 39 +- .../concurrent/PriorityBlockingQueue.java | 15 +- .../util/concurrent/SynchronousQueue.java | 90 ++-- .../util/concurrent/atomic/AtomicBoolean.java | 247 +++++++++-- .../util/concurrent/atomic/AtomicInteger.java | 346 +++++++++++---- .../concurrent/atomic/AtomicIntegerArray.java | 393 +++++++++++++----- .../atomic/AtomicIntegerFieldUpdater.java | 7 +- .../util/concurrent/atomic/AtomicLong.java | 349 ++++++++++++---- .../concurrent/atomic/AtomicLongArray.java | 391 ++++++++++++----- .../atomic/AtomicLongFieldUpdater.java | 9 +- .../atomic/AtomicMarkableReference.java | 16 +- .../concurrent/atomic/AtomicReference.java | 287 ++++++++++--- .../atomic/AtomicReferenceArray.java | 380 ++++++++++++----- .../atomic/AtomicReferenceFieldUpdater.java | 7 +- .../atomic/AtomicStampedReference.java | 16 +- .../concurrent/atomic/LongAccumulator.java | 2 +- .../util/concurrent/atomic/Striped64.java | 38 +- .../util/concurrent/atomic/package-info.java | 134 +----- .../locks/AbstractQueuedLongSynchronizer.java | 41 +- .../locks/AbstractQueuedSynchronizer.java | 81 ++-- .../java/util/concurrent/locks/Condition.java | 2 - .../util/concurrent/locks/LockSupport.java | 18 +- .../util/concurrent/locks/ReadWriteLock.java | 1 - .../util/concurrent/locks/ReentrantLock.java | 26 +- .../locks/ReentrantReadWriteLock.java | 49 +-- .../java/util/concurrent/package-info.java | 1 - .../java/util/concurrent/tck/Atomic8Test.java | 8 +- .../concurrent/tck/AtomicBoolean9Test.java | 203 +++++++++ .../concurrent/tck/AtomicInteger9Test.java | 203 +++++++++ .../tck/AtomicIntegerArray9Test.java | 267 ++++++++++++ .../util/concurrent/tck/AtomicLong9Test.java | 203 +++++++++ .../concurrent/tck/AtomicLongArray9Test.java | 266 ++++++++++++ .../concurrent/tck/AtomicReference9Test.java | 203 +++++++++ .../tck/AtomicReferenceArray9Test.java | 266 ++++++++++++ .../tck/AtomicReferenceArrayTest.java | 1 + .../concurrent/tck/AtomicReferenceTest.java | 2 +- .../util/concurrent/tck/JSR166TestCase.java | 24 +- 48 files changed, 3971 insertions(+), 1288 deletions(-) create mode 100644 jdk/test/java/util/concurrent/tck/AtomicBoolean9Test.java create mode 100644 jdk/test/java/util/concurrent/tck/AtomicInteger9Test.java create mode 100644 jdk/test/java/util/concurrent/tck/AtomicIntegerArray9Test.java create mode 100644 jdk/test/java/util/concurrent/tck/AtomicLong9Test.java create mode 100644 jdk/test/java/util/concurrent/tck/AtomicLongArray9Test.java create mode 100644 jdk/test/java/util/concurrent/tck/AtomicReference9Test.java create mode 100644 jdk/test/java/util/concurrent/tck/AtomicReferenceArray9Test.java diff --git a/jdk/src/java.base/share/classes/java/util/Queue.java b/jdk/src/java.base/share/classes/java/util/Queue.java index 7d5e39c7030..e94b22c7fb2 100644 --- a/jdk/src/java.base/share/classes/java/util/Queue.java +++ b/jdk/src/java.base/share/classes/java/util/Queue.java @@ -124,7 +124,6 @@ package java.util; * always well-defined for queues with the same elements but different * ordering properties. * - * *

    This interface is a member of the * * Java Collections Framework. diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java index d2392c62798..471316a9040 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java @@ -68,6 +68,7 @@ import java.util.function.ToIntFunction; 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 @@ public class ConcurrentHashMap extends AbstractMap /* ---------------- 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 @@ public class ConcurrentHashMap extends AbstractMap * 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 @@ public class ConcurrentHashMap extends AbstractMap } 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 @@ public class ConcurrentHashMap extends AbstractMap 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 @@ public class ConcurrentHashMap extends AbstractMap } // 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java index 698079066cb..e8193a00256 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java @@ -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 @@ public class ConcurrentLinkedDeque 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 @@ public class ConcurrentLinkedDeque 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 @@ public class ConcurrentLinkedDeque * 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 @@ public class ConcurrentLinkedDeque 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 @@ public class ConcurrentLinkedDeque 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 @@ public class ConcurrentLinkedDeque // 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 @@ public class ConcurrentLinkedDeque 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 @@ public class ConcurrentLinkedDeque // 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 @@ public class ConcurrentLinkedDeque 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 @@ public class ConcurrentLinkedDeque (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 @@ public class ConcurrentLinkedDeque (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 @@ public class ConcurrentLinkedDeque } // 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 @@ public class ConcurrentLinkedDeque } // 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 @@ public class ConcurrentLinkedDeque 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 @@ public class ConcurrentLinkedDeque 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 @@ public class ConcurrentLinkedDeque * Constructs an empty deque. */ public ConcurrentLinkedDeque() { - head = tail = new Node(null); + head = tail = new Node(); } /** @@ -818,12 +781,12 @@ public class ConcurrentLinkedDeque // 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 @@ public class ConcurrentLinkedDeque 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 class ConcurrentLinkedDeque 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 class ConcurrentLinkedDeque 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 @@ public class ConcurrentLinkedDeque 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 @@ public class ConcurrentLinkedDeque 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 @@ public class ConcurrentLinkedDeque // 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 @@ public class ConcurrentLinkedDeque 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 @@ public class ConcurrentLinkedDeque 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java index ae246a4e780..1e3f1aad4f6 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java @@ -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 @@ public class ConcurrentLinkedQueue extends AbstractQueue * 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 @@ public class ConcurrentLinkedQueue extends AbstractQueue * 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 @@ public class ConcurrentLinkedQueue extends AbstractQueue if (h == null) h = t = newNode; else { - lazySetNext(t, newNode); + NEXT.set(t, newNode); t = newNode; } } @@ -286,8 +275,8 @@ public class ConcurrentLinkedQueue extends AbstractQueue */ 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 @@ public class ConcurrentLinkedQueue extends AbstractQueue 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 @@ public class ConcurrentLinkedQueue extends AbstractQueue 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 @@ public class ConcurrentLinkedQueue extends AbstractQueue 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 @@ public class ConcurrentLinkedQueue extends AbstractQueue if (beginningOfTheEnd == null) beginningOfTheEnd = last = newNode; else { - lazySetNext(last, newNode); + NEXT.set(last, newNode); last = newNode; } } @@ -532,15 +521,15 @@ public class ConcurrentLinkedQueue extends AbstractQueue 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 @@ public class ConcurrentLinkedQueue extends AbstractQueue } // unlink deleted nodes if ((q = succ(p)) != null) - casNext(pred, p, q); + NEXT.compareAndSet(pred, p, q); } } @@ -801,7 +790,7 @@ public class ConcurrentLinkedQueue extends AbstractQueue if (h == null) h = t = newNode; else { - lazySetNext(t, newNode); + NEXT.set(t, newNode); t = newNode; } } @@ -919,31 +908,20 @@ public class ConcurrentLinkedQueue extends AbstractQueue 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java index fa53ded9710..7734a265bed 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java @@ -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 @@ public class ConcurrentSkipListMap extends AbstractMap * 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 @@ public class ConcurrentSkipListMap extends AbstractMap * 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 @@ public class ConcurrentSkipListMap extends AbstractMap 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 @@ public class ConcurrentSkipListMap extends AbstractMap * 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 @@ public class ConcurrentSkipListMap extends AbstractMap 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 @@ public class ConcurrentSkipListMap extends AbstractMap } } - // 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java index 2ecacf8e185..45bde4215c5 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java @@ -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 @@ public class ConcurrentSkipListSet // 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java b/jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java index 65baf7c87c8..1f1b83cbfca 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java @@ -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 @@ public class CopyOnWriteArrayList } } - // 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java b/jdk/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java index 1e56b09dfc1..a61762b5669 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java @@ -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 @@ public abstract class CountedCompleter extends ForkJoinTask { * @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 @@ public abstract class CountedCompleter extends ForkJoinTask { * @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 abstract class CountedCompleter extends ForkJoinTask { 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 @@ public abstract class CountedCompleter extends ForkJoinTask { return; } } - else if (U.compareAndSwapInt(a, PENDING, c, c - 1)) + else if (PENDING.weakCompareAndSetVolatile(a, c, c - 1)) return; } } @@ -604,7 +607,7 @@ public abstract class CountedCompleter extends ForkJoinTask { return; } } - else if (U.compareAndSwapInt(a, PENDING, c, c - 1)) + else if (PENDING.weakCompareAndSetVolatile(a, c, c - 1)) return; } } @@ -649,7 +652,7 @@ public abstract class CountedCompleter extends ForkJoinTask { 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 @@ public abstract class CountedCompleter extends ForkJoinTask { */ 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/Exchanger.java b/jdk/src/java.base/share/classes/java/util/concurrent/Exchanger.java index 069d6e0f871..8dcfe9bf1df 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/Exchanger.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/Exchanger.java @@ -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 @@ public class Exchanger { * 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 @@ public class Exchanger { * 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 @@ public class Exchanger { */ /** - * 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 @@ public class Exchanger { */ 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 @@ public class Exchanger { (--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 @@ public class Exchanger { 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 @@ public class Exchanger { 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 @@ public class Exchanger { 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 @@ public class Exchanger { @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 @@ public class Exchanger { 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/FutureTask.java b/jdk/src/java.base/share/classes/java/util/concurrent/FutureTask.java index aade083ffe3..82e773c7630 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/FutureTask.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/FutureTask.java @@ -35,6 +35,8 @@ package java.util.concurrent; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.concurrent.locks.LockSupport; /** @@ -69,9 +71,6 @@ public class FutureTask implements RunnableFuture { * 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 class FutureTask implements RunnableFuture { } 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 @@ public class FutureTask implements RunnableFuture { if (t != null) t.interrupt(); } finally { // final state - U.putIntRelease(this, STATE, INTERRUPTED); + STATE.setRelease(this, INTERRUPTED); } } } finally { @@ -228,9 +226,9 @@ public class FutureTask implements RunnableFuture { * @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 @@ public class FutureTask implements RunnableFuture { * @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 @@ public class FutureTask implements RunnableFuture { */ 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 @@ public class FutureTask implements RunnableFuture { 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 @@ public class FutureTask implements RunnableFuture { 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 @@ public class FutureTask implements RunnableFuture { 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 @@ public class FutureTask implements RunnableFuture { } } - // 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/LinkedTransferQueue.java b/jdk/src/java.base/share/classes/java/util/concurrent/LinkedTransferQueue.java index 69b9a694f85..cb613e3f092 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/LinkedTransferQueue.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/LinkedTransferQueue.java @@ -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 @@ public class LinkedTransferQueue extends AbstractQueue /** * 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 @@ public class LinkedTransferQueue extends AbstractQueue // 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 @@ public class LinkedTransferQueue extends AbstractQueue * 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 @@ public class LinkedTransferQueue extends AbstractQueue * 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 @@ public class LinkedTransferQueue extends AbstractQueue * 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 @@ public class LinkedTransferQueue extends AbstractQueue 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 @@ public class LinkedTransferQueue extends AbstractQueue // 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 @@ public class LinkedTransferQueue extends AbstractQueue } } - // 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/Phaser.java b/jdk/src/java.base/share/classes/java/util/concurrent/Phaser.java index 3fd30e7d3a8..c0e6fbadb01 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/Phaser.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/Phaser.java @@ -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 @@ import java.util.concurrent.locks.LockSupport; * 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 @@ public class Phaser { 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 @@ public class Phaser { 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 @@ public class Phaser { 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 @@ public class Phaser { // 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 @@ public class Phaser { // 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 @@ public class Phaser { 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 @@ public class Phaser { 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 @@ public class Phaser { 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 @@ public class Phaser { 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 @@ public class Phaser { } } - // 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java b/jdk/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java index 652fd6958cf..36f5a47e3f9 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java @@ -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 @@ public class PriorityBlockingQueue extends AbstractQueue 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 @@ public class PriorityBlockingQueue extends AbstractQueue 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/SynchronousQueue.java b/jdk/src/java.base/share/classes/java/util/concurrent/SynchronousQueue.java index 069d0a6b851..e12d7afecc6 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/SynchronousQueue.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/SynchronousQueue.java @@ -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 @@ public class SynchronousQueue extends AbstractQueue boolean casNext(SNode cmp, SNode val) { return cmp == next && - U.compareAndSwapObject(this, NEXT, cmp, val); + SNEXT.compareAndSet(this, cmp, val); } /** @@ -260,7 +262,7 @@ public class SynchronousQueue extends AbstractQueue */ 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 @@ public class SynchronousQueue extends AbstractQueue * 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 @@ public class SynchronousQueue extends AbstractQueue boolean casHead(SNode h, SNode nh) { return h == head && - U.compareAndSwapObject(this, HEAD, h, nh); + SHEAD.compareAndSet(this, h, nh); } /** @@ -451,8 +450,10 @@ public class SynchronousQueue extends AbstractQueue 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 @@ public class SynchronousQueue extends AbstractQueue } } - // 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 @@ public class SynchronousQueue extends AbstractQueue 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 @@ public class SynchronousQueue extends AbstractQueue 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 @@ public class SynchronousQueue extends AbstractQueue */ 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 @@ public class SynchronousQueue extends AbstractQueue */ void advanceTail(QNode t, QNode nt) { if (tail == t) - U.compareAndSwapObject(this, TAIL, t, nt); + QTAIL.compareAndSet(this, t, nt); } /** @@ -631,7 +628,7 @@ public class SynchronousQueue extends AbstractQueue */ boolean casCleanMe(QNode cmp, QNode val) { return cleanMe == cmp && - U.compareAndSwapObject(this, CLEANME, cmp, val); + QCLEANME.compareAndSet(this, cmp, val); } /** @@ -752,8 +749,10 @@ public class SynchronousQueue extends AbstractQueue 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 @@ public class SynchronousQueue extends AbstractQueue } } - 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicBoolean.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicBoolean.java index 5241392ed92..d32624aaab7 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicBoolean.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicBoolean.java @@ -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 @@ public class AtomicBoolean implements java.io.Serializable { } /** - * 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 @@ public class AtomicBoolean implements java.io.Serializable { } /** - * 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 @@ public class AtomicBoolean implements java.io.Serializable { } /** - * 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 @@ public class AtomicBoolean implements java.io.Serializable { 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicInteger.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicInteger.java index 920b45bd5ab..a6221a86a80 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicInteger.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicInteger.java @@ -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 @@ public class AtomicInteger extends Number implements java.io.Serializable { } /** - * 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 @@ public class AtomicInteger extends Number implements java.io.Serializable { } /** - * 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 @@ public class AtomicInteger extends Number implements java.io.Serializable { } /** - * 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 @@ public class AtomicInteger extends Number implements java.io.Serializable { * @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 @@ public class AtomicInteger extends Number implements java.io.Serializable { * @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 class AtomicInteger extends Number implements java.io.Serializable { */ 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 class AtomicInteger extends Number implements java.io.Serializable { */ 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 @@ public class AtomicInteger extends Number implements java.io.Serializable { } /** - * 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 @@ public class AtomicInteger extends Number implements java.io.Serializable { } /** - * 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 @@ public class AtomicInteger extends Number implements java.io.Serializable { } /** - * 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 @@ public class AtomicInteger extends Number implements java.io.Serializable { } /** - * 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java index 001a35008d9..301aa4a5844 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java @@ -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 @@ public class AtomicIntegerArray implements java.io.Serializable { } /** - * 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 @@ public class AtomicIntegerArray implements java.io.Serializable { * @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 @@ public class AtomicIntegerArray implements java.io.Serializable { * @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 class AtomicIntegerArray implements java.io.Serializable { */ 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 class AtomicIntegerArray implements java.io.Serializable { */ 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 @@ public class AtomicIntegerArray implements java.io.Serializable { 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java index 10ee364b5b9..c86f41ddee7 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java @@ -42,6 +42,7 @@ import java.security.PrivilegedActionException; 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 class AtomicIntegerFieldUpdater { 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 @@ public abstract class AtomicIntegerFieldUpdater { */ 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java index 85ff9030da3..2c79beaadae 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java @@ -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 @@ public class AtomicLong extends Number implements java.io.Serializable { 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 @@ public class AtomicLong extends Number implements java.io.Serializable { } /** - * 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 @@ public class AtomicLong extends Number implements java.io.Serializable { } /** - * 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 @@ public class AtomicLong extends Number implements java.io.Serializable { * @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 @@ public class AtomicLong extends Number implements java.io.Serializable { * @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 class AtomicLong extends Number implements java.io.Serializable { */ 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 class AtomicLong extends Number implements java.io.Serializable { */ 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 @@ public class AtomicLong extends Number implements java.io.Serializable { } /** - * 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 @@ public class AtomicLong extends Number implements java.io.Serializable { } /** - * 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 @@ public class AtomicLong extends Number implements java.io.Serializable { } /** - * 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 @@ public class AtomicLong extends Number implements java.io.Serializable { } /** - * 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongArray.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongArray.java index 041e561b2a7..3d584cf44e0 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongArray.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongArray.java @@ -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 @@ public class AtomicLongArray implements java.io.Serializable { } /** - * 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 @@ public class AtomicLongArray implements java.io.Serializable { * @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 @@ public class AtomicLongArray implements java.io.Serializable { * @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 class AtomicLongArray implements java.io.Serializable { */ 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 class AtomicLongArray implements java.io.Serializable { */ 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 @@ public class AtomicLongArray implements java.io.Serializable { 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java index c3ad0afff21..af39d8a4603 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java @@ -42,6 +42,7 @@ import java.security.PrivilegedActionException; 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 class AtomicLongFieldUpdater { 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 @@ public abstract class AtomicLongFieldUpdater { } 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 @@ public abstract class AtomicLongFieldUpdater { } 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java index 8204c3fcc61..367808c7192 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java @@ -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 @@ public class AtomicMarkableReference { 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReference.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReference.java index d0a0b0a90cf..27b5bed586a 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReference.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReference.java @@ -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 @@ public class AtomicReference implements java.io.Serializable { } /** - * 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 @@ public class AtomicReference implements java.io.Serializable { } /** - * 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); + VALUE.setRelease(this, 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 + * Atomically sets the value to {@code newValue} + * if the current value {@code == expectedValue}, + * with memory effects as specified by {@link VarHandle#compareAndSet}. + * + * @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(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 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(V expect, V update) { - return U.compareAndSwapObject(this, VALUE, expect, update); + public final boolean weakCompareAndSet(V expectedValue, V newValue) { + return VALUE.weakCompareAndSet(this, expectedValue, 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 */ @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 @@ public class AtomicReference implements java.io.Serializable { * @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 @@ public class AtomicReference implements java.io.Serializable { * @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 class AtomicReference implements java.io.Serializable { */ 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 class AtomicReference implements java.io.Serializable { */ 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 @@ public class AtomicReference implements java.io.Serializable { 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java index a2e8b2b7805..34bb0f135da 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java @@ -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 @@ public class AtomicReferenceArray implements java.io.Serializable { } /** - * 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 E get(int i) { - return getRaw(checkedByteOffset(i)); - } - @SuppressWarnings("unchecked") - private E getRaw(long offset) { - return (E) U.getObjectVolatile(array, offset); + public final E get(int i) { + 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 @@ public class AtomicReferenceArray implements java.io.Serializable { */ @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 @@ public class AtomicReferenceArray implements java.io.Serializable { * @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 @@ public class AtomicReferenceArray implements java.io.Serializable { * @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 class AtomicReferenceArray implements java.io.Serializable { */ 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 class AtomicReferenceArray implements java.io.Serializable { */ 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 @@ public class AtomicReferenceArray implements java.io.Serializable { 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 @@ public class AtomicReferenceArray implements java.io.Serializable { 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java index f6dbbe36479..faacb7fc432 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java @@ -42,6 +42,7 @@ import java.security.PrivilegedActionException; 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 class AtomicReferenceFieldUpdater { 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 @@ public abstract class AtomicReferenceFieldUpdater { 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java index fd520d7d889..f031fe81d81 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java @@ -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 @@ public class AtomicStampedReference { 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/LongAccumulator.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/LongAccumulator.java index 0e9a8f5927b..939a4a2eed9 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/LongAccumulator.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/LongAccumulator.java @@ -68,7 +68,7 @@ import java.util.function.LongBinaryOperator; *

    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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java index 77ee89a614b..a70094e3623 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java @@ -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 @@ abstract class Striped64 extends Number { 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 @@ abstract class Striped64 extends Number { * 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 @@ abstract class Striped64 extends Number { } } - // 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/package-info.java b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/package-info.java index a8e1ff34528..b0ff27f96e3 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/package-info.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/package-info.java @@ -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: + *

    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 get} has the memory effects of reading a - * {@code volatile} variable. - * - *
    • {@code set} has the memory effects of writing (assigning) a - * {@code volatile} variable. - * - *
    • {@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. - * - *
    • {@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. + *

    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. * + *

    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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java index cffab6b1286..7c42896079d 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java @@ -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 @@ public abstract class AbstractQueuedLongSynchronizer 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 @@ public abstract class AbstractQueuedLongSynchronizer * 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 @@ public abstract class AbstractQueuedLongSynchronizer 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 @@ public abstract class AbstractQueuedLongSynchronizer 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 @@ public abstract class AbstractQueuedLongSynchronizer } } - /** - * 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 @@ public abstract class AbstractQueuedLongSynchronizer */ 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 @@ public abstract class AbstractQueuedLongSynchronizer * 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java index a90e4fdd629..ba4e9edc942 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java @@ -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 @@ public abstract class AbstractQueuedSynchronizer /** 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); } - 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; + final void setPrevRelaxed(Node p) { + PREV.set(this, p); + } + + // 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 @@ public abstract class AbstractQueuedSynchronizer * 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 @@ public abstract class AbstractQueuedSynchronizer 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 @@ public abstract class AbstractQueuedSynchronizer 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 @@ public abstract class AbstractQueuedSynchronizer * @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 @@ public abstract class AbstractQueuedSynchronizer * {@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 @@ public abstract class AbstractQueuedSynchronizer * 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 @@ public abstract class AbstractQueuedSynchronizer * 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 @@ public abstract class AbstractQueuedSynchronizer } } - /** - * 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 @@ public abstract class AbstractQueuedSynchronizer */ 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 @@ public abstract class AbstractQueuedSynchronizer * 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/Condition.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/Condition.java index 79181fd9dc6..a4770a5e0ed 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/Condition.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/Condition.java @@ -396,7 +396,6 @@ public interface Condition { * 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 @@ public interface Condition { * 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java
      index c219ca55c7b..7bf6e8a54cd 100644
      --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java
      +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java
      @@ -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 @@ public class LockSupport {
               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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReadWriteLock.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReadWriteLock.java
      index 00fb84530b9..8455944fe4c 100644
      --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReadWriteLock.java
      +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReadWriteLock.java
      @@ -79,7 +79,6 @@ package java.util.concurrent.locks;
        * 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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantLock.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantLock.java index c39c94724e7..8e8a94f89bd 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantLock.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantLock.java @@ -118,12 +118,6 @@ public class ReentrantLock implements Lock, java.io.Serializable { abstract static class Sync extends AbstractQueuedSynchronizer { 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 @@ public class ReentrantLock implements Lock, java.io.Serializable { */ 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 @@ public class ReentrantLock implements Lock, java.io.Serializable { */ 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 @@ public class ReentrantLock implements Lock, java.io.Serializable { * at which time the lock hold count is set to one. */ public void lock() { - sync.lock(); + sync.acquire(1); } /** diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java index 3f39da71270..5a9d8bc2980 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java @@ -37,6 +37,7 @@ package java.util.concurrent.locks; 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 @@ public class ReentrantReadWriteLock 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 @@ public class ReentrantReadWriteLock * 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 @@ public class ReentrantReadWriteLock return free; } + @ReservedStackAccess protected final boolean tryAcquire(int acquires) { /* * Walkthrough: @@ -411,6 +413,7 @@ public class ReentrantReadWriteLock return true; } + @ReservedStackAccess protected final boolean tryReleaseShared(int unused) { Thread current = Thread.currentThread(); if (firstReader == current) { @@ -421,7 +424,8 @@ public class ReentrantReadWriteLock 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 @@ public class ReentrantReadWriteLock "attempt to unlock read lock, not locked by current thread"); } + @ReservedStackAccess protected final int tryAcquireShared(int unused) { /* * Walkthrough: @@ -479,7 +484,8 @@ public class ReentrantReadWriteLock 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 @@ public class ReentrantReadWriteLock } 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 @@ public class ReentrantReadWriteLock } 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 @@ public class ReentrantReadWriteLock * 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 @@ public class ReentrantReadWriteLock * 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 @@ public class ReentrantReadWriteLock 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 @@ public class ReentrantReadWriteLock 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 @@ public class ReentrantReadWriteLock "[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 --git a/jdk/src/java.base/share/classes/java/util/concurrent/package-info.java b/jdk/src/java.base/share/classes/java/util/concurrent/package-info.java index 387068da401..46b9398f6e2 100644 --- a/jdk/src/java.base/share/classes/java/util/concurrent/package-info.java +++ b/jdk/src/java.base/share/classes/java/util/concurrent/package-info.java @@ -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 --git a/jdk/test/java/util/concurrent/tck/Atomic8Test.java b/jdk/test/java/util/concurrent/tck/Atomic8Test.java index 1e49299c21a..721f502cdc1 100644 --- a/jdk/test/java/util/concurrent/tck/Atomic8Test.java +++ b/jdk/test/java/util/concurrent/tck/Atomic8Test.java @@ -179,7 +179,7 @@ public class Atomic8Test extends JSR166TestCase { * 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 @@ public class Atomic8Test extends JSR166TestCase { * 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 @@ public class Atomic8Test extends JSR166TestCase { * 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 @@ public class Atomic8Test extends JSR166TestCase { * 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 --git a/jdk/test/java/util/concurrent/tck/AtomicBoolean9Test.java b/jdk/test/java/util/concurrent/tck/AtomicBoolean9Test.java new file mode 100644 index 00000000000..7211dca7ebd --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicBoolean9Test.java @@ -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 --git a/jdk/test/java/util/concurrent/tck/AtomicInteger9Test.java b/jdk/test/java/util/concurrent/tck/AtomicInteger9Test.java new file mode 100644 index 00000000000..7a0ed578f9a --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicInteger9Test.java @@ -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 --git a/jdk/test/java/util/concurrent/tck/AtomicIntegerArray9Test.java b/jdk/test/java/util/concurrent/tck/AtomicIntegerArray9Test.java new file mode 100644 index 00000000000..d074b140ba2 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicIntegerArray9Test.java @@ -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 --git a/jdk/test/java/util/concurrent/tck/AtomicLong9Test.java b/jdk/test/java/util/concurrent/tck/AtomicLong9Test.java new file mode 100644 index 00000000000..c11561690b8 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicLong9Test.java @@ -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 --git a/jdk/test/java/util/concurrent/tck/AtomicLongArray9Test.java b/jdk/test/java/util/concurrent/tck/AtomicLongArray9Test.java new file mode 100644 index 00000000000..488b195e014 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicLongArray9Test.java @@ -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 --git a/jdk/test/java/util/concurrent/tck/AtomicReference9Test.java b/jdk/test/java/util/concurrent/tck/AtomicReference9Test.java new file mode 100644 index 00000000000..f1d974908c7 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicReference9Test.java @@ -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 --git a/jdk/test/java/util/concurrent/tck/AtomicReferenceArray9Test.java b/jdk/test/java/util/concurrent/tck/AtomicReferenceArray9Test.java new file mode 100644 index 00000000000..9ebec5b2ef6 --- /dev/null +++ b/jdk/test/java/util/concurrent/tck/AtomicReferenceArray9Test.java @@ -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 --git a/jdk/test/java/util/concurrent/tck/AtomicReferenceArrayTest.java b/jdk/test/java/util/concurrent/tck/AtomicReferenceArrayTest.java index 2457d40b426..8e7bf10642f 100644 --- a/jdk/test/java/util/concurrent/tck/AtomicReferenceArrayTest.java +++ b/jdk/test/java/util/concurrent/tck/AtomicReferenceArrayTest.java @@ -243,4 +243,5 @@ public class AtomicReferenceArrayTest extends JSR166TestCase { AtomicReferenceArray aa = new AtomicReferenceArray(a); assertEquals(Arrays.toString(a), aa.toString()); } + } diff --git a/jdk/test/java/util/concurrent/tck/AtomicReferenceTest.java b/jdk/test/java/util/concurrent/tck/AtomicReferenceTest.java index bae2a43906a..6242374ee89 100644 --- a/jdk/test/java/util/concurrent/tck/AtomicReferenceTest.java +++ b/jdk/test/java/util/concurrent/tck/AtomicReferenceTest.java @@ -161,7 +161,7 @@ public class AtomicReferenceTest extends JSR166TestCase { * 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 --git a/jdk/test/java/util/concurrent/tck/JSR166TestCase.java b/jdk/test/java/util/concurrent/tck/JSR166TestCase.java index 14907badc6f..8ea9b697bc4 100644 --- a/jdk/test/java/util/concurrent/tck/JSR166TestCase.java +++ b/jdk/test/java/util/concurrent/tck/JSR166TestCase.java @@ -548,6 +548,13 @@ public class JSR166TestCase extends TestCase { // Java9+ test classes if (atLeastJava9()) { String[] java9TestClassNames = { + "AtomicBoolean9Test", + "AtomicInteger9Test", + "AtomicIntegerArray9Test", + "AtomicLong9Test", + "AtomicLongArray9Test", + "AtomicReference9Test", + "AtomicReferenceArray9Test", "ExecutorCompletionService9Test", }; addNamedTestClasses(suite, java9TestClassNames); @@ -975,7 +982,11 @@ public class JSR166TestCase extends TestCase { } } - /** 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 @@ public class JSR166TestCase extends TestCase { * 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 @@ public class JSR166TestCase extends TestCase { System.err.print(info); } System.err.println("------ stacktrace dump end ------"); + + if (sm != null) System.setSecurityManager(sm); } /** From eba2a20a2be3ecfe39cdd42c59d396ed3380135c Mon Sep 17 00:00:00 2001 From: Brian Burkhalter Date: Fri, 15 Jul 2016 17:49:42 -0700 Subject: [PATCH 40/44] 8160220: (fs) Tests in jdk/test/java/nio/file/WatchService leave directory trees behind Create temporary files by default in a scratch directory. Reviewed-by: alanb, chegar --- .../java/nio/file/WatchService/DeleteInterference.java | 5 +++-- jdk/test/java/nio/file/WatchService/LotsOfCancels.java | 10 ++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/jdk/test/java/nio/file/WatchService/DeleteInterference.java b/jdk/test/java/nio/file/WatchService/DeleteInterference.java index 347912c434c..032d02a574b 100644 --- a/jdk/test/java/nio/file/WatchService/DeleteInterference.java +++ b/jdk/test/java/nio/file/WatchService/DeleteInterference.java @@ -32,6 +32,7 @@ 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.WatchService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -49,7 +50,8 @@ public class DeleteInterference { * 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 @@ public class DeleteInterference { task2.get(); } finally { pool.shutdown(); - deleteFileTree(dir); } } diff --git a/jdk/test/java/nio/file/WatchService/LotsOfCancels.java b/jdk/test/java/nio/file/WatchService/LotsOfCancels.java index e4abc308d67..66597f35535 100644 --- a/jdk/test/java/nio/file/WatchService/LotsOfCancels.java +++ b/jdk/test/java/nio/file/WatchService/LotsOfCancels.java @@ -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 @@ public class LotsOfCancels { // 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 @@ public class LotsOfCancels { failed = true; } } - } - From a80a380ddb13e65aef2119bc4a5f1cbe634478cb Mon Sep 17 00:00:00 2001 From: Amy Lu Date: Mon, 18 Jul 2016 13:13:52 +0800 Subject: [PATCH 41/44] 8161347: Mark java/lang/ProcessBuilder/Zombies.java as intermittently failing Reviewed-by: rriggs --- jdk/test/java/lang/ProcessBuilder/Zombies.java | 1 + 1 file changed, 1 insertion(+) diff --git a/jdk/test/java/lang/ProcessBuilder/Zombies.java b/jdk/test/java/lang/ProcessBuilder/Zombies.java index e77f01cce93..91cbd210653 100644 --- a/jdk/test/java/lang/ProcessBuilder/Zombies.java +++ b/jdk/test/java/lang/ProcessBuilder/Zombies.java @@ -24,6 +24,7 @@ /* * @test * @bug 6474073 + * @key intermittent * @summary Make sure zombies don't get created on Unix * @author Martin Buchholz */ From 7112e19bd12d5ad1665e6aef381f591892b66feb Mon Sep 17 00:00:00 2001 From: Michael Haupt Date: Mon, 18 Jul 2016 14:06:50 +0200 Subject: [PATCH 42/44] 8161212: Test times out: java/lang/invoke/LoopCombinatorLongSignatureTest.java Reviewed-by: redestad --- .../invoke/LoopCombinatorLongSignatureTest.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/jdk/test/java/lang/invoke/LoopCombinatorLongSignatureTest.java b/jdk/test/java/lang/invoke/LoopCombinatorLongSignatureTest.java index 0e4f6dff726..ccd8c53c7b0 100644 --- a/jdk/test/java/lang/invoke/LoopCombinatorLongSignatureTest.java +++ b/jdk/test/java/lang/invoke/LoopCombinatorLongSignatureTest.java @@ -39,6 +39,11 @@ import java.util.Arrays; * 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 @@ public class LoopCombinatorLongSignatureTest { 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 @@ public class LoopCombinatorLongSignatureTest { 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 @@ public class LoopCombinatorLongSignatureTest { r = (int) loop.invokeWithArguments(args); } System.out.println("SUCCEEDED (OK) -> " + r); + } else { + System.out.println("SUCCEEDED (OK)"); } } catch (IllegalArgumentException iae) { if (excessive) { From 9118632bbd54fa4435e4fe2a980434d7d58f57a7 Mon Sep 17 00:00:00 2001 From: Pavel Rappo Date: Mon, 18 Jul 2016 13:43:49 +0100 Subject: [PATCH 43/44] 8161474: Extract interface from java.net.http.RawChannel Reviewed-by: michaelm --- .../java/net/http/HttpResponseImpl.java | 2 +- .../classes/java/net/http/RawChannel.java | 138 ++---------------- .../classes/java/net/http/RawChannelImpl.java | 113 ++++++++++++++ .../classes/java/net/http/WSReceiver.java | 6 +- .../share/classes/java/net/http/WSWriter.java | 6 +- .../java/net/http/SelectorTest.java | 6 +- 6 files changed, 137 insertions(+), 134 deletions(-) create mode 100644 jdk/src/java.httpclient/share/classes/java/net/http/RawChannelImpl.java diff --git a/jdk/src/java.httpclient/share/classes/java/net/http/HttpResponseImpl.java b/jdk/src/java.httpclient/share/classes/java/net/http/HttpResponseImpl.java index dcbfbdb8f1a..90d3e7bea60 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpResponseImpl.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpResponseImpl.java @@ -178,7 +178,7 @@ class HttpResponseImpl extends HttpResponse { */ RawChannel rawChannel() throws IOException { if (rawchan == null) { - rawchan = new RawChannel(request.client(), connection); + rawchan = new RawChannelImpl(request.client(), connection); } return rawchan; } diff --git a/jdk/src/java.httpclient/share/classes/java/net/http/RawChannel.java b/jdk/src/java.httpclient/share/classes/java/net/http/RawChannel.java index 5f6fb4df6c0..7804a38f5b7 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/RawChannel.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/RawChannel.java @@ -25,146 +25,36 @@ 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(); - } + long write(ByteBuffer[] src, int offset, int len) throws IOException; - @Override - public void close() throws IOException { - connection.close(); - } + boolean isOpen(); - @Override - public long write(ByteBuffer[] src) throws IOException { - return connection.write(src, 0, src.length); - } - - @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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/RawChannelImpl.java b/jdk/src/java.httpclient/share/classes/java/net/http/RawChannelImpl.java new file mode 100644 index 00000000000..2a48d1f4c0c --- /dev/null +++ b/jdk/src/java.httpclient/share/classes/java/net/http/RawChannelImpl.java @@ -0,0 +1,113 @@ +/* + * 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 + */ +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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/WSReceiver.java b/jdk/src/java.httpclient/share/classes/java/net/http/WSReceiver.java index 9b4da82ca0f..401b629a2f2 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSReceiver.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSReceiver.java @@ -53,7 +53,7 @@ final class WSReceiver { 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 +251,8 @@ final class WSReceiver { 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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/WSWriter.java b/jdk/src/java.httpclient/share/classes/java/net/http/WSWriter.java index 18cbe4cadda..b637f74d519 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSWriter.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSWriter.java @@ -60,7 +60,7 @@ import static java.util.Objects.requireNonNull; 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 +110,8 @@ final class WSWriter { return -1; } - private RawChannel.NonBlockingEvent createHandler() { - return new RawChannel.NonBlockingEvent() { + private RawChannel.RawEvent createHandler() { + return new RawChannel.RawEvent() { @Override public int interestOps() { diff --git a/jdk/test/java/net/httpclient/whitebox/java.httpclient/java/net/http/SelectorTest.java b/jdk/test/java/net/httpclient/whitebox/java.httpclient/java/net/http/SelectorTest.java index 001e7a4c8eb..64728e69553 100644 --- a/jdk/test/java/net/httpclient/whitebox/java.httpclient/java/net/http/SelectorTest.java +++ b/jdk/test/java/net/httpclient/whitebox/java.httpclient/java/net/http/SelectorTest.java @@ -78,7 +78,7 @@ public class SelectorTest { 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 @@ public class SelectorTest { } }); - chan.registerEvent(new RawChannel.NonBlockingEvent() { + chan.registerEvent(new RawChannel.RawEvent() { @Override public int interestOps() { return SelectionKey.OP_WRITE; @@ -111,7 +111,7 @@ public class SelectorTest { 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); } From 741506301013ff9c021103b041d2ffac10010783 Mon Sep 17 00:00:00 2001 From: Chris Hegarty Date: Mon, 18 Jul 2016 15:34:22 +0100 Subject: [PATCH 44/44] 8160993: Fix headers in the java/net/http package Reviewed-by: alanb --- .../java/net/http/AsyncConnection.java | 2 ++ .../classes/java/net/http/AsyncEvent.java | 1 + .../java/net/http/AsyncSSLConnection.java | 2 ++ .../java/net/http/AsyncSSLDelegate.java | 2 ++ .../java/net/http/AuthenticationFilter.java | 2 ++ .../classes/java/net/http/BufferHandler.java | 1 + .../java/net/http/ByteBufferConsumer.java | 1 + .../java/net/http/ByteBufferGenerator.java | 1 + .../classes/java/net/http/CharsetToolkit.java | 4 ++- .../classes/java/net/http/ConnectionPool.java | 2 ++ .../java/net/http/ContinuationFrame.java | 1 + .../classes/java/net/http/CookieFilter.java | 1 + .../classes/java/net/http/DataFrame.java | 1 + .../classes/java/net/http/ErrorFrame.java | 1 + .../share/classes/java/net/http/Exchange.java | 2 ++ .../classes/java/net/http/ExchangeImpl.java | 2 ++ .../java/net/http/ExecutorWrapper.java | 2 ++ .../classes/java/net/http/FilterFactory.java | 1 + .../classes/java/net/http/FrameReader.java | 26 ++++++++++++++++--- .../classes/java/net/http/GoAwayFrame.java | 1 + .../classes/java/net/http/HeaderFilter.java | 1 + .../classes/java/net/http/HeaderFrame.java | 1 + .../classes/java/net/http/HeaderParser.java | 2 ++ .../classes/java/net/http/HeadersFrame.java | 1 + .../classes/java/net/http/Http1Exchange.java | 2 ++ .../classes/java/net/http/Http1Request.java | 2 ++ .../classes/java/net/http/Http1Response.java | 2 ++ .../java/net/http/Http2ClientImpl.java | 2 ++ .../java/net/http/Http2Connection.java | 2 ++ .../classes/java/net/http/Http2Frame.java | 2 ++ .../java/net/http/HttpClientBuilderImpl.java | 1 + .../classes/java/net/http/HttpClientImpl.java | 2 ++ .../classes/java/net/http/HttpConnection.java | 2 ++ .../java/net/http/HttpHeadersImpl.java | 2 ++ .../java/net/http/HttpRequestBuilderImpl.java | 2 ++ .../java/net/http/HttpRequestImpl.java | 2 ++ .../classes/java/net/http/HttpResponse.java | 1 + .../java/net/http/ImmutableHeaders.java | 1 + .../share/classes/java/net/http/Log.java | 2 ++ .../classes/java/net/http/MultiExchange.java | 1 + .../java/net/http/OutgoingHeaders.java | 1 + .../share/classes/java/net/http/Pair.java | 9 ++++--- .../classes/java/net/http/PingFrame.java | 1 + .../java/net/http/PlainHttpConnection.java | 2 ++ .../java/net/http/PlainProxyConnection.java | 1 + .../net/http/PlainTunnelingConnection.java | 1 + .../classes/java/net/http/PriorityFrame.java | 1 + .../java/net/http/PushPromiseFrame.java | 1 + .../share/classes/java/net/http/Queue.java | 1 + .../classes/java/net/http/RawChannel.java | 2 ++ .../classes/java/net/http/RawChannelImpl.java | 4 ++- .../classes/java/net/http/RedirectFilter.java | 1 + .../classes/java/net/http/ResetFrame.java | 1 + .../java/net/http/ResponseContent.java | 2 ++ .../java/net/http/ResponseHeaders.java | 2 ++ .../classes/java/net/http/SSLConnection.java | 2 ++ .../classes/java/net/http/SSLDelegate.java | 2 ++ .../java/net/http/SSLTunnelConnection.java | 2 ++ .../classes/java/net/http/SettingsFrame.java | 1 + .../share/classes/java/net/http/Stream.java | 1 + .../classes/java/net/http/TimeoutEvent.java | 1 + .../share/classes/java/net/http/Utils.java | 2 ++ .../share/classes/java/net/http/WS.java | 1 + .../classes/java/net/http/WSBuilder.java | 1 + .../java/net/http/WSCharsetToolkit.java | 4 ++- .../classes/java/net/http/WSDisposable.java | 9 ++++--- .../share/classes/java/net/http/WSFrame.java | 9 ++++--- .../java/net/http/WSFrameConsumer.java | 3 ++- .../java/net/http/WSMessageConsumer.java | 3 ++- .../java/net/http/WSMessageSender.java | 9 ++++--- .../java/net/http/WSOpeningHandshake.java | 1 + .../java/net/http/WSOutgoingMessage.java | 9 ++++--- .../java/net/http/WSProtocolException.java | 25 ++++++++++++++++++ .../classes/java/net/http/WSReceiver.java | 3 ++- .../share/classes/java/net/http/WSShared.java | 9 ++++--- .../classes/java/net/http/WSSharedPool.java | 1 + .../java/net/http/WSSignalHandler.java | 9 ++++--- .../classes/java/net/http/WSTransmitter.java | 9 ++++--- .../share/classes/java/net/http/WSUtils.java | 9 ++++--- .../share/classes/java/net/http/WSWriter.java | 9 ++++--- .../classes/java/net/http/WebSocket.java | 1 + .../net/http/WebSocketHandshakeException.java | 1 + .../java/net/http/WindowUpdateFrame.java | 1 + .../share/classes/module-info.java | 2 +- 84 files changed, 208 insertions(+), 50 deletions(-) diff --git a/jdk/src/java.httpclient/share/classes/java/net/http/AsyncConnection.java b/jdk/src/java.httpclient/share/classes/java/net/http/AsyncConnection.java index 8818823af30..8f6027ea276 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/AsyncConnection.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/AsyncConnection.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/AsyncEvent.java b/jdk/src/java.httpclient/share/classes/java/net/http/AsyncEvent.java index b6284398189..8fb05bf4f9c 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/AsyncEvent.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/AsyncEvent.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLConnection.java b/jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLConnection.java index 3dcd2d4933f..1d6847e7d44 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLConnection.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLConnection.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLDelegate.java b/jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLDelegate.java index 6d500285f4b..76a14dbad20 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLDelegate.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLDelegate.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/AuthenticationFilter.java b/jdk/src/java.httpclient/share/classes/java/net/http/AuthenticationFilter.java index 2f1a65cc73f..5bd425b5557 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/AuthenticationFilter.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/AuthenticationFilter.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/BufferHandler.java b/jdk/src/java.httpclient/share/classes/java/net/http/BufferHandler.java index eb25e98e9d1..5563c41901d 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/BufferHandler.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/BufferHandler.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/ByteBufferConsumer.java b/jdk/src/java.httpclient/share/classes/java/net/http/ByteBufferConsumer.java index 5a37af38b87..ffaedb929d1 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/ByteBufferConsumer.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ByteBufferConsumer.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/ByteBufferGenerator.java b/jdk/src/java.httpclient/share/classes/java/net/http/ByteBufferGenerator.java index 601c4a526b5..3c006941162 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/ByteBufferGenerator.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ByteBufferGenerator.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/CharsetToolkit.java b/jdk/src/java.httpclient/share/classes/java/net/http/CharsetToolkit.java index 1264fda0a7b..92c1993655f 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/CharsetToolkit.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/CharsetToolkit.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/ConnectionPool.java b/jdk/src/java.httpclient/share/classes/java/net/http/ConnectionPool.java index fa32ff30adc..bbc59a071ae 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/ConnectionPool.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ConnectionPool.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/ContinuationFrame.java b/jdk/src/java.httpclient/share/classes/java/net/http/ContinuationFrame.java index 4981df21ca6..fb34a28bd75 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/ContinuationFrame.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ContinuationFrame.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/CookieFilter.java b/jdk/src/java.httpclient/share/classes/java/net/http/CookieFilter.java index df291b93df9..d837ada90ba 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/CookieFilter.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/CookieFilter.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/DataFrame.java b/jdk/src/java.httpclient/share/classes/java/net/http/DataFrame.java index d82c7e11623..c8d0563d52b 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/DataFrame.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/DataFrame.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/ErrorFrame.java b/jdk/src/java.httpclient/share/classes/java/net/http/ErrorFrame.java index 3500949b503..761ed674249 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/ErrorFrame.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ErrorFrame.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/Exchange.java b/jdk/src/java.httpclient/share/classes/java/net/http/Exchange.java index 6d00bc2b215..356edc412a8 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/Exchange.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Exchange.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/ExchangeImpl.java b/jdk/src/java.httpclient/share/classes/java/net/http/ExchangeImpl.java index bc3b6508d3c..ca927fb57d0 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/ExchangeImpl.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ExchangeImpl.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/ExecutorWrapper.java b/jdk/src/java.httpclient/share/classes/java/net/http/ExecutorWrapper.java index ba8a07cf1cc..a18dd9afeba 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/ExecutorWrapper.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ExecutorWrapper.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/FilterFactory.java b/jdk/src/java.httpclient/share/classes/java/net/http/FilterFactory.java index 840eacaa64a..c2599423117 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/FilterFactory.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/FilterFactory.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/FrameReader.java b/jdk/src/java.httpclient/share/classes/java/net/http/FrameReader.java index 5e0d9fb4ebe..44cc043b8ce 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/FrameReader.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/FrameReader.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/GoAwayFrame.java b/jdk/src/java.httpclient/share/classes/java/net/http/GoAwayFrame.java index 7737240d300..49847d2dc97 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/GoAwayFrame.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/GoAwayFrame.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/HeaderFilter.java b/jdk/src/java.httpclient/share/classes/java/net/http/HeaderFilter.java index d0fae94ada8..9387838ae5a 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/HeaderFilter.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HeaderFilter.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/HeaderFrame.java b/jdk/src/java.httpclient/share/classes/java/net/http/HeaderFrame.java index 57440ea6d37..b4f9c59ea2c 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/HeaderFrame.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HeaderFrame.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/HeaderParser.java b/jdk/src/java.httpclient/share/classes/java/net/http/HeaderParser.java index d4922d6a141..60321e7365d 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/HeaderParser.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HeaderParser.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/HeadersFrame.java b/jdk/src/java.httpclient/share/classes/java/net/http/HeadersFrame.java index 657cc351ed2..2e5fca6fe31 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/HeadersFrame.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HeadersFrame.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/Http1Exchange.java b/jdk/src/java.httpclient/share/classes/java/net/http/Http1Exchange.java index d2049016115..a9d668a55b1 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/Http1Exchange.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http1Exchange.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/Http1Request.java b/jdk/src/java.httpclient/share/classes/java/net/http/Http1Request.java index 752383370e7..7141acc29c7 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/Http1Request.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http1Request.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/Http1Response.java b/jdk/src/java.httpclient/share/classes/java/net/http/Http1Response.java index d0a937ed4e9..13576fea4ec 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/Http1Response.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http1Response.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/Http2ClientImpl.java b/jdk/src/java.httpclient/share/classes/java/net/http/Http2ClientImpl.java index 2ee03c9de27..9b4e6bb8b0b 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/Http2ClientImpl.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http2ClientImpl.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/Http2Connection.java b/jdk/src/java.httpclient/share/classes/java/net/http/Http2Connection.java index a648679c818..7cd3329e997 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/Http2Connection.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http2Connection.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/Http2Frame.java b/jdk/src/java.httpclient/share/classes/java/net/http/Http2Frame.java index 273fb75fc32..ede42112405 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/Http2Frame.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http2Frame.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/HttpClientBuilderImpl.java b/jdk/src/java.httpclient/share/classes/java/net/http/HttpClientBuilderImpl.java index c93025d477a..bc9b1a20241 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpClientBuilderImpl.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpClientBuilderImpl.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/HttpClientImpl.java b/jdk/src/java.httpclient/share/classes/java/net/http/HttpClientImpl.java index 226cee95c55..1e9112f9a64 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpClientImpl.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpClientImpl.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/HttpConnection.java b/jdk/src/java.httpclient/share/classes/java/net/http/HttpConnection.java index 21cecd6498e..b431f35edcf 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpConnection.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpConnection.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/HttpHeadersImpl.java b/jdk/src/java.httpclient/share/classes/java/net/http/HttpHeadersImpl.java index ee4122d22f8..406291cae6a 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpHeadersImpl.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpHeadersImpl.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestBuilderImpl.java b/jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestBuilderImpl.java index d6d6604fd51..59a48273954 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestBuilderImpl.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestBuilderImpl.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestImpl.java b/jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestImpl.java index dce47d43f72..118564702dd 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestImpl.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestImpl.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/HttpResponse.java b/jdk/src/java.httpclient/share/classes/java/net/http/HttpResponse.java index eb7f7fa6930..a81fb3a90ea 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpResponse.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpResponse.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/ImmutableHeaders.java b/jdk/src/java.httpclient/share/classes/java/net/http/ImmutableHeaders.java index f1c3d50ff06..46e3a416b73 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/ImmutableHeaders.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ImmutableHeaders.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/Log.java b/jdk/src/java.httpclient/share/classes/java/net/http/Log.java index 51e5ce9633e..4502fba02d8 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/Log.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Log.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/MultiExchange.java b/jdk/src/java.httpclient/share/classes/java/net/http/MultiExchange.java index 69e4bbbc7c4..846388c6574 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/MultiExchange.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/MultiExchange.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/OutgoingHeaders.java b/jdk/src/java.httpclient/share/classes/java/net/http/OutgoingHeaders.java index 6194022c0cc..0af2969d675 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/OutgoingHeaders.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/OutgoingHeaders.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/Pair.java b/jdk/src/java.httpclient/share/classes/java/net/http/Pair.java index b597b54bd92..27591f3cf9a 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/Pair.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Pair.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/PingFrame.java b/jdk/src/java.httpclient/share/classes/java/net/http/PingFrame.java index f9f202d86dc..ef831893d79 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/PingFrame.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/PingFrame.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/PlainHttpConnection.java b/jdk/src/java.httpclient/share/classes/java/net/http/PlainHttpConnection.java index a690956670e..8f5c829dfdf 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/PlainHttpConnection.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/PlainHttpConnection.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/PlainProxyConnection.java b/jdk/src/java.httpclient/share/classes/java/net/http/PlainProxyConnection.java index 1e1170ad184..13a29723677 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/PlainProxyConnection.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/PlainProxyConnection.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/PlainTunnelingConnection.java b/jdk/src/java.httpclient/share/classes/java/net/http/PlainTunnelingConnection.java index 239bf3f8285..fe88f03f339 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/PlainTunnelingConnection.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/PlainTunnelingConnection.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/PriorityFrame.java b/jdk/src/java.httpclient/share/classes/java/net/http/PriorityFrame.java index ab609e897ae..1e8df51ac33 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/PriorityFrame.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/PriorityFrame.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/PushPromiseFrame.java b/jdk/src/java.httpclient/share/classes/java/net/http/PushPromiseFrame.java index 6de21209dc5..f727f015bc1 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/PushPromiseFrame.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/PushPromiseFrame.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/Queue.java b/jdk/src/java.httpclient/share/classes/java/net/http/Queue.java index 1016ada9e1f..a1c71ded3fa 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/Queue.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Queue.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/RawChannel.java b/jdk/src/java.httpclient/share/classes/java/net/http/RawChannel.java index 7804a38f5b7..3086d7a50b9 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/RawChannel.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/RawChannel.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/RawChannelImpl.java b/jdk/src/java.httpclient/share/classes/java/net/http/RawChannelImpl.java index 2a48d1f4c0c..8bead25b3dd 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/RawChannelImpl.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/RawChannelImpl.java @@ -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.io.IOException; diff --git a/jdk/src/java.httpclient/share/classes/java/net/http/RedirectFilter.java b/jdk/src/java.httpclient/share/classes/java/net/http/RedirectFilter.java index 4bd18bd9c50..2024d84e929 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/RedirectFilter.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/RedirectFilter.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/ResetFrame.java b/jdk/src/java.httpclient/share/classes/java/net/http/ResetFrame.java index 6e56586fb14..dd4f3245bf0 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/ResetFrame.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ResetFrame.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/ResponseContent.java b/jdk/src/java.httpclient/share/classes/java/net/http/ResponseContent.java index f6e8d7504d9..1a2e6704afa 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/ResponseContent.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ResponseContent.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/ResponseHeaders.java b/jdk/src/java.httpclient/share/classes/java/net/http/ResponseHeaders.java index 6c25e23df55..9dc20dece84 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/ResponseHeaders.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/ResponseHeaders.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/SSLConnection.java b/jdk/src/java.httpclient/share/classes/java/net/http/SSLConnection.java index 926afd2258b..97d42d3e2d1 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/SSLConnection.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/SSLConnection.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/SSLDelegate.java b/jdk/src/java.httpclient/share/classes/java/net/http/SSLDelegate.java index 80c378e39cf..ef30842c7bd 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/SSLDelegate.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/SSLDelegate.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/SSLTunnelConnection.java b/jdk/src/java.httpclient/share/classes/java/net/http/SSLTunnelConnection.java index 3b33d468185..ccef6efa5d6 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/SSLTunnelConnection.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/SSLTunnelConnection.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/SettingsFrame.java b/jdk/src/java.httpclient/share/classes/java/net/http/SettingsFrame.java index 822a2564407..f6a36a284bb 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/SettingsFrame.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/SettingsFrame.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/Stream.java b/jdk/src/java.httpclient/share/classes/java/net/http/Stream.java index 2c1ad4178e7..0cfda518805 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/Stream.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Stream.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/TimeoutEvent.java b/jdk/src/java.httpclient/share/classes/java/net/http/TimeoutEvent.java index 87786a62a77..117f84dc2aa 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/TimeoutEvent.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/TimeoutEvent.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/Utils.java b/jdk/src/java.httpclient/share/classes/java/net/http/Utils.java index 97313997653..8dde36546b0 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/Utils.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/Utils.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/WS.java b/jdk/src/java.httpclient/share/classes/java/net/http/WS.java index ac327a47609..fd23113a4ef 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/WS.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WS.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/WSBuilder.java b/jdk/src/java.httpclient/share/classes/java/net/http/WSBuilder.java index 42a166e4616..aafba04dc0b 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSBuilder.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSBuilder.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/WSCharsetToolkit.java b/jdk/src/java.httpclient/share/classes/java/net/http/WSCharsetToolkit.java index 5883ea6265e..47e4ad94b59 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSCharsetToolkit.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSCharsetToolkit.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/WSDisposable.java b/jdk/src/java.httpclient/share/classes/java/net/http/WSDisposable.java index bad775e390c..5ece033e5cb 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSDisposable.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSDisposable.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/WSFrame.java b/jdk/src/java.httpclient/share/classes/java/net/http/WSFrame.java index 86cfac99f47..ca46a7221d9 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSFrame.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSFrame.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/WSFrameConsumer.java b/jdk/src/java.httpclient/share/classes/java/net/http/WSFrameConsumer.java index eebfce66e0a..8c30aa211e4 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSFrameConsumer.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSFrameConsumer.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/WSMessageConsumer.java b/jdk/src/java.httpclient/share/classes/java/net/http/WSMessageConsumer.java index 69f46668cfb..3d1e7b1b73f 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSMessageConsumer.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSMessageConsumer.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/WSMessageSender.java b/jdk/src/java.httpclient/share/classes/java/net/http/WSMessageSender.java index 46f8060d0d8..aad9cec650d 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSMessageSender.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSMessageSender.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/WSOpeningHandshake.java b/jdk/src/java.httpclient/share/classes/java/net/http/WSOpeningHandshake.java index 9d0a9c42cc2..996af565d8a 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSOpeningHandshake.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSOpeningHandshake.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/WSOutgoingMessage.java b/jdk/src/java.httpclient/share/classes/java/net/http/WSOutgoingMessage.java index 34e91f81716..d27228049a2 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSOutgoingMessage.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSOutgoingMessage.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/WSProtocolException.java b/jdk/src/java.httpclient/share/classes/java/net/http/WSProtocolException.java index 642e96a508d..4af62466113 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSProtocolException.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSProtocolException.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/WSReceiver.java b/jdk/src/java.httpclient/share/classes/java/net/http/WSReceiver.java index 401b629a2f2..240673eb570 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSReceiver.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSReceiver.java @@ -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; diff --git a/jdk/src/java.httpclient/share/classes/java/net/http/WSShared.java b/jdk/src/java.httpclient/share/classes/java/net/http/WSShared.java index 5534d85b51e..4a39f456b62 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSShared.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSShared.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/WSSharedPool.java b/jdk/src/java.httpclient/share/classes/java/net/http/WSSharedPool.java index 76f993bda2f..2a776e6c071 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSSharedPool.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSSharedPool.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/WSSignalHandler.java b/jdk/src/java.httpclient/share/classes/java/net/http/WSSignalHandler.java index 2dc75f83291..43b827b0400 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSSignalHandler.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSSignalHandler.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/WSTransmitter.java b/jdk/src/java.httpclient/share/classes/java/net/http/WSTransmitter.java index 62219d07009..5293b1fdff1 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSTransmitter.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSTransmitter.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/WSUtils.java b/jdk/src/java.httpclient/share/classes/java/net/http/WSUtils.java index 32928c89e1c..de0d70c6379 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSUtils.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSUtils.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/WSWriter.java b/jdk/src/java.httpclient/share/classes/java/net/http/WSWriter.java index b637f74d519..8ca3271bcbb 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/WSWriter.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSWriter.java @@ -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; diff --git a/jdk/src/java.httpclient/share/classes/java/net/http/WebSocket.java b/jdk/src/java.httpclient/share/classes/java/net/http/WebSocket.java index 6d1452567ed..0756529b4b7 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/WebSocket.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WebSocket.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/java/net/http/WebSocketHandshakeException.java b/jdk/src/java.httpclient/share/classes/java/net/http/WebSocketHandshakeException.java index d9f630632fd..041a014de51 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/WebSocketHandshakeException.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WebSocketHandshakeException.java @@ -22,6 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package java.net.http; /** diff --git a/jdk/src/java.httpclient/share/classes/java/net/http/WindowUpdateFrame.java b/jdk/src/java.httpclient/share/classes/java/net/http/WindowUpdateFrame.java index af744e6ded8..4d96ce89416 100644 --- a/jdk/src/java.httpclient/share/classes/java/net/http/WindowUpdateFrame.java +++ b/jdk/src/java.httpclient/share/classes/java/net/http/WindowUpdateFrame.java @@ -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 --git a/jdk/src/java.httpclient/share/classes/module-info.java b/jdk/src/java.httpclient/share/classes/module-info.java index eee3f326028..5adf7f6e5b2 100644 --- a/jdk/src/java.httpclient/share/classes/module-info.java +++ b/jdk/src/java.httpclient/share/classes/module-info.java @@ -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

    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