--- a/.hgtags Thu Jul 21 16:45:56 2016 +0000
+++ b/.hgtags Thu Jul 21 20:09:19 2016 -0700
@@ -370,3 +370,4 @@
3aa52182b3ad7c5b3a61cf05a59dd07e4c5884e5 jdk-9+125
03e7b2c5ae345be3caf981d76ceb3efe5ff447f8 jdk-9+126
8e45018bde9de4ad15b972ae62874bba52dba2d5 jdk-9+127
+5bf88dce615f6804f9e101a96ffa7c9dfb4fbbbe jdk-9+128
--- a/.hgtags-top-repo Thu Jul 21 16:45:56 2016 +0000
+++ b/.hgtags-top-repo Thu Jul 21 20:09:19 2016 -0700
@@ -370,3 +370,4 @@
9aa7d40f3a453f51e47f4c1b19eff5740a74a9f8 jdk-9+125
3a58466296d36944454756ef01e7513ac5e14a16 jdk-9+126
8fa686245bd2a072ece3392743460030f0854520 jdk-9+127
+b30ae794d974d7dd3eb4e84203f70021823fa6c6 jdk-9+128
--- a/common/autoconf/boot-jdk.m4 Thu Jul 21 16:45:56 2016 +0000
+++ b/common/autoconf/boot-jdk.m4 Thu Jul 21 20:09:19 2016 -0700
@@ -345,6 +345,9 @@
# Disable special log output when a debug build is used as Boot JDK...
ADD_JVM_ARG_IF_OK([-XX:-PrintVMOptions -XX:-UnlockDiagnosticVMOptions -XX:-LogVMOutput],boot_jdk_jvmargs,[$JAVA])
+ # Force en-US environment
+ ADD_JVM_ARG_IF_OK([-Duser.language=en -Duser.country=US],boot_jdk_jvmargs,[$JAVA])
+
# Apply user provided options.
ADD_JVM_ARG_IF_OK([$with_boot_jdk_jvmargs],boot_jdk_jvmargs,[$JAVA])
--- a/common/autoconf/generated-configure.sh Thu Jul 21 16:45:56 2016 +0000
+++ b/common/autoconf/generated-configure.sh Thu Jul 21 20:09:19 2016 -0700
@@ -5094,7 +5094,7 @@
#CUSTOM_AUTOCONF_INCLUDE
# Do not change or remove the following line, it is needed for consistency checks:
-DATE_WHEN_GENERATED=1467960715
+DATE_WHEN_GENERATED=1469202305
###############################################################################
#
@@ -65048,6 +65048,23 @@
fi
+ # Force en-US environment
+
+ $ECHO "Check if jvm arg is ok: -Duser.language=en -Duser.country=US" >&5
+ $ECHO "Command: $JAVA -Duser.language=en -Duser.country=US -version" >&5
+ OUTPUT=`$JAVA -Duser.language=en -Duser.country=US -version 2>&1`
+ FOUND_WARN=`$ECHO "$OUTPUT" | $GREP -i warn`
+ FOUND_VERSION=`$ECHO $OUTPUT | $GREP " version \""`
+ if test "x$FOUND_VERSION" != x && test "x$FOUND_WARN" = x; then
+ boot_jdk_jvmargs="$boot_jdk_jvmargs -Duser.language=en -Duser.country=US"
+ JVM_ARG_OK=true
+ else
+ $ECHO "Arg failed:" >&5
+ $ECHO "$OUTPUT" >&5
+ JVM_ARG_OK=false
+ fi
+
+
# Apply user provided options.
$ECHO "Check if jvm arg is ok: $with_boot_jdk_jvmargs" >&5
--- a/corba/.hgtags Thu Jul 21 16:45:56 2016 +0000
+++ b/corba/.hgtags Thu Jul 21 20:09:19 2016 -0700
@@ -370,3 +370,4 @@
1d48e67d1b91eb9f72e49e69a4021edb85e357fc jdk-9+125
c7f5ba08fcd4b8416e62c21229f9a07c95498919 jdk-9+126
8fab452b6f4710762ba1d8e55fd62db00b1355fe jdk-9+127
+1f093d3f8cd99cd37c3b0af4cf5c3bffaa9c8b98 jdk-9+128
--- a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/activation/ORBD.java Thu Jul 21 16:45:56 2016 +0000
+++ b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/activation/ORBD.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,4 @@
/*
- *
* Copyright (c) 1997, 2004, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -22,7 +21,6 @@
* Please 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.corba.se.impl.activation;
--- a/corba/src/java.corba/share/classes/com/sun/corba/se/impl/orbutil/ORBUtility.java Thu Jul 21 16:45:56 2016 +0000
+++ b/corba/src/java.corba/share/classes/com/sun/corba/se/impl/orbutil/ORBUtility.java Thu Jul 21 20:09:19 2016 -0700
@@ -34,21 +34,13 @@
import java.security.Policy;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Set;
-import java.util.Map.Entry;
-import java.util.Collection;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Enumeration;
-import java.util.Properties;
-import java.util.IdentityHashMap;
import java.util.StringTokenizer;
import java.util.NoSuchElementException;
@@ -165,8 +157,18 @@
* Return default ValueHandler
*/
public static ValueHandler createValueHandler() {
+ ValueHandler vh;
+ try {
+ vh = AccessController.doPrivileged(new PrivilegedExceptionAction<ValueHandler>() {
+ public ValueHandler run() throws Exception {
return Util.createValueHandler();
}
+ });
+ } catch (PrivilegedActionException e) {
+ throw new InternalError(e.getCause());
+ }
+ return vh;
+ }
/**
* Returns true if it was accurately determined that the remote ORB is
@@ -664,7 +666,16 @@
* ValueHandler.
*/
public static byte getMaxStreamFormatVersion() {
- ValueHandler vh = Util.createValueHandler();
+ ValueHandler vh;
+ try {
+ vh = AccessController.doPrivileged(new PrivilegedExceptionAction<ValueHandler>() {
+ public ValueHandler run() throws Exception {
+ return Util.createValueHandler();
+ }
+ });
+ } catch (PrivilegedActionException e) {
+ throw new InternalError(e.getCause());
+ }
if (!(vh instanceof javax.rmi.CORBA.ValueHandlerMultiFormat))
return ORBConstants.STREAM_FORMAT_VERSION_1;
--- a/corba/src/java.corba/share/classes/javax/rmi/CORBA/Util.java Thu Jul 21 16:45:56 2016 +0000
+++ b/corba/src/java.corba/share/classes/javax/rmi/CORBA/Util.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 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
@@ -45,6 +45,7 @@
import java.rmi.Remote;
import java.io.File;
import java.io.FileInputStream;
+import java.io.SerializablePermission;
import java.net.MalformedURLException ;
import java.security.AccessController;
import java.security.PrivilegedAction;
@@ -195,6 +196,8 @@
*/
public static ValueHandler createValueHandler() {
+ isCustomSerializationPermitted();
+
if (utilDelegate != null) {
return utilDelegate.createValueHandler();
}
@@ -337,6 +340,7 @@
// security reasons. If you know a better solution how to share this code
// then remove it from PortableRemoteObject. Also in Stub.java
private static Object createDelegate(String classKey) {
+
String className = (String)
AccessController.doPrivileged(new GetPropertyAction(classKey));
if (className == null) {
@@ -345,7 +349,6 @@
className = props.getProperty(classKey);
}
}
-
if (className == null) {
return new com.sun.corba.se.impl.javax.rmi.CORBA.Util();
}
@@ -389,4 +392,14 @@
new GetORBPropertiesFileAction());
}
+ private static void isCustomSerializationPermitted() {
+ SecurityManager sm = System.getSecurityManager();
+ if ( sm != null) {
+ // check that a serialization permission has been
+ // set to allow the loading of the Util delegate
+ // which provides access to custom ValueHandler
+ sm.checkPermission(new SerializablePermission(
+ "enableCustomValueHandler"));
}
+ }
+}
--- a/hotspot/.hgtags Thu Jul 21 16:45:56 2016 +0000
+++ b/hotspot/.hgtags Thu Jul 21 20:09:19 2016 -0700
@@ -530,3 +530,4 @@
bb640b49741af3f57f9994129934c46fc173219f jdk-9+125
adc8c84b7cf8c540d920182f78a2bc982366432a jdk-9+126
352357128f602dcf0426b1cbe011a4685a4d9f97 jdk-9+127
+22bf6db9767b1b3a1994cbf32eb3331f31ae2093 jdk-9+128
--- a/hotspot/src/share/vm/classfile/compactHashtable.hpp Thu Jul 21 16:45:56 2016 +0000
+++ b/hotspot/src/share/vm/classfile/compactHashtable.hpp Thu Jul 21 20:09:19 2016 -0700
@@ -270,6 +270,10 @@
// For reading from/writing to the CDS archive
void serialize(SerializeClosure* soc);
+
+ uintx base_address() {
+ return (uintx) _base_address;
+ }
};
////////////////////////////////////////////////////////////////////////
--- a/hotspot/src/share/vm/classfile/symbolTable.cpp Thu Jul 21 16:45:56 2016 +0000
+++ b/hotspot/src/share/vm/classfile/symbolTable.cpp Thu Jul 21 20:09:19 2016 -0700
@@ -238,6 +238,29 @@
}
}
+u4 SymbolTable::encode_shared(Symbol* sym) {
+ assert(DumpSharedSpaces, "called only during dump time");
+ uintx base_address = uintx(MetaspaceShared::shared_rs()->base());
+ uintx offset = uintx(sym) - base_address;
+ assert(offset < 0x7fffffff, "sanity");
+ return u4(offset);
+}
+
+Symbol* SymbolTable::decode_shared(u4 offset) {
+ assert(!DumpSharedSpaces, "called only during runtime");
+ uintx base_address = _shared_table.base_address();
+ Symbol* sym = (Symbol*)(base_address + offset);
+
+#ifndef PRODUCT
+ const char* s = (const char*)sym->bytes();
+ int len = sym->utf8_length();
+ unsigned int hash = hash_symbol(s, len);
+ assert(sym == lookup_shared(s, len, hash), "must be shared symbol");
+#endif
+
+ return sym;
+}
+
// Pick hashing algorithm.
unsigned int SymbolTable::hash_symbol(const char* s, int len) {
return use_alternate_hashcode() ?
--- a/hotspot/src/share/vm/classfile/symbolTable.hpp Thu Jul 21 16:45:56 2016 +0000
+++ b/hotspot/src/share/vm/classfile/symbolTable.hpp Thu Jul 21 20:09:19 2016 -0700
@@ -253,6 +253,8 @@
// Sharing
static void serialize(SerializeClosure* soc);
+ static u4 encode_shared(Symbol* sym);
+ static Symbol* decode_shared(u4 offset);
// Rehash the symbol table if it gets out of balance
static void rehash_table();
--- a/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp Thu Jul 21 16:45:56 2016 +0000
+++ b/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp Thu Jul 21 20:09:19 2016 -0700
@@ -78,7 +78,19 @@
TRAPS) {
return NULL;
}
+
static void serialize(SerializeClosure* soc) {}
+
+ // The (non-application) CDS implementation supports only classes in the boot
+ // class loader, which ensures that the verification constraints are the same
+ // during archive creation time and runtime. Thus we can do the constraint checks
+ // entirely during archive creation time.
+ static bool add_verification_constraint(Klass* k, Symbol* name,
+ Symbol* from_name, bool from_field_is_protected,
+ bool from_is_array, bool from_is_object) {return false;}
+ static void finalize_verification_constraints() {}
+ static void check_verification_constraints(instanceKlassHandle klass,
+ TRAPS) {}
};
#endif // SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP
--- a/hotspot/src/share/vm/classfile/verificationType.cpp Thu Jul 21 16:45:56 2016 +0000
+++ b/hotspot/src/share/vm/classfile/verificationType.cpp Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 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
@@ -24,6 +24,7 @@
#include "precompiled.hpp"
#include "classfile/symbolTable.hpp"
+#include "classfile/systemDictionaryShared.hpp"
#include "classfile/verificationType.hpp"
#include "classfile/verifier.hpp"
@@ -41,6 +42,39 @@
}
}
+bool VerificationType::resolve_and_check_assignability(instanceKlassHandle klass, Symbol* name,
+ Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object, TRAPS) {
+ Klass* obj = SystemDictionary::resolve_or_fail(
+ name, Handle(THREAD, klass->class_loader()),
+ Handle(THREAD, klass->protection_domain()), true, CHECK_false);
+ if (log_is_enabled(Debug, class, resolve)) {
+ Verifier::trace_class_resolution(obj, klass());
+ }
+
+ KlassHandle this_class(THREAD, obj);
+
+ if (this_class->is_interface() && (!from_field_is_protected ||
+ from_name != vmSymbols::java_lang_Object())) {
+ // If we are not trying to access a protected field or method in
+ // java.lang.Object then, for arrays, we only allow assignability
+ // to interfaces java.lang.Cloneable and java.io.Serializable.
+ // Otherwise, we treat interfaces as java.lang.Object.
+ return !from_is_array ||
+ this_class == SystemDictionary::Cloneable_klass() ||
+ this_class == SystemDictionary::Serializable_klass();
+ } else if (from_is_object) {
+ Klass* from_class = SystemDictionary::resolve_or_fail(
+ from_name, Handle(THREAD, klass->class_loader()),
+ Handle(THREAD, klass->protection_domain()), true, CHECK_false);
+ if (log_is_enabled(Debug, class, resolve)) {
+ Verifier::trace_class_resolution(from_class, klass());
+ }
+ return InstanceKlass::cast(from_class)->is_subclass_of(this_class());
+ }
+
+ return false;
+}
+
bool VerificationType::is_reference_assignable_from(
const VerificationType& from, ClassVerifier* context,
bool from_field_is_protected, TRAPS) const {
@@ -58,33 +92,17 @@
// any object or array is assignable to java.lang.Object
return true;
}
- Klass* obj = SystemDictionary::resolve_or_fail(
- name(), Handle(THREAD, klass->class_loader()),
- Handle(THREAD, klass->protection_domain()), true, CHECK_false);
- if (log_is_enabled(Debug, class, resolve)) {
- Verifier::trace_class_resolution(obj, klass());
+
+ if (DumpSharedSpaces && SystemDictionaryShared::add_verification_constraint(klass(),
+ name(), from.name(), from_field_is_protected, from.is_array(),
+ from.is_object())) {
+ // If add_verification_constraint() returns true, the resolution/check should be
+ // delayed until runtime.
+ return true;
}
- KlassHandle this_class(THREAD, obj);
-
- if (this_class->is_interface() && (!from_field_is_protected ||
- from.name() != vmSymbols::java_lang_Object())) {
- // If we are not trying to access a protected field or method in
- // java.lang.Object then, for arrays, we only allow assignability
- // to interfaces java.lang.Cloneable and java.io.Serializable.
- // Otherwise, we treat interfaces as java.lang.Object.
- return !from.is_array() ||
- this_class == SystemDictionary::Cloneable_klass() ||
- this_class == SystemDictionary::Serializable_klass();
- } else if (from.is_object()) {
- Klass* from_class = SystemDictionary::resolve_or_fail(
- from.name(), Handle(THREAD, klass->class_loader()),
- Handle(THREAD, klass->protection_domain()), true, CHECK_false);
- if (log_is_enabled(Debug, class, resolve)) {
- Verifier::trace_class_resolution(from_class, klass());
- }
- return InstanceKlass::cast(from_class)->is_subclass_of(this_class());
- }
+ return resolve_and_check_assignability(klass(), name(), from.name(),
+ from_field_is_protected, from.is_array(), from.is_object(), THREAD);
} else if (is_array() && from.is_array()) {
VerificationType comp_this = get_component(context, CHECK_false);
VerificationType comp_from = from.get_component(context, CHECK_false);
--- a/hotspot/src/share/vm/classfile/verificationType.hpp Thu Jul 21 16:45:56 2016 +0000
+++ b/hotspot/src/share/vm/classfile/verificationType.hpp Thu Jul 21 20:09:19 2016 -0700
@@ -333,6 +333,12 @@
bool is_reference_assignable_from(
const VerificationType&, ClassVerifier*, bool from_field_is_protected,
TRAPS) const;
+
+ public:
+ static bool resolve_and_check_assignability(instanceKlassHandle klass, Symbol* name,
+ Symbol* from_name, bool from_field_is_protected,
+ bool from_is_array, bool from_is_object,
+ TRAPS);
};
#endif // SHARE_VM_CLASSFILE_VERIFICATIONTYPE_HPP
--- a/hotspot/src/share/vm/classfile/verifier.cpp Thu Jul 21 16:45:56 2016 +0000
+++ b/hotspot/src/share/vm/classfile/verifier.cpp Thu Jul 21 20:09:19 2016 -0700
@@ -2377,9 +2377,17 @@
case Bytecodes::_ifnonnull:
target = bcs.dest();
if (visited_branches->contains(bci)) {
- if (bci_stack->is_empty()) return true;
- // Pop a bytecode starting offset and scan from there.
- bcs.set_start(bci_stack->pop());
+ if (bci_stack->is_empty()) {
+ if (handler_stack->is_empty()) {
+ return true;
+ } else {
+ // Parse the catch handlers for try blocks containing athrow.
+ bcs.set_start(handler_stack->pop());
+ }
+ } else {
+ // Pop a bytecode starting offset and scan from there.
+ bcs.set_start(bci_stack->pop());
+ }
} else {
if (target > bci) { // forward branch
if (target >= code_length) return false;
@@ -2402,9 +2410,17 @@
case Bytecodes::_goto_w:
target = (opcode == Bytecodes::_goto ? bcs.dest() : bcs.dest_w());
if (visited_branches->contains(bci)) {
- if (bci_stack->is_empty()) return true;
- // Been here before, pop new starting offset from stack.
- bcs.set_start(bci_stack->pop());
+ if (bci_stack->is_empty()) {
+ if (handler_stack->is_empty()) {
+ return true;
+ } else {
+ // Parse the catch handlers for try blocks containing athrow.
+ bcs.set_start(handler_stack->pop());
+ }
+ } else {
+ // Been here before, pop new starting offset from stack.
+ bcs.set_start(bci_stack->pop());
+ }
} else {
if (target >= code_length) return false;
// Continue scanning from the target onward.
--- a/hotspot/src/share/vm/interpreter/bytecodeStream.cpp Thu Jul 21 16:45:56 2016 +0000
+++ b/hotspot/src/share/vm/interpreter/bytecodeStream.cpp Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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,12 +31,12 @@
// set next bytecode position
address bcp = RawBytecodeStream::bcp();
address end = method()->code_base() + end_bci();
- int l = Bytecodes::raw_special_length_at(bcp, end);
- if (l <= 0 || (_bci + l) > _end_bci) {
+ int len = Bytecodes::raw_special_length_at(bcp, end);
+ // Very large tableswitch or lookupswitch size can cause _next_bci to overflow.
+ if (len <= 0 || (_bci > _end_bci - len) || (_bci - len >= _next_bci)) {
code = Bytecodes::_illegal;
} else {
- _next_bci += l;
- assert(_bci < _next_bci, "length must be > 0");
+ _next_bci += len;
// set attributes
_is_wide = false;
// check for special (uncommon) cases
--- a/hotspot/src/share/vm/interpreter/bytecodeStream.hpp Thu Jul 21 16:45:56 2016 +0000
+++ b/hotspot/src/share/vm/interpreter/bytecodeStream.hpp Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -135,12 +135,15 @@
code = Bytecodes::code_or_bp_at(bcp);
// set next bytecode position
- int l = Bytecodes::length_for(code);
- if (l > 0 && (_bci + l) <= _end_bci) {
+ int len = Bytecodes::length_for(code);
+ if (len > 0 && (_bci <= _end_bci - len)) {
assert(code != Bytecodes::_wide && code != Bytecodes::_tableswitch
&& code != Bytecodes::_lookupswitch, "can't be special bytecode");
_is_wide = false;
- _next_bci += l;
+ _next_bci += len;
+ if (_next_bci <= _bci) { // Check for integer overflow
+ code = Bytecodes::_illegal;
+ }
_raw_code = code;
return code;
} else {
@@ -189,19 +192,23 @@
// note that we cannot advance before having the
// tty bytecode otherwise the stepping is wrong!
// (carefull: length_for(...) must be used first!)
- int l = Bytecodes::length_for(code);
- if (l == 0) l = Bytecodes::length_at(_method(), bcp);
- _next_bci += l;
- assert(_bci < _next_bci, "length must be > 0");
- // set attributes
- _is_wide = false;
- // check for special (uncommon) cases
- if (code == Bytecodes::_wide) {
- raw_code = (Bytecodes::Code)bcp[1];
- code = raw_code; // wide BCs are always Java-normal
- _is_wide = true;
+ int len = Bytecodes::length_for(code);
+ if (len == 0) len = Bytecodes::length_at(_method(), bcp);
+ if (len <= 0 || (_bci > _end_bci - len) || (_bci - len >= _next_bci)) {
+ raw_code = code = Bytecodes::_illegal;
+ } else {
+ _next_bci += len;
+ assert(_bci < _next_bci, "length must be > 0");
+ // set attributes
+ _is_wide = false;
+ // check for special (uncommon) cases
+ if (code == Bytecodes::_wide) {
+ raw_code = (Bytecodes::Code)bcp[1];
+ code = raw_code; // wide BCs are always Java-normal
+ _is_wide = true;
+ }
+ assert(Bytecodes::is_java_code(code), "sanity check");
}
- assert(Bytecodes::is_java_code(code), "sanity check");
}
_raw_code = raw_code;
_code = code;
--- a/hotspot/src/share/vm/memory/metaspaceShared.cpp Thu Jul 21 16:45:56 2016 +0000
+++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp Thu Jul 21 20:09:19 2016 -0700
@@ -60,6 +60,7 @@
bool MetaspaceShared::_check_classes_made_progress;
bool MetaspaceShared::_has_error_classes;
bool MetaspaceShared::_archive_loading_failed = false;
+bool MetaspaceShared::_remapped_readwrite = false;
address MetaspaceShared::_cds_i2i_entry_code_buffers = NULL;
size_t MetaspaceShared::_cds_i2i_entry_code_buffers_size = 0;
SharedMiscRegion MetaspaceShared::_mc;
@@ -806,6 +807,10 @@
exit(1);
}
}
+
+ // Copy the verification constraints from C_HEAP-alloced GrowableArrays to RO-alloced
+ // Arrays
+ SystemDictionaryShared::finalize_verification_constraints();
}
void MetaspaceShared::prepare_for_dumping() {
@@ -1181,6 +1186,7 @@
if (!mapinfo->remap_shared_readonly_as_readwrite()) {
return false;
}
+ _remapped_readwrite = true;
}
return true;
}
--- a/hotspot/src/share/vm/memory/metaspaceShared.hpp Thu Jul 21 16:45:56 2016 +0000
+++ b/hotspot/src/share/vm/memory/metaspaceShared.hpp Thu Jul 21 20:09:19 2016 -0700
@@ -125,6 +125,7 @@
static bool _check_classes_made_progress;
static bool _has_error_classes;
static bool _archive_loading_failed;
+ static bool _remapped_readwrite;
static address _cds_i2i_entry_code_buffers;
static size_t _cds_i2i_entry_code_buffers_size;
@@ -205,6 +206,10 @@
// sharing is enabled. Simply returns true if sharing is not enabled
// or if the remapping has already been done by a prior call.
static bool remap_shared_readonly_as_readwrite() NOT_CDS_RETURN_(true);
+ static bool remapped_readwrite() {
+ CDS_ONLY(return _remapped_readwrite);
+ NOT_CDS(return false);
+ }
static void print_shared_spaces();
--- a/hotspot/src/share/vm/oops/instanceKlass.cpp Thu Jul 21 16:45:56 2016 +0000
+++ b/hotspot/src/share/vm/oops/instanceKlass.cpp Thu Jul 21 20:09:19 2016 -0700
@@ -27,6 +27,7 @@
#include "classfile/classFileStream.hpp"
#include "classfile/javaClasses.hpp"
#include "classfile/systemDictionary.hpp"
+#include "classfile/systemDictionaryShared.hpp"
#include "classfile/verifier.hpp"
#include "classfile/vmSymbols.hpp"
#include "code/dependencyContext.hpp"
@@ -597,6 +598,8 @@
// also sets rewritten
this_k->rewrite_class(CHECK_false);
+ } else if (this_k->is_shared()) {
+ SystemDictionaryShared::check_verification_constraints(this_k, CHECK_false);
}
// relocate jsrs and link methods after they are all rewritten
@@ -606,7 +609,12 @@
// methods have been rewritten since rewrite may
// fabricate new Method*s.
// also does loader constraint checking
- if (!this_k()->is_shared()) {
+ //
+ // initialize_vtable and initialize_itable need to be rerun for
+ // a shared class if the class is not loaded by the NULL classloader.
+ ClassLoaderData * loader_data = this_k->class_loader_data();
+ if (!(this_k->is_shared() &&
+ loader_data->is_the_null_class_loader_data())) {
ResourceMark rm(THREAD);
this_k->vtable()->initialize_vtable(true, CHECK_false);
this_k->itable()->initialize_itable(true, CHECK_false);
--- a/hotspot/src/share/vm/oops/klassVtable.cpp Thu Jul 21 16:45:56 2016 +0000
+++ b/hotspot/src/share/vm/oops/klassVtable.cpp Thu Jul 21 20:09:19 2016 -0700
@@ -27,6 +27,7 @@
#include "classfile/vmSymbols.hpp"
#include "gc/shared/gcLocker.hpp"
#include "logging/log.hpp"
+#include "memory/metaspaceShared.hpp"
#include "memory/resourceArea.hpp"
#include "memory/universe.inline.hpp"
#include "oops/instanceKlass.hpp"
@@ -42,6 +43,10 @@
return InstanceKlass::cast(_klass());
}
+bool klassVtable::is_preinitialized_vtable() {
+ return _klass->is_shared() && !MetaspaceShared::remapped_readwrite();
+}
+
// this function computes the vtable size (including the size needed for miranda
// methods) and the number of miranda methods in this class.
@@ -126,6 +131,12 @@
int klassVtable::initialize_from_super(KlassHandle super) {
if (super.is_null()) {
return 0;
+ } else if (is_preinitialized_vtable()) {
+ // A shared class' vtable is preinitialized at dump time. No need to copy
+ // methods from super class for shared class, as that was already done
+ // during archiving time. However, if Jvmti has redefined a class,
+ // copy super class's vtable in case the super class has changed.
+ return super->vtable()->length();
} else {
// copy methods from superKlass
klassVtable* superVtable = super->vtable();
@@ -152,6 +163,8 @@
KlassHandle super (THREAD, klass()->java_super());
int nofNewEntries = 0;
+ bool is_shared = _klass->is_shared();
+
if (!klass()->is_array_klass()) {
ResourceMark rm(THREAD);
log_develop_debug(vtables)("Initializing: %s", _klass->name()->as_C_string());
@@ -164,6 +177,7 @@
#endif
if (Universe::is_bootstrapping()) {
+ assert(!is_shared, "sanity");
// just clear everything
for (int i = 0; i < _length; i++) table()[i].clear();
return;
@@ -203,6 +217,7 @@
if (len > 0) {
Array<int>* def_vtable_indices = NULL;
if ((def_vtable_indices = ik()->default_vtable_indices()) == NULL) {
+ assert(!is_shared, "shared class def_vtable_indices does not exist");
def_vtable_indices = ik()->create_new_default_vtable_indices(len, CHECK);
} else {
assert(def_vtable_indices->length() == len, "reinit vtable len?");
@@ -217,7 +232,15 @@
// needs new entry
if (needs_new_entry) {
put_method_at(mh(), initialized);
- def_vtable_indices->at_put(i, initialized); //set vtable index
+ if (is_preinitialized_vtable()) {
+ // At runtime initialize_vtable is rerun for a shared class
+ // (loaded by the non-boot loader) as part of link_class_impl().
+ // The dumptime vtable index should be the same as the runtime index.
+ assert(def_vtable_indices->at(i) == initialized,
+ "dump time vtable index is different from runtime index");
+ } else {
+ def_vtable_indices->at_put(i, initialized); //set vtable index
+ }
initialized++;
}
}
@@ -378,7 +401,8 @@
}
// we need a new entry if there is no superclass
- if (klass->super() == NULL) {
+ Klass* super = klass->super();
+ if (super == NULL) {
return allocate_new;
}
@@ -407,7 +431,15 @@
Symbol* target_classname = target_klass->name();
for(int i = 0; i < super_vtable_len; i++) {
- Method* super_method = method_at(i);
+ Method* super_method;
+ if (is_preinitialized_vtable()) {
+ // If this is a shared class, the vtable is already in the final state (fully
+ // initialized). Need to look at the super's vtable.
+ klassVtable* superVtable = super->vtable();
+ super_method = superVtable->method_at(i);
+ } else {
+ super_method = method_at(i);
+ }
// Check if method name matches
if (super_method->name() == name && super_method->signature() == signature) {
@@ -475,7 +507,15 @@
target_method()->set_vtable_index(i);
} else {
if (def_vtable_indices != NULL) {
- def_vtable_indices->at_put(default_index, i);
+ if (is_preinitialized_vtable()) {
+ // At runtime initialize_vtable is rerun as part of link_class_impl()
+ // for a shared class loaded by the non-boot loader.
+ // The dumptime vtable index should be the same as the runtime index.
+ assert(def_vtable_indices->at(default_index) == i,
+ "dump time vtable index is different from runtime index");
+ } else {
+ def_vtable_indices->at_put(default_index, i);
+ }
}
assert(super_method->is_default_method() || super_method->is_overpass()
|| super_method->is_abstract(), "default override error");
@@ -490,17 +530,26 @@
}
void klassVtable::put_method_at(Method* m, int index) {
- if (log_develop_is_enabled(Trace, vtables)) {
- ResourceMark rm;
- outputStream* logst = Log(vtables)::trace_stream();
- const char* sig = (m != NULL) ? m->name_and_sig_as_C_string() : "<NULL>";
- logst->print("adding %s at index %d, flags: ", sig, index);
- if (m != NULL) {
- m->print_linkage_flags(logst);
+ if (is_preinitialized_vtable()) {
+ // At runtime initialize_vtable is rerun as part of link_class_impl()
+ // for shared class loaded by the non-boot loader to obtain the loader
+ // constraints based on the runtime classloaders' context. The dumptime
+ // method at the vtable index should be the same as the runtime method.
+ assert(table()[index].method() == m,
+ "archived method is different from the runtime method");
+ } else {
+ if (log_develop_is_enabled(Trace, vtables)) {
+ ResourceMark rm;
+ outputStream* logst = Log(vtables)::trace_stream();
+ const char* sig = (m != NULL) ? m->name_and_sig_as_C_string() : "<NULL>";
+ logst->print("adding %s at index %d, flags: ", sig, index);
+ if (m != NULL) {
+ m->print_linkage_flags(logst);
+ }
+ logst->cr();
}
- logst->cr();
+ table()[index].set(m);
}
- table()[index].set(m);
}
// Find out if a method "m" with superclass "super", loader "classloader" and
@@ -950,7 +999,15 @@
void itableMethodEntry::initialize(Method* m) {
if (m == NULL) return;
- _method = m;
+ if (MetaspaceShared::is_in_shared_space((void*)&_method) &&
+ !MetaspaceShared::remapped_readwrite()) {
+ // At runtime initialize_itable is rerun as part of link_class_impl()
+ // for a shared class loaded by the non-boot loader.
+ // The dumptime itable method entry should be the same as the runtime entry.
+ assert(_method == m, "sanity");
+ } else {
+ _method = m;
+ }
}
klassItable::klassItable(instanceKlassHandle klass) {
@@ -1054,7 +1111,11 @@
logst->cr();
}
if (!m->has_vtable_index()) {
- assert(m->vtable_index() == Method::pending_itable_index, "set by initialize_vtable");
+ // A shared method could have an initialized itable_index that
+ // is < 0.
+ assert(m->vtable_index() == Method::pending_itable_index ||
+ m->is_shared(),
+ "set by initialize_vtable");
m->set_itable_index(ime_num);
// Progress to next itable entry
ime_num++;
@@ -1248,7 +1309,6 @@
}
#endif // INCLUDE_JVMTI
-
// Setup
class InterfaceVisiterClosure : public StackObj {
public:
--- a/hotspot/src/share/vm/oops/klassVtable.hpp Thu Jul 21 16:45:56 2016 +0000
+++ b/hotspot/src/share/vm/oops/klassVtable.hpp Thu Jul 21 20:09:19 2016 -0700
@@ -153,6 +153,19 @@
Array<Klass*>* local_interfaces);
void verify_against(outputStream* st, klassVtable* vt, int index);
inline InstanceKlass* ik() const;
+ // When loading a class from CDS archive at run time, and no class redefintion
+ // has happened, it is expected that the class's itable/vtables are
+ // laid out exactly the same way as they had been during dump time.
+ // Therefore, in klassVtable::initialize_[iv]table, we do not layout the
+ // tables again. Instead, we only rerun the process to create/check
+ // the class loader constraints. In non-product builds, we add asserts to
+ // guarantee that the table's layout would be the same as at dump time.
+ //
+ // If JVMTI redefines any class, the read-only shared memory are remapped
+ // as read-write. A shared class' vtable/itable are re-initialized and
+ // might have different layout due to class redefinition of the shared class
+ // or its super types.
+ bool is_preinitialized_vtable();
};
--- a/hotspot/src/share/vm/oops/method.cpp Thu Jul 21 16:45:56 2016 +0000
+++ b/hotspot/src/share/vm/oops/method.cpp Thu Jul 21 20:09:19 2016 -0700
@@ -313,6 +313,33 @@
unlink_method();
}
+void Method::set_vtable_index(int index) {
+ if (is_shared() && !MetaspaceShared::remapped_readwrite()) {
+ // At runtime initialize_vtable is rerun as part of link_class_impl()
+ // for a shared class loaded by the non-boot loader to obtain the loader
+ // constraints based on the runtime classloaders' context.
+ return; // don't write into the shared class
+ } else {
+ _vtable_index = index;
+ }
+}
+
+void Method::set_itable_index(int index) {
+ if (is_shared() && !MetaspaceShared::remapped_readwrite()) {
+ // At runtime initialize_itable is rerun as part of link_class_impl()
+ // for a shared class loaded by the non-boot loader to obtain the loader
+ // constraints based on the runtime classloaders' context. The dumptime
+ // itable index should be the same as the runtime index.
+ assert(_vtable_index == itable_index_max - index,
+ "archived itable index is different from runtime index");
+ return; // don’t write into the shared class
+ } else {
+ _vtable_index = itable_index_max - index;
+ }
+ assert(valid_itable_index(), "");
+}
+
+
bool Method::was_executed_more_than(int n) {
// Invocation counter is reset when the Method* is compiled.
--- a/hotspot/src/share/vm/oops/method.hpp Thu Jul 21 16:45:56 2016 +0000
+++ b/hotspot/src/share/vm/oops/method.hpp Thu Jul 21 20:09:19 2016 -0700
@@ -470,12 +470,12 @@
DEBUG_ONLY(bool valid_vtable_index() const { return _vtable_index >= nonvirtual_vtable_index; })
bool has_vtable_index() const { return _vtable_index >= 0; }
int vtable_index() const { return _vtable_index; }
- void set_vtable_index(int index) { _vtable_index = index; }
+ void set_vtable_index(int index);
DEBUG_ONLY(bool valid_itable_index() const { return _vtable_index <= pending_itable_index; })
bool has_itable_index() const { return _vtable_index <= itable_index_max; }
int itable_index() const { assert(valid_itable_index(), "");
return itable_index_max - _vtable_index; }
- void set_itable_index(int index) { _vtable_index = itable_index_max - index; assert(valid_itable_index(), ""); }
+ void set_itable_index(int index);
// interpreter entry
address interpreter_entry() const { return _i2i_entry; }
--- a/hotspot/src/share/vm/opto/library_call.cpp Thu Jul 21 16:45:56 2016 +0000
+++ b/hotspot/src/share/vm/opto/library_call.cpp Thu Jul 21 20:09:19 2016 -0700
@@ -2405,8 +2405,13 @@
Compile::AliasType* alias_type = C->alias_type(adr_type);
assert(alias_type->index() != Compile::AliasIdxBot, "no bare pointers here");
- assert(alias_type->adr_type() == TypeRawPtr::BOTTOM || alias_type->adr_type() == TypeOopPtr::BOTTOM ||
- alias_type->basic_type() != T_ILLEGAL, "field, array element or unknown");
+ // Only field, array element or unknown locations are supported.
+ if (alias_type->adr_type() != TypeRawPtr::BOTTOM &&
+ alias_type->adr_type() != TypeOopPtr::BOTTOM &&
+ alias_type->basic_type() == T_ILLEGAL) {
+ return false;
+ }
+
bool mismatched = false;
BasicType bt = alias_type->basic_type();
if (bt != T_ILLEGAL) {
@@ -2782,12 +2787,6 @@
ShouldNotReachHere();
}
- // Null check receiver.
- receiver = null_check(receiver);
- if (stopped()) {
- return true;
- }
-
// Build field offset expression.
// We currently rely on the cookies produced by Unsafe.xxxFieldOffset
// to be plain byte offsets, which are also the same as those accepted
@@ -2799,8 +2798,6 @@
const TypePtr *adr_type = _gvn.type(adr)->isa_ptr();
Compile::AliasType* alias_type = C->alias_type(adr_type);
- assert(alias_type->adr_type() == TypeRawPtr::BOTTOM || alias_type->adr_type() == TypeOopPtr::BOTTOM ||
- alias_type->basic_type() != T_ILLEGAL, "field, array element or unknown");
BasicType bt = alias_type->basic_type();
if (bt != T_ILLEGAL &&
((bt == T_OBJECT || bt == T_ARRAY) != (type == T_OBJECT))) {
@@ -2832,6 +2829,12 @@
ShouldNotReachHere();
}
+ // Null check receiver.
+ receiver = null_check(receiver);
+ if (stopped()) {
+ return true;
+ }
+
int alias_idx = C->get_alias_index(adr_type);
// Memory-model-wise, a LoadStore acts like a little synchronized
--- a/hotspot/test/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java Thu Jul 21 16:45:56 2016 +0000
+++ b/hotspot/test/compiler/jvmci/jdk.vm.ci.runtime.test/src/jdk/vm/ci/runtime/test/TestResolvedJavaType.java Thu Jul 21 20:09:19 2016 -0700
@@ -25,6 +25,7 @@
* @test
* @requires (vm.simpleArch == "x64" | vm.simpleArch == "sparcv9" | vm.simpleArch == "aarch64")
* @library ../../../../../
+ * @ignore 8161550
* @modules java.base/jdk.internal.reflect
* jdk.vm.ci/jdk.vm.ci.meta
* jdk.vm.ci/jdk.vm.ci.runtime
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/unsafe/OpaqueAccesses.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,134 @@
+/*
+ * 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 8155781
+ * @modules java.base/jdk.internal.misc
+ *
+ * @run main/bootclasspath/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions
+ * -XX:-TieredCompilation -Xbatch
+ * -XX:CompileCommand=dontinline,compiler.unsafe.OpaqueAccesses::test*
+ * compiler.unsafe.OpaqueAccesses
+ */
+package compiler.unsafe;
+
+import jdk.internal.misc.Unsafe;
+
+import java.lang.reflect.Field;
+
+public class OpaqueAccesses {
+ private static final Unsafe UNSAFE = Unsafe.getUnsafe();
+
+ private static final Object INSTANCE = new OpaqueAccesses();
+
+ private static final Object[] ARRAY = new Object[10];
+
+ private static final long F_OFFSET;
+ private static final long E_OFFSET;
+
+ static {
+ try {
+ Field field = OpaqueAccesses.class.getDeclaredField("f");
+ F_OFFSET = UNSAFE.objectFieldOffset(field);
+
+ E_OFFSET = UNSAFE.arrayBaseOffset(ARRAY.getClass());
+ } catch (NoSuchFieldException e) {
+ throw new Error(e);
+ }
+ }
+
+ private Object f = new Object();
+
+ static Object testFixedOffsetField(Object o) {
+ return UNSAFE.getObject(o, F_OFFSET);
+ }
+
+ static int testFixedOffsetHeader0(Object o) {
+ return UNSAFE.getInt(o, 0);
+ }
+
+ static int testFixedOffsetHeader4(Object o) {
+ return UNSAFE.getInt(o, 4);
+ }
+
+ static Object testFixedBase(long off) {
+ return UNSAFE.getObject(INSTANCE, off);
+ }
+
+ static Object testOpaque(Object o, long off) {
+ return UNSAFE.getObject(o, off);
+ }
+
+ static int testFixedOffsetHeaderArray0(Object[] arr) {
+ return UNSAFE.getInt(arr, 0);
+ }
+
+ static int testFixedOffsetHeaderArray4(Object[] arr) {
+ return UNSAFE.getInt(arr, 4);
+ }
+
+ static Object testFixedOffsetArray(Object[] arr) {
+ return UNSAFE.getObject(arr, E_OFFSET);
+ }
+
+ static Object testFixedBaseArray(long off) {
+ return UNSAFE.getObject(ARRAY, off);
+ }
+
+ static Object testOpaqueArray(Object[] o, long off) {
+ return UNSAFE.getObject(o, off);
+ }
+
+ static final long ADDR = UNSAFE.allocateMemory(10);
+ static boolean flag;
+
+ static int testMixedAccess() {
+ flag = !flag;
+ Object o = (flag ? INSTANCE : null);
+ long off = (flag ? F_OFFSET : ADDR);
+ return UNSAFE.getInt(o, off);
+ }
+
+ public static void main(String[] args) {
+ for (int i = 0; i < 20_000; i++) {
+ // Instance
+ testFixedOffsetField(INSTANCE);
+ testFixedOffsetHeader0(INSTANCE);
+ testFixedOffsetHeader4(INSTANCE);
+ testFixedBase(F_OFFSET);
+ testOpaque(INSTANCE, F_OFFSET);
+ testMixedAccess();
+
+ // Array
+ testFixedOffsetHeaderArray0(ARRAY);
+ testFixedOffsetHeaderArray4(ARRAY);
+ testFixedOffsetArray(ARRAY);
+ testFixedBaseArray(E_OFFSET);
+ testOpaqueArray(ARRAY, E_OFFSET);
+ }
+ System.out.println("TEST PASSED");
+ }
+}
--- a/hotspot/test/gc/TestSmallHeap.java Thu Jul 21 16:45:56 2016 +0000
+++ b/hotspot/test/gc/TestSmallHeap.java Thu Jul 21 20:09:19 2016 -0700
@@ -27,6 +27,7 @@
* @requires vm.gc=="null"
* @summary Verify that starting the VM with a small heap works
* @library /testlibrary /test/lib /test/lib/share/classes
+ * @ignore 8161552
* @modules java.base/jdk.internal.misc
* @modules java.management/sun.management
* @build TestSmallHeap
--- a/hotspot/test/gc/arguments/TestParallelHeapSizeFlags.java Thu Jul 21 16:45:56 2016 +0000
+++ b/hotspot/test/gc/arguments/TestParallelHeapSizeFlags.java Thu Jul 21 20:09:19 2016 -0700
@@ -29,6 +29,7 @@
* parallel collectors.
* @requires vm.gc=="null"
* @library /testlibrary /test/lib
+ * @ignore 8161552
* @modules java.base/jdk.internal.misc
* java.management
* @build TestParallelHeapSizeFlags TestMaxHeapSizeTools
--- a/jaxp/.hgtags Thu Jul 21 16:45:56 2016 +0000
+++ b/jaxp/.hgtags Thu Jul 21 20:09:19 2016 -0700
@@ -370,3 +370,4 @@
493eb91ec32a6dea7604cfbd86c10045ad9af15b jdk-9+125
15722f71281f034bc696d8b96136da2ef34da44f jdk-9+126
bdc3c0b737efbf899709eb3121ce760dcfb51151 jdk-9+127
+8a7681a9d70640ac7fbf05c28f53c1d51d8d00a1 jdk-9+128
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/XalanConstants.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/XalanConstants.java Thu Jul 21 20:09:19 2016 -0700
@@ -80,6 +80,14 @@
*/
public static final String JDK_GENERAL_ENTITY_SIZE_LIMIT =
ORACLE_JAXP_PROPERTY_PREFIX + "maxGeneralEntitySizeLimit";
+
+ /**
+ * JDK node count limit in entities that limits the total number of nodes
+ * in all of entity references.
+ */
+ public static final String JDK_ENTITY_REPLACEMENT_LIMIT =
+ ORACLE_JAXP_PROPERTY_PREFIX + "entityReplacementLimit";
+
/**
* JDK maximum parameter entity size limit
*/
@@ -136,6 +144,13 @@
* JDK maximum general entity size limit
*/
public static final String SP_GENERAL_ENTITY_SIZE_LIMIT = "jdk.xml.maxGeneralEntitySizeLimit";
+
+ /**
+ * JDK node count limit in entities that limits the total number of nodes
+ * in all of entity references.
+ */
+ public static final String SP_ENTITY_REPLACEMENT_LIMIT = "jdk.xml.entityReplacementLimit";
+
/**
* JDK maximum parameter entity size limit
*/
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/utils/XMLSecurityManager.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/utils/XMLSecurityManager.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 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
@@ -82,7 +82,9 @@
MAX_ELEMENT_DEPTH_LIMIT("MaxElementDepthLimit", XalanConstants.JDK_MAX_ELEMENT_DEPTH,
XalanConstants.SP_MAX_ELEMENT_DEPTH, 0, 0),
MAX_NAME_LIMIT("MaxXMLNameLimit", XalanConstants.JDK_XML_NAME_LIMIT,
- XalanConstants.SP_XML_NAME_LIMIT, 1000, 1000);
+ XalanConstants.SP_XML_NAME_LIMIT, 1000, 1000),
+ ENTITY_REPLACEMENT_LIMIT("EntityReplacementLimit", XalanConstants.JDK_ENTITY_REPLACEMENT_LIMIT,
+ XalanConstants.SP_ENTITY_REPLACEMENT_LIMIT, 0, 3000000);
final String key;
final String apiProperty;
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Constants.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/Constants.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,15 +1,15 @@
/*
- * reserved comment block
- * DO NOT REMOVE OR ALTER!
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
*/
/*
- * Copyright 2001-2004 The Apache Software Foundation.
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -98,6 +98,10 @@
public static final int ACC_STATIC
= com.sun.org.apache.bcel.internal.Constants.ACC_STATIC;
+ public static final String MODULE_SIG
+ = "Ljava/lang/reflect/Module;";
+ public static final String CLASS_SIG
+ = "Ljava/lang/Class;";
public static final String STRING_SIG
= "Ljava/lang/String;";
public static final String STRING_BUFFER_SIG
@@ -246,8 +250,12 @@
= "com.sun.org.apache.xalan.internal.xsltc.DOM";
public static final String DOM_IMPL
= "com.sun.org.apache.xalan.internal.xsltc.dom.SAXImpl";
- public static final String SAX_IMPL
+ public static final String SAX_IMPL
= "com.sun.org.apache.xalan.internal.xsltc.dom.SAXImpl";
+ public static final String CLASS_CLASS
+ = "java.lang.Class";
+ public static final String MODULE_CLASS
+ = "java.lang.reflect.Module";
public static final String STRING_CLASS
= "java.lang.String";
public static final String OBJECT_CLASS
@@ -293,7 +301,7 @@
= "()D";
public static final String DOM_PNAME
- = "dom";
+ = "dom";
public static final String NODE_PNAME
= "node";
public static final String TRANSLET_OUTPUT_PNAME
@@ -335,6 +343,19 @@
= "setStartNode";
public static final String RESET
= "reset";
+ public static final String GET_MODULE
+ = "getModule";
+ public static final String FOR_NAME
+ = "forName";
+ public static final String ADD_READS
+ = "addReads";
+
+ public static final String GET_MODULE_SIG
+ = "()" + MODULE_SIG;
+ public static final String FOR_NAME_SIG
+ = "(" + STRING_SIG + ")" + CLASS_SIG;
+ public static final String ADD_READS_SIG
+ = "(" + MODULE_SIG + ")" + MODULE_SIG;
public static final String ATTR_SET_SIG
= "(" + DOM_INTF_SIG + NODE_ITERATOR_SIG + TRANSLET_OUTPUT_SIG + "I)V";
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/FunctionCall.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/compiler/FunctionCall.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
@@ -17,9 +17,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-/*
- * $Id: FunctionCall.java,v 1.2.4.1 2005/09/12 10:31:32 pvedula Exp $
- */
package com.sun.org.apache.xalan.internal.xsltc.compiler;
@@ -32,6 +29,7 @@
import com.sun.org.apache.bcel.internal.generic.InstructionConstants;
import com.sun.org.apache.bcel.internal.generic.InstructionList;
import com.sun.org.apache.bcel.internal.generic.InvokeInstruction;
+import com.sun.org.apache.bcel.internal.generic.LDC;
import com.sun.org.apache.bcel.internal.generic.LocalVariableGen;
import com.sun.org.apache.bcel.internal.generic.NEW;
import com.sun.org.apache.bcel.internal.generic.PUSH;
@@ -792,6 +790,11 @@
final String clazz =
_chosenConstructor.getDeclaringClass().getName();
+
+ // Generate call to Module.addReads:
+ // <TransletClass>.class.getModule().addReads(
+ generateAddReads(classGen, methodGen, clazz);
+
Class[] paramTypes = _chosenConstructor.getParameterTypes();
LocalVariableGen[] paramTemp = new LocalVariableGen[n];
@@ -855,6 +858,12 @@
final String clazz = _chosenMethod.getDeclaringClass().getName();
Class[] paramTypes = _chosenMethod.getParameterTypes();
+
+ // Generate call to Module.addReads:
+ // <TransletClass>.class.getModule().addReads(
+ // Class.forName(<clazz>).getModule());
+ generateAddReads(classGen, methodGen, clazz);
+
// Push "this" if it is an instance method
if (_thisArgument != null) {
_thisArgument.translate(classGen, methodGen);
@@ -896,6 +905,41 @@
}
}
+ private void generateAddReads(ClassGenerator classGen, MethodGenerator methodGen,
+ String clazz) {
+ final ConstantPoolGen cpg = classGen.getConstantPool();
+ final InstructionList il = methodGen.getInstructionList();
+
+ // Generate call to Module.addReads:
+ // <TransletClass>.class.getModule().addReads(
+ // Class.forName(<clazz>).getModule());
+ // Class.forName may throw ClassNotFoundException.
+ // This is OK as it will caught higher up the stack in
+ // TransformerImpl.transform() and wrapped into a
+ // TransformerException.
+ methodGen.markChunkStart();
+
+ int index = cpg.addMethodref(CLASS_CLASS,
+ GET_MODULE,
+ GET_MODULE_SIG);
+ int index2 = cpg.addMethodref(CLASS_CLASS,
+ FOR_NAME,
+ FOR_NAME_SIG);
+ il.append(new LDC(cpg.addString(classGen.getClassName())));
+ il.append(new INVOKESTATIC(index2));
+ il.append(new INVOKEVIRTUAL(index));
+ il.append(new LDC(cpg.addString(clazz)));
+ il.append(new INVOKESTATIC(index2));
+ il.append(new INVOKEVIRTUAL(index));
+ index = cpg.addMethodref(MODULE_CLASS,
+ ADD_READS,
+ ADD_READS_SIG);
+ il.append(new INVOKEVIRTUAL(index));
+ il.append(InstructionConstants.POP);
+
+ methodGen.markChunkEnd();
+ }
+
@Override
public String toString() {
return "funcall(" + _fname + ", " + _arguments + ')';
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TemplatesImpl.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xalan/internal/xsltc/trax/TemplatesImpl.java Thu Jul 21 20:09:19 2016 -0700
@@ -58,7 +58,6 @@
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.URIResolver;
-import jdk.internal.module.Modules;
/**
* @author Morten Jorgensen
@@ -486,10 +485,6 @@
thisModule.addExports(p, m);
});
- // For now, the module reads all unnnamed modules. This will be changed once
- // the XSLT compiler is updated to generate code to invoke addReads.
- Modules.addReadsAllUnnamed(m);
-
// java.xml needs to instanitate the translet class
thisModule.addReads(m);
@@ -513,7 +508,7 @@
}
catch (ClassFormatError e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_CLASS_ERR, _name);
- throw new TransformerConfigurationException(err.toString());
+ throw new TransformerConfigurationException(err.toString(), e);
}
catch (LinkageError e) {
ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/Constants.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/Constants.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved.
*/
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
@@ -239,6 +239,14 @@
*/
public static final String JDK_GENERAL_ENTITY_SIZE_LIMIT =
ORACLE_JAXP_PROPERTY_PREFIX + "maxGeneralEntitySizeLimit";
+
+ /**
+ * JDK node count limit in entities that limits the total number of nodes
+ * in all of entity references.
+ */
+ public static final String JDK_ENTITY_REPLACEMENT_LIMIT =
+ ORACLE_JAXP_PROPERTY_PREFIX + "entityReplacementLimit";
+
/**
* JDK maximum parameter entity size limit
*/
@@ -292,6 +300,13 @@
* JDK maximum general entity size limit
*/
public static final String SP_GENERAL_ENTITY_SIZE_LIMIT = "jdk.xml.maxGeneralEntitySizeLimit";
+
+ /**
+ * JDK node count limit in entities that limits the total number of nodes
+ * in all of entity references.
+ */
+ public static final String SP_ENTITY_REPLACEMENT_LIMIT = "jdk.xml.entityReplacementLimit";
+
/**
* JDK maximum parameter entity size limit
*/
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XML11DTDScannerImpl.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XML11DTDScannerImpl.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,62 +1,21 @@
/*
- * reserved comment block
- * DO NOT REMOVE OR ALTER!
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
*/
/*
- * The Apache Software License, Version 1.1
- *
- *
- * Copyright (c) 1999-2004 The Apache Software Foundation.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * 3. The end-user documentation included with the redistribution,
- * if any, must include the following acknowledgment:
- * "This product includes software developed by the
- * Apache Software Foundation (http://www.apache.org/)."
- * Alternately, this acknowledgment may appear in the software itself,
- * if and wherever such third-party acknowledgments normally appear.
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
*
- * 4. The names "Xerces" and "Apache Software Foundation" must
- * not be used to endorse or promote products derived from this
- * software without prior written permission. For written
- * permission, please contact apache@apache.org.
- *
- * 5. Products derived from this software may not be called "Apache",
- * nor may "Apache" appear in their name, without prior written
- * permission of the Apache Software Foundation.
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation and was
- * originally based on software copyright (c) 1999, International
- * Business Machines, Inc., http://www.apache.org. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package com.sun.org.apache.xerces.internal.impl;
@@ -146,7 +105,7 @@
protected boolean scanPubidLiteral(XMLString literal)
throws IOException, XNIException
{
- int quote = fEntityScanner.scanChar();
+ int quote = fEntityScanner.scanChar(null);
if (quote != '\'' && quote != '"') {
reportFatalError("QuoteRequiredInPublicID", null);
return false;
@@ -157,7 +116,7 @@
boolean skipSpace = true;
boolean dataok = true;
while (true) {
- int c = fEntityScanner.scanChar();
+ int c = fEntityScanner.scanChar(null);
// REVISIT: it could really only be \n or 0x20; all else is normalized, no? - neilg
if (c == ' ' || c == '\n' || c == '\r' || c == 0x85 || c == 0x2028) {
if (!skipSpace) {
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XML11DocumentScannerImpl.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XML11DocumentScannerImpl.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,62 +1,21 @@
/*
- * reserved comment block
- * DO NOT REMOVE OR ALTER!
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
*/
/*
- * The Apache Software License, Version 1.1
- *
- *
- * Copyright (c) 1999-2004 The Apache Software Foundation.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * 3. The end-user documentation included with the redistribution,
- * if any, must include the following acknowledgment:
- * "This product includes software developed by the
- * Apache Software Foundation (http://www.apache.org/)."
- * Alternately, this acknowledgment may appear in the software itself,
- * if and wherever such third-party acknowledgments normally appear.
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
*
- * 4. The names "Xerces" and "Apache Software Foundation" must
- * not be used to endorse or promote products derived from this
- * software without prior written permission. For written
- * permission, please contact apache@apache.org.
- *
- * 5. Products derived from this software may not be called "Apache",
- * nor may "Apache" appear in their name, without prior written
- * permission of the Apache Software Foundation.
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation and was
- * originally based on software copyright (c) 1999, International
- * Business Machines, Inc., http://www.apache.org. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package com.sun.org.apache.xerces.internal.impl;
@@ -134,7 +93,7 @@
// happens when there is the character reference
// but scanContent doesn't do entity expansions...
// is this *really* necessary??? - NG
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
content.append((char)c);
c = -1;
}
@@ -143,7 +102,7 @@
} */
if (c == ']') {
- content.append((char)fEntityScanner.scanChar());
+ content.append((char)fEntityScanner.scanChar(null));
// remember where we are in case we get an endEntity before we
// could flush the buffer out - this happens when we're parsing an
// entity which ends with a ]
@@ -152,12 +111,12 @@
// We work on a single character basis to handle cases such as:
// ']]]>' which we might otherwise miss.
//
- if (fEntityScanner.skipChar(']')) {
+ if (fEntityScanner.skipChar(']', null)) {
content.append(']');
- while (fEntityScanner.skipChar(']')) {
+ while (fEntityScanner.skipChar(']', null)) {
content.append(']');
}
- if (fEntityScanner.skipChar('>')) {
+ if (fEntityScanner.skipChar('>', null)) {
reportFatalError("CDEndInContent", null);
}
}
@@ -184,6 +143,7 @@
* @param checkEntities true if undeclared entities should be reported as VC violation,
* false if undeclared entities should be reported as WFC violation.
* @param eleName The name of element to which this attribute belongs.
+ * @param isNSURI The flag indicating whether the content is a namespace URI
*
* @return true if the non-normalized and normalized value are the same
*
@@ -193,7 +153,7 @@
protected boolean scanAttributeValue(XMLString value,
XMLString nonNormalizedValue,
String atName,
- boolean checkEntities,String eleName)
+ boolean checkEntities,String eleName, boolean isNSURI)
throws IOException, XNIException
{
// quote
@@ -202,10 +162,10 @@
reportFatalError("OpenQuoteExpected", new Object[]{eleName,atName});
}
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(NameType.ATTRIBUTE);
int entityDepth = fEntityDepth;
- int c = fEntityScanner.scanLiteral(quote, value);
+ int c = fEntityScanner.scanLiteral(quote, value, isNSURI);
if (DEBUG_ATTR_NORMALIZATION) {
System.out.println("** scanLiteral -> \""
+ value.toString() + "\"");
@@ -215,7 +175,7 @@
if (c == quote && (fromIndex = isUnchangedByNormalization(value)) == -1) {
/** Both the non-normalized and normalized attribute values are equal. **/
nonNormalizedValue.setValues(value);
- int cquote = fEntityScanner.scanChar();
+ int cquote = fEntityScanner.scanChar(NameType.ATTRIBUTE);
if (cquote != quote) {
reportFatalError("CloseQuoteExpected", new Object[]{eleName,atName});
}
@@ -238,11 +198,11 @@
+ fStringBuffer.toString() + "\"");
}
if (c == '&') {
- fEntityScanner.skipChar('&');
+ fEntityScanner.skipChar('&', NameType.REFERENCE);
if (entityDepth == fEntityDepth) {
fStringBuffer2.append('&');
}
- if (fEntityScanner.skipChar('#')) {
+ if (fEntityScanner.skipChar('#', NameType.REFERENCE)) {
if (entityDepth == fEntityDepth) {
fStringBuffer2.append('#');
}
@@ -256,59 +216,22 @@
}
}
else {
- String entityName = fEntityScanner.scanName();
+ String entityName = fEntityScanner.scanName(NameType.REFERENCE);
if (entityName == null) {
reportFatalError("NameRequiredInReference", null);
}
else if (entityDepth == fEntityDepth) {
fStringBuffer2.append(entityName);
}
- if (!fEntityScanner.skipChar(';')) {
+ if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
reportFatalError("SemicolonRequiredInReference",
new Object []{entityName});
}
else if (entityDepth == fEntityDepth) {
fStringBuffer2.append(';');
}
- if (entityName == fAmpSymbol) {
- fStringBuffer.append('&');
- if (DEBUG_ATTR_NORMALIZATION) {
- System.out.println("** value5: \""
- + fStringBuffer.toString()
- + "\"");
- }
- }
- else if (entityName == fAposSymbol) {
- fStringBuffer.append('\'');
- if (DEBUG_ATTR_NORMALIZATION) {
- System.out.println("** value7: \""
- + fStringBuffer.toString()
- + "\"");
- }
- }
- else if (entityName == fLtSymbol) {
- fStringBuffer.append('<');
- if (DEBUG_ATTR_NORMALIZATION) {
- System.out.println("** value9: \""
- + fStringBuffer.toString()
- + "\"");
- }
- }
- else if (entityName == fGtSymbol) {
- fStringBuffer.append('>');
- if (DEBUG_ATTR_NORMALIZATION) {
- System.out.println("** valueB: \""
- + fStringBuffer.toString()
- + "\"");
- }
- }
- else if (entityName == fQuotSymbol) {
- fStringBuffer.append('"');
- if (DEBUG_ATTR_NORMALIZATION) {
- System.out.println("** valueD: \""
- + fStringBuffer.toString()
- + "\"");
- }
+ if (resolveCharacter(entityName, fStringBuffer)) {
+ checkEntityLimit(false, fEntityScanner.fCurrentEntity.name, 1);
}
else {
if (fEntityManager.isExternalEntity(entityName)) {
@@ -339,13 +262,13 @@
else if (c == '<') {
reportFatalError("LessthanInAttValue",
new Object[] { eleName, atName });
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
if (entityDepth == fEntityDepth) {
fStringBuffer2.append((char)c);
}
}
else if (c == '%' || c == ']') {
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
fStringBuffer.append((char)c);
if (entityDepth == fEntityDepth) {
fStringBuffer2.append((char)c);
@@ -359,7 +282,7 @@
// XML11EntityScanner. Not sure why
// this check was originally necessary. - NG
else if (c == '\n' || c == '\r' || c == 0x85 || c == 0x2028) {
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
fStringBuffer.append(' ');
if (entityDepth == fEntityDepth) {
fStringBuffer2.append('\n');
@@ -382,12 +305,12 @@
else if (c != -1 && isInvalidLiteral(c)) {
reportFatalError("InvalidCharInAttValue",
new Object[] {eleName, atName, Integer.toString(c, 16)});
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
if (entityDepth == fEntityDepth) {
fStringBuffer2.append((char)c);
}
}
- c = fEntityScanner.scanLiteral(quote, value);
+ c = fEntityScanner.scanLiteral(quote, value, isNSURI);
if (entityDepth == fEntityDepth) {
fStringBuffer2.append(value);
}
@@ -404,7 +327,7 @@
nonNormalizedValue.setValues(fStringBuffer2);
// quote
- int cquote = fEntityScanner.scanChar();
+ int cquote = fEntityScanner.scanChar(null);
if (cquote != quote) {
reportFatalError("CloseQuoteExpected", new Object[]{eleName,atName});
}
@@ -439,7 +362,7 @@
protected boolean scanPubidLiteral(XMLString literal)
throws IOException, XNIException
{
- int quote = fEntityScanner.scanChar();
+ int quote = fEntityScanner.scanChar(null);
if (quote != '\'' && quote != '"') {
reportFatalError("QuoteRequiredInPublicID", null);
return false;
@@ -450,7 +373,7 @@
boolean skipSpace = true;
boolean dataok = true;
while (true) {
- int c = fEntityScanner.scanChar();
+ int c = fEntityScanner.scanChar(null);
// REVISIT: none of these except \n and 0x20 should make it past the entity scanner
if (c == ' ' || c == '\n' || c == '\r' || c == 0x85 || c == 0x2028) {
if (!skipSpace) {
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XML11EntityScanner.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XML11EntityScanner.java Thu Jul 21 20:09:19 2016 -0700
@@ -21,6 +21,7 @@
package com.sun.org.apache.xerces.internal.impl;
+import com.sun.org.apache.xerces.internal.impl.XMLScanner.NameType;
import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
import com.sun.org.apache.xerces.internal.util.XML11Char;
import com.sun.org.apache.xerces.internal.util.XMLChar;
@@ -92,7 +93,7 @@
* @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file.
*/
- public int scanChar() throws IOException {
+ protected int scanChar(NameType nt) throws IOException {
// load more characters, if needed
if (fCurrentEntity.position == fCurrentEntity.count) {
@@ -100,6 +101,7 @@
}
// scan character
+ int offset = fCurrentEntity.position;
int c = fCurrentEntity.ch[fCurrentEntity.position++];
boolean external = false;
if (c == '\n' ||
@@ -110,6 +112,7 @@
invokeListeners(1);
fCurrentEntity.ch[0] = (char)c;
load(1, false, false);
+ offset = 0;
}
if (c == '\r' && external) {
int cc = fCurrentEntity.ch[fCurrentEntity.position++];
@@ -122,6 +125,9 @@
// return character that was scanned
fCurrentEntity.columnNumber++;
+ if (!detectingVersion) {
+ checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
+ }
return c;
} // scanChar():int
@@ -141,7 +147,7 @@
* @see com.sun.org.apache.xerces.internal.util.SymbolTable
* @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11Name
*/
- public String scanNmtoken() throws IOException {
+ protected String scanNmtoken() throws IOException {
// load more characters, if needed
if (fCurrentEntity.position == fCurrentEntity.count) {
load(0, true, true);
@@ -248,6 +254,8 @@
* <strong>Note:</strong> The string returned must be a symbol. The
* SymbolTable can be used for this purpose.
*
+ * @param nt The type of the name (element or attribute)
+ *
* @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file.
*
@@ -255,7 +263,7 @@
* @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11Name
* @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11NameStart
*/
- public String scanName() throws IOException {
+ protected String scanName(NameType nt) throws IOException {
// load more characters, if needed
if (fCurrentEntity.position == fCurrentEntity.count) {
load(0, true, true);
@@ -310,23 +318,11 @@
return null;
}
+ int length = 0;
do {
ch = fCurrentEntity.ch[fCurrentEntity.position];
if (XML11Char.isXML11Name(ch)) {
- if (++fCurrentEntity.position == fCurrentEntity.count) {
- int length = fCurrentEntity.position - offset;
- invokeListeners(length);
- if (length == fCurrentEntity.ch.length) {
- // bad luck we have to resize our buffer
- char[] tmp = new char[fCurrentEntity.ch.length << 1];
- System.arraycopy(fCurrentEntity.ch, offset,
- tmp, 0, length);
- fCurrentEntity.ch = tmp;
- }
- else {
- System.arraycopy(fCurrentEntity.ch, offset,
- fCurrentEntity.ch, 0, length);
- }
+ if ((length = checkBeforeLoad(fCurrentEntity, offset, offset)) > 0) {
offset = 0;
if (load(length, false, false)) {
break;
@@ -334,20 +330,7 @@
}
}
else if (XML11Char.isXML11NameHighSurrogate(ch)) {
- if (++fCurrentEntity.position == fCurrentEntity.count) {
- int length = fCurrentEntity.position - offset;
- invokeListeners(length);
- if (length == fCurrentEntity.ch.length) {
- // bad luck we have to resize our buffer
- char[] tmp = new char[fCurrentEntity.ch.length << 1];
- System.arraycopy(fCurrentEntity.ch, offset,
- tmp, 0, length);
- fCurrentEntity.ch = tmp;
- }
- else {
- System.arraycopy(fCurrentEntity.ch, offset,
- fCurrentEntity.ch, 0, length);
- }
+ if ((length = checkBeforeLoad(fCurrentEntity, offset, offset)) > 0) {
offset = 0;
if (load(length, false, false)) {
--fCurrentEntity.position;
@@ -361,20 +344,7 @@
--fCurrentEntity.position;
break;
}
- if (++fCurrentEntity.position == fCurrentEntity.count) {
- int length = fCurrentEntity.position - offset;
- invokeListeners(length);
- if (length == fCurrentEntity.ch.length) {
- // bad luck we have to resize our buffer
- char[] tmp = new char[fCurrentEntity.ch.length << 1];
- System.arraycopy(fCurrentEntity.ch, offset,
- tmp, 0, length);
- fCurrentEntity.ch = tmp;
- }
- else {
- System.arraycopy(fCurrentEntity.ch, offset,
- fCurrentEntity.ch, 0, length);
- }
+ if ((length = checkBeforeLoad(fCurrentEntity, offset, offset)) > 0) {
offset = 0;
if (load(length, false, false)) {
break;
@@ -387,12 +357,14 @@
}
while (true);
- int length = fCurrentEntity.position - offset;
+ length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length;
// return name
String symbol = null;
if (length > 0) {
+ checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length);
+ checkEntityLimit(nt, fCurrentEntity, offset, length);
symbol = fSymbolTable.addSymbol(fCurrentEntity.ch, offset, length);
}
return symbol;
@@ -415,7 +387,7 @@
* @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11NCName
* @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11NCNameStart
*/
- public String scanNCName() throws IOException {
+ protected String scanNCName() throws IOException {
// load more characters, if needed
if (fCurrentEntity.position == fCurrentEntity.count) {
@@ -571,6 +543,7 @@
* this purpose.
*
* @param qname The qualified name structure to fill.
+ * @param nt The type of the name (element or attribute)
*
* @return Returns true if a qualified name appeared immediately on
* the input and was scanned, false otherwise.
@@ -582,7 +555,7 @@
* @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11Name
* @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11NameStart
*/
- public boolean scanQName(QName qname) throws IOException {
+ protected boolean scanQName(QName qname, XMLScanner.NameType nt) throws IOException {
// load more characters, if needed
if (fCurrentEntity.position == fCurrentEntity.count) {
@@ -602,6 +575,7 @@
fCurrentEntity.columnNumber++;
String name = fSymbolTable.addSymbol(fCurrentEntity.ch, 0, 1);
qname.setValues(null, name, name, null);
+ checkEntityLimit(nt, fCurrentEntity, 0, 1);
return true;
}
}
@@ -632,6 +606,7 @@
fCurrentEntity.columnNumber += 2;
String name = fSymbolTable.addSymbol(fCurrentEntity.ch, 0, 2);
qname.setValues(null, name, name, null);
+ checkEntityLimit(nt, fCurrentEntity, 0, 2);
return true;
}
}
@@ -641,6 +616,7 @@
}
int index = -1;
+ int length = 0;
boolean sawIncompleteSurrogatePair = false;
do {
ch = fCurrentEntity.ch[fCurrentEntity.position];
@@ -653,22 +629,7 @@
//check prefix before further read
checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, index - offset);
}
- if (++fCurrentEntity.position == fCurrentEntity.count) {
- int length = fCurrentEntity.position - offset;
- //check localpart before loading more data
- checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length - index - 1);
- invokeListeners(length);
- if (length == fCurrentEntity.ch.length) {
- // bad luck we have to resize our buffer
- char[] tmp = new char[fCurrentEntity.ch.length << 1];
- System.arraycopy(fCurrentEntity.ch, offset,
- tmp, 0, length);
- fCurrentEntity.ch = tmp;
- }
- else {
- System.arraycopy(fCurrentEntity.ch, offset,
- fCurrentEntity.ch, 0, length);
- }
+ if ((length = checkBeforeLoad(fCurrentEntity, offset, index)) > 0) {
if (index != -1) {
index = index - offset;
}
@@ -679,20 +640,7 @@
}
}
else if (XML11Char.isXML11NameHighSurrogate(ch)) {
- if (++fCurrentEntity.position == fCurrentEntity.count) {
- int length = fCurrentEntity.position - offset;
- invokeListeners(length);
- if (length == fCurrentEntity.ch.length) {
- // bad luck we have to resize our buffer
- char[] tmp = new char[fCurrentEntity.ch.length << 1];
- System.arraycopy(fCurrentEntity.ch, offset,
- tmp, 0, length);
- fCurrentEntity.ch = tmp;
- }
- else {
- System.arraycopy(fCurrentEntity.ch, offset,
- fCurrentEntity.ch, 0, length);
- }
+ if ((length = checkBeforeLoad(fCurrentEntity, offset, index)) > 0) {
if (index != -1) {
index = index - offset;
}
@@ -711,20 +659,7 @@
--fCurrentEntity.position;
break;
}
- if (++fCurrentEntity.position == fCurrentEntity.count) {
- int length = fCurrentEntity.position - offset;
- invokeListeners(length);
- if (length == fCurrentEntity.ch.length) {
- // bad luck we have to resize our buffer
- char[] tmp = new char[fCurrentEntity.ch.length << 1];
- System.arraycopy(fCurrentEntity.ch, offset,
- tmp, 0, length);
- fCurrentEntity.ch = tmp;
- }
- else {
- System.arraycopy(fCurrentEntity.ch, offset,
- fCurrentEntity.ch, 0, length);
- }
+ if ((length = checkBeforeLoad(fCurrentEntity, offset, index)) > 0) {
if (index != -1) {
index = index - offset;
}
@@ -740,7 +675,7 @@
}
while (true);
- int length = fCurrentEntity.position - offset;
+ length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length;
if (length > 0) {
@@ -776,6 +711,7 @@
checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length);
}
qname.setValues(prefix, localpart, rawname, null);
+ checkEntityLimit(nt, fCurrentEntity, offset, length);
return true;
}
return false;
@@ -808,7 +744,7 @@
* @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file.
*/
- public int scanContent(XMLString content) throws IOException {
+ protected int scanContent(XMLString content) throws IOException {
// load more characters, if needed
if (fCurrentEntity.position == fCurrentEntity.count) {
@@ -826,6 +762,7 @@
int offset = fCurrentEntity.position;
int c = fCurrentEntity.ch[offset];
int newlines = 0;
+ boolean counted = false;
boolean external = fCurrentEntity.isExternal();
if (c == '\n' || ((c == '\r' || c == 0x85 || c == 0x2028) && external)) {
do {
@@ -835,11 +772,13 @@
fCurrentEntity.lineNumber++;
fCurrentEntity.columnNumber = 1;
if (fCurrentEntity.position == fCurrentEntity.count) {
+ checkEntityLimit(null, fCurrentEntity, offset, newlines);
offset = 0;
fCurrentEntity.baseCharOffset += (fCurrentEntity.position - fCurrentEntity.startPosition);
fCurrentEntity.position = newlines;
fCurrentEntity.startPosition = newlines;
if (load(newlines, false, true)) {
+ counted = true;
break;
}
}
@@ -858,11 +797,13 @@
fCurrentEntity.lineNumber++;
fCurrentEntity.columnNumber = 1;
if (fCurrentEntity.position == fCurrentEntity.count) {
+ checkEntityLimit(null, fCurrentEntity, offset, newlines);
offset = 0;
fCurrentEntity.baseCharOffset += (fCurrentEntity.position - fCurrentEntity.startPosition);
fCurrentEntity.position = newlines;
fCurrentEntity.startPosition = newlines;
if (load(newlines, false, true)) {
+ counted = true;
break;
}
}
@@ -877,6 +818,7 @@
}
int length = fCurrentEntity.position - offset;
if (fCurrentEntity.position == fCurrentEntity.count - 1) {
+ checkEntityLimit(null, fCurrentEntity, offset, length);
content.setValues(fCurrentEntity.ch, offset, length);
return -1;
}
@@ -904,8 +846,8 @@
}
int length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length - newlines;
- if (fCurrentEntity.isGE) {
- checkLimit(Limit.TOTAL_ENTITY_SIZE_LIMIT, fCurrentEntity, offset, length);
+ if (!counted) {
+ checkEntityLimit(null, fCurrentEntity, offset, length);
}
content.setValues(fCurrentEntity.ch, offset, length);
@@ -945,6 +887,7 @@
* @param quote The quote character that signifies the end of the
* attribute value data.
* @param content The content structure to fill.
+ * @param isNSURI a flag indicating whether the content is a Namespace URI
*
* @return Returns the next character on the input, if known. This
* value may be -1 but this does <em>note</em> designate
@@ -953,7 +896,7 @@
* @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file.
*/
- public int scanLiteral(int quote, XMLString content)
+ protected int scanLiteral(int quote, XMLString content, boolean isNSURI)
throws IOException {
// load more characters, if needed
if (fCurrentEntity.position == fCurrentEntity.count) {
@@ -1051,8 +994,10 @@
}
int length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length - newlines;
- if (fCurrentEntity.isGE) {
- checkLimit(Limit.TOTAL_ENTITY_SIZE_LIMIT, fCurrentEntity, offset, length);
+
+ checkEntityLimit(null, fCurrentEntity, offset, length);
+ if (isNSURI) {
+ checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length);
}
content.setValues(fCurrentEntity.ch, offset, length);
@@ -1103,7 +1048,7 @@
* @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file.
*/
- public boolean scanData(String delimiter, XMLStringBuffer buffer)
+ protected boolean scanData(String delimiter, XMLStringBuffer buffer)
throws IOException {
boolean done = false;
@@ -1135,6 +1080,7 @@
if (fCurrentEntity.position >= fCurrentEntity.count - delimLen) {
// something must be wrong with the input: e.g., file ends an unterminated comment
int length = fCurrentEntity.count - fCurrentEntity.position;
+ checkEntityLimit(NameType.COMMENT, fCurrentEntity, fCurrentEntity.position, length);
buffer.append (fCurrentEntity.ch, fCurrentEntity.position, length);
fCurrentEntity.columnNumber += fCurrentEntity.count;
fCurrentEntity.baseCharOffset += (fCurrentEntity.position - fCurrentEntity.startPosition);
@@ -1199,6 +1145,7 @@
}
int length = fCurrentEntity.position - offset;
if (fCurrentEntity.position == fCurrentEntity.count - 1) {
+ checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length);
buffer.append(fCurrentEntity.ch, offset, length);
return true;
}
@@ -1237,6 +1184,7 @@
fCurrentEntity.position--;
int length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length - newlines;
+ checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length);
buffer.append(fCurrentEntity.ch, offset, length);
return true;
}
@@ -1274,6 +1222,7 @@
fCurrentEntity.position--;
int length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length - newlines;
+ checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length);
buffer.append(fCurrentEntity.ch, offset, length);
return true;
}
@@ -1281,6 +1230,7 @@
}
int length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length - newlines;
+ checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length);
if (done) {
length -= delimLen;
}
@@ -1305,7 +1255,7 @@
* @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file.
*/
- public boolean skipChar(int c) throws IOException {
+ protected boolean skipChar(int c, NameType nt) throws IOException {
// load more characters, if needed
if (fCurrentEntity.position == fCurrentEntity.count) {
@@ -1313,6 +1263,7 @@
}
// skip character
+ int offset = fCurrentEntity.position;
int cc = fCurrentEntity.ch[fCurrentEntity.position];
if (cc == c) {
fCurrentEntity.position++;
@@ -1323,12 +1274,14 @@
else {
fCurrentEntity.columnNumber++;
}
+ checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
return true;
}
else if (c == '\n' && ((cc == 0x2028 || cc == 0x85) && fCurrentEntity.isExternal())) {
fCurrentEntity.position++;
fCurrentEntity.lineNumber++;
fCurrentEntity.columnNumber = 1;
+ checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
return true;
}
else if (c == '\n' && (cc == '\r' ) && fCurrentEntity.isExternal()) {
@@ -1344,6 +1297,7 @@
}
fCurrentEntity.lineNumber++;
fCurrentEntity.columnNumber = 1;
+ checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
return true;
}
@@ -1366,7 +1320,7 @@
* @see com.sun.org.apache.xerces.internal.util.XMLChar#isSpace
* @see com.sun.org.apache.xerces.internal.util.XML11Char#isXML11Space
*/
- public boolean skipSpaces() throws IOException {
+ protected boolean skipSpaces() throws IOException {
// load more characters, if needed
if (fCurrentEntity.position == fCurrentEntity.count) {
@@ -1386,7 +1340,7 @@
// skip spaces
int c = fCurrentEntity.ch[fCurrentEntity.position];
-
+ int offset = fCurrentEntity.position - 1;
// External -- Match: S + 0x85 + 0x2028, and perform end of line normalization
if (fCurrentEntity.isExternal()) {
if (XML11Char.isXML11Space(c)) {
@@ -1422,6 +1376,11 @@
else {
fCurrentEntity.columnNumber++;
}
+
+ //If this is a general entity, spaces within a start element should be counted
+ checkEntityLimit(null, fCurrentEntity, offset, fCurrentEntity.position - offset);
+ offset = fCurrentEntity.position;
+
// load more characters, if needed
if (!entityChanged)
fCurrentEntity.position++;
@@ -1462,6 +1421,11 @@
else {
fCurrentEntity.columnNumber++;
}
+
+ //If this is a general entity, spaces within a start element should be counted
+ checkEntityLimit(null, fCurrentEntity, offset, fCurrentEntity.position - offset);
+ offset = fCurrentEntity.position;
+
// load more characters, if needed
if (!entityChanged)
fCurrentEntity.position++;
@@ -1495,7 +1459,7 @@
* @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file.
*/
- public boolean skipString(String s) throws IOException {
+ protected boolean skipString(String s) throws IOException {
// load more characters, if needed
if (fCurrentEntity.position == fCurrentEntity.count) {
@@ -1504,6 +1468,7 @@
// skip string
final int length = s.length();
+ final int beforeSkip = fCurrentEntity.position ;
for (int i = 0; i < length; i++) {
char c = fCurrentEntity.ch[fCurrentEntity.position++];
if (c != s.charAt(i)) {
@@ -1523,6 +1488,9 @@
}
}
fCurrentEntity.columnNumber += length;
+ if (!detectingVersion) {
+ checkEntityLimit(null, fCurrentEntity, beforeSkip, length);
+ }
return true;
} // skipString(String):boolean
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XML11NSDocumentScannerImpl.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XML11NSDocumentScannerImpl.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
*/
/*
@@ -135,7 +135,7 @@
if (DEBUG_START_END_ELEMENT)
System.out.println(">>> scanStartElementNS()");
// Note: namespace processing is on by default
- fEntityScanner.scanQName(fElementQName);
+ fEntityScanner.scanQName(fElementQName, NameType.ATTRIBUTE);
// REVISIT - [Q] Why do we need this local variable? -- mrglavas
String rawname = fElementQName.rawname;
if (fBindNamespaces) {
@@ -173,11 +173,11 @@
// end tag?
int c = fEntityScanner.peekChar();
if (c == '>') {
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
break;
} else if (c == '/') {
- fEntityScanner.scanChar();
- if (!fEntityScanner.skipChar('>')) {
+ fEntityScanner.scanChar(null);
+ if (!fEntityScanner.skipChar('>', null)) {
reportFatalError(
"ElementUnterminated",
new Object[] { rawname });
@@ -345,7 +345,7 @@
protected void scanStartElementName ()
throws IOException, XNIException {
// Note: namespace processing is on by default
- fEntityScanner.scanQName(fElementQName);
+ fEntityScanner.scanQName(fElementQName, NameType.ATTRIBUTE);
// Must skip spaces here because the DTD scanner
// would consume them at the end of the external subset.
fSawSpace = fEntityScanner.skipSpaces();
@@ -395,11 +395,11 @@
// end tag?
int c = fEntityScanner.peekChar();
if (c == '>') {
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
break;
} else if (c == '/') {
- fEntityScanner.scanChar();
- if (!fEntityScanner.skipChar('>')) {
+ fEntityScanner.scanChar(null);
+ if (!fEntityScanner.skipChar('>', null)) {
reportFatalError(
"ElementUnterminated",
new Object[] { rawname });
@@ -571,11 +571,11 @@
System.out.println(">>> scanAttribute()");
// name
- fEntityScanner.scanQName(fAttributeQName);
+ fEntityScanner.scanQName(fAttributeQName, NameType.ATTRIBUTE);
// equals
fEntityScanner.skipSpaces();
- if (!fEntityScanner.skipChar('=')) {
+ if (!fEntityScanner.skipChar('=', NameType.ATTRIBUTE)) {
reportFatalError(
"EqRequiredInAttribute",
new Object[] {
@@ -614,13 +614,20 @@
//REVISIT: one more case needs to be included: external PE and standalone is no
boolean isVC = fHasExternalDTD && !fStandalone;
- // REVISIT: it seems that this function should not take attributes, and length
- scanAttributeValue(
- this.fTempString,
- fTempString2,
- fAttributeQName.rawname,
- isVC,
- fCurrentElement.rawname);
+ /**
+ * Determine whether this is a namespace declaration that will be subject
+ * to the name limit check in the scanAttributeValue operation.
+ * Namespace declaration format: xmlns="..." or xmlns:prefix="..."
+ * Note that prefix:xmlns="..." isn't a namespace.
+ */
+ String localpart = fAttributeQName.localpart;
+ String prefix = fAttributeQName.prefix != null
+ ? fAttributeQName.prefix : XMLSymbols.EMPTY_STRING;
+ boolean isNSDecl = fBindNamespaces & (prefix == XMLSymbols.PREFIX_XMLNS ||
+ prefix == XMLSymbols.EMPTY_STRING && localpart == XMLSymbols.PREFIX_XMLNS);
+
+ scanAttributeValue(this.fTempString, fTempString2, fAttributeQName.rawname,
+ isVC, fCurrentElement.rawname, isNSDecl);
String value = fTempString.toString();
attributes.setValue(attrIndex, value);
attributes.setNonNormalizedValue(attrIndex, fTempString2.toString());
@@ -628,17 +635,7 @@
// record namespace declarations if any.
if (fBindNamespaces) {
-
- String localpart = fAttributeQName.localpart;
- String prefix =
- fAttributeQName.prefix != null
- ? fAttributeQName.prefix
- : XMLSymbols.EMPTY_STRING;
- // when it's of form xmlns="..." or xmlns:prefix="...",
- // it's a namespace declaration. but prefix:xmlns="..." isn't.
- if (prefix == XMLSymbols.PREFIX_XMLNS
- || prefix == XMLSymbols.EMPTY_STRING
- && localpart == XMLSymbols.PREFIX_XMLNS) {
+ if (isNSDecl) {
if (value.length() > fXMLNameLimit) {
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
"MaxXMLNameLimit",
@@ -758,7 +755,7 @@
// end
fEntityScanner.skipSpaces();
- if (!fEntityScanner.skipChar('>')) {
+ if (!fEntityScanner.skipChar('>', NameType.ELEMENTEND)) {
reportFatalError(
"ETagUnterminated",
new Object[] { endElementName.rawname });
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDTDScannerImpl.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDTDScannerImpl.java Thu Jul 21 20:09:19 2016 -0700
@@ -21,10 +21,7 @@
package com.sun.org.apache.xerces.internal.impl;
-import com.sun.org.apache.xerces.internal.impl.Constants;
import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
-import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
-import com.sun.org.apache.xerces.internal.impl.XMLEntityHandler;
import com.sun.org.apache.xerces.internal.util.SymbolTable;
import com.sun.org.apache.xerces.internal.util.XMLAttributesImpl;
import com.sun.org.apache.xerces.internal.util.XMLChar;
@@ -367,6 +364,7 @@
// we're done, set starting state for external subset
setScannerState(SCANNER_STATE_TEXT_DECL);
// we're done scanning DTD.
+ fLimitAnalyzer.reset(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT);
fLimitAnalyzer.reset(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT);
return false;
}
@@ -399,7 +397,7 @@
if (isInvalidLiteral(c)) {
reportFatalError("InvalidCharInDTD",
new Object[] { Integer.toHexString(c) });
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
}
}
}
@@ -767,7 +765,7 @@
fStringBuffer.clear();
fStringBuffer.append("xml");
while (isValidNameChar(fEntityScanner.peekChar())) {
- fStringBuffer.append((char)fEntityScanner.scanChar());
+ fStringBuffer.append((char)fEntityScanner.scanChar(null));
}
String target =
fSymbolTable.addSymbol(fStringBuffer.ch,
@@ -867,7 +865,7 @@
}
// element name
- String name = fEntityScanner.scanName();
+ String name = fEntityScanner.scanName(NameType.ELEMENTSTART);
if (name == null) {
reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_ELEMENTDECL",
null);
@@ -900,7 +898,7 @@
}
}
else {
- if (!fEntityScanner.skipChar('(')) {
+ if (!fEntityScanner.skipChar('(', null)) {
reportFatalError("MSG_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED_IN_CHILDREN",
new Object[]{name});
}
@@ -930,7 +928,7 @@
fReportEntity = false;
skipSeparator(false, !scanningInternalSubset());
// end
- if (!fEntityScanner.skipChar('>')) {
+ if (!fEntityScanner.skipChar('>', null)) {
reportFatalError("ElementDeclUnterminated", new Object[]{name});
}
fReportEntity = true;
@@ -967,7 +965,7 @@
fDTDContentModelHandler.pcdata(null);
}
skipSeparator(false, !scanningInternalSubset());
- while (fEntityScanner.skipChar('|')) {
+ while (fEntityScanner.skipChar('|', null)) {
fStringBuffer.append('|');
// call handler
if (fDTDContentModelHandler != null) {
@@ -976,7 +974,7 @@
}
skipSeparator(false, !scanningInternalSubset());
- childName = fEntityScanner.scanName();
+ childName = fEntityScanner.scanName(NameType.ENTITY);
if (childName == null) {
reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_MIXED_CONTENT",
new Object[]{elName});
@@ -1005,7 +1003,7 @@
reportFatalError("MixedContentUnterminated",
new Object[]{elName});
}
- else if (fEntityScanner.skipChar(')')){
+ else if (fEntityScanner.skipChar(')', null)){
fStringBuffer.append(')');
// call handler
if (fDTDContentModelHandler != null) {
@@ -1043,7 +1041,7 @@
int currentOp = 0;
int c;
while (true) {
- if (fEntityScanner.skipChar('(')) {
+ if (fEntityScanner.skipChar('(', null)) {
fMarkUpDepth++;
fStringBuffer.append('(');
// call handler
@@ -1057,7 +1055,7 @@
continue;
}
skipSeparator(false, !scanningInternalSubset());
- String childName = fEntityScanner.scanName();
+ String childName = fEntityScanner.scanName(NameType.ELEMENTSTART);
if (childName == null) {
reportFatalError("MSG_OPEN_PAREN_OR_ELEMENT_TYPE_REQUIRED_IN_CHILDREN",
new Object[]{elName});
@@ -1084,7 +1082,7 @@
}
fDTDContentModelHandler.occurrence(oc, null);
}
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
fStringBuffer.append((char)c);
}
while (true) {
@@ -1097,7 +1095,7 @@
fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_SEQUENCE,
null);
}
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
fStringBuffer.append(',');
break;
}
@@ -1108,7 +1106,7 @@
fDTDContentModelHandler.separator(XMLDTDContentModelHandler.SEPARATOR_CHOICE,
null);
}
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
fStringBuffer.append('|');
break;
}
@@ -1154,7 +1152,7 @@
}
else {
// no occurrence specified
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
fStringBuffer.append(')');
}
fMarkUpDepth--;
@@ -1186,7 +1184,7 @@
}
// element name
- String elName = fEntityScanner.scanName();
+ String elName = fEntityScanner.scanName(NameType.ELEMENTSTART);
if (elName == null) {
reportFatalError("MSG_ELEMENT_TYPE_REQUIRED_IN_ATTLISTDECL",
null);
@@ -1200,7 +1198,7 @@
// spaces
if (!skipSeparator(true, !scanningInternalSubset())) {
// no space, is it the end yet?
- if (fEntityScanner.skipChar('>')) {
+ if (fEntityScanner.skipChar('>', null)) {
// yes, stop here
// call handler
if (fDTDHandler != null) {
@@ -1216,8 +1214,8 @@
}
// definitions
- while (!fEntityScanner.skipChar('>')) {
- String name = fEntityScanner.scanName();
+ while (!fEntityScanner.skipChar('>', null)) {
+ String name = fEntityScanner.scanName(NameType.ATTRIBUTE);
if (name == null) {
reportFatalError("AttNameRequiredInAttDef",
new Object[]{elName});
@@ -1353,7 +1351,7 @@
new Object[]{elName, atName});
}
// open paren
- int c = fEntityScanner.scanChar();
+ int c = fEntityScanner.scanChar(null);
if (c != '(') {
reportFatalError("MSG_OPEN_PAREN_REQUIRED_IN_NOTATIONTYPE",
new Object[]{elName, atName});
@@ -1361,7 +1359,7 @@
fMarkUpDepth++;
do {
skipSeparator(false, !scanningInternalSubset());
- String aName = fEntityScanner.scanName();
+ String aName = fEntityScanner.scanName(NameType.ATTRIBUTE);
if (aName == null) {
reportFatalError("MSG_NAME_REQUIRED_IN_NOTATIONTYPE",
new Object[]{elName, atName});
@@ -1369,7 +1367,7 @@
ensureEnumerationSize(fEnumerationCount + 1);
fEnumeration[fEnumerationCount++] = aName;
skipSeparator(false, !scanningInternalSubset());
- c = fEntityScanner.scanChar();
+ c = fEntityScanner.scanChar(null);
} while (c == '|');
if (c != ')') {
reportFatalError("NotationTypeUnterminated",
@@ -1380,7 +1378,7 @@
else { // Enumeration
type = "ENUMERATION";
// open paren
- int c = fEntityScanner.scanChar();
+ int c = fEntityScanner.scanChar(null);
if (c != '(') {
// "OPEN_PAREN_REQUIRED_BEFORE_ENUMERATION_IN_ATTRDECL",
reportFatalError("AttTypeRequiredInAttDef",
@@ -1397,7 +1395,7 @@
ensureEnumerationSize(fEnumerationCount + 1);
fEnumeration[fEnumerationCount++] = token;
skipSeparator(false, !scanningInternalSubset());
- c = fEntityScanner.scanChar();
+ c = fEntityScanner.scanChar(null);
} while (c == '|');
if (c != ')') {
reportFatalError("EnumerationUnterminated",
@@ -1447,7 +1445,7 @@
// AttValue
boolean isVC = !fStandalone && (fSeenExternalDTD || fSeenExternalPE) ;
scanAttributeValue(defaultVal, nonNormalizedDefaultVal, atName,
- fAttributes, 0, isVC, elName);
+ fAttributes, 0, isVC, elName, false);
}
return defaultType;
@@ -1475,7 +1473,7 @@
boolean sawPERef = false;
fReportEntity = false;
if (fEntityScanner.skipSpaces()) {
- if (!fEntityScanner.skipChar('%')) {
+ if (!fEntityScanner.skipChar('%', NameType.REFERENCE)) {
isPEDecl = false; // <!ENTITY x "x">
}
else if (skipSeparator(true, !scanningInternalSubset())) {
@@ -1496,7 +1494,7 @@
sawPERef = true;
}
}
- else if (scanningInternalSubset() || !fEntityScanner.skipChar('%')) {
+ else if (scanningInternalSubset() || !fEntityScanner.skipChar('%', NameType.REFERENCE)) {
// <!ENTITY[^ ]...> or <!ENTITY[^ %]...>
reportFatalError("MSG_SPACE_REQUIRED_BEFORE_ENTITY_NAME_IN_ENTITYDECL",
null);
@@ -1513,11 +1511,11 @@
}
if (sawPERef) {
while (true) {
- String peName = fEntityScanner.scanName();
+ String peName = fEntityScanner.scanName(NameType.REFERENCE);
if (peName == null) {
reportFatalError("NameRequiredInPEReference", null);
}
- else if (!fEntityScanner.skipChar(';')) {
+ else if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
reportFatalError("SemicolonRequiredInPEReference",
new Object[]{peName});
}
@@ -1525,20 +1523,20 @@
startPE(peName, false);
}
fEntityScanner.skipSpaces();
- if (!fEntityScanner.skipChar('%'))
+ if (!fEntityScanner.skipChar('%', NameType.REFERENCE))
break;
if (!isPEDecl) {
if (skipSeparator(true, !scanningInternalSubset())) {
isPEDecl = true;
break;
}
- isPEDecl = fEntityScanner.skipChar('%');
+ isPEDecl = fEntityScanner.skipChar('%', NameType.REFERENCE);
}
}
}
// name
- String name = fEntityScanner.scanName();
+ String name = fEntityScanner.scanName(NameType.ENTITY);
if (name == null) {
reportFatalError("MSG_ENTITY_NAME_REQUIRED_IN_ENTITYDECL", null);
}
@@ -1573,7 +1571,7 @@
reportFatalError("MSG_SPACE_REQUIRED_BEFORE_NOTATION_NAME_IN_UNPARSED_ENTITYDECL",
new Object[]{name});
}
- notation = fEntityScanner.scanName();
+ notation = fEntityScanner.scanName(NameType.NOTATION);
if (notation == null) {
reportFatalError("MSG_NOTATION_NAME_REQUIRED_FOR_UNPARSED_ENTITYDECL",
new Object[]{name});
@@ -1595,7 +1593,7 @@
skipSeparator(false, !scanningInternalSubset());
// end
- if (!fEntityScanner.skipChar('>')) {
+ if (!fEntityScanner.skipChar('>', null)) {
reportFatalError("EntityDeclUnterminated", new Object[]{name});
}
fMarkUpDepth--;
@@ -1650,7 +1648,7 @@
protected final void scanEntityValue(String entityName, boolean isPEDecl, XMLString value,
XMLString nonNormalizedValue)
throws IOException, XNIException {
- int quote = fEntityScanner.scanChar();
+ int quote = fEntityScanner.scanChar(null);
if (quote != '\'' && quote != '"') {
reportFatalError("OpenQuoteMissingInDecl", null);
}
@@ -1665,23 +1663,24 @@
}
fLimitAnalyzer.startEntity(entityName);
- if (fEntityScanner.scanLiteral(quote, fString) != quote) {
+ if (fEntityScanner.scanLiteral(quote, fString, false) != quote) {
fStringBuffer.clear();
fStringBuffer2.clear();
+ int offset;
do {
- checkEntityLimit(isPEDecl, entityName, fString.length + countChar);
countChar = 0;
+ offset = fStringBuffer.length;
fStringBuffer.append(fString);
fStringBuffer2.append(fString);
- if (fEntityScanner.skipChar('&')) {
- if (fEntityScanner.skipChar('#')) {
+ if (fEntityScanner.skipChar('&', NameType.REFERENCE)) {
+ if (fEntityScanner.skipChar('#', NameType.REFERENCE)) {
fStringBuffer2.append("&#");
scanCharReferenceValue(fStringBuffer, fStringBuffer2);
}
else {
fStringBuffer.append('&');
fStringBuffer2.append('&');
- String eName = fEntityScanner.scanName();
+ String eName = fEntityScanner.scanName(NameType.REFERENCE);
if (eName == null) {
reportFatalError("NameRequiredInReference",
null);
@@ -1690,7 +1689,7 @@
fStringBuffer.append(eName);
fStringBuffer2.append(eName);
}
- if (!fEntityScanner.skipChar(';')) {
+ if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
reportFatalError("SemicolonRequiredInReference",
new Object[]{eName});
}
@@ -1700,15 +1699,15 @@
}
}
}
- else if (fEntityScanner.skipChar('%')) {
+ else if (fEntityScanner.skipChar('%', NameType.REFERENCE)) {
while (true) {
fStringBuffer2.append('%');
- String peName = fEntityScanner.scanName();
+ String peName = fEntityScanner.scanName(NameType.REFERENCE);
if (peName == null) {
reportFatalError("NameRequiredInPEReference",
null);
}
- else if (!fEntityScanner.skipChar(';')) {
+ else if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
reportFatalError("SemicolonRequiredInPEReference",
new Object[]{peName});
}
@@ -1725,20 +1724,20 @@
// REVISIT: This will make returning the non-
// normalized value harder. -Ac
fEntityScanner.skipSpaces();
- if (!fEntityScanner.skipChar('%'))
+ if (!fEntityScanner.skipChar('%', NameType.REFERENCE))
break;
}
}
else {
- countChar++;
int c = fEntityScanner.peekChar();
if (XMLChar.isHighSurrogate(c)) {
+ countChar++;
scanSurrogates(fStringBuffer2);
}
else if (isInvalidLiteral(c)) {
reportFatalError("InvalidCharInLiteral",
new Object[]{Integer.toHexString(c)});
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
}
// if it's not the delimiting quote or if it is but from a
// different entity than the one this literal started from,
@@ -1746,10 +1745,12 @@
else if (c != quote || entityDepth != fEntityDepth) {
fStringBuffer.append((char)c);
fStringBuffer2.append((char)c);
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
}
}
- } while (fEntityScanner.scanLiteral(quote, fString) != quote);
+ checkEntityLimit(isPEDecl, entityName, fStringBuffer.length - offset + countChar);
+ } while (fEntityScanner.scanLiteral(quote, fString, false) != quote);
+ checkEntityLimit(isPEDecl, entityName, fString.length);
fStringBuffer.append(fString);
fStringBuffer2.append(fString);
literal = fStringBuffer;
@@ -1760,10 +1761,14 @@
value.setValues(literal);
nonNormalizedValue.setValues(literal2);
if (fLimitAnalyzer != null) {
- fLimitAnalyzer.endEntity(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, entityName);
+ if (isPEDecl) {
+ fLimitAnalyzer.endEntity(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, entityName);
+ } else {
+ fLimitAnalyzer.endEntity(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT, entityName);
+ }
}
- if (!fEntityScanner.skipChar(quote)) {
+ if (!fEntityScanner.skipChar(quote, null)) {
reportFatalError("CloseQuoteMissingInDecl", null);
}
} // scanEntityValue(XMLString,XMLString):void
@@ -1788,7 +1793,7 @@
}
// notation name
- String name = fEntityScanner.scanName();
+ String name = fEntityScanner.scanName(NameType.NOTATION);
if (name == null) {
reportFatalError("MSG_NOTATION_NAME_REQUIRED_IN_NOTATIONDECL",
null);
@@ -1815,7 +1820,7 @@
skipSeparator(false, !scanningInternalSubset());
// end
- if (!fEntityScanner.skipChar('>')) {
+ if (!fEntityScanner.skipChar('>', null)) {
reportFatalError("NotationDeclUnterminated", new Object[]{name});
}
fMarkUpDepth--;
@@ -1863,7 +1868,7 @@
XMLErrorReporter.SEVERITY_ERROR);
}
// call handler
- if (!fEntityScanner.skipChar('[')) {
+ if (!fEntityScanner.skipChar('[', null)) {
reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
}
@@ -1888,7 +1893,7 @@
fDTDHandler.startConditional(XMLDTDHandler.CONDITIONAL_IGNORE,
null);
}
- if (!fEntityScanner.skipChar('[')) {
+ if (!fEntityScanner.skipChar('[', null)) {
reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
}
fReportEntity = true;
@@ -1897,7 +1902,7 @@
fIgnoreConditionalBuffer.clear();
}
while (true) {
- if (fEntityScanner.skipChar('<')) {
+ if (fEntityScanner.skipChar('<', null)) {
if (fDTDHandler != null) {
fIgnoreConditionalBuffer.append('<');
}
@@ -1905,8 +1910,8 @@
// These tests are split so that we handle cases like
// '<<![' and '<!<![' which we might otherwise miss.
//
- if (fEntityScanner.skipChar('!')) {
- if(fEntityScanner.skipChar('[')) {
+ if (fEntityScanner.skipChar('!', null)) {
+ if(fEntityScanner.skipChar('[', null)) {
if (fDTDHandler != null) {
fIgnoreConditionalBuffer.append("![");
}
@@ -1918,24 +1923,24 @@
}
}
}
- else if (fEntityScanner.skipChar(']')) {
+ else if (fEntityScanner.skipChar(']', null)) {
if (fDTDHandler != null) {
fIgnoreConditionalBuffer.append(']');
}
//
// The same thing goes for ']<![' and '<]]>', etc.
//
- if (fEntityScanner.skipChar(']')) {
+ if (fEntityScanner.skipChar(']', null)) {
if (fDTDHandler != null) {
fIgnoreConditionalBuffer.append(']');
}
- while (fEntityScanner.skipChar(']')) {
+ while (fEntityScanner.skipChar(']', null)) {
/* empty loop body */
if (fDTDHandler != null) {
fIgnoreConditionalBuffer.append(']');
}
}
- if (fEntityScanner.skipChar('>')) {
+ if (fEntityScanner.skipChar('>', null)) {
if (fIncludeSectDepth-- == initialDepth) {
fMarkUpDepth--;
// call handler
@@ -1953,7 +1958,7 @@
}
}
else {
- int c = fEntityScanner.scanChar();
+ int c = fEntityScanner.scanChar(null);
if (fScannerState == SCANNER_STATE_END_OF_INPUT) {
reportFatalError("IgnoreSectUnterminated", null);
return;
@@ -1990,16 +1995,16 @@
//System.out.println("scanDecls"+fScannerState);
while (again && fScannerState == SCANNER_STATE_MARKUP_DECL) {
again = complete;
- if (fEntityScanner.skipChar('<')) {
+ if (fEntityScanner.skipChar('<', null)) {
fMarkUpDepth++;
- if (fEntityScanner.skipChar('?')) {
+ if (fEntityScanner.skipChar('?', null)) {
fStringBuffer.clear();
scanPI(fStringBuffer);
fMarkUpDepth--; // we're done with this decl
}
- else if (fEntityScanner.skipChar('!')) {
- if (fEntityScanner.skipChar('-')) {
- if (!fEntityScanner.skipChar('-')) {
+ else if (fEntityScanner.skipChar('!', null)) {
+ if (fEntityScanner.skipChar('-', null)) {
+ if (!fEntityScanner.skipChar('-', null)) {
reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD",
null);
} else {
@@ -2018,7 +2023,7 @@
else if (fEntityScanner.skipString("NOTATION")) {
scanNotationDecl();
}
- else if (fEntityScanner.skipChar('[') &&
+ else if (fEntityScanner.skipChar('[', null) &&
!scanningInternalSubset()) {
scanConditionalSect(fPEDepth);
}
@@ -2033,10 +2038,10 @@
reportFatalError("MSG_MARKUP_NOT_RECOGNIZED_IN_DTD", null);
}
}
- else if (fIncludeSectDepth > 0 && fEntityScanner.skipChar(']')) {
+ else if (fIncludeSectDepth > 0 && fEntityScanner.skipChar(']', null)) {
// end of conditional section?
- if (!fEntityScanner.skipChar(']')
- || !fEntityScanner.skipChar('>')) {
+ if (!fEntityScanner.skipChar(']', null)
+ || !fEntityScanner.skipChar('>', null)) {
reportFatalError("IncludeSectUnterminated", null);
}
// call handler
@@ -2083,21 +2088,21 @@
throws IOException, XNIException {
int depth = fPEDepth;
boolean sawSpace = fEntityScanner.skipSpaces();
- if (!lookForPERefs || !fEntityScanner.skipChar('%')) {
+ if (!lookForPERefs || !fEntityScanner.skipChar('%', NameType.REFERENCE)) {
return !spaceRequired || sawSpace || (depth != fPEDepth);
}
while (true) {
- String name = fEntityScanner.scanName();
+ String name = fEntityScanner.scanName(NameType.ENTITY);
if (name == null) {
reportFatalError("NameRequiredInPEReference", null);
}
- else if (!fEntityScanner.skipChar(';')) {
+ else if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
reportFatalError("SemicolonRequiredInPEReference",
new Object[]{name});
}
startPE(name, false);
fEntityScanner.skipSpaces();
- if (!fEntityScanner.skipChar('%'))
+ if (!fEntityScanner.skipChar('%', NameType.REFERENCE))
return true;
}
}
@@ -2181,56 +2186,6 @@
fSecurityManager = fEntityManager.fSecurityManager;
}
- /**
- * Add the count of the content buffer and check if the accumulated
- * value exceeds the limit
- * @param isPEDecl a flag to indicate whether the entity is parameter
- * @param entityName entity name
- * @param buffer content buffer
- */
- private void checkEntityLimit(boolean isPEDecl, String entityName, XMLString buffer) {
- checkEntityLimit(isPEDecl, entityName, buffer.length);
- }
-
- /**
- * Add the count and check limit
- * @param isPEDecl a flag to indicate whether the entity is parameter
- * @param entityName entity name
- * @param len length of the buffer
- */
- private void checkEntityLimit(boolean isPEDecl, String entityName, int len) {
- if (fLimitAnalyzer == null) {
- fLimitAnalyzer = fEntityManager.fLimitAnalyzer;
- }
- if (isPEDecl) {
- fLimitAnalyzer.addValue(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, "%" + entityName, len);
- if (fSecurityManager.isOverLimit(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, fLimitAnalyzer)) {
- fSecurityManager.debugPrint(fLimitAnalyzer);
- reportFatalError("MaxEntitySizeLimit", new Object[]{"%" + entityName,
- fLimitAnalyzer.getValue(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT),
- fSecurityManager.getLimit(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT),
- fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT)});
- }
- } else {
- fLimitAnalyzer.addValue(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT, entityName, len);
- if (fSecurityManager.isOverLimit(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT, fLimitAnalyzer)) {
- fSecurityManager.debugPrint(fLimitAnalyzer);
- reportFatalError("MaxEntitySizeLimit", new Object[]{entityName,
- fLimitAnalyzer.getValue(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT),
- fSecurityManager.getLimit(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT),
- fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT)});
- }
- }
- if (fSecurityManager.isOverLimit(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT, fLimitAnalyzer)) {
- fSecurityManager.debugPrint(fLimitAnalyzer);
- reportFatalError("TotalEntitySizeLimit",
- new Object[]{fLimitAnalyzer.getTotalValue(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT),
- fSecurityManager.getLimit(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT),
- fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT)});
- }
-
- }
-
public DTDGrammar getGrammar(){
return nvGrammarInfo;
}
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentFragmentScannerImpl.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentFragmentScannerImpl.java Thu Jul 21 20:09:19 2016 -0700
@@ -21,14 +21,6 @@
package com.sun.org.apache.xerces.internal.impl;
-import com.sun.xml.internal.stream.XMLBufferListener;
-import com.sun.xml.internal.stream.XMLEntityStorage;
-import com.sun.xml.internal.stream.dtd.DTDGrammarUtil;
-
-import java.io.EOFException;
-import java.io.IOException;
-import javax.xml.stream.XMLInputFactory;
-import javax.xml.stream.events.XMLEvent;
import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
import com.sun.org.apache.xerces.internal.util.AugmentationsImpl;
import com.sun.org.apache.xerces.internal.util.XMLAttributesIteratorImpl;
@@ -47,13 +39,18 @@
import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentScanner;
import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource;
import com.sun.org.apache.xerces.internal.xni.Augmentations;
-import com.sun.org.apache.xerces.internal.impl.Constants;
-import com.sun.org.apache.xerces.internal.impl.XMLEntityHandler;
import com.sun.org.apache.xerces.internal.utils.SecuritySupport;
import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager;
import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager.Limit;
import com.sun.org.apache.xerces.internal.utils.XMLSecurityPropertyManager;
+import com.sun.xml.internal.stream.XMLBufferListener;
+import com.sun.xml.internal.stream.XMLEntityStorage;
+import com.sun.xml.internal.stream.dtd.DTDGrammarUtil;
+import java.io.EOFException;
+import java.io.IOException;
+import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.events.XMLEvent;
/**
*
@@ -454,6 +451,7 @@
//fDocumentHandler.startElement(getElementQName(),fAttributes,null);
break;
case XMLStreamConstants.CHARACTERS :
+ fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity);
fDocumentHandler.characters(getCharacterData(),null);
break;
case XMLStreamConstants.SPACE:
@@ -462,13 +460,15 @@
//fDocumentHandler.ignorableWhitespace(getCharacterData(), null);
break;
case XMLStreamConstants.ENTITY_REFERENCE :
+ fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity);
//entity reference callback are given in startEntity
break;
case XMLStreamConstants.PROCESSING_INSTRUCTION :
+ fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity);
fDocumentHandler.processingInstruction(getPITarget(),getPIData(),null);
break;
case XMLStreamConstants.COMMENT :
- //System.out.println(" in COMMENT of the XMLNSDocumentScannerImpl");
+ fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity);
fDocumentHandler.comment(getCharacterData(),null);
break;
case XMLStreamConstants.DTD :
@@ -477,6 +477,7 @@
//therefore we don't need to take care of anything here. So Just break;
break;
case XMLStreamConstants.CDATA:
+ fEntityScanner.checkNodeCount(fEntityScanner.fCurrentEntity);
fDocumentHandler.startCDATA(null);
//xxx: check if CDATA values comes from getCharacterData() function
fDocumentHandler.characters(getCharacterData(),null);
@@ -1273,9 +1274,9 @@
fElementQName = fElementStack.nextElement();
// name
if (fNamespaces) {
- fEntityScanner.scanQName(fElementQName);
+ fEntityScanner.scanQName(fElementQName, NameType.ELEMENTSTART);
} else {
- String name = fEntityScanner.scanName();
+ String name = fEntityScanner.scanName(NameType.ELEMENTSTART);
fElementQName.setValues(null, name, name, null);
}
@@ -1376,11 +1377,11 @@
// end tag?
final int c = fEntityScanner.peekChar();
if (c == '>') {
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
return true;
} else if (c == '/') {
- fEntityScanner.scanChar();
- if (!fEntityScanner.skipChar('>')) {
+ fEntityScanner.scanChar(null);
+ if (!fEntityScanner.skipChar('>', NameType.ELEMENTEND)) {
reportFatalError("ElementUnterminated",
new Object[]{fElementQName.rawname});
}
@@ -1518,15 +1519,15 @@
// name
if (fNamespaces) {
- fEntityScanner.scanQName(fAttributeQName);
+ fEntityScanner.scanQName(fAttributeQName, NameType.ATTRIBUTENAME);
} else {
- String name = fEntityScanner.scanName();
+ String name = fEntityScanner.scanName(NameType.ATTRIBUTENAME);
fAttributeQName.setValues(null, name, name, null);
}
// equals
fEntityScanner.skipSpaces();
- if (!fEntityScanner.skipChar('=')) {
+ if (!fEntityScanner.skipChar('=', NameType.ATTRIBUTE)) {
reportFatalError("EqRequiredInAttribute",
new Object[] {fCurrentElement.rawname, fAttributeQName.rawname});
}
@@ -1544,9 +1545,8 @@
//can safely add the attribute later..
XMLString tmpStr = getString();
- scanAttributeValue(tmpStr, fTempString2,
- fAttributeQName.rawname, attributes,
- attIndex, isVC, fCurrentElement.rawname);
+ scanAttributeValue(tmpStr, fTempString2, fAttributeQName.rawname, attributes,
+ attIndex, isVC, fCurrentElement.rawname, false);
// content
int oldLen = attributes.getLength();
@@ -1594,13 +1594,13 @@
if (c == '\r') {
// happens when there is the character reference
//xxx: We know the next chracter.. we should just skip it and add ']' directlry
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
content.append((char)c);
c = -1;
} else if (c == ']') {
//fStringBuffer.clear();
//xxx: We know the next chracter.. we should just skip it and add ']' directlry
- content.append((char)fEntityScanner.scanChar());
+ content.append((char)fEntityScanner.scanChar(null));
// remember where we are in case we get an endEntity before we
// could flush the buffer out - this happens when we're parsing an
// entity which ends with a ]
@@ -1609,12 +1609,12 @@
// We work on a single character basis to handle cases such as:
// ']]]>' which we might otherwise miss.
//
- if (fEntityScanner.skipChar(']')) {
+ if (fEntityScanner.skipChar(']', null)) {
content.append(']');
- while (fEntityScanner.skipChar(']')) {
+ while (fEntityScanner.skipChar(']', null)) {
content.append(']');
}
- if (fEntityScanner.skipChar('>')) {
+ if (fEntityScanner.skipChar('>', null)) {
reportFatalError("CDEndInContent", null);
}
}
@@ -1689,7 +1689,7 @@
} else {
reportFatalError("InvalidCharInCDSect",
new Object[]{Integer.toString(c,16)});
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
}
}
//by this time we have also read surrogate contents if any...
@@ -1751,7 +1751,7 @@
// end
fEntityScanner.skipSpaces();
- if (!fEntityScanner.skipChar('>')) {
+ if (!fEntityScanner.skipChar('>', NameType.ELEMENTEND)) {
reportFatalError("ETagUnterminated",
new Object[]{rawname});
}
@@ -1841,12 +1841,12 @@
* notification.
*/
protected void scanEntityReference(XMLStringBuffer content) throws IOException, XNIException {
- String name = fEntityScanner.scanName();
+ String name = fEntityScanner.scanName(NameType.REFERENCE);
if (name == null) {
reportFatalError("NameRequiredInReference", null);
return;
}
- if (!fEntityScanner.skipChar(';')) {
+ if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
reportFatalError("SemicolonRequiredInReference", new Object []{name});
}
if (fEntityStore.isUnparsedEntity(name)) {
@@ -1943,6 +1943,7 @@
*/
private void handleCharacter(char c, String entity, XMLStringBuffer content) throws XNIException {
foundBuiltInRefs = true;
+ checkEntityLimit(false, fEntityScanner.fCurrentEntity.name, 1);
content.append(c);
if (fDocumentHandler != null) {
fSingleChar[0] = c;
@@ -2608,13 +2609,13 @@
switch(ch){
case '?' :{
setScannerState(SCANNER_STATE_PI);
- fEntityScanner.skipChar(ch);
+ fEntityScanner.skipChar(ch, null);
break;
}
case '!' :{
- fEntityScanner.skipChar(ch);
- if (fEntityScanner.skipChar('-')) {
- if (!fEntityScanner.skipChar('-')) {
+ fEntityScanner.skipChar(ch, null);
+ if (fEntityScanner.skipChar('-', null)) {
+ if (!fEntityScanner.skipChar('-', NameType.COMMENT)) {
reportFatalError("InvalidCommentStart",
null);
}
@@ -2629,7 +2630,7 @@
}
case '/' :{
setScannerState(SCANNER_STATE_END_ELEMENT_TAG);
- fEntityScanner.skipChar(ch);
+ fEntityScanner.skipChar(ch, NameType.ELEMENTEND);
break;
}
default :{
@@ -2641,9 +2642,9 @@
}//startOfMarkup
private void startOfContent() throws IOException {
- if (fEntityScanner.skipChar('<')) {
+ if (fEntityScanner.skipChar('<', null)) {
setScannerState(SCANNER_STATE_START_OF_MARKUP);
- } else if (fEntityScanner.skipChar('&')) {
+ } else if (fEntityScanner.skipChar('&', NameType.REFERENCE)) {
setScannerState(SCANNER_STATE_REFERENCE) ; //XMLEvent.ENTITY_REFERENCE ); //SCANNER_STATE_REFERENCE
} else {
//element content is there..
@@ -2716,10 +2717,10 @@
case SCANNER_STATE_CONTENT: {
final int ch = fEntityScanner.peekChar();
if (ch == '<') {
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
setScannerState(SCANNER_STATE_START_OF_MARKUP);
} else if (ch == '&') {
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(NameType.REFERENCE);
setScannerState(SCANNER_STATE_REFERENCE) ; //XMLEvent.ENTITY_REFERENCE ); //SCANNER_STATE_REFERENCE
break;
} else {
@@ -2819,9 +2820,9 @@
if(DEBUG){
System.out.println("fTempString = " + fTempString);
}
- if(fEntityScanner.skipChar('<')){
+ if(fEntityScanner.skipChar('<', null)){
//check if we have reached end of element
- if(fEntityScanner.skipChar('/')){
+ if(fEntityScanner.skipChar('/', NameType.ELEMENTEND)){
//increase the mark up depth
fMarkupDepth++;
fLastSectionWasCharacterData = false;
@@ -2871,7 +2872,7 @@
}
// happens when there is the character reference
//xxx: We know the next chracter.. we should just skip it and add ']' directlry
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
fUsebuffer = true;
fContentBuffer.append((char)c);
c = -1 ;
@@ -2879,7 +2880,7 @@
//fStringBuffer.clear();
//xxx: We know the next chracter.. we should just skip it and add ']' directlry
fUsebuffer = true;
- fContentBuffer.append((char)fEntityScanner.scanChar());
+ fContentBuffer.append((char)fEntityScanner.scanChar(null));
// remember where we are in case we get an endEntity before we
// could flush the buffer out - this happens when we're parsing an
// entity which ends with a ]
@@ -2888,12 +2889,12 @@
// We work on a single character basis to handle cases such as:
// ']]]>' which we might otherwise miss.
//
- if (fEntityScanner.skipChar(']')) {
+ if (fEntityScanner.skipChar(']', null)) {
fContentBuffer.append(']');
- while (fEntityScanner.skipChar(']')) {
+ while (fEntityScanner.skipChar(']', null)) {
fContentBuffer.append(']');
}
- if (fEntityScanner.skipChar('>')) {
+ if (fEntityScanner.skipChar('>', null)) {
reportFatalError("CDEndInContent", null);
}
}
@@ -2906,12 +2907,12 @@
// we need not to grow the buffer only when isCoalesce() is not true;
if (c == '<') {
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
setScannerState(SCANNER_STATE_START_OF_MARKUP);
break;
}//xxx what should be the behavior if entity reference is present in the content ?
else if (c == '&') {
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(NameType.REFERENCE);
setScannerState(SCANNER_STATE_REFERENCE);
break;
}///xxx since this part is also characters, it should be merged...
@@ -2924,7 +2925,7 @@
reportFatalError("InvalidCharInContent",
new Object[] {
Integer.toString(c, 16)});
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
}
break;
}
@@ -3050,7 +3051,7 @@
}
fUsebuffer = true ;
//take care of character reference
- if (fEntityScanner.skipChar('#')) {
+ if (fEntityScanner.skipChar('#', NameType.REFERENCE)) {
scanCharReferenceValue(fContentBuffer, null);
fMarkupDepth--;
if(!fIsCoalesce){
@@ -3106,11 +3107,11 @@
if (fNamespaces) {
while (isValidNCName(fEntityScanner.peekChar())) {
- fStringBuffer.append((char)fEntityScanner.scanChar());
+ fStringBuffer.append((char)fEntityScanner.scanChar(null));
}
} else {
while (isValidNameChar(fEntityScanner.peekChar())) {
- fStringBuffer.append((char)fEntityScanner.scanChar());
+ fStringBuffer.append((char)fEntityScanner.scanChar(null));
}
}
String target = fSymbolTable.addSymbol(fStringBuffer.ch, fStringBuffer.offset, fStringBuffer.length);
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentScannerImpl.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLDocumentScannerImpl.java Thu Jul 21 20:09:19 2016 -0700
@@ -631,7 +631,7 @@
}
// root element name
- fDoctypeName = fEntityScanner.scanName();
+ fDoctypeName = fEntityScanner.scanName(NameType.DOCTYPE);
if (fDoctypeName == null) {
reportFatalError("MSG_ROOT_ELEMENT_TYPE_REQUIRED", null);
}
@@ -671,10 +671,10 @@
// is there an internal subset?
boolean internalSubset = true;
- if (!fEntityScanner.skipChar('[')) {
+ if (!fEntityScanner.skipChar('[', null)) {
internalSubset = false;
fEntityScanner.skipSpaces();
- if (!fEntityScanner.skipChar('>')) {
+ if (!fEntityScanner.skipChar('>', null)) {
reportFatalError("DoctypedeclUnterminated", new Object[]{fDoctypeName});
}
fMarkupDepth--;
@@ -753,7 +753,7 @@
fStringBuffer.clear();
fStringBuffer.append("xml");
while (XMLChar.isName(fEntityScanner.peekChar())) {
- fStringBuffer.append((char)fEntityScanner.scanChar());
+ fStringBuffer.append((char)fEntityScanner.scanChar(null));
}
String target = fSymbolTable.addSymbol(fStringBuffer.ch, fStringBuffer.offset, fStringBuffer.length);
//this function should fill the data.. and set the fEvent object to this event.
@@ -831,9 +831,9 @@
switch (fScannerState) {
case SCANNER_STATE_PROLOG: {
fEntityScanner.skipSpaces();
- if (fEntityScanner.skipChar('<')) {
+ if (fEntityScanner.skipChar('<', null)) {
setScannerState(SCANNER_STATE_START_OF_MARKUP);
- } else if (fEntityScanner.skipChar('&')) {
+ } else if (fEntityScanner.skipChar('&', NameType.REFERENCE)) {
setScannerState(SCANNER_STATE_REFERENCE);
} else {
setScannerState(SCANNER_STATE_CONTENT);
@@ -849,9 +849,9 @@
setDriver(fContentDriver);
//from now onwards this would be handled by fContentDriver,in the same next() call
return fContentDriver.next();
- } else if (fEntityScanner.skipChar('!')) {
- if (fEntityScanner.skipChar('-')) {
- if (!fEntityScanner.skipChar('-')) {
+ } else if (fEntityScanner.skipChar('!', null)) {
+ if (fEntityScanner.skipChar('-', null)) {
+ if (!fEntityScanner.skipChar('-', null)) {
reportFatalError("InvalidCommentStart",
null);
}
@@ -871,7 +871,7 @@
reportFatalError("MarkupNotRecognizedInProlog",
null);
}
- } else if (fEntityScanner.skipChar('?')) {
+ } else if (fEntityScanner.skipChar('?', null)) {
setScannerState(SCANNER_STATE_PI);
} else {
reportFatalError("MarkupNotRecognizedInProlog",
@@ -991,7 +991,7 @@
case SCANNER_STATE_CONTENT: {
reportFatalError("ContentIllegalInProlog", null);
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
}
case SCANNER_STATE_REFERENCE: {
reportFatalError("ReferenceIllegalInProlog", null);
@@ -1105,11 +1105,11 @@
fReadingDTD=false;
if (!moreToScan) {
// end doctype declaration
- if (!fEntityScanner.skipChar(']')) {
+ if (!fEntityScanner.skipChar(']', null)) {
reportFatalError("DoctypedeclNotClosed", new Object[]{fDoctypeName});
}
fEntityScanner.skipSpaces();
- if (!fEntityScanner.skipChar('>')) {
+ if (!fEntityScanner.skipChar('>', null)) {
reportFatalError("DoctypedeclUnterminated", new Object[]{fDoctypeName});
}
fMarkupDepth--;
@@ -1373,7 +1373,7 @@
if(fScannerState == SCANNER_STATE_TERMINATED ){
return XMLEvent.END_DOCUMENT ;
}
- if (fEntityScanner.skipChar('<')) {
+ if (fEntityScanner.skipChar('<', null)) {
setScannerState(SCANNER_STATE_START_OF_MARKUP);
} else {
setScannerState(SCANNER_STATE_CONTENT);
@@ -1382,11 +1382,11 @@
}
case SCANNER_STATE_START_OF_MARKUP: {
fMarkupDepth++;
- if (fEntityScanner.skipChar('?')) {
+ if (fEntityScanner.skipChar('?', null)) {
setScannerState(SCANNER_STATE_PI);
- } else if (fEntityScanner.skipChar('!')) {
+ } else if (fEntityScanner.skipChar('!', null)) {
setScannerState(SCANNER_STATE_COMMENT);
- } else if (fEntityScanner.skipChar('/')) {
+ } else if (fEntityScanner.skipChar('/', null)) {
reportFatalError("MarkupNotRecognizedInMisc",
null);
} else if (isValidNameStartChar(fEntityScanner.peekChar()) ||
@@ -1429,7 +1429,7 @@
} else{
reportFatalError("ContentIllegalInTrailingMisc",
null);
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
setScannerState(SCANNER_STATE_TRAILING_MISC);
return XMLEvent.CHARACTERS;
}
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityManager.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityManager.java Thu Jul 21 20:09:19 2016 -0700
@@ -2066,6 +2066,7 @@
// system id has to be a valid URI
if (strict) {
+
try {
// if it's already an absolute one, return it
new URI(systemId);
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityScanner.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLEntityScanner.java Thu Jul 21 20:09:19 2016 -0700
@@ -21,6 +21,7 @@
package com.sun.org.apache.xerces.internal.impl;
+import com.sun.org.apache.xerces.internal.impl.XMLScanner.NameType;
import com.sun.org.apache.xerces.internal.impl.io.ASCIIReader;
import com.sun.org.apache.xerces.internal.impl.io.UCSReader;
import com.sun.org.apache.xerces.internal.impl.io.UTF8Reader;
@@ -144,6 +145,9 @@
// so that XMLStreamReader.getVersion() can find that out.
protected boolean xmlVersionSetExplicitly = false;
+ // indicates that the operation is for detecting XML version
+ boolean detectingVersion = false;
+
//
// Constructors
//
@@ -530,10 +534,12 @@
* <p>
* <strong>Note:</strong> The character is consumed.
*
+ * @param nt The type of the name (element or attribute)
+ *
* @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file.
*/
- public int scanChar() throws IOException {
+ protected int scanChar(NameType nt) throws IOException {
if (DEBUG_BUFFER) {
System.out.print("(scanChar: ");
print();
@@ -546,6 +552,7 @@
}
// scan character
+ int offset = fCurrentEntity.position;
int c = fCurrentEntity.ch[fCurrentEntity.position++];
if (c == '\n' || (c == '\r' && isExternal)) {
fCurrentEntity.lineNumber++;
@@ -554,6 +561,7 @@
invokeListeners(1);
fCurrentEntity.ch[0] = (char)c;
load(1, false, false);
+ offset = 0;
}
if (c == '\r' && isExternal) {
if (fCurrentEntity.ch[fCurrentEntity.position++] != '\n') {
@@ -570,6 +578,9 @@
System.out.println(" -> '"+(char)c+"'");
}
fCurrentEntity.columnNumber++;
+ if (!detectingVersion) {
+ checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
+ }
return c;
} // scanChar():int
@@ -589,7 +600,7 @@
* @see com.sun.org.apache.xerces.internal.util.SymbolTable
* @see com.sun.org.apache.xerces.internal.util.XMLChar#isName
*/
- public String scanNmtoken() throws IOException {
+ protected String scanNmtoken() throws IOException {
if (DEBUG_BUFFER) {
System.out.print("(scanNmtoken: ");
print();
@@ -661,6 +672,8 @@
* <strong>Note:</strong> The string returned must be a symbol. The
* SymbolTable can be used for this purpose.
*
+ * @param nt The type of the name (element or attribute)
+ *
* @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file.
*
@@ -668,7 +681,7 @@
* @see com.sun.org.apache.xerces.internal.util.XMLChar#isName
* @see com.sun.org.apache.xerces.internal.util.XMLChar#isNameStart
*/
- public String scanName() throws IOException {
+ protected String scanName(NameType nt) throws IOException {
if (DEBUG_BUFFER) {
System.out.print("(scanName: ");
print();
@@ -682,6 +695,7 @@
// scan name
int offset = fCurrentEntity.position;
+ int length;
if (XMLChar.isNameStart(fCurrentEntity.ch[offset])) {
if (++fCurrentEntity.position == fCurrentEntity.count) {
invokeListeners(1);
@@ -709,20 +723,7 @@
vc = XMLChar.isName(c);
}
if(!vc)break;
- if (++fCurrentEntity.position == fCurrentEntity.count) {
- int length = fCurrentEntity.position - offset;
- invokeListeners(length);
- if (length == fCurrentEntity.fBufferSize) {
- // bad luck we have to resize our buffer
- char[] tmp = new char[fCurrentEntity.fBufferSize * 2];
- System.arraycopy(fCurrentEntity.ch, offset,
- tmp, 0, length);
- fCurrentEntity.ch = tmp;
- fCurrentEntity.fBufferSize *= 2;
- } else {
- System.arraycopy(fCurrentEntity.ch, offset,
- fCurrentEntity.ch, 0, length);
- }
+ if ((length = checkBeforeLoad(fCurrentEntity, offset, offset)) > 0) {
offset = 0;
if (load(length, false, false)) {
break;
@@ -730,12 +731,14 @@
}
}
}
- int length = fCurrentEntity.position - offset;
+ length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length;
// return name
String symbol;
if (length > 0) {
+ checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length);
+ checkEntityLimit(nt, fCurrentEntity, offset, length);
symbol = fSymbolTable.addSymbol(fCurrentEntity.ch, offset, length);
} else
symbol = null;
@@ -759,6 +762,7 @@
* this purpose.
*
* @param qname The qualified name structure to fill.
+ * @param nt The type of the name (element or attribute)
*
* @return Returns true if a qualified name appeared immediately on
* the input and was scanned, false otherwise.
@@ -770,7 +774,7 @@
* @see com.sun.org.apache.xerces.internal.util.XMLChar#isName
* @see com.sun.org.apache.xerces.internal.util.XMLChar#isNameStart
*/
- public boolean scanQName(QName qname) throws IOException {
+ protected boolean scanQName(QName qname, NameType nt) throws IOException {
if (DEBUG_BUFFER) {
System.out.print("(scanQName, "+qname+": ");
print();
@@ -806,11 +810,13 @@
print();
System.out.println(" -> true");
}
+ checkEntityLimit(nt, fCurrentEntity, 0, 1);
return true;
}
}
int index = -1;
boolean vc = false;
+ int length;
while ( true){
//XMLChar.isName(fCurrentEntity.ch[fCurrentEntity.position])) ;
@@ -829,22 +835,7 @@
//check prefix before further read
checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, index - offset);
}
- if (++fCurrentEntity.position == fCurrentEntity.count) {
- int length = fCurrentEntity.position - offset;
- //check localpart before loading more data
- checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length - index - 1);
- invokeListeners(length);
- if (length == fCurrentEntity.fBufferSize) {
- // bad luck we have to resize our buffer
- char[] tmp = new char[fCurrentEntity.fBufferSize * 2];
- System.arraycopy(fCurrentEntity.ch, offset,
- tmp, 0, length);
- fCurrentEntity.ch = tmp;
- fCurrentEntity.fBufferSize *= 2;
- } else {
- System.arraycopy(fCurrentEntity.ch, offset,
- fCurrentEntity.ch, 0, length);
- }
+ if ((length = checkBeforeLoad(fCurrentEntity, offset, index)) > 0) {
if (index != -1) {
index = index - offset;
}
@@ -854,7 +845,7 @@
}
}
}
- int length = fCurrentEntity.position - offset;
+ length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length;
if (length > 0) {
String prefix = null;
@@ -885,6 +876,7 @@
print();
System.out.println(" -> true");
}
+ checkEntityLimit(nt, fCurrentEntity, offset, length);
return true;
}
}
@@ -900,22 +892,104 @@
} // scanQName(QName):boolean
/**
+ * Checks whether the end of the entity buffer has been reached. If yes,
+ * checks against the limit and buffer size before loading more characters.
+ *
+ * @param entity the current entity
+ * @param offset the offset from which the current read was started
+ * @param nameOffset the offset from which the current name starts
+ * @return the length of characters scanned before the end of the buffer,
+ * zero if there is more to be read in the buffer
+ */
+ protected int checkBeforeLoad(Entity.ScannedEntity entity, int offset,
+ int nameOffset) throws IOException {
+ int length = 0;
+ if (++entity.position == entity.count) {
+ length = entity.position - offset;
+ int nameLength = length;
+ if (nameOffset != -1) {
+ nameOffset = nameOffset - offset;
+ nameLength = length - nameOffset;
+ } else {
+ nameOffset = offset;
+ }
+ //check limit before loading more data
+ checkLimit(Limit.MAX_NAME_LIMIT, entity, nameOffset, nameLength);
+ invokeListeners(length);
+ if (length == entity.ch.length) {
+ // bad luck we have to resize our buffer
+ char[] tmp = new char[entity.fBufferSize * 2];
+ System.arraycopy(entity.ch, offset, tmp, 0, length);
+ entity.ch = tmp;
+ entity.fBufferSize *= 2;
+ }
+ else {
+ System.arraycopy(entity.ch, offset, entity.ch, 0, length);
+ }
+ }
+ return length;
+ }
+
+ /**
+ * If the current entity is an Entity reference, check the accumulated size
+ * against the limit.
+ *
+ * @param nt type of name (element, attribute or entity)
+ * @param entity The current entity
+ * @param offset The index of the first byte
+ * @param length The length of the entity scanned
+ */
+ protected void checkEntityLimit(NameType nt, ScannedEntity entity, int offset, int length) {
+ if (entity == null || !entity.isGE) {
+ return;
+ }
+
+ if (nt != NameType.REFERENCE) {
+ checkLimit(Limit.GENERAL_ENTITY_SIZE_LIMIT, entity, offset, length);
+ }
+ if (nt == NameType.ELEMENTSTART || nt == NameType.ATTRIBUTENAME) {
+ checkNodeCount(entity);
+ }
+ }
+
+ /**
+ * If the current entity is an Entity reference, counts the total nodes in
+ * the entity and checks the accumulated value against the limit.
+ *
+ * @param entity The current entity
+ */
+ protected void checkNodeCount(ScannedEntity entity) {
+ if (entity != null && entity.isGE) {
+ checkLimit(Limit.ENTITY_REPLACEMENT_LIMIT, entity, 0, 1);
+ }
+ }
+
+ /**
* Checks whether the value of the specified Limit exceeds its limit
*
- * @param limit The Limit to be checked.
- * @param entity The current entity.
+ * @param limit The Limit to be checked
+ * @param entity The current entity
* @param offset The index of the first byte
- * @param length The length of the entity scanned.
+ * @param length The length of the entity scanned
*/
protected void checkLimit(Limit limit, ScannedEntity entity, int offset, int length) {
- fLimitAnalyzer.addValue(limit, null, length);
+ fLimitAnalyzer.addValue(limit, entity.name, length);
if (fSecurityManager.isOverLimit(limit, fLimitAnalyzer)) {
fSecurityManager.debugPrint(fLimitAnalyzer);
+ Object[] e = (limit == Limit.ENTITY_REPLACEMENT_LIMIT) ?
+ new Object[]{fLimitAnalyzer.getValue(limit),
+ fSecurityManager.getLimit(limit), fSecurityManager.getStateLiteral(limit)} :
+ new Object[]{entity.name, fLimitAnalyzer.getValue(limit),
+ fSecurityManager.getLimit(limit), fSecurityManager.getStateLiteral(limit)};
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, limit.key(),
- new Object[]{new String(entity.ch, offset, length),
- fLimitAnalyzer.getTotalValue(limit),
- fSecurityManager.getLimit(limit),
- fSecurityManager.getStateLiteral(limit)},
+ e, XMLErrorReporter.SEVERITY_FATAL_ERROR);
+ }
+ if (fSecurityManager.isOverLimit(Limit.TOTAL_ENTITY_SIZE_LIMIT, fLimitAnalyzer)) {
+ fSecurityManager.debugPrint(fLimitAnalyzer);
+ fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN, "TotalEntitySizeLimit",
+ new Object[]{fLimitAnalyzer.getTotalValue(Limit.TOTAL_ENTITY_SIZE_LIMIT),
+ fSecurityManager.getLimit(Limit.TOTAL_ENTITY_SIZE_LIMIT),
+ fSecurityManager.getStateLiteral(Limit.TOTAL_ENTITY_SIZE_LIMIT)},
XMLErrorReporter.SEVERITY_FATAL_ERROR);
}
}
@@ -942,7 +1016,7 @@
* @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file.
*/
- public int scanContent(XMLString content) throws IOException {
+ protected int scanContent(XMLString content) throws IOException {
if (DEBUG_BUFFER) {
System.out.print("(scanContent: ");
print();
@@ -963,6 +1037,7 @@
int offset = fCurrentEntity.position;
int c = fCurrentEntity.ch[offset];
int newlines = 0;
+ boolean counted = false;
if (c == '\n' || (c == '\r' && isExternal)) {
if (DEBUG_BUFFER) {
System.out.print("[newline, "+offset+", "+fCurrentEntity.position+": ");
@@ -976,9 +1051,11 @@
fCurrentEntity.lineNumber++;
fCurrentEntity.columnNumber = 1;
if (fCurrentEntity.position == fCurrentEntity.count) {
+ checkEntityLimit(null, fCurrentEntity, offset, newlines);
offset = 0;
fCurrentEntity.position = newlines;
if (load(newlines, false, true)) {
+ counted = true;
break;
}
}
@@ -995,9 +1072,11 @@
fCurrentEntity.lineNumber++;
fCurrentEntity.columnNumber = 1;
if (fCurrentEntity.position == fCurrentEntity.count) {
+ checkEntityLimit(null, fCurrentEntity, offset, newlines);
offset = 0;
fCurrentEntity.position = newlines;
if (load(newlines, false, true)) {
+ counted = true;
break;
}
}
@@ -1011,6 +1090,7 @@
}
int length = fCurrentEntity.position - offset;
if (fCurrentEntity.position == fCurrentEntity.count - 1) {
+ checkEntityLimit(null, fCurrentEntity, offset, length);
//CHANGED: dont replace the value.. append to the buffer. This gives control to the callee
//on buffering the data..
content.setValues(fCurrentEntity.ch, offset, length);
@@ -1038,8 +1118,8 @@
}
int length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length - newlines;
- if (fCurrentEntity.isGE) {
- checkLimit(Limit.TOTAL_ENTITY_SIZE_LIMIT, fCurrentEntity, offset, length);
+ if (!counted) {
+ checkEntityLimit(null, fCurrentEntity, offset, length);
}
//CHANGED: dont replace the value.. append to the buffer. This gives control to the callee
@@ -1086,6 +1166,7 @@
* @param quote The quote character that signifies the end of the
* attribute value data.
* @param content The content structure to fill.
+ * @param isNSURI a flag indicating whether the content is a Namespace URI
*
* @return Returns the next character on the input, if known. This
* value may be -1 but this does <em>note</em> designate
@@ -1094,7 +1175,7 @@
* @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file.
*/
- public int scanLiteral(int quote, XMLString content)
+ protected int scanLiteral(int quote, XMLString content, boolean isNSURI)
throws IOException {
if (DEBUG_BUFFER) {
System.out.print("(scanLiteral, '"+(char)quote+"': ");
@@ -1205,8 +1286,10 @@
}
int length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length - newlines;
- if (fCurrentEntity.isGE) {
- checkLimit(Limit.TOTAL_ENTITY_SIZE_LIMIT, fCurrentEntity, offset, length);
+
+ checkEntityLimit(null, fCurrentEntity, offset, length);
+ if (isNSURI) {
+ checkLimit(Limit.MAX_NAME_LIMIT, fCurrentEntity, offset, length);
}
content.setValues(fCurrentEntity.ch, offset, length);
@@ -1273,7 +1356,7 @@
* @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file.
*/
- public boolean scanData(String delimiter, XMLStringBuffer buffer)
+ protected boolean scanData(String delimiter, XMLStringBuffer buffer)
throws IOException {
boolean done = false;
@@ -1311,6 +1394,7 @@
if (fCurrentEntity.position > fCurrentEntity.count - delimLen) {
// something must be wrong with the input: e.g., file ends in an unterminated comment
int length = fCurrentEntity.count - fCurrentEntity.position;
+ checkEntityLimit(NameType.COMMENT, fCurrentEntity, fCurrentEntity.position, length);
buffer.append (fCurrentEntity.ch, fCurrentEntity.position, length);
fCurrentEntity.columnNumber += fCurrentEntity.count;
fCurrentEntity.baseCharOffset += (fCurrentEntity.position - fCurrentEntity.startPosition);
@@ -1373,6 +1457,7 @@
}
int length = fCurrentEntity.position - offset;
if (fCurrentEntity.position == fCurrentEntity.count - 1) {
+ checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length);
buffer.append(fCurrentEntity.ch, offset, length);
if (DEBUG_BUFFER) {
System.out.print("]newline, "+offset+", "+fCurrentEntity.position+": ");
@@ -1416,12 +1501,14 @@
fCurrentEntity.position--;
int length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length - newlines;
+ checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length);
buffer.append(fCurrentEntity.ch, offset, length);
return true;
}
}
int length = fCurrentEntity.position - offset;
fCurrentEntity.columnNumber += length - newlines;
+ checkEntityLimit(NameType.COMMENT, fCurrentEntity, offset, length);
if (done) {
length -= delimLen;
}
@@ -1445,13 +1532,14 @@
* the specified character.
*
* @param c The character to skip.
+ * @param nt The type of the name (element or attribute)
*
* @return Returns true if the character was skipped.
*
* @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file.
*/
- public boolean skipChar(int c) throws IOException {
+ protected boolean skipChar(int c, NameType nt) throws IOException {
if (DEBUG_BUFFER) {
System.out.print("(skipChar, '"+(char)c+"': ");
print();
@@ -1464,6 +1552,7 @@
}
// skip character
+ int offset = fCurrentEntity.position;
int cc = fCurrentEntity.ch[fCurrentEntity.position];
if (cc == c) {
fCurrentEntity.position++;
@@ -1478,6 +1567,7 @@
print();
System.out.println(" -> true");
}
+ checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
return true;
} else if (c == '\n' && cc == '\r' && isExternal) {
// handle newlines
@@ -1497,6 +1587,7 @@
print();
System.out.println(" -> true");
}
+ checkEntityLimit(nt, fCurrentEntity, offset, fCurrentEntity.position - offset);
return true;
}
@@ -1526,7 +1617,7 @@
*
* @see com.sun.org.apache.xerces.internal.util.XMLChar#isSpace
*/
- public boolean skipSpaces() throws IOException {
+ protected boolean skipSpaces() throws IOException {
if (DEBUG_BUFFER) {
System.out.print("(skipSpaces: ");
print();
@@ -1550,6 +1641,7 @@
// skip spaces
int c = fCurrentEntity.ch[fCurrentEntity.position];
+ int offset = fCurrentEntity.position - 1;
if (XMLChar.isSpace(c)) {
do {
boolean entityChanged = false;
@@ -1579,6 +1671,11 @@
} else {
fCurrentEntity.columnNumber++;
}
+
+ //If this is a general entity, spaces within a start element should be counted
+ checkEntityLimit(null, fCurrentEntity, offset, fCurrentEntity.position - offset);
+ offset = fCurrentEntity.position;
+
// load more characters, if needed
if (!entityChanged){
fCurrentEntity.position++;
@@ -1620,7 +1717,7 @@
/**
- * @param legnth This function checks that following number of characters are available.
+ * @param length This function checks that following number of characters are available.
* to the underlying buffer.
* @return This function returns true if capacity asked is available.
*/
@@ -1629,9 +1726,9 @@
}
/**
- * @param legnth This function checks that following number of characters are available.
+ * @param length This function checks that following number of characters are available.
* to the underlying buffer.
- * @param if the underlying function should change the entity
+ * @param changeEntity a flag to indicate that the underlying function should change the entity
* @return This function returns true if capacity asked is available.
*
*/
@@ -1694,7 +1791,7 @@
* @throws IOException Thrown if i/o error occurs.
* @throws EOFException Thrown on end of file.
*/
- public boolean skipString(String s) throws IOException {
+ protected boolean skipString(String s) throws IOException {
final int length = s.length();
@@ -1714,6 +1811,9 @@
if(afterSkip-- == beforeSkip){
fCurrentEntity.position = fCurrentEntity.position + length ;
fCurrentEntity.columnNumber += length;
+ if (!detectingVersion) {
+ checkEntityLimit(null, fCurrentEntity, beforeSkip, length);
+ }
return true;
}
}
@@ -1722,7 +1822,7 @@
return false;
} // skipString(String):boolean
- public boolean skipString(char [] s) throws IOException {
+ protected boolean skipString(char [] s) throws IOException {
final int length = s.length;
//first make sure that required capacity is avaible
@@ -1741,6 +1841,9 @@
}
fCurrentEntity.position = fCurrentEntity.position + length ;
fCurrentEntity.columnNumber += length;
+ if (!detectingVersion) {
+ checkEntityLimit(null, fCurrentEntity, beforeSkip, length);
+ }
return true;
}
@@ -2138,7 +2241,7 @@
*
* @see com.sun.org.apache.xerces.internal.util.XMLChar#isSpace
*/
- public final boolean skipDeclSpaces() throws IOException {
+ protected final boolean skipDeclSpaces() throws IOException {
if (DEBUG_BUFFER) {
System.out.print("(skipDeclSpaces: ");
//XMLEntityManager.print(fCurrentEntity);
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLNSDocumentScannerImpl.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLNSDocumentScannerImpl.java Thu Jul 21 20:09:19 2016 -0700
@@ -189,9 +189,9 @@
// There are two variables,fNamespaces and fBindNamespaces
//StAX uses XMLNSDocumentScannerImpl so this distinction needs to be maintained
if (fNamespaces) {
- fEntityScanner.scanQName(fElementQName);
+ fEntityScanner.scanQName(fElementQName, NameType.ELEMENTSTART);
} else {
- String name = fEntityScanner.scanName();
+ String name = fEntityScanner.scanName(NameType.ELEMENTSTART);
fElementQName.setValues(null, name, name, null);
}
@@ -404,11 +404,11 @@
if (DEBUG_START_END_ELEMENT) System.out.println(this.getClass().toString() +">>> scanAttribute()");
// name
- fEntityScanner.scanQName(fAttributeQName);
+ fEntityScanner.scanQName(fAttributeQName, NameType.ATTRIBUTE);
// equals
fEntityScanner.skipSpaces();
- if (!fEntityScanner.skipChar('=')) {
+ if (!fEntityScanner.skipChar('=', NameType.ATTRIBUTE)) {
reportFatalError("EqRequiredInAttribute",
new Object[]{fCurrentElement.rawname,fAttributeQName.rawname});
}
@@ -430,23 +430,28 @@
//since scanAttributeValue doesn't use attIndex parameter therefore we
//can safely add the attribute later..
XMLString tmpStr = getString();
- scanAttributeValue(tmpStr, fTempString2,
- fAttributeQName.rawname, attributes,
- attrIndex, isVC, fCurrentElement.rawname);
+
+ /**
+ * Determine whether this is a namespace declaration that will be subject
+ * to the name limit check in the scanAttributeValue operation.
+ * Namespace declaration format: xmlns="..." or xmlns:prefix="..."
+ * Note that prefix:xmlns="..." isn't a namespace.
+ */
+ String localpart = fAttributeQName.localpart;
+ String prefix = fAttributeQName.prefix != null
+ ? fAttributeQName.prefix : XMLSymbols.EMPTY_STRING;
+ boolean isNSDecl = fBindNamespaces & (prefix == XMLSymbols.PREFIX_XMLNS ||
+ prefix == XMLSymbols.EMPTY_STRING && localpart == XMLSymbols.PREFIX_XMLNS);
+
+ scanAttributeValue(tmpStr, fTempString2, fAttributeQName.rawname, attributes,
+ attrIndex, isVC, fCurrentElement.rawname, isNSDecl);
String value = null;
//fTempString.toString();
// record namespace declarations if any.
if (fBindNamespaces) {
-
- String localpart = fAttributeQName.localpart;
- String prefix = fAttributeQName.prefix != null
- ? fAttributeQName.prefix : XMLSymbols.EMPTY_STRING;
- // when it's of form xmlns="..." or xmlns:prefix="...",
- // it's a namespace declaration. but prefix:xmlns="..." isn't.
- if (prefix == XMLSymbols.PREFIX_XMLNS ||
- prefix == XMLSymbols.EMPTY_STRING && localpart == XMLSymbols.PREFIX_XMLNS) {
+ if (isNSDecl) {
//check the length of URI
if (tmpStr.length > fXMLNameLimit) {
fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLScanner.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLScanner.java Thu Jul 21 20:09:19 2016 -0700
@@ -114,6 +114,30 @@
/** Debug attribute normalization. */
protected static final boolean DEBUG_ATTR_NORMALIZATION = false;
+ /**
+ * Type of names
+ */
+ public static enum NameType {
+ ATTRIBUTE("attribute"),
+ ATTRIBUTENAME("attribute name"),
+ COMMENT("comment"),
+ DOCTYPE("doctype"),
+ ELEMENTSTART("startelement"),
+ ELEMENTEND("endelement"),
+ ENTITY("entity"),
+ NOTATION("notation"),
+ PI("pi"),
+ REFERENCE("reference");
+
+ final String literal;
+ NameType(String literal) {
+ this.literal = literal;
+ }
+
+ String literal() {
+ return literal;
+ }
+ }
//xxx: setting the default value as false, as we dont need to calculate this value
//we should have a feature when set to true computes this value
@@ -144,7 +168,7 @@
protected boolean fNotifyCharRefs = false;
/** Internal parser-settings feature */
- protected boolean fParserSettings = true;
+ protected boolean fParserSettings = true;
// properties
@@ -173,13 +197,13 @@
/** event type */
protected XMLEvent fEvent ;
- /** Entity scanner, this alwasy works on last entity that was opened. */
+ /** Entity scanner, this always works on last entity that was opened. */
protected XMLEntityScanner fEntityScanner = null;
/** Entity depth. */
protected int fEntityDepth;
- /** Literal value of the last character refence scanned. */
+ /** Literal value of the last character reference scanned. */
protected String fCharRefLiteral = null;
/** Scanning attribute. */
@@ -547,10 +571,10 @@
}
// end
- if (!fEntityScanner.skipChar('?')) {
+ if (!fEntityScanner.skipChar('?', null)) {
reportFatalError("XMLDeclUnterminated", null);
}
- if (!fEntityScanner.skipChar('>')) {
+ if (!fEntityScanner.skipChar('>', null)) {
reportFatalError("XMLDeclUnterminated", null);
}
@@ -577,7 +601,7 @@
* <strong>Note:</strong> This method uses fStringBuffer2, anything in it
* at the time of calling is lost.
*/
- public String scanPseudoAttribute(boolean scanningTextDecl,
+ protected String scanPseudoAttribute(boolean scanningTextDecl,
XMLString value)
throws IOException, XNIException {
@@ -588,7 +612,7 @@
reportFatalError("PseudoAttrNameExpected", null);
}
fEntityScanner.skipSpaces();
- if (!fEntityScanner.skipChar('=')) {
+ if (!fEntityScanner.skipChar('=', null)) {
reportFatalError(scanningTextDecl ? "EqRequiredInTextDecl"
: "EqRequiredInXMLDecl", new Object[]{name});
}
@@ -598,15 +622,15 @@
reportFatalError(scanningTextDecl ? "QuoteRequiredInTextDecl"
: "QuoteRequiredInXMLDecl" , new Object[]{name});
}
- fEntityScanner.scanChar();
- int c = fEntityScanner.scanLiteral(quote, value);
+ fEntityScanner.scanChar(NameType.ATTRIBUTE);
+ int c = fEntityScanner.scanLiteral(quote, value, false);
if (c != quote) {
fStringBuffer2.clear();
do {
fStringBuffer2.append(value);
if (c != -1) {
if (c == '&' || c == '%' || c == '<' || c == ']') {
- fStringBuffer2.append((char)fEntityScanner.scanChar());
+ fStringBuffer2.append((char)fEntityScanner.scanChar(NameType.ATTRIBUTE));
} else if (XMLChar.isHighSurrogate(c)) {
scanSurrogates(fStringBuffer2);
} else if (isInvalidLiteral(c)) {
@@ -614,15 +638,15 @@
? "InvalidCharInTextDecl" : "InvalidCharInXMLDecl";
reportFatalError(key,
new Object[] {Integer.toString(c, 16)});
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
}
}
- c = fEntityScanner.scanLiteral(quote, value);
+ c = fEntityScanner.scanLiteral(quote, value, false);
} while (c != quote);
fStringBuffer2.append(value);
value.setValues(fStringBuffer2);
}
- if (!fEntityScanner.skipChar(quote)) {
+ if (!fEntityScanner.skipChar(quote, null)) {
reportFatalError(scanningTextDecl ? "CloseQuoteMissingInTextDecl"
: "CloseQuoteMissingInXMLDecl",
new Object[]{name});
@@ -680,7 +704,7 @@
// target
fReportEntity = false;
- String target = fEntityScanner.scanName();
+ String target = fEntityScanner.scanName(NameType.PI);
if (target == null) {
reportFatalError("PITargetRequired", null);
}
@@ -745,7 +769,7 @@
} else if (isInvalidLiteral(c)) {
reportFatalError("InvalidCharInPI",
new Object[]{Integer.toHexString(c)});
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
}
}
} while (fEntityScanner.scanData("?>", data));
@@ -786,11 +810,11 @@
else if (isInvalidLiteral(c)) {
reportFatalError("InvalidCharInComment",
new Object[] { Integer.toHexString(c) });
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(NameType.COMMENT);
}
}
}
- if (!fEntityScanner.skipChar('>')) {
+ if (!fEntityScanner.skipChar('>', NameType.COMMENT)) {
reportFatalError("DashDashInComment", null);
}
@@ -811,15 +835,14 @@
* @param checkEntities true if undeclared entities should be reported as VC violation,
* false if undeclared entities should be reported as WFC violation.
* @param eleName The name of element to which this attribute belongs.
+ * @param isNSURI a flag indicating whether the content is a Namespace URI
*
* <strong>Note:</strong> This method uses fStringBuffer2, anything in it
* at the time of calling is lost.
**/
- protected void scanAttributeValue(XMLString value,
- XMLString nonNormalizedValue,
- String atName,
- XMLAttributes attributes, int attrIndex,
- boolean checkEntities, String eleName)
+ protected void scanAttributeValue(XMLString value, XMLString nonNormalizedValue,
+ String atName, XMLAttributes attributes, int attrIndex, boolean checkEntities,
+ String eleName, boolean isNSURI)
throws IOException, XNIException {
XMLStringBuffer stringBuffer = null;
// quote
@@ -828,10 +851,10 @@
reportFatalError("OpenQuoteExpected", new Object[]{eleName, atName});
}
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(NameType.ATTRIBUTE);
int entityDepth = fEntityDepth;
- int c = fEntityScanner.scanLiteral(quote, value);
+ int c = fEntityScanner.scanLiteral(quote, value, isNSURI);
if (DEBUG_ATTR_NORMALIZATION) {
System.out.println("** scanLiteral -> \""
+ value.toString() + "\"");
@@ -857,11 +880,11 @@
+ stringBuffer.toString() + "\"");
}
if (c == '&') {
- fEntityScanner.skipChar('&');
+ fEntityScanner.skipChar('&', NameType.REFERENCE);
if (entityDepth == fEntityDepth && fNeedNonNormalizedValue ) {
fStringBuffer2.append('&');
}
- if (fEntityScanner.skipChar('#')) {
+ if (fEntityScanner.skipChar('#', NameType.REFERENCE)) {
if (entityDepth == fEntityDepth && fNeedNonNormalizedValue ) {
fStringBuffer2.append('#');
}
@@ -879,53 +902,20 @@
}
}
} else {
- String entityName = fEntityScanner.scanName();
+ String entityName = fEntityScanner.scanName(NameType.ENTITY);
if (entityName == null) {
reportFatalError("NameRequiredInReference", null);
} else if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
fStringBuffer2.append(entityName);
}
- if (!fEntityScanner.skipChar(';')) {
+ if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
reportFatalError("SemicolonRequiredInReference",
new Object []{entityName});
} else if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
fStringBuffer2.append(';');
}
- if (entityName == fAmpSymbol) {
- stringBuffer.append('&');
- if (DEBUG_ATTR_NORMALIZATION) {
- System.out.println("** value5: \""
- + stringBuffer.toString()
- + "\"");
- }
- } else if (entityName == fAposSymbol) {
- stringBuffer.append('\'');
- if (DEBUG_ATTR_NORMALIZATION) {
- System.out.println("** value7: \""
- + stringBuffer.toString()
- + "\"");
- }
- } else if (entityName == fLtSymbol) {
- stringBuffer.append('<');
- if (DEBUG_ATTR_NORMALIZATION) {
- System.out.println("** value9: \""
- + stringBuffer.toString()
- + "\"");
- }
- } else if (entityName == fGtSymbol) {
- stringBuffer.append('>');
- if (DEBUG_ATTR_NORMALIZATION) {
- System.out.println("** valueB: \""
- + stringBuffer.toString()
- + "\"");
- }
- } else if (entityName == fQuotSymbol) {
- stringBuffer.append('"');
- if (DEBUG_ATTR_NORMALIZATION) {
- System.out.println("** valueD: \""
- + stringBuffer.toString()
- + "\"");
- }
+ if (resolveCharacter(entityName, stringBuffer)) {
+ checkEntityLimit(false, fEntityScanner.fCurrentEntity.name, 1);
} else {
if (fEntityStore.isExternalEntity(entityName)) {
reportFatalError("ReferenceToExternalEntity",
@@ -952,12 +942,12 @@
} else if (c == '<') {
reportFatalError("LessthanInAttValue",
new Object[] { eleName, atName });
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
fStringBuffer2.append((char)c);
}
} else if (c == '%' || c == ']') {
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
stringBuffer.append((char)c);
if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
fStringBuffer2.append((char)c);
@@ -967,7 +957,7 @@
+ stringBuffer.toString() + "\"");
}
} else if (c == '\n' || c == '\r') {
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
stringBuffer.append(' ');
if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
fStringBuffer2.append('\n');
@@ -988,12 +978,12 @@
} else if (c != -1 && isInvalidLiteral(c)) {
reportFatalError("InvalidCharInAttValue",
new Object[] {eleName, atName, Integer.toString(c, 16)});
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
fStringBuffer2.append((char)c);
}
}
- c = fEntityScanner.scanLiteral(quote, value);
+ c = fEntityScanner.scanLiteral(quote, value, isNSURI);
if (entityDepth == fEntityDepth && fNeedNonNormalizedValue) {
fStringBuffer2.append(value);
}
@@ -1014,7 +1004,7 @@
nonNormalizedValue.setValues(fStringBuffer2);
// quote
- int cquote = fEntityScanner.scanChar();
+ int cquote = fEntityScanner.scanChar(NameType.ATTRIBUTE);
if (cquote != quote) {
reportFatalError("CloseQuoteExpected", new Object[]{eleName, atName});
}
@@ -1022,6 +1012,39 @@
/**
+ * Resolves character entity references.
+ * @param entityName the name of the entity
+ * @param stringBuffer the current XMLStringBuffer to append the character to.
+ * @return true if resolved, false otherwise
+ */
+ protected boolean resolveCharacter(String entityName, XMLStringBuffer stringBuffer) {
+ /**
+ * entityNames (symbols) are interned. The equals method would do the same,
+ * but I'm leaving it as comparisons by references are common in the impl
+ * and it made it explicit to others who read this code.
+ */
+ if (entityName == fAmpSymbol) {
+ stringBuffer.append('&');
+ return true;
+ } else if (entityName == fAposSymbol) {
+ stringBuffer.append('\'');
+ return true;
+ } else if (entityName == fLtSymbol) {
+ stringBuffer.append('<');
+ return true;
+ } else if (entityName == fGtSymbol) {
+ checkEntityLimit(false, fEntityScanner.fCurrentEntity.name, 1);
+ stringBuffer.append('>');
+ return true;
+ } else if (entityName == fQuotSymbol) {
+ checkEntityLimit(false, fEntityScanner.fCurrentEntity.name, 1);
+ stringBuffer.append('"');
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Scans External ID and return the public and system IDs.
*
* @param identifiers An array of size 2 to return the system id,
@@ -1064,25 +1087,25 @@
}
reportFatalError("QuoteRequiredInSystemID", null);
}
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
XMLString ident = fString;
- if (fEntityScanner.scanLiteral(quote, ident) != quote) {
+ if (fEntityScanner.scanLiteral(quote, ident, false) != quote) {
fStringBuffer.clear();
do {
fStringBuffer.append(ident);
int c = fEntityScanner.peekChar();
if (XMLChar.isMarkup(c) || c == ']') {
- fStringBuffer.append((char)fEntityScanner.scanChar());
+ fStringBuffer.append((char)fEntityScanner.scanChar(null));
} else if (c != -1 && isInvalidLiteral(c)) {
reportFatalError("InvalidCharInSystemID",
new Object[] {Integer.toString(c, 16)});
}
- } while (fEntityScanner.scanLiteral(quote, ident) != quote);
+ } while (fEntityScanner.scanLiteral(quote, ident, false) != quote);
fStringBuffer.append(ident);
ident = fStringBuffer;
}
systemId = ident.toString();
- if (!fEntityScanner.skipChar(quote)) {
+ if (!fEntityScanner.skipChar(quote, null)) {
reportFatalError("SystemIDUnterminated", null);
}
}
@@ -1114,7 +1137,7 @@
*/
protected boolean scanPubidLiteral(XMLString literal)
throws IOException, XNIException {
- int quote = fEntityScanner.scanChar();
+ int quote = fEntityScanner.scanChar(null);
if (quote != '\'' && quote != '"') {
reportFatalError("QuoteRequiredInPublicID", null);
return false;
@@ -1125,7 +1148,7 @@
boolean skipSpace = true;
boolean dataok = true;
while (true) {
- int c = fEntityScanner.scanChar();
+ int c = fEntityScanner.scanChar(null);
if (c == ' ' || c == '\n' || c == '\r') {
if (!skipSpace) {
// take the first whitespace as a space and skip the others
@@ -1241,9 +1264,10 @@
*/
protected int scanCharReferenceValue(XMLStringBuffer buf, XMLStringBuffer buf2)
throws IOException, XNIException {
+ int initLen = buf.length;
// scan hexadecimal value
boolean hex = false;
- if (fEntityScanner.skipChar('x')) {
+ if (fEntityScanner.skipChar('x', NameType.REFERENCE)) {
if (buf2 != null) { buf2.append('x'); }
hex = true;
fStringBuffer3.clear();
@@ -1255,7 +1279,7 @@
(c >= 'A' && c <= 'F');
if (digit) {
if (buf2 != null) { buf2.append((char)c); }
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(NameType.REFERENCE);
fStringBuffer3.append((char)c);
do {
@@ -1265,7 +1289,7 @@
(c >= 'A' && c <= 'F');
if (digit) {
if (buf2 != null) { buf2.append((char)c); }
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(NameType.REFERENCE);
fStringBuffer3.append((char)c);
}
} while (digit);
@@ -1283,7 +1307,7 @@
digit = c >= '0' && c <= '9';
if (digit) {
if (buf2 != null) { buf2.append((char)c); }
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(NameType.REFERENCE);
fStringBuffer3.append((char)c);
do {
@@ -1291,7 +1315,7 @@
digit = c >= '0' && c <= '9';
if (digit) {
if (buf2 != null) { buf2.append((char)c); }
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(NameType.REFERENCE);
fStringBuffer3.append((char)c);
}
} while (digit);
@@ -1301,7 +1325,7 @@
}
// end
- if (!fEntityScanner.skipChar(';')) {
+ if (!fEntityScanner.skipChar(';', NameType.REFERENCE)) {
reportFatalError("SemicolonRequiredInCharRef", null);
}
if (buf2 != null) { buf2.append(';'); }
@@ -1347,6 +1371,9 @@
}
}
+ if (fEntityScanner.fCurrentEntity.isGE) {
+ checkEntityLimit(false, fEntityScanner.fCurrentEntity.name, buf.length - initLen);
+ }
return value;
}
// returns true if the given character is not
@@ -1408,14 +1435,14 @@
protected boolean scanSurrogates(XMLStringBuffer buf)
throws IOException, XNIException {
- int high = fEntityScanner.scanChar();
+ int high = fEntityScanner.scanChar(null);
int low = fEntityScanner.peekChar();
if (!XMLChar.isLowSurrogate(low)) {
reportFatalError("InvalidCharInContent",
new Object[] {Integer.toString(high, 16)});
return false;
}
- fEntityScanner.scanChar();
+ fEntityScanner.scanChar(null);
// convert surrogates to supplemental character
int c = XMLChar.supplemental((char)high, (char)low);
@@ -1478,5 +1505,52 @@
}
}
+ /**
+ * Add the count of the content buffer and check if the accumulated
+ * value exceeds the limit
+ * @param isPEDecl a flag to indicate whether the entity is parameter
+ * @param entityName entity name
+ * @param buffer content buffer
+ */
+ void checkEntityLimit(boolean isPEDecl, String entityName, XMLString buffer) {
+ checkEntityLimit(isPEDecl, entityName, buffer.length);
+ }
+ /**
+ * Add the count and check limit
+ * @param isPEDecl a flag to indicate whether the entity is parameter
+ * @param entityName entity name
+ * @param len length of the buffer
+ */
+ void checkEntityLimit(boolean isPEDecl, String entityName, int len) {
+ if (fLimitAnalyzer == null) {
+ fLimitAnalyzer = fEntityManager.fLimitAnalyzer;
+ }
+ if (isPEDecl) {
+ fLimitAnalyzer.addValue(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, "%" + entityName, len);
+ if (fSecurityManager.isOverLimit(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT, fLimitAnalyzer)) {
+ fSecurityManager.debugPrint(fLimitAnalyzer);
+ reportFatalError("MaxEntitySizeLimit", new Object[]{"%" + entityName,
+ fLimitAnalyzer.getValue(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT),
+ fSecurityManager.getLimit(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT),
+ fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.PARAMETER_ENTITY_SIZE_LIMIT)});
+ }
+ } else {
+ fLimitAnalyzer.addValue(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT, entityName, len);
+ if (fSecurityManager.isOverLimit(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT, fLimitAnalyzer)) {
+ fSecurityManager.debugPrint(fLimitAnalyzer);
+ reportFatalError("MaxEntitySizeLimit", new Object[]{entityName,
+ fLimitAnalyzer.getValue(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT),
+ fSecurityManager.getLimit(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT),
+ fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.GENERAL_ENTITY_SIZE_LIMIT)});
+ }
+ }
+ if (fSecurityManager.isOverLimit(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT, fLimitAnalyzer)) {
+ fSecurityManager.debugPrint(fLimitAnalyzer);
+ reportFatalError("TotalEntitySizeLimit",
+ new Object[]{fLimitAnalyzer.getTotalValue(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT),
+ fSecurityManager.getLimit(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT),
+ fSecurityManager.getStateLiteral(XMLSecurityManager.Limit.TOTAL_ENTITY_SIZE_LIMIT)});
+ }
+ }
} // class XMLScanner
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLVersionDetector.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/XMLVersionDetector.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,62 +1,21 @@
/*
- * reserved comment block
- * DO NOT REMOVE OR ALTER!
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
*/
/*
- * The Apache Software License, Version 1.1
- *
- *
- * Copyright (c) 1999-2003 The Apache Software Foundation.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * 3. The end-user documentation included with the redistribution,
- * if any, must include the following acknowledgment:
- * "This product includes software developed by the
- * Apache Software Foundation (http://www.apache.org/)."
- * Alternately, this acknowledgment may appear in the software itself,
- * if and wherever such third-party acknowledgments normally appear.
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
*
- * 4. The names "Xerces" and "Apache Software Foundation" must
- * not be used to endorse or promote products derived from this
- * software without prior written permission. For written
- * permission, please contact apache@apache.org.
- *
- * 5. Products derived from this software may not be called "Apache",
- * nor may "Apache" appear in their name, without prior written
- * permission of the Apache Software Foundation.
+ * http://www.apache.org/licenses/LICENSE-2.0
*
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation and was
- * originally based on software copyright (c) 2003, International
- * Business Machines, Inc., http://www.apache.org. For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
*/
package com.sun.org.apache.xerces.internal.impl;
@@ -192,40 +151,46 @@
// in the XML declaration.
fEntityManager.setScannerVersion(Constants.XML_VERSION_1_0);
XMLEntityScanner scanner = fEntityManager.getEntityScanner();
+ scanner.detectingVersion = true;
try {
if (!scanner.skipString("<?xml")) {
// definitely not a well-formed 1.1 doc!
+ scanner.detectingVersion = false;
return Constants.XML_VERSION_1_0;
}
if (!scanner.skipDeclSpaces()) {
fixupCurrentEntity(fEntityManager, fExpectedVersionString, 5);
+ scanner.detectingVersion = false;
return Constants.XML_VERSION_1_0;
}
if (!scanner.skipString("version")) {
fixupCurrentEntity(fEntityManager, fExpectedVersionString, 6);
+ scanner.detectingVersion = false;
return Constants.XML_VERSION_1_0;
}
scanner.skipDeclSpaces();
// Check if the next character is '='. If it is then consume it.
if (scanner.peekChar() != '=') {
fixupCurrentEntity(fEntityManager, fExpectedVersionString, 13);
+ scanner.detectingVersion = false;
return Constants.XML_VERSION_1_0;
}
- scanner.scanChar();
+ scanner.scanChar(null);
scanner.skipDeclSpaces();
- int quoteChar = scanner.scanChar();
+ int quoteChar = scanner.scanChar(null);
fExpectedVersionString[14] = (char) quoteChar;
for (int versionPos = 0; versionPos < XML11_VERSION.length; versionPos++) {
- fExpectedVersionString[15 + versionPos] = (char) scanner.scanChar();
+ fExpectedVersionString[15 + versionPos] = (char) scanner.scanChar(null);
}
// REVISIT: should we check whether this equals quoteChar?
- fExpectedVersionString[18] = (char) scanner.scanChar();
+ fExpectedVersionString[18] = (char) scanner.scanChar(null);
fixupCurrentEntity(fEntityManager, fExpectedVersionString, 19);
int matched = 0;
for (; matched < XML11_VERSION.length; matched++) {
if (fExpectedVersionString[15 + matched] != XML11_VERSION[matched])
break;
}
+ scanner.detectingVersion = false;
if (matched == XML11_VERSION.length)
return Constants.XML_VERSION_1_1;
return Constants.XML_VERSION_1_0;
@@ -237,10 +202,9 @@
"PrematureEOF",
null,
XMLErrorReporter.SEVERITY_FATAL_ERROR);
+ scanner.detectingVersion = false;
return Constants.XML_VERSION_1_0;
-
}
-
}
// This method prepends "length" chars from the char array,
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLMessages.properties Thu Jul 21 16:45:56 2016 +0000
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/impl/msg/XMLMessages.properties Thu Jul 21 20:09:19 2016 -0700
@@ -298,7 +298,8 @@
EntityExpansionLimit=JAXP00010001: The parser has encountered more than \"{0}\" entity expansions in this document; this is the limit imposed by the JDK.
ElementAttributeLimit=JAXP00010002: Element \"{0}\" has more than \"{1}\" attributes, \"{1}\" is the limit imposed by the JDK.
MaxEntitySizeLimit=JAXP00010003: The length of entity \"{0}\" is \"{1}\" that exceeds the \"{2}\" limit set by \"{3}\".
- TotalEntitySizeLimit=JAXP00010004: The accumulated size of entities is \"{1}\" that exceeded the \"{2}\" limit set by \"{3}\".
+ TotalEntitySizeLimit=JAXP00010004: The accumulated size of entities is \"{0}\" that exceeded the \"{1}\" limit set by \"{2}\".
MaxXMLNameLimit=JAXP00010005: The length of entity \"{0}\" is \"{1}\" that exceeds the \"{2}\" limit set by \"{3}\".
MaxElementDepthLimit=JAXP00010006: The element \"{0}\" has a depth of \"{1}\" that exceeds the limit \"{2}\" set by \"{3}\".
+ EntityReplacementLimit=JAXP00010007: The total number of nodes in entity references is \"{0}\" that is over the limit \"{1}\" set by \"{2}\".
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/utils/XMLLimitAnalyzer.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/utils/XMLLimitAnalyzer.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,7 +1,7 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
@@ -129,13 +129,15 @@
if (index == Limit.ENTITY_EXPANSION_LIMIT.ordinal() ||
index == Limit.MAX_OCCUR_NODE_LIMIT.ordinal() ||
index == Limit.ELEMENT_ATTRIBUTE_LIMIT.ordinal() ||
- index == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal()
+ index == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal() ||
+ index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal()
) {
totalValue[index] += value;
return;
}
if (index == Limit.MAX_ELEMENT_DEPTH_LIMIT.ordinal() ||
index == Limit.MAX_NAME_LIMIT.ordinal()) {
+ values[index] = value;
totalValue[index] = value;
return;
}
@@ -175,10 +177,13 @@
* @return the value of the property
*/
public int getValue(Limit limit) {
- return values[limit.ordinal()];
+ return getValue(limit.ordinal());
}
public int getValue(int index) {
+ if (index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal()) {
+ return totalValue[index];
+ }
return values[index];
}
/**
@@ -233,6 +238,11 @@
public void reset(Limit limit) {
if (limit.ordinal() == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal()) {
totalValue[limit.ordinal()] = 0;
+ } else if (limit.ordinal() == Limit.GENERAL_ENTITY_SIZE_LIMIT.ordinal()) {
+ names[limit.ordinal()] = null;
+ values[limit.ordinal()] = 0;
+ caches[limit.ordinal()] = null;
+ totalValue[limit.ordinal()] = 0;
}
}
--- a/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/utils/XMLSecurityManager.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jaxp/src/java.xml/share/classes/com/sun/org/apache/xerces/internal/utils/XMLSecurityManager.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2015, 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
@@ -78,7 +78,9 @@
MAX_ELEMENT_DEPTH_LIMIT("MaxElementDepthLimit",
Constants.JDK_MAX_ELEMENT_DEPTH, Constants.SP_MAX_ELEMENT_DEPTH, 0, 0),
MAX_NAME_LIMIT("MaxXMLNameLimit",
- Constants.JDK_XML_NAME_LIMIT, Constants.SP_XML_NAME_LIMIT, 1000, 1000);
+ Constants.JDK_XML_NAME_LIMIT, Constants.SP_XML_NAME_LIMIT, 1000, 1000),
+ ENTITY_REPLACEMENT_LIMIT("EntityReplacementLimit",
+ Constants.JDK_ENTITY_REPLACEMENT_LIMIT, Constants.SP_ENTITY_REPLACEMENT_LIMIT, 0, 3000000);
final String key;
final String apiProperty;
@@ -450,6 +452,7 @@
if (index == Limit.ELEMENT_ATTRIBUTE_LIMIT.ordinal() ||
index == Limit.ENTITY_EXPANSION_LIMIT.ordinal() ||
index == Limit.TOTAL_ENTITY_SIZE_LIMIT.ordinal() ||
+ index == Limit.ENTITY_REPLACEMENT_LIMIT.ordinal() ||
index == Limit.MAX_ELEMENT_DEPTH_LIMIT.ordinal() ||
index == Limit.MAX_NAME_LIMIT.ordinal()
) {
--- a/jaxp/test/javax/xml/jaxp/unittest/transform/XSLTFunctionsTest.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jaxp/test/javax/xml/jaxp/unittest/transform/XSLTFunctionsTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 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
@@ -46,7 +46,7 @@
public class XSLTFunctionsTest {
/**
- * @bug 8062518
+ * @bug 8062518 8153082
* Verifies that a reference to the DTM created by XSLT document function is
* actually read from the DTM by an extension function.
* @param xml Content of xml file to process
--- a/jaxws/.hgtags Thu Jul 21 16:45:56 2016 +0000
+++ b/jaxws/.hgtags Thu Jul 21 20:09:19 2016 -0700
@@ -373,3 +373,4 @@
5b0570e3db29f6b8c80a4beac70d51284507b203 jdk-9+125
264a44128cd6286e598d5a849ceeb613c06269d0 jdk-9+126
06d706c70634775418dc79a2671780ba1c624fd2 jdk-9+127
+fe4e11bd2423635dc0f5f5cb9a64eb2f2cce7f4c jdk-9+128
--- a/jdk/.hgtags Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/.hgtags Thu Jul 21 20:09:19 2016 -0700
@@ -370,3 +370,4 @@
073ab1d4edf5590cf1af7b6d819350c14e425c1a jdk-9+125
6fda66a5bdf2da8994032b9da2078a4137f4d954 jdk-9+126
7a97b89ba83077ca62e4aa5a05437adc8f315343 jdk-9+127
+9446c534f0222b4eecfd9d9e25ab37c4fd4400a5 jdk-9+128
--- a/jdk/src/java.base/macosx/classes/java/lang/ClassLoaderHelper.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/macosx/classes/java/lang/ClassLoaderHelper.java Thu Jul 21 20:09:19 2016 -0700
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.lang;
import java.io.File;
--- a/jdk/src/java.base/macosx/native/libjava/java_props_macosx.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/macosx/native/libjava/java_props_macosx.c Thu Jul 21 20:09:19 2016 -0700
@@ -177,8 +177,14 @@
OSVerStruct (*procInfoFn)(id rec, SEL sel) = (OSVerStruct(*)(id, SEL))objc_msgSend_stret;
OSVerStruct osVer = procInfoFn([NSProcessInfo processInfo],
@selector(operatingSystemVersion));
- NSString *nsVerStr = [NSString stringWithFormat:@"%ld.%ld.%ld",
- (long)osVer.majorVersion, (long)osVer.minorVersion, (long)osVer.patchVersion];
+ NSString *nsVerStr;
+ if (osVer.patchVersion == 0) { // Omit trailing ".0"
+ nsVerStr = [NSString stringWithFormat:@"%ld.%ld",
+ (long)osVer.majorVersion, (long)osVer.minorVersion];
+ } else {
+ nsVerStr = [NSString stringWithFormat:@"%ld.%ld.%ld",
+ (long)osVer.majorVersion, (long)osVer.minorVersion, (long)osVer.patchVersion];
+ }
// Copy out the char*
osVersionCStr = strdup([nsVerStr UTF8String]);
}
--- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/AESCipher.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/AESCipher.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -172,6 +172,11 @@
*/
private final int fixedKeySize; // in bytes, -1 if no restriction
+ /*
+ * needed to enforce ISE thrown when updateAAD is called after update for GCM mode.
+ */
+ private boolean updateCalled;
+
/**
* Creates an instance of AES cipher with default ECB mode and
* PKCS5Padding.
@@ -304,6 +309,7 @@
protected void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException {
checkKeySize(key, fixedKeySize);
+ updateCalled = false;
core.init(opmode, key, random);
}
@@ -336,6 +342,7 @@
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
checkKeySize(key, fixedKeySize);
+ updateCalled = false;
core.init(opmode, key, params, random);
}
@@ -344,6 +351,7 @@
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
checkKeySize(key, fixedKeySize);
+ updateCalled = false;
core.init(opmode, key, params, random);
}
@@ -368,6 +376,7 @@
*/
protected byte[] engineUpdate(byte[] input, int inputOffset,
int inputLen) {
+ updateCalled = true;
return core.update(input, inputOffset, inputLen);
}
@@ -397,6 +406,7 @@
protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws ShortBufferException {
+ updateCalled = true;
return core.update(input, inputOffset, inputLen, output,
outputOffset);
}
@@ -433,7 +443,9 @@
*/
protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
throws IllegalBlockSizeException, BadPaddingException {
- return core.doFinal(input, inputOffset, inputLen);
+ byte[] out = core.doFinal(input, inputOffset, inputLen);
+ updateCalled = false;
+ return out;
}
/**
@@ -476,8 +488,10 @@
byte[] output, int outputOffset)
throws IllegalBlockSizeException, ShortBufferException,
BadPaddingException {
- return core.doFinal(input, inputOffset, inputLen, output,
- outputOffset);
+ int outLen = core.doFinal(input, inputOffset, inputLen, output,
+ outputOffset);
+ updateCalled = false;
+ return outLen;
}
/**
@@ -574,6 +588,9 @@
*/
@Override
protected void engineUpdateAAD(byte[] src, int offset, int len) {
+ if (core.getMode() == CipherCore.GCM_MODE && updateCalled) {
+ throw new IllegalStateException("AAD must be supplied before encryption/decryption starts");
+ }
core.updateAAD(src, offset, len);
}
@@ -606,6 +623,9 @@
*/
@Override
protected void engineUpdateAAD(ByteBuffer src) {
+ if (core.getMode() == CipherCore.GCM_MODE && updateCalled) {
+ throw new IllegalStateException("AAD must be supplied before encryption/decryption starts");
+ }
if (src != null) {
int aadLen = src.limit() - src.position();
if (aadLen != 0) {
--- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/CipherCore.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/CipherCore.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -124,7 +124,7 @@
private static final int PCBC_MODE = 4;
private static final int CTR_MODE = 5;
private static final int CTS_MODE = 6;
- private static final int GCM_MODE = 7;
+ static final int GCM_MODE = 7;
/*
* variables used for performing the GCM (key+iv) uniqueness check.
@@ -196,7 +196,7 @@
cipher = new CounterMode(rawImpl);
unitBytes = 1;
padding = null;
- } else if (modeUpperCase.startsWith("GCM")) {
+ } else if (modeUpperCase.equals("GCM")) {
// can only be used for block ciphers w/ 128-bit block size
if (blockSize != 16) {
throw new NoSuchAlgorithmException
@@ -223,6 +223,15 @@
}
}
+ /**
+ * Returns the mode of this cipher.
+ *
+ * @return the parsed cipher mode
+ */
+ int getMode() {
+ return cipherMode;
+ }
+
private static int getNumOfUnit(String mode, int offset, int blockSize)
throws NoSuchAlgorithmException {
int result = blockSize; // use blockSize as default value
--- a/jdk/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -49,6 +49,16 @@
static int DEFAULT_TAG_LEN = AES_BLOCK_SIZE;
static int DEFAULT_IV_LEN = 12; // in bytes
+ // In NIST SP 800-38D, GCM input size is limited to be no longer
+ // than (2^36 - 32) bytes. Otherwise, the counter will wrap
+ // around and lead to a leak of plaintext.
+ // However, given the current GCM spec requirement that recovered
+ // text can only be returned after successful tag verification,
+ // we are bound by limiting the data size to the size limit of
+ // java byte array, e.g. Integer.MAX_VALUE, since all data
+ // can only be returned by the doFinal(...) call.
+ private static final int MAX_BUF_SIZE = Integer.MAX_VALUE;
+
// buffer for AAD data; if null, meaning update has been called
private ByteArrayOutputStream aadBuffer = new ByteArrayOutputStream();
private int sizeOfAAD = 0;
@@ -89,9 +99,13 @@
}
}
- // ivLen in bits
- private static byte[] getLengthBlock(int ivLen) {
+ private static byte[] getLengthBlock(int ivLenInBytes) {
+ long ivLen = ((long)ivLenInBytes) << 3;
byte[] out = new byte[AES_BLOCK_SIZE];
+ out[8] = (byte)(ivLen >>> 56);
+ out[9] = (byte)(ivLen >>> 48);
+ out[10] = (byte)(ivLen >>> 40);
+ out[11] = (byte)(ivLen >>> 32);
out[12] = (byte)(ivLen >>> 24);
out[13] = (byte)(ivLen >>> 16);
out[14] = (byte)(ivLen >>> 8);
@@ -99,13 +113,22 @@
return out;
}
- // aLen and cLen both in bits
- private static byte[] getLengthBlock(int aLen, int cLen) {
+ private static byte[] getLengthBlock(int aLenInBytes, int cLenInBytes) {
+ long aLen = ((long)aLenInBytes) << 3;
+ long cLen = ((long)cLenInBytes) << 3;
byte[] out = new byte[AES_BLOCK_SIZE];
+ out[0] = (byte)(aLen >>> 56);
+ out[1] = (byte)(aLen >>> 48);
+ out[2] = (byte)(aLen >>> 40);
+ out[3] = (byte)(aLen >>> 32);
out[4] = (byte)(aLen >>> 24);
out[5] = (byte)(aLen >>> 16);
out[6] = (byte)(aLen >>> 8);
out[7] = (byte)aLen;
+ out[8] = (byte)(cLen >>> 56);
+ out[9] = (byte)(cLen >>> 48);
+ out[10] = (byte)(cLen >>> 40);
+ out[11] = (byte)(cLen >>> 32);
out[12] = (byte)(cLen >>> 24);
out[13] = (byte)(cLen >>> 16);
out[14] = (byte)(cLen >>> 8);
@@ -142,13 +165,20 @@
} else {
g.update(iv);
}
- byte[] lengthBlock = getLengthBlock(iv.length*8);
+ byte[] lengthBlock = getLengthBlock(iv.length);
g.update(lengthBlock);
j0 = g.digest();
}
return j0;
}
+ private static void checkDataLength(int processed, int len) {
+ if (processed > MAX_BUF_SIZE - len) {
+ throw new ProviderException("SunJCE provider only supports " +
+ "input size up to " + MAX_BUF_SIZE + " bytes");
+ }
+ }
+
GaloisCounterMode(SymmetricCipher embeddedCipher) {
super(embeddedCipher);
aadBuffer = new ByteArrayOutputStream();
@@ -319,20 +349,22 @@
// Feed the AAD data to GHASH, pad if necessary
void processAAD() {
- if (aadBuffer != null && aadBuffer.size() > 0) {
- byte[] aad = aadBuffer.toByteArray();
- sizeOfAAD = aad.length;
- aadBuffer = null;
+ if (aadBuffer != null) {
+ if (aadBuffer.size() > 0) {
+ byte[] aad = aadBuffer.toByteArray();
+ sizeOfAAD = aad.length;
- int lastLen = aad.length % AES_BLOCK_SIZE;
- if (lastLen != 0) {
- ghashAllToS.update(aad, 0, aad.length - lastLen);
- byte[] padded = expandToOneBlock(aad, aad.length - lastLen,
- lastLen);
- ghashAllToS.update(padded);
- } else {
- ghashAllToS.update(aad);
+ int lastLen = aad.length % AES_BLOCK_SIZE;
+ if (lastLen != 0) {
+ ghashAllToS.update(aad, 0, aad.length - lastLen);
+ byte[] padded = expandToOneBlock(aad, aad.length - lastLen,
+ lastLen);
+ ghashAllToS.update(padded);
+ } else {
+ ghashAllToS.update(aad);
+ }
}
+ aadBuffer = null;
}
}
@@ -384,6 +416,9 @@
if ((len % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering");
}
+
+ checkDataLength(processed, len);
+
processAAD();
if (len > 0) {
gctrPAndC.update(in, inOfs, len, out, outOfs);
@@ -405,17 +440,23 @@
*/
int encryptFinal(byte[] in, int inOfs, int len, byte[] out, int outOfs)
throws IllegalBlockSizeException, ShortBufferException {
+ if (len > MAX_BUF_SIZE - tagLenBytes) {
+ throw new ShortBufferException
+ ("Can't fit both data and tag into one buffer");
+ }
if (out.length - outOfs < (len + tagLenBytes)) {
throw new ShortBufferException("Output buffer too small");
}
+ checkDataLength(processed, len);
+
processAAD();
if (len > 0) {
doLastBlock(in, inOfs, len, out, outOfs, true);
}
byte[] lengthBlock =
- getLengthBlock(sizeOfAAD*8, processed*8);
+ getLengthBlock(sizeOfAAD, processed);
ghashAllToS.update(lengthBlock);
byte[] s = ghashAllToS.digest();
byte[] sOut = new byte[s.length];
@@ -447,6 +488,9 @@
if ((len % blockSize) != 0) {
throw new ProviderException("Internal error in input buffering");
}
+
+ checkDataLength(ibuffer.size(), len);
+
processAAD();
if (len > 0) {
@@ -481,10 +525,21 @@
if (len < tagLenBytes) {
throw new AEADBadTagException("Input too short - need tag");
}
+ // do this check here can also catch the potential integer overflow
+ // scenario for the subsequent output buffer capacity check.
+ checkDataLength(ibuffer.size(), (len - tagLenBytes));
+
if (out.length - outOfs < ((ibuffer.size() + len) - tagLenBytes)) {
throw new ShortBufferException("Output buffer too small");
}
+
processAAD();
+
+ // get the trailing tag bytes from 'in'
+ byte[] tag = new byte[tagLenBytes];
+ System.arraycopy(in, inOfs + len - tagLenBytes, tag, 0, tagLenBytes);
+ len -= tagLenBytes;
+
if (len != 0) {
ibuffer.write(in, inOfs, len);
}
@@ -495,17 +550,12 @@
len = in.length;
ibuffer.reset();
- byte[] tag = new byte[tagLenBytes];
- // get the trailing tag bytes from 'in'
- System.arraycopy(in, len - tagLenBytes, tag, 0, tagLenBytes);
- len -= tagLenBytes;
-
if (len > 0) {
doLastBlock(in, inOfs, len, out, outOfs, false);
}
byte[] lengthBlock =
- getLengthBlock(sizeOfAAD*8, processed*8);
+ getLengthBlock(sizeOfAAD, processed);
ghashAllToS.update(lengthBlock);
byte[] s = ghashAllToS.digest();
--- a/jdk/src/java.base/share/classes/com/sun/security/ntlm/Server.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/com/sun/security/ntlm/Server.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/classes/java/lang/Class.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/Class.java Thu Jul 21 20:09:19 2016 -0700
@@ -238,15 +238,11 @@
TypeVariable<?>[] typeparms = component.getTypeParameters();
if (typeparms.length > 0) {
- boolean first = true;
- sb.append('<');
+ StringJoiner sj = new StringJoiner(",", "<", ">");
for(TypeVariable<?> typeparm: typeparms) {
- if (!first)
- sb.append(',');
- sb.append(typeparm.getTypeName());
- first = false;
+ sj.add(typeparm.getTypeName());
}
- sb.append('>');
+ sb.append(sj.toString());
}
for (int i = 0; i < arrayDepth; i++)
--- a/jdk/src/java.base/share/classes/java/lang/Runtime.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/Runtime.java Thu Jul 21 20:09:19 2016 -0700
@@ -945,7 +945,7 @@
}
/**
- * A representation of a version string for an implemenation of the
+ * A representation of a version string for an implementation of the
* Java SE Platform. A version string contains a version number
* optionally followed by pre-release and build information.
*
@@ -1058,10 +1058,10 @@
* <p> When comparing two version strings, the value of {@code $OPT}, if
* present, may or may not be significant depending on the chosen
* comparison method. The comparison methods {@link #compareTo(Version)
- * compareTo()} and {@link #compareToIgnoreOpt(Version)
- * compareToIgnoreOpt()} should be used consistently with the
+ * compareTo()} and {@link #compareToIgnoreOptional(Version)
+ * compareToIgnoreOptional()} should be used consistently with the
* corresponding methods {@link #equals(Object) equals()} and {@link
- * #equalsIgnoreOpt(Object) equalsIgnoreOpt()}. </p>
+ * #equalsIgnoreOptional(Object) equalsIgnoreOptional()}. </p>
*
* <p> A <em>short version string</em>, {@code $SVSTR}, often useful in
* less formal contexts, is a version number optionally followed by a
@@ -1249,7 +1249,7 @@
* @throws NullPointerException
* If the given object is {@code null}
*/
- public int compareToIgnoreOpt(Version ob) {
+ public int compareToIgnoreOptional(Version ob) {
return compare(ob, true);
}
@@ -1270,7 +1270,7 @@
return ret;
if (!ignoreOpt)
- return compareOpt(ob);
+ return compareOptional(ob);
return 0;
}
@@ -1325,7 +1325,7 @@
return 0;
}
- private int compareOpt(Version ob) {
+ private int compareOptional(Version ob) {
Optional<String> oOpt = ob.optional();
if (!optional.isPresent()) {
if (oOpt.isPresent())
@@ -1384,7 +1384,7 @@
*/
@Override
public boolean equals(Object ob) {
- boolean ret = equalsIgnoreOpt(ob);
+ boolean ret = equalsIgnoreOptional(ob);
if (!ret)
return false;
@@ -1407,7 +1407,7 @@
* ignoring the optinal build information
*
*/
- public boolean equalsIgnoreOpt(Object ob) {
+ public boolean equalsIgnoreOptional(Object ob) {
if (this == ob)
return true;
if (!(ob instanceof Version))
--- a/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java Thu Jul 21 20:09:19 2016 -0700
@@ -155,7 +155,7 @@
private static LambdaForm preparedLambdaForm(MemberName m) {
assert(m.isInvocable()) : m; // call preparedFieldLambdaForm instead
MethodType mtype = m.getInvocationType().basicType();
- assert(!m.isMethodHandleInvoke() || "invokeBasic".equals(m.getName())) : m;
+ assert(!m.isMethodHandleInvoke()) : m;
int which;
switch (m.getReferenceKind()) {
case REF_invokeVirtual: which = LF_INVVIRTUAL; break;
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java Thu Jul 21 20:09:19 2016 -0700
@@ -1049,7 +1049,7 @@
this.member = member;
this.resolvedHandle = resolvedHandle;
// The following assert is almost always correct, but will fail for corner cases, such as PrivateInvokeTest.
- //assert(!isInvokeBasic());
+ //assert(!isInvokeBasic(member));
}
NamedFunction(MethodType basicInvokerType) {
assert(basicInvokerType == basicInvokerType.basicType()) : basicInvokerType;
@@ -1060,13 +1060,13 @@
// necessary to pass BigArityTest
this.member = Invokers.invokeBasicMethod(basicInvokerType);
}
- assert(isInvokeBasic());
+ assert(isInvokeBasic(member));
}
- private boolean isInvokeBasic() {
+ private static boolean isInvokeBasic(MemberName member) {
return member != null &&
- member.isMethodHandleInvoke() &&
- "invokeBasic".equals(member.getName());
+ member.getDeclaringClass() == MethodHandle.class &&
+ "invokeBasic".equals(member.getName());
}
// The next 2 constructors are used to break circular dependencies on MH.invokeStatic, etc.
@@ -1204,7 +1204,7 @@
assert(mh.type().basicType() == MethodType.genericMethodType(arity).changeReturnType(rtype))
: Arrays.asList(mh, rtype, arity);
MemberName member = mh.internalMemberName();
- if (member != null && member.getName().equals("invokeBasic") && member.isMethodHandleInvoke()) {
+ if (isInvokeBasic(member)) {
assert(arity > 0);
assert(a[0] instanceof MethodHandle);
MethodHandle mh2 = (MethodHandle) a[0];
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java Thu Jul 21 20:09:19 2016 -0700
@@ -346,7 +346,6 @@
}
/** Utility method to query if this member is a method handle invocation (invoke or invokeExact).
- * Also returns true for the non-public MH.invokeBasic.
*/
public boolean isMethodHandleInvoke() {
final int bits = MH_INVOKE_MODS &~ Modifier.PUBLIC;
@@ -361,7 +360,6 @@
switch (name) {
case "invoke":
case "invokeExact":
- case "invokeBasic": // internal sig-poly method
return true;
default:
return false;
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java Thu Jul 21 20:09:19 2016 -0700
@@ -951,8 +951,6 @@
return invoker(type);
if ("invokeExact".equals(name))
return exactInvoker(type);
- if ("invokeBasic".equals(name))
- return basicInvoker(type);
assert(!MemberName.isMethodHandleInvokeName(name));
return null;
}
@@ -3268,6 +3266,16 @@
*/
public static
MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes) {
+ return dropArguments0(target, pos, copyTypes(valueTypes));
+ }
+
+ private static List<Class<?>> copyTypes(List<Class<?>> types) {
+ Object[] a = types.toArray();
+ return Arrays.asList(Arrays.copyOf(a, a.length, Class[].class));
+ }
+
+ private static
+ MethodHandle dropArguments0(MethodHandle target, int pos, List<Class<?>> valueTypes) {
MethodType oldType = target.type(); // get NPE
int dropped = dropArgumentChecks(oldType, pos, valueTypes);
MethodType newType = oldType.insertParameterTypes(pos, valueTypes);
@@ -3348,6 +3356,7 @@
// private version which allows caller some freedom with error handling
private static MethodHandle dropArgumentsToMatch(MethodHandle target, int skip, List<Class<?>> newTypes, int pos,
boolean nullOnFailure) {
+ newTypes = copyTypes(newTypes);
List<Class<?>> oldTypes = target.type().parameterList();
int match = oldTypes.size();
if (skip != 0) {
@@ -3379,11 +3388,11 @@
// target: ( S*[skip], M*[match] )
MethodHandle adapter = target;
if (add > 0) {
- adapter = dropArguments(adapter, skip+ match, addTypes);
+ adapter = dropArguments0(adapter, skip+ match, addTypes);
}
// adapter: (S*[skip], M*[match], A*[add] )
if (pos > 0) {
- adapter = dropArguments(adapter, skip, newTypes.subList(0, pos));
+ adapter = dropArguments0(adapter, skip, newTypes.subList(0, pos));
}
// adapter: (S*[skip], P*[pos], M*[match], A*[add] )
return adapter;
@@ -3787,7 +3796,7 @@
int filterValues = filterType.parameterCount();
if (filterValues == 0
? (rtype != void.class)
- : (rtype != filterType.parameterType(0)))
+ : (rtype != filterType.parameterType(0) || filterValues != 1))
throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
}
@@ -4290,7 +4299,7 @@
step.set(i, dropArgumentsToMatch(identityOrVoid(t), 0, commonParameterSequence, i));
}
if (pred.get(i) == null) {
- pred.set(i, dropArguments(constant(boolean.class, true), 0, commonParameterSequence));
+ pred.set(i, dropArguments0(constant(boolean.class, true), 0, commonParameterSequence));
}
if (fini.get(i) == null) {
fini.set(i, empty(methodType(t, commonParameterSequence)));
@@ -4315,7 +4324,7 @@
return hs.stream().map(h -> {
int pc = h.type().parameterCount();
int tpsize = targetParams.size();
- return pc < tpsize ? dropArguments(h, pc, targetParams.subList(pc, tpsize)) : h;
+ return pc < tpsize ? dropArguments0(h, pc, targetParams.subList(pc, tpsize)) : h;
}).collect(Collectors.toList());
}
--- a/jdk/src/java.base/share/classes/java/lang/module/ModulePath.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/module/ModulePath.java Thu Jul 21 20:09:19 2016 -0700
@@ -52,6 +52,7 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -420,7 +421,7 @@
// scan the entries in the JAR file to locate the .class and service
// configuration file
Map<Boolean, Set<String>> map =
- jf.stream()
+ versionedStream(jf)
.map(JarEntry::getName)
.filter(s -> (s.endsWith(".class") ^ s.startsWith(SERVICES_PREFIX)))
.collect(Collectors.partitioningBy(s -> s.endsWith(".class"),
@@ -503,8 +504,21 @@
return mn;
}
+ private Stream<JarEntry> versionedStream(JarFile jf) {
+ if (jf.isMultiRelease()) {
+ // a stream of JarEntries whose names are base names and whose
+ // contents are from the corresponding versioned entries in
+ // a multi-release jar file
+ return jf.stream().map(JarEntry::getName)
+ .filter(name -> !name.startsWith("META-INF/versions/"))
+ .map(jf::getJarEntry);
+ } else {
+ return jf.stream();
+ }
+ }
+
private Set<String> jarPackages(JarFile jf) {
- return jf.stream()
+ return versionedStream(jf)
.filter(e -> e.getName().endsWith(".class"))
.map(e -> toPackageName(e.getName()))
.filter(pkg -> pkg.length() > 0) // module-info
--- a/jdk/src/java.base/share/classes/java/lang/reflect/Executable.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/reflect/Executable.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,6 +28,7 @@
import java.lang.annotation.*;
import java.util.Map;
import java.util.Objects;
+import java.util.StringJoiner;
import jdk.internal.misc.SharedSecrets;
import sun.reflect.annotation.AnnotationParser;
@@ -86,15 +87,6 @@
getDeclaringClass());
}
- void separateWithCommas(Class<?>[] types, StringBuilder sb) {
- for (int j = 0; j < types.length; j++) {
- sb.append(types[j].getTypeName());
- if (j < (types.length - 1))
- sb.append(",");
- }
-
- }
-
void printModifiersIfNonzero(StringBuilder sb, int mask, boolean isDefault) {
int mod = getModifiers() & mask;
@@ -121,13 +113,20 @@
printModifiersIfNonzero(sb, modifierMask, isDefault);
specificToStringHeader(sb);
-
sb.append('(');
- separateWithCommas(parameterTypes, sb);
+ StringJoiner sj = new StringJoiner(",");
+ for (Class<?> parameterType : parameterTypes) {
+ sj.add(parameterType.getTypeName());
+ }
+ sb.append(sj.toString());
sb.append(')');
+
if (exceptionTypes.length > 0) {
- sb.append(" throws ");
- separateWithCommas(exceptionTypes, sb);
+ StringJoiner joiner = new StringJoiner(",", "throws ", "");
+ for (Class<?> exceptionType : exceptionTypes) {
+ joiner.add(exceptionType.getTypeName());
+ }
+ sb.append(joiner.toString());
}
return sb.toString();
} catch (Exception e) {
@@ -149,42 +148,34 @@
TypeVariable<?>[] typeparms = getTypeParameters();
if (typeparms.length > 0) {
- boolean first = true;
- sb.append('<');
+ StringJoiner sj = new StringJoiner(",", "<", "> ");
for(TypeVariable<?> typeparm: typeparms) {
- if (!first)
- sb.append(',');
- // Class objects can't occur here; no need to test
- // and call Class.getName().
- sb.append(typeparm.toString());
- first = false;
+ sj.add(typeparm.getTypeName());
}
- sb.append("> ");
+ sb.append(sj.toString());
}
specificToGenericStringHeader(sb);
sb.append('(');
+ StringJoiner sj = new StringJoiner(",");
Type[] params = getGenericParameterTypes();
for (int j = 0; j < params.length; j++) {
String param = params[j].getTypeName();
if (isVarArgs() && (j == params.length - 1)) // replace T[] with T...
param = param.replaceFirst("\\[\\]$", "...");
- sb.append(param);
- if (j < (params.length - 1))
- sb.append(',');
+ sj.add(param);
}
+ sb.append(sj.toString());
sb.append(')');
- Type[] exceptions = getGenericExceptionTypes();
- if (exceptions.length > 0) {
- sb.append(" throws ");
- for (int k = 0; k < exceptions.length; k++) {
- sb.append((exceptions[k] instanceof Class)?
- ((Class)exceptions[k]).getName():
- exceptions[k].toString());
- if (k < (exceptions.length - 1))
- sb.append(',');
+
+ Type[] exceptionTypes = getGenericExceptionTypes();
+ if (exceptionTypes.length > 0) {
+ StringJoiner joiner = new StringJoiner(",", " throws ", "");
+ for (Type exceptionType : exceptionTypes) {
+ joiner.add(exceptionType.getTypeName());
}
+ sb.append(joiner.toString());
}
return sb.toString();
} catch (Exception e) {
--- a/jdk/src/java.base/share/classes/java/net/URLPermission.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/net/URLPermission.java Thu Jul 21 20:09:19 2016 -0700
@@ -461,11 +461,10 @@
}
private String actions() {
- String b = String.join(",", methods);
- if (!requestHeaders.isEmpty()) {
- b += ":" + String.join(",", requestHeaders);
- }
- return b;
+ // The colon separator is optional when the request headers list is
+ // empty.This implementation chooses to include it even when the request
+ // headers list is empty.
+ return String.join(",", methods) + ":" + String.join(",", requestHeaders);
}
/**
--- a/jdk/src/java.base/share/classes/java/security/ProtectionDomain.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/security/ProtectionDomain.java Thu Jul 21 20:09:19 2016 -0700
@@ -132,7 +132,7 @@
/* the PermissionCollection is static (pre 1.4 constructor)
or dynamic (via a policy refresh) */
- private boolean staticPermissions;
+ private final boolean staticPermissions;
/*
* An object used as a key when the ProtectionDomain is stored in a Map.
@@ -143,8 +143,12 @@
* Creates a new ProtectionDomain with the given CodeSource and
* Permissions. If the permissions object is not null, then
* {@code setReadOnly()} will be called on the passed in
- * Permissions object. The only permissions granted to this domain
- * are the ones specified; the current Policy will not be consulted.
+ * Permissions object.
+ * <p>
+ * The permissions granted to this domain are static, i.e.
+ * invoking the {@link #staticPermissionsOnly()} method returns true.
+ * They contain only the ones passed to this constructor and
+ * the current Policy will not be consulted.
*
* @param codesource the codesource associated with this domain
* @param permissions the permissions granted to this domain
@@ -170,9 +174,11 @@
* Permissions, ClassLoader and array of Principals. If the
* permissions object is not null, then {@code setReadOnly()}
* will be called on the passed in Permissions object.
- * The permissions granted to this domain are dynamic; they include
- * both the static permissions passed to this constructor, and any
- * permissions granted to this domain by the current Policy at the
+ * <p>
+ * The permissions granted to this domain are dynamic, i.e.
+ * invoking the {@link #staticPermissionsOnly()} method returns false.
+ * They include both the static permissions passed to this constructor,
+ * and any permissions granted to this domain by the current Policy at the
* time a permission is checked.
* <p>
* This constructor is typically used by
@@ -256,6 +262,19 @@
}
/**
+ * Returns true if this domain contains only static permissions
+ * and does not check the current {@code Policy} at the time of
+ * permission checking.
+ *
+ * @return true if this domain contains only static permissions.
+ *
+ * @since 9
+ */
+ public final boolean staticPermissionsOnly() {
+ return this.staticPermissions;
+ }
+
+ /**
* Check and see if this ProtectionDomain implies the permissions
* expressed in the Permission object.
* <p>
@@ -263,25 +282,19 @@
* ProtectionDomain was constructed with a static set of permissions
* or it was bound to a dynamically mapped set of permissions.
* <p>
- * If the ProtectionDomain was constructed to a
- * {@link #ProtectionDomain(CodeSource, PermissionCollection)
- * statically bound} PermissionCollection then the permission will
- * only be checked against the PermissionCollection supplied at
- * construction.
+ * If the {@link #staticPermissionsOnly()} method returns
+ * true, then the permission will only be checked against the
+ * PermissionCollection supplied at construction.
* <p>
- * However, if the ProtectionDomain was constructed with
- * the constructor variant which supports
- * {@link #ProtectionDomain(CodeSource, PermissionCollection,
- * ClassLoader, java.security.Principal[]) dynamically binding}
- * permissions, then the permission will be checked against the
- * combination of the PermissionCollection supplied at construction and
+ * Otherwise, the permission will be checked against the combination
+ * of the PermissionCollection supplied at construction and
* the current Policy binding.
*
- * @param permission the Permission object to check.
+ * @param perm the Permission object to check.
*
- * @return true if "permission" is implicit to this ProtectionDomain.
+ * @return true if {@code perm} is implied by this ProtectionDomain.
*/
- public boolean implies(Permission permission) {
+ public boolean implies(Permission perm) {
if (hasAllPerm) {
// internal permission collection already has AllPermission -
@@ -290,10 +303,10 @@
}
if (!staticPermissions &&
- Policy.getPolicyNoCheck().implies(this, permission))
+ Policy.getPolicyNoCheck().implies(this, perm))
return true;
if (permissions != null)
- return permissions.implies(permission);
+ return permissions.implies(perm);
return false;
}
--- a/jdk/src/java.base/share/classes/java/security/Provider.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/security/Provider.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved
+ * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
--- a/jdk/src/java.base/share/classes/java/util/Queue.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/Queue.java Thu Jul 21 20:09:19 2016 -0700
@@ -124,7 +124,6 @@
* always well-defined for queues with the same elements but different
* ordering properties.
*
- *
* <p>This interface is a member of the
* <a href="{@docRoot}/../technotes/guides/collections/index.html">
* Java Collections Framework</a>.
--- a/jdk/src/java.base/share/classes/java/util/ResourceBundle.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/ResourceBundle.java Thu Jul 21 20:09:19 2016 -0700
@@ -660,6 +660,7 @@
// ResourceBundleProviders for loading ResourceBundles
private ServiceLoader<ResourceBundleProvider> providers;
+ private boolean providersChecked;
// Boolean.TRUE if the factory method caller provides a ResourceBundleProvier.
private Boolean callerHasProvider;
@@ -675,7 +676,6 @@
this.loaderRef = new KeyElementReference<>(loader, referenceQueue, this);
}
this.moduleRef = new KeyElementReference<>(module, referenceQueue, this);
- this.providers = getServiceLoader(module, baseName);
calculateHashCode();
}
@@ -712,11 +712,15 @@
}
ServiceLoader<ResourceBundleProvider> getProviders() {
+ if (!providersChecked) {
+ providers = getServiceLoader(getModule(), name);
+ providersChecked = true;
+ }
return providers;
}
boolean hasProviders() {
- return providers != null;
+ return getProviders() != null;
}
boolean callerHasProvider() {
@@ -789,8 +793,9 @@
}
clone.moduleRef = new KeyElementReference<>(getModule(),
referenceQueue, clone);
- // Clear the reference to ResourceBundleProviders
+ // Clear the reference to ResourceBundleProviders and the flag
clone.providers = null;
+ clone.providersChecked = false;
// Clear the reference to a Throwable
clone.cause = null;
// Clear callerHasProvider
@@ -1841,6 +1846,9 @@
private static ServiceLoader<ResourceBundleProvider> getServiceLoader(Module module,
String baseName) {
+ if (!module.isNamed()) {
+ return null;
+ }
PrivilegedAction<ClassLoader> pa = module::getClassLoader;
ClassLoader loader = AccessController.doPrivileged(pa);
return getServiceLoader(module, loader, baseName);
--- a/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/CompletableFuture.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,6 +35,8 @@
package java.util.concurrent;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.util.concurrent.locks.LockSupport;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
@@ -149,26 +151,29 @@
* applies across normal vs exceptional outcomes, sync vs async
* actions, binary triggers, and various forms of completions.
*
- * Non-nullness of field result (set via CAS) indicates done. An
- * AltResult is used to box null as a result, as well as to hold
- * exceptions. Using a single field makes completion simple to
- * detect and trigger. Encoding and decoding is straightforward
- * but adds to the sprawl of trapping and associating exceptions
- * with targets. Minor simplifications rely on (static) NIL (to
- * box null results) being the only AltResult with a null
- * exception field, so we don't usually need explicit comparisons.
- * Even though some of the generics casts are unchecked (see
- * SuppressWarnings annotations), they are placed to be
- * appropriate even if checked.
+ * Non-nullness of volatile field "result" indicates done. It may
+ * be set directly if known to be thread-confined, else via CAS.
+ * An AltResult is used to box null as a result, as well as to
+ * hold exceptions. Using a single field makes completion simple
+ * to detect and trigger. Result encoding and decoding is
+ * straightforward but tedious and adds to the sprawl of trapping
+ * and associating exceptions with targets. Minor simplifications
+ * rely on (static) NIL (to box null results) being the only
+ * AltResult with a null exception field, so we don't usually need
+ * explicit comparisons. Even though some of the generics casts
+ * are unchecked (see SuppressWarnings annotations), they are
+ * placed to be appropriate even if checked.
*
* Dependent actions are represented by Completion objects linked
* as Treiber stacks headed by field "stack". There are Completion
- * classes for each kind of action, grouped into single-input
- * (UniCompletion), two-input (BiCompletion), projected
- * (BiCompletions using either (not both) of two inputs), shared
- * (CoCompletion, used by the second of two sources), zero-input
- * source actions, and Signallers that unblock waiters. Class
- * Completion extends ForkJoinTask to enable async execution
+ * classes for each kind of action, grouped into:
+ * - single-input (UniCompletion),
+ * - two-input (BiCompletion),
+ * - projected (BiCompletions using exactly one of two inputs),
+ * - shared (CoCompletion, used by the second of two sources),
+ * - zero-input source actions,
+ * - Signallers that unblock waiters.
+ * Class Completion extends ForkJoinTask to enable async execution
* (adding no space overhead because we exploit its "tag" methods
* to maintain claims). It is also declared as Runnable to allow
* usage with arbitrary executors.
@@ -184,7 +189,7 @@
* encounter layers of adapters in common usages.
*
* * Boolean CompletableFuture method x(...) (for example
- * uniApply) takes all of the arguments needed to check that an
+ * biApply) takes all of the arguments needed to check that an
* action is triggerable, and then either runs the action or
* arranges its async execution by executing its Completion
* argument, if present. The method returns true if known to be
@@ -194,24 +199,32 @@
* 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
+ * does this pairwise to form trees of completions. Method
+ * anyOf is handled differently from allOf because completion of
+ * any source should trigger a cleanStack of other sources.
+ * Each AnyOf completion can reach others via a shared array.
*
* Note that the generic type parameters of methods vary according
* to whether "this" is a source, dependent, or completion.
@@ -236,29 +249,30 @@
* pointing back to its sources. So we null out fields as soon as
* possible. The screening checks needed anyway harmlessly ignore
* null arguments that may have been obtained during races with
- * threads nulling out fields. We also try to unlink fired
- * Completions from stacks that might never be popped (see method
- * postFire). Completion fields need not be declared as final or
- * volatile because they are only visible to other threads upon
- * safe publication.
+ * threads nulling out fields. We also try to unlink non-isLive
+ * (fired or cancelled) Completions from stacks that might
+ * otherwise never be popped: Method cleanStack always unlinks non
+ * isLive completions from the head of stack; others may
+ * occasionally remain if racing with other cancellations or
+ * removals.
+ *
+ * Completion fields need not be declared as final or volatile
+ * because they are only visible to other threads upon safe
+ * publication.
*/
volatile Object result; // Either the result or boxed AltResult
volatile Completion stack; // Top of Treiber stack of dependent actions
final boolean internalComplete(Object r) { // CAS from null to r
- return U.compareAndSwapObject(this, RESULT, null, r);
- }
-
- final boolean casStack(Completion cmp, Completion val) {
- return U.compareAndSwapObject(this, STACK, cmp, val);
+ return RESULT.compareAndSet(this, null, r);
}
/** Returns true if successfully pushed c onto stack. */
final boolean tryPushStack(Completion c) {
Completion h = stack;
- lazySetNext(c, h);
- return U.compareAndSwapObject(this, STACK, h, c);
+ NEXT.set(c, h); // CAS piggyback
+ return STACK.compareAndSet(this, h, c);
}
/** Unconditionally pushes c onto stack, retrying if necessary. */
@@ -278,8 +292,7 @@
/** Completes with the null value, unless already completed. */
final boolean completeNull() {
- return U.compareAndSwapObject(this, RESULT, null,
- NIL);
+ return RESULT.compareAndSet(this, null, NIL);
}
/** Returns the encoding of the given non-exceptional value. */
@@ -289,8 +302,7 @@
/** Completes with a non-exceptional result, unless already completed. */
final boolean completeValue(T t) {
- return U.compareAndSwapObject(this, RESULT, null,
- (t == null) ? NIL : t);
+ return RESULT.compareAndSet(this, null, (t == null) ? NIL : t);
}
/**
@@ -304,8 +316,7 @@
/** Completes with an exceptional result, unless already completed. */
final boolean completeThrowable(Throwable x) {
- return U.compareAndSwapObject(this, RESULT, null,
- encodeThrowable(x));
+ return RESULT.compareAndSet(this, null, encodeThrowable(x));
}
/**
@@ -332,8 +343,7 @@
* existing CompletionException.
*/
final boolean completeThrowable(Throwable x, Object r) {
- return U.compareAndSwapObject(this, RESULT, null,
- encodeThrowable(x, r));
+ return RESULT.compareAndSet(this, null, encodeThrowable(x, r));
}
/**
@@ -351,10 +361,11 @@
*/
static Object encodeRelay(Object r) {
Throwable x;
- return (((r instanceof AltResult) &&
- (x = ((AltResult)r).ex) != null &&
- !(x instanceof CompletionException)) ?
- new AltResult(new CompletionException(x)) : r);
+ if (r instanceof AltResult
+ && (x = ((AltResult)r).ex) != null
+ && !(x instanceof CompletionException))
+ r = new AltResult(new CompletionException(x));
+ return r;
}
/**
@@ -362,14 +373,13 @@
* If exceptional, r is first coerced to a CompletionException.
*/
final boolean completeRelay(Object r) {
- return U.compareAndSwapObject(this, RESULT, null,
- encodeRelay(r));
+ return RESULT.compareAndSet(this, null, encodeRelay(r));
}
/**
* Reports result using Future.get conventions.
*/
- private static <T> 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 +394,13 @@
x = cause;
throw new ExecutionException(x);
}
- @SuppressWarnings("unchecked") T t = (T) r;
- return t;
+ return r;
}
/**
* Decodes outcome to return result or throw unchecked exception.
*/
- private static <T> T reportJoin(Object r) {
+ private static Object reportJoin(Object r) {
if (r instanceof AltResult) {
Throwable x;
if ((x = ((AltResult)r).ex) == null)
@@ -402,8 +411,7 @@
throw (CompletionException)x;
throw new CompletionException(x);
}
- @SuppressWarnings("unchecked") T t = (T) r;
- return t;
+ return r;
}
/* ------------- Async task preliminaries -------------- */
@@ -449,12 +457,6 @@
static final int ASYNC = 1;
static final int NESTED = -1;
- /**
- * Spins before blocking in waitingGet
- */
- static final int SPINS = (Runtime.getRuntime().availableProcessors() > 1 ?
- 1 << 8 : 0);
-
/* ------------- Base Completion classes and operations -------------- */
@SuppressWarnings("serial")
@@ -479,10 +481,6 @@
public final void setRawResult(Void v) {}
}
- static void lazySetNext(Completion c, Completion next) {
- U.putObjectRelease(c, NEXT, next);
- }
-
/**
* Pops and tries to trigger all reachable dependents. Call only
* when known to be done.
@@ -497,40 +495,47 @@
while ((h = f.stack) != null ||
(f != this && (h = (f = this).stack) != null)) {
CompletableFuture<?> d; Completion t;
- if (f.casStack(h, t = h.next)) {
+ if (STACK.compareAndSet(f, h, t = h.next)) {
if (t != null) {
if (f != this) {
pushStack(h);
continue;
}
- h.next = null; // detach
+ NEXT.compareAndSet(h, t, null); // try to detach
}
f = (d = h.tryFire(NESTED)) == null ? this : d;
}
}
}
- /** Traverses stack and unlinks dead Completions. */
+ /** Traverses stack and unlinks one or more dead Completions, if found. */
final void cleanStack() {
- for (Completion p = null, q = stack; q != null;) {
+ Completion p = stack;
+ // ensure head of stack live
+ for (boolean unlinked = false;;) {
+ if (p == null)
+ return;
+ else if (p.isLive()) {
+ if (unlinked)
+ return;
+ else
+ break;
+ }
+ else if (STACK.weakCompareAndSetVolatile(this, p, (p = p.next)))
+ unlinked = true;
+ else
+ p = stack;
+ }
+ // try to unlink first non-live
+ for (Completion q = p.next; q != null;) {
Completion s = q.next;
if (q.isLive()) {
p = q;
q = s;
- }
- else if (p == null) {
- casStack(q, s);
- q = stack;
- }
- else {
- p.next = s;
- if (p.isLive())
- q = s;
- else {
- p = null; // restart
- q = stack;
- }
- }
+ } else if (NEXT.weakCompareAndSetVolatile(p, q, s))
+ break;
+ else
+ q = p.next;
}
}
@@ -568,24 +573,34 @@
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);
}
}
/**
- * Post-processing by dependent after successful UniCompletion
- * tryFire. Tries to clean stack of source a, and then either runs
- * postComplete or returns this to caller, depending on mode.
+ * Post-processing by dependent after successful UniCompletion tryFire.
+ * Tries to clean stack of source a, and then either runs postComplete
+ * or returns this to caller, depending on mode.
*/
final CompletableFuture<T> 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 +622,65 @@
}
final CompletableFuture<V> tryFire(int mode) {
CompletableFuture<V> d; CompletableFuture<T> a;
- if ((d = dep) == null ||
- !d.uniApply(a = src, fn, mode > 0 ? null : this))
+ Object r; Throwable x; Function<? super T,? extends V> f;
+ if ((d = dep) == null || (f = fn) == null
+ || (a = src) == null || (r = a.result) == null)
return null;
+ tryComplete: if (d.result == null) {
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ d.completeThrowable(x, r);
+ break tryComplete;
+ }
+ r = null;
+ }
+ try {
+ if (mode <= 0 && !claim())
+ return null;
+ else {
+ @SuppressWarnings("unchecked") T t = (T) r;
+ d.completeValue(f.apply(t));
+ }
+ } catch (Throwable ex) {
+ d.completeThrowable(ex);
+ }
+ }
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
- final <S> boolean uniApply(CompletableFuture<S> a,
- Function<? super S,? extends T> f,
- UniApply<S,T> 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 <V> CompletableFuture<V> uniApplyStage(
Executor e, Function<? super T,? extends V> f) {
if (f == null) throw new NullPointerException();
+ Object r;
+ if ((r = result) != null)
+ return uniApplyNow(r, e, f);
CompletableFuture<V> d = newIncompleteFuture();
- if (e != null || !d.uniApply(this, f, null)) {
- UniApply<T,V> c = new UniApply<T,V>(e, d, this, f);
- push(c);
- c.tryFire(SYNC);
+ unipush(new UniApply<T,V>(e, d, this, f));
+ return d;
+ }
+
+ private <V> CompletableFuture<V> uniApplyNow(
+ Object r, Executor e, Function<? super T,? extends V> f) {
+ Throwable x;
+ CompletableFuture<V> 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<T,V>(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 +694,67 @@
}
final CompletableFuture<Void> tryFire(int mode) {
CompletableFuture<Void> d; CompletableFuture<T> a;
- if ((d = dep) == null ||
- !d.uniAccept(a = src, fn, mode > 0 ? null : this))
+ Object r; Throwable x; Consumer<? super T> f;
+ if ((d = dep) == null || (f = fn) == null
+ || (a = src) == null || (r = a.result) == null)
return null;
+ tryComplete: if (d.result == null) {
+ if (r instanceof AltResult) {
+ if ((x = ((AltResult)r).ex) != null) {
+ d.completeThrowable(x, r);
+ break tryComplete;
+ }
+ r = null;
+ }
+ try {
+ if (mode <= 0 && !claim())
+ return null;
+ else {
+ @SuppressWarnings("unchecked") T t = (T) r;
+ f.accept(t);
+ d.completeNull();
+ }
+ } catch (Throwable ex) {
+ d.completeThrowable(ex);
+ }
+ }
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
- final <S> boolean uniAccept(CompletableFuture<S> a,
- Consumer<? super S> f, UniAccept<S> 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<Void> uniAcceptStage(Executor e,
Consumer<? super T> f) {
if (f == null) throw new NullPointerException();
+ Object r;
+ if ((r = result) != null)
+ return uniAcceptNow(r, e, f);
CompletableFuture<Void> d = newIncompleteFuture();
- if (e != null || !d.uniAccept(this, f, null)) {
- UniAccept<T> c = new UniAccept<T>(e, d, this, f);
- push(c);
- c.tryFire(SYNC);
+ unipush(new UniAccept<T>(e, d, this, f));
+ return d;
+ }
+
+ private CompletableFuture<Void> uniAcceptNow(
+ Object r, Executor e, Consumer<? super T> f) {
+ Throwable x;
+ CompletableFuture<Void> 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<T>(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 +768,56 @@
}
final CompletableFuture<Void> tryFire(int mode) {
CompletableFuture<Void> d; CompletableFuture<T> 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<Void> uniRunStage(Executor e, Runnable f) {
+ if (f == null) throw new NullPointerException();
+ Object r;
+ if ((r = result) != null)
+ return uniRunNow(r, e, f);
+ CompletableFuture<Void> d = newIncompleteFuture();
+ unipush(new UniRun<T>(e, d, this, f));
+ return d;
}
- private CompletableFuture<Void> uniRunStage(Executor e, Runnable f) {
- if (f == null) throw new NullPointerException();
+ private CompletableFuture<Void> uniRunNow(Object r, Executor e, Runnable f) {
+ Throwable x;
CompletableFuture<Void> d = newIncompleteFuture();
- if (e != null || !d.uniRun(this, f, null)) {
- UniRun<T> c = new UniRun<T>(e, d, this, f);
- push(c);
- c.tryFire(SYNC);
- }
+ if (r instanceof AltResult && (x = ((AltResult)r).ex) != null)
+ d.result = encodeThrowable(x, r);
+ else
+ try {
+ if (e != null) {
+ e.execute(new UniRun<T>(null, d, this, f));
+ } else {
+ f.run();
+ d.result = NIL;
+ }
+ } catch (Throwable ex) {
+ d.result = encodeThrowable(ex);
+ }
return d;
}
@@ -766,20 +831,20 @@
}
final CompletableFuture<T> tryFire(int mode) {
CompletableFuture<T> d; CompletableFuture<T> a;
- if ((d = dep) == null ||
- !d.uniWhenComplete(a = src, fn, mode > 0 ? null : this))
+ Object r; BiConsumer<? super T, ? super Throwable> f;
+ if ((d = dep) == null || (f = fn) == null
+ || (a = src) == null || (r = a.result) == null
+ || !d.uniWhenComplete(r, f, mode > 0 ? null : this))
return null;
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
- final boolean uniWhenComplete(CompletableFuture<T> a,
+ final boolean uniWhenComplete(Object r,
BiConsumer<? super T,? super Throwable> f,
UniWhenComplete<T> 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 +876,17 @@
Executor e, BiConsumer<? super T, ? super Throwable> f) {
if (f == null) throw new NullPointerException();
CompletableFuture<T> d = newIncompleteFuture();
- if (e != null || !d.uniWhenComplete(this, f, null)) {
- UniWhenComplete<T> c = new UniWhenComplete<T>(e, d, this, f);
- push(c);
- c.tryFire(SYNC);
+ Object r;
+ if ((r = result) == null)
+ unipush(new UniWhenComplete<T>(e, d, this, f));
+ else if (e == null)
+ d.uniWhenComplete(r, f, null);
+ else {
+ try {
+ e.execute(new UniWhenComplete<T>(null, d, this, f));
+ } catch (Throwable ex) {
+ d.result = encodeThrowable(ex);
+ }
}
return d;
}
@@ -829,20 +901,20 @@
}
final CompletableFuture<V> tryFire(int mode) {
CompletableFuture<V> d; CompletableFuture<T> a;
- if ((d = dep) == null ||
- !d.uniHandle(a = src, fn, mode > 0 ? null : this))
+ Object r; BiFunction<? super T, Throwable, ? extends V> f;
+ if ((d = dep) == null || (f = fn) == null
+ || (a = src) == null || (r = a.result) == null
+ || !d.uniHandle(r, f, mode > 0 ? null : this))
return null;
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
- final <S> boolean uniHandle(CompletableFuture<S> a,
+ final <S> boolean uniHandle(Object r,
BiFunction<? super S, Throwable, ? extends T> f,
UniHandle<S,T> 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 +939,17 @@
Executor e, BiFunction<? super T, Throwable, ? extends V> f) {
if (f == null) throw new NullPointerException();
CompletableFuture<V> d = newIncompleteFuture();
- if (e != null || !d.uniHandle(this, f, null)) {
- UniHandle<T,V> c = new UniHandle<T,V>(e, d, this, f);
- push(c);
- c.tryFire(SYNC);
+ Object r;
+ if ((r = result) == null)
+ unipush(new UniHandle<T,V>(e, d, this, f));
+ else if (e == null)
+ d.uniHandle(r, f, null);
+ else {
+ try {
+ e.execute(new UniHandle<T,V>(null, d, this, f));
+ } catch (Throwable ex) {
+ d.result = encodeThrowable(ex);
+ }
}
return d;
}
@@ -885,19 +964,20 @@
final CompletableFuture<T> tryFire(int mode) { // never ASYNC
// assert mode != ASYNC;
CompletableFuture<T> d; CompletableFuture<T> a;
- if ((d = dep) == null || !d.uniExceptionally(a = src, fn, this))
+ Object r; Function<? super Throwable, ? extends T> f;
+ if ((d = dep) == null || (f = fn) == null
+ || (a = src) == null || (r = a.result) == null
+ || !d.uniExceptionally(r, f, this))
return null;
dep = null; src = null; fn = null;
return d.postFire(a, mode);
}
}
- final boolean uniExceptionally(CompletableFuture<T> a,
+ final boolean uniExceptionally(Object r,
Function<? super Throwable, ? extends T> f,
UniExceptionally<T> 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 +997,39 @@
Function<Throwable, ? extends T> f) {
if (f == null) throw new NullPointerException();
CompletableFuture<T> d = newIncompleteFuture();
- if (!d.uniExceptionally(this, f, null)) {
- UniExceptionally<T> c = new UniExceptionally<T>(d, this, f);
- push(c);
- c.tryFire(SYNC);
- }
+ Object r;
+ if ((r = result) == null)
+ unipush(new UniExceptionally<T>(d, this, f));
+ else
+ d.uniExceptionally(r, f, null);
return d;
}
@SuppressWarnings("serial")
- static final class UniRelay<T> extends UniCompletion<T,T> { // for Compose
- UniRelay(CompletableFuture<T> dep, CompletableFuture<T> src) {
+ static final class UniRelay<U, T extends U> extends UniCompletion<T,U> {
+ UniRelay(CompletableFuture<U> dep, CompletableFuture<T> src) {
super(null, dep, src);
}
- final CompletableFuture<T> tryFire(int mode) {
- CompletableFuture<T> d; CompletableFuture<T> a;
- if ((d = dep) == null || !d.uniRelay(a = src))
+ final CompletableFuture<U> tryFire(int mode) {
+ CompletableFuture<U> d; CompletableFuture<T> 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<T> a) {
+ private static <U, T extends U> CompletableFuture<U> uniCopyStage(
+ CompletableFuture<T> src) {
Object r;
- if (a == null || (r = a.result) == null)
- return false;
- if (result == null) // no need to claim
- completeRelay(r);
- return true;
- }
-
- private CompletableFuture<T> uniCopyStage() {
- Object r;
- CompletableFuture<T> d = newIncompleteFuture();
- if ((r = result) != null)
- d.completeRelay(r);
- else {
- UniRelay<T> c = new UniRelay<T>(d, this);
- push(c);
- c.tryFire(SYNC);
- }
+ CompletableFuture<U> d = src.newIncompleteFuture();
+ if ((r = src.result) != null)
+ d.result = encodeRelay(r);
+ else
+ src.unipush(new UniRelay<U,T>(d, src));
return d;
}
@@ -966,9 +1038,7 @@
if ((r = result) != null)
return new MinimalStage<T>(encodeRelay(r));
MinimalStage<T> d = new MinimalStage<T>();
- UniRelay<T> c = new UniRelay<T>(d, this);
- push(c);
- c.tryFire(SYNC);
+ unipush(new UniRelay<T,T>(d, this));
return d;
}
@@ -982,54 +1052,48 @@
}
final CompletableFuture<V> tryFire(int mode) {
CompletableFuture<V> d; CompletableFuture<T> a;
- if ((d = dep) == null ||
- !d.uniCompose(a = src, fn, mode > 0 ? null : this))
+ Function<? super T, ? extends CompletionStage<V>> 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<V> g = f.apply(t).toCompletableFuture();
+ if ((r = g.result) != null)
+ d.completeRelay(r);
+ else {
+ g.unipush(new UniRelay<V,V>(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 <S> boolean uniCompose(
- CompletableFuture<S> a,
- Function<? super S, ? extends CompletionStage<T>> f,
- UniCompose<S,T> 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<T> g = f.apply(s).toCompletableFuture();
- if (g.result == null || !uniRelay(g)) {
- UniRelay<T> copy = new UniRelay<T>(this, g);
- g.push(copy);
- copy.tryFire(SYNC);
- if (result == null)
- return false;
- }
- } catch (Throwable ex) {
- completeThrowable(ex);
- }
- }
- return true;
- }
-
private <V> CompletableFuture<V> uniComposeStage(
Executor e, Function<? super T, ? extends CompletionStage<V>> f) {
if (f == null) throw new NullPointerException();
+ CompletableFuture<V> d = newIncompleteFuture();
Object r, s; Throwable x;
- CompletableFuture<V> d = newIncompleteFuture();
- if (e == null && (r = result) != null) {
+ if ((r = result) == null)
+ unipush(new UniCompose<T,V>(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 +1105,20 @@
@SuppressWarnings("unchecked") T t = (T) r;
CompletableFuture<V> g = f.apply(t).toCompletableFuture();
if ((s = g.result) != null)
- d.completeRelay(s);
+ d.result = encodeRelay(s);
else {
- UniRelay<V> c = new UniRelay<V>(d, g);
- g.push(c);
- c.tryFire(SYNC);
+ g.unipush(new UniRelay<V,V>(d, g));
}
- return d;
} catch (Throwable ex) {
d.result = encodeThrowable(ex);
- return d;
}
}
- UniCompose<T,V> c = new UniCompose<T,V>(e, d, this, f);
- push(c);
- c.tryFire(SYNC);
+ else
+ try {
+ e.execute(new UniCompose<T,V>(null, d, this, f));
+ } catch (Throwable ex) {
+ d.result = encodeThrowable(ex);
+ }
return d;
}
@@ -1085,21 +1148,28 @@
}
final boolean isLive() {
BiCompletion<?,?,?> c;
- return (c = base) != null && c.dep != null;
+ return (c = base) != null
+ // && c.isLive()
+ && c.dep != null;
}
}
- /** Pushes completion to this and b unless both done. */
+ /**
+ * Pushes completion to this and b unless both done.
+ * Caller should first check that either result or b.result is null.
+ */
final void bipush(CompletableFuture<?> b, BiCompletion<?,?,?> c) {
if (c != null) {
- Object r;
- while ((r = result) == null && !tryPushStack(c))
- lazySetNext(c, null); // clear on failure
- if (b != null && b != this && b.result == null) {
- Completion q = (r != null) ? c : new CoCompletion(c);
- while (b.result == null && !b.tryPushStack(q))
- lazySetNext(q, null); // clear on failure
+ while (result == null) {
+ if (tryPushStack(c)) {
+ if (b.result == null)
+ b.unipush(new CoCompletion(c));
+ else if (result != null)
+ c.tryFire(SYNC);
+ return;
+ }
}
+ b.unipush(c);
}
}
@@ -1107,9 +1177,10 @@
final CompletableFuture<T> 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 +1198,21 @@
CompletableFuture<V> d;
CompletableFuture<T> a;
CompletableFuture<U> b;
- if ((d = dep) == null ||
- !d.biApply(a = src, b = snd, fn, mode > 0 ? null : this))
+ Object r, s; BiFunction<? super T,? super U,? extends V> f;
+ if ((d = dep) == null || (f = fn) == null
+ || (a = src) == null || (r = a.result) == null
+ || (b = snd) == null || (s = b.result) == null
+ || !d.biApply(r, s, f, mode > 0 ? null : this))
return null;
dep = null; src = null; snd = null; fn = null;
return d.postFire(a, b, mode);
}
}
- final <R,S> boolean biApply(CompletableFuture<R> a,
- CompletableFuture<S> b,
+ final <R,S> boolean biApply(Object r, Object s,
BiFunction<? super R,? super S,? extends T> f,
BiApply<R,S,T> 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 +1244,20 @@
private <U,V> CompletableFuture<V> biApplyStage(
Executor e, CompletionStage<U> o,
BiFunction<? super T,? super U,? extends V> f) {
- CompletableFuture<U> b;
+ CompletableFuture<U> b; Object r, s;
if (f == null || (b = o.toCompletableFuture()) == null)
throw new NullPointerException();
CompletableFuture<V> d = newIncompleteFuture();
- if (e != null || !d.biApply(this, b, f, null)) {
- BiApply<T,U,V> c = new BiApply<T,U,V>(e, d, this, b, f);
- bipush(b, c);
- c.tryFire(SYNC);
- }
+ if ((r = result) == null || (s = b.result) == null)
+ bipush(b, new BiApply<T,U,V>(e, d, this, b, f));
+ else if (e == null)
+ d.biApply(r, s, f, null);
+ else
+ try {
+ e.execute(new BiApply<T,U,V>(null, d, this, b, f));
+ } catch (Throwable ex) {
+ d.result = encodeThrowable(ex);
+ }
return d;
}
@@ -1198,22 +1273,21 @@
CompletableFuture<Void> d;
CompletableFuture<T> a;
CompletableFuture<U> b;
- if ((d = dep) == null ||
- !d.biAccept(a = src, b = snd, fn, mode > 0 ? null : this))
+ Object r, s; BiConsumer<? super T,? super U> 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 <R,S> boolean biAccept(CompletableFuture<R> a,
- CompletableFuture<S> b,
+ final <R,S> boolean biAccept(Object r, Object s,
BiConsumer<? super R,? super S> f,
BiAccept<R,S> 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 +1320,20 @@
private <U> CompletableFuture<Void> biAcceptStage(
Executor e, CompletionStage<U> o,
BiConsumer<? super T,? super U> f) {
- CompletableFuture<U> b;
+ CompletableFuture<U> b; Object r, s;
if (f == null || (b = o.toCompletableFuture()) == null)
throw new NullPointerException();
CompletableFuture<Void> d = newIncompleteFuture();
- if (e != null || !d.biAccept(this, b, f, null)) {
- BiAccept<T,U> c = new BiAccept<T,U>(e, d, this, b, f);
- bipush(b, c);
- c.tryFire(SYNC);
- }
+ if ((r = result) == null || (s = b.result) == null)
+ bipush(b, new BiAccept<T,U>(e, d, this, b, f));
+ else if (e == null)
+ d.biAccept(r, s, f, null);
+ else
+ try {
+ e.execute(new BiAccept<T,U>(null, d, this, b, f));
+ } catch (Throwable ex) {
+ d.result = encodeThrowable(ex);
+ }
return d;
}
@@ -1262,8 +1341,7 @@
static final class BiRun<T,U> extends BiCompletion<T,U,Void> {
Runnable fn;
BiRun(Executor executor, CompletableFuture<Void> dep,
- CompletableFuture<T> src,
- CompletableFuture<U> snd,
+ CompletableFuture<T> src, CompletableFuture<U> snd,
Runnable fn) {
super(executor, dep, src, snd); this.fn = fn;
}
@@ -1271,25 +1349,25 @@
CompletableFuture<Void> d;
CompletableFuture<T> a;
CompletableFuture<U> 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 +1383,52 @@
private CompletableFuture<Void> 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<Void> d = newIncompleteFuture();
- if (e != null || !d.biRun(this, b, f, null)) {
- BiRun<T,?> 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<T,U> extends BiCompletion<T,U,Void> { // for And
BiRelay(CompletableFuture<Void> dep,
- CompletableFuture<T> src,
- CompletableFuture<U> snd) {
+ CompletableFuture<T> src, CompletableFuture<U> snd) {
super(null, dep, src, snd);
}
final CompletableFuture<Void> tryFire(int mode) {
CompletableFuture<Void> d;
CompletableFuture<T> a;
CompletableFuture<U> 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<Void> andTree(CompletableFuture<?>[] cfs,
int lo, int hi) {
@@ -1358,39 +1436,44 @@
if (lo > hi) // empty
d.result = NIL;
else {
- CompletableFuture<?> a, b;
+ CompletableFuture<?> a, b; Object r, s, z; Throwable x;
int mid = (lo + hi) >>> 1;
if ((a = (lo == mid ? cfs[lo] :
andTree(cfs, lo, mid))) == null ||
(b = (lo == hi ? a : (hi == mid+1) ? cfs[hi] :
andTree(cfs, mid+1, hi))) == null)
throw new NullPointerException();
- if (!d.biRelay(a, b)) {
- BiRelay<?,?> c = new BiRelay<>(d, a, b);
- a.bipush(b, c);
- c.tryFire(SYNC);
- }
+ if ((r = a.result) == null || (s = b.result) == null)
+ a.bipush(b, new BiRelay<>(d, a, b));
+ else if ((r instanceof AltResult
+ && (x = ((AltResult)(z = r)).ex) != null) ||
+ (s instanceof AltResult
+ && (x = ((AltResult)(z = s)).ex) != null))
+ d.result = encodeThrowable(x, z);
+ else
+ d.result = NIL;
}
return d;
}
/* ------------- Projected (Ored) BiCompletions -------------- */
- /** Pushes completion to this and b unless either done. */
+ /**
+ * Pushes completion to this and b unless either done.
+ * Caller should first check that result and b.result are both null.
+ */
final void orpush(CompletableFuture<?> b, BiCompletion<?,?,?> c) {
if (c != null) {
- while ((b == null || b.result == null) && result == null) {
- if (tryPushStack(c)) {
- if (b != null && b != this && b.result == null) {
- Completion q = new CoCompletion(c);
- while (result == null && b.result == null &&
- !b.tryPushStack(q))
- lazySetNext(q, null); // clear on failure
- }
+ while (!tryPushStack(c)) {
+ if (result != null) {
+ NEXT.set(c, null);
break;
}
- lazySetNext(c, null); // clear on failure
}
+ if (result != null)
+ c.tryFire(SYNC);
+ else
+ b.unipush(new CoCompletion(c));
}
}
@@ -1398,8 +1481,7 @@
static final class OrApply<T,U extends T,V> extends BiCompletion<T,U,V> {
Function<? super T,? extends V> fn;
OrApply(Executor executor, CompletableFuture<V> dep,
- CompletableFuture<T> src,
- CompletableFuture<U> snd,
+ CompletableFuture<T> src, CompletableFuture<U> snd,
Function<? super T,? extends V> fn) {
super(executor, dep, src, snd); this.fn = fn;
}
@@ -1407,54 +1489,46 @@
CompletableFuture<V> d;
CompletableFuture<T> a;
CompletableFuture<U> b;
- if ((d = dep) == null ||
- !d.orApply(a = src, b = snd, fn, mode > 0 ? null : this))
+ Object r; Throwable x; Function<? super T,? extends V> f;
+ if ((d = dep) == null || (f = fn) == null
+ || (a = src) == null || (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 <R,S extends R> boolean orApply(CompletableFuture<R> a,
- CompletableFuture<S> b,
- Function<? super R, ? extends T> f,
- OrApply<R,S,T> 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 <U extends T,V> CompletableFuture<V> orApplyStage(
- Executor e, CompletionStage<U> o,
- Function<? super T, ? extends V> f) {
+ Executor e, CompletionStage<U> o, Function<? super T, ? extends V> f) {
CompletableFuture<U> b;
if (f == null || (b = o.toCompletableFuture()) == null)
throw new NullPointerException();
+
+ Object r; CompletableFuture<? extends T> z;
+ if ((r = (z = this).result) != null ||
+ (r = (z = b).result) != null)
+ return z.uniApplyNow(r, e, f);
+
CompletableFuture<V> d = newIncompleteFuture();
- if (e != null || !d.orApply(this, b, f, null)) {
- OrApply<T,U,V> c = new OrApply<T,U,V>(e, d, this, b, f);
- orpush(b, c);
- c.tryFire(SYNC);
- }
+ orpush(b, new OrApply<T,U,V>(e, d, this, b, f));
return d;
}
@@ -1462,8 +1536,7 @@
static final class OrAccept<T,U extends T> extends BiCompletion<T,U,Void> {
Consumer<? super T> fn;
OrAccept(Executor executor, CompletableFuture<Void> dep,
- CompletableFuture<T> src,
- CompletableFuture<U> snd,
+ CompletableFuture<T> src, CompletableFuture<U> snd,
Consumer<? super T> fn) {
super(executor, dep, src, snd); this.fn = fn;
}
@@ -1471,54 +1544,47 @@
CompletableFuture<Void> d;
CompletableFuture<T> a;
CompletableFuture<U> b;
- if ((d = dep) == null ||
- !d.orAccept(a = src, b = snd, fn, mode > 0 ? null : this))
+ Object r; Throwable x; Consumer<? super T> 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 <R,S extends R> boolean orAccept(CompletableFuture<R> a,
- CompletableFuture<S> b,
- Consumer<? super R> f,
- OrAccept<R,S> 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 <U extends T> CompletableFuture<Void> orAcceptStage(
Executor e, CompletionStage<U> o, Consumer<? super T> f) {
CompletableFuture<U> b;
if (f == null || (b = o.toCompletableFuture()) == null)
throw new NullPointerException();
+
+ Object r; CompletableFuture<? extends T> z;
+ if ((r = (z = this).result) != null ||
+ (r = (z = b).result) != null)
+ return z.uniAcceptNow(r, e, f);
+
CompletableFuture<Void> d = newIncompleteFuture();
- if (e != null || !d.orAccept(this, b, f, null)) {
- OrAccept<T,U> c = new OrAccept<T,U>(e, d, this, b, f);
- orpush(b, c);
- c.tryFire(SYNC);
- }
+ orpush(b, new OrAccept<T,U>(e, d, this, b, f));
return d;
}
@@ -1526,8 +1592,7 @@
static final class OrRun<T,U> extends BiCompletion<T,U,Void> {
Runnable fn;
OrRun(Executor executor, CompletableFuture<Void> dep,
- CompletableFuture<T> src,
- CompletableFuture<U> snd,
+ CompletableFuture<T> src, CompletableFuture<U> snd,
Runnable fn) {
super(executor, dep, src, snd); this.fn = fn;
}
@@ -1535,97 +1600,81 @@
CompletableFuture<Void> d;
CompletableFuture<T> a;
CompletableFuture<U> 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<Void> 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<Void> d = newIncompleteFuture();
- if (e != null || !d.orRun(this, b, f, null)) {
- OrRun<T,?> 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;
}
+ /** Completion for an anyOf input future. */
@SuppressWarnings("serial")
- static final class OrRelay<T,U> extends BiCompletion<T,U,Object> { // for Or
- OrRelay(CompletableFuture<Object> dep, CompletableFuture<T> src,
- CompletableFuture<U> snd) {
- super(null, dep, src, snd);
+ static class AnyOf extends Completion {
+ CompletableFuture<Object> dep; CompletableFuture<?> src;
+ CompletableFuture<?>[] srcs;
+ AnyOf(CompletableFuture<Object> dep, CompletableFuture<?> src,
+ CompletableFuture<?>[] srcs) {
+ this.dep = dep; this.src = src; this.srcs = srcs;
}
final CompletableFuture<Object> tryFire(int mode) {
- CompletableFuture<Object> d;
- CompletableFuture<T> a;
- CompletableFuture<U> b;
- if ((d = dep) == null || !d.orRelay(a = src, b = snd))
+ // assert mode != ASYNC;
+ CompletableFuture<Object> d; CompletableFuture<?> a;
+ CompletableFuture<?>[] as;
+ Object r;
+ if ((d = dep) == null
+ || (a = src) == null || (r = a.result) == null
+ || (as = srcs) == null)
return null;
- src = null; snd = null; dep = null;
- return d.postFire(a, b, mode);
+ dep = null; src = null; srcs = null;
+ if (d.completeRelay(r)) {
+ for (CompletableFuture<?> b : as)
+ if (b != a)
+ b.cleanStack();
+ if (mode < 0)
+ return d;
+ else
+ d.postComplete();
+ }
+ return null;
}
- }
-
- 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<Object> orTree(CompletableFuture<?>[] cfs,
- int lo, int hi) {
- CompletableFuture<Object> d = new CompletableFuture<Object>();
- if (lo <= hi) {
- CompletableFuture<?> a, b;
- 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);
- }
+ final boolean isLive() {
+ CompletableFuture<Object> d;
+ return (d = dep) != null && d.result == null;
}
- return d;
}
/* ------------- Zero-input Async forms -------------- */
@@ -1640,7 +1689,7 @@
public final Void getRawResult() { return null; }
public final void setRawResult(Void v) {}
- public final boolean exec() { run(); return true; }
+ public final boolean exec() { run(); return false; }
public void run() {
CompletableFuture<T> d; Supplier<? extends T> f;
@@ -1676,7 +1725,7 @@
public final Void getRawResult() { return null; }
public final void setRawResult(Void v) {}
- public final boolean exec() { run(); return true; }
+ public final boolean exec() { run(); return false; }
public void run() {
CompletableFuture<Void> d; Runnable f;
@@ -1760,15 +1809,13 @@
private Object waitingGet(boolean interruptible) {
Signaller q = null;
boolean queued = false;
- int spins = SPINS;
Object r;
while ((r = result) == null) {
- if (spins > 0) {
- if (ThreadLocalRandom.nextSecondarySeed() >= 0)
- --spins;
+ if (q == null) {
+ q = new Signaller(interruptible, 0L, 0L);
+ if (Thread.currentThread() instanceof ForkJoinWorkerThread)
+ ForkJoinPool.helpAsyncBlocker(defaultExecutor(), q);
}
- else if (q == null)
- q = new Signaller(interruptible, 0L, 0L);
else if (!queued)
queued = tryPushStack(q);
else {
@@ -1781,16 +1828,14 @@
break;
}
}
- if (q != null) {
+ if (q != null && queued) {
q.thread = null;
- if (q.interrupted) {
- if (interruptible)
- cleanStack();
- else
- Thread.currentThread().interrupt();
- }
+ if (!interruptible && q.interrupted)
+ Thread.currentThread().interrupt();
+ if (r == null)
+ cleanStack();
}
- if (r != null)
+ if (r != null || (r = result) != null)
postComplete();
return r;
}
@@ -1808,9 +1853,12 @@
Signaller q = null;
boolean queued = false;
Object r;
- while ((r = result) == null) { // similar to untimed, without spins
- if (q == null)
+ while ((r = result) == null) { // similar to untimed
+ if (q == null) {
q = new Signaller(true, nanos, deadline);
+ if (Thread.currentThread() instanceof ForkJoinWorkerThread)
+ ForkJoinPool.helpAsyncBlocker(defaultExecutor(), q);
+ }
else if (!queued)
queued = tryPushStack(q);
else if (q.nanos <= 0L)
@@ -1825,12 +1873,13 @@
break;
}
}
- if (q != null)
+ if (q != null && queued) {
q.thread = null;
- if (r != null)
+ if (r == null)
+ cleanStack();
+ }
+ if (r != null || (r = result) != null)
postComplete();
- else
- cleanStack();
if (r != null || (q != null && q.interrupted))
return r;
}
@@ -1942,9 +1991,12 @@
* @throws InterruptedException if the current thread was interrupted
* while waiting
*/
+ @SuppressWarnings("unchecked")
public T get() throws InterruptedException, ExecutionException {
Object r;
- return reportGet((r = result) == null ? waitingGet(true) : r);
+ if ((r = result) == null)
+ r = waitingGet(true);
+ return (T) reportGet(r);
}
/**
@@ -1960,11 +2012,14 @@
* while waiting
* @throws TimeoutException if the wait timed out
*/
+ @SuppressWarnings("unchecked")
public T get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
+ long nanos = unit.toNanos(timeout);
Object r;
- long nanos = unit.toNanos(timeout);
- return reportGet((r = result) == null ? timedGet(nanos) : r);
+ if ((r = result) == null)
+ r = timedGet(nanos);
+ return (T) reportGet(r);
}
/**
@@ -1981,9 +2036,12 @@
* @throws CompletionException if this future completed
* exceptionally or a completion computation threw an exception
*/
+ @SuppressWarnings("unchecked")
public T join() {
Object r;
- return reportJoin((r = result) == null ? waitingGet(false) : r);
+ if ((r = result) == null)
+ r = waitingGet(false);
+ return (T) reportJoin(r);
}
/**
@@ -1996,9 +2054,10 @@
* @throws CompletionException if this future completed
* exceptionally or a completion computation threw an exception
*/
+ @SuppressWarnings("unchecked")
public T getNow(T valueIfAbsent) {
Object r;
- return ((r = result) == null) ? valueIfAbsent : reportJoin(r);
+ return ((r = result) == null) ? valueIfAbsent : (T) reportJoin(r);
}
/**
@@ -2294,7 +2353,28 @@
* {@code null}
*/
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) {
- return orTree(cfs, 0, cfs.length - 1);
+ int n; Object r;
+ if ((n = cfs.length) <= 1)
+ return (n == 0)
+ ? new CompletableFuture<Object>()
+ : uniCopyStage(cfs[0]);
+ for (CompletableFuture<?> cf : cfs)
+ if ((r = cf.result) != null)
+ return new CompletableFuture<Object>(encodeRelay(r));
+ cfs = cfs.clone();
+ CompletableFuture<Object> d = new CompletableFuture<>();
+ for (CompletableFuture<?> cf : cfs)
+ cf.unipush(new AnyOf(d, cf, cfs));
+ // If d was completed while we were adding completions, we should
+ // clean the stack of any sources that may have had completions
+ // pushed on their stack after d was completed.
+ if (d.result != null)
+ for (int i = 0, len = cfs.length; i < len; i++)
+ if (cfs[i].result != null)
+ for (i++; i < len; i++)
+ if (cfs[i].result == null)
+ cfs[i].cleanStack();
+ return d;
}
/* ------------- Control and status methods -------------- */
@@ -2466,7 +2546,7 @@
* @since 9
*/
public CompletableFuture<T> copy() {
- return uniCopyStage();
+ return uniCopyStage(this);
}
/**
@@ -2775,19 +2855,16 @@
throw new UnsupportedOperationException(); }
}
- // Unsafe mechanics
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long RESULT;
- private static final long STACK;
- private static final long NEXT;
+ // VarHandle mechanics
+ private static final VarHandle RESULT;
+ private static final VarHandle STACK;
+ private static final VarHandle NEXT;
static {
try {
- RESULT = U.objectFieldOffset
- (CompletableFuture.class.getDeclaredField("result"));
- STACK = U.objectFieldOffset
- (CompletableFuture.class.getDeclaredField("stack"));
- NEXT = U.objectFieldOffset
- (Completion.class.getDeclaredField("next"));
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ RESULT = l.findVarHandle(CompletableFuture.class, "result", Object.class);
+ STACK = l.findVarHandle(CompletableFuture.class, "stack", Completion.class);
+ NEXT = l.findVarHandle(Completion.class, "next", Completion.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentHashMap.java Thu Jul 21 20:09:19 2016 -0700
@@ -68,6 +68,7 @@
import java.util.function.ToLongBiFunction;
import java.util.function.ToLongFunction;
import java.util.stream.Stream;
+import jdk.internal.misc.Unsafe;
/**
* A hash table supporting full concurrency of retrievals and
@@ -747,7 +748,7 @@
/* ---------------- Table element access -------------- */
/*
- * Volatile access methods are used for table elements as well as
+ * Atomic access methods are used for table elements as well as
* elements of in-progress next table while resizing. All uses of
* the tab arguments must be null checked by callers. All callers
* also paranoically precheck that tab's length is not zero (or an
@@ -757,14 +758,12 @@
* errors by users, these checks must operate on local variables,
* which accounts for some odd-looking inline assignments below.
* Note that calls to setTabAt always occur within locked regions,
- * and so in principle require only release ordering, not
- * full volatile semantics, but are currently coded as volatile
- * writes to be conservative.
+ * and so require only release ordering.
*/
@SuppressWarnings("unchecked")
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
- return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
+ return (Node<K,V>)U.getObjectAcquire(tab, ((long)i << ASHIFT) + ABASE);
}
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
@@ -773,7 +772,7 @@
}
static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
- U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
+ U.putObjectRelease(tab, ((long)i << ASHIFT) + ABASE, v);
}
/* ---------------- Fields -------------- */
@@ -1024,7 +1023,7 @@
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
- Node<K,V> f; int n, i, fh;
+ Node<K,V> f; int n, i, fh; K fk; V fv;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
@@ -1033,6 +1032,10 @@
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
+ else if (onlyIfAbsent && fh == hash && // check first node
+ ((fk = f.key) == key || fk != null && key.equals(fk)) &&
+ (fv = f.val) != null)
+ return fv;
else {
V oldVal = null;
synchronized (f) {
@@ -1703,7 +1706,7 @@
V val = null;
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
- Node<K,V> f; int n, i, fh;
+ Node<K,V> f; int n, i, fh; K fk; V fv;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
@@ -1725,6 +1728,10 @@
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
+ else if (fh == h && // check first node
+ ((fk = f.key) == key || fk != null && key.equals(fk)) &&
+ (fv = f.val) != null)
+ return fv;
else {
boolean added = false;
synchronized (f) {
@@ -3298,7 +3305,7 @@
return true;
}
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
+ private static final Unsafe U = Unsafe.getUnsafe();
private static final long LOCKSTATE;
static {
try {
@@ -4554,14 +4561,21 @@
return true;
}
- public final boolean removeAll(Collection<?> c) {
+ public boolean removeAll(Collection<?> c) {
if (c == null) throw new NullPointerException();
boolean modified = false;
- for (Iterator<E> it = iterator(); it.hasNext();) {
- if (c.contains(it.next())) {
- it.remove();
- modified = true;
+ // Use (c instanceof Set) as a hint that lookup in c is as
+ // efficient as this view
+ if (c instanceof Set<?> && c.size() > map.table.length) {
+ for (Iterator<?> it = iterator(); it.hasNext(); ) {
+ if (c.contains(it.next())) {
+ it.remove();
+ modified = true;
+ }
}
+ } else {
+ for (Object e : c)
+ modified |= remove(e);
}
return modified;
}
@@ -4748,6 +4762,18 @@
throw new UnsupportedOperationException();
}
+ @Override public boolean removeAll(Collection<?> c) {
+ if (c == null) throw new NullPointerException();
+ boolean modified = false;
+ for (Iterator<V> it = iterator(); it.hasNext();) {
+ if (c.contains(it.next())) {
+ it.remove();
+ modified = true;
+ }
+ }
+ return modified;
+ }
+
public boolean removeIf(Predicate<? super V> filter) {
return map.removeValueIf(filter);
}
@@ -6341,7 +6367,7 @@
}
// Unsafe mechanics
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
+ private static final Unsafe U = Unsafe.getUnsafe();
private static final long SIZECTL;
private static final long TRANSFERINDEX;
private static final long BASECOUNT;
--- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedDeque.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,6 +35,8 @@
package java.util.concurrent;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.util.AbstractCollection;
import java.util.Arrays;
import java.util.Collection;
@@ -292,64 +294,23 @@
volatile Node<E> prev;
volatile E item;
volatile Node<E> 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<E> val) {
- U.putObjectRelease(this, NEXT, val);
- }
-
- boolean casNext(Node<E> cmp, Node<E> val) {
- return U.compareAndSwapObject(this, NEXT, cmp, val);
- }
+ }
- void lazySetPrev(Node<E> val) {
- U.putObjectRelease(this, PREV, val);
- }
-
- boolean casPrev(Node<E> cmp, Node<E> 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 <E> Node<E> newNode(E item) {
+ Node<E> node = new Node<E>();
+ ITEM.set(node, item);
+ return node;
}
/**
* Links e as first element.
*/
private void linkFirst(E e) {
- final Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
+ final Node<E> newNode = newNode(Objects.requireNonNull(e));
restartFromHead:
for (;;)
@@ -363,13 +324,13 @@
continue restartFromHead;
else {
// p is first node
- newNode.lazySetNext(p); // CAS piggyback
- if (p.casPrev(null, newNode)) {
+ NEXT.set(newNode, p); // CAS piggyback
+ if (PREV.compareAndSet(p, null, newNode)) {
// Successful CAS is the linearization point
// for e to become an element of this deque,
// and for newNode to become "live".
- if (p != h) // hop two nodes at a time
- casHead(h, newNode); // Failure is OK.
+ if (p != h) // hop two nodes at a time; failure is OK
+ HEAD.weakCompareAndSetVolatile(this, h, newNode);
return;
}
// Lost CAS race to another thread; re-read prev
@@ -381,7 +342,7 @@
* Links e as last element.
*/
private void linkLast(E e) {
- final Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
+ final Node<E> newNode = newNode(Objects.requireNonNull(e));
restartFromTail:
for (;;)
@@ -395,13 +356,13 @@
continue restartFromTail;
else {
// p is last node
- newNode.lazySetPrev(p); // CAS piggyback
- if (p.casNext(null, newNode)) {
+ PREV.set(newNode, p); // CAS piggyback
+ if (NEXT.compareAndSet(p, null, newNode)) {
// Successful CAS is the linearization point
// for e to become an element of this deque,
// and for newNode to become "live".
- if (p != t) // hop two nodes at a time
- casTail(t, newNode); // Failure is OK.
+ if (p != t) // hop two nodes at a time; failure is OK
+ TAIL.weakCompareAndSetVolatile(this, t, newNode);
return;
}
// Lost CAS race to another thread; re-read next
@@ -516,8 +477,8 @@
updateTail(); // Ensure x is not reachable from tail
// Finally, actually gc-unlink
- x.lazySetPrev(isFirst ? prevTerminator() : x);
- x.lazySetNext(isLast ? nextTerminator() : x);
+ PREV.setRelease(x, isFirst ? prevTerminator() : x);
+ NEXT.setRelease(x, isLast ? nextTerminator() : x);
}
}
}
@@ -531,7 +492,8 @@
// assert first.item == null;
for (Node<E> o = null, p = next, q;;) {
if (p.item != null || (q = p.next) == null) {
- if (o != null && p.prev != p && first.casNext(next, p)) {
+ if (o != null && p.prev != p &&
+ NEXT.compareAndSet(first, next, p)) {
skipDeletedPredecessors(p);
if (first.prev == null &&
(p.next == null || p.item != null) &&
@@ -541,8 +503,8 @@
updateTail(); // Ensure o is not reachable from tail
// Finally, actually gc-unlink
- o.lazySetNext(o);
- o.lazySetPrev(prevTerminator());
+ NEXT.setRelease(o, o);
+ PREV.setRelease(o, prevTerminator());
}
}
return;
@@ -565,7 +527,8 @@
// assert last.item == null;
for (Node<E> o = null, p = prev, q;;) {
if (p.item != null || (q = p.prev) == null) {
- if (o != null && p.next != p && last.casPrev(prev, p)) {
+ if (o != null && p.next != p &&
+ PREV.compareAndSet(last, prev, p)) {
skipDeletedSuccessors(p);
if (last.next == null &&
(p.prev == null || p.item != null) &&
@@ -575,8 +538,8 @@
updateTail(); // Ensure o is not reachable from tail
// Finally, actually gc-unlink
- o.lazySetPrev(o);
- o.lazySetNext(nextTerminator());
+ PREV.setRelease(o, o);
+ NEXT.setRelease(o, nextTerminator());
}
}
return;
@@ -607,7 +570,7 @@
(q = (p = q).prev) == null) {
// It is possible that p is PREV_TERMINATOR,
// but if so, the CAS is guaranteed to fail.
- if (casHead(h, p))
+ if (HEAD.compareAndSet(this, h, p))
return;
else
continue restartFromHead;
@@ -637,7 +600,7 @@
(q = (p = q).next) == null) {
// It is possible that p is NEXT_TERMINATOR,
// but if so, the CAS is guaranteed to fail.
- if (casTail(t, p))
+ if (TAIL.compareAndSet(this, t, p))
return;
else
continue restartFromTail;
@@ -675,7 +638,7 @@
}
// found active CAS target
- if (prev == p || x.casPrev(prev, p))
+ if (prev == p || PREV.compareAndSet(x, prev, p))
return;
} while (x.item != null || x.next == null);
@@ -706,7 +669,7 @@
}
// found active CAS target
- if (next == p || x.casNext(next, p))
+ if (next == p || NEXT.compareAndSet(x, next, p))
return;
} while (x.item != null || x.prev == null);
@@ -751,7 +714,7 @@
else if (p == h
// It is possible that p is PREV_TERMINATOR,
// but if so, the CAS is guaranteed to fail.
- || casHead(h, p))
+ || HEAD.compareAndSet(this, h, p))
return p;
else
continue restartFromHead;
@@ -776,7 +739,7 @@
else if (p == t
// It is possible that p is NEXT_TERMINATOR,
// but if so, the CAS is guaranteed to fail.
- || casTail(t, p))
+ || TAIL.compareAndSet(this, t, p))
return p;
else
continue restartFromTail;
@@ -802,7 +765,7 @@
* Constructs an empty deque.
*/
public ConcurrentLinkedDeque() {
- head = tail = new Node<E>(null);
+ head = tail = new Node<E>();
}
/**
@@ -818,12 +781,12 @@
// Copy c into a private chain of Nodes
Node<E> h = null, t = null;
for (E e : c) {
- Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
+ Node<E> newNode = newNode(Objects.requireNonNull(e));
if (h == null)
h = t = newNode;
else {
- t.lazySetNext(newNode);
- newNode.lazySetPrev(t);
+ NEXT.set(t, newNode);
+ PREV.set(newNode, t);
t = newNode;
}
}
@@ -836,12 +799,12 @@
private void initHeadTail(Node<E> h, Node<E> t) {
if (h == t) {
if (h == null)
- h = t = new Node<E>(null);
+ h = t = new Node<E>();
else {
// Avoid edge case of a single Node with non-null item.
- Node<E> newNode = new Node<E>(null);
- t.lazySetNext(newNode);
- newNode.lazySetPrev(t);
+ Node<E> newNode = new Node<E>();
+ NEXT.set(t, newNode);
+ PREV.set(newNode, t);
t = newNode;
}
}
@@ -934,7 +897,7 @@
public E pollFirst() {
for (Node<E> p = first(); p != null; p = succ(p)) {
E item = p.item;
- if (item != null && p.casItem(item, null)) {
+ if (item != null && ITEM.compareAndSet(p, item, null)) {
unlink(p);
return item;
}
@@ -945,7 +908,7 @@
public E pollLast() {
for (Node<E> p = last(); p != null; p = pred(p)) {
E item = p.item;
- if (item != null && p.casItem(item, null)) {
+ if (item != null && ITEM.compareAndSet(p, item, null)) {
unlink(p);
return item;
}
@@ -1031,7 +994,8 @@
Objects.requireNonNull(o);
for (Node<E> p = first(); p != null; p = succ(p)) {
E item = p.item;
- if (item != null && o.equals(item) && p.casItem(item, null)) {
+ if (item != null && o.equals(item) &&
+ ITEM.compareAndSet(p, item, null)) {
unlink(p);
return true;
}
@@ -1055,7 +1019,8 @@
Objects.requireNonNull(o);
for (Node<E> p = last(); p != null; p = pred(p)) {
E item = p.item;
- if (item != null && o.equals(item) && p.casItem(item, null)) {
+ if (item != null && o.equals(item) &&
+ ITEM.compareAndSet(p, item, null)) {
unlink(p);
return true;
}
@@ -1159,12 +1124,12 @@
// Copy c into a private chain of Nodes
Node<E> beginningOfTheEnd = null, last = null;
for (E e : c) {
- Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
+ Node<E> newNode = newNode(Objects.requireNonNull(e));
if (beginningOfTheEnd == null)
beginningOfTheEnd = last = newNode;
else {
- last.lazySetNext(newNode);
- newNode.lazySetPrev(last);
+ NEXT.set(last, newNode);
+ PREV.set(newNode, last);
last = newNode;
}
}
@@ -1184,16 +1149,16 @@
continue restartFromTail;
else {
// p is last node
- beginningOfTheEnd.lazySetPrev(p); // CAS piggyback
- if (p.casNext(null, beginningOfTheEnd)) {
+ PREV.set(beginningOfTheEnd, p); // CAS piggyback
+ if (NEXT.compareAndSet(p, null, beginningOfTheEnd)) {
// Successful CAS is the linearization point
// for all elements to be added to this deque.
- if (!casTail(t, last)) {
+ if (!TAIL.weakCompareAndSetVolatile(this, t, last)) {
// Try a little harder to update tail,
// since we may be adding many elements.
t = tail;
if (last.next == null)
- casTail(t, last);
+ TAIL.weakCompareAndSetVolatile(this, t, last);
}
return true;
}
@@ -1586,41 +1551,38 @@
Node<E> h = null, t = null;
for (Object item; (item = s.readObject()) != null; ) {
@SuppressWarnings("unchecked")
- Node<E> newNode = new Node<E>((E) item);
+ Node<E> 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<E> cmp, Node<E> val) {
- return U.compareAndSwapObject(this, HEAD, cmp, val);
- }
-
- private boolean casTail(Node<E> cmp, Node<E> 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<Object>();
PREV_TERMINATOR.next = PREV_TERMINATOR;
NEXT_TERMINATOR = new Node<Object>();
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);
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentLinkedQueue.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,6 +35,8 @@
package java.util.concurrent;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.util.AbstractQueue;
import java.util.Arrays;
import java.util.Collection;
@@ -166,9 +168,8 @@
* this is merely an optimization.
*
* When constructing a Node (before enqueuing it) we avoid paying
- * for a volatile write to item by using Unsafe.putObject instead
- * of a normal write. This allows the cost of enqueue to be
- * "one-and-a-half" CASes.
+ * for a volatile write to item. This allows the cost of enqueue
+ * to be "one-and-a-half" CASes.
*
* Both head and tail may or may not point to a Node with a
* non-null item. If the queue is empty, all items must of course
@@ -178,33 +179,21 @@
* optimization.
*/
- private static class Node<E> {
+ static final class Node<E> {
volatile E item;
volatile Node<E> 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 <E> Node<E> newNode(E item) {
Node<E> node = new Node<E>();
- U.putObject(node, ITEM, item);
+ ITEM.set(node, item);
return node;
}
- static <E> boolean casItem(Node<E> node, E cmp, E val) {
- return U.compareAndSwapObject(node, ITEM, cmp, val);
- }
-
- static <E> void lazySetNext(Node<E> node, Node<E> val) {
- U.putObjectRelease(node, NEXT, val);
- }
-
- static <E> boolean casNext(Node<E> node, Node<E> cmp, Node<E> val) {
- return U.compareAndSwapObject(node, NEXT, cmp, val);
- }
-
/**
* A node from which the first live (non-deleted) node (if any)
* can be reached in O(1) time.
@@ -256,7 +245,7 @@
if (h == null)
h = t = newNode;
else {
- lazySetNext(t, newNode);
+ NEXT.set(t, newNode);
t = newNode;
}
}
@@ -286,8 +275,8 @@
*/
final void updateHead(Node<E> h, Node<E> p) {
// assert h != null && p != null && (h == p || h.item == null);
- if (h != p && casHead(h, p))
- lazySetNext(h, h);
+ if (h != p && HEAD.compareAndSet(this, h, p))
+ NEXT.setRelease(h, h);
}
/**
@@ -314,12 +303,12 @@
Node<E> q = p.next;
if (q == null) {
// p is last node
- if (casNext(p, null, newNode)) {
+ if (NEXT.compareAndSet(p, null, newNode)) {
// Successful CAS is the linearization point
// for e to become an element of this queue,
// and for newNode to become "live".
- if (p != t) // hop two nodes at a time
- casTail(t, newNode); // Failure is OK.
+ if (p != t) // hop two nodes at a time; failure is OK
+ TAIL.weakCompareAndSetVolatile(this, t, newNode);
return true;
}
// Lost CAS race to another thread; re-read next
@@ -342,7 +331,7 @@
for (Node<E> h = head, p = h, q;;) {
E item = p.item;
- if (item != null && casItem(p, item, null)) {
+ if (item != null && ITEM.compareAndSet(p, item, null)) {
// Successful CAS is the linearization point
// for item to be removed from this queue.
if (p != h) // hop two nodes at a time
@@ -483,12 +472,12 @@
next = succ(p);
continue;
}
- removed = casItem(p, item, null);
+ removed = ITEM.compareAndSet(p, item, null);
}
next = succ(p);
if (pred != null && next != null) // unlink
- casNext(pred, p, next);
+ NEXT.weakCompareAndSetVolatile(pred, p, next);
if (removed)
return true;
}
@@ -520,7 +509,7 @@
if (beginningOfTheEnd == null)
beginningOfTheEnd = last = newNode;
else {
- lazySetNext(last, newNode);
+ NEXT.set(last, newNode);
last = newNode;
}
}
@@ -532,15 +521,15 @@
Node<E> q = p.next;
if (q == null) {
// p is last node
- if (casNext(p, null, beginningOfTheEnd)) {
+ if (NEXT.compareAndSet(p, null, beginningOfTheEnd)) {
// Successful CAS is the linearization point
// for all elements to be added to this queue.
- if (!casTail(t, last)) {
+ if (!TAIL.weakCompareAndSetVolatile(this, t, last)) {
// Try a little harder to update tail,
// since we may be adding many elements.
t = tail;
if (last.next == null)
- casTail(t, last);
+ TAIL.weakCompareAndSetVolatile(this, t, last);
}
return true;
}
@@ -744,7 +733,7 @@
}
// unlink deleted nodes
if ((q = succ(p)) != null)
- casNext(pred, p, q);
+ NEXT.compareAndSet(pred, p, q);
}
}
@@ -801,7 +790,7 @@
if (h == null)
h = t = newNode;
else {
- lazySetNext(t, newNode);
+ NEXT.set(t, newNode);
t = newNode;
}
}
@@ -919,31 +908,20 @@
return new CLQSpliterator<E>(this);
}
- private boolean casTail(Node<E> cmp, Node<E> val) {
- return U.compareAndSwapObject(this, TAIL, cmp, val);
- }
-
- private boolean casHead(Node<E> cmp, Node<E> 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);
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListMap.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,6 +35,8 @@
package java.util.concurrent;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.io.Serializable;
import java.util.AbstractCollection;
import java.util.AbstractMap;
@@ -401,7 +403,7 @@
* compareAndSet head node.
*/
private boolean casHead(HeadIndex<K,V> cmp, HeadIndex<K,V> val) {
- return U.compareAndSwapObject(this, HEAD, cmp, val);
+ return HEAD.compareAndSet(this, cmp, val);
}
/* ---------------- Nodes -------------- */
@@ -444,14 +446,14 @@
* compareAndSet value field.
*/
boolean casValue(Object cmp, Object val) {
- return U.compareAndSwapObject(this, VALUE, cmp, val);
+ return VALUE.compareAndSet(this, cmp, val);
}
/**
* compareAndSet next field.
*/
boolean casNext(Node<K,V> cmp, Node<K,V> val) {
- return U.compareAndSwapObject(this, NEXT, cmp, val);
+ return NEXT.compareAndSet(this, cmp, val);
}
/**
@@ -532,20 +534,16 @@
return new AbstractMap.SimpleImmutableEntry<K,V>(key, vv);
}
- // Unsafe mechanics
-
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long VALUE;
- private static final long NEXT;
-
+ // VarHandle mechanics
+ private static final VarHandle VALUE;
+ private static final VarHandle NEXT;
static {
try {
- VALUE = U.objectFieldOffset
- (Node.class.getDeclaredField("value"));
- NEXT = U.objectFieldOffset
- (Node.class.getDeclaredField("next"));
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ VALUE = l.findVarHandle(Node.class, "value", Object.class);
+ NEXT = l.findVarHandle(Node.class, "next", Node.class);
} catch (ReflectiveOperationException e) {
- throw new Error(e);
+ throw new Error(e);
}
}
}
@@ -577,7 +575,7 @@
* compareAndSet right field.
*/
final boolean casRight(Index<K,V> cmp, Index<K,V> val) {
- return U.compareAndSwapObject(this, RIGHT, cmp, val);
+ return RIGHT.compareAndSet(this, cmp, val);
}
/**
@@ -613,13 +611,12 @@
return node.value != null && casRight(succ, succ.right);
}
- // Unsafe mechanics
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long RIGHT;
+ // VarHandle mechanics
+ private static final VarHandle RIGHT;
static {
try {
- RIGHT = U.objectFieldOffset
- (Index.class.getDeclaredField("right"));
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ RIGHT = l.findVarHandle(Index.class, "right", Index.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
@@ -3607,13 +3604,13 @@
}
}
- // Unsafe mechanics
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long HEAD;
+ // VarHandle mechanics
+ private static final VarHandle HEAD;
static {
try {
- HEAD = U.objectFieldOffset
- (ConcurrentSkipListMap.class.getDeclaredField("head"));
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ HEAD = l.findVarHandle(ConcurrentSkipListMap.class, "head",
+ HeadIndex.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/ConcurrentSkipListSet.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,6 +35,8 @@
package java.util.concurrent;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Collections;
@@ -507,15 +509,16 @@
// Support for resetting map in clone
private void setMap(ConcurrentNavigableMap<E,Object> 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);
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/CopyOnWriteArrayList.java Thu Jul 21 20:09:19 2016 -0700
@@ -34,6 +34,7 @@
package java.util.concurrent;
+import java.lang.reflect.Field;
import java.util.AbstractList;
import java.util.Arrays;
import java.util.Collection;
@@ -1541,17 +1542,21 @@
}
}
- // Support for resetting lock while deserializing
+ /** Initializes the lock; for use when deserializing or cloning. */
private void resetLock() {
- U.putObjectVolatile(this, LOCK, new Object());
- }
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long LOCK;
- static {
+ Field lockField = java.security.AccessController.doPrivileged(
+ (java.security.PrivilegedAction<Field>) () -> {
+ 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);
}
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/CountedCompleter.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,6 +35,9 @@
package java.util.concurrent;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
/**
* A {@link ForkJoinTask} with a completion action performed when
* triggered and there are no remaining pending actions.
@@ -524,7 +527,7 @@
* @param delta the value to add
*/
public final void addToPendingCount(int delta) {
- U.getAndAddInt(this, PENDING, delta);
+ PENDING.getAndAdd(this, delta);
}
/**
@@ -536,7 +539,7 @@
* @return {@code true} if successful
*/
public final boolean compareAndSetPendingCount(int expected, int count) {
- return U.compareAndSwapInt(this, PENDING, expected, count);
+ return PENDING.compareAndSet(this, expected, count);
}
/**
@@ -548,7 +551,7 @@
public final int decrementPendingCountUnlessZero() {
int c;
do {} while ((c = pending) != 0 &&
- !U.compareAndSwapInt(this, PENDING, c, c - 1));
+ !PENDING.weakCompareAndSetVolatile(this, c, c - 1));
return c;
}
@@ -581,7 +584,7 @@
return;
}
}
- else if (U.compareAndSwapInt(a, PENDING, c, c - 1))
+ else if (PENDING.weakCompareAndSetVolatile(a, c, c - 1))
return;
}
}
@@ -604,7 +607,7 @@
return;
}
}
- else if (U.compareAndSwapInt(a, PENDING, c, c - 1))
+ else if (PENDING.weakCompareAndSetVolatile(a, c, c - 1))
return;
}
}
@@ -649,7 +652,7 @@
for (int c;;) {
if ((c = pending) == 0)
return this;
- else if (U.compareAndSwapInt(this, PENDING, c, c - 1))
+ else if (PENDING.weakCompareAndSetVolatile(this, c, c - 1))
return null;
}
}
@@ -753,13 +756,13 @@
*/
protected void setRawResult(T t) { }
- // Unsafe mechanics
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long PENDING;
+ // VarHandle mechanics
+ private static final VarHandle PENDING;
static {
try {
- PENDING = U.objectFieldOffset
- (CountedCompleter.class.getDeclaredField("pending"));
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ PENDING = l.findVarHandle(CountedCompleter.class, "pending", int.class);
+
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/Exchanger.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/Exchanger.java Thu Jul 21 20:09:19 2016 -0700
@@ -36,6 +36,10 @@
package java.util.concurrent;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import java.util.concurrent.locks.LockSupport;
+
/**
* A synchronization point at which threads can pair and swap elements
* within pairs. Each thread presents some object on entry to the
@@ -155,9 +159,7 @@
* a value that is enough for common platforms. Additionally,
* extra care elsewhere is taken to avoid other false/unintended
* sharing and to enhance locality, including adding padding (via
- * @Contended) to Nodes, embedding "bound" as an Exchanger field,
- * and reworking some park/unpark mechanics compared to
- * LockSupport versions.
+ * @Contended) to Nodes, embedding "bound" as an Exchanger field.
*
* The arena starts out with only one used slot. We expand the
* effective arena size by tracking collisions; i.e., failed CASes
@@ -233,29 +235,23 @@
* As is too common in this sort of code, methods are monolithic
* 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
- * 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
- * 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
- * uses by the owning thread are otherwise acceptably ordered by
- * other operations. (Because the actual points of atomicity are
- * slot CASes, it would also be legal for the write to Node.match
- * in a release to be weaker than a full volatile write. However,
- * this is not done because it could allow further postponement of
- * the write, delaying progress.)
+ * mainly, here, bulky spin->yield->block/cancel code. 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 uses by the owning
+ * thread are otherwise acceptably ordered by other operations.
+ * (Because the actual points of atomicity are slot CASes, it
+ * would also be legal for the write to Node.match in a release to
+ * be weaker than a full volatile write. However, this is not done
+ * because it could allow further postponement of the write,
+ * delaying progress.)
*/
/**
- * 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 +352,31 @@
*/
private final Object arenaExchange(Object item, boolean timed, long ns) {
Node[] a = arena;
+ int alen = a.length;
Node p = participant.get();
for (int i = p.index;;) { // access slot at i
- int b, m, c; long j; // j is raw array offset
- Node q = (Node)U.getObjectVolatile(a, j = (i << ASHIFT) + ABASE);
- if (q != null && U.compareAndSwapObject(a, j, q, null)) {
+ int b, m, c;
+ int j = (i << ASHIFT) + ((1 << ASHIFT) - 1);
+ if (j < 0 || j >= alen)
+ j = alen - 1;
+ Node q = (Node)AA.getAcquire(a, j);
+ if (q != null && AA.compareAndSet(a, j, q, null)) {
Object v = q.item; // release
q.match = item;
Thread w = q.parked;
if (w != null)
- U.unpark(w);
+ LockSupport.unpark(w);
return v;
}
else if (i <= (m = (b = bound) & MMASK) && q == null) {
p.item = item; // offer
- if (U.compareAndSwapObject(a, j, null, p)) {
+ if (AA.compareAndSet(a, j, null, p)) {
long end = (timed && m == 0) ? System.nanoTime() + ns : 0L;
Thread t = Thread.currentThread(); // wait
for (int h = p.hash, spins = SPINS;;) {
Object v = p.match;
if (v != null) {
- U.putObjectRelease(p, MATCH, null);
+ MATCH.setRelease(p, null);
p.item = null; // clear for next use
p.hash = h;
return v;
@@ -389,22 +389,24 @@
(--spins & ((SPINS >>> 1) - 1)) == 0)
Thread.yield(); // two yields per wait
}
- else if (U.getObjectVolatile(a, j) != p)
+ else if (AA.getAcquire(a, j) != p)
spins = SPINS; // releaser hasn't set match yet
else if (!t.isInterrupted() && m == 0 &&
(!timed ||
(ns = end - System.nanoTime()) > 0L)) {
- U.putObject(t, BLOCKER, this); // emulate LockSupport
p.parked = t; // minimize window
- if (U.getObjectVolatile(a, j) == p)
- U.park(false, ns);
+ if (AA.getAcquire(a, j) == p) {
+ if (ns == 0L)
+ LockSupport.park(this);
+ else
+ LockSupport.parkNanos(this, ns);
+ }
p.parked = null;
- U.putObject(t, BLOCKER, null);
}
- else if (U.getObjectVolatile(a, j) == p &&
- U.compareAndSwapObject(a, j, p, null)) {
+ else if (AA.getAcquire(a, j) == p &&
+ AA.compareAndSet(a, j, p, null)) {
if (m != 0) // try to shrink
- U.compareAndSwapInt(this, BOUND, b, b + SEQ - 1);
+ BOUND.compareAndSet(this, b, b + SEQ - 1);
p.item = null;
p.hash = h;
i = p.index >>>= 1; // descend
@@ -426,7 +428,7 @@
i = (i != m || m == 0) ? m : m - 1;
}
else if ((c = p.collides) < m || m == FULL ||
- !U.compareAndSwapInt(this, BOUND, b, b + SEQ + 1)) {
+ !BOUND.compareAndSet(this, b, b + SEQ + 1)) {
p.collides = c + 1;
i = (i == 0) ? m : i - 1; // cyclically traverse
}
@@ -455,24 +457,24 @@
for (Node q;;) {
if ((q = slot) != null) {
- if (U.compareAndSwapObject(this, SLOT, q, null)) {
+ if (SLOT.compareAndSet(this, q, null)) {
Object v = q.item;
q.match = item;
Thread w = q.parked;
if (w != null)
- U.unpark(w);
+ LockSupport.unpark(w);
return v;
}
// create arena on contention, but continue until slot null
if (NCPU > 1 && bound == 0 &&
- U.compareAndSwapInt(this, BOUND, 0, SEQ))
+ BOUND.compareAndSet(this, 0, SEQ))
arena = new Node[(FULL + 2) << ASHIFT];
}
else if (arena != null)
return null; // caller must reroute to arenaExchange
else {
p.item = item;
- if (U.compareAndSwapObject(this, SLOT, null, p))
+ if (SLOT.compareAndSet(this, null, p))
break;
p.item = null;
}
@@ -495,19 +497,21 @@
spins = SPINS;
else if (!t.isInterrupted() && arena == null &&
(!timed || (ns = end - System.nanoTime()) > 0L)) {
- U.putObject(t, BLOCKER, this);
p.parked = t;
- if (slot == p)
- U.park(false, ns);
+ if (slot == p) {
+ if (ns == 0L)
+ LockSupport.park(this);
+ else
+ LockSupport.parkNanos(this, ns);
+ }
p.parked = null;
- U.putObject(t, BLOCKER, null);
}
- else if (U.compareAndSwapObject(this, SLOT, p, null)) {
+ else if (SLOT.compareAndSet(this, p, null)) {
v = timed && ns <= 0L && !t.isInterrupted() ? TIMED_OUT : null;
break;
}
}
- U.putObjectRelease(p, MATCH, null);
+ MATCH.setRelease(p, null);
p.item = null;
p.hash = h;
return v;
@@ -556,8 +560,9 @@
@SuppressWarnings("unchecked")
public V exchange(V x) throws InterruptedException {
Object v;
+ Node[] a;
Object item = (x == null) ? NULL_ITEM : x; // translate null args
- if ((arena != null ||
+ if (((a = arena) != null ||
(v = slotExchange(item, false, 0L)) == null) &&
((Thread.interrupted() || // disambiguates null return
(v = arenaExchange(item, false, 0L)) == null)))
@@ -623,31 +628,18 @@
return (v == NULL_ITEM) ? null : (V)v;
}
- // Unsafe mechanics
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long BOUND;
- private static final long SLOT;
- private static final long MATCH;
- private static final long BLOCKER;
- private static final int ABASE;
+ // VarHandle mechanics
+ private static final VarHandle BOUND;
+ private static final VarHandle SLOT;
+ private static final VarHandle MATCH;
+ private static final VarHandle AA;
static {
try {
- BOUND = U.objectFieldOffset
- (Exchanger.class.getDeclaredField("bound"));
- SLOT = U.objectFieldOffset
- (Exchanger.class.getDeclaredField("slot"));
-
- MATCH = U.objectFieldOffset
- (Node.class.getDeclaredField("match"));
-
- BLOCKER = U.objectFieldOffset
- (Thread.class.getDeclaredField("parkBlocker"));
-
- int scale = U.arrayIndexScale(Node[].class);
- if ((scale & (scale - 1)) != 0 || scale > (1 << ASHIFT))
- throw new Error("Unsupported array scale");
- // ABASE absorbs padding in front of element 0
- ABASE = U.arrayBaseOffset(Node[].class) + (1 << ASHIFT);
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ BOUND = l.findVarHandle(Exchanger.class, "bound", int.class);
+ SLOT = l.findVarHandle(Exchanger.class, "slot", Node.class);
+ MATCH = l.findVarHandle(Node.class, "match", Object.class);
+ AA = MethodHandles.arrayElementVarHandle(Node[].class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinPool.java Thu Jul 21 20:09:19 2016 -0700
@@ -36,6 +36,8 @@
package java.util.concurrent;
import java.lang.Thread.UncaughtExceptionHandler;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.security.AccessControlContext;
import java.security.Permissions;
import java.security.ProtectionDomain;
@@ -44,7 +46,11 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.Predicate;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.CountedCompleter;
+import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.ForkJoinWorkerThread;
import java.util.concurrent.locks.LockSupport;
/**
@@ -81,7 +87,9 @@
* However, no such adjustments are guaranteed in the face of blocked
* I/O or other unmanaged synchronization. The nested {@link
* ManagedBlocker} interface enables extension of the kinds of
- * synchronization accommodated.
+ * synchronization accommodated. The default policies may be
+ * overridden using a constructor with parameters corresponding to
+ * those documented in class {@link ThreadPoolExecutor}.
*
* <p>In addition to execution and lifecycle control methods, this
* class provides status check methods (for example
@@ -162,7 +170,6 @@
* @since 1.7
* @author Doug Lea
*/
-@jdk.internal.vm.annotation.Contended
public class ForkJoinPool extends AbstractExecutorService {
/*
@@ -229,10 +236,9 @@
* (CAS slot to null))
* increment base and return task;
*
- * There are several variants of each of these; for example most
- * versions of poll pre-screen the CAS by rechecking that the base
- * has not changed since reading the slot, and most methods only
- * attempt the CAS if base appears not to be equal to top.
+ * There are several variants of each of these. In particular,
+ * almost all uses of poll occur within scan operations that also
+ * interleave contention tracking (with associated code sprawl.)
*
* Memory ordering. See "Correct and Efficient Work-Stealing for
* Weak Memory Models" by Le, Pop, Cohen, and Nardelli, PPoPP 2013
@@ -264,10 +270,7 @@
* thief chooses a different random victim target to try next. So,
* in order for one thief to progress, it suffices for any
* in-progress poll or new push on any empty queue to
- * complete. (This is why we normally use method pollAt and its
- * variants that try once at the apparent base index, else
- * consider alternative actions, rather than method poll, which
- * retries.)
+ * complete.
*
* This approach also enables support of a user mode in which
* local task processing is in FIFO, not LIFO order, simply by
@@ -282,16 +285,13 @@
* choosing existing queues, and may be randomly repositioned upon
* contention with other submitters. In essence, submitters act
* like workers except that they are restricted to executing local
- * tasks that they submitted (or in the case of CountedCompleters,
- * others with the same root task). Insertion of tasks in shared
- * mode requires a lock but we use only a simple spinlock (using
- * field qlock), because submitters encountering a busy queue move
- * on to try or create other queues -- they block only when
- * creating and registering new queues. Because it is used only as
- * a spinlock, unlocking requires only a "releasing" store (using
- * putIntRelease). The qlock is also used during termination
- * detection, in which case it is forced to a negative
- * non-lockable value.
+ * tasks that they submitted. Insertion of tasks in shared mode
+ * requires a lock but we use only a simple spinlock (using field
+ * phase), because submitters encountering a busy queue move to a
+ * different position to use or create other queues -- they block
+ * only when creating and registering new queues. Because it is
+ * used only as a spinlock, unlocking requires only a "releasing"
+ * store (using setRelease).
*
* Management
* ==========
@@ -305,42 +305,34 @@
* There are only a few properties that we can globally track or
* maintain, so we pack them into a small number of variables,
* often maintaining atomicity without blocking or locking.
- * Nearly all essentially atomic control state is held in two
+ * Nearly all essentially atomic control state is held in a few
* volatile variables that are by far most often read (not
- * written) as status and consistency checks. (Also, field
- * "config" holds unchanging configuration state.)
+ * written) as status and consistency checks. We pack as much
+ * information into them as we can.
*
* Field "ctl" contains 64 bits holding information needed to
- * atomically decide to add, inactivate, enqueue (on an event
- * queue), dequeue, and/or re-activate workers. To enable this
+ * atomically decide to add, enqueue (on an event queue), and
+ * dequeue (and release)-activate workers. To enable this
* packing, we restrict maximum parallelism to (1<<15)-1 (which is
* far in excess of normal operating range) to allow ids, counts,
* and their negations (used for thresholding) to fit into 16bit
* subfields.
*
- * Field "runState" holds lifetime status, atomically and
- * monotonically setting STARTED, SHUTDOWN, STOP, and finally
- * TERMINATED bits.
- *
- * Field "auxState" is a ReentrantLock subclass that also
- * opportunistically holds some other bookkeeping fields accessed
- * only when locked. It is mainly used to lock (infrequent)
- * updates to workQueues. The auxState instance is itself lazily
- * constructed (see tryInitialize), requiring a double-check-style
- * bootstrapping use of field runState, and locking a private
- * static.
+ * Field "mode" holds configuration parameters as well as lifetime
+ * status, atomically and monotonically setting SHUTDOWN, STOP,
+ * and finally TERMINATED bits.
*
* Field "workQueues" holds references to WorkQueues. It is
- * updated (only during worker creation and termination) under the
- * lock, but is otherwise concurrently readable, and accessed
- * directly. We also ensure that reads of the array reference
- * itself never become too stale (for example, re-reading before
- * each scan). To simplify index-based operations, the array size
- * is always a power of two, and all readers must tolerate null
- * slots. Worker queues are at odd indices. Shared (submission)
- * queues are at even indices, up to a maximum of 64 slots, to
- * limit growth even if array needs to expand to add more
- * workers. Grouping them together in this way simplifies and
+ * updated (only during worker creation and termination) under
+ * lock (using field workerNamePrefix as lock), but is otherwise
+ * concurrently readable, and accessed directly. We also ensure
+ * that uses of the array reference itself never become too stale
+ * in case of resizing. To simplify index-based operations, the
+ * array size is always a power of two, and all readers must
+ * tolerate null slots. Worker queues are at odd indices. Shared
+ * (submission) queues are at even indices, up to a maximum of 64
+ * slots, to limit growth even if array needs to expand to add
+ * more workers. Grouping them together in this way simplifies and
* speeds up task scanning.
*
* All worker thread creation is on-demand, triggered by task
@@ -360,30 +352,37 @@
* workers unless there appear to be tasks available. On the
* other hand, we must quickly prod them into action when new
* tasks are submitted or generated. In many usages, ramp-up time
- * to activate workers is the main limiting factor in overall
- * performance, which is compounded at program start-up by JIT
- * compilation and allocation. So we streamline this as much as
- * possible.
+ * is the main limiting factor in overall performance, which is
+ * compounded at program start-up by JIT compilation and
+ * allocation. So we streamline this as much as possible.
*
- * The "ctl" field atomically maintains active and total worker
- * counts as well as a queue to place waiting threads so they can
- * be located for signalling. Active counts also play the role of
- * quiescence indicators, so are decremented when workers believe
- * that there are no more tasks to execute. The "queue" is
- * actually a form of Treiber stack. A stack is ideal for
- * activating threads in most-recently used order. This improves
+ * The "ctl" field atomically maintains total worker and
+ * "released" worker counts, plus the head of the available worker
+ * queue (actually stack, represented by the lower 32bit subfield
+ * of ctl). Released workers are those known to be scanning for
+ * and/or running tasks. Unreleased ("available") workers are
+ * recorded in the ctl stack. These workers are made available for
+ * signalling by enqueuing in ctl (see method runWorker). The
+ * "queue" is a form of Treiber stack. This is ideal for
+ * activating threads in most-recently used order, and improves
* performance and locality, outweighing the disadvantages of
* being prone to contention and inability to release a worker
- * unless it is topmost on stack. We block/unblock workers after
- * pushing on the idle worker stack (represented by the lower
- * 32bit subfield of ctl) when they cannot find work. The top
- * stack state holds the value of the "scanState" field of the
- * worker: its index and status, plus a version counter that, in
- * addition to the count subfields (also serving as version
- * stamps) provide protection against Treiber stack ABA effects.
+ * unless it is topmost on stack. To avoid missed signal problems
+ * inherent in any wait/signal design, available workers rescan
+ * for (and if found run) tasks after enqueuing. Normally their
+ * release status will be updated while doing so, but the released
+ * worker ctl count may underestimate the number of active
+ * threads. (However, it is still possible to determine quiescence
+ * via a validation traversal -- see isQuiescent). After an
+ * unsuccessful rescan, available workers are blocked until
+ * signalled (see signalWork). The top stack state holds the
+ * value of the "phase" field of the worker: its index and status,
+ * plus a version counter that, in addition to the count subfields
+ * (also serving as version stamps) provide protection against
+ * Treiber stack ABA effects.
*
- * Creating workers. To create a worker, we pre-increment total
- * count (serving as a reservation), and attempt to construct a
+ * Creating workers. To create a worker, we pre-increment counts
+ * (serving as a reservation), and attempt to construct a
* ForkJoinWorkerThread via its factory. Upon construction, the
* new thread invokes registerWorker, where it constructs a
* WorkQueue and is assigned an index in the workQueues array
@@ -405,16 +404,15 @@
* submission queues for existing external threads (see
* externalPush).
*
- * WorkQueue field scanState is used by both workers and the pool
- * to manage and track whether a worker is UNSIGNALLED (possibly
- * blocked waiting for a signal). When a worker is inactivated,
- * its scanState field is set, and is prevented from executing
- * tasks, even though it must scan once for them to avoid queuing
- * races. Note that scanState updates lag queue CAS releases so
- * usage requires care. When queued, the lower 16 bits of
- * scanState must hold its pool index. So we place the index there
- * upon initialization (see registerWorker) and otherwise keep it
- * there or restore it when necessary.
+ * WorkQueue field "phase" is used by both workers and the pool to
+ * manage and track whether a worker is UNSIGNALLED (possibly
+ * blocked waiting for a signal). When a worker is enqueued its
+ * phase field is set. Note that phase field updates lag queue CAS
+ * releases so usage requires care -- seeing a negative phase does
+ * not guarantee that the worker is available. When queued, the
+ * lower 16 bits of scanState must hold its pool index. So we
+ * place the index there upon initialization (see registerWorker)
+ * and otherwise keep it there or restore it when necessary.
*
* The ctl field also serves as the basis for memory
* synchronization surrounding activation. This uses a more
@@ -423,15 +421,14 @@
* if to its current value). This would be extremely costly. So
* we relax it in several ways: (1) Producers only signal when
* their queue is empty. Other workers propagate this signal (in
- * method scan) when they find tasks. (2) Workers only enqueue
- * after scanning (see below) and not finding any tasks. (3)
- * Rather than CASing ctl to its current value in the common case
- * where no action is required, we reduce write contention by
- * equivalently prefacing signalWork when called by an external
- * task producer using a memory access with full-volatile
- * semantics or a "fullFence". (4) For internal task producers we
- * rely on the fact that even if no other workers awaken, the
- * producer itself will eventually see the task and execute it.
+ * method scan) when they find tasks; to further reduce flailing,
+ * each worker signals only one other per activation. (2) Workers
+ * only enqueue after scanning (see below) and not finding any
+ * tasks. (3) Rather than CASing ctl to its current value in the
+ * common case where no action is required, we reduce write
+ * contention by equivalently prefacing signalWork when called by
+ * an external task producer using a memory access with
+ * full-volatile semantics or a "fullFence".
*
* Almost always, too many signals are issued. A task producer
* cannot in general tell if some existing worker is in the midst
@@ -443,64 +440,40 @@
* and bookkeeping bottlenecks during ramp-up, ramp-down, and small
* computations involving only a few workers.
*
- * Scanning. Method scan() performs top-level scanning for tasks.
- * Each scan traverses (and tries to poll from) each queue in
- * pseudorandom permutation order by randomly selecting an origin
- * index and a step value. (The pseudorandom generator need not
- * have high-quality statistical properties in the long term, but
- * just within computations; We use 64bit and 32bit Marsaglia
- * XorShifts, which are cheap and suffice here.) Scanning also
- * employs contention reduction: When scanning workers fail a CAS
- * polling for work, they soon restart with a different
- * pseudorandom scan order (thus likely retrying at different
- * intervals). This improves throughput when many threads are
- * trying to take tasks from few queues. Scans do not otherwise
- * explicitly take into account core affinities, loads, cache
- * localities, etc, However, they do exploit temporal locality
- * (which usually approximates these) by preferring to re-poll (up
- * to POLL_LIMIT times) from the same queue after a successful
- * poll before trying others. Restricted forms of scanning occur
- * in methods helpComplete and findNonEmptyStealQueue, and take
- * similar but simpler forms.
- *
- * Deactivation and waiting. Queuing encounters several intrinsic
- * races; most notably that an inactivating scanning worker can
- * miss seeing a task produced during a scan. So when a worker
- * cannot find a task to steal, it inactivates and enqueues, and
- * then rescans to ensure that it didn't miss one, reactivating
- * upon seeing one with probability approximately proportional to
- * probability of a miss. (In most cases, the worker will be
- * signalled before self-signalling, avoiding cascades of multiple
- * signals for the same task).
- *
- * Workers block (in method awaitWork) using park/unpark;
- * advertising the need for signallers to unpark by setting their
- * "parker" fields.
+ * Scanning. Method runWorker performs top-level scanning for
+ * tasks. Each scan traverses and tries to poll from each queue
+ * starting at a random index and circularly stepping. Scans are
+ * not performed in ideal random permutation order, to reduce
+ * cacheline contention. The pseudorandom generator need not have
+ * high-quality statistical properties in the long term, but just
+ * within computations; We use Marsaglia XorShifts (often via
+ * ThreadLocalRandom.nextSecondarySeed), which are cheap and
+ * suffice. Scanning also employs contention reduction: When
+ * scanning workers fail to extract an apparently existing task,
+ * they soon restart at a different pseudorandom index. This
+ * improves throughput when many threads are trying to take tasks
+ * from few queues, which can be common in some usages. Scans do
+ * not otherwise explicitly take into account core affinities,
+ * loads, cache localities, etc, However, they do exploit temporal
+ * locality (which usually approximates these) by preferring to
+ * re-poll (at most #workers times) from the same queue after a
+ * successful poll before trying others.
*
* Trimming workers. To release resources after periods of lack of
* use, a worker starting to wait when the pool is quiescent will
- * time out and terminate (see awaitWork) if the pool has remained
- * quiescent for period given by IDLE_TIMEOUT_MS, increasing the
- * period as the number of threads decreases, eventually removing
- * all workers.
+ * time out and terminate (see method scan) if the pool has
+ * remained quiescent for period given by field keepAlive.
*
* Shutdown and Termination. A call to shutdownNow invokes
* tryTerminate to atomically set a runState bit. The calling
* thread, as well as every other worker thereafter terminating,
- * helps terminate others by setting their (qlock) status,
- * cancelling their unprocessed tasks, and waking them up, doing
- * so repeatedly until stable. Calls to non-abrupt shutdown()
- * preface this by checking whether termination should commence.
- * This relies primarily on the active count bits of "ctl"
- * maintaining consensus -- tryTerminate is called from awaitWork
- * whenever quiescent. However, external submitters do not take
- * part in this consensus. So, tryTerminate sweeps through queues
- * (until stable) to ensure lack of in-flight submissions and
- * workers about to process them before triggering the "STOP"
- * phase of termination. (Note: there is an intrinsic conflict if
- * helpQuiescePool is called when shutdown is enabled. Both wait
- * for quiescence, but tryTerminate is biased to not trigger until
- * helpQuiescePool completes.)
+ * helps terminate others by cancelling their unprocessed tasks,
+ * and waking them up, doing so repeatedly until stable. Calls to
+ * non-abrupt shutdown() preface this by checking whether
+ * termination should commence by sweeping through queues (until
+ * stable) to ensure lack of in-flight submissions and workers
+ * about to process them before triggering the "STOP" phase of
+ * termination.
*
* Joining Tasks
* =============
@@ -508,12 +481,12 @@
* Any of several actions may be taken when one worker is waiting
* to join a task stolen (or always held) by another. Because we
* are multiplexing many tasks on to a pool of workers, we can't
- * just let them block (as in Thread.join). We also cannot just
- * reassign the joiner's run-time stack with another and replace
- * it later, which would be a form of "continuation", that even if
- * possible is not necessarily a good idea since we may need both
- * an unblocked task and its continuation to progress. Instead we
- * combine two tactics:
+ * always just let them block (as in Thread.join). We also cannot
+ * just reassign the joiner's run-time stack with another and
+ * replace it later, which would be a form of "continuation", that
+ * even if possible is not necessarily a good idea since we may
+ * need both an unblocked task and its continuation to progress.
+ * Instead we combine two tactics:
*
* Helping: Arranging for the joiner to execute some task that it
* would be running if the steal had not occurred.
@@ -526,79 +499,43 @@
* helping a hypothetical compensator: If we can readily tell that
* a possible action of a compensator is to steal and execute the
* task being joined, the joining thread can do so directly,
- * without the need for a compensation thread (although at the
- * expense of larger run-time stacks, but the tradeoff is
- * typically worthwhile).
+ * without the need for a compensation thread.
*
* The ManagedBlocker extension API can't use helping so relies
* only on compensation in method awaitBlocker.
*
- * The algorithm in helpStealer entails a form of "linear
- * helping". Each worker records (in field currentSteal) the most
- * recent task it stole from some other worker (or a submission).
- * It also records (in field currentJoin) the task it is currently
- * actively joining. Method helpStealer uses these markers to try
- * to find a worker to help (i.e., steal back a task from and
- * execute it) that could hasten completion of the actively joined
- * task. Thus, the joiner executes a task that would be on its
- * own local deque had the to-be-joined task not been stolen. This
- * is a conservative variant of the approach described in Wagner &
- * Calder "Leapfrogging: a portable technique for implementing
- * efficient futures" SIGPLAN Notices, 1993
- * (http://portal.acm.org/citation.cfm?id=155354). It differs in
- * that: (1) We only maintain dependency links across workers upon
- * steals, rather than use per-task bookkeeping. This sometimes
- * requires a linear scan of workQueues array to locate stealers,
- * but often doesn't because stealers leave hints (that may become
- * stale/wrong) of where to locate them. It is only a hint
- * because a worker might have had multiple steals and the hint
- * records only one of them (usually the most current). Hinting
- * isolates cost to when it is needed, rather than adding to
- * per-task overhead. (2) It is "shallow", ignoring nesting and
- * potentially cyclic mutual steals. (3) It is intentionally
- * racy: field currentJoin is updated only while actively joining,
- * which means that we miss links in the chain during long-lived
- * tasks, GC stalls etc (which is OK since blocking in such cases
- * is usually a good idea). (4) We bound the number of attempts
- * to find work using checksums and fall back to suspending the
- * worker and if necessary replacing it with another.
+ * The algorithm in awaitJoin entails a form of "linear helping".
+ * Each worker records (in field source) the id of the queue from
+ * which it last stole a task. The scan in method awaitJoin uses
+ * these markers to try to find a worker to help (i.e., steal back
+ * a task from and execute it) that could hasten completion of the
+ * actively joined task. Thus, the joiner executes a task that
+ * would be on its own local deque if the to-be-joined task had
+ * not been stolen. This is a conservative variant of the approach
+ * described in Wagner & Calder "Leapfrogging: a portable
+ * technique for implementing efficient futures" SIGPLAN Notices,
+ * 1993 (http://portal.acm.org/citation.cfm?id=155354). It differs
+ * mainly in that we only record queue ids, not full dependency
+ * links. This requires a linear scan of the workQueues array to
+ * locate stealers, but isolates cost to when it is needed, rather
+ * than adding to per-task overhead. Searches can fail to locate
+ * stealers GC stalls and the like delay recording sources.
+ * Further, even when accurately identified, stealers might not
+ * ever produce a task that the joiner can in turn help with. So,
+ * compensation is tried upon failure to find tasks to run.
*
- * Helping actions for CountedCompleters do not require tracking
- * currentJoins: Method helpComplete takes and executes any task
- * with the same root as the task being waited on (preferring
- * local pops to non-local polls). However, this still entails
- * some traversal of completer chains, so is less efficient than
- * using CountedCompleters without explicit joins.
- *
- * Compensation does not aim to keep exactly the target
+ * Compensation does not by default aim to keep exactly the target
* parallelism number of unblocked threads running at any given
* time. Some previous versions of this class employed immediate
* compensations for any blocked join. However, in practice, the
* vast majority of blockages are transient byproducts of GC and
* other JVM or OS activities that are made worse by replacement.
- * Currently, compensation is attempted only after validating that
- * all purportedly active threads are processing tasks by checking
- * field WorkQueue.scanState, which eliminates most false
- * positives. Also, compensation is bypassed (tolerating fewer
- * threads) in the most common case in which it is rarely
- * beneficial: when a worker with an empty queue (thus no
- * continuation tasks) blocks on a join and there still remain
- * enough threads to ensure liveness.
- *
- * Spare threads are removed as soon as they notice that the
- * target parallelism level has been exceeded, in method
- * tryDropSpare. (Method scan arranges returns for rechecks upon
- * each probe via the "bound" parameter.)
- *
- * The compensation mechanism may be bounded. Bounds for the
- * commonPool (see COMMON_MAX_SPARES) better enable JVMs to cope
- * with programming errors and abuse before running out of
- * resources to do so. In other cases, users may supply factories
- * that limit thread construction. The effects of bounding in this
- * pool (like all others) is imprecise. Total worker counts are
- * decremented when threads deregister, not when they exit and
- * resources are reclaimed by the JVM and OS. So the number of
- * simultaneously live threads may transiently exceed bounds.
+ * Rather than impose arbitrary policies, we allow users to
+ * override the default of only adding threads upon apparent
+ * starvation. The compensation mechanism may also be bounded.
+ * Bounds for the commonPool (see COMMON_MAX_SPARES) better enable
+ * JVMs to cope with programming errors and abuse before running
+ * out of resources to do so.
*
* Common Pool
* ===========
@@ -606,9 +543,7 @@
* The static common pool always exists after static
* initialization. Since it (or any other created pool) need
* never be used, we minimize initial construction overhead and
- * footprint to the setup of about a dozen fields, with no nested
- * allocation. Most bootstrapping occurs within method
- * externalSubmit during the first submission to the pool.
+ * footprint to the setup of about a dozen fields.
*
* When external threads submit to the common pool, they can
* perform subtask processing (see externalHelpComplete and
@@ -628,28 +563,22 @@
* InnocuousForkJoinWorkerThread when there is a SecurityManager
* present. These workers have no permissions set, do not belong
* to any user-defined ThreadGroup, and erase all ThreadLocals
- * after executing any top-level task (see WorkQueue.runTask).
- * The associated mechanics (mainly in ForkJoinWorkerThread) may
- * be JVM-dependent and must access particular Thread class fields
- * to achieve this effect.
+ * after executing any top-level task (see
+ * WorkQueue.afterTopLevelExec). The associated mechanics (mainly
+ * in ForkJoinWorkerThread) may be JVM-dependent and must access
+ * particular Thread class fields to achieve this effect.
*
* Style notes
* ===========
*
- * Memory ordering relies mainly on Unsafe intrinsics that carry
- * the further responsibility of explicitly performing null- and
- * bounds- checks otherwise carried out implicitly by JVMs. This
- * can be awkward and ugly, but also reflects the need to control
+ * Memory ordering relies mainly on VarHandles. This can be
+ * awkward and ugly, but also reflects the need to control
* outcomes across the unusual cases that arise in very racy code
- * with very few invariants. So these explicit checks would exist
- * in some form anyway. All fields are read into locals before
- * use, and null-checked if they are references. This is usually
- * done in a "C"-like style of listing declarations at the heads
- * of methods or blocks, and using inline assignments on first
- * encounter. Array bounds-checks are usually performed by
- * masking with array.length-1, which relies on the invariant that
- * these arrays are created with positive lengths, which is itself
- * paranoically checked. Nearly all explicit checks lead to
+ * with very few invariants. All fields are read into locals
+ * before use, and null-checked if they are references. This is
+ * usually done in a "C"-like style of listing declarations at the
+ * heads of methods or blocks, and using inline assignments on
+ * first encounter. Nearly all explicit checks lead to
* bypass/return, not exception throws, because they may
* legitimately arise due to cancellation/revocation during
* shutdown.
@@ -701,10 +630,17 @@
public static interface ForkJoinWorkerThreadFactory {
/**
* Returns a new worker thread operating in the given pool.
+ * Returning null or throwing an exception may result in tasks
+ * never being executed. If this method throws an exception,
+ * it is relayed to the caller of the method (for example
+ * {@code execute}) causing attempted thread creation. If this
+ * method returns null or throws an exception, it is not
+ * retried until the next attempted creation (for example
+ * another call to {@code execute}).
*
* @param pool the pool this thread works in
* @return the new worker thread, or {@code null} if the request
- * to create a thread is rejected
+ * to create a thread is rejected.
* @throws NullPointerException if the pool is null
*/
public ForkJoinWorkerThread newThread(ForkJoinPool pool);
@@ -721,56 +657,35 @@
}
}
- /**
- * Class for artificial tasks that are used to replace the target
- * of local joins if they are removed from an interior queue slot
- * in WorkQueue.tryRemoveAndExec. We don't need the proxy to
- * actually do anything beyond having a unique identity.
- */
- private static final class EmptyTask extends ForkJoinTask<Void> {
- private static final long serialVersionUID = -7721805057305804111L;
- EmptyTask() { status = ForkJoinTask.NORMAL; } // force done
- public final Void getRawResult() { return null; }
- public final void setRawResult(Void x) {}
- public final boolean exec() { return true; }
- }
-
- /**
- * Additional fields and lock created upon initialization.
- */
- private static final class AuxState extends ReentrantLock {
- private static final long serialVersionUID = -6001602636862214147L;
- volatile long stealCount; // cumulative steal count
- long indexSeed; // index bits for registerWorker
- AuxState() {}
- }
-
// Constants shared across ForkJoinPool and WorkQueue
// Bounds
+ static final int SWIDTH = 16; // width of short
static final int SMASK = 0xffff; // short bits == max index
static final int MAX_CAP = 0x7fff; // max #workers - 1
- static final int EVENMASK = 0xfffe; // even short bits
static final int SQMASK = 0x007e; // max 64 (even) slots
- // Masks and units for WorkQueue.scanState and ctl sp subfield
+ // Masks and units for WorkQueue.phase and ctl sp subfield
static final int UNSIGNALLED = 1 << 31; // must be negative
static final int SS_SEQ = 1 << 16; // version count
+ static final int QLOCK = 1; // must be 1
- // Mode bits for ForkJoinPool.config and WorkQueue.config
- static final int MODE_MASK = 0xffff << 16; // top half of int
- static final int SPARE_WORKER = 1 << 17; // set if tc > 0 on creation
- static final int UNREGISTERED = 1 << 18; // to skip some of deregister
- static final int FIFO_QUEUE = 1 << 31; // must be negative
- static final int LIFO_QUEUE = 0; // for clarity
- static final int IS_OWNED = 1; // low bit 0 if shared
+ // Mode bits and sentinels, some also used in WorkQueue id and.source fields
+ static final int OWNED = 1; // queue has owner thread
+ static final int FIFO = 1 << 16; // fifo queue or access mode
+ static final int SHUTDOWN = 1 << 18;
+ static final int TERMINATED = 1 << 19;
+ static final int STOP = 1 << 31; // must be negative
+ static final int QUIET = 1 << 30; // not scanning or working
+ static final int DORMANT = QUIET | UNSIGNALLED;
/**
- * The maximum number of task executions from the same queue
- * before checking other queues, bounding unfairness and impact of
- * infinite user task recursion. Must be a power of two minus 1.
+ * The maximum number of local polls from the same queue before
+ * checking others. This is a safeguard against infinitely unfair
+ * looping under unbounded user task recursion, and must be larger
+ * than plausible cases of intentional bounded task recursion.
*/
- static final int POLL_LIMIT = (1 << 10) - 1;
+ static final int POLL_LIMIT = 1 << 10;
/**
* Queues supporting work-stealing as well as external task
@@ -805,23 +720,16 @@
static final int MAXIMUM_QUEUE_CAPACITY = 1 << 26; // 64M
// Instance fields
-
- volatile int scanState; // versioned, negative if inactive
- int stackPred; // pool stack (ctl) predecessor
+ volatile int phase; // versioned, negative: queued, 1: locked
+ int stackPred; // pool stack (ctl) predecessor link
int nsteals; // number of steals
- int hint; // randomization and stealer index hint
- int config; // pool index and mode
- volatile int qlock; // 1: locked, < 0: terminate; else 0
+ int id; // index, mode, tag
+ volatile int source; // source queue id, or sentinel
volatile int base; // index of next slot for poll
int top; // index of next slot for push
ForkJoinTask<?>[] array; // the elements (initially unallocated)
final ForkJoinPool pool; // the containing pool (may be null)
final ForkJoinWorkerThread owner; // owning thread or null if shared
- volatile Thread parker; // == owner during call to park; else null
- volatile ForkJoinTask<?> currentJoin; // task being joined in awaitJoin
-
- @jdk.internal.vm.annotation.Contended("group2") // segregate
- volatile ForkJoinTask<?> currentSteal; // nonnull when running some task
WorkQueue(ForkJoinPool pool, ForkJoinWorkerThread owner) {
this.pool = pool;
@@ -834,7 +742,7 @@
* Returns an exportable index (used by ForkJoinWorkerThread).
*/
final int getPoolIndex() {
- return (config & 0xffff) >>> 1; // ignore odd/even tag bit
+ return (id & 0xffff) >>> 1; // ignore odd/even tag bit
}
/**
@@ -851,13 +759,14 @@
* near-empty queue has at least one unclaimed task.
*/
final boolean isEmpty() {
- ForkJoinTask<?>[] a; int n, al, s;
- return ((n = base - (s = top)) >= 0 || // possibly one task
+ ForkJoinTask<?>[] a; int n, al, b;
+ return ((n = (b = base) - top) >= 0 || // possibly one task
(n == -1 && ((a = array) == null ||
(al = a.length) == 0 ||
- a[(al - 1) & (s - 1)] == null)));
+ a[(al - 1) & b] == null)));
}
+
/**
* Pushes a task. Call only by owner in unshared queues.
*
@@ -865,17 +774,17 @@
* @throws RejectedExecutionException if array cannot be resized
*/
final void push(ForkJoinTask<?> task) {
- U.storeFence(); // ensure safe publication
- int s = top, al, d; ForkJoinTask<?>[] a;
+ int s = top; ForkJoinTask<?>[] a; int al, d;
if ((a = array) != null && (al = a.length) > 0) {
- a[(al - 1) & s] = task; // relaxed writes OK
- top = s + 1;
+ int index = (al - 1) & s;
ForkJoinPool p = pool;
+ top = s + 1;
+ QA.setRelease(a, index, task);
if ((d = base - s) == 0 && p != null) {
- U.fullFence();
+ VarHandle.fullFence();
p.signalWork();
}
- else if (al + d == 1)
+ else if (d + al == 1)
growArray();
}
}
@@ -887,24 +796,24 @@
*/
final ForkJoinTask<?>[] growArray() {
ForkJoinTask<?>[] oldA = array;
- int size = oldA != null ? oldA.length << 1 : INITIAL_QUEUE_CAPACITY;
+ int oldSize = oldA != null ? oldA.length : 0;
+ int size = oldSize > 0 ? oldSize << 1 : INITIAL_QUEUE_CAPACITY;
if (size < INITIAL_QUEUE_CAPACITY || size > MAXIMUM_QUEUE_CAPACITY)
throw new RejectedExecutionException("Queue capacity exceeded");
int oldMask, t, b;
ForkJoinTask<?>[] a = array = new ForkJoinTask<?>[size];
- if (oldA != null && (oldMask = oldA.length - 1) > 0 &&
+ if (oldA != null && (oldMask = oldSize - 1) > 0 &&
(t = top) - (b = base) > 0) {
int mask = size - 1;
do { // emulate poll from old array, push to new array
int index = b & oldMask;
- long offset = ((long)index << ASHIFT) + ABASE;
ForkJoinTask<?> x = (ForkJoinTask<?>)
- U.getObjectVolatile(oldA, offset);
+ QA.getAcquire(oldA, index);
if (x != null &&
- U.compareAndSwapObject(oldA, offset, x, null))
+ QA.compareAndSet(oldA, index, x, null))
a[b & mask] = x;
} while (++b != t);
- U.storeFence();
+ VarHandle.releaseFence();
}
return a;
}
@@ -917,33 +826,12 @@
int b = base, s = top, al, i; ForkJoinTask<?>[] a;
if ((a = array) != null && b != s && (al = a.length) > 0) {
int index = (al - 1) & --s;
- long offset = ((long)index << ASHIFT) + ABASE;
ForkJoinTask<?> t = (ForkJoinTask<?>)
- U.getObject(a, offset);
+ QA.get(a, index);
if (t != null &&
- U.compareAndSwapObject(a, offset, t, null)) {
+ QA.compareAndSet(a, index, t, null)) {
top = s;
- return t;
- }
- }
- return null;
- }
-
- /**
- * Takes a task in FIFO order if b is base of queue and a task
- * can be claimed without contention. Specialized versions
- * appear in ForkJoinPool methods scan and helpStealer.
- */
- final ForkJoinTask<?> pollAt(int b) {
- ForkJoinTask<?>[] a; int al;
- if ((a = array) != null && (al = a.length) > 0) {
- int index = (al - 1) & b;
- long offset = ((long)index << ASHIFT) + ABASE;
- ForkJoinTask<?> t = (ForkJoinTask<?>)
- U.getObjectVolatile(a, offset);
- if (t != null && b++ == base &&
- U.compareAndSwapObject(a, offset, t, null)) {
- base = b;
+ VarHandle.releaseFence();
return t;
}
}
@@ -959,12 +847,11 @@
if ((a = array) != null && (d = b - s) < 0 &&
(al = a.length) > 0) {
int index = (al - 1) & b;
- long offset = ((long)index << ASHIFT) + ABASE;
ForkJoinTask<?> t = (ForkJoinTask<?>)
- U.getObjectVolatile(a, offset);
+ QA.getAcquire(a, index);
if (b++ == base) {
if (t != null) {
- if (U.compareAndSwapObject(a, offset, t, null)) {
+ if (QA.compareAndSet(a, index, t, null)) {
base = b;
return t;
}
@@ -983,7 +870,7 @@
* Takes next task, if one exists, in order specified by mode.
*/
final ForkJoinTask<?> nextLocalTask() {
- return (config < 0) ? poll() : pop();
+ return ((id & FIFO) != 0) ? poll() : pop();
}
/**
@@ -992,7 +879,8 @@
final ForkJoinTask<?> peek() {
int al; ForkJoinTask<?>[] a;
return ((a = array) != null && (al = a.length) > 0) ?
- a[(al - 1) & (config < 0 ? base : top - 1)] : null;
+ a[(al - 1) &
+ ((id & FIFO) != 0 ? base : top - 1)] : null;
}
/**
@@ -1002,9 +890,9 @@
int b = base, s = top, al; ForkJoinTask<?>[] a;
if ((a = array) != null && b != s && (al = a.length) > 0) {
int index = (al - 1) & --s;
- long offset = ((long)index << ASHIFT) + ABASE;
- if (U.compareAndSwapObject(a, offset, task, null)) {
+ if (QA.compareAndSet(a, index, task, null)) {
top = s;
+ VarHandle.releaseFence();
return true;
}
}
@@ -1012,105 +900,32 @@
}
/**
- * Shared version of push. Fails if already locked.
- *
- * @return status: > 0 locked, 0 possibly was empty, < 0 was nonempty
- */
- final int sharedPush(ForkJoinTask<?> task) {
- int stat;
- if (U.compareAndSwapInt(this, QLOCK, 0, 1)) {
- int b = base, s = top, al, d; ForkJoinTask<?>[] a;
- if ((a = array) != null && (al = a.length) > 0 &&
- al - 1 + (d = b - s) > 0) {
- a[(al - 1) & s] = task;
- top = s + 1; // relaxed writes OK here
- qlock = 0;
- stat = (d < 0 && b == base) ? d : 0;
- }
- else {
- growAndSharedPush(task);
- stat = 0;
- }
- }
- else
- stat = 1;
- return stat;
- }
-
- /**
- * Helper for sharedPush; called only when locked and resize
- * needed.
- */
- private void growAndSharedPush(ForkJoinTask<?> task) {
- try {
- growArray();
- int s = top, al; ForkJoinTask<?>[] a;
- if ((a = array) != null && (al = a.length) > 0) {
- a[(al - 1) & s] = task;
- top = s + 1;
- }
- } finally {
- qlock = 0;
- }
- }
-
- /**
- * Shared version of tryUnpush.
- */
- final boolean trySharedUnpush(ForkJoinTask<?> task) {
- boolean popped = false;
- int s = top - 1, al; ForkJoinTask<?>[] a;
- if ((a = array) != null && (al = a.length) > 0) {
- int index = (al - 1) & s;
- long offset = ((long)index << ASHIFT) + ABASE;
- ForkJoinTask<?> t = (ForkJoinTask<?>) U.getObject(a, offset);
- if (t == task &&
- U.compareAndSwapInt(this, QLOCK, 0, 1)) {
- if (top == s + 1 && array == a &&
- U.compareAndSwapObject(a, offset, task, null)) {
- popped = true;
- top = s;
- }
- U.putIntRelease(this, QLOCK, 0);
- }
- }
- return popped;
- }
-
- /**
* Removes and cancels all known tasks, ignoring any exceptions.
*/
final void cancelAll() {
- ForkJoinTask<?> t;
- if ((t = currentJoin) != null) {
- currentJoin = null;
- ForkJoinTask.cancelIgnoringExceptions(t);
- }
- if ((t = currentSteal) != null) {
- currentSteal = null;
- ForkJoinTask.cancelIgnoringExceptions(t);
- }
- while ((t = poll()) != null)
+ for (ForkJoinTask<?> t; (t = poll()) != null; )
ForkJoinTask.cancelIgnoringExceptions(t);
}
// Specialized execution methods
/**
- * Pops and executes up to POLL_LIMIT tasks or until empty.
+ * Pops and executes up to limit consecutive tasks or until empty.
+ *
+ * @param limit max runs, or zero for no limit
*/
- final void localPopAndExec() {
- for (int nexec = 0;;) {
+ final void localPopAndExec(int limit) {
+ for (;;) {
int b = base, s = top, al; ForkJoinTask<?>[] a;
if ((a = array) != null && b != s && (al = a.length) > 0) {
int index = (al - 1) & --s;
- long offset = ((long)index << ASHIFT) + ABASE;
ForkJoinTask<?> t = (ForkJoinTask<?>)
- U.getAndSetObject(a, offset, null);
+ QA.getAndSet(a, index, null);
if (t != null) {
top = s;
- (currentSteal = t).doExec();
- if (++nexec > POLL_LIMIT)
+ VarHandle.releaseFence();
+ t.doExec();
+ if (limit != 0 && --limit == 0)
break;
}
else
@@ -1122,22 +937,28 @@
}
/**
- * Polls and executes up to POLL_LIMIT tasks or until empty.
+ * Polls and executes up to limit consecutive tasks or until empty.
+ *
+ * @param limit, or zero for no limit
*/
- final void localPollAndExec() {
- for (int nexec = 0;;) {
- int b = base, s = top, al; ForkJoinTask<?>[] a;
- if ((a = array) != null && b != s && (al = a.length) > 0) {
+ final void localPollAndExec(int limit) {
+ for (int polls = 0;;) {
+ int b = base, s = top, d, al; ForkJoinTask<?>[] a;
+ if ((a = array) != null && (d = b - s) < 0 &&
+ (al = a.length) > 0) {
int index = (al - 1) & b++;
- long offset = ((long)index << ASHIFT) + ABASE;
ForkJoinTask<?> t = (ForkJoinTask<?>)
- U.getAndSetObject(a, offset, null);
+ QA.getAndSet(a, index, null);
if (t != null) {
base = b;
t.doExec();
- if (++nexec > POLL_LIMIT)
+ if (limit != 0 && ++polls == limit)
break;
}
+ else if (d == -1)
+ break; // now empty
+ else
+ polls = 0; // stolen; reset
}
else
break;
@@ -1145,188 +966,156 @@
}
/**
- * Executes the given task and (some) remaining local tasks.
+ * If present, removes task from queue and executes it.
*/
- final void runTask(ForkJoinTask<?> task) {
- if (task != null) {
- task.doExec();
- if (config < 0)
- localPollAndExec();
- else
- localPopAndExec();
- int ns = ++nsteals;
- ForkJoinWorkerThread thread = owner;
- currentSteal = null;
- if (ns < 0) // collect on overflow
- transferStealCount(pool);
- if (thread != null)
- thread.afterTopLevelExec();
- }
- }
-
- /**
- * Adds steal count to pool steal count if it exists, and resets.
- */
- final void transferStealCount(ForkJoinPool p) {
- AuxState aux;
- if (p != null && (aux = p.auxState) != null) {
- long s = nsteals;
- nsteals = 0; // if negative, correct for overflow
- if (s < 0) s = Integer.MAX_VALUE;
- aux.lock();
- try {
- aux.stealCount += s;
- } finally {
- aux.unlock();
+ final void tryRemoveAndExec(ForkJoinTask<?> task) {
+ ForkJoinTask<?>[] wa; int s, wal;
+ if (base - (s = top) < 0 && // traverse from top
+ (wa = array) != null && (wal = wa.length) > 0) {
+ for (int m = wal - 1, ns = s - 1, i = ns; ; --i) {
+ int index = i & m;
+ ForkJoinTask<?> t = (ForkJoinTask<?>)
+ QA.get(wa, index);
+ if (t == null)
+ break;
+ else if (t == task) {
+ if (QA.compareAndSet(wa, index, t, null)) {
+ top = ns; // safely shift down
+ for (int j = i; j != ns; ++j) {
+ ForkJoinTask<?> f;
+ int pindex = (j + 1) & m;
+ f = (ForkJoinTask<?>)QA.get(wa, pindex);
+ QA.setVolatile(wa, pindex, null);
+ int jindex = j & m;
+ QA.setRelease(wa, jindex, f);
+ }
+ VarHandle.releaseFence();
+ t.doExec();
+ }
+ break;
+ }
}
}
}
/**
- * If present, removes from queue and executes the given task,
- * or any other cancelled task. Used only by awaitJoin.
+ * Tries to steal and run tasks within the target's
+ * computation until done, not found, or limit exceeded.
*
- * @return true if queue empty and task not known to be done
+ * @param task root of CountedCompleter computation
+ * @param limit max runs, or zero for no limit
+ * @return task status on exit
*/
- final boolean tryRemoveAndExec(ForkJoinTask<?> task) {
- if (task != null && task.status >= 0) {
- int b, s, d, al; ForkJoinTask<?>[] a;
- while ((d = (b = base) - (s = top)) < 0 &&
- (a = array) != null && (al = a.length) > 0) {
- for (;;) { // traverse from s to b
- int index = --s & (al - 1);
- long offset = (index << ASHIFT) + ABASE;
- ForkJoinTask<?> t = (ForkJoinTask<?>)
- U.getObjectVolatile(a, offset);
- if (t == null)
- break; // restart
- else if (t == task) {
- boolean removed = false;
- if (s + 1 == top) { // pop
- if (U.compareAndSwapObject(a, offset, t, null)) {
- top = s;
- removed = true;
+ final int localHelpCC(CountedCompleter<?> task, int limit) {
+ int status = 0;
+ if (task != null && (status = task.status) >= 0) {
+ for (;;) {
+ boolean help = false;
+ int b = base, s = top, al; ForkJoinTask<?>[] a;
+ if ((a = array) != null && b != s && (al = a.length) > 0) {
+ int index = (al - 1) & (s - 1);
+ ForkJoinTask<?> o = (ForkJoinTask<?>)
+ QA.get(a, index);
+ if (o instanceof CountedCompleter) {
+ CountedCompleter<?> t = (CountedCompleter<?>)o;
+ for (CountedCompleter<?> f = t;;) {
+ if (f != task) {
+ if ((f = f.completer) == null) // try parent
+ break;
+ }
+ else {
+ if (QA.compareAndSet(a, index, t, null)) {
+ top = s - 1;
+ VarHandle.releaseFence();
+ t.doExec();
+ help = true;
+ }
+ break;
}
}
- else if (base == b) // replace with proxy
- removed = U.compareAndSwapObject(a, offset, t,
- new EmptyTask());
- if (removed) {
- ForkJoinTask<?> ps = currentSteal;
- (currentSteal = task).doExec();
- currentSteal = ps;
- }
- break;
- }
- else if (t.status < 0 && s + 1 == top) {
- if (U.compareAndSwapObject(a, offset, t, null)) {
- top = s;
- }
- break; // was cancelled
- }
- else if (++d == 0) {
- if (base != b) // rescan
- break;
- return false;
}
}
- if (task.status < 0)
- return false;
+ if ((status = task.status) < 0 || !help ||
+ (limit != 0 && --limit == 0))
+ break;
}
}
- return true;
+ return status;
+ }
+
+ // Operations on shared queues
+
+ /**
+ * Tries to lock shared queue by CASing phase field.
+ */
+ final boolean tryLockSharedQueue() {
+ return PHASE.compareAndSet(this, 0, QLOCK);
}
/**
- * Pops task if in the same CC computation as the given task,
- * in either shared or owned mode. Used only by helpComplete.
+ * Shared version of tryUnpush.
*/
- final CountedCompleter<?> popCC(CountedCompleter<?> task, int mode) {
- int b = base, s = top, al; ForkJoinTask<?>[] a;
- if ((a = array) != null && b != s && (al = a.length) > 0) {
- int index = (al - 1) & (s - 1);
- long offset = ((long)index << ASHIFT) + ABASE;
- ForkJoinTask<?> o = (ForkJoinTask<?>)
- U.getObjectVolatile(a, offset);
- if (o instanceof CountedCompleter) {
- CountedCompleter<?> t = (CountedCompleter<?>)o;
- for (CountedCompleter<?> r = t;;) {
- if (r == task) {
- if ((mode & IS_OWNED) == 0) {
- boolean popped = false;
- if (U.compareAndSwapInt(this, QLOCK, 0, 1)) {
- if (top == s && array == a &&
- U.compareAndSwapObject(a, offset,
- t, null)) {
- popped = true;
- top = s - 1;
- }
- U.putIntRelease(this, QLOCK, 0);
- if (popped)
- return t;
- }
- }
- else if (U.compareAndSwapObject(a, offset,
- t, null)) {
- top = s - 1;
- return t;
- }
- break;
- }
- else if ((r = r.completer) == null) // try parent
- break;
+ final boolean trySharedUnpush(ForkJoinTask<?> task) {
+ boolean popped = false;
+ int s = top - 1, al; ForkJoinTask<?>[] a;
+ if ((a = array) != null && (al = a.length) > 0) {
+ int index = (al - 1) & s;
+ ForkJoinTask<?> t = (ForkJoinTask<?>) QA.get(a, index);
+ if (t == task &&
+ PHASE.compareAndSet(this, 0, QLOCK)) {
+ if (top == s + 1 && array == a &&
+ QA.compareAndSet(a, index, task, null)) {
+ popped = true;
+ top = s;
}
+ PHASE.setRelease(this, 0);
}
}
- return null;
+ return popped;
}
/**
- * Steals and runs a task in the same CC computation as the
- * given task if one exists and can be taken without
- * contention. Otherwise returns a checksum/control value for
- * use by method helpComplete.
- *
- * @return 1 if successful, 2 if retryable (lost to another
- * stealer), -1 if non-empty but no matching task found, else
- * the base index, forced negative.
+ * Shared version of localHelpCC.
*/
- final int pollAndExecCC(CountedCompleter<?> task) {
- ForkJoinTask<?>[] a;
- int b = base, s = top, al, h;
- if ((a = array) != null && b != s && (al = a.length) > 0) {
- int index = (al - 1) & b;
- long offset = ((long)index << ASHIFT) + ABASE;
- ForkJoinTask<?> o = (ForkJoinTask<?>)
- U.getObjectVolatile(a, offset);
- if (o == null)
- h = 2; // retryable
- else if (!(o instanceof CountedCompleter))
- h = -1; // unmatchable
- else {
- CountedCompleter<?> t = (CountedCompleter<?>)o;
- for (CountedCompleter<?> r = t;;) {
- if (r == task) {
- if (b++ == base &&
- U.compareAndSwapObject(a, offset, t, null)) {
- base = b;
- t.doExec();
- h = 1; // success
+ final int sharedHelpCC(CountedCompleter<?> task, int limit) {
+ int status = 0;
+ if (task != null && (status = task.status) >= 0) {
+ for (;;) {
+ boolean help = false;
+ int b = base, s = top, al; ForkJoinTask<?>[] a;
+ if ((a = array) != null && b != s && (al = a.length) > 0) {
+ int index = (al - 1) & (s - 1);
+ ForkJoinTask<?> o = (ForkJoinTask<?>)
+ QA.get(a, index);
+ if (o instanceof CountedCompleter) {
+ CountedCompleter<?> t = (CountedCompleter<?>)o;
+ for (CountedCompleter<?> f = t;;) {
+ if (f != task) {
+ if ((f = f.completer) == null)
+ break;
+ }
+ else {
+ if (PHASE.compareAndSet(this, 0, QLOCK)) {
+ if (top == s && array == a &&
+ QA.compareAndSet(a, index, t, null)) {
+ help = true;
+ top = s - 1;
+ }
+ PHASE.setRelease(this, 0);
+ if (help)
+ t.doExec();
+ }
+ break;
+ }
}
- else
- h = 2; // lost CAS
- break;
- }
- else if ((r = r.completer) == null) {
- h = -1; // unmatched
- break;
}
}
+ if ((status = task.status) < 0 || !help ||
+ (limit != 0 && --limit == 0))
+ break;
}
}
- else
- h = b | Integer.MIN_VALUE; // to sense movement on re-poll
- return h;
+ return status;
}
/**
@@ -1334,27 +1123,18 @@
*/
final boolean isApparentlyUnblocked() {
Thread wt; Thread.State s;
- return (scanState >= 0 &&
- (wt = owner) != null &&
+ return ((wt = owner) != null &&
(s = wt.getState()) != Thread.State.BLOCKED &&
s != Thread.State.WAITING &&
s != Thread.State.TIMED_WAITING);
}
- // Unsafe mechanics. Note that some are (and must be) the same as in FJP
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long QLOCK;
- private static final int ABASE;
- private static final int ASHIFT;
+ // VarHandle mechanics.
+ private static final VarHandle PHASE;
static {
try {
- QLOCK = U.objectFieldOffset
- (WorkQueue.class.getDeclaredField("qlock"));
- ABASE = U.arrayBaseOffset(ForkJoinTask[].class);
- int scale = U.arrayIndexScale(ForkJoinTask[].class);
- if ((scale & (scale - 1)) != 0)
- throw new Error("array index scale not a power of two");
- ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ PHASE = l.findVarHandle(WorkQueue.class, "phase", int.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
@@ -1372,7 +1152,7 @@
/**
* Permission required for callers of methods that may start or
- * kill threads. Also used as a static lock in tryInitialize.
+ * kill threads.
*/
static final RuntimePermission modifyThreadPermission;
@@ -1413,18 +1193,15 @@
// static configuration constants
/**
- * Initial timeout value (in milliseconds) for the thread
- * triggering quiescence to park waiting for new work. On timeout,
- * the thread will instead try to shrink the number of workers.
- * The value should be large enough to avoid overly aggressive
- * shrinkage during most transient stalls (long GCs etc).
+ * Default idle timeout value (in milliseconds) for the thread
+ * triggering quiescence to park waiting for new work
*/
- private static final long IDLE_TIMEOUT_MS = 2000L; // 2sec
+ private static final long DEFAULT_KEEPALIVE = 60000L;
/**
- * Tolerance for idle timeouts, to cope with timer undershoots.
+ * Undershoot tolerance for idle timeouts
*/
- private static final long TIMEOUT_SLOP_MS = 20L; // 20ms
+ private static final long TIMEOUT_SLOP = 20L;
/**
* The default value for COMMON_MAX_SPARES. Overridable using the
@@ -1444,7 +1221,7 @@
/*
* Bits and masks for field ctl, packed with 4 16 bit subfields:
- * AC: Number of active running workers minus target parallelism
+ * RC: Number of released (unqueued) workers minus target parallelism
* TC: Number of total workers minus target parallelism
* SS: version count and status of top waiting thread
* ID: poolIndex of top of Treiber stack of waiters
@@ -1453,26 +1230,30 @@
* (including version bits) as sp=(int)ctl. The offsets of counts
* by the target parallelism and the positionings of fields makes
* it possible to perform the most common checks via sign tests of
- * fields: When ac is negative, there are not enough active
+ * fields: When ac is negative, there are not enough unqueued
* workers, when tc is negative, there are not enough total
* workers. When sp is non-zero, there are waiting workers. To
* deal with possibly negative fields, we use casts in and out of
* "short" and/or signed shifts to maintain signedness.
*
- * Because it occupies uppermost bits, we can add one active count
- * using getAndAddLong of AC_UNIT, rather than CAS, when returning
+ * Because it occupies uppermost bits, we can add one release count
+ * using getAndAddLong of RC_UNIT, rather than CAS, when returning
* from a blocked join. Other updates entail multiple subfields
* and masking, requiring CAS.
+ *
+ * The limits packed in field "bounds" are also offset by the
+ * parallelism level to make them comparable to the ctl rc and tc
+ * fields.
*/
// Lower and upper word masks
private static final long SP_MASK = 0xffffffffL;
private static final long UC_MASK = ~SP_MASK;
- // Active counts
- private static final int AC_SHIFT = 48;
- private static final long AC_UNIT = 0x0001L << AC_SHIFT;
- private static final long AC_MASK = 0xffffL << AC_SHIFT;
+ // Release counts
+ private static final int RC_SHIFT = 48;
+ private static final long RC_UNIT = 0x0001L << RC_SHIFT;
+ private static final long RC_MASK = 0xffffL << RC_SHIFT;
// Total counts
private static final int TC_SHIFT = 32;
@@ -1480,52 +1261,21 @@
private static final long TC_MASK = 0xffffL << TC_SHIFT;
private static final long ADD_WORKER = 0x0001L << (TC_SHIFT + 15); // sign
- // runState bits: SHUTDOWN must be negative, others arbitrary powers of two
- private static final int STARTED = 1;
- private static final int STOP = 1 << 1;
- private static final int TERMINATED = 1 << 2;
- private static final int SHUTDOWN = 1 << 31;
+ // Instance fields
- // Instance fields
- volatile long ctl; // main pool control
- volatile int runState;
- final int config; // parallelism, mode
- AuxState auxState; // lock, steal counts
- volatile WorkQueue[] workQueues; // main registry
- final String workerNamePrefix; // to create worker name string
+ volatile long stealCount; // collects worker nsteals
+ final long keepAlive; // milliseconds before dropping if idle
+ int indexSeed; // next worker index
+ final int bounds; // min, max threads packed as shorts
+ volatile int mode; // parallelism, runstate, queue mode
+ WorkQueue[] workQueues; // main registry
+ final String workerNamePrefix; // for worker thread string; sync lock
final ForkJoinWorkerThreadFactory factory;
final UncaughtExceptionHandler ueh; // per-worker UEH
+ final Predicate<? super ForkJoinPool> saturate;
- /**
- * Instantiates fields upon first submission, or upon shutdown if
- * no submissions. If checkTermination true, also responds to
- * termination by external calls submitting tasks.
- */
- private void tryInitialize(boolean checkTermination) {
- if (runState == 0) { // bootstrap by locking static field
- int p = config & SMASK;
- int n = (p > 1) ? p - 1 : 1; // ensure at least 2 slots
- n |= n >>> 1; // create workQueues array with size a power of two
- n |= n >>> 2;
- n |= n >>> 4;
- n |= n >>> 8;
- n |= n >>> 16;
- n = ((n + 1) << 1) & SMASK;
- AuxState aux = new AuxState();
- WorkQueue[] ws = new WorkQueue[n];
- synchronized (modifyThreadPermission) { // double-check
- if (runState == 0) {
- workQueues = ws;
- auxState = aux;
- runState = STARTED;
- }
- }
- }
- if (checkTermination && runState < 0) {
- tryTerminate(false, false); // help terminate
- throw new RejectedExecutionException();
- }
- }
+ @jdk.internal.vm.annotation.Contended("fjpctl") // segregate
+ volatile long ctl; // main pool control
// Creating, registering and deregistering workers
@@ -1534,18 +1284,14 @@
* count has already been incremented as a reservation. Invokes
* deregisterWorker on any failure.
*
- * @param isSpare true if this is a spare thread
* @return true if successful
*/
- private boolean createWorker(boolean isSpare) {
+ private boolean createWorker() {
ForkJoinWorkerThreadFactory fac = factory;
Throwable ex = null;
ForkJoinWorkerThread wt = null;
- WorkQueue q;
try {
if (fac != null && (wt = fac.newThread(this)) != null) {
- if (isSpare && (q = wt.workQueue) != null)
- q.config |= SPARE_WORKER;
wt.start();
return true;
}
@@ -1566,10 +1312,10 @@
*/
private void tryAddWorker(long c) {
do {
- long nc = ((AC_MASK & (c + AC_UNIT)) |
+ long nc = ((RC_MASK & (c + RC_UNIT)) |
(TC_MASK & (c + TC_UNIT)));
- if (ctl == c && U.compareAndSwapLong(this, CTL, c, nc)) {
- createWorker(false);
+ if (ctl == c && CTL.compareAndSet(this, c, nc)) {
+ createWorker();
break;
}
} while (((c = ctl) & ADD_WORKER) != 0L && (int)c == 0);
@@ -1584,41 +1330,57 @@
*/
final WorkQueue registerWorker(ForkJoinWorkerThread wt) {
UncaughtExceptionHandler handler;
- AuxState aux;
- wt.setDaemon(true); // configure thread
+ wt.setDaemon(true); // configure thread
if ((handler = ueh) != null)
wt.setUncaughtExceptionHandler(handler);
WorkQueue w = new WorkQueue(this, wt);
- int i = 0; // assign a pool index
- int mode = config & MODE_MASK;
- if ((aux = auxState) != null) {
- aux.lock();
- try {
- int s = (int)(aux.indexSeed += SEED_INCREMENT), n, m;
- WorkQueue[] ws = workQueues;
- if (ws != null && (n = ws.length) > 0) {
- i = (m = n - 1) & ((s << 1) | 1); // odd-numbered indices
- if (ws[i] != null) { // collision
- int probes = 0; // step by approx half n
- int step = (n <= 4) ? 2 : ((n >>> 1) & EVENMASK) + 2;
- while (ws[i = (i + step) & m] != null) {
- if (++probes >= n) {
- workQueues = ws = Arrays.copyOf(ws, n <<= 1);
- m = n - 1;
- probes = 0;
- }
+ int tid = 0; // for thread name
+ int fifo = mode & FIFO;
+ String prefix = workerNamePrefix;
+ if (prefix != null) {
+ synchronized (prefix) {
+ WorkQueue[] ws = workQueues; int n;
+ int s = indexSeed += SEED_INCREMENT;
+ if (ws != null && (n = ws.length) > 1) {
+ int m = n - 1;
+ tid = s & m;
+ int i = m & ((s << 1) | 1); // odd-numbered indices
+ for (int probes = n >>> 1;;) { // find empty slot
+ WorkQueue q;
+ if ((q = ws[i]) == null || q.phase == QUIET)
+ break;
+ else if (--probes == 0) {
+ i = n | 1; // resize below
+ break;
}
+ else
+ i = (i + 2) & m;
}
- w.hint = s; // use as random seed
- w.config = i | mode;
- w.scanState = i | (s & 0x7fff0000); // random seq bits
- ws[i] = w;
+
+ int id = i | fifo | (s & ~(SMASK | FIFO | DORMANT));
+ w.phase = w.id = id; // now publishable
+
+ if (i < n)
+ ws[i] = w;
+ else { // expand array
+ int an = n << 1;
+ WorkQueue[] as = new WorkQueue[an];
+ as[i] = w;
+ int am = an - 1;
+ for (int j = 0; j < n; ++j) {
+ WorkQueue v; // copy external queue
+ if ((v = ws[j]) != null) // position may change
+ as[v.id & am & SQMASK] = v;
+ if (++j >= n)
+ break;
+ as[j] = ws[j]; // copy worker
+ }
+ workQueues = as;
+ }
}
- } finally {
- aux.unlock();
}
+ wt.setName(prefix.concat(Integer.toString(tid)));
}
- wt.setName(workerNamePrefix.concat(Integer.toString(i >>> 1)));
return w;
}
@@ -1633,64 +1395,48 @@
*/
final void deregisterWorker(ForkJoinWorkerThread wt, Throwable ex) {
WorkQueue w = null;
+ int phase = 0;
if (wt != null && (w = wt.workQueue) != null) {
- AuxState aux; WorkQueue[] ws; // remove index from array
- int idx = w.config & SMASK;
- int ns = w.nsteals;
- if ((aux = auxState) != null) {
- aux.lock();
- try {
+ Object lock = workerNamePrefix;
+ long ns = (long)w.nsteals & 0xffffffffL;
+ int idx = w.id & SMASK;
+ if (lock != null) {
+ WorkQueue[] ws; // remove index from array
+ synchronized (lock) {
if ((ws = workQueues) != null && ws.length > idx &&
ws[idx] == w)
ws[idx] = null;
- aux.stealCount += ns;
- } finally {
- aux.unlock();
+ stealCount += ns;
}
}
- }
- if (w == null || (w.config & UNREGISTERED) == 0) { // else pre-adjusted
- long c; // decrement counts
- do {} while (!U.compareAndSwapLong
- (this, CTL, c = ctl, ((AC_MASK & (c - AC_UNIT)) |
- (TC_MASK & (c - TC_UNIT)) |
- (SP_MASK & c))));
- }
- if (w != null) {
- w.currentSteal = null;
- w.qlock = -1; // ensure set
- w.cancelAll(); // cancel remaining tasks
+ phase = w.phase;
}
- while (tryTerminate(false, false) >= 0) { // possibly replace
- WorkQueue[] ws; int wl, sp; long c;
- if (w == null || w.array == null ||
- (ws = workQueues) == null || (wl = ws.length) <= 0)
- break;
- else if ((sp = (int)(c = ctl)) != 0) { // wake up replacement
- if (tryRelease(c, ws[(wl - 1) & sp], AC_UNIT))
- break;
- }
- else if (ex != null && (c & ADD_WORKER) != 0L) {
- tryAddWorker(c); // create replacement
- break;
- }
- else // don't need replacement
- break;
+ if (phase != QUIET) { // else pre-adjusted
+ long c; // decrement counts
+ do {} while (!CTL.weakCompareAndSetVolatile
+ (this, c = ctl, ((RC_MASK & (c - RC_UNIT)) |
+ (TC_MASK & (c - TC_UNIT)) |
+ (SP_MASK & c))));
}
+ if (w != null)
+ w.cancelAll(); // cancel remaining tasks
+
+ if (!tryTerminate(false, false) && // possibly replace worker
+ w != null && w.array != null) // avoid repeated failures
+ signalWork();
+
if (ex == null) // help clean on way out
ForkJoinTask.helpExpungeStaleExceptions();
else // rethrow
ForkJoinTask.rethrow(ex);
}
- // Signalling
-
/**
- * Tries to create or activate a worker if too few are active.
+ * Tries to create or release a worker if too few are running.
*/
final void signalWork() {
for (;;) {
- long c; int sp, i; WorkQueue v; WorkQueue[] ws;
+ long c; int sp; WorkQueue[] ws; int i; WorkQueue v;
if ((c = ctl) >= 0L) // enough workers
break;
else if ((sp = (int)c) == 0) { // no idle workers
@@ -1705,12 +1451,14 @@
else if ((v = ws[i]) == null)
break; // terminating
else {
- int ns = sp & ~UNSIGNALLED;
- int vs = v.scanState;
- long nc = (v.stackPred & SP_MASK) | (UC_MASK & (c + AC_UNIT));
- if (sp == vs && U.compareAndSwapLong(this, CTL, c, nc)) {
- v.scanState = ns;
- LockSupport.unpark(v.parker);
+ int np = sp & ~UNSIGNALLED;
+ int vp = v.phase;
+ long nc = (v.stackPred & SP_MASK) | (UC_MASK & (c + RC_UNIT));
+ Thread vt = v.owner;
+ if (sp == vp && CTL.compareAndSet(this, c, nc)) {
+ v.phase = np;
+ if (v.source < 0)
+ LockSupport.unpark(vt);
break;
}
}
@@ -1718,442 +1466,183 @@
}
/**
- * Signals and releases worker v if it is top of idle worker
- * stack. This performs a one-shot version of signalWork only if
- * there is (apparently) at least one idle worker.
+ * Tries to decrement counts (sometimes implicitly) and possibly
+ * arrange for a compensating worker in preparation for blocking:
+ * If not all core workers yet exist, creates one, else if any are
+ * unreleased (possibly including caller) releases one, else if
+ * fewer than the minimum allowed number of workers running,
+ * checks to see that they are all active, and if so creates an
+ * extra worker unless over maximum limit and policy is to
+ * saturate. Most of these steps can fail due to interference, in
+ * which case 0 is returned so caller will retry. A negative
+ * return value indicates that the caller doesn't need to
+ * re-adjust counts when later unblocked.
*
- * @param c incoming ctl value
- * @param v if non-null, a worker
- * @param inc the increment to active count (zero when compensating)
- * @return true if successful
- */
- private boolean tryRelease(long c, WorkQueue v, long inc) {
- int sp = (int)c, ns = sp & ~UNSIGNALLED;
- if (v != null) {
- int vs = v.scanState;
- long nc = (v.stackPred & SP_MASK) | (UC_MASK & (c + inc));
- if (sp == vs && U.compareAndSwapLong(this, CTL, c, nc)) {
- v.scanState = ns;
- LockSupport.unpark(v.parker);
- return true;
- }
- }
- return false;
- }
-
- /**
- * With approx probability of a missed signal, tries (once) to
- * reactivate worker w (or some other worker), failing if stale or
- * known to be already active.
- *
- * @param w the worker
- * @param ws the workQueue array to use
- * @param r random seed
- */
- private void tryReactivate(WorkQueue w, WorkQueue[] ws, int r) {
- long c; int sp, wl; WorkQueue v;
- if ((sp = (int)(c = ctl)) != 0 && w != null &&
- ws != null && (wl = ws.length) > 0 &&
- ((sp ^ r) & SS_SEQ) == 0 &&
- (v = ws[(wl - 1) & sp]) != null) {
- long nc = (v.stackPred & SP_MASK) | (UC_MASK & (c + AC_UNIT));
- int ns = sp & ~UNSIGNALLED;
- if (w.scanState < 0 &&
- v.scanState == sp &&
- U.compareAndSwapLong(this, CTL, c, nc)) {
- v.scanState = ns;
- LockSupport.unpark(v.parker);
- }
- }
- }
-
- /**
- * If worker w exists and is active, enqueues and sets status to inactive.
- *
- * @param w the worker
- * @param ss current (non-negative) scanState
+ * @return 1: block then adjust, -1: block without adjust, 0 : retry
*/
- private void inactivate(WorkQueue w, int ss) {
- int ns = (ss + SS_SEQ) | UNSIGNALLED;
- long lc = ns & SP_MASK, nc, c;
- if (w != null) {
- w.scanState = ns;
- do {
- nc = lc | (UC_MASK & ((c = ctl) - AC_UNIT));
- w.stackPred = (int)c;
- } while (!U.compareAndSwapLong(this, CTL, c, nc));
- }
- }
-
- /**
- * Possibly blocks worker w waiting for signal, or returns
- * negative status if the worker should terminate. May return
- * without status change if multiple stale unparks and/or
- * interrupts occur.
- *
- * @param w the calling worker
- * @return negative if w should terminate
- */
- private int awaitWork(WorkQueue w) {
- int stat = 0;
- if (w != null && w.scanState < 0) {
- long c = ctl;
- if ((int)(c >> AC_SHIFT) + (config & SMASK) <= 0)
- stat = timedAwaitWork(w, c); // possibly quiescent
- else if ((runState & STOP) != 0)
- stat = w.qlock = -1; // pool terminating
- else if (w.scanState < 0) {
- w.parker = Thread.currentThread();
- if (w.scanState < 0) // recheck after write
- LockSupport.park(this);
- w.parker = null;
- if ((runState & STOP) != 0)
- stat = w.qlock = -1; // recheck
- else if (w.scanState < 0)
- Thread.interrupted(); // clear status
+ private int tryCompensate(WorkQueue w) {
+ int t, n, sp;
+ long c = ctl;
+ WorkQueue[] ws = workQueues;
+ if ((t = (short)(c >>> TC_SHIFT)) >= 0) {
+ if (ws == null || (n = ws.length) <= 0 || w == null)
+ return 0; // disabled
+ else if ((sp = (int)c) != 0) { // replace or release
+ WorkQueue v = ws[sp & (n - 1)];
+ int wp = w.phase;
+ long uc = UC_MASK & ((wp < 0) ? c + RC_UNIT : c);
+ int np = sp & ~UNSIGNALLED;
+ if (v != null) {
+ int vp = v.phase;
+ Thread vt = v.owner;
+ long nc = ((long)v.stackPred & SP_MASK) | uc;
+ if (vp == sp && CTL.compareAndSet(this, c, nc)) {
+ v.phase = np;
+ if (v.source < 0)
+ LockSupport.unpark(vt);
+ return (wp < 0) ? -1 : 1;
+ }
+ }
+ return 0;
+ }
+ else if ((int)(c >> RC_SHIFT) - // reduce parallelism
+ (short)(bounds & SMASK) > 0) {
+ long nc = ((RC_MASK & (c - RC_UNIT)) | (~RC_MASK & c));
+ return CTL.compareAndSet(this, c, nc) ? 1 : 0;
}
- }
- return stat;
- }
-
- /**
- * Possibly triggers shutdown and tries (once) to block worker
- * when pool is (or may be) quiescent. Waits up to a duration
- * determined by number of workers. On timeout, if ctl has not
- * changed, terminates the worker, which will in turn wake up
- * another worker to possibly repeat this process.
- *
- * @param w the calling worker
- * @return negative if w should terminate
- */
- private int timedAwaitWork(WorkQueue w, long c) {
- int stat = 0;
- int scale = 1 - (short)(c >>> TC_SHIFT);
- long deadline = (((scale <= 0) ? 1 : scale) * IDLE_TIMEOUT_MS +
- System.currentTimeMillis());
- if ((runState >= 0 || (stat = tryTerminate(false, false)) > 0) &&
- w != null && w.scanState < 0) {
- int ss; AuxState aux;
- w.parker = Thread.currentThread();
- if (w.scanState < 0)
- LockSupport.parkUntil(this, deadline);
- w.parker = null;
- if ((runState & STOP) != 0)
- stat = w.qlock = -1; // pool terminating
- else if ((ss = w.scanState) < 0 && !Thread.interrupted() &&
- (int)c == ss && (aux = auxState) != null && ctl == c &&
- deadline - System.currentTimeMillis() <= TIMEOUT_SLOP_MS) {
- aux.lock();
- try { // pre-deregister
- WorkQueue[] ws;
- int cfg = w.config, idx = cfg & SMASK;
- long nc = ((UC_MASK & (c - TC_UNIT)) |
- (SP_MASK & w.stackPred));
- if ((runState & STOP) == 0 &&
- (ws = workQueues) != null &&
- idx < ws.length && idx >= 0 && ws[idx] == w &&
- U.compareAndSwapLong(this, CTL, c, nc)) {
- ws[idx] = null;
- w.config = cfg | UNREGISTERED;
- stat = w.qlock = -1;
+ else { // validate
+ int md = mode, pc = md & SMASK, tc = pc + t, bc = 0;
+ boolean unstable = false;
+ for (int i = 1; i < n; i += 2) {
+ WorkQueue q; Thread wt; Thread.State ts;
+ if ((q = ws[i]) != null) {
+ if (q.source == 0) {
+ unstable = true;
+ break;
+ }
+ else {
+ --tc;
+ if ((wt = q.owner) != null &&
+ ((ts = wt.getState()) == Thread.State.BLOCKED ||
+ ts == Thread.State.WAITING))
+ ++bc; // worker is blocking
+ }
}
- } finally {
- aux.unlock();
+ }
+ if (unstable || tc != 0 || ctl != c)
+ return 0; // inconsistent
+ else if (t + pc >= MAX_CAP || t >= (bounds >>> SWIDTH)) {
+ Predicate<? super ForkJoinPool> sat;
+ if ((sat = saturate) != null && sat.test(this))
+ return -1;
+ else if (bc < pc) { // lagging
+ Thread.yield(); // for retry spins
+ return 0;
+ }
+ else
+ throw new RejectedExecutionException(
+ "Thread limit exceeded replacing blocked worker");
}
}
}
- return stat;
- }
- /**
- * If the given worker is a spare with no queued tasks, and there
- * are enough existing workers, drops it from ctl counts and sets
- * its state to terminated.
- *
- * @param w the calling worker -- must be a spare
- * @return true if dropped (in which case it must not process more tasks)
- */
- private boolean tryDropSpare(WorkQueue w) {
- if (w != null && w.isEmpty()) { // no local tasks
- long c; int sp, wl; WorkQueue[] ws; WorkQueue v;
- while ((short)((c = ctl) >> TC_SHIFT) > 0 &&
- ((sp = (int)c) != 0 || (int)(c >> AC_SHIFT) > 0) &&
- (ws = workQueues) != null && (wl = ws.length) > 0) {
- boolean dropped, canDrop;
- if (sp == 0) { // no queued workers
- long nc = ((AC_MASK & (c - AC_UNIT)) |
- (TC_MASK & (c - TC_UNIT)) | (SP_MASK & c));
- dropped = U.compareAndSwapLong(this, CTL, c, nc);
- }
- else if (
- (v = ws[(wl - 1) & sp]) == null || v.scanState != sp)
- dropped = false; // stale; retry
- else {
- long nc = v.stackPred & SP_MASK;
- if (w == v || w.scanState >= 0) {
- canDrop = true; // w unqueued or topmost
- nc |= ((AC_MASK & c) | // ensure replacement
- (TC_MASK & (c - TC_UNIT)));
- }
- else { // w may be queued
- canDrop = false; // help uncover
- nc |= ((AC_MASK & (c + AC_UNIT)) |
- (TC_MASK & c));
- }
- if (U.compareAndSwapLong(this, CTL, c, nc)) {
- v.scanState = sp & ~UNSIGNALLED;
- LockSupport.unpark(v.parker);
- dropped = canDrop;
- }
- else
- dropped = false;
- }
- if (dropped) { // pre-deregister
- int cfg = w.config, idx = cfg & SMASK;
- if (idx >= 0 && idx < ws.length && ws[idx] == w)
- ws[idx] = null;
- w.config = cfg | UNREGISTERED;
- w.qlock = -1;
- return true;
- }
- }
- }
- return false;
+ long nc = ((c + TC_UNIT) & TC_MASK) | (c & ~TC_MASK); // expand pool
+ return CTL.compareAndSet(this, c, nc) && createWorker() ? 1 : 0;
}
/**
* Top-level runloop for workers, called by ForkJoinWorkerThread.run.
+ * See above for explanation.
*/
final void runWorker(WorkQueue w) {
+ WorkQueue[] ws;
w.growArray(); // allocate queue
- int bound = (w.config & SPARE_WORKER) != 0 ? 0 : POLL_LIMIT;
- long seed = w.hint * 0xdaba0b6eb09322e3L; // initial random seed
- if ((runState & STOP) == 0) {
- for (long r = (seed == 0L) ? 1L : seed;;) { // ensure nonzero
- if (bound == 0 && tryDropSpare(w))
- break;
- // high bits of prev seed for step; current low bits for idx
- int step = (int)(r >>> 48) | 1;
- r ^= r >>> 12; r ^= r << 25; r ^= r >>> 27; // xorshift
- if (scan(w, bound, step, (int)r) < 0 && awaitWork(w) < 0)
- break;
- }
- }
- }
-
- // Scanning for tasks
-
- /**
- * Repeatedly scans for and tries to steal and execute (via
- * workQueue.runTask) a queued task. Each scan traverses queues in
- * pseudorandom permutation. Upon finding a non-empty queue, makes
- * at most the given bound attempts to re-poll (fewer if
- * contended) on the same queue before returning (impossible
- * scanState value) 0 to restart scan. Else returns after at least
- * 1 and at most 32 full scans.
- *
- * @param w the worker (via its WorkQueue)
- * @param bound repoll bound as bitmask (0 if spare)
- * @param step (circular) index increment per iteration (must be odd)
- * @param r a random seed for origin index
- * @return negative if should await signal
- */
- private int scan(WorkQueue w, int bound, int step, int r) {
- int stat = 0, wl; WorkQueue[] ws;
- if ((ws = workQueues) != null && w != null && (wl = ws.length) > 0) {
- for (int m = wl - 1,
- origin = m & r, idx = origin,
- npolls = 0,
- ss = w.scanState;;) { // negative if inactive
- WorkQueue q; ForkJoinTask<?>[] a; int b, al;
- if ((q = ws[idx]) != null && (b = q.base) - q.top < 0 &&
+ int r = w.id ^ ThreadLocalRandom.nextSecondarySeed();
+ if (r == 0) // initial nonzero seed
+ r = 1;
+ int lastSignalId = 0; // avoid unneeded signals
+ while ((ws = workQueues) != null) {
+ boolean nonempty = false; // scan
+ for (int n = ws.length, j = n, m = n - 1; j > 0; --j) {
+ WorkQueue q; int i, b, al; ForkJoinTask<?>[] a;
+ if ((i = r & m) >= 0 && i < n && // always true
+ (q = ws[i]) != null && (b = q.base) - q.top < 0 &&
(a = q.array) != null && (al = a.length) > 0) {
+ int qid = q.id; // (never zero)
int index = (al - 1) & b;
- long offset = ((long)index << ASHIFT) + ABASE;
ForkJoinTask<?> t = (ForkJoinTask<?>)
- U.getObjectVolatile(a, offset);
- if (t == null)
- break; // empty or busy
- else if (b++ != q.base)
- break; // busy
- else if (ss < 0) {
- tryReactivate(w, ws, r);
- break; // retry upon rescan
- }
- else if (!U.compareAndSwapObject(a, offset, t, null))
- break; // contended
- else {
- q.base = b;
- w.currentSteal = t;
- if (b != q.top) // propagate signal
- signalWork();
- w.runTask(t);
- if (++npolls > bound)
- break;
+ QA.getAcquire(a, index);
+ if (t != null && b++ == q.base &&
+ QA.compareAndSet(a, index, t, null)) {
+ if ((q.base = b) - q.top < 0 && qid != lastSignalId)
+ signalWork(); // propagate signal
+ w.source = lastSignalId = qid;
+ t.doExec();
+ if ((w.id & FIFO) != 0) // run remaining locals
+ w.localPollAndExec(POLL_LIMIT);
+ else
+ w.localPopAndExec(POLL_LIMIT);
+ ForkJoinWorkerThread thread = w.owner;
+ ++w.nsteals;
+ w.source = 0; // now idle
+ if (thread != null)
+ thread.afterTopLevelExec();
}
+ nonempty = true;
}
- else if (npolls != 0) // rescan
+ else if (nonempty)
break;
- else if ((idx = (idx + step) & m) == origin) {
- if (ss < 0) { // await signal
- stat = ss;
- break;
- }
- else if (r >= 0) {
- inactivate(w, ss);
- break;
- }
- else
- r <<= 1; // at most 31 rescans
- }
+ else
+ ++r;
}
- }
- return stat;
- }
-
- // Joining tasks
- /**
- * Tries to steal and run tasks within the target's computation.
- * Uses a variant of the top-level algorithm, restricted to tasks
- * with the given task as ancestor: It prefers taking and running
- * eligible tasks popped from the worker's own queue (via
- * popCC). Otherwise it scans others, randomly moving on
- * contention or execution, deciding to give up based on a
- * checksum (via return codes from pollAndExecCC). The maxTasks
- * argument supports external usages; internal calls use zero,
- * allowing unbounded steps (external calls trap non-positive
- * values).
- *
- * @param w caller
- * @param maxTasks if non-zero, the maximum number of other tasks to run
- * @return task status on exit
- */
- final int helpComplete(WorkQueue w, CountedCompleter<?> task,
- int maxTasks) {
- WorkQueue[] ws; int s = 0, wl;
- if ((ws = workQueues) != null && (wl = ws.length) > 1 &&
- task != null && w != null) {
- for (int m = wl - 1,
- mode = w.config,
- r = ~mode, // scanning seed
- origin = r & m, k = origin, // first queue to scan
- step = 3, // first scan step
- h = 1, // 1:ran, >1:contended, <0:hash
- oldSum = 0, checkSum = 0;;) {
- CountedCompleter<?> p; WorkQueue q; int i;
- if ((s = task.status) < 0)
- break;
- if (h == 1 && (p = w.popCC(task, mode)) != null) {
- p.doExec(); // run local task
- if (maxTasks != 0 && --maxTasks == 0)
- break;
- origin = k; // reset
- oldSum = checkSum = 0;
- }
- else { // poll other worker queues
- if ((i = k | 1) < 0 || i > m || (q = ws[i]) == null)
- h = 0;
- else if ((h = q.pollAndExecCC(task)) < 0)
- checkSum += h;
- if (h > 0) {
- if (h == 1 && maxTasks != 0 && --maxTasks == 0)
- break;
- step = (r >>> 16) | 3;
- r ^= r << 13; r ^= r >>> 17; r ^= r << 5; // xorshift
- k = origin = r & m; // move and restart
- oldSum = checkSum = 0;
- }
- else if ((k = (k + step) & m) == origin) {
- if (oldSum == (oldSum = checkSum))
- break;
- checkSum = 0;
- }
- }
+ if (nonempty) { // move (xorshift)
+ r ^= r << 13; r ^= r >>> 17; r ^= r << 5;
}
- }
- return s;
- }
-
- /**
- * Tries to locate and execute tasks for a stealer of the given
- * task, or in turn one of its stealers. Traces currentSteal ->
- * currentJoin links looking for a thread working on a descendant
- * of the given task and with a non-empty queue to steal back and
- * execute tasks from. The first call to this method upon a
- * waiting join will often entail scanning/search, (which is OK
- * because the joiner has nothing better to do), but this method
- * leaves hints in workers to speed up subsequent calls.
- *
- * @param w caller
- * @param task the task to join
- */
- private void helpStealer(WorkQueue w, ForkJoinTask<?> task) {
- if (task != null && w != null) {
- ForkJoinTask<?> ps = w.currentSteal;
- WorkQueue[] ws; int wl, oldSum = 0;
- outer: while (w.tryRemoveAndExec(task) && task.status >= 0 &&
- (ws = workQueues) != null && (wl = ws.length) > 0) {
- ForkJoinTask<?> subtask;
- int m = wl - 1, checkSum = 0; // for stability check
- WorkQueue j = w, v; // v is subtask stealer
- descent: for (subtask = task; subtask.status >= 0; ) {
- for (int h = j.hint | 1, k = 0, i;;) {
- if ((v = ws[i = (h + (k << 1)) & m]) != null) {
- if (v.currentSteal == subtask) {
- j.hint = i;
- break;
- }
- checkSum += v.base;
+ else {
+ int phase;
+ lastSignalId = 0; // clear for next scan
+ if ((phase = w.phase) >= 0) { // enqueue
+ int np = w.phase = (phase + SS_SEQ) | UNSIGNALLED;
+ long c, nc;
+ do {
+ w.stackPred = (int)(c = ctl);
+ nc = ((c - RC_UNIT) & UC_MASK) | (SP_MASK & np);
+ } while (!CTL.weakCompareAndSetVolatile(this, c, nc));
+ }
+ else { // already queued
+ int pred = w.stackPred;
+ w.source = DORMANT; // enable signal
+ for (int steps = 0;;) {
+ int md, rc; long c;
+ if (w.phase >= 0) {
+ w.source = 0;
+ break;
}
- if (++k > m) // can't find stealer
- break outer;
- }
-
- for (;;) { // help v or descend
- ForkJoinTask<?>[] a; int b, al;
- if (subtask.status < 0) // too late to help
- break descent;
- checkSum += (b = v.base);
- ForkJoinTask<?> next = v.currentJoin;
- ForkJoinTask<?> t = null;
- if ((a = v.array) != null && (al = a.length) > 0) {
- int index = (al - 1) & b;
- long offset = ((long)index << ASHIFT) + ABASE;
- t = (ForkJoinTask<?>)
- U.getObjectVolatile(a, offset);
- if (t != null && b++ == v.base) {
- if (j.currentJoin != subtask ||
- v.currentSteal != subtask ||
- subtask.status < 0)
- break descent; // stale
- if (U.compareAndSwapObject(a, offset, t, null)) {
- v.base = b;
- w.currentSteal = t;
- for (int top = w.top;;) {
- t.doExec(); // help
- w.currentSteal = ps;
- if (task.status < 0)
- break outer;
- if (w.top == top)
- break; // run local tasks
- if ((t = w.pop()) == null)
- break descent;
- w.currentSteal = t;
- }
+ else if ((md = mode) < 0) // shutting down
+ return;
+ else if ((rc = ((md & SMASK) + // possibly quiescent
+ (int)((c = ctl) >> RC_SHIFT))) <= 0 &&
+ (md & SHUTDOWN) != 0 &&
+ tryTerminate(false, false))
+ return; // help terminate
+ else if ((++steps & 1) == 0)
+ Thread.interrupted(); // clear between parks
+ else if (rc <= 0 && pred != 0 && phase == (int)c) {
+ long d = keepAlive + System.currentTimeMillis();
+ LockSupport.parkUntil(this, d);
+ if (ctl == c &&
+ d - System.currentTimeMillis() <= TIMEOUT_SLOP) {
+ long nc = ((UC_MASK & (c - TC_UNIT)) |
+ (SP_MASK & pred));
+ if (CTL.compareAndSet(this, c, nc)) {
+ w.phase = QUIET;
+ return; // drop on timeout
}
}
}
- if (t == null && b == v.base && b - v.top >= 0) {
- if ((subtask = next) == null) { // try to descend
- if (next == v.currentJoin &&
- oldSum == (oldSum = checkSum))
- break outer;
- break descent;
- }
- j = v;
- break;
- }
+ else
+ LockSupport.park(this);
}
}
}
@@ -2161,59 +1650,10 @@
}
/**
- * Tries to decrement active count (sometimes implicitly) and
- * possibly release or create a compensating worker in preparation
- * for blocking. Returns false (retryable by caller), on
- * contention, detected staleness, instability, or termination.
- *
- * @param w caller
- */
- private boolean tryCompensate(WorkQueue w) {
- boolean canBlock; int wl;
- long c = ctl;
- WorkQueue[] ws = workQueues;
- int pc = config & SMASK;
- int ac = pc + (int)(c >> AC_SHIFT);
- int tc = pc + (short)(c >> TC_SHIFT);
- if (w == null || w.qlock < 0 || pc == 0 || // terminating or disabled
- ws == null || (wl = ws.length) <= 0)
- canBlock = false;
- else {
- int m = wl - 1, sp;
- boolean busy = true; // validate ac
- for (int i = 0; i <= m; ++i) {
- int k; WorkQueue v;
- if ((k = (i << 1) | 1) <= m && k >= 0 && (v = ws[k]) != null &&
- v.scanState >= 0 && v.currentSteal == null) {
- busy = false;
- break;
- }
- }
- if (!busy || ctl != c)
- canBlock = false; // unstable or stale
- else if ((sp = (int)c) != 0) // release idle worker
- canBlock = tryRelease(c, ws[m & sp], 0L);
- else if (tc >= pc && ac > 1 && w.isEmpty()) {
- long nc = ((AC_MASK & (c - AC_UNIT)) |
- (~AC_MASK & c)); // uncompensated
- canBlock = U.compareAndSwapLong(this, CTL, c, nc);
- }
- else if (tc >= MAX_CAP ||
- (this == common && tc >= pc + COMMON_MAX_SPARES))
- throw new RejectedExecutionException(
- "Thread limit exceeded replacing blocked worker");
- else { // similar to tryAddWorker
- boolean isSpare = (tc >= pc);
- long nc = (AC_MASK & c) | (TC_MASK & (c + TC_UNIT));
- canBlock = (U.compareAndSwapLong(this, CTL, c, nc) &&
- createWorker(isSpare)); // throws on exception
- }
- }
- return canBlock;
- }
-
- /**
* Helps and/or blocks until the given task is done or timeout.
+ * First tries locally helping, then scans other queues for a task
+ * produced by one of w's stealers; compensating and blocking if
+ * none are found (rescanning if tryCompensate fails).
*
* @param w caller
* @param task the task
@@ -2222,61 +1662,166 @@
*/
final int awaitJoin(WorkQueue w, ForkJoinTask<?> task, long deadline) {
int s = 0;
- if (w != null) {
- ForkJoinTask<?> prevJoin = w.currentJoin;
- if (task != null && (s = task.status) >= 0) {
- w.currentJoin = task;
- CountedCompleter<?> cc = (task instanceof CountedCompleter) ?
- (CountedCompleter<?>)task : null;
- for (;;) {
- if (cc != null)
- helpComplete(w, cc, 0);
- else
- helpStealer(w, task);
- if ((s = task.status) < 0)
- break;
- long ms, ns;
+ if (w != null && task != null &&
+ (!(task instanceof CountedCompleter) ||
+ (s = w.localHelpCC((CountedCompleter<?>)task, 0)) >= 0)) {
+ w.tryRemoveAndExec(task);
+ int src = w.source, id = w.id;
+ s = task.status;
+ while (s >= 0) {
+ WorkQueue[] ws;
+ boolean nonempty = false;
+ int r = ThreadLocalRandom.nextSecondarySeed() | 1; // odd indices
+ if ((ws = workQueues) != null) { // scan for matching id
+ for (int n = ws.length, m = n - 1, j = -n; j < n; j += 2) {
+ WorkQueue q; int i, b, al; ForkJoinTask<?>[] a;
+ if ((i = (r + j) & m) >= 0 && i < n &&
+ (q = ws[i]) != null && q.source == id &&
+ (b = q.base) - q.top < 0 &&
+ (a = q.array) != null && (al = a.length) > 0) {
+ int qid = q.id;
+ int index = (al - 1) & b;
+ ForkJoinTask<?> t = (ForkJoinTask<?>)
+ QA.getAcquire(a, index);
+ if (t != null && b++ == q.base && id == q.source &&
+ QA.compareAndSet(a, index, t, null)) {
+ q.base = b;
+ w.source = qid;
+ t.doExec();
+ w.source = src;
+ }
+ nonempty = true;
+ break;
+ }
+ }
+ }
+ if ((s = task.status) < 0)
+ break;
+ else if (!nonempty) {
+ long ms, ns; int block;
if (deadline == 0L)
- ms = 0L;
+ ms = 0L; // untimed
else if ((ns = deadline - System.nanoTime()) <= 0L)
- break;
+ break; // timeout
else if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) <= 0L)
- ms = 1L;
- if (tryCompensate(w)) {
+ ms = 1L; // avoid 0 for timed wait
+ if ((block = tryCompensate(w)) != 0) {
task.internalWait(ms);
- U.getAndAddLong(this, CTL, AC_UNIT);
+ CTL.getAndAdd(this, (block > 0) ? RC_UNIT : 0L);
}
- if ((s = task.status) < 0)
- break;
+ s = task.status;
}
- w.currentJoin = prevJoin;
}
}
return s;
}
- // Specialized scanning
+ /**
+ * Runs tasks until {@code isQuiescent()}. Rather than blocking
+ * when tasks cannot be found, rescans until all others cannot
+ * find tasks either.
+ */
+ final void helpQuiescePool(WorkQueue w) {
+ int prevSrc = w.source, fifo = w.id & FIFO;
+ for (int source = prevSrc, released = -1;;) { // -1 until known
+ WorkQueue[] ws;
+ if (fifo != 0)
+ w.localPollAndExec(0);
+ else
+ w.localPopAndExec(0);
+ if (released == -1 && w.phase >= 0)
+ released = 1;
+ boolean quiet = true, empty = true;
+ int r = ThreadLocalRandom.nextSecondarySeed();
+ if ((ws = workQueues) != null) {
+ for (int n = ws.length, j = n, m = n - 1; j > 0; --j) {
+ WorkQueue q; int i, b, al; ForkJoinTask<?>[] a;
+ if ((i = (r - j) & m) >= 0 && i < n && (q = ws[i]) != null) {
+ if ((b = q.base) - q.top < 0 &&
+ (a = q.array) != null && (al = a.length) > 0) {
+ int qid = q.id;
+ if (released == 0) { // increment
+ released = 1;
+ CTL.getAndAdd(this, RC_UNIT);
+ }
+ int index = (al - 1) & b;
+ ForkJoinTask<?> t = (ForkJoinTask<?>)
+ QA.getAcquire(a, index);
+ if (t != null && b++ == q.base &&
+ QA.compareAndSet(a, index, t, null)) {
+ q.base = b;
+ w.source = source = q.id;
+ t.doExec();
+ w.source = source = prevSrc;
+ }
+ quiet = empty = false;
+ break;
+ }
+ else if ((q.source & QUIET) == 0)
+ quiet = false;
+ }
+ }
+ }
+ if (quiet) {
+ if (released == 0)
+ CTL.getAndAdd(this, RC_UNIT);
+ w.source = prevSrc;
+ break;
+ }
+ else if (empty) {
+ if (source != QUIET)
+ w.source = source = QUIET;
+ if (released == 1) { // decrement
+ released = 0;
+ CTL.getAndAdd(this, RC_MASK & -RC_UNIT);
+ }
+ }
+ }
+ }
/**
- * Returns a (probably) non-empty steal queue, if one is found
- * during a scan, else null. This method must be retried by
- * caller if, by the time it tries to use the queue, it is empty.
+ * Scans for and returns a polled task, if available.
+ * Used only for untracked polls.
+ *
+ * @param submissionsOnly if true, only scan submission queues
*/
- private WorkQueue findNonEmptyStealQueue() {
- WorkQueue[] ws; int wl; // one-shot version of scan loop
- int r = ThreadLocalRandom.nextSecondarySeed();
- if ((ws = workQueues) != null && (wl = ws.length) > 0) {
- int m = wl - 1, origin = r & m;
+ private ForkJoinTask<?> pollScan(boolean submissionsOnly) {
+ WorkQueue[] ws; int n;
+ rescan: while ((mode & STOP) == 0 && (ws = workQueues) != null &&
+ (n = ws.length) > 0) {
+ int m = n - 1;
+ int r = ThreadLocalRandom.nextSecondarySeed();
+ int h = r >>> 16;
+ int origin, step;
+ if (submissionsOnly) {
+ origin = (r & ~1) & m; // even indices and steps
+ step = (h & ~1) | 2;
+ }
+ else {
+ origin = r & m;
+ step = h | 1;
+ }
for (int k = origin, oldSum = 0, checkSum = 0;;) {
- WorkQueue q; int b;
+ WorkQueue q; int b, al; ForkJoinTask<?>[] a;
if ((q = ws[k]) != null) {
- if ((b = q.base) - q.top < 0)
- return q;
- checkSum += b;
+ checkSum += b = q.base;
+ if (b - q.top < 0 &&
+ (a = q.array) != null && (al = a.length) > 0) {
+ int index = (al - 1) & b;
+ ForkJoinTask<?> t = (ForkJoinTask<?>)
+ QA.getAcquire(a, index);
+ if (t != null && b++ == q.base &&
+ QA.compareAndSet(a, index, t, null)) {
+ q.base = b;
+ return t;
+ }
+ else
+ break; // restart
+ }
}
- if ((k = (k + 1) & m) == origin) {
+ if ((k = (k + step) & m) == origin) {
if (oldSum == (oldSum = checkSum))
- break;
+ break rescan;
checkSum = 0;
}
}
@@ -2285,58 +1830,160 @@
}
/**
- * Runs tasks until {@code isQuiescent()}. We piggyback on
- * active count ctl maintenance, but rather than blocking
- * when tasks cannot be found, we rescan until all others cannot
- * find tasks either.
- */
- final void helpQuiescePool(WorkQueue w) {
- ForkJoinTask<?> ps = w.currentSteal; // save context
- int wc = w.config;
- for (boolean active = true;;) {
- long c; WorkQueue q; ForkJoinTask<?> t;
- if (wc >= 0 && (t = w.pop()) != null) { // run locals if LIFO
- (w.currentSteal = t).doExec();
- w.currentSteal = ps;
- }
- else if ((q = findNonEmptyStealQueue()) != null) {
- if (!active) { // re-establish active count
- active = true;
- U.getAndAddLong(this, CTL, AC_UNIT);
- }
- if ((t = q.pollAt(q.base)) != null) {
- (w.currentSteal = t).doExec();
- w.currentSteal = ps;
- if (++w.nsteals < 0)
- w.transferStealCount(this);
- }
- }
- else if (active) { // decrement active count without queuing
- long nc = (AC_MASK & ((c = ctl) - AC_UNIT)) | (~AC_MASK & c);
- if (U.compareAndSwapLong(this, CTL, c, nc))
- active = false;
- }
- else if ((int)((c = ctl) >> AC_SHIFT) + (config & SMASK) <= 0 &&
- U.compareAndSwapLong(this, CTL, c, c + AC_UNIT))
- break;
- }
- }
-
- /**
* Gets and removes a local or stolen task for the given worker.
*
* @return a task, if available
*/
final ForkJoinTask<?> nextTaskFor(WorkQueue w) {
- for (ForkJoinTask<?> t;;) {
- WorkQueue q;
- if ((t = w.nextLocalTask()) != null)
- return t;
- if ((q = findNonEmptyStealQueue()) == null)
- return null;
- if ((t = q.pollAt(q.base)) != null)
- return t;
+ ForkJoinTask<?> t;
+ if (w != null &&
+ (t = (w.id & FIFO) != 0 ? w.poll() : w.pop()) != null)
+ return t;
+ else
+ return pollScan(false);
+ }
+
+ // External operations
+
+ /**
+ * Adds the given task to a submission queue at submitter's
+ * current queue, creating one if null or contended.
+ *
+ * @param task the task. Caller must ensure non-null.
+ */
+ final void externalPush(ForkJoinTask<?> task) {
+ int r; // initialize caller's probe
+ if ((r = ThreadLocalRandom.getProbe()) == 0) {
+ ThreadLocalRandom.localInit();
+ r = ThreadLocalRandom.getProbe();
}
+ for (;;) {
+ int md = mode, n;
+ WorkQueue[] ws = workQueues;
+ if ((md & SHUTDOWN) != 0 || ws == null || (n = ws.length) <= 0)
+ throw new RejectedExecutionException();
+ else {
+ WorkQueue q;
+ boolean push = false, grow = false;
+ if ((q = ws[(n - 1) & r & SQMASK]) == null) {
+ Object lock = workerNamePrefix;
+ int qid = (r | QUIET) & ~(FIFO | OWNED);
+ q = new WorkQueue(this, null);
+ q.id = qid;
+ q.source = QUIET;
+ q.phase = QLOCK; // lock queue
+ if (lock != null) {
+ synchronized (lock) { // lock pool to install
+ int i;
+ if ((ws = workQueues) != null &&
+ (n = ws.length) > 0 &&
+ ws[i = qid & (n - 1) & SQMASK] == null) {
+ ws[i] = q;
+ push = grow = true;
+ }
+ }
+ }
+ }
+ else if (q.tryLockSharedQueue()) {
+ int b = q.base, s = q.top, al, d; ForkJoinTask<?>[] a;
+ if ((a = q.array) != null && (al = a.length) > 0 &&
+ al - 1 + (d = b - s) > 0) {
+ a[(al - 1) & s] = task;
+ q.top = s + 1; // relaxed writes OK here
+ q.phase = 0;
+ if (d < 0 && q.base - s < -1)
+ break; // no signal needed
+ }
+ else
+ grow = true;
+ push = true;
+ }
+ if (push) {
+ if (grow) {
+ try {
+ q.growArray();
+ int s = q.top, al; ForkJoinTask<?>[] a;
+ if ((a = q.array) != null && (al = a.length) > 0) {
+ a[(al - 1) & s] = task;
+ q.top = s + 1;
+ }
+ } finally {
+ q.phase = 0;
+ }
+ }
+ signalWork();
+ break;
+ }
+ else // move if busy
+ r = ThreadLocalRandom.advanceProbe(r);
+ }
+ }
+ }
+
+ /**
+ * Pushes a possibly-external submission.
+ */
+ private <T> ForkJoinTask<T> externalSubmit(ForkJoinTask<T> task) {
+ Thread t; ForkJoinWorkerThread w; WorkQueue q;
+ if (task == null)
+ throw new NullPointerException();
+ if (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) &&
+ (w = (ForkJoinWorkerThread)t).pool == this &&
+ (q = w.workQueue) != null)
+ q.push(task);
+ else
+ externalPush(task);
+ return task;
+ }
+
+ /**
+ * Returns common pool queue for an external thread.
+ */
+ static WorkQueue commonSubmitterQueue() {
+ ForkJoinPool p = common;
+ int r = ThreadLocalRandom.getProbe();
+ WorkQueue[] ws; int n;
+ return (p != null && (ws = p.workQueues) != null &&
+ (n = ws.length) > 0) ?
+ ws[(n - 1) & r & SQMASK] : null;
+ }
+
+ /**
+ * Performs tryUnpush for an external submitter.
+ */
+ final boolean tryExternalUnpush(ForkJoinTask<?> task) {
+ int r = ThreadLocalRandom.getProbe();
+ WorkQueue[] ws; WorkQueue w; int n;
+ return ((ws = workQueues) != null &&
+ (n = ws.length) > 0 &&
+ (w = ws[(n - 1) & r & SQMASK]) != null &&
+ w.trySharedUnpush(task));
+ }
+
+ /**
+ * Performs helpComplete for an external submitter.
+ */
+ final int externalHelpComplete(CountedCompleter<?> task, int maxTasks) {
+ int r = ThreadLocalRandom.getProbe();
+ WorkQueue[] ws; WorkQueue w; int n;
+ return ((ws = workQueues) != null && (n = ws.length) > 0 &&
+ (w = ws[(n - 1) & r & SQMASK]) != null) ?
+ w.sharedHelpCC(task, maxTasks) : 0;
+ }
+
+ /**
+ * Tries to steal and run tasks within the target's computation.
+ * The maxTasks argument supports external usages; internal calls
+ * use zero, allowing unbounded steps (external calls trap
+ * non-positive values).
+ *
+ * @param w caller
+ * @param maxTasks if non-zero, the maximum number of other tasks to run
+ * @return task status on exit
+ */
+ final int helpComplete(WorkQueue w, CountedCompleter<?> task,
+ int maxTasks) {
+ return (w == null) ? 0 : w.localHelpCC(task, maxTasks);
}
/**
@@ -2383,10 +2030,12 @@
*/
static int getSurplusQueuedTaskCount() {
Thread t; ForkJoinWorkerThread wt; ForkJoinPool pool; WorkQueue q;
- if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) {
- int p = (pool = (wt = (ForkJoinWorkerThread)t).pool).config & SMASK;
- int n = (q = wt.workQueue).top - q.base;
- int a = (int)(pool.ctl >> AC_SHIFT) + p;
+ if (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) &&
+ (pool = (wt = (ForkJoinWorkerThread)t).pool) != null &&
+ (q = wt.workQueue) != null) {
+ int p = pool.mode & SMASK;
+ int a = p + (int)(pool.ctl >> RC_SHIFT);
+ int n = q.top - q.base;
return n - (a > (p >>>= 1) ? 0 :
a > (p >>>= 1) ? 1 :
a > (p >>>= 1) ? 2 :
@@ -2396,7 +2045,7 @@
return 0;
}
- // Termination
+ // Termination
/**
* Possibly initiates and/or completes termination.
@@ -2404,198 +2053,86 @@
* @param now if true, unconditionally terminate, else only
* if no work and no active workers
* @param enable if true, terminate when next possible
- * @return -1: terminating/terminated, 0: retry if internal caller, else 1
+ * @return true if terminating or terminated
*/
- private int tryTerminate(boolean now, boolean enable) {
- int rs; // 3 phases: try to set SHUTDOWN, then STOP, then TERMINATED
+ private boolean tryTerminate(boolean now, boolean enable) {
+ int md; // 3 phases: try to set SHUTDOWN, then STOP, then TERMINATED
- while ((rs = runState) >= 0) {
+ while (((md = mode) & SHUTDOWN) == 0) {
if (!enable || this == common) // cannot shutdown
- return 1;
- else if (rs == 0)
- tryInitialize(false); // ensure initialized
+ return false;
else
- U.compareAndSwapInt(this, RUNSTATE, rs, rs | SHUTDOWN);
+ MODE.compareAndSet(this, md, md | SHUTDOWN);
}
- if ((rs & STOP) == 0) { // try to initiate termination
- if (!now) { // check quiescence
+ while (((md = mode) & STOP) == 0) { // try to initiate termination
+ if (!now) { // check if quiescent & empty
for (long oldSum = 0L;;) { // repeat until stable
- WorkQueue[] ws; WorkQueue w; int b;
+ boolean running = false;
long checkSum = ctl;
- if ((int)(checkSum >> AC_SHIFT) + (config & SMASK) > 0)
- return 0; // still active workers
- if ((ws = workQueues) != null) {
+ WorkQueue[] ws = workQueues;
+ if ((md & SMASK) + (int)(checkSum >> RC_SHIFT) > 0)
+ running = true;
+ else if (ws != null) {
+ WorkQueue w; int b;
for (int i = 0; i < ws.length; ++i) {
if ((w = ws[i]) != null) {
- checkSum += (b = w.base);
- if (w.currentSteal != null || b != w.top)
- return 0; // retry if internal caller
+ checkSum += (b = w.base) + w.id;
+ if (b != w.top ||
+ ((i & 1) == 1 && w.source >= 0)) {
+ running = true;
+ break;
+ }
}
}
}
- if (oldSum == (oldSum = checkSum))
+ if (((md = mode) & STOP) != 0)
+ break; // already triggered
+ else if (running)
+ return false;
+ else if (workQueues == ws && oldSum == (oldSum = checkSum))
break;
}
}
- do {} while (!U.compareAndSwapInt(this, RUNSTATE,
- rs = runState, rs | STOP));
+ if ((md & STOP) == 0)
+ MODE.compareAndSet(this, md, md | STOP);
}
- for (long oldSum = 0L;;) { // repeat until stable
- WorkQueue[] ws; WorkQueue w; ForkJoinWorkerThread wt;
- long checkSum = ctl;
- if ((ws = workQueues) != null) { // help terminate others
- for (int i = 0; i < ws.length; ++i) {
- if ((w = ws[i]) != null) {
- w.cancelAll(); // clear queues
- checkSum += w.base;
- if (w.qlock >= 0) {
- w.qlock = -1; // racy set OK
- if ((wt = w.owner) != null) {
+ while (((md = mode) & TERMINATED) == 0) { // help terminate others
+ for (long oldSum = 0L;;) { // repeat until stable
+ WorkQueue[] ws; WorkQueue w;
+ long checkSum = ctl;
+ if ((ws = workQueues) != null) {
+ for (int i = 0; i < ws.length; ++i) {
+ if ((w = ws[i]) != null) {
+ ForkJoinWorkerThread wt = w.owner;
+ w.cancelAll(); // clear queues
+ if (wt != null) {
try { // unblock join or park
wt.interrupt();
} catch (Throwable ignore) {
}
}
+ checkSum += w.base + w.id;
}
}
}
- }
- if (oldSum == (oldSum = checkSum))
- break;
- }
-
- if ((short)(ctl >>> TC_SHIFT) + (config & SMASK) <= 0) {
- runState = (STARTED | SHUTDOWN | STOP | TERMINATED); // final write
- synchronized (this) {
- notifyAll(); // for awaitTermination
+ if (((md = mode) & TERMINATED) != 0 ||
+ (workQueues == ws && oldSum == (oldSum = checkSum)))
+ break;
}
- }
-
- return -1;
- }
-
- // External operations
-
- /**
- * Constructs and tries to install a new external queue,
- * failing if the workQueues array already has a queue at
- * the given index.
- *
- * @param index the index of the new queue
- */
- private void tryCreateExternalQueue(int index) {
- AuxState aux;
- if ((aux = auxState) != null && index >= 0) {
- WorkQueue q = new WorkQueue(this, null);
- q.config = index;
- q.scanState = ~UNSIGNALLED;
- q.qlock = 1; // lock queue
- boolean installed = false;
- aux.lock();
- try { // lock pool to install
- WorkQueue[] ws;
- if ((ws = workQueues) != null && index < ws.length &&
- ws[index] == null) {
- ws[index] = q; // else throw away
- installed = true;
+ if ((md & TERMINATED) != 0)
+ break;
+ else if ((md & SMASK) + (short)(ctl >>> TC_SHIFT) > 0)
+ break;
+ else if (MODE.compareAndSet(this, md, md | TERMINATED)) {
+ synchronized (this) {
+ notifyAll(); // for awaitTermination
}
- } finally {
- aux.unlock();
- }
- if (installed) {
- try {
- q.growArray();
- } finally {
- q.qlock = 0;
- }
+ break;
}
}
- }
-
- /**
- * Adds the given task to a submission queue at submitter's
- * current queue. Also performs secondary initialization upon the
- * first submission of the first task to the pool, and detects
- * first submission by an external thread and creates a new shared
- * queue if the one at index if empty or contended.
- *
- * @param task the task. Caller must ensure non-null.
- */
- final void externalPush(ForkJoinTask<?> task) {
- int r; // initialize caller's probe
- if ((r = ThreadLocalRandom.getProbe()) == 0) {
- ThreadLocalRandom.localInit();
- r = ThreadLocalRandom.getProbe();
- }
- for (;;) {
- WorkQueue q; int wl, k, stat;
- int rs = runState;
- WorkQueue[] ws = workQueues;
- if (rs <= 0 || ws == null || (wl = ws.length) <= 0)
- tryInitialize(true);
- else if ((q = ws[k = (wl - 1) & r & SQMASK]) == null)
- tryCreateExternalQueue(k);
- else if ((stat = q.sharedPush(task)) < 0)
- break;
- else if (stat == 0) {
- signalWork();
- break;
- }
- else // move if busy
- r = ThreadLocalRandom.advanceProbe(r);
- }
- }
-
- /**
- * Pushes a possibly-external submission.
- */
- private <T> ForkJoinTask<T> externalSubmit(ForkJoinTask<T> task) {
- Thread t; ForkJoinWorkerThread w; WorkQueue q;
- if (task == null)
- throw new NullPointerException();
- if (((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) &&
- (w = (ForkJoinWorkerThread)t).pool == this &&
- (q = w.workQueue) != null)
- q.push(task);
- else
- externalPush(task);
- return task;
- }
-
- /**
- * Returns common pool queue for an external thread.
- */
- static WorkQueue commonSubmitterQueue() {
- ForkJoinPool p = common;
- int r = ThreadLocalRandom.getProbe();
- WorkQueue[] ws; int wl;
- return (p != null && (ws = p.workQueues) != null &&
- (wl = ws.length) > 0) ?
- ws[(wl - 1) & r & SQMASK] : null;
- }
-
- /**
- * Performs tryUnpush for an external submitter.
- */
- final boolean tryExternalUnpush(ForkJoinTask<?> task) {
- int r = ThreadLocalRandom.getProbe();
- WorkQueue[] ws; WorkQueue w; int wl;
- return ((ws = workQueues) != null &&
- (wl = ws.length) > 0 &&
- (w = ws[(wl - 1) & r & SQMASK]) != null &&
- w.trySharedUnpush(task));
- }
-
- /**
- * Performs helpComplete for an external submitter.
- */
- final int externalHelpComplete(CountedCompleter<?> task, int maxTasks) {
- WorkQueue[] ws; int wl;
- int r = ThreadLocalRandom.getProbe();
- return ((ws = workQueues) != null && (wl = ws.length) > 0) ?
- helpComplete(ws[(wl - 1) & r & SQMASK], task, maxTasks) : 0;
+ return true;
}
// Exported methods
@@ -2604,9 +2141,10 @@
/**
* Creates a {@code ForkJoinPool} with parallelism equal to {@link
- * java.lang.Runtime#availableProcessors}, using the {@linkplain
- * #defaultForkJoinWorkerThreadFactory default thread factory},
- * no UncaughtExceptionHandler, and non-async LIFO processing mode.
+ * java.lang.Runtime#availableProcessors}, using defaults for all
+ * other parameters (see {@link #ForkJoinPool(int,
+ * ForkJoinWorkerThreadFactory, UncaughtExceptionHandler, boolean,
+ * int, int, int, Predicate, long, TimeUnit)}).
*
* @throws SecurityException if a security manager exists and
* the caller is not permitted to modify threads
@@ -2615,14 +2153,16 @@
*/
public ForkJoinPool() {
this(Math.min(MAX_CAP, Runtime.getRuntime().availableProcessors()),
- defaultForkJoinWorkerThreadFactory, null, false);
+ defaultForkJoinWorkerThreadFactory, null, false,
+ 0, MAX_CAP, 1, null, DEFAULT_KEEPALIVE, TimeUnit.MILLISECONDS);
}
/**
* Creates a {@code ForkJoinPool} with the indicated parallelism
- * level, the {@linkplain
- * #defaultForkJoinWorkerThreadFactory default thread factory},
- * no UncaughtExceptionHandler, and non-async LIFO processing mode.
+ * level, using defaults for all other parameters (see {@link
+ * #ForkJoinPool(int, ForkJoinWorkerThreadFactory,
+ * UncaughtExceptionHandler, boolean, int, int, int, Predicate,
+ * long, TimeUnit)}).
*
* @param parallelism the parallelism level
* @throws IllegalArgumentException if parallelism less than or
@@ -2633,11 +2173,15 @@
* java.lang.RuntimePermission}{@code ("modifyThread")}
*/
public ForkJoinPool(int parallelism) {
- this(parallelism, defaultForkJoinWorkerThreadFactory, null, false);
+ this(parallelism, defaultForkJoinWorkerThreadFactory, null, false,
+ 0, MAX_CAP, 1, null, DEFAULT_KEEPALIVE, TimeUnit.MILLISECONDS);
}
/**
- * Creates a {@code ForkJoinPool} with the given parameters.
+ * Creates a {@code ForkJoinPool} with the given parameters (using
+ * defaults for others -- see {@link #ForkJoinPool(int,
+ * ForkJoinWorkerThreadFactory, UncaughtExceptionHandler, boolean,
+ * int, int, int, Predicate, long, TimeUnit)}).
*
* @param parallelism the parallelism level. For default value,
* use {@link java.lang.Runtime#availableProcessors}.
@@ -2664,43 +2208,185 @@
ForkJoinWorkerThreadFactory factory,
UncaughtExceptionHandler handler,
boolean asyncMode) {
- this(checkParallelism(parallelism),
- checkFactory(factory),
- handler,
- asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
- "ForkJoinPool-" + nextPoolId() + "-worker-");
+ this(parallelism, factory, handler, asyncMode,
+ 0, MAX_CAP, 1, null, DEFAULT_KEEPALIVE, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * Creates a {@code ForkJoinPool} with the given parameters.
+ *
+ * @param parallelism the parallelism level. For default value,
+ * use {@link java.lang.Runtime#availableProcessors}.
+ *
+ * @param factory the factory for creating new threads. For
+ * default value, use {@link #defaultForkJoinWorkerThreadFactory}.
+ *
+ * @param handler the handler for internal worker threads that
+ * terminate due to unrecoverable errors encountered while
+ * executing tasks. For default value, use {@code null}.
+ *
+ * @param asyncMode if true, establishes local first-in-first-out
+ * scheduling mode for forked tasks that are never joined. This
+ * mode may be more appropriate than default locally stack-based
+ * mode in applications in which worker threads only process
+ * event-style asynchronous tasks. For default value, use {@code
+ * false}.
+ *
+ * @param corePoolSize the number of threads to keep in the pool
+ * (unless timed out after an elapsed keep-alive). Normally (and
+ * by default) this is the same value as the parallelism level,
+ * but may be set to a larger value to reduce dynamic overhead if
+ * tasks regularly block. Using a smaller value (for example
+ * {@code 0}) has the same effect as the default.
+ *
+ * @param maximumPoolSize the maximum number of threads allowed.
+ * When the maximum is reached, attempts to replace blocked
+ * threads fail. (However, because creation and termination of
+ * different threads may overlap, and may be managed by the given
+ * thread factory, this value may be transiently exceeded.) To
+ * arrange the same value as is used by default for the common
+ * pool, use {@code 256} plus the {@code parallelism} level. (By
+ * default, the common pool allows a maximum of 256 spare
+ * threads.) Using a value (for example {@code
+ * Integer.MAX_VALUE}) larger than the implementation's total
+ * thread limit has the same effect as using this limit (which is
+ * the default).
+ *
+ * @param minimumRunnable the minimum allowed number of core
+ * threads not blocked by a join or {@link ManagedBlocker}. To
+ * ensure progress, when too few unblocked threads exist and
+ * unexecuted tasks may exist, new threads are constructed, up to
+ * the given maximumPoolSize. For the default value, use {@code
+ * 1}, that ensures liveness. A larger value might improve
+ * throughput in the presence of blocked activities, but might
+ * not, due to increased overhead. A value of zero may be
+ * acceptable when submitted tasks cannot have dependencies
+ * requiring additional threads.
+ *
+ * @param saturate if non-null, a predicate invoked upon attempts
+ * to create more than the maximum total allowed threads. By
+ * default, when a thread is about to block on a join or {@link
+ * ManagedBlocker}, but cannot be replaced because the
+ * maximumPoolSize would be exceeded, a {@link
+ * RejectedExecutionException} is thrown. But if this predicate
+ * returns {@code true}, then no exception is thrown, so the pool
+ * continues to operate with fewer than the target number of
+ * runnable threads, which might not ensure progress.
+ *
+ * @param keepAliveTime the elapsed time since last use before
+ * a thread is terminated (and then later replaced if needed).
+ * For the default value, use {@code 60, TimeUnit.SECONDS}.
+ *
+ * @param unit the time unit for the {@code keepAliveTime} argument
+ *
+ * @throws IllegalArgumentException if parallelism is less than or
+ * equal to zero, or is greater than implementation limit,
+ * or if maximumPoolSize is less than parallelism,
+ * of if the keepAliveTime is less than or equal to zero.
+ * @throws NullPointerException if the factory is null
+ * @throws SecurityException if a security manager exists and
+ * the caller is not permitted to modify threads
+ * because it does not hold {@link
+ * java.lang.RuntimePermission}{@code ("modifyThread")}
+ * @since 9
+ */
+ public ForkJoinPool(int parallelism,
+ ForkJoinWorkerThreadFactory factory,
+ UncaughtExceptionHandler handler,
+ boolean asyncMode,
+ int corePoolSize,
+ int maximumPoolSize,
+ int minimumRunnable,
+ Predicate<? super ForkJoinPool> saturate,
+ long keepAliveTime,
+ TimeUnit unit) {
+ // check, encode, pack parameters
+ if (parallelism <= 0 || parallelism > MAX_CAP ||
+ maximumPoolSize < parallelism || keepAliveTime <= 0L)
+ throw new IllegalArgumentException();
+ if (factory == null)
+ throw new NullPointerException();
+ long ms = Math.max(unit.toMillis(keepAliveTime), TIMEOUT_SLOP);
+
+ String prefix = "ForkJoinPool-" + nextPoolId() + "-worker-";
+ int corep = Math.min(Math.max(corePoolSize, parallelism), MAX_CAP);
+ long c = ((((long)(-corep) << TC_SHIFT) & TC_MASK) |
+ (((long)(-parallelism) << RC_SHIFT) & RC_MASK));
+ int m = parallelism | (asyncMode ? FIFO : 0);
+ int maxSpares = Math.min(maximumPoolSize, MAX_CAP) - parallelism;
+ int minAvail = Math.min(Math.max(minimumRunnable, 0), MAX_CAP);
+ int b = ((minAvail - parallelism) & SMASK) | (maxSpares << SWIDTH);
+ int n = (parallelism > 1) ? parallelism - 1 : 1; // at least 2 slots
+ n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16;
+ n = (n + 1) << 1; // power of two, including space for submission queues
+
+ this.workQueues = new WorkQueue[n];
+ this.workerNamePrefix = prefix;
+ this.factory = factory;
+ this.ueh = handler;
+ this.saturate = saturate;
+ this.keepAlive = ms;
+ this.bounds = b;
+ this.mode = m;
+ this.ctl = c;
checkPermission();
}
- private static int checkParallelism(int parallelism) {
- if (parallelism <= 0 || parallelism > MAX_CAP)
- throw new IllegalArgumentException();
- return parallelism;
- }
-
- private static ForkJoinWorkerThreadFactory checkFactory
- (ForkJoinWorkerThreadFactory factory) {
- if (factory == null)
- throw new NullPointerException();
- return factory;
- }
+ /**
+ * Constructor for common pool using parameters possibly
+ * overridden by system properties
+ */
+ @SuppressWarnings("deprecation") // Class.newInstance
+ private ForkJoinPool(byte forCommonPoolOnly) {
+ int parallelism = -1;
+ ForkJoinWorkerThreadFactory fac = null;
+ UncaughtExceptionHandler handler = null;
+ try { // ignore exceptions in accessing/parsing properties
+ String pp = System.getProperty
+ ("java.util.concurrent.ForkJoinPool.common.parallelism");
+ String fp = System.getProperty
+ ("java.util.concurrent.ForkJoinPool.common.threadFactory");
+ String hp = System.getProperty
+ ("java.util.concurrent.ForkJoinPool.common.exceptionHandler");
+ if (pp != null)
+ parallelism = Integer.parseInt(pp);
+ if (fp != null)
+ fac = ((ForkJoinWorkerThreadFactory)ClassLoader.
+ getSystemClassLoader().loadClass(fp).newInstance());
+ if (hp != null)
+ handler = ((UncaughtExceptionHandler)ClassLoader.
+ getSystemClassLoader().loadClass(hp).newInstance());
+ } catch (Exception ignore) {
+ }
- /**
- * Creates a {@code ForkJoinPool} with the given parameters, without
- * any security checks or parameter validation. Invoked directly by
- * makeCommonPool.
- */
- private ForkJoinPool(int parallelism,
- ForkJoinWorkerThreadFactory factory,
- UncaughtExceptionHandler handler,
- int mode,
- String workerNamePrefix) {
- this.workerNamePrefix = workerNamePrefix;
- this.factory = factory;
+ if (fac == null) {
+ if (System.getSecurityManager() == null)
+ fac = defaultForkJoinWorkerThreadFactory;
+ else // use security-managed default
+ fac = new InnocuousForkJoinWorkerThreadFactory();
+ }
+ if (parallelism < 0 && // default 1 less than #cores
+ (parallelism = Runtime.getRuntime().availableProcessors() - 1) <= 0)
+ parallelism = 1;
+ if (parallelism > MAX_CAP)
+ parallelism = MAX_CAP;
+
+ long c = ((((long)(-parallelism) << TC_SHIFT) & TC_MASK) |
+ (((long)(-parallelism) << RC_SHIFT) & RC_MASK));
+ int b = ((1 - parallelism) & SMASK) | (COMMON_MAX_SPARES << SWIDTH);
+ int n = (parallelism > 1) ? parallelism - 1 : 1;
+ n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16;
+ n = (n + 1) << 1;
+
+ this.workQueues = new WorkQueue[n];
+ this.workerNamePrefix = "ForkJoinPool.commonPool-worker-";
+ this.factory = fac;
this.ueh = handler;
- this.config = (parallelism & SMASK) | mode;
- long np = (long)(-parallelism); // offset ctl counts
- this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
+ this.saturate = null;
+ this.keepAlive = DEFAULT_KEEPALIVE;
+ this.bounds = b;
+ this.mode = parallelism;
+ this.ctl = c;
}
/**
@@ -2876,8 +2562,8 @@
* @return the targeted parallelism level of this pool
*/
public int getParallelism() {
- int par;
- return ((par = config & SMASK) > 0) ? par : 1;
+ int par = mode & SMASK;
+ return (par > 0) ? par : 1;
}
/**
@@ -2899,7 +2585,7 @@
* @return the number of worker threads
*/
public int getPoolSize() {
- return (config & SMASK) + (short)(ctl >>> TC_SHIFT);
+ return ((mode & SMASK) + (short)(ctl >>> TC_SHIFT));
}
/**
@@ -2909,7 +2595,7 @@
* @return {@code true} if this pool uses async mode
*/
public boolean getAsyncMode() {
- return (config & FIFO_QUEUE) != 0;
+ return (mode & FIFO) != 0;
}
/**
@@ -2940,7 +2626,7 @@
* @return the number of active threads
*/
public int getActiveThreadCount() {
- int r = (config & SMASK) + (int)(ctl >> AC_SHIFT);
+ int r = (mode & SMASK) + (int)(ctl >> RC_SHIFT);
return (r <= 0) ? 0 : r; // suppress momentarily negative values
}
@@ -2956,7 +2642,30 @@
* @return {@code true} if all threads are currently idle
*/
public boolean isQuiescent() {
- return (config & SMASK) + (int)(ctl >> AC_SHIFT) <= 0;
+ for (;;) {
+ long c = ctl;
+ int md = mode, pc = md & SMASK;
+ int tc = pc + (short)(c >>> TC_SHIFT);
+ int rc = pc + (int)(c >> RC_SHIFT);
+ if ((md & (STOP | TERMINATED)) != 0)
+ return true;
+ else if (rc > 0)
+ return false;
+ else {
+ WorkQueue[] ws; WorkQueue v;
+ if ((ws = workQueues) != null) {
+ for (int i = 1; i < ws.length; i += 2) {
+ if ((v = ws[i]) != null) {
+ if ((v.source & QUIET) == 0)
+ return false;
+ --tc;
+ }
+ }
+ }
+ if (tc == 0 && ctl == c)
+ return true;
+ }
+ }
}
/**
@@ -2971,13 +2680,12 @@
* @return the number of steals
*/
public long getStealCount() {
- AuxState sc = auxState;
- long count = (sc == null) ? 0L : sc.stealCount;
+ long count = stealCount;
WorkQueue[] ws; WorkQueue w;
if ((ws = workQueues) != null) {
for (int i = 1; i < ws.length; i += 2) {
if ((w = ws[i]) != null)
- count += w.nsteals;
+ count += (long)w.nsteals & 0xffffffffL;
}
}
return count;
@@ -3049,15 +2757,7 @@
* @return the next submission, or {@code null} if none
*/
protected ForkJoinTask<?> pollSubmission() {
- WorkQueue[] ws; int wl; WorkQueue w; ForkJoinTask<?> t;
- int r = ThreadLocalRandom.nextSecondarySeed();
- if ((ws = workQueues) != null && (wl = ws.length) > 0) {
- for (int m = wl - 1, i = 0; i < wl; ++i) {
- if ((w = ws[(i << 1) & m]) != null && (t = w.poll()) != null)
- return t;
- }
- }
- return null;
+ return pollScan(true);
}
/**
@@ -3103,9 +2803,7 @@
public String toString() {
// Use a single pass through workQueues to collect counts
long qt = 0L, qs = 0L; int rc = 0;
- AuxState sc = auxState;
- long st = (sc == null) ? 0L : sc.stealCount;
- long c = ctl;
+ long st = stealCount;
WorkQueue[] ws; WorkQueue w;
if ((ws = workQueues) != null) {
for (int i = 0; i < ws.length; ++i) {
@@ -3115,22 +2813,24 @@
qs += size;
else {
qt += size;
- st += w.nsteals;
+ st += (long)w.nsteals & 0xffffffffL;
if (w.isApparentlyUnblocked())
++rc;
}
}
}
}
- int pc = (config & SMASK);
+
+ int md = mode;
+ int pc = (md & SMASK);
+ long c = ctl;
int tc = pc + (short)(c >>> TC_SHIFT);
- int ac = pc + (int)(c >> AC_SHIFT);
+ int ac = pc + (int)(c >> RC_SHIFT);
if (ac < 0) // ignore transient negative
ac = 0;
- int rs = runState;
- String level = ((rs & TERMINATED) != 0 ? "Terminated" :
- (rs & STOP) != 0 ? "Terminating" :
- (rs & SHUTDOWN) != 0 ? "Shutting down" :
+ String level = ((md & TERMINATED) != 0 ? "Terminated" :
+ (md & STOP) != 0 ? "Terminating" :
+ (md & SHUTDOWN) != 0 ? "Shutting down" :
"Running");
return super.toString() +
"[" + level +
@@ -3193,7 +2893,7 @@
* @return {@code true} if all tasks have completed following shut down
*/
public boolean isTerminated() {
- return (runState & TERMINATED) != 0;
+ return (mode & TERMINATED) != 0;
}
/**
@@ -3210,8 +2910,8 @@
* @return {@code true} if terminating but not yet terminated
*/
public boolean isTerminating() {
- int rs = runState;
- return (rs & STOP) != 0 && (rs & TERMINATED) == 0;
+ int md = mode;
+ return (md & STOP) != 0 && (md & TERMINATED) == 0;
}
/**
@@ -3220,7 +2920,7 @@
* @return {@code true} if this pool has been shut down
*/
public boolean isShutdown() {
- return (runState & SHUTDOWN) != 0;
+ return (mode & SHUTDOWN) != 0;
}
/**
@@ -3284,30 +2984,19 @@
helpQuiescePool(wt.workQueue);
return true;
}
- long startTime = System.nanoTime();
- WorkQueue[] ws;
- int r = 0, wl;
- boolean found = true;
- while (!isQuiescent() && (ws = workQueues) != null &&
- (wl = ws.length) > 0) {
- if (!found) {
- if ((System.nanoTime() - startTime) > nanos)
+ else {
+ for (long startTime = System.nanoTime();;) {
+ ForkJoinTask<?> t;
+ if ((t = pollScan(false)) != null)
+ t.doExec();
+ else if (isQuiescent())
+ return true;
+ else if ((System.nanoTime() - startTime) > nanos)
return false;
- Thread.yield(); // cannot block
- }
- found = false;
- for (int m = wl - 1, j = (m + 1) << 2; j >= 0; --j) {
- ForkJoinTask<?> t; WorkQueue q; int b, k;
- if ((k = r++ & m) <= m && k >= 0 && (q = ws[k]) != null &&
- (b = q.base) - q.top < 0) {
- found = true;
- if ((t = q.pollAt(b)) != null)
- t.doExec();
- break;
- }
+ else
+ Thread.yield(); // cannot block
}
}
- return true;
}
/**
@@ -3422,17 +3111,19 @@
throws InterruptedException {
ForkJoinPool p;
ForkJoinWorkerThread wt;
+ WorkQueue w;
Thread t = Thread.currentThread();
if ((t instanceof ForkJoinWorkerThread) &&
- (p = (wt = (ForkJoinWorkerThread)t).pool) != null) {
- WorkQueue w = wt.workQueue;
+ (p = (wt = (ForkJoinWorkerThread)t).pool) != null &&
+ (w = wt.workQueue) != null) {
+ int block;
while (!blocker.isReleasable()) {
- if (p.tryCompensate(w)) {
+ if ((block = p.tryCompensate(w)) != 0) {
try {
do {} while (!blocker.isReleasable() &&
!blocker.block());
} finally {
- U.getAndAddLong(p, CTL, AC_UNIT);
+ CTL.getAndAdd(p, (block > 0) ? RC_UNIT : 0L);
}
break;
}
@@ -3444,6 +3135,55 @@
}
}
+ /**
+ * If the given executor is a ForkJoinPool, poll and execute
+ * AsynchronousCompletionTasks from worker's queue until none are
+ * available or blocker is released.
+ */
+ static void helpAsyncBlocker(Executor e, ManagedBlocker blocker) {
+ if (blocker != null && (e instanceof ForkJoinPool)) {
+ WorkQueue w; ForkJoinWorkerThread wt; WorkQueue[] ws; int r, n;
+ ForkJoinPool p = (ForkJoinPool)e;
+ Thread thread = Thread.currentThread();
+ if (thread instanceof ForkJoinWorkerThread &&
+ (wt = (ForkJoinWorkerThread)thread).pool == p)
+ w = wt.workQueue;
+ else if ((r = ThreadLocalRandom.getProbe()) != 0 &&
+ (ws = p.workQueues) != null && (n = ws.length) > 0)
+ w = ws[(n - 1) & r & SQMASK];
+ else
+ w = null;
+ if (w != null) {
+ for (;;) {
+ int b = w.base, s = w.top, d, al; ForkJoinTask<?>[] a;
+ if ((a = w.array) != null && (d = b - s) < 0 &&
+ (al = a.length) > 0) {
+ int index = (al - 1) & b;
+ ForkJoinTask<?> t = (ForkJoinTask<?>)
+ QA.getAcquire(a, index);
+ if (blocker.isReleasable())
+ break;
+ else if (b++ == w.base) {
+ if (t == null) {
+ if (d == -1)
+ break;
+ }
+ else if (!(t instanceof CompletableFuture.
+ AsynchronousCompletionTask))
+ break;
+ else if (QA.compareAndSet(a, index, t, null)) {
+ w.base = b;
+ t.doExec();
+ }
+ }
+ }
+ else
+ break;
+ }
+ }
+ }
+ }
+
// AbstractExecutorService overrides. These rely on undocumented
// fact that ForkJoinTask.adapt returns ForkJoinTasks that also
// implement RunnableFuture.
@@ -3456,24 +3196,17 @@
return new ForkJoinTask.AdaptedCallable<T>(callable);
}
- // Unsafe mechanics
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long CTL;
- private static final long RUNSTATE;
- private static final int ABASE;
- private static final int ASHIFT;
+ // VarHandle mechanics
+ private static final VarHandle CTL;
+ private static final VarHandle MODE;
+ private static final VarHandle QA;
static {
try {
- CTL = U.objectFieldOffset
- (ForkJoinPool.class.getDeclaredField("ctl"));
- RUNSTATE = U.objectFieldOffset
- (ForkJoinPool.class.getDeclaredField("runState"));
- ABASE = U.arrayBaseOffset(ForkJoinTask[].class);
- int scale = U.arrayIndexScale(ForkJoinTask[].class);
- if ((scale & (scale - 1)) != 0)
- throw new Error("array index scale not a power of two");
- ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ CTL = l.findVarHandle(ForkJoinPool.class, "ctl", long.class);
+ MODE = l.findVarHandle(ForkJoinPool.class, "mode", int.class);
+ QA = MethodHandles.arrayElementVarHandle(ForkJoinTask[].class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
@@ -3497,51 +3230,10 @@
common = java.security.AccessController.doPrivileged
(new java.security.PrivilegedAction<ForkJoinPool>() {
- public ForkJoinPool run() { return makeCommonPool(); }});
-
- // report 1 even if threads disabled
- COMMON_PARALLELISM = Math.max(common.config & SMASK, 1);
- }
+ public ForkJoinPool run() {
+ return new ForkJoinPool((byte)0); }});
- /**
- * Creates and returns the common pool, respecting user settings
- * specified via system properties.
- */
- @SuppressWarnings("deprecation") // Class.newInstance
- static ForkJoinPool makeCommonPool() {
- int parallelism = -1;
- ForkJoinWorkerThreadFactory factory = null;
- UncaughtExceptionHandler handler = null;
- try { // ignore exceptions in accessing/parsing properties
- String pp = System.getProperty
- ("java.util.concurrent.ForkJoinPool.common.parallelism");
- String fp = System.getProperty
- ("java.util.concurrent.ForkJoinPool.common.threadFactory");
- String hp = System.getProperty
- ("java.util.concurrent.ForkJoinPool.common.exceptionHandler");
- if (pp != null)
- parallelism = Integer.parseInt(pp);
- if (fp != null)
- factory = ((ForkJoinWorkerThreadFactory)ClassLoader.
- getSystemClassLoader().loadClass(fp).newInstance());
- if (hp != null)
- handler = ((UncaughtExceptionHandler)ClassLoader.
- getSystemClassLoader().loadClass(hp).newInstance());
- } catch (Exception ignore) {
- }
- if (factory == null) {
- if (System.getSecurityManager() == null)
- factory = defaultForkJoinWorkerThreadFactory;
- else // use security-managed default
- factory = new InnocuousForkJoinWorkerThreadFactory();
- }
- if (parallelism < 0 && // default 1 less than #cores
- (parallelism = Runtime.getRuntime().availableProcessors() - 1) <= 0)
- parallelism = 1;
- if (parallelism > MAX_CAP)
- parallelism = MAX_CAP;
- return new ForkJoinPool(parallelism, factory, handler, LIFO_QUEUE,
- "ForkJoinPool.commonPool-worker-");
+ COMMON_PARALLELISM = Math.max(common.mode & SMASK, 1);
}
/**
--- a/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinTask.java Thu Jul 21 20:09:19 2016 -0700
@@ -36,6 +36,8 @@
package java.util.concurrent;
import java.io.Serializable;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.lang.reflect.Constructor;
@@ -92,7 +94,7 @@
* encountering the exception; minimally only the latter.
*
* <p>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 <em>other</em> tasks should be dependent on a task
* that blocks on external synchronization or I/O. Event-style async
* tasks that are never joined (for example, those subclassing {@link
@@ -259,7 +261,7 @@
for (int s;;) {
if ((s = status) < 0)
return s;
- if (U.compareAndSwapInt(this, STATUS, s, s | completion)) {
+ if (STATUS.compareAndSet(this, s, s | completion)) {
if ((s >>> 16) != 0)
synchronized (this) { notifyAll(); }
return completion;
@@ -297,7 +299,7 @@
final void internalWait(long timeout) {
int s;
if ((s = status) >= 0 && // force completer to issue notify
- U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
+ STATUS.compareAndSet(this, s, s | SIGNAL)) {
synchronized (this) {
if (status >= 0)
try { wait(timeout); } catch (InterruptedException ie) { }
@@ -319,7 +321,7 @@
if (s >= 0 && (s = status) >= 0) {
boolean interrupted = false;
do {
- if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
+ if (STATUS.compareAndSet(this, s, s | SIGNAL)) {
synchronized (this) {
if (status >= 0) {
try {
@@ -353,7 +355,7 @@
ForkJoinPool.common.tryExternalUnpush(this) ? doExec() :
0)) >= 0) {
while ((s = status) >= 0) {
- if (U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
+ if (STATUS.compareAndSet(this, s, s | SIGNAL)) {
synchronized (this) {
if (status >= 0)
wait(0L);
@@ -400,22 +402,24 @@
// Exception table support
/**
- * Table of exceptions thrown by tasks, to enable reporting by
- * callers. Because exceptions are rare, we don't directly keep
+ * Hash table of exceptions thrown by tasks, to enable reporting
+ * by callers. Because exceptions are rare, we don't directly keep
* them with task objects, but instead use a weak ref table. Note
* that cancellation exceptions don't appear in the table, but are
* instead recorded as status values.
*
- * Note: These statics are initialized below in static block.
+ * The exception table has a fixed capacity.
*/
- private static final ExceptionNode[] exceptionTable;
- private static final ReentrantLock exceptionTableLock;
- private static final ReferenceQueue<Object> 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<ForkJoinTask<?>> exceptionTableRefQueue
+ = new ReferenceQueue<ForkJoinTask<?>>();
/**
* Key-value nodes for exception table. The chained hash table
@@ -435,7 +439,7 @@
final long thrower; // use id not ref to avoid weak cycles
final int hashCode; // store task hashCode before weak ref disappears
ExceptionNode(ForkJoinTask<?> task, Throwable ex, ExceptionNode next,
- ReferenceQueue<Object> exceptionTableRefQueue) {
+ ReferenceQueue<ForkJoinTask<?>> exceptionTableRefQueue) {
super(task, exceptionTableRefQueue);
this.ex = ex;
this.next = next;
@@ -599,9 +603,8 @@
private static void expungeStaleExceptions() {
for (Object x; (x = exceptionTableRefQueue.poll()) != null;) {
if (x instanceof ExceptionNode) {
- int hashCode = ((ExceptionNode)x).hashCode;
ExceptionNode[] t = exceptionTable;
- int i = hashCode & (t.length - 1);
+ int i = ((ExceptionNode)x).hashCode & (t.length - 1);
ExceptionNode e = t[i];
ExceptionNode pred = null;
while (e != null) {
@@ -1031,7 +1034,7 @@
while ((s = status) >= 0 &&
(ns = deadline - System.nanoTime()) > 0L) {
if ((ms = TimeUnit.NANOSECONDS.toMillis(ns)) > 0L &&
- U.compareAndSwapInt(this, STATUS, s, s | SIGNAL)) {
+ STATUS.compareAndSet(this, s, s | SIGNAL)) {
synchronized (this) {
if (status >= 0)
wait(ms); // OK to throw InterruptedException
@@ -1324,8 +1327,8 @@
*/
public final short setForkJoinTaskTag(short newValue) {
for (int s;;) {
- if (U.compareAndSwapInt(this, STATUS, s = status,
- (s & ~SMASK) | (newValue & SMASK)))
+ if (STATUS.compareAndSet(this, s = status,
+ (s & ~SMASK) | (newValue & SMASK)))
return (short)s;
}
}
@@ -1348,8 +1351,8 @@
for (int s;;) {
if ((short)(s = status) != expect)
return false;
- if (U.compareAndSwapInt(this, STATUS, s,
- (s & ~SMASK) | (update & SMASK)))
+ if (STATUS.compareAndSet(this, s,
+ (s & ~SMASK) | (update & SMASK)))
return true;
}
}
@@ -1510,17 +1513,12 @@
setExceptionalCompletion((Throwable)ex);
}
- // Unsafe mechanics
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long STATUS;
-
+ // VarHandle mechanics
+ private static final VarHandle STATUS;
static {
- exceptionTableLock = new ReentrantLock();
- exceptionTableRefQueue = new ReferenceQueue<Object>();
- 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);
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/ForkJoinWorkerThread.java Thu Jul 21 20:09:19 2016 -0700
@@ -66,8 +66,9 @@
* owning thread.
*
* Support for (non-public) subclass InnocuousForkJoinWorkerThread
- * requires that we break quite a lot of encapsulation (via Unsafe)
- * both here and in the subclass to access and set Thread fields.
+ * requires that we break quite a lot of encapsulation (via helper
+ * methods in ThreadLocalRandom) both here and in the subclass to
+ * access and set Thread fields.
*/
final ForkJoinPool pool; // the pool this thread works in
@@ -92,8 +93,8 @@
ForkJoinWorkerThread(ForkJoinPool pool, ThreadGroup threadGroup,
AccessControlContext acc) {
super(threadGroup, null, "aForkJoinWorkerThread");
- U.putObjectRelease(this, INHERITEDACCESSCONTROLCONTEXT, acc);
- eraseThreadLocals(); // clear before registering
+ ThreadLocalRandom.setInheritedAccessControlContext(this, acc);
+ ThreadLocalRandom.eraseThreadLocals(this); // clear before registering
this.pool = pool;
this.workQueue = pool.registerWorker(this);
}
@@ -171,37 +172,11 @@
}
/**
- * Erases ThreadLocals by nulling out Thread maps.
- */
- final void eraseThreadLocals() {
- U.putObject(this, THREADLOCALS, null);
- U.putObject(this, INHERITABLETHREADLOCALS, null);
- }
-
- /**
* Non-public hook method for InnocuousForkJoinWorkerThread.
*/
void afterTopLevelExec() {
}
- // Set up to allow setting thread fields in constructor
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long THREADLOCALS;
- private static final long INHERITABLETHREADLOCALS;
- private static final long INHERITEDACCESSCONTROLCONTEXT;
- static {
- try {
- THREADLOCALS = U.objectFieldOffset
- (Thread.class.getDeclaredField("threadLocals"));
- INHERITABLETHREADLOCALS = U.objectFieldOffset
- (Thread.class.getDeclaredField("inheritableThreadLocals"));
- INHERITEDACCESSCONTROLCONTEXT = U.objectFieldOffset
- (Thread.class.getDeclaredField("inheritedAccessControlContext"));
- } catch (ReflectiveOperationException e) {
- throw new Error(e);
- }
- }
-
/**
* A worker thread that has no permissions, is not a member of any
* user-defined ThreadGroup, and erases all ThreadLocals after
@@ -210,7 +185,7 @@
static final class InnocuousForkJoinWorkerThread extends ForkJoinWorkerThread {
/** The ThreadGroup for all InnocuousForkJoinWorkerThreads */
private static final ThreadGroup innocuousThreadGroup =
- createThreadGroup();
+ ThreadLocalRandom.createThreadGroup("InnocuousForkJoinWorkerThreadGroup");
/** An AccessControlContext supporting no privileges */
private static final AccessControlContext INNOCUOUS_ACC =
@@ -225,7 +200,7 @@
@Override // to erase ThreadLocals
void afterTopLevelExec() {
- eraseThreadLocals();
+ ThreadLocalRandom.eraseThreadLocals(this);
}
@Override // to always report system loader
@@ -241,33 +216,5 @@
throw new SecurityException("setContextClassLoader");
}
- /**
- * Returns a new group with the system ThreadGroup (the
- * topmost, parent-less group) as parent. Uses Unsafe to
- * traverse Thread.group and ThreadGroup.parent fields.
- */
- private static ThreadGroup createThreadGroup() {
- try {
- jdk.internal.misc.Unsafe u = jdk.internal.misc.Unsafe.getUnsafe();
- long tg = u.objectFieldOffset
- (Thread.class.getDeclaredField("group"));
- long gp = u.objectFieldOffset
- (ThreadGroup.class.getDeclaredField("parent"));
- ThreadGroup group = (ThreadGroup)
- u.getObject(Thread.currentThread(), tg);
- while (group != null) {
- ThreadGroup parent = (ThreadGroup)u.getObject(group, gp);
- if (parent == null)
- return new ThreadGroup(group,
- "InnocuousForkJoinWorkerThreadGroup");
- group = parent;
- }
- } catch (ReflectiveOperationException e) {
- throw new Error(e);
- }
- // fall through if null as cannot-happen safeguard
- throw new Error("Cannot create ThreadGroup");
- }
}
-
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/FutureTask.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/FutureTask.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,6 +35,8 @@
package java.util.concurrent;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.util.concurrent.locks.LockSupport;
/**
@@ -69,9 +71,6 @@
* cancellation races. Sync control in the current design relies
* on a "state" field updated via CAS to track completion, along
* with a simple Treiber stack to hold waiting threads.
- *
- * Style note: As usual, we bypass overhead of using
- * AtomicXFieldUpdaters and instead directly use Unsafe intrinsics.
*/
/**
@@ -163,9 +162,8 @@
}
public boolean cancel(boolean mayInterruptIfRunning) {
- if (!(state == NEW &&
- U.compareAndSwapInt(this, STATE, NEW,
- mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
+ if (!(state == NEW && STATE.compareAndSet
+ (this, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) {
@@ -174,7 +172,7 @@
if (t != null)
t.interrupt();
} finally { // final state
- U.putIntRelease(this, STATE, INTERRUPTED);
+ STATE.setRelease(this, INTERRUPTED);
}
}
} finally {
@@ -228,9 +226,9 @@
* @param v the value
*/
protected void set(V v) {
- if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
+ if (STATE.compareAndSet(this, NEW, COMPLETING)) {
outcome = v;
- U.putIntRelease(this, STATE, NORMAL); // final state
+ STATE.setRelease(this, NORMAL); // final state
finishCompletion();
}
}
@@ -246,16 +244,16 @@
* @param t the cause of failure
*/
protected void setException(Throwable t) {
- if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
+ if (STATE.compareAndSet(this, NEW, COMPLETING)) {
outcome = t;
- U.putIntRelease(this, STATE, EXCEPTIONAL); // final state
+ STATE.setRelease(this, EXCEPTIONAL); // final state
finishCompletion();
}
}
public void run() {
if (state != NEW ||
- !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
+ !RUNNER.compareAndSet(this, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
@@ -296,7 +294,7 @@
*/
protected boolean runAndReset() {
if (state != NEW ||
- !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
+ !RUNNER.compareAndSet(this, null, Thread.currentThread()))
return false;
boolean ran = false;
int s = state;
@@ -363,7 +361,7 @@
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
- if (U.compareAndSwapObject(this, WAITERS, q, null)) {
+ if (WAITERS.weakCompareAndSetVolatile(this, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
@@ -425,8 +423,7 @@
q = new WaitNode();
}
else if (!queued)
- queued = U.compareAndSwapObject(this, WAITERS,
- q.next = waiters, q);
+ queued = WAITERS.weakCompareAndSetVolatile(this, q.next = waiters, q);
else if (timed) {
final long parkNanos;
if (startTime == 0L) { // first time
@@ -475,7 +472,7 @@
if (pred.thread == null) // check for race
continue retry;
}
- else if (!U.compareAndSwapObject(this, WAITERS, q, s))
+ else if (!WAITERS.compareAndSet(this, q, s))
continue retry;
}
break;
@@ -483,19 +480,16 @@
}
}
- // Unsafe mechanics
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long STATE;
- private static final long RUNNER;
- private static final long WAITERS;
+ // VarHandle mechanics
+ private static final VarHandle STATE;
+ private static final VarHandle RUNNER;
+ private static final VarHandle WAITERS;
static {
try {
- STATE = U.objectFieldOffset
- (FutureTask.class.getDeclaredField("state"));
- RUNNER = U.objectFieldOffset
- (FutureTask.class.getDeclaredField("runner"));
- WAITERS = U.objectFieldOffset
- (FutureTask.class.getDeclaredField("waiters"));
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ STATE = l.findVarHandle(FutureTask.class, "state", int.class);
+ RUNNER = l.findVarHandle(FutureTask.class, "runner", Thread.class);
+ WAITERS = l.findVarHandle(FutureTask.class, "waiters", WaitNode.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/LinkedTransferQueue.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/LinkedTransferQueue.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,6 +35,8 @@
package java.util.concurrent;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.util.AbstractQueue;
import java.util.Arrays;
import java.util.Collection;
@@ -444,7 +446,7 @@
/**
* Queue nodes. Uses Object, not E, for items to allow forgetting
- * them after use. Relies heavily on Unsafe mechanics to minimize
+ * them after use. Relies heavily on VarHandles to minimize
* unnecessary ordering constraints: Writes that are intrinsically
* ordered wrt other accesses or CASes use simple relaxed forms.
*/
@@ -456,12 +458,12 @@
// CAS methods for fields
final boolean casNext(Node cmp, Node val) {
- return U.compareAndSwapObject(this, NEXT, cmp, val);
+ return NEXT.compareAndSet(this, cmp, val);
}
final boolean casItem(Object cmp, Object val) {
// assert cmp == null || cmp.getClass() != Node.class;
- return U.compareAndSwapObject(this, ITEM, cmp, val);
+ return ITEM.compareAndSet(this, cmp, val);
}
/**
@@ -469,7 +471,7 @@
* only be seen after publication via casNext.
*/
Node(Object item, boolean isData) {
- U.putObject(this, ITEM, item); // relaxed write
+ ITEM.set(this, item); // relaxed write
this.isData = isData;
}
@@ -478,7 +480,7 @@
* only after CASing head field, so uses relaxed write.
*/
final void forgetNext() {
- U.putObject(this, NEXT, this);
+ NEXT.set(this, this);
}
/**
@@ -491,8 +493,8 @@
* else we don't care).
*/
final void forgetContents() {
- U.putObject(this, ITEM, this);
- U.putObject(this, WAITER, null);
+ ITEM.set(this, this);
+ WAITER.set(this, null);
}
/**
@@ -537,19 +539,16 @@
private static final long serialVersionUID = -3375979862319811754L;
- // Unsafe mechanics
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long ITEM;
- private static final long NEXT;
- private static final long WAITER;
+ // VarHandle mechanics
+ private static final VarHandle ITEM;
+ private static final VarHandle NEXT;
+ private static final VarHandle WAITER;
static {
try {
- ITEM = U.objectFieldOffset
- (Node.class.getDeclaredField("item"));
- NEXT = U.objectFieldOffset
- (Node.class.getDeclaredField("next"));
- WAITER = U.objectFieldOffset
- (Node.class.getDeclaredField("waiter"));
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ ITEM = l.findVarHandle(Node.class, "item", Object.class);
+ NEXT = l.findVarHandle(Node.class, "next", Node.class);
+ WAITER = l.findVarHandle(Node.class, "waiter", Thread.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
@@ -567,15 +566,15 @@
// CAS methods for fields
private boolean casTail(Node cmp, Node val) {
- return U.compareAndSwapObject(this, TAIL, cmp, val);
+ return TAIL.compareAndSet(this, cmp, val);
}
private boolean casHead(Node cmp, Node val) {
- return U.compareAndSwapObject(this, HEAD, cmp, val);
+ return HEAD.compareAndSet(this, cmp, val);
}
private boolean casSweepVotes(int cmp, int val) {
- return U.compareAndSwapInt(this, SWEEPVOTES, cmp, val);
+ return SWEEPVOTES.compareAndSet(this, cmp, val);
}
/*
@@ -1562,20 +1561,19 @@
}
}
- // Unsafe mechanics
-
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long HEAD;
- private static final long TAIL;
- private static final long SWEEPVOTES;
+ // VarHandle mechanics
+ private static final VarHandle HEAD;
+ private static final VarHandle TAIL;
+ private static final VarHandle SWEEPVOTES;
static {
try {
- HEAD = U.objectFieldOffset
- (LinkedTransferQueue.class.getDeclaredField("head"));
- TAIL = U.objectFieldOffset
- (LinkedTransferQueue.class.getDeclaredField("tail"));
- SWEEPVOTES = U.objectFieldOffset
- (LinkedTransferQueue.class.getDeclaredField("sweepVotes"));
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ HEAD = l.findVarHandle(LinkedTransferQueue.class, "head",
+ Node.class);
+ TAIL = l.findVarHandle(LinkedTransferQueue.class, "tail",
+ Node.class);
+ SWEEPVOTES = l.findVarHandle(LinkedTransferQueue.class, "sweepVotes",
+ int.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/Phaser.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/Phaser.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,6 +35,8 @@
package java.util.concurrent;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
@@ -221,7 +223,6 @@
* phaser.arriveAndDeregister();
* }}</pre>
*
- *
* <p>To create a set of {@code n} tasks using a tree of phasers, you
* could use code of the following form, assuming a Task class with a
* constructor accepting a {@code Phaser} that it registers with upon
@@ -384,7 +385,7 @@
int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
if (unarrived <= 0)
throw new IllegalStateException(badArrive(s));
- if (U.compareAndSwapLong(this, STATE, s, s-=adjust)) {
+ if (STATE.compareAndSet(this, s, s-=adjust)) {
if (unarrived == 1) {
long n = s & PARTIES_MASK; // base of next state
int nextUnarrived = (int)n >>> PARTIES_SHIFT;
@@ -397,12 +398,12 @@
n |= nextUnarrived;
int nextPhase = (phase + 1) & MAX_PHASE;
n |= (long)nextPhase << PHASE_SHIFT;
- U.compareAndSwapLong(this, STATE, s, n);
+ STATE.compareAndSet(this, s, n);
releaseWaiters(phase);
}
else if (nextUnarrived == 0) { // propagate deregistration
phase = parent.doArrive(ONE_DEREGISTER);
- U.compareAndSwapLong(this, STATE, s, s | EMPTY);
+ STATE.compareAndSet(this, s, s | EMPTY);
}
else
phase = parent.doArrive(ONE_ARRIVAL);
@@ -437,13 +438,13 @@
if (parent == null || reconcileState() == s) {
if (unarrived == 0) // wait out advance
root.internalAwaitAdvance(phase, null);
- else if (U.compareAndSwapLong(this, STATE, s, s + adjust))
+ else if (STATE.compareAndSet(this, s, s + adjust))
break;
}
}
else if (parent == null) { // 1st root registration
long next = ((long)phase << PHASE_SHIFT) | adjust;
- if (U.compareAndSwapLong(this, STATE, s, next))
+ if (STATE.compareAndSet(this, s, next))
break;
}
else {
@@ -455,8 +456,8 @@
// finish registration whenever parent registration
// succeeded, even when racing with termination,
// since these are part of the same "transaction".
- while (!U.compareAndSwapLong
- (this, STATE, s,
+ while (!STATE.weakCompareAndSetVolatile
+ (this, s,
((long)phase << PHASE_SHIFT) | adjust)) {
s = state;
phase = (int)(root.state >>> PHASE_SHIFT);
@@ -487,8 +488,8 @@
// CAS to root phase with current parties, tripping unarrived
while ((phase = (int)(root.state >>> PHASE_SHIFT)) !=
(int)(s >>> PHASE_SHIFT) &&
- !U.compareAndSwapLong
- (this, STATE, s,
+ !STATE.weakCompareAndSetVolatile
+ (this, s,
s = (((long)phase << PHASE_SHIFT) |
((phase < 0) ? (s & COUNTS_MASK) :
(((p = (int)s >>> PARTIES_SHIFT) == 0) ? EMPTY :
@@ -677,7 +678,7 @@
int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
if (unarrived <= 0)
throw new IllegalStateException(badArrive(s));
- if (U.compareAndSwapLong(this, STATE, s, s -= ONE_ARRIVAL)) {
+ if (STATE.compareAndSet(this, s, s -= ONE_ARRIVAL)) {
if (unarrived > 1)
return root.internalAwaitAdvance(phase, null);
if (root != this)
@@ -692,7 +693,7 @@
n |= nextUnarrived;
int nextPhase = (phase + 1) & MAX_PHASE;
n |= (long)nextPhase << PHASE_SHIFT;
- if (!U.compareAndSwapLong(this, STATE, s, n))
+ if (!STATE.compareAndSet(this, s, n))
return (int)(state >>> PHASE_SHIFT); // terminated
releaseWaiters(phase);
return nextPhase;
@@ -808,7 +809,7 @@
final Phaser root = this.root;
long s;
while ((s = root.state) >= 0) {
- if (U.compareAndSwapLong(root, STATE, s, s | TERMINATION_BIT)) {
+ if (STATE.compareAndSet(root, s, s | TERMINATION_BIT)) {
// signal all threads
releaseWaiters(0); // Waiters on evenQ
releaseWaiters(1); // Waiters on oddQ
@@ -1043,6 +1044,8 @@
node = new QNode(this, phase, false, false, 0L);
node.wasInterrupted = interrupted;
}
+ else
+ Thread.onSpinWait();
}
else if (node.isReleasable()) // done or aborted
break;
@@ -1131,14 +1134,12 @@
}
}
- // Unsafe mechanics
-
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long STATE;
+ // VarHandle mechanics
+ private static final VarHandle STATE;
static {
try {
- STATE = U.objectFieldOffset
- (Phaser.class.getDeclaredField("state"));
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ STATE = l.findVarHandle(Phaser.class, "state", long.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/PriorityBlockingQueue.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,6 +35,8 @@
package java.util.concurrent;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.util.AbstractQueue;
import java.util.Arrays;
import java.util.Collection;
@@ -289,7 +291,7 @@
lock.unlock(); // must release and then re-acquire main lock
Object[] newArray = null;
if (allocationSpinLock == 0 &&
- U.compareAndSwapInt(this, ALLOCATIONSPINLOCK, 0, 1)) {
+ ALLOCATIONSPINLOCK.compareAndSet(this, 0, 1)) {
try {
int newCap = oldCap + ((oldCap < 64) ?
(oldCap + 2) : // grow faster if small
@@ -1009,13 +1011,14 @@
return new PBQSpliterator<E>(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);
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/SubmissionPublisher.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,6 +35,8 @@
package java.util.concurrent;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.LockSupport;
@@ -866,7 +868,7 @@
/** Subscriber for method consume */
private static final class ConsumerSubscriber<T>
- implements Flow.Subscriber<T> {
+ implements Flow.Subscriber<T> {
final CompletableFuture<Void> status;
final Consumer<? super T> consumer;
Flow.Subscription subscription;
@@ -906,7 +908,7 @@
*/
@SuppressWarnings("serial")
static final class ConsumerTask<T> extends ForkJoinTask<Void>
- implements Runnable {
+ implements Runnable, CompletableFuture.AsynchronousCompletionTask {
final BufferedSubscription<T> consumer;
ConsumerTask(BufferedSubscription<T> consumer) {
this.consumer = consumer;
@@ -959,11 +961,9 @@
* Blocking control relies on the "waiter" field. Producers set
* the field before trying to block, but must then recheck (via
* offer) before parking. Signalling then just unparks and clears
- * waiter field. If the producer and consumer are both in the same
- * ForkJoinPool, or consumers are running in commonPool, the
- * producer attempts to help run consumer tasks that it forked
- * before blocking. To avoid potential cycles, only one level of
- * helping is currently supported.
+ * waiter field. If the producer and/or consumer are using a
+ * ForkJoinPool, the producer attempts to help run consumer tasks
+ * via ForkJoinPool.helpAsyncBlocker before blocking.
*
* This class uses @Contended and heuristic field declaration
* ordering to reduce false-sharing-based memory contention among
@@ -983,7 +983,6 @@
volatile long demand; // # unfilled requests
int maxCapacity; // reduced on OOME
int putStat; // offer result for ManagedBlocker
- int helpDepth; // nested helping depth (at most 1)
volatile int ctl; // atomic run state flags
volatile int head; // next position to take
int tail; // next position to put
@@ -1077,7 +1076,7 @@
alloc = true;
}
else {
- U.fullFence(); // recheck
+ VarHandle.fullFence(); // recheck
int h = head, t = tail, size = t + 1 - h;
if (cap >= size) {
a[(cap - 1) & t] = item;
@@ -1116,10 +1115,10 @@
if (a != null && cap > 0) {
int mask = cap - 1;
for (int j = head; j != t; ++j) {
- long k = ((long)(j & mask) << ASHIFT) + ABASE;
- Object x = U.getObjectVolatile(a, k);
+ int k = j & mask;
+ Object x = QA.getAcquire(a, k);
if (x != null && // races with consumer
- U.compareAndSwapObject(a, k, x, null))
+ QA.compareAndSet(a, k, x, null))
newArray[j & newMask] = x;
}
}
@@ -1136,28 +1135,20 @@
* initial offer return 0.
*/
final int submit(T item) {
- int stat; Executor e; ForkJoinWorkerThread w;
- if ((stat = offer(item)) == 0 && helpDepth == 0 &&
- ((e = executor) instanceof ForkJoinPool)) {
- helpDepth = 1;
- Thread thread = Thread.currentThread();
- if ((thread instanceof ForkJoinWorkerThread) &&
- ((w = (ForkJoinWorkerThread)thread)).getPool() == e)
- stat = internalHelpConsume(w.workQueue, item);
- else if (e == ForkJoinPool.commonPool())
- stat = externalHelpConsume
- (ForkJoinPool.commonSubmitterQueue(), item);
- helpDepth = 0;
- }
- if (stat == 0 && (stat = offer(item)) == 0) {
+ int stat;
+ if ((stat = offer(item)) == 0) {
putItem = item;
timeout = 0L;
- try {
- ForkJoinPool.managedBlock(this);
- } catch (InterruptedException ie) {
- timeout = INTERRUPTED;
+ putStat = 0;
+ ForkJoinPool.helpAsyncBlocker(executor, this);
+ if ((stat = putStat) == 0) {
+ try {
+ ForkJoinPool.managedBlock(this);
+ } catch (InterruptedException ie) {
+ timeout = INTERRUPTED;
+ }
+ stat = putStat;
}
- stat = putStat;
if (timeout < 0L)
Thread.currentThread().interrupt();
}
@@ -1165,71 +1156,22 @@
}
/**
- * Tries helping for FJ submitter.
- */
- private int internalHelpConsume(ForkJoinPool.WorkQueue w, T item) {
- int stat = 0;
- if (w != null) {
- ForkJoinTask<?> t;
- while ((t = w.peek()) != null && (t instanceof ConsumerTask)) {
- if ((stat = offer(item)) != 0 || !w.tryUnpush(t))
- break;
- ((ConsumerTask<?>)t).consumer.consume();
- }
- }
- return stat;
- }
-
- /**
- * Tries helping for non-FJ submitter.
- */
- private int externalHelpConsume(ForkJoinPool.WorkQueue w, T item) {
- int stat = 0;
- if (w != null) {
- ForkJoinTask<?> t;
- while ((t = w.peek()) != null && (t instanceof ConsumerTask)) {
- if ((stat = offer(item)) != 0 || !w.trySharedUnpush(t))
- break;
- ((ConsumerTask<?>)t).consumer.consume();
- }
- }
- return stat;
- }
-
- /**
* Timeout version; similar to submit.
*/
final int timedOffer(T item, long nanos) {
- int stat; Executor e;
- if ((stat = offer(item)) == 0 && helpDepth == 0 &&
- ((e = executor) instanceof ForkJoinPool)) {
- Thread thread = Thread.currentThread();
- if (((thread instanceof ForkJoinWorkerThread) &&
- ((ForkJoinWorkerThread)thread).getPool() == e) ||
- e == ForkJoinPool.commonPool()) {
- helpDepth = 1;
- ForkJoinTask<?> t;
- long deadline = System.nanoTime() + nanos;
- while ((t = ForkJoinTask.peekNextLocalTask()) != null &&
- (t instanceof ConsumerTask)) {
- if ((stat = offer(item)) != 0 ||
- (nanos = deadline - System.nanoTime()) <= 0L ||
- !t.tryUnfork())
- break;
- ((ConsumerTask<?>)t).consumer.consume();
+ int stat;
+ if ((stat = offer(item)) == 0 && (timeout = nanos) > 0L) {
+ putItem = item;
+ putStat = 0;
+ ForkJoinPool.helpAsyncBlocker(executor, this);
+ if ((stat = putStat) == 0) {
+ try {
+ ForkJoinPool.managedBlock(this);
+ } catch (InterruptedException ie) {
+ timeout = INTERRUPTED;
}
- helpDepth = 0;
+ stat = putStat;
}
- }
- if (stat == 0 && (stat = offer(item)) == 0 &&
- (timeout = nanos) > 0L) {
- putItem = item;
- try {
- ForkJoinPool.managedBlock(this);
- } catch (InterruptedException ie) {
- timeout = INTERRUPTED;
- }
- stat = putStat;
if (timeout < 0L)
Thread.currentThread().interrupt();
}
@@ -1249,22 +1191,20 @@
}
else if ((c & ACTIVE) != 0) { // ensure keep-alive
if ((c & CONSUME) != 0 ||
- U.compareAndSwapInt(this, CTL, c,
- c | CONSUME))
+ CTL.compareAndSet(this, c, c | CONSUME))
break;
}
else if (demand == 0L || tail == head)
break;
- else if (U.compareAndSwapInt(this, CTL, c,
- c | (ACTIVE | CONSUME))) {
+ else if (CTL.compareAndSet(this, c, c | (ACTIVE | CONSUME))) {
try {
e.execute(new ConsumerTask<T>(this));
break;
} catch (RuntimeException | Error ex) { // back out
do {} while (((c = ctl) & DISABLED) == 0 &&
(c & ACTIVE) != 0 &&
- !U.compareAndSwapInt(this, CTL, c,
- c & ~ACTIVE));
+ !CTL.weakCompareAndSetVolatile
+ (this, c, c & ~ACTIVE));
throw ex;
}
}
@@ -1300,10 +1240,10 @@
break;
else if ((c & ACTIVE) != 0) {
pendingError = ex;
- if (U.compareAndSwapInt(this, CTL, c, c | ERROR))
+ if (CTL.compareAndSet(this, c, c | ERROR))
break; // cause consumer task to exit
}
- else if (U.compareAndSwapInt(this, CTL, c, DISABLED)) {
+ else if (CTL.compareAndSet(this, c, DISABLED)) {
Flow.Subscriber<? super T> s = subscriber;
if (s != null && ex != null) {
try {
@@ -1330,7 +1270,7 @@
for (int c;;) {
if ((c = ctl) == DISABLED || (c & ACTIVE) == 0)
break;
- if (U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE)) {
+ if (CTL.compareAndSet(this, c, c & ~ACTIVE)) {
onError(ex);
break;
}
@@ -1343,8 +1283,8 @@
for (int c;;) {
if ((c = ctl) == DISABLED)
break;
- if (U.compareAndSwapInt(this, CTL, c,
- c | (ACTIVE | CONSUME | COMPLETE))) {
+ if (CTL.compareAndSet(this, c,
+ c | (ACTIVE | CONSUME | COMPLETE))) {
if ((c & ACTIVE) == 0)
startOrDisable();
break;
@@ -1356,8 +1296,8 @@
for (int c;;) {
if ((c = ctl) == DISABLED)
break;
- if (U.compareAndSwapInt(this, CTL, c,
- c | (ACTIVE | CONSUME | SUBSCRIBE))) {
+ if (CTL.compareAndSet(this, c,
+ c | (ACTIVE | CONSUME | SUBSCRIBE))) {
if ((c & ACTIVE) == 0)
startOrDisable();
break;
@@ -1375,11 +1315,11 @@
if ((c = ctl) == DISABLED)
break;
else if ((c & ACTIVE) != 0) {
- if (U.compareAndSwapInt(this, CTL, c,
- c | (CONSUME | ERROR)))
+ if (CTL.compareAndSet(this, c,
+ c | (CONSUME | ERROR)))
break;
}
- else if (U.compareAndSwapInt(this, CTL, c, DISABLED)) {
+ else if (CTL.compareAndSet(this, c, DISABLED)) {
detach();
break;
}
@@ -1395,19 +1335,18 @@
long prev = demand, d;
if ((d = prev + n) < prev) // saturate
d = Long.MAX_VALUE;
- if (U.compareAndSwapLong(this, DEMAND, prev, d)) {
+ if (DEMAND.compareAndSet(this, prev, d)) {
for (int c, h;;) {
if ((c = ctl) == DISABLED)
break;
else if ((c & ACTIVE) != 0) {
if ((c & CONSUME) != 0 ||
- U.compareAndSwapInt(this, CTL, c,
- c | CONSUME))
+ CTL.compareAndSet(this, c, c | CONSUME))
break;
}
else if ((h = head) != tail) {
- if (U.compareAndSwapInt(this, CTL, c,
- c | (ACTIVE|CONSUME))) {
+ if (CTL.compareAndSet(this, c,
+ c | (ACTIVE|CONSUME))) {
startOrDisable();
break;
}
@@ -1476,16 +1415,14 @@
if ((s = subscriber) != null) { // else disabled
for (;;) {
long d = demand;
- int c; Object[] a; int n; long i; Object x; Thread w;
+ int c; Object[] a; int n, i; Object x; Thread w;
if (((c = ctl) & (ERROR | SUBSCRIBE | DISABLED)) != 0) {
if (!checkControl(s, c))
break;
}
else if ((a = array) == null || h == tail ||
(n = a.length) == 0 ||
- (x = U.getObjectVolatile
- (a, (i = ((long)((n - 1) & h) << ASHIFT) + ABASE)))
- == null) {
+ (x = QA.getAcquire(a, i = (n - 1) & h)) == null) {
if (!checkEmpty(s, c))
break;
}
@@ -1494,10 +1431,10 @@
break;
}
else if (((c & CONSUME) != 0 ||
- U.compareAndSwapInt(this, CTL, c, c | CONSUME)) &&
- U.compareAndSwapObject(a, i, x, null)) {
- U.putIntRelease(this, HEAD, ++h);
- U.getAndAddLong(this, DEMAND, -1L);
+ CTL.compareAndSet(this, c, c | CONSUME)) &&
+ QA.compareAndSet(a, i, x, null)) {
+ HEAD.setRelease(this, ++h);
+ DEMAND.getAndAdd(this, -1L);
if ((w = waiter) != null)
signalWaiter(w);
try {
@@ -1528,7 +1465,7 @@
}
}
else if ((c & SUBSCRIBE) != 0) {
- if (U.compareAndSwapInt(this, CTL, c, c & ~SUBSCRIBE)) {
+ if (CTL.compareAndSet(this, c, c & ~SUBSCRIBE)) {
try {
if (s != null)
s.onSubscribe(this);
@@ -1551,9 +1488,9 @@
boolean stat = true;
if (head == tail) {
if ((c & CONSUME) != 0)
- U.compareAndSwapInt(this, CTL, c, c & ~CONSUME);
+ CTL.compareAndSet(this, c, c & ~CONSUME);
else if ((c & COMPLETE) != 0) {
- if (U.compareAndSwapInt(this, CTL, c, DISABLED)) {
+ if (CTL.compareAndSet(this, c, DISABLED)) {
try {
if (s != null)
s.onComplete();
@@ -1561,7 +1498,7 @@
}
}
}
- else if (U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE))
+ else if (CTL.compareAndSet(this, c, c & ~ACTIVE))
stat = false;
}
return stat;
@@ -1574,8 +1511,8 @@
boolean stat = true;
if (demand == 0L) {
if ((c & CONSUME) != 0)
- U.compareAndSwapInt(this, CTL, c, c & ~CONSUME);
- else if (U.compareAndSwapInt(this, CTL, c, c & ~ACTIVE))
+ CTL.compareAndSet(this, c, c & ~CONSUME);
+ else if (CTL.compareAndSet(this, c, c & ~ACTIVE))
stat = false;
}
return stat;
@@ -1595,31 +1532,25 @@
onError(ex);
}
- // Unsafe mechanics
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long CTL;
- private static final long TAIL;
- private static final long HEAD;
- private static final long DEMAND;
- private static final int ABASE;
- private static final int ASHIFT;
+ // VarHandle mechanics
+ private static final VarHandle CTL;
+ private static final VarHandle TAIL;
+ private static final VarHandle HEAD;
+ private static final VarHandle DEMAND;
+ private static final VarHandle QA;
static {
try {
- CTL = U.objectFieldOffset
- (BufferedSubscription.class.getDeclaredField("ctl"));
- TAIL = U.objectFieldOffset
- (BufferedSubscription.class.getDeclaredField("tail"));
- HEAD = U.objectFieldOffset
- (BufferedSubscription.class.getDeclaredField("head"));
- DEMAND = U.objectFieldOffset
- (BufferedSubscription.class.getDeclaredField("demand"));
-
- ABASE = U.arrayBaseOffset(Object[].class);
- int scale = U.arrayIndexScale(Object[].class);
- if ((scale & (scale - 1)) != 0)
- throw new Error("data type scale not a power of two");
- ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ CTL = l.findVarHandle(BufferedSubscription.class, "ctl",
+ int.class);
+ TAIL = l.findVarHandle(BufferedSubscription.class, "tail",
+ int.class);
+ HEAD = l.findVarHandle(BufferedSubscription.class, "head",
+ int.class);
+ DEMAND = l.findVarHandle(BufferedSubscription.class, "demand",
+ long.class);
+ QA = MethodHandles.arrayElementVarHandle(Object[].class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/SynchronousQueue.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/SynchronousQueue.java Thu Jul 21 20:09:19 2016 -0700
@@ -36,6 +36,8 @@
package java.util.concurrent;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.util.AbstractQueue;
import java.util.Collection;
import java.util.Collections;
@@ -247,7 +249,7 @@
boolean casNext(SNode cmp, SNode val) {
return cmp == next &&
- U.compareAndSwapObject(this, NEXT, cmp, val);
+ SNEXT.compareAndSet(this, cmp, val);
}
/**
@@ -260,7 +262,7 @@
*/
boolean tryMatch(SNode s) {
if (match == null &&
- U.compareAndSwapObject(this, MATCH, null, s)) {
+ SMATCH.compareAndSet(this, null, s)) {
Thread w = waiter;
if (w != null) { // waiters need at most one unpark
waiter = null;
@@ -275,24 +277,21 @@
* Tries to cancel a wait by matching node to itself.
*/
void tryCancel() {
- U.compareAndSwapObject(this, MATCH, null, this);
+ SMATCH.compareAndSet(this, null, this);
}
boolean isCancelled() {
return match == this;
}
- // Unsafe mechanics
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long MATCH;
- private static final long NEXT;
-
+ // VarHandle mechanics
+ private static final VarHandle SMATCH;
+ private static final VarHandle SNEXT;
static {
try {
- MATCH = U.objectFieldOffset
- (SNode.class.getDeclaredField("match"));
- NEXT = U.objectFieldOffset
- (SNode.class.getDeclaredField("next"));
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ SMATCH = l.findVarHandle(SNode.class, "match", SNode.class);
+ SNEXT = l.findVarHandle(SNode.class, "next", SNode.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
@@ -304,7 +303,7 @@
boolean casHead(SNode h, SNode nh) {
return h == head &&
- U.compareAndSwapObject(this, HEAD, h, nh);
+ SHEAD.compareAndSet(this, h, nh);
}
/**
@@ -451,8 +450,10 @@
continue;
}
}
- if (spins > 0)
+ if (spins > 0) {
+ Thread.onSpinWait();
spins = shouldSpin(s) ? (spins - 1) : 0;
+ }
else if (s.waiter == null)
s.waiter = w; // establish waiter so can park next iter
else if (!timed)
@@ -508,13 +509,12 @@
}
}
- // Unsafe mechanics
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long HEAD;
+ // VarHandle mechanics
+ private static final VarHandle SHEAD;
static {
try {
- HEAD = U.objectFieldOffset
- (TransferStack.class.getDeclaredField("head"));
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ SHEAD = l.findVarHandle(TransferStack.class, "head", SNode.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
@@ -546,19 +546,19 @@
boolean casNext(QNode cmp, QNode val) {
return next == cmp &&
- U.compareAndSwapObject(this, NEXT, cmp, val);
+ QNEXT.compareAndSet(this, cmp, val);
}
boolean casItem(Object cmp, Object val) {
return item == cmp &&
- U.compareAndSwapObject(this, ITEM, cmp, val);
+ QITEM.compareAndSet(this, cmp, val);
}
/**
* Tries to cancel by CAS'ing ref to this as item.
*/
void tryCancel(Object cmp) {
- U.compareAndSwapObject(this, ITEM, cmp, this);
+ QITEM.compareAndSet(this, cmp, this);
}
boolean isCancelled() {
@@ -574,17 +574,14 @@
return next == this;
}
- // Unsafe mechanics
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long ITEM;
- private static final long NEXT;
-
+ // VarHandle mechanics
+ private static final VarHandle QITEM;
+ private static final VarHandle QNEXT;
static {
try {
- ITEM = U.objectFieldOffset
- (QNode.class.getDeclaredField("item"));
- NEXT = U.objectFieldOffset
- (QNode.class.getDeclaredField("next"));
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ QITEM = l.findVarHandle(QNode.class, "item", Object.class);
+ QNEXT = l.findVarHandle(QNode.class, "next", QNode.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
@@ -614,7 +611,7 @@
*/
void advanceHead(QNode h, QNode nh) {
if (h == head &&
- U.compareAndSwapObject(this, HEAD, h, nh))
+ QHEAD.compareAndSet(this, h, nh))
h.next = h; // forget old next
}
@@ -623,7 +620,7 @@
*/
void advanceTail(QNode t, QNode nt) {
if (tail == t)
- U.compareAndSwapObject(this, TAIL, t, nt);
+ QTAIL.compareAndSet(this, t, nt);
}
/**
@@ -631,7 +628,7 @@
*/
boolean casCleanMe(QNode cmp, QNode val) {
return cleanMe == cmp &&
- U.compareAndSwapObject(this, CLEANME, cmp, val);
+ QCLEANME.compareAndSet(this, cmp, val);
}
/**
@@ -752,8 +749,10 @@
continue;
}
}
- if (spins > 0)
+ if (spins > 0) {
--spins;
+ Thread.onSpinWait();
+ }
else if (s.waiter == null)
s.waiter = w;
else if (!timed)
@@ -817,18 +816,19 @@
}
}
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long HEAD;
- private static final long TAIL;
- private static final long CLEANME;
+ // VarHandle mechanics
+ private static final VarHandle QHEAD;
+ private static final VarHandle QTAIL;
+ private static final VarHandle QCLEANME;
static {
try {
- HEAD = U.objectFieldOffset
- (TransferQueue.class.getDeclaredField("head"));
- TAIL = U.objectFieldOffset
- (TransferQueue.class.getDeclaredField("tail"));
- CLEANME = U.objectFieldOffset
- (TransferQueue.class.getDeclaredField("cleanMe"));
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ QHEAD = l.findVarHandle(TransferQueue.class, "head",
+ QNode.class);
+ QTAIL = l.findVarHandle(TransferQueue.class, "tail",
+ QNode.class);
+ QCLEANME = l.findVarHandle(TransferQueue.class, "cleanMe",
+ QNode.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/ThreadLocalRandom.java Thu Jul 21 20:09:19 2016 -0700
@@ -36,6 +36,7 @@
package java.util.concurrent;
import java.io.ObjectStreamField;
+import java.security.AccessControlContext;
import java.util.Random;
import java.util.Spliterator;
import java.util.concurrent.atomic.AtomicInteger;
@@ -47,6 +48,7 @@
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.StreamSupport;
+import jdk.internal.misc.Unsafe;
/**
* A random number generator isolated to the current thread. Like the
@@ -95,7 +97,9 @@
* ThreadLocalRandom sequence. The dual use is a marriage of
* convenience, but is a simple and efficient way of reducing
* application-level overhead and footprint of most concurrent
- * programs.
+ * programs. Even more opportunistically, we also define here
+ * other package-private utilities that access Thread class
+ * fields.
*
* Even though this class subclasses java.util.Random, it uses the
* same basic algorithm as java.util.SplittableRandom. (See its
@@ -958,6 +962,49 @@
return r;
}
+ // Support for other package-private ThreadLocal access
+
+ /**
+ * Erases ThreadLocals by nulling out Thread maps.
+ */
+ static final void eraseThreadLocals(Thread thread) {
+ U.putObject(thread, THREADLOCALS, null);
+ U.putObject(thread, INHERITABLETHREADLOCALS, null);
+ }
+
+ static final void setInheritedAccessControlContext(Thread thread,
+ AccessControlContext acc) {
+ U.putObjectRelease(thread, INHERITEDACCESSCONTROLCONTEXT, acc);
+ }
+
+ /**
+ * Returns a new group with the system ThreadGroup (the
+ * topmost, parent-less group) as parent. Uses Unsafe to
+ * traverse Thread.group and ThreadGroup.parent fields.
+ */
+ static final ThreadGroup createThreadGroup(String name) {
+ if (name == null)
+ throw new NullPointerException();
+ try {
+ long tg = U.objectFieldOffset
+ (Thread.class.getDeclaredField("group"));
+ long gp = U.objectFieldOffset
+ (ThreadGroup.class.getDeclaredField("parent"));
+ ThreadGroup group = (ThreadGroup)
+ U.getObject(Thread.currentThread(), tg);
+ while (group != null) {
+ ThreadGroup parent = (ThreadGroup)U.getObject(group, gp);
+ if (parent == null)
+ return new ThreadGroup(group, name);
+ group = parent;
+ }
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
+ // fall through if null as cannot-happen safeguard
+ throw new Error("Cannot create ThreadGroup");
+ }
+
// Serialization support
private static final long serialVersionUID = -5851777807851030925L;
@@ -1022,10 +1069,13 @@
static final String BAD_SIZE = "size must be non-negative";
// Unsafe mechanics
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
+ private static final Unsafe U = Unsafe.getUnsafe();
private static final long SEED;
private static final long PROBE;
private static final long SECONDARY;
+ private static final long THREADLOCALS;
+ private static final long INHERITABLETHREADLOCALS;
+ private static final long INHERITEDACCESSCONTROLCONTEXT;
static {
try {
SEED = U.objectFieldOffset
@@ -1034,6 +1084,12 @@
(Thread.class.getDeclaredField("threadLocalRandomProbe"));
SECONDARY = U.objectFieldOffset
(Thread.class.getDeclaredField("threadLocalRandomSecondarySeed"));
+ THREADLOCALS = U.objectFieldOffset
+ (Thread.class.getDeclaredField("threadLocals"));
+ INHERITABLETHREADLOCALS = U.objectFieldOffset
+ (Thread.class.getDeclaredField("inheritableThreadLocals"));
+ INHERITEDACCESSCONTROLCONTEXT = U.objectFieldOffset
+ (Thread.class.getDeclaredField("inheritedAccessControlContext"));
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicBoolean.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicBoolean.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,27 +35,26 @@
package java.util.concurrent.atomic;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
/**
* A {@code boolean} value that may be updated atomically. See the
- * {@link java.util.concurrent.atomic} package specification for
- * description of the properties of atomic variables. An
- * {@code AtomicBoolean} is used in applications such as atomically
- * updated flags, and cannot be used as a replacement for a
- * {@link java.lang.Boolean}.
+ * {@link VarHandle} specification for descriptions of the properties
+ * of atomic accesses. An {@code AtomicBoolean} is used in
+ * applications such as atomically updated flags, and cannot be used
+ * as a replacement for a {@link java.lang.Boolean}.
*
* @since 1.5
* @author Doug Lea
*/
public class AtomicBoolean implements java.io.Serializable {
private static final long serialVersionUID = 4654671469794556979L;
-
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long VALUE;
-
+ private static final VarHandle VALUE;
static {
try {
- VALUE = U.objectFieldOffset
- (AtomicBoolean.class.getDeclaredField("value"));
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ VALUE = l.findVarHandle(AtomicBoolean.class, "value", int.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
@@ -79,7 +78,8 @@
}
/**
- * Returns the current value.
+ * Returns the current value,
+ * with memory effects as specified by {@link VarHandle#getVolatile}.
*
* @return the current value
*/
@@ -88,40 +88,39 @@
}
/**
- * Atomically sets the value to the given updated value
- * if the current value {@code ==} the expected value.
+ * Atomically sets the value to {@code newValue}
+ * if the current value {@code == expectedValue},
+ * with memory effects as specified by {@link VarHandle#compareAndSet}.
*
- * @param expect the expected value
- * @param update the new value
+ * @param expectedValue the expected value
+ * @param newValue the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
- public final boolean compareAndSet(boolean expect, boolean update) {
- return U.compareAndSwapInt(this, VALUE,
- (expect ? 1 : 0),
- (update ? 1 : 0));
+ public final boolean compareAndSet(boolean expectedValue, boolean newValue) {
+ return VALUE.compareAndSet(this,
+ (expectedValue ? 1 : 0),
+ (newValue ? 1 : 0));
}
/**
- * Atomically sets the value to the given updated value
- * if the current value {@code ==} the expected value.
+ * Possibly atomically sets the value to {@code newValue}
+ * if the current value {@code == expectedValue},
+ * with memory effects as specified by {@link VarHandle#weakCompareAndSet}.
*
- * <p><a href="package-summary.html#weakCompareAndSet">May fail
- * spuriously and does not provide ordering guarantees</a>, so is
- * only rarely an appropriate alternative to {@code compareAndSet}.
- *
- * @param expect the expected value
- * @param update the new value
+ * @param expectedValue the expected value
+ * @param newValue the new value
* @return {@code true} if successful
*/
- public boolean weakCompareAndSet(boolean expect, boolean update) {
- return U.compareAndSwapInt(this, VALUE,
- (expect ? 1 : 0),
- (update ? 1 : 0));
+ public boolean weakCompareAndSet(boolean expectedValue, boolean newValue) {
+ return VALUE.weakCompareAndSet(this,
+ (expectedValue ? 1 : 0),
+ (newValue ? 1 : 0));
}
/**
- * Unconditionally sets to the given value.
+ * Sets the value to {@code newValue},
+ * with memory effects as specified by {@link VarHandle#setVolatile}.
*
* @param newValue the new value
*/
@@ -130,17 +129,19 @@
}
/**
- * Eventually sets to the given value.
+ * Sets the value to {@code newValue},
+ * with memory effects as specified by {@link VarHandle#setRelease}.
*
* @param newValue the new value
* @since 1.6
*/
public final void lazySet(boolean newValue) {
- U.putIntRelease(this, VALUE, (newValue ? 1 : 0));
+ VALUE.setRelease(this, (newValue ? 1 : 0));
}
/**
- * Atomically sets to the given value and returns the previous value.
+ * Atomically sets the value to {@code newValue} and returns the old value,
+ * with memory effects as specified by {@link VarHandle#getAndSet}.
*
* @param newValue the new value
* @return the previous value
@@ -161,4 +162,178 @@
return Boolean.toString(get());
}
+ // jdk9
+
+ /**
+ * Returns the current value, with memory semantics of reading as
+ * if the variable was declared non-{@code volatile}.
+ *
+ * @return the value
+ * @since 9
+ */
+ public final boolean getPlain() {
+ return (int)VALUE.get(this) != 0;
+ }
+
+ /**
+ * Sets the value to {@code newValue}, with memory semantics
+ * of setting as if the variable was declared non-{@code volatile}
+ * and non-{@code final}.
+ *
+ * @param newValue the new value
+ * @since 9
+ */
+ public final void setPlain(boolean newValue) {
+ VALUE.set(this, newValue ? 1 : 0);
+ }
+
+ /**
+ * Returns the current value,
+ * with memory effects as specified by {@link VarHandle#getOpaque}.
+ *
+ * @return the value
+ * @since 9
+ */
+ public final boolean getOpaque() {
+ return (int)VALUE.getOpaque(this) != 0;
+ }
+
+ /**
+ * Sets the value to {@code newValue},
+ * with memory effects as specified by {@link VarHandle#setOpaque}.
+ *
+ * @param newValue the new value
+ * @since 9
+ */
+ public final void setOpaque(boolean newValue) {
+ VALUE.setOpaque(this, newValue ? 1 : 0);
+ }
+
+ /**
+ * Returns the current value,
+ * with memory effects as specified by {@link VarHandle#getAcquire}.
+ *
+ * @return the value
+ * @since 9
+ */
+ public final boolean getAcquire() {
+ return (int)VALUE.getAcquire(this) != 0;
+ }
+
+ /**
+ * Sets the value to {@code newValue},
+ * with memory effects as specified by {@link VarHandle#setRelease}.
+ *
+ * @param newValue the new value
+ * @since 9
+ */
+ public final void setRelease(boolean newValue) {
+ VALUE.setRelease(this, newValue ? 1 : 0);
+ }
+
+ /**
+ * Atomically sets the value to {@code newValue} if the current value,
+ * referred to as the <em>witness value</em>, {@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 <em>witness value</em>, {@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 <em>witness value</em>, {@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));
+ }
+
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicInteger.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicInteger.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,18 +35,18 @@
package java.util.concurrent.atomic;
+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
@@ -54,6 +54,10 @@
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
+ /*
+ * This class intended to be implemented using VarHandles, but there
+ * are unresolved cyclic startup dependencies.
+ */
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long VALUE;
@@ -84,7 +88,8 @@
}
/**
- * Gets the current value.
+ * Returns the current value,
+ * with memory effects as specified by {@link VarHandle#getVolatile}.
*
* @return the current value
*/
@@ -93,7 +98,8 @@
}
/**
- * Sets to the given value.
+ * Sets the value to {@code newValue},
+ * with memory effects as specified by {@link VarHandle#setVolatile}.
*
* @param newValue the new value
*/
@@ -102,7 +108,8 @@
}
/**
- * 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
@@ -112,7 +119,8 @@
}
/**
- * 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
@@ -122,36 +130,37 @@
}
/**
- * 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 U.compareAndSwapInt(this, VALUE, 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}.
*
- * <p><a href="package-summary.html#weakCompareAndSet">May fail
- * spuriously and does not provide ordering guarantees</a>, 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 U.weakCompareAndSwapInt(this, VALUE, expectedValue, newValue);
}
/**
- * Atomically increments by one the current value.
+ * Atomically increments the current value,
+ * with memory effects as specified by {@link VarHandle#getAndAdd}.
+ *
+ * <p>Equivalent to {@code getAndAdd(1)}.
*
* @return the previous value
*/
@@ -160,7 +169,10 @@
}
/**
- * Atomically decrements by one the current value.
+ * Atomically decrements the current value,
+ * with memory effects as specified by {@link VarHandle#getAndAdd}.
+ *
+ * <p>Equivalent to {@code getAndAdd(-1)}.
*
* @return the previous value
*/
@@ -169,7 +181,8 @@
}
/**
- * 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
@@ -179,7 +192,10 @@
}
/**
- * Atomically increments by one the current value.
+ * Atomically increments the current value,
+ * with memory effects as specified by {@link VarHandle#addAndGet}.
+ *
+ * <p>Equivalent to {@code addAndGet(1)}.
*
* @return the updated value
*/
@@ -188,7 +204,10 @@
}
/**
- * Atomically decrements by one the current value.
+ * Atomically decrements the current value,
+ * with memory effects as specified by {@link VarHandle#addAndGet}.
+ *
+ * <p>Equivalent to {@code addAndGet(-1)}.
*
* @return the updated value
*/
@@ -197,7 +216,8 @@
}
/**
- * 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
@@ -217,12 +237,14 @@
* @since 1.8
*/
public final int getAndUpdate(IntUnaryOperator updateFunction) {
- int prev, next;
- do {
- prev = get();
- next = updateFunction.applyAsInt(prev);
- } while (!compareAndSet(prev, next));
- return prev;
+ int prev = get(), next = 0;
+ for (boolean haveNext = false;;) {
+ if (!haveNext)
+ next = updateFunction.applyAsInt(prev);
+ if (weakCompareAndSetVolatile(prev, next))
+ return prev;
+ haveNext = (prev == (prev = get()));
+ }
}
/**
@@ -236,12 +258,14 @@
* @since 1.8
*/
public final int updateAndGet(IntUnaryOperator updateFunction) {
- int prev, next;
- do {
- prev = get();
- next = updateFunction.applyAsInt(prev);
- } while (!compareAndSet(prev, next));
- return next;
+ int prev = get(), next = 0;
+ for (boolean haveNext = false;;) {
+ if (!haveNext)
+ next = updateFunction.applyAsInt(prev);
+ if (weakCompareAndSetVolatile(prev, next))
+ return next;
+ haveNext = (prev == (prev = get()));
+ }
}
/**
@@ -260,12 +284,14 @@
*/
public final int getAndAccumulate(int x,
IntBinaryOperator accumulatorFunction) {
- int prev, next;
- do {
- prev = get();
- next = accumulatorFunction.applyAsInt(prev, x);
- } while (!compareAndSet(prev, next));
- return prev;
+ int prev = get(), next = 0;
+ for (boolean haveNext = false;;) {
+ if (!haveNext)
+ next = accumulatorFunction.applyAsInt(prev, x);
+ if (weakCompareAndSetVolatile(prev, next))
+ return prev;
+ haveNext = (prev == (prev = get()));
+ }
}
/**
@@ -284,12 +310,14 @@
*/
public final int accumulateAndGet(int x,
IntBinaryOperator accumulatorFunction) {
- int prev, next;
- do {
- prev = get();
- next = accumulatorFunction.applyAsInt(prev, x);
- } while (!compareAndSet(prev, next));
- return next;
+ int prev = get(), next = 0;
+ for (boolean haveNext = false;;) {
+ if (!haveNext)
+ next = accumulatorFunction.applyAsInt(prev, x);
+ if (weakCompareAndSetVolatile(prev, next))
+ return next;
+ haveNext = (prev == (prev = get()));
+ }
}
/**
@@ -301,7 +329,10 @@
}
/**
- * Returns the value of this {@code AtomicInteger} as an {@code int}.
+ * Returns the current value of this {@code AtomicInteger} as an
+ * {@code int},
+ * with memory effects as specified by {@link VarHandle#getVolatile}.
+ *
* Equivalent to {@link #get()}.
*/
public int intValue() {
@@ -309,8 +340,9 @@
}
/**
- * Returns the value of this {@code AtomicInteger} as a {@code long}
- * after a widening primitive conversion.
+ * Returns the current value of this {@code AtomicInteger} as a
+ * {@code long} after a widening primitive conversion,
+ * with memory effects as specified by {@link VarHandle#getVolatile}.
* @jls 5.1.2 Widening Primitive Conversions
*/
public long longValue() {
@@ -318,8 +350,9 @@
}
/**
- * Returns the value of this {@code AtomicInteger} as a {@code float}
- * after a widening primitive conversion.
+ * Returns the current value of this {@code AtomicInteger} as a
+ * {@code float} after a widening primitive conversion,
+ * with memory effects as specified by {@link VarHandle#getVolatile}.
* @jls 5.1.2 Widening Primitive Conversions
*/
public float floatValue() {
@@ -327,12 +360,175 @@
}
/**
- * Returns the value of this {@code AtomicInteger} as a {@code double}
- * after a widening primitive conversion.
+ * Returns the current value of this {@code AtomicInteger} as a
+ * {@code double} after a widening primitive conversion,
+ * with memory effects as specified by {@link VarHandle#getVolatile}.
* @jls 5.1.2 Widening Primitive Conversions
*/
public double doubleValue() {
return (double)get();
}
+ // jdk9
+
+ /**
+ * Returns the current value, with memory semantics of reading as
+ * if the variable was declared non-{@code volatile}.
+ *
+ * @return the value
+ * @since 9
+ */
+ public final int getPlain() {
+ return U.getInt(this, VALUE);
+ }
+
+ /**
+ * 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) {
+ U.putInt(this, VALUE, newValue);
+ }
+
+ /**
+ * Returns the current value,
+ * with memory effects as specified by {@link VarHandle#getOpaque}.
+ *
+ * @return the value
+ * @since 9
+ */
+ public final int getOpaque() {
+ return U.getIntOpaque(this, VALUE);
+ }
+
+ /**
+ * 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) {
+ U.putIntOpaque(this, VALUE, newValue);
+ }
+
+ /**
+ * Returns the current value,
+ * with memory effects as specified by {@link VarHandle#getAcquire}.
+ *
+ * @return the value
+ * @since 9
+ */
+ public final int getAcquire() {
+ return U.getIntAcquire(this, VALUE);
+ }
+
+ /**
+ * 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) {
+ U.putIntRelease(this, VALUE, newValue);
+ }
+
+ /**
+ * Atomically sets the value to {@code newValue} if the current value,
+ * referred to as the <em>witness value</em>, {@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 U.compareAndExchangeIntVolatile(this, VALUE, expectedValue, newValue);
+ }
+
+ /**
+ * Atomically sets the value to {@code newValue} if the current value,
+ * referred to as the <em>witness value</em>, {@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 U.compareAndExchangeIntAcquire(this, VALUE, expectedValue, newValue);
+ }
+
+ /**
+ * Atomically sets the value to {@code newValue} if the current value,
+ * referred to as the <em>witness value</em>, {@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 U.compareAndExchangeIntRelease(this, VALUE, 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 U.weakCompareAndSwapIntVolatile(this, VALUE, 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 U.weakCompareAndSwapIntAcquire(this, VALUE, 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 U.weakCompareAndSwapIntRelease(this, VALUE, expectedValue, newValue);
+ }
+
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerArray.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,44 +35,24 @@
package java.util.concurrent.atomic;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.util.function.IntBinaryOperator;
import java.util.function.IntUnaryOperator;
/**
* An {@code int} array in which elements may be updated atomically.
- * See the {@link java.util.concurrent.atomic} package
- * specification for description of the properties of atomic
- * variables.
+ * See the {@link VarHandle} specification for descriptions of the
+ * properties of atomic accesses.
* @since 1.5
* @author Doug Lea
*/
public class AtomicIntegerArray implements java.io.Serializable {
private static final long serialVersionUID = 2862133569453604235L;
-
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final int ABASE;
- private static final int ASHIFT;
+ private static final VarHandle AA
+ = MethodHandles.arrayElementVarHandle(int[].class);
private final int[] array;
- static {
- ABASE = U.arrayBaseOffset(int[].class);
- int scale = U.arrayIndexScale(int[].class);
- if ((scale & (scale - 1)) != 0)
- throw new Error("array index scale not a power of two");
- ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
- }
-
- private long checkedByteOffset(int i) {
- if (i < 0 || i >= array.length)
- throw new IndexOutOfBoundsException("index " + i);
-
- return byteOffset(i);
- }
-
- private static long byteOffset(int i) {
- return ((long) i << ASHIFT) + ABASE;
- }
-
/**
* Creates a new AtomicIntegerArray of the given length, with all
* elements initially zero.
@@ -105,147 +85,155 @@
}
/**
- * Gets the current value at position {@code i}.
+ * Returns the current value of the element at index {@code i},
+ * with memory effects as specified by {@link VarHandle#getVolatile}.
*
* @param i the index
* @return the current value
*/
public final int get(int i) {
- return getRaw(checkedByteOffset(i));
- }
-
- private int getRaw(long offset) {
- return U.getIntVolatile(array, offset);
+ return (int)AA.getVolatile(array, i);
}
/**
- * Sets the element at position {@code i} to the given value.
+ * Sets the element at index {@code i} to {@code newValue},
+ * with memory effects as specified by {@link VarHandle#setVolatile}.
*
* @param i the index
* @param newValue the new value
*/
public final void set(int i, int newValue) {
- U.putIntVolatile(array, checkedByteOffset(i), newValue);
+ AA.setVolatile(array, i, newValue);
}
/**
- * Eventually sets the element at position {@code i} to the given value.
+ * Sets the element at index {@code i} to {@code newValue},
+ * with memory effects as specified by {@link VarHandle#setRelease}.
*
* @param i the index
* @param newValue the new value
* @since 1.6
*/
public final void lazySet(int i, int newValue) {
- U.putIntRelease(array, checkedByteOffset(i), newValue);
+ AA.setRelease(array, i, newValue);
}
/**
- * Atomically sets the element at position {@code i} to the given
- * value and returns the old value.
+ * Atomically sets the element at index {@code i} to {@code
+ * newValue} and returns the old value,
+ * with memory effects as specified by {@link VarHandle#getAndSet}.
*
* @param i the index
* @param newValue the new value
* @return the previous value
*/
public final int getAndSet(int i, int newValue) {
- return U.getAndSetInt(array, checkedByteOffset(i), newValue);
+ return (int)AA.getAndSet(array, i, newValue);
}
/**
- * Atomically sets the element at position {@code i} to the given
- * updated value if the current value {@code ==} the expected value.
+ * Atomically sets the element at index {@code i} to {@code
+ * newValue} if the element's current value {@code == expectedValue},
+ * with memory effects as specified by {@link VarHandle#compareAndSet}.
*
* @param i the index
- * @param expect the expected value
- * @param update the new value
+ * @param expectedValue the expected value
+ * @param newValue the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
- public final boolean compareAndSet(int i, int expect, int update) {
- return compareAndSetRaw(checkedByteOffset(i), expect, update);
- }
-
- private boolean compareAndSetRaw(long offset, int expect, int update) {
- return U.compareAndSwapInt(array, offset, expect, update);
+ public final boolean compareAndSet(int i, int expectedValue, int newValue) {
+ return AA.compareAndSet(array, i, expectedValue, newValue);
}
/**
- * Atomically sets the element at position {@code i} to the given
- * updated value if the current value {@code ==} the expected value.
- *
- * <p><a href="package-summary.html#weakCompareAndSet">May fail
- * spuriously and does not provide ordering guarantees</a>, 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}.
+ *
+ * <p>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}.
+ *
+ * <p>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}.
+ *
+ * <p>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}.
+ *
+ * <p>Equivalent to {@code addAndGet(i, -1)}.
*
* @param i the index
* @return the updated value
*/
public final int decrementAndGet(int i) {
- return getAndAdd(i, -1) - 1;
+ return (int)AA.addAndGet(array, i, -1);
}
/**
- * Atomically adds the given value to the element at index {@code i}.
+ * Atomically adds the given value to the element at index {@code i},
+ * with memory effects as specified by {@link VarHandle#addAndGet}.
*
* @param i the index
* @param delta the value to add
* @return the updated value
*/
public final int addAndGet(int i, int delta) {
- return getAndAdd(i, delta) + delta;
+ return (int)AA.addAndGet(array, i, delta);
}
/**
@@ -260,13 +248,14 @@
* @since 1.8
*/
public final int getAndUpdate(int i, IntUnaryOperator updateFunction) {
- long offset = checkedByteOffset(i);
- int prev, next;
- do {
- prev = getRaw(offset);
- next = updateFunction.applyAsInt(prev);
- } while (!compareAndSetRaw(offset, prev, next));
- return prev;
+ int prev = get(i), next = 0;
+ for (boolean haveNext = false;;) {
+ if (!haveNext)
+ next = updateFunction.applyAsInt(prev);
+ if (weakCompareAndSetVolatile(i, prev, next))
+ return prev;
+ haveNext = (prev == (prev = get(i)));
+ }
}
/**
@@ -281,23 +270,25 @@
* @since 1.8
*/
public final int updateAndGet(int i, IntUnaryOperator updateFunction) {
- long offset = checkedByteOffset(i);
- int prev, next;
- do {
- prev = getRaw(offset);
- next = updateFunction.applyAsInt(prev);
- } while (!compareAndSetRaw(offset, prev, next));
- return next;
+ int prev = get(i), next = 0;
+ for (boolean haveNext = false;;) {
+ if (!haveNext)
+ next = updateFunction.applyAsInt(prev);
+ if (weakCompareAndSetVolatile(i, prev, next))
+ return next;
+ haveNext = (prev == (prev = get(i)));
+ }
}
/**
* Atomically updates the element at index {@code i} with the
- * results of applying the given function to the current and
- * given values, returning the previous value. The function should
- * be side-effect-free, since it may be re-applied when attempted
+ * results of applying the given function to the current and given
+ * values, returning the previous value. The function should be
+ * side-effect-free, since it may be re-applied when attempted
* updates fail due to contention among threads. The function is
- * applied with the current value at index {@code i} as its first
- * argument, and the given update as the second argument.
+ * applied with the current value of the element at index {@code i}
+ * as its first argument, and the given update as the second
+ * argument.
*
* @param i the index
* @param x the update value
@@ -307,23 +298,25 @@
*/
public final int getAndAccumulate(int i, int x,
IntBinaryOperator accumulatorFunction) {
- long offset = checkedByteOffset(i);
- int prev, next;
- do {
- prev = getRaw(offset);
- next = accumulatorFunction.applyAsInt(prev, x);
- } while (!compareAndSetRaw(offset, prev, next));
- return prev;
+ int prev = get(i), next = 0;
+ for (boolean haveNext = false;;) {
+ if (!haveNext)
+ next = accumulatorFunction.applyAsInt(prev, x);
+ if (weakCompareAndSetVolatile(i, prev, next))
+ return prev;
+ haveNext = (prev == (prev = get(i)));
+ }
}
/**
* Atomically updates the element at index {@code i} with the
- * results of applying the given function to the current and
- * given values, returning the updated value. The function should
- * be side-effect-free, since it may be re-applied when attempted
+ * results of applying the given function to the current and given
+ * values, returning the updated value. The function should be
+ * side-effect-free, since it may be re-applied when attempted
* updates fail due to contention among threads. The function is
- * applied with the current value at index {@code i} as its first
- * argument, and the given update as the second argument.
+ * applied with the current value of the element at index {@code i}
+ * as its first argument, and the given update as the second
+ * argument.
*
* @param i the index
* @param x the update value
@@ -333,13 +326,14 @@
*/
public final int accumulateAndGet(int i, int x,
IntBinaryOperator accumulatorFunction) {
- long offset = checkedByteOffset(i);
- int prev, next;
- do {
- prev = getRaw(offset);
- next = accumulatorFunction.applyAsInt(prev, x);
- } while (!compareAndSetRaw(offset, prev, next));
- return next;
+ int prev = get(i), next = 0;
+ for (boolean haveNext = false;;) {
+ if (!haveNext)
+ next = accumulatorFunction.applyAsInt(prev, x);
+ if (weakCompareAndSetVolatile(i, prev, next))
+ return next;
+ haveNext = (prev == (prev = get(i)));
+ }
}
/**
@@ -354,11 +348,190 @@
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) {
- b.append(getRaw(byteOffset(i)));
+ b.append(get(i));
if (i == iMax)
return b.append(']').toString();
b.append(',').append(' ');
}
}
+ // jdk9
+
+ /**
+ * Returns the current value of the element at index {@code i},
+ * with memory semantics of reading as if the variable was declared
+ * non-{@code volatile}.
+ *
+ * @param i the index
+ * @return the value
+ * @since 9
+ */
+ public final int getPlain(int i) {
+ return (int)AA.get(array, i);
+ }
+
+ /**
+ * Sets the element at index {@code i} to {@code newValue},
+ * with memory semantics of setting as if the variable was
+ * declared non-{@code volatile} and non-{@code final}.
+ *
+ * @param i the index
+ * @param newValue the new value
+ * @since 9
+ */
+ public final void setPlain(int i, int newValue) {
+ AA.set(array, i, newValue);
+ }
+
+ /**
+ * Returns the current value of the element at index {@code i},
+ * with memory effects as specified by {@link VarHandle#getOpaque}.
+ *
+ * @param i the index
+ * @return the value
+ * @since 9
+ */
+ public final int getOpaque(int i) {
+ return (int)AA.getOpaque(array, i);
+ }
+
+ /**
+ * Sets the element at index {@code i} to {@code newValue},
+ * with memory effects as specified by {@link VarHandle#setOpaque}.
+ *
+ * @param i the index
+ * @param newValue the new value
+ * @since 9
+ */
+ public final void setOpaque(int i, int newValue) {
+ AA.setOpaque(array, i, newValue);
+ }
+
+ /**
+ * Returns the current value of the element at index {@code i},
+ * with memory effects as specified by {@link VarHandle#getAcquire}.
+ *
+ * @param i the index
+ * @return the value
+ * @since 9
+ */
+ public final int getAcquire(int i) {
+ return (int)AA.getAcquire(array, i);
+ }
+
+ /**
+ * Sets the element at index {@code i} to {@code newValue},
+ * with memory effects as specified by {@link VarHandle#setRelease}.
+ *
+ * @param i the index
+ * @param newValue the new value
+ * @since 9
+ */
+ public final void setRelease(int i, int newValue) {
+ AA.setRelease(array, i, newValue);
+ }
+
+ /**
+ * Atomically sets the element at index {@code i} to {@code newValue}
+ * if the element's current value, referred to as the <em>witness
+ * value</em>, {@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 <em>witness
+ * value</em>, {@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 <em>witness
+ * value</em>, {@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);
+ }
+
+
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java Thu Jul 21 20:09:19 2016 -0700
@@ -42,6 +42,7 @@
import java.security.PrivilegedExceptionAction;
import java.util.function.IntBinaryOperator;
import java.util.function.IntUnaryOperator;
+import jdk.internal.misc.Unsafe;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
@@ -150,8 +151,8 @@
public abstract void lazySet(T obj, int newValue);
/**
- * Gets the current value held in the field of the given object managed
- * by this updater.
+ * Returns the current value held in the field of the given object
+ * managed by this updater.
*
* @param obj An object whose field to get
* @return the current value
@@ -367,7 +368,7 @@
*/
private static final class AtomicIntegerFieldUpdaterImpl<T>
extends AtomicIntegerFieldUpdater<T> {
- 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
--- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,18 +35,18 @@
package java.util.concurrent.atomic;
+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
@@ -54,12 +54,9 @@
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;
-
/**
* 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.
*/
@@ -71,6 +68,13 @@
*/
private static native boolean VMSupportsCS8();
+ /*
+ * This class intended to be implemented using VarHandles, but there
+ * are unresolved cyclic startup dependencies.
+ */
+ private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
+ private static final long VALUE;
+
static {
try {
VALUE = U.objectFieldOffset
@@ -98,7 +102,8 @@
}
/**
- * Gets the current value.
+ * Returns the current value,
+ * with memory effects as specified by {@link VarHandle#getVolatile}.
*
* @return the current value
*/
@@ -107,7 +112,8 @@
}
/**
- * Sets to the given value.
+ * Sets the value to {@code newValue},
+ * with memory effects as specified by {@link VarHandle#setVolatile}.
*
* @param newValue the new value
*/
@@ -118,7 +124,8 @@
}
/**
- * 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
@@ -128,7 +135,8 @@
}
/**
- * 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
@@ -138,36 +146,37 @@
}
/**
- * 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 U.compareAndSwapLong(this, VALUE, 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}.
*
- * <p><a href="package-summary.html#weakCompareAndSet">May fail
- * spuriously and does not provide ordering guarantees</a>, 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 U.weakCompareAndSwapLong(this, VALUE, expectedValue, newValue);
}
/**
- * Atomically increments by one the current value.
+ * Atomically increments the current value,
+ * with memory effects as specified by {@link VarHandle#getAndAdd}.
+ *
+ * <p>Equivalent to {@code getAndAdd(1)}.
*
* @return the previous value
*/
@@ -176,7 +185,10 @@
}
/**
- * Atomically decrements by one the current value.
+ * Atomically decrements the current value,
+ * with memory effects as specified by {@link VarHandle#getAndAdd}.
+ *
+ * <p>Equivalent to {@code getAndAdd(-1)}.
*
* @return the previous value
*/
@@ -185,7 +197,8 @@
}
/**
- * 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
@@ -195,7 +208,10 @@
}
/**
- * Atomically increments by one the current value.
+ * Atomically increments the current value,
+ * with memory effects as specified by {@link VarHandle#addAndGet}.
+ *
+ * <p>Equivalent to {@code addAndGet(1)}.
*
* @return the updated value
*/
@@ -204,7 +220,10 @@
}
/**
- * Atomically decrements by one the current value.
+ * Atomically decrements the current value,
+ * with memory effects as specified by {@link VarHandle#addAndGet}.
+ *
+ * <p>Equivalent to {@code addAndGet(-1)}.
*
* @return the updated value
*/
@@ -213,7 +232,8 @@
}
/**
- * 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
@@ -233,12 +253,14 @@
* @since 1.8
*/
public final long getAndUpdate(LongUnaryOperator updateFunction) {
- long prev, next;
- do {
- prev = get();
- next = updateFunction.applyAsLong(prev);
- } while (!compareAndSet(prev, next));
- return prev;
+ long prev = get(), next = 0L;
+ for (boolean haveNext = false;;) {
+ if (!haveNext)
+ next = updateFunction.applyAsLong(prev);
+ if (weakCompareAndSetVolatile(prev, next))
+ return prev;
+ haveNext = (prev == (prev = get()));
+ }
}
/**
@@ -252,12 +274,14 @@
* @since 1.8
*/
public final long updateAndGet(LongUnaryOperator updateFunction) {
- long prev, next;
- do {
- prev = get();
- next = updateFunction.applyAsLong(prev);
- } while (!compareAndSet(prev, next));
- return next;
+ long prev = get(), next = 0L;
+ for (boolean haveNext = false;;) {
+ if (!haveNext)
+ next = updateFunction.applyAsLong(prev);
+ if (weakCompareAndSetVolatile(prev, next))
+ return next;
+ haveNext = (prev == (prev = get()));
+ }
}
/**
@@ -276,12 +300,14 @@
*/
public final long getAndAccumulate(long x,
LongBinaryOperator accumulatorFunction) {
- long prev, next;
- do {
- prev = get();
- next = accumulatorFunction.applyAsLong(prev, x);
- } while (!compareAndSet(prev, next));
- return prev;
+ long prev = get(), next = 0L;
+ for (boolean haveNext = false;;) {
+ if (!haveNext)
+ next = accumulatorFunction.applyAsLong(prev, x);
+ if (weakCompareAndSetVolatile(prev, next))
+ return prev;
+ haveNext = (prev == (prev = get()));
+ }
}
/**
@@ -300,12 +326,14 @@
*/
public final long accumulateAndGet(long x,
LongBinaryOperator accumulatorFunction) {
- long prev, next;
- do {
- prev = get();
- next = accumulatorFunction.applyAsLong(prev, x);
- } while (!compareAndSet(prev, next));
- return next;
+ long prev = get(), next = 0L;
+ for (boolean haveNext = false;;) {
+ if (!haveNext)
+ next = accumulatorFunction.applyAsLong(prev, x);
+ if (weakCompareAndSetVolatile(prev, next))
+ return next;
+ haveNext = (prev == (prev = get()));
+ }
}
/**
@@ -317,8 +345,9 @@
}
/**
- * Returns the value of this {@code AtomicLong} as an {@code int}
- * after a narrowing primitive conversion.
+ * Returns the current value of this {@code AtomicLong} as an {@code int}
+ * after a narrowing primitive conversion,
+ * with memory effects as specified by {@link VarHandle#getVolatile}.
* @jls 5.1.3 Narrowing Primitive Conversions
*/
public int intValue() {
@@ -326,7 +355,8 @@
}
/**
- * Returns the value of this {@code AtomicLong} as a {@code long}.
+ * Returns the current value of this {@code AtomicLong} as a {@code long},
+ * with memory effects as specified by {@link VarHandle#getVolatile}.
* Equivalent to {@link #get()}.
*/
public long longValue() {
@@ -334,8 +364,9 @@
}
/**
- * Returns the value of this {@code AtomicLong} as a {@code float}
- * after a widening primitive conversion.
+ * Returns the current value of this {@code AtomicLong} as a {@code float}
+ * after a widening primitive conversion,
+ * with memory effects as specified by {@link VarHandle#getVolatile}.
* @jls 5.1.2 Widening Primitive Conversions
*/
public float floatValue() {
@@ -343,12 +374,175 @@
}
/**
- * Returns the value of this {@code AtomicLong} as a {@code double}
- * after a widening primitive conversion.
+ * Returns the current value of this {@code AtomicLong} as a {@code double}
+ * after a widening primitive conversion,
+ * with memory effects as specified by {@link VarHandle#getVolatile}.
* @jls 5.1.2 Widening Primitive Conversions
*/
public double doubleValue() {
return (double)get();
}
+ // jdk9
+
+ /**
+ * Returns the current value, with memory semantics of reading as if the
+ * variable was declared non-{@code volatile}.
+ *
+ * @return the value
+ * @since 9
+ */
+ public final long getPlain() {
+ return U.getLong(this, VALUE);
+ }
+
+ /**
+ * 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) {
+ U.putLong(this, VALUE, newValue);
+ }
+
+ /**
+ * Returns the current value,
+ * with memory effects as specified by {@link VarHandle#getOpaque}.
+ *
+ * @return the value
+ * @since 9
+ */
+ public final long getOpaque() {
+ return U.getLongOpaque(this, VALUE);
+ }
+
+ /**
+ * 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) {
+ U.putLongOpaque(this, VALUE, newValue);
+ }
+
+ /**
+ * Returns the current value,
+ * with memory effects as specified by {@link VarHandle#getAcquire}.
+ *
+ * @return the value
+ * @since 9
+ */
+ public final long getAcquire() {
+ return U.getLongAcquire(this, VALUE);
+ }
+
+ /**
+ * 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) {
+ U.putLongRelease(this, VALUE, newValue);
+ }
+
+ /**
+ * Atomically sets the value to {@code newValue} if the current value,
+ * referred to as the <em>witness value</em>, {@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 U.compareAndExchangeLongVolatile(this, VALUE, expectedValue, newValue);
+ }
+
+ /**
+ * Atomically sets the value to {@code newValue} if the current value,
+ * referred to as the <em>witness value</em>, {@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 U.compareAndExchangeLongAcquire(this, VALUE, expectedValue, newValue);
+ }
+
+ /**
+ * Atomically sets the value to {@code newValue} if the current value,
+ * referred to as the <em>witness value</em>, {@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 U.compareAndExchangeLongRelease(this, VALUE, 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 U.weakCompareAndSwapLongVolatile(this, VALUE, 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 U.weakCompareAndSwapLongAcquire(this, VALUE, 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 U.weakCompareAndSwapLongRelease(this, VALUE, expectedValue, newValue);
+ }
+
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongArray.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongArray.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,43 +35,24 @@
package java.util.concurrent.atomic;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.util.function.LongBinaryOperator;
import java.util.function.LongUnaryOperator;
/**
* A {@code long} array in which elements may be updated atomically.
- * See the {@link java.util.concurrent.atomic} package specification
- * for description of the properties of atomic variables.
+ * See the {@link VarHandle} specification for descriptions of the
+ * properties of atomic accesses.
* @since 1.5
* @author Doug Lea
*/
public class AtomicLongArray implements java.io.Serializable {
private static final long serialVersionUID = -2308431214976778248L;
-
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final int ABASE;
- private static final int ASHIFT;
+ private static final VarHandle AA
+ = MethodHandles.arrayElementVarHandle(long[].class);
private final long[] array;
- static {
- ABASE = U.arrayBaseOffset(long[].class);
- int scale = U.arrayIndexScale(long[].class);
- if ((scale & (scale - 1)) != 0)
- throw new Error("array index scale not a power of two");
- ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
- }
-
- private long checkedByteOffset(int i) {
- if (i < 0 || i >= array.length)
- throw new IndexOutOfBoundsException("index " + i);
-
- return byteOffset(i);
- }
-
- private static long byteOffset(int i) {
- return ((long) i << ASHIFT) + ABASE;
- }
-
/**
* Creates a new AtomicLongArray of the given length, with all
* elements initially zero.
@@ -104,147 +85,155 @@
}
/**
- * Gets the current value at position {@code i}.
+ * Returns the current value of the element at index {@code i},
+ * with memory effects as specified by {@link VarHandle#getVolatile}.
*
* @param i the index
* @return the current value
*/
public final long get(int i) {
- return getRaw(checkedByteOffset(i));
- }
-
- private long getRaw(long offset) {
- return U.getLongVolatile(array, offset);
+ return (long)AA.getVolatile(array, i);
}
/**
- * Sets the element at position {@code i} to the given value.
+ * Sets the element at index {@code i} to {@code newValue},
+ * with memory effects as specified by {@link VarHandle#setVolatile}.
*
* @param i the index
* @param newValue the new value
*/
public final void set(int i, long newValue) {
- U.putLongVolatile(array, checkedByteOffset(i), newValue);
+ AA.setVolatile(array, i, newValue);
}
/**
- * Eventually sets the element at position {@code i} to the given value.
+ * Sets the element at index {@code i} to {@code newValue},
+ * with memory effects as specified by {@link VarHandle#setRelease}.
*
* @param i the index
* @param newValue the new value
* @since 1.6
*/
public final void lazySet(int i, long newValue) {
- U.putLongRelease(array, checkedByteOffset(i), newValue);
+ AA.setRelease(array, i, newValue);
}
/**
- * Atomically sets the element at position {@code i} to the given value
- * and returns the old value.
+ * Atomically sets the element at index {@code i} to {@code
+ * newValue} and returns the old value,
+ * with memory effects as specified by {@link VarHandle#getAndSet}.
*
* @param i the index
* @param newValue the new value
* @return the previous value
*/
public final long getAndSet(int i, long newValue) {
- return U.getAndSetLong(array, checkedByteOffset(i), newValue);
+ return (long)AA.getAndSet(array, i, newValue);
}
/**
- * Atomically sets the element at position {@code i} to the given
- * updated value if the current value {@code ==} the expected value.
+ * Atomically sets the element at index {@code i} to {@code newValue}
+ * if the element's current value {@code == expectedValue},
+ * with memory effects as specified by {@link VarHandle#compareAndSet}.
*
* @param i the index
- * @param expect the expected value
- * @param update the new value
+ * @param expectedValue the expected value
+ * @param newValue the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
- public final boolean compareAndSet(int i, long expect, long update) {
- return compareAndSetRaw(checkedByteOffset(i), expect, update);
- }
-
- private boolean compareAndSetRaw(long offset, long expect, long update) {
- return U.compareAndSwapLong(array, offset, expect, update);
+ public final boolean compareAndSet(int i, long expectedValue, long newValue) {
+ return AA.compareAndSet(array, i, expectedValue, newValue);
}
/**
- * Atomically sets the element at position {@code i} to the given
- * updated value if the current value {@code ==} the expected value.
- *
- * <p><a href="package-summary.html#weakCompareAndSet">May fail
- * spuriously and does not provide ordering guarantees</a>, 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}.
+ *
+ * <p>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}.
+ *
+ * <p>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}.
+ *
+ * <p>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}.
+ *
+ * <p>Equivalent to {@code addAndGet(i, -1)}.
*
* @param i the index
* @return the updated value
*/
public final long decrementAndGet(int i) {
- return getAndAdd(i, -1) - 1;
+ return (long)AA.addAndGet(array, i, -1L);
}
/**
- * Atomically adds the given value to the element at index {@code i}.
+ * Atomically adds the given value to the element at index {@code i},
+ * with memory effects as specified by {@link VarHandle#addAndGet}.
*
* @param i the index
* @param delta the value to add
* @return the updated value
*/
public long addAndGet(int i, long delta) {
- return getAndAdd(i, delta) + delta;
+ return (long)AA.addAndGet(array, i, delta);
}
/**
@@ -259,13 +248,14 @@
* @since 1.8
*/
public final long getAndUpdate(int i, LongUnaryOperator updateFunction) {
- long offset = checkedByteOffset(i);
- long prev, next;
- do {
- prev = getRaw(offset);
- next = updateFunction.applyAsLong(prev);
- } while (!compareAndSetRaw(offset, prev, next));
- return prev;
+ long prev = get(i), next = 0L;
+ for (boolean haveNext = false;;) {
+ if (!haveNext)
+ next = updateFunction.applyAsLong(prev);
+ if (weakCompareAndSetVolatile(i, prev, next))
+ return prev;
+ haveNext = (prev == (prev = get(i)));
+ }
}
/**
@@ -280,23 +270,25 @@
* @since 1.8
*/
public final long updateAndGet(int i, LongUnaryOperator updateFunction) {
- long offset = checkedByteOffset(i);
- long prev, next;
- do {
- prev = getRaw(offset);
- next = updateFunction.applyAsLong(prev);
- } while (!compareAndSetRaw(offset, prev, next));
- return next;
+ long prev = get(i), next = 0L;
+ for (boolean haveNext = false;;) {
+ if (!haveNext)
+ next = updateFunction.applyAsLong(prev);
+ if (weakCompareAndSetVolatile(i, prev, next))
+ return next;
+ haveNext = (prev == (prev = get(i)));
+ }
}
/**
* Atomically updates the element at index {@code i} with the
- * results of applying the given function to the current and
- * given values, returning the previous value. The function should
- * be side-effect-free, since it may be re-applied when attempted
+ * results of applying the given function to the current and given
+ * values, returning the previous value. The function should be
+ * side-effect-free, since it may be re-applied when attempted
* updates fail due to contention among threads. The function is
- * applied with the current value at index {@code i} as its first
- * argument, and the given update as the second argument.
+ * applied with the current value of the element at index {@code i}
+ * as its first argument, and the given update as the second
+ * argument.
*
* @param i the index
* @param x the update value
@@ -306,23 +298,25 @@
*/
public final long getAndAccumulate(int i, long x,
LongBinaryOperator accumulatorFunction) {
- long offset = checkedByteOffset(i);
- long prev, next;
- do {
- prev = getRaw(offset);
- next = accumulatorFunction.applyAsLong(prev, x);
- } while (!compareAndSetRaw(offset, prev, next));
- return prev;
+ long prev = get(i), next = 0L;
+ for (boolean haveNext = false;;) {
+ if (!haveNext)
+ next = accumulatorFunction.applyAsLong(prev, x);
+ if (weakCompareAndSetVolatile(i, prev, next))
+ return prev;
+ haveNext = (prev == (prev = get(i)));
+ }
}
/**
* Atomically updates the element at index {@code i} with the
- * results of applying the given function to the current and
- * given values, returning the updated value. The function should
- * be side-effect-free, since it may be re-applied when attempted
+ * results of applying the given function to the current and given
+ * values, returning the updated value. The function should be
+ * side-effect-free, since it may be re-applied when attempted
* updates fail due to contention among threads. The function is
- * applied with the current value at index {@code i} as its first
- * argument, and the given update as the second argument.
+ * applied with the current value of the element at index {@code i}
+ * as its first argument, and the given update as the second
+ * argument.
*
* @param i the index
* @param x the update value
@@ -332,13 +326,14 @@
*/
public final long accumulateAndGet(int i, long x,
LongBinaryOperator accumulatorFunction) {
- long offset = checkedByteOffset(i);
- long prev, next;
- do {
- prev = getRaw(offset);
- next = accumulatorFunction.applyAsLong(prev, x);
- } while (!compareAndSetRaw(offset, prev, next));
- return next;
+ long prev = get(i), next = 0L;
+ for (boolean haveNext = false;;) {
+ if (!haveNext)
+ next = accumulatorFunction.applyAsLong(prev, x);
+ if (weakCompareAndSetVolatile(i, prev, next))
+ return next;
+ haveNext = (prev == (prev = get(i)));
+ }
}
/**
@@ -353,11 +348,189 @@
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) {
- b.append(getRaw(byteOffset(i)));
+ b.append(get(i));
if (i == iMax)
return b.append(']').toString();
b.append(',').append(' ');
}
}
+ // jdk9
+
+ /**
+ * Returns the current value of the element at index {@code i},
+ * with memory semantics of reading as if the variable was declared
+ * non-{@code volatile}.
+ *
+ * @param i the index
+ * @return the value
+ * @since 9
+ */
+ public final long getPlain(int i) {
+ return (long)AA.get(array, i);
+ }
+
+ /**
+ * Sets the element at index {@code i} to {@code newValue},
+ * with memory semantics of setting as if the variable was
+ * declared non-{@code volatile} and non-{@code final}.
+ *
+ * @param i the index
+ * @param newValue the new value
+ * @since 9
+ */
+ public final void setPlain(int i, long newValue) {
+ AA.set(array, i, newValue);
+ }
+
+ /**
+ * Returns the current value of the element at index {@code i},
+ * with memory effects as specified by {@link VarHandle#getOpaque}.
+ *
+ * @param i the index
+ * @return the value
+ * @since 9
+ */
+ public final long getOpaque(int i) {
+ return (long)AA.getOpaque(array, i);
+ }
+
+ /**
+ * Sets the element at index {@code i} to {@code newValue},
+ * with memory effects as specified by {@link VarHandle#setOpaque}.
+ *
+ * @param i the index
+ * @param newValue the new value
+ * @since 9
+ */
+ public final void setOpaque(int i, long newValue) {
+ AA.setOpaque(array, i, newValue);
+ }
+
+ /**
+ * Returns the current value of the element at index {@code i},
+ * with memory effects as specified by {@link VarHandle#getAcquire}.
+ *
+ * @param i the index
+ * @return the value
+ * @since 9
+ */
+ public final long getAcquire(int i) {
+ return (long)AA.getAcquire(array, i);
+ }
+
+ /**
+ * Sets the element at index {@code i} to {@code newValue},
+ * with memory effects as specified by {@link VarHandle#setRelease}.
+ *
+ * @param i the index
+ * @param newValue the new value
+ * @since 9
+ */
+ public final void setRelease(int i, long newValue) {
+ AA.setRelease(array, i, newValue);
+ }
+
+ /**
+ * Atomically sets the element at index {@code i} to {@code newValue}
+ * if the element's current value, referred to as the <em>witness
+ * value</em>, {@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 <em>witness
+ * value</em>, {@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 <em>witness
+ * value</em>, {@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);
+ }
+
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java Thu Jul 21 20:09:19 2016 -0700
@@ -42,6 +42,7 @@
import java.security.PrivilegedExceptionAction;
import java.util.function.LongBinaryOperator;
import java.util.function.LongUnaryOperator;
+import jdk.internal.misc.Unsafe;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
@@ -153,8 +154,8 @@
public abstract void lazySet(T obj, long newValue);
/**
- * Gets the current value held in the field of the given object managed
- * by this updater.
+ * Returns the current value held in the field of the given object
+ * managed by this updater.
*
* @param obj An object whose field to get
* @return the current value
@@ -366,7 +367,7 @@
}
private static final class CASUpdater<T> extends AtomicLongFieldUpdater<T> {
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
+ private static final Unsafe U = Unsafe.getUnsafe();
private final long offset;
/**
* if field is protected, the subclass constructing updater, else
@@ -497,7 +498,7 @@
}
private static final class LockedUpdater<T> extends AtomicLongFieldUpdater<T> {
- 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
--- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicMarkableReference.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,6 +35,9 @@
package java.util.concurrent.atomic;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
/**
* An {@code AtomicMarkableReference} maintains an object reference
* along with a mark bit, that can be updated atomically.
@@ -188,20 +191,19 @@
casPair(current, Pair.of(expectedReference, newMark)));
}
- // Unsafe mechanics
-
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long PAIR;
+ // VarHandle mechanics
+ private static final VarHandle PAIR;
static {
try {
- PAIR = U.objectFieldOffset
- (AtomicMarkableReference.class.getDeclaredField("pair"));
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ PAIR = l.findVarHandle(AtomicMarkableReference.class, "pair",
+ Pair.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
private boolean casPair(Pair<V> cmp, Pair<V> val) {
- return U.compareAndSwapObject(this, PAIR, cmp, val);
+ return PAIR.compareAndSet(this, cmp, val);
}
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReference.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReference.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,27 +35,26 @@
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 <V> The type of object referred to by this reference
*/
public class AtomicReference<V> 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);
}
@@ -79,7 +78,8 @@
}
/**
- * Gets the current value.
+ * Returns the current value,
+ * with memory effects as specified by {@link VarHandle#getVolatile}.
*
* @return the current value
*/
@@ -88,7 +88,8 @@
}
/**
- * Sets to the given value.
+ * Sets the value to {@code newValue},
+ * with memory effects as specified by {@link VarHandle#setVolatile}.
*
* @param newValue the new value
*/
@@ -97,52 +98,53 @@
}
/**
- * Eventually sets to the given value.
+ * Sets the value to {@code newValue},
+ * with memory effects as specified by {@link VarHandle#setRelease}.
*
* @param newValue the new value
* @since 1.6
*/
public final void lazySet(V newValue) {
- U.putObjectRelease(this, VALUE, newValue);
- }
-
- /**
- * Atomically sets the value to the given updated value
- * if the current value {@code ==} the expected value.
- * @param expect the expected value
- * @param update the new value
- * @return {@code true} if successful. False return indicates that
- * the actual value was not equal to the expected value.
- */
- public final boolean compareAndSet(V expect, V update) {
- return U.compareAndSwapObject(this, VALUE, expect, update);
+ VALUE.setRelease(this, newValue);
}
/**
- * Atomically sets the value to the given updated value
- * if the current value {@code ==} the expected value.
- *
- * <p><a href="package-summary.html#weakCompareAndSet">May fail
- * spuriously and does not provide ordering guarantees</a>, so is
- * only rarely an appropriate alternative to {@code compareAndSet}.
+ * Atomically sets the value to {@code newValue}
+ * if the current value {@code == expectedValue},
+ * with memory effects as specified by {@link VarHandle#compareAndSet}.
*
- * @param expect the expected value
- * @param update the new value
- * @return {@code true} if successful
+ * @param expectedValue the expected value
+ * @param newValue the new value
+ * @return {@code true} if successful. False return indicates that
+ * the actual value was not equal to the expected value.
*/
- public final boolean weakCompareAndSet(V expect, V update) {
- return U.compareAndSwapObject(this, VALUE, expect, update);
+ public final boolean compareAndSet(V expectedValue, V newValue) {
+ return VALUE.compareAndSet(this, expectedValue, newValue);
}
/**
- * Atomically sets to the given value and returns the old value.
+ * Possibly atomically sets the value to {@code newValue}
+ * if the current value {@code == expectedValue},
+ * with memory effects as specified by {@link VarHandle#weakCompareAndSet}.
+ *
+ * @param expectedValue the expected value
+ * @param newValue the new value
+ * @return {@code true} if successful
+ */
+ public final boolean weakCompareAndSet(V expectedValue, V newValue) {
+ return VALUE.weakCompareAndSet(this, expectedValue, newValue);
+ }
+
+ /**
+ * Atomically sets the value to {@code newValue} and returns the old value,
+ * with memory effects as specified by {@link VarHandle#getAndSet}.
*
* @param newValue the new value
* @return the previous value
*/
@SuppressWarnings("unchecked")
public final V getAndSet(V newValue) {
- return (V)U.getAndSetObject(this, VALUE, newValue);
+ return (V)VALUE.getAndSet(this, newValue);
}
/**
@@ -156,12 +158,14 @@
* @since 1.8
*/
public final V getAndUpdate(UnaryOperator<V> 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 +179,14 @@
* @since 1.8
*/
public final V updateAndGet(UnaryOperator<V> 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 +205,14 @@
*/
public final V getAndAccumulate(V x,
BinaryOperator<V> 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 +231,14 @@
*/
public final V accumulateAndGet(V x,
BinaryOperator<V> 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 +249,166 @@
return String.valueOf(get());
}
+ // jdk9
+
+ /**
+ * Returns the current value, with memory semantics of reading as
+ * if the variable was declared non-{@code volatile}.
+ *
+ * @return the value
+ * @since 9
+ */
+ public final V getPlain() {
+ return (V)VALUE.get(this);
+ }
+
+ /**
+ * Sets the value to {@code newValue}, with memory semantics
+ * of setting as if the variable was declared non-{@code volatile}
+ * and non-{@code final}.
+ *
+ * @param newValue the new value
+ * @since 9
+ */
+ public final void setPlain(V newValue) {
+ VALUE.set(this, newValue);
+ }
+
+ /**
+ * Returns the current value,
+ * with memory effects as specified by {@link VarHandle#getOpaque}.
+ *
+ * @return the value
+ * @since 9
+ */
+ public final V getOpaque() {
+ return (V)VALUE.getOpaque(this);
+ }
+
+ /**
+ * Sets the value to {@code newValue},
+ * with memory effects as specified by {@link VarHandle#setOpaque}.
+ *
+ * @param newValue the new value
+ * @since 9
+ */
+ public final void setOpaque(V newValue) {
+ VALUE.setOpaque(this, newValue);
+ }
+
+ /**
+ * Returns the current value,
+ * with memory effects as specified by {@link VarHandle#getAcquire}.
+ *
+ * @return the value
+ * @since 9
+ */
+ public final V getAcquire() {
+ return (V)VALUE.getAcquire(this);
+ }
+
+ /**
+ * Sets the value to {@code newValue},
+ * with memory effects as specified by {@link VarHandle#setRelease}.
+ *
+ * @param newValue the new value
+ * @since 9
+ */
+ public final void setRelease(V newValue) {
+ VALUE.setRelease(this, newValue);
+ }
+
+ /**
+ * Atomically sets the value to {@code newValue} if the current value,
+ * referred to as the <em>witness value</em>, {@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 <em>witness value</em>, {@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 <em>witness value</em>, {@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);
+ }
+
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceArray.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,54 +35,28 @@
package java.util.concurrent.atomic;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.lang.reflect.Array;
+import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.function.BinaryOperator;
import java.util.function.UnaryOperator;
/**
* An array of object references in which elements may be updated
- * atomically. See the {@link java.util.concurrent.atomic} package
- * specification for description of the properties of atomic
- * variables.
+ * atomically. See the {@link VarHandle} specification for
+ * descriptions of the properties of atomic accesses.
* @since 1.5
* @author Doug Lea
* @param <E> The base class of elements held in this array
*/
public class AtomicReferenceArray<E> implements java.io.Serializable {
private static final long serialVersionUID = -6209656149925076980L;
-
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long ARRAY;
- private static final int ABASE;
- private static final int ASHIFT;
+ private static final VarHandle AA
+ = MethodHandles.arrayElementVarHandle(Object[].class);
private final Object[] array; // must have exact type Object[]
- static {
- try {
- ARRAY = U.objectFieldOffset
- (AtomicReferenceArray.class.getDeclaredField("array"));
- ABASE = U.arrayBaseOffset(Object[].class);
- int scale = U.arrayIndexScale(Object[].class);
- if ((scale & (scale - 1)) != 0)
- throw new Error("array index scale not a power of two");
- ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
- } catch (ReflectiveOperationException e) {
- throw new Error(e);
- }
- }
-
- private long checkedByteOffset(int i) {
- if (i < 0 || i >= array.length)
- throw new IndexOutOfBoundsException("index " + i);
-
- return byteOffset(i);
- }
-
- private static long byteOffset(int i) {
- return ((long) i << ASHIFT) + ABASE;
- }
-
/**
* Creates a new AtomicReferenceArray of the given length, with all
* elements initially null.
@@ -115,44 +89,44 @@
}
/**
- * Gets the current value at position {@code i}.
+ * Returns the current value of the element at index {@code i},
+ * with memory effects as specified by {@link VarHandle#getVolatile}.
*
* @param i the index
* @return the current value
*/
+ @SuppressWarnings("unchecked")
public final E get(int i) {
- return getRaw(checkedByteOffset(i));
- }
-
- @SuppressWarnings("unchecked")
- private E getRaw(long offset) {
- return (E) U.getObjectVolatile(array, offset);
+ return (E)AA.getVolatile(array, i);
}
/**
- * Sets the element at position {@code i} to the given value.
+ * Sets the element at index {@code i} to {@code newValue},
+ * with memory effects as specified by {@link VarHandle#setVolatile}.
*
* @param i the index
* @param newValue the new value
*/
public final void set(int i, E newValue) {
- U.putObjectVolatile(array, checkedByteOffset(i), newValue);
+ AA.setVolatile(array, i, newValue);
}
/**
- * Eventually sets the element at position {@code i} to the given value.
+ * Sets the element at index {@code i} to {@code newValue},
+ * with memory effects as specified by {@link VarHandle#setRelease}.
*
* @param i the index
* @param newValue the new value
* @since 1.6
*/
public final void lazySet(int i, E newValue) {
- U.putObjectRelease(array, checkedByteOffset(i), newValue);
+ AA.setRelease(array, i, newValue);
}
/**
- * Atomically sets the element at position {@code i} to the given
- * value and returns the old value.
+ * Atomically sets the element at index {@code i} to {@code
+ * newValue} and returns the old value,
+ * with memory effects as specified by {@link VarHandle#getAndSet}.
*
* @param i the index
* @param newValue the new value
@@ -160,42 +134,36 @@
*/
@SuppressWarnings("unchecked")
public final E getAndSet(int i, E newValue) {
- return (E)U.getAndSetObject(array, checkedByteOffset(i), newValue);
+ return (E)AA.getAndSet(array, i, newValue);
}
/**
- * Atomically sets the element at position {@code i} to the given
- * updated value if the current value {@code ==} the expected value.
+ * Atomically sets the element at index {@code i} to {@code newValue}
+ * if the element's current value {@code == expectedValue},
+ * with memory effects as specified by {@link VarHandle#compareAndSet}.
*
* @param i the index
- * @param expect the expected value
- * @param update the new value
+ * @param expectedValue the expected value
+ * @param newValue the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
- public final boolean compareAndSet(int i, E expect, E update) {
- return compareAndSetRaw(checkedByteOffset(i), expect, update);
- }
-
- private boolean compareAndSetRaw(long offset, E expect, E update) {
- return U.compareAndSwapObject(array, offset, expect, update);
+ public final boolean compareAndSet(int i, E expectedValue, E newValue) {
+ return AA.compareAndSet(array, i, expectedValue, newValue);
}
/**
- * Atomically sets the element at position {@code i} to the given
- * updated value if the current value {@code ==} the expected value.
- *
- * <p><a href="package-summary.html#weakCompareAndSet">May fail
- * spuriously and does not provide ordering guarantees</a>, so is
- * only rarely an appropriate alternative to {@code compareAndSet}.
+ * Possibly atomically sets the element at index {@code i} to
+ * {@code newValue} if the element's current value {@code == expectedValue},
+ * with memory effects as specified by {@link VarHandle#weakCompareAndSet}.
*
* @param i the index
- * @param expect the expected value
- * @param update the new value
+ * @param expectedValue the expected value
+ * @param newValue the new value
* @return {@code true} if successful
*/
- public final boolean weakCompareAndSet(int i, E expect, E update) {
- return compareAndSet(i, expect, update);
+ public final boolean weakCompareAndSet(int i, E expectedValue, E newValue) {
+ return AA.weakCompareAndSet(array, i, expectedValue, newValue);
}
/**
@@ -210,13 +178,14 @@
* @since 1.8
*/
public final E getAndUpdate(int i, UnaryOperator<E> updateFunction) {
- long offset = checkedByteOffset(i);
- E prev, next;
- do {
- prev = getRaw(offset);
- next = updateFunction.apply(prev);
- } while (!compareAndSetRaw(offset, prev, next));
- return prev;
+ E prev = get(i), next = null;
+ for (boolean haveNext = false;;) {
+ if (!haveNext)
+ next = updateFunction.apply(prev);
+ if (weakCompareAndSetVolatile(i, prev, next))
+ return prev;
+ haveNext = (prev == (prev = get(i)));
+ }
}
/**
@@ -231,23 +200,25 @@
* @since 1.8
*/
public final E updateAndGet(int i, UnaryOperator<E> updateFunction) {
- long offset = checkedByteOffset(i);
- E prev, next;
- do {
- prev = getRaw(offset);
- next = updateFunction.apply(prev);
- } while (!compareAndSetRaw(offset, prev, next));
- return next;
+ E prev = get(i), next = null;
+ for (boolean haveNext = false;;) {
+ if (!haveNext)
+ next = updateFunction.apply(prev);
+ if (weakCompareAndSetVolatile(i, prev, next))
+ return next;
+ haveNext = (prev == (prev = get(i)));
+ }
}
/**
* Atomically updates the element at index {@code i} with the
- * results of applying the given function to the current and
- * given values, returning the previous value. The function should
- * be side-effect-free, since it may be re-applied when attempted
+ * results of applying the given function to the current and given
+ * values, returning the previous value. The function should be
+ * side-effect-free, since it may be re-applied when attempted
* updates fail due to contention among threads. The function is
- * applied with the current value at index {@code i} as its first
- * argument, and the given update as the second argument.
+ * applied with the current value of the element at index {@code i}
+ * as its first argument, and the given update as the second
+ * argument.
*
* @param i the index
* @param x the update value
@@ -257,23 +228,25 @@
*/
public final E getAndAccumulate(int i, E x,
BinaryOperator<E> accumulatorFunction) {
- long offset = checkedByteOffset(i);
- E prev, next;
- do {
- prev = getRaw(offset);
- next = accumulatorFunction.apply(prev, x);
- } while (!compareAndSetRaw(offset, prev, next));
- return prev;
+ E prev = get(i), next = null;
+ for (boolean haveNext = false;;) {
+ if (!haveNext)
+ next = accumulatorFunction.apply(prev, x);
+ if (weakCompareAndSetVolatile(i, prev, next))
+ return prev;
+ haveNext = (prev == (prev = get(i)));
+ }
}
/**
* Atomically updates the element at index {@code i} with the
- * results of applying the given function to the current and
- * given values, returning the updated value. The function should
- * be side-effect-free, since it may be re-applied when attempted
+ * results of applying the given function to the current and given
+ * values, returning the updated value. The function should be
+ * side-effect-free, since it may be re-applied when attempted
* updates fail due to contention among threads. The function is
- * applied with the current value at index {@code i} as its first
- * argument, and the given update as the second argument.
+ * applied with the current value of the element at index {@code i}
+ * as its first argument, and the given update as the second
+ * argument.
*
* @param i the index
* @param x the update value
@@ -283,13 +256,14 @@
*/
public final E accumulateAndGet(int i, E x,
BinaryOperator<E> accumulatorFunction) {
- long offset = checkedByteOffset(i);
- E prev, next;
- do {
- prev = getRaw(offset);
- next = accumulatorFunction.apply(prev, x);
- } while (!compareAndSetRaw(offset, prev, next));
- return next;
+ E prev = get(i), next = null;
+ for (boolean haveNext = false;;) {
+ if (!haveNext)
+ next = accumulatorFunction.apply(prev, x);
+ if (weakCompareAndSetVolatile(i, prev, next))
+ return next;
+ haveNext = (prev == (prev = get(i)));
+ }
}
/**
@@ -304,7 +278,7 @@
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) {
- b.append(getRaw(byteOffset(i)));
+ b.append(get(i));
if (i == iMax)
return b.append(']').toString();
b.append(',').append(' ');
@@ -326,7 +300,199 @@
throw new java.io.InvalidObjectException("Not array type");
if (a.getClass() != Object[].class)
a = Arrays.copyOf((Object[])a, Array.getLength(a), Object[].class);
- U.putObjectVolatile(this, ARRAY, a);
+ Field arrayField = java.security.AccessController.doPrivileged(
+ (java.security.PrivilegedAction<Field>) () -> {
+ 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 <em>witness
+ * value</em>, {@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 <em>witness
+ * value</em>, {@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 <em>witness
+ * value</em>, {@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);
}
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java Thu Jul 21 20:09:19 2016 -0700
@@ -42,6 +42,7 @@
import java.security.PrivilegedExceptionAction;
import java.util.function.BinaryOperator;
import java.util.function.UnaryOperator;
+import jdk.internal.misc.Unsafe;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
@@ -168,8 +169,8 @@
public abstract void lazySet(T obj, V newValue);
/**
- * Gets the current value held in the field of the given object managed
- * by this updater.
+ * Returns the current value held in the field of the given object
+ * managed by this updater.
*
* @param obj An object whose field to get
* @return the current value
@@ -284,7 +285,7 @@
private static final class AtomicReferenceFieldUpdaterImpl<T,V>
extends AtomicReferenceFieldUpdater<T,V> {
- 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
--- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/AtomicStampedReference.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,6 +35,9 @@
package java.util.concurrent.atomic;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+
/**
* An {@code AtomicStampedReference} maintains an object reference
* along with an integer "stamp", that can be updated atomically.
@@ -188,20 +191,19 @@
casPair(current, Pair.of(expectedReference, newStamp)));
}
- // Unsafe mechanics
-
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long PAIR;
+ // VarHandle mechanics
+ private static final VarHandle PAIR;
static {
try {
- PAIR = U.objectFieldOffset
- (AtomicStampedReference.class.getDeclaredField("pair"));
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ PAIR = l.findVarHandle(AtomicStampedReference.class, "pair",
+ Pair.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
private boolean casPair(Pair<V> cmp, Pair<V> val) {
- return U.compareAndSwapObject(this, PAIR, cmp, val);
+ return PAIR.compareAndSet(this, cmp, val);
}
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/LongAccumulator.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/LongAccumulator.java Thu Jul 21 20:09:19 2016 -0700
@@ -68,7 +68,7 @@
* <p>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)}.
*
* <p>This class extends {@link Number}, but does <em>not</em> define
* methods such as {@code equals}, {@code hashCode} and {@code
--- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/Striped64.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,10 +35,13 @@
package java.util.concurrent.atomic;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.util.Arrays;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.DoubleBinaryOperator;
import java.util.function.LongBinaryOperator;
+import jdk.internal.misc.Unsafe;
/**
* A package-local class holding common representation and mechanics
@@ -123,22 +126,21 @@
volatile long value;
Cell(long x) { value = x; }
final boolean cas(long cmp, long val) {
- return U.compareAndSwapLong(this, VALUE, cmp, val);
+ return VALUE.compareAndSet(this, cmp, val);
}
final void reset() {
- U.putLongVolatile(this, VALUE, 0L);
+ VALUE.setVolatile(this, 0L);
}
final void reset(long identity) {
- U.putLongVolatile(this, VALUE, identity);
+ VALUE.setVolatile(this, identity);
}
- // Unsafe mechanics
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long VALUE;
+ // VarHandle mechanics
+ private static final VarHandle VALUE;
static {
try {
- VALUE = U.objectFieldOffset
- (Cell.class.getDeclaredField("value"));
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ VALUE = l.findVarHandle(Cell.class, "value", long.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
@@ -174,14 +176,14 @@
* CASes the base field.
*/
final boolean casBase(long cmp, long val) {
- return U.compareAndSwapLong(this, BASE, cmp, val);
+ return BASE.compareAndSet(this, cmp, val);
}
/**
* CASes the cellsBusy field from 0 to 1 to acquire lock.
*/
final boolean casCellsBusy() {
- return U.compareAndSwapInt(this, CELLSBUSY, 0, 1);
+ return CELLSBUSY.compareAndSet(this, 0, 1);
}
/**
@@ -371,18 +373,16 @@
}
}
- // Unsafe mechanics
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long BASE;
- private static final long CELLSBUSY;
+ // Unsafe and VarHandle mechanics
+ private static final Unsafe U = Unsafe.getUnsafe();
+ private static final VarHandle BASE;
+ private static final VarHandle CELLSBUSY;
private static final long PROBE;
static {
try {
- BASE = U.objectFieldOffset
- (Striped64.class.getDeclaredField("base"));
- CELLSBUSY = U.objectFieldOffset
- (Striped64.class.getDeclaredField("cellsBusy"));
-
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ BASE = l.findVarHandle(Striped64.class, "base", long.class);
+ CELLSBUSY = l.findVarHandle(Striped64.class, "cellsBusy", int.class);
PROBE = U.objectFieldOffset
(Thread.class.getDeclaredField("threadLocalRandomProbe"));
} catch (ReflectiveOperationException e) {
--- a/jdk/src/java.base/share/classes/java/util/concurrent/atomic/package-info.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/atomic/package-info.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,26 +35,10 @@
/**
* A small toolkit of classes that support lock-free thread-safe
- * programming on single variables. In essence, the classes in this
- * package extend the notion of {@code volatile} values, fields, and
- * array elements to those that also provide an atomic conditional update
- * operation of the form:
- *
- * <pre> {@code boolean compareAndSet(expectedValue, updateValue);}</pre>
- *
- * <p>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.
- *
- * <p>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.
*
* <p>Instances of classes
* {@link java.util.concurrent.atomic.AtomicBoolean},
@@ -92,45 +76,26 @@
* return prev; // return next; for transformAndGet
* }}</pre>
*
- * <p>The memory effects for accesses and updates of atomics generally
- * follow the rules for volatiles, as stated in
- * <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4">
- * Chapter 17 of
- * <cite>The Java™ Language Specification</cite></a>:
- *
- * <ul>
- *
- * <li>{@code get} has the memory effects of reading a
- * {@code volatile} variable.
- *
- * <li>{@code set} has the memory effects of writing (assigning) a
- * {@code volatile} variable.
+ * <p>These classes are not general purpose replacements for {@code
+ * java.lang.Integer} and related classes. They do <em>not</em>
+ * 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.
*
- * <li>{@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.
+ * <p>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.
*
- * <li>{@code weakCompareAndSet} atomically reads and conditionally
- * writes a variable but does <em>not</em>
- * 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}.
- *
- * <li>{@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.
- * </ul>
- *
- * <p>In addition to classes representing single values, this package
- * contains <em>Updater</em> classes that can be used to obtain
- * {@code compareAndSet} operations on any selected {@code volatile}
- * field of any selected class.
- *
+ * <p>In addition to classes representing single values and arrays,
+ * this package contains <em>Updater</em> 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.
*
- * <p>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.
- *
- * <p id="weakCompareAndSet">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} <em>spuriously</em> (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 <em>other</em> variables that
- * occurred before the {@code weakCompareAndSet}. This may be
- * acceptable when, for example, updating performance statistics, but
- * rarely otherwise.
- *
* <p>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.
*
- * <p>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 <em>single</em> variable.
- *
- * <p>Atomic classes are not general purpose replacements for
- * {@code java.lang.Integer} and related classes. They do <em>not</em>
- * 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;
--- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,6 +35,8 @@
package java.util.concurrent.locks;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
@@ -113,7 +115,7 @@
protected final void setState(long newState) {
// Use putLongVolatile instead of ordinary volatile store when
// using compareAndSwapLong, for sake of some 32bit systems.
- U.putLongVolatile(this, STATE, newState);
+ STATE.setVolatile(this, newState);
}
/**
@@ -128,7 +130,7 @@
* value was not equal to the expected value.
*/
protected final boolean compareAndSetState(long expect, long update) {
- return U.compareAndSwapLong(this, STATE, expect, update);
+ return STATE.compareAndSet(this, expect, update);
}
// Queuing utilities
@@ -149,7 +151,7 @@
for (;;) {
Node oldTail = tail;
if (oldTail != null) {
- U.putObject(node, Node.PREV, oldTail);
+ node.setPrevRelaxed(oldTail);
if (compareAndSetTail(oldTail, node)) {
oldTail.next = node;
return oldTail;
@@ -172,7 +174,7 @@
for (;;) {
Node oldTail = tail;
if (oldTail != null) {
- U.putObject(node, Node.PREV, oldTail);
+ node.setPrevRelaxed(oldTail);
if (compareAndSetTail(oldTail, node)) {
oldTail.next = node;
return node;
@@ -1810,28 +1812,17 @@
}
}
- /**
- * Setup to support compareAndSet. We need to natively implement
- * this here: For the sake of permitting future enhancements, we
- * cannot explicitly subclass AtomicLong, which would be
- * efficient and useful otherwise. So, as the lesser of evils, we
- * natively implement using hotspot intrinsics API. And while we
- * are at it, we do the same for other CASable fields (which could
- * otherwise be done with atomic field updaters).
- */
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long STATE;
- private static final long HEAD;
- private static final long TAIL;
+ // VarHandle mechanics
+ private static final VarHandle STATE;
+ private static final VarHandle HEAD;
+ private static final VarHandle TAIL;
static {
try {
- STATE = U.objectFieldOffset
- (AbstractQueuedLongSynchronizer.class.getDeclaredField("state"));
- HEAD = U.objectFieldOffset
- (AbstractQueuedLongSynchronizer.class.getDeclaredField("head"));
- TAIL = U.objectFieldOffset
- (AbstractQueuedLongSynchronizer.class.getDeclaredField("tail"));
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ STATE = l.findVarHandle(AbstractQueuedLongSynchronizer.class, "state", long.class);
+ HEAD = l.findVarHandle(AbstractQueuedLongSynchronizer.class, "head", Node.class);
+ TAIL = l.findVarHandle(AbstractQueuedLongSynchronizer.class, "tail", Node.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
@@ -1846,7 +1837,7 @@
*/
private final void initializeSyncQueue() {
Node h;
- if (U.compareAndSwapObject(this, HEAD, null, (h = new Node())))
+ if (HEAD.compareAndSet(this, null, (h = new Node())))
tail = h;
}
@@ -1854,6 +1845,6 @@
* CASes tail field.
*/
private final boolean compareAndSetTail(Node expect, Node update) {
- return U.compareAndSwapObject(this, TAIL, expect, update);
+ return TAIL.compareAndSet(this, expect, update);
}
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,11 +35,12 @@
package java.util.concurrent.locks;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.concurrent.TimeUnit;
-import jdk.internal.vm.annotation.ReservedStackAccess;
/**
* Provides a framework for implementing blocking locks and related
@@ -506,40 +507,41 @@
/** Constructor used by addWaiter. */
Node(Node nextWaiter) {
this.nextWaiter = nextWaiter;
- U.putObject(this, THREAD, Thread.currentThread());
+ THREAD.set(this, Thread.currentThread());
}
/** Constructor used by addConditionWaiter. */
Node(int waitStatus) {
- U.putInt(this, WAITSTATUS, waitStatus);
- U.putObject(this, THREAD, Thread.currentThread());
+ WAITSTATUS.set(this, waitStatus);
+ THREAD.set(this, Thread.currentThread());
}
/** CASes waitStatus field. */
final boolean compareAndSetWaitStatus(int expect, int update) {
- return U.compareAndSwapInt(this, WAITSTATUS, expect, update);
+ return WAITSTATUS.compareAndSet(this, expect, update);
}
/** CASes next field. */
final boolean compareAndSetNext(Node expect, Node update) {
- return U.compareAndSwapObject(this, NEXT, expect, update);
+ return NEXT.compareAndSet(this, expect, update);
+ }
+
+ final void setPrevRelaxed(Node p) {
+ PREV.set(this, p);
}
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long NEXT;
- static final long PREV;
- private static final long THREAD;
- private static final long WAITSTATUS;
+ // VarHandle mechanics
+ private static final VarHandle NEXT;
+ private static final VarHandle PREV;
+ private static final VarHandle THREAD;
+ private static final VarHandle WAITSTATUS;
static {
try {
- NEXT = U.objectFieldOffset
- (Node.class.getDeclaredField("next"));
- PREV = U.objectFieldOffset
- (Node.class.getDeclaredField("prev"));
- THREAD = U.objectFieldOffset
- (Node.class.getDeclaredField("thread"));
- WAITSTATUS = U.objectFieldOffset
- (Node.class.getDeclaredField("waitStatus"));
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ NEXT = l.findVarHandle(Node.class, "next", Node.class);
+ PREV = l.findVarHandle(Node.class, "prev", Node.class);
+ THREAD = l.findVarHandle(Node.class, "thread", Thread.class);
+ WAITSTATUS = l.findVarHandle(Node.class, "waitStatus", int.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
@@ -595,7 +597,7 @@
* value was not equal to the expected value.
*/
protected final boolean compareAndSetState(int expect, int update) {
- return U.compareAndSwapInt(this, STATE, expect, update);
+ return STATE.compareAndSet(this, expect, update);
}
// Queuing utilities
@@ -616,7 +618,7 @@
for (;;) {
Node oldTail = tail;
if (oldTail != null) {
- U.putObject(node, Node.PREV, oldTail);
+ node.setPrevRelaxed(oldTail);
if (compareAndSetTail(oldTail, node)) {
oldTail.next = node;
return oldTail;
@@ -639,7 +641,7 @@
for (;;) {
Node oldTail = tail;
if (oldTail != null) {
- U.putObject(node, Node.PREV, oldTail);
+ node.setPrevRelaxed(oldTail);
if (compareAndSetTail(oldTail, node)) {
oldTail.next = node;
return node;
@@ -887,7 +889,6 @@
* @param arg the acquire argument
* @return {@code true} if interrupted while waiting
*/
- @ReservedStackAccess
final boolean acquireQueued(final Node node, int arg) {
try {
boolean interrupted = false;
@@ -1220,7 +1221,6 @@
* {@link #tryAcquire} but is otherwise uninterpreted and
* can represent anything you like.
*/
- @ReservedStackAccess
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
@@ -1284,7 +1284,6 @@
* can represent anything you like.
* @return the value returned from {@link #tryRelease}
*/
- @ReservedStackAccess
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
@@ -1365,7 +1364,6 @@
* and can represent anything you like.
* @return the value returned from {@link #tryReleaseShared}
*/
- @ReservedStackAccess
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
@@ -2279,28 +2277,17 @@
}
}
- /**
- * Setup to support compareAndSet. We need to natively implement
- * this here: For the sake of permitting future enhancements, we
- * cannot explicitly subclass AtomicInteger, which would be
- * efficient and useful otherwise. So, as the lesser of evils, we
- * natively implement using hotspot intrinsics API. And while we
- * are at it, we do the same for other CASable fields (which could
- * otherwise be done with atomic field updaters).
- */
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long STATE;
- private static final long HEAD;
- private static final long TAIL;
+ // VarHandle mechanics
+ private static final VarHandle STATE;
+ private static final VarHandle HEAD;
+ private static final VarHandle TAIL;
static {
try {
- STATE = U.objectFieldOffset
- (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
- HEAD = U.objectFieldOffset
- (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
- TAIL = U.objectFieldOffset
- (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ STATE = l.findVarHandle(AbstractQueuedSynchronizer.class, "state", int.class);
+ HEAD = l.findVarHandle(AbstractQueuedSynchronizer.class, "head", Node.class);
+ TAIL = l.findVarHandle(AbstractQueuedSynchronizer.class, "tail", Node.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
@@ -2315,7 +2302,7 @@
*/
private final void initializeSyncQueue() {
Node h;
- if (U.compareAndSwapObject(this, HEAD, null, (h = new Node())))
+ if (HEAD.compareAndSet(this, null, (h = new Node())))
tail = h;
}
@@ -2323,6 +2310,6 @@
* CASes tail field.
*/
private final boolean compareAndSetTail(Node expect, Node update) {
- return U.compareAndSwapObject(this, TAIL, expect, update);
+ return TAIL.compareAndSet(this, expect, update);
}
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/Condition.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/Condition.java Thu Jul 21 20:09:19 2016 -0700
@@ -396,7 +396,6 @@
* re-acquire the lock associated with this condition. When the
* thread returns it is <em>guaranteed</em> to hold this lock.
*
- *
* <p>If the current thread:
* <ul>
* <li>has its interrupted status set on entry to this method; or
@@ -408,7 +407,6 @@
* case, whether or not the test for interruption occurs before the lock
* is released.
*
- *
* <p>The return value indicates whether the deadline has elapsed,
* which can be used as follows:
* <pre> {@code
--- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,6 +35,8 @@
package java.util.concurrent.locks;
+import jdk.internal.misc.Unsafe;
+
/**
* Basic thread blocking primitives for creating locks and other
* synchronization classes.
@@ -405,16 +407,30 @@
return r;
}
+ /**
+ * Returns the thread id for the given thread. We must access
+ * this directly rather than via method Thread.getId() because
+ * getId() is not final, and has been known to be overridden in
+ * ways that do not preserve unique mappings.
+ */
+ static final long getThreadId(Thread thread) {
+ return U.getLongVolatile(thread, TID);
+ }
+
// Hotspot implementation via intrinsics API
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
+ private static final Unsafe U = Unsafe.getUnsafe();
private static final long PARKBLOCKER;
private static final long SECONDARY;
+ private static final long TID;
static {
try {
PARKBLOCKER = U.objectFieldOffset
(Thread.class.getDeclaredField("parkBlocker"));
SECONDARY = U.objectFieldOffset
(Thread.class.getDeclaredField("threadLocalRandomSecondarySeed"));
+ TID = U.objectFieldOffset
+ (Thread.class.getDeclaredField("tid"));
+
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReadWriteLock.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReadWriteLock.java Thu Jul 21 20:09:19 2016 -0700
@@ -79,7 +79,6 @@
* and measurement will establish whether the use of a read-write lock is
* suitable for your application.
*
- *
* <p>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.
--- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantLock.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantLock.java Thu Jul 21 20:09:19 2016 -0700
@@ -119,12 +119,6 @@
private static final long serialVersionUID = -5179523762034025860L;
/**
- * Performs {@link Lock#lock}. The main reason for subclassing
- * is to allow fast path for nonfair version.
- */
- abstract void lock();
-
- /**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
@@ -201,19 +195,6 @@
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
-
- /**
- * Performs lock. Try immediate barge, backing up to normal
- * acquire on failure.
- */
- @ReservedStackAccess
- final void lock() {
- if (compareAndSetState(0, 1))
- setExclusiveOwnerThread(Thread.currentThread());
- else
- acquire(1);
- }
-
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
@@ -224,11 +205,6 @@
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
-
- final void lock() {
- acquire(1);
- }
-
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
@@ -288,7 +264,7 @@
* at which time the lock hold count is set to one.
*/
public void lock() {
- sync.lock();
+ sync.acquire(1);
}
/**
--- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java Thu Jul 21 20:09:19 2016 -0700
@@ -37,6 +37,7 @@
import java.util.Collection;
import java.util.concurrent.TimeUnit;
+import jdk.internal.vm.annotation.ReservedStackAccess;
/**
* An implementation of {@link ReadWriteLock} supporting similar
@@ -278,7 +279,7 @@
static final class HoldCounter {
int count; // initially 0
// Use id, not reference, to avoid garbage retention
- final long tid = getThreadId(Thread.currentThread());
+ final long tid = LockSupport.getThreadId(Thread.currentThread());
}
/**
@@ -367,7 +368,7 @@
* both read and write holds that are all released during a
* condition wait and re-established in tryAcquire.
*/
-
+ @ReservedStackAccess
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
@@ -379,6 +380,7 @@
return free;
}
+ @ReservedStackAccess
protected final boolean tryAcquire(int acquires) {
/*
* Walkthrough:
@@ -411,6 +413,7 @@
return true;
}
+ @ReservedStackAccess
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
if (firstReader == current) {
@@ -421,7 +424,8 @@
firstReaderHoldCount--;
} else {
HoldCounter rh = cachedHoldCounter;
- if (rh == null || rh.tid != getThreadId(current))
+ if (rh == null ||
+ rh.tid != LockSupport.getThreadId(current))
rh = readHolds.get();
int count = rh.count;
if (count <= 1) {
@@ -447,6 +451,7 @@
"attempt to unlock read lock, not locked by current thread");
}
+ @ReservedStackAccess
protected final int tryAcquireShared(int unused) {
/*
* Walkthrough:
@@ -479,7 +484,8 @@
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;
- if (rh == null || rh.tid != getThreadId(current))
+ if (rh == null ||
+ rh.tid != LockSupport.getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
@@ -516,7 +522,8 @@
} else {
if (rh == null) {
rh = cachedHoldCounter;
- if (rh == null || rh.tid != getThreadId(current)) {
+ if (rh == null ||
+ rh.tid != LockSupport.getThreadId(current)) {
rh = readHolds.get();
if (rh.count == 0)
readHolds.remove();
@@ -537,7 +544,8 @@
} else {
if (rh == null)
rh = cachedHoldCounter;
- if (rh == null || rh.tid != getThreadId(current))
+ if (rh == null ||
+ rh.tid != LockSupport.getThreadId(current))
rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
@@ -554,6 +562,7 @@
* This is identical in effect to tryAcquire except for lack
* of calls to writerShouldBlock.
*/
+ @ReservedStackAccess
final boolean tryWriteLock() {
Thread current = Thread.currentThread();
int c = getState();
@@ -575,6 +584,7 @@
* This is identical in effect to tryAcquireShared except for
* lack of calls to readerShouldBlock.
*/
+ @ReservedStackAccess
final boolean tryReadLock() {
Thread current = Thread.currentThread();
for (;;) {
@@ -593,7 +603,8 @@
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;
- if (rh == null || rh.tid != getThreadId(current))
+ if (rh == null ||
+ rh.tid != LockSupport.getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
@@ -644,7 +655,7 @@
return firstReaderHoldCount;
HoldCounter rh = cachedHoldCounter;
- if (rh != null && rh.tid == getThreadId(current))
+ if (rh != null && rh.tid == LockSupport.getThreadId(current))
return rh.count;
int count = readHolds.get().count;
@@ -1490,26 +1501,4 @@
"[Write locks = " + w + ", Read locks = " + r + "]";
}
- /**
- * Returns the thread id for the given thread. We must access
- * this directly rather than via method Thread.getId() because
- * getId() is not final, and has been known to be overridden in
- * ways that do not preserve unique mappings.
- */
- static final long getThreadId(Thread thread) {
- return U.getLongVolatile(thread, TID);
- }
-
- // Unsafe mechanics
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long TID;
- static {
- try {
- TID = U.objectFieldOffset
- (Thread.class.getDeclaredField("tid"));
- } catch (ReflectiveOperationException e) {
- throw new Error(e);
- }
- }
-
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,7 +35,10 @@
package java.util.concurrent.locks;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
import java.util.concurrent.TimeUnit;
+import jdk.internal.vm.annotation.ReservedStackAccess;
/**
* A capability-based lock with three modes for controlling read/write
@@ -108,6 +111,10 @@
* into initial unlocked state, so they are not useful for remote
* locking.
*
+ * <p>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.
+ *
* <p>The scheduling policy of StampedLock does not consistently
* prefer readers over writers or vice versa. All "try" methods are
* best-effort and do not necessarily conform to any scheduling or
@@ -126,7 +133,7 @@
* in a class that maintains simple two-dimensional points. The sample
* code illustrates some try/catch conventions even though they are
* not strictly needed here because no exceptions can occur in their
- * bodies.<br>
+ * bodies.
*
* <pre> {@code
* class Point {
@@ -234,9 +241,7 @@
* used in the acquire methods to reduce (increasingly expensive)
* context switching while also avoiding sustained memory
* thrashing among many threads. We limit spins to the head of
- * queue. A thread spin-waits up to SPINS times (where each
- * iteration decreases spin count with 50% probability) before
- * blocking. If, upon wakening it fails to obtain lock, and is
+ * queue. If, upon wakening, a thread fails to obtain lock, and is
* still (or becomes) the first waiting thread (which indicates
* that some other thread barged and obtained lock), it escalates
* spins (up to MAX_HEAD_SPINS) to reduce the likelihood of
@@ -251,8 +256,12 @@
* method validate()) requires stricter ordering rules than apply
* 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.
+ * cases where this is not already forced, we use acquireFence.
+ * Unlike in that paper, we allow writers to use plain writes.
+ * One would not expect reorderings of such writes with the lock
+ * acquisition CAS because there is a "control dependency", but it
+ * is theoretically possible, so we additionally add a
+ * storeStoreFence after lock acquisition CAS.
*
* The memory layout keeps lock state and queue pointers together
* (normally on the same cache line). This usually works well for
@@ -290,7 +299,20 @@
private static final long ABITS = RBITS | WBIT;
private static final long SBITS = ~RBITS; // note overlap with ABITS
- // Initial value for lock state; avoid failure value zero
+ /*
+ * 3 stamp modes can be distinguished by examining (m = stamp & ABITS):
+ * write mode: m == WBIT
+ * optimistic read mode: m == 0L (even when read lock is held)
+ * read mode: m > 0L && m <= RFULL (the stamp is a copy of state, but the
+ * read hold count in the stamp is unused other than to determine mode)
+ *
+ * This differs slightly from the encoding of state:
+ * (state & ABITS) == 0L indicates the lock is currently unlocked.
+ * (state & ABITS) == RBITS is a special transient value
+ * indicating spin-locked to manipulate reader bits overflow.
+ */
+
+ /** Initial value for lock state; avoids failure value zero. */
private static final long ORIGIN = WBIT << 1;
// Special value from cancelled acquire methods so caller can throw IE
@@ -337,30 +359,42 @@
state = ORIGIN;
}
+ private boolean casState(long expectedValue, long newValue) {
+ return STATE.compareAndSet(this, expectedValue, newValue);
+ }
+
+ private long tryWriteLock(long s) {
+ // assert (s & ABITS) == 0L;
+ long next;
+ if (casState(s, next = s | WBIT)) {
+ VarHandle.storeStoreFence();
+ return next;
+ }
+ return 0L;
+ }
+
/**
* 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)) ?
- next : acquireWrite(false, 0L));
+ long next;
+ return ((next = tryWriteLock()) != 0L) ? 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)) ?
- next : 0L);
+ long s;
+ return (((s = state) & ABITS) == 0L) ? tryWriteLock(s) : 0L;
}
/**
@@ -371,7 +405,7 @@
*
* @param time the maximum time to wait for the lock
* @param unit the time unit of the {@code time} argument
- * @return a stamp that can be used to unlock or convert mode,
+ * @return a write stamp that can be used to unlock or convert mode,
* or zero if the lock is not available
* @throws InterruptedException if the current thread is interrupted
* before acquiring the lock
@@ -399,10 +433,11 @@
* Behavior under interruption matches that specified
* for method {@link Lock#lockInterruptibly()}.
*
- * @return a stamp that can be used to unlock or convert mode
+ * @return a write stamp that can be used to unlock or convert mode
* @throws InterruptedException if the current thread is interrupted
* before acquiring the lock
*/
+ @ReservedStackAccess
public long writeLockInterruptibly() throws InterruptedException {
long next;
if (!Thread.interrupted() &&
@@ -415,33 +450,37 @@
* 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)) ?
- next : acquireRead(false, 0L));
+ long s, next;
+ // bypass acquireRead on common uncontended case
+ return (whead == wtail
+ && ((s = state) & ABITS) < RFULL
+ && casState(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 (casState(s, next = s + RUNIT))
return next;
}
else if ((next = tryIncReaderOverflow(s)) != 0L)
return next;
}
+ return 0L;
}
/**
@@ -452,11 +491,12 @@
*
* @param time the maximum time to wait for the lock
* @param unit the time unit of the {@code time} argument
- * @return a stamp that can be used to unlock or convert mode,
+ * @return a read stamp that can be used to unlock or convert mode,
* or zero if the lock is not available
* @throws InterruptedException if the current thread is interrupted
* before acquiring the lock
*/
+ @ReservedStackAccess
public long tryReadLock(long time, TimeUnit unit)
throws InterruptedException {
long s, m, next, deadline;
@@ -464,7 +504,7 @@
if (!Thread.interrupted()) {
if ((m = (s = state) & ABITS) != WBIT) {
if (m < RFULL) {
- if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
+ if (casState(s, next = s + RUNIT))
return next;
}
else if ((next = tryIncReaderOverflow(s)) != 0L)
@@ -486,14 +526,20 @@
* 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
*/
- public long readLockInterruptibly() throws InterruptedException {
- long next;
- if (!Thread.interrupted() &&
- (next = acquireRead(true, 0L)) != INTERRUPTED)
+ @ReservedStackAccess
+ public long readLockInterruptibly() throws InterruptedException {
+ long s, next;
+ if (!Thread.interrupted()
+ // bypass acquireRead on common uncontended case
+ && ((whead == wtail
+ && ((s = state) & ABITS) < RFULL
+ && casState(s, next = s + RUNIT))
+ ||
+ (next = acquireRead(true, 0L)) != INTERRUPTED))
return next;
throw new InterruptedException();
}
@@ -502,7 +548,7 @@
* Returns a stamp that can later be validated, or zero
* if exclusively locked.
*
- * @return a stamp, or zero if exclusively locked
+ * @return a valid optimistic read stamp, or zero if exclusively locked
*/
public long tryOptimisticRead() {
long s;
@@ -522,11 +568,29 @@
* since issuance of the given stamp; else false
*/
public boolean validate(long stamp) {
- U.loadFence();
+ VarHandle.acquireFence();
return (stamp & SBITS) == (state & SBITS);
}
/**
+ * Returns an unlocked state, incrementing the version and
+ * avoiding special failure value 0L.
+ *
+ * @param s a write-locked state (or stamp)
+ */
+ private static long unlockWriteState(long s) {
+ return ((s += WBIT) == 0L) ? ORIGIN : s;
+ }
+
+ private long unlockWriteInternal(long s) {
+ long next; WNode h;
+ STATE.setVolatile(this, next = unlockWriteState(s));
+ if ((h = whead) != null && h.status != 0)
+ release(h);
+ return next;
+ }
+
+ /**
* If the lock state matches the given stamp, releases the
* exclusive lock.
*
@@ -534,13 +598,11 @@
* @throws IllegalMonitorStateException if the stamp does
* not match the current state of this lock
*/
+ @ReservedStackAccess
public void unlockWrite(long stamp) {
- WNode h;
if (state != stamp || (stamp & WBIT) == 0L)
throw new IllegalMonitorStateException();
- U.putLongVolatile(this, STATE, (stamp += WBIT) == 0L ? ORIGIN : stamp);
- if ((h = whead) != null && h.status != 0)
- release(h);
+ unlockWriteInternal(stamp);
}
/**
@@ -551,22 +613,23 @@
* @throws IllegalMonitorStateException if the stamp does
* not match the current state of this lock
*/
+ @ReservedStackAccess
public void unlockRead(long stamp) {
long s, m; WNode h;
- for (;;) {
- if (((s = state) & SBITS) != (stamp & SBITS) ||
- (stamp & ABITS) == 0L || (m = s & ABITS) == 0L || m == WBIT)
- throw new IllegalMonitorStateException();
+ while (((s = state) & SBITS) == (stamp & SBITS)
+ && (stamp & RBITS) > 0L
+ && ((m = s & RBITS) > 0L)) {
if (m < RFULL) {
- if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
+ if (casState(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 +640,12 @@
* @throws IllegalMonitorStateException if the stamp does
* not match the current state of this lock
*/
+ @ReservedStackAccess
public void unlock(long stamp) {
- long a = stamp & ABITS, m, s; WNode h;
- while (((s = state) & SBITS) == (stamp & SBITS)) {
- if ((m = s & ABITS) == 0L)
- break;
- else if (m == WBIT) {
- if (a != m)
- break;
- U.putLongVolatile(this, STATE, (s += WBIT) == 0L ? ORIGIN : s);
- if ((h = whead) != null && h.status != 0)
- release(h);
- return;
- }
- else if (a == 0L || a >= WBIT)
- break;
- else if (m < RFULL) {
- if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
- if (m == RUNIT && (h = whead) != null && h.status != 0)
- release(h);
- return;
- }
- }
- else if (tryDecReaderOverflow(s) != 0L)
- return;
- }
- throw new IllegalMonitorStateException();
+ if ((stamp & WBIT) != 0L)
+ unlockWrite(stamp);
+ else
+ unlockRead(stamp);
}
/**
@@ -623,7 +666,7 @@
if ((m = s & ABITS) == 0L) {
if (a != 0L)
break;
- if (U.compareAndSwapLong(this, STATE, s, next = s + WBIT))
+ if ((next = tryWriteLock(s)) != 0L)
return next;
}
else if (m == WBIT) {
@@ -632,9 +675,10 @@
return stamp;
}
else if (m == RUNIT && a != 0L) {
- if (U.compareAndSwapLong(this, STATE, s,
- next = s - RUNIT + WBIT))
+ if (casState(s, next = s - RUNIT + WBIT)) {
+ VarHandle.storeStoreFence();
return next;
+ }
}
else
break;
@@ -654,30 +698,32 @@
* @return a valid read stamp, or zero on failure
*/
public long tryConvertToReadLock(long stamp) {
- long a = stamp & ABITS, m, s, next; WNode h;
+ long a, s, next; WNode h;
while (((s = state) & SBITS) == (stamp & SBITS)) {
- if ((m = s & ABITS) == 0L) {
- if (a != 0L)
+ if ((a = stamp & ABITS) >= WBIT) {
+ // write stamp
+ if (s != stamp)
break;
- else if (m < RFULL) {
- if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
+ STATE.setVolatile(this, next = unlockWriteState(s) + RUNIT);
+ if ((h = whead) != null && h.status != 0)
+ release(h);
+ return next;
+ }
+ else if (a == 0L) {
+ // optimistic read stamp
+ if ((s & ABITS) < RFULL) {
+ if (casState(s, next = s + RUNIT))
return next;
}
else if ((next = tryIncReaderOverflow(s)) != 0L)
return next;
}
- else if (m == WBIT) {
- if (a != m)
+ else {
+ // already a read stamp
+ if ((s & ABITS) == 0L)
break;
- U.putLongVolatile(this, STATE, next = s + (WBIT + RUNIT));
- if ((h = whead) != null && h.status != 0)
- release(h);
- return next;
+ return stamp;
}
- else if (a != 0L && a < WBIT)
- return stamp;
- else
- break;
}
return 0L;
}
@@ -693,29 +739,22 @@
* @return a valid optimistic read stamp, or zero on failure
*/
public long tryConvertToOptimisticRead(long stamp) {
- long a = stamp & ABITS, m, s, next; WNode h;
- U.loadFence();
- for (;;) {
- if (((s = state) & SBITS) != (stamp & SBITS))
- break;
- if ((m = s & ABITS) == 0L) {
- if (a != 0L)
+ long a, m, s, next; WNode h;
+ VarHandle.acquireFence();
+ while (((s = state) & SBITS) == (stamp & SBITS)) {
+ if ((a = stamp & ABITS) >= WBIT) {
+ // write stamp
+ if (s != stamp)
break;
- return s;
+ return unlockWriteInternal(s);
}
- else if (m == WBIT) {
- if (a != m)
- break;
- U.putLongVolatile(this, STATE,
- next = (s += WBIT) == 0L ? ORIGIN : s);
- if ((h = whead) != null && h.status != 0)
- release(h);
- return next;
- }
- else if (a == 0L || a >= WBIT)
+ else if (a == 0L)
+ // already an optimistic read stamp
+ return stamp;
+ else if ((m = s & ABITS) == 0L) // invalid read stamp
break;
else if (m < RFULL) {
- if (U.compareAndSwapLong(this, STATE, s, next = s - RUNIT)) {
+ if (casState(s, next = s - RUNIT)) {
if (m == RUNIT && (h = whead) != null && h.status != 0)
release(h);
return next & SBITS;
@@ -734,12 +773,11 @@
*
* @return {@code true} if the lock was held, else false
*/
+ @ReservedStackAccess
public boolean tryUnlockWrite() {
- long s; WNode h;
+ long s;
if (((s = state) & WBIT) != 0L) {
- U.putLongVolatile(this, STATE, (s += WBIT) == 0L ? ORIGIN : s);
- if ((h = whead) != null && h.status != 0)
- release(h);
+ unlockWriteInternal(s);
return true;
}
return false;
@@ -752,11 +790,12 @@
*
* @return {@code true} if the read lock was held, else false
*/
+ @ReservedStackAccess
public boolean tryUnlockRead() {
long s, m; WNode h;
while ((m = (s = state) & ABITS) != 0L && m < WBIT) {
if (m < RFULL) {
- if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
+ if (casState(s, s - RUNIT)) {
if (m == RUNIT && (h = whead) != null && h.status != 0)
release(h);
return true;
@@ -832,32 +871,30 @@
* Returns a plain {@link Lock} view of this StampedLock in which
* the {@link Lock#lock} method is mapped to {@link #readLock},
* and similarly for other methods. The returned Lock does not
- * support a {@link Condition}; method {@link
- * Lock#newCondition()} throws {@code
- * UnsupportedOperationException}.
+ * support a {@link Condition}; method {@link Lock#newCondition()}
+ * throws {@code UnsupportedOperationException}.
*
* @return the lock
*/
public Lock asReadLock() {
ReadLockView v;
- return ((v = readLockView) != null ? v :
- (readLockView = new ReadLockView()));
+ if ((v = readLockView) != null) return v;
+ return readLockView = new ReadLockView();
}
/**
* Returns a plain {@link Lock} view of this StampedLock in which
* the {@link Lock#lock} method is mapped to {@link #writeLock},
* and similarly for other methods. The returned Lock does not
- * support a {@link Condition}; method {@link
- * Lock#newCondition()} throws {@code
- * UnsupportedOperationException}.
+ * support a {@link Condition}; method {@link Lock#newCondition()}
+ * throws {@code UnsupportedOperationException}.
*
* @return the lock
*/
public Lock asWriteLock() {
WriteLockView v;
- return ((v = writeLockView) != null ? v :
- (writeLockView = new WriteLockView()));
+ if ((v = writeLockView) != null) return v;
+ return writeLockView = new WriteLockView();
}
/**
@@ -870,8 +907,8 @@
*/
public ReadWriteLock asReadWriteLock() {
ReadWriteLockView v;
- return ((v = readWriteLockView) != null ? v :
- (readWriteLockView = new ReadWriteLockView()));
+ if ((v = readWriteLockView) != null) return v;
+ return readWriteLockView = new ReadWriteLockView();
}
// view classes
@@ -917,35 +954,32 @@
// Needed because view-class lock methods throw away stamps.
final void unstampedUnlockWrite() {
- WNode h; long s;
+ long s;
if (((s = state) & WBIT) == 0L)
throw new IllegalMonitorStateException();
- U.putLongVolatile(this, STATE, (s += WBIT) == 0L ? ORIGIN : s);
- if ((h = whead) != null && h.status != 0)
- release(h);
+ unlockWriteInternal(s);
}
final void unstampedUnlockRead() {
- for (;;) {
- long s, m; WNode h;
- if ((m = (s = state) & ABITS) == 0L || m >= WBIT)
- throw new IllegalMonitorStateException();
- else if (m < RFULL) {
- if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
+ long s, m; WNode h;
+ while ((m = (s = state) & RBITS) > 0L) {
+ if (m < RFULL) {
+ if (casState(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 +995,16 @@
private long tryIncReaderOverflow(long s) {
// assert (s & ABITS) >= RFULL;
if ((s & ABITS) == RFULL) {
- if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
+ if (casState(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 +1017,7 @@
private long tryDecReaderOverflow(long s) {
// assert (s & ABITS) >= RFULL;
if ((s & ABITS) == RFULL) {
- if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
+ if (casState(s, s | RBITS)) {
int r; long next;
if ((r = readerOverflow) > 0) {
readerOverflow = r - 1;
@@ -990,13 +1025,14 @@
}
else
next = s - RUNIT;
- U.putLongVolatile(this, STATE, next);
+ STATE.setVolatile(this, next);
return next;
}
}
- else if ((LockSupport.nextSecondarySeed() &
- OVERFLOW_YIELD_RATE) == 0)
+ else if ((LockSupport.nextSecondarySeed() & OVERFLOW_YIELD_RATE) == 0)
Thread.yield();
+ else
+ Thread.onSpinWait();
return 0L;
}
@@ -1010,14 +1046,14 @@
private void release(WNode h) {
if (h != null) {
WNode q; Thread w;
- U.compareAndSwapInt(h, WSTATUS, WAITING, 0);
+ WSTATUS.compareAndSet(h, WAITING, 0);
if ((q = h.next) == null || q.status == CANCELLED) {
for (WNode t = wtail; t != null && t != h; t = t.prev)
if (t.status <= 0)
q = t;
}
if (q != null && (w = q.thread) != null)
- U.unpark(w);
+ LockSupport.unpark(w);
}
}
@@ -1035,25 +1071,25 @@
for (int spins = -1;;) { // spin while enqueuing
long m, s, ns;
if ((m = (s = state) & ABITS) == 0L) {
- if (U.compareAndSwapLong(this, STATE, s, ns = s + WBIT))
+ if ((ns = tryWriteLock(s)) != 0L)
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 +1103,10 @@
spins = HEAD_SPINS;
else if (spins < MAX_HEAD_SPINS)
spins <<= 1;
- for (int k = spins;;) { // spin at head
+ for (int k = spins; k > 0; --k) { // spin at head
long s, ns;
if (((s = state) & ABITS) == 0L) {
- if (U.compareAndSwapLong(this, STATE, s,
- ns = s + WBIT)) {
+ if ((ns = tryWriteLock(s)) != 0L) {
whead = node;
node.prev = null;
if (wasInterrupted)
@@ -1079,17 +1114,16 @@
return ns;
}
}
- else if (LockSupport.nextSecondarySeed() >= 0 &&
- --k <= 0)
- break;
+ else
+ Thread.onSpinWait();
}
}
else if (h != null) { // help release stale waiters
WNode c; Thread w;
while ((c = h.cowait) != null) {
- if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
+ if (WCOWAIT.weakCompareAndSetVolatile(h, c, c.cowait) &&
(w = c.thread) != null)
- U.unpark(w);
+ LockSupport.unpark(w);
}
}
if (whead == h) {
@@ -1098,7 +1132,7 @@
(p = np).next = node; // stale
}
else if ((ps = p.status) == 0)
- U.compareAndSwapInt(p, WSTATUS, 0, WAITING);
+ WSTATUS.compareAndSet(p, 0, WAITING);
else if (ps == CANCELLED) {
if ((pp = p.prev) != null) {
node.prev = pp;
@@ -1112,13 +1146,15 @@
else if ((time = deadline - System.nanoTime()) <= 0L)
return cancelWaiter(node, node, false);
Thread wt = Thread.currentThread();
- U.putObject(wt, PARKBLOCKER, this);
node.thread = wt;
if (p.status < 0 && (p != h || (state & ABITS) != 0L) &&
- whead == h && node.prev == p)
- U.park(false, time); // emulate LockSupport.park
+ whead == h && node.prev == p) {
+ if (time == 0L)
+ LockSupport.park(this);
+ else
+ LockSupport.parkNanos(this, time);
+ }
node.thread = null;
- U.putObject(wt, PARKBLOCKER, null);
if (Thread.interrupted()) {
if (interruptible)
return cancelWaiter(node, node, true);
@@ -1146,7 +1182,7 @@
if ((h = whead) == (p = wtail)) {
for (long m, s, ns;;) {
if ((m = (s = state) & ABITS) < RFULL ?
- U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
+ casState(s, ns = s + RUNIT) :
(m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) {
if (wasInterrupted)
Thread.currentThread().interrupt();
@@ -1154,8 +1190,8 @@
}
else if (m >= WBIT) {
if (spins > 0) {
- if (LockSupport.nextSecondarySeed() >= 0)
- --spins;
+ --spins;
+ Thread.onSpinWait();
}
else {
if (spins == 0) {
@@ -1170,7 +1206,7 @@
}
if (p == null) { // initialize queue
WNode hd = new WNode(WMODE, null);
- if (U.compareAndSwapObject(this, WHEAD, null, hd))
+ if (WHEAD.weakCompareAndSetVolatile(this, null, hd))
wtail = hd;
}
else if (node == null)
@@ -1178,27 +1214,25 @@
else if (h == p || p.mode != RMODE) {
if (node.prev != p)
node.prev = p;
- else if (U.compareAndSwapObject(this, WTAIL, p, node)) {
+ else if (WTAIL.weakCompareAndSetVolatile(this, p, node)) {
p.next = node;
break;
}
}
- else if (!U.compareAndSwapObject(p, WCOWAIT,
- node.cowait = p.cowait, node))
+ else if (!WCOWAIT.compareAndSet(p, node.cowait = p.cowait, node))
node.cowait = null;
else {
for (;;) {
WNode pp, c; Thread w;
if ((h = whead) != null && (c = h.cowait) != null &&
- U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
+ WCOWAIT.compareAndSet(h, c, c.cowait) &&
(w = c.thread) != null) // help release
- U.unpark(w);
+ LockSupport.unpark(w);
if (h == (pp = p.prev) || h == p || pp == null) {
long m, s, ns;
do {
if ((m = (s = state) & ABITS) < RFULL ?
- U.compareAndSwapLong(this, STATE, s,
- ns = s + RUNIT) :
+ casState(s, ns = s + RUNIT) :
(m < WBIT &&
(ns = tryIncReaderOverflow(s)) != 0L)) {
if (wasInterrupted)
@@ -1221,13 +1255,15 @@
return cancelWaiter(node, p, false);
}
Thread wt = Thread.currentThread();
- U.putObject(wt, PARKBLOCKER, this);
node.thread = wt;
if ((h != pp || (state & ABITS) == WBIT) &&
- whead == h && p.prev == pp)
- U.park(false, time);
+ whead == h && p.prev == pp) {
+ if (time == 0L)
+ LockSupport.park(this);
+ else
+ LockSupport.parkNanos(this, time);
+ }
node.thread = null;
- U.putObject(wt, PARKBLOCKER, null);
if (Thread.interrupted()) {
if (interruptible)
return cancelWaiter(node, p, true);
@@ -1248,32 +1284,32 @@
for (int k = spins;;) { // spin at head
long m, s, ns;
if ((m = (s = state) & ABITS) < RFULL ?
- U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
+ casState(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 +1318,7 @@
(p = np).next = node; // stale
}
else if ((ps = p.status) == 0)
- U.compareAndSwapInt(p, WSTATUS, 0, WAITING);
+ WSTATUS.compareAndSet(p, 0, WAITING);
else if (ps == CANCELLED) {
if ((pp = p.prev) != null) {
node.prev = pp;
@@ -1296,14 +1332,16 @@
else if ((time = deadline - System.nanoTime()) <= 0L)
return cancelWaiter(node, node, false);
Thread wt = Thread.currentThread();
- U.putObject(wt, PARKBLOCKER, this);
node.thread = wt;
if (p.status < 0 &&
(p != h || (state & ABITS) == WBIT) &&
- whead == h && node.prev == p)
- U.park(false, time);
+ whead == h && node.prev == p) {
+ if (time == 0L)
+ LockSupport.park(this);
+ else
+ LockSupport.parkNanos(this, time);
+ }
node.thread = null;
- U.putObject(wt, PARKBLOCKER, null);
if (Thread.interrupted()) {
if (interruptible)
return cancelWaiter(node, node, true);
@@ -1325,7 +1363,7 @@
* AbstractQueuedSynchronizer (see its detailed explanation in AQS
* internal documentation).
*
- * @param node if nonnull, the waiter
+ * @param node if non-null, the waiter
* @param group either node or the group node is cowaiting with
* @param interrupted if already interrupted
* @return INTERRUPTED if interrupted or Thread.interrupted, else zero
@@ -1337,7 +1375,7 @@
// unsplice cancelled nodes from group
for (WNode p = group, q; (q = p.cowait) != null;) {
if (q.status == CANCELLED) {
- U.compareAndSwapObject(p, WCOWAIT, q, q.cowait);
+ WCOWAIT.compareAndSet(p, q, q.cowait);
p = group; // restart
}
else
@@ -1346,7 +1384,7 @@
if (group == node) {
for (WNode r = group.cowait; r != null; r = r.cowait) {
if ((w = r.thread) != null)
- U.unpark(w); // wake up uncancelled co-waiters
+ LockSupport.unpark(w); // wake up uncancelled co-waiters
}
for (WNode pred = node.prev; pred != null; ) { // unsplice
WNode succ, pp; // find valid successor
@@ -1357,23 +1395,23 @@
if (t.status != CANCELLED)
q = t; // don't link if succ cancelled
if (succ == q || // ensure accurate successor
- U.compareAndSwapObject(node, WNEXT,
- succ, succ = q)) {
+ WNEXT.compareAndSet(node, succ, succ = q)) {
if (succ == null && node == wtail)
- U.compareAndSwapObject(this, WTAIL, node, pred);
+ WTAIL.compareAndSet(this, node, pred);
break;
}
}
if (pred.next == node) // unsplice pred link
- U.compareAndSwapObject(pred, WNEXT, node, succ);
+ WNEXT.compareAndSet(pred, node, succ);
if (succ != null && (w = succ.thread) != null) {
+ // wake up succ to observe new pred
succ.thread = null;
- U.unpark(w); // wake up succ to observe new pred
+ LockSupport.unpark(w);
}
if (pred.status != CANCELLED || (pp = pred.prev) == null)
break;
node.prev = pp; // repeat if new pred wrong/cancelled
- U.compareAndSwapObject(pp, WNEXT, pred, succ);
+ WNEXT.compareAndSet(pp, pred, succ);
pred = pp;
}
}
@@ -1397,34 +1435,22 @@
return (interrupted || Thread.interrupted()) ? INTERRUPTED : 0L;
}
- // Unsafe mechanics
- private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
- private static final long STATE;
- private static final long WHEAD;
- private static final long WTAIL;
- private static final long WNEXT;
- private static final long WSTATUS;
- private static final long WCOWAIT;
- private static final long PARKBLOCKER;
-
+ // VarHandle mechanics
+ private static final VarHandle STATE;
+ private static final VarHandle WHEAD;
+ private static final VarHandle WTAIL;
+ private static final VarHandle WNEXT;
+ private static final VarHandle WSTATUS;
+ private static final VarHandle WCOWAIT;
static {
try {
- STATE = U.objectFieldOffset
- (StampedLock.class.getDeclaredField("state"));
- WHEAD = U.objectFieldOffset
- (StampedLock.class.getDeclaredField("whead"));
- WTAIL = U.objectFieldOffset
- (StampedLock.class.getDeclaredField("wtail"));
-
- WSTATUS = U.objectFieldOffset
- (WNode.class.getDeclaredField("status"));
- WNEXT = U.objectFieldOffset
- (WNode.class.getDeclaredField("next"));
- WCOWAIT = U.objectFieldOffset
- (WNode.class.getDeclaredField("cowait"));
-
- PARKBLOCKER = U.objectFieldOffset
- (Thread.class.getDeclaredField("parkBlocker"));
+ MethodHandles.Lookup l = MethodHandles.lookup();
+ STATE = l.findVarHandle(StampedLock.class, "state", long.class);
+ WHEAD = l.findVarHandle(StampedLock.class, "whead", WNode.class);
+ WTAIL = l.findVarHandle(StampedLock.class, "wtail", WNode.class);
+ WSTATUS = l.findVarHandle(WNode.class, "status", int.class);
+ WNEXT = l.findVarHandle(WNode.class, "next", WNode.class);
+ WCOWAIT = l.findVarHandle(WNode.class, "cowait", WNode.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
--- a/jdk/src/java.base/share/classes/java/util/concurrent/package-info.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/package-info.java Thu Jul 21 20:09:19 2016 -0700
@@ -262,7 +262,6 @@
*
* </ul>
*
- *
* The methods of all classes in {@code java.util.concurrent} and its
* subpackages extend these guarantees to higher-level
* synchronization. In particular:
--- a/jdk/src/java.base/share/classes/java/util/jar/JarFile.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/jar/JarFile.java Thu Jul 21 20:09:19 2016 -0700
@@ -141,7 +141,6 @@
private boolean verify;
private final Runtime.Version version; // current version
private final int versionMajor; // version.major()
- private boolean notVersioned; // legacy constructor called
private boolean isMultiRelease; // is jar multi-release?
// indicates if Class-Path attribute present
@@ -290,7 +289,6 @@
*/
public JarFile(File file, boolean verify, int mode) throws IOException {
this(file, verify, mode, BASE_VERSION);
- this.notVersioned = true;
}
/**
@@ -496,42 +494,14 @@
Iterator<JarEntry>
{
final Enumeration<? extends ZipEntry> e = JarFile.super.entries();
- ZipEntry ze;
public boolean hasNext() {
- if (notVersioned) {
- return e.hasMoreElements();
- }
- if (ze != null) {
- return true;
- }
- return findNext();
- }
-
- private boolean findNext() {
- while (e.hasMoreElements()) {
- ZipEntry ze2 = e.nextElement();
- if (!ze2.getName().startsWith(META_INF_VERSIONS)) {
- ze = ze2;
- return true;
- }
- }
- return false;
+ return e.hasMoreElements();
}
public JarEntry next() {
- ZipEntry ze2;
-
- if (notVersioned) {
- ze2 = e.nextElement();
- return new JarFileEntry(ze2.getName(), ze2);
- }
- if (ze != null || findNext()) {
- ze2 = ze;
- ze = null;
- return new JarFileEntry(ze2);
- }
- throw new NoSuchElementException();
+ ZipEntry ze = e.nextElement();
+ return new JarFileEntry(ze.getName(), ze);
}
public boolean hasMoreElements() {
@@ -548,19 +518,7 @@
}
/**
- * Returns an enumeration of the jar file entries. The set of entries
- * returned depends on whether or not the jar file is a multi-release jar
- * file, and on the constructor used to create the {@code JarFile}. If the
- * jar file is not a multi-release jar file, all entries are returned,
- * regardless of how the {@code JarFile} is created. If the constructor
- * does not take a {@code Release} argument, all entries are returned.
- * If the jar file is a multi-release jar file and the constructor takes a
- * {@code Release} argument, then the set of entries returned is equivalent
- * to the set of entries that would be returned if the set was built by
- * invoking {@link JarFile#getEntry(String)} or
- * {@link JarFile#getJarEntry(String)} with the name of each base entry in
- * the jar file. A base entry is an entry whose path name does not start
- * with "META-INF/versions/".
+ * Returns an enumeration of the jar file entries.
*
* @return an enumeration of the jar file entries
* @throws IllegalStateException
@@ -571,24 +529,26 @@
}
/**
- * Returns an ordered {@code Stream} over all the jar file entries.
+ * Returns an ordered {@code Stream} over the jar file entries.
* Entries appear in the {@code Stream} in the order they appear in
- * the central directory of the jar file. The set of entries
- * returned depends on whether or not the jar file is a multi-release jar
- * file, and on the constructor used to create the {@code JarFile}. If the
- * jar file is not a multi-release jar file, all entries are returned,
- * regardless of how the {@code JarFile} is created. If the constructor
- * does not take a {@code Release} argument, all entries are returned.
- * If the jar file is a multi-release jar file and the constructor takes a
- * {@code Release} argument, then the set of entries returned is equivalent
- * to the set of entries that would be returned if the set was built by
- * invoking {@link JarFile#getEntry(String)} or
- * {@link JarFile#getJarEntry(String)} with the name of each base entry in
- * the jar file. A base entry is an entry whose path name does not start
- * with "META-INF/versions/".
+ * the central directory of the jar file.
+ *
* @return an ordered {@code Stream} of entries in this jar file
* @throws IllegalStateException if the jar file has been closed
* @since 1.8
+ *
+ * @apiNote A versioned view of the stream obtained from a {@code JarFile}
+ * configured to process a multi-release jar file can be created with code
+ * similar to the following:
+ * <pre>
+ * {@code
+ * Stream<JarEntry> versionedStream(JarFile jf) {
+ * return jf.stream().map(JarEntry::getName)
+ * .filter(name -> !name.startsWith("META-INF/versions/"))
+ * .map(jf::getJarEntry);
+ * }
+ * }
+ * </pre>
*/
public Stream<JarEntry> stream() {
return StreamSupport.stream(Spliterators.spliterator(
--- a/jdk/src/java.base/share/classes/java/util/zip/ZipEntry.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/zip/ZipEntry.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 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
@@ -568,9 +568,18 @@
int pos = off + 4; // reserved 4 bytes
if (get16(extra, pos) != 0x0001 || get16(extra, pos + 2) != 24)
break;
- mtime = winTimeToFileTime(get64(extra, pos + 4));
- atime = winTimeToFileTime(get64(extra, pos + 12));
- ctime = winTimeToFileTime(get64(extra, pos + 20));
+ long wtime = get64(extra, pos + 4);
+ if (wtime != WINDOWS_TIME_NOT_AVAILABLE) {
+ mtime = winTimeToFileTime(wtime);
+ }
+ wtime = get64(extra, pos + 12);
+ if (wtime != WINDOWS_TIME_NOT_AVAILABLE) {
+ atime = winTimeToFileTime(wtime);
+ }
+ wtime = get64(extra, pos + 20);
+ if (wtime != WINDOWS_TIME_NOT_AVAILABLE) {
+ ctime = winTimeToFileTime(wtime);
+ }
break;
case EXTID_EXTT:
int flag = Byte.toUnsignedInt(extra[off]);
--- a/jdk/src/java.base/share/classes/java/util/zip/ZipOutputStream.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/zip/ZipOutputStream.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -421,22 +421,36 @@
byte[] nameBytes = zc.getBytes(e.name);
writeShort(nameBytes.length);
- int elenEXTT = 0; // info-zip extended timestamp
+ int elenEXTT = 0; // info-zip extended timestamp
int flagEXTT = 0;
+ long umtime = -1;
+ long uatime = -1;
+ long uctime = -1;
if (e.mtime != null) {
elenEXTT += 4;
flagEXTT |= EXTT_FLAG_LMT;
+ umtime = fileTimeToUnixTime(e.mtime);
}
if (e.atime != null) {
elenEXTT += 4;
flagEXTT |= EXTT_FLAG_LAT;
+ uatime = fileTimeToUnixTime(e.atime);
}
if (e.ctime != null) {
elenEXTT += 4;
flagEXTT |= EXTT_FLAT_CT;
+ uctime = fileTimeToUnixTime(e.ctime);
}
- if (flagEXTT != 0)
- elen += (elenEXTT + 5); // headid(2) + size(2) + flag(1) + data
+ if (flagEXTT != 0) {
+ // to use ntfs time if any m/a/ctime is beyond unixtime upper bound
+ if (umtime > UPPER_UNIXTIME_BOUND ||
+ uatime > UPPER_UNIXTIME_BOUND ||
+ uctime > UPPER_UNIXTIME_BOUND) {
+ elen += 36; // NTFS time, total 36 bytes
+ } else {
+ elen += (elenEXTT + 5); // headid(2) + size(2) + flag(1) + data
+ }
+ }
writeShort(elen);
writeBytes(nameBytes, 0, nameBytes.length);
if (hasZip64) {
@@ -446,15 +460,31 @@
writeLong(e.csize);
}
if (flagEXTT != 0) {
- writeShort(EXTID_EXTT);
- writeShort(elenEXTT + 1); // flag + data
- writeByte(flagEXTT);
- if (e.mtime != null)
- writeInt(fileTimeToUnixTime(e.mtime));
- if (e.atime != null)
- writeInt(fileTimeToUnixTime(e.atime));
- if (e.ctime != null)
- writeInt(fileTimeToUnixTime(e.ctime));
+ if (umtime > UPPER_UNIXTIME_BOUND ||
+ uatime > UPPER_UNIXTIME_BOUND ||
+ uctime > UPPER_UNIXTIME_BOUND) {
+ writeShort(EXTID_NTFS); // id
+ writeShort(32); // data size
+ writeInt(0); // reserved
+ writeShort(0x0001); // NTFS attr tag
+ writeShort(24);
+ writeLong(e.mtime == null ? WINDOWS_TIME_NOT_AVAILABLE
+ : fileTimeToWinTime(e.mtime));
+ writeLong(e.atime == null ? WINDOWS_TIME_NOT_AVAILABLE
+ : fileTimeToWinTime(e.atime));
+ writeLong(e.ctime == null ? WINDOWS_TIME_NOT_AVAILABLE
+ : fileTimeToWinTime(e.ctime));
+ } else {
+ writeShort(EXTID_EXTT);
+ writeShort(elenEXTT + 1); // flag + data
+ writeByte(flagEXTT);
+ if (e.mtime != null)
+ writeInt(umtime);
+ if (e.atime != null)
+ writeInt(uatime);
+ if (e.ctime != null)
+ writeInt(uctime);
+ }
}
writeExtra(e.extra);
locoff = written;
@@ -528,18 +558,30 @@
// cen info-zip extended timestamp only outputs mtime
// but set the flag for a/ctime, if present in loc
int flagEXTT = 0;
+ long umtime = -1;
+ long uatime = -1;
+ long uctime = -1;
if (e.mtime != null) {
- elen += 4; // + mtime(4)
flagEXTT |= EXTT_FLAG_LMT;
+ umtime = fileTimeToUnixTime(e.mtime);
}
if (e.atime != null) {
flagEXTT |= EXTT_FLAG_LAT;
+ uatime = fileTimeToUnixTime(e.atime);
}
if (e.ctime != null) {
flagEXTT |= EXTT_FLAT_CT;
+ uctime = fileTimeToUnixTime(e.ctime);
}
if (flagEXTT != 0) {
- elen += 5; // headid + sz + flag
+ // to use ntfs time if any m/a/ctime is beyond unixtime upper bound
+ if (umtime > UPPER_UNIXTIME_BOUND ||
+ uatime > UPPER_UNIXTIME_BOUND ||
+ uctime > UPPER_UNIXTIME_BOUND) {
+ elen += 36; // NTFS time total 36 bytes
+ } else {
+ elen += 9; // headid(2) + sz(2) + flag(1) + mtime (4)
+ }
}
writeShort(elen);
byte[] commentBytes;
@@ -568,14 +610,30 @@
writeLong(xentry.offset);
}
if (flagEXTT != 0) {
- writeShort(EXTID_EXTT);
- if (e.mtime != null) {
- writeShort(5); // flag + mtime
- writeByte(flagEXTT);
- writeInt(fileTimeToUnixTime(e.mtime));
+ if (umtime > UPPER_UNIXTIME_BOUND ||
+ uatime > UPPER_UNIXTIME_BOUND ||
+ uctime > UPPER_UNIXTIME_BOUND) {
+ writeShort(EXTID_NTFS); // id
+ writeShort(32); // data size
+ writeInt(0); // reserved
+ writeShort(0x0001); // NTFS attr tag
+ writeShort(24);
+ writeLong(e.mtime == null ? WINDOWS_TIME_NOT_AVAILABLE
+ : fileTimeToWinTime(e.mtime));
+ writeLong(e.atime == null ? WINDOWS_TIME_NOT_AVAILABLE
+ : fileTimeToWinTime(e.atime));
+ writeLong(e.ctime == null ? WINDOWS_TIME_NOT_AVAILABLE
+ : fileTimeToWinTime(e.ctime));
} else {
- writeShort(1); // flag only
- writeByte(flagEXTT);
+ writeShort(EXTID_EXTT);
+ if (e.mtime != null) {
+ writeShort(5); // flag + mtime
+ writeByte(flagEXTT);
+ writeInt(umtime);
+ } else {
+ writeShort(1); // flag only
+ writeByte(flagEXTT);
+ }
}
}
writeExtra(e.extra);
--- a/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/java/util/zip/ZipUtils.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2015, 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
@@ -38,6 +38,9 @@
// used to adjust values between Windows and java epoch
private static final long WINDOWS_EPOCH_IN_MICROSECONDS = -11644473600000000L;
+ // used to indicate the corresponding windows time is not available
+ public static final long WINDOWS_TIME_NOT_AVAILABLE = Long.MIN_VALUE;
+
/**
* Converts Windows time (in microseconds, UTC/GMT) time to FileTime.
*/
@@ -54,6 +57,11 @@
}
/**
+ * The upper bound of the 32-bit unix time, the "year 2038 problem".
+ */
+ public static final long UPPER_UNIXTIME_BOUND = 0x7fffffff;
+
+ /**
* Converts "standard Unix time"(in seconds, UTC/GMT) to FileTime
*/
public static final FileTime unixTimeToFileTime(long utime) {
--- a/jdk/src/java.base/share/classes/javax/security/auth/SubjectDomainCombiner.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/javax/security/auth/SubjectDomainCombiner.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 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
@@ -110,16 +110,18 @@
* {@code SubjectDomainCombiner}.
*
* <p> A new {@code ProtectionDomain} instance is created
- * for each {@code ProtectionDomain} in the
- * {@code currentDomains} array. Each new {@code ProtectionDomain}
+ * for each non-static {@code ProtectionDomain} (
+ * (staticPermissionsOnly() == false)
+ * in the {@code currentDomains} array. Each new {@code ProtectionDomain}
* instance is created using the {@code CodeSource},
* {@code Permission}s and {@code ClassLoader}
* from the corresponding {@code ProtectionDomain} in
* {@code currentDomains}, as well as with the Principals from
* the {@code Subject} associated with this
- * {@code SubjectDomainCombiner}.
+ * {@code SubjectDomainCombiner}. Static ProtectionDomains are
+ * combined as-is and no new instance is created.
*
- * <p> All of the newly instantiated ProtectionDomains are
+ * <p> All of the ProtectionDomains (static and newly instantiated) are
* combined into a new array. The ProtectionDomains from the
* {@code assignedDomains} array are appended to this new array,
* and the result is returned.
@@ -233,10 +235,15 @@
subjectPd = cachedPDs.getValue(pd);
if (subjectPd == null) {
- subjectPd = new ProtectionDomain(pd.getCodeSource(),
+ if (pd.staticPermissionsOnly()) {
+ // keep static ProtectionDomain objects static
+ subjectPd = pd;
+ } else {
+ subjectPd = new ProtectionDomain(pd.getCodeSource(),
pd.getPermissions(),
pd.getClassLoader(),
principals);
+ }
cachedPDs.putValue(pd, subjectPd);
} else {
allNew = false;
@@ -335,60 +342,62 @@
ProtectionDomain subjectPd = cachedPDs.getValue(pd);
if (subjectPd == null) {
-
- // XXX
- // we must first add the original permissions.
- // that way when we later add the new JAAS permissions,
- // any unresolved JAAS-related permissions will
- // automatically get resolved.
+ if (pd.staticPermissionsOnly()) {
+ // keep static ProtectionDomain objects static
+ subjectPd = pd;
+ } else {
+ // XXX
+ // we must first add the original permissions.
+ // that way when we later add the new JAAS permissions,
+ // any unresolved JAAS-related permissions will
+ // automatically get resolved.
- // get the original perms
- Permissions perms = new Permissions();
- PermissionCollection coll = pd.getPermissions();
- java.util.Enumeration<Permission> e;
- if (coll != null) {
- synchronized (coll) {
- e = coll.elements();
- while (e.hasMoreElements()) {
- Permission newPerm =
+ // get the original perms
+ Permissions perms = new Permissions();
+ PermissionCollection coll = pd.getPermissions();
+ java.util.Enumeration<Permission> e;
+ if (coll != null) {
+ synchronized (coll) {
+ e = coll.elements();
+ while (e.hasMoreElements()) {
+ Permission newPerm =
e.nextElement();
- perms.add(newPerm);
+ perms.add(newPerm);
+ }
}
}
- }
-
- // get perms from the policy
- final java.security.CodeSource finalCs = pd.getCodeSource();
- final Subject finalS = subject;
- PermissionCollection newPerms =
- java.security.AccessController.doPrivileged
- (new PrivilegedAction<PermissionCollection>() {
- @SuppressWarnings("deprecation")
- public PermissionCollection run() {
- return
- javax.security.auth.Policy.getPolicy().getPermissions
- (finalS, finalCs);
- }
- });
+ // get perms from the policy
+ final java.security.CodeSource finalCs = pd.getCodeSource();
+ final Subject finalS = subject;
+ PermissionCollection newPerms =
+ java.security.AccessController.doPrivileged
+ (new PrivilegedAction<PermissionCollection>() {
+ @SuppressWarnings("deprecation")
+ public PermissionCollection run() {
+ return
+ javax.security.auth.Policy.getPolicy().getPermissions
+ (finalS, finalCs);
+ }
+ });
- // add the newly granted perms,
- // avoiding duplicates
- synchronized (newPerms) {
- e = newPerms.elements();
- while (e.hasMoreElements()) {
- Permission newPerm = e.nextElement();
- if (!perms.implies(newPerm)) {
- perms.add(newPerm);
- if (debug != null)
- debug.println (
- "Adding perm " + newPerm + "\n");
+ // add the newly granted perms,
+ // avoiding duplicates
+ synchronized (newPerms) {
+ e = newPerms.elements();
+ while (e.hasMoreElements()) {
+ Permission newPerm = e.nextElement();
+ if (!perms.implies(newPerm)) {
+ perms.add(newPerm);
+ if (debug != null)
+ debug.println (
+ "Adding perm " + newPerm + "\n");
+ }
}
}
+ subjectPd = new ProtectionDomain
+ (finalCs, perms, pd.getClassLoader(), principals);
}
- subjectPd = new ProtectionDomain
- (finalCs, perms, pd.getClassLoader(), principals);
-
if (allowCaching)
cachedPDs.putValue(pd, subjectPd);
}
--- a/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageHeader.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageHeader.java Thu Jul 21 20:09:19 2016 -0700
@@ -21,7 +21,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 jdk.internal.jimage;
--- a/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageStream.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageStream.java Thu Jul 21 20:09:19 2016 -0700
@@ -3,18 +3,18 @@
* 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.
*
--- a/jdk/src/java.base/share/classes/jdk/internal/misc/Unsafe.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/misc/Unsafe.java Thu Jul 21 20:09:19 2016 -0700
@@ -26,8 +26,6 @@
package jdk.internal.misc;
import jdk.internal.HotSpotIntrinsicCandidate;
-import jdk.internal.reflect.CallerSensitive;
-import jdk.internal.reflect.Reflection;
import jdk.internal.vm.annotation.ForceInline;
import java.lang.reflect.Field;
@@ -57,7 +55,6 @@
private static native void registerNatives();
static {
registerNatives();
- Reflection.registerMethodsToFilter(Unsafe.class, "getUnsafe");
}
private Unsafe() {}
@@ -87,16 +84,8 @@
* }}</pre>
*
* (It may assist compilers to make the local variable {@code final}.)
- *
- * @throws SecurityException if the class loader of the caller
- * class is not in the system domain in which all permissions
- * are granted.
*/
- @CallerSensitive
public static Unsafe getUnsafe() {
- Class<?> caller = Reflection.getCallerClass();
- if (!VM.isSystemDomainLoader(caller.getClassLoader()))
- throw new SecurityException("Unsafe");
return theUnsafe;
}
--- a/jdk/src/java.base/share/classes/jdk/internal/ref/WeakCleanable.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/ref/WeakCleanable.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,3 @@
-package jdk.internal.ref;
-
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -25,6 +23,8 @@
* questions.
*/
+package jdk.internal.ref;
+
import java.lang.ref.Cleaner;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
--- a/jdk/src/java.base/share/classes/module-info.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/module-info.java Thu Jul 21 20:09:19 2016 -0700
@@ -149,7 +149,6 @@
exports jdk.internal.module to
java.instrument,
java.management,
- java.xml,
jdk.dynalink,
jdk.jartool,
jdk.jlink;
@@ -282,15 +281,18 @@
jdk.security.auth;
exports sun.text.resources to
jdk.localedata;
- exports sun.util.resources to
- jdk.localedata;
+ exports sun.util.cldr to
+ jdk.jlink;
exports sun.util.locale.provider to
java.desktop,
+ jdk.jlink,
jdk.localedata;
exports sun.util.logging to
java.desktop,
java.logging,
java.prefs;
+ exports sun.util.resources to
+ jdk.localedata;
// JDK-internal service types
uses jdk.internal.logger.DefaultLoggerFinder;
@@ -306,4 +308,3 @@
provides java.nio.file.spi.FileSystemProvider with
jdk.internal.jrtfs.JrtFileSystemProvider;
}
-
--- a/jdk/src/java.base/share/classes/sun/net/ftp/impl/FtpClient.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/sun/net/ftp/impl/FtpClient.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 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
@@ -1707,7 +1707,7 @@
*/
public InputStream nameList(String path) throws sun.net.ftp.FtpProtocolException, IOException {
Socket s;
- s = openDataConnection("NLST " + path);
+ s = openDataConnection(path == null ? "NLST" : "NLST " + path);
if (s != null) {
return createInputStream(s.getInputStream());
}
--- a/jdk/src/java.base/share/classes/sun/reflect/generics/reflectiveObjects/GenericArrayTypeImpl.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/sun/reflect/generics/reflectiveObjects/GenericArrayTypeImpl.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -65,15 +65,7 @@
}
public String toString() {
- Type componentType = getGenericComponentType();
- StringBuilder sb = new StringBuilder();
-
- if (componentType instanceof Class)
- sb.append(((Class)componentType).getName() );
- else
- sb.append(componentType.toString());
- sb.append("[]");
- return sb.toString();
+ return getGenericComponentType().getTypeName() + "[]";
}
@Override
--- a/jdk/src/java.base/share/classes/sun/reflect/generics/reflectiveObjects/ParameterizedTypeImpl.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/sun/reflect/generics/reflectiveObjects/ParameterizedTypeImpl.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 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
@@ -33,6 +33,7 @@
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
+import java.util.StringJoiner;
import java.util.Objects;
/** Implementing class for ParameterizedType interface. */
@@ -207,12 +208,9 @@
StringBuilder sb = new StringBuilder();
if (ownerType != null) {
- if (ownerType instanceof Class)
- sb.append(((Class)ownerType).getName());
- else
- sb.append(ownerType.toString());
+ sb.append(ownerType.getTypeName());
- sb.append(".");
+ sb.append("$");
if (ownerType instanceof ParameterizedTypeImpl) {
// Find simple name of nested type by removing the
@@ -220,21 +218,17 @@
sb.append(rawType.getName().replace( ((ParameterizedTypeImpl)ownerType).rawType.getName() + "$",
""));
} else
- sb.append(rawType.getName());
+ sb.append(rawType.getSimpleName());
} else
sb.append(rawType.getName());
- if (actualTypeArguments != null &&
- actualTypeArguments.length > 0) {
- sb.append("<");
- boolean first = true;
+ if (actualTypeArguments != null) {
+ StringJoiner sj = new StringJoiner(", ", "<", ">");
+ sj.setEmptyValue("");
for(Type t: actualTypeArguments) {
- if (!first)
- sb.append(", ");
- sb.append(t.getTypeName());
- first = false;
+ sj.add(t.getTypeName());
}
- sb.append(">");
+ sb.append(sj.toString());
}
return sb.toString();
--- a/jdk/src/java.base/share/classes/sun/reflect/generics/reflectiveObjects/WildcardTypeImpl.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/sun/reflect/generics/reflectiveObjects/WildcardTypeImpl.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 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,7 @@
import sun.reflect.generics.tree.FieldTypeSignature;
import sun.reflect.generics.visitor.Reifier;
import java.util.Arrays;
+import java.util.StringJoiner;
/**
@@ -156,14 +157,12 @@
assert bounds.length > 0;
- boolean first = true;
+ StringJoiner sj = new StringJoiner(" & ");
for(Type bound: bounds) {
- if (!first)
- sb.append(" & ");
+ sj.add(bound.getTypeName());
+ }
+ sb.append(sj.toString());
- first = false;
- sb.append(bound.getTypeName());
- }
return sb.toString();
}
--- a/jdk/src/java.base/share/classes/sun/security/provider/DSA.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/provider/DSA.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -329,6 +329,10 @@
r = values[0].getBigInteger();
s = values[1].getBigInteger();
+ // Check for trailing signature data
+ if (in.available() != 0) {
+ throw new IOException("Incorrect signature length");
+ }
} catch (IOException e) {
throw new SignatureException("invalid encoding for signature");
}
--- a/jdk/src/java.base/share/classes/sun/security/util/AbstractAlgorithmConstraints.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/util/AbstractAlgorithmConstraints.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 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
--- a/jdk/src/java.base/share/conf/security/java.security Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/conf/security/java.security Thu Jul 21 20:09:19 2016 -0700
@@ -652,8 +652,8 @@
# jdk.certpath.disabledAlgorithms=MD2, DSA, RSA keySize < 2048
#
#
-jdk.certpath.disabledAlgorithms=MD2, MD5, RSA keySize < 1024, \
- DSA keySize < 1024, EC keySize < 224
+jdk.certpath.disabledAlgorithms=MD2, MD5, SHA1 jdkCA & denyAfter 2017-01-01, \
+ RSA keySize < 1024, DSA keySize < 1024, EC keySize < 224
# Algorithm restrictions for Secure Socket Layer/Transport Layer Security
# (SSL/TLS/DTLS) processing
--- a/jdk/src/java.base/share/native/include/jvmti.h Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/include/jvmti.h Thu Jul 21 20:09:19 2016 -0700
@@ -1217,8 +1217,11 @@
jmethodID method,
jlocation location);
- /* 40 : RESERVED */
- void *reserved40;
+ /* 40 : Get Named Module */
+ jvmtiError (JNICALL *GetNamedModule) (jvmtiEnv* env,
+ jobject class_loader,
+ const char* package_name,
+ jobject* module_ptr);
/* 41 : Set Field Access Watch */
jvmtiError (JNICALL *SetFieldAccessWatch) (jvmtiEnv* env,
@@ -2146,6 +2149,12 @@
return functions->GetAllModules(this, module_count_ptr, modules_ptr);
}
+ jvmtiError GetNamedModule(jobject class_loader,
+ const char* package_name,
+ jobject* module_ptr) {
+ return functions->GetNamedModule(this, class_loader, package_name, module_ptr);
+ }
+
jvmtiError GetLoadedClasses(jint* class_count_ptr,
jclass** classes_ptr) {
return functions->GetLoadedClasses(this, class_count_ptr, classes_ptr);
--- a/jdk/src/java.base/share/native/libfdlibm/e_acos.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/e_acos.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/e_asin.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/e_asin.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/e_atan2.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/e_atan2.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2004, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/e_atanh.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/e_atanh.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/e_cosh.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/e_cosh.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/e_exp.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/e_exp.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/e_fmod.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/e_fmod.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/e_log.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/e_log.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/e_log10.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/e_log10.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/e_rem_pio2.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/e_rem_pio2.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/e_remainder.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/e_remainder.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/e_scalb.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/e_scalb.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/e_sinh.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/e_sinh.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/fdlibm.h Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/fdlibm.h Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/k_cos.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/k_cos.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/k_rem_pio2.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/k_rem_pio2.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/k_sin.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/k_sin.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/k_standard.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/k_standard.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/k_tan.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/k_tan.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2004, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/s_atan.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/s_atan.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/s_ceil.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/s_ceil.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/s_copysign.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/s_copysign.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/s_cos.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/s_cos.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/s_expm1.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/s_expm1.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/s_fabs.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/s_fabs.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/s_finite.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/s_finite.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/s_floor.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/s_floor.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/s_frexp.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/s_frexp.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/s_ilogb.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/s_ilogb.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/s_isnan.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/s_isnan.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/s_ldexp.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/s_ldexp.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/s_lib_version.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/s_lib_version.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/s_log1p.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/s_log1p.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/s_logb.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/s_logb.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/s_matherr.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/s_matherr.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/s_modf.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/s_modf.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/s_nextafter.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/s_nextafter.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/s_rint.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/s_rint.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/s_scalbn.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/s_scalbn.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/s_significand.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/s_significand.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/s_sin.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/s_sin.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/s_tan.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/s_tan.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libfdlibm/s_tanh.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libfdlibm/s_tanh.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 1998, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.base/share/native/libjimage/NativeImageBuffer.cpp Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libjimage/NativeImageBuffer.cpp Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2016 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
--- a/jdk/src/java.base/share/native/libjimage/endian.cpp Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libjimage/endian.cpp Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2016 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
--- a/jdk/src/java.base/share/native/libjimage/imageFile.cpp Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libjimage/imageFile.cpp Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2016 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
--- a/jdk/src/java.base/share/native/libjimage/jimage.cpp Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/share/native/libjimage/jimage.cpp Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2016 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -85,7 +85,7 @@
* Ex.
* const char* package = (*JImagePackageToModule)(image, "java/lang");
* tty->print_cr(package);
- * —> java.base
+ * -> java.base
*/
extern "C" const char* JIMAGE_PackageToModule(JImageFile* image, const char* package_name) {
return ((ImageFileReader*) image)->get_image_module_data()->package_to_module(package_name);
@@ -137,7 +137,7 @@
}
/*
- * JImageGetResource - Given an open image file (see JImageOpen), a resource’s
+ * JImageGetResource - Given an open image file (see JImageOpen), a resource's
* location information (see JImageFindResource), a buffer of appropriate
* size and the size, retrieve the bytes associated with the
* resource. If the size is less than the resource size then the read is truncated.
@@ -168,7 +168,7 @@
* Ex.
* bool ctw_visitor(JImageFile* jimage, const char* module_name, const char* version,
* const char* package, const char* name, const char* extension, void* arg) {
- * if (strcmp(extension, “class”) == 0) {
+ * if (strcmp(extension, "class") == 0) {
* char path[JIMAGE_MAX_PATH];
* Thread* THREAD = Thread::current();
* jio_snprintf(path, JIMAGE_MAX_PATH - 1, "/%s/%s", package, name);
--- a/jdk/src/java.base/unix/classes/java/lang/ClassLoaderHelper.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/unix/classes/java/lang/ClassLoaderHelper.java Thu Jul 21 20:09:19 2016 -0700
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.lang;
import java.io.File;
--- a/jdk/src/java.base/unix/classes/java/net/DefaultDatagramSocketImplFactory.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/unix/classes/java/net/DefaultDatagramSocketImplFactory.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007,2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2011, 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;
import sun.security.action.GetPropertyAction;
--- a/jdk/src/java.base/unix/native/libnet/NetworkInterface.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/unix/native/libnet/NetworkInterface.c Thu Jul 21 20:09:19 2016 -0700
@@ -25,9 +25,6 @@
#include <errno.h>
#include <strings.h>
-#if defined(_ALLBSD_SOURCE) && defined(__OpenBSD__)
-#include <sys/types.h>
-#endif
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
@@ -46,7 +43,6 @@
#if defined(__linux__)
#include <sys/ioctl.h>
-#include <bits/ioctls.h>
#include <sys/utsname.h>
#include <stdio.h>
#endif
@@ -76,9 +72,23 @@
#include "net_util.h"
#if defined(__linux__)
-#define _PATH_PROCNET_IFINET6 "/proc/net/if_inet6"
+ #define _PATH_PROCNET_IFINET6 "/proc/net/if_inet6"
+#elif defined(__solaris__)
+ #ifndef SIOCGLIFHWADDR
+ #define SIOCGLIFHWADDR _IOWR('i', 192, struct lifreq)
+ #endif
+ #define DEV_PREFIX "/dev/"
#endif
+#define CHECKED_MALLOC3(_pointer, _type, _size) \
+ do { \
+ _pointer = (_type)malloc(_size); \
+ if (_pointer == NULL) { \
+ JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed"); \
+ return ifs; /* return untouched list */ \
+ } \
+ } while(0)
+
typedef struct _netaddr {
struct sockaddr *addr;
struct sockaddr *brdcast;
@@ -130,40 +140,31 @@
static netif *enumInterfaces(JNIEnv *env);
static netif *enumIPv4Interfaces(JNIEnv *env, int sock, netif *ifs);
-#ifdef AF_INET6
+#if defined(AF_INET6)
static netif *enumIPv6Interfaces(JNIEnv *env, int sock, netif *ifs);
#endif
static netif *addif(JNIEnv *env, int sock, const char *if_name, netif *ifs,
- struct sockaddr *ifr_addrP, struct sockaddr *ifr_broadaddrP,
- struct sockaddr *ifr_subnetaddrP, int family, short prefix);
+ struct sockaddr *ifr_addrP,
+ struct sockaddr *ifr_broadaddrP,
+ int family, short prefix);
static void freeif(netif *ifs);
static int openSocket(JNIEnv *env, int proto);
static int openSocketWithFallback(JNIEnv *env, const char *ifname);
+static short translateIPv4AddressToPrefix(struct sockaddr_in *addr);
+static short translateIPv6AddressToPrefix(struct sockaddr_in6 *addr);
-static struct sockaddr *getBroadcast(JNIEnv *env, int sock, const char *name,
- struct sockaddr *brdcast_store);
-static short getSubnet(JNIEnv *env, int sock, const char *ifname);
-static short computeMaskFromAddress(struct sockaddr *ifr_subnetaddrP);
static int getIndex(int sock, const char *ifname);
-
static int getFlags(int sock, const char *ifname, int *flags);
-static int getMacAddress(JNIEnv *env, int sock, const char *ifname,
+static int getMacAddress(JNIEnv *env, int sock, const char *ifname,
const struct in_addr *addr, unsigned char *buf);
static int getMTU(JNIEnv *env, int sock, const char *ifname);
-
#if defined(__solaris__)
-static netif *enumIPvXInterfaces(JNIEnv *env, int sock, netif *ifs, int family);
static int getMacFromDevice(JNIEnv *env, const char *ifname,
unsigned char *retbuf);
-
-#ifndef SIOCGLIFHWADDR
-#define SIOCGLIFHWADDR _IOWR('i', 192, struct lifreq)
-#endif
-
#endif
/******************* Java entry points *****************************/
@@ -259,7 +260,7 @@
}
// if found create a NetworkInterface
- if (curr != NULL) {;
+ if (curr != NULL) {
obj = createNetworkInterface(env, curr);
}
@@ -299,7 +300,7 @@
}
// if found create a NetworkInterface
- if (curr != NULL) {;
+ if (curr != NULL) {
obj = createNetworkInterface(env, curr);
}
@@ -317,7 +318,7 @@
{
netif *ifs, *curr;
-#ifdef AF_INET6
+#if defined(AF_INET6)
int family = (getInetAddress_family(env, iaObj) == IPv4) ? AF_INET : AF_INET6;
#else
int family = AF_INET;
@@ -335,7 +336,7 @@
while (curr != NULL) {
netaddr *addrP = curr->addr;
- // Iterate through each address on the interface
+ // iterate through each address on the interface
while (addrP != NULL) {
if (family == addrP->family) {
@@ -350,7 +351,7 @@
}
}
-#ifdef AF_INET6
+#if defined(AF_INET6)
if (family == AF_INET6) {
jbyte *bytes = (jbyte *)&(
((struct sockaddr_in6*)addrP->addr)->sin6_addr);
@@ -385,7 +386,7 @@
}
// if found create a NetworkInterface
- if (match) {;
+ if (match) {
obj = createNetworkInterface(env, curr);
}
@@ -634,7 +635,7 @@
* populates the InetAddress array based on the IP addresses for this
* interface.
*/
-jobject createNetworkInterface(JNIEnv *env, netif *ifs) {
+static jobject createNetworkInterface(JNIEnv *env, netif *ifs) {
jobject netifObj;
jobject name;
jobjectArray addrArr;
@@ -712,7 +713,7 @@
}
}
-#ifdef AF_INET6
+#if defined(AF_INET6)
if (addrP->family == AF_INET6) {
int scope=0;
iaObj = (*env)->NewObject(env, ia6_class, ia6_ctrID);
@@ -803,7 +804,7 @@
// return partial list if an exception occurs in the middle of process ???
// If IPv6 is available then enumerate IPv6 addresses.
-#ifdef AF_INET6
+#if defined(AF_INET6)
// User can disable ipv6 explicitly by -Djava.net.preferIPv4Stack=true,
// so we have to call ipv6_available()
@@ -829,20 +830,10 @@
return ifs;
}
-#define CHECKED_MALLOC3(_pointer, _type, _size) \
- do { \
- _pointer = (_type)malloc(_size); \
- if (_pointer == NULL) { \
- JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed"); \
- return ifs; /* return untouched list */ \
- } \
- } while(0)
-
-
/*
- * Frees an interface list (including any attached addresses)
+ * Frees an interface list (including any attached addresses).
*/
-void freeif(netif *ifs) {
+static void freeif(netif *ifs) {
netif *currif = ifs;
netif *child = NULL;
@@ -865,9 +856,10 @@
}
}
-netif *addif(JNIEnv *env, int sock, const char *if_name, netif *ifs,
- struct sockaddr *ifr_addrP, struct sockaddr *ifr_broadaddrP,
- struct sockaddr *ifr_subnetaddrP, int family, short prefix)
+static netif *addif(JNIEnv *env, int sock, const char *if_name, netif *ifs,
+ struct sockaddr *ifr_addrP,
+ struct sockaddr *ifr_broadaddrP,
+ int family, short prefix)
{
netif *currif = ifs, *parent;
netaddr *addrP;
@@ -881,7 +873,6 @@
#endif
char *name_colonP;
- int mask;
int isVirtual = 0;
int addr_size;
int flags = 0;
@@ -894,12 +885,12 @@
name[ifnam_size - 1] = '\0';
*vname = 0;
- // Create and populate the netaddr node. If allocation fails
- // return an un-updated list.
+ // Create and populate the netaddr node. If allocation fails
+ // return an un-updated list.
- // Allocate for addr and brdcast at once
+ // Allocate for addr and brdcast at once
-#ifdef AF_INET6
+#if defined(AF_INET6)
addr_size = (family == AF_INET) ? sizeof(struct sockaddr_in)
: sizeof(struct sockaddr_in6);
#else
@@ -911,33 +902,16 @@
memcpy(addrP->addr, ifr_addrP, addr_size);
addrP->family = family;
- addrP->brdcast = NULL;
addrP->mask = prefix;
addrP->next = 0;
- if (family == AF_INET) {
- // Deal with broadcast addr & subnet mask
- if (ifr_broadaddrP != NULL) { // just set it, if already known
- addrP->brdcast =
- (struct sockaddr *)((char *)addrP + sizeof(netaddr) + addr_size);
- memcpy(addrP->brdcast, ifr_broadaddrP, addr_size);
- } else { // otherwise look it up
- struct sockaddr *brdcast_to =
- (struct sockaddr *)((char *)addrP + sizeof(netaddr) + addr_size);
- addrP->brdcast = getBroadcast(env, sock, name, brdcast_to);
- if ((*env)->ExceptionCheck(env) == JNI_TRUE) {
- return ifs;
- }
- }
- if (ifr_subnetaddrP != NULL) { // just compute the mask, if already known
- addrP->mask = computeMaskFromAddress(ifr_subnetaddrP);
- } else { // otherwise look it up
- if ((mask = getSubnet(env, sock, name)) != -1) {
- addrP->mask = mask;
- } else if((*env)->ExceptionCheck(env)) {
- return ifs;
- }
- }
+ // for IPv4 add broadcast address
+ if (family == AF_INET && ifr_broadaddrP != NULL) {
+ addrP->brdcast = (struct sockaddr *)
+ ((char *)addrP + sizeof(netaddr) + addr_size);
+ memcpy(addrP->brdcast, ifr_broadaddrP, addr_size);
+ } else {
+ addrP->brdcast = NULL;
}
// Deal with virtual interface with colon notation e.g. eth0:1
@@ -1037,18 +1011,55 @@
return ifs;
}
-static short computeMaskFromAddress(struct sockaddr *ifr_subnetaddrP) {
- short ret = 0;
- unsigned int mask;
+/*
+ * Determines the prefix value for an AF_INET subnet address.
+ */
+static short translateIPv4AddressToPrefix(struct sockaddr_in *addr) {
+ short prefix = 0;
+ unsigned int mask = ntohl(addr->sin_addr.s_addr);
+ while (mask) {
+ mask <<= 1;
+ prefix++;
+ }
+ return prefix;
+}
+
+/*
+ * Determines the prefix value for an AF_INET6 subnet address.
+ */
+static short translateIPv6AddressToPrefix(struct sockaddr_in6 *addr) {
+ short prefix = 0;
+ u_char *addrBytes = (u_char *)&(addr->sin6_addr);
+ unsigned int byte, bit;
- mask = ntohl(((struct sockaddr_in*)ifr_subnetaddrP)->sin_addr.s_addr);
-
- while (mask) {
- mask <<= 1;
- ret++;
+ for (byte = 0; byte < sizeof(struct in6_addr); byte++, prefix += 8) {
+ if (addrBytes[byte] != 0xff) {
+ break;
+ }
+ }
+ if (byte != sizeof(struct in6_addr)) {
+ for (bit = 7; bit != 0; bit--, prefix++) {
+ if (!(addrBytes[byte] & (1 << bit))) {
+ break;
+ }
+ }
+ for (; bit != 0; bit--) {
+ if (addrBytes[byte] & (1 << bit)) {
+ prefix = 0;
+ break;
+ }
+ }
+ if (prefix > 0) {
+ byte++;
+ for (; byte < sizeof(struct in6_addr); byte++) {
+ if (addrBytes[byte]) {
+ prefix = 0;
+ }
+ }
+ }
}
- return ret;
+ return prefix;
}
/*
@@ -1070,19 +1081,16 @@
return sock;
}
-
-/** Linux, AIX **/
-#if defined(__linux__) || defined(_AIX)
+/** Linux **/
+#if defined(__linux__)
-#ifdef AF_INET6
+#if defined(AF_INET6)
/*
- * Opens a socket for further ioct calls. Tries AF_INET socket first and
- * if it falls return AF_INET6 socket.
+ * Opens a socket for further ioctl calls. Tries AF_INET socket first and
+ * if it fails return AF_INET6 socket.
*/
-// unused arg ifname and struct if2
static int openSocketWithFallback(JNIEnv *env, const char *ifname) {
int sock;
- struct ifreq if2;
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
if (errno == EPROTONOSUPPORT) {
@@ -1102,23 +1110,22 @@
// IPv6 socket regardless of type of address of an interface.
return sock;
}
-
#else
static int openSocketWithFallback(JNIEnv *env, const char *ifname) {
- return openSocket(env,AF_INET);
+ return openSocket(env, AF_INET);
}
#endif
+/*
+ * Enumerates and returns all IPv4 interfaces on Linux.
+ */
static netif *enumIPv4Interfaces(JNIEnv *env, int sock, netif *ifs) {
struct ifconf ifc;
struct ifreq *ifreqP;
char *buf = NULL;
- int numifs;
unsigned i;
- int siocgifconfRequest = SIOCGIFCONF;
-#if defined(__linux__)
- // need to do a dummy SIOCGIFCONF to determine the buffer size.
+ // do a dummy SIOCGIFCONF to determine the buffer size
// SIOCGIFCOUNT doesn't work
ifc.ifc_buf = NULL;
if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
@@ -1126,160 +1133,9 @@
(env, JNU_JAVANETPKG "SocketException", "ioctl(SIOCGIFCONF) failed");
return ifs;
}
-#elif defined(_AIX)
- ifc.ifc_buf = NULL;
- if (ioctl(sock, SIOCGSIZIFCONF, &(ifc.ifc_len)) < 0) {
- JNU_ThrowByNameWithMessageAndLastError
- (env, JNU_JAVANETPKG "SocketException", "ioctl(SIOCGSIZIFCONF) failed");
- return ifs;
- }
-#endif /* __linux__ */
+ // call SIOCGIFCONF to enumerate the interfaces
CHECKED_MALLOC3(buf, char *, ifc.ifc_len);
-
- ifc.ifc_buf = buf;
-#if defined(_AIX)
- siocgifconfRequest = CSIOCGIFCONF;
-#endif
- if (ioctl(sock, siocgifconfRequest, (char *)&ifc) < 0) {
- JNU_ThrowByNameWithMessageAndLastError
- (env, JNU_JAVANETPKG "SocketException", "ioctl(SIOCGIFCONF) failed");
- free(buf);
- return ifs;
- }
-
- // Iterate through each interface
- ifreqP = ifc.ifc_req;
- struct sockaddr addr, broadaddr, netmask;
- for (i = 0; i < ifc.ifc_len / sizeof(struct ifreq); i++, ifreqP++) {
- struct sockaddr* broadaddrP = NULL;
- struct sockaddr* subnetaddrP = NULL;
-
- // Ignore non IPv4 Interfaces
- if ((struct sockaddr *)&(ifreqP->ifr_addr) != NULL &&
- ((struct sockaddr *)&(ifreqP->ifr_addr))->sa_family != AF_INET) {
- continue;
- }
-
- memcpy(&addr, &(ifreqP->ifr_addr), sizeof(struct sockaddr));
-
- // set broadaddrP, if applicable
- if ((ifreqP->ifr_flags & IFF_POINTOPOINT) == 0 &&
- ifreqP->ifr_flags & IFF_BROADCAST) {
-
- if (ioctl(sock, SIOCGIFBRDADDR, ifreqP) == 0) {
- memcpy(&broadaddr, &(ifreqP->ifr_broadaddr), sizeof(struct sockaddr));
- broadaddrP = &broadaddr;
- }
- // restore the address, for subsequent calls
- memcpy(&(ifreqP->ifr_addr), &addr, sizeof(struct sockaddr));
- }
-
- if (ioctl(sock, SIOCGIFNETMASK, ifreqP) == 0) {
-#if defined(_AIX)
- memcpy(&netmask, &(ifreqP->ifr_addr), sizeof(struct sockaddr));
-#else
- memcpy(&netmask, &(ifreqP->ifr_netmask), sizeof(struct sockaddr));
-#endif
- subnetaddrP = &netmask;
- }
-
- // Add to the list
- ifs = addif(env, sock, ifreqP->ifr_name, ifs,
- &addr, broadaddrP, subnetaddrP, AF_INET, 0);
-
- // If an exception occurred then free the list
- if ((*env)->ExceptionOccurred(env)) {
- free(buf);
- freeif(ifs);
- return NULL;
- }
- }
-
- // Free socket and buffer
- free(buf);
- return ifs;
-}
-
-
-#if defined(AF_INET6) && defined(__linux__)
-
-/*
- * Enumerates and returns all IPv6 interfaces on Linux.
- */
-static netif *enumIPv6Interfaces(JNIEnv *env, int sock, netif *ifs) {
- FILE *f;
- char addr6[40], devname[21];
- char addr6p[8][5];
- int prefix, scope, dad_status, if_idx;
- uint8_t ipv6addr[16];
-
- if ((f = fopen(_PATH_PROCNET_IFINET6, "r")) != NULL) {
- while (fscanf(f, "%4s%4s%4s%4s%4s%4s%4s%4s %08x %02x %02x %02x %20s\n",
- addr6p[0], addr6p[1], addr6p[2], addr6p[3],
- addr6p[4], addr6p[5], addr6p[6], addr6p[7],
- &if_idx, &prefix, &scope, &dad_status, devname) != EOF) {
-
- struct netif *ifs_ptr = NULL;
- struct netif *last_ptr = NULL;
- struct sockaddr_in6 addr;
-
- sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s",
- addr6p[0], addr6p[1], addr6p[2], addr6p[3],
- addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
- inet_pton(AF_INET6, addr6, ipv6addr);
-
- memset(&addr, 0, sizeof(struct sockaddr_in6));
- memcpy((void*)addr.sin6_addr.s6_addr, (const void*)ipv6addr, 16);
-
- addr.sin6_scope_id = if_idx;
-
- ifs = addif(env, sock, devname, ifs, (struct sockaddr *)&addr,
- NULL, NULL, AF_INET6, (short)prefix);
-
- // If an exception occurred then return the list as is.
- if ((*env)->ExceptionOccurred(env)) {
- fclose(f);
- return ifs;
- }
- }
- fclose(f);
- }
- return ifs;
-}
-#endif
-
-
-#if defined(AF_INET6) && defined(_AIX)
-
-/*
- * Enumerates and returns all IPv6 interfaces on AIX.
- */
-static netif *enumIPv6Interfaces(JNIEnv *env, int sock, netif *ifs) {
- struct ifconf ifc;
- struct ifreq *ifreqP;
- char *buf;
- int numifs;
- unsigned i;
- unsigned bufsize;
- char *cp, *cplimit;
-
- // use SIOCGSIZIFCONF to get size for SIOCGIFCONF
-
- ifc.ifc_buf = NULL;
- if (ioctl(sock, SIOCGSIZIFCONF, &(ifc.ifc_len)) < 0) {
- JNU_ThrowByNameWithMessageAndLastError
- (env, JNU_JAVANETPKG "SocketException", "ioctl(SIOCGSIZIFCONF) failed");
- return ifs;
- }
- bufsize = ifc.ifc_len;
-
- buf = (char *)malloc(bufsize);
- if (!buf) {
- JNU_ThrowOutOfMemoryError(env, "Network interface native buffer allocation failed");
- return ifs;
- }
- ifc.ifc_len = bufsize;
ifc.ifc_buf = buf;
if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
JNU_ThrowByNameWithMessageAndLastError
@@ -1288,41 +1144,48 @@
return ifs;
}
- // Iterate through each interface
+ // iterate through each interface
ifreqP = ifc.ifc_req;
- cp = (char *)ifc.ifc_req;
- cplimit = cp + ifc.ifc_len;
+ for (i = 0; i < ifc.ifc_len / sizeof(struct ifreq); i++, ifreqP++) {
+ struct sockaddr addr, broadaddr, *broadaddrP = NULL;
+ short prefix = 0;
+
+ // ignore non IPv4 interfaces
+ if (ifreqP->ifr_addr.sa_family != AF_INET) {
+ continue;
+ }
- for (; cp < cplimit;
- cp += (sizeof(ifreqP->ifr_name) +
- MAX((ifreqP->ifr_addr).sa_len, sizeof(ifreqP->ifr_addr))))
- {
- ifreqP = (struct ifreq *)cp;
- struct ifreq if2;
- memset((char *)&if2, 0, sizeof(if2));
- strncpy(if2.ifr_name, ifreqP->ifr_name, sizeof(if2.ifr_name) - 1);
+ // save socket address
+ memcpy(&addr, &(ifreqP->ifr_addr), sizeof(struct sockaddr));
+
+ // determine broadcast address, if applicable
+ if ((ioctl(sock, SIOCGIFFLAGS, ifreqP) == 0) &&
+ ifreqP->ifr_flags & IFF_BROADCAST) {
- // Skip interface that aren't UP
- if (ioctl(sock, SIOCGIFFLAGS, (char *)&if2) >= 0) {
- if (!(if2.ifr_flags & IFF_UP)) {
- continue;
+ // restore socket address to ifreqP
+ memcpy(&(ifreqP->ifr_addr), &addr, sizeof(struct sockaddr));
+
+ if (ioctl(sock, SIOCGIFBRDADDR, ifreqP) == 0) {
+ memcpy(&broadaddr, &(ifreqP->ifr_broadaddr),
+ sizeof(struct sockaddr));
+ broadaddrP = &broadaddr;
}
}
- if (ifreqP->ifr_addr.sa_family != AF_INET6)
- continue;
+ // restore socket address to ifreqP
+ memcpy(&(ifreqP->ifr_addr), &addr, sizeof(struct sockaddr));
- if (ioctl(sock, SIOCGIFSITE6, (char *)&if2) >= 0) {
- struct sockaddr_in6 *s6= (struct sockaddr_in6 *)&(ifreqP->ifr_addr);
- s6->sin6_scope_id = if2.ifr_site6;
+ // determine netmask
+ if (ioctl(sock, SIOCGIFNETMASK, ifreqP) == 0) {
+ prefix = translateIPv4AddressToPrefix(
+ (struct sockaddr_in *)&(ifreqP->ifr_netmask));
}
- // Add to the list
+ // add interface to the list
ifs = addif(env, sock, ifreqP->ifr_name, ifs,
- (struct sockaddr *)&(ifreqP->ifr_addr),
- NULL, NULL, AF_INET6, 0);
+ &addr, broadaddrP, AF_INET, prefix);
- // If an exception occurred then free the list
+ // in case of exception, free interface list and buffer and return NULL
if ((*env)->ExceptionOccurred(env)) {
free(buf);
freeif(ifs);
@@ -1330,18 +1193,60 @@
}
}
- // Free socket and buffer
+ // free buffer
free(buf);
return ifs;
}
-#endif
+
+#if defined(AF_INET6)
+
+/*
+ * Enumerates and returns all IPv6 interfaces on Linux.
+ */
+static netif *enumIPv6Interfaces(JNIEnv *env, int sock, netif *ifs) {
+ FILE *f;
+ char devname[21], addr6p[8][5];
+ int prefix, scope, dad_status, if_idx;
+
+ if ((f = fopen(_PATH_PROCNET_IFINET6, "r")) != NULL) {
+ while (fscanf(f, "%4s%4s%4s%4s%4s%4s%4s%4s %08x %02x %02x %02x %20s\n",
+ addr6p[0], addr6p[1], addr6p[2], addr6p[3],
+ addr6p[4], addr6p[5], addr6p[6], addr6p[7],
+ &if_idx, &prefix, &scope, &dad_status, devname) != EOF) {
+
+ char addr6[40];
+ struct sockaddr_in6 addr;
+
+ sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s",
+ addr6p[0], addr6p[1], addr6p[2], addr6p[3],
+ addr6p[4], addr6p[5], addr6p[6], addr6p[7]);
+ memset(&addr, 0, sizeof(struct sockaddr_in6));
+ inet_pton(AF_INET6, addr6, (void*)addr.sin6_addr.s6_addr);
+ // set scope ID to interface index
+ addr.sin6_scope_id = if_idx;
+
+ // add interface to the list
+ ifs = addif(env, sock, devname, ifs, (struct sockaddr *)&addr,
+ NULL, AF_INET6, (short)prefix);
+
+ // if an exception occurred then return the list as is
+ if ((*env)->ExceptionOccurred(env)) {
+ break;
+ }
+ }
+ fclose(f);
+ }
+ return ifs;
+}
+
+#endif /* AF_INET6 */
+
+/*
+ * Try to get the interface index.
+ */
static int getIndex(int sock, const char *name) {
- // Try to get the interface index
-#if defined(_AIX)
- return if_nametoindex(name);
-#else
struct ifreq if2;
memset((char *)&if2, 0, sizeof(if2));
strncpy(if2.ifr_name, name, sizeof(if2.ifr_name) - 1);
@@ -1351,61 +1256,6 @@
}
return if2.ifr_ifindex;
-#endif
-}
-
-/*
- * Returns the IPv4 broadcast address of a named interface, if it exists.
- * Returns 0 if it doesn't have one.
- */
-static struct sockaddr *getBroadcast
- (JNIEnv *env, int sock, const char *ifname, struct sockaddr *brdcast_store)
-{
- struct sockaddr *ret = NULL;
- struct ifreq if2;
- memset((char *)&if2, 0, sizeof(if2));
- strncpy(if2.ifr_name, ifname, sizeof(if2.ifr_name) - 1);
-
- // Let's make sure the interface does have a broadcast address.
- if (ioctl(sock, SIOCGIFFLAGS, (char *)&if2) < 0) {
- JNU_ThrowByNameWithMessageAndLastError
- (env, JNU_JAVANETPKG "SocketException", "ioctl(SIOCGIFFLAGS) failed");
- return ret;
- }
-
- if (if2.ifr_flags & IFF_BROADCAST) {
- // It does, let's retrieve it
- if (ioctl(sock, SIOCGIFBRDADDR, (char *)&if2) < 0) {
- JNU_ThrowByNameWithMessageAndLastError
- (env, JNU_JAVANETPKG "SocketException", "ioctl(SIOCGIFBRDADDR) failed");
- return ret;
- }
-
- ret = brdcast_store;
- memcpy(ret, &if2.ifr_broadaddr, sizeof(struct sockaddr));
- }
-
- return ret;
-}
-
-/*
- * Returns the IPv4 subnet prefix length (aka subnet mask) for the named
- * interface, if it has one, otherwise return -1.
- */
-static short getSubnet(JNIEnv *env, int sock, const char *ifname) {
- unsigned int mask;
- short ret;
- struct ifreq if2;
- memset((char *)&if2, 0, sizeof(if2));
- strncpy(if2.ifr_name, ifname, sizeof(if2.ifr_name) - 1);
-
- if (ioctl(sock, SIOCGIFNETMASK, (char *)&if2) < 0) {
- JNU_ThrowByNameWithMessageAndLastError
- (env, JNU_JAVANETPKG "SocketException", "ioctl(SIOCGIFNETMASK) failed");
- return -1;
- }
-
- return computeMaskFromAddress(&(if2.ifr_addr));
}
/*
@@ -1414,10 +1264,272 @@
* MAC address. Returns -1 if there is no hardware address on that interface.
*/
static int getMacAddress
- (JNIEnv *env, int sock, const char* ifname, const struct in_addr* addr,
+ (JNIEnv *env, int sock, const char *ifname, const struct in_addr *addr,
unsigned char *buf)
{
-#if defined (_AIX)
+ static struct ifreq ifr;
+ int i;
+ memset((char *)&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1);
+ if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) {
+ JNU_ThrowByNameWithMessageAndLastError
+ (env, JNU_JAVANETPKG "SocketException", "ioctl(SIOCGIFHWADDR) failed");
+ return -1;
+ }
+
+ memcpy(buf, &ifr.ifr_hwaddr.sa_data, IFHWADDRLEN);
+
+ // all bytes to 0 means no hardware address
+ for (i = 0; i < IFHWADDRLEN; i++) {
+ if (buf[i] != 0)
+ return IFHWADDRLEN;
+ }
+
+ return -1;
+}
+
+static int getMTU(JNIEnv *env, int sock, const char *ifname) {
+ struct ifreq if2;
+ memset((char *)&if2, 0, sizeof(if2));
+ strncpy(if2.ifr_name, ifname, sizeof(if2.ifr_name) - 1);
+
+ if (ioctl(sock, SIOCGIFMTU, (char *)&if2) < 0) {
+ JNU_ThrowByNameWithMessageAndLastError
+ (env, JNU_JAVANETPKG "SocketException", "ioctl(SIOCGIFMTU) failed");
+ return -1;
+ }
+
+ return if2.ifr_mtu;
+}
+
+static int getFlags(int sock, const char *ifname, int *flags) {
+ struct ifreq if2;
+ memset((char *)&if2, 0, sizeof(if2));
+ strncpy(if2.ifr_name, ifname, sizeof(if2.ifr_name) - 1);
+
+ if (ioctl(sock, SIOCGIFFLAGS, (char *)&if2) < 0) {
+ return -1;
+ }
+
+ if (sizeof(if2.ifr_flags) == sizeof(short)) {
+ *flags = (if2.ifr_flags & 0xffff);
+ } else {
+ *flags = if2.ifr_flags;
+ }
+ return 0;
+}
+
+#endif /* __linux__ */
+
+/** AIX **/
+#if defined(_AIX)
+
+#if defined(AF_INET6)
+/*
+ * Opens a socket for further ioctl calls. Tries AF_INET socket first and
+ * if it fails return AF_INET6 socket.
+ */
+static int openSocketWithFallback(JNIEnv *env, const char *ifname) {
+ int sock;
+
+ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ if (errno == EPROTONOSUPPORT) {
+ if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ JNU_ThrowByNameWithMessageAndLastError
+ (env, JNU_JAVANETPKG "SocketException", "IPV6 Socket creation failed");
+ return -1;
+ }
+ } else { // errno is not NOSUPPORT
+ JNU_ThrowByNameWithMessageAndLastError
+ (env, JNU_JAVANETPKG "SocketException", "IPV4 Socket creation failed");
+ return -1;
+ }
+ }
+
+ return sock;
+}
+#else
+static int openSocketWithFallback(JNIEnv *env, const char *ifname) {
+ return openSocket(env, AF_INET);
+}
+#endif
+
+/*
+ * Enumerates and returns all IPv4 interfaces on AIX.
+ */
+static netif *enumIPv4Interfaces(JNIEnv *env, int sock, netif *ifs) {
+ struct ifconf ifc;
+ struct ifreq *ifreqP;
+ char *buf = NULL;
+ unsigned i;
+
+ // call SIOCGSIZIFCONF to get the size of SIOCGIFCONF buffer
+ if (ioctl(sock, SIOCGSIZIFCONF, &(ifc.ifc_len)) < 0) {
+ JNU_ThrowByNameWithMessageAndLastError
+ (env, JNU_JAVANETPKG "SocketException", "ioctl(SIOCGSIZIFCONF) failed");
+ return ifs;
+ }
+
+ // call CSIOCGIFCONF instead of SIOCGIFCONF where interface
+ // records will always have sizeof(struct ifreq) length.
+ // Be aware that only IPv4 data is complete this way.
+ CHECKED_MALLOC3(buf, char *, ifc.ifc_len);
+ ifc.ifc_buf = buf;
+ if (ioctl(sock, CSIOCGIFCONF, (char *)&ifc) < 0) {
+ JNU_ThrowByNameWithMessageAndLastError
+ (env, JNU_JAVANETPKG "SocketException", "ioctl(CSIOCGIFCONF) failed");
+ free(buf);
+ return ifs;
+ }
+
+ // iterate through each interface
+ ifreqP = ifc.ifc_req;
+ for (i = 0; i < ifc.ifc_len / sizeof(struct ifreq); i++, ifreqP++) {
+ struct sockaddr addr, broadaddr, *broadaddrP = NULL;
+ short prefix = 0;
+
+ // ignore non IPv4 interfaces
+ if (ifreqP->ifr_addr.sa_family != AF_INET) {
+ continue;
+ }
+
+ // save socket address
+ memcpy(&addr, &(ifreqP->ifr_addr), sizeof(struct sockaddr));
+
+ // determine broadcast address, if applicable
+ if ((ioctl(sock, SIOCGIFFLAGS, ifreqP) == 0) &&
+ ifreqP->ifr_flags & IFF_BROADCAST) {
+
+ // restore socket address to ifreqP
+ memcpy(&(ifreqP->ifr_addr), &addr, sizeof(struct sockaddr));
+
+ if (ioctl(sock, SIOCGIFBRDADDR, ifreqP) == 0) {
+ memcpy(&broadaddr, &(ifreqP->ifr_broadaddr),
+ sizeof(struct sockaddr));
+ broadaddrP = &broadaddr;
+ }
+ }
+
+ // restore socket address to ifreqP
+ memcpy(&(ifreqP->ifr_addr), &addr, sizeof(struct sockaddr));
+
+ // determine netmask
+ if (ioctl(sock, SIOCGIFNETMASK, ifreqP) == 0) {
+ prefix = translateIPv4AddressToPrefix(
+ (struct sockaddr_in *)&(ifreqP->ifr_addr));
+ }
+
+ // add interface to the list
+ ifs = addif(env, sock, ifreqP->ifr_name, ifs,
+ &addr, broadaddrP, AF_INET, prefix);
+
+ // in case of exception, free interface list and buffer and return NULL
+ if ((*env)->ExceptionOccurred(env)) {
+ free(buf);
+ freeif(ifs);
+ return NULL;
+ }
+ }
+
+ // free buffer
+ free(buf);
+ return ifs;
+}
+
+#if defined(AF_INET6)
+
+/*
+ * Enumerates and returns all IPv6 interfaces on AIX.
+ */
+static netif *enumIPv6Interfaces(JNIEnv *env, int sock, netif *ifs) {
+ struct ifconf ifc;
+ struct ifreq *ifreqP;
+ char *buf;
+
+ // call SIOCGSIZIFCONF to get size for SIOCGIFCONF buffer
+ if (ioctl(sock, SIOCGSIZIFCONF, &(ifc.ifc_len)) < 0) {
+ JNU_ThrowByNameWithMessageAndLastError
+ (env, JNU_JAVANETPKG "SocketException", "ioctl(SIOCGSIZIFCONF) failed");
+ return ifs;
+ }
+
+ // call SIOCGIFCONF to enumerate the interfaces
+ CHECKED_MALLOC3(buf, char *, ifc.ifc_len);
+ ifc.ifc_buf = buf;
+ if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
+ JNU_ThrowByNameWithMessageAndLastError
+ (env, JNU_JAVANETPKG "SocketException", "ioctl(SIOCGIFCONF) failed");
+ free(buf);
+ return ifs;
+ }
+
+ // iterate through each interface
+ char *cp = (char *)ifc.ifc_req;
+ char *cplimit = cp + ifc.ifc_len;
+
+ for (; cp < cplimit;
+ cp += (sizeof(ifreqP->ifr_name) +
+ MAX((ifreqP->ifr_addr).sa_len, sizeof(ifreqP->ifr_addr))))
+ {
+ ifreqP = (struct ifreq *)cp;
+ short prefix = 0;
+
+ // ignore non IPv6 interfaces
+ if (ifreqP->ifr_addr.sa_family != AF_INET6) {
+ continue;
+ }
+
+ // determine netmask
+ struct in6_ifreq if6;
+ memset((char *)&if6, 0, sizeof(if6));
+ strncpy(if6.ifr_name, ifreqP->ifr_name, sizeof(if6.ifr_name) - 1);
+ memcpy(&(if6.ifr_Addr), &(ifreqP->ifr_addr),
+ sizeof(struct sockaddr_in6));
+ if (ioctl(sock, SIOCGIFNETMASK6, (char *)&if6) >= 0) {
+ prefix = translateIPv6AddressToPrefix(&(if6.ifr_Addr));
+ }
+
+ // set scope ID to interface index
+ ((struct sockaddr_in6 *)&(ifreqP->ifr_addr))->sin6_scope_id =
+ getIndex(sock, ifreqP->ifr_name);
+
+ // add interface to the list
+ ifs = addif(env, sock, ifreqP->ifr_name, ifs,
+ (struct sockaddr *)&(ifreqP->ifr_addr),
+ NULL, AF_INET6, prefix);
+
+ // if an exception occurred then free the list
+ if ((*env)->ExceptionOccurred(env)) {
+ free(buf);
+ freeif(ifs);
+ return NULL;
+ }
+ }
+
+ // free buffer
+ free(buf);
+ return ifs;
+}
+
+#endif /* AF_INET6 */
+
+/*
+ * Try to get the interface index.
+ */
+static int getIndex(int sock, const char *name) {
+ int index = if_nametoindex(name);
+ return (index == 0) ? -1 : index;
+}
+
+/*
+ * Gets the Hardware address (usually MAC address) for the named interface.
+ * On return puts the data in buf, and returns the length, in byte, of the
+ * MAC address. Returns -1 if there is no hardware address on that interface.
+ */
+static int getMacAddress
+ (JNIEnv *env, int sock, const char *ifname, const struct in_addr *addr,
+ unsigned char *buf)
+{
int size;
struct kinfo_ndd *nddp;
void *end;
@@ -1457,40 +1569,12 @@
}
return -1;
-#elif defined(__linux__)
- static struct ifreq ifr;
- int i;
- memset((char *)&ifr, 0, sizeof(ifr));
- strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name) - 1);
- if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) {
- JNU_ThrowByNameWithMessageAndLastError
- (env, JNU_JAVANETPKG "SocketException", "ioctl(SIOCGIFHWADDR) failed");
- return -1;
- }
-
- memcpy(buf, &ifr.ifr_hwaddr.sa_data, IFHWADDRLEN);
-
- // All bytes to 0 means no hardware address.
-
- for (i = 0; i < IFHWADDRLEN; i++) {
- if (buf[i] != 0)
- return IFHWADDRLEN;
- }
-
- return -1;
-#endif
}
-static int getMTU(JNIEnv *env, int sock, const char *ifname) {
+static int getMTU(JNIEnv *env, int sock, const char *ifname) {
struct ifreq if2;
memset((char *)&if2, 0, sizeof(if2));
-
- if (ifname != NULL) {
- strncpy(if2.ifr_name, ifname, sizeof(if2.ifr_name) - 1);
- } else {
- JNU_ThrowNullPointerException(env, "network interface name is NULL");
- return -1;
- }
+ strncpy(if2.ifr_name, ifname, sizeof(if2.ifr_name) - 1);
if (ioctl(sock, SIOCGIFMTU, (char *)&if2) < 0) {
JNU_ThrowByNameWithMessageAndLastError
@@ -1498,7 +1582,7 @@
return -1;
}
- return if2.ifr_mtu;
+ return if2.ifr_mtu;
}
static int getFlags(int sock, const char *ifname, int *flags) {
@@ -1518,16 +1602,16 @@
return 0;
}
-#endif /* defined(__linux__) || defined(_AIX) */
+#endif /* _AIX */
/** Solaris **/
#if defined(__solaris__)
+#if defined(AF_INET6)
/*
- * Opens a socket for further ioct calls. Tries AF_INET socket first and
- * if it falls return AF_INET6 socket.
+ * Opens a socket for further ioctl calls. Tries AF_INET socket first and
+ * if it fails return AF_INET6 socket.
*/
-#ifdef AF_INET6
static int openSocketWithFallback(JNIEnv *env, const char *ifname) {
int sock, alreadyV6 = 0;
struct lifreq if2;
@@ -1539,8 +1623,7 @@
(env, JNU_JAVANETPKG "SocketException", "IPV6 Socket creation failed");
return -1;
}
-
- alreadyV6=1;
+ alreadyV6 = 1;
} else { // errno is not NOSUPPORT
JNU_ThrowByNameWithMessageAndLastError
(env, JNU_JAVANETPKG "SocketException", "IPV4 Socket creation failed");
@@ -1548,14 +1631,12 @@
}
}
-
// Solaris requires that we have an IPv6 socket to query an interface
// without an IPv4 address - check it here. POSIX 1 require the kernel to
// return ENOTTY if the call is inappropriate for a device e.g. the NETMASK
// for a device having IPv6 only address but not all devices follow the
// standard so fall back on any error. It's not an ecologically friendly
// gesture but more reliable.
-
if (!alreadyV6) {
memset((char *)&if2, 0, sizeof(if2));
strncpy(if2.lifr_name, ifname, sizeof(if2.lifr_name) - 1);
@@ -1571,40 +1652,24 @@
return sock;
}
-
#else
static int openSocketWithFallback(JNIEnv *env, const char *ifname) {
- return openSocket(env,AF_INET);
+ return openSocket(env, AF_INET);
}
#endif
/*
- * Enumerates and returns all IPv4 interfaces.
+ * Enumerates and returns all IPv4 interfaces on Solaris.
*/
static netif *enumIPv4Interfaces(JNIEnv *env, int sock, netif *ifs) {
- return enumIPvXInterfaces(env,sock, ifs, AF_INET);
-}
-
-#ifdef AF_INET6
-static netif *enumIPv6Interfaces(JNIEnv *env, int sock, netif *ifs) {
- return enumIPvXInterfaces(env,sock, ifs, AF_INET6);
-}
-#endif
+ struct lifconf ifc;
+ struct lifreq *ifreqP;
+ struct lifnum numifs;
+ char *buf = NULL;
+ unsigned i;
-/*
- * Enumerates and returns all interfaces on Solaris.
- * Uses the same code for IPv4 and IPv6.
- */
-static netif *enumIPvXInterfaces(JNIEnv *env, int sock, netif *ifs, int family) {
- struct lifconf ifc;
- struct lifreq *ifr;
- int n;
- char *buf;
- struct lifnum numifs;
- unsigned bufsize;
-
- // Get the interface count
- numifs.lifn_family = family;
+ // call SIOCGLIFNUM to get the size of SIOCGIFCONF buffer
+ numifs.lifn_family = AF_INET;
numifs.lifn_flags = 0;
if (ioctl(sock, SIOCGLIFNUM, (char *)&numifs) < 0) {
JNU_ThrowByNameWithMessageAndLastError
@@ -1612,14 +1677,12 @@
return ifs;
}
- // Enumerate the interface configurations
- bufsize = numifs.lifn_count * sizeof (struct lifreq);
- CHECKED_MALLOC3(buf, char *, bufsize);
-
- ifc.lifc_family = family;
+ // call SIOCGLIFCONF to enumerate the interfaces
+ ifc.lifc_len = numifs.lifn_count * sizeof(struct lifreq);
+ CHECKED_MALLOC3(buf, char *, ifc.lifc_len);
+ ifc.lifc_buf = buf;
+ ifc.lifc_family = AF_INET;
ifc.lifc_flags = 0;
- ifc.lifc_len = bufsize;
- ifc.lifc_buf = buf;
if (ioctl(sock, SIOCGLIFCONF, (char *)&ifc) < 0) {
JNU_ThrowByNameWithMessageAndLastError
(env, JNU_JAVANETPKG "SocketException", "ioctl(SIOCGLIFCONF) failed");
@@ -1627,43 +1690,119 @@
return ifs;
}
- // Iterate through each interface
- ifr = ifc.lifc_req;
- for (n=0; n<numifs.lifn_count; n++, ifr++) {
- int index = -1;
- struct lifreq if2;
+ // iterate through each interface
+ ifreqP = ifc.lifc_req;
+ for (i = 0; i < numifs.lifn_count; i++, ifreqP++) {
+ struct sockaddr addr, *broadaddrP = NULL;
- // Ignore either IPv4 or IPv6 addresses
- if (ifr->lifr_addr.ss_family != family) {
+ // ignore non IPv4 interfaces
+ if (ifreqP->lifr_addr.ss_family != AF_INET) {
continue;
}
-#ifdef AF_INET6
- if (ifr->lifr_addr.ss_family == AF_INET6) {
- struct sockaddr_in6 *s6= (struct sockaddr_in6 *)&(ifr->lifr_addr);
- s6->sin6_scope_id = getIndex(sock, ifr->lifr_name);
+ // save socket address
+ memcpy(&addr, &(ifreqP->lifr_addr), sizeof(struct sockaddr));
+
+ // determine broadcast address, if applicable
+ if ((ioctl(sock, SIOCGLIFFLAGS, ifreqP) == 0) &&
+ ifreqP->lifr_flags & IFF_BROADCAST) {
+
+ // restore socket address to ifreqP
+ memcpy(&(ifreqP->lifr_addr), &addr, sizeof(struct sockaddr));
+
+ // query broadcast address and set pointer to it
+ if (ioctl(sock, SIOCGLIFBRDADDR, ifreqP) == 0) {
+ broadaddrP = (struct sockaddr *)&(ifreqP->lifr_broadaddr);
+ }
}
-#endif
// add to the list
- ifs = addif(env, sock,ifr->lifr_name, ifs,
- (struct sockaddr *)&(ifr->lifr_addr),
- NULL, NULL, family, (short)ifr->lifr_addrlen);
+ ifs = addif(env, sock, ifreqP->lifr_name, ifs,
+ &addr, broadaddrP, AF_INET, (short)ifreqP->lifr_addrlen);
- // If an exception occurred we return immediately
+ // if an exception occurred we return immediately
if ((*env)->ExceptionOccurred(env)) {
free(buf);
return ifs;
}
-
}
+ // free buffer
free(buf);
return ifs;
}
+#if defined(AF_INET6)
+
+/*
+ * Enumerates and returns all IPv6 interfaces on Solaris.
+ */
+static netif *enumIPv6Interfaces(JNIEnv *env, int sock, netif *ifs) {
+ struct lifconf ifc;
+ struct lifreq *ifreqP;
+ struct lifnum numifs;
+ char *buf = NULL;
+ unsigned i;
+
+ // call SIOCGLIFNUM to get the size of SIOCGLIFCONF buffer
+ numifs.lifn_family = AF_INET6;
+ numifs.lifn_flags = 0;
+ if (ioctl(sock, SIOCGLIFNUM, (char *)&numifs) < 0) {
+ JNU_ThrowByNameWithMessageAndLastError
+ (env, JNU_JAVANETPKG "SocketException", "ioctl(SIOCGLIFNUM) failed");
+ return ifs;
+ }
+
+ // call SIOCGLIFCONF to enumerate the interfaces
+ ifc.lifc_len = numifs.lifn_count * sizeof(struct lifreq);
+ CHECKED_MALLOC3(buf, char *, ifc.lifc_len);
+ ifc.lifc_buf = buf;
+ ifc.lifc_family = AF_INET6;
+ ifc.lifc_flags = 0;
+ if (ioctl(sock, SIOCGLIFCONF, (char *)&ifc) < 0) {
+ JNU_ThrowByNameWithMessageAndLastError
+ (env, JNU_JAVANETPKG "SocketException", "ioctl(SIOCGLIFCONF) failed");
+ free(buf);
+ return ifs;
+ }
+
+ // iterate through each interface
+ ifreqP = ifc.lifc_req;
+ for (i = 0; i < numifs.lifn_count; i++, ifreqP++) {
+
+ // ignore non IPv6 interfaces
+ if (ifreqP->lifr_addr.ss_family != AF_INET6) {
+ continue;
+ }
+
+ // set scope ID to interface index
+ ((struct sockaddr_in6 *)&(ifreqP->lifr_addr))->sin6_scope_id =
+ getIndex(sock, ifreqP->lifr_name);
+
+ // add to the list
+ ifs = addif(env, sock, ifreqP->lifr_name, ifs,
+ (struct sockaddr *)&(ifreqP->lifr_addr),
+ NULL, AF_INET6, (short)ifreqP->lifr_addrlen);
+
+ // if an exception occurred we return immediately
+ if ((*env)->ExceptionOccurred(env)) {
+ free(buf);
+ return ifs;
+ }
+ }
+
+ // free buffer
+ free(buf);
+ return ifs;
+}
+
+#endif /* AF_INET6 */
+
+/*
+ * Try to get the interface index.
+ * (Not supported on Solaris 2.6 or 7)
+ */
static int getIndex(int sock, const char *name) {
- // Try to get the interface index. (Not supported on Solaris 2.6 or 7)
struct lifreq if2;
memset((char *)&if2, 0, sizeof(if2));
strncpy(if2.lifr_name, name, sizeof(if2.lifr_name) - 1);
@@ -1676,69 +1815,12 @@
}
/*
- * Returns the IPv4 broadcast address of a named interface, if it exists.
- * Returns 0 if it doesn't have one.
- */
-static struct sockaddr *getBroadcast
- (JNIEnv *env, int sock, const char *ifname, struct sockaddr *brdcast_store)
-{
- struct sockaddr *ret = NULL;
- struct lifreq if2;
- memset((char *)&if2, 0, sizeof(if2));
- strncpy(if2.lifr_name, ifname, sizeof(if2.lifr_name) - 1);
-
- // Let's make sure the interface does have a broadcast address
- if (ioctl(sock, SIOCGLIFFLAGS, (char *)&if2) < 0) {
- JNU_ThrowByNameWithMessageAndLastError
- (env, JNU_JAVANETPKG "SocketException", "ioctl(SIOCGLIFFLAGS) failed");
- return ret;
- }
-
- if (if2.lifr_flags & IFF_BROADCAST) {
- // It does, let's retrieve it
- if (ioctl(sock, SIOCGLIFBRDADDR, (char *)&if2) < 0) {
- JNU_ThrowByNameWithMessageAndLastError
- (env, JNU_JAVANETPKG "SocketException", "ioctl(SIOCGLIFBRDADDR) failed");
- return ret;
- }
-
- ret = brdcast_store;
- memcpy(ret, &if2.lifr_broadaddr, sizeof(struct sockaddr));
- }
-
- return ret;
-}
-
-/*
- * Returns the IPv4 subnet prefix length (aka subnet mask) for the named
- * interface, if it has one, otherwise return -1.
- */
-static short getSubnet(JNIEnv *env, int sock, const char *ifname) {
- unsigned int mask;
- short ret;
- struct lifreq if2;
- memset((char *)&if2, 0, sizeof(if2));
- strncpy(if2.lifr_name, ifname, sizeof(if2.lifr_name) - 1);
-
- if (ioctl(sock, SIOCGLIFNETMASK, (char *)&if2) < 0) {
- JNU_ThrowByNameWithMessageAndLastError
- (env, JNU_JAVANETPKG "SocketException", "ioctl(SIOCGLIFNETMASK) failed");
- return -1;
- }
-
- return computeMaskFromAddress(&(if2.lifr_addr));
-}
-
-
-#define DEV_PREFIX "/dev/"
-
-/*
* Solaris specific DLPI code to get hardware address from a device.
* Unfortunately, at least up to Solaris X, you have to have special
* privileges (i.e. be root).
*/
static int getMacFromDevice
- (JNIEnv *env, const char* ifname, unsigned char* retbuf)
+ (JNIEnv *env, const char *ifname, unsigned char *retbuf)
{
char style1dev[MAXPATHLEN];
int fd;
@@ -1796,23 +1878,20 @@
* MAC address. Returns -1 if there is no hardware address on that interface.
*/
static int getMacAddress
- (JNIEnv *env, int sock, const char *ifname, const struct in_addr* addr,
+ (JNIEnv *env, int sock, const char *ifname, const struct in_addr *addr,
unsigned char *buf)
{
- struct arpreq arpreq;
- struct sockaddr_in* sin;
- struct sockaddr_in ipAddr;
+ struct lifreq if2;
int len, i;
- struct lifreq lif;
// First, try the new (S11) SIOCGLIFHWADDR ioctl(). If that fails
// try the old way.
- memset(&lif, 0, sizeof(lif));
- strlcpy(lif.lifr_name, ifname, sizeof(lif.lifr_name));
+ memset((char *)&if2, 0, sizeof(if2));
+ strncpy(if2.lifr_name, ifname, sizeof(if2.lifr_name) - 1);
- if (ioctl(sock, SIOCGLIFHWADDR, &lif) != -1) {
+ if (ioctl(sock, SIOCGLIFHWADDR, &if2) != -1) {
struct sockaddr_dl *sp;
- sp = (struct sockaddr_dl *)&lif.lifr_addr;
+ sp = (struct sockaddr_dl *)&if2.lifr_addr;
memcpy(buf, &sp->sdl_data[0], sp->sdl_alen);
return sp->sdl_alen;
}
@@ -1823,6 +1902,10 @@
if ((len = getMacFromDevice(env, ifname, buf)) == 0) {
// DLPI failed - trying to do arp lookup
+ struct arpreq arpreq;
+ struct sockaddr_in *sin;
+ struct sockaddr_in ipAddr;
+
if (addr == NULL) {
// No IPv4 address for that interface, so can't do an ARP lookup.
return -1;
@@ -1830,8 +1913,8 @@
len = 6; //???
- sin = (struct sockaddr_in *) &arpreq.arp_pa;
- memset((char *) &arpreq, 0, sizeof(struct arpreq));
+ sin = (struct sockaddr_in *)&arpreq.arp_pa;
+ memset((char *)&arpreq, 0, sizeof(struct arpreq));
ipAddr.sin_port = 0;
ipAddr.sin_family = AF_INET;
memcpy(&ipAddr.sin_addr, addr, sizeof(struct in_addr));
@@ -1842,19 +1925,19 @@
return -1;
}
- memcpy(buf, &arpreq.arp_ha.sa_data[0], len );
+ memcpy(buf, &arpreq.arp_ha.sa_data[0], len);
}
- // All bytes to 0 means no hardware address.
+ // all bytes to 0 means no hardware address
for (i = 0; i < len; i++) {
- if (buf[i] != 0)
- return len;
+ if (buf[i] != 0)
+ return len;
}
return -1;
}
-static int getMTU(JNIEnv *env, int sock, const char *ifname) {
+static int getMTU(JNIEnv *env, int sock, const char *ifname) {
struct lifreq if2;
memset((char *)&if2, 0, sizeof(if2));
strncpy(if2.lifr_name, ifname, sizeof(if2.lifr_name) - 1);
@@ -1881,47 +1964,43 @@
return 0;
}
-
-#endif /* __solaris__ */
-
+#endif /* __solaris__ */
/** BSD **/
-#ifdef _ALLBSD_SOURCE
+#if defined(_ALLBSD_SOURCE)
+#if defined(AF_INET6)
/*
- * Opens a socket for further ioct calls. Tries AF_INET socket first and
- * if it falls return AF_INET6 socket.
+ * Opens a socket for further ioctl calls. Tries AF_INET socket first and
+ * if it fails return AF_INET6 socket.
*/
-#ifdef AF_INET6
static int openSocketWithFallback(JNIEnv *env, const char *ifname) {
int sock;
- struct ifreq if2;
- if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
- if (errno == EPROTONOSUPPORT) {
- if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
- JNU_ThrowByNameWithMessageAndLastError
- (env, JNU_JAVANETPKG "SocketException", "IPV6 Socket creation failed");
- return -1;
- }
- } else { // errno is not NOSUPPORT
- JNU_ThrowByNameWithMessageAndLastError
- (env, JNU_JAVANETPKG "SocketException", "IPV4 Socket creation failed");
- return -1;
- }
- }
+ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ if (errno == EPROTONOSUPPORT) {
+ if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ JNU_ThrowByNameWithMessageAndLastError
+ (env, JNU_JAVANETPKG "SocketException", "IPV6 Socket creation failed");
+ return -1;
+ }
+ } else { // errno is not NOSUPPORT
+ JNU_ThrowByNameWithMessageAndLastError
+ (env, JNU_JAVANETPKG "SocketException", "IPV4 Socket creation failed");
+ return -1;
+ }
+ }
- return sock;
+ return sock;
}
-
#else
static int openSocketWithFallback(JNIEnv *env, const char *ifname) {
- return openSocket(env,AF_INET);
+ return openSocket(env, AF_INET);
}
#endif
/*
- * Enumerates and returns all IPv4 interfaces.
+ * Enumerates and returns all IPv4 interfaces on BSD.
*/
static netif *enumIPv4Interfaces(JNIEnv *env, int sock, netif *ifs) {
struct ifaddrs *ifa, *origifa;
@@ -1933,23 +2012,25 @@
}
for (ifa = origifa; ifa != NULL; ifa = ifa->ifa_next) {
- struct sockaddr* ifa_broadaddr = NULL;
+ struct sockaddr *broadaddrP = NULL;
- // Skip non-AF_INET entries.
+ // ignore non IPv4 interfaces
if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_INET)
continue;
// set ifa_broadaddr, if there is one
if ((ifa->ifa_flags & IFF_POINTOPOINT) == 0 &&
ifa->ifa_flags & IFF_BROADCAST) {
- ifa_broadaddr = ifa->ifa_broadaddr;
+ broadaddrP = ifa->ifa_dstaddr;
}
- // Add to the list.
+ // add interface to the list
ifs = addif(env, sock, ifa->ifa_name, ifs, ifa->ifa_addr,
- ifa_broadaddr, ifa->ifa_netmask, AF_INET, 0);
+ broadaddrP, AF_INET,
+ translateIPv4AddressToPrefix((struct sockaddr_in *)
+ ifa->ifa_netmask));
- // If an exception occurred then free the list.
+ // if an exception occurred then free the list
if ((*env)->ExceptionOccurred(env)) {
freeifaddrs(origifa);
freeif(ifs);
@@ -1957,44 +2038,18 @@
}
}
- // Free socket and buffer
+ // free ifaddrs buffer
freeifaddrs(origifa);
return ifs;
}
-#ifdef AF_INET6
-/*
- * Determines the prefix on BSD for IPv6 interfaces.
- */
-static int prefix(void *val, int size) {
- u_char *name = (u_char *)val;
- int byte, bit, prefix = 0;
-
- for (byte = 0; byte < size; byte++, prefix += 8)
- if (name[byte] != 0xff)
- break;
- if (byte == size)
- return prefix;
- for (bit = 7; bit != 0; bit--, prefix++)
- if (!(name[byte] & (1 << bit)))
- break;
- for (; bit != 0; bit--)
- if (name[byte] & (1 << bit))
- return (0);
- byte++;
- for (; byte < size; byte++)
- if (name[byte])
- return (0);
- return prefix;
-}
+#if defined(AF_INET6)
/*
* Enumerates and returns all IPv6 interfaces on BSD.
*/
static netif *enumIPv6Interfaces(JNIEnv *env, int sock, netif *ifs) {
struct ifaddrs *ifa, *origifa;
- struct sockaddr_in6 *sin6;
- struct in6_ifreq ifr6;
if (getifaddrs(&origifa) != 0) {
JNU_ThrowByNameWithMessageAndLastError
@@ -2003,31 +2058,21 @@
}
for (ifa = origifa; ifa != NULL; ifa = ifa->ifa_next) {
-
- // Skip non-AF_INET6 entries.
+ // ignore non IPv6 interfaces
if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_INET6)
continue;
- memset(&ifr6, 0, sizeof(ifr6));
- strlcpy(ifr6.ifr_name, ifa->ifa_name, sizeof(ifr6.ifr_name));
- memcpy(&ifr6.ifr_addr, ifa->ifa_addr,
- MIN(sizeof(ifr6.ifr_addr), ifa->ifa_addr->sa_len));
+ // set scope ID to interface index
+ ((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_scope_id =
+ getIndex(sock, ifa->ifa_name);
- if (ioctl(sock, SIOCGIFNETMASK_IN6, (caddr_t)&ifr6) < 0) {
- JNU_ThrowByNameWithMessageAndLastError
- (env, JNU_JAVANETPKG "SocketException", "ioctl(SIOCGIFNETMASK_IN6) failed");
- freeifaddrs(origifa);
- freeif(ifs);
- return NULL;
- }
+ // add interface to the list
+ ifs = addif(env, sock, ifa->ifa_name, ifs, ifa->ifa_addr, NULL,
+ AF_INET6,
+ translateIPv6AddressToPrefix((struct sockaddr_in6 *)
+ ifa->ifa_netmask));
- // Add to the list.
- sin6 = (struct sockaddr_in6 *)&ifr6.ifr_addr;
- ifs = addif(env, sock, ifa->ifa_name, ifs, ifa->ifa_addr, NULL, NULL,
- AF_INET6,
- (short)prefix(&sin6->sin6_addr, sizeof(struct in6_addr)));
-
- // If an exception occurred then free the list.
+ // if an exception occurred then free the list
if ((*env)->ExceptionOccurred(env)) {
freeifaddrs(origifa);
freeif(ifs);
@@ -2035,16 +2080,21 @@
}
}
- // Free socket and ifaddrs buffer
+ // free ifaddrs buffer
freeifaddrs(origifa);
return ifs;
}
-#endif
+
+#endif /* AF_INET6 */
+/*
+ * Try to get the interface index.
+ */
static int getIndex(int sock, const char *name) {
-#ifdef __FreeBSD__
- // Try to get the interface index
- // (Not supported on Solaris 2.6 or 7)
+#if !defined(__FreeBSD__)
+ int index = if_nametoindex(name);
+ return (index == 0) ? -1 : index;
+#else
struct ifreq if2;
memset((char *)&if2, 0, sizeof(if2));
strncpy(if2.ifr_name, name, sizeof(if2.ifr_name) - 1);
@@ -2054,74 +2104,16 @@
}
return if2.ifr_index;
-#else
- // Try to get the interface index using BSD specific if_nametoindex
- int index = if_nametoindex(name);
- return (index == 0) ? -1 : index;
#endif
}
/*
- * Returns the IPv4 broadcast address of a named interface, if it exists.
- * Returns 0 if it doesn't have one.
- */
-static struct sockaddr *getBroadcast
- (JNIEnv *env, int sock, const char *ifname, struct sockaddr *brdcast_store)
-{
- struct sockaddr *ret = NULL;
- struct ifreq if2;
- memset((char *)&if2, 0, sizeof(if2));
- strncpy(if2.ifr_name, ifname, sizeof(if2.ifr_name) - 1);
-
- // Make sure the interface does have a broadcast address
- if (ioctl(sock, SIOCGIFFLAGS, (char *)&if2) < 0) {
- JNU_ThrowByNameWithMessageAndLastError
- (env, JNU_JAVANETPKG "SocketException", "ioctl(SIOCGIFFLAGS) failed");
- return ret;
- }
-
- if (if2.ifr_flags & IFF_BROADCAST) {
- // It does, let's retrieve it
- if (ioctl(sock, SIOCGIFBRDADDR, (char *)&if2) < 0) {
- JNU_ThrowByNameWithMessageAndLastError
- (env, JNU_JAVANETPKG "SocketException", "ioctl(SIOCGIFBRDADDR) failed");
- return ret;
- }
-
- ret = brdcast_store;
- memcpy(ret, &if2.ifr_broadaddr, sizeof(struct sockaddr));
- }
-
- return ret;
-}
-
-/*
- * Returns the IPv4 subnet prefix length (aka subnet mask) for the named
- * interface, if it has one, otherwise return -1.
- */
-static short getSubnet(JNIEnv *env, int sock, const char *ifname) {
- unsigned int mask;
- short ret;
- struct ifreq if2;
- memset((char *)&if2, 0, sizeof(if2));
- strncpy(if2.ifr_name, ifname, sizeof(if2.ifr_name) - 1);
-
- if (ioctl(sock, SIOCGIFNETMASK, (char *)&if2) < 0) {
- JNU_ThrowByNameWithMessageAndLastError
- (env, JNU_JAVANETPKG "SocketException", "ioctl(SIOCGIFNETMASK) failed");
- return -1;
- }
-
- return computeMaskFromAddress(&(if2.ifr_addr));
-}
-
-/*
* Gets the Hardware address (usually MAC address) for the named interface.
- * return puts the data in buf, and returns the length, in byte, of the
+ * On return puts the data in buf, and returns the length, in byte, of the
* MAC address. Returns -1 if there is no hardware address on that interface.
*/
static int getMacAddress
- (JNIEnv *env, int sock, const char* ifname, const struct in_addr* addr,
+ (JNIEnv *env, int sock, const char *ifname, const struct in_addr *addr,
unsigned char *buf)
{
struct ifaddrs *ifa0, *ifa;
@@ -2150,7 +2142,7 @@
return -1;
}
-static int getMTU(JNIEnv *env, int sock, const char *ifname) {
+static int getMTU(JNIEnv *env, int sock, const char *ifname) {
struct ifreq if2;
memset((char *)&if2, 0, sizeof(if2));
strncpy(if2.ifr_name, ifname, sizeof(if2.ifr_name) - 1);
@@ -2161,12 +2153,11 @@
return -1;
}
- return if2.ifr_mtu;
+ return if2.ifr_mtu;
}
static int getFlags(int sock, const char *ifname, int *flags) {
struct ifreq if2;
- int ret = -1;
memset((char *)&if2, 0, sizeof(if2));
strncpy(if2.ifr_name, ifname, sizeof(if2.ifr_name) - 1);
@@ -2181,4 +2172,4 @@
}
return 0;
}
-#endif /* __ALLBSD_SOURCE__ */
+#endif /* _ALLBSD_SOURCE */
--- a/jdk/src/java.base/windows/classes/java/lang/ClassLoaderHelper.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/windows/classes/java/lang/ClassLoaderHelper.java Thu Jul 21 20:09:19 2016 -0700
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.lang;
import java.io.File;
--- a/jdk/src/java.base/windows/classes/sun/nio/ch/PipeImpl.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/windows/classes/sun/nio/ch/PipeImpl.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -37,6 +37,7 @@
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
+import java.security.SecureRandom;
import java.util.Random;
@@ -47,24 +48,16 @@
class PipeImpl
extends Pipe
{
+ // Number of bytes in the secret handshake.
+ private static final int NUM_SECRET_BYTES = 16;
+
+ // Random object for handshake values
+ private static final Random RANDOM_NUMBER_GENERATOR = new SecureRandom();
// Source and sink channels
private SourceChannel source;
private SinkChannel sink;
- // Random object for handshake values
- private static final Random rnd;
-
- static {
- byte[] someBytes = new byte[8];
- boolean resultOK = IOUtil.randomBytes(someBytes);
- if (resultOK) {
- rnd = new Random(ByteBuffer.wrap(someBytes).getLong());
- } else {
- rnd = new Random();
- }
- }
-
private class Initializer
implements PrivilegedExceptionAction<Void>
{
@@ -112,6 +105,10 @@
SocketChannel sc2 = null;
try {
+ // Create secret with a backing array.
+ ByteBuffer secret = ByteBuffer.allocate(NUM_SECRET_BYTES);
+ ByteBuffer bb = ByteBuffer.allocate(NUM_SECRET_BYTES);
+
// Loopback address
InetAddress lb = InetAddress.getByName("127.0.0.1");
assert(lb.isLoopbackAddress());
@@ -128,18 +125,22 @@
// Establish connection (assume connections are eagerly
// accepted)
sc1 = SocketChannel.open(sa);
- ByteBuffer bb = ByteBuffer.allocate(8);
- long secret = rnd.nextLong();
- bb.putLong(secret).flip();
- sc1.write(bb);
+ RANDOM_NUMBER_GENERATOR.nextBytes(secret.array());
+ do {
+ sc1.write(secret);
+ } while (secret.hasRemaining());
+ secret.rewind();
// Get a connection and verify it is legitimate
sc2 = ssc.accept();
- bb.clear();
- sc2.read(bb);
+ do {
+ sc2.read(bb);
+ } while (bb.hasRemaining());
bb.rewind();
- if (bb.getLong() == secret)
+
+ if (bb.equals(secret))
break;
+
sc2.close();
sc1.close();
}
--- a/jdk/src/java.base/windows/native/libjava/jni_util_md.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/windows/native/libjava/jni_util_md.c Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004, 2014 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2014, 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
--- a/jdk/src/java.base/windows/native/libjli/java_md.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.base/windows/native/libjli/java_md.c Thu Jul 21 20:09:19 2016 -0700
@@ -1006,10 +1006,11 @@
// make a copy of the args which will be expanded in java if required.
nargv = (char **)JLI_MemAlloc(argc * sizeof(char*));
for (i = 0; i < argc; i++) {
+ jboolean arg_expand;
j = appArgIdx[i];
- jboolean arg_expand = (JLI_StrCmp(stdargs[j].arg, strv[i]) == 0)
- ? stdargs[j].has_wildcard
- : JNI_FALSE;
+ arg_expand = (JLI_StrCmp(stdargs[j].arg, strv[i]) == 0)
+ ? stdargs[j].has_wildcard
+ : JNI_FALSE;
if (needs_expansion == JNI_FALSE)
needs_expansion = arg_expand;
--- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Font.cpp Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Font.cpp Thu Jul 21 20:09:19 2016 -0700
@@ -256,7 +256,7 @@
AwtFont* awtFont = NULL;
jobjectArray compFont = NULL;
- int cfnum;
+ int cfnum = 0;
try {
if (env->EnsureLocalCapacity(3) < 0)
@@ -264,7 +264,9 @@
if (IsMultiFont(env, font)) {
compFont = GetComponentFonts(env, font);
- cfnum = env->GetArrayLength(compFont);
+ if (compFont != NULL) {
+ cfnum = env->GetArrayLength(compFont);
+ }
} else {
compFont = NULL;
cfnum = 0;
@@ -647,7 +649,9 @@
if (IsMultiFont(env, font)) {
array = GetComponentFonts(env, font);
- num = env->GetArrayLength(array);
+ if (array != NULL) {
+ num = env->GetArrayLength(array);
+ }
} else {
array = NULL;
num = 0;
@@ -705,14 +709,16 @@
if (IsMultiFont(env, font)) {
jobject peer = env->CallObjectMethod(font, AwtFont::peerMID);
- array = (jobjectArray)(env->CallObjectMethod(
- peer, AwtFont::makeConvertedMultiFontStringMID, str));
- DASSERT(!safe_ExceptionOccurred(env));
+ if (peer != NULL) {
+ array = (jobjectArray)(env->CallObjectMethod(
+ peer, AwtFont::makeConvertedMultiFontStringMID, str));
+ DASSERT(!safe_ExceptionOccurred(env));
- if (array != NULL) {
- arrayLength = env->GetArrayLength(array);
+ if (array != NULL) {
+ arrayLength = env->GetArrayLength(array);
+ }
+ env->DeleteLocalRef(peer);
}
- env->DeleteLocalRef(peer);
} else {
array = NULL;
arrayLength = 0;
--- a/jdk/src/java.desktop/windows/native/libawt/windows/awt_Font.h Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.desktop/windows/native/libawt/windows/awt_Font.h Thu Jul 21 20:09:19 2016 -0700
@@ -230,11 +230,14 @@
INLINE static jobjectArray GetComponentFonts(JNIEnv *env,
jobject font) {
jobject platformFont = env->CallObjectMethod(font, AwtFont::peerMID);
- jobjectArray result =
- (jobjectArray)(env->GetObjectField(platformFont,
- AwtFont::componentFontsID));
- env->DeleteLocalRef(platformFont);
- return result;
+ if (platformFont != NULL) {
+ jobjectArray result =
+ (jobjectArray)(env->GetObjectField(platformFont,
+ AwtFont::componentFontsID));
+ env->DeleteLocalRef(platformFont);
+ return result;
+ }
+ return NULL;
}
/*
--- a/jdk/src/java.httpclient/share/classes/java/net/http/AsyncConnection.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/AsyncConnection.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.nio.ByteBuffer;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/AsyncEvent.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/AsyncEvent.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLConnection.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLConnection.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.io.IOException;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLDelegate.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/AsyncSSLDelegate.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.io.Closeable;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/AuthenticationFilter.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/AuthenticationFilter.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.io.IOException;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/BufferHandler.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/BufferHandler.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/ByteBufferConsumer.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/ByteBufferConsumer.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/ByteBufferGenerator.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/ByteBufferGenerator.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/CharsetToolkit.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/CharsetToolkit.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.nio.ByteBuffer;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/ConnectionPool.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/ConnectionPool.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.net.InetSocketAddress;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/ContinuationFrame.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/ContinuationFrame.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/CookieFilter.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/CookieFilter.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/DataFrame.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/DataFrame.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/ErrorFrame.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/ErrorFrame.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/Exchange.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/Exchange.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.io.IOException;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/ExchangeImpl.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/ExchangeImpl.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.io.IOException;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/ExecutorWrapper.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/ExecutorWrapper.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.security.AccessControlContext;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/FilterFactory.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/FilterFactory.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/FrameReader.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/FrameReader.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,8 +1,28 @@
/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.nio.ByteBuffer;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/GoAwayFrame.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/GoAwayFrame.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/HeaderFilter.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HeaderFilter.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/HeaderFrame.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HeaderFrame.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/HeaderParser.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HeaderParser.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.util.Iterator;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/HeadersFrame.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HeadersFrame.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/Http1Exchange.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http1Exchange.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.io.IOException;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/Http1Request.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http1Request.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.io.IOException;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/Http1Response.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http1Response.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.io.IOException;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/Http2ClientImpl.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http2ClientImpl.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.io.IOException;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/Http2Connection.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http2Connection.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.io.IOException;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/Http2Frame.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/Http2Frame.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.io.IOException;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpClientBuilderImpl.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpClientBuilderImpl.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpClientImpl.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpClientImpl.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import javax.net.ssl.SSLContext;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpConnection.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpConnection.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.io.Closeable;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpHeadersImpl.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpHeadersImpl.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.util.Collections;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestBuilderImpl.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestBuilderImpl.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.net.URI;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestImpl.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpRequestImpl.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.io.IOException;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpResponse.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpResponse.java Thu Jul 21 20:09:19 2016 -0700
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.net.http;
import java.io.IOException;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/HttpResponseImpl.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/HttpResponseImpl.java Thu Jul 21 20:09:19 2016 -0700
@@ -178,7 +178,7 @@
*/
RawChannel rawChannel() throws IOException {
if (rawchan == null) {
- rawchan = new RawChannel(request.client(), connection);
+ rawchan = new RawChannelImpl(request.client(), connection);
}
return rawchan;
}
--- a/jdk/src/java.httpclient/share/classes/java/net/http/ImmutableHeaders.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/ImmutableHeaders.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/Log.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/Log.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.util.Locale;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/MultiExchange.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/MultiExchange.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/OutgoingHeaders.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/OutgoingHeaders.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/Pair.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/Pair.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,20 +1,20 @@
/*
- * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General License version 2 only, as
+ * under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General License
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
- * You should have received a copy of the GNU General License version
+ * You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.net.http;
/**
--- a/jdk/src/java.httpclient/share/classes/java/net/http/PingFrame.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/PingFrame.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/PlainHttpConnection.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/PlainHttpConnection.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.io.IOException;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/PlainProxyConnection.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/PlainProxyConnection.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/PlainTunnelingConnection.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/PlainTunnelingConnection.java Thu Jul 21 20:09:19 2016 -0700
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.net.http;
import java.io.IOException;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/PriorityFrame.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/PriorityFrame.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/PushPromiseFrame.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/PushPromiseFrame.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/Queue.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/Queue.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/RawChannel.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/RawChannel.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,151 +20,43 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.io.IOException;
import java.nio.ByteBuffer;
-import java.nio.channels.ByteChannel;
-import java.nio.channels.GatheringByteChannel;
-import java.nio.channels.SelectableChannel;
-import java.nio.channels.SelectionKey;
-import java.nio.channels.SocketChannel;
-//
-// Used to implement WebSocket. Each RawChannel corresponds to a TCP connection
-// (SocketChannel) but is connected to a Selector and an ExecutorService for
-// invoking the send and receive callbacks. Also includes SSL processing.
-//
-final class RawChannel implements ByteChannel, GatheringByteChannel {
+/*
+ * I/O abstraction used to implement WebSocket.
+ */
+public interface RawChannel {
- private final HttpClientImpl client;
- private final HttpConnection connection;
+ interface RawEvent {
- private interface RawEvent {
-
- /**
- * must return the selector interest op flags OR'd.
+ /*
+ * Must return the selector interest op flags.
*/
int interestOps();
- /**
- * called when event occurs.
+ /*
+ * Called when event occurs.
*/
void handle();
}
- interface NonBlockingEvent extends RawEvent {
- }
-
- RawChannel(HttpClientImpl client, HttpConnection connection)
- throws IOException {
- this.client = client;
- this.connection = connection;
- SocketChannel chan = connection.channel();
- client.cancelRegistration(chan);
- chan.configureBlocking(false);
- }
-
- SocketChannel socketChannel() {
- return connection.channel();
- }
-
- ByteBuffer getRemaining() {
- return connection.getRemaining();
- }
-
- private class RawAsyncEvent extends AsyncEvent {
-
- private final RawEvent re;
-
- RawAsyncEvent(RawEvent re) {
- super(AsyncEvent.BLOCKING); // BLOCKING & !REPEATING
- this.re = re;
- }
-
- RawAsyncEvent(RawEvent re, int flags) {
- super(flags);
- this.re = re;
- }
-
- @Override
- public SelectableChannel channel() {
- return connection.channel();
- }
-
- // must return the selector interest op flags OR'd
- @Override
- public int interestOps() {
- return re.interestOps();
- }
-
- // called when event occurs
- @Override
- public void handle() {
- re.handle();
- }
-
- @Override
- public void abort() { }
- }
-
- private class NonBlockingRawAsyncEvent extends RawAsyncEvent {
-
- NonBlockingRawAsyncEvent(RawEvent re) {
- super(re, 0); // !BLOCKING & !REPEATING
- }
- }
-
/*
* Register given event whose callback will be called once only.
* (i.e. register new event for each callback)
*/
- public void registerEvent(RawEvent event) throws IOException {
- if (!(event instanceof NonBlockingEvent)) {
- throw new InternalError();
- }
- if ((event.interestOps() & SelectionKey.OP_READ) != 0
- && connection.buffer.hasRemaining()) {
- // FIXME: a hack to deal with leftovers from previous reads into an
- // internal buffer (works in conjunction with change in
- // java.net.http.PlainHttpConnection.readImpl(java.nio.ByteBuffer)
- connection.channel().configureBlocking(false);
- event.handle();
- } else {
- client.registerEvent(new NonBlockingRawAsyncEvent(event));
- }
- }
+ void registerEvent(RawEvent event) throws IOException;
- @Override
- public int read(ByteBuffer dst) throws IOException {
- assert !connection.channel().isBlocking();
- return connection.read(dst);
- }
+ int read(ByteBuffer dst) throws IOException;
- @Override
- public boolean isOpen() {
- return connection.isOpen();
- }
-
- @Override
- public void close() throws IOException {
- connection.close();
- }
+ long write(ByteBuffer[] src, int offset, int len) throws IOException;
- @Override
- public long write(ByteBuffer[] src) throws IOException {
- return connection.write(src, 0, src.length);
- }
+ boolean isOpen();
- @Override
- public long write(ByteBuffer[] src, int offset, int len)
- throws IOException {
- return connection.write(src, offset, len);
- }
-
- @Override
- public int write(ByteBuffer src) throws IOException {
- return (int) connection.write(src);
- }
+ void close() throws IOException;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/RawChannelImpl.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.net.http;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectableChannel;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.SocketChannel;
+
+/*
+ * Each RawChannel corresponds to a TCP connection (SocketChannel) but is
+ * connected to a Selector and an ExecutorService for invoking the send and
+ * receive callbacks. Also includes SSL processing.
+ */
+final class RawChannelImpl implements RawChannel {
+
+ private final HttpClientImpl client;
+ private final HttpConnection connection;
+
+ RawChannelImpl(HttpClientImpl client, HttpConnection connection)
+ throws IOException {
+ this.client = client;
+ this.connection = connection;
+ SocketChannel chan = connection.channel();
+ client.cancelRegistration(chan);
+ chan.configureBlocking(false);
+ }
+
+ private class NonBlockingRawAsyncEvent extends AsyncEvent {
+
+ private final RawEvent re;
+
+ NonBlockingRawAsyncEvent(RawEvent re) {
+ super(0); // !BLOCKING & !REPEATING
+ this.re = re;
+ }
+
+ @Override
+ public SelectableChannel channel() {
+ return connection.channel();
+ }
+
+ @Override
+ public int interestOps() {
+ return re.interestOps();
+ }
+
+ @Override
+ public void handle() {
+ re.handle();
+ }
+
+ @Override
+ public void abort() { }
+ }
+
+ @Override
+ public void registerEvent(RawEvent event) throws IOException {
+ if ((event.interestOps() & SelectionKey.OP_READ) != 0
+ && connection.buffer.hasRemaining()) {
+ // FIXME: a hack to deal with leftovers from previous reads into an
+ // internal buffer (works in conjunction with change in
+ // java.net.http.PlainHttpConnection.readImpl(java.nio.ByteBuffer)
+ connection.channel().configureBlocking(false);
+ event.handle();
+ } else {
+ client.registerEvent(new NonBlockingRawAsyncEvent(event));
+ }
+ }
+
+ @Override
+ public int read(ByteBuffer dst) throws IOException {
+ assert !connection.channel().isBlocking();
+ return connection.read(dst);
+ }
+
+ @Override
+ public long write(ByteBuffer[] src, int offset, int len) throws IOException {
+ return connection.write(src, offset, len);
+ }
+
+ @Override
+ public boolean isOpen() {
+ return connection.isOpen();
+ }
+
+ @Override
+ public void close() throws IOException {
+ connection.close();
+ }
+}
--- a/jdk/src/java.httpclient/share/classes/java/net/http/RedirectFilter.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/RedirectFilter.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/ResetFrame.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/ResetFrame.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/ResponseContent.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/ResponseContent.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.io.IOException;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/ResponseHeaders.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/ResponseHeaders.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.io.IOException;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/SSLConnection.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/SSLConnection.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.io.IOException;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/SSLDelegate.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/SSLDelegate.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.io.IOException;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/SSLTunnelConnection.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/SSLTunnelConnection.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.io.IOException;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/SettingsFrame.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/SettingsFrame.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/Stream.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/Stream.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
@@ -466,7 +467,7 @@
public synchronized void take(int amount) throws InterruptedException {
assert permits >= 0;
- while (permits < amount) {
+ while (amount > 0) {
int n = Math.min(amount, permits);
permits -= n;
amount -= n;
@@ -498,7 +499,7 @@
DataFrame getDataFrame() throws IOException, InterruptedException {
userRequestFlowController.take();
- int maxpayloadLen = connection.getMaxSendFrameSize() - 9;
+ int maxpayloadLen = connection.getMaxSendFrameSize();
ByteBuffer buffer = connection.getBuffer();
buffer.limit(maxpayloadLen);
boolean complete = requestProcessor.onRequestBodyChunk(buffer);
--- a/jdk/src/java.httpclient/share/classes/java/net/http/TimeoutEvent.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/TimeoutEvent.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/Utils.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/Utils.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import sun.net.NetProperties;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/WS.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/WS.java Thu Jul 21 20:09:19 2016 -0700
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.net.http;
import java.io.IOException;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/WSBuilder.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSBuilder.java Thu Jul 21 20:09:19 2016 -0700
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.net.http;
import java.net.URI;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/WSCharsetToolkit.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSCharsetToolkit.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -20,7 +20,9 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
+
package java.net.http;
import java.nio.ByteBuffer;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/WSDisposable.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSDisposable.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,20 +1,20 @@
/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General License version 2 only, as
+ * under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General License
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
- * You should have received a copy of the GNU General License version
+ * You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.net.http;
interface WSDisposable {
--- a/jdk/src/java.httpclient/share/classes/java/net/http/WSFrame.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSFrame.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,20 +1,20 @@
/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General License version 2 only, as
+ * under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General License
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
- * You should have received a copy of the GNU General License version
+ * You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.net.http;
import java.nio.ByteBuffer;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/WSFrameConsumer.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSFrameConsumer.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.net.http;
import java.net.http.WSFrame.Opcode;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/WSMessageConsumer.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSMessageConsumer.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.net.http;
import java.net.http.WebSocket.CloseCode;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/WSMessageSender.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSMessageSender.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,20 +1,20 @@
/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General License version 2 only, as
+ * under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General License
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
- * You should have received a copy of the GNU General License version
+ * You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.net.http;
import java.net.http.WSFrame.HeaderBuilder;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/WSOpeningHandshake.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSOpeningHandshake.java Thu Jul 21 20:09:19 2016 -0700
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.net.http;
import java.io.UncheckedIOException;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/WSOutgoingMessage.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSOutgoingMessage.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,20 +1,20 @@
/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General License version 2 only, as
+ * under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General License
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
- * You should have received a copy of the GNU General License version
+ * You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.net.http;
import java.nio.ByteBuffer;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/WSProtocolException.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSProtocolException.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,3 +1,28 @@
+/*
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
package java.net.http;
import java.net.http.WebSocket.CloseCode;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/WSReceiver.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSReceiver.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.net.http;
import java.io.IOException;
@@ -53,7 +54,7 @@
private final Supplier<WSShared<ByteBuffer>> buffersSupplier =
new WSSharedPool<>(() -> ByteBuffer.allocateDirect(32768), 2);
private final RawChannel channel;
- private final RawChannel.NonBlockingEvent channelEvent;
+ private final RawChannel.RawEvent channelEvent;
private final WSSignalHandler handler;
private final AtomicLong demand = new AtomicLong();
private final AtomicBoolean readable = new AtomicBoolean();
@@ -251,8 +252,8 @@
assert newDemand >= 0 : newDemand;
}
- private RawChannel.NonBlockingEvent createChannelEvent() {
- return new RawChannel.NonBlockingEvent() {
+ private RawChannel.RawEvent createChannelEvent() {
+ return new RawChannel.RawEvent() {
@Override
public int interestOps() {
--- a/jdk/src/java.httpclient/share/classes/java/net/http/WSShared.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSShared.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,20 +1,20 @@
/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General License version 2 only, as
+ * under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General License
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
- * You should have received a copy of the GNU General License version
+ * You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.net.http;
import java.nio.Buffer;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/WSSharedPool.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSSharedPool.java Thu Jul 21 20:09:19 2016 -0700
@@ -3,18 +3,18 @@
* 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;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/WSSignalHandler.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSSignalHandler.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,20 +1,20 @@
/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General License version 2 only, as
+ * under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General License
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
- * You should have received a copy of the GNU General License version
+ * You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.net.http;
import java.util.concurrent.Executor;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/WSTransmitter.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSTransmitter.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,20 +1,20 @@
/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General License version 2 only, as
+ * under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General License
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
- * You should have received a copy of the GNU General License version
+ * You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.net.http;
import java.net.http.WSOutgoingMessage.Binary;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/WSUtils.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSUtils.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,20 +1,20 @@
/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General License version 2 only, as
+ * under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General License
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
- * You should have received a copy of the GNU General License version
+ * You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.net.http;
import java.net.URI;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/WSWriter.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/WSWriter.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,20 +1,20 @@
/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General License version 2 only, as
+ * under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General License
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
- * You should have received a copy of the GNU General License version
+ * You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.net.http;
import java.io.IOException;
@@ -60,7 +61,7 @@
final class WSWriter {
private final RawChannel channel;
- private final RawChannel.NonBlockingEvent writeReadinessHandler;
+ private final RawChannel.RawEvent writeReadinessHandler;
private final Consumer<Throwable> completionCallback;
private ByteBuffer[] buffers;
private int offset;
@@ -110,8 +111,8 @@
return -1;
}
- private RawChannel.NonBlockingEvent createHandler() {
- return new RawChannel.NonBlockingEvent() {
+ private RawChannel.RawEvent createHandler() {
+ return new RawChannel.RawEvent() {
@Override
public int interestOps() {
--- a/jdk/src/java.httpclient/share/classes/java/net/http/WebSocket.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/WebSocket.java Thu Jul 21 20:09:19 2016 -0700
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.net.http;
import java.io.IOException;
--- a/jdk/src/java.httpclient/share/classes/java/net/http/WebSocketHandshakeException.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/WebSocketHandshakeException.java Thu Jul 21 20:09:19 2016 -0700
@@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package java.net.http;
/**
--- a/jdk/src/java.httpclient/share/classes/java/net/http/WindowUpdateFrame.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/java/net/http/WindowUpdateFrame.java Thu Jul 21 20:09:19 2016 -0700
@@ -20,6 +20,7 @@
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
+ * questions.
*/
package java.net.http;
--- a/jdk/src/java.httpclient/share/classes/module-info.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.httpclient/share/classes/module-info.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
--- a/jdk/src/java.instrument/share/classes/java/lang/instrument/ClassFileTransformer.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.instrument/share/classes/java/lang/instrument/ClassFileTransformer.java Thu Jul 21 20:09:19 2016 -0700
@@ -38,7 +38,7 @@
* A transformer of class files. An agent registers an implementation of this
* interface using the {@link Instrumentation#addTransformer addTransformer}
* method so that the transformer's {@link
- * ClassFileTransformer#transform(Module,String,Class,ProtectionDomain,byte[])
+ * ClassFileTransformer#transform(Module,ClassLoader,String,Class,ProtectionDomain,byte[])
* transform} method is invoked when classes are loaded,
* {@link Instrumentation#redefineClasses redefined}, or
* {@link Instrumentation#retransformClasses retransformed}. The implementation
@@ -170,13 +170,13 @@
/**
* Transforms the given class file and returns a new replacement class file.
* This method is invoked when the {@link Module Module} bearing {@link
- * ClassFileTransformer#transform(Module,String,Class,ProtectionDomain,byte[])
+ * ClassFileTransformer#transform(Module,ClassLoader,String,Class,ProtectionDomain,byte[])
* transform} is not overridden.
*
* @implSpec The default implementation returns null.
*
* @param loader the defining loader of the class to be transformed,
- * may be <code>null</code> if the bootstrap loader
+ * may be {@code null} if the bootstrap loader
* @param className the name of the class in the internal form of fully
* qualified class and interface names as defined in
* <i>The Java Virtual Machine Specification</i>.
@@ -208,9 +208,11 @@
*
* @implSpec The default implementation of this method invokes the
* {@link #transform(ClassLoader,String,Class,ProtectionDomain,byte[]) transform}
- * method with the {@link Module#getClassLoader() ClassLoader} for the module.
+ * method.
*
* @param module the module of the class to be transformed
+ * @param loader the defining loader of the class to be transformed,
+ * may be {@code null} if the bootstrap loader
* @param className the name of the class in the internal form of fully
* qualified class and interface names as defined in
* <i>The Java Virtual Machine Specification</i>.
@@ -230,15 +232,13 @@
*/
default byte[]
transform( Module module,
+ ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer)
throws IllegalClassFormatException {
- PrivilegedAction<ClassLoader> pa = module::getClassLoader;
- ClassLoader loader = AccessController.doPrivileged(pa);
-
// invoke the legacy transform method
return transform(loader,
className,
--- a/jdk/src/java.instrument/share/classes/java/lang/instrument/Instrumentation.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.instrument/share/classes/java/lang/instrument/Instrumentation.java Thu Jul 21 20:09:19 2016 -0700
@@ -162,7 +162,7 @@
* </li>
* <li>for each transformer that was added with <code>canRetransform</code>
* false, the bytes returned by
- * {@link ClassFileTransformer#transform(Module,String,Class,ProtectionDomain,byte[])
+ * {@link ClassFileTransformer#transform(Module,ClassLoader,String,Class,ProtectionDomain,byte[])
* transform} during the last class load or redefine are
* reused as the output of the transformation; note that this is
* equivalent to reapplying the previous transformation, unaltered;
@@ -170,7 +170,7 @@
* </li>
* <li>for each transformer that was added with <code>canRetransform</code>
* true, the
- * {@link ClassFileTransformer#transform(Module,String,Class,ProtectionDomain,byte[])
+ * {@link ClassFileTransformer#transform(Module,ClassLoader,String,Class,ProtectionDomain,byte[])
* transform} method is called in these transformers
* </li>
* <li>the transformed class file bytes are installed as the new
--- a/jdk/src/java.instrument/share/classes/sun/instrument/InstrumentationImpl.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.instrument/share/classes/sun/instrument/InstrumentationImpl.java Thu Jul 21 20:09:19 2016 -0700
@@ -420,8 +420,8 @@
// WARNING: the native code knows the name & signature of this method
private byte[]
- transform( ClassLoader loader,
- Module module,
+ transform( Module module,
+ ClassLoader loader,
String classname,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
@@ -444,6 +444,7 @@
return null; // no manager, no transform
} else {
return mgr.transform( module,
+ loader,
classname,
classBeingRedefined,
protectionDomain,
--- a/jdk/src/java.instrument/share/classes/sun/instrument/TransformerManager.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.instrument/share/classes/sun/instrument/TransformerManager.java Thu Jul 21 20:09:19 2016 -0700
@@ -169,6 +169,7 @@
public byte[]
transform( Module module,
+ ClassLoader loader,
String classname,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
@@ -187,6 +188,7 @@
try {
transformedBytes = transformer.transform( module,
+ loader,
classname,
classBeingRedefined,
protectionDomain,
--- a/jdk/src/java.instrument/share/native/libinstrument/JPLISAgent.c Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.instrument/share/native/libinstrument/JPLISAgent.c Thu Jul 21 20:09:19 2016 -0700
@@ -771,12 +771,11 @@
}
static jobject
-getModuleObject(JNIEnv * jnienv,
+getModuleObject(jvmtiEnv* jvmti,
jobject loaderObject,
const char* cname) {
- jboolean errorOutstanding = JNI_FALSE;
+ jvmtiError err = JVMTI_ERROR_NONE;
jobject moduleObject = NULL;
- jstring package = NULL;
/* find last slash in the class name */
char* last_slash = (cname == NULL) ? NULL : strrchr(cname, '/');
@@ -789,14 +788,9 @@
}
pkg_name_buf[len] = '\0';
- package = (*jnienv)->NewStringUTF(jnienv, pkg_name_buf);
- jplis_assert_msg(package != NULL, "OOM error in NewStringUTF");
+ err = (*jvmti)->GetNamedModule(jvmti, loaderObject, pkg_name_buf, &moduleObject);
+ jplis_assert_msg(err == JVMTI_ERROR_NONE, "error in the JVMTI GetNamedModule");
- moduleObject = JVM_GetModuleByPackageName(jnienv, loaderObject, package);
-
- errorOutstanding = checkForAndClearThrowable(jnienv);
- jplis_assert_msg(!errorOutstanding,
- "error in lookup of a module of the class being instrumented");
free((void*)pkg_name_buf);
return moduleObject;
}
@@ -862,7 +856,7 @@
jobject moduleObject = NULL;
if (classBeingRedefined == NULL) {
- moduleObject = getModuleObject(jnienv, loaderObject, name);
+ moduleObject = getModuleObject(jvmti(agent), loaderObject, name);
} else {
// Redefine or retransform, InstrumentationImpl.transform() will use
// classBeingRedefined.getModule() to get the module.
@@ -873,8 +867,8 @@
jnienv,
agent->mInstrumentationImpl,
agent->mTransform,
+ moduleObject,
loaderObject,
- moduleObject,
classNameStringObject,
classBeingRedefined,
protectionDomain,
--- a/jdk/src/java.instrument/share/native/libinstrument/JPLISAgent.h Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.instrument/share/native/libinstrument/JPLISAgent.h Thu Jul 21 20:09:19 2016 -0700
@@ -66,7 +66,7 @@
#define JPLIS_INSTRUMENTIMPL_AGENTMAININVOKER_METHODSIGNATURE "(Ljava/lang/String;Ljava/lang/String;)V"
#define JPLIS_INSTRUMENTIMPL_TRANSFORM_METHODNAME "transform"
#define JPLIS_INSTRUMENTIMPL_TRANSFORM_METHODSIGNATURE \
- "(Ljava/lang/ClassLoader;Ljava/lang/reflect/Module;Ljava/lang/String;Ljava/lang/Class;Ljava/security/ProtectionDomain;[BZ)[B"
+ "(Ljava/lang/reflect/Module;Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/Class;Ljava/security/ProtectionDomain;[BZ)[B"
/*
--- a/jdk/src/java.management/share/classes/com/sun/jmx/remote/security/JMXSubjectDomainCombiner.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.management/share/classes/com/sun/jmx/remote/security/JMXSubjectDomainCombiner.java Thu Jul 21 20:09:19 2016 -0700
@@ -81,7 +81,7 @@
* A ProtectionDomain with a null CodeSource and an empty permission set.
*/
private static final ProtectionDomain pdNoPerms =
- new ProtectionDomain(nullCodeSource, new Permissions());
+ new ProtectionDomain(nullCodeSource, new Permissions(), null, null);
/**
* Get the current AccessControlContext combined with the supplied subject.
--- a/jdk/src/java.management/share/classes/com/sun/jmx/remote/util/EnvHelp.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.management/share/classes/com/sun/jmx/remote/util/EnvHelp.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
--- a/jdk/src/java.management/share/classes/javax/management/ConstructorParameters.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/java.management/share/classes/javax/management/ConstructorParameters.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006, 2015 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2015, 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
--- a/jdk/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSASignature.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDSASignature.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 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
@@ -25,6 +25,7 @@
package sun.security.ec;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.math.BigInteger;
@@ -461,6 +462,11 @@
DerValue[] values = in.getSequence(2);
BigInteger r = values[0].getPositiveBigInteger();
BigInteger s = values[1].getPositiveBigInteger();
+
+ // Check for trailing signature data
+ if (in.available() != 0) {
+ throw new IOException("Incorrect signature length");
+ }
// trim leading zeroes
byte[] rBytes = trimZeroes(r.toByteArray());
byte[] sBytes = trimZeroes(s.toByteArray());
--- a/jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/P11Signature.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/P11Signature.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 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
@@ -742,6 +742,11 @@
DerValue[] values = in.getSequence(2);
BigInteger r = values[0].getPositiveBigInteger();
BigInteger s = values[1].getPositiveBigInteger();
+
+ // Check for trailing signature data
+ if (in.available() != 0) {
+ throw new IOException("Incorrect signature length");
+ }
byte[] br = toByteArray(r, 20);
byte[] bs = toByteArray(s, 20);
if ((br == null) || (bs == null)) {
@@ -761,6 +766,11 @@
DerValue[] values = in.getSequence(2);
BigInteger r = values[0].getPositiveBigInteger();
BigInteger s = values[1].getPositiveBigInteger();
+
+ // Check for trailing signature data
+ if (in.available() != 0) {
+ throw new IOException("Incorrect signature length");
+ }
// trim leading zeroes
byte[] br = KeyUtil.trimZeroes(r.toByteArray());
byte[] bs = KeyUtil.trimZeroes(s.toByteArray());
--- a/jdk/src/jdk.crypto.ucrypto/solaris/classes/com/oracle/security/ucrypto/LibMDMech.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/jdk.crypto.ucrypto/solaris/classes/com/oracle/security/ucrypto/LibMDMech.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
--- a/jdk/src/jdk.crypto.ucrypto/solaris/classes/com/oracle/security/ucrypto/NativeGCMCipher.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/jdk.crypto.ucrypto/solaris/classes/com/oracle/security/ucrypto/NativeGCMCipher.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2015, 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
@@ -57,12 +57,18 @@
private static final int DEFAULT_TAG_LEN = 128; // same as SunJCE provider
+ // same as SunJCE provider, see GaloisCounterMode.java for details
+ private static final int MAX_BUF_SIZE = Integer.MAX_VALUE;
+
// buffer for storing AAD data; if null, meaning buffer content has been
// supplied to native context
- private ByteArrayOutputStream aadBuffer = new ByteArrayOutputStream();
+ private ByteArrayOutputStream aadBuffer;
// buffer for storing input in decryption, not used for encryption
- private ByteArrayOutputStream ibuffer = null;
+ private ByteArrayOutputStream ibuffer;
+
+ // needed for checking against MAX_BUF_SIZE
+ private int processed;
private int tagLen = DEFAULT_TAG_LEN;
@@ -75,10 +81,22 @@
* key + iv values used in previous encryption.
* For decryption operations, no checking is necessary.
*/
- private boolean requireReinit = false;
+ private boolean requireReinit;
private byte[] lastEncKey = null;
private byte[] lastEncIv = null;
+ private void checkAndUpdateProcessed(int len) {
+ // Currently, cipher text and tag are packed in one byte array, so
+ // the impl-specific limit for input data size is (MAX_BUF_SIZE - tagLen)
+ int inputDataLimit = MAX_BUF_SIZE - tagLen;
+
+ if (processed > inputDataLimit - len) {
+ throw new ProviderException("OracleUcrypto provider only supports " +
+ "input size up to " + inputDataLimit + " bytes");
+ }
+ processed += len;
+ }
+
NativeGCMCipher(int fixedKeySize) throws NoSuchAlgorithmException {
super(UcryptoMech.CRYPTO_AES_GCM, fixedKeySize);
}
@@ -86,12 +104,14 @@
@Override
protected void ensureInitialized() {
if (!initialized) {
- if (aadBuffer != null && aadBuffer.size() > 0) {
- init(encrypt, keyValue, iv, tagLen, aadBuffer.toByteArray());
- aadBuffer = null;
- } else {
- init(encrypt, keyValue, iv, tagLen, null);
+ byte[] aad = null;
+ if (aadBuffer != null) {
+ if (aadBuffer.size() > 0) {
+ aad = aadBuffer.toByteArray();
+ }
}
+ init(encrypt, keyValue, iv, tagLen, aad);
+ aadBuffer = null;
if (!initialized) {
throw new UcryptoException("Cannot initialize Cipher");
}
@@ -136,6 +156,7 @@
ibuffer.reset();
}
if (!encrypt) requireReinit = false;
+ processed = 0;
}
// actual init() implementation - caller should clone key and iv if needed
@@ -185,6 +206,7 @@
throw new InvalidAlgorithmParameterException
("Unsupported mode: " + opmode);
}
+ aadBuffer = new ByteArrayOutputStream();
boolean doEncrypt = (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE);
byte[] keyBytes = key.getEncoded().clone();
byte[] ivBytes = null;
@@ -219,6 +241,7 @@
}
lastEncIv = ivBytes;
lastEncKey = keyBytes;
+ ibuffer = null;
} else {
requireReinit = false;
ibuffer = new ByteArrayOutputStream();
@@ -246,15 +269,18 @@
// see JCE spec
@Override
protected synchronized byte[] engineUpdate(byte[] in, int inOfs, int inLen) {
- if (aadBuffer != null && aadBuffer.size() > 0) {
- // init again with AAD data
- init(encrypt, keyValue, iv, tagLen, aadBuffer.toByteArray());
+ if (aadBuffer != null) {
+ if (aadBuffer.size() > 0) {
+ // init again with AAD data
+ init(encrypt, keyValue, iv, tagLen, aadBuffer.toByteArray());
+ }
aadBuffer = null;
}
if (requireReinit) {
throw new IllegalStateException
("Must use either different key or iv for GCM encryption");
}
+ checkAndUpdateProcessed(inLen);
if (inLen > 0) {
if (!encrypt) {
ibuffer.write(in, inOfs, inLen);
@@ -274,15 +300,18 @@
"(at least) " + len + " bytes long. Got: " +
(out.length - outOfs));
}
- if (aadBuffer != null && aadBuffer.size() > 0) {
- // init again with AAD data
- init(encrypt, keyValue, iv, tagLen, aadBuffer.toByteArray());
+ if (aadBuffer != null) {
+ if (aadBuffer.size() > 0) {
+ // init again with AAD data
+ init(encrypt, keyValue, iv, tagLen, aadBuffer.toByteArray());
+ }
aadBuffer = null;
}
if (requireReinit) {
throw new IllegalStateException
("Must use either different key or iv for GCM encryption");
}
+ checkAndUpdateProcessed(inLen);
if (inLen > 0) {
if (!encrypt) {
ibuffer.write(in, inOfs, inLen);
@@ -374,15 +403,19 @@
+ "(at least) " + len + " bytes long. Got: " +
(out.length - outOfs));
}
- if (aadBuffer != null && aadBuffer.size() > 0) {
- // init again with AAD data
- init(encrypt, keyValue, iv, tagLen, aadBuffer.toByteArray());
+ if (aadBuffer != null) {
+ if (aadBuffer.size() > 0) {
+ // init again with AAD data
+ init(encrypt, keyValue, iv, tagLen, aadBuffer.toByteArray());
+ }
aadBuffer = null;
}
if (requireReinit) {
throw new IllegalStateException
("Must use either different key or iv for GCM encryption");
}
+
+ checkAndUpdateProcessed(inLen);
if (!encrypt) {
if (inLen > 0) {
ibuffer.write(in, inOfs, inLen);
--- a/jdk/src/jdk.crypto.ucrypto/solaris/classes/com/oracle/security/ucrypto/NativeRSASignature.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/jdk.crypto.ucrypto/solaris/classes/com/oracle/security/ucrypto/NativeRSASignature.java Thu Jul 21 20:09:19 2016 -0700
@@ -330,9 +330,9 @@
protected synchronized boolean engineVerify(byte[] sigBytes, int sigOfs, int sigLen)
throws SignatureException {
if (sigBytes == null || (sigOfs < 0) || (sigBytes.length < (sigOfs + this.sigLength))
- || (sigLen < this.sigLength)) {
- throw new SignatureException("Invalid signature buffer. sigOfs: " +
- sigOfs + ". sigLen: " + sigLen + ". this.sigLength: " + this.sigLength);
+ || (sigLen != this.sigLength)) {
+ throw new SignatureException("Invalid signature length: got " +
+ sigLen + " but was expecting " + this.sigLength);
}
int rv = doFinal(sigBytes, sigOfs, sigLen);
--- a/jdk/src/jdk.crypto.ucrypto/solaris/classes/com/oracle/security/ucrypto/UcryptoMech.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/jdk.crypto.ucrypto/solaris/classes/com/oracle/security/ucrypto/UcryptoMech.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jimage/JImageTask.java Thu Jul 21 20:09:19 2016 -0700
@@ -39,6 +39,8 @@
import jdk.internal.jimage.BasicImageReader;
import jdk.internal.jimage.ImageHeader;
import jdk.internal.jimage.ImageLocation;
+import jdk.internal.org.objectweb.asm.ClassReader;
+import jdk.internal.org.objectweb.asm.tree.ClassNode;
import jdk.tools.jlink.internal.ImageResourcesTree;
import jdk.tools.jlink.internal.TaskHelper;
import jdk.tools.jlink.internal.TaskHelper.BadArgs;
@@ -354,16 +356,14 @@
}
void verify(BasicImageReader reader, String name, ImageLocation location) {
- if (name.endsWith(".class")) {
- byte[] bytes = reader.getResource(location);
-
- if (bytes == null || bytes.length <= 4 ||
- (bytes[0] & 0xFF) != 0xCA ||
- (bytes[1] & 0xFF) != 0xFE ||
- (bytes[2] & 0xFF) != 0xBA ||
- (bytes[3] & 0xFF) != 0xBE) {
- log.print(" NOT A CLASS: ");
- print(reader, name);
+ if (name.endsWith(".class") && !name.endsWith("module-info.class")) {
+ try {
+ byte[] bytes = reader.getResource(location);
+ ClassReader cr =new ClassReader(bytes);
+ ClassNode cn = new ClassNode();
+ cr.accept(cn, ClassReader.EXPAND_FRAMES);
+ } catch (Exception ex) {
+ log.println("Error(s) in Class: " + name);
}
}
}
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage.properties Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jimage/resources/jimage.properties Thu Jul 21 20:09:19 2016 -0700
@@ -63,7 +63,7 @@
\ --dir Target directory for extract directive
main.opt.include=\
-\ --include <pattern-list> Pattern list for filtering list or extract entries.
+\ --include <pattern-list> Pattern list for filtering entries.
main.opt.footer=\
\n\
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,4 +1,3 @@
-
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -23,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package jdk.tools.jlink.builder;
import java.io.BufferedOutputStream;
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/BasicImageWriter.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/BasicImageWriter.java Thu Jul 21 20:09:19 2016 -0700
@@ -21,7 +21,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 jdk.tools.jlink.internal;
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/PerfectHashBuilder.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/PerfectHashBuilder.java Thu Jul 21 20:09:19 2016 -0700
@@ -21,7 +21,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 jdk.tools.jlink.internal;
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/IncludeLocalesPlugin.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/IncludeLocalesPlugin.java Thu Jul 21 20:09:19 2016 -0700
@@ -24,6 +24,7 @@
*/
package jdk.tools.jlink.internal.plugins;
+import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.IllformedLocaleException;
@@ -31,6 +32,7 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import static java.util.ResourceBundle.Control;
import java.util.Set;
import java.util.function.Predicate;
import java.util.regex.Pattern;
@@ -45,6 +47,10 @@
import jdk.tools.jlink.plugin.PluginException;
import jdk.tools.jlink.plugin.ModulePool;
import jdk.tools.jlink.plugin.Plugin;
+import sun.util.cldr.CLDRBaseLocaleDataMetaInfo;
+import sun.util.locale.provider.LocaleProviderAdapter;
+import sun.util.locale.provider.LocaleProviderAdapter.Type;
+import sun.util.locale.provider.ResourceBundleBasedAdapter;
/**
* Plugin to explicitly specify the locale data included in jdk.localedata
@@ -95,6 +101,42 @@
private List<Locale> available;
private List<String> filtered;
+ private static final ResourceBundleBasedAdapter CLDR_ADAPTER =
+ (ResourceBundleBasedAdapter)LocaleProviderAdapter.forType(Type.CLDR);
+ private static final Map<Locale, String[]> CLDR_PARENT_LOCALES =
+ new CLDRBaseLocaleDataMetaInfo().parentLocales();
+
+ // Equivalent map
+ private static final Map<String, List<String>> EQUIV_MAP =
+ Stream.concat(
+ // COMPAT equivalence
+ Map.of(
+ "zh-Hans", List.of("zh-Hans", "zh-CN", "zh-SG"),
+ "zh-Hant", List.of("zh-Hant", "zh-HK", "zh-MO", "zh-TW"))
+ .entrySet()
+ .stream(),
+
+ // CLDR parent locales
+ CLDR_PARENT_LOCALES.entrySet().stream()
+ .map(entry -> {
+ String parent = entry.getKey().toLanguageTag();
+ List<String> children = new ArrayList<>();
+ children.add(parent);
+
+ Arrays.stream(entry.getValue())
+ .filter(child -> !child.isEmpty())
+ .flatMap(child ->
+ Stream.concat(
+ Arrays.stream(CLDR_PARENT_LOCALES.getOrDefault(
+ Locale.forLanguageTag(child), new String[0]))
+ .filter(grandchild -> !grandchild.isEmpty()),
+ List.of(child).stream()))
+ .distinct()
+ .forEach(children::add);
+ return new AbstractMap.SimpleEntry<String, List<String>>(parent, children);
+ })
+ ).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+
// Special COMPAT provider locales
private static final String jaJPJPTag = "ja-JP-JP";
private static final String noNONYTag = "no-NO-NY";
@@ -152,16 +194,14 @@
@Override
public void configure(Map<String, String> config) {
userParam = config.get(NAME);
- priorityList = Arrays.stream(userParam.split(","))
- .map(s -> {
- try {
- return new Locale.LanguageRange(s);
- } catch (IllegalArgumentException iae) {
- throw new IllegalArgumentException(String.format(
- PluginsResourceBundle.getMessage(NAME + ".invalidtag"), s));
- }
- })
- .collect(Collectors.toList());
+
+ try {
+ priorityList = Locale.LanguageRange.parse(userParam, EQUIV_MAP);
+ } catch (IllegalArgumentException iae) {
+ throw new IllegalArgumentException(String.format(
+ PluginsResourceBundle.getMessage(NAME + ".invalidtag"),
+ iae.getMessage().replaceFirst("^range=", "")));
+ }
}
@Override
@@ -193,6 +233,7 @@
// jdk.localedata is not added.
throw new PluginException(PluginsResourceBundle.getMessage(NAME + ".localedatanotfound"));
}
+
filtered = filterLocales(available);
if (filtered.isEmpty()) {
@@ -205,56 +246,26 @@
filtered.stream().flatMap(s -> includeLocaleFilePatterns(s).stream()))
.map(s -> "regex:" + s)
.collect(Collectors.toList());
+
predicate = ResourceFilter.includeFilter(value);
}
private List<String> includeLocaleFilePatterns(String tag) {
- List<String> files = new ArrayList<>();
- String pTag = tag.replaceAll("-", "_");
- int lastDelimiter = tag.length();
- String isoSpecial = pTag.matches("^(he|yi|id).*") ?
- pTag.replaceFirst("he", "iw")
- .replaceFirst("yi", "ji")
- .replaceFirst("id", "in") : "";
-
- // Add tag patterns including parents
- while (true) {
- pTag = pTag.substring(0, lastDelimiter);
- files.addAll(includeLocaleFiles(pTag));
-
- if (!isoSpecial.isEmpty()) {
- isoSpecial = isoSpecial.substring(0, lastDelimiter);
- files.addAll(includeLocaleFiles(isoSpecial));
- }
-
- lastDelimiter = pTag.lastIndexOf('_');
- if (lastDelimiter == -1) {
- break;
- }
+ // Ignore extension variations
+ if (tag.matches(".+-[a-z]-.+")) {
+ return List.of();
}
- final String lang = pTag;
-
- // Add possible special locales of the COMPAT provider
- Set.of(jaJPJPTag, noNONYTag, thTHTHTag).stream()
- .filter(stag -> lang.equals(stag.substring(0,2)))
- .map(t -> includeLocaleFiles(t.replaceAll("-", "_")))
- .forEach(files::addAll);
-
- // Add possible UN.M49 files (unconditional for now) for each language
- files.addAll(includeLocaleFiles(lang + "_[0-9]{3}"));
- if (!isoSpecial.isEmpty()) {
- files.addAll(includeLocaleFiles(isoSpecial + "_[0-9]{3}"));
- }
+ List<String> files = new ArrayList<>(includeLocaleFiles(tag.replaceAll("-", "_")));
// Add Thai BreakIterator related data files
- if (lang.equals("th")) {
+ if (tag.equals("th")) {
files.add(".+sun/text/resources/thai_dict");
files.add(".+sun/text/resources/[^_]+BreakIteratorData_th");
}
// Add Taiwan resource bundles for Hong Kong
- if (tag.startsWith("zh-HK")) {
+ if (tag.equals("zh-HK")) {
files.addAll(includeLocaleFiles("zh_TW"));
}
@@ -306,6 +317,11 @@
byte[] filteredBytes = filterLocales(locales).stream()
.collect(Collectors.joining(" "))
.getBytes();
+
+ if (filteredBytes.length > b.length) {
+ throw new InternalError("Size of filtered locales is bigger than the original one");
+ }
+
System.arraycopy(filteredBytes, 0, b, 0, filteredBytes.length);
Arrays.fill(b, filteredBytes.length, b.length, (byte)' ');
return true;
@@ -314,6 +330,9 @@
private List<String> filterLocales(List<Locale> locales) {
List<String> ret =
Locale.filter(priorityList, locales, Locale.FilteringMode.EXTENDED_FILTERING).stream()
+ .flatMap(loc -> Stream.concat(Control.getNoFallbackControl(Control.FORMAT_DEFAULT)
+ .getCandidateLocales("", loc).stream(),
+ CLDR_ADAPTER.getCandidateLocales("", loc).stream()))
.map(loc ->
// Locale.filter() does not preserve the case, which is
// significant for "variant" equality. Retrieve the original
@@ -321,15 +340,12 @@
locales.stream()
.filter(l -> l.toString().equalsIgnoreCase(loc.toString()))
.findAny()
- .orElse(Locale.ROOT)
- .toLanguageTag())
+ .orElse(Locale.ROOT))
+ .filter(loc -> !loc.equals(Locale.ROOT))
+ .flatMap(IncludeLocalesPlugin::localeToTags)
+ .distinct()
.collect(Collectors.toList());
- // no-NO-NY.toLanguageTag() returns "nn-NO", so specially handle it here
- if (ret.contains("no-NO")) {
- ret.add(noNONYTag);
- }
-
return ret;
}
@@ -338,6 +354,7 @@
// ISO3166 compatibility
tag = tag.replaceFirst("^iw", "he").replaceFirst("^ji", "yi").replaceFirst("^in", "id");
+ // Special COMPAT provider locales
switch (tag) {
case jaJPJPTag:
return jaJPJP;
@@ -351,4 +368,42 @@
return LOCALE_BUILDER.build();
}
}
+
+ private static Stream<String> localeToTags(Locale loc) {
+ String tag = loc.toLanguageTag();
+ Stream<String> ret = null;
+
+ switch (loc.getLanguage()) {
+ // ISO3166 compatibility
+ case "iw":
+ ret = List.of(tag, tag.replaceFirst("^he", "iw")).stream();
+ break;
+ case "in":
+ ret = List.of(tag, tag.replaceFirst("^id", "in")).stream();
+ break;
+ case "ji":
+ ret = List.of(tag, tag.replaceFirst("^yi", "ji")).stream();
+ break;
+
+ // Special COMPAT provider locales
+ case "ja":
+ if (loc.getCountry() == "JP") {
+ ret = List.of(tag, jaJPJPTag).stream();
+ }
+ break;
+ case "no":
+ case "nn":
+ if (loc.getCountry() == "NO") {
+ ret = List.of(tag, noNONYTag).stream();
+ }
+ break;
+ case "th":
+ if (loc.getCountry() == "TH") {
+ ret = List.of(tag, thTHTHTag).stream();
+ }
+ break;
+ }
+
+ return ret == null ? List.of(tag).stream() : ret;
+ }
}
--- a/jdk/src/sample/share/annotations/DependencyChecker/PluginChecker/src/checker/Device.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/sample/share/annotations/DependencyChecker/PluginChecker/src/checker/Device.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,3 @@
-package checker;
-
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
*
@@ -31,6 +29,8 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+package checker;
+
/*
* This source code is provided to illustrate the usage of a given feature
* or technique and has been deliberately simplified. Additional steps
--- a/jdk/src/sample/share/annotations/DependencyChecker/PluginChecker/src/checker/Module.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/sample/share/annotations/DependencyChecker/PluginChecker/src/checker/Module.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,3 @@
-package checker;
-
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
*
@@ -31,6 +29,8 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+package checker;
+
/*
* This source code is provided to illustrate the usage of a given feature
* or technique and has been deliberately simplified. Additional steps
--- a/jdk/src/sample/share/annotations/DependencyChecker/PluginChecker/src/checker/RequireContainer.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/sample/share/annotations/DependencyChecker/PluginChecker/src/checker/RequireContainer.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,3 @@
-package checker;
-
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
*
@@ -31,6 +29,8 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+package checker;
+
/*
* This source code is provided to illustrate the usage of a given feature
* or technique and has been deliberately simplified. Additional steps
--- a/jdk/src/sample/share/nio/chatserver/ChatServer.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/sample/share/nio/chatserver/ChatServer.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
--- a/jdk/src/sample/share/nio/chatserver/Client.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/sample/share/nio/chatserver/Client.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
--- a/jdk/src/sample/share/nio/chatserver/ClientReader.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/sample/share/nio/chatserver/ClientReader.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
--- a/jdk/src/sample/share/nio/chatserver/DataReader.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/sample/share/nio/chatserver/DataReader.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
--- a/jdk/src/sample/share/nio/chatserver/MessageReader.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/sample/share/nio/chatserver/MessageReader.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
--- a/jdk/src/sample/share/nio/chatserver/NameReader.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/src/sample/share/nio/chatserver/NameReader.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
--- a/jdk/test/ProblemList.txt Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/ProblemList.txt Thu Jul 21 20:09:19 2016 -0700
@@ -138,6 +138,8 @@
java/lang/instrument/BootClassPath/BootClassPathTest.sh 8072130 macosx-all
+java/lang/instrument/DaemonThread/TestDaemonThread.java 8161225 generic-all
+
java/lang/management/MemoryMXBean/LowMemoryTest.java 8130339 generic-all
java/lang/management/MemoryMXBean/Pending.java 8158837 generic-all
@@ -171,6 +173,8 @@
java/net/DatagramSocket/SendDatagramToBadAddress.java 7143960 macosx-all
+java/net/httpclient/SplitResponse.java 8157533 generic-all
+
java/net/httpclient/http2/BasicTest.java 8157408 linux-all
java/net/httpclient/http2/ErrorTest.java 8158127 solaris-all,windows-all
java/net/httpclient/http2/TLSConnection.java 8157482 macosx-all
--- a/jdk/test/java/lang/Class/GenericStringTest.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/lang/Class/GenericStringTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2015, 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
@@ -23,7 +23,7 @@
/*
* @test
- * @bug 6298888 6992705
+ * @bug 6298888 6992705 8161500
* @summary Check Class.toGenericString()
* @author Joseph D. Darcy
*/
@@ -58,15 +58,11 @@
f = GenericStringTest.class.getDeclaredField("mixed2");
failures += checkToGenericString(f.getType(), "java.util.Map<K,V>[][]");
- Class<?>[] types = {
- GenericStringTest.class,
- AnInterface.class,
- LocalMap.class,
- AnEnum.class,
- AnotherEnum.class,
- };
-
- for(Class<?> clazz : types) {
+ for(Class<?> clazz : List.of(GenericStringTest.class,
+ AnInterface.class,
+ LocalMap.class,
+ AnEnum.class,
+ AnotherEnum.class)) {
failures += checkToGenericString(clazz, clazz.getAnnotation(ExpectedGenericString.class).value());
}
--- a/jdk/test/java/lang/ProcessBuilder/Zombies.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/lang/ProcessBuilder/Zombies.java Thu Jul 21 20:09:19 2016 -0700
@@ -24,6 +24,7 @@
/*
* @test
* @bug 6474073
+ * @key intermittent
* @summary Make sure zombies don't get created on Unix
* @author Martin Buchholz
*/
--- a/jdk/test/java/lang/Runtime/Version/Basic.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/lang/Runtime/Version/Basic.java Thu Jul 21 20:09:19 2016 -0700
@@ -24,7 +24,7 @@
/*
* @test
* @summary Unit test for java.lang.Runtime.Version.
- * @bug 8072379 8144062
+ * @bug 8072379 8144062 8161236
*/
import java.lang.reflect.InvocationTargetException;
@@ -55,7 +55,7 @@
public static void main(String ... args) {
//// Tests for parse(), major(), minor(), security(), pre(),
- //// build(), opt(), version(), toString()
+ //// build(), optional(), version(), toString()
// v M m sec pre bld opt
// $VNUM
@@ -119,7 +119,8 @@
//// Test for Runtime.version()
testVersion();
- //// Test for equals{IgnoreOpt}?(), hashCode(), compareTo{IgnoreOpt}?()
+ //// Test for equals{IgnoreOptional}?(), hashCode(),
+ //// compareTo{IgnoreOptional}?()
// compare: after "<" == -1, equal == 0, before ">" == 1
// v0 v1 eq eqNO cmp cmpNO
testEHC("9", "9", true, true, 0, 0);
@@ -293,9 +294,9 @@
}
private static void testEqualsNO(Version v0, Version v1, boolean eq) {
- if ((eq && !v0.equalsIgnoreOpt(v1))
- || (!eq && v0.equalsIgnoreOpt(v1))) {
- fail("equalsIgnoreOpt() " + Boolean.toString(eq),
+ if ((eq && !v0.equalsIgnoreOptional(v1))
+ || (!eq && v0.equalsIgnoreOptional(v1))) {
+ fail("equalsIgnoreOptional() " + Boolean.toString(eq),
v0.toString(), v1.toString());
} else {
pass();
@@ -328,12 +329,12 @@
private static void testCompareNO(Version v0, Version v1, int compare)
{
try {
- Method m = VERSION.getMethod("compareToIgnoreOpt", VERSION);
+ Method m = VERSION.getMethod("compareToIgnoreOptional", VERSION);
int cmp = (int) m.invoke(v0, v1);
checkCompare(v0, v1, compare, cmp);
} catch (IllegalAccessException | InvocationTargetException |
NoSuchMethodException ex) {
- fail(String.format("compareToIgnoreOpt() invocation: %s",
+ fail(String.format("compareToIgnoreOptional() invocation: %s",
ex.getClass()),
null);
}
--- a/jdk/test/java/lang/instrument/ATransformerManagementTestCase.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/lang/instrument/ATransformerManagementTestCase.java Thu Jul 21 20:09:19 2016 -0700
@@ -302,6 +302,7 @@
public byte[]
transform(
Module module,
+ ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
@@ -311,6 +312,7 @@
if (classBeingRedefined != null) checkInTransformer(MyClassFileTransformer.this);
return super.transform( module,
+ loader,
className,
classBeingRedefined,
protectionDomain,
--- a/jdk/test/java/lang/instrument/RetransformAgent.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/lang/instrument/RetransformAgent.java Thu Jul 21 20:09:19 2016 -0700
@@ -69,6 +69,7 @@
}
public byte[] transform(Module module,
+ ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
--- a/jdk/test/java/lang/instrument/SimpleIdentityTransformer.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/lang/instrument/SimpleIdentityTransformer.java Thu Jul 21 20:09:19 2016 -0700
@@ -63,6 +63,7 @@
public byte[]
transform(
Module module,
+ ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
--- a/jdk/test/java/lang/invoke/LoopCombinatorLongSignatureTest.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/lang/invoke/LoopCombinatorLongSignatureTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -39,6 +39,11 @@
* If a loop with an excessive amount of clauses is created, so that the number of parameters to the resulting loop
* handle exceeds the allowed maximum, an IAE must be signalled. The test is run first in LambdaForm interpretation mode
* and then in default mode, wherein bytecode generation falls back to LFI mode due to excessively long methods.
+ * <p>
+ * By default, the test run only checks whether loop handle construction succeeds and fails. If executing the generated
+ * loops is desired, this should be indicated by setting the {@code java.lang.invoke.LoopCombinatorLongSignatureTest.RUN}
+ * environment variable to {@code true}. This is disabled by default as it considerably increases the time needed to run
+ * the test.
*/
public class LoopCombinatorLongSignatureTest {
@@ -51,13 +56,15 @@
static final int ARG_LIMIT = 254; // for internal reasons, this is the maximum allowed number of arguments
public static void main(String[] args) {
+ boolean run = Boolean.parseBoolean(
+ System.getProperty("java.lang.invoke.LoopCombinatorLongSignatureTest.RUN", "false"));
for (int loopArgs = 0; loopArgs < 2; ++loopArgs) {
- testLongSignature(loopArgs, false);
- testLongSignature(loopArgs, true);
+ testLongSignature(loopArgs, false, run);
+ testLongSignature(loopArgs, true, run);
}
}
- static void testLongSignature(int loopArgs, boolean excessive) {
+ static void testLongSignature(int loopArgs, boolean excessive, boolean run) {
int nClauses = ARG_LIMIT - loopArgs + (excessive ? 1 : 0);
System.out.print((excessive ? "(EXCESSIVE)" : "(LONG )") + " arguments: " + loopArgs + ", clauses: " + nClauses + " -> ");
@@ -78,7 +85,7 @@
MethodHandle loop = MethodHandles.loop(clauses);
if (excessive) {
throw new AssertionError("loop construction should have failed");
- } else {
+ } else if (run) {
int r;
if (loopArgs == 0) {
r = (int) loop.invoke();
@@ -88,6 +95,8 @@
r = (int) loop.invokeWithArguments(args);
}
System.out.println("SUCCEEDED (OK) -> " + r);
+ } else {
+ System.out.println("SUCCEEDED (OK)");
}
} catch (IllegalArgumentException iae) {
if (excessive) {
--- a/jdk/test/java/lang/reflect/Constructor/GenericStringTest.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/lang/reflect/Constructor/GenericStringTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 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
@@ -23,7 +23,7 @@
/*
* @test
- * @bug 5033583 6316717 6470106
+ * @bug 5033583 6316717 6470106 8161500
* @summary Check toGenericString() and toString() methods
* @author Joseph D. Darcy
*/
@@ -35,12 +35,8 @@
public class GenericStringTest {
public static void main(String argv[]) throws Exception{
int failures = 0;
- List<Class<?>> classList = new LinkedList<Class<?>>();
- classList.add(TestClass1.class);
- classList.add(TestClass2.class);
-
- for(Class<?> clazz: classList)
+ for(Class<?> clazz: List.of(TestClass1.class, TestClass2.class))
for(Constructor<?> ctor: clazz.getDeclaredConstructors()) {
ExpectedGenericString egs = ctor.getAnnotation(ExpectedGenericString.class);
String actual = ctor.toGenericString();
--- a/jdk/test/java/lang/reflect/Field/GenericStringTest.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/lang/reflect/Field/GenericStringTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 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
@@ -23,7 +23,7 @@
/*
* @test
- * @bug 5033583
+ * @bug 5033583 8161500
* @summary Check toGenericString() method
* @author Joseph D. Darcy
*/
@@ -35,12 +35,8 @@
public class GenericStringTest {
public static void main(String argv[]) throws Exception {
int failures = 0;
- List<Class> classList = new LinkedList<Class>();
- classList.add(TestClass1.class);
- classList.add(TestClass2.class);
-
- for(Class clazz: classList)
+ for(Class clazz: List.of(TestClass1.class, TestClass2.class))
for(Field field: clazz.getDeclaredFields()) {
ExpectedString es = field.getAnnotation(ExpectedString.class);
String genericString = field.toGenericString();
@@ -61,15 +57,15 @@
class TestClass1 {
@ExpectedString("int TestClass1.field1")
- int field1;
+ int field1;
@ExpectedString("private static java.lang.String TestClass1.field2")
- private static String field2;
+ private static String field2;
}
class TestClass2<E> {
@ExpectedString("public E TestClass2.field1")
- public E field1;
+ public E field1;
}
@Retention(RetentionPolicy.RUNTIME)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/reflect/Generics/TestGenericReturnTypeToString.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,135 @@
+/*
+ * 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 8054213
+ * @summary Check that toString method works properly for generic return type
+ * obtained via reflection
+ * @run main TestGenericReturnTypeToString
+ */
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Method;
+import java.util.List;
+
+public class TestGenericReturnTypeToString {
+
+ public static void main(String[] args) {
+ boolean hasFailures = false;
+ for (Method method : TestGenericReturnTypeToString.class.getMethods()) {
+ if (method.isAnnotationPresent(ExpectedGenericString.class)) {
+ ExpectedGenericString es = method.getAnnotation
+ (ExpectedGenericString.class);
+ String result = method.getGenericReturnType().toString();
+ if (!es.value().equals(result)) {
+ hasFailures = true;
+ System.err.println("Unexpected result of " +
+ "getGenericReturnType().toString() " +
+ " for " + method.getName()
+ + " expected: " + es.value() + " actual: " + result);
+ }
+ }
+ if (hasFailures) {
+ throw new RuntimeException("Test failed");
+ }
+ }
+ }
+
+ @ExpectedGenericString("TestGenericReturnTypeToString$" +
+ "FirstInnerClassGeneric<Dummy>$SecondInnerClassGeneric<Dummy>")
+ public FirstInnerClassGeneric<Dummy>.SecondInnerClassGeneric<Dummy> foo1() {
+ return null;
+ }
+
+ @ExpectedGenericString("TestGenericReturnTypeToString$" +
+ "FirstInnerClassGeneric<Dummy>$SecondInnerClass")
+ public FirstInnerClassGeneric<Dummy>.SecondInnerClass foo2() {
+ return null;
+ }
+
+ @ExpectedGenericString("TestGenericReturnTypeToString$" +
+ "FirstInnerClass$SecondInnerClassGeneric<Dummy>")
+ public FirstInnerClass.SecondInnerClassGeneric<Dummy> foo3() {
+ return null;
+ }
+
+ @ExpectedGenericString("class TestGenericReturnTypeToString$" +
+ "FirstInnerClass$SecondInnerClass")
+ public FirstInnerClass.SecondInnerClass foo4() {
+ return null;
+ }
+
+ @ExpectedGenericString(
+ "java.util.List<java.lang.String>")
+ public java.util.List<java.lang.String> foo5() {
+ return null;
+ }
+
+ @ExpectedGenericString("interface TestGenericReturnTypeToString$" +
+ "FirstInnerClass$Interface")
+ public FirstInnerClass.Interface foo6() {
+ return null;
+ }
+
+ @ExpectedGenericString("TestGenericReturnTypeToString$" +
+ "FirstInnerClass$InterfaceGeneric<Dummy>")
+ public FirstInnerClass.InterfaceGeneric<Dummy> foo7() {
+ return null;
+ }
+
+ public static class FirstInnerClass {
+
+ public class SecondInnerClassGeneric<T> {
+ }
+
+ public class SecondInnerClass {
+ }
+
+ interface Interface {
+ }
+
+ interface InterfaceGeneric<T> {
+ }
+ }
+
+ public class FirstInnerClassGeneric<T> {
+
+ public class SecondInnerClassGeneric<T> {
+ }
+
+ public class SecondInnerClass {
+ }
+ }
+}
+
+@Retention(RetentionPolicy.RUNTIME)
+@interface ExpectedGenericString {
+ String value();
+}
+
+class Dummy {
+}
--- a/jdk/test/java/lang/reflect/Method/GenericStringTest.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/lang/reflect/Method/GenericStringTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 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
@@ -23,7 +23,7 @@
/*
* @test
- * @bug 5033583 6316717 6470106 8004979
+ * @bug 5033583 6316717 6470106 8004979 8161500
* @summary Check toGenericString() and toString() methods
* @author Joseph D. Darcy
*/
@@ -35,14 +35,9 @@
public class GenericStringTest {
public static void main(String argv[]) throws Exception {
int failures = 0;
- List<Class<?>> classList = new LinkedList<Class<?>>();
- classList.add(TestClass1.class);
- classList.add(TestClass2.class);
- classList.add(Roebling.class);
- classList.add(TestInterface1.class);
-
- for(Class<?> clazz: classList)
+ for(Class<?> clazz: List.of(TestClass1.class, TestClass2.class,
+ Roebling.class, TestInterface1.class))
for(Method method: clazz.getDeclaredMethods()) {
ExpectedGenericString egs = method.getAnnotation(ExpectedGenericString.class);
if (egs != null) {
@@ -121,6 +116,30 @@
@ExpectedGenericString(
"public void TestClass2.method2() throws F")
public void method2() throws F {return;}
+
+ @ExpectedGenericString(
+ "public E[] TestClass2.method3()")
+ public E[] method3() {return null;}
+
+ @ExpectedGenericString(
+ "public E[][] TestClass2.method4()")
+ public E[][] method4() {return null;}
+
+ @ExpectedGenericString(
+ "public java.util.List<E[]> TestClass2.method5()")
+ public List<E[]> method5() {return null;}
+
+ @ExpectedGenericString(
+ "public java.util.List<?> TestClass2.method6()")
+ public List<?> method6() {return null;}
+
+ @ExpectedGenericString(
+ "public java.util.List<?>[] TestClass2.method7()")
+ public List<?>[] method7() {return null;}
+
+ @ExpectedGenericString(
+ "public <K,V> java.util.Map<K, V> TestClass2.method8()")
+ public <K, V> Map<K, V> method8() {return null;}
}
class Roebling implements Comparable<Roebling> {
@@ -157,7 +176,6 @@
@ExpectedGenericString(
"public default strictfp double TestInterface1.quux()")
strictfp default double quux(){return 1.0;}
-
}
@Retention(RetentionPolicy.RUNTIME)
--- a/jdk/test/java/net/URLPermission/URLPermissionTest.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/net/URLPermission/URLPermissionTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -26,7 +26,7 @@
/**
* @test
- * @bug 8010464 8027570 8027687 8029354 8114860 8071660
+ * @bug 8010464 8027570 8027687 8029354 8114860 8071660 8161291
*/
public class URLPermissionTest {
@@ -355,15 +355,16 @@
};
static Test[] actionsStringTest = {
- actionstest("", ""),
+ actionstest("", ":"),
+ actionstest(":", ":"),
actionstest(":X-Bar", ":X-Bar"),
- actionstest("GET", "GET"),
- actionstest("get", "GET"),
- actionstest("GET,POST", "GET,POST"),
- actionstest("GET,post", "GET,POST"),
- actionstest("get,post", "GET,POST"),
- actionstest("get,post,DELETE", "DELETE,GET,POST"),
- actionstest("GET,POST:", "GET,POST"),
+ actionstest("GET", "GET:"),
+ actionstest("get", "GET:"),
+ actionstest("GET,POST", "GET,POST:"),
+ actionstest("GET,post", "GET,POST:"),
+ actionstest("get,post", "GET,POST:"),
+ actionstest("get,post,DELETE", "DELETE,GET,POST:"),
+ actionstest("GET,POST:", "GET,POST:"),
actionstest("GET:X-Foo,X-bar", "GET:X-Bar,X-Foo"),
actionstest("GET,POST,DELETE:X-Bar,X-Foo,X-Bar,Y-Foo", "DELETE,GET,POST:X-Bar,X-Bar,X-Foo,Y-Foo")
};
--- a/jdk/test/java/net/URLPermission/URLTest.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/net/URLPermission/URLTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -23,167 +23,194 @@
import java.net.URLPermission;
/*
- * Run the tests once without security manager and once with
- *
* @test
* @bug 8010464
* @modules jdk.httpserver
- * @key intermittent
* @library /lib/testlibrary/
* @build jdk.testlibrary.SimpleSSLContext
- * @run main/othervm/java.security.policy=policy.1 URLTest one
- * @run main/othervm URLTest one
- * @run main/othervm/java.security.policy=policy.2 URLTest two
- * @run main/othervm URLTest two
- * @run main/othervm/java.security.policy=policy.3 URLTest three
- * @run main/othervm URLTest three
+ * @run main/othervm URLTest
+ * @summary check URLPermission with Http(s)URLConnection
*/
import java.net.*;
import java.io.*;
-import java.util.*;
+import java.security.*;
import java.util.concurrent.*;
-import java.util.logging.*;
import com.sun.net.httpserver.*;
import javax.net.ssl.*;
import jdk.testlibrary.SimpleSSLContext;
public class URLTest {
- static boolean failed = false;
+
+ static boolean failed;
public static void main (String[] args) throws Exception {
- boolean no = false, yes = true;
-
- if (System.getSecurityManager() == null) {
- yes = false;
- }
createServers();
- InetSocketAddress addr1 = httpServer.getAddress();
- int port1 = addr1.getPort();
- InetSocketAddress addr2 = httpsServer.getAddress();
- int port2 = addr2.getPort();
-
- // each of the following cases is run with a different policy file
- switch (args[0]) {
- case "one":
- String url1 = "http://127.0.0.1:"+ port1 + "/foo.html";
- String url2 = "https://127.0.0.1:"+ port2 + "/foo.html";
- String url3 = "http://127.0.0.1:"+ port1 + "/bar.html";
- String url4 = "https://127.0.0.1:"+ port2 + "/bar.html";
-
- // simple positive test. Should succceed
- test(url1, "GET", "X-Foo", no);
- test(url1, "GET", "Z-Bar", "X-Foo", no);
- test(url1, "GET", "X-Foo", "Z-Bar", no);
- test(url1, "GET", "Z-Bar", no);
- test(url2, "POST", "X-Fob", no);
-
- // reverse the methods, should fail
- test(url1, "POST", "X-Foo", yes);
- test(url2, "GET", "X-Fob", yes);
+ try {
+ // Verify without a Security Manager
+ test1();
+ test2();
+ test3();
- // different URLs, should fail
- test(url3, "GET", "X-Foo", yes);
- test(url4, "POST", "X-Fob", yes);
- break;
-
- case "two":
- url1 = "http://127.0.0.1:"+ port1 + "/foo.html";
- url2 = "https://127.0.0.1:"+ port2 + "/foo.html";
- url3 = "http://127.0.0.1:"+ port1 + "/bar.html";
- url4 = "https://127.0.0.1:"+ port2 + "/bar.html";
+ // Set the security manager. Each test will set its own policy.
+ Policy.setPolicy(new CustomPolicy());
+ System.setSecurityManager(new SecurityManager());
+ System.out.println("\n Security Manager has been set.");
- // simple positive test. Should succceed
- test(url1, "GET", "X-Foo", no);
- test(url2, "POST", "X-Fob", no);
- test(url3, "GET", "X-Foo", no);
- test(url4, "POST", "X-Fob", no);
- break;
+ test1();
+ test2();
+ test3();
- case "three":
- url1 = "http://127.0.0.1:"+ port1 + "/foo.html";
- url2 = "https://127.0.0.1:"+ port2 + "/a/c/d/e/foo.html";
- url3 = "http://127.0.0.1:"+ port1 + "/a/b/c";
- url4 = "https://127.0.0.1:"+ port2 + "/a/b/c";
-
- test(url1, "GET", "X-Foo", yes);
- test(url2, "POST", "X-Zxc", no);
- test(url3, "DELETE", "Y-Foo", no);
- test(url4, "POST", "Y-Foo", yes);
- break;
- }
- shutdown();
- if (failed) {
- throw new RuntimeException("Test failed");
+ if (failed)
+ throw new RuntimeException("Test failed");
+ } finally {
+ shutdown();
}
}
- public static void test (
- String u, String method,
- String header, boolean exceptionExpected
- )
- throws Exception
- {
- test(u, method, header, null, exceptionExpected);
+ static void test1() throws IOException {
+ System.out.println("\n--- Test 1 ---");
+
+ boolean expectException = false;
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ expectException = true;
+ Policy.setPolicy(new CustomPolicy(
+ new URLPermission("http://127.0.0.1:"+httpPort+"/foo.html", "GET:X-Foo,Z-Bar"),
+ new URLPermission("https://127.0.0.1:"+httpsPort+"/foo.html", "POST:X-Fob,T-Bar")));
+ }
+
+ String url1 = "http://127.0.0.1:"+httpPort+"/foo.html";
+ String url2 = "https://127.0.0.1:"+httpsPort+"/foo.html";
+ String url3 = "http://127.0.0.1:"+httpPort+"/bar.html";
+ String url4 = "https://127.0.0.1:"+httpsPort+"/bar.html";
+
+ // simple positive test. Should succeed
+ test(url1, "GET", "X-Foo");
+ test(url1, "GET", "Z-Bar", "X-Foo");
+ test(url1, "GET", "X-Foo", "Z-Bar");
+ test(url1, "GET", "Z-Bar");
+ test(url2, "POST", "X-Fob");
+
+ // reverse the methods, should fail
+ test(url1, "POST", "X-Foo", expectException);
+ test(url2, "GET", "X-Fob", expectException);
+
+ // different URLs, should fail
+ test(url3, "GET", "X-Foo", expectException);
+ test(url4, "POST", "X-Fob", expectException);
}
- public static void test (
- String u, String method,
- String header1, String header2, boolean exceptionExpected
- )
- throws Exception
+ static void test2() throws IOException {
+ System.out.println("\n--- Test 2 ---");
+
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ Policy.setPolicy(new CustomPolicy(
+ new URLPermission("http://127.0.0.1:"+httpPort+"/*", "GET:X-Foo"),
+ new URLPermission("https://127.0.0.1:"+httpsPort+"/*", "POST:X-Fob")));
+ }
+
+ String url1 = "http://127.0.0.1:"+httpPort+"/foo.html";
+ String url2 = "https://127.0.0.1:"+httpsPort+"/foo.html";
+ String url3 = "http://127.0.0.1:"+httpPort+"/bar.html";
+ String url4 = "https://127.0.0.1:"+httpsPort+"/bar.html";
+
+ // simple positive test. Should succeed
+ test(url1, "GET", "X-Foo");
+ test(url2, "POST", "X-Fob");
+ test(url3, "GET", "X-Foo");
+ test(url4, "POST", "X-Fob");
+ }
+
+ static void test3() throws IOException {
+ System.out.println("\n--- Test 3 ---");
+
+ boolean expectException = false;
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ expectException = true;
+ Policy.setPolicy(new CustomPolicy(
+ new URLPermission("http://127.0.0.1:"+httpPort+"/a/b/-", "DELETE,GET:X-Foo,Y-Foo"),
+ new URLPermission("https://127.0.0.1:"+httpsPort+"/a/c/-", "POST:*")));
+ }
+
+ String url1 = "http://127.0.0.1:"+httpPort+"/foo.html";
+ String url2 = "https://127.0.0.1:"+httpsPort+"/a/c/d/e/foo.html";
+ String url3 = "http://127.0.0.1:"+httpPort+"/a/b/c";
+ String url4 = "https://127.0.0.1:"+httpsPort+"/a/b/c";
+
+ test(url1, "GET", "X-Foo", expectException);
+ test(url2, "POST", "X-Zxc");
+ test(url3, "DELETE", "Y-Foo");
+ test(url4, "POST", "Y-Foo", expectException);
+ }
+
+ // Convenience methods to simplify previous explicit test scenarios.
+ static void test(String u, String method, String header) throws IOException {
+ test(u, method, header, null, false);
+ }
+
+ static void test(String u, String method, String header, boolean expectException)
+ throws IOException
+ {
+ test(u, method, header, null, expectException);
+ }
+
+ static void test(String u, String method, String header1, String header2)
+ throws IOException
+ {
+ test(u, method, header1, header2, false);
+ }
+
+ static void test(String u,
+ String method,
+ String header1,
+ String header2,
+ boolean expectException)
+ throws IOException
{
URL url = new URL(u);
- System.out.println ("url=" + u + " method="+method + " header1="+header1
- +" header2 = " + header2
- +" exceptionExpected="+exceptionExpected);
+ System.out.println("url=" + u + " method=" + method +
+ " header1=" + header1 + " header2=" + header2 +
+ " expectException=" + expectException);
HttpURLConnection urlc = (HttpURLConnection)url.openConnection();
if (urlc instanceof HttpsURLConnection) {
HttpsURLConnection ssl = (HttpsURLConnection)urlc;
- ssl.setHostnameVerifier(new HostnameVerifier() {
- public boolean verify(String host, SSLSession sess) {
- return true;
- }
- });
- ssl.setSSLSocketFactory (ctx.getSocketFactory());
+ ssl.setHostnameVerifier((host, sess) -> true);
+ ssl.setSSLSocketFactory(ctx.getSocketFactory());
}
urlc.setRequestMethod(method);
- if (header1 != null) {
+ if (header1 != null)
urlc.addRequestProperty(header1, "foo");
- }
- if (header2 != null) {
+ if (header2 != null)
urlc.addRequestProperty(header2, "bar");
- }
+
try {
- int g = urlc.getResponseCode();
- if (exceptionExpected) {
+ int code = urlc.getResponseCode();
+ if (expectException) {
failed = true;
- System.out.println ("FAIL");
+ System.out.println("FAIL");
return;
}
- if (g != 200) {
- String s = Integer.toString(g);
- throw new RuntimeException("unexpected response "+ s);
- }
+ if (code != 200)
+ throw new RuntimeException("Unexpected response " + code);
+
InputStream is = urlc.getInputStream();
- int c,count=0;
- byte[] buf = new byte[1024];
- while ((c=is.read(buf)) != -1) {
- count += c;
- }
+ is.readAllBytes();
is.close();
} catch (RuntimeException e) {
- if (! (e instanceof SecurityException) &&
- !(e.getCause() instanceof SecurityException) ||
- !exceptionExpected)
- {
- System.out.println ("FAIL");
- //e.printStackTrace();
+ if (!expectException || !(e.getCause() instanceof SecurityException)) {
+ System.out.println ("FAIL. Unexpected: " + e.getMessage());
+ e.printStackTrace();
failed = true;
+ return;
+ } else {
+ System.out.println("Got expected exception: " + e.getMessage());
}
}
- System.out.println ("OK");
+ System.out.println ("PASS");
}
static HttpServer httpServer;
@@ -191,33 +218,31 @@
static HttpContext c, cs;
static ExecutorService e, es;
static SSLContext ctx;
-
- // These ports need to be hard-coded until we support port number
- // ranges in the permission class
-
- static final int PORT1 = 12567;
- static final int PORT2 = 12568;
+ static int httpPort;
+ static int httpsPort;
static void createServers() throws Exception {
- InetSocketAddress addr1 = new InetSocketAddress (PORT1);
- InetSocketAddress addr2 = new InetSocketAddress (PORT2);
- httpServer = HttpServer.create (addr1, 0);
- httpsServer = HttpsServer.create (addr2, 0);
+ InetSocketAddress any = new InetSocketAddress(0);
+ httpServer = HttpServer.create(any, 0);
+ httpsServer = HttpsServer.create(any, 0);
- MyHandler h = new MyHandler();
+ OkHandler h = new OkHandler();
- c = httpServer.createContext ("/", h);
- cs = httpsServer.createContext ("/", h);
+ c = httpServer.createContext("/", h);
+ cs = httpsServer.createContext("/", h);
e = Executors.newCachedThreadPool();
es = Executors.newCachedThreadPool();
- httpServer.setExecutor (e);
- httpsServer.setExecutor (es);
+ httpServer.setExecutor(e);
+ httpsServer.setExecutor(es);
ctx = new SimpleSSLContext().get();
httpsServer.setHttpsConfigurator(new HttpsConfigurator (ctx));
httpServer.start();
httpsServer.start();
+
+ httpPort = httpServer.getAddress().getPort();
+ httpsPort = httpsServer.getAddress().getPort();
}
static void shutdown() {
@@ -227,15 +252,38 @@
es.shutdown();
}
- static class MyHandler implements HttpHandler {
-
- MyHandler() {
- }
-
+ static class OkHandler implements HttpHandler {
public void handle(HttpExchange x) throws IOException {
x.sendResponseHeaders(200, -1);
x.close();
}
}
+ static class CustomPolicy extends Policy {
+ final PermissionCollection perms = new Permissions();
+ CustomPolicy(Permission... permissions) {
+ java.util.Arrays.stream(permissions).forEach(perms::add);
+
+ // needed for the HTTP(S) server
+ perms.add(new SocketPermission("localhost:1024-", "listen,resolve,accept"));
+ // needed by the test to reset the policy, per testX method
+ perms.add(new SecurityPermission("setPolicy"));
+ // needed to shutdown the ThreadPoolExecutor ( used by the servers )
+ perms.add(new RuntimePermission("modifyThread"));
+ // needed by the client code forHttpsURLConnection.setSSLSocketFactory
+ perms.add(new RuntimePermission("setFactory"));
+ }
+
+ public PermissionCollection getPermissions(ProtectionDomain domain) {
+ return perms;
+ }
+
+ public PermissionCollection getPermissions(CodeSource codesource) {
+ return perms;
+ }
+
+ public boolean implies(ProtectionDomain domain, Permission perm) {
+ return perms.implies(perm);
+ }
+ }
}
--- a/jdk/test/java/net/URLPermission/policy.1 Thu Jul 21 16:45:56 2016 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-//
-// Copyright (c) 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.
-//
-// 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.
-//
-
-grant {
- permission java.net.URLPermission "http://127.0.0.1:12567/foo.html", "GET:X-Foo,Z-Bar";
- permission java.net.URLPermission "https://127.0.0.1:12568/foo.html", "POST:X-Fob,T-Bar";
-
- // needed for HttpServer
- permission "java.net.SocketPermission" "localhost:1024-", "listen,resolve,accept";
- permission "java.util.PropertyPermission" "test.src", "read";
- permission java.io.FilePermission "${test.src}/../../../lib/testlibrary/jdk/testlibrary/testkeys", "read";
-
- //permission "java.util.logging.LoggingPermission" "control";
- //permission "java.io.FilePermission" "/tmp/-", "read,write";
- permission "java.lang.RuntimePermission" "modifyThread";
- permission "java.lang.RuntimePermission" "setFactory";
- permission "java.util.PropertyPermission" "test.src.path", "read";
-};
-
--- a/jdk/test/java/net/URLPermission/policy.2 Thu Jul 21 16:45:56 2016 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-//
-// Copyright (c) 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.
-//
-// 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.
-//
-
-grant {
- permission java.net.URLPermission "http://127.0.0.1:12567/*", "GET:X-Foo";
- permission java.net.URLPermission "https://127.0.0.1:12568/*", "POST:X-Fob";
-
- // needed for HttpServer
- permission "java.net.SocketPermission" "localhost:1024-", "listen,resolve,accept";
- permission "java.util.PropertyPermission" "test.src", "read";
- permission java.io.FilePermission "${test.src}/../../../lib/testlibrary/jdk/testlibrary/testkeys", "read";
-
- //permission "java.util.logging.LoggingPermission" "control";
- //permission "java.io.FilePermission" "/tmp/-", "read,write";
- permission "java.lang.RuntimePermission" "modifyThread";
- permission "java.lang.RuntimePermission" "setFactory";
- permission "java.util.PropertyPermission" "test.src.path", "read";
-};
-
--- a/jdk/test/java/net/URLPermission/policy.3 Thu Jul 21 16:45:56 2016 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-//
-// Copyright (c) 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.
-//
-// 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.
-//
-
-grant {
- permission java.net.URLPermission "http://127.0.0.1:12567/a/b/-", "DELETE,GET:X-Foo,Y-Foo";
- permission java.net.URLPermission "https://127.0.0.1:12568/a/c/-", "POST:*";
-
- // needed for HttpServer
- permission "java.net.SocketPermission" "localhost:1024-", "listen,resolve,accept";
- permission "java.util.PropertyPermission" "test.src", "read";
- permission java.io.FilePermission "${test.src}/../../../lib/testlibrary/jdk/testlibrary/testkeys", "read";
-
- //permission "java.util.logging.LoggingPermission" "control";
- //permission "java.io.FilePermission" "/tmp/-", "read,write";
- permission "java.lang.RuntimePermission" "modifyThread";
- permission "java.lang.RuntimePermission" "setFactory";
- permission "java.util.PropertyPermission" "test.src.path", "read";
-};
--- a/jdk/test/java/net/httpclient/whitebox/java.httpclient/java/net/http/SelectorTest.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/net/httpclient/whitebox/java.httpclient/java/net/http/SelectorTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -78,7 +78,7 @@
final RawChannel chan = getARawChannel(port);
- chan.registerEvent(new RawChannel.NonBlockingEvent() {
+ chan.registerEvent(new RawChannel.RawEvent() {
@Override
public int interestOps() {
return SelectionKey.OP_READ;
@@ -95,7 +95,7 @@
}
});
- chan.registerEvent(new RawChannel.NonBlockingEvent() {
+ chan.registerEvent(new RawChannel.RawEvent() {
@Override
public int interestOps() {
return SelectionKey.OP_WRITE;
@@ -111,7 +111,7 @@
ByteBuffer bb = ByteBuffer.wrap(TestServer.INPUT);
counter.incrementAndGet();
try {
- chan.write(bb);
+ chan.write(new ByteBuffer[]{bb}, 0, 1);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
--- a/jdk/test/java/nio/file/WatchService/DeleteInterference.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/nio/file/WatchService/DeleteInterference.java Thu Jul 21 20:09:19 2016 -0700
@@ -32,6 +32,7 @@
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.Paths;
import java.nio.file.WatchService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -49,7 +50,8 @@
* directory.
*/
public static void main(String[] args) throws Exception {
- Path dir = Files.createTempDirectory("DeleteInterference");
+ Path testDir = Paths.get(System.getProperty("test.dir", "."));
+ Path dir = Files.createTempDirectory(testDir, "DeleteInterference");
ExecutorService pool = Executors.newCachedThreadPool();
try {
Future<?> task1 = pool.submit(() -> openAndCloseWatcher(dir));
@@ -58,7 +60,6 @@
task2.get();
} finally {
pool.shutdown();
- deleteFileTree(dir);
}
}
--- a/jdk/test/java/nio/file/WatchService/LotsOfCancels.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/nio/file/WatchService/LotsOfCancels.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,11 +27,11 @@
* an outstanding I/O operation on directory completes after the
* directory has been closed
*/
-
import java.nio.file.ClosedWatchServiceException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.Paths;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import static java.nio.file.StandardWatchEventKinds.*;
@@ -50,8 +50,8 @@
// one to bash on cancel, the other to poll the events
ExecutorService pool = Executors.newCachedThreadPool();
try {
- Path top = Files.createTempDirectory("LotsOfCancels");
- top.toFile().deleteOnExit();
+ Path testDir = Paths.get(System.getProperty("test.dir", "."));
+ Path top = Files.createTempDirectory(testDir, "LotsOfCancels");
for (int i=1; i<=16; i++) {
Path dir = Files.createDirectory(top.resolve("dir-" + i));
WatchService watcher = FileSystems.getDefault().newWatchService();
@@ -114,6 +114,4 @@
failed = true;
}
}
-
}
-
--- a/jdk/test/java/security/Provider/SecurityProviderModularTest.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/security/Provider/SecurityProviderModularTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -63,8 +63,7 @@
"TestSecurityProviderClient.java");
private static final String C_PKG = "client";
private static final String C_JAR_NAME = C_PKG + JAR_EXTN;
- private static final String MC_DEPENDS_ON_AUTO_SERVICE_JAR_NAME = MODULAR
- + C_PKG + AUTO + JAR_EXTN;
+ private static final String MCN_JAR_NAME = MODULAR + C_PKG + "N" + JAR_EXTN;
private static final String MC_JAR_NAME = MODULAR + C_PKG + JAR_EXTN;
private static final Path BUILD_DIR = Paths.get(".").resolve("build");
@@ -72,7 +71,7 @@
private static final Path S_BUILD_DIR = COMPILE_DIR.resolve(S_PKG);
private static final Path S_WITH_META_DESCR_BUILD_DIR = COMPILE_DIR.resolve(
S_PKG + DESCRIPTOR);
- private static final Path C_BUILD_DIR = COMPILE_DIR.resolve(C_PKG);
+ private static final Path C_BLD_DIR = COMPILE_DIR.resolve(C_PKG);
private static final Path M_BASE_PATH = BUILD_DIR.resolve("mbase");
private static final Path ARTIFACTS_DIR = BUILD_DIR.resolve("artifacts");
@@ -88,8 +87,7 @@
private static final Path C_ARTIFACTS_DIR = ARTIFACTS_DIR.resolve(C_PKG);
private static final Path C_JAR = C_ARTIFACTS_DIR.resolve(C_JAR_NAME);
private static final Path MC_JAR = C_ARTIFACTS_DIR.resolve(MC_JAR_NAME);
- private static final Path MC_DEPENDS_ON_AUTO_SERVICE_JAR = C_ARTIFACTS_DIR
- .resolve(MC_DEPENDS_ON_AUTO_SERVICE_JAR_NAME);
+ private static final Path MCN_JAR = C_ARTIFACTS_DIR.resolve(MCN_JAR_NAME);
private static final String MAIN = C_PKG + ".TestSecurityProviderClient";
private static final String S_INTERFACE = "java.security.Provider";
@@ -102,8 +100,6 @@
private static final boolean WITH_S_DESCR = true;
private static final boolean WITHOUT_S_DESCR = false;
- private static final String CLASS_NOT_FOUND_MSG = "NoClassDefFoundError:"
- + " provider/TestSecurityProvider";
private static final String PROVIDER_NOT_FOUND_MSG = "Unable to find Test"
+ " Security Provider";
private static final String CAN_NOT_ACCESS_MSG = "cannot access class";
@@ -126,10 +122,10 @@
boolean useCLoader = CLASS_LOADER.equals(mechanism);
boolean useSLoader = SERVICE_LOADER.equals(mechanism);
String[] args = new String[]{mechanism};
- //PARAMETER ORDERS -
- //client Module Type, Service Module Type,
- //Service META Descriptor Required,
- //Expected Failure message, mech used to find the provider
+ // PARAMETER ORDERS -
+ // Client Module Type, Service Module Type,
+ // If Service META Descriptor Required,
+ // Expected Failure message, mechanism used to find the provider
params.add(Arrays.asList(MODULE_TYPE.EXPLICIT, MODULE_TYPE.EXPLICIT,
WITH_S_DESCR, NO_FAILURE, args));
params.add(Arrays.asList(MODULE_TYPE.EXPLICIT, MODULE_TYPE.EXPLICIT,
@@ -138,7 +134,8 @@
WITH_S_DESCR, ((useCLoader) ? CAN_NOT_ACCESS_MSG
: NO_FAILURE), args));
params.add(Arrays.asList(MODULE_TYPE.EXPLICIT, MODULE_TYPE.AUTO,
- WITHOUT_S_DESCR, CLASS_NOT_FOUND_MSG, args));
+ WITHOUT_S_DESCR, ((useCLoader) ? CAN_NOT_ACCESS_MSG
+ : PROVIDER_NOT_FOUND_MSG), args));
params.add(Arrays.asList(MODULE_TYPE.EXPLICIT, MODULE_TYPE.UNNAMED,
WITH_S_DESCR, ((useCLoader) ? CAN_NOT_ACCESS_MSG
: NO_FAILURE), args));
@@ -150,11 +147,12 @@
params.add(Arrays.asList(MODULE_TYPE.AUTO, MODULE_TYPE.EXPLICIT,
WITH_S_DESCR, NO_FAILURE, args));
params.add(Arrays.asList(MODULE_TYPE.AUTO, MODULE_TYPE.EXPLICIT,
- WITH_S_DESCR, NO_FAILURE, args));
+ WITHOUT_S_DESCR, NO_FAILURE, args));
params.add(Arrays.asList(MODULE_TYPE.AUTO, MODULE_TYPE.AUTO,
WITH_S_DESCR, NO_FAILURE, args));
params.add(Arrays.asList(MODULE_TYPE.AUTO, MODULE_TYPE.AUTO,
- WITHOUT_S_DESCR, CLASS_NOT_FOUND_MSG, args));
+ WITHOUT_S_DESCR,
+ (useCLoader) ? NO_FAILURE : PROVIDER_NOT_FOUND_MSG, args));
params.add(Arrays.asList(MODULE_TYPE.AUTO, MODULE_TYPE.UNNAMED,
WITH_S_DESCR, NO_FAILURE, args));
params.add(Arrays.asList(MODULE_TYPE.AUTO, MODULE_TYPE.UNNAMED,
@@ -168,7 +166,8 @@
params.add(Arrays.asList(MODULE_TYPE.UNNAMED, MODULE_TYPE.AUTO,
WITH_S_DESCR, NO_FAILURE, args));
params.add(Arrays.asList(MODULE_TYPE.UNNAMED, MODULE_TYPE.AUTO,
- WITHOUT_S_DESCR, CLASS_NOT_FOUND_MSG, args));
+ WITHOUT_S_DESCR,
+ (useCLoader) ? NO_FAILURE : PROVIDER_NOT_FOUND_MSG, args));
params.add(Arrays.asList(MODULE_TYPE.UNNAMED, MODULE_TYPE.UNNAMED,
WITH_S_DESCR, NO_FAILURE, args));
params.add(Arrays.asList(MODULE_TYPE.UNNAMED, MODULE_TYPE.UNNAMED,
@@ -191,22 +190,23 @@
done &= CompilerUtils.compile(S_SRC, S_BUILD_DIR);
done &= CompilerUtils.compile(S_SRC, S_WITH_META_DESCR_BUILD_DIR);
done &= createMetaInfServiceDescriptor(S_META_DESCR_FPATH, S_IMPL);
- //Generate regular/modular jars with(out) META-INF
- //Service descriptor
+ // Generate modular/regular jars with(out) META-INF
+ // service descriptor
generateJar(true, MODULE_TYPE.EXPLICIT, MS_JAR, S_BUILD_DIR, false);
generateJar(true, MODULE_TYPE.EXPLICIT, MS_WITH_DESCR_JAR,
S_WITH_META_DESCR_BUILD_DIR, false);
generateJar(true, MODULE_TYPE.UNNAMED, S_JAR, S_BUILD_DIR, false);
generateJar(true, MODULE_TYPE.UNNAMED, S_WITH_DESCRIPTOR_JAR,
S_WITH_META_DESCR_BUILD_DIR, false);
- //Generate regular/modular(depends on explicit/auto Service)
- //jars for client
- done &= CompilerUtils.compile(C_SRC, C_BUILD_DIR, "-cp",
+ // Compile client source codes.
+ done &= CompilerUtils.compile(C_SRC, C_BLD_DIR, "-cp",
S_JAR.toFile().getCanonicalPath());
- generateJar(false, MODULE_TYPE.EXPLICIT, MC_JAR, C_BUILD_DIR, true);
- generateJar(false, MODULE_TYPE.EXPLICIT,
- MC_DEPENDS_ON_AUTO_SERVICE_JAR, C_BUILD_DIR, false);
- generateJar(false, MODULE_TYPE.UNNAMED, C_JAR, C_BUILD_DIR, false);
+ // Generate modular client jar with explicit dependency
+ generateJar(false, MODULE_TYPE.EXPLICIT, MC_JAR, C_BLD_DIR, true);
+ // Generate modular client jar without any dependency
+ generateJar(false, MODULE_TYPE.EXPLICIT, MCN_JAR, C_BLD_DIR, false);
+ // Generate regular client jar
+ generateJar(false, MODULE_TYPE.UNNAMED, C_JAR, C_BLD_DIR, false);
System.out.format("%nArtifacts generated successfully? %s", done);
if (!done) {
throw new RuntimeException("Artifacts generation failed");
@@ -244,10 +244,9 @@
OutputAnalyzer output = null;
try {
-
- //For automated/explicit module type copy the corresponding
- //jars to module base folder, which will be considered as
- //module base path during execution.
+ // For automated/explicit module types, copy the corresponding
+ // jars to module base folder, which will be considered as
+ // module base path during execution.
if (!(cModuleType == MODULE_TYPE.UNNAMED
&& sModuletype == MODULE_TYPE.UNNAMED)) {
copyJarsToModuleBase(cModuleType, cJarPath, M_BASE_PATH);
@@ -262,15 +261,14 @@
String cPath = buildClassPath(cModuleType, cJarPath, sModuletype,
sJarPath);
- Map<String, String> VM_ARGS = getVMArgs(sModuletype, args);
+ Map<String, String> vmArgs = getVMArgs(sModuletype,
+ getModuleName(sModuletype, sJarPath, S_PKG), args);
output = ProcessTools.executeTestJava(
- getJavaCommand(cmBasePath, cPath, mName, MAIN, VM_ARGS,
+ getJavaCommand(cmBasePath, cPath, mName, MAIN, vmArgs,
args)).outputTo(System.out).errorTo(System.out);
} finally {
- //clean module path so that the modulepath can hold only
- //the required jars for next run.
+ // Clean module path to hold required jars for next run.
cleanModuleBasePath(M_BASE_PATH);
- System.out.println("--------------------------------------------");
}
return output;
}
@@ -297,11 +295,12 @@
}
}
} else {
+ // Choose corresponding client jar to use dependent module
if (moduleType == MODULE_TYPE.EXPLICIT) {
if (dependsOnServiceModule) {
return MC_JAR;
} else {
- return MC_DEPENDS_ON_AUTO_SERVICE_JAR;
+ return MCN_JAR;
}
} else {
return C_JAR;
@@ -313,13 +312,16 @@
* VM argument required for the test.
*/
private Map<String, String> getVMArgs(MODULE_TYPE sModuletype,
- String... args) throws IOException {
- final Map<String, String> VM_ARGS = new LinkedHashMap<>();
- VM_ARGS.put("-Duser.language=", "en");
- VM_ARGS.put("-Duser.region=", "US");
- //If mechanism selected to find the provider through
- //Security.getProvider() then use providerName/ProviderClassName based
- //on modular/regular provider jar in security configuration file.
+ String addModName, String... args) throws IOException {
+ final Map<String, String> vmArgs = new LinkedHashMap<>();
+ vmArgs.put("-Duser.language=", "en");
+ vmArgs.put("-Duser.region=", "US");
+ if (addModName != null && sModuletype == MODULE_TYPE.AUTO) {
+ vmArgs.put("-addmods ", addModName);
+ }
+ // If mechanism selected to find the provider through
+ // Security.getProvider() then use providerName/ProviderClassName based
+ // on modular/regular provider jar in security configuration file.
if (args != null && args.length > 0 && SECURITY_PROP.equals(args[0])) {
if (sModuletype == MODULE_TYPE.UNNAMED) {
Files.write(SECURE_PROP_EXTN, ("security.provider.10=" + S_IMPL)
@@ -328,10 +330,10 @@
Files.write(SECURE_PROP_EXTN, "security.provider.10=TEST"
.getBytes());
}
- VM_ARGS.put("-Djava.security.properties=", SECURE_PROP_EXTN.toFile()
+ vmArgs.put("-Djava.security.properties=", SECURE_PROP_EXTN.toFile()
.getCanonicalPath());
}
- return VM_ARGS;
+ return vmArgs;
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/security/Signature/SignatureLength.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+import java.security.*;
+
+/*
+ * @test
+ * @bug 8161571
+ * @summary Reject signatures presented for verification that contain extra
+ * bytes.
+ * @run main SignatureLength
+ */
+public class SignatureLength {
+
+ public static void main(String[] args) throws Exception {
+ main0("EC", 256, "SHA256withECDSA", "SunEC");
+ main0("RSA", 2048, "SHA256withRSA", "SunRsaSign");
+ main0("DSA", 2048, "SHA256withDSA", "SUN");
+
+ if (System.getProperty("os.name").equals("SunOS")) {
+ main0("EC", 256, "SHA256withECDSA", null);
+ main0("RSA", 2048, "SHA256withRSA", null);
+ }
+ }
+
+ private static void main0(String keyAlgorithm, int keysize,
+ String signatureAlgorithm, String provider) throws Exception {
+ byte[] plaintext = "aaa".getBytes("UTF-8");
+
+ // Generate
+ KeyPairGenerator generator =
+ provider == null ?
+ (KeyPairGenerator) KeyPairGenerator.getInstance(keyAlgorithm) :
+ (KeyPairGenerator) KeyPairGenerator.getInstance(
+ keyAlgorithm, provider);
+ generator.initialize(keysize);
+ System.out.println("Generating " + keyAlgorithm + " keypair using " +
+ generator.getProvider().getName() + " JCE provider");
+ KeyPair keypair = generator.generateKeyPair();
+
+ // Sign
+ Signature signer =
+ provider == null ?
+ Signature.getInstance(signatureAlgorithm) :
+ Signature.getInstance(signatureAlgorithm, provider);
+ signer.initSign(keypair.getPrivate());
+ signer.update(plaintext);
+ System.out.println("Signing using " + signer.getProvider().getName() +
+ " JCE provider");
+ byte[] signature = signer.sign();
+
+ // Invalidate
+ System.out.println("Invalidating signature ...");
+ byte[] badSignature = new byte[signature.length + 5];
+ System.arraycopy(signature, 0, badSignature, 0, signature.length);
+ badSignature[signature.length] = 0x01;
+ badSignature[signature.length + 1] = 0x01;
+ badSignature[signature.length + 2] = 0x01;
+ badSignature[signature.length + 3] = 0x01;
+ badSignature[signature.length + 4] = 0x01;
+
+ // Verify
+ Signature verifier =
+ provider == null ?
+ Signature.getInstance(signatureAlgorithm) :
+ Signature.getInstance(signatureAlgorithm, provider);
+ verifier.initVerify(keypair.getPublic());
+ verifier.update(plaintext);
+ System.out.println("Verifying using " +
+ verifier.getProvider().getName() + " JCE provider");
+
+ try {
+ System.out.println("Valid? " + verifier.verify(badSignature));
+ throw new Exception(
+ "ERROR: expected a SignatureException but none was thrown");
+ } catch (SignatureException e) {
+ System.out.println("OK: caught expected exception: " + e);
+ }
+ System.out.println();
+ }
+}
--- a/jdk/test/java/security/modules/ModularTest.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/security/modules/ModularTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -82,7 +82,7 @@
String testName = new StringJoiner("_").add(cModuleType.toString())
.add(sModuletype.toString()).add(
- (addMetaDesc) ? "DESCRIPTOR" : "NO_DESCRIPTOR")
+ (addMetaDesc) ? "WITH_SERVICE" : "NO_SERVICE")
.toString();
System.out.format("%nStarting Test case: '%s'", testName);
@@ -165,10 +165,12 @@
if (moduleType == MODULE_TYPE.EXPLICIT) {
System.out.format(" %nGenerating ModuleDescriptor object");
builder = new Builder(moduleName).exports(pkg);
- if (isService) {
+ if (isService && serviceInterface != null && serviceImpl != null) {
builder.provides(serviceInterface, serviceImpl);
} else {
- builder.uses(serviceInterface);
+ if (serviceInterface != null) {
+ builder.uses(serviceInterface);
+ }
if (depends) {
builder.requires(serviceModuleName);
}
@@ -261,7 +263,7 @@
String jarName = jarPath.toFile().getName();
return (moduleType == MODULE_TYPE.EXPLICIT) ? mName
: ((moduleType == MODULE_TYPE.AUTO) ? jarName.substring(0,
- jarName.indexOf(JAR_EXTN)) : "");
+ jarName.indexOf(JAR_EXTN)) : null);
}
/**
--- a/jdk/test/java/util/concurrent/BlockingQueue/PollMemoryLeak.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/util/concurrent/BlockingQueue/PollMemoryLeak.java Thu Jul 21 20:09:19 2016 -0700
@@ -42,6 +42,7 @@
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.SynchronousQueue;
@@ -50,9 +51,11 @@
public class PollMemoryLeak {
public static void main(String[] args) throws InterruptedException {
final BlockingQueue[] qs = {
+ new LinkedBlockingDeque(10),
new LinkedBlockingQueue(10),
new LinkedTransferQueue(),
new ArrayBlockingQueue(10),
+ new ArrayBlockingQueue(10, true),
new SynchronousQueue(),
new SynchronousQueue(true),
};
--- a/jdk/test/java/util/concurrent/forkjoin/FJExceptionTableLeak.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/util/concurrent/forkjoin/FJExceptionTableLeak.java Thu Jul 21 20:09:19 2016 -0700
@@ -35,6 +35,7 @@
* @test
* @author Doug Lea
* @bug 8004138
+ * @key intermittent
* @summary Check if ForkJoinPool table leaks thrown exceptions.
* @run main/othervm -Xmx8m -Djava.util.concurrent.ForkJoinPool.common.parallelism=4 FJExceptionTableLeak
*/
--- a/jdk/test/java/util/concurrent/tck/Atomic8Test.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/util/concurrent/tck/Atomic8Test.java Thu Jul 21 20:09:19 2016 -0700
@@ -179,7 +179,7 @@
* result of supplied function
*/
public void testReferenceGetAndUpdate() {
- AtomicReference<Integer> a = new AtomicReference<Integer>(one);
+ AtomicReference<Integer> a = new AtomicReference<>(one);
assertEquals(new Integer(1), a.getAndUpdate(Atomic8Test::addInteger17));
assertEquals(new Integer(18), a.getAndUpdate(Atomic8Test::addInteger17));
assertEquals(new Integer(35), a.get());
@@ -190,7 +190,7 @@
* returns result.
*/
public void testReferenceUpdateAndGet() {
- AtomicReference<Integer> a = new AtomicReference<Integer>(one);
+ AtomicReference<Integer> a = new AtomicReference<>(one);
assertEquals(new Integer(18), a.updateAndGet(Atomic8Test::addInteger17));
assertEquals(new Integer(35), a.updateAndGet(Atomic8Test::addInteger17));
assertEquals(new Integer(35), a.get());
@@ -201,7 +201,7 @@
* with supplied function.
*/
public void testReferenceGetAndAccumulate() {
- AtomicReference<Integer> a = new AtomicReference<Integer>(one);
+ AtomicReference<Integer> a = new AtomicReference<>(one);
assertEquals(new Integer(1), a.getAndAccumulate(2, Atomic8Test::sumInteger));
assertEquals(new Integer(3), a.getAndAccumulate(3, Atomic8Test::sumInteger));
assertEquals(new Integer(6), a.get());
@@ -212,7 +212,7 @@
* returns result.
*/
public void testReferenceAccumulateAndGet() {
- AtomicReference<Integer> a = new AtomicReference<Integer>(one);
+ AtomicReference<Integer> 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());
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/concurrent/tck/AtomicBoolean9Test.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,203 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicBoolean9Test extends JSR166TestCase {
+ public static void main(String[] args) {
+ main(suite(), args);
+ }
+ public static Test suite() {
+ return new TestSuite(AtomicBoolean9Test.class);
+ }
+
+ /**
+ * getPlain returns the last value set
+ */
+ public void testGetPlainSet() {
+ AtomicBoolean ai = new AtomicBoolean(true);
+ assertEquals(true, ai.getPlain());
+ ai.set(false);
+ assertEquals(false, ai.getPlain());
+ ai.set(true);
+ assertEquals(true, ai.getPlain());
+ }
+
+ /**
+ * getOpaque returns the last value set
+ */
+ public void testGetOpaqueSet() {
+ AtomicBoolean ai = new AtomicBoolean(true);
+ assertEquals(true, ai.getOpaque());
+ ai.set(false);
+ assertEquals(false, ai.getOpaque());
+ ai.set(true);
+ assertEquals(true, ai.getOpaque());
+ }
+
+ /**
+ * getAcquire returns the last value set
+ */
+ public void testGetAcquireSet() {
+ AtomicBoolean ai = new AtomicBoolean(true);
+ assertEquals(true, ai.getAcquire());
+ ai.set(false);
+ assertEquals(false, ai.getAcquire());
+ ai.set(true);
+ assertEquals(true, ai.getAcquire());
+ }
+
+ /**
+ * get returns the last value setPlain
+ */
+ public void testGetSetPlain() {
+ AtomicBoolean ai = new AtomicBoolean(true);
+ assertEquals(true, ai.get());
+ ai.setPlain(false);
+ assertEquals(false, ai.get());
+ ai.setPlain(true);
+ assertEquals(true, ai.get());
+ }
+
+ /**
+ * get returns the last value setOpaque
+ */
+ public void testGetSetOpaque() {
+ AtomicBoolean ai = new AtomicBoolean(true);
+ assertEquals(true, ai.get());
+ ai.setOpaque(false);
+ assertEquals(false, ai.get());
+ ai.setOpaque(true);
+ assertEquals(true, ai.get());
+ }
+
+ /**
+ * get returns the last value setRelease
+ */
+ public void testGetSetRelease() {
+ AtomicBoolean ai = new AtomicBoolean(true);
+ assertEquals(true, ai.get());
+ ai.setRelease(false);
+ assertEquals(false, ai.get());
+ ai.setRelease(true);
+ assertEquals(true, ai.get());
+ }
+
+ /**
+ * compareAndExchange succeeds in changing value if equal to
+ * expected else fails
+ */
+ public void testCompareAndExchange() {
+ AtomicBoolean ai = new AtomicBoolean(true);
+ assertEquals(true, ai.compareAndExchange(true, false));
+ assertEquals(false, ai.compareAndExchange(false, false));
+ assertEquals(false, ai.get());
+ assertEquals(false, ai.compareAndExchange(true, true));
+ assertEquals(false, ai.get());
+ assertEquals(false, ai.compareAndExchange(false, true));
+ assertEquals(true, ai.get());
+ }
+
+ /**
+ * compareAndExchangeAcquire succeeds in changing value if equal to
+ * expected else fails
+ */
+ public void testCompareAndExchangeAcquire() {
+ AtomicBoolean ai = new AtomicBoolean(true);
+ assertEquals(true, ai.compareAndExchangeAcquire(true, false));
+ assertEquals(false, ai.compareAndExchangeAcquire(false, false));
+ assertEquals(false, ai.get());
+ assertEquals(false, ai.compareAndExchangeAcquire(true, true));
+ assertEquals(false, ai.get());
+ assertEquals(false, ai.compareAndExchangeAcquire(false, true));
+ assertEquals(true, ai.get());
+ }
+
+ /**
+ * compareAndExchangeRelease succeeds in changing value if equal to
+ * expected else fails
+ */
+ public void testCompareAndExchangeRelease() {
+ AtomicBoolean ai = new AtomicBoolean(true);
+ assertEquals(true, ai.compareAndExchangeRelease(true, false));
+ assertEquals(false, ai.compareAndExchangeRelease(false, false));
+ assertEquals(false, ai.get());
+ assertEquals(false, ai.compareAndExchangeRelease(true, true));
+ assertEquals(false, ai.get());
+ assertEquals(false, ai.compareAndExchangeRelease(false, true));
+ assertEquals(true, ai.get());
+ }
+
+ /**
+ * repeated weakCompareAndSetVolatile succeeds in changing value when equal
+ * to expected
+ */
+ public void testWeakCompareAndSetVolatile() {
+ AtomicBoolean ai = new AtomicBoolean(true);
+ do {} while (!ai.weakCompareAndSetVolatile(true, false));
+ do {} while (!ai.weakCompareAndSetVolatile(false, false));
+ assertEquals(false, ai.get());
+ do {} while (!ai.weakCompareAndSetVolatile(false, true));
+ assertEquals(true, ai.get());
+ }
+
+ /**
+ * repeated weakCompareAndSetAcquire succeeds in changing value when equal
+ * to expected
+ */
+ public void testWeakCompareAndSetAcquire() {
+ AtomicBoolean ai = new AtomicBoolean(true);
+ do {} while (!ai.weakCompareAndSetAcquire(true, false));
+ do {} while (!ai.weakCompareAndSetAcquire(false, false));
+ assertEquals(false, ai.get());
+ do {} while (!ai.weakCompareAndSetAcquire(false, true));
+ assertEquals(true, ai.get());
+ }
+
+ /**
+ * repeated weakCompareAndSetRelease succeeds in changing value when equal
+ * to expected
+ */
+ public void testWeakCompareAndSetRelease() {
+ AtomicBoolean ai = new AtomicBoolean(true);
+ do {} while (!ai.weakCompareAndSetRelease(true, false));
+ do {} while (!ai.weakCompareAndSetRelease(false, false));
+ assertEquals(false, ai.get());
+ do {} while (!ai.weakCompareAndSetRelease(false, true));
+ assertEquals(true, ai.get());
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/concurrent/tck/AtomicInteger9Test.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,203 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicInteger9Test extends JSR166TestCase {
+ public static void main(String[] args) {
+ main(suite(), args);
+ }
+ public static Test suite() {
+ return new TestSuite(AtomicInteger9Test.class);
+ }
+
+ /**
+ * getPlain returns the last value set
+ */
+ public void testGetPlainSet() {
+ AtomicInteger ai = new AtomicInteger(1);
+ assertEquals(1, ai.getPlain());
+ ai.set(2);
+ assertEquals(2, ai.getPlain());
+ ai.set(-3);
+ assertEquals(-3, ai.getPlain());
+ }
+
+ /**
+ * getOpaque returns the last value set
+ */
+ public void testGetOpaqueSet() {
+ AtomicInteger ai = new AtomicInteger(1);
+ assertEquals(1, ai.getOpaque());
+ ai.set(2);
+ assertEquals(2, ai.getOpaque());
+ ai.set(-3);
+ assertEquals(-3, ai.getOpaque());
+ }
+
+ /**
+ * getAcquire returns the last value set
+ */
+ public void testGetAcquireSet() {
+ AtomicInteger ai = new AtomicInteger(1);
+ assertEquals(1, ai.getAcquire());
+ ai.set(2);
+ assertEquals(2, ai.getAcquire());
+ ai.set(-3);
+ assertEquals(-3, ai.getAcquire());
+ }
+
+ /**
+ * get returns the last value setPlain
+ */
+ public void testGetSetPlain() {
+ AtomicInteger ai = new AtomicInteger(1);
+ assertEquals(1, ai.get());
+ ai.setPlain(2);
+ assertEquals(2, ai.get());
+ ai.setPlain(-3);
+ assertEquals(-3, ai.get());
+ }
+
+ /**
+ * get returns the last value setOpaque
+ */
+ public void testGetSetOpaque() {
+ AtomicInteger ai = new AtomicInteger(1);
+ assertEquals(1, ai.get());
+ ai.setOpaque(2);
+ assertEquals(2, ai.get());
+ ai.setOpaque(-3);
+ assertEquals(-3, ai.get());
+ }
+
+ /**
+ * get returns the last value setRelease
+ */
+ public void testGetSetRelease() {
+ AtomicInteger ai = new AtomicInteger(1);
+ assertEquals(1, ai.get());
+ ai.setRelease(2);
+ assertEquals(2, ai.get());
+ ai.setRelease(-3);
+ assertEquals(-3, ai.get());
+ }
+
+ /**
+ * compareAndExchange succeeds in changing value if equal to
+ * expected else fails
+ */
+ public void testCompareAndExchange() {
+ AtomicInteger ai = new AtomicInteger(1);
+ assertEquals(1, ai.compareAndExchange(1, 2));
+ assertEquals(2, ai.compareAndExchange(2, -4));
+ assertEquals(-4, ai.get());
+ assertEquals(-4, ai.compareAndExchange(-5, 7));
+ assertEquals(-4, ai.get());
+ assertEquals(-4, ai.compareAndExchange(-4, 7));
+ assertEquals(7, ai.get());
+ }
+
+ /**
+ * compareAndExchangeAcquire succeeds in changing value if equal to
+ * expected else fails
+ */
+ public void testCompareAndExchangeAcquire() {
+ AtomicInteger ai = new AtomicInteger(1);
+ assertEquals(1, ai.compareAndExchangeAcquire(1, 2));
+ assertEquals(2, ai.compareAndExchangeAcquire(2, -4));
+ assertEquals(-4, ai.get());
+ assertEquals(-4, ai.compareAndExchangeAcquire(-5, 7));
+ assertEquals(-4, ai.get());
+ assertEquals(-4, ai.compareAndExchangeAcquire(-4, 7));
+ assertEquals(7, ai.get());
+ }
+
+ /**
+ * compareAndExchangeRelease succeeds in changing value if equal to
+ * expected else fails
+ */
+ public void testCompareAndExchangeRelease() {
+ AtomicInteger ai = new AtomicInteger(1);
+ assertEquals(1, ai.compareAndExchangeRelease(1, 2));
+ assertEquals(2, ai.compareAndExchangeRelease(2, -4));
+ assertEquals(-4, ai.get());
+ assertEquals(-4, ai.compareAndExchangeRelease(-5, 7));
+ assertEquals(-4, ai.get());
+ assertEquals(-4, ai.compareAndExchangeRelease(-4, 7));
+ assertEquals(7, ai.get());
+ }
+
+ /**
+ * repeated weakCompareAndSetVolatile succeeds in changing value when equal
+ * to expected
+ */
+ public void testWeakCompareAndSetVolatile() {
+ AtomicInteger ai = new AtomicInteger(1);
+ do {} while (!ai.weakCompareAndSetVolatile(1, 2));
+ do {} while (!ai.weakCompareAndSetVolatile(2, -4));
+ assertEquals(-4, ai.get());
+ do {} while (!ai.weakCompareAndSetVolatile(-4, 7));
+ assertEquals(7, ai.get());
+ }
+
+ /**
+ * repeated weakCompareAndSetAcquire succeeds in changing value when equal
+ * to expected
+ */
+ public void testWeakCompareAndSetAcquire() {
+ AtomicInteger ai = new AtomicInteger(1);
+ do {} while (!ai.weakCompareAndSetAcquire(1, 2));
+ do {} while (!ai.weakCompareAndSetAcquire(2, -4));
+ assertEquals(-4, ai.get());
+ do {} while (!ai.weakCompareAndSetAcquire(-4, 7));
+ assertEquals(7, ai.get());
+ }
+
+ /**
+ * repeated weakCompareAndSetRelease succeeds in changing value when equal
+ * to expected
+ */
+ public void testWeakCompareAndSetRelease() {
+ AtomicInteger ai = new AtomicInteger(1);
+ do {} while (!ai.weakCompareAndSetRelease(1, 2));
+ do {} while (!ai.weakCompareAndSetRelease(2, -4));
+ assertEquals(-4, ai.get());
+ do {} while (!ai.weakCompareAndSetRelease(-4, 7));
+ assertEquals(7, ai.get());
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/concurrent/tck/AtomicIntegerArray9Test.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,267 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicIntegerArray;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicIntegerArray9Test extends JSR166TestCase {
+
+ public static void main(String[] args) {
+ main(suite(), args);
+ }
+ public static Test suite() {
+ return new TestSuite(AtomicIntegerArray9Test.class);
+ }
+
+ /**
+ * get and set for out of bound indices throw IndexOutOfBoundsException
+ */
+ public void testIndexing() {
+ AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+ for (int index : new int[] { -1, SIZE }) {
+ final int j = index;
+ final Runnable[] tasks = {
+ () -> aa.getPlain(j),
+ () -> aa.getOpaque(j),
+ () -> aa.getAcquire(j),
+ () -> aa.setPlain(j, 1),
+ () -> aa.setOpaque(j, 1),
+ () -> aa.setRelease(j, 1),
+ () -> aa.compareAndExchange(j, 1, 2),
+ () -> aa.compareAndExchangeAcquire(j, 1, 2),
+ () -> aa.compareAndExchangeRelease(j, 1, 2),
+ () -> aa.weakCompareAndSetVolatile(j, 1, 2),
+ () -> aa.weakCompareAndSetAcquire(j, 1, 2),
+ () -> aa.weakCompareAndSetRelease(j, 1, 2),
+ };
+
+ assertThrows(IndexOutOfBoundsException.class, tasks);
+ }
+ }
+
+ /**
+ * getPlain returns the last value set
+ */
+ public void testGetPlainSet() {
+ AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+ for (int i = 0; i < SIZE; i++) {
+ aa.set(i, 1);
+ assertEquals(1, aa.getPlain(i));
+ aa.set(i, 2);
+ assertEquals(2, aa.getPlain(i));
+ aa.set(i, -3);
+ assertEquals(-3, aa.getPlain(i));
+ }
+ }
+
+ /**
+ * getOpaque returns the last value set
+ */
+ public void testGetOpaqueSet() {
+ AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+ for (int i = 0; i < SIZE; i++) {
+ aa.set(i, 1);
+ assertEquals(1, aa.getOpaque(i));
+ aa.set(i, 2);
+ assertEquals(2, aa.getOpaque(i));
+ aa.set(i, -3);
+ assertEquals(-3, aa.getOpaque(i));
+ }
+ }
+
+ /**
+ * getAcquire returns the last value set
+ */
+ public void testGetAcquireSet() {
+ AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+ for (int i = 0; i < SIZE; i++) {
+ aa.set(i, 1);
+ assertEquals(1, aa.getAcquire(i));
+ aa.set(i, 2);
+ assertEquals(2, aa.getAcquire(i));
+ aa.set(i, -3);
+ assertEquals(-3, aa.getAcquire(i));
+ }
+ }
+
+ /**
+ * get returns the last value setPlain
+ */
+ public void testGetSetPlain() {
+ AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+ for (int i = 0; i < SIZE; i++) {
+ aa.setPlain(i, 1);
+ assertEquals(1, aa.get(i));
+ aa.setPlain(i, 2);
+ assertEquals(2, aa.get(i));
+ aa.setPlain(i, -3);
+ assertEquals(-3, aa.get(i));
+ }
+ }
+
+ /**
+ * get returns the last value setOpaque
+ */
+ public void testGetSetOpaque() {
+ AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+ for (int i = 0; i < SIZE; i++) {
+ aa.setOpaque(i, 1);
+ assertEquals(1, aa.get(i));
+ aa.setOpaque(i, 2);
+ assertEquals(2, aa.get(i));
+ aa.setOpaque(i, -3);
+ assertEquals(-3, aa.get(i));
+ }
+ }
+
+ /**
+ * get returns the last value setRelease
+ */
+ public void testGetSetRelease() {
+ AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+ for (int i = 0; i < SIZE; i++) {
+ aa.setRelease(i, 1);
+ assertEquals(1, aa.get(i));
+ aa.setRelease(i, 2);
+ assertEquals(2, aa.get(i));
+ aa.setRelease(i, -3);
+ assertEquals(-3, aa.get(i));
+ }
+ }
+
+ /**
+ * compareAndExchange succeeds in changing value if equal to
+ * expected else fails
+ */
+ public void testCompareAndExchange() {
+ AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+ for (int i = 0; i < SIZE; i++) {
+ aa.set(i, 1);
+ assertEquals(1, aa.compareAndExchange(i, 1, 2));
+ assertEquals(2, aa.compareAndExchange(i, 2, -4));
+ assertEquals(-4, aa.get(i));
+ assertEquals(-4, aa.compareAndExchange(i,-5, 7));
+ assertEquals(-4, aa.get(i));
+ assertEquals(-4, aa.compareAndExchange(i, -4, 7));
+ assertEquals(7, aa.get(i));
+ }
+ }
+
+ /**
+ * compareAndExchangeAcquire succeeds in changing value if equal to
+ * expected else fails
+ */
+ public void testCompareAndExchangeAcquire() {
+ AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+ for (int i = 0; i < SIZE; i++) {
+ aa.set(i, 1);
+ assertEquals(1, aa.compareAndExchangeAcquire(i, 1, 2));
+ assertEquals(2, aa.compareAndExchangeAcquire(i, 2, -4));
+ assertEquals(-4, aa.get(i));
+ assertEquals(-4, aa.compareAndExchangeAcquire(i,-5, 7));
+ assertEquals(-4, aa.get(i));
+ assertEquals(-4, aa.compareAndExchangeAcquire(i, -4, 7));
+ assertEquals(7, aa.get(i));
+ }
+ }
+
+ /**
+ * compareAndExchangeRelease succeeds in changing value if equal to
+ * expected else fails
+ */
+ public void testCompareAndExchangeRelease() {
+ AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+ for (int i = 0; i < SIZE; i++) {
+ aa.set(i, 1);
+ assertEquals(1, aa.compareAndExchangeRelease(i, 1, 2));
+ assertEquals(2, aa.compareAndExchangeRelease(i, 2, -4));
+ assertEquals(-4, aa.get(i));
+ assertEquals(-4, aa.compareAndExchangeRelease(i,-5, 7));
+ assertEquals(-4, aa.get(i));
+ assertEquals(-4, aa.compareAndExchangeRelease(i, -4, 7));
+ assertEquals(7, aa.get(i));
+ }
+ }
+
+ /**
+ * repeated weakCompareAndSetVolatile succeeds in changing value when equal
+ * to expected
+ */
+ public void testWeakCompareAndSetVolatile() {
+ AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+ for (int i = 0; i < SIZE; i++) {
+ aa.set(i, 1);
+ do {} while (!aa.weakCompareAndSetVolatile(i, 1, 2));
+ do {} while (!aa.weakCompareAndSetVolatile(i, 2, -4));
+ assertEquals(-4, aa.get(i));
+ do {} while (!aa.weakCompareAndSetVolatile(i, -4, 7));
+ assertEquals(7, aa.get(i));
+ }
+ }
+
+ /**
+ * repeated weakCompareAndSetAcquire succeeds in changing value when equal
+ * to expected
+ */
+ public void testWeakCompareAndSetAcquire() {
+ AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+ for (int i = 0; i < SIZE; i++) {
+ aa.set(i, 1);
+ do {} while (!aa.weakCompareAndSetAcquire(i, 1, 2));
+ do {} while (!aa.weakCompareAndSetAcquire(i, 2, -4));
+ assertEquals(-4, aa.get(i));
+ do {} while (!aa.weakCompareAndSetAcquire(i, -4, 7));
+ assertEquals(7, aa.get(i));
+ }
+ }
+
+ /**
+ * repeated weakCompareAndSetRelease succeeds in changing value when equal
+ * to expected
+ */
+ public void testWeakCompareAndSetRelease() {
+ AtomicIntegerArray aa = new AtomicIntegerArray(SIZE);
+ for (int i = 0; i < SIZE; i++) {
+ aa.set(i, 1);
+ do {} while (!aa.weakCompareAndSetRelease(i, 1, 2));
+ do {} while (!aa.weakCompareAndSetRelease(i, 2, -4));
+ assertEquals(-4, aa.get(i));
+ do {} while (!aa.weakCompareAndSetRelease(i, -4, 7));
+ assertEquals(7, aa.get(i));
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/concurrent/tck/AtomicLong9Test.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,203 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicLong9Test extends JSR166TestCase {
+ public static void main(String[] args) {
+ main(suite(), args);
+ }
+ public static Test suite() {
+ return new TestSuite(AtomicLong9Test.class);
+ }
+
+ /**
+ * getPlain returns the last value set
+ */
+ public void testGetPlainSet() {
+ AtomicLong ai = new AtomicLong(1);
+ assertEquals(1, ai.getPlain());
+ ai.set(2);
+ assertEquals(2, ai.getPlain());
+ ai.set(-3);
+ assertEquals(-3, ai.getPlain());
+ }
+
+ /**
+ * getOpaque returns the last value set
+ */
+ public void testGetOpaqueSet() {
+ AtomicLong ai = new AtomicLong(1);
+ assertEquals(1, ai.getOpaque());
+ ai.set(2);
+ assertEquals(2, ai.getOpaque());
+ ai.set(-3);
+ assertEquals(-3, ai.getOpaque());
+ }
+
+ /**
+ * getAcquire returns the last value set
+ */
+ public void testGetAcquireSet() {
+ AtomicLong ai = new AtomicLong(1);
+ assertEquals(1, ai.getAcquire());
+ ai.set(2);
+ assertEquals(2, ai.getAcquire());
+ ai.set(-3);
+ assertEquals(-3, ai.getAcquire());
+ }
+
+ /**
+ * get returns the last value setPlain
+ */
+ public void testGetSetPlain() {
+ AtomicLong ai = new AtomicLong(1);
+ assertEquals(1, ai.get());
+ ai.setPlain(2);
+ assertEquals(2, ai.get());
+ ai.setPlain(-3);
+ assertEquals(-3, ai.get());
+ }
+
+ /**
+ * get returns the last value setOpaque
+ */
+ public void testGetSetOpaque() {
+ AtomicLong ai = new AtomicLong(1);
+ assertEquals(1, ai.get());
+ ai.setOpaque(2);
+ assertEquals(2, ai.get());
+ ai.setOpaque(-3);
+ assertEquals(-3, ai.get());
+ }
+
+ /**
+ * get returns the last value setRelease
+ */
+ public void testGetSetRelease() {
+ AtomicLong ai = new AtomicLong(1);
+ assertEquals(1, ai.get());
+ ai.setRelease(2);
+ assertEquals(2, ai.get());
+ ai.setRelease(-3);
+ assertEquals(-3, ai.get());
+ }
+
+ /**
+ * compareAndExchange succeeds in changing value if equal to
+ * expected else fails
+ */
+ public void testCompareAndExchange() {
+ AtomicLong ai = new AtomicLong(1);
+ assertEquals(1, ai.compareAndExchange(1, 2));
+ assertEquals(2, ai.compareAndExchange(2, -4));
+ assertEquals(-4, ai.get());
+ assertEquals(-4, ai.compareAndExchange(-5, 7));
+ assertEquals(-4, ai.get());
+ assertEquals(-4, ai.compareAndExchange(-4, 7));
+ assertEquals(7, ai.get());
+ }
+
+ /**
+ * compareAndExchangeAcquire succeeds in changing value if equal to
+ * expected else fails
+ */
+ public void testCompareAndExchangeAcquire() {
+ AtomicLong ai = new AtomicLong(1);
+ assertEquals(1, ai.compareAndExchangeAcquire(1, 2));
+ assertEquals(2, ai.compareAndExchangeAcquire(2, -4));
+ assertEquals(-4, ai.get());
+ assertEquals(-4, ai.compareAndExchangeAcquire(-5, 7));
+ assertEquals(-4, ai.get());
+ assertEquals(-4, ai.compareAndExchangeAcquire(-4, 7));
+ assertEquals(7, ai.get());
+ }
+
+ /**
+ * compareAndExchangeRelease succeeds in changing value if equal to
+ * expected else fails
+ */
+ public void testCompareAndExchangeRelease() {
+ AtomicLong ai = new AtomicLong(1);
+ assertEquals(1, ai.compareAndExchangeRelease(1, 2));
+ assertEquals(2, ai.compareAndExchangeRelease(2, -4));
+ assertEquals(-4, ai.get());
+ assertEquals(-4, ai.compareAndExchangeRelease(-5, 7));
+ assertEquals(-4, ai.get());
+ assertEquals(-4, ai.compareAndExchangeRelease(-4, 7));
+ assertEquals(7, ai.get());
+ }
+
+ /**
+ * repeated weakCompareAndSetVolatile succeeds in changing value when equal
+ * to expected
+ */
+ public void testWeakCompareAndSetVolatile() {
+ AtomicLong ai = new AtomicLong(1);
+ do {} while (!ai.weakCompareAndSetVolatile(1, 2));
+ do {} while (!ai.weakCompareAndSetVolatile(2, -4));
+ assertEquals(-4, ai.get());
+ do {} while (!ai.weakCompareAndSetVolatile(-4, 7));
+ assertEquals(7, ai.get());
+ }
+
+ /**
+ * repeated weakCompareAndSetAcquire succeeds in changing value when equal
+ * to expected
+ */
+ public void testWeakCompareAndSetAcquire() {
+ AtomicLong ai = new AtomicLong(1);
+ do {} while (!ai.weakCompareAndSetAcquire(1, 2));
+ do {} while (!ai.weakCompareAndSetAcquire(2, -4));
+ assertEquals(-4, ai.get());
+ do {} while (!ai.weakCompareAndSetAcquire(-4, 7));
+ assertEquals(7, ai.get());
+ }
+
+ /**
+ * repeated weakCompareAndSetRelease succeeds in changing value when equal
+ * to expected
+ */
+ public void testWeakCompareAndSetRelease() {
+ AtomicLong ai = new AtomicLong(1);
+ do {} while (!ai.weakCompareAndSetRelease(1, 2));
+ do {} while (!ai.weakCompareAndSetRelease(2, -4));
+ assertEquals(-4, ai.get());
+ do {} while (!ai.weakCompareAndSetRelease(-4, 7));
+ assertEquals(7, ai.get());
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/concurrent/tck/AtomicLongArray9Test.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,266 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicLongArray;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicLongArray9Test extends JSR166TestCase {
+ public static void main(String[] args) {
+ main(suite(), args);
+ }
+ public static Test suite() {
+ return new TestSuite(AtomicLongArray9Test.class);
+ }
+
+ /**
+ * get and set for out of bound indices throw IndexOutOfBoundsException
+ */
+ public void testIndexing() {
+ AtomicLongArray aa = new AtomicLongArray(SIZE);
+ for (int index : new int[] { -1, SIZE }) {
+ final int j = index;
+ final Runnable[] tasks = {
+ () -> aa.getPlain(j),
+ () -> aa.getOpaque(j),
+ () -> aa.getAcquire(j),
+ () -> aa.setPlain(j, 1),
+ () -> aa.setOpaque(j, 1),
+ () -> aa.setRelease(j, 1),
+ () -> aa.compareAndExchange(j, 1, 2),
+ () -> aa.compareAndExchangeAcquire(j, 1, 2),
+ () -> aa.compareAndExchangeRelease(j, 1, 2),
+ () -> aa.weakCompareAndSetVolatile(j, 1, 2),
+ () -> aa.weakCompareAndSetAcquire(j, 1, 2),
+ () -> aa.weakCompareAndSetRelease(j, 1, 2),
+ };
+
+ assertThrows(IndexOutOfBoundsException.class, tasks);
+ }
+ }
+
+ /**
+ * getPlain returns the last value set
+ */
+ public void testGetPlainSet() {
+ AtomicLongArray aa = new AtomicLongArray(SIZE);
+ for (int i = 0; i < SIZE; i++) {
+ aa.set(i, 1);
+ assertEquals(1, aa.getPlain(i));
+ aa.set(i, 2);
+ assertEquals(2, aa.getPlain(i));
+ aa.set(i, -3);
+ assertEquals(-3, aa.getPlain(i));
+ }
+ }
+
+ /**
+ * getOpaque returns the last value set
+ */
+ public void testGetOpaqueSet() {
+ AtomicLongArray aa = new AtomicLongArray(SIZE);
+ for (int i = 0; i < SIZE; i++) {
+ aa.set(i, 1);
+ assertEquals(1, aa.getOpaque(i));
+ aa.set(i, 2);
+ assertEquals(2, aa.getOpaque(i));
+ aa.set(i, -3);
+ assertEquals(-3, aa.getOpaque(i));
+ }
+ }
+
+ /**
+ * getAcquire returns the last value set
+ */
+ public void testGetAcquireSet() {
+ AtomicLongArray aa = new AtomicLongArray(SIZE);
+ for (int i = 0; i < SIZE; i++) {
+ aa.set(i, 1);
+ assertEquals(1, aa.getAcquire(i));
+ aa.set(i, 2);
+ assertEquals(2, aa.getAcquire(i));
+ aa.set(i, -3);
+ assertEquals(-3, aa.getAcquire(i));
+ }
+ }
+
+ /**
+ * get returns the last value setPlain
+ */
+ public void testGetSetPlain() {
+ AtomicLongArray aa = new AtomicLongArray(SIZE);
+ for (int i = 0; i < SIZE; i++) {
+ aa.setPlain(i, 1);
+ assertEquals(1, aa.get(i));
+ aa.setPlain(i, 2);
+ assertEquals(2, aa.get(i));
+ aa.setPlain(i, -3);
+ assertEquals(-3, aa.get(i));
+ }
+ }
+
+ /**
+ * get returns the last value setOpaque
+ */
+ public void testGetSetOpaque() {
+ AtomicLongArray aa = new AtomicLongArray(SIZE);
+ for (int i = 0; i < SIZE; i++) {
+ aa.setOpaque(i, 1);
+ assertEquals(1, aa.get(i));
+ aa.setOpaque(i, 2);
+ assertEquals(2, aa.get(i));
+ aa.setOpaque(i, -3);
+ assertEquals(-3, aa.get(i));
+ }
+ }
+
+ /**
+ * get returns the last value setRelease
+ */
+ public void testGetSetRelease() {
+ AtomicLongArray aa = new AtomicLongArray(SIZE);
+ for (int i = 0; i < SIZE; i++) {
+ aa.setRelease(i, 1);
+ assertEquals(1, aa.get(i));
+ aa.setRelease(i, 2);
+ assertEquals(2, aa.get(i));
+ aa.setRelease(i, -3);
+ assertEquals(-3, aa.get(i));
+ }
+ }
+
+ /**
+ * compareAndExchange succeeds in changing value if equal to
+ * expected else fails
+ */
+ public void testCompareAndExchange() {
+ AtomicLongArray aa = new AtomicLongArray(SIZE);
+ for (int i = 0; i < SIZE; i++) {
+ aa.set(i, 1);
+ assertEquals(1, aa.compareAndExchange(i, 1, 2));
+ assertEquals(2, aa.compareAndExchange(i, 2, -4));
+ assertEquals(-4, aa.get(i));
+ assertEquals(-4, aa.compareAndExchange(i,-5, 7));
+ assertEquals(-4, aa.get(i));
+ assertEquals(-4, aa.compareAndExchange(i, -4, 7));
+ assertEquals(7, aa.get(i));
+ }
+ }
+
+ /**
+ * compareAndExchangeAcquire succeeds in changing value if equal to
+ * expected else fails
+ */
+ public void testCompareAndExchangeAcquire() {
+ AtomicLongArray aa = new AtomicLongArray(SIZE);
+ for (int i = 0; i < SIZE; i++) {
+ aa.set(i, 1);
+ assertEquals(1, aa.compareAndExchangeAcquire(i, 1, 2));
+ assertEquals(2, aa.compareAndExchangeAcquire(i, 2, -4));
+ assertEquals(-4, aa.get(i));
+ assertEquals(-4, aa.compareAndExchangeAcquire(i,-5, 7));
+ assertEquals(-4, aa.get(i));
+ assertEquals(-4, aa.compareAndExchangeAcquire(i, -4, 7));
+ assertEquals(7, aa.get(i));
+ }
+ }
+
+ /**
+ * compareAndExchangeRelease succeeds in changing value if equal to
+ * expected else fails
+ */
+ public void testCompareAndExchangeRelease() {
+ AtomicLongArray aa = new AtomicLongArray(SIZE);
+ for (int i = 0; i < SIZE; i++) {
+ aa.set(i, 1);
+ assertEquals(1, aa.compareAndExchangeRelease(i, 1, 2));
+ assertEquals(2, aa.compareAndExchangeRelease(i, 2, -4));
+ assertEquals(-4, aa.get(i));
+ assertEquals(-4, aa.compareAndExchangeRelease(i,-5, 7));
+ assertEquals(-4, aa.get(i));
+ assertEquals(-4, aa.compareAndExchangeRelease(i, -4, 7));
+ assertEquals(7, aa.get(i));
+ }
+ }
+
+ /**
+ * repeated weakCompareAndSetVolatile succeeds in changing value when equal
+ * to expected
+ */
+ public void testWeakCompareAndSetVolatile() {
+ AtomicLongArray aa = new AtomicLongArray(SIZE);
+ for (int i = 0; i < SIZE; i++) {
+ aa.set(i, 1);
+ do {} while (!aa.weakCompareAndSetVolatile(i, 1, 2));
+ do {} while (!aa.weakCompareAndSetVolatile(i, 2, -4));
+ assertEquals(-4, aa.get(i));
+ do {} while (!aa.weakCompareAndSetVolatile(i, -4, 7));
+ assertEquals(7, aa.get(i));
+ }
+ }
+
+ /**
+ * repeated weakCompareAndSetAcquire succeeds in changing value when equal
+ * to expected
+ */
+ public void testWeakCompareAndSetAcquire() {
+ AtomicLongArray aa = new AtomicLongArray(SIZE);
+ for (int i = 0; i < SIZE; i++) {
+ aa.set(i, 1);
+ do {} while (!aa.weakCompareAndSetAcquire(i, 1, 2));
+ do {} while (!aa.weakCompareAndSetAcquire(i, 2, -4));
+ assertEquals(-4, aa.get(i));
+ do {} while (!aa.weakCompareAndSetAcquire(i, -4, 7));
+ assertEquals(7, aa.get(i));
+ }
+ }
+
+ /**
+ * repeated weakCompareAndSetRelease succeeds in changing value when equal
+ * to expected
+ */
+ public void testWeakCompareAndSetRelease() {
+ AtomicLongArray aa = new AtomicLongArray(SIZE);
+ for (int i = 0; i < SIZE; i++) {
+ aa.set(i, 1);
+ do {} while (!aa.weakCompareAndSetRelease(i, 1, 2));
+ do {} while (!aa.weakCompareAndSetRelease(i, 2, -4));
+ assertEquals(-4, aa.get(i));
+ do {} while (!aa.weakCompareAndSetRelease(i, -4, 7));
+ assertEquals(7, aa.get(i));
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/concurrent/tck/AtomicReference9Test.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,203 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.concurrent.atomic.AtomicReference;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicReference9Test extends JSR166TestCase {
+ public static void main(String[] args) {
+ main(suite(), args);
+ }
+ public static Test suite() {
+ return new TestSuite(AtomicReference9Test.class);
+ }
+
+ /**
+ * getPlain returns the last value set
+ */
+ public void testGetPlainSet() {
+ AtomicReference<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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());
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/concurrent/tck/AtomicReferenceArray9Test.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,266 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicReferenceArray;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AtomicReferenceArray9Test extends JSR166TestCase {
+ public static void main(String[] args) {
+ main(suite(), args);
+ }
+ public static Test suite() {
+ return new TestSuite(AtomicReferenceArray9Test.class);
+ }
+
+ /**
+ * get and set for out of bound indices throw IndexOutOfBoundsException
+ */
+ public void testIndexing() {
+ AtomicReferenceArray<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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));
+ }
+ }
+
+}
--- a/jdk/test/java/util/concurrent/tck/AtomicReferenceArrayTest.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/util/concurrent/tck/AtomicReferenceArrayTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -243,4 +243,5 @@
AtomicReferenceArray<Integer> aa = new AtomicReferenceArray<Integer>(a);
assertEquals(Arrays.toString(a), aa.toString());
}
+
}
--- a/jdk/test/java/util/concurrent/tck/AtomicReferenceTest.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/util/concurrent/tck/AtomicReferenceTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -161,7 +161,7 @@
* toString returns current value.
*/
public void testToString() {
- AtomicReference<Integer> ai = new AtomicReference<Integer>(one);
+ AtomicReference<Integer> ai = new AtomicReference<>(one);
assertEquals(one.toString(), ai.toString());
ai.set(two);
assertEquals(two.toString(), ai.toString());
--- a/jdk/test/java/util/concurrent/tck/CompletableFutureTest.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/util/concurrent/tck/CompletableFutureTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -57,6 +57,7 @@
import java.util.concurrent.Executor;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
+import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@@ -486,62 +487,68 @@
class FailingSupplier extends CheckedAction
implements Supplier<Integer>
{
- 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<Integer>
{
- 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<Integer, Integer>
{
- 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<Integer, Integer>
{
- 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<Integer, Integer, Integer>
{
- FailingBiFunction(ExecutionMode m) { super(m); }
+ final CFException ex;
+ FailingBiFunction(ExecutionMode m) { super(m); ex = new CFException(); }
public Integer apply(Integer x, Integer y) {
invoked();
value = subtract(x, y);
- throw new CFException();
+ throw ex;
}
}
class FailingRunnable extends CheckedAction implements Runnable {
- FailingRunnable(ExecutionMode m) { super(m); }
+ final CFException ex;
+ FailingRunnable(ExecutionMode m) { super(m); ex = new CFException(); }
public void run() {
invoked();
- throw new CFException();
+ throw ex;
}
}
@@ -561,11 +568,21 @@
class FailingCompletableFutureFunction extends CheckedIntegerAction
implements Function<Integer, CompletableFuture<Integer>>
{
- FailingCompletableFutureFunction(ExecutionMode m) { super(m); }
+ final CFException ex;
+ FailingCompletableFutureFunction(ExecutionMode m) { super(m); ex = new CFException(); }
public CompletableFuture<Integer> apply(Integer x) {
invoked();
value = x;
- throw new CFException();
+ throw ex;
+ }
+ }
+
+ static class CountingRejectingExecutor implements Executor {
+ final RejectedExecutionException ex = new RejectedExecutionException();
+ final AtomicInteger count = new AtomicInteger(0);
+ public void execute(Runnable r) {
+ count.getAndIncrement();
+ throw ex;
}
}
@@ -1249,10 +1266,22 @@
{
final FailingRunnable r = new FailingRunnable(m);
final CompletableFuture<Void> f = m.runAsync(r);
- checkCompletedWithWrappedCFException(f);
+ checkCompletedWithWrappedException(f, r.ex);
r.assertInvoked();
}}
+ public void testRunAsync_rejectingExecutor() {
+ CountingRejectingExecutor e = new CountingRejectingExecutor();
+ try {
+ CompletableFuture.runAsync(() -> {}, e);
+ shouldThrow();
+ } catch (Throwable t) {
+ assertSame(e.ex, t);
+ }
+
+ assertEquals(1, e.count.get());
+ }
+
/**
* supplyAsync completes with result of supplier
*/
@@ -1283,10 +1312,22 @@
{
FailingSupplier r = new FailingSupplier(m);
CompletableFuture<Integer> f = m.supplyAsync(r);
- checkCompletedWithWrappedCFException(f);
+ checkCompletedWithWrappedException(f, r.ex);
r.assertInvoked();
}}
+ public void testSupplyAsync_rejectingExecutor() {
+ CountingRejectingExecutor e = new CountingRejectingExecutor();
+ try {
+ CompletableFuture.supplyAsync(() -> null, e);
+ shouldThrow();
+ } catch (Throwable t) {
+ assertSame(e.ex, t);
+ }
+
+ assertEquals(1, e.count.get());
+ }
+
// seq completion methods
/**
@@ -1405,12 +1446,12 @@
final CompletableFuture<Void> h4 = m.runAfterBoth(f, f, rs[4]);
final CompletableFuture<Void> h5 = m.runAfterEither(f, f, rs[5]);
- checkCompletedWithWrappedCFException(h0);
- checkCompletedWithWrappedCFException(h1);
- checkCompletedWithWrappedCFException(h2);
- checkCompletedWithWrappedCFException(h3);
- checkCompletedWithWrappedCFException(h4);
- checkCompletedWithWrappedCFException(h5);
+ checkCompletedWithWrappedException(h0, rs[0].ex);
+ checkCompletedWithWrappedException(h1, rs[1].ex);
+ checkCompletedWithWrappedException(h2, rs[2].ex);
+ checkCompletedWithWrappedException(h3, rs[3].ex);
+ checkCompletedWithWrappedException(h4, rs[4].ex);
+ checkCompletedWithWrappedException(h5, rs[5].ex);
checkCompletedNormally(f, v1);
}}
@@ -1509,10 +1550,10 @@
final CompletableFuture<Integer> h2 = m.thenApply(f, rs[2]);
final CompletableFuture<Integer> h3 = m.applyToEither(f, f, rs[3]);
- checkCompletedWithWrappedCFException(h0);
- checkCompletedWithWrappedCFException(h1);
- checkCompletedWithWrappedCFException(h2);
- checkCompletedWithWrappedCFException(h3);
+ checkCompletedWithWrappedException(h0, rs[0].ex);
+ checkCompletedWithWrappedException(h1, rs[1].ex);
+ checkCompletedWithWrappedException(h2, rs[2].ex);
+ checkCompletedWithWrappedException(h3, rs[3].ex);
checkCompletedNormally(f, v1);
}}
@@ -1611,10 +1652,10 @@
final CompletableFuture<Void> h2 = m.thenAccept(f, rs[2]);
final CompletableFuture<Void> h3 = m.acceptEither(f, f, rs[3]);
- checkCompletedWithWrappedCFException(h0);
- checkCompletedWithWrappedCFException(h1);
- checkCompletedWithWrappedCFException(h2);
- checkCompletedWithWrappedCFException(h3);
+ checkCompletedWithWrappedException(h0, rs[0].ex);
+ checkCompletedWithWrappedException(h1, rs[1].ex);
+ checkCompletedWithWrappedException(h2, rs[2].ex);
+ checkCompletedWithWrappedException(h3, rs[3].ex);
checkCompletedNormally(f, v1);
}}
@@ -1776,9 +1817,9 @@
assertTrue(snd.complete(w2));
final CompletableFuture<Integer> h3 = m.thenCombine(f, g, r3);
- checkCompletedWithWrappedCFException(h1);
- checkCompletedWithWrappedCFException(h2);
- checkCompletedWithWrappedCFException(h3);
+ checkCompletedWithWrappedException(h1, r1.ex);
+ checkCompletedWithWrappedException(h2, r2.ex);
+ checkCompletedWithWrappedException(h3, r3.ex);
r1.assertInvoked();
r2.assertInvoked();
r3.assertInvoked();
@@ -1940,9 +1981,9 @@
assertTrue(snd.complete(w2));
final CompletableFuture<Void> h3 = m.thenAcceptBoth(f, g, r3);
- checkCompletedWithWrappedCFException(h1);
- checkCompletedWithWrappedCFException(h2);
- checkCompletedWithWrappedCFException(h3);
+ checkCompletedWithWrappedException(h1, r1.ex);
+ checkCompletedWithWrappedException(h2, r2.ex);
+ checkCompletedWithWrappedException(h3, r3.ex);
r1.assertInvoked();
r2.assertInvoked();
r3.assertInvoked();
@@ -2104,9 +2145,9 @@
assertTrue(snd.complete(w2));
final CompletableFuture<Void> h3 = m.runAfterBoth(f, g, r3);
- checkCompletedWithWrappedCFException(h1);
- checkCompletedWithWrappedCFException(h2);
- checkCompletedWithWrappedCFException(h3);
+ checkCompletedWithWrappedException(h1, r1.ex);
+ checkCompletedWithWrappedException(h2, r2.ex);
+ checkCompletedWithWrappedException(h3, r3.ex);
r1.assertInvoked();
r2.assertInvoked();
r3.assertInvoked();
@@ -2396,10 +2437,10 @@
f.complete(v1);
final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]);
final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]);
- checkCompletedWithWrappedCFException(h0);
- checkCompletedWithWrappedCFException(h1);
- checkCompletedWithWrappedCFException(h2);
- checkCompletedWithWrappedCFException(h3);
+ checkCompletedWithWrappedException(h0, rs[0].ex);
+ checkCompletedWithWrappedException(h1, rs[1].ex);
+ checkCompletedWithWrappedException(h2, rs[2].ex);
+ checkCompletedWithWrappedException(h3, rs[3].ex);
for (int i = 0; i < 4; i++) rs[i].assertValue(v1);
g.complete(v2);
@@ -2408,10 +2449,10 @@
final CompletableFuture<Integer> h4 = m.applyToEither(f, g, rs[4]);
final CompletableFuture<Integer> h5 = m.applyToEither(g, f, rs[5]);
- checkCompletedWithWrappedCFException(h4);
+ checkCompletedWithWrappedException(h4, rs[4].ex);
assertTrue(Objects.equals(v1, rs[4].value) ||
Objects.equals(v2, rs[4].value));
- checkCompletedWithWrappedCFException(h5);
+ checkCompletedWithWrappedException(h5, rs[5].ex);
assertTrue(Objects.equals(v1, rs[5].value) ||
Objects.equals(v2, rs[5].value));
@@ -2655,10 +2696,10 @@
f.complete(v1);
final CompletableFuture<Void> h2 = m.acceptEither(f, g, rs[2]);
final CompletableFuture<Void> h3 = m.acceptEither(g, f, rs[3]);
- checkCompletedWithWrappedCFException(h0);
- checkCompletedWithWrappedCFException(h1);
- checkCompletedWithWrappedCFException(h2);
- checkCompletedWithWrappedCFException(h3);
+ checkCompletedWithWrappedException(h0, rs[0].ex);
+ checkCompletedWithWrappedException(h1, rs[1].ex);
+ checkCompletedWithWrappedException(h2, rs[2].ex);
+ checkCompletedWithWrappedException(h3, rs[3].ex);
for (int i = 0; i < 4; i++) rs[i].assertValue(v1);
g.complete(v2);
@@ -2667,10 +2708,10 @@
final CompletableFuture<Void> h4 = m.acceptEither(f, g, rs[4]);
final CompletableFuture<Void> h5 = m.acceptEither(g, f, rs[5]);
- checkCompletedWithWrappedCFException(h4);
+ checkCompletedWithWrappedException(h4, rs[4].ex);
assertTrue(Objects.equals(v1, rs[4].value) ||
Objects.equals(v2, rs[4].value));
- checkCompletedWithWrappedCFException(h5);
+ checkCompletedWithWrappedException(h5, rs[5].ex);
assertTrue(Objects.equals(v1, rs[5].value) ||
Objects.equals(v2, rs[5].value));
@@ -2686,6 +2727,7 @@
for (ExecutionMode m : ExecutionMode.values())
for (Integer v1 : new Integer[] { 1, null })
for (Integer v2 : new Integer[] { 2, null })
+ for (boolean pushNop : new boolean[] { true, false })
{
final CompletableFuture<Integer> f = new CompletableFuture<>();
final CompletableFuture<Integer> g = new CompletableFuture<>();
@@ -2698,6 +2740,10 @@
checkIncomplete(h1);
rs[0].assertNotInvoked();
rs[1].assertNotInvoked();
+ if (pushNop) { // ad hoc test of intra-completion interference
+ m.thenRun(f, () -> {});
+ m.thenRun(g, () -> {});
+ }
f.complete(v1);
checkCompletedNormally(h0, null);
checkCompletedNormally(h1, null);
@@ -2910,16 +2956,16 @@
assertTrue(f.complete(v1));
final CompletableFuture<Void> h2 = m.runAfterEither(f, g, rs[2]);
final CompletableFuture<Void> 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<Void> h4 = m.runAfterEither(f, g, rs[4]);
final CompletableFuture<Void> h5 = m.runAfterEither(g, f, rs[5]);
- checkCompletedWithWrappedCFException(h4);
- checkCompletedWithWrappedCFException(h5);
+ checkCompletedWithWrappedException(h4, rs[4].ex);
+ checkCompletedWithWrappedException(h5, rs[5].ex);
checkCompletedNormally(f, v1);
checkCompletedNormally(g, v2);
@@ -2980,7 +3026,7 @@
final CompletableFuture<Integer> g = m.thenCompose(f, r);
if (createIncomplete) assertTrue(f.complete(v1));
- checkCompletedWithWrappedCFException(g);
+ checkCompletedWithWrappedException(g, r.ex);
checkCompletedNormally(f, v1);
}}
@@ -3089,7 +3135,7 @@
}
}
- public void testAllOf_backwards() throws Exception {
+ public void testAllOf_normal_backwards() throws Exception {
for (int k = 1; k < 10; k++) {
CompletableFuture<Integer>[] fs
= (CompletableFuture<Integer>[]) new CompletableFuture[k];
@@ -3337,6 +3383,151 @@
}
/**
+ * Test submissions to an executor that rejects all tasks.
+ */
+ public void testRejectingExecutor() {
+ for (Integer v : new Integer[] { 1, null })
+ {
+ final CountingRejectingExecutor e = new CountingRejectingExecutor();
+
+ final CompletableFuture<Integer> complete = CompletableFuture.completedFuture(v);
+ final CompletableFuture<Integer> incomplete = new CompletableFuture<>();
+
+ List<CompletableFuture<?>> futures = new ArrayList<>();
+
+ List<CompletableFuture<Integer>> srcs = new ArrayList<>();
+ srcs.add(complete);
+ srcs.add(incomplete);
+
+ for (CompletableFuture<Integer> src : srcs) {
+ List<CompletableFuture<?>> 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<CompletableFuture<?>> 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<CompletableFuture<?>> 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<Integer> complete = CompletableFuture.completedFuture(v);
+ final CompletableFuture<Integer> incomplete = new CompletableFuture<>();
+
+ List<CompletableFuture<?>> futures = new ArrayList<>();
+
+ List<CompletableFuture<Integer>> srcs = new ArrayList<>();
+ srcs.add(complete);
+ srcs.add(incomplete);
+
+ List<CompletableFuture<?>> fs = new ArrayList<>();
+ fs.add(incomplete.thenRunAsync(() -> {}, e));
+ fs.add(incomplete.thenAcceptAsync((z) -> {}, e));
+ fs.add(incomplete.thenApplyAsync((z) -> z, e));
+
+ fs.add(incomplete.thenCombineAsync(incomplete, (x, y) -> x, e));
+ fs.add(incomplete.thenAcceptBothAsync(incomplete, (x, y) -> {}, e));
+ fs.add(incomplete.runAfterBothAsync(incomplete, () -> {}, e));
+
+ fs.add(incomplete.applyToEitherAsync(incomplete, (z) -> z, e));
+ fs.add(incomplete.acceptEitherAsync(incomplete, (z) -> {}, e));
+ fs.add(incomplete.runAfterEitherAsync(incomplete, () -> {}, e));
+
+ fs.add(incomplete.thenComposeAsync((z) -> null, e));
+ fs.add(incomplete.whenCompleteAsync((z, t) -> {}, e));
+ fs.add(incomplete.handleAsync((z, t) -> null, e));
+
+ fs.add(complete.thenCombineAsync(incomplete, (x, y) -> x, e));
+ fs.add(incomplete.thenCombineAsync(complete, (x, y) -> x, e));
+
+ fs.add(complete.thenAcceptBothAsync(incomplete, (x, y) -> {}, e));
+ fs.add(incomplete.thenAcceptBothAsync(complete, (x, y) -> {}, e));
+
+ fs.add(complete.runAfterBothAsync(incomplete, () -> {}, e));
+ fs.add(incomplete.runAfterBothAsync(complete, () -> {}, e));
+
+ for (CompletableFuture<?> future : fs)
+ checkIncomplete(future);
+
+ for (CompletableFuture<?> future : fs)
+ future.complete(null);
+
+ incomplete.complete(v);
+
+ for (CompletableFuture<?> future : fs)
+ checkCompletedNormally(future, null);
+
+ assertEquals(0, e.count.get());
+ }}
+
+ /**
* toCompletableFuture returns this CompletableFuture.
*/
public void testToCompletableFuture() {
@@ -3659,12 +3850,25 @@
//--- tests of implementation details; not part of official tck ---
Object resultOf(CompletableFuture<?> f) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ try {
+ System.setSecurityManager(null);
+ } catch (SecurityException giveUp) {
+ return "Reflection not available";
+ }
+ }
+
try {
java.lang.reflect.Field resultField
= CompletableFuture.class.getDeclaredField("result");
resultField.setAccessible(true);
return resultField.get(f);
- } catch (Throwable t) { throw new AssertionError(t); }
+ } catch (Throwable t) {
+ throw new AssertionError(t);
+ } finally {
+ if (sm != null) System.setSecurityManager(sm);
+ }
}
public void testExceptionPropagationReusesResultObject() {
@@ -3675,33 +3879,44 @@
final CompletableFuture<Integer> v42 = CompletableFuture.completedFuture(42);
final CompletableFuture<Integer> incomplete = new CompletableFuture<>();
+ final Runnable noopRunnable = new Noop(m);
+ final Consumer<Integer> noopConsumer = new NoopConsumer(m);
+ final Function<Integer, Integer> incFunction = new IncFunction(m);
+
List<Function<CompletableFuture<Integer>, CompletableFuture<?>>> funs
= new ArrayList<>();
- funs.add((y) -> m.thenRun(y, new Noop(m)));
- funs.add((y) -> m.thenAccept(y, new NoopConsumer(m)));
- funs.add((y) -> m.thenApply(y, new IncFunction(m)));
-
- funs.add((y) -> m.runAfterEither(y, incomplete, new Noop(m)));
- funs.add((y) -> m.acceptEither(y, incomplete, new NoopConsumer(m)));
- funs.add((y) -> m.applyToEither(y, incomplete, new IncFunction(m)));
-
- funs.add((y) -> m.runAfterBoth(y, v42, new Noop(m)));
+ funs.add((y) -> m.thenRun(y, noopRunnable));
+ funs.add((y) -> m.thenAccept(y, noopConsumer));
+ funs.add((y) -> m.thenApply(y, incFunction));
+
+ funs.add((y) -> m.runAfterEither(y, incomplete, noopRunnable));
+ funs.add((y) -> m.acceptEither(y, incomplete, noopConsumer));
+ funs.add((y) -> m.applyToEither(y, incomplete, incFunction));
+
+ funs.add((y) -> m.runAfterBoth(y, v42, noopRunnable));
+ funs.add((y) -> m.runAfterBoth(v42, y, noopRunnable));
funs.add((y) -> m.thenAcceptBoth(y, v42, new SubtractAction(m)));
+ funs.add((y) -> m.thenAcceptBoth(v42, y, new SubtractAction(m)));
funs.add((y) -> m.thenCombine(y, v42, new SubtractFunction(m)));
+ funs.add((y) -> m.thenCombine(v42, y, new SubtractFunction(m)));
funs.add((y) -> m.whenComplete(y, (Integer r, Throwable t) -> {}));
funs.add((y) -> m.thenCompose(y, new CompletableFutureInc(m)));
- funs.add((y) -> CompletableFuture.allOf(new CompletableFuture<?>[] {y, v42}));
- funs.add((y) -> CompletableFuture.anyOf(new CompletableFuture<?>[] {y, incomplete}));
+ funs.add((y) -> CompletableFuture.allOf(y));
+ funs.add((y) -> CompletableFuture.allOf(y, v42));
+ funs.add((y) -> CompletableFuture.allOf(v42, y));
+ funs.add((y) -> CompletableFuture.anyOf(y));
+ funs.add((y) -> CompletableFuture.anyOf(y, incomplete));
+ funs.add((y) -> CompletableFuture.anyOf(incomplete, y));
for (Function<CompletableFuture<Integer>, CompletableFuture<?>>
fun : funs) {
CompletableFuture<Integer> f = new CompletableFuture<>();
f.completeExceptionally(ex);
- CompletableFuture<Integer> src = m.thenApply(f, new IncFunction(m));
+ CompletableFuture<Integer> src = m.thenApply(f, incFunction);
checkCompletedWithWrappedException(src, ex);
CompletableFuture<?> dep = fun.apply(src);
checkCompletedWithWrappedException(dep, ex);
@@ -3711,7 +3926,7 @@
for (Function<CompletableFuture<Integer>, CompletableFuture<?>>
fun : funs) {
CompletableFuture<Integer> f = new CompletableFuture<>();
- CompletableFuture<Integer> src = m.thenApply(f, new IncFunction(m));
+ CompletableFuture<Integer> src = m.thenApply(f, incFunction);
CompletableFuture<?> dep = fun.apply(src);
f.completeExceptionally(ex);
checkCompletedWithWrappedException(src, ex);
@@ -3725,7 +3940,7 @@
CompletableFuture<Integer> f = new CompletableFuture<>();
f.cancel(mayInterruptIfRunning);
checkCancelled(f);
- CompletableFuture<Integer> src = m.thenApply(f, new IncFunction(m));
+ CompletableFuture<Integer> src = m.thenApply(f, incFunction);
checkCompletedWithWrappedCancellationException(src);
CompletableFuture<?> dep = fun.apply(src);
checkCompletedWithWrappedCancellationException(dep);
@@ -3736,7 +3951,7 @@
for (Function<CompletableFuture<Integer>, CompletableFuture<?>>
fun : funs) {
CompletableFuture<Integer> f = new CompletableFuture<>();
- CompletableFuture<Integer> src = m.thenApply(f, new IncFunction(m));
+ CompletableFuture<Integer> src = m.thenApply(f, incFunction);
CompletableFuture<?> dep = fun.apply(src);
f.cancel(mayInterruptIfRunning);
checkCancelled(f);
@@ -3747,7 +3962,7 @@
}}
/**
- * Minimal completion stages throw UOE for all non-CompletionStage methods
+ * Minimal completion stages throw UOE for most non-CompletionStage methods
*/
public void testMinimalCompletionStage_minimality() {
if (!testImplementationDetails) return;
@@ -3776,8 +3991,10 @@
.filter((method) -> !permittedMethodSignatures.contains(toSignature.apply(method)))
.collect(Collectors.toList());
- CompletionStage<Integer> minimalStage =
- new CompletableFuture<Integer>().minimalCompletionStage();
+ List<CompletionStage<Integer>> stages = new ArrayList<>();
+ stages.add(new CompletableFuture<Integer>().minimalCompletionStage());
+ stages.add(CompletableFuture.completedStage(1));
+ stages.add(CompletableFuture.failedStage(new CFException()));
List<Method> bugs = new ArrayList<>();
for (Method method : allMethods) {
@@ -3793,20 +4010,22 @@
else if (parameterTypes[i] == long.class)
args[i] = 0L;
}
- try {
- method.invoke(minimalStage, args);
- bugs.add(method);
+ for (CompletionStage<Integer> stage : stages) {
+ try {
+ method.invoke(stage, args);
+ bugs.add(method);
+ }
+ catch (java.lang.reflect.InvocationTargetException expected) {
+ if (! (expected.getCause() instanceof UnsupportedOperationException)) {
+ bugs.add(method);
+ // expected.getCause().printStackTrace();
+ }
+ }
+ catch (ReflectiveOperationException bad) { throw new Error(bad); }
}
- catch (java.lang.reflect.InvocationTargetException expected) {
- if (! (expected.getCause() instanceof UnsupportedOperationException)) {
- bugs.add(method);
- // expected.getCause().printStackTrace();
- }
- }
- catch (ReflectiveOperationException bad) { throw new Error(bad); }
}
if (!bugs.isEmpty())
- throw new Error("Methods did not throw UOE: " + bugs.toString());
+ throw new Error("Methods did not throw UOE: " + bugs);
}
static class Monad {
@@ -3955,12 +4174,33 @@
Monad.plus(godot, Monad.unit(5L)));
}
+ /** Test long recursive chains of CompletableFutures with cascading completions */
+ public void testRecursiveChains() throws Throwable {
+ for (ExecutionMode m : ExecutionMode.values())
+ for (boolean addDeadEnds : new boolean[] { true, false })
+ {
+ final int val = 42;
+ final int n = expensiveTests ? 1_000 : 2;
+ CompletableFuture<Integer> head = new CompletableFuture<>();
+ CompletableFuture<Integer> 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<Void> head = new CompletableFuture<>();
final CompletableFuture<Void> complete = CompletableFuture.completedFuture((Void)null);
final AtomicInteger count = new AtomicInteger(0);
@@ -3987,6 +4227,97 @@
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<Integer> incomplete = new CompletableFuture<>();
+ CompletableFuture<Integer> 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);
+ }
+ }
+
+ /**
+ * Reproduction recipe for:
+ * 8160402: Garbage retention with CompletableFuture.anyOf
+ * cvs update -D '2016-05-01' ./src/main/java/util/concurrent/CompletableFuture.java && ant -Dvmoptions=-Xmx8m -Djsr166.expensiveTests=true -Djsr166.tckTestClass=CompletableFutureTest -Djsr166.methodFilter=testAnyOfGarbageRetention tck; cvs update -A
+ */
+ public void testAnyOfGarbageRetention() throws Throwable {
+ for (Integer v : new Integer[] { 1, null })
+ {
+ final int n = expensiveTests ? 100_000 : 10;
+ CompletableFuture<Integer>[] fs
+ = (CompletableFuture<Integer>[]) 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.
+ *
+ * As of 2016-07, fails with OOME:
+ * ant -Dvmoptions=-Xmx8m -Djsr166.expensiveTests=true -Djsr166.tckTestClass=CompletableFutureTest -Djsr166.methodFilter=testCancelledAllOfGarbageRetention tck
+ */
+ public void testCancelledAllOfGarbageRetention() throws Throwable {
+ final int n = expensiveTests ? 100_000 : 10;
+ CompletableFuture<Integer>[] fs
+ = (CompletableFuture<Integer>[]) 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));
+ }
+
+ /**
+ * Checks for garbage retention when a dependent future is
+ * cancelled and garbage-collected.
+ * 8161600: Garbage retention when source CompletableFutures are never completed
+ *
+ * As of 2016-07, fails with OOME:
+ * ant -Dvmoptions=-Xmx8m -Djsr166.expensiveTests=true -Djsr166.tckTestClass=CompletableFutureTest -Djsr166.methodFilter=testCancelledGarbageRetention tck
+ */
+ public void testCancelledGarbageRetention() throws Throwable {
+ final int n = expensiveTests ? 100_000 : 10;
+ CompletableFuture<Integer> neverCompleted = new CompletableFuture<>();
+ for (int i = 0; i < n; i++)
+ assertTrue(neverCompleted.thenRun(() -> {}).cancel(true));
+ }
+
// static <U> U join(CompletionStage<U> stage) {
// CompletableFuture<U> f = new CompletableFuture<>();
// stage.whenComplete((v, ex) -> {
--- a/jdk/test/java/util/concurrent/tck/ConcurrentHashMapTest.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/util/concurrent/tck/ConcurrentHashMapTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -43,6 +43,8 @@
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import junit.framework.Test;
import junit.framework.TestSuite;
@@ -830,4 +832,47 @@
}
}
+ /**
+ * Tests performance of removeAll when the other collection is much smaller.
+ * ant -Djsr166.tckTestClass=ConcurrentHashMapTest -Djsr166.methodFilter=testRemoveAll_performance -Djsr166.expensiveTests=true tck
+ */
+ public void testRemoveAll_performance() {
+ final int mapSize = expensiveTests ? 1_000_000 : 100;
+ final int iterations = expensiveTests ? 500 : 2;
+ final ConcurrentHashMap<Integer, Integer> map = new ConcurrentHashMap<>();
+ for (int i = 0; i < mapSize; i++)
+ map.put(i, i);
+ Set<Integer> keySet = map.keySet();
+ Collection<Integer> removeMe = Arrays.asList(new Integer[] { -99, -86 });
+ for (int i = 0; i < iterations; i++)
+ assertFalse(keySet.removeAll(removeMe));
+ assertEquals(mapSize, map.size());
+ }
+
+ /**
+ * Tests performance of computeIfAbsent when the element is present.
+ * See JDK-8161372
+ * ant -Djsr166.tckTestClass=ConcurrentHashMapTest -Djsr166.methodFilter=testcomputeIfAbsent_performance -Djsr166.expensiveTests=true tck
+ */
+ public void testcomputeIfAbsent_performance() {
+ final int mapSize = 20;
+ final int iterations = expensiveTests ? (1 << 23) : mapSize * 2;
+ final int threads = expensiveTests ? 10 : 2;
+ final ConcurrentHashMap<Integer, Integer> map = new ConcurrentHashMap<>();
+ for (int i = 0; i < mapSize; i++)
+ map.put(i, i);
+ final ExecutorService pool = Executors.newFixedThreadPool(2);
+ try (PoolCleaner cleaner = cleaner(pool)) {
+ Runnable r = new CheckedRunnable() {
+ public void realRun() {
+ int result = 0;
+ for (int i = 0; i < iterations; i++)
+ result += map.computeIfAbsent(i % mapSize, (k) -> k + k);
+ if (result == -42) throw new Error();
+ }};
+ for (int i = 0; i < threads; i++)
+ pool.execute(r);
+ }
+ }
+
}
--- a/jdk/test/java/util/concurrent/tck/JSR166TestCase.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/util/concurrent/tck/JSR166TestCase.java Thu Jul 21 20:09:19 2016 -0700
@@ -548,6 +548,13 @@
// Java9+ test classes
if (atLeastJava9()) {
String[] java9TestClassNames = {
+ "AtomicBoolean9Test",
+ "AtomicInteger9Test",
+ "AtomicIntegerArray9Test",
+ "AtomicLong9Test",
+ "AtomicLongArray9Test",
+ "AtomicReference9Test",
+ "AtomicReferenceArray9Test",
"ExecutorCompletionService9Test",
};
addNamedTestClasses(suite, java9TestClassNames);
@@ -975,7 +982,11 @@
}
}
- /** Like Runnable, but with the freedom to throw anything */
+ /**
+ * Like Runnable, but with the freedom to throw anything.
+ * junit folks had the same idea:
+ * http://junit.org/junit5/docs/snapshot/api/org/junit/gen5/api/Executable.html
+ */
interface Action { public void run() throws Throwable; }
/**
@@ -1006,6 +1017,15 @@
* Uninteresting threads are filtered out.
*/
static void dumpTestThreads() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ try {
+ System.setSecurityManager(null);
+ } catch (SecurityException giveUp) {
+ return;
+ }
+ }
+
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
System.err.println("------ stacktrace dump start ------");
for (ThreadInfo info : threadMXBean.dumpAllThreads(true, true)) {
@@ -1023,6 +1043,8 @@
System.err.print(info);
}
System.err.println("------ stacktrace dump end ------");
+
+ if (sm != null) System.setSecurityManager(sm);
}
/**
--- a/jdk/test/java/util/concurrent/tck/StampedLockTest.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/util/concurrent/tck/StampedLockTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -32,11 +32,18 @@
* http://creativecommons.org/publicdomain/zero/1.0/
*/
+import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.StampedLock;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Function;
import junit.framework.Test;
import junit.framework.TestSuite;
@@ -745,28 +752,41 @@
public void testTryConvertToOptimisticRead() throws InterruptedException {
StampedLock lock = new StampedLock();
long s, p;
- s = 0L;
- assertFalse((p = lock.tryConvertToOptimisticRead(s)) != 0L);
+ assertEquals(0L, lock.tryConvertToOptimisticRead(0L));
+
assertTrue((s = lock.tryOptimisticRead()) != 0L);
- assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
+ assertEquals(s, lock.tryConvertToOptimisticRead(s));
+ assertTrue(lock.validate(s));
+
+ assertTrue((p = lock.readLock()) != 0L);
+ assertTrue((s = lock.tryOptimisticRead()) != 0L);
+ assertEquals(s, lock.tryConvertToOptimisticRead(s));
+ assertTrue(lock.validate(s));
+ lock.unlockRead(p);
+
assertTrue((s = lock.writeLock()) != 0L);
assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
assertTrue(lock.validate(p));
+
assertTrue((s = lock.readLock()) != 0L);
assertTrue(lock.validate(s));
assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
assertTrue(lock.validate(p));
+
assertTrue((s = lock.tryWriteLock()) != 0L);
assertTrue(lock.validate(s));
assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
assertTrue(lock.validate(p));
+
assertTrue((s = lock.tryReadLock()) != 0L);
assertTrue(lock.validate(s));
assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
assertTrue(lock.validate(p));
+
assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L);
assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
assertTrue(lock.validate(p));
+
assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L);
assertTrue(lock.validate(s));
assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
@@ -780,39 +800,67 @@
public void testTryConvertToReadLock() throws InterruptedException {
StampedLock lock = new StampedLock();
long s, p;
- s = 0L;
- assertFalse((p = lock.tryConvertToReadLock(s)) != 0L);
+
+ assertFalse((p = lock.tryConvertToReadLock(0L)) != 0L);
+
assertTrue((s = lock.tryOptimisticRead()) != 0L);
assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
+ assertTrue(lock.isReadLocked());
+ assertEquals(1, lock.getReadLockCount());
lock.unlockRead(p);
+
+ assertTrue((s = lock.tryOptimisticRead()) != 0L);
+ lock.readLock();
+ assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
+ assertTrue(lock.isReadLocked());
+ assertEquals(2, lock.getReadLockCount());
+ lock.unlockRead(p);
+ lock.unlockRead(p);
+
assertTrue((s = lock.writeLock()) != 0L);
assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
assertTrue(lock.validate(p));
+ assertTrue(lock.isReadLocked());
+ assertEquals(1, lock.getReadLockCount());
lock.unlockRead(p);
+
assertTrue((s = lock.readLock()) != 0L);
assertTrue(lock.validate(s));
- assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
- assertTrue(lock.validate(p));
- lock.unlockRead(p);
+ assertEquals(s, lock.tryConvertToReadLock(s));
+ assertTrue(lock.validate(s));
+ assertTrue(lock.isReadLocked());
+ assertEquals(1, lock.getReadLockCount());
+ lock.unlockRead(s);
+
assertTrue((s = lock.tryWriteLock()) != 0L);
assertTrue(lock.validate(s));
assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
assertTrue(lock.validate(p));
+ assertEquals(1, lock.getReadLockCount());
lock.unlockRead(p);
+
assertTrue((s = lock.tryReadLock()) != 0L);
assertTrue(lock.validate(s));
- assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
- assertTrue(lock.validate(p));
- lock.unlockRead(p);
+ assertEquals(s, lock.tryConvertToReadLock(s));
+ assertTrue(lock.validate(s));
+ assertTrue(lock.isReadLocked());
+ assertEquals(1, lock.getReadLockCount());
+ lock.unlockRead(s);
+
assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L);
assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
assertTrue(lock.validate(p));
+ assertTrue(lock.isReadLocked());
+ assertEquals(1, lock.getReadLockCount());
lock.unlockRead(p);
+
assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L);
assertTrue(lock.validate(s));
- assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
- assertTrue(lock.validate(p));
- lock.unlockRead(p);
+ assertEquals(s, lock.tryConvertToReadLock(s));
+ assertTrue(lock.validate(s));
+ assertTrue(lock.isReadLocked());
+ assertEquals(1, lock.getReadLockCount());
+ lock.unlockRead(s);
}
/**
@@ -822,38 +870,52 @@
public void testTryConvertToWriteLock() throws InterruptedException {
StampedLock lock = new StampedLock();
long s, p;
- s = 0L;
- assertFalse((p = lock.tryConvertToWriteLock(s)) != 0L);
+
+ assertFalse((p = lock.tryConvertToWriteLock(0L)) != 0L);
+
assertTrue((s = lock.tryOptimisticRead()) != 0L);
assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
+ assertTrue(lock.isWriteLocked());
lock.unlockWrite(p);
+
assertTrue((s = lock.writeLock()) != 0L);
- assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
- assertTrue(lock.validate(p));
- lock.unlockWrite(p);
+ assertEquals(s, lock.tryConvertToWriteLock(s));
+ assertTrue(lock.validate(s));
+ assertTrue(lock.isWriteLocked());
+ lock.unlockWrite(s);
+
assertTrue((s = lock.readLock()) != 0L);
assertTrue(lock.validate(s));
assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
assertTrue(lock.validate(p));
+ assertTrue(lock.isWriteLocked());
lock.unlockWrite(p);
+
assertTrue((s = lock.tryWriteLock()) != 0L);
assertTrue(lock.validate(s));
- assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
- assertTrue(lock.validate(p));
- lock.unlockWrite(p);
+ assertEquals(s, lock.tryConvertToWriteLock(s));
+ assertTrue(lock.validate(s));
+ assertTrue(lock.isWriteLocked());
+ lock.unlockWrite(s);
+
assertTrue((s = lock.tryReadLock()) != 0L);
assertTrue(lock.validate(s));
assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
assertTrue(lock.validate(p));
+ assertTrue(lock.isWriteLocked());
lock.unlockWrite(p);
+
assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L);
assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
assertTrue(lock.validate(p));
+ assertTrue(lock.isWriteLocked());
lock.unlockWrite(p);
+
assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L);
assertTrue(lock.validate(s));
assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
assertTrue(lock.validate(p));
+ assertTrue(lock.isWriteLocked());
lock.unlockWrite(p);
}
@@ -903,4 +965,241 @@
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);
+ }
+
+ static long writeLockInterruptiblyUninterrupted(StampedLock sl) {
+ try { return sl.writeLockInterruptibly(); }
+ catch (InterruptedException ex) { throw new AssertionError(ex); }
+ }
+
+ static long tryWriteLockUninterrupted(StampedLock sl, long time, TimeUnit unit) {
+ try { return sl.tryWriteLock(time, unit); }
+ catch (InterruptedException ex) { throw new AssertionError(ex); }
+ }
+
+ static long readLockInterruptiblyUninterrupted(StampedLock sl) {
+ try { return sl.readLockInterruptibly(); }
+ catch (InterruptedException ex) { throw new AssertionError(ex); }
+ }
+
+ static long tryReadLockUninterrupted(StampedLock sl, long time, TimeUnit unit) {
+ try { return sl.tryReadLock(time, unit); }
+ catch (InterruptedException ex) { throw new AssertionError(ex); }
+ }
+
+ /**
+ * Invalid write stamps result in IllegalMonitorStateException
+ */
+ public void testInvalidWriteStampsThrowIllegalMonitorStateException() {
+ List<Function<StampedLock, Long>> writeLockers = new ArrayList<>();
+ writeLockers.add((sl) -> sl.writeLock());
+ writeLockers.add((sl) -> writeLockInterruptiblyUninterrupted(sl));
+ writeLockers.add((sl) -> tryWriteLockUninterrupted(sl, Long.MIN_VALUE, DAYS));
+ writeLockers.add((sl) -> tryWriteLockUninterrupted(sl, 0, DAYS));
+
+ List<BiConsumer<StampedLock, Long>> writeUnlockers = new ArrayList<>();
+ writeUnlockers.add((sl, stamp) -> sl.unlockWrite(stamp));
+ writeUnlockers.add((sl, stamp) -> assertTrue(sl.tryUnlockWrite()));
+ writeUnlockers.add((sl, stamp) -> sl.asWriteLock().unlock());
+ writeUnlockers.add((sl, stamp) -> sl.unlock(stamp));
+
+ List<Consumer<StampedLock>> mutaters = new ArrayList<>();
+ mutaters.add((sl) -> {});
+ mutaters.add((sl) -> sl.readLock());
+ for (Function<StampedLock, Long> writeLocker : writeLockers)
+ mutaters.add((sl) -> writeLocker.apply(sl));
+
+ for (Function<StampedLock, Long> writeLocker : writeLockers)
+ for (BiConsumer<StampedLock, Long> writeUnlocker : writeUnlockers)
+ for (Consumer<StampedLock> mutater : mutaters) {
+ final StampedLock sl = new StampedLock();
+ final long stamp = writeLocker.apply(sl);
+ assertTrue(stamp != 0L);
+ assertThrows(IllegalMonitorStateException.class,
+ () -> sl.unlockRead(stamp));
+ writeUnlocker.accept(sl, stamp);
+ mutater.accept(sl);
+ assertThrows(IllegalMonitorStateException.class,
+ () -> sl.unlock(stamp),
+ () -> sl.unlockRead(stamp),
+ () -> sl.unlockWrite(stamp));
+ }
+ }
+
+ /**
+ * Invalid read stamps result in IllegalMonitorStateException
+ */
+ public void testInvalidReadStampsThrowIllegalMonitorStateException() {
+ List<Function<StampedLock, Long>> readLockers = new ArrayList<>();
+ readLockers.add((sl) -> sl.readLock());
+ readLockers.add((sl) -> readLockInterruptiblyUninterrupted(sl));
+ readLockers.add((sl) -> tryReadLockUninterrupted(sl, Long.MIN_VALUE, DAYS));
+ readLockers.add((sl) -> tryReadLockUninterrupted(sl, 0, DAYS));
+
+ List<BiConsumer<StampedLock, Long>> readUnlockers = new ArrayList<>();
+ readUnlockers.add((sl, stamp) -> sl.unlockRead(stamp));
+ readUnlockers.add((sl, stamp) -> assertTrue(sl.tryUnlockRead()));
+ readUnlockers.add((sl, stamp) -> sl.asReadLock().unlock());
+ readUnlockers.add((sl, stamp) -> sl.unlock(stamp));
+
+ List<Function<StampedLock, Long>> writeLockers = new ArrayList<>();
+ writeLockers.add((sl) -> sl.writeLock());
+ writeLockers.add((sl) -> writeLockInterruptiblyUninterrupted(sl));
+ writeLockers.add((sl) -> tryWriteLockUninterrupted(sl, Long.MIN_VALUE, DAYS));
+ writeLockers.add((sl) -> tryWriteLockUninterrupted(sl, 0, DAYS));
+
+ List<BiConsumer<StampedLock, Long>> writeUnlockers = new ArrayList<>();
+ writeUnlockers.add((sl, stamp) -> sl.unlockWrite(stamp));
+ writeUnlockers.add((sl, stamp) -> assertTrue(sl.tryUnlockWrite()));
+ writeUnlockers.add((sl, stamp) -> sl.asWriteLock().unlock());
+ writeUnlockers.add((sl, stamp) -> sl.unlock(stamp));
+
+
+ for (Function<StampedLock, Long> readLocker : readLockers)
+ for (BiConsumer<StampedLock, Long> readUnlocker : readUnlockers)
+ for (Function<StampedLock, Long> writeLocker : writeLockers)
+ for (BiConsumer<StampedLock, Long> writeUnlocker : writeUnlockers) {
+ final StampedLock sl = new StampedLock();
+ final long stamp = readLocker.apply(sl);
+ assertTrue(stamp != 0L);
+ assertThrows(IllegalMonitorStateException.class,
+ () -> sl.unlockWrite(stamp));
+ readUnlocker.accept(sl, stamp);
+ assertThrows(IllegalMonitorStateException.class,
+ () -> sl.unlock(stamp),
+ () -> sl.unlockRead(stamp),
+ () -> sl.unlockWrite(stamp));
+ final long writeStamp = writeLocker.apply(sl);
+ assertTrue(writeStamp != 0L);
+ assertTrue(writeStamp != stamp);
+ assertThrows(IllegalMonitorStateException.class,
+ () -> sl.unlock(stamp),
+ () -> sl.unlockRead(stamp),
+ () -> sl.unlockWrite(stamp));
+ writeUnlocker.accept(sl, writeStamp);
+ assertThrows(IllegalMonitorStateException.class,
+ () -> sl.unlock(stamp),
+ () -> sl.unlockRead(stamp),
+ () -> sl.unlockWrite(stamp));
+ }
+ }
+
}
--- a/jdk/test/java/util/jar/JarFile/mrjar/MultiReleaseJarIterators.java Thu Jul 21 16:45:56 2016 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,228 +0,0 @@
-/*
- * 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.
- *
- * 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 8132734 8144062
- * @summary Test the extended API and the aliasing additions in JarFile that
- * support multi-release jar files
- * @library /lib/testlibrary/java/util/jar
- * @build Compiler JarBuilder CreateMultiReleaseTestJars
- * @run testng MultiReleaseJarIterators
- */
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-import java.util.stream.Collectors;
-import java.util.zip.ZipFile;
-
-import org.testng.Assert;
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-
-public class MultiReleaseJarIterators {
-
- static final int MAJOR_VERSION = Runtime.version().major();
-
- String userdir = System.getProperty("user.dir", ".");
- File unversioned = new File(userdir, "unversioned.jar");
- File multirelease = new File(userdir, "multi-release.jar");
- Map<String,JarEntry> uvEntries = new HashMap<>();
- Map<String,JarEntry> mrEntries = new HashMap<>();
- Map<String,JarEntry> baseEntries = new HashMap<>();
- Map<String,JarEntry> v9Entries = new HashMap<>();
- Map<String, JarEntry> v10Entries = new HashMap<>();
-
- @BeforeClass
- public void initialize() throws Exception {
- CreateMultiReleaseTestJars creator = new CreateMultiReleaseTestJars();
- creator.compileEntries();
- creator.buildUnversionedJar();
- creator.buildMultiReleaseJar();
-
- try (JarFile jf = new JarFile(multirelease)) {
- for (Enumeration<JarEntry> e = jf.entries(); e.hasMoreElements(); ) {
- JarEntry je = e.nextElement();
- String name = je.getName();
- mrEntries.put(name, je);
- if (name.startsWith("META-INF/versions/")) {
- if (name.startsWith("META-INF/versions/9/")) {
- v9Entries.put(name.substring(20), je);
- } else if (name.startsWith("META-INF/versions/10/")) {
- v10Entries.put(name.substring(21), je);
- }
- } else {
- baseEntries.put(name, je);
- }
- }
- }
- Assert.assertEquals(mrEntries.size(), 14);
- Assert.assertEquals(baseEntries.size(), 6);
- Assert.assertEquals(v9Entries.size(), 5);
- Assert.assertEquals(v10Entries.size(), 3);
-
- try (JarFile jf = new JarFile(unversioned)) {
- jf.entries().asIterator().forEachRemaining(je -> uvEntries.put(je.getName(), je));
- }
- Assert.assertEquals(uvEntries.size(), 6);
- }
-
- @AfterClass
- public void close() throws IOException {
- Files.delete(unversioned.toPath());
- Files.delete(multirelease.toPath());
- }
-
- @Test
- public void testMultiReleaseJar() throws IOException {
- try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ)) {
- testEnumeration(jf, mrEntries);
- testStream(jf, mrEntries);
- }
-
- try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ, JarFile.baseVersion())) {
- testEnumeration(jf, baseEntries);
- testStream(jf, baseEntries);
- }
-
- try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ, Runtime.Version.parse("9"))) {
- testEnumeration(jf, v9Entries);
- testStream(jf, v9Entries);
- }
-
- try (JarFile jf = new JarFile(multirelease, true, ZipFile.OPEN_READ, Runtime.version())) {
- Map<String,JarEntry> expectedEntries;
- switch (MAJOR_VERSION) {
- case 9:
- expectedEntries = v9Entries;
- break;
- case 10: // won't get here until JDK 10
- expectedEntries = v10Entries;
- break;
- default:
- expectedEntries = baseEntries;
- break;
- }
-
- testEnumeration(jf, expectedEntries);
- testStream(jf, expectedEntries);
- }
- }
-
- @Test
- public void testUnversionedJar() throws IOException {
- try (JarFile jf = new JarFile(unversioned, true, ZipFile.OPEN_READ)) {
- testEnumeration(jf, uvEntries);
- testStream(jf, uvEntries);
- }
-
- try (JarFile jf = new JarFile(unversioned, true, ZipFile.OPEN_READ, JarFile.baseVersion())) {
- testEnumeration(jf, uvEntries);
- testStream(jf, uvEntries);
- }
-
- try (JarFile jf = new JarFile(unversioned, true, ZipFile.OPEN_READ, Runtime.Version.parse("9"))) {
- testEnumeration(jf, uvEntries);
- testStream(jf, uvEntries);
- }
-
- try (JarFile jf = new JarFile(unversioned, true, ZipFile.OPEN_READ, Runtime.version())) {
- testEnumeration(jf, uvEntries);
- testStream(jf, uvEntries);
- }
- }
-
- private void testEnumeration(JarFile jf, Map<String,JarEntry> expectedEntries) {
- Map<String, JarEntry> actualEntries = new HashMap<>();
- for (Enumeration<JarEntry> e = jf.entries(); e.hasMoreElements(); ) {
- JarEntry je = e.nextElement();
- actualEntries.put(je.getName(), je);
- }
-
- testEntries(jf, actualEntries, expectedEntries);
- }
-
-
- private void testStream(JarFile jf, Map<String,JarEntry> expectedEntries) {
- Map<String,JarEntry> actualEntries = jf.stream().collect(Collectors.toMap(je -> je.getName(), je -> je));
-
- testEntries(jf, actualEntries, expectedEntries);
- }
-
- private void testEntries(JarFile jf, Map<String,JarEntry> actualEntries, Map<String,JarEntry> expectedEntries) {
- /* For multi-release jar files constructed with a Release object,
- * actualEntries contain versionedEntries that are considered part of the
- * public API. They have a 1-1 correspondence with baseEntries,
- * so entries that are not part of the public API won't be present,
- * i.e. those entries with a name that starts with version/PackagePrivate
- * in this particular jar file (multi-release.jar)
- */
-
- Map<String,JarEntry> entries;
- if (expectedEntries == mrEntries) {
- Assert.assertEquals(actualEntries.size(), mrEntries.size());
- entries = mrEntries;
- } else if (expectedEntries == uvEntries) {
- Assert.assertEquals(actualEntries.size(), uvEntries.size());
- entries = uvEntries;
- } else {
- Assert.assertEquals(actualEntries.size(), baseEntries.size()); // this is correct
- entries = baseEntries;
- }
-
- entries.keySet().forEach(name -> {
- JarEntry ee = expectedEntries.get(name);
- if (ee == null) ee = entries.get(name);
- JarEntry ae = actualEntries.get(name);
- try {
- compare(jf, ae, ee);
- } catch (IOException x) {
- throw new RuntimeException(x);
- }
- });
- }
-
- private void compare(JarFile jf, JarEntry actual, JarEntry expected) throws IOException {
- byte[] abytes;
- byte[] ebytes;
-
- try (InputStream is = jf.getInputStream(actual)) {
- abytes = is.readAllBytes();
- }
-
- try (InputStream is = jf.getInputStream(expected)) {
- ebytes = is.readAllBytes();
- }
-
- Assert.assertEquals(abytes, ebytes);
- }
-}
--- a/jdk/test/java/util/zip/TestExtraTime.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/util/zip/TestExtraTime.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2015, 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
@@ -23,7 +23,7 @@
/**
* @test
- * @bug 4759491 6303183 7012868 8015666 8023713 8068790 8076641 8075526 8130914
+ * @bug 4759491 6303183 7012868 8015666 8023713 8068790 8076641 8075526 8130914 8161942
* @summary Test ZOS and ZIS timestamp in extra field correctly
*/
@@ -32,6 +32,7 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileTime;
+import java.time.Instant;
import java.util.Arrays;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
@@ -40,37 +41,52 @@
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
+
+
public class TestExtraTime {
public static void main(String[] args) throws Throwable{
File src = new File(System.getProperty("test.src", "."), "TestExtraTime.java");
- if (src.exists()) {
+ if (!src.exists()) {
+ return;
+ }
+
+ TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");
+
+ for (byte[] extra : new byte[][] { null, new byte[] {1, 2, 3}}) {
+
+ // ms-dos 1980 epoch problem
+ test0(FileTime.from(10, TimeUnit.MILLISECONDS), null, null, null, extra);
+ // negative epoch time
+ test0(FileTime.from(-100, TimeUnit.DAYS), null, null, null, extra);
+
long time = src.lastModified();
- FileTime mtime = FileTime.from(time, TimeUnit.MILLISECONDS);
- FileTime atime = FileTime.from(time + 300000, TimeUnit.MILLISECONDS);
- FileTime ctime = FileTime.from(time - 300000, TimeUnit.MILLISECONDS);
- TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");
-
- for (byte[] extra : new byte[][] { null, new byte[] {1, 2, 3}}) {
- test(mtime, null, null, null, extra);
+ test(FileTime.from(time, TimeUnit.MILLISECONDS),
+ FileTime.from(time + 300000, TimeUnit.MILLISECONDS),
+ FileTime.from(time - 300000, TimeUnit.MILLISECONDS),
+ tz, extra);
- // ms-dos 1980 epoch problem
- test(FileTime.from(10, TimeUnit.MILLISECONDS), null, null, null, extra);
- // negative epoch time
- test(FileTime.from(-100, TimeUnit.DAYS), null, null, null, extra);
-
- // non-default tz
- test(mtime, null, null, tz, extra);
+ // now
+ time = Instant.now().toEpochMilli();
+ test(FileTime.from(time, TimeUnit.MILLISECONDS),
+ FileTime.from(time + 300000, TimeUnit.MILLISECONDS),
+ FileTime.from(time - 300000, TimeUnit.MILLISECONDS),
+ tz, extra);
- test(mtime, atime, null, null, extra);
- test(mtime, null, ctime, null, extra);
- test(mtime, atime, ctime, null, extra);
+ // unix 2038
+ time = 0x80000000L;
+ test(FileTime.from(time, TimeUnit.SECONDS),
+ FileTime.from(time, TimeUnit.SECONDS),
+ FileTime.from(time, TimeUnit.SECONDS),
+ tz, extra);
- test(mtime, atime, null, tz, extra);
- test(mtime, null, ctime, tz, extra);
- test(mtime, atime, ctime, tz, extra);
- }
+ // mtime < unix 2038
+ time = 0x7fffffffL;
+ test(FileTime.from(time, TimeUnit.SECONDS),
+ FileTime.from(time + 30000, TimeUnit.SECONDS),
+ FileTime.from(time + 30000, TimeUnit.SECONDS),
+ tz, extra);
}
testNullHandling();
@@ -80,6 +96,18 @@
static void test(FileTime mtime, FileTime atime, FileTime ctime,
TimeZone tz, byte[] extra) throws Throwable {
+ test0(mtime, null, null, null, extra);
+ test0(mtime, null, null, tz, extra); // non-default tz
+ test0(mtime, atime, null, null, extra);
+ test0(mtime, null, ctime, null, extra);
+ test0(mtime, atime, ctime, null, extra);
+ test0(mtime, atime, null, tz, extra);
+ test0(mtime, null, ctime, tz, extra);
+ test0(mtime, atime, ctime, tz, extra);
+ }
+
+ static void test0(FileTime mtime, FileTime atime, FileTime ctime,
+ TimeZone tz, byte[] extra) throws Throwable {
System.out.printf("--------------------%nTesting: [%s]/[%s]/[%s]%n",
mtime, atime, ctime);
TimeZone tz0 = TimeZone.getDefault();
@@ -120,13 +148,14 @@
Path zpath = Paths.get(System.getProperty("test.dir", "."),
"TestExtraTime.zip");
Files.copy(new ByteArrayInputStream(baos.toByteArray()), zpath);
- ZipFile zf = new ZipFile(zpath.toFile());
- ze = zf.getEntry("TestExtraTime.java");
- // ZipFile read entry from cen, which does not have a/ctime,
- // for now.
- check(mtime, null, null, ze, extra);
- zf.close();
- Files.delete(zpath);
+ try (ZipFile zf = new ZipFile(zpath.toFile())) {
+ ze = zf.getEntry("TestExtraTime.java");
+ // ZipFile read entry from cen, which does not have a/ctime,
+ // for now.
+ check(mtime, null, null, ze, extra);
+ } finally {
+ Files.delete(zpath);
+ }
}
static void check(FileTime mtime, FileTime atime, FileTime ctime,
--- a/jdk/test/java/util/zip/TestLocalTime.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/java/util/zip/TestLocalTime.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 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
@@ -23,7 +23,7 @@
/*
* @test
- * @bug 8075526 8135108
+ * @bug 8075526 8135108 8155616 8161942
* @summary Test timestamp via ZipEntry.get/setTimeLocal()
*/
@@ -39,14 +39,13 @@
public static void main(String[] args) throws Throwable{
try {
LocalDateTime ldt = LocalDateTime.now();
- test(getBytes(ldt), ldt); // now
-
- ldt = ldt.withYear(1968); test(getBytes(ldt), ldt);
- ldt = ldt.withYear(1970); test(getBytes(ldt), ldt);
- ldt = ldt.withYear(1982); test(getBytes(ldt), ldt);
- ldt = ldt.withYear(2037); test(getBytes(ldt), ldt);
- ldt = ldt.withYear(2100); test(getBytes(ldt), ldt);
- ldt = ldt.withYear(2106); test(getBytes(ldt), ldt);
+ test(ldt); // now
+ test(ldt.withYear(1968));
+ test(ldt.withYear(1970));
+ test(ldt.withYear(1982));
+ test(ldt.withYear(2037));
+ test(ldt.withYear(2100));
+ test(ldt.withYear(2106));
TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");
// dos time does not support < 1980, have to use
@@ -57,10 +56,17 @@
testWithTZ(tz, ldt.withYear(2106));
// for #8135108
- ldt = LocalDateTime.of(2100, 12, 06, 12, 34, 34, 973);
- test(getBytes(ldt), ldt);
- ldt = LocalDateTime.of(2106, 12, 06, 12, 34, 34, 973);
- test(getBytes(ldt), ldt);
+ test(LocalDateTime.of(2100, 12, 06, 12, 34, 34, 973));
+ test(LocalDateTime.of(2106, 12, 06, 12, 34, 34, 973));
+
+ // for #8155616
+ test(LocalDateTime.of(2016, 03, 13, 2, 50, 00)); // gap
+ test(LocalDateTime.of(2015, 11, 1, 1, 30, 00)); // overlap
+ test(LocalDateTime.of(1968, 04, 28, 2, 51, 25));
+ test(LocalDateTime.of(1970, 04, 26, 2, 31, 52));
+
+ // for #8161942
+ test(LocalDateTime.of(2200, 04, 26, 2, 31, 52)); // unix 2038
} finally {
TimeZone.setDefault(tz0);
@@ -73,7 +79,6 @@
ZipEntry ze = new ZipEntry("TestLocalTime.java");
ze.setTimeLocal(mtime);
check(ze, mtime);
-
zos.putNextEntry(ze);
zos.write(new byte[] { 1, 2, 3, 4});
zos.close();
@@ -87,6 +92,10 @@
test(zbytes, ldt);
}
+ static void test(LocalDateTime ldt) throws Throwable {
+ test(getBytes(ldt), ldt);
+ }
+
static void test(byte[] zbytes, LocalDateTime expected) throws Throwable {
System.out.printf("--------------------%nTesting: [%s]%n", expected);
// ZipInputStream
@@ -113,6 +122,23 @@
LocalDateTime ldt = ze.getTimeLocal();
if (ldt.atOffset(ZoneOffset.UTC).toEpochSecond() >> 1
!= expected.atOffset(ZoneOffset.UTC).toEpochSecond() >> 1) {
+ // if the LDT is out of the range of the standard ms-dos date-time
+ // format ( < 1980 ) AND the date-time is within the daylight saving
+ // time gap (which means the LDT is actually "invalid"), the LDT will
+ // be adjusted accordingly when ZipEntry.setTimeLocal() converts the
+ // date-time via ldt -> zdt -> Instant -> FileTime.
+ // See ZonedDateTime.of(LocalDateTime, ZoneId) for more details.
+ if (ldt.getYear() < 1980 || ldt.getYear() > (1980 + 0x7f)) {
+ System.out.println(" Non-MSDOS ldt : " + ldt);
+ System.out.println(" expected : " + expected);
+ // try to adjust the "expected", assume daylight saving gap
+ expected = ZonedDateTime.of(expected, ZoneId.systemDefault())
+ .toLocalDateTime();
+ if (ldt.atOffset(ZoneOffset.UTC).toEpochSecond() >> 1
+ == expected.atOffset(ZoneOffset.UTC).toEpochSecond() >> 1) {
+ return;
+ }
+ }
throw new RuntimeException("Timestamp: storing mtime failed!");
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/net/ssl/templates/SSLSocketSample.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,404 @@
+/*
+ * 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.
+ */
+
+// Please run in othervm mode. SunJSSE does not support dynamic system
+// properties, no way to re-use system properties in samevm/agentvm mode.
+
+/*
+ * @test
+ * @bug 8161106
+ * @summary Improve SSLSocket test template
+ * @run main/othervm SSLSocketSample
+ */
+
+import java.io.*;
+import javax.net.ssl.*;
+import java.net.*;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Template to help speed your client/server tests.
+ */
+public final class SSLSocketSample {
+
+ /*
+ * =============================================================
+ * Set the various variables needed for the tests, then
+ * specify what tests to run on each side.
+ */
+
+ /*
+ * Should we run the client or server in a separate thread?
+ * Both sides can throw exceptions, but do you have a preference
+ * as to which side should be the main thread.
+ */
+ private static final boolean separateServerThread = false;
+
+ /*
+ * Where do we find the keystores?
+ */
+ private static final String pathToStores = "../etc";
+ private static final String keyStoreFile = "keystore";
+ private static final String trustStoreFile = "truststore";
+ private static final String passwd = "passphrase";
+
+ /*
+ * Turn on SSL debugging?
+ */
+ private static final boolean debug = false;
+
+ /*
+ * Is the server ready to serve?
+ */
+ private static final CountDownLatch serverCondition = new CountDownLatch(1);
+
+ /*
+ * Is the client ready to handshake?
+ */
+ private static final CountDownLatch clientCondition = new CountDownLatch(1);
+
+ /*
+ * What's the server port? Use any free port by default
+ */
+ private volatile int serverPort = 0;
+
+ /*
+ * If the client or server is doing some kind of object creation
+ * that the other side depends on, and that thread prematurely
+ * exits, you may experience a hang. The test harness will
+ * terminate all hung threads after its timeout has expired,
+ * currently 3 minutes by default, but you might try to be
+ * smart about it....
+ */
+
+ /*
+ * Define the server side of the test.
+ */
+ void doServerSide() throws Exception {
+ SSLServerSocket sslServerSocket;
+
+ // kick start the server side service
+ SSLServerSocketFactory sslssf =
+ (SSLServerSocketFactory)SSLServerSocketFactory.getDefault();
+ sslServerSocket =
+ (SSLServerSocket)sslssf.createServerSocket(serverPort);
+
+ serverPort = sslServerSocket.getLocalPort();
+
+ // Signal the client, the server is ready to accept connection.
+ serverCondition.countDown();
+
+
+ // Try to accept a connection in 30 seconds.
+ SSLSocket sslSocket;
+ try {
+ sslServerSocket.setSoTimeout(30000);
+ sslSocket = (SSLSocket)sslServerSocket.accept();
+ } catch (SocketTimeoutException ste) {
+ sslServerSocket.close();
+
+ // Ignore the test case if no connection within 30 seconds.
+ System.out.println(
+ "No incoming client connection in 30 seconds. " +
+ "Ignore in server side.");
+ return;
+ }
+
+ // handle the connection
+ try {
+ // Is it the expected client connection?
+ //
+ // Naughty test cases or third party routines may try to
+ // connection to this server port unintentionally. In
+ // order to mitigate the impact of unexpected client
+ // connections and avoid intermittent failure, it should
+ // be checked that the accepted connection is really linked
+ // to the expected client.
+ boolean clientIsReady =
+ clientCondition.await(30L, TimeUnit.SECONDS);
+
+ if (clientIsReady) {
+ // Run the application in server side.
+ runServerApplication(sslSocket);
+ } else { // Otherwise, ignore
+ // We don't actually care about plain socket connections
+ // for TLS communication testing generally. Just ignore
+ // the test if the accepted connection is not linked to
+ // the expected client or the client connection timeout
+ // in 30 seconds.
+ System.out.println(
+ "The client is not the expected one or timeout. " +
+ "Ignore in server side.");
+ }
+ } finally {
+ sslSocket.close();
+ sslServerSocket.close();
+ }
+ }
+
+ /*
+ * Define the server side application of the test for the specified socket.
+ */
+ void runServerApplication(SSLSocket socket) throws Exception {
+ // here comes the test logic
+ InputStream sslIS = socket.getInputStream();
+ OutputStream sslOS = socket.getOutputStream();
+
+ sslIS.read();
+ sslOS.write(85);
+ sslOS.flush();
+ }
+
+ /*
+ * Define the client side of the test.
+ */
+ void doClientSide() throws Exception {
+
+ // Wait for server to get started.
+ //
+ // The server side takes care of the issue if the server cannot
+ // get started in 90 seconds. The client side would just ignore
+ // the test case if the serer is not ready.
+ boolean serverIsReady =
+ serverCondition.await(90L, TimeUnit.SECONDS);
+ if (!serverIsReady) {
+ System.out.println(
+ "The server is not ready yet in 90 seconds. " +
+ "Ignore in client side.");
+ return;
+ }
+
+ SSLSocketFactory sslsf =
+ (SSLSocketFactory)SSLSocketFactory.getDefault();
+ try (SSLSocket sslSocket = (SSLSocket)sslsf.createSocket()) {
+ try {
+ sslSocket.connect(
+ new InetSocketAddress("localhost", serverPort), 15000);
+ } catch (IOException ioe) {
+ // The server side may be impacted by naughty test cases or
+ // third party routines, and cannot accept connections.
+ //
+ // Just ignore the test if the connection cannot be
+ // established.
+ System.out.println(
+ "Cannot make a connection in 15 seconds. " +
+ "Ignore in client side.");
+ return;
+ }
+
+ // OK, here the client and server get connected.
+
+ // Signal the server, the client is ready to communicate.
+ clientCondition.countDown();
+
+ // There is still a chance in theory that the server thread may
+ // wait client-ready timeout and then quit. The chance should
+ // be really rare so we don't consider it until it becomes a
+ // real problem.
+
+ // Run the application in client side.
+ runClientApplication(sslSocket);
+ }
+ }
+
+ /*
+ * Define the server side application of the test for the specified socket.
+ */
+ void runClientApplication(SSLSocket socket) throws Exception {
+ InputStream sslIS = socket.getInputStream();
+ OutputStream sslOS = socket.getOutputStream();
+
+ sslOS.write(280);
+ sslOS.flush();
+ sslIS.read();
+ }
+
+ /*
+ * =============================================================
+ * The remainder is just support stuff
+ */
+
+ private volatile Exception serverException = null;
+ private volatile Exception clientException = null;
+
+ public static void main(String[] args) throws Exception {
+ String keyFilename =
+ System.getProperty("test.src", ".") + "/" + pathToStores +
+ "/" + keyStoreFile;
+ String trustFilename =
+ System.getProperty("test.src", ".") + "/" + pathToStores +
+ "/" + trustStoreFile;
+
+ System.setProperty("javax.net.ssl.keyStore", keyFilename);
+ System.setProperty("javax.net.ssl.keyStorePassword", passwd);
+ System.setProperty("javax.net.ssl.trustStore", trustFilename);
+ System.setProperty("javax.net.ssl.trustStorePassword", passwd);
+
+ if (debug) {
+ System.setProperty("javax.net.debug", "all");
+ }
+
+ /*
+ * Start the tests.
+ */
+ new SSLSocketSample();
+ }
+
+ private Thread clientThread = null;
+ private Thread serverThread = null;
+
+ /*
+ * Primary constructor, used to drive remainder of the test.
+ *
+ * Fork off the other side, then do your work.
+ */
+ SSLSocketSample() throws Exception {
+ Exception startException = null;
+ try {
+ if (separateServerThread) {
+ startServer(true);
+ startClient(false);
+ } else {
+ startClient(true);
+ startServer(false);
+ }
+ } catch (Exception e) {
+ startException = e;
+ }
+
+ /*
+ * Wait for other side to close down.
+ */
+ if (separateServerThread) {
+ if (serverThread != null) {
+ serverThread.join();
+ }
+ } else {
+ if (clientThread != null) {
+ clientThread.join();
+ }
+ }
+
+ /*
+ * When we get here, the test is pretty much over.
+ * Which side threw the error?
+ */
+ Exception local;
+ Exception remote;
+
+ if (separateServerThread) {
+ remote = serverException;
+ local = clientException;
+ } else {
+ remote = clientException;
+ local = serverException;
+ }
+
+ Exception exception = null;
+
+ /*
+ * Check various exception conditions.
+ */
+ if ((local != null) && (remote != null)) {
+ // If both failed, return the curthread's exception.
+ local.initCause(remote);
+ exception = local;
+ } else if (local != null) {
+ exception = local;
+ } else if (remote != null) {
+ exception = remote;
+ } else if (startException != null) {
+ exception = startException;
+ }
+
+ /*
+ * If there was an exception *AND* a startException,
+ * output it.
+ */
+ if (exception != null) {
+ if (exception != startException && startException != null) {
+ exception.addSuppressed(startException);
+ }
+ throw exception;
+ }
+
+ // Fall-through: no exception to throw!
+ }
+
+ void startServer(boolean newThread) throws Exception {
+ if (newThread) {
+ serverThread = new Thread() {
+ @Override
+ public void run() {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ /*
+ * Our server thread just died.
+ *
+ * Release the client, if not active already...
+ */
+ System.out.println("Server died: " + e);
+ serverException = e;
+ }
+ }
+ };
+ serverThread.start();
+ } else {
+ try {
+ doServerSide();
+ } catch (Exception e) {
+ System.out.println("Server failed: " + e);
+ serverException = e;
+ }
+ }
+ }
+
+ void startClient(boolean newThread) throws Exception {
+ if (newThread) {
+ clientThread = new Thread() {
+ @Override
+ public void run() {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ /*
+ * Our client thread just died.
+ */
+ System.out.println("Client died: " + e);
+ clientException = e;
+ }
+ }
+ };
+ clientThread.start();
+ } else {
+ try {
+ doClientSide();
+ } catch (Exception e) {
+ System.out.println("Client failed: " + e);
+ clientException = e;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/security/auth/login/modules/JaasClientWithDefaultHandler.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,74 @@
+/*
+ * 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 login;
+
+import java.security.Principal;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import com.sun.security.auth.UnixPrincipal;
+
+public class JaasClientWithDefaultHandler {
+
+ private static final String USER_NAME = "testUser";
+ private static final String LOGIN_CONTEXT = "ModularLoginConf";
+ private static final String CBH_PROP = "auth.login.defaultCallbackHandler";
+
+ public static void main(String[] args) {
+ try {
+ java.security.Security.setProperty(CBH_PROP, args[0]);
+ LoginContext lc = new LoginContext(LOGIN_CONTEXT);
+ lc.login();
+ checkPrincipal(lc, true);
+ lc.logout();
+ checkPrincipal(lc, false);
+ } catch (LoginException le) {
+ throw new RuntimeException(le);
+ }
+ System.out.println("Test passed.");
+
+ }
+
+ /*
+ * Verify principal for the test user.
+ */
+ private static void checkPrincipal(LoginContext loginContext,
+ boolean principalShouldExist) {
+ if (!principalShouldExist) {
+ if (loginContext.getSubject().getPrincipals().size() != 0) {
+ throw new RuntimeException("Test failed. Principal was not "
+ + "cleared.");
+ }
+ return;
+ }
+ for (Principal p : loginContext.getSubject().getPrincipals()) {
+ if (p instanceof UnixPrincipal
+ && USER_NAME.equals(p.getName())) {
+ //Proper principal was found, return.
+ return;
+ }
+ }
+ throw new RuntimeException("Test failed. UnixPrincipal "
+ + USER_NAME + " expected.");
+ }
+
+}
--- a/jdk/test/javax/security/auth/login/modules/JaasModularClientTest.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/javax/security/auth/login/modules/JaasModularClientTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -59,8 +59,7 @@
private static final Path C_SRC = SRC.resolve("JaasClient.java");
private static final String C_PKG = "client";
private static final String C_JAR_NAME = C_PKG + JAR_EXTN;
- private static final String MC_DEPENDS_ON_AUTO_SERVICE_JAR_NAME = MODULAR
- + C_PKG + AUTO + JAR_EXTN;
+ private static final String MCN_JAR_NAME = MODULAR + C_PKG + "N" + JAR_EXTN;
private static final String MC_JAR_NAME = MODULAR + C_PKG + JAR_EXTN;
private static final Path BUILD_DIR = Paths.get(".").resolve("build");
@@ -68,7 +67,7 @@
private static final Path S_BUILD_DIR = COMPILE_DIR.resolve(S_PKG);
private static final Path S_WITH_META_DESCR_BUILD_DIR = COMPILE_DIR.resolve(
S_PKG + DESCRIPTOR);
- private static final Path C_BUILD_DIR = COMPILE_DIR.resolve(C_PKG);
+ private static final Path C_BLD_DIR = COMPILE_DIR.resolve(C_PKG);
private static final Path M_BASE_PATH = BUILD_DIR.resolve("mbase");
private static final Path ARTIFACTS_DIR = BUILD_DIR.resolve("artifacts");
@@ -83,8 +82,7 @@
private static final Path C_ARTIFACTS_DIR = ARTIFACTS_DIR.resolve(C_PKG);
private static final Path C_JAR = C_ARTIFACTS_DIR.resolve(C_JAR_NAME);
private static final Path MC_JAR = C_ARTIFACTS_DIR.resolve(MC_JAR_NAME);
- private static final Path MC_DEPENDS_ON_AUTO_SERVICE_JAR = C_ARTIFACTS_DIR
- .resolve(MC_DEPENDS_ON_AUTO_SERVICE_JAR_NAME);
+ private static final Path MCN_JAR = C_ARTIFACTS_DIR.resolve(MCN_JAR_NAME);
private static final String MAIN = C_PKG + ".JaasClient";
private static final String S_INTERFACE
@@ -99,10 +97,7 @@
private static final boolean WITH_S_DESCR = true;
private static final boolean WITHOUT_S_DESCR = false;
- private static final String LOGIN_MODULE_NOT_FOUND_MSG
- = "No LoginModule found";
private static final String NO_FAILURE = null;
- private static final Map<String, String> VM_ARGS = new LinkedHashMap<>();
/**
* Generates Test specific input parameters.
@@ -112,10 +107,10 @@
List<List<Object>> params = new ArrayList<>();
String[] args = new String[]{};
- //PARAMETER ORDERS -
- //client Module Type, Service Module Type,
- //Service META Descriptor Required,
- //Expected Failure message, mechanism used to find the provider
+ // PARAMETER ORDERS -
+ // Client Module Type, Service Module Type,
+ // If Service META descriptor Required,
+ // Expected Failure message, Client arguments
params.add(Arrays.asList(MODULE_TYPE.EXPLICIT, MODULE_TYPE.EXPLICIT,
WITH_S_DESCR, NO_FAILURE, args));
params.add(Arrays.asList(MODULE_TYPE.EXPLICIT, MODULE_TYPE.EXPLICIT,
@@ -123,7 +118,7 @@
params.add(Arrays.asList(MODULE_TYPE.EXPLICIT, MODULE_TYPE.AUTO,
WITH_S_DESCR, NO_FAILURE, args));
params.add(Arrays.asList(MODULE_TYPE.EXPLICIT, MODULE_TYPE.AUTO,
- WITHOUT_S_DESCR, LOGIN_MODULE_NOT_FOUND_MSG, args));
+ WITHOUT_S_DESCR, NO_FAILURE, args));
params.add(Arrays.asList(MODULE_TYPE.EXPLICIT, MODULE_TYPE.UNNAMED,
WITH_S_DESCR, NO_FAILURE, args));
params.add(Arrays.asList(MODULE_TYPE.EXPLICIT, MODULE_TYPE.UNNAMED,
@@ -136,7 +131,7 @@
params.add(Arrays.asList(MODULE_TYPE.AUTO, MODULE_TYPE.AUTO,
WITH_S_DESCR, NO_FAILURE, args));
params.add(Arrays.asList(MODULE_TYPE.AUTO, MODULE_TYPE.AUTO,
- WITHOUT_S_DESCR, LOGIN_MODULE_NOT_FOUND_MSG, args));
+ WITHOUT_S_DESCR, NO_FAILURE, args));
params.add(Arrays.asList(MODULE_TYPE.AUTO, MODULE_TYPE.UNNAMED,
WITH_S_DESCR, NO_FAILURE, args));
params.add(Arrays.asList(MODULE_TYPE.AUTO, MODULE_TYPE.UNNAMED,
@@ -149,7 +144,7 @@
params.add(Arrays.asList(MODULE_TYPE.UNNAMED, MODULE_TYPE.AUTO,
WITH_S_DESCR, NO_FAILURE, args));
params.add(Arrays.asList(MODULE_TYPE.UNNAMED, MODULE_TYPE.AUTO,
- WITHOUT_S_DESCR, LOGIN_MODULE_NOT_FOUND_MSG, args));
+ WITHOUT_S_DESCR, NO_FAILURE, args));
params.add(Arrays.asList(MODULE_TYPE.UNNAMED, MODULE_TYPE.UNNAMED,
WITH_S_DESCR, NO_FAILURE, args));
params.add(Arrays.asList(MODULE_TYPE.UNNAMED, MODULE_TYPE.UNNAMED,
@@ -166,29 +161,25 @@
boolean done = true;
try {
- VM_ARGS.put("-Duser.language=", "en");
- VM_ARGS.put("-Duser.region", "US");
- VM_ARGS.put("-Djava.security.auth.login.config=", SRC.resolve(
- "jaas.conf").toFile().getCanonicalPath());
-
done = CompilerUtils.compile(S_SRC, S_BUILD_DIR);
done &= CompilerUtils.compile(S_SRC, S_WITH_META_DESCR_BUILD_DIR);
done &= createMetaInfServiceDescriptor(S_META_DESCR_FPATH, S_IMPL);
- //Generate regular/modular jars with(out) META-INF
- //service descriptor
+ // Generate modular/regular jars with(out) META-INF
+ // service descriptor
generateJar(true, MODULE_TYPE.EXPLICIT, MS_JAR, S_BUILD_DIR, false);
generateJar(true, MODULE_TYPE.EXPLICIT, MS_WITH_DESCR_JAR,
S_WITH_META_DESCR_BUILD_DIR, false);
generateJar(true, MODULE_TYPE.UNNAMED, S_JAR, S_BUILD_DIR, false);
generateJar(true, MODULE_TYPE.UNNAMED, S_WITH_DESCRIPTOR_JAR,
S_WITH_META_DESCR_BUILD_DIR, false);
- //Generate regular/modular(depends on explicit/auto service)
- //jars for client
- done &= CompilerUtils.compile(C_SRC, C_BUILD_DIR);
- generateJar(false, MODULE_TYPE.EXPLICIT, MC_JAR, C_BUILD_DIR, true);
- generateJar(false, MODULE_TYPE.EXPLICIT,
- MC_DEPENDS_ON_AUTO_SERVICE_JAR, C_BUILD_DIR, false);
- generateJar(false, MODULE_TYPE.UNNAMED, C_JAR, C_BUILD_DIR, false);
+ // Compile client source codes.
+ done &= CompilerUtils.compile(C_SRC, C_BLD_DIR);
+ // Generate modular client jar with explicit dependency
+ generateJar(false, MODULE_TYPE.EXPLICIT, MC_JAR, C_BLD_DIR, true);
+ // Generate modular client jar without any dependency
+ generateJar(false, MODULE_TYPE.EXPLICIT, MCN_JAR, C_BLD_DIR, false);
+ // Generate regular client jar
+ generateJar(false, MODULE_TYPE.UNNAMED, C_JAR, C_BLD_DIR, false);
System.out.format("%nArtifacts generated successfully? %s", done);
if (!done) {
throw new RuntimeException("Artifact generation failed");
@@ -226,9 +217,9 @@
OutputAnalyzer output = null;
try {
- //For automated/explicit module type copy the corresponding
- //jars to module base folder, which will be considered as
- //module base path during execution.
+ // For automated/explicit module types, copy the corresponding
+ // jars to module base folder, which will be considered as
+ // module base path during execution.
if (!(cModuleType == MODULE_TYPE.UNNAMED
&& sModuletype == MODULE_TYPE.UNNAMED)) {
copyJarsToModuleBase(cModuleType, cJarPath, M_BASE_PATH);
@@ -237,20 +228,19 @@
System.out.format("%nExecuting java client with required"
+ " custom service in class/module path.");
- String mName = getModuleName(cModuleType, cJarPath,
- C_PKG);
+ String mName = getModuleName(cModuleType, cJarPath, C_PKG);
Path cmBasePath = (cModuleType != MODULE_TYPE.UNNAMED
|| sModuletype != MODULE_TYPE.UNNAMED) ? M_BASE_PATH : null;
String cPath = buildClassPath(cModuleType, cJarPath, sModuletype,
sJarPath);
+ Map<String, String> vmArgs = getVMArgs(sModuletype,
+ getModuleName(sModuletype, sJarPath, S_PKG));
output = ProcessTools.executeTestJava(
- getJavaCommand(cmBasePath, cPath, mName, MAIN, VM_ARGS,
+ getJavaCommand(cmBasePath, cPath, mName, MAIN, vmArgs,
args)).outputTo(System.out).errorTo(System.out);
} finally {
- //clean module path so that the modulepath can hold only
- //the required jars for next run.
+ // Clean module path to hold required jars for next run.
cleanModuleBasePath(M_BASE_PATH);
- System.out.println("--------------------------------------------");
}
return output;
}
@@ -260,9 +250,9 @@
* based on client/service module type.
*/
@Override
- public Path findJarPath(boolean service, MODULE_TYPE moduleType,
+ public Path findJarPath(boolean isService, MODULE_TYPE moduleType,
boolean addMetaDesc, boolean dependsOnServiceModule) {
- if (service) {
+ if (isService) {
if (moduleType == MODULE_TYPE.EXPLICIT) {
if (addMetaDesc) {
return MS_WITH_DESCR_JAR;
@@ -277,11 +267,12 @@
}
}
} else {
+ // Choose corresponding client jar using dependent module
if (moduleType == MODULE_TYPE.EXPLICIT) {
if (dependsOnServiceModule) {
return MC_JAR;
} else {
- return MC_DEPENDS_ON_AUTO_SERVICE_JAR;
+ return MCN_JAR;
}
} else {
return C_JAR;
@@ -289,4 +280,20 @@
}
}
+ /**
+ * VM argument required for the test.
+ */
+ private Map<String, String> getVMArgs(MODULE_TYPE sModuletype,
+ String addModName) throws IOException {
+ final Map<String, String> vmArgs = new LinkedHashMap<>();
+ vmArgs.put("-Duser.language=", "en");
+ vmArgs.put("-Duser.region=", "US");
+ vmArgs.put("-Djava.security.auth.login.config=", SRC.resolve(
+ "jaas.conf").toFile().getCanonicalPath());
+ if (addModName != null && sModuletype == MODULE_TYPE.AUTO) {
+ vmArgs.put("-addmods ", addModName);
+ }
+ return vmArgs;
+ }
+
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/security/auth/login/modules/JaasModularDefaultHandlerTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,255 @@
+/*
+ * 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.
+ */
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Arrays;
+import java.io.IOException;
+import java.lang.module.ModuleDescriptor;
+import java.util.ArrayList;
+import jdk.testlibrary.ProcessTools;
+import jdk.testlibrary.OutputAnalyzer;
+import org.testng.annotations.BeforeTest;
+
+/**
+ * @test
+ * @bug 8151654
+ * @library /lib/testlibrary
+ * @library /java/security/modules
+ * @build CompilerUtils JarUtils
+ * @summary Test custom JAAS callback handler with all possible modular option.
+ * @run testng JaasModularDefaultHandlerTest
+ */
+public class JaasModularDefaultHandlerTest extends ModularTest {
+
+ private static final Path S_SRC = SRC.resolve("TestCallbackHandler.java");
+ private static final String MODULAR = "M";
+ private static final String S_PKG = "handler";
+ private static final String S_JAR_NAME = S_PKG + JAR_EXTN;
+ private static final String MS_JAR_NAME = MODULAR + S_PKG + JAR_EXTN;
+ private static final String HANDLER = S_PKG + ".TestCallbackHandler";
+
+ private static final Path C_SRC
+ = SRC.resolve("JaasClientWithDefaultHandler.java");
+ private static final Path CL_SRC = SRC.resolve("TestLoginModule.java");
+ private static final String C_PKG = "login";
+ private static final String C_JAR_NAME = C_PKG + JAR_EXTN;
+ private static final String MCN_JAR_NAME
+ = MODULAR + C_PKG + "NoMUse" + JAR_EXTN;
+ private static final String MC_JAR_NAME = MODULAR + C_PKG + JAR_EXTN;
+
+ private static final Path BUILD_DIR = Paths.get(".").resolve("build");
+ private static final Path COMPILE_DIR = BUILD_DIR.resolve("bin");
+ private static final Path S_BUILD_DIR = COMPILE_DIR.resolve(S_PKG);
+ private static final Path C_BLD_DIR = COMPILE_DIR.resolve(C_PKG);
+ private static final Path M_BASE_PATH = BUILD_DIR.resolve("mbase");
+ private static final Path ARTIFACTS_DIR = BUILD_DIR.resolve("artifacts");
+
+ private static final Path S_ARTIFACTS_DIR = ARTIFACTS_DIR.resolve(S_PKG);
+ private static final Path S_JAR = S_ARTIFACTS_DIR.resolve(S_JAR_NAME);
+ private static final Path MS_JAR = S_ARTIFACTS_DIR.resolve(MS_JAR_NAME);
+
+ private static final Path C_ARTIFACTS_DIR = ARTIFACTS_DIR.resolve(C_PKG);
+ private static final Path C_JAR = C_ARTIFACTS_DIR.resolve(C_JAR_NAME);
+ private static final Path MC_JAR = C_ARTIFACTS_DIR.resolve(MC_JAR_NAME);
+ private static final Path MCN_JAR = C_ARTIFACTS_DIR.resolve(MCN_JAR_NAME);
+
+ private static final String MAIN = C_PKG + ".JaasClientWithDefaultHandler";
+ private static final List<String> M_REQUIRED = Arrays.asList("java.base",
+ "jdk.security.auth");
+
+ private static final String CLASS_NOT_FOUND_MSG
+ = "java.lang.ClassNotFoundException: handler.TestCallbackHandler";
+ private static final String NO_FAILURE = null;
+
+ /**
+ * Generates Test specific input parameters.
+ */
+ @Override
+ public Object[][] getTestInput() {
+
+ List<List<Object>> params = new ArrayList<>();
+ String[] args = new String[]{HANDLER};
+ // PARAMETER ORDERS -
+ // Client Module Type, Service Module Type,
+ // Service META Descriptor Required,
+ // Expected Failure message, Client arguments
+ params.add(Arrays.asList(MODULE_TYPE.EXPLICIT, MODULE_TYPE.EXPLICIT,
+ false, NO_FAILURE, args));
+ params.add(Arrays.asList(MODULE_TYPE.EXPLICIT, MODULE_TYPE.AUTO,
+ false, NO_FAILURE, args));
+ params.add(Arrays.asList(MODULE_TYPE.EXPLICIT, MODULE_TYPE.UNNAMED,
+ false, NO_FAILURE, args));
+
+ params.add(Arrays.asList(MODULE_TYPE.AUTO, MODULE_TYPE.EXPLICIT,
+ false, NO_FAILURE, args));
+ params.add(Arrays.asList(MODULE_TYPE.AUTO, MODULE_TYPE.AUTO,
+ false, NO_FAILURE, args));
+ params.add(Arrays.asList(MODULE_TYPE.AUTO, MODULE_TYPE.UNNAMED,
+ false, NO_FAILURE, args));
+
+ params.add(Arrays.asList(MODULE_TYPE.UNNAMED, MODULE_TYPE.EXPLICIT,
+ false, NO_FAILURE, args));
+ params.add(Arrays.asList(MODULE_TYPE.UNNAMED, MODULE_TYPE.AUTO,
+ false, NO_FAILURE, args));
+ params.add(Arrays.asList(MODULE_TYPE.UNNAMED, MODULE_TYPE.UNNAMED,
+ false, NO_FAILURE, args));
+ return params.stream().map(p -> p.toArray()).toArray(Object[][]::new);
+ }
+
+ /**
+ * Pre-compile and generate the artifacts required to run this test before
+ * running each test cases.
+ */
+ @BeforeTest
+ public void buildArtifacts() {
+
+ boolean done = true;
+ try {
+ done = CompilerUtils.compile(S_SRC, S_BUILD_DIR);
+ // Generate modular/regular handler jars.
+ generateJar(true, MODULE_TYPE.EXPLICIT, MS_JAR, S_BUILD_DIR, false);
+ generateJar(true, MODULE_TYPE.UNNAMED, S_JAR, S_BUILD_DIR, false);
+ // Compile client source codes.
+ done &= CompilerUtils.compile(C_SRC, C_BLD_DIR);
+ done &= CompilerUtils.compile(CL_SRC, C_BLD_DIR);
+ // Generate modular client jar with explicit dependency
+ generateJar(false, MODULE_TYPE.EXPLICIT, MC_JAR, C_BLD_DIR, true);
+ // Generate modular client jar without any dependency
+ generateJar(false, MODULE_TYPE.EXPLICIT, MCN_JAR, C_BLD_DIR, false);
+ // Generate regular client jar
+ generateJar(false, MODULE_TYPE.UNNAMED, C_JAR, C_BLD_DIR, false);
+ System.out.format("%nArtifacts generated successfully? %s", done);
+ if (!done) {
+ throw new RuntimeException("Artifact generation failed");
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Generate modular/regular jar based on module type for this test.
+ */
+ private void generateJar(boolean isService, MODULE_TYPE moduleType,
+ Path jar, Path compilePath, boolean depends) throws IOException {
+
+ ModuleDescriptor mDescriptor = null;
+ if (isService) {
+ mDescriptor = generateModuleDescriptor(isService, moduleType, S_PKG,
+ S_PKG, null, null, null, M_REQUIRED, depends);
+ } else {
+ mDescriptor = generateModuleDescriptor(isService, moduleType, C_PKG,
+ C_PKG, null, null, S_PKG, M_REQUIRED, depends);
+ }
+ generateJar(mDescriptor, jar, compilePath);
+ }
+
+ /**
+ * Holds Logic for the test client. This method will get called with each
+ * test parameter.
+ */
+ @Override
+ public OutputAnalyzer executeTestClient(MODULE_TYPE cModuleType,
+ Path cJarPath, MODULE_TYPE sModuletype, Path sJarPath,
+ String... args) throws Exception {
+
+ OutputAnalyzer output = null;
+ try {
+ // For automated/explicit module types, copy the corresponding
+ // jars to module base folder, which will be considered as
+ // module base path during execution.
+ if (!(cModuleType == MODULE_TYPE.UNNAMED
+ && sModuletype == MODULE_TYPE.UNNAMED)) {
+ copyJarsToModuleBase(cModuleType, cJarPath, M_BASE_PATH);
+ copyJarsToModuleBase(sModuletype, sJarPath, M_BASE_PATH);
+ }
+
+ System.out.format("%nExecuting java client with required"
+ + " custom service in class/module path.");
+ String mName = getModuleName(cModuleType, cJarPath, C_PKG);
+ Path cmBasePath = (cModuleType != MODULE_TYPE.UNNAMED
+ || sModuletype != MODULE_TYPE.UNNAMED) ? M_BASE_PATH : null;
+ String cPath = buildClassPath(cModuleType, cJarPath, sModuletype,
+ sJarPath);
+ Map<String, String> vmArgs = getVMArgs(sModuletype, cModuleType,
+ getModuleName(sModuletype, sJarPath, S_PKG));
+ output = ProcessTools.executeTestJava(
+ getJavaCommand(cmBasePath, cPath, mName, MAIN, vmArgs,
+ args)).outputTo(System.out).errorTo(System.out);
+ } finally {
+ // Clean module path to hold required jars for next run.
+ cleanModuleBasePath(M_BASE_PATH);
+ }
+ return output;
+ }
+
+ /**
+ * Decide the pre-generated client/service jar path for each test case
+ * based on client/service module type.
+ */
+ @Override
+ public Path findJarPath(boolean depends, MODULE_TYPE moduleType,
+ boolean addMetaDesc, boolean dependsOnServiceModule) {
+ if (depends) {
+ if (moduleType == MODULE_TYPE.EXPLICIT) {
+ return MS_JAR;
+ } else {
+ return S_JAR;
+ }
+ } else {
+ // Choose corresponding client jar using dependent module
+ if (moduleType == MODULE_TYPE.EXPLICIT) {
+ if (dependsOnServiceModule) {
+ return MC_JAR;
+ } else {
+ return MCN_JAR;
+ }
+ } else {
+ return C_JAR;
+ }
+ }
+ }
+
+ /**
+ * VM argument required for the test.
+ */
+ private Map<String, String> getVMArgs(MODULE_TYPE sModuletype,
+ MODULE_TYPE cModuleType, String addModName) throws IOException {
+ final Map<String, String> vmArgs = new LinkedHashMap<>();
+ vmArgs.put("-Duser.language=", "en");
+ vmArgs.put("-Duser.region=", "US");
+ vmArgs.put("-Djava.security.auth.login.config=", SRC.resolve(
+ "jaas.conf").toFile().getCanonicalPath());
+ if (addModName != null
+ && !(cModuleType == MODULE_TYPE.EXPLICIT
+ && sModuletype == MODULE_TYPE.EXPLICIT)) {
+ vmArgs.put("-addmods ", addModName);
+ }
+ return vmArgs;
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/security/auth/login/modules/TestCallbackHandler.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,54 @@
+/*
+ * 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 handler;
+
+import java.io.IOException;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+public class TestCallbackHandler implements CallbackHandler {
+
+ private static final String USER_NAME = "testUser";
+ private static final String PASSWORD = "testPassword";
+
+ @Override
+ public void handle(Callback[] callbacks) throws IOException,
+ UnsupportedCallbackException {
+ System.out.println("TestCallbackHandler will get resolved through"
+ + " auth.login.defaultCallbackHandler property.");
+ for (Callback callback : callbacks) {
+ if (callback instanceof NameCallback) {
+ ((NameCallback) callback).setName(USER_NAME);
+ } else if (callback instanceof PasswordCallback) {
+ ((PasswordCallback) callback).setPassword(
+ PASSWORD.toCharArray());
+ } else {
+ throw new UnsupportedCallbackException(callback);
+ }
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/net/ftp/TestFtpClientNameListWithNull.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,142 @@
+/*
+ * 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 8022580
+ * @summary "null" should be treated as "current directory" in nameList()
+ * method of FtpClient
+ * @modules java.base/sun.net.ftp
+ * @run main TestFtpClientNameListWithNull
+*/
+
+
+import sun.net.ftp.FtpClient;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+
+
+public class TestFtpClientNameListWithNull {
+
+ private static volatile boolean commandHasArgs;
+
+ public static void main(String[] args) throws Exception {
+ try (FtpServer server = new FtpServer();
+ FtpClient client = FtpClient.create()) {
+ (new Thread(server)).start();
+ int port = server.getPort();
+ client.connect(new InetSocketAddress("localhost", port));
+ client.nameList(null);
+ } finally {
+ if (commandHasArgs) {
+ throw new RuntimeException("Test failed. NLST shouldn't have " +
+ "args if nameList parameter is null");
+ }
+ }
+ }
+
+ private static class FtpServer implements AutoCloseable, Runnable {
+ private final ServerSocket serverSocket;
+
+ FtpServer() throws IOException {
+ serverSocket = new ServerSocket(0);
+ }
+
+ public void handleClient(Socket client) throws IOException {
+ boolean done = false;
+ String str;
+
+ client.setSoTimeout(2000);
+ BufferedReader in = new BufferedReader(new InputStreamReader(client.
+ getInputStream()));
+ PrintWriter out = new PrintWriter(client.getOutputStream(), true);
+ out.println("220 FTP serverSocket is ready.");
+ while (!done) {
+ try {
+ str = in.readLine();
+ } catch (SocketException e) {
+ done = true;
+ continue;
+ }
+ String cmd = str.substring(0, str.indexOf(" ") > 0 ?
+ str.indexOf(" ") : str.length());
+ String args = (cmd.equals(str)) ?
+ "" : str.substring(str.indexOf(" "));
+ switch (cmd) {
+ case "QUIT":
+ out.println("221 Goodbye.");
+ out.flush();
+ done = true;
+ break;
+ case "EPSV":
+ if ("all".equalsIgnoreCase(args)) {
+ out.println("200 EPSV ALL command successful.");
+ continue;
+ }
+ out.println("229 Entering Extended Passive Mode " +
+ "(|||" + getPort() + "|)");
+ break;
+ case "NLST":
+ if (args.trim().length() != 0) {
+ commandHasArgs = true;
+ }
+ out.println("200 Command okay.");
+ break;
+ default:
+ out.println("500 unsupported command: " + str);
+ }
+ }
+ }
+
+ public int getPort() {
+ if (serverSocket != null) {
+ return serverSocket.getLocalPort();
+ }
+ return 0;
+ }
+
+ public void close() throws IOException {
+ if (serverSocket != null && !serverSocket.isClosed()) {
+ serverSocket.close();
+ }
+ }
+
+ @Override
+ public void run() {
+ try {
+ try (Socket client = serverSocket.accept()) {
+ handleClient(client);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Problem in test execution", e);
+ }
+ }
+ }
+}
--- a/jdk/test/sun/tools/jps/JpsBase.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/sun/tools/jps/JpsBase.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2015, 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
@@ -59,7 +59,17 @@
return JpsBase.class.getName();
}
+ private static boolean userDirSanityCheck(String fullProcessName) {
+ String userDir = System.getProperty("user.dir");
+ if (!fullProcessName.startsWith(userDir)) {
+ System.err.printf("Test skipped. user.dir '%s' is not a prefix of '%s'\n", userDir, fullProcessName);
+ return false;
+ }
+ return true;
+ }
+
public static void main(String[] args) throws Exception {
+ System.out.printf("INFO: user.dir: '%s''\n", System.getProperty("user.dir"));
long pid = ProcessTools.getProcessId();
List<List<JpsHelper.JpsArg>> combinations = JpsHelper.JpsArg.generateCombinations();
@@ -85,8 +95,12 @@
// 30673 /tmp/jtreg/jtreg-workdir/scratch/JpsBase.jar ...
isFull = true;
String fullProcessName = getFullProcessName();
- pattern = "^" + pid + "\\s+" + replaceSpecialChars(fullProcessName) + ".*";
- output.shouldMatch(pattern);
+ // Skip the test if user.dir is not a prefix of the current path
+ // It's possible if the test is run from symlinked dir or windows alias drive
+ if (userDirSanityCheck(fullProcessName)) {
+ pattern = "^" + pid + "\\s+" + replaceSpecialChars(fullProcessName) + ".*";
+ output.shouldMatch(pattern);
+ }
break;
case m:
// If '-m' is specified output should contain the arguments passed to the main method:
--- a/jdk/test/sun/tools/jps/TestJpsJar.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/sun/tools/jps/TestJpsJar.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2015, 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
@@ -52,6 +52,7 @@
cmd.addAll(JpsHelper.getVmArgs());
cmd.add("-Dtest.jdk=" + testJdk);
cmd.add("-Dtest.src=" + testSrc);
+ cmd.add("-Duser.dir=" + System.getProperty("user.dir"));
cmd.add("-jar");
cmd.add(jar.getAbsolutePath());
cmd.add("monkey");
--- a/jdk/test/tools/jimage/JImageToolTest.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/tools/jimage/JImageToolTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -63,6 +63,8 @@
String jimage = jimagePath.toAbsolutePath().toString();
String bootimage = modulesimagePath.toAbsolutePath().toString();
String extractDir = Paths.get(".", "extract").toAbsolutePath().toString();
+ jimage("list", bootimage);
+ jimage("verify", bootimage);
jimage("extract", "--dir", extractDir, bootimage);
System.out.println("Test successful");
} else {
--- a/jdk/test/tools/jlink/plugins/IncludeLocalesPluginTest.java Thu Jul 21 16:45:56 2016 +0000
+++ b/jdk/test/tools/jlink/plugins/IncludeLocalesPluginTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -90,6 +90,71 @@
"",
},
+ // Asterisk works exactly the same as above
+ {
+ "*",
+ "jdk.localedata",
+ List.of(
+ "/jdk.localedata/sun/text/resources/ext/FormatData_en_GB.class",
+ "/jdk.localedata/sun/text/resources/ext/FormatData_ja.class",
+ "/jdk.localedata/sun/text/resources/ext/FormatData_th.class",
+ "/jdk.localedata/sun/text/resources/ext/FormatData_zh.class",
+ "/jdk.localedata/sun/text/resources/cldr/ext/FormatData_en_001.class",
+ "/jdk.localedata/sun/text/resources/cldr/ext/FormatData_ja.class",
+ "/jdk.localedata/sun/text/resources/cldr/ext/FormatData_th.class",
+ "/jdk.localedata/sun/text/resources/cldr/ext/FormatData_zh.class"),
+ List.of(),
+ Arrays.stream(Locale.getAvailableLocales())
+ // "(root)" for Locale.ROOT rather than ""
+ .map(loc -> loc.equals(Locale.ROOT) ? "(root)" : loc.toString())
+ .collect(Collectors.toList()),
+ "",
+ },
+
+ // World English/Spanish in Latin America
+ {
+ "--include-locales=en-001,es-419",
+ "jdk.localedata",
+ List.of(
+ "/jdk.localedata/sun/text/resources/ext/FormatData_en_AU.class",
+ "/jdk.localedata/sun/text/resources/ext/FormatData_es.class",
+ "/jdk.localedata/sun/text/resources/ext/FormatData_es_AR.class",
+ "/jdk.localedata/sun/text/resources/cldr/ext/FormatData_en_001.class",
+ "/jdk.localedata/sun/text/resources/cldr/ext/FormatData_en_150.class",
+ "/jdk.localedata/sun/text/resources/cldr/ext/FormatData_en_AT.class",
+ "/jdk.localedata/sun/text/resources/cldr/ext/FormatData_es.class",
+ "/jdk.localedata/sun/text/resources/cldr/ext/FormatData_es_419.class",
+ "/jdk.localedata/sun/text/resources/cldr/ext/FormatData_es_AR.class"),
+ List.of(
+ "/jdk.localedata/sun/text/resources/LineBreakIteratorData_th",
+ "/jdk.localedata/sun/text/resources/thai_dict",
+ "/jdk.localedata/sun/text/resources/WordBreakIteratorData_th",
+ "/jdk.localedata/sun/text/resources/ext/BreakIteratorInfo_th.class",
+ "/jdk.localedata/sun/text/resources/ext/BreakIteratorRules_th.class",
+ "/jdk.localedata/sun/text/resources/ext/FormatData_ja.class",
+ "/jdk.localedata/sun/text/resources/ext/FormatData_th.class",
+ "/jdk.localedata/sun/text/resources/cldr/ext/FormatData_ja.class",
+ "/jdk.localedata/sun/text/resources/cldr/ext/FormatData_th.class"),
+ List.of(
+ "(root)", "en", "en_US", "en_US_POSIX", "en_001", "en_150", "en_AG", "en_AI",
+ "en_AT", "en_AU", "en_BB", "en_BE", "en_BM", "en_BS", "en_BW", "en_BZ",
+ "en_CA", "en_CC", "en_CH", "en_CK", "en_CM", "en_CX", "en_CY", "en_DE",
+ "en_DG", "en_DK", "en_DM", "en_ER", "en_FI", "en_FJ", "en_FK", "en_FM",
+ "en_GB", "en_GD", "en_GG", "en_GH", "en_GI", "en_GM", "en_GY", "en_HK",
+ "en_IE", "en_IL", "en_IM", "en_IN", "en_IO", "en_JE", "en_JM", "en_KE",
+ "en_KI", "en_KN", "en_KY", "en_LC", "en_LR", "en_LS", "en_MG", "en_MO",
+ "en_MS", "en_MT", "en_MU", "en_MW", "en_MY", "en_NA", "en_NF", "en_NG",
+ "en_NL", "en_NR", "en_NU", "en_NZ", "en_PG", "en_PH", "en_PK", "en_PN",
+ "en_PW", "en_RW", "en_SB", "en_SC", "en_SD", "en_SE", "en_SG", "en_SH",
+ "en_SI", "en_SL", "en_SS", "en_SX", "en_SZ", "en_TC", "en_TK", "en_TO",
+ "en_TT", "en_TV", "en_TZ", "en_UG", "en_VC", "en_VG", "en_VU", "en_WS",
+ "en_ZA", "en_ZM", "en_ZW", "es", "es_419", "es_AR", "es_BO", "es_BR",
+ "es_CL", "es_CO", "es_CR", "es_CU", "es_DO", "es_EC", "es_GT", "es_HN",
+ "es_MX", "es_NI", "es_PA", "es_PE", "es_PR", "es_PY", "es_SV", "es_US",
+ "es_UY", "es_VE"),
+ "",
+ },
+
// All English and Japanese locales
{
"--include-locales=en,ja",
@@ -128,6 +193,35 @@
"",
},
+ // All locales in Austria
+ {
+ "--include-locales=*-AT",
+ "jdk.localedata",
+ List.of(
+ "/jdk.localedata/sun/text/resources/ext/FormatData_de.class",
+ "/jdk.localedata/sun/text/resources/ext/FormatData_de_AT.class",
+ "/jdk.localedata/sun/text/resources/cldr/ext/FormatData_de.class",
+ "/jdk.localedata/sun/text/resources/cldr/ext/FormatData_de_AT.class",
+ "/jdk.localedata/sun/text/resources/cldr/ext/FormatData_en_001.class",
+ "/jdk.localedata/sun/text/resources/cldr/ext/FormatData_en_150.class",
+ "/jdk.localedata/sun/text/resources/cldr/ext/FormatData_en_AT.class"),
+ List.of(
+ "/jdk.localedata/sun/text/resources/LineBreakIteratorData_th",
+ "/jdk.localedata/sun/text/resources/thai_dict",
+ "/jdk.localedata/sun/text/resources/WordBreakIteratorData_th",
+ "/jdk.localedata/sun/text/resources/ext/BreakIteratorInfo_th.class",
+ "/jdk.localedata/sun/text/resources/ext/BreakIteratorRules_th.class",
+ "/jdk.localedata/sun/text/resources/ext/FormatData_en_GB.class",
+ "/jdk.localedata/sun/text/resources/ext/FormatData_ja.class",
+ "/jdk.localedata/sun/text/resources/ext/FormatData_th.class",
+ "/jdk.localedata/sun/text/resources/cldr/ext/FormatData_ja.class",
+ "/jdk.localedata/sun/text/resources/cldr/ext/FormatData_th.class"),
+ List.of(
+ "(root)", "en", "en_US", "en_US_POSIX", "en_001", "en_150", "en_AT",
+ "de", "de_AT"),
+ "",
+ },
+
// All locales in India
{
"--include-locales=*-IN",
@@ -154,10 +248,11 @@
"/jdk.localedata/sun/text/resources/cldr/ext/FormatData_th.class",
"/jdk.localedata/sun/text/resources/cldr/ext/FormatData_zh.class"),
List.of(
- "(root)", "as_IN", "bn_IN", "bo_IN", "brx_IN", "en", /* "en_001", */
- "en_IN", "en_US", "en_US_POSIX", "gu_IN", "hi_IN", "kn_IN", "kok_IN",
- "ks_IN", "ml_IN", "mr_IN", "ne_IN", "or_IN", "pa_IN", "pa_IN_#Guru",
- "ta_IN", "te_IN", "ur_IN"),
+ "(root)", "as_IN", "as", "bn_IN", "bn", "bo_IN", "bo", "brx_IN", "brx",
+ "en", "en_001", "en_IN", "en_US", "en_US_POSIX", "gu_IN", "gu", "hi_IN",
+ "hi", "kn_IN", "kn", "kok_IN", "kok", "ks_IN", "ks", "ml_IN", "ml",
+ "mr_IN", "mr", "ne_IN", "ne", "or_IN", "or", "pa_IN", "pa", "pa_IN_#Guru",
+ "pa__#Guru", "ta_IN", "ta", "te_IN", "te", "ur_IN", "ur"),
"",
},
@@ -203,12 +298,40 @@
"/jdk.localedata/sun/text/resources/ext/FormatData_en_GB.class",
"/jdk.localedata/sun/text/resources/ext/FormatData_ja.class",
"/jdk.localedata/sun/text/resources/ext/FormatData_th.class",
+ "/jdk.localedata/sun/text/resources/ext/FormatData_zh_CN.class",
"/jdk.localedata/sun/text/resources/cldr/ext/FormatData_en_001.class",
"/jdk.localedata/sun/text/resources/cldr/ext/FormatData_ja.class",
"/jdk.localedata/sun/text/resources/cldr/ext/FormatData_th.class"),
List.of(
- "(root)", "en", "en_US", "en_US_POSIX", "zh_HK", "zh_HK_#Hans",
- "zh_HK_#Hant"),
+ "(root)", "en", "en_US", "en_US_POSIX", "zh", "zh__#Hans", "zh__#Hant",
+ "zh_HK", "zh_HK_#Hans", "zh_HK_#Hant"),
+ "",
+ },
+
+ // Simplified Chinese
+ {
+ "--include-locales=zh-Hans",
+ "jdk.localedata",
+ List.of(
+ "/jdk.localedata/sun/text/resources/ext/FormatData_zh.class",
+ "/jdk.localedata/sun/text/resources/ext/FormatData_zh_CN.class",
+ "/jdk.localedata/sun/text/resources/ext/FormatData_zh_SG.class",
+ "/jdk.localedata/sun/text/resources/cldr/ext/FormatData_zh.class"),
+ List.of(
+ "/jdk.localedata/sun/text/resources/LineBreakIteratorData_th",
+ "/jdk.localedata/sun/text/resources/thai_dict",
+ "/jdk.localedata/sun/text/resources/WordBreakIteratorData_th",
+ "/jdk.localedata/sun/text/resources/ext/BreakIteratorInfo_th.class",
+ "/jdk.localedata/sun/text/resources/ext/BreakIteratorRules_th.class",
+ "/jdk.localedata/sun/text/resources/ext/FormatData_en_GB.class",
+ "/jdk.localedata/sun/text/resources/ext/FormatData_ja.class",
+ "/jdk.localedata/sun/text/resources/ext/FormatData_th.class",
+ "/jdk.localedata/sun/text/resources/cldr/ext/FormatData_en_001.class",
+ "/jdk.localedata/sun/text/resources/cldr/ext/FormatData_ja.class",
+ "/jdk.localedata/sun/text/resources/cldr/ext/FormatData_th.class"),
+ List.of(
+ "(root)", "en", "en_US", "en_US_POSIX", "zh", "zh__#Hans", "zh_CN",
+ "zh_CN_#Hans", "zh_HK_#Hans", "zh_MO_#Hans", "zh_SG", "zh_SG_#Hans"),
"",
},
@@ -290,7 +413,7 @@
null,
null,
new PluginException(String.format(
- PluginsResourceBundle.getMessage("include-locales.invalidtag"), "zh_HK"))
+ PluginsResourceBundle.getMessage("include-locales.invalidtag"), "zh_hk"))
.getMessage(),
},
--- a/langtools/.hgtags Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/.hgtags Thu Jul 21 20:09:19 2016 -0700
@@ -370,3 +370,4 @@
2d65e127e93d5ff0df61bf78e57d7f46a2f1edeb jdk-9+125
ea4eea2997b9e2f26cd7965839921710ff4065c8 jdk-9+126
a42768b48cb0c5af9063e12093975baeeca3b5fa jdk-9+127
+2764986661b6d339ba73af52d69d3506ce12e648 jdk-9+128
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java Thu Jul 21 20:09:19 2016 -0700
@@ -1900,7 +1900,8 @@
Type qualifier = (tree.meth.hasTag(SELECT))
? ((JCFieldAccess) tree.meth).selected.type
: env.enclClass.sym.type;
- restype = adjustMethodReturnType(qualifier, methName, argtypes, restype);
+ Symbol msym = TreeInfo.symbol(tree.meth);
+ restype = adjustMethodReturnType(msym, qualifier, methName, argtypes, restype);
chk.checkRefTypes(tree.typeargs, typeargtypes);
@@ -1912,19 +1913,25 @@
chk.validate(tree.typeargs, localEnv);
}
//where
- Type adjustMethodReturnType(Type qualifierType, Name methodName, List<Type> argtypes, Type restype) {
- if (methodName == names.clone && types.isArray(qualifierType)) {
+ Type adjustMethodReturnType(Symbol msym, Type qualifierType, Name methodName, List<Type> argtypes, Type restype) {
+ if (msym != null &&
+ msym.owner == syms.objectType.tsym &&
+ methodName == names.getClass &&
+ argtypes.isEmpty()) {
+ // as a special case, x.getClass() has type Class<? extends |X|>
+ return new ClassType(restype.getEnclosingType(),
+ List.<Type>of(new WildcardType(types.erasure(qualifierType),
+ BoundKind.EXTENDS,
+ syms.boundClass)),
+ restype.tsym,
+ restype.getMetadata());
+ } else if (msym != null &&
+ msym.owner == syms.arrayClass &&
+ methodName == names.clone &&
+ types.isArray(qualifierType)) {
// as a special case, array.clone() has a result that is
// the same as static type of the array being cloned
return qualifierType;
- } else if (methodName == names.getClass && argtypes.isEmpty()) {
- // as a special case, x.getClass() has type Class<? extends |X|>
- return new ClassType(restype.getEnclosingType(),
- List.<Type>of(new WildcardType(types.erasure(qualifierType),
- BoundKind.EXTENDS,
- syms.boundClass)),
- restype.tsym,
- restype.getMetadata());
} else {
return restype;
}
@@ -2989,7 +2996,7 @@
if (!refType.isErroneous()) {
refType = types.createMethodTypeWithReturn(refType,
- adjustMethodReturnType(lookupHelper.site, that.name, checkInfo.pt.getParameterTypes(), refType.getReturnType()));
+ adjustMethodReturnType(refSym, lookupHelper.site, that.name, checkInfo.pt.getParameterTypes(), refType.getReturnType()));
}
//go ahead with standard method reference compatibility check - note that param check
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java Thu Jul 21 20:09:19 2016 -0700
@@ -902,13 +902,7 @@
/** Return binary operator that corresponds to given access code.
*/
private OperatorSymbol binaryAccessOperator(int acode) {
- for (Symbol sym : syms.predefClass.members().getSymbols(NON_RECURSIVE)) {
- if (sym instanceof OperatorSymbol) {
- OperatorSymbol op = (OperatorSymbol)sym;
- if (accessCode(op.opcode) == acode) return op;
- }
- }
- return null;
+ return (OperatorSymbol)operators.lookupBinaryOp(sym -> accessCode(((OperatorSymbol)sym).opcode) == acode);
}
/** Return tree tag for assignment operation corresponding
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Operators.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Operators.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 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
@@ -798,6 +798,15 @@
.addBinaryOperator(BOOLEAN, BOOLEAN, BOOLEAN, bool_or));
}
+ Symbol lookupBinaryOp(Predicate<Symbol> applicabilityTest) {
+ return binaryOperators.values().stream()
+ .flatMap(List::stream)
+ .map(helper -> helper.doLookup(applicabilityTest))
+ .distinct()
+ .filter(sym -> sym != syms.noSymbol)
+ .findFirst().get();
+ }
+
/**
* Complete the initialization of an operator helper by storing it into the corresponding operator map.
*/
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java Thu Jul 21 20:09:19 2016 -0700
@@ -449,7 +449,7 @@
? CompileState.valueOf(options.get("shouldstop.ifNoError"))
: CompileState.GENERATE;
- if (options.isUnset("oldDiags"))
+ if (options.isUnset("diags.legacy"))
log.setDiagnosticFormatter(RichDiagnosticFormatter.instance(context));
PlatformDescription platformProvider = context.get(PlatformDescription.class);
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java Thu Jul 21 20:09:19 2016 -0700
@@ -346,21 +346,6 @@
}
},
- DIAGS("-XDdiags=", null, HIDDEN, INFO) {
- @Override
- public boolean process(OptionHelper helper, String option) {
- option = option.substring(option.indexOf('=') + 1);
- String diagsOption = option.contains("%") ?
- "-XDdiagsFormat=" :
- "-XDdiags=";
- diagsOption += option;
- if (XD.matches(diagsOption))
- return XD.process(helper, diagsOption);
- else
- return false;
- }
- },
-
HELP("-help", "opt.help", STANDARD, INFO) {
@Override
public boolean process(OptionHelper helper, String option) {
@@ -506,30 +491,6 @@
XDIAGS("-Xdiags:", "opt.diags", EXTENDED, BASIC, ONEOF, "compact", "verbose"),
- /* This is a back door to the compiler's option table.
- * -XDx=y sets the option x to the value y.
- * -XDx sets the option x to the value x.
- */
- XD("-XD", null, HIDDEN, BASIC) {
- @Override
- public boolean matches(String s) {
- return s.startsWith(text);
- }
- @Override
- public boolean process(OptionHelper helper, String option) {
- return process(helper, option, option.substring(text.length()));
- }
-
- @Override
- public boolean process(OptionHelper helper, String option, String arg) {
- int eq = arg.indexOf('=');
- String key = (eq < 0) ? arg : arg.substring(0, eq);
- String value = (eq < 0) ? arg : arg.substring(eq+1);
- helper.put(key, value);
- return false;
- }
- },
-
XDEBUG("-Xdebug:", null, HIDDEN, BASIC) {
@Override
public boolean process(OptionHelper helper, String option) {
@@ -556,6 +517,37 @@
}
},
+ DIAGS("-diags:", null, HIDDEN, BASIC, true) {
+ @Override
+ public boolean process(OptionHelper helper, String option) {
+ return HiddenGroup.DIAGS.process(helper, option);
+ }
+ },
+
+ /* This is a back door to the compiler's option table.
+ * -XDx=y sets the option x to the value y.
+ * -XDx sets the option x to the value x.
+ */
+ XD("-XD", null, HIDDEN, BASIC) {
+ @Override
+ public boolean matches(String s) {
+ return s.startsWith(text);
+ }
+ @Override
+ public boolean process(OptionHelper helper, String option) {
+ return process(helper, option, option.substring(text.length()));
+ }
+
+ @Override
+ public boolean process(OptionHelper helper, String option, String arg) {
+ int eq = arg.indexOf('=');
+ String key = (eq < 0) ? arg : arg.substring(0, eq);
+ String value = (eq < 0) ? arg : arg.substring(eq+1);
+ helper.put(key, value);
+ return false;
+ }
+ },
+
XADDEXPORTS("-XaddExports:", "opt.arg.addExports", "opt.addExports", EXTENDED, BASIC) {
@Override
public boolean process(OptionHelper helper, String option) {
@@ -672,6 +664,26 @@
ANYOF
}
+ enum HiddenGroup {
+ DIAGS("diags");
+
+ final String text;
+
+ HiddenGroup(String text) {
+ this.text = text;
+ }
+
+ public boolean process(OptionHelper helper, String option) {
+ String p = option.substring(option.indexOf(':') + 1).trim();
+ String[] subOptions = p.split(";");
+ for (String subOption : subOptions) {
+ subOption = text + "." + subOption.trim();
+ XD.process(helper, subOption, subOption);
+ }
+ return false;
+ }
+ }
+
public final String text;
final OptionKind kind;
@@ -705,6 +717,12 @@
this(text, null, descrKey, kind, group, null, null, false);
}
+ Option(String text, String descrKey,
+ OptionKind kind, OptionGroup group,
+ boolean doHasSuffix) {
+ this(text, null, descrKey, kind, group, null, null, doHasSuffix);
+ }
+
Option(String text, String argsNameKey, String descrKey,
OptionKind kind, OptionGroup group) {
this(text, argsNameKey, descrKey, kind, group, null, null, false);
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractDiagnosticFormatter.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/util/AbstractDiagnosticFormatter.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 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
@@ -403,13 +403,13 @@
public SimpleConfiguration(Options options, Set<DiagnosticPart> parts) {
this(parts);
String showSource = null;
- if ((showSource = options.get("showSource")) != null) {
+ if ((showSource = options.get("diags.showSource")) != null) {
if (showSource.equals("true"))
setVisiblePart(DiagnosticPart.SOURCE, true);
else if (showSource.equals("false"))
setVisiblePart(DiagnosticPart.SOURCE, false);
}
- String diagOpts = options.get("diags");
+ String diagOpts = options.get("diags.formatterOptions");
if (diagOpts != null) {//override -XDshowSource
Collection<String> args = Arrays.asList(diagOpts.split(","));
if (args.contains("short")) {
@@ -422,7 +422,7 @@
setVisiblePart(DiagnosticPart.SOURCE, false);
}
String multiPolicy = null;
- if ((multiPolicy = options.get("multilinePolicy")) != null) {
+ if ((multiPolicy = options.get("diags.multilinePolicy")) != null) {
if (multiPolicy.equals("disabled"))
setVisiblePart(DiagnosticPart.SUBDIAGNOSTICS, false);
else if (multiPolicy.startsWith("limit:")) {
@@ -447,7 +447,7 @@
}
}
String showCaret = null;
- if (((showCaret = options.get("showCaret")) != null) &&
+ if (((showCaret = options.get("diags.showCaret")) != null) &&
showCaret.equals("false"))
setCaretEnabled(false);
else
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/util/BasicDiagnosticFormatter.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/util/BasicDiagnosticFormatter.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 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
@@ -229,9 +229,9 @@
DiagnosticPart.SOURCE));
initFormat();
initIndentation();
- if (options.isSet("oldDiags"))
+ if (options.isSet("diags.legacy"))
initOldFormat();
- String fmt = options.get("diagsFormat");
+ String fmt = options.get("diags.layout");
if (fmt != null) {
if (fmt.equals("OLD"))
initOldFormat();
@@ -239,12 +239,12 @@
initFormats(fmt);
}
String srcPos = null;
- if ((((srcPos = options.get("sourcePosition")) != null)) &&
+ if ((((srcPos = options.get("diags.sourcePosition")) != null)) &&
srcPos.equals("bottom"))
setSourcePosition(SourcePosition.BOTTOM);
else
setSourcePosition(SourcePosition.AFTER_SUMMARY);
- String indent = options.get("diagsIndentation");
+ String indent = options.get("diags.indent");
if (indent != null) {
String[] levels = indent.split("\\|");
try {
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/util/RichDiagnosticFormatter.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/util/RichDiagnosticFormatter.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 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
@@ -644,7 +644,7 @@
EnumSet.of(RichFormatterFeature.SIMPLE_NAMES,
RichFormatterFeature.WHERE_CLAUSES,
RichFormatterFeature.UNIQUE_TYPEVAR_NAMES);
- String diagOpts = options.get("diags");
+ String diagOpts = options.get("diags.formatterOptions");
if (diagOpts != null) {
for (String args: diagOpts.split(",")) {
if (args.equals("-where")) {
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/jdi/ClassTracker.java Thu Jul 21 16:45:56 2016 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,130 +0,0 @@
-/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. 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.internal.jshell.jdi;
-
-import java.util.HashMap;
-import java.util.Objects;
-import com.sun.jdi.ReferenceType;
-import java.util.List;
-import com.sun.jdi.VirtualMachine;
-
-/**
- * Tracks the state of a class.
- */
-class ClassTracker {
-
- private final VirtualMachine vm;
- private final HashMap<String, ClassInfo> map;
-
- ClassTracker(VirtualMachine vm) {
- this.vm = vm;
- this.map = new HashMap<>();
- }
-
- /**
- * Associates a class name, class bytes, and ReferenceType.
- */
- class ClassInfo {
-
- // The name of the class -- always set
- private final String className;
-
- // The corresponding compiled class bytes when a load or redefine
- // is started. May not be the loaded bytes. May be null.
- private byte[] bytes;
-
- // The class bytes successfully loaded/redefined into the remote VM.
- private byte[] loadedBytes;
-
- // The corresponding JDI ReferenceType. Used by redefineClasses and
- // acts as indicator of successful load (null if not loaded).
- private ReferenceType rt;
-
- private ClassInfo(String className) {
- this.className = className;
- }
-
- String getClassName() {
- return className;
- }
-
- byte[] getLoadedBytes() {
- return loadedBytes;
- }
-
- byte[] getBytes() {
- return bytes;
- }
-
- private void setBytes(byte[] potentialBytes) {
- this.bytes = potentialBytes;
- }
-
- // The class has been successful loaded redefined. The class bytes
- // sent are now actually loaded.
- void markLoaded() {
- loadedBytes = bytes;
- }
-
- // Ask JDI for the ReferenceType, null if not loaded.
- ReferenceType getReferenceTypeOrNull() {
- if (rt == null) {
- rt = nameToRef(className);
- }
- return rt;
- }
-
- private ReferenceType nameToRef(String name) {
- List<ReferenceType> rtl = vm.classesByName(name);
- if (rtl.size() != 1) {
- return null;
- }
- return rtl.get(0);
- }
-
- @Override
- public boolean equals(Object o) {
- return o instanceof ClassInfo
- && ((ClassInfo) o).className.equals(className);
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(this.className);
- }
- }
-
- // Map a class name to the current compiled class bytes.
- ClassInfo classInfo(String className, byte[] bytes) {
- ClassInfo ci = get(className);
- ci.setBytes(bytes);
- return ci;
- }
-
- // Lookup the ClassInfo by class name, create if it does not exist.
- ClassInfo get(String className) {
- return map.computeIfAbsent(className, k -> new ClassInfo(k));
- }
-}
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/jdi/FailOverExecutionControl.java Thu Jul 21 16:45:56 2016 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,119 +0,0 @@
-/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. 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.internal.jshell.jdi;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import jdk.internal.jshell.debug.InternalDebugControl;
-import jdk.jshell.JShellException;
-import jdk.jshell.spi.ExecutionControl;
-import jdk.jshell.spi.ExecutionEnv;
-
-/**
- * A meta implementation of ExecutionControl which cycles through the specified
- * ExecutionControl instances until it finds one that starts.
- */
-public class FailOverExecutionControl implements ExecutionControl {
-
- private final List<ExecutionControl> ecl = new ArrayList<>();
- private ExecutionControl active = null;
- private final List<Exception> thrown = new ArrayList<>();
-
- /**
- * Create the ExecutionControl instance with at least one actual
- * ExecutionControl instance.
- *
- * @param ec0 the first instance to try
- * @param ecs the second and on instance to try
- */
- public FailOverExecutionControl(ExecutionControl ec0, ExecutionControl... ecs) {
- ecl.add(ec0);
- for (ExecutionControl ec : ecs) {
- ecl.add(ec);
- }
- }
-
- @Override
- public void start(ExecutionEnv env) throws Exception {
- for (ExecutionControl ec : ecl) {
- try {
- ec.start(env);
- // Success! This is our active ExecutionControl
- active = ec;
- return;
- } catch (Exception ex) {
- thrown.add(ex);
- } catch (Throwable ex) {
- thrown.add(new RuntimeException(ex));
- }
- InternalDebugControl.debug(env.state(), env.userErr(),
- thrown.get(thrown.size() - 1), "failed one in FailOverExecutionControl");
- }
- // They have all failed -- rethrow the first exception we encountered
- throw thrown.get(0);
- }
-
- @Override
- public void close() {
- active.close();
- }
-
- @Override
- public boolean addToClasspath(String path) {
- return active.addToClasspath(path);
- }
-
- @Override
- public String invoke(String classname, String methodname) throws JShellException {
- return active.invoke(classname, methodname);
- }
-
- @Override
- public boolean load(Collection<String> classes) {
- return active.load(classes);
- }
-
- @Override
- public boolean redefine(Collection<String> classes) {
- return active.redefine(classes);
- }
-
- @Override
- public ClassStatus getClassStatus(String classname) {
- return active.getClassStatus(classname);
- }
-
- @Override
- public void stop() {
- active.stop();
- }
-
- @Override
- public String varValue(String classname, String varname) {
- return active.varValue(classname, varname);
- }
-
-}
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/jdi/JDIConnection.java Thu Jul 21 16:45:56 2016 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,346 +0,0 @@
-/*
- * Copyright (c) 1998, 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.
- */
-
-/*
- * This source code is provided to illustrate the usage of a given feature
- * or technique and has been deliberately simplified. Additional steps
- * required for a production-quality application, such as security checks,
- * input validation and proper error handling, might not be present in
- * this sample code.
- */
-
-
-package jdk.internal.jshell.jdi;
-
-import com.sun.jdi.*;
-import com.sun.jdi.connect.*;
-
-import java.util.*;
-import java.util.Map.Entry;
-import java.io.*;
-import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
-
-/**
- * Connection to a Java Debug Interface VirtualMachine instance.
- * Adapted from jdb VMConnection. Message handling, exception handling, and I/O
- * redirection changed. Interface to JShell added.
- */
-class JDIConnection {
-
- private static final String REMOTE_AGENT = "jdk.internal.jshell.remote.RemoteAgent";
-
- private VirtualMachine vm;
- private boolean active = true;
- private Process process = null;
- private int outputCompleteCount = 0;
-
- private final JDIExecutionControl ec;
- private final Connector connector;
- private final Map<String, com.sun.jdi.connect.Connector.Argument> connectorArgs;
- private final int traceFlags;
-
- private synchronized void notifyOutputComplete() {
- outputCompleteCount++;
- notifyAll();
- }
-
- private synchronized void waitOutputComplete() {
- // Wait for stderr and stdout
- if (process != null) {
- while (outputCompleteCount < 2) {
- try {wait();} catch (InterruptedException e) {}
- }
- }
- }
-
- private Connector findConnector(String name) {
- for (Connector cntor :
- Bootstrap.virtualMachineManager().allConnectors()) {
- if (cntor.name().equals(name)) {
- return cntor;
- }
- }
- return null;
- }
-
- private Map <String, Connector.Argument> mergeConnectorArgs(Connector connector, Map<String, String> argumentName2Value) {
- Map<String, Connector.Argument> arguments = connector.defaultArguments();
-
- for (Entry<String, String> argumentEntry : argumentName2Value.entrySet()) {
- String name = argumentEntry.getKey();
- String value = argumentEntry.getValue();
- Connector.Argument argument = arguments.get(name);
-
- if (argument == null) {
- throw new IllegalArgumentException("Argument is not defined for connector:" +
- name + " -- " + connector.name());
- }
-
- argument.setValue(value);
- }
-
- return arguments;
- }
-
- /**
- * The JShell specific Connector args for the LaunchingConnector.
- *
- * @param portthe socket port for (non-JDI) commands
- * @param remoteVMOptions any user requested VM options
- * @return the argument map
- */
- private static Map<String, String> launchArgs(int port, String remoteVMOptions) {
- Map<String, String> argumentName2Value = new HashMap<>();
- argumentName2Value.put("main", REMOTE_AGENT + " " + port);
- argumentName2Value.put("options", remoteVMOptions);
- return argumentName2Value;
- }
-
- /**
- * Start the remote agent and establish a JDI connection to it.
- *
- * @param ec the execution control instance
- * @param port the socket port for (non-JDI) commands
- * @param remoteVMOptions any user requested VM options
- * @param isLaunch does JDI do the launch? That is, LaunchingConnector,
- * otherwise we start explicitly and use ListeningConnector
- */
- JDIConnection(JDIExecutionControl ec, int port, List<String> remoteVMOptions, boolean isLaunch) {
- this(ec,
- isLaunch
- ? "com.sun.jdi.CommandLineLaunch"
- : "com.sun.jdi.SocketListen",
- isLaunch
- ? launchArgs(port, String.join(" ", remoteVMOptions))
- : new HashMap<>(),
- 0);
- if (isLaunch) {
- vm = launchTarget();
- } else {
- vm = listenTarget(port, remoteVMOptions);
- }
-
- if (isOpen() && vm().canBeModified()) {
- /*
- * Connection opened on startup.
- */
- new JDIEventHandler(vm(), (b) -> ec.handleVMExit())
- .start();
- }
- }
-
- /**
- * Base constructor -- set-up a JDI connection.
- *
- * @param ec the execution control instance
- * @param connectorName the standardized name of the connector
- * @param argumentName2Value the argument map
- * @param traceFlags should we trace JDI behavior
- */
- JDIConnection(JDIExecutionControl ec, String connectorName, Map<String, String> argumentName2Value, int traceFlags) {
- this.ec = ec;
- this.connector = findConnector(connectorName);
- if (connector == null) {
- throw new IllegalArgumentException("No connector named: " + connectorName);
- }
- connectorArgs = mergeConnectorArgs(connector, argumentName2Value);
- this.traceFlags = traceFlags;
- }
-
- final synchronized VirtualMachine vm() {
- if (vm == null) {
- throw new JDINotConnectedException();
- } else {
- return vm;
- }
- }
-
- private synchronized boolean isOpen() {
- return (vm != null);
- }
-
- synchronized boolean isRunning() {
- return process != null && process.isAlive();
- }
-
- // Beginning shutdown, ignore any random dying squeals
- void beginShutdown() {
- active = false;
- }
-
- synchronized void disposeVM() {
- try {
- if (vm != null) {
- vm.dispose(); // This could NPE, so it is caught below
- vm = null;
- }
- } catch (VMDisconnectedException ex) {
- // Ignore if already closed
- } catch (Throwable e) {
- ec.debug(DBG_GEN, null, "disposeVM threw: " + e);
- } finally {
- if (process != null) {
- process.destroy();
- process = null;
- }
- waitOutputComplete();
- }
- }
-
- private void dumpStream(InputStream inStream, final PrintStream pStream) throws IOException {
- BufferedReader in =
- new BufferedReader(new InputStreamReader(inStream));
- int i;
- try {
- while ((i = in.read()) != -1) {
- // directly copy input to output, but skip if asked to close
- if (active) {
- pStream.print((char) i);
- }
- }
- } catch (IOException ex) {
- String s = ex.getMessage();
- if (active && !s.startsWith("Bad file number")) {
- throw ex;
- }
- // else we are being shutdown (and don't want any spurious death
- // throws to ripple) or
- // we got a Bad file number IOException which just means
- // that the debuggee has gone away. We'll just treat it the
- // same as if we got an EOF.
- }
- }
-
- /**
- * Create a Thread that will retrieve and display any output.
- * Needs to be high priority, else debugger may exit before
- * it can be displayed.
- */
- private void displayRemoteOutput(final InputStream inStream, final PrintStream pStream) {
- Thread thr = new Thread("output reader") {
- @Override
- public void run() {
- try {
- dumpStream(inStream, pStream);
- } catch (IOException ex) {
- ec.debug(ex, "Failed reading output");
- ec.handleVMExit();
- } finally {
- notifyOutputComplete();
- }
- }
- };
- thr.setPriority(Thread.MAX_PRIORITY-1);
- thr.start();
- }
-
- /**
- * Create a Thread that will ship all input to remote.
- * Does it need be high priority?
- */
- private void readRemoteInput(final OutputStream outStream, final InputStream inputStream) {
- Thread thr = new Thread("input reader") {
- @Override
- public void run() {
- try {
- byte[] buf = new byte[256];
- int cnt;
- while ((cnt = inputStream.read(buf)) != -1) {
- outStream.write(buf, 0, cnt);
- outStream.flush();
- }
- } catch (IOException ex) {
- ec.debug(ex, "Failed reading output");
- ec.handleVMExit();
- }
- }
- };
- thr.setPriority(Thread.MAX_PRIORITY-1);
- thr.start();
- }
-
- private void forwardIO() {
- displayRemoteOutput(process.getErrorStream(), ec.execEnv.userErr());
- displayRemoteOutput(process.getInputStream(), ec.execEnv.userOut());
- readRemoteInput(process.getOutputStream(), ec.execEnv.userIn());
- }
-
- /* launch child target vm */
- private VirtualMachine launchTarget() {
- LaunchingConnector launcher = (LaunchingConnector)connector;
- try {
- VirtualMachine new_vm = launcher.launch(connectorArgs);
- process = new_vm.process();
- forwardIO();
- return new_vm;
- } catch (Exception ex) {
- reportLaunchFail(ex, "launch");
- }
- return null;
- }
-
- /**
- * Directly launch the remote agent and connect JDI to it with a
- * ListeningConnector.
- */
- private VirtualMachine listenTarget(int port, List<String> remoteVMOptions) {
- ListeningConnector listener = (ListeningConnector) connector;
- try {
- // Start listening, get the JDI connection address
- String addr = listener.startListening(connectorArgs);
- ec.debug(DBG_GEN, "Listening at address: " + addr);
-
- // Launch the RemoteAgent requesting a connection on that address
- String javaHome = System.getProperty("java.home");
- List<String> args = new ArrayList<>();
- args.add(javaHome == null
- ? "java"
- : javaHome + File.separator + "bin" + File.separator + "java");
- args.add("-agentlib:jdwp=transport=" + connector.transport().name() +
- ",address=" + addr);
- args.addAll(remoteVMOptions);
- args.add(REMOTE_AGENT);
- args.add("" + port);
- ProcessBuilder pb = new ProcessBuilder(args);
- process = pb.start();
-
- // Forward out, err, and in
- forwardIO();
-
- // Accept the connection from the remote agent
- vm = listener.accept(connectorArgs);
- listener.stopListening(connectorArgs);
- return vm;
- } catch (Exception ex) {
- reportLaunchFail(ex, "listen");
- }
- return null;
- }
-
- private void reportLaunchFail(Exception ex, String context) {
- throw new InternalError("Failed remote " + context + ": " + connector +
- " -- " + connectorArgs, ex);
- }
-}
\ No newline at end of file
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/jdi/JDIEventHandler.java Thu Jul 21 16:45:56 2016 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,182 +0,0 @@
-/*
- * Copyright (c) 1998, 2014, 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.internal.jshell.jdi;
-
-import java.util.function.Consumer;
-import com.sun.jdi.*;
-import com.sun.jdi.event.*;
-
-/**
- * Handler of Java Debug Interface events.
- * Adapted from jdb EventHandler; Handling of events not used by JShell stubbed out.
- */
-class JDIEventHandler implements Runnable {
-
- private final Thread thread;
- private volatile boolean connected = true;
- private boolean completed = false;
- private final VirtualMachine vm;
- private final Consumer<Boolean> reportVMExit;
-
- JDIEventHandler(VirtualMachine vm, Consumer<Boolean> reportVMExit) {
- this.vm = vm;
- this.reportVMExit = reportVMExit;
- this.thread = new Thread(this, "event-handler");
- }
-
- void start() {
- thread.start();
- }
-
- synchronized void shutdown() {
- connected = false; // force run() loop termination
- thread.interrupt();
- while (!completed) {
- try {wait();} catch (InterruptedException exc) {}
- }
- }
-
- @Override
- public void run() {
- EventQueue queue = vm.eventQueue();
- while (connected) {
- try {
- EventSet eventSet = queue.remove();
- boolean resumeStoppedApp = false;
- EventIterator it = eventSet.eventIterator();
- while (it.hasNext()) {
- resumeStoppedApp |= handleEvent(it.nextEvent());
- }
-
- if (resumeStoppedApp) {
- eventSet.resume();
- }
- } catch (InterruptedException exc) {
- // Do nothing. Any changes will be seen at top of loop.
- } catch (VMDisconnectedException discExc) {
- handleDisconnectedException();
- break;
- }
- }
- synchronized (this) {
- completed = true;
- notifyAll();
- }
- }
-
- private boolean handleEvent(Event event) {
- if (event instanceof ExceptionEvent) {
- exceptionEvent(event);
- } else if (event instanceof WatchpointEvent) {
- fieldWatchEvent(event);
- } else if (event instanceof MethodEntryEvent) {
- methodEntryEvent(event);
- } else if (event instanceof MethodExitEvent) {
- methodExitEvent(event);
- } else if (event instanceof ClassPrepareEvent) {
- classPrepareEvent(event);
- } else if (event instanceof ThreadStartEvent) {
- threadStartEvent(event);
- } else if (event instanceof ThreadDeathEvent) {
- threadDeathEvent(event);
- } else if (event instanceof VMStartEvent) {
- vmStartEvent(event);
- return true;
- } else {
- handleExitEvent(event);
- }
- return true;
- }
-
- private boolean vmDied = false;
-
- private void handleExitEvent(Event event) {
- if (event instanceof VMDeathEvent) {
- vmDied = true;
- } else if (event instanceof VMDisconnectEvent) {
- connected = false;
- } else {
- throw new InternalError("Unexpected event type: " +
- event.getClass());
- }
- reportVMExit.accept(vmDied);
- }
-
- private synchronized void handleDisconnectedException() {
- /*
- * A VMDisconnectedException has happened while dealing with
- * another event. We need to flush the event queue, dealing only
- * with exit events (VMDeath, VMDisconnect) so that we terminate
- * correctly.
- */
- EventQueue queue = vm.eventQueue();
- while (connected) {
- try {
- EventSet eventSet = queue.remove();
- EventIterator iter = eventSet.eventIterator();
- while (iter.hasNext()) {
- handleExitEvent(iter.next());
- }
- } catch (InterruptedException exc) {
- // ignore
- } catch (InternalError exc) {
- // ignore
- }
- }
- }
-
- private void vmStartEvent(Event event) {
- VMStartEvent se = (VMStartEvent)event;
- }
-
- private void methodEntryEvent(Event event) {
- MethodEntryEvent me = (MethodEntryEvent)event;
- }
-
- private void methodExitEvent(Event event) {
- MethodExitEvent me = (MethodExitEvent)event;
- }
-
- private void fieldWatchEvent(Event event) {
- WatchpointEvent fwe = (WatchpointEvent)event;
- }
-
- private void classPrepareEvent(Event event) {
- ClassPrepareEvent cle = (ClassPrepareEvent)event;
- }
-
- private void exceptionEvent(Event event) {
- ExceptionEvent ee = (ExceptionEvent)event;
- }
-
- private void threadDeathEvent(Event event) {
- ThreadDeathEvent tee = (ThreadDeathEvent)event;
- }
-
- private void threadStartEvent(Event event) {
- ThreadStartEvent tse = (ThreadStartEvent)event;
- }
-}
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/jdi/JDIExecutionControl.java Thu Jul 21 16:45:56 2016 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,598 +0,0 @@
-/*
- * Copyright (c) 2014, 2015, 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.internal.jshell.jdi;
-
-import static jdk.internal.jshell.remote.RemoteCodes.*;
-import java.io.DataInputStream;
-import java.io.InputStream;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.PrintStream;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.io.EOFException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import com.sun.jdi.BooleanValue;
-import com.sun.jdi.ClassNotLoadedException;
-import com.sun.jdi.IncompatibleThreadStateException;
-import com.sun.jdi.InvalidTypeException;
-import com.sun.jdi.ObjectReference;
-import com.sun.jdi.ReferenceType;
-import com.sun.jdi.StackFrame;
-import com.sun.jdi.ThreadReference;
-import com.sun.jdi.VirtualMachine;
-import static java.util.stream.Collectors.toList;
-import jdk.jshell.JShellException;
-import jdk.jshell.spi.ExecutionControl;
-import jdk.jshell.spi.ExecutionEnv;
-import jdk.internal.jshell.jdi.ClassTracker.ClassInfo;
-import static java.util.stream.Collectors.toMap;
-import jdk.internal.jshell.debug.InternalDebugControl;
-import static jdk.internal.jshell.debug.InternalDebugControl.DBG_GEN;
-
-/**
- * Controls the remote execution environment.
- * Interfaces to the JShell-core by implementing ExecutionControl SPI.
- * Interfaces to RemoteAgent over a socket and via JDI.
- * Launches a remote process.
- */
-public class JDIExecutionControl implements ExecutionControl {
-
- ExecutionEnv execEnv;
- private final boolean isLaunch;
- private JDIConnection connection;
- private ClassTracker classTracker;
- private Socket socket;
- private ObjectInputStream remoteIn;
- private ObjectOutputStream remoteOut;
-
- /**
- * Creates an ExecutionControl instance based on JDI.
- *
- * @param isLaunch true for LaunchingConnector; false for ListeningConnector
- */
- public JDIExecutionControl(boolean isLaunch) {
- this.isLaunch = isLaunch;
- }
-
- /**
- * Creates an ExecutionControl instance based on a JDI LaunchingConnector.
- */
- public JDIExecutionControl() {
- this.isLaunch = true;
- }
-
- /**
- * Initializes the launching JDI execution engine. Initialize JDI and use it
- * to launch the remote JVM. Set-up control and result communications socket
- * to the remote execution environment. This socket also transports the
- * input/output channels.
- *
- * @param execEnv the execution environment provided by the JShell-core
- * @throws IOException
- */
- @Override
- public void start(ExecutionEnv execEnv) throws IOException {
- this.execEnv = execEnv;
- StringBuilder sb = new StringBuilder();
- try (ServerSocket listener = new ServerSocket(0)) {
- // timeout after 60 seconds
- listener.setSoTimeout(60000);
- int port = listener.getLocalPort();
- connection = new JDIConnection(this, port, execEnv.extraRemoteVMOptions(), isLaunch);
- this.socket = listener.accept();
- // out before in -- match remote creation so we don't hang
- this.remoteOut = new ObjectOutputStream(socket.getOutputStream());
- PipeInputStream commandIn = new PipeInputStream();
- new DemultiplexInput(socket.getInputStream(), commandIn, execEnv.userOut(), execEnv.userErr()).start();
- this.remoteIn = new ObjectInputStream(commandIn);
- }
- }
-
- /**
- * Closes the execution engine. Send an exit command to the remote agent.
- * Shuts down the JDI connection. Should this close the socket?
- */
- @Override
- public void close() {
- try {
- if (connection != null) {
- connection.beginShutdown();
- }
- if (remoteOut != null) {
- remoteOut.writeInt(CMD_EXIT);
- remoteOut.flush();
- }
- if (connection != null) {
- connection.disposeVM();
- }
- } catch (IOException ex) {
- debug(DBG_GEN, "Exception on JDI exit: %s\n", ex);
- }
- }
-
- /**
- * Loads the list of classes specified. Sends a load command to the remote
- * agent with pairs of classname/bytes.
- *
- * @param classes the names of the wrapper classes to loaded
- * @return true if all classes loaded successfully
- */
- @Override
- public boolean load(Collection<String> classes) {
- try {
- // Create corresponding ClassInfo instances to track the classes.
- // Each ClassInfo has the current class bytes associated with it.
- List<ClassInfo> infos = withBytes(classes);
- // Send a load command to the remote agent.
- remoteOut.writeInt(CMD_LOAD);
- remoteOut.writeInt(classes.size());
- for (ClassInfo ci : infos) {
- remoteOut.writeUTF(ci.getClassName());
- remoteOut.writeObject(ci.getBytes());
- }
- remoteOut.flush();
- // Retrieve and report results from the remote agent.
- boolean result = readAndReportResult();
- // For each class that now has a JDI ReferenceType, mark the bytes
- // as loaded.
- infos.stream()
- .filter(ci -> ci.getReferenceTypeOrNull() != null)
- .forEach(ci -> ci.markLoaded());
- return result;
- } catch (IOException ex) {
- debug(DBG_GEN, "IOException on remote load operation: %s\n", ex);
- return false;
- }
- }
-
- /**
- * Invoke the doit method on the specified class.
- *
- * @param classname name of the wrapper class whose doit should be invoked
- * @return return the result value of the doit
- * @throws JShellException if a user exception was thrown (EvalException) or
- * an unresolved reference was encountered (UnresolvedReferenceException)
- */
- @Override
- public String invoke(String classname, String methodname) throws JShellException {
- try {
- synchronized (STOP_LOCK) {
- userCodeRunning = true;
- }
- // Send the invoke command to the remote agent.
- remoteOut.writeInt(CMD_INVOKE);
- remoteOut.writeUTF(classname);
- remoteOut.writeUTF(methodname);
- remoteOut.flush();
- // Retrieve and report results from the remote agent.
- if (readAndReportExecutionResult()) {
- String result = remoteIn.readUTF();
- return result;
- }
- } catch (IOException | RuntimeException ex) {
- if (!connection.isRunning()) {
- // The JDI connection is no longer live, shutdown.
- handleVMExit();
- } else {
- debug(DBG_GEN, "Exception on remote invoke: %s\n", ex);
- return "Execution failure: " + ex.getMessage();
- }
- } finally {
- synchronized (STOP_LOCK) {
- userCodeRunning = false;
- }
- }
- return "";
- }
-
- /**
- * Retrieves the value of a JShell variable.
- *
- * @param classname name of the wrapper class holding the variable
- * @param varname name of the variable
- * @return the value as a String
- */
- @Override
- public String varValue(String classname, String varname) {
- try {
- // Send the variable-value command to the remote agent.
- remoteOut.writeInt(CMD_VARVALUE);
- remoteOut.writeUTF(classname);
- remoteOut.writeUTF(varname);
- remoteOut.flush();
- // Retrieve and report results from the remote agent.
- if (readAndReportResult()) {
- String result = remoteIn.readUTF();
- return result;
- }
- } catch (EOFException ex) {
- handleVMExit();
- } catch (IOException ex) {
- debug(DBG_GEN, "Exception on remote var value: %s\n", ex);
- return "Execution failure: " + ex.getMessage();
- }
- return "";
- }
-
- /**
- * Adds a path to the remote classpath.
- *
- * @param cp the additional path element
- * @return true if succesful
- */
- @Override
- public boolean addToClasspath(String cp) {
- try {
- // Send the classpath addition command to the remote agent.
- remoteOut.writeInt(CMD_CLASSPATH);
- remoteOut.writeUTF(cp);
- remoteOut.flush();
- // Retrieve and report results from the remote agent.
- return readAndReportResult();
- } catch (IOException ex) {
- throw new InternalError("Classpath addition failed: " + cp, ex);
- }
- }
-
- /**
- * Redefine the specified classes. Where 'redefine' is, as in JDI and JVMTI,
- * an in-place replacement of the classes (preserving class identity) --
- * that is, existing references to the class do not need to be recompiled.
- * This implementation uses JDI redefineClasses. It will be unsuccessful if
- * the signature of the class has changed (see the JDI spec). The
- * JShell-core is designed to adapt to unsuccessful redefine.
- *
- * @param classes the names of the classes to redefine
- * @return true if all the classes were redefined
- */
- @Override
- public boolean redefine(Collection<String> classes) {
- try {
- // Create corresponding ClassInfo instances to track the classes.
- // Each ClassInfo has the current class bytes associated with it.
- List<ClassInfo> infos = withBytes(classes);
- // Convert to the JDI ReferenceType to class bytes map form needed
- // by JDI.
- Map<ReferenceType, byte[]> rmp = infos.stream()
- .collect(toMap(
- ci -> ci.getReferenceTypeOrNull(),
- ci -> ci.getBytes()));
- // Attempt redefine. Throws exceptions on failure.
- connection.vm().redefineClasses(rmp);
- // Successful: mark the bytes as loaded.
- infos.stream()
- .forEach(ci -> ci.markLoaded());
- return true;
- } catch (UnsupportedOperationException ex) {
- // A form of class transformation not supported by JDI
- return false;
- } catch (Exception ex) {
- debug(DBG_GEN, "Exception on JDI redefine: %s\n", ex);
- return false;
- }
- }
-
- // the VM has gone down in flames or because user evaled System.exit() or the like
- void handleVMExit() {
- if (connection != null) {
- // If there is anything left dispose of it
- connection.disposeVM();
- }
- // Tell JShell-core that the VM has died
- execEnv.closeDown();
- }
-
- // Lazy init class tracker
- private ClassTracker classTracker() {
- if (classTracker == null) {
- classTracker = new ClassTracker(connection.vm());
- }
- return classTracker;
- }
-
- /**
- * Converts a collection of class names into ClassInfo instances associated
- * with the most recently compiled class bytes.
- *
- * @param classes names of the classes
- * @return a list of corresponding ClassInfo instances
- */
- private List<ClassInfo> withBytes(Collection<String> classes) {
- return classes.stream()
- .map(cn -> classTracker().classInfo(cn, execEnv.getClassBytes(cn)))
- .collect(toList());
- }
-
- /**
- * Reports the status of the named class. UNKNOWN if not loaded. CURRENT if
- * the most recent successfully loaded/redefined bytes match the current
- * compiled bytes.
- *
- * @param classname the name of the class to test
- * @return the status
- */
- @Override
- public ClassStatus getClassStatus(String classname) {
- ClassInfo ci = classTracker().get(classname);
- if (ci.getReferenceTypeOrNull() == null) {
- // If the class does not have a JDI ReferenceType it has not been loaded
- return ClassStatus.UNKNOWN;
- }
- // Compare successfully loaded with last compiled bytes.
- return (Arrays.equals(execEnv.getClassBytes(classname), ci.getLoadedBytes()))
- ? ClassStatus.CURRENT
- : ClassStatus.NOT_CURRENT;
- }
-
- /**
- * Reports results from a remote agent command that does not expect
- * exceptions.
- *
- * @return true if successful
- * @throws IOException if the connection has dropped
- */
- private boolean readAndReportResult() throws IOException {
- int ok = remoteIn.readInt();
- switch (ok) {
- case RESULT_SUCCESS:
- return true;
- case RESULT_FAIL: {
- String ex = remoteIn.readUTF();
- debug(DBG_GEN, "Exception on remote operation: %s\n", ex);
- return false;
- }
- default: {
- debug(DBG_GEN, "Bad remote result code: %s\n", ok);
- return false;
- }
- }
- }
-
- /**
- * Reports results from a remote agent command that expects runtime
- * exceptions.
- *
- * @return true if successful
- * @throws IOException if the connection has dropped
- * @throws EvalException if a user exception was encountered on invoke
- * @throws UnresolvedReferenceException if an unresolved reference was
- * encountered
- */
- private boolean readAndReportExecutionResult() throws IOException, JShellException {
- int ok = remoteIn.readInt();
- switch (ok) {
- case RESULT_SUCCESS:
- return true;
- case RESULT_FAIL: {
- // An internal error has occurred.
- String ex = remoteIn.readUTF();
- return false;
- }
- case RESULT_EXCEPTION: {
- // A user exception was encountered.
- String exceptionClassName = remoteIn.readUTF();
- String message = remoteIn.readUTF();
- StackTraceElement[] elems = readStackTrace();
- throw execEnv.createEvalException(message, exceptionClassName, elems);
- }
- case RESULT_CORRALLED: {
- // An unresolved reference was encountered.
- int id = remoteIn.readInt();
- StackTraceElement[] elems = readStackTrace();
- throw execEnv.createUnresolvedReferenceException(id, elems);
- }
- case RESULT_KILLED: {
- // Execution was aborted by the stop()
- debug(DBG_GEN, "Killed.");
- return false;
- }
- default: {
- debug(DBG_GEN, "Bad remote result code: %s\n", ok);
- return false;
- }
- }
- }
-
- private StackTraceElement[] readStackTrace() throws IOException {
- int elemCount = remoteIn.readInt();
- StackTraceElement[] elems = new StackTraceElement[elemCount];
- for (int i = 0; i < elemCount; ++i) {
- String className = remoteIn.readUTF();
- String methodName = remoteIn.readUTF();
- String fileName = remoteIn.readUTF();
- int line = remoteIn.readInt();
- elems[i] = new StackTraceElement(className, methodName, fileName, line);
- }
- return elems;
- }
-
- private final Object STOP_LOCK = new Object();
- private boolean userCodeRunning = false;
-
- /**
- * Interrupt a running invoke.
- */
- @Override
- public void stop() {
- synchronized (STOP_LOCK) {
- if (!userCodeRunning) {
- return;
- }
-
- VirtualMachine vm = connection.vm();
- vm.suspend();
- try {
- OUTER:
- for (ThreadReference thread : vm.allThreads()) {
- // could also tag the thread (e.g. using name), to find it easier
- for (StackFrame frame : thread.frames()) {
- String remoteAgentName = "jdk.internal.jshell.remote.RemoteAgent";
- if (remoteAgentName.equals(frame.location().declaringType().name())
- && "commandLoop".equals(frame.location().method().name())) {
- ObjectReference thiz = frame.thisObject();
- if (((BooleanValue) thiz.getValue(thiz.referenceType().fieldByName("inClientCode"))).value()) {
- thiz.setValue(thiz.referenceType().fieldByName("expectingStop"), vm.mirrorOf(true));
- ObjectReference stopInstance = (ObjectReference) thiz.getValue(thiz.referenceType().fieldByName("stopException"));
-
- vm.resume();
- debug(DBG_GEN, "Attempting to stop the client code...\n");
- thread.stop(stopInstance);
- thiz.setValue(thiz.referenceType().fieldByName("expectingStop"), vm.mirrorOf(false));
- }
-
- break OUTER;
- }
- }
- }
- } catch (ClassNotLoadedException | IncompatibleThreadStateException | InvalidTypeException ex) {
- debug(DBG_GEN, "Exception on remote stop: %s\n", ex);
- } finally {
- vm.resume();
- }
- }
- }
-
- void debug(int flags, String format, Object... args) {
- InternalDebugControl.debug(execEnv.state(), execEnv.userErr(), flags, format, args);
- }
-
- void debug(Exception ex, String where) {
- InternalDebugControl.debug(execEnv.state(), execEnv.userErr(), ex, where);
- }
-
- private final class DemultiplexInput extends Thread {
-
- private final DataInputStream delegate;
- private final PipeInputStream command;
- private final PrintStream out;
- private final PrintStream err;
-
- public DemultiplexInput(InputStream input,
- PipeInputStream command,
- PrintStream out,
- PrintStream err) {
- super("output reader");
- this.delegate = new DataInputStream(input);
- this.command = command;
- this.out = out;
- this.err = err;
- }
-
- public void run() {
- try {
- while (true) {
- int nameLen = delegate.read();
- if (nameLen == (-1))
- break;
- byte[] name = new byte[nameLen];
- DemultiplexInput.this.delegate.readFully(name);
- int dataLen = delegate.read();
- byte[] data = new byte[dataLen];
- DemultiplexInput.this.delegate.readFully(data);
- switch (new String(name, "UTF-8")) {
- case "err":
- err.write(data);
- break;
- case "out":
- out.write(data);
- break;
- case "command":
- for (byte b : data) {
- command.write(Byte.toUnsignedInt(b));
- }
- break;
- }
- }
- } catch (IOException ex) {
- debug(ex, "Failed reading output");
- } finally {
- command.close();
- }
- }
-
- }
-
- public static final class PipeInputStream extends InputStream {
- public static final int INITIAL_SIZE = 128;
-
- private int[] buffer = new int[INITIAL_SIZE];
- private int start;
- private int end;
- private boolean closed;
-
- @Override
- public synchronized int read() {
- while (start == end) {
- if (closed) {
- return -1;
- }
- try {
- wait();
- } catch (InterruptedException ex) {
- //ignore
- }
- }
- try {
- return buffer[start];
- } finally {
- start = (start + 1) % buffer.length;
- }
- }
-
- public synchronized void write(int b) {
- if (closed)
- throw new IllegalStateException("Already closed.");
- int newEnd = (end + 1) % buffer.length;
- if (newEnd == start) {
- //overflow:
- int[] newBuffer = new int[buffer.length * 2];
- int rightPart = (end > start ? end : buffer.length) - start;
- int leftPart = end > start ? 0 : start - 1;
- System.arraycopy(buffer, start, newBuffer, 0, rightPart);
- System.arraycopy(buffer, 0, newBuffer, rightPart, leftPart);
- buffer = newBuffer;
- start = 0;
- end = rightPart + leftPart;
- newEnd = end + 1;
- }
- buffer[end] = b;
- end = newEnd;
- notifyAll();
- }
-
- @Override
- public synchronized void close() {
- closed = true;
- notifyAll();
- }
-
- }
-}
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/jdi/JDINotConnectedException.java Thu Jul 21 16:45:56 2016 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-/*
- * Copyright (c) 1999, 2015, 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.internal.jshell.jdi;
-
-/**
- * Internal exception when Java Debug Interface VirtualMacine is not connected.
- * Copy of jdb VMNotConnectedException.
- */
-class JDINotConnectedException extends RuntimeException {
-
- private static final long serialVersionUID = -7433430494903950165L;
-
- public JDINotConnectedException() {
- super();
- }
-
- public JDINotConnectedException(String s) {
- super(s);
- }
-}
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/remote/RemoteAgent.java Thu Jul 21 16:45:56 2016 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,326 +0,0 @@
-/*
- * Copyright (c) 2014, 2015, 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.internal.jshell.remote;
-import jdk.jshell.spi.SPIResolutionException;
-import java.io.File;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.io.UnsupportedEncodingException;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.net.Socket;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static jdk.internal.jshell.remote.RemoteCodes.*;
-
-import java.util.Map;
-import java.util.TreeMap;
-
-/**
- * The remote agent runs in the execution process (separate from the main JShell
- * process. This agent loads code over a socket from the main JShell process,
- * executes the code, and other misc,
- * @author Robert Field
- */
-class RemoteAgent {
-
- private final RemoteClassLoader loader = new RemoteClassLoader();
- private final Map<String, Class<?>> klasses = new TreeMap<>();
-
- public static void main(String[] args) throws Exception {
- String loopBack = null;
- Socket socket = new Socket(loopBack, Integer.parseInt(args[0]));
- (new RemoteAgent()).commandLoop(socket);
- }
-
- void commandLoop(Socket socket) throws IOException {
- // in before out -- so we don't hang the controlling process
- ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
- OutputStream socketOut = socket.getOutputStream();
- System.setOut(new PrintStream(new MultiplexingOutputStream("out", socketOut), true));
- System.setErr(new PrintStream(new MultiplexingOutputStream("err", socketOut), true));
- ObjectOutputStream out = new ObjectOutputStream(new MultiplexingOutputStream("command", socketOut));
- while (true) {
- int cmd = in.readInt();
- switch (cmd) {
- case CMD_EXIT:
- // Terminate this process
- return;
- case CMD_LOAD:
- // Load a generated class file over the wire
- try {
- int count = in.readInt();
- List<String> names = new ArrayList<>(count);
- for (int i = 0; i < count; ++i) {
- String name = in.readUTF();
- byte[] kb = (byte[]) in.readObject();
- loader.delare(name, kb);
- names.add(name);
- }
- for (String name : names) {
- Class<?> klass = loader.loadClass(name);
- klasses.put(name, klass);
- // Get class loaded to the point of, at least, preparation
- klass.getDeclaredMethods();
- }
- out.writeInt(RESULT_SUCCESS);
- out.flush();
- } catch (IOException | ClassNotFoundException | ClassCastException ex) {
- debug("*** Load failure: %s\n", ex);
- out.writeInt(RESULT_FAIL);
- out.writeUTF(ex.toString());
- out.flush();
- }
- break;
- case CMD_INVOKE: {
- // Invoke executable entry point in loaded code
- String name = in.readUTF();
- Class<?> klass = klasses.get(name);
- if (klass == null) {
- debug("*** Invoke failure: no such class loaded %s\n", name);
- out.writeInt(RESULT_FAIL);
- out.writeUTF("no such class loaded: " + name);
- out.flush();
- break;
- }
- String methodName = in.readUTF();
- Method doitMethod;
- try {
- this.getClass().getModule().addExports(SPIResolutionException.class.getPackage().getName(), klass.getModule());
- doitMethod = klass.getDeclaredMethod(methodName, new Class<?>[0]);
- doitMethod.setAccessible(true);
- Object res;
- try {
- clientCodeEnter();
- res = doitMethod.invoke(null, new Object[0]);
- } catch (InvocationTargetException ex) {
- if (ex.getCause() instanceof StopExecutionException) {
- expectingStop = false;
- throw (StopExecutionException) ex.getCause();
- }
- throw ex;
- } catch (StopExecutionException ex) {
- expectingStop = false;
- throw ex;
- } finally {
- clientCodeLeave();
- }
- out.writeInt(RESULT_SUCCESS);
- out.writeUTF(valueString(res));
- out.flush();
- } catch (InvocationTargetException ex) {
- Throwable cause = ex.getCause();
- StackTraceElement[] elems = cause.getStackTrace();
- if (cause instanceof SPIResolutionException) {
- out.writeInt(RESULT_CORRALLED);
- out.writeInt(((SPIResolutionException) cause).id());
- } else {
- out.writeInt(RESULT_EXCEPTION);
- out.writeUTF(cause.getClass().getName());
- out.writeUTF(cause.getMessage() == null ? "<none>" : cause.getMessage());
- }
- out.writeInt(elems.length);
- for (StackTraceElement ste : elems) {
- out.writeUTF(ste.getClassName());
- out.writeUTF(ste.getMethodName());
- out.writeUTF(ste.getFileName() == null ? "<none>" : ste.getFileName());
- out.writeInt(ste.getLineNumber());
- }
- out.flush();
- } catch (NoSuchMethodException | IllegalAccessException ex) {
- debug("*** Invoke failure: %s -- %s\n", ex, ex.getCause());
- out.writeInt(RESULT_FAIL);
- out.writeUTF(ex.toString());
- out.flush();
- } catch (StopExecutionException ex) {
- try {
- out.writeInt(RESULT_KILLED);
- out.flush();
- } catch (IOException err) {
- debug("*** Error writing killed result: %s -- %s\n", ex, ex.getCause());
- }
- }
- System.out.flush();
- break;
- }
- case CMD_VARVALUE: {
- // Retrieve a variable value
- String classname = in.readUTF();
- String varname = in.readUTF();
- Class<?> klass = klasses.get(classname);
- if (klass == null) {
- debug("*** Var value failure: no such class loaded %s\n", classname);
- out.writeInt(RESULT_FAIL);
- out.writeUTF("no such class loaded: " + classname);
- out.flush();
- break;
- }
- try {
- Field var = klass.getDeclaredField(varname);
- var.setAccessible(true);
- Object res = var.get(null);
- out.writeInt(RESULT_SUCCESS);
- out.writeUTF(valueString(res));
- out.flush();
- } catch (Exception ex) {
- debug("*** Var value failure: no such field %s.%s\n", classname, varname);
- out.writeInt(RESULT_FAIL);
- out.writeUTF("no such field loaded: " + varname + " in class: " + classname);
- out.flush();
- }
- break;
- }
- case CMD_CLASSPATH: {
- // Append to the claspath
- String cp = in.readUTF();
- for (String path : cp.split(File.pathSeparator)) {
- loader.addURL(new File(path).toURI().toURL());
- }
- out.writeInt(RESULT_SUCCESS);
- out.flush();
- break;
- }
- default:
- debug("*** Bad command code: %d\n", cmd);
- break;
- }
- }
- }
-
- // These three variables are used by the main JShell process in interrupting
- // the running process. Access is via JDI, so the reference is not visible
- // to code inspection.
- private boolean inClientCode; // Queried by the main process
- private boolean expectingStop; // Set by the main process
-
- // thrown by the main process via JDI:
- private final StopExecutionException stopException = new StopExecutionException();
-
- @SuppressWarnings("serial") // serialVersionUID intentionally omitted
- private class StopExecutionException extends ThreadDeath {
- @Override public synchronized Throwable fillInStackTrace() {
- return this;
- }
- }
-
- void clientCodeEnter() {
- expectingStop = false;
- inClientCode = true;
- }
-
- void clientCodeLeave() {
- inClientCode = false;
- while (expectingStop) {
- try {
- Thread.sleep(0);
- } catch (InterruptedException ex) {
- debug("*** Sleep interrupted while waiting for stop exception: %s\n", ex);
- }
- }
- }
-
- private void debug(String format, Object... args) {
- System.err.printf("REMOTE: "+format, args);
- }
-
- static String valueString(Object value) {
- if (value == null) {
- return "null";
- } else if (value instanceof String) {
- return "\"" + (String)value + "\"";
- } else if (value instanceof Character) {
- return "'" + value + "'";
- } else {
- return value.toString();
- }
- }
-
- private static final class MultiplexingOutputStream extends OutputStream {
-
- private static final int PACKET_SIZE = 127;
-
- private final byte[] name;
- private final OutputStream delegate;
-
- public MultiplexingOutputStream(String name, OutputStream delegate) {
- try {
- this.name = name.getBytes("UTF-8");
- this.delegate = delegate;
- } catch (UnsupportedEncodingException ex) {
- throw new IllegalStateException(ex); //should not happen
- }
- }
-
- @Override
- public void write(int b) throws IOException {
- synchronized (delegate) {
- delegate.write(name.length); //assuming the len is small enough to fit into byte
- delegate.write(name);
- delegate.write(1);
- delegate.write(b);
- delegate.flush();
- }
- }
-
- @Override
- public void write(byte[] b, int off, int len) throws IOException {
- synchronized (delegate) {
- int i = 0;
- while (len > 0) {
- int size = Math.min(PACKET_SIZE, len);
-
- delegate.write(name.length); //assuming the len is small enough to fit into byte
- delegate.write(name);
- delegate.write(size);
- delegate.write(b, off + i, size);
- i += size;
- len -= size;
- }
-
- delegate.flush();
- }
- }
-
- @Override
- public void flush() throws IOException {
- super.flush();
- delegate.flush();
- }
-
- @Override
- public void close() throws IOException {
- super.close();
- delegate.close();
- }
-
- }
-}
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/remote/RemoteClassLoader.java Thu Jul 21 16:45:56 2016 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * Copyright (c) 2014, 2015, 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.internal.jshell.remote;
-
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.security.CodeSource;
-import java.util.Map;
-import java.util.TreeMap;
-
-/**
- * Class loader wrapper which caches class files by name until requested.
- * @author Robert Field
- */
-class RemoteClassLoader extends URLClassLoader {
-
- private final Map<String, byte[]> classObjects = new TreeMap<>();
-
- RemoteClassLoader() {
- super(new URL[0]);
- }
-
- void delare(String name, byte[] bytes) {
- classObjects.put(name, bytes);
- }
-
- @Override
- protected Class<?> findClass(String name) throws ClassNotFoundException {
- byte[] b = classObjects.get(name);
- if (b == null) {
- return super.findClass(name);
- }
- return super.defineClass(name, b, 0, b.length, (CodeSource) null);
- }
-
- @Override
- public void addURL(URL url) {
- super.addURL(url);
- }
-
-}
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/remote/RemoteCodes.java Thu Jul 21 16:45:56 2016 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-/*
- * Copyright (c) 2014, 2015, 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.internal.jshell.remote;
-
-/**
- * Communication constants shared between the main process and the remote
- * execution process
- * @author Robert Field
- */
-public class RemoteCodes {
- // Command codes
- public static final int CMD_EXIT = 0;
- public static final int CMD_LOAD = 1;
- public static final int CMD_INVOKE = 3;
- public static final int CMD_CLASSPATH = 4;
- public static final int CMD_VARVALUE = 5;
-
- // Return result codes
- public static final int RESULT_SUCCESS = 100;
- public static final int RESULT_FAIL = 101;
- public static final int RESULT_EXCEPTION = 102;
- public static final int RESULT_CORRALLED = 103;
- public static final int RESULT_KILLED = 104;
-}
--- a/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java Thu Jul 21 20:09:19 2016 -0700
@@ -233,6 +233,7 @@
private static final String DOCUMENTATION_SHORTCUT = "\033\133\132"; //Shift-TAB
private static final String[] SHORTCUT_FIXES = {
"\033\015", //Alt-Enter (Linux)
+ "\033\012", //Alt-Enter (Linux)
"\033\133\061\067\176", //F6/Alt-F1 (Mac)
"\u001BO3P" //Alt-F1 (Linux)
};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/ClassTracker.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.jshell;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Objects;
+import jdk.jshell.spi.ExecutionControl.ClassBytecodes;
+
+/**
+ * Tracks the state of a class.
+ */
+class ClassTracker {
+
+ private final HashMap<String, ClassInfo> map;
+
+ ClassTracker() {
+ this.map = new HashMap<>();
+ }
+
+ /**
+ * Associates a class name, class bytes (current and loaded).
+ */
+ class ClassInfo {
+
+ // The name of the class
+ private final String className;
+
+ // The corresponding compiled class bytes when a load or redefine
+ // is started. May not be the loaded bytes. May be null.
+ private byte[] currentBytes;
+
+ // The class bytes successfully loaded/redefined into the remote VM.
+ private byte[] loadedBytes;
+
+ private ClassInfo(String className) {
+ this.className = className;
+ }
+
+ String getClassName() {
+ return className;
+ }
+
+ byte[] getLoadedBytes() {
+ return loadedBytes;
+ }
+
+ byte[] getCurrentBytes() {
+ return currentBytes;
+ }
+
+ void setCurrentBytes(byte[] bytes) {
+ this.currentBytes = bytes;
+ }
+
+ void setLoadedBytes(byte[] bytes) {
+ this.loadedBytes = bytes;
+ }
+
+ boolean isLoaded() {
+ return loadedBytes != null;
+ }
+
+ boolean isCurrent() {
+ return Arrays.equals(currentBytes, loadedBytes);
+ }
+
+ ClassBytecodes toClassBytecodes() {
+ return new ClassBytecodes(className, currentBytes);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof ClassInfo
+ && ((ClassInfo) o).className.equals(className);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(this.className);
+ }
+ }
+
+ void markLoaded(ClassBytecodes[] cbcs) {
+ for (ClassBytecodes cbc : cbcs) {
+ get(cbc.name()).setLoadedBytes(cbc.bytecodes());
+ }
+ }
+
+ void markLoaded(ClassBytecodes[] cbcs, boolean[] isLoaded) {
+ for (int i = 0; i < cbcs.length; ++i) {
+ if (isLoaded[i]) {
+ ClassBytecodes cbc = cbcs[i];
+ get(cbc.name()).setLoadedBytes(cbc.bytecodes());
+ }
+ }
+ }
+
+ // Map a class name to the current compiled class bytes.
+ void setCurrentBytes(String className, byte[] bytes) {
+ ClassInfo ci = get(className);
+ ci.setCurrentBytes(bytes);
+ }
+
+ // Lookup the ClassInfo by class name, create if it does not exist.
+ ClassInfo get(String className) {
+ return map.computeIfAbsent(className, k -> new ClassInfo(k));
+ }
+}
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/Eval.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/Eval.java Thu Jul 21 20:09:19 2016 -0700
@@ -61,6 +61,14 @@
import jdk.jshell.TreeDissector.ExpressionInfo;
import jdk.jshell.Wrap.Range;
import jdk.jshell.Snippet.Status;
+import jdk.jshell.spi.ExecutionControl.ClassBytecodes;
+import jdk.jshell.spi.ExecutionControl.ClassInstallException;
+import jdk.jshell.spi.ExecutionControl.EngineTerminationException;
+import jdk.jshell.spi.ExecutionControl.InternalException;
+import jdk.jshell.spi.ExecutionControl.NotImplementedException;
+import jdk.jshell.spi.ExecutionControl.ResolutionException;
+import jdk.jshell.spi.ExecutionControl.RunException;
+import jdk.jshell.spi.ExecutionControl.UserException;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static java.util.Collections.singletonList;
@@ -541,15 +549,21 @@
if (si.status().isDefined()) {
if (si.isExecutable()) {
try {
- value = state.executionControl().invoke(si.classFullName(), DOIT_METHOD_NAME);
+ value = state.executionControl().invoke(si.classFullName(), DOIT_METHOD_NAME);
value = si.subKind().hasValue()
? expunge(value)
: "";
- } catch (EvalException ex) {
+ } catch (ResolutionException ex) {
+ DeclarationSnippet sn = (DeclarationSnippet) state.maps.getSnippetDeadOrAlive(ex.id());
+ exception = new UnresolvedReferenceException(sn, ex.getStackTrace());
+ } catch (UserException ex) {
exception = translateExecutionException(ex);
- } catch (JShellException ex) {
- // UnresolvedReferenceException
- exception = ex;
+ } catch (RunException ex) {
+ // StopException - no-op
+ } catch (InternalException ex) {
+ state.debug(ex, "invoke");
+ } catch (EngineTerminationException ex) {
+ state.closeDown();
}
} else if (si.subKind() == SubKind.VAR_DECLARATION_SUBKIND) {
switch (((VarSnippet) si).typeName()) {
@@ -700,15 +714,25 @@
/**
* If there are classes to load, loads by calling the execution engine.
- * @param classnames names of the classes to load.
+ * @param classbytecoes names of the classes to load.
*/
- private void load(Collection<String> classnames) {
- if (!classnames.isEmpty()) {
- state.executionControl().load(classnames);
+ private void load(Collection<ClassBytecodes> classbytecoes) {
+ if (!classbytecoes.isEmpty()) {
+ ClassBytecodes[] cbcs = classbytecoes.toArray(new ClassBytecodes[classbytecoes.size()]);
+ try {
+ state.executionControl().load(cbcs);
+ state.classTracker.markLoaded(cbcs);
+ } catch (ClassInstallException ex) {
+ state.classTracker.markLoaded(cbcs, ex.installed());
+ } catch (NotImplementedException ex) {
+ state.debug(ex, "Seriously?!? load not implemented");
+ } catch (EngineTerminationException ex) {
+ state.closeDown();
+ }
}
}
- private EvalException translateExecutionException(EvalException ex) {
+ private EvalException translateExecutionException(UserException ex) {
StackTraceElement[] raw = ex.getStackTrace();
int last = raw.length;
do {
@@ -739,7 +763,7 @@
if (msg.equals("<none>")) {
msg = null;
}
- return new EvalException(msg, ex.getExceptionClassName(), elems);
+ return new EvalException(msg, ex.causeExceptionClass(), elems);
}
private boolean isWrap(StackTraceElement ste) {
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/JShell.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/JShell.java Thu Jul 21 20:09:19 2016 -0700
@@ -44,13 +44,15 @@
import java.util.function.Supplier;
import jdk.internal.jshell.debug.InternalDebugControl;
-import jdk.internal.jshell.jdi.FailOverExecutionControl;
+import jdk.jshell.Snippet.Status;
+import jdk.jshell.execution.JDIDefaultExecutionControl;
+import jdk.jshell.spi.ExecutionControl.EngineTerminationException;
+import jdk.jshell.spi.ExecutionControl.ExecutionControlException;
+import jdk.jshell.spi.ExecutionEnv;
+import static jdk.jshell.execution.Util.failOverExecutionControlGenerator;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toList;
import static jdk.jshell.Util.expunge;
-import jdk.jshell.Snippet.Status;
-import jdk.internal.jshell.jdi.JDIExecutionControl;
-import jdk.jshell.spi.ExecutionEnv;
/**
* The JShell evaluation state engine. This is the central class in the JShell
@@ -90,17 +92,17 @@
final BiFunction<Snippet, Integer, String> idGenerator;
final List<String> extraRemoteVMOptions;
final List<String> extraCompilerOptions;
- final ExecutionControl executionControl;
+ final ExecutionControl.Generator executionControlGenerator;
private int nextKeyIndex = 1;
final Eval eval;
- private final Map<String, byte[]> classnameToBytes = new HashMap<>();
+ final ClassTracker classTracker;
private final Map<Subscription, Consumer<JShell>> shutdownListeners = new HashMap<>();
private final Map<Subscription, Consumer<SnippetEvent>> keyStatusListeners = new HashMap<>();
private boolean closed = false;
- private boolean executionControlLaunched = false;
+ private ExecutionControl executionControl = null;
private SourceCodeAnalysisImpl sourceCodeAnalysis = null;
private static final String L10N_RB_NAME = "jdk.jshell.resources.l10n";
@@ -114,17 +116,18 @@
this.idGenerator = b.idGenerator;
this.extraRemoteVMOptions = b.extraRemoteVMOptions;
this.extraCompilerOptions = b.extraCompilerOptions;
- this.executionControl = b.executionControl==null
- ? new FailOverExecutionControl(
- new JDIExecutionControl(),
- new JDIExecutionControl(false))
- : b.executionControl;
+ this.executionControlGenerator = b.executionControlGenerator==null
+ ? failOverExecutionControlGenerator(
+ JDIDefaultExecutionControl.launch(),
+ JDIDefaultExecutionControl.listen())
+ : b.executionControlGenerator;
this.maps = new SnippetMaps(this);
this.keyMap = new KeyMap(this);
this.outerMap = new OuterWrapMap(this);
this.taskFactory = new TaskFactory(this);
this.eval = new Eval(this);
+ this.classTracker = new ClassTracker();
}
/**
@@ -154,7 +157,7 @@
BiFunction<Snippet, Integer, String> idGenerator = null;
List<String> extraRemoteVMOptions = new ArrayList<>();
List<String> extraCompilerOptions = new ArrayList<>();
- ExecutionControl executionControl;
+ ExecutionControl.Generator executionControlGenerator;
Builder() { }
@@ -310,12 +313,12 @@
* Sets the custom engine for execution. Snippet execution will be
* provided by the specified {@link ExecutionControl} instance.
*
- * @param execEngine the execution engine
+ * @param executionControlGenerator the execution engine generator
* @return the {@code Builder} instance (for use in chained
* initialization)
*/
- public Builder executionEngine(ExecutionControl execEngine) {
- this.executionControl = execEngine;
+ public Builder executionEngine(ExecutionControl.Generator executionControlGenerator) {
+ this.executionControlGenerator = executionControlGenerator;
return this;
}
@@ -397,7 +400,8 @@
* be an event showing its status changed to OVERWRITTEN, this will not
* occur for dropped, rejected, or already overwritten declarations.
* <p>
- * The execution environment is out of process. If the evaluated code
+ * If execution environment is out of process, as is the default case, then
+ * if the evaluated code
* causes the execution environment to terminate, this {@code JShell}
* instance will be closed but the calling process and VM remain valid.
* @param input The input String to evaluate
@@ -447,8 +451,14 @@
* @param path the path to add to the classpath.
*/
public void addToClasspath(String path) {
- taskFactory.addToClasspath(path); // Compiler
- executionControl().addToClasspath(path); // Runtime
+ // Compiler
+ taskFactory.addToClasspath(path);
+ // Runtime
+ try {
+ executionControl().addToClasspath(path);
+ } catch (ExecutionControlException ex) {
+ debug(ex, "on addToClasspath(" + path + ")");
+ }
if (sourceCodeAnalysis != null) {
sourceCodeAnalysis.classpathChanged();
}
@@ -468,8 +478,13 @@
* catching the {@link ThreadDeath} exception.
*/
public void stop() {
- if (executionControl != null)
- executionControl.stop();
+ if (executionControl != null) {
+ try {
+ executionControl.stop();
+ } catch (ExecutionControlException ex) {
+ debug(ex, "on stop()");
+ }
+ }
}
/**
@@ -622,7 +637,15 @@
throw new IllegalArgumentException(
messageFormat("jshell.exc.var.not.valid", snippet, snippet.status()));
}
- String value = executionControl().varValue(snippet.classFullName(), snippet.name());
+ String value;
+ try {
+ value = executionControl().varValue(snippet.classFullName(), snippet.name());
+ } catch (EngineTerminationException ex) {
+ throw new IllegalStateException(ex.getMessage());
+ } catch (ExecutionControlException ex) {
+ debug(ex, "In varValue()");
+ return "[" + ex.getMessage() + "]";
+ }
return expunge(value);
}
@@ -696,43 +719,22 @@
}
@Override
- public JShell state() {
- return JShell.this;
- }
-
- @Override
public List<String> extraRemoteVMOptions() {
return extraRemoteVMOptions;
}
@Override
- public byte[] getClassBytes(String classname) {
- return classnameToBytes.get(classname);
- }
-
- @Override
- public EvalException createEvalException(String message, String exceptionClass, StackTraceElement[] stackElements) {
- return new EvalException(message, exceptionClass, stackElements);
- }
-
- @Override
- public UnresolvedReferenceException createUnresolvedReferenceException(int id, StackTraceElement[] stackElements) {
- DeclarationSnippet sn = (DeclarationSnippet) maps.getSnippetDeadOrAlive(id);
- return new UnresolvedReferenceException(sn, stackElements);
- }
-
- @Override
public void closeDown() {
JShell.this.closeDown();
}
+
}
// --- private / package-private implementation support ---
ExecutionControl executionControl() {
- if (!executionControlLaunched) {
+ if (executionControl == null) {
try {
- executionControlLaunched = true;
- executionControl.start(new ExecutionEnvImpl());
+ executionControl = executionControlGenerator.generate(new ExecutionEnvImpl());
} catch (Throwable ex) {
throw new InternalError("Launching execution engine threw: " + ex.getMessage(), ex);
}
@@ -740,10 +742,6 @@
return executionControl;
}
- void setClassnameToBytes(String classname, byte[] bytes) {
- classnameToBytes.put(classname, bytes);
- }
-
void debug(int flags, String format, Object... args) {
InternalDebugControl.debug(this, err, flags, format, args);
}
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/TaskFactory.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/TaskFactory.java Thu Jul 21 20:09:19 2016 -0700
@@ -82,7 +82,7 @@
throw new UnsupportedOperationException("Compiler not available, must be run with full JDK 9.");
}
Version current = Version.parse(System.getProperty("java.specification.version"));
- if (INITIAL_SUPPORTED_VER.compareToIgnoreOpt(current) > 0) {
+ if (INITIAL_SUPPORTED_VER.compareToIgnoreOptional(current) > 0) {
throw new UnsupportedOperationException("Wrong compiler, must be run with full JDK 9.");
}
this.fileManager = new MemoryFileManager(
@@ -223,7 +223,6 @@
new WrapSourceHandler(),
Util.join(new String[] {
"-Xshouldstop:at=FLOW", "-Xlint:unchecked",
- "-XaddExports:jdk.jshell/jdk.internal.jshell.remote=ALL-UNNAMED",
"-proc:none"
}, extraArgs));
}
@@ -267,7 +266,7 @@
CompileTask(final Collection<OuterWrap> wraps) {
super(wraps.stream(), new WrapSourceHandler(),
- "-Xlint:unchecked", "-XaddExports:jdk.jshell/jdk.internal.jshell.remote=ALL-UNNAMED", "-proc:none", "-parameters");
+ "-Xlint:unchecked", "-proc:none", "-parameters");
}
boolean compile() {
@@ -286,7 +285,7 @@
}
List<String> list = new ArrayList<>();
for (OutputMemoryJavaFileObject fo : l) {
- state.setClassnameToBytes(fo.getName(), fo.getBytes());
+ state.classTracker.setCurrentBytes(fo.getName(), fo.getBytes());
list.add(fo.getName());
}
return list;
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/Unit.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/Unit.java Thu Jul 21 20:09:19 2016 -0700
@@ -32,11 +32,16 @@
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
+import jdk.jshell.ClassTracker.ClassInfo;
import jdk.jshell.Snippet.Kind;
import jdk.jshell.Snippet.Status;
import jdk.jshell.Snippet.SubKind;
import jdk.jshell.TaskFactory.AnalyzeTask;
import jdk.jshell.TaskFactory.CompileTask;
+import jdk.jshell.spi.ExecutionControl.ClassBytecodes;
+import jdk.jshell.spi.ExecutionControl.ClassInstallException;
+import jdk.jshell.spi.ExecutionControl.EngineTerminationException;
+import jdk.jshell.spi.ExecutionControl.NotImplementedException;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static jdk.internal.jshell.debug.InternalDebugControl.DBG_EVNT;
@@ -75,7 +80,7 @@
private SnippetEvent replaceOldEvent;
private List<SnippetEvent> secondaryEvents;
private boolean isAttemptingCorral;
- private List<String> toRedefine;
+ private List<ClassInfo> toRedefine;
private boolean dependenciesNeeded;
Unit(JShell state, Snippet si, Snippet causalSnippet,
@@ -261,30 +266,29 @@
}
/**
- * Process the class information from the last compile.
- * Requires loading of returned list.
+ * Process the class information from the last compile. Requires loading of
+ * returned list.
+ *
* @return the list of classes to load
*/
- Stream<String> classesToLoad(List<String> classnames) {
+ Stream<ClassBytecodes> classesToLoad(List<String> classnames) {
toRedefine = new ArrayList<>();
- List<String> toLoad = new ArrayList<>();
+ List<ClassBytecodes> toLoad = new ArrayList<>();
if (status.isDefined() && !isImport()) {
// Classes should only be loaded/redefined if the compile left them
// in a defined state. Imports do not have code and are not loaded.
for (String cn : classnames) {
- switch (state.executionControl().getClassStatus(cn)) {
- case UNKNOWN:
- // If not loaded, add to the list of classes to load.
- toLoad.add(cn);
- dependenciesNeeded = true;
- break;
- case NOT_CURRENT:
- // If loaded but out of date, add to the list of classes to attempt redefine.
- toRedefine.add(cn);
- break;
- case CURRENT:
- // Loaded and current, so nothing to do
- break;
+ ClassInfo ci = state.classTracker.get(cn);
+ if (ci.isLoaded()) {
+ if (ci.isCurrent()) {
+ // nothing to do
+ } else {
+ toRedefine.add(ci);
+ }
+ } else {
+ // If not loaded, add to the list of classes to load.
+ toLoad.add(ci.toClassBytecodes());
+ dependenciesNeeded = true;
}
}
}
@@ -292,14 +296,30 @@
}
/**
- * Redefine classes needing redefine.
- * classesToLoad() must be called first.
+ * Redefine classes needing redefine. classesToLoad() must be called first.
+ *
* @return true if all redefines succeeded (can be vacuously true)
*/
boolean doRedefines() {
- return toRedefine.isEmpty()
- ? true
- : state.executionControl().redefine(toRedefine);
+ if (toRedefine.isEmpty()) {
+ return true;
+ }
+ ClassBytecodes[] cbcs = toRedefine.stream()
+ .map(ci -> ci.toClassBytecodes())
+ .toArray(size -> new ClassBytecodes[size]);
+ try {
+ state.executionControl().redefine(cbcs);
+ state.classTracker.markLoaded(cbcs);
+ return true;
+ } catch (ClassInstallException ex) {
+ state.classTracker.markLoaded(cbcs, ex.installed());
+ return false;
+ } catch (EngineTerminationException ex) {
+ state.closeDown();
+ return false;
+ } catch (NotImplementedException ex) {
+ return false;
+ }
}
void markForReplacement() {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/DefaultLoaderDelegate.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,132 @@
+/*
+ * 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.jshell.execution;
+
+import java.io.File;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.CodeSource;
+import java.util.Map;
+import java.util.TreeMap;
+import jdk.jshell.spi.ExecutionControl.ClassBytecodes;
+import jdk.jshell.spi.ExecutionControl.ClassInstallException;
+import jdk.jshell.spi.ExecutionControl.EngineTerminationException;
+import jdk.jshell.spi.ExecutionControl.InternalException;
+import jdk.jshell.spi.ExecutionControl.NotImplementedException;
+
+/**
+ * The standard implementation of {@link LoaderDelegate} using
+ * a {@link URLClassLoader}.
+ *
+ * @author Robert Field
+ */
+class DefaultLoaderDelegate implements LoaderDelegate {
+
+ private final RemoteClassLoader loader;
+ private final Map<String, Class<?>> klasses = new TreeMap<>();
+
+ class RemoteClassLoader extends URLClassLoader {
+
+ private final Map<String, byte[]> classObjects = new TreeMap<>();
+
+ RemoteClassLoader() {
+ super(new URL[0]);
+ }
+
+ void delare(String name, byte[] bytes) {
+ classObjects.put(name, bytes);
+ }
+
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ byte[] b = classObjects.get(name);
+ if (b == null) {
+ return super.findClass(name);
+ }
+ return super.defineClass(name, b, 0, b.length, (CodeSource) null);
+ }
+
+ @Override
+ public void addURL(URL url) {
+ super.addURL(url);
+ }
+
+ }
+
+ public DefaultLoaderDelegate() {
+ this.loader = new RemoteClassLoader();
+ }
+
+ @Override
+ public void load(ClassBytecodes[] cbcs)
+ throws ClassInstallException, EngineTerminationException {
+ boolean[] loaded = new boolean[cbcs.length];
+ try {
+ for (ClassBytecodes cbc : cbcs) {
+ loader.delare(cbc.name(), cbc.bytecodes());
+ }
+ for (int i = 0; i < cbcs.length; ++i) {
+ ClassBytecodes cbc = cbcs[i];
+ Class<?> klass = loader.loadClass(cbc.name());
+ klasses.put(cbc.name(), klass);
+ loaded[i] = true;
+ // Get class loaded to the point of, at least, preparation
+ klass.getDeclaredMethods();
+ }
+ } catch (Throwable ex) {
+ throw new ClassInstallException("load: " + ex.getMessage(), loaded);
+ }
+ }
+
+
+ @Override
+ public void addToClasspath(String cp)
+ throws EngineTerminationException, InternalException {
+ try {
+ for (String path : cp.split(File.pathSeparator)) {
+ loader.addURL(new File(path).toURI().toURL());
+ }
+ } catch (Exception ex) {
+ throw new InternalException(ex.toString());
+ }
+ }
+
+ @Override
+ public void setClasspath(String path)
+ throws EngineTerminationException, InternalException {
+ throw new NotImplementedException("setClasspath: Not supported yet.");
+ }
+
+ @Override
+ public Class<?> findClass(String name) throws ClassNotFoundException {
+ Class<?> klass = klasses.get(name);
+ if (klass == null) {
+ throw new ClassNotFoundException(name + " not found");
+ } else {
+ return klass;
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/DemultiplexInput.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.jshell.execution;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Map;
+
+/**
+ * Read from an InputStream which has been packetized and write its contents
+ * to the named OutputStreams.
+ *
+ * @author Jan Lahoda
+ * @see Util#demultiplexInput(java.io.InputStream, java.io.OutputStream, java.io.OutputStream, java.io.OutputStream...)
+ */
+class DemultiplexInput extends Thread {
+
+ private final DataInputStream delegate;
+ private final PipeInputStream command;
+ private final Map<String, OutputStream> io;
+
+ DemultiplexInput(InputStream input, PipeInputStream command,
+ Map<String, OutputStream> io) {
+ super("output reader");
+ this.delegate = new DataInputStream(input);
+ this.command = command;
+ this.io = io;
+ }
+
+ @Override
+ public void run() {
+ try {
+ while (true) {
+ int nameLen = delegate.read();
+ if (nameLen == (-1)) {
+ break;
+ }
+ byte[] name = new byte[nameLen];
+ DemultiplexInput.this.delegate.readFully(name);
+ int dataLen = delegate.read();
+ byte[] data = new byte[dataLen];
+ DemultiplexInput.this.delegate.readFully(data);
+ String chan = new String(name, "UTF-8");
+ if (chan.equals("command")) {
+ for (byte b : data) {
+ command.write(Byte.toUnsignedInt(b));
+ }
+ } else {
+ OutputStream out = io.get(chan);
+ if (out == null) {
+ debug("Unexpected channel name: %s", chan);
+ } else {
+ out.write(data);
+ }
+ }
+ }
+ } catch (IOException ex) {
+ debug(ex, "Failed reading output");
+ } finally {
+ command.close();
+ }
+ }
+
+ /**
+ * Log debugging information. Arguments as for {@code printf}.
+ *
+ * @param format a format string as described in Format string syntax
+ * @param args arguments referenced by the format specifiers in the format
+ * string.
+ */
+ private void debug(String format, Object... args) {
+ // Reserved for future logging
+ }
+
+ /**
+ * Log a serious unexpected internal exception.
+ *
+ * @param ex the exception
+ * @param where a description of the context of the exception
+ */
+ private void debug(Throwable ex, String where) {
+ // Reserved for future logging
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/DirectExecutionControl.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,257 @@
+/*
+ * 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.jshell.execution;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import jdk.jshell.spi.ExecutionControl;
+import jdk.jshell.spi.SPIResolutionException;
+
+/**
+ * An {@link ExecutionControl} implementation that runs in the current process.
+ * May be used directly, or over a channel with
+ * {@link Util#forwardExecutionControl(ExecutionControl, java.io.ObjectInput, java.io.ObjectOutput) }.
+ *
+ * @author Robert Field
+ * @author Jan Lahoda
+ */
+public class DirectExecutionControl implements ExecutionControl {
+
+ private final LoaderDelegate loaderDelegate;
+
+ /**
+ * Creates an instance, delegating loader operations to the specified
+ * delegate.
+ *
+ * @param loaderDelegate the delegate to handle loading classes
+ */
+ public DirectExecutionControl(LoaderDelegate loaderDelegate) {
+ this.loaderDelegate = loaderDelegate;
+ }
+
+ /**
+ * Create an instance using the default class loading.
+ */
+ public DirectExecutionControl() {
+ this(new DefaultLoaderDelegate());
+ }
+
+ @Override
+ public void load(ClassBytecodes[] cbcs)
+ throws ClassInstallException, NotImplementedException, EngineTerminationException {
+ loaderDelegate.load(cbcs);
+ }
+
+ @Override
+ public void redefine(ClassBytecodes[] cbcs)
+ throws ClassInstallException, NotImplementedException, EngineTerminationException {
+ throw new NotImplementedException("redefine not supported");
+ }
+
+ @Override
+ public String invoke(String className, String methodName)
+ throws RunException, InternalException, EngineTerminationException {
+ Method doitMethod;
+ try {
+ Class<?> klass = findClass(className);
+ doitMethod = klass.getDeclaredMethod(methodName, new Class<?>[0]);
+ doitMethod.setAccessible(true);
+ } catch (Throwable ex) {
+ throw new InternalException(ex.toString());
+ }
+
+ try {
+ clientCodeEnter();
+ String result = invoke(doitMethod);
+ System.out.flush();
+ return result;
+ } catch (RunException | InternalException | EngineTerminationException ex) {
+ throw ex;
+ } catch (SPIResolutionException ex) {
+ return throwConvertedInvocationException(ex);
+ } catch (InvocationTargetException ex) {
+ return throwConvertedInvocationException(ex.getCause());
+ } catch (Throwable ex) {
+ return throwConvertedOtherException(ex);
+ } finally {
+ clientCodeLeave();
+ }
+ }
+
+ @Override
+ public String varValue(String className, String varName)
+ throws RunException, EngineTerminationException, InternalException {
+ Object val;
+ try {
+ Class<?> klass = findClass(className);
+ Field var = klass.getDeclaredField(varName);
+ var.setAccessible(true);
+ val = var.get(null);
+ } catch (Throwable ex) {
+ throw new InternalException(ex.toString());
+ }
+
+ try {
+ clientCodeEnter();
+ return valueString(val);
+ } catch (Throwable ex) {
+ return throwConvertedInvocationException(ex);
+ } finally {
+ clientCodeLeave();
+ }
+ }
+
+ @Override
+ public void addToClasspath(String cp)
+ throws EngineTerminationException, InternalException {
+ loaderDelegate.addToClasspath(cp);
+ }
+
+ @Override
+ public void setClasspath(String path)
+ throws EngineTerminationException, InternalException {
+ loaderDelegate.setClasspath(path);
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * Not supported.
+ */
+ @Override
+ public void stop()
+ throws EngineTerminationException, InternalException {
+ throw new NotImplementedException("stop: Not supported.");
+ }
+
+ @Override
+ public Object extensionCommand(String command, Object arg)
+ throws RunException, EngineTerminationException, InternalException {
+ throw new NotImplementedException("Unknown command: " + command);
+ }
+
+ @Override
+ public void close() {
+ }
+
+ /**
+ * Finds the class with the specified binary name.
+ *
+ * @param name the binary name of the class
+ * @return the Class Object
+ * @throws ClassNotFoundException if the class could not be found
+ */
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ return loaderDelegate.findClass(name);
+ }
+
+ /**
+ * Invoke the specified "doit-method", a static method with no parameters.
+ * The {@link DirectExecutionControl#invoke(java.lang.String, java.lang.String) }
+ * in this class will call this to invoke.
+ *
+ * @param doitMethod the Method to invoke
+ * @return the value or null
+ * @throws Exception any exceptions thrown by
+ * {@link java.lang.reflect.Method#invoke(Object, Object...) }
+ * or any {@link ExecutionControl.ExecutionControlException}
+ * to pass-through.
+ */
+ protected String invoke(Method doitMethod) throws Exception {
+ Object res = doitMethod.invoke(null, new Object[0]);
+ return valueString(res);
+ }
+
+ /**
+ * Converts the {@code Object} value from
+ * {@link ExecutionControl#invoke(String, String) } or
+ * {@link ExecutionControl#varValue(String, String) } to {@code String}.
+ *
+ * @param value the value to convert
+ * @return the {@code String} representation
+ */
+ protected static String valueString(Object value) {
+ if (value == null) {
+ return "null";
+ } else if (value instanceof String) {
+ return "\"" + (String) value + "\"";
+ } else if (value instanceof Character) {
+ return "'" + value + "'";
+ } else {
+ return value.toString();
+ }
+ }
+
+ /**
+ * Converts incoming exceptions in user code into instances of subtypes of
+ * {@link ExecutionControl.ExecutionControlException} and throws the
+ * converted exception.
+ *
+ * @param cause the exception to convert
+ * @return never returns as it always throws
+ * @throws ExecutionControl.RunException for normal exception occurrences
+ * @throws ExecutionControl.InternalException for internal problems
+ */
+ protected String throwConvertedInvocationException(Throwable cause) throws RunException, InternalException {
+ if (cause instanceof SPIResolutionException) {
+ SPIResolutionException spire = (SPIResolutionException) cause;
+ throw new ResolutionException(spire.id(), spire.getStackTrace());
+ } else {
+ throw new UserException(cause.getMessage(), cause.getClass().getName(), cause.getStackTrace());
+ }
+ }
+
+ /**
+ * Converts incoming exceptions in agent code into instances of subtypes of
+ * {@link ExecutionControl.ExecutionControlException} and throws the
+ * converted exception.
+ *
+ * @param ex the exception to convert
+ * @return never returns as it always throws
+ * @throws ExecutionControl.RunException for normal exception occurrences
+ * @throws ExecutionControl.InternalException for internal problems
+ */
+ protected String throwConvertedOtherException(Throwable ex) throws RunException, InternalException {
+ throw new InternalException(ex.toString());
+ }
+
+ /**
+ * Marks entry into user code.
+ *
+ * @throws ExecutionControl.InternalException in unexpected failure cases
+ */
+ protected void clientCodeEnter() throws InternalException {
+ }
+
+ /**
+ * Marks departure from user code.
+ *
+ * @throws ExecutionControl.InternalException in unexpected failure cases
+ */
+ protected void clientCodeLeave() throws InternalException {
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/ExecutionControlForwarder.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,229 @@
+/*
+ * 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.jshell.execution;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import jdk.jshell.spi.ExecutionControl;
+import jdk.jshell.spi.ExecutionControl.ClassBytecodes;
+import jdk.jshell.spi.ExecutionControl.ClassInstallException;
+import jdk.jshell.spi.ExecutionControl.EngineTerminationException;
+import jdk.jshell.spi.ExecutionControl.InternalException;
+import jdk.jshell.spi.ExecutionControl.NotImplementedException;
+import jdk.jshell.spi.ExecutionControl.ResolutionException;
+import jdk.jshell.spi.ExecutionControl.StoppedException;
+import jdk.jshell.spi.ExecutionControl.UserException;
+import static jdk.jshell.execution.RemoteCodes.*;
+
+/**
+ * Forwards commands from the input to the specified {@link ExecutionControl}
+ * instance, then responses back on the output.
+ */
+class ExecutionControlForwarder {
+
+ private final ExecutionControl ec;
+ private final ObjectInput in;
+ private final ObjectOutput out;
+
+ ExecutionControlForwarder(ExecutionControl ec, ObjectInput in, ObjectOutput out) {
+ this.ec = ec;
+ this.in = in;
+ this.out = out;
+ }
+
+ private boolean writeSuccess() throws IOException {
+ writeStatus(RESULT_SUCCESS);
+ flush();
+ return true;
+ }
+
+ private boolean writeSuccessAndResult(String result) throws IOException {
+ writeStatus(RESULT_SUCCESS);
+ writeUTF(result);
+ flush();
+ return true;
+ }
+
+ private boolean writeSuccessAndResult(Object result) throws IOException {
+ writeStatus(RESULT_SUCCESS);
+ writeObject(result);
+ flush();
+ return true;
+ }
+
+ private void writeStatus(int status) throws IOException {
+ out.writeInt(status);
+ }
+
+ private void writeObject(Object o) throws IOException {
+ out.writeObject(o);
+ }
+
+ private void writeInt(int i) throws IOException {
+ out.writeInt(i);
+ }
+
+ private void writeUTF(String s) throws IOException {
+ if (s == null) {
+ s = "";
+ }
+ out.writeUTF(s);
+ }
+
+ private void flush() throws IOException {
+ out.flush();
+ }
+
+ private boolean processCommand() throws IOException {
+ try {
+ int prefix = in.readInt();
+ if (prefix != COMMAND_PREFIX) {
+ throw new EngineTerminationException("Invalid command prefix: " + prefix);
+ }
+ String cmd = in.readUTF();
+ switch (cmd) {
+ case CMD_LOAD: {
+ // Load a generated class file over the wire
+ ClassBytecodes[] cbcs = (ClassBytecodes[]) in.readObject();
+ ec.load(cbcs);
+ return writeSuccess();
+ }
+ case CMD_REDEFINE: {
+ // Load a generated class file over the wire
+ ClassBytecodes[] cbcs = (ClassBytecodes[]) in.readObject();
+ ec.redefine(cbcs);
+ return writeSuccess();
+ }
+ case CMD_INVOKE: {
+ // Invoke executable entry point in loaded code
+ String className = in.readUTF();
+ String methodName = in.readUTF();
+ String res = ec.invoke(className, methodName);
+ return writeSuccessAndResult(res);
+ }
+ case CMD_VAR_VALUE: {
+ // Retrieve a variable value
+ String className = in.readUTF();
+ String varName = in.readUTF();
+ String res = ec.varValue(className, varName);
+ return writeSuccessAndResult(res);
+ }
+ case CMD_ADD_CLASSPATH: {
+ // Append to the claspath
+ String cp = in.readUTF();
+ ec.addToClasspath(cp);
+ return writeSuccess();
+ }
+ case CMD_SET_CLASSPATH: {
+ // Set the claspath
+ String cp = in.readUTF();
+ ec.setClasspath(cp);
+ return writeSuccess();
+ }
+ case CMD_STOP: {
+ // Stop the current execution
+ try {
+ ec.stop();
+ } catch (Throwable ex) {
+ // JShell-core not waiting for a result, ignore
+ }
+ return true;
+ }
+ case CMD_CLOSE: {
+ // Terminate this process
+ try {
+ ec.close();
+ } catch (Throwable ex) {
+ // JShell-core not waiting for a result, ignore
+ }
+ return true;
+ }
+ default: {
+ Object arg = in.readObject();
+ Object res = ec.extensionCommand(cmd, arg);
+ return writeSuccessAndResult(res);
+ }
+ }
+ } catch (IOException ex) {
+ // handled by the outer level
+ throw ex;
+ } catch (EngineTerminationException ex) {
+ writeStatus(RESULT_TERMINATED);
+ writeUTF(ex.getMessage());
+ flush();
+ return false;
+ } catch (NotImplementedException ex) {
+ writeStatus(RESULT_NOT_IMPLEMENTED);
+ writeUTF(ex.getMessage());
+ flush();
+ return true;
+ } catch (InternalException ex) {
+ writeStatus(RESULT_INTERNAL_PROBLEM);
+ writeUTF(ex.getMessage());
+ flush();
+ return true;
+ } catch (ClassInstallException ex) {
+ writeStatus(RESULT_CLASS_INSTALL_EXCEPTION);
+ writeUTF(ex.getMessage());
+ writeObject(ex.installed());
+ flush();
+ return true;
+ } catch (UserException ex) {
+ writeStatus(RESULT_USER_EXCEPTION);
+ writeUTF(ex.getMessage());
+ writeUTF(ex.causeExceptionClass());
+ writeObject(ex.getStackTrace());
+ flush();
+ return true;
+ } catch (ResolutionException ex) {
+ writeStatus(RESULT_CORRALLED);
+ writeInt(ex.id());
+ writeObject(ex.getStackTrace());
+ flush();
+ return true;
+ } catch (StoppedException ex) {
+ writeStatus(RESULT_STOPPED);
+ flush();
+ return true;
+ } catch (Throwable ex) {
+ writeStatus(RESULT_TERMINATED);
+ writeUTF(ex.getMessage());
+ flush();
+ return false;
+ }
+ }
+
+ void commandLoop() {
+ try {
+ while (processCommand()) {
+ // condition is loop action
+ }
+ } catch (IOException ex) {
+ // drop out of loop
+ }
+ }
+
+}
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/Internal.java Thu Jul 21 16:45:56 2016 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
-/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. 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.jshell.execution;
-
-/**
- * Temp class needed so the package exists.
- */
-class Internal {}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JDIDefaultExecutionControl.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,278 @@
+/*
+ * 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.jshell.execution;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import com.sun.jdi.BooleanValue;
+import com.sun.jdi.ClassNotLoadedException;
+import com.sun.jdi.Field;
+import com.sun.jdi.IncompatibleThreadStateException;
+import com.sun.jdi.InvalidTypeException;
+import com.sun.jdi.ObjectReference;
+import com.sun.jdi.StackFrame;
+import com.sun.jdi.ThreadReference;
+import com.sun.jdi.VMDisconnectedException;
+import com.sun.jdi.VirtualMachine;
+import jdk.jshell.spi.ExecutionControl;
+import jdk.jshell.spi.ExecutionEnv;
+import static jdk.jshell.execution.Util.remoteInput;
+
+/**
+ * The implementation of {@link jdk.jshell.spi.ExecutionControl} that the
+ * JShell-core uses by default.
+ * Launches a remote process -- the "remote agent".
+ * Interfaces to the remote agent over a socket and via JDI.
+ * Designed to work with {@link RemoteExecutionControl}.
+ *
+ * @author Robert Field
+ * @author Jan Lahoda
+ */
+public class JDIDefaultExecutionControl extends JDIExecutionControl {
+
+ private static final String REMOTE_AGENT = RemoteExecutionControl.class.getName();
+
+ private VirtualMachine vm;
+ private Process process;
+
+ private final Object STOP_LOCK = new Object();
+ private boolean userCodeRunning = false;
+
+ /**
+ * Creates an ExecutionControl instance based on a JDI
+ * {@code LaunchingConnector}.
+ *
+ * @return the generator
+ */
+ public static ExecutionControl.Generator launch() {
+ return env -> create(env, true);
+ }
+
+ /**
+ * Creates an ExecutionControl instance based on a JDI
+ * {@code ListeningConnector}.
+ *
+ * @return the generator
+ */
+ public static ExecutionControl.Generator listen() {
+ return env -> create(env, false);
+ }
+
+ /**
+ * Creates an ExecutionControl instance based on a JDI
+ * {@code ListeningConnector} or {@code LaunchingConnector}.
+ *
+ * Initialize JDI and use it to launch the remote JVM. Set-up a socket for
+ * commands and results. This socket also transports the user
+ * input/output/error.
+ *
+ * @param env the context passed by
+ * {@link jdk.jshell.spi.ExecutionControl#start(jdk.jshell.spi.ExecutionEnv) }
+ * @return the channel
+ * @throws IOException if there are errors in set-up
+ */
+ private static JDIDefaultExecutionControl create(ExecutionEnv env, boolean isLaunch) throws IOException {
+ try (final ServerSocket listener = new ServerSocket(0)) {
+ // timeout after 60 seconds
+ listener.setSoTimeout(60000);
+ int port = listener.getLocalPort();
+
+ // Set-up the JDI connection
+ JDIInitiator jdii = new JDIInitiator(port,
+ env.extraRemoteVMOptions(), REMOTE_AGENT, isLaunch);
+ VirtualMachine vm = jdii.vm();
+ Process process = jdii.process();
+
+ // Forward input to the remote agent
+ Util.forwardInputToRemote(env.userIn(), process.getOutputStream(),
+ ex -> debug(ex, "input forwarding failure"));
+
+ List<Consumer<String>> deathListeners = new ArrayList<>();
+ deathListeners.add(s -> env.closeDown());
+ Util.detectJDIExitEvent(vm, s -> {
+ for (Consumer<String> h : deathListeners) {
+ h.accept(s);
+ }
+ });
+
+ // Set-up the commands/reslts on the socket. Piggy-back snippet
+ // output.
+ Socket socket = listener.accept();
+ // out before in -- match remote creation so we don't hang
+ ObjectOutput cmdout = new ObjectOutputStream(socket.getOutputStream());
+ Map<String, OutputStream> io = new HashMap<>();
+ io.put("out", env.userOut());
+ io.put("err", env.userErr());
+ ObjectInput cmdin = remoteInput(socket.getInputStream(), io);
+ return new JDIDefaultExecutionControl(cmdout, cmdin, vm, process, deathListeners);
+ }
+ }
+
+ /**
+ * Create an instance.
+ *
+ * @param cmdout the output for commands
+ * @param cmdin the input for responses
+ */
+ private JDIDefaultExecutionControl(ObjectOutput cmdout, ObjectInput cmdin,
+ VirtualMachine vm, Process process, List<Consumer<String>> deathListeners) {
+ super(cmdout, cmdin);
+ this.vm = vm;
+ this.process = process;
+ deathListeners.add(s -> disposeVM());
+ }
+
+ @Override
+ public String invoke(String classname, String methodname)
+ throws RunException,
+ EngineTerminationException, InternalException {
+ String res;
+ synchronized (STOP_LOCK) {
+ userCodeRunning = true;
+ }
+ try {
+ res = super.invoke(classname, methodname);
+ } finally {
+ synchronized (STOP_LOCK) {
+ userCodeRunning = false;
+ }
+ }
+ return res;
+ }
+
+ /**
+ * Interrupts a running remote invoke by manipulating remote variables
+ * and sending a stop via JDI.
+ *
+ * @throws EngineTerminationException the execution engine has terminated
+ * @throws InternalException an internal problem occurred
+ */
+ @Override
+ public void stop() throws EngineTerminationException, InternalException {
+ synchronized (STOP_LOCK) {
+ if (!userCodeRunning) {
+ return;
+ }
+
+ vm().suspend();
+ try {
+ OUTER:
+ for (ThreadReference thread : vm().allThreads()) {
+ // could also tag the thread (e.g. using name), to find it easier
+ for (StackFrame frame : thread.frames()) {
+ if (REMOTE_AGENT.equals(frame.location().declaringType().name()) &&
+ ( "invoke".equals(frame.location().method().name())
+ || "varValue".equals(frame.location().method().name()))) {
+ ObjectReference thiz = frame.thisObject();
+ Field inClientCode = thiz.referenceType().fieldByName("inClientCode");
+ Field expectingStop = thiz.referenceType().fieldByName("expectingStop");
+ Field stopException = thiz.referenceType().fieldByName("stopException");
+ if (((BooleanValue) thiz.getValue(inClientCode)).value()) {
+ thiz.setValue(expectingStop, vm().mirrorOf(true));
+ ObjectReference stopInstance = (ObjectReference) thiz.getValue(stopException);
+
+ vm().resume();
+ debug("Attempting to stop the client code...\n");
+ thread.stop(stopInstance);
+ thiz.setValue(expectingStop, vm().mirrorOf(false));
+ }
+
+ break OUTER;
+ }
+ }
+ }
+ } catch (ClassNotLoadedException | IncompatibleThreadStateException | InvalidTypeException ex) {
+ throw new InternalException("Exception on remote stop: " + ex);
+ } finally {
+ vm().resume();
+ }
+ }
+ }
+
+ @Override
+ public void close() {
+ super.close();
+ disposeVM();
+ }
+
+ private synchronized void disposeVM() {
+ try {
+ if (vm != null) {
+ vm.dispose(); // This could NPE, so it is caught below
+ vm = null;
+ }
+ } catch (VMDisconnectedException ex) {
+ // Ignore if already closed
+ } catch (Throwable ex) {
+ debug(ex, "disposeVM");
+ } finally {
+ if (process != null) {
+ process.destroy();
+ process = null;
+ }
+ }
+ }
+
+ @Override
+ protected synchronized VirtualMachine vm() throws EngineTerminationException {
+ if (vm == null) {
+ throw new EngineTerminationException("VM closed");
+ } else {
+ return vm;
+ }
+ }
+
+ /**
+ * Log debugging information. Arguments as for {@code printf}.
+ *
+ * @param format a format string as described in Format string syntax
+ * @param args arguments referenced by the format specifiers in the format
+ * string.
+ */
+ private static void debug(String format, Object... args) {
+ // Reserved for future logging
+ }
+
+ /**
+ * Log a serious unexpected internal exception.
+ *
+ * @param ex the exception
+ * @param where a description of the context of the exception
+ */
+ private static void debug(Throwable ex, String where) {
+ // Reserved for future logging
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JDIEventHandler.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 1998, 2014, 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.jshell.execution;
+
+import java.util.function.Consumer;
+import com.sun.jdi.*;
+import com.sun.jdi.event.*;
+
+/**
+ * Handler of Java Debug Interface events.
+ * Adapted from jdb EventHandler.
+ * Only exit and disconnect events processed.
+ */
+class JDIEventHandler implements Runnable {
+
+ private final Thread thread;
+ private volatile boolean connected = true;
+ private boolean completed = false;
+ private final VirtualMachine vm;
+ private final Consumer<String> reportVMExit;
+
+ /**
+ * Creates an event handler. Start with {@code start()}.
+ *
+ * @param vm the virtual machine for which to handle events
+ * @param reportVMExit callback to report exit/disconnect
+ * (passed true if the VM has died)
+ */
+ JDIEventHandler(VirtualMachine vm, Consumer<String> reportVMExit) {
+ this.vm = vm;
+ this.reportVMExit = reportVMExit;
+ this.thread = new Thread(this, "event-handler");
+ }
+
+ /**
+ * Starts the event handler.
+ */
+ void start() {
+ thread.start();
+ }
+
+ synchronized void shutdown() {
+ connected = false; // force run() loop termination
+ thread.interrupt();
+ while (!completed) {
+ try {wait();} catch (InterruptedException exc) {}
+ }
+ }
+
+ @Override
+ public void run() {
+ EventQueue queue = vm.eventQueue();
+ while (connected) {
+ try {
+ EventSet eventSet = queue.remove();
+ boolean resumeStoppedApp = false;
+ EventIterator it = eventSet.eventIterator();
+ while (it.hasNext()) {
+ resumeStoppedApp |= handleEvent(it.nextEvent());
+ }
+
+ if (resumeStoppedApp) {
+ eventSet.resume();
+ }
+ } catch (InterruptedException exc) {
+ // Do nothing. Any changes will be seen at top of loop.
+ } catch (VMDisconnectedException discExc) {
+ handleDisconnectedException();
+ break;
+ }
+ }
+ synchronized (this) {
+ completed = true;
+ notifyAll();
+ }
+ }
+
+ private boolean handleEvent(Event event) {
+ handleExitEvent(event);
+ return true;
+ }
+
+ private void handleExitEvent(Event event) {
+ if (event instanceof VMDeathEvent) {
+ reportVMExit.accept("VM Died");
+ } else if (event instanceof VMDisconnectEvent) {
+ connected = false;
+ reportVMExit.accept("VM Disconnected");
+ } else {
+ // ignore everything else
+ }
+ }
+
+ private synchronized void handleDisconnectedException() {
+ /*
+ * A VMDisconnectedException has happened while dealing with
+ * another event. We need to flush the event queue, dealing only
+ * with exit events (VMDeath, VMDisconnect) so that we terminate
+ * correctly.
+ */
+ EventQueue queue = vm.eventQueue();
+ while (connected) {
+ try {
+ EventSet eventSet = queue.remove();
+ EventIterator iter = eventSet.eventIterator();
+ while (iter.hasNext()) {
+ handleExitEvent(iter.next());
+ }
+ } catch (InterruptedException exc) {
+ // ignore
+ } catch (InternalError exc) {
+ // ignore
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JDIExecutionControl.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,117 @@
+/*
+ * 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
+ * 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.jshell.execution;
+
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
+import com.sun.jdi.ReferenceType;
+import com.sun.jdi.VirtualMachine;
+import jdk.jshell.spi.ExecutionControl;
+import static java.util.stream.Collectors.toMap;
+
+/**
+ * Abstract JDI implementation of {@link jdk.jshell.spi.ExecutionControl}
+ */
+public abstract class JDIExecutionControl extends StreamingExecutionControl implements ExecutionControl {
+
+ /**
+ * Mapping from class names to JDI {@link ReferenceType}.
+ */
+ private final Map<String, ReferenceType> toReferenceType = new HashMap<>();
+
+ /**
+ * Create an instance.
+ * @param out the output from the remote agent
+ * @param in the input to the remote agent
+ */
+ protected JDIExecutionControl(ObjectOutput out, ObjectInput in) {
+ super(out, in);
+ }
+
+ /**
+ * Returns the JDI {@link VirtualMachine} instance.
+ *
+ * @return the virtual machine
+ * @throws EngineTerminationException if the VM is dead/disconnected
+ */
+ protected abstract VirtualMachine vm() throws EngineTerminationException;
+
+ /**
+ * Redefine the specified classes. Where 'redefine' is, as in JDI and JVMTI,
+ * an in-place replacement of the classes (preserving class identity) --
+ * that is, existing references to the class do not need to be recompiled.
+ * This implementation uses JDI
+ * {@link com.sun.jdi.VirtualMachine#redefineClasses(java.util.Map) }.
+ * It will be unsuccessful if
+ * the signature of the class has changed (see the JDI spec). The
+ * JShell-core is designed to adapt to unsuccessful redefine.
+ */
+ @Override
+ public void redefine(ClassBytecodes[] cbcs)
+ throws ClassInstallException, EngineTerminationException {
+ try {
+ // Convert to the JDI ReferenceType to class bytes map form needed
+ // by JDI.
+ VirtualMachine vm = vm();
+ Map<ReferenceType, byte[]> rmp = Stream.of(cbcs)
+ .collect(toMap(
+ cbc -> referenceType(vm, cbc.name()),
+ cbc -> cbc.bytecodes()));
+ // Attempt redefine. Throws exceptions on failure.
+ vm().redefineClasses(rmp);
+ } catch (EngineTerminationException ex) {
+ throw ex;
+ } catch (Exception ex) {
+ throw new ClassInstallException("redefine: " + ex.getMessage(), new boolean[cbcs.length]);
+ }
+ }
+
+ /**
+ * Returns the JDI {@link ReferenceType} corresponding to the specified
+ * class name.
+ *
+ * @param vm the current JDI {@link VirtualMachine} as returned by
+ * {@code vm()}
+ * @param name the class name to look-up
+ * @return the corresponding {@link ReferenceType}
+ */
+ protected ReferenceType referenceType(VirtualMachine vm, String name) {
+ return toReferenceType.computeIfAbsent(name, n -> nameToRef(vm, n));
+ }
+
+ private static ReferenceType nameToRef(VirtualMachine vm, String name) {
+ List<ReferenceType> rtl = vm.classesByName(name);
+ if (rtl.size() != 1) {
+ return null;
+ }
+ return rtl.get(0);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/JDIInitiator.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,218 @@
+/*
+ * 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.jshell.execution;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import com.sun.jdi.Bootstrap;
+import com.sun.jdi.VirtualMachine;
+import com.sun.jdi.connect.Connector;
+import com.sun.jdi.connect.LaunchingConnector;
+import com.sun.jdi.connect.ListeningConnector;
+
+/**
+ * Sets up a JDI connection, providing the resulting JDI {@link VirtualMachine}
+ * and the {@link Process} the remote agent is running in.
+ */
+public class JDIInitiator {
+
+ private VirtualMachine vm;
+ private Process process = null;
+ private final Connector connector;
+ private final String remoteAgent;
+ private final Map<String, com.sun.jdi.connect.Connector.Argument> connectorArgs;
+
+ /**
+ * Start the remote agent and establish a JDI connection to it.
+ *
+ * @param port the socket port for (non-JDI) commands
+ * @param remoteVMOptions any user requested VM options
+ * @param remoteAgent full class name of remote agent to launch
+ * @param isLaunch does JDI do the launch? That is, LaunchingConnector,
+ * otherwise we start explicitly and use ListeningConnector
+ */
+ public JDIInitiator(int port, List<String> remoteVMOptions,
+ String remoteAgent, boolean isLaunch) {
+ this.remoteAgent = remoteAgent;
+ String connectorName
+ = isLaunch
+ ? "com.sun.jdi.CommandLineLaunch"
+ : "com.sun.jdi.SocketListen";
+ this.connector = findConnector(connectorName);
+ if (connector == null) {
+ throw new IllegalArgumentException("No connector named: " + connectorName);
+ }
+ Map<String, String> argumentName2Value
+ = isLaunch
+ ? launchArgs(port, String.join(" ", remoteVMOptions))
+ : new HashMap<>();
+ this.connectorArgs = mergeConnectorArgs(connector, argumentName2Value);
+ this.vm = isLaunch
+ ? launchTarget()
+ : listenTarget(port, remoteVMOptions);
+
+ }
+
+ /**
+ * Returns the resulting {@code VirtualMachine} instance.
+ *
+ * @return the virtual machine
+ */
+ public VirtualMachine vm() {
+ return vm;
+ }
+
+ /**
+ * Returns the launched process.
+ *
+ * @return the remote agent process
+ */
+ public Process process() {
+ return process;
+ }
+
+ /* launch child target vm */
+ private VirtualMachine launchTarget() {
+ LaunchingConnector launcher = (LaunchingConnector) connector;
+ try {
+ VirtualMachine new_vm = launcher.launch(connectorArgs);
+ process = new_vm.process();
+ return new_vm;
+ } catch (Exception ex) {
+ reportLaunchFail(ex, "launch");
+ }
+ return null;
+ }
+
+ /**
+ * Directly launch the remote agent and connect JDI to it with a
+ * ListeningConnector.
+ */
+ private VirtualMachine listenTarget(int port, List<String> remoteVMOptions) {
+ ListeningConnector listener = (ListeningConnector) connector;
+ try {
+ // Start listening, get the JDI connection address
+ String addr = listener.startListening(connectorArgs);
+ debug("Listening at address: " + addr);
+
+ // Launch the RemoteAgent requesting a connection on that address
+ String javaHome = System.getProperty("java.home");
+ List<String> args = new ArrayList<>();
+ args.add(javaHome == null
+ ? "java"
+ : javaHome + File.separator + "bin" + File.separator + "java");
+ args.add("-agentlib:jdwp=transport=" + connector.transport().name() +
+ ",address=" + addr);
+ args.addAll(remoteVMOptions);
+ args.add(remoteAgent);
+ args.add("" + port);
+ ProcessBuilder pb = new ProcessBuilder(args);
+ process = pb.start();
+
+ // Forward out, err, and in
+ // Accept the connection from the remote agent
+ vm = listener.accept(connectorArgs);
+ listener.stopListening(connectorArgs);
+ return vm;
+ } catch (Exception ex) {
+ reportLaunchFail(ex, "listen");
+ }
+ return null;
+ }
+
+ private Connector findConnector(String name) {
+ for (Connector cntor
+ : Bootstrap.virtualMachineManager().allConnectors()) {
+ if (cntor.name().equals(name)) {
+ return cntor;
+ }
+ }
+ return null;
+ }
+
+ private Map<String, Connector.Argument> mergeConnectorArgs(Connector connector, Map<String, String> argumentName2Value) {
+ Map<String, Connector.Argument> arguments = connector.defaultArguments();
+
+ for (Entry<String, String> argumentEntry : argumentName2Value.entrySet()) {
+ String name = argumentEntry.getKey();
+ String value = argumentEntry.getValue();
+ Connector.Argument argument = arguments.get(name);
+
+ if (argument == null) {
+ throw new IllegalArgumentException("Argument is not defined for connector:" +
+ name + " -- " + connector.name());
+ }
+
+ argument.setValue(value);
+ }
+
+ return arguments;
+ }
+
+ /**
+ * The JShell specific Connector args for the LaunchingConnector.
+ *
+ * @param portthe socket port for (non-JDI) commands
+ * @param remoteVMOptions any user requested VM options
+ * @return the argument map
+ */
+ private Map<String, String> launchArgs(int port, String remoteVMOptions) {
+ Map<String, String> argumentName2Value = new HashMap<>();
+ argumentName2Value.put("main", remoteAgent + " " + port);
+ argumentName2Value.put("options", remoteVMOptions);
+ return argumentName2Value;
+ }
+
+ private void reportLaunchFail(Exception ex, String context) {
+ throw new InternalError("Failed remote " + context + ": " + connector +
+ " -- " + connectorArgs, ex);
+ }
+
+ /**
+ * Log debugging information. Arguments as for {@code printf}.
+ *
+ * @param format a format string as described in Format string syntax
+ * @param args arguments referenced by the format specifiers in the format
+ * string.
+ */
+ private void debug(String format, Object... args) {
+ // Reserved for future logging
+ }
+
+ /**
+ * Log a serious unexpected internal exception.
+ *
+ * @param ex the exception
+ * @param where a description of the context of the exception
+ */
+ private void debug(Throwable ex, String where) {
+ // Reserved for future logging
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/LoaderDelegate.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,80 @@
+/*
+ * 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.jshell.execution;
+
+import jdk.jshell.spi.ExecutionControl.ClassBytecodes;
+import jdk.jshell.spi.ExecutionControl.ClassInstallException;
+import jdk.jshell.spi.ExecutionControl.EngineTerminationException;
+import jdk.jshell.spi.ExecutionControl.InternalException;
+import jdk.jshell.spi.ExecutionControl.NotImplementedException;
+
+/**
+ * This interface specifies the loading specific subset of
+ * {@link jdk.jshell.spi.ExecutionControl}. For use in encapsulating the
+ * {@link java.lang.ClassLoader} implementation.
+ */
+public interface LoaderDelegate {
+
+ /**
+ * Attempts to load new classes.
+ *
+ * @param cbcs the class name and bytecodes to load
+ * @throws ClassInstallException exception occurred loading the classes,
+ * some or all were not loaded
+ * @throws NotImplementedException if not implemented
+ * @throws EngineTerminationException the execution engine has terminated
+ */
+ void load(ClassBytecodes[] cbcs)
+ throws ClassInstallException, NotImplementedException, EngineTerminationException;
+
+ /**
+ * Adds the path to the execution class path.
+ *
+ * @param path the path to add
+ * @throws EngineTerminationException the execution engine has terminated
+ * @throws InternalException an internal problem occurred
+ */
+ void addToClasspath(String path)
+ throws EngineTerminationException, InternalException;
+
+ /**
+ * Sets the execution class path to the specified path.
+ *
+ * @param path the path to add
+ * @throws EngineTerminationException the execution engine has terminated
+ * @throws InternalException an internal problem occurred
+ */
+ void setClasspath(String path)
+ throws EngineTerminationException, InternalException;
+
+ /**
+ * Finds the class with the specified binary name.
+ *
+ * @param name the binary name of the class
+ * @return the Class Object
+ * @throws ClassNotFoundException if the class could not be found
+ */
+ Class<?> findClass(String name) throws ClassNotFoundException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/LocalExecutionControl.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,164 @@
+/*
+ * 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.jshell.execution;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.concurrent.atomic.AtomicReference;
+import jdk.jshell.spi.ExecutionControl;
+
+/**
+ * An implementation of {@link jdk.jshell.spi.ExecutionControl} which executes
+ * in the same JVM as the JShell-core.
+ *
+ * @author Grigory Ptashko
+ */
+public class LocalExecutionControl extends DirectExecutionControl {
+
+ private final Object STOP_LOCK = new Object();
+ private boolean userCodeRunning = false;
+ private ThreadGroup execThreadGroup;
+
+ /**
+ * Creates a local ExecutionControl instance.
+ *
+ * @return the generator
+ */
+ public static ExecutionControl.Generator create() {
+ return env -> new LocalExecutionControl();
+ }
+
+ /**
+ * Creates an instance, delegating loader operations to the specified
+ * delegate.
+ *
+ * @param loaderDelegate the delegate to handle loading classes
+ */
+ public LocalExecutionControl(LoaderDelegate loaderDelegate) {
+ super(loaderDelegate);
+ }
+
+ /**
+ * Create an instance using the default class loading.
+ */
+ public LocalExecutionControl() {
+ }
+
+ @Override
+ protected String invoke(Method doitMethod) throws Exception {
+ execThreadGroup = new ThreadGroup("JShell process local execution");
+
+ AtomicReference<InvocationTargetException> iteEx = new AtomicReference<>();
+ AtomicReference<IllegalAccessException> iaeEx = new AtomicReference<>();
+ AtomicReference<NoSuchMethodException> nmeEx = new AtomicReference<>();
+ AtomicReference<Boolean> stopped = new AtomicReference<>(false);
+
+ Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
+ if (e instanceof InvocationTargetException) {
+ if (e.getCause() instanceof ThreadDeath) {
+ stopped.set(true);
+ } else {
+ iteEx.set((InvocationTargetException) e);
+ }
+ } else if (e instanceof IllegalAccessException) {
+ iaeEx.set((IllegalAccessException) e);
+ } else if (e instanceof NoSuchMethodException) {
+ nmeEx.set((NoSuchMethodException) e);
+ } else if (e instanceof ThreadDeath) {
+ stopped.set(true);
+ }
+ });
+
+ final Object[] res = new Object[1];
+ Thread snippetThread = new Thread(execThreadGroup, () -> {
+ try {
+ res[0] = doitMethod.invoke(null, new Object[0]);
+ } catch (InvocationTargetException e) {
+ if (e.getCause() instanceof ThreadDeath) {
+ stopped.set(true);
+ } else {
+ iteEx.set(e);
+ }
+ } catch (IllegalAccessException e) {
+ iaeEx.set(e);
+ } catch (ThreadDeath e) {
+ stopped.set(true);
+ }
+ });
+
+ snippetThread.start();
+ Thread[] threadList = new Thread[execThreadGroup.activeCount()];
+ execThreadGroup.enumerate(threadList);
+ for (Thread thread : threadList) {
+ if (thread != null) {
+ thread.join();
+ }
+ }
+
+ if (stopped.get()) {
+ throw new StoppedException();
+ }
+
+ if (iteEx.get() != null) {
+ throw iteEx.get();
+ } else if (nmeEx.get() != null) {
+ throw nmeEx.get();
+ } else if (iaeEx.get() != null) {
+ throw iaeEx.get();
+ }
+
+ return valueString(res[0]);
+ }
+
+ @Override
+ @SuppressWarnings("deprecation")
+ public void stop() throws EngineTerminationException, InternalException {
+ synchronized (STOP_LOCK) {
+ if (!userCodeRunning) {
+ return;
+ }
+ if (execThreadGroup == null) {
+ throw new InternalException("Process-local code snippets thread group is null. Aborting stop.");
+ }
+
+ execThreadGroup.stop();
+ }
+ }
+
+ @Override
+ protected void clientCodeEnter() {
+ synchronized (STOP_LOCK) {
+ userCodeRunning = true;
+ }
+ }
+
+ @Override
+ protected void clientCodeLeave() {
+ synchronized (STOP_LOCK) {
+ userCodeRunning = false;
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/MultiplexingOutputStream.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. 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.jshell.execution;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Packetize an OutputStream, dividing it into named channels.
+ *
+ * @author Jan Lahoda
+ */
+class MultiplexingOutputStream extends OutputStream {
+
+ private static final int PACKET_SIZE = 127;
+ private final byte[] name;
+ private final OutputStream delegate;
+
+ MultiplexingOutputStream(String name, OutputStream delegate) {
+ try {
+ this.name = name.getBytes("UTF-8");
+ this.delegate = delegate;
+ } catch (UnsupportedEncodingException ex) {
+ throw new IllegalStateException(ex); //should not happen
+ }
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ synchronized (delegate) {
+ delegate.write(name.length); //assuming the len is small enough to fit into byte
+ delegate.write(name);
+ delegate.write(1);
+ delegate.write(b);
+ delegate.flush();
+ }
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ synchronized (delegate) {
+ int i = 0;
+ while (len > 0) {
+ int size = Math.min(PACKET_SIZE, len);
+ delegate.write(name.length); //assuming the len is small enough to fit into byte
+ delegate.write(name);
+ delegate.write(size);
+ delegate.write(b, off + i, size);
+ i += size;
+ len -= size;
+ }
+ delegate.flush();
+ }
+ }
+
+ @Override
+ public void flush() throws IOException {
+ super.flush();
+ delegate.flush();
+ }
+
+ @Override
+ public void close() throws IOException {
+ super.close();
+ delegate.close();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/PipeInputStream.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,88 @@
+/*
+ * 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.jshell.execution;
+
+import java.io.InputStream;
+
+/**
+ *
+ * @author Jan Lahoda
+ */
+class PipeInputStream extends InputStream {
+
+ private static final int INITIAL_SIZE = 128;
+ private int[] buffer = new int[INITIAL_SIZE];
+ private int start;
+ private int end;
+ private boolean closed;
+
+ @Override
+ public synchronized int read() {
+ while (start == end) {
+ if (closed) {
+ return -1;
+ }
+ try {
+ wait();
+ } catch (InterruptedException ex) {
+ //ignore
+ }
+ }
+ try {
+ return buffer[start];
+ } finally {
+ start = (start + 1) % buffer.length;
+ }
+ }
+
+ public synchronized void write(int b) {
+ if (closed) {
+ throw new IllegalStateException("Already closed.");
+ }
+ int newEnd = (end + 1) % buffer.length;
+ if (newEnd == start) {
+ //overflow:
+ int[] newBuffer = new int[buffer.length * 2];
+ int rightPart = (end > start ? end : buffer.length) - start;
+ int leftPart = end > start ? 0 : start - 1;
+ System.arraycopy(buffer, start, newBuffer, 0, rightPart);
+ System.arraycopy(buffer, 0, newBuffer, rightPart, leftPart);
+ buffer = newBuffer;
+ start = 0;
+ end = rightPart + leftPart;
+ newEnd = end + 1;
+ }
+ buffer[end] = b;
+ end = newEnd;
+ notifyAll();
+ }
+
+ @Override
+ public synchronized void close() {
+ closed = true;
+ notifyAll();
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/RemoteCodes.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,112 @@
+/*
+ * 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
+ * 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.jshell.execution;
+
+/**
+ * Communication constants shared between the main process and the remote
+ * execution process. These are not enums to allow for future expansion, and
+ * remote/local of different versions.
+ *
+ * @author Robert Field
+ */
+class RemoteCodes {
+
+ /**
+ * Command prefix markers.
+ */
+ static final int COMMAND_PREFIX = 0xC03DC03D;
+
+ // Command codes
+
+ /**
+ * Exit the the agent.
+ */
+ static final String CMD_CLOSE = "CMD_CLOSE";
+ /**
+ * Load classes.
+ */
+ static final String CMD_LOAD = "CMD_LOAD";
+ /**
+ * Redefine classes.
+ */
+ static final String CMD_REDEFINE = "CMD_REDEFINE";
+ /**
+ * Invoke a method.
+ */
+ static final String CMD_INVOKE = "CMD_INVOKE";
+ /**
+ * Retrieve the value of a variable.
+ */
+ static final String CMD_VAR_VALUE = "CMD_VAR_VALUE";
+ /**
+ * Add to the class-path.
+ */
+ static final String CMD_ADD_CLASSPATH = "CMD_ADD_CLASSPATH";
+ /**
+ * Set the class-path.
+ */
+ static final String CMD_SET_CLASSPATH = "CMD_SET_CLASSPATH";
+ /**
+ * Stop an invoke.
+ */
+ static final String CMD_STOP = "CMD_STOP";
+
+ // Return result codes
+
+ /**
+ * The command succeeded.
+ */
+ static final int RESULT_SUCCESS = 100;
+ /**
+ * Unbidden execution engine termination.
+ */
+ static final int RESULT_TERMINATED = 101;
+ /**
+ * Command not implemented.
+ */
+ static final int RESULT_NOT_IMPLEMENTED = 102;
+ /**
+ * The command failed.
+ */
+ static final int RESULT_INTERNAL_PROBLEM = 103;
+ /**
+ * User exception encountered.
+ */
+ static final int RESULT_USER_EXCEPTION = 104;
+ /**
+ * Corralled code exception encountered.
+ */
+ static final int RESULT_CORRALLED = 105;
+ /**
+ * Exception encountered during class load/redefine.
+ */
+ static final int RESULT_CLASS_INSTALL_EXCEPTION = 106;
+ /**
+ * The invoke has been stopped.
+ */
+ static final int RESULT_STOPPED = 107;
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/RemoteExecutionControl.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,160 @@
+/*
+ * 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
+ * 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.jshell.execution;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.lang.reflect.Method;
+import java.net.Socket;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+import jdk.jshell.spi.ExecutionControl;
+import static jdk.jshell.execution.Util.forwardExecutionControlAndIO;
+
+/**
+ * The remote agent runs in the execution process (separate from the main JShell
+ * process). This agent loads code over a socket from the main JShell process,
+ * executes the code, and other misc, Specialization of
+ * {@link DirectExecutionControl} which adds stop support controlled by
+ * an external process. Designed to work with {@link JDIDefaultExecutionControl}.
+ *
+ * @author Jan Lahoda
+ * @author Robert Field
+ */
+public class RemoteExecutionControl extends DirectExecutionControl implements ExecutionControl {
+
+ /**
+ * Launch the agent, connecting to the JShell-core over the socket specified
+ * in the command-line argument.
+ *
+ * @param args standard command-line arguments, expectation is the socket
+ * number is the only argument
+ * @throws Exception any unexpected exception
+ */
+ public static void main(String[] args) throws Exception {
+ String loopBack = null;
+ Socket socket = new Socket(loopBack, Integer.parseInt(args[0]));
+ InputStream inStream = socket.getInputStream();
+ OutputStream outStream = socket.getOutputStream();
+ Map<String, Consumer<OutputStream>> chans = new HashMap<>();
+ chans.put("out", st -> System.setOut(new PrintStream(st, true)));
+ chans.put("err", st -> System.setErr(new PrintStream(st, true)));
+ forwardExecutionControlAndIO(new RemoteExecutionControl(), inStream, outStream, chans);
+ }
+
+ // These three variables are used by the main JShell process in interrupting
+ // the running process. Access is via JDI, so the reference is not visible
+ // to code inspection.
+ private boolean inClientCode; // Queried by the main process (in superclass)
+ private boolean expectingStop; // Set by the main process
+// Set by the main process
+
+ // thrown by the main process via JDI:
+ private final StopExecutionException stopException = new StopExecutionException();
+
+ /**
+ * Creates an instance, delegating loader operations to the specified
+ * delegate.
+ *
+ * @param loaderDelegate the delegate to handle loading classes
+ */
+ public RemoteExecutionControl(LoaderDelegate loaderDelegate) {
+ super(loaderDelegate);
+ }
+
+ /**
+ * Create an instance using the default class loading.
+ */
+ public RemoteExecutionControl() {
+ }
+
+ @Override
+ public void stop() throws EngineTerminationException, InternalException {
+ // handled by JDI
+ }
+
+ // Overridden only so this stack frame is seen
+ @Override
+ protected String invoke(Method doitMethod) throws Exception {
+ return super.invoke(doitMethod);
+ }
+
+ // Overridden only so this stack frame is seen
+ @Override
+ public String varValue(String className, String varName) throws RunException, EngineTerminationException, InternalException {
+ return super.varValue(className, varName);
+ }
+
+ @Override
+ protected String throwConvertedInvocationException(Throwable cause) throws RunException, InternalException {
+ if (cause instanceof StopExecutionException) {
+ expectingStop = false;
+ throw new StoppedException();
+ } else {
+ return super.throwConvertedInvocationException(cause);
+ }
+ }
+
+ @Override
+ protected String throwConvertedOtherException(Throwable ex) throws RunException, InternalException {
+ if (ex instanceof StopExecutionException ||
+ ex.getCause() instanceof StopExecutionException) {
+ expectingStop = false;
+ throw new StoppedException();
+ }
+ return super.throwConvertedOtherException(ex);
+ }
+
+ @Override
+ protected void clientCodeEnter() {
+ expectingStop = false;
+ inClientCode = true;
+ }
+
+ @Override
+ protected void clientCodeLeave() throws InternalException {
+ inClientCode = false;
+ while (expectingStop) {
+ try {
+ Thread.sleep(0);
+ } catch (InterruptedException ex) {
+ throw new InternalException("*** Sleep interrupted while waiting for stop exception: " + ex);
+ }
+ }
+ }
+
+ @SuppressWarnings("serial") // serialVersionUID intentionally omitted
+ private class StopExecutionException extends ThreadDeath {
+
+ @Override
+ public synchronized Throwable fillInStackTrace() {
+ return this;
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/StreamingExecutionControl.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,324 @@
+/*
+ * 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.jshell.execution;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import jdk.jshell.JShellException;
+import jdk.jshell.spi.ExecutionControl;
+import static jdk.jshell.execution.RemoteCodes.*;
+
+/**
+ * An implementation of the {@link jdk.jshell.spi.ExecutionControl}
+ * execution engine SPI which streams requests to a remote agent where
+ * execution takes place.
+ *
+ * @author Robert Field
+ */
+public class StreamingExecutionControl implements ExecutionControl {
+
+ private final ObjectOutput out;
+ private final ObjectInput in;
+
+ /**
+ * Creates an instance.
+ *
+ * @param out the output for commands
+ * @param in the input for command responses
+ */
+ public StreamingExecutionControl(ObjectOutput out, ObjectInput in) {
+ this.out = out;
+ this.in = in;
+ }
+
+ @Override
+ public void load(ClassBytecodes[] cbcs)
+ throws ClassInstallException, NotImplementedException, EngineTerminationException {
+ try {
+ // Send a load command to the remote agent.
+ writeCommand(CMD_LOAD);
+ out.writeObject(cbcs);
+ out.flush();
+ // Retrieve and report results from the remote agent.
+ readAndReportClassInstallResult();
+ } catch (IOException ex) {
+ throw new EngineTerminationException("Exception writing remote load: " + ex);
+ }
+ }
+
+ @Override
+ public void redefine(ClassBytecodes[] cbcs)
+ throws ClassInstallException, NotImplementedException, EngineTerminationException {
+ try {
+ // Send a load command to the remote agent.
+ writeCommand(CMD_REDEFINE);
+ out.writeObject(cbcs);
+ out.flush();
+ // Retrieve and report results from the remote agent.
+ readAndReportClassInstallResult();
+ } catch (IOException ex) {
+ throw new EngineTerminationException("Exception writing remote redefine: " + ex);
+ }
+ }
+
+ @Override
+ public String invoke(String classname, String methodname)
+ throws RunException, EngineTerminationException, InternalException {
+ try {
+ // Send the invoke command to the remote agent.
+ writeCommand(CMD_INVOKE);
+ out.writeUTF(classname);
+ out.writeUTF(methodname);
+ out.flush();
+ // Retrieve and report results from the remote agent.
+ readAndReportExecutionResult();
+ String result = in.readUTF();
+ return result;
+ } catch (IOException ex) {
+ throw new EngineTerminationException("Exception writing remote invoke: " + ex);
+ }
+ }
+
+ @Override
+ public String varValue(String classname, String varname)
+ throws RunException, EngineTerminationException, InternalException {
+ try {
+ // Send the variable-value command to the remote agent.
+ writeCommand(CMD_VAR_VALUE);
+ out.writeUTF(classname);
+ out.writeUTF(varname);
+ out.flush();
+ // Retrieve and report results from the remote agent.
+ readAndReportExecutionResult();
+ String result = in.readUTF();
+ return result;
+ } catch (IOException ex) {
+ throw new EngineTerminationException("Exception writing remote varValue: " + ex);
+ }
+ }
+
+
+ @Override
+ public void addToClasspath(String path)
+ throws EngineTerminationException, InternalException {
+ try {
+ // Send the classpath addition command to the remote agent.
+ writeCommand(CMD_ADD_CLASSPATH);
+ out.writeUTF(path);
+ out.flush();
+ // Retrieve and report results from the remote agent.
+ readAndReportClassSimpleResult();
+ } catch (IOException ex) {
+ throw new EngineTerminationException("Exception writing remote add to classpath: " + ex);
+ }
+ }
+
+ @Override
+ public void setClasspath(String path)
+ throws EngineTerminationException, InternalException {
+ try {
+ // Send the classpath addition command to the remote agent.
+ writeCommand(CMD_SET_CLASSPATH);
+ out.writeUTF(path);
+ out.flush();
+ // Retrieve and report results from the remote agent.
+ readAndReportClassSimpleResult();
+ } catch (IOException ex) {
+ throw new EngineTerminationException("Exception writing remote set classpath: " + ex);
+ }
+ }
+
+ @Override
+ public void stop()
+ throws EngineTerminationException, InternalException {
+ try {
+ // Send the variable-value command to the remote agent.
+ writeCommand(CMD_STOP);
+ out.flush();
+ } catch (IOException ex) {
+ throw new EngineTerminationException("Exception writing remote stop: " + ex);
+ }
+ }
+
+ @Override
+ public Object extensionCommand(String command, Object arg)
+ throws RunException, EngineTerminationException, InternalException {
+ try {
+ writeCommand(command);
+ out.writeObject(arg);
+ out.flush();
+ // Retrieve and report results from the remote agent.
+ readAndReportExecutionResult();
+ Object result = in.readObject();
+ return result;
+ } catch (IOException | ClassNotFoundException ex) {
+ throw new EngineTerminationException("Exception transmitting remote extensionCommand: "
+ + command + " -- " + ex);
+ }
+ }
+
+ /**
+ * Closes the execution engine. Send an exit command to the remote agent.
+ */
+ @Override
+ public void close() {
+ try {
+ writeCommand(CMD_CLOSE);
+ out.flush();
+ } catch (IOException ex) {
+ // ignore;
+ }
+ }
+
+ private void writeCommand(String cmd) throws IOException {
+ out.writeInt(COMMAND_PREFIX);
+ out.writeUTF(cmd);
+ }
+
+ /**
+ * Reports results from a remote agent command that does not expect
+ * exceptions.
+ */
+ private void readAndReportClassSimpleResult() throws EngineTerminationException, InternalException {
+ try {
+ int status = in.readInt();
+ switch (status) {
+ case RESULT_SUCCESS:
+ return;
+ case RESULT_NOT_IMPLEMENTED: {
+ String message = in.readUTF();
+ throw new NotImplementedException(message);
+ }
+ case RESULT_INTERNAL_PROBLEM: {
+ String message = in.readUTF();
+ throw new InternalException(message);
+ }
+ case RESULT_TERMINATED: {
+ String message = in.readUTF();
+ throw new EngineTerminationException(message);
+ }
+ default: {
+ throw new EngineTerminationException("Bad remote result code: " + status);
+ }
+ }
+ } catch (IOException ex) {
+ throw new EngineTerminationException(ex.toString());
+ }
+ }
+
+ /**
+ * Reports results from a remote agent command that does not expect
+ * exceptions.
+ */
+ private void readAndReportClassInstallResult() throws ClassInstallException,
+ NotImplementedException, EngineTerminationException {
+ try {
+ int status = in.readInt();
+ switch (status) {
+ case RESULT_SUCCESS:
+ return;
+ case RESULT_NOT_IMPLEMENTED: {
+ String message = in.readUTF();
+ throw new NotImplementedException(message);
+ }
+ case RESULT_CLASS_INSTALL_EXCEPTION: {
+ String message = in.readUTF();
+ boolean[] loaded = (boolean[]) in.readObject();
+ throw new ClassInstallException(message, loaded);
+ }
+ case RESULT_TERMINATED: {
+ String message = in.readUTF();
+ throw new EngineTerminationException(message);
+ }
+ default: {
+ throw new EngineTerminationException("Bad remote result code: " + status);
+ }
+ }
+ } catch (IOException | ClassNotFoundException ex) {
+ throw new EngineTerminationException(ex.toString());
+ }
+ }
+
+ /**
+ * Reports results from a remote agent command that expects runtime
+ * exceptions.
+ *
+ * @return true if successful
+ * @throws IOException if the connection has dropped
+ * @throws JShellException {@link jdk.jshell.EvalException}, if a user
+ * exception was encountered on invoke;
+ * {@link jdk.jshell.UnresolvedReferenceException}, if an unresolved
+ * reference was encountered
+ * @throws java.lang.ClassNotFoundException
+ */
+ private void readAndReportExecutionResult() throws RunException,
+ EngineTerminationException, InternalException {
+ try {
+ int status = in.readInt();
+ switch (status) {
+ case RESULT_SUCCESS:
+ return;
+ case RESULT_NOT_IMPLEMENTED: {
+ String message = in.readUTF();
+ throw new NotImplementedException(message);
+ }
+ case RESULT_USER_EXCEPTION: {
+ // A user exception was encountered.
+ String message = in.readUTF();
+ String exceptionClassName = in.readUTF();
+ StackTraceElement[] elems = (StackTraceElement[]) in.readObject();
+ throw new UserException(message, exceptionClassName, elems);
+ }
+ case RESULT_CORRALLED: {
+ // An unresolved reference was encountered.
+ int id = in.readInt();
+ StackTraceElement[] elems = (StackTraceElement[]) in.readObject();
+ ResolutionException re = new ResolutionException(id, elems);
+ throw re;
+ }
+ case RESULT_STOPPED: {
+ // Execution was aborted by the stop()
+ throw new StoppedException();
+ }
+ case RESULT_INTERNAL_PROBLEM: {
+ // An internal error has occurred.
+ String message = in.readUTF();
+ throw new InternalException(message);
+ }
+ case RESULT_TERMINATED: {
+ String message = in.readUTF();
+ throw new EngineTerminationException(message);
+ }
+ default: {
+ throw new EngineTerminationException("Bad remote result code: " + status);
+ }
+ }
+ } catch (IOException | ClassNotFoundException ex) {
+ throw new EngineTerminationException(ex.toString());
+ }
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/execution/Util.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,182 @@
+/*
+ * 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.jshell.execution;
+
+import jdk.jshell.spi.ExecutionEnv;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.function.Consumer;
+import com.sun.jdi.VirtualMachine;
+import jdk.jshell.spi.ExecutionControl;
+
+
+/**
+ * Miscellaneous utility methods for setting-up implementations of
+ * {@link ExecutionControl}. Particularly implementations with remote
+ * execution.
+ *
+ * @author Jan Lahoda
+ * @author Robert Field
+ */
+public class Util {
+
+ // never instanciated
+ private Util() {}
+
+ /**
+ * Create a composite {@link ExecutionControl.Generator} instance that, when
+ * generating, will try each specified generator until successfully creating
+ * an {@link ExecutionControl} instance, or, if all fail, will re-throw the
+ * first exception.
+ *
+ * @param gec0 the first instance to try
+ * @param gecs the second through Nth instance to try
+ * @return the fail-over generator
+ */
+ public static ExecutionControl.Generator failOverExecutionControlGenerator(
+ ExecutionControl.Generator gec0, ExecutionControl.Generator... gecs) {
+ return (ExecutionEnv env) -> {
+ Throwable thrown;
+ try {
+ return gec0.generate(env);
+ } catch (Throwable ex) {
+ thrown = ex;
+ }
+ for (ExecutionControl.Generator gec : gecs) {
+ try {
+ return gec.generate(env);
+ } catch (Throwable ignore) {
+ // only care about the first, and only if they all fail
+ }
+ }
+ throw thrown;
+ };
+ }
+
+ /**
+ * Forward commands from the input to the specified {@link ExecutionControl}
+ * instance, then responses back on the output.
+ * @param ec the direct instance of {@link ExecutionControl} to process commands
+ * @param in the command input
+ * @param out the command response output
+ */
+ public static void forwardExecutionControl(ExecutionControl ec,
+ ObjectInput in, ObjectOutput out) {
+ new ExecutionControlForwarder(ec, in, out).commandLoop();
+ }
+
+ /**
+ * Forward commands from the input to the specified {@link ExecutionControl}
+ * instance, then responses back on the output.
+ * @param ec the direct instance of {@link ExecutionControl} to process commands
+ * @param inStream the stream from which to create the command input
+ * @param outStream the stream that will carry {@code System.out},
+ * {@code System.err}, any specified auxiliary channels, and the
+ * command response output.
+ * @param streamMap a map between names of additional streams to carry and setters
+ * for the stream
+ * @throws IOException if there are errors using the passed streams
+ */
+ public static void forwardExecutionControlAndIO(ExecutionControl ec,
+ InputStream inStream, OutputStream outStream,
+ Map<String, Consumer<OutputStream>> streamMap) throws IOException {
+ ObjectInputStream cmdIn = new ObjectInputStream(inStream);
+ for (Entry<String, Consumer<OutputStream>> e : streamMap.entrySet()) {
+ e.getValue().accept(multiplexingOutputStream(e.getKey(), outStream));
+ }
+ ObjectOutputStream cmdOut = new ObjectOutputStream(multiplexingOutputStream("command", outStream));
+ forwardExecutionControl(ec, cmdIn, cmdOut);
+ }
+
+ static OutputStream multiplexingOutputStream(String label, OutputStream outputStream) {
+ return new MultiplexingOutputStream(label, outputStream);
+ }
+
+ /**
+ * Reads from an InputStream which has been packetized and write its contents
+ * to the out and err OutputStreams; Copies the command stream.
+ * @param input the packetized input stream
+ * @param streamMap a map between stream names and the output streams to forward
+ * @return the command stream
+ * @throws IOException if setting up the streams raised an exception
+ */
+ public static ObjectInput remoteInput(InputStream input,
+ Map<String, OutputStream> streamMap) throws IOException {
+ PipeInputStream commandIn = new PipeInputStream();
+ new DemultiplexInput(input, commandIn, streamMap).start();
+ return new ObjectInputStream(commandIn);
+ }
+
+ /**
+ * Monitor the JDI event stream for {@link com.sun.jdi.event.VMDeathEvent}
+ * and {@link com.sun.jdi.event.VMDisconnectEvent}. If encountered, invokes
+ * {@code unbiddenExitHandler}.
+ *
+ * @param vm the virtual machine to check
+ * @param unbiddenExitHandler the handler, which will accept the exit
+ * information
+ */
+ public static void detectJDIExitEvent(VirtualMachine vm, Consumer<String> unbiddenExitHandler) {
+ if (vm.canBeModified()) {
+ new JDIEventHandler(vm, unbiddenExitHandler).start();
+ }
+ }
+
+ /**
+ * Creates a Thread that will ship all input to the remote agent.
+ *
+ * @param inputStream the user input
+ * @param outStream the input to the remote agent
+ * @param handler a failure handler
+ */
+ public static void forwardInputToRemote(final InputStream inputStream,
+ final OutputStream outStream, final Consumer<Exception> handler) {
+ Thread thr = new Thread("input reader") {
+ @Override
+ public void run() {
+ try {
+ byte[] buf = new byte[256];
+ int cnt;
+ while ((cnt = inputStream.read(buf)) != -1) {
+ outStream.write(buf, 0, cnt);
+ outStream.flush();
+ }
+ } catch (Exception ex) {
+ handler.accept(ex);
+ }
+ }
+ };
+ thr.setPriority(Thread.MAX_PRIORITY - 1);
+ thr.start();
+ }
+
+}
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/spi/ExecutionControl.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/spi/ExecutionControl.java Thu Jul 21 20:09:19 2016 -0700
@@ -22,62 +22,152 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
-
package jdk.jshell.spi;
-import java.util.Collection;
-import jdk.jshell.JShellException;
+import java.io.Serializable;
/**
- * This interface specifies the functionality that must provided to implement
- * a pluggable JShell execution engine.
+ * This interface specifies the functionality that must provided to implement a
+ * pluggable JShell execution engine.
* <p>
- * The audience for this Service Provider Interface is engineers
- * wishing to implement their own version of the execution engine in support
- * of the JShell API. This is NOT a part of the JShell API.
+ * The audience for this Service Provider Interface is engineers wishing to
+ * implement their own version of the execution engine in support of the JShell
+ * API.
* <p>
- * A Snippet is compiled into code wrapped in a 'wrapper class'. The execution
- * engine is used by the core JShell implementation to load and, for
- * executable Snippets, execute the Snippet.
+ * A Snippet is compiled into code wrapped in a 'wrapper class'. The execution
+ * engine is used by the core JShell implementation to load and, for executable
+ * Snippets, execute the Snippet.
* <p>
* Methods defined in this interface should only be called by the core JShell
* implementation.
* <p>
- * To install an instance of ExecutionControl, it is passed to
- * {@link jdk.jshell.JShell.Builder#executionEngine(jdk.jshell.spi.ExecutionControl) }.
+ * To install an {@code ExecutionControl}, its {@code Generator} is passed to
+ * {@link jdk.jshell.JShell.Builder#executionEngine(ExecutionControl.Generator) }.
*/
public interface ExecutionControl {
/**
- * Represents the current status of a class in the execution engine.
+ * Defines a functional interface for creating {@link ExecutionControl}
+ * instances.
*/
- public enum ClassStatus {
- /**
- * Class is not known to the execution engine (not loaded).
- */
- UNKNOWN,
-
- /**
- * Class is loaded, but the loaded/redefined bytes do not match those
- * returned by {@link ExecutionEnv#getClassBytes(java.lang.String) }.
- */
- NOT_CURRENT,
+ public interface Generator {
/**
- * Class is loaded and loaded/redefined bytes match those
- * returned by {@link ExecutionEnv#getClassBytes(java.lang.String) }.
+ * Generates an execution engine, given an execution environment.
+ *
+ * @param env the context in which the {@link ExecutionControl} is to
+ * be created
+ * @return the created instance
+ * @throws Throwable if problems occurred
*/
- CURRENT
- };
+ ExecutionControl generate(ExecutionEnv env) throws Throwable;
+ }
+
+ /**
+ * Attempts to load new classes.
+ *
+ * @param cbcs the class name and bytecodes to load
+ * @throws ClassInstallException exception occurred loading the classes,
+ * some or all were not loaded
+ * @throws NotImplementedException if not implemented
+ * @throws EngineTerminationException the execution engine has terminated
+ */
+ void load(ClassBytecodes[] cbcs)
+ throws ClassInstallException, NotImplementedException, EngineTerminationException;
+
+ /**
+ * Attempts to redefine previously loaded classes.
+ *
+ * @param cbcs the class name and bytecodes to redefine
+ * @throws ClassInstallException exception occurred redefining the classes,
+ * some or all were not redefined
+ * @throws NotImplementedException if not implemented
+ * @throws EngineTerminationException the execution engine has terminated
+ */
+ void redefine(ClassBytecodes[] cbcs)
+ throws ClassInstallException, NotImplementedException, EngineTerminationException;
+
+ /**
+ * Invokes an executable Snippet by calling a method on the specified
+ * wrapper class. The method must have no arguments and return String.
+ *
+ * @param className the class whose method should be invoked
+ * @param methodName the name of method to invoke
+ * @return the result of the execution or null if no result
+ * @throws UserException the invoke raised a user exception
+ * @throws ResolutionException the invoke attempted to directly or
+ * indirectly invoke an unresolved snippet
+ * @throws StoppedException if the {@code invoke()} was canceled by
+ * {@link ExecutionControl#stop}
+ * @throws EngineTerminationException the execution engine has terminated
+ * @throws InternalException an internal problem occurred
+ */
+ String invoke(String className, String methodName)
+ throws RunException, EngineTerminationException, InternalException;
/**
- * Initializes the instance. No methods in this interface can be called
- * before this.
+ * Returns the value of a variable.
+ *
+ * @param className the name of the wrapper class of the variable
+ * @param varName the name of the variable
+ * @return the value of the variable
+ * @throws UserException formatting the value raised a user exception
+ * @throws ResolutionException formatting the value attempted to directly or
+ * indirectly invoke an unresolved snippet
+ * @throws StoppedException if the formatting the value was canceled by
+ * {@link ExecutionControl#stop}
+ * @throws EngineTerminationException the execution engine has terminated
+ * @throws InternalException an internal problem occurred
+ */
+ String varValue(String className, String varName)
+ throws RunException, EngineTerminationException, InternalException;
+
+ /**
+ * Adds the path to the execution class path.
+ *
+ * @param path the path to add
+ * @throws EngineTerminationException the execution engine has terminated
+ * @throws InternalException an internal problem occurred
+ */
+ void addToClasspath(String path)
+ throws EngineTerminationException, InternalException;
+
+ /**
+ * Sets the execution class path to the specified path.
*
- * @param env the execution environment information provided by JShell
- * @throws Exception if the instance is unable to initialize
+ * @param path the path to add
+ * @throws EngineTerminationException the execution engine has terminated
+ * @throws InternalException an internal problem occurred
+ */
+ void setClasspath(String path)
+ throws EngineTerminationException, InternalException;
+
+ /**
+ * Interrupts a running invoke.
+ *
+ * @throws EngineTerminationException the execution engine has terminated
+ * @throws InternalException an internal problem occurred
*/
- void start(ExecutionEnv env) throws Exception;
+ void stop()
+ throws EngineTerminationException, InternalException;
+
+ /**
+ * Run a non-standard command (or a standard command from a newer version).
+ *
+ * @param command the non-standard command
+ * @param arg the commands argument
+ * @return the commands return value
+ * @throws UserException the command raised a user exception
+ * @throws ResolutionException the command attempted to directly or
+ * indirectly invoke an unresolved snippet
+ * @throws StoppedException if the command was canceled by
+ * {@link ExecutionControl#stop}
+ * @throws EngineTerminationException the execution engine has terminated
+ * @throws NotImplementedException if not implemented
+ * @throws InternalException an internal problem occurred
+ */
+ Object extensionCommand(String command, Object arg)
+ throws RunException, EngineTerminationException, InternalException;
/**
* Shuts down this execution engine. Implementation should free all
@@ -88,67 +178,206 @@
void close();
/**
- * Adds the path to the execution class path.
- *
- * @param path the path to add
- * @return true if successful
+ * Bundles class name with class bytecodes.
*/
- boolean addToClasspath(String path);
+ public static final class ClassBytecodes implements Serializable {
+
+ private static final long serialVersionUID = 0xC1A55B47EC0DE5L;
+ private final String name;
+ private final byte[] bytecodes;
+
+ /**
+ * Creates a name/bytecode pair.
+ * @param name the class name
+ * @param bytecodes the class bytecodes
+ */
+ public ClassBytecodes(String name, byte[] bytecodes) {
+ this.name = name;
+ this.bytecodes = bytecodes;
+ }
+
+ /**
+ * The bytecodes for the class.
+ *
+ * @return the bytecodes
+ */
+ public byte[] bytecodes() {
+ return bytecodes;
+ }
+
+ /**
+ * The class name.
+ *
+ * @return the class name
+ */
+ public String name() {
+ return name;
+ }
+ }
/**
- * Invokes an executable Snippet by calling a method on the specified
- * wrapper class. The method must have no arguments and return String.
- *
- * @param classname the class whose method should be invoked
- * @param methodname the name of method to invoke
- * @return the result of the execution or null if no result
- * @throws JShellException if a user exception if thrown,
- * {@link jdk.jshell.EvalException EvalException} will be thrown; if an
- * unresolved reference is encountered,
- * {@link jdk.jshell.UnresolvedReferenceException UnresolvedReferenceException}
- * will be thrown
+ * The abstract base of all {@code ExecutionControl} exceptions.
*/
- String invoke(String classname, String methodname) throws JShellException;
+ public static abstract class ExecutionControlException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ public ExecutionControlException(String message) {
+ super(message);
+ }
+ }
/**
- * Attempts to load new classes. Class bytes are retrieved from
- * {@link ExecutionEnv#getClassBytes(java.lang.String) }
- *
- * @param classes list of class names to load
- * @return true if load succeeded
+ * Unbidden execution engine termination has occurred.
+ */
+ public static class EngineTerminationException extends ExecutionControlException {
+
+ private static final long serialVersionUID = 1L;
+
+ public EngineTerminationException(String message) {
+ super(message);
+ }
+ }
+
+ /**
+ * The command is not implemented.
*/
- boolean load(Collection<String> classes);
+ public static class NotImplementedException extends InternalException {
+
+ private static final long serialVersionUID = 1L;
+
+ public NotImplementedException(String message) {
+ super(message);
+ }
+ }
+
+ /**
+ * An internal problem has occurred.
+ */
+ public static class InternalException extends ExecutionControlException {
+
+ private static final long serialVersionUID = 1L;
+
+ public InternalException(String message) {
+ super(message);
+ }
+ }
/**
- * Attempts to redefine previously loaded classes. Class bytes are retrieved
- * from {@link ExecutionEnv#getClassBytes(java.lang.String) }
- *
- * @param classes list of class names to redefine
- * @return true if redefine succeeded
+ * A class install (load or redefine) encountered a problem.
*/
- boolean redefine(Collection<String> classes);
+ public static class ClassInstallException extends ExecutionControlException {
+
+ private static final long serialVersionUID = 1L;
+
+ private final boolean[] installed;
+
+ public ClassInstallException(String message, boolean[] installed) {
+ super(message);
+ this.installed = installed;
+ }
+
+ /**
+ * Indicates which of the passed classes were successfully
+ * loaded/redefined.
+ * @return a one-to-one array with the {@link ClassBytecodes}{@code[]}
+ * array -- {@code true} if installed
+ */
+ public boolean[] installed() {
+ return installed;
+ }
+ }
+
+ /**
+ * The abstract base of of exceptions specific to running user code.
+ */
+ public static abstract class RunException extends ExecutionControlException {
+
+ private static final long serialVersionUID = 1L;
+
+ private RunException(String message) {
+ super(message);
+ }
+ }
/**
- * Queries if the class is loaded and the class bytes are current.
- *
- * @param classname name of the wrapper class to query
- * @return {@code UNKNOWN} if the class is not loaded; {@code CURRENT} if
- * the loaded/redefined bytes are equal to the most recent bytes for this
- * wrapper class; otherwise {@code NOT_CURRENT}
+ * A 'normal' user exception occurred.
*/
- ClassStatus getClassStatus(String classname);
+ public static class UserException extends RunException {
+
+ private static final long serialVersionUID = 1L;
+
+ private final String causeExceptionClass;
+
+ public UserException(String message, String causeExceptionClass, StackTraceElement[] stackElements) {
+ super(message);
+ this.causeExceptionClass = causeExceptionClass;
+ this.setStackTrace(stackElements);
+ }
+
+ /**
+ * Returns the class of the user exception.
+ * @return the name of the user exception class
+ */
+ public String causeExceptionClass() {
+ return causeExceptionClass;
+ }
+ }
/**
- * Interrupt a running invoke.
+ * An exception indicating that a {@code DeclarationSnippet} with unresolved
+ * references has been encountered.
+ * <p>
+ * Contrast this with the initiating {@link SPIResolutionException}
+ * (a {@code RuntimeException}) which is embedded in generated corralled
+ * code. Also, contrast this with
+ * {@link jdk.jshell.UnresolvedReferenceException} the high-level
+ * exception (with {@code DeclarationSnippet} reference) provided in the
+ * main API.
*/
- void stop();
+ public static class ResolutionException extends RunException {
+
+ private static final long serialVersionUID = 1L;
+
+ private final int id;
+
+ /**
+ * Constructs an exception indicating that a {@code DeclarationSnippet}
+ * with unresolved references has been encountered.
+ *
+ * @param id An internal identifier of the specific method
+ * @param stackElements the stack trace
+ */
+ public ResolutionException(int id, StackTraceElement[] stackElements) {
+ super("resolution exception: " + id);
+ this.id = id;
+ this.setStackTrace(stackElements);
+ }
+
+ /**
+ * Retrieves the internal identifier of the unresolved identifier.
+ *
+ * @return the internal identifier
+ */
+ public int id() {
+ return id;
+ }
+ }
/**
- * Returns the value of a variable.
- *
- * @param classname the name of the wrapper class of the variable
- * @param varname the name of the variable
- * @return the value of the variable
+ * An exception indicating that an
+ * {@link ExecutionControl#invoke(java.lang.String, java.lang.String) }
+ * (or theoretically a
+ * {@link ExecutionControl#varValue(java.lang.String, java.lang.String) })
+ * has been interrupted by a {@link ExecutionControl#stop() }.
*/
- String varValue(String classname, String varname);
+ public static class StoppedException extends RunException {
+
+ private static final long serialVersionUID = 1L;
+
+ public StoppedException() {
+ super("stopped by stop()");
+ }
+ }
+
}
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/spi/ExecutionEnv.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/spi/ExecutionEnv.java Thu Jul 21 20:09:19 2016 -0700
@@ -28,14 +28,11 @@
import java.io.InputStream;
import java.io.PrintStream;
import java.util.List;
-import jdk.jshell.EvalException;
import jdk.jshell.JShell;
-import jdk.jshell.UnresolvedReferenceException;
/**
* Functionality made available to a pluggable JShell execution engine. It is
- * provided to the execution engine by the core JShell implementation calling
- * {@link ExecutionControl#start(jdk.jshell.spi.ExecutionEnv) }.
+ * provided to the execution engine by the core JShell implementation.
* <p>
* This interface is designed to provide the access to core JShell functionality
* needed to implement ExecutionControl.
@@ -66,11 +63,6 @@
PrintStream userErr();
/**
- * @return the JShell instance
- */
- JShell state();
-
- /**
* Returns the additional VM options to be used when launching the remote
* JVM. This is advice to the execution engine.
* <p>
@@ -81,47 +73,8 @@
List<String> extraRemoteVMOptions();
/**
- * Retrieves the class file bytes for the specified wrapper class.
- *
- * @param className the name of the wrapper class
- * @return the current class file bytes as a byte array
- */
- byte[] getClassBytes(String className);
-
- /**
- * Creates an {@code EvalException} corresponding to a user exception. An
- * user exception thrown during
- * {@link ExecutionControl#invoke(java.lang.String, java.lang.String) }
- * should be converted to an {@code EvalException} using this method.
- *
- * @param message the exception message to use (from the user exception)
- * @param exceptionClass the class name of the user exception
- * @param stackElements the stack trace elements to install
- * @return a user API EvalException for the user exception
- */
- EvalException createEvalException(String message, String exceptionClass,
- StackTraceElement[] stackElements);
-
- /**
- * Creates an {@code UnresolvedReferenceException} for the Snippet identifed
- * by the specified identifier. An {@link SPIResolutionException} thrown
- * during {@link ExecutionControl#invoke(java.lang.String, java.lang.String) }
- * should be converted to an {@code UnresolvedReferenceException} using
- * this method.
- * <p>
- * The identifier is an internal id, different from the id in the API. This
- * internal id is returned by {@link SPIResolutionException#id()}.
- *
- * @param id the internal integer identifier
- * @param stackElements the stack trace elements to install
- * @return an {@code UnresolvedReferenceException} for the unresolved
- * reference
- */
- UnresolvedReferenceException createUnresolvedReferenceException(int id,
- StackTraceElement[] stackElements);
-
- /**
* Reports that the execution engine has shutdown.
*/
void closeDown();
+
}
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/spi/SPIResolutionException.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/spi/SPIResolutionException.java Thu Jul 21 20:09:19 2016 -0700
@@ -33,9 +33,6 @@
* <p>
* This exception is seen by the execution engine, but not seen by
* the end user nor through the JShell API.
- *
- * @see ExecutionEnv#createUnresolvedReferenceException(int,
- * java.lang.StackTraceElement[])
*/
@SuppressWarnings("serial") // serialVersionUID intentionally omitted
public class SPIResolutionException extends RuntimeException {
@@ -57,11 +54,9 @@
}
/**
- * Retrieves the internal identifer of the unresolved identifer.
+ * Retrieves the internal identifier of the unresolved identifier.
*
- * @return the internal identifer
- * @see ExecutionEnv#createUnresolvedReferenceException(int,
- * java.lang.StackTraceElement[])
+ * @return the internal identifier
*/
public int id() {
return id;
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/spi/package-info.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/spi/package-info.java Thu Jul 21 20:09:19 2016 -0700
@@ -31,9 +31,9 @@
* implementation includes a default execution engine (currently a remote
* process which is JDI controlled). By implementing the
* {@link jdk.jshell.spi.ExecutionControl} interface and installing it with
- * {@link jdk.jshell.JShell.Builder#executionEngine(jdk.jshell.spi.ExecutionControl) }
+ * {@link jdk.jshell.JShell.Builder#executionEngine(jdk.jshell.spi.ExecutionControl.Generator) }
* other execution engines can be used.
*
- * @see jdk.jshell.execution jdk.jshell.execution for execution implementation support
+ * @see jdk.jshell.execution for execution implementation support
*/
package jdk.jshell.spi;
--- a/langtools/test/jdk/jshell/ComputeFQNsTest.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/jdk/jshell/ComputeFQNsTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -76,7 +76,7 @@
assertInferredFQNs("class X { ArrayList", "ArrayList".length(), false, "java.util.ArrayList");
}
- @Test
+ @Test(enabled = false) //TODO 8161165
public void testSuspendIndexing() throws Throwable {
compiler.compile(outDir, "package test; public class FQNTest { }");
String jarName = "test.jar";
--- a/langtools/test/jdk/jshell/FailOverExecutionControlTest.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/jdk/jshell/FailOverExecutionControlTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -23,23 +23,20 @@
/*
* @test
- * @bug 8131029
- * @summary Test that fail-over works for FailOverExecutionControl
- * @modules jdk.jshell/jdk.internal.jshell.jdi
+ * @bug 8131029 8160127 8159935
+ * @summary Test that fail-over works for fail-over ExecutionControl generators.
+ * @modules jdk.jshell/jdk.jshell.execution
* jdk.jshell/jdk.jshell.spi
* @build KullaTesting ExecutionControlTestBase
* @run testng FailOverExecutionControlTest
*/
-
-import java.util.Collection;
import org.testng.annotations.Test;
import org.testng.annotations.BeforeMethod;
-import jdk.internal.jshell.jdi.FailOverExecutionControl;
-import jdk.internal.jshell.jdi.JDIExecutionControl;
-import jdk.jshell.JShellException;
+import jdk.jshell.execution.JDIDefaultExecutionControl;
import jdk.jshell.spi.ExecutionControl;
import jdk.jshell.spi.ExecutionEnv;
+import static jdk.jshell.execution.Util.failOverExecutionControlGenerator;
@Test
public class FailOverExecutionControlTest extends ExecutionControlTestBase {
@@ -47,56 +44,16 @@
@BeforeMethod
@Override
public void setUp() {
- setUp(new FailOverExecutionControl(
- new AlwaysFailingExecutionControl(),
- new AlwaysFailingExecutionControl(),
- new JDIExecutionControl()));
+ setUp(builder -> builder.executionEngine(failOverExecutionControlGenerator(
+ new AlwaysFailingGenerator(),
+ new AlwaysFailingGenerator(),
+ JDIDefaultExecutionControl.launch())));
}
- class AlwaysFailingExecutionControl implements ExecutionControl {
-
- @Override
- public void start(ExecutionEnv env) throws Exception {
- throw new UnsupportedOperationException("This operation intentionally broken.");
- }
-
- @Override
- public void close() {
- throw new UnsupportedOperationException("This operation intentionally broken.");
- }
-
- @Override
- public boolean addToClasspath(String path) {
- throw new UnsupportedOperationException("This operation intentionally broken.");
- }
+ class AlwaysFailingGenerator implements ExecutionControl.Generator {
@Override
- public String invoke(String classname, String methodname) throws JShellException {
- throw new UnsupportedOperationException("This operation intentionally broken.");
- }
-
- @Override
- public boolean load(Collection<String> classes) {
- throw new UnsupportedOperationException("This operation intentionally broken.");
- }
-
- @Override
- public boolean redefine(Collection<String> classes) {
- throw new UnsupportedOperationException("This operation intentionally broken.");
- }
-
- @Override
- public ClassStatus getClassStatus(String classname) {
- throw new UnsupportedOperationException("This operation intentionally broken.");
- }
-
- @Override
- public void stop() {
- throw new UnsupportedOperationException("This operation intentionally broken.");
- }
-
- @Override
- public String varValue(String classname, String varname) {
+ public ExecutionControl generate(ExecutionEnv env) throws UnsupportedOperationException {
throw new UnsupportedOperationException("This operation intentionally broken.");
}
--- a/langtools/test/jdk/jshell/JDIListeningExecutionControlTest.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/jdk/jshell/JDIListeningExecutionControlTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -23,9 +23,9 @@
/*
* @test
- * @bug 8131029
+ * @bug 8131029 8159935 8160127
* @summary Tests for alternate JDI connector -- listening
- * @modules jdk.jshell/jdk.internal.jshell.jdi
+ * @modules jdk.jshell/jdk.jshell.execution
* @build KullaTesting ExecutionControlTestBase
* @run testng JDIListeningExecutionControlTest
*/
@@ -33,7 +33,7 @@
import org.testng.annotations.Test;
import org.testng.annotations.BeforeMethod;
-import jdk.internal.jshell.jdi.JDIExecutionControl;
+import jdk.jshell.execution.JDIDefaultExecutionControl;
@Test
public class JDIListeningExecutionControlTest extends ExecutionControlTestBase {
@@ -41,6 +41,6 @@
@BeforeMethod
@Override
public void setUp() {
- setUp(new JDIExecutionControl(false));
+ setUp(builder -> builder.executionEngine(JDIDefaultExecutionControl.listen()));
}
}
--- a/langtools/test/jdk/jshell/KullaTesting.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/jdk/jshell/KullaTesting.java Thu Jul 21 20:09:19 2016 -0700
@@ -73,7 +73,6 @@
import static jdk.jshell.Snippet.Status.*;
import static org.testng.Assert.*;
import static jdk.jshell.Snippet.SubKind.METHOD_SUBKIND;
-import jdk.jshell.spi.ExecutionControl;
public class KullaTesting {
@@ -158,10 +157,6 @@
setUp(b -> {});
}
- public void setUp(ExecutionControl ec) {
- setUp(b -> b.executionEngine(ec));
- }
-
public void setUp(Consumer<JShell.Builder> bc) {
inStream = new TestingInputStream();
outStream = new ByteArrayOutputStream();
--- a/langtools/test/jdk/jshell/LocalExecutionControl.java Thu Jul 21 16:45:56 2016 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,313 +0,0 @@
-/*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. 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 jdk.jshell.spi.ExecutionControl;
-import jdk.jshell.spi.ExecutionEnv;
-import jdk.jshell.spi.SPIResolutionException;
-import jdk.jshell.EvalException;
-import jdk.jshell.UnresolvedReferenceException;
-
-import java.io.File;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.security.CodeSource;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * An implementation of ExecutionControl which executes in the same JVM as the
- * JShell core.
- *
- * @author Grigory Ptashko
- */
-class LocalExecutionControl implements ExecutionControl {
- private class REPLClassLoader extends URLClassLoader {
- REPLClassLoader() {
- super(new URL[0]);
- }
-
- @Override
- protected Class<?> findClass(String name) throws ClassNotFoundException {
- debug("findClass %s\n", name);
- byte[] b = execEnv.getClassBytes(name);
- if (b == null) {
- return super.findClass(name);
- }
- return super.defineClass(name, b, 0, b.length, (CodeSource)null);
- }
-
- @Override
- public void addURL(URL url) {
- super.addURL(url);
- }
- }
-
- private ExecutionEnv execEnv;
- private final Object STOP_LOCK = new Object();
- private boolean userCodeRunning = false;
- private REPLClassLoader loader = new REPLClassLoader();
- private final Map<String, Class<?>> klasses = new TreeMap<>();
- private final Map<String, byte[]> classBytes = new HashMap<>();
- private ThreadGroup execThreadGroup;
-
- @Override
- public void start(ExecutionEnv execEnv) throws Exception {
- this.execEnv = execEnv;
-
- debug("Process-local code snippets execution control started");
- }
-
- @Override
- public void close() {
- }
-
- @Override
- public boolean load(Collection<String> classes) {
- try {
- loadLocal(classes);
-
- return true;
- } catch (ClassNotFoundException | ClassCastException ex) {
- debug(ex, "Exception on load operation");
- }
-
- return false;
- }
-
- @Override
- public String invoke(String classname, String methodname) throws EvalException, UnresolvedReferenceException {
- try {
- synchronized (STOP_LOCK) {
- userCodeRunning = true;
- }
-
- // Invoke executable entry point in loaded code
- Class<?> klass = klasses.get(classname);
- if (klass == null) {
- debug("Invoke failure: no such class loaded %s\n", classname);
-
- return "";
- }
-
- Method doitMethod;
- try {
- this.getClass().getModule().addReads(klass.getModule());
- this.getClass().getModule().addExports(SPIResolutionException.class.getPackage()
- .getName(), klass.getModule());
- doitMethod = klass.getDeclaredMethod(methodname, new Class<?>[0]);
- doitMethod.setAccessible(true);
-
- execThreadGroup = new ThreadGroup("JShell process local execution");
-
- AtomicReference<InvocationTargetException> iteEx = new AtomicReference<>();
- AtomicReference<IllegalAccessException> iaeEx = new AtomicReference<>();
- AtomicReference<NoSuchMethodException> nmeEx = new AtomicReference<>();
- AtomicReference<Boolean> stopped = new AtomicReference<>(false);
-
- Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
- if (e instanceof InvocationTargetException) {
- if (e.getCause() instanceof ThreadDeath) {
- stopped.set(true);
- } else {
- iteEx.set((InvocationTargetException)e);
- }
- } else if (e instanceof IllegalAccessException) {
- iaeEx.set((IllegalAccessException)e);
- } else if (e instanceof NoSuchMethodException) {
- nmeEx.set((NoSuchMethodException)e);
- } else if (e instanceof ThreadDeath) {
- stopped.set(true);
- }
- });
-
- final Object[] res = new Object[1];
- Thread snippetThread = new Thread(execThreadGroup, () -> {
- try {
- res[0] = doitMethod.invoke(null, new Object[0]);
- } catch (InvocationTargetException e) {
- if (e.getCause() instanceof ThreadDeath) {
- stopped.set(true);
- } else {
- iteEx.set(e);
- }
- } catch (IllegalAccessException e) {
- iaeEx.set(e);
- } catch (ThreadDeath e) {
- stopped.set(true);
- }
- });
-
- snippetThread.start();
- Thread[] threadList = new Thread[execThreadGroup.activeCount()];
- execThreadGroup.enumerate(threadList);
- for (Thread thread : threadList) {
- if (thread != null)
- thread.join();
- }
-
- if (stopped.get()) {
- debug("Killed.");
-
- return "";
- }
-
- if (iteEx.get() != null) {
- throw iteEx.get();
- } else if (nmeEx.get() != null) {
- throw nmeEx.get();
- } else if (iaeEx.get() != null) {
- throw iaeEx.get();
- }
-
- return valueString(res[0]);
- } catch (InvocationTargetException ex) {
- Throwable cause = ex.getCause();
- StackTraceElement[] elems = cause.getStackTrace();
- if (cause instanceof SPIResolutionException) {
- int id = ((SPIResolutionException)cause).id();
-
- throw execEnv.createUnresolvedReferenceException(id, elems);
- } else {
- throw execEnv.createEvalException(cause.getMessage() == null ?
- "<none>" : cause.getMessage(), cause.getClass().getName(), elems);
- }
- } catch (NoSuchMethodException | IllegalAccessException | InterruptedException ex) {
- debug(ex, "Invoke failure");
- }
- } finally {
- synchronized (STOP_LOCK) {
- userCodeRunning = false;
- }
- }
-
- return "";
- }
-
- @Override
- @SuppressWarnings("deprecation")
- public void stop() {
- synchronized (STOP_LOCK) {
- if (!userCodeRunning)
- return;
-
- if (execThreadGroup == null) {
- debug("Process-local code snippets thread group is null. Aborting stop.");
-
- return;
- }
-
- execThreadGroup.stop();
- }
- }
-
- @Override
- public String varValue(String classname, String varname) {
- Class<?> klass = klasses.get(classname);
- if (klass == null) {
- debug("Var value failure: no such class loaded %s\n", classname);
-
- return "";
- }
- try {
- this.getClass().getModule().addReads(klass.getModule());
- Field var = klass.getDeclaredField(varname);
- var.setAccessible(true);
- Object res = var.get(null);
-
- return valueString(res);
- } catch (Exception ex) {
- debug("Var value failure: no such field %s.%s\n", classname, varname);
- }
-
- return "";
- }
-
- @Override
- public boolean addToClasspath(String cp) {
- // Append to the claspath
- for (String path : cp.split(File.pathSeparator)) {
- try {
- loader.addURL(new File(path).toURI().toURL());
- } catch (MalformedURLException e) {
- throw new InternalError("Classpath addition failed: " + cp, e);
- }
- }
-
- return true;
- }
-
- @Override
- public boolean redefine(Collection<String> classes) {
- return false;
- }
-
- @Override
- public ClassStatus getClassStatus(String classname) {
- if (!classBytes.containsKey(classname)) {
- return ClassStatus.UNKNOWN;
- } else if (!Arrays.equals(classBytes.get(classname), execEnv.getClassBytes(classname))) {
- return ClassStatus.NOT_CURRENT;
- } else {
- return ClassStatus.CURRENT;
- }
- }
-
- private void loadLocal(Collection<String> classes) throws ClassNotFoundException {
- for (String className : classes) {
- Class<?> klass = loader.loadClass(className);
- klasses.put(className, klass);
- classBytes.put(className, execEnv.getClassBytes(className));
- klass.getDeclaredMethods();
- }
- }
-
- private void debug(String format, Object... args) {
- //debug(execEnv.state(), execEnv.userErr(), flags, format, args);
- }
-
- private void debug(Exception ex, String where) {
- //debug(execEnv.state(), execEnv.userErr(), ex, where);
- }
-
- private static String valueString(Object value) {
- if (value == null) {
- return "null";
- } else if (value instanceof String) {
- return "\"" + (String)value + "\"";
- } else if (value instanceof Character) {
- return "'" + value + "'";
- } else {
- return value.toString();
- }
- }
-}
--- a/langtools/test/jdk/jshell/UserExecutionControlTest.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/jdk/jshell/UserExecutionControlTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -23,13 +23,14 @@
/*
* @test
- * @bug 8156101
+ * @bug 8156101 8159935 8159122
* @summary Tests for ExecutionControl SPI
- * @build KullaTesting LocalExecutionControl ExecutionControlTestBase
+ * @build KullaTesting ExecutionControlTestBase
* @run testng UserExecutionControlTest
*/
+import jdk.jshell.execution.LocalExecutionControl;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import org.testng.annotations.BeforeMethod;
@@ -40,7 +41,7 @@
@BeforeMethod
@Override
public void setUp() {
- setUp(new LocalExecutionControl());
+ setUp(builder -> builder.executionEngine(LocalExecutionControl.create()));
}
public void verifyLocal() throws ClassNotFoundException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/jshell/UserJDIUserRemoteTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,284 @@
+/*
+ * 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 8160128 8159935
+ * @summary Tests for Aux channel, custom remote agents, custom JDI implementations.
+ * @build KullaTesting ExecutionControlTestBase
+ * @run testng UserJDIUserRemoteTest
+ */
+import java.io.ByteArrayOutputStream;
+import org.testng.annotations.Test;
+import org.testng.annotations.BeforeMethod;
+import jdk.jshell.Snippet;
+import static jdk.jshell.Snippet.Status.OVERWRITTEN;
+import static jdk.jshell.Snippet.Status.VALID;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.net.ServerSocket;
+import java.util.ArrayList;
+import java.util.List;
+import com.sun.jdi.VMDisconnectedException;
+import com.sun.jdi.VirtualMachine;
+import jdk.jshell.VarSnippet;
+import jdk.jshell.execution.DirectExecutionControl;
+import jdk.jshell.execution.JDIExecutionControl;
+import jdk.jshell.execution.JDIInitiator;
+import jdk.jshell.execution.Util;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.Socket;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+import jdk.jshell.spi.ExecutionControl;
+import jdk.jshell.spi.ExecutionControl.ExecutionControlException;
+import jdk.jshell.spi.ExecutionEnv;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+import static jdk.jshell.execution.Util.forwardExecutionControlAndIO;
+import static jdk.jshell.execution.Util.remoteInput;
+
+@Test
+public class UserJDIUserRemoteTest extends ExecutionControlTestBase {
+
+ ExecutionControl currentEC;
+ ByteArrayOutputStream auxStream;
+
+ @BeforeMethod
+ @Override
+ public void setUp() {
+ auxStream = new ByteArrayOutputStream();
+ setUp(builder -> builder.executionEngine(MyExecutionControl.create(this)));
+ }
+
+ public void testVarValue() {
+ VarSnippet dv = varKey(assertEval("double aDouble = 1.5;"));
+ String vd = getState().varValue(dv);
+ assertEquals(vd, "1.5");
+ assertEquals(auxStream.toString(), "aDouble");
+ }
+
+ public void testExtension() throws ExecutionControlException {
+ assertEval("42;");
+ Object res = currentEC.extensionCommand("FROG", "test");
+ assertEquals(res, "ribbit");
+ }
+
+ public void testRedefine() {
+ Snippet vx = varKey(assertEval("int x;"));
+ Snippet mu = methodKey(assertEval("int mu() { return x * 4; }"));
+ Snippet c = classKey(assertEval("class C { String v() { return \"#\" + mu(); } }"));
+ assertEval("C c0 = new C();");
+ assertEval("c0.v();", "\"#0\"");
+ assertEval("int x = 10;", "10",
+ ste(MAIN_SNIPPET, VALID, VALID, false, null),
+ ste(vx, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+ assertEval("c0.v();", "\"#40\"");
+ assertEval("C c = new C();");
+ assertEval("c.v();", "\"#40\"");
+ assertEval("int mu() { return x * 3; }",
+ ste(MAIN_SNIPPET, VALID, VALID, false, null),
+ ste(mu, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+ assertEval("c.v();", "\"#30\"");
+ assertEval("class C { String v() { return \"@\" + mu(); } }",
+ ste(MAIN_SNIPPET, VALID, VALID, false, null),
+ ste(c, VALID, OVERWRITTEN, false, MAIN_SNIPPET));
+ assertEval("c0.v();", "\"@30\"");
+ assertEval("c = new C();");
+ assertEval("c.v();", "\"@30\"");
+ assertActiveKeys();
+ }
+}
+
+class MyExecutionControl extends JDIExecutionControl {
+
+ private static final String REMOTE_AGENT = MyRemoteExecutionControl.class.getName();
+
+ private VirtualMachine vm;
+ private Process process;
+
+ /**
+ * Creates an ExecutionControl instance based on a JDI
+ * {@code LaunchingConnector}.
+ *
+ * @return the generator
+ */
+ public static ExecutionControl.Generator create(UserJDIUserRemoteTest test) {
+ return env -> make(env, test);
+ }
+
+ /**
+ * Creates an ExecutionControl instance based on a JDI
+ * {@code ListeningConnector} or {@code LaunchingConnector}.
+ *
+ * Initialize JDI and use it to launch the remote JVM. Set-up a socket for
+ * commands and results. This socket also transports the user
+ * input/output/error.
+ *
+ * @param env the context passed by
+ * {@link jdk.jshell.spi.ExecutionControl#start(jdk.jshell.spi.ExecutionEnv) }
+ * @return the channel
+ * @throws IOException if there are errors in set-up
+ */
+ static MyExecutionControl make(ExecutionEnv env, UserJDIUserRemoteTest test) throws IOException {
+ try (final ServerSocket listener = new ServerSocket(0)) {
+ // timeout after 60 seconds
+ listener.setSoTimeout(60000);
+ int port = listener.getLocalPort();
+
+ // Set-up the JDI connection
+ List<String> opts = new ArrayList<>(env.extraRemoteVMOptions());
+ opts.add("-classpath");
+ opts.add(System.getProperty("java.class.path")
+ + System.getProperty("path.separator")
+ + System.getProperty("user.dir"));
+ JDIInitiator jdii = new JDIInitiator(port,
+ opts, REMOTE_AGENT, true);
+ VirtualMachine vm = jdii.vm();
+ Process process = jdii.process();
+
+ List<Consumer<String>> deathListeners = new ArrayList<>();
+ deathListeners.add(s -> env.closeDown());
+ Util.detectJDIExitEvent(vm, s -> {
+ for (Consumer<String> h : deathListeners) {
+ h.accept(s);
+ }
+ });
+
+ // Set-up the commands/reslts on the socket. Piggy-back snippet
+ // output.
+ Socket socket = listener.accept();
+ // out before in -- match remote creation so we don't hang
+ ObjectOutput cmdout = new ObjectOutputStream(socket.getOutputStream());
+ Map<String, OutputStream> io = new HashMap<>();
+ io.put("out", env.userOut());
+ io.put("err", env.userErr());
+ io.put("aux", test.auxStream);
+ ObjectInput cmdin = remoteInput(socket.getInputStream(), io);
+ MyExecutionControl myec = new MyExecutionControl(cmdout, cmdin, vm, process, deathListeners);
+ test.currentEC = myec;
+ return myec;
+ }
+ }
+
+ /**
+ * Create an instance.
+ *
+ * @param out the output for commands
+ * @param in the input for responses
+ */
+ private MyExecutionControl(ObjectOutput out, ObjectInput in,
+ VirtualMachine vm, Process process,
+ List<Consumer<String>> deathListeners) {
+ super(out, in);
+ this.vm = vm;
+ this.process = process;
+ deathListeners.add(s -> disposeVM());
+ }
+
+ @Override
+ public void close() {
+ super.close();
+ disposeVM();
+ }
+
+ private synchronized void disposeVM() {
+ try {
+ if (vm != null) {
+ vm.dispose(); // This could NPE, so it is caught below
+ vm = null;
+ }
+ } catch (VMDisconnectedException ex) {
+ // Ignore if already closed
+ } catch (Throwable e) {
+ fail("disposeVM threw: " + e);
+ } finally {
+ if (process != null) {
+ process.destroy();
+ process = null;
+ }
+ }
+ }
+
+ @Override
+ protected synchronized VirtualMachine vm() throws EngineTerminationException {
+ if (vm == null) {
+ throw new EngineTerminationException("VM closed");
+ } else {
+ return vm;
+ }
+ }
+
+}
+
+class MyRemoteExecutionControl extends DirectExecutionControl implements ExecutionControl {
+
+ static PrintStream auxPrint;
+
+ /**
+ * Launch the agent, connecting to the JShell-core over the socket specified
+ * in the command-line argument.
+ *
+ * @param args standard command-line arguments, expectation is the socket
+ * number is the only argument
+ * @throws Exception any unexpected exception
+ */
+ public static void main(String[] args) throws Exception {
+ try {
+ String loopBack = null;
+ Socket socket = new Socket(loopBack, Integer.parseInt(args[0]));
+ InputStream inStream = socket.getInputStream();
+ OutputStream outStream = socket.getOutputStream();
+ Map<String, Consumer<OutputStream>> chans = new HashMap<>();
+ chans.put("out", st -> System.setOut(new PrintStream(st, true)));
+ chans.put("err", st -> System.setErr(new PrintStream(st, true)));
+ chans.put("aux", st -> { auxPrint = new PrintStream(st, true); });
+ forwardExecutionControlAndIO(new MyRemoteExecutionControl(), inStream, outStream, chans);
+ } catch (Throwable ex) {
+ throw ex;
+ }
+ }
+
+ @Override
+ public String varValue(String className, String varName)
+ throws RunException, EngineTerminationException, InternalException {
+ auxPrint.print(varName);
+ return super.varValue(className, varName);
+ }
+
+ @Override
+ public Object extensionCommand(String className, Object arg)
+ throws RunException, EngineTerminationException, InternalException {
+ if (!arg.equals("test")) {
+ throw new InternalException("expected extensionCommand arg to be 'test' got: " + arg);
+ }
+ return "ribbit";
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/8161985/T8161985a.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,28 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8161985
+ * @summary Spurious override of Object.getClass leads to NPE
+ * @compile/fail/ref=T8161985a.out -XDrawDiagnostics T8161985a.java
+ */
+
+class T8161985 {
+ public static void main(String [] arg) {
+ T8161985 t = new T8161985();
+ t.getClass();
+
+ }
+ public void getClass() {
+ Fred1 f = new Fred1();
+ System.out.println( "fred classname: " + f.getClassName());
+ }
+
+
+ abstract class Fred {
+ public String getClassName() {
+ return this.getClass().getSimpleName();
+ }
+ }
+
+ class Fred1 extends Fred {
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/8161985/T8161985a.out Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,2 @@
+T8161985a.java:14:17: compiler.err.override.meth: (compiler.misc.cant.override: getClass(), T8161985, getClass(), java.lang.Object), final
+1 error
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/8161985/T8161985b.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,14 @@
+/*
+ * @test /nodynamiccopyright/
+ * @bug 8161985
+ * @summary Spurious override of Object.getClass leads to NPE
+ * @compile/fail/ref=T8161985b.out -XDrawDiagnostics T8161985b.java
+ */
+
+class T8161985b {
+ public String getClass() { return ""; }
+
+ void test() {
+ this.getClass().getSimpleName();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/8161985/T8161985b.out Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,3 @@
+T8161985b.java:9:18: compiler.err.override.meth: (compiler.misc.cant.override: getClass(), T8161985b, getClass(), java.lang.Object), final
+T8161985b.java:12:22: compiler.err.cant.resolve.location.args: kindname.method, getSimpleName, , , (compiler.misc.location: kindname.class, java.lang.String, null)
+2 errors
--- a/langtools/test/tools/javac/Diagnostics/6722234/T6722234a.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/Diagnostics/6722234/T6722234a.java Thu Jul 21 20:09:19 2016 -0700
@@ -3,8 +3,8 @@
* @bug 6722234
* @summary javac diagnostics need better integration with the type-system
* @author mcimadamore
- * @compile/fail/ref=T6722234a_1.out -XDrawDiagnostics -XDdiags=disambiguateTvars T6722234a.java
- * @compile/fail/ref=T6722234a_2.out -XDrawDiagnostics -XDdiags=disambiguateTvars,where T6722234a.java
+ * @compile/fail/ref=T6722234a_1.out -XDrawDiagnostics -diags:formatterOptions=disambiguateTvars T6722234a.java
+ * @compile/fail/ref=T6722234a_2.out -XDrawDiagnostics -diags:formatterOptions=disambiguateTvars,where T6722234a.java
*/
class T6722234a<T extends String> {
--- a/langtools/test/tools/javac/Diagnostics/6722234/T6722234b.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/Diagnostics/6722234/T6722234b.java Thu Jul 21 20:09:19 2016 -0700
@@ -3,8 +3,8 @@
* @bug 6722234 8078024
* @summary javac diagnostics need better integration with the type-system
* @author mcimadamore
- * @compile/fail/ref=T6722234b_1.out -XDrawDiagnostics -XDdiags=simpleNames T6722234b.java
- * @compile/fail/ref=T6722234b_2.out -XDrawDiagnostics -XDdiags=simpleNames,where T6722234b.java
+ * @compile/fail/ref=T6722234b_1.out -XDrawDiagnostics -diags:formatterOptions=simpleNames T6722234b.java
+ * @compile/fail/ref=T6722234b_2.out -XDrawDiagnostics -diags:formatterOptions=simpleNames,where T6722234b.java
*/
import java.util.*;
--- a/langtools/test/tools/javac/Diagnostics/6722234/T6722234c.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/Diagnostics/6722234/T6722234c.java Thu Jul 21 20:09:19 2016 -0700
@@ -3,7 +3,7 @@
* @bug 6722234
* @summary javac diagnostics need better integration with the type-system
* @author mcimadamore
- * @compile/fail/ref=T6722234c.out -XDrawDiagnostics -XDdiags=simpleNames T6722234c.java
+ * @compile/fail/ref=T6722234c.out -XDrawDiagnostics -diags:formatterOptions=simpleNames T6722234c.java
*/
class T6722234c {
--- a/langtools/test/tools/javac/Diagnostics/6722234/T6722234d.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/Diagnostics/6722234/T6722234d.java Thu Jul 21 20:09:19 2016 -0700
@@ -3,8 +3,8 @@
* @bug 6722234 8078024
* @summary javac diagnostics need better integration with the type-system
* @author mcimadamore
- * @compile/fail/ref=T6722234d_1.out -XDrawDiagnostics -XDdiags=where T6722234d.java
- * @compile/fail/ref=T6722234d_2.out -XDrawDiagnostics -XDdiags=where,simpleNames T6722234d.java
+ * @compile/fail/ref=T6722234d_1.out -XDrawDiagnostics -diags:formatterOptions=where T6722234d.java
+ * @compile/fail/ref=T6722234d_2.out -XDrawDiagnostics -diags:formatterOptions=where,simpleNames T6722234d.java
*/
class T6722234d {
--- a/langtools/test/tools/javac/Diagnostics/6769027/T6769027.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/Diagnostics/6769027/T6769027.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 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
@@ -59,8 +59,8 @@
enum CaretKind {
DEFAULT("", ""),
- SHOW("showCaret","true"),
- HIDE("showCaret","false");
+ SHOW("diags.showCaret","true"),
+ HIDE("diags.showCaret","false");
String key;
String value;
@@ -81,8 +81,8 @@
enum SourceLineKind {
DEFAULT("", ""),
- AFTER_SUMMARY("sourcePosition", "top"),
- BOTTOM("sourcePosition", "bottom");
+ AFTER_SUMMARY("diags.sourcePosition", "top"),
+ BOTTOM("diags.sourcePosition", "bottom");
String key;
String value;
@@ -110,9 +110,9 @@
void init(Options opts) {
if (this != DEFAULT) {
- String flags = opts.get("diags");
+ String flags = opts.get("diags.formatterOptions");
flags = flags == null ? flag : flags + "," + flag;
- opts.put("diags", flags);
+ opts.put("diags.formatterOptions", flags);
}
}
@@ -136,9 +136,9 @@
void init(Options opts) {
if (this != DEFAULT) {
- String flags = opts.get("diags");
+ String flags = opts.get("diags.formatterOptions");
flags = flags == null ? flag : flags + "," + flag;
- opts.put("diags", flags);
+ opts.put("diags.formatterOptions", flags);
}
}
@@ -243,11 +243,11 @@
}
enum MultilinePolicy {
- ENABLED(0, "multilinePolicy", "enabled"),
- DISABLED(1, "multilinePolicy", "disabled"),
- LIMIT_LENGTH(2, "multilinePolicy", "limit:1:*"),
- LIMIT_DEPTH(3, "multilinePolicy", "limit:*:1"),
- LIMIT_BOTH(4, "multilinePolicy", "limit:1:1");
+ ENABLED(0, "diags.multilinePolicy", "enabled"),
+ DISABLED(1, "diags.multilinePolicy", "disabled"),
+ LIMIT_LENGTH(2, "diags.multilinePolicy", "limit:1:*"),
+ LIMIT_DEPTH(3, "diags.multilinePolicy", "limit:*:1"),
+ LIMIT_BOTH(4, "diags.multilinePolicy", "limit:1:1");
String name;
String value;
@@ -371,7 +371,7 @@
indentString += (detailsIndent == IndentKind.CUSTOM) ? "|3" : "|0";
indentString += (sourceIndent == IndentKind.CUSTOM) ? "|3" : "|0";
indentString += (subdiagsIndent == IndentKind.CUSTOM) ? "|3" : "|0";
- options.put("diagsIndentation", indentString);
+ options.put("diags.indent", indentString);
MyLog log = new MyLog(ctx);
JavacMessages messages = JavacMessages.instance(ctx);
messages.add(locale -> ResourceBundle.getBundle("tester", locale));
--- a/langtools/test/tools/javac/Diagnostics/6862608/T6862608a.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/Diagnostics/6862608/T6862608a.java Thu Jul 21 20:09:19 2016 -0700
@@ -3,7 +3,7 @@
* @bug 6862608
* @summary rich diagnostic sometimes contain wrong type variable numbering
* @author mcimadamore
- * @compile/fail/ref=T6862608a.out -XDrawDiagnostics -XDdiags=disambiguateTvars,where T6862608a.java
+ * @compile/fail/ref=T6862608a.out -XDrawDiagnostics -diags:formatterOptions=disambiguateTvars,where T6862608a.java
*/
--- a/langtools/test/tools/javac/Diagnostics/6862608/T6862608b.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/Diagnostics/6862608/T6862608b.java Thu Jul 21 20:09:19 2016 -0700
@@ -3,7 +3,7 @@
* @bug 6862608
* @summary rich diagnostic sometimes contain wrong type variable numbering
* @author mcimadamore
- * @compile/fail/ref=T6862608b.out -XDrawDiagnostics -XDdiags=disambiguateTvars,where T6862608b.java
+ * @compile/fail/ref=T6862608b.out -XDrawDiagnostics -diags:formatterOptions=disambiguateTvars,where T6862608b.java
*/
class T66862608b<T extends String, S> {
--- a/langtools/test/tools/javac/Diagnostics/7010608/Test.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/Diagnostics/7010608/Test.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
@@ -46,9 +46,9 @@
try {
test(Arrays.<String>asList(),
"myfo://test:1: error: cannot find symbol");
- test(Arrays.asList("-XDdiagsFormat=OLD"),
+ test(Arrays.asList("-diags:layout=OLD"),
"myfo://test:1: cannot find symbol");
- test(Arrays.asList("-XDoldDiags"),
+ test(Arrays.asList("-diags:legacy"),
"myfo://test:1: cannot find symbol");
} finally {
Locale.setDefault(prev);
--- a/langtools/test/tools/javac/Diagnostics/8010387/T8010387.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/Diagnostics/8010387/T8010387.java Thu Jul 21 20:09:19 2016 -0700
@@ -2,7 +2,7 @@
* @test /nodynamiccopyright/
* @bug 8010387
* @summary rich diagnostic sometimes contain wrong type variable numbering
- * @compile/fail/ref=T8010387.out -XDrawDiagnostics -XDdiags=disambiguateTvars,where T8010387.java
+ * @compile/fail/ref=T8010387.out -XDrawDiagnostics -diags:formatterOptions=disambiguateTvars,where T8010387.java
*/
abstract class T8010387<X> {
--- a/langtools/test/tools/javac/InterfaceMemberClassModifiers.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/InterfaceMemberClassModifiers.java Thu Jul 21 20:09:19 2016 -0700
@@ -4,7 +4,7 @@
* @summary Verify that invalid access modifiers on interface members don't cause crash.
* @author maddox
*
- * @compile/fail/ref=InterfaceMemberClassModifiers.out -XDdiags=%b:%l:%_%m InterfaceMemberClassModifiers.java
+ * @compile/fail/ref=InterfaceMemberClassModifiers.out -diags:layout=%b:%l:%_%m InterfaceMemberClassModifiers.java
*/
public interface InterfaceMemberClassModifiers {
--- a/langtools/test/tools/javac/T5003235/T5003235a.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/T5003235/T5003235a.java Thu Jul 21 20:09:19 2016 -0700
@@ -3,7 +3,7 @@
* @bug 5003235
* @summary Private inner class accessible from subclasses
* @author Peter von der Ah\u00e9
- * @compile/fail/ref=T5003235a.out -XDdiags=%b:%l:%_%m T5003235a.java
+ * @compile/fail/ref=T5003235a.out -diags:layout=%b:%l:%_%m T5003235a.java
*/
class Super {
--- a/langtools/test/tools/javac/T5003235/T5003235b.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/T5003235/T5003235b.java Thu Jul 21 20:09:19 2016 -0700
@@ -3,7 +3,7 @@
* @bug 5003235
* @summary Accessibility of private inner class
* @author Peter von der Ah\u00e9
- * @compile/fail/ref=T5003235b.out -XDdiags=%b:%l:%_%m T5003235b.java
+ * @compile/fail/ref=T5003235b.out -diags:layout=%b:%l:%_%m T5003235b.java
*/
class Outer {
--- a/langtools/test/tools/javac/T6214885.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/T6214885.java Thu Jul 21 20:09:19 2016 -0700
@@ -2,8 +2,8 @@
* @test /nodynamiccopyright/
* @bug 6214885
* @summary This test exercises features provided by the new internal Diagnostics API
- * @compile/fail/ref=T6214885a.out -XDdiags=%b:%l%_%t%m|%p%m T6214885.java
- * @compile/fail/ref=T6214885b.out -XDdiags=%b:%l:%c%_%t%m|%p%m T6214885.java
+ * @compile/fail/ref=T6214885a.out -diags:layout=%b:%l%_%t%m|%p%m T6214885.java
+ * @compile/fail/ref=T6214885b.out -diags:layout=%b:%l:%c%_%t%m|%p%m T6214885.java
*/
class T6214885
{
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/T8161383/LookingForOperatorSymbolsAtWrongPlaceTest.java Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,18 @@
+/**
+ * @test /nodynamiccopyright/
+ * @bug 8161383
+ * @summary javac is looking for operator symbols at the wrong place
+ * @compile LookingForOperatorSymbolsAtWrongPlaceTest.java
+ */
+
+public class LookingForOperatorSymbolsAtWrongPlaceTest {
+ class Base {
+ protected int i = 1;
+ }
+
+ class Sub extends Base {
+ void func(){
+ Sub.super.i += 10;
+ }
+ }
+}
--- a/langtools/test/tools/javac/api/6731573/T6731573.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/api/6731573/T6731573.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 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
@@ -62,8 +62,8 @@
enum SourceLine {
STANDARD(null),
- ENABLED("-XDshowSource=true"),
- DISABLED("-XDshowSource=false");
+ ENABLED("-diags:showSource=true"),
+ DISABLED("-diags:showSource=false");
String optValue;
--- a/langtools/test/tools/javac/diags/CheckResourceKeys.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/diags/CheckResourceKeys.java Thu Jul 21 20:09:19 2016 -0700
@@ -263,6 +263,9 @@
// ignore shouldstop flag names
if (cs.startsWith("shouldstop."))
continue;
+ // ignore diagsformat flag names
+ if (cs.startsWith("diags."))
+ continue;
// explicit known exceptions
if (noResourceRequired.contains(cs))
continue;
--- a/langtools/test/tools/javac/diags/examples/WhereCaptured.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/diags/examples/WhereCaptured.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 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 @@
// key: compiler.err.cant.apply.symbol
// key: compiler.misc.incompatible.eq.bounds
// key: compiler.misc.captured.type
-// options: -XDdiags=where,simpleNames
+// options: -diags:formatterOptions=where,simpleNames
// run: simple
import java.util.*;
--- a/langtools/test/tools/javac/diags/examples/WhereCaptured1.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/diags/examples/WhereCaptured1.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 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
@@ -29,7 +29,7 @@
// key: compiler.misc.incompatible.eq.bounds
// key: compiler.misc.captured.type
// key: compiler.misc.type.null
-// options: -XDdiags=where,simpleNames
+// options: -diags:formatterOptions=where,simpleNames
// run: simple
import java.util.*;
--- a/langtools/test/tools/javac/diags/examples/WhereFreshTvar.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/diags/examples/WhereFreshTvar.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 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
@@ -25,7 +25,7 @@
// key: compiler.misc.where.description.typevar
// key: compiler.err.prob.found.req
// key: compiler.misc.inconvertible.types
-// options: -XDdiags=where,simpleNames
+// options: -diags:formatterOptions=where,simpleNames
// run: simple
import java.util.*;
--- a/langtools/test/tools/javac/diags/examples/WhereIntersection.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/diags/examples/WhereIntersection.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 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,7 +26,7 @@
// key: compiler.misc.where.description.intersection.1
// key: compiler.misc.where.intersection
// key: compiler.err.prob.found.req
-// options: -XDdiags=where
+// options: -diags:formatterOptions=where
// run: simple
class WhereIntersection {
--- a/langtools/test/tools/javac/diags/examples/WhereIntersection2.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/diags/examples/WhereIntersection2.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 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
@@ -29,7 +29,7 @@
// key: compiler.misc.where.description.intersection
// key: compiler.misc.where.intersection
// key: compiler.err.prob.found.req
-// options: -XDdiags=where
+// options: -diags:formatterOptions=where
// run: simple
class WhereIntersection2 {
--- a/langtools/test/tools/javac/diags/examples/WhereTypeVar.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/diags/examples/WhereTypeVar.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 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,7 +27,7 @@
// key: compiler.err.cant.apply.symbol
// key: compiler.misc.no.conforming.assignment.exists
// key: compiler.misc.inconvertible.types
-// options: -XDdiags=where,disambiguateTvars
+// options: -diags:formatterOptions=where,disambiguateTvars
// run: simple
class WhereTypeVar<T extends String> {
--- a/langtools/test/tools/javac/diags/examples/WhereTypeVar2.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/diags/examples/WhereTypeVar2.java Thu Jul 21 20:09:19 2016 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 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
@@ -25,7 +25,7 @@
// key: compiler.misc.where.description.typevar
// key: compiler.misc.where.typevar
// key: compiler.err.prob.found.req
-// options: -XDdiags=where
+// options: -diags:formatterOptions=where
// run: simple
class WhereTypeVar2 {
--- a/langtools/test/tools/javac/missingSuperRecovery/MissingSuperRecovery.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/missingSuperRecovery/MissingSuperRecovery.java Thu Jul 21 20:09:19 2016 -0700
@@ -5,7 +5,7 @@
* class is no longer available during a subsequent compilation.
* @author maddox
* @build impl
- * @compile/fail/ref=MissingSuperRecovery.out -XDdiags=%b:%l:%_%m MissingSuperRecovery.java
+ * @compile/fail/ref=MissingSuperRecovery.out -diags:layout=%b:%l:%_%m MissingSuperRecovery.java
*/
// Requires "golden" class file 'impl.class', which contains
--- a/langtools/test/tools/javac/protectedAccess/ProtectedMemberAccess2.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/protectedAccess/ProtectedMemberAccess2.java Thu Jul 21 20:09:19 2016 -0700
@@ -4,7 +4,7 @@
* @summary Verify correct implementation of JLS2e 6.6.2.1
* @author maddox
*
- * @compile/fail/ref=ProtectedMemberAccess2.out -XDdiags=-simpleNames -XDdiagsFormat=%b:%l:%_%m ProtectedMemberAccess2.java
+ * @compile/fail/ref=ProtectedMemberAccess2.out -diags:formatterOptions=-simpleNames;layout=%b:%l:%_%m ProtectedMemberAccess2.java
*/
// 71 errors expected.
--- a/langtools/test/tools/javac/protectedAccess/ProtectedMemberAccess3.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/protectedAccess/ProtectedMemberAccess3.java Thu Jul 21 20:09:19 2016 -0700
@@ -4,7 +4,7 @@
* @summary Verify correct implementation of JLS2e 6.6.2.1
* @author maddox
*
- * @compile/fail/ref=ProtectedMemberAccess3.out -XDdiags=-simpleNames -XDdiagsFormat=%b:%l:%_%m ProtectedMemberAccess3.java
+ * @compile/fail/ref=ProtectedMemberAccess3.out -diags:formatterOptions=-simpleNames;layout=%b:%l:%_%m ProtectedMemberAccess3.java
*/
// 46 errors expected.
--- a/langtools/test/tools/javac/protectedAccess/ProtectedMemberAccess4.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/protectedAccess/ProtectedMemberAccess4.java Thu Jul 21 20:09:19 2016 -0700
@@ -4,7 +4,7 @@
* @summary Verify correct implementation of JLS2e 6.6.2.1
* @author maddox
*
- * @compile/fail/ref=ProtectedMemberAccess4.out -XDdiags=-simpleNames -XDdiagsFormat=%b:%l:%_%m ProtectedMemberAccess4.java
+ * @compile/fail/ref=ProtectedMemberAccess4.out -diags:formatterOptions=-simpleNames;layout=%b:%l:%_%m ProtectedMemberAccess4.java
*/
// 33 errors expected.
--- a/langtools/test/tools/javac/unicode/UnicodeNewline.java Thu Jul 21 16:45:56 2016 +0000
+++ b/langtools/test/tools/javac/unicode/UnicodeNewline.java Thu Jul 21 20:09:19 2016 -0700
@@ -3,7 +3,7 @@
* @bug 4739428 4785453
* @summary when \u000a is used, diagnostics are reported on the wrong line.
*
- * @compile/fail/ref=UnicodeNewline.out -XDdiags=%b:%l:%_%m UnicodeNewline.java
+ * @compile/fail/ref=UnicodeNewline.out -diags:layout=%b:%l:%_%m UnicodeNewline.java
*/
class UnicodeNewline {
--- a/nashorn/.hgtags Thu Jul 21 16:45:56 2016 +0000
+++ b/nashorn/.hgtags Thu Jul 21 20:09:19 2016 -0700
@@ -361,3 +361,4 @@
a32d419d73fe881a935b567c57dab9bfe3ed5f92 jdk-9+125
ee90c69a18409533df8f7b602044bf966a28381a jdk-9+126
ff07be6106fa56b72c163244f45a3ecb4c995564 jdk-9+127
+5a189c5b396c353786343b590f6c19a5d929f01d jdk-9+128
--- a/nashorn/make/build-nasgen.xml Thu Jul 21 16:45:56 2016 +0000
+++ b/nashorn/make/build-nasgen.xml Thu Jul 21 20:09:19 2016 -0700
@@ -43,14 +43,14 @@
</java>
</target>
- <target name="run-nasgen-eclipse">
+ <target name="run-nasgen-eclipse" depends="load-properties">
<mkdir dir="${basedir}/build/eclipse/.nasgentmp"/>
<java classname="jdk.nashorn.internal.tools.nasgen.Main" fork="true" failonerror="true">
<classpath>
<pathelement location="${basedir}/buildtools/nasgen/dist/nasgen.jar"/>
</classpath>
- <jvmarg value="-Xbootclasspath/p:${basedir}/build/eclipse"/>
+ <jvmarg line="${nasgen.module.imports}"/>
<arg value="${basedir}/build/eclipse"/>
<arg value="jdk.nashorn.internal.objects"/>
<arg value="${basedir}/build/eclipse/.nasgentmp"/>
--- a/nashorn/make/build.xml Thu Jul 21 16:45:56 2016 +0000
+++ b/nashorn/make/build.xml Thu Jul 21 20:09:19 2016 -0700
@@ -27,11 +27,14 @@
<import file="build-nasgen.xml"/>
<import file="code_coverage.xml"/>
- <target name="init-conditions">
+ <target name="load-properties">
<!-- loading locally defined resources and properties. NB they owerwrite default ones defined later -->
<property file="${user.home}/.nashorn.project.local.properties"/>
<loadproperties srcFile="make/project.properties"/>
+ </target>
+
+ <target name="init-conditions" depends="load-properties">
<path id="nashorn.jar.path">
<pathelement location="${nashorn.jar}"/>
</path>
--- a/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/linker/GuardingTypeConverterFactory.java Thu Jul 21 16:45:56 2016 +0000
+++ b/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/linker/GuardingTypeConverterFactory.java Thu Jul 21 20:09:19 2016 -0700
@@ -108,15 +108,19 @@
* language's objects to Java interfaces and classes by generating adapters
* for them.
* <p>
- * The type of the invocation is {@code targetType(sourceType)}, while the
- * type of the guard is {@code boolean(sourceType)}. You are allowed to
+ * The type of the invocation is <tt>(sourceType)→targetType</tt>, while the
+ * type of the guard is <tt>(sourceType)→boolean</tt>. You are allowed to
* return unconditional invocations (with no guard) if the source type is
* specific to your runtime and your runtime only.
- * <p>Note that this method will never be invoked for type conversions
- * allowed by the JLS 5.3 "Method Invocation Conversion", see
- * {@link TypeUtilities#isMethodInvocationConvertible(Class, Class)} for
- * details. An implementation can assume it is never requested to produce a
- * converter for these conversions.
+ * <p>Note that this method will never be invoked for
+ * {@link TypeUtilities#isMethodInvocationConvertible(Class, Class) method
+ * invocation conversions} as those can be automatically applied by
+ * {@link java.lang.invoke.MethodHandle#asType(MethodType)}.
+ * An implementation can assume it is never requested to produce a
+ * converter for those conversions. If a language runtime needs to customize
+ * method invocation conversions, it should
+ * {@link jdk.dynalink.DynamicLinkerFactory#setAutoConversionStrategy(MethodTypeConversionStrategy)
+ * set an autoconversion strategy in the dynamic linker factory} instead.
* <p>Dynalink is at liberty to either cache some of the returned converters
* or to repeatedly request the converter factory to create the same
* conversion.
@@ -127,6 +131,9 @@
* on whose behalf a type converter is requested. When a converter is
* requested as part of linking an {@code invokedynamic} instruction the
* supplier will return the lookup passed to the bootstrap method, otherwise
+ * if the method is invoked from within a
+ * {@link LinkerServices#getWithLookup(Supplier, jdk.dynalink.SecureLookupSupplier)}
+ * it will delegate to the secure lookup supplier. In any other case,
* it will return the public lookup. A typical case where the lookup might
* be needed is when the converter creates a Java adapter class on the fly
* (e.g. to convert some object from the dynamic language into a Java
--- a/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/linker/package-info.java Thu Jul 21 16:45:56 2016 +0000
+++ b/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/linker/package-info.java Thu Jul 21 20:09:19 2016 -0700
@@ -96,10 +96,8 @@
* language-specific manner if no other linker managed to handle the operation.)
* </p><p>
* A language runtime that wishes to make at least some of its linkers available
- * to other language runtimes for interoperability will need to declare the
- * class names of those linkers in
- * {@code /META-INF/services/jdk.dynalink.linker.GuardingDynamicLinker} file in
- * its distribution (typically, JAR file).
+ * to other language runtimes for interoperability will need to use a
+ * {@link jdk.dynalink.linker.GuardingDynamicLinkerExporter}.
* </p><p>
* Most language runtimes will be able to implement their own linking logic by
* implementing {@link jdk.dynalink.linker.TypeBasedGuardingDynamicLinker}
--- a/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/linker/support/TypeUtilities.java Thu Jul 21 16:45:56 2016 +0000
+++ b/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/linker/support/TypeUtilities.java Thu Jul 21 20:09:19 2016 -0700
@@ -138,7 +138,7 @@
* Determines whether one type can be converted to another type using a method invocation conversion, as per JLS 5.3
* "Method Invocation Conversion". This is basically all conversions allowed by subtyping (see
* {@link #isSubtype(Class, Class)}) as well as boxing conversion (JLS 5.1.7) optionally followed by widening
- * reference conversion and unboxing conversion (JLS 5.1.8) optionally followed by widening primitive conversion.
+ * reference conversion, and unboxing conversion (JLS 5.1.8) optionally followed by widening primitive conversion.
*
* @param sourceType the type being converted from (call site type for parameter types, method type for return types)
* @param targetType the parameter type being converted to (method type for parameter types, call site type for return types)
--- a/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/support/SimpleRelinkableCallSite.java Thu Jul 21 16:45:56 2016 +0000
+++ b/nashorn/src/jdk.dynalink/share/classes/jdk/dynalink/support/SimpleRelinkableCallSite.java Thu Jul 21 20:09:19 2016 -0700
@@ -90,9 +90,10 @@
/**
* A relinkable call site that implements monomorphic inline caching strategy,
- * only being linked to a single {@link GuardedInvocation}. If that invocation
- * is invalidated, it will throw it away and ask its associated
- * {@link DynamicLinker} to relink it.
+ * only being linked to a single {@link GuardedInvocation} at any given time.
+ * If the guard of that single invocation fails, or it has an invalidated
+ * switch point, or its invalidating exception triggered, then the call site
+ * will throw it away and ask its associated {@link DynamicLinker} to relink it.
*/
public class SimpleRelinkableCallSite extends AbstractRelinkableCallSite {
/**
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeArray.java Thu Jul 21 16:45:56 2016 +0000
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/NativeArray.java Thu Jul 21 20:09:19 2016 -0700
@@ -1329,30 +1329,31 @@
return ScriptRuntime.UNDEFINED;
}
- final Object start = args.length > 0 ? args[0] : ScriptRuntime.UNDEFINED;
- final Object deleteCount = args.length > 1 ? args[1] : ScriptRuntime.UNDEFINED;
-
- Object[] items;
-
- if (args.length > 2) {
- items = new Object[args.length - 2];
- System.arraycopy(args, 2, items, 0, items.length);
- } else {
- items = ScriptRuntime.EMPTY_ARRAY;
- }
-
- final ScriptObject sobj = (ScriptObject)obj;
- final long len = JSType.toUint32(sobj.getLength());
- final long relativeStart = JSType.toLong(start);
+ final ScriptObject sobj = (ScriptObject)obj;
+ final long len = JSType.toUint32(sobj.getLength());
+ final long relativeStart = JSType.toLong(args.length > 0 ? args[0] : ScriptRuntime.UNDEFINED);
final long actualStart = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len);
- final long actualDeleteCount = Math.min(Math.max(JSType.toLong(deleteCount), 0), len - actualStart);
+ final long actualDeleteCount;
+ Object[] items = ScriptRuntime.EMPTY_ARRAY;
+
+ if (args.length == 0) {
+ actualDeleteCount = 0;
+ } else if (args.length == 1) {
+ actualDeleteCount = len - actualStart;
+ } else {
+ actualDeleteCount = Math.min(Math.max(JSType.toLong(args[1]), 0), len - actualStart);
+ if (args.length > 2) {
+ items = new Object[args.length - 2];
+ System.arraycopy(args, 2, items, 0, items.length);
+ }
+ }
NativeArray returnValue;
if (actualStart <= Integer.MAX_VALUE && actualDeleteCount <= Integer.MAX_VALUE && bulkable(sobj)) {
try {
- returnValue = new NativeArray(sobj.getArray().fastSplice((int)actualStart, (int)actualDeleteCount, items.length));
+ returnValue = new NativeArray(sobj.getArray().fastSplice((int)actualStart, (int)actualDeleteCount, items.length));
// Since this is a dense bulkable array we can use faster defineOwnProperty to copy new elements
int k = (int) actualStart;
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/FindProperty.java Thu Jul 21 16:45:56 2016 +0000
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/FindProperty.java Thu Jul 21 20:09:19 2016 -0700
@@ -127,7 +127,7 @@
// Fold an accessor getter into the method handle of a user accessor property.
private MethodHandle insertAccessorsGetter(final UserAccessorProperty uap, final LinkRequest request, final MethodHandle mh) {
MethodHandle superGetter = uap.getAccessorsGetter();
- if (isInherited()) {
+ if (!isSelf()) {
superGetter = ScriptObject.addProtoFilter(superGetter, getProtoChainLength());
}
if (request != null && !(request.getReceiver() instanceof ScriptObject)) {
@@ -183,11 +183,12 @@
}
/**
- * Check if the property found was inherited, i.e. not directly in the self
- * @return true if inherited property
+ * Check if the property found was inherited from a prototype and it is an ordinary
+ * property (one that has no accessor function).
+ * @return true if the found property is an inherited ordinary property
*/
- public boolean isInherited() {
- return self != prototype;
+ public boolean isInheritedOrdinaryProperty() {
+ return !isSelf() && !getProperty().isAccessorProperty();
}
/**
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java Thu Jul 21 16:45:56 2016 +0000
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptObject.java Thu Jul 21 20:09:19 2016 -0700
@@ -796,7 +796,7 @@
* @param start the object on which the lookup was originally initiated
* @return FindPropertyData or null if not found.
*/
- protected FindProperty findProperty(final Object key, final boolean deep, boolean isScope, final ScriptObject start) {
+ protected FindProperty findProperty(final Object key, final boolean deep, final boolean isScope, final ScriptObject start) {
final PropertyMap selfMap = getMap();
final Property property = selfMap.findProperty(key);
@@ -1108,7 +1108,7 @@
*
* @return value of property as a MethodHandle or null.
*/
- protected MethodHandle getCallMethodHandle(final FindProperty find, final MethodType type, final String bindName) {
+ protected static MethodHandle getCallMethodHandle(final FindProperty find, final MethodType type, final String bindName) {
return getCallMethodHandle(find.getObjectValue(), type, bindName);
}
@@ -1121,7 +1121,7 @@
*
* @return value of property as a MethodHandle or null.
*/
- protected static MethodHandle getCallMethodHandle(final Object value, final MethodType type, final String bindName) {
+ private static MethodHandle getCallMethodHandle(final Object value, final MethodType type, final String bindName) {
return value instanceof ScriptFunction ? ((ScriptFunction)value).getCallMethodHandle(type, bindName) : null;
}
@@ -2107,13 +2107,13 @@
* @param desc call site descriptor
* @return method handle for getter
*/
- protected MethodHandle findGetIndexMethodHandle(final Class<?> returnType, final String name, final Class<?> elementType, final CallSiteDescriptor desc) {
+ private static MethodHandle findGetIndexMethodHandle(final Class<?> returnType, final String name, final Class<?> elementType, final CallSiteDescriptor desc) {
if (!returnType.isPrimitive()) {
- return findOwnMH_V(getClass(), name, returnType, elementType);
+ return findOwnMH_V(name, returnType, elementType);
}
return MH.insertArguments(
- findOwnMH_V(getClass(), name, returnType, elementType, int.class),
+ findOwnMH_V(name, returnType, elementType, int.class),
2,
NashornCallSiteDescriptor.isOptimistic(desc) ?
NashornCallSiteDescriptor.getProgramPoint(desc) :
@@ -2184,7 +2184,7 @@
FindProperty find = findProperty(name, true, NashornCallSiteDescriptor.isScope(desc), this);
// If it's not a scope search, then we don't want any inherited properties except those with user defined accessors.
- if (find != null && find.isInherited() && !find.getProperty().isAccessorProperty()) {
+ if (find != null && find.isInheritedOrdinaryProperty()) {
// We should still check if inherited data property is not writable
if (isExtensible() && !find.getProperty().isWritable()) {
return createEmptySetMethod(desc, explicitInstanceOfCheck, "property.not.writable", true);
@@ -2257,11 +2257,11 @@
}
}
- private GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) {
+ private static GuardedInvocation findMegaMorphicSetMethod(final CallSiteDescriptor desc, final String name) {
Context.getContextTrusted().getLogger(ObjectClassGenerator.class).warning("Megamorphic setter: ", desc, " ", name);
final MethodType type = desc.getMethodType().insertParameterTypes(1, Object.class);
//never bother with ClassCastExceptionGuard for megamorphic callsites
- final GuardedInvocation inv = findSetIndexMethod(getClass(), desc, false, type);
+ final GuardedInvocation inv = findSetIndexMethod(desc, false, type);
return inv.replaceMethods(MH.insertArguments(inv.getInvocation(), 1, name), inv.getGuard());
}
@@ -2284,25 +2284,24 @@
* @return GuardedInvocation to be invoked at call site.
*/
protected GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc, final LinkRequest request) { // array, index, value
- return findSetIndexMethod(getClass(), desc, explicitInstanceOfCheck(desc, request), desc.getMethodType());
+ return findSetIndexMethod(desc, explicitInstanceOfCheck(desc, request), desc.getMethodType());
}
/**
* Find the appropriate SETINDEX method for an invoke dynamic call.
*
- * @param clazz the receiver class
* @param desc the call site descriptor
* @param explicitInstanceOfCheck add an explicit instanceof check?
* @param callType the method type at the call site
*
* @return GuardedInvocation to be invoked at call site.
*/
- private static GuardedInvocation findSetIndexMethod(final Class<? extends ScriptObject> clazz, final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final MethodType callType) {
+ private static GuardedInvocation findSetIndexMethod(final CallSiteDescriptor desc, final boolean explicitInstanceOfCheck, final MethodType callType) {
assert callType.parameterCount() == 3;
final Class<?> keyClass = callType.parameterType(1);
final Class<?> valueClass = callType.parameterType(2);
- MethodHandle methodHandle = findOwnMH_V(clazz, "set", void.class, keyClass, valueClass, int.class);
+ MethodHandle methodHandle = findOwnMH_V("set", void.class, keyClass, valueClass, int.class);
methodHandle = MH.insertArguments(methodHandle, 3, NashornCallSiteDescriptor.getFlags(desc));
return new GuardedInvocation(methodHandle, getScriptObjectGuard(callType, explicitInstanceOfCheck), (SwitchPoint)null, explicitInstanceOfCheck ? null : ClassCastException.class);
@@ -2953,18 +2952,6 @@
return false;
}
- private boolean doesNotHaveCheckArrayKeys(final long longIndex, final long value, final int callSiteFlags) {
- if (getMap().containsArrayKeys()) {
- final String key = JSType.toString(longIndex);
- final FindProperty find = findProperty(key, true);
- if (find != null) {
- setObject(find, callSiteFlags, key, value);
- return true;
- }
- }
- return false;
- }
-
private boolean doesNotHaveCheckArrayKeys(final long longIndex, final double value, final int callSiteFlags) {
if (getMap().containsArrayKeys()) {
final String key = JSType.toString(longIndex);
@@ -3044,7 +3031,7 @@
invalidateGlobalConstant(key);
- if (f != null && f.isInherited() && !f.getProperty().isAccessorProperty()) {
+ if (f != null && f.isInheritedOrdinaryProperty()) {
final boolean isScope = isScopeFlag(callSiteFlags);
// If the start object of the find is not this object it means the property was found inside a
// 'with' statement expression (see WithObject.findProperty()). In this case we forward the 'set'
@@ -3467,15 +3454,10 @@
return this;
}
- private static MethodHandle findOwnMH_V(final Class<? extends ScriptObject> clazz, final String name, final Class<?> rtype, final Class<?>... types) {
- // TODO: figure out how can it work for NativeArray$Prototype etc.
+ private static MethodHandle findOwnMH_V(final String name, final Class<?> rtype, final Class<?>... types) {
return MH.findVirtual(MethodHandles.lookup(), ScriptObject.class, name, MH.type(rtype, types));
}
- private static MethodHandle findOwnMH_V(final String name, final Class<?> rtype, final Class<?>... types) {
- return findOwnMH_V(ScriptObject.class, name, rtype, types);
- }
-
private static MethodHandle findOwnMH_S(final String name, final Class<?> rtype, final Class<?>... types) {
return MH.findStatic(MethodHandles.lookup(), ScriptObject.class, name, MH.type(rtype, types));
}
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SetMethodCreator.java Thu Jul 21 16:45:56 2016 +0000
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/SetMethodCreator.java Thu Jul 21 20:09:19 2016 -0700
@@ -170,7 +170,7 @@
assert property != null;
final MethodHandle boundHandle;
- if (!property.isAccessorProperty() && find.isInherited()) {
+ if (find.isInheritedOrdinaryProperty()) {
boundHandle = ScriptObject.addProtoFilter(methodHandle, find.getProtoChainLength());
} else {
boundHandle = methodHandle;
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java Thu Jul 21 16:45:56 2016 +0000
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/PrimitiveLookup.java Thu Jul 21 20:09:19 2016 -0700
@@ -117,7 +117,7 @@
return new GuardedInvocation(GlobalConstants.staticConstantGetter(find.getObjectValue()), guard, sp, null);
}
- if (find.isInherited() && !(find.getProperty().isAccessorProperty())) {
+ if (find.isInheritedOrdinaryProperty()) {
// If property is found in the prototype object bind the method handle directly to
// the proto filter instead of going through wrapper instantiation below.
final ScriptObject proto = wrappedReceiver.getProto();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/basic/JDK-8068972.js Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+/**
+ * JDK-8068972: Array.splice should follow the ES6 specification
+ *
+ * @test
+ * @run
+ */
+
+
+function assertEqualArrays(a, b) {
+ Assert.assertTrue(Array.isArray(a));
+ Assert.assertTrue(Array.isArray(b));
+ Assert.assertTrue(a.length === b.length);
+ Assert.assertTrue(a.every(function(v, j) {
+ return v === b[j];
+ }));
+}
+
+var array = [1, 2, 3, 4, 5, 6, 7];
+
+var result = array.splice();
+assertEqualArrays(array, [1, 2, 3, 4, 5, 6, 7]);
+assertEqualArrays(result, []);
+
+result = array.splice(4);
+assertEqualArrays(array, [1, 2, 3, 4]);
+assertEqualArrays(result, [5, 6, 7]);
+
+result = array.splice(1, 2, -2, -3);
+assertEqualArrays(array, [1, -2, -3, 4]);
+assertEqualArrays(result, [2, 3]);
--- a/nashorn/test/script/currently-failing/logcoverage.js Thu Jul 21 16:45:56 2016 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,108 +0,0 @@
-/*
- * 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.
- *
- * 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.
- */
-
-/**
- * mh_coverage.js
- * Screen scrape various logs to ensure that we cover enough functionality,
- * e.g. method handle instrumentation
- *
- * @test
- * @run
- */
-
-/*
- * creates new script engine initialized with given options and
- * runs given code on it. Returns standard output captured.
- */
-
-function runScriptEngine(opts, name) {
- var imports = new JavaImporter(
- Packages.jdk.nashorn.api.scripting,
- java.io, java.lang, java.util);
-
- with(imports) {
- var fac = new NashornScriptEngineFactory();
- // get current System.err
- var oldErr = System.err;
- var oldOut = System.out;
- var baosErr = new ByteArrayOutputStream();
- var newErr = new PrintStream(baosErr);
- var baosOut = new ByteArrayOutputStream();
- var newOut = new PrintStream(baosOut);
- try {
- // set new standard err
- System.setErr(newErr);
- System.setOut(newOut);
- var engine = fac.getScriptEngine(Java.to(opts, "java.lang.String[]"));
- var reader = new java.io.FileReader(name);
- engine.eval(reader);
- newErr.flush();
- newOut.flush();
- return new java.lang.String(baosErr.toByteArray());
- } finally {
- // restore System.err to old value
- System.setErr(oldErr);
- System.setOut(oldOut);
- }
- }
-}
-
-var str;
-
-var methodsCalled = [
- 'asCollector',
- 'asType',
- 'bindTo',
- 'dropArguments',
- 'explicitCastArguments',
- 'filterArguments',
- 'filterReturnValue',
- 'findStatic',
- 'findVirtual',
- 'foldArguments',
- 'getter',
- 'guardWithTest',
- 'insertArguments',
- 'methodType',
- 'setter'
-];
-
-function check(str, strs) {
- for each (s in strs) {
- if (str.indexOf(s) !== -1) {
- continue;
- }
- print(s + " not found");
- return;
- }
- print("check ok!");
-}
-
-str = runScriptEngine([ "--log=codegen,compiler=finest,methodhandles=finest,fields=finest" ], __DIR__ + "../basic/NASHORN-19.js");
-str += runScriptEngine([ "--log=codegen,compiler=finest,methodhandles=finest,fields=finest" ], __DIR__ + "../basic/varargs.js");
-
-check(str, methodsCalled);
-check(str, ['return', 'get', 'set', '[fields]']);
-check(str, ['codegen']);
-
-print("hello, world!");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/nosecurity/logcoverage.js Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2010, 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.
+ */
+
+/**
+ * Screen scrape various logs to ensure that we cover enough functionality,
+ * e.g. method handle instrumentation
+ *
+ * @test
+ * @fork
+ * @option -Dnashorn.debug=true
+ */
+
+/*
+ * creates new script engine initialized with given options and
+ * runs given code on it. Returns standard output captured.
+ */
+
+function runScriptEngine(opts, name) {
+ var imports = new JavaImporter(
+ Packages.jdk.nashorn.api.scripting,
+ java.io, java.lang, java.util);
+
+ with (imports) {
+ var fac = new NashornScriptEngineFactory();
+ // get current System.err
+ var oldErr = System.err;
+ var oldOut = System.out;
+ var baosErr = new ByteArrayOutputStream();
+ var newErr = new PrintStream(baosErr);
+ var baosOut = new ByteArrayOutputStream();
+ var newOut = new PrintStream(baosOut);
+ try {
+ // set new standard err
+ System.setErr(newErr);
+ System.setOut(newOut);
+ var engine = fac.getScriptEngine(Java.to(opts, "java.lang.String[]"));
+ var reader = new java.io.FileReader(name);
+ engine.eval(reader);
+ newErr.flush();
+ newOut.flush();
+ return new java.lang.String(baosErr.toByteArray());
+ } finally {
+ // restore System.err to old value
+ System.setErr(oldErr);
+ System.setOut(oldOut);
+ }
+ }
+}
+
+var str;
+
+var methodsCalled = [
+ 'asCollector',
+ 'asType',
+ 'bindTo',
+ 'dropArguments',
+ 'explicitCastArguments',
+ 'filterArguments',
+ 'filterReturnValue',
+ 'findStatic',
+ 'findVirtual',
+ 'foldArguments',
+ 'getter',
+ 'guardWithTest',
+ 'insertArguments',
+ 'methodType',
+ 'setter'
+];
+
+function check(str, strs) {
+ for each (s in strs) {
+ if (str.indexOf(s) !== -1) {
+ continue;
+ }
+ print(s + " not found");
+ return;
+ }
+ print("check ok!");
+}
+
+str = runScriptEngine(["--log=codegen,compiler=finest,methodhandles=finest,fields=finest"], __DIR__ + "../basic/NASHORN-19.js");
+str += runScriptEngine(["--log=codegen,compiler=finest,methodhandles=finest,fields=finest"], __DIR__ + "../basic/varargs.js");
+
+check(str, methodsCalled);
+check(str, ['return', 'get', 'set', '[fields]']);
+check(str, ['codegen']);
+
+print("hello, world!");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/nosecurity/logcoverage.js.EXPECTED Thu Jul 21 20:09:19 2016 -0700
@@ -0,0 +1,4 @@
+check ok!
+check ok!
+check ok!
+hello, world!